if_iwn.c revision 221634
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 221634 2011-05-08 10:19:29Z 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);
157206474Sbschmidt#if 0	/* HT */
158206474Sbschmidtstatic void	iwn_read_eeprom_ht40(struct iwn_softc *, int);
159206474Sbschmidt#endif
160220726Sbschmidtstatic void	iwn_read_eeprom_channels(struct iwn_softc *, int, uint32_t);
161220723Sbschmidtstatic struct iwn_eeprom_chan *iwn_find_eeprom_channel(struct iwn_softc *,
162220723Sbschmidt		    struct ieee80211_channel *);
163220723Sbschmidtstatic int	iwn_setregdomain(struct ieee80211com *,
164220723Sbschmidt		    struct ieee80211_regdomain *, int,
165220726Sbschmidt		    struct ieee80211_channel[]);
166206477Sbschmidtstatic void	iwn_read_eeprom_enhinfo(struct iwn_softc *);
167206477Sbschmidtstatic struct ieee80211_node *iwn_node_alloc(struct ieee80211vap *,
168198429Srpaulo		    const uint8_t mac[IEEE80211_ADDR_LEN]);
169220715Sbschmidtstatic void	iwn_newassoc(struct ieee80211_node *, int);
170206477Sbschmidtstatic int	iwn_media_change(struct ifnet *);
171206477Sbschmidtstatic int	iwn_newstate(struct ieee80211vap *, enum ieee80211_state, int);
172220667Sbschmidtstatic void	iwn_calib_timeout(void *);
173206477Sbschmidtstatic void	iwn_rx_phy(struct iwn_softc *, struct iwn_rx_desc *,
174198429Srpaulo		    struct iwn_rx_data *);
175206477Sbschmidtstatic void	iwn_rx_done(struct iwn_softc *, struct iwn_rx_desc *,
176178676Ssam		    struct iwn_rx_data *);
177201209Srpaulo#if 0	/* HT */
178206477Sbschmidtstatic void	iwn_rx_compressed_ba(struct iwn_softc *, struct iwn_rx_desc *,
179201209Srpaulo		    struct iwn_rx_data *);
180201209Srpaulo#endif
181220674Sbschmidtstatic void	iwn5000_rx_calib_results(struct iwn_softc *,
182220674Sbschmidt		    struct iwn_rx_desc *, struct iwn_rx_data *);
183206477Sbschmidtstatic void	iwn_rx_statistics(struct iwn_softc *, struct iwn_rx_desc *,
184198429Srpaulo		    struct iwn_rx_data *);
185206477Sbschmidtstatic void	iwn4965_tx_done(struct iwn_softc *, struct iwn_rx_desc *,
186198429Srpaulo		    struct iwn_rx_data *);
187206477Sbschmidtstatic void	iwn5000_tx_done(struct iwn_softc *, struct iwn_rx_desc *,
188198429Srpaulo		    struct iwn_rx_data *);
189206477Sbschmidtstatic void	iwn_tx_done(struct iwn_softc *, struct iwn_rx_desc *, int,
190198429Srpaulo		    uint8_t);
191206477Sbschmidtstatic void	iwn_cmd_done(struct iwn_softc *, struct iwn_rx_desc *);
192206477Sbschmidtstatic void	iwn_notif_intr(struct iwn_softc *);
193206477Sbschmidtstatic void	iwn_wakeup_intr(struct iwn_softc *);
194206477Sbschmidtstatic void	iwn_rftoggle_intr(struct iwn_softc *);
195206477Sbschmidtstatic void	iwn_fatal_intr(struct iwn_softc *);
196206477Sbschmidtstatic void	iwn_intr(void *);
197206477Sbschmidtstatic void	iwn4965_update_sched(struct iwn_softc *, int, int, uint8_t,
198198429Srpaulo		    uint16_t);
199206477Sbschmidtstatic void	iwn5000_update_sched(struct iwn_softc *, int, int, uint8_t,
200198429Srpaulo		    uint16_t);
201206475Sbschmidt#ifdef notyet
202206477Sbschmidtstatic void	iwn5000_reset_sched(struct iwn_softc *, int, int);
203206475Sbschmidt#endif
204206474Sbschmidtstatic uint8_t	iwn_plcp_signal(int);
205206477Sbschmidtstatic int	iwn_tx_data(struct iwn_softc *, struct mbuf *,
206220720Sbschmidt		    struct ieee80211_node *);
207220720Sbschmidtstatic int	iwn_tx_data_raw(struct iwn_softc *, struct mbuf *,
208220720Sbschmidt		    struct ieee80211_node *,
209220720Sbschmidt		    const struct ieee80211_bpf_params *params);
210198429Srpaulostatic int	iwn_raw_xmit(struct ieee80211_node *, struct mbuf *,
211198429Srpaulo		    const struct ieee80211_bpf_params *);
212206477Sbschmidtstatic void	iwn_start(struct ifnet *);
213206477Sbschmidtstatic void	iwn_start_locked(struct ifnet *);
214220667Sbschmidtstatic void	iwn_watchdog(void *);
215206477Sbschmidtstatic int	iwn_ioctl(struct ifnet *, u_long, caddr_t);
216206477Sbschmidtstatic int	iwn_cmd(struct iwn_softc *, int, const void *, int, int);
217206477Sbschmidtstatic int	iwn4965_add_node(struct iwn_softc *, struct iwn_node_info *,
218198429Srpaulo		    int);
219206477Sbschmidtstatic int	iwn5000_add_node(struct iwn_softc *, struct iwn_node_info *,
220198429Srpaulo		    int);
221220715Sbschmidtstatic int	iwn_set_link_quality(struct iwn_softc *,
222220715Sbschmidt		    struct ieee80211_node *);
223206477Sbschmidtstatic int	iwn_add_broadcast_node(struct iwn_softc *, int);
224220721Sbschmidtstatic int	iwn_updateedca(struct ieee80211com *);
225201209Srpaulostatic void	iwn_update_mcast(struct ifnet *);
226206477Sbschmidtstatic void	iwn_set_led(struct iwn_softc *, uint8_t, uint8_t, uint8_t);
227206477Sbschmidtstatic int	iwn_set_critical_temp(struct iwn_softc *);
228206477Sbschmidtstatic int	iwn_set_timing(struct iwn_softc *, struct ieee80211_node *);
229206477Sbschmidtstatic void	iwn4965_power_calibration(struct iwn_softc *, int);
230206477Sbschmidtstatic int	iwn4965_set_txpower(struct iwn_softc *,
231201882Skeramida		    struct ieee80211_channel *, int);
232206477Sbschmidtstatic int	iwn5000_set_txpower(struct iwn_softc *,
233201882Skeramida		    struct ieee80211_channel *, int);
234206477Sbschmidtstatic int	iwn4965_get_rssi(struct iwn_softc *, struct iwn_rx_stat *);
235206477Sbschmidtstatic int	iwn5000_get_rssi(struct iwn_softc *, struct iwn_rx_stat *);
236206477Sbschmidtstatic int	iwn_get_noise(const struct iwn_rx_general_stats *);
237206477Sbschmidtstatic int	iwn4965_get_temperature(struct iwn_softc *);
238206477Sbschmidtstatic int	iwn5000_get_temperature(struct iwn_softc *);
239206477Sbschmidtstatic int	iwn_init_sensitivity(struct iwn_softc *);
240206477Sbschmidtstatic void	iwn_collect_noise(struct iwn_softc *,
241178676Ssam		    const struct iwn_rx_general_stats *);
242206477Sbschmidtstatic int	iwn4965_init_gains(struct iwn_softc *);
243206477Sbschmidtstatic int	iwn5000_init_gains(struct iwn_softc *);
244206477Sbschmidtstatic int	iwn4965_set_gains(struct iwn_softc *);
245206477Sbschmidtstatic int	iwn5000_set_gains(struct iwn_softc *);
246206477Sbschmidtstatic void	iwn_tune_sensitivity(struct iwn_softc *,
247178676Ssam		    const struct iwn_rx_stats *);
248206477Sbschmidtstatic int	iwn_send_sensitivity(struct iwn_softc *);
249206477Sbschmidtstatic int	iwn_set_pslevel(struct iwn_softc *, int, int, int);
250220662Sbschmidtstatic int	iwn_send_btcoex(struct iwn_softc *);
251220891Sbschmidtstatic int	iwn_send_advanced_btcoex(struct iwn_softc *);
252206477Sbschmidtstatic int	iwn_config(struct iwn_softc *);
253220634Sbschmidtstatic uint8_t	*ieee80211_add_ssid(uint8_t *, const uint8_t *, u_int);
254206477Sbschmidtstatic int	iwn_scan(struct iwn_softc *);
255206477Sbschmidtstatic int	iwn_auth(struct iwn_softc *, struct ieee80211vap *vap);
256206477Sbschmidtstatic int	iwn_run(struct iwn_softc *, struct ieee80211vap *vap);
257206474Sbschmidt#if 0	/* HT */
258206474Sbschmidtstatic int	iwn_ampdu_rx_start(struct ieee80211com *,
259206474Sbschmidt		    struct ieee80211_node *, uint8_t);
260206474Sbschmidtstatic void	iwn_ampdu_rx_stop(struct ieee80211com *,
261206474Sbschmidt		    struct ieee80211_node *, uint8_t);
262206474Sbschmidtstatic int	iwn_ampdu_tx_start(struct ieee80211com *,
263206474Sbschmidt		    struct ieee80211_node *, uint8_t);
264206474Sbschmidtstatic void	iwn_ampdu_tx_stop(struct ieee80211com *,
265206474Sbschmidt		    struct ieee80211_node *, uint8_t);
266206474Sbschmidtstatic void	iwn4965_ampdu_tx_start(struct iwn_softc *,
267206474Sbschmidt		    struct ieee80211_node *, uint8_t, uint16_t);
268220726Sbschmidtstatic void	iwn4965_ampdu_tx_stop(struct iwn_softc *,
269220726Sbschmidt		    uint8_t, uint16_t);
270206474Sbschmidtstatic void	iwn5000_ampdu_tx_start(struct iwn_softc *,
271206474Sbschmidt		    struct ieee80211_node *, uint8_t, uint16_t);
272220726Sbschmidtstatic void	iwn5000_ampdu_tx_stop(struct iwn_softc *,
273220726Sbschmidt		    uint8_t, uint16_t);
274206474Sbschmidt#endif
275220674Sbschmidtstatic int	iwn5000_query_calibration(struct iwn_softc *);
276220674Sbschmidtstatic int	iwn5000_send_calibration(struct iwn_softc *);
277206477Sbschmidtstatic int	iwn5000_send_wimax_coex(struct iwn_softc *);
278220677Sbschmidtstatic int	iwn5000_crystal_calib(struct iwn_softc *);
279220676Sbschmidtstatic int	iwn5000_temp_offset_calib(struct iwn_softc *);
280206477Sbschmidtstatic int	iwn4965_post_alive(struct iwn_softc *);
281206477Sbschmidtstatic int	iwn5000_post_alive(struct iwn_softc *);
282206477Sbschmidtstatic int	iwn4965_load_bootcode(struct iwn_softc *, const uint8_t *,
283198429Srpaulo		    int);
284206477Sbschmidtstatic int	iwn4965_load_firmware(struct iwn_softc *);
285206477Sbschmidtstatic int	iwn5000_load_firmware_section(struct iwn_softc *, uint32_t,
286198429Srpaulo		    const uint8_t *, int);
287206477Sbschmidtstatic int	iwn5000_load_firmware(struct iwn_softc *);
288210111Sbschmidtstatic int	iwn_read_firmware_leg(struct iwn_softc *,
289210111Sbschmidt		    struct iwn_fw_info *);
290210111Sbschmidtstatic int	iwn_read_firmware_tlv(struct iwn_softc *,
291210111Sbschmidt		    struct iwn_fw_info *, uint16_t);
292206477Sbschmidtstatic int	iwn_read_firmware(struct iwn_softc *);
293206477Sbschmidtstatic int	iwn_clock_wait(struct iwn_softc *);
294206477Sbschmidtstatic int	iwn_apm_init(struct iwn_softc *);
295206477Sbschmidtstatic void	iwn_apm_stop_master(struct iwn_softc *);
296206477Sbschmidtstatic void	iwn_apm_stop(struct iwn_softc *);
297206477Sbschmidtstatic int	iwn4965_nic_config(struct iwn_softc *);
298206477Sbschmidtstatic int	iwn5000_nic_config(struct iwn_softc *);
299206477Sbschmidtstatic int	iwn_hw_prepare(struct iwn_softc *);
300206477Sbschmidtstatic int	iwn_hw_init(struct iwn_softc *);
301206477Sbschmidtstatic void	iwn_hw_stop(struct iwn_softc *);
302220723Sbschmidtstatic void	iwn_radio_on(void *, int);
303220723Sbschmidtstatic void	iwn_radio_off(void *, int);
304206477Sbschmidtstatic void	iwn_init_locked(struct iwn_softc *);
305206477Sbschmidtstatic void	iwn_init(void *);
306206477Sbschmidtstatic void	iwn_stop_locked(struct iwn_softc *);
307206477Sbschmidtstatic void	iwn_stop(struct iwn_softc *);
308220726Sbschmidtstatic void	iwn_scan_start(struct ieee80211com *);
309220726Sbschmidtstatic void	iwn_scan_end(struct ieee80211com *);
310220726Sbschmidtstatic void	iwn_set_channel(struct ieee80211com *);
311220726Sbschmidtstatic void	iwn_scan_curchan(struct ieee80211_scan_state *, unsigned long);
312220726Sbschmidtstatic void	iwn_scan_mindwell(struct ieee80211_scan_state *);
313198429Srpaulostatic void	iwn_hw_reset(void *, int);
314178676Ssam
315178676Ssam#define IWN_DEBUG
316178676Ssam#ifdef IWN_DEBUG
317178676Ssamenum {
318178676Ssam	IWN_DEBUG_XMIT		= 0x00000001,	/* basic xmit operation */
319178676Ssam	IWN_DEBUG_RECV		= 0x00000002,	/* basic recv operation */
320178676Ssam	IWN_DEBUG_STATE		= 0x00000004,	/* 802.11 state transitions */
321178676Ssam	IWN_DEBUG_TXPOW		= 0x00000008,	/* tx power processing */
322178676Ssam	IWN_DEBUG_RESET		= 0x00000010,	/* reset processing */
323178676Ssam	IWN_DEBUG_OPS		= 0x00000020,	/* iwn_ops processing */
324178676Ssam	IWN_DEBUG_BEACON 	= 0x00000040,	/* beacon handling */
325178676Ssam	IWN_DEBUG_WATCHDOG 	= 0x00000080,	/* watchdog timeout */
326178676Ssam	IWN_DEBUG_INTR		= 0x00000100,	/* ISR */
327178676Ssam	IWN_DEBUG_CALIBRATE	= 0x00000200,	/* periodic calibration */
328178676Ssam	IWN_DEBUG_NODE		= 0x00000400,	/* node management */
329178676Ssam	IWN_DEBUG_LED		= 0x00000800,	/* led management */
330178676Ssam	IWN_DEBUG_CMD		= 0x00001000,	/* cmd submission */
331178676Ssam	IWN_DEBUG_FATAL		= 0x80000000,	/* fatal errors */
332178676Ssam	IWN_DEBUG_ANY		= 0xffffffff
333178676Ssam};
334178676Ssam
335178676Ssam#define DPRINTF(sc, m, fmt, ...) do {			\
336178676Ssam	if (sc->sc_debug & (m))				\
337178676Ssam		printf(fmt, __VA_ARGS__);		\
338178676Ssam} while (0)
339178676Ssam
340220723Sbschmidtstatic const char *
341220723Sbschmidtiwn_intr_str(uint8_t cmd)
342220723Sbschmidt{
343220723Sbschmidt	switch (cmd) {
344220723Sbschmidt	/* Notifications */
345220723Sbschmidt	case IWN_UC_READY:		return "UC_READY";
346220723Sbschmidt	case IWN_ADD_NODE_DONE:		return "ADD_NODE_DONE";
347220723Sbschmidt	case IWN_TX_DONE:		return "TX_DONE";
348220723Sbschmidt	case IWN_START_SCAN:		return "START_SCAN";
349220723Sbschmidt	case IWN_STOP_SCAN:		return "STOP_SCAN";
350220723Sbschmidt	case IWN_RX_STATISTICS:		return "RX_STATS";
351220723Sbschmidt	case IWN_BEACON_STATISTICS:	return "BEACON_STATS";
352220723Sbschmidt	case IWN_STATE_CHANGED:		return "STATE_CHANGED";
353220723Sbschmidt	case IWN_BEACON_MISSED:		return "BEACON_MISSED";
354220723Sbschmidt	case IWN_RX_PHY:		return "RX_PHY";
355220723Sbschmidt	case IWN_MPDU_RX_DONE:		return "MPDU_RX_DONE";
356220723Sbschmidt	case IWN_RX_DONE:		return "RX_DONE";
357220723Sbschmidt
358220723Sbschmidt	/* Command Notifications */
359220723Sbschmidt	case IWN_CMD_RXON:		return "IWN_CMD_RXON";
360220723Sbschmidt	case IWN_CMD_RXON_ASSOC:	return "IWN_CMD_RXON_ASSOC";
361220723Sbschmidt	case IWN_CMD_EDCA_PARAMS:	return "IWN_CMD_EDCA_PARAMS";
362220723Sbschmidt	case IWN_CMD_TIMING:		return "IWN_CMD_TIMING";
363220723Sbschmidt	case IWN_CMD_LINK_QUALITY:	return "IWN_CMD_LINK_QUALITY";
364220723Sbschmidt	case IWN_CMD_SET_LED:		return "IWN_CMD_SET_LED";
365220723Sbschmidt	case IWN5000_CMD_WIMAX_COEX:	return "IWN5000_CMD_WIMAX_COEX";
366220723Sbschmidt	case IWN5000_CMD_CALIB_CONFIG:	return "IWN5000_CMD_CALIB_CONFIG";
367220723Sbschmidt	case IWN5000_CMD_CALIB_RESULT:	return "IWN5000_CMD_CALIB_RESULT";
368220723Sbschmidt	case IWN5000_CMD_CALIB_COMPLETE: return "IWN5000_CMD_CALIB_COMPLETE";
369220723Sbschmidt	case IWN_CMD_SET_POWER_MODE:	return "IWN_CMD_SET_POWER_MODE";
370220723Sbschmidt	case IWN_CMD_SCAN:		return "IWN_CMD_SCAN";
371220723Sbschmidt	case IWN_CMD_SCAN_RESULTS:	return "IWN_CMD_SCAN_RESULTS";
372220723Sbschmidt	case IWN_CMD_TXPOWER:		return "IWN_CMD_TXPOWER";
373220723Sbschmidt	case IWN_CMD_TXPOWER_DBM:	return "IWN_CMD_TXPOWER_DBM";
374220723Sbschmidt	case IWN5000_CMD_TX_ANT_CONFIG:	return "IWN5000_CMD_TX_ANT_CONFIG";
375220723Sbschmidt	case IWN_CMD_BT_COEX:		return "IWN_CMD_BT_COEX";
376220723Sbschmidt	case IWN_CMD_SET_CRITICAL_TEMP:	return "IWN_CMD_SET_CRITICAL_TEMP";
377220723Sbschmidt	case IWN_CMD_SET_SENSITIVITY:	return "IWN_CMD_SET_SENSITIVITY";
378220723Sbschmidt	case IWN_CMD_PHY_CALIB:		return "IWN_CMD_PHY_CALIB";
379220723Sbschmidt	}
380220723Sbschmidt	return "UNKNOWN INTR NOTIF/CMD";
381220723Sbschmidt}
382178676Ssam#else
383178676Ssam#define DPRINTF(sc, m, fmt, ...) do { (void) sc; } while (0)
384178676Ssam#endif
385178676Ssam
386220723Sbschmidtstatic device_method_t iwn_methods[] = {
387220723Sbschmidt	/* Device interface */
388220723Sbschmidt	DEVMETHOD(device_probe,		iwn_probe),
389220723Sbschmidt	DEVMETHOD(device_attach,	iwn_attach),
390220723Sbschmidt	DEVMETHOD(device_detach,	iwn_detach),
391220723Sbschmidt	DEVMETHOD(device_shutdown,	iwn_shutdown),
392220723Sbschmidt	DEVMETHOD(device_suspend,	iwn_suspend),
393220723Sbschmidt	DEVMETHOD(device_resume,	iwn_resume),
394220723Sbschmidt	{ 0, 0 }
395178676Ssam};
396178676Ssam
397220723Sbschmidtstatic driver_t iwn_driver = {
398220723Sbschmidt	"iwn",
399220723Sbschmidt	iwn_methods,
400220726Sbschmidt	sizeof(struct iwn_softc)
401178676Ssam};
402220723Sbschmidtstatic devclass_t iwn_devclass;
403178676Ssam
404220723SbschmidtDRIVER_MODULE(iwn, pci, iwn_driver, iwn_devclass, 0, 0);
405220726Sbschmidt
406220723SbschmidtMODULE_DEPEND(iwn, firmware, 1, 1, 1);
407220723SbschmidtMODULE_DEPEND(iwn, pci, 1, 1, 1);
408220723SbschmidtMODULE_DEPEND(iwn, wlan, 1, 1, 1);
409220723Sbschmidt
410178676Ssamstatic int
411178676Ssamiwn_probe(device_t dev)
412178676Ssam{
413198429Srpaulo	const struct iwn_ident *ident;
414178676Ssam
415198429Srpaulo	for (ident = iwn_ident_table; ident->name != NULL; ident++) {
416198429Srpaulo		if (pci_get_vendor(dev) == ident->vendor &&
417198429Srpaulo		    pci_get_device(dev) == ident->device) {
418198429Srpaulo			device_set_desc(dev, ident->name);
419198429Srpaulo			return 0;
420198429Srpaulo		}
421198429Srpaulo	}
422198429Srpaulo	return ENXIO;
423178676Ssam}
424178676Ssam
425178676Ssamstatic int
426178676Ssamiwn_attach(device_t dev)
427178676Ssam{
428178676Ssam	struct iwn_softc *sc = (struct iwn_softc *)device_get_softc(dev);
429178676Ssam	struct ieee80211com *ic;
430178676Ssam	struct ifnet *ifp;
431220721Sbschmidt	uint32_t reg;
432184233Smav	int i, error, result;
433190526Ssam	uint8_t macaddr[IEEE80211_ADDR_LEN];
434178676Ssam
435178676Ssam	sc->sc_dev = dev;
436178676Ssam
437198429Srpaulo	/*
438198429Srpaulo	 * Get the offset of the PCI Express Capability Structure in PCI
439198429Srpaulo	 * Configuration Space.
440198429Srpaulo	 */
441219902Sjhb	error = pci_find_cap(dev, PCIY_EXPRESS, &sc->sc_cap_off);
442198429Srpaulo	if (error != 0) {
443198429Srpaulo		device_printf(dev, "PCIe capability structure not found!\n");
444198429Srpaulo		return error;
445178676Ssam	}
446178676Ssam
447198429Srpaulo	/* Clear device-specific "PCI retry timeout" register (41h). */
448178676Ssam	pci_write_config(dev, 0x41, 0, 1);
449178676Ssam
450198429Srpaulo	/* Hardware bug workaround. */
451220721Sbschmidt	reg = pci_read_config(dev, PCIR_COMMAND, 1);
452220721Sbschmidt	if (reg & PCIM_CMD_INTxDIS) {
453198429Srpaulo		DPRINTF(sc, IWN_DEBUG_RESET, "%s: PCIe INTx Disable set\n",
454198429Srpaulo		    __func__);
455220721Sbschmidt		reg &= ~PCIM_CMD_INTxDIS;
456220721Sbschmidt		pci_write_config(dev, PCIR_COMMAND, reg, 1);
457198429Srpaulo	}
458198429Srpaulo
459198429Srpaulo	/* Enable bus-mastering. */
460178676Ssam	pci_enable_busmaster(dev);
461178676Ssam
462198429Srpaulo	sc->mem_rid = PCIR_BAR(0);
463178676Ssam	sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid,
464198429Srpaulo	    RF_ACTIVE);
465220726Sbschmidt	if (sc->mem == NULL) {
466220724Sbschmidt		device_printf(dev, "can't map mem space\n");
467198429Srpaulo		error = ENOMEM;
468178676Ssam		return error;
469178676Ssam	}
470178676Ssam	sc->sc_st = rman_get_bustag(sc->mem);
471178676Ssam	sc->sc_sh = rman_get_bushandle(sc->mem);
472220726Sbschmidt
473178676Ssam	sc->irq_rid = 0;
474184233Smav	if ((result = pci_msi_count(dev)) == 1 &&
475184233Smav	    pci_alloc_msi(dev, &result) == 0)
476184233Smav		sc->irq_rid = 1;
477220725Sbschmidt	/* Install interrupt handler. */
478178676Ssam	sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irq_rid,
479198429Srpaulo	    RF_ACTIVE | RF_SHAREABLE);
480178676Ssam	if (sc->irq == NULL) {
481220724Sbschmidt		device_printf(dev, "can't map interrupt\n");
482178676Ssam		error = ENOMEM;
483198429Srpaulo		goto fail;
484178676Ssam	}
485178676Ssam
486178676Ssam	IWN_LOCK_INIT(sc);
487178676Ssam
488220728Sbschmidt	/* Read hardware revision and attach. */
489220728Sbschmidt	sc->hw_type = (IWN_READ(sc, IWN_HW_REV) >> 4) & 0xf;
490220728Sbschmidt	if (sc->hw_type == IWN_HW_REV_TYPE_4965)
491220728Sbschmidt		error = iwn4965_attach(sc, pci_get_device(dev));
492220728Sbschmidt	else
493220728Sbschmidt		error = iwn5000_attach(sc, pci_get_device(dev));
494220728Sbschmidt	if (error != 0) {
495220728Sbschmidt		device_printf(dev, "could not attach device, error %d\n",
496220728Sbschmidt		    error);
497198429Srpaulo		goto fail;
498198429Srpaulo	}
499198429Srpaulo
500220726Sbschmidt	if ((error = iwn_hw_prepare(sc)) != 0) {
501198429Srpaulo		device_printf(dev, "hardware not ready, error %d\n", error);
502178676Ssam		goto fail;
503178676Ssam	}
504178676Ssam
505198429Srpaulo	/* Allocate DMA memory for firmware transfers. */
506220726Sbschmidt	if ((error = iwn_alloc_fwmem(sc)) != 0) {
507178676Ssam		device_printf(dev,
508198429Srpaulo		    "could not allocate memory for firmware, error %d\n",
509198429Srpaulo		    error);
510178676Ssam		goto fail;
511178676Ssam	}
512178676Ssam
513198429Srpaulo	/* Allocate "Keep Warm" page. */
514220726Sbschmidt	if ((error = iwn_alloc_kw(sc)) != 0) {
515178676Ssam		device_printf(dev,
516220724Sbschmidt		    "could not allocate keep warm page, error %d\n", error);
517178676Ssam		goto fail;
518178676Ssam	}
519178676Ssam
520201209Srpaulo	/* Allocate ICT table for 5000 Series. */
521201209Srpaulo	if (sc->hw_type != IWN_HW_REV_TYPE_4965 &&
522201209Srpaulo	    (error = iwn_alloc_ict(sc)) != 0) {
523220724Sbschmidt		device_printf(dev, "could not allocate ICT table, error %d\n",
524220724Sbschmidt		    error);
525201209Srpaulo		goto fail;
526201209Srpaulo	}
527201209Srpaulo
528198429Srpaulo	/* Allocate TX scheduler "rings". */
529220726Sbschmidt	if ((error = iwn_alloc_sched(sc)) != 0) {
530178676Ssam		device_printf(dev,
531220726Sbschmidt		    "could not allocate TX scheduler rings, error %d\n", error);
532178676Ssam		goto fail;
533178676Ssam	}
534178676Ssam
535220725Sbschmidt	/* Allocate TX rings (16 on 4965AGN, 20 on >=5000). */
536220728Sbschmidt	for (i = 0; i < sc->ntxqs; i++) {
537220726Sbschmidt		if ((error = iwn_alloc_tx_ring(sc, &sc->txq[i], i)) != 0) {
538178676Ssam			device_printf(dev,
539220724Sbschmidt			    "could not allocate TX ring %d, error %d\n", i,
540220724Sbschmidt			    error);
541178676Ssam			goto fail;
542178676Ssam		}
543178676Ssam	}
544178676Ssam
545198429Srpaulo	/* Allocate RX ring. */
546220726Sbschmidt	if ((error = iwn_alloc_rx_ring(sc, &sc->rxq)) != 0) {
547220724Sbschmidt		device_printf(dev, "could not allocate RX ring, error %d\n",
548220724Sbschmidt		    error);
549178676Ssam		goto fail;
550178676Ssam	}
551178676Ssam
552198429Srpaulo	/* Clear pending interrupts. */
553198429Srpaulo	IWN_WRITE(sc, IWN_INT, 0xffffffff);
554198429Srpaulo
555201209Srpaulo	/* Count the number of available chains. */
556201209Srpaulo	sc->ntxchains =
557201209Srpaulo	    ((sc->txchainmask >> 2) & 1) +
558201209Srpaulo	    ((sc->txchainmask >> 1) & 1) +
559201209Srpaulo	    ((sc->txchainmask >> 0) & 1);
560201209Srpaulo	sc->nrxchains =
561201209Srpaulo	    ((sc->rxchainmask >> 2) & 1) +
562201209Srpaulo	    ((sc->rxchainmask >> 1) & 1) +
563201209Srpaulo	    ((sc->rxchainmask >> 0) & 1);
564220724Sbschmidt	if (bootverbose) {
565220724Sbschmidt		device_printf(dev, "MIMO %dT%dR, %.4s, address %6D\n",
566220724Sbschmidt		    sc->ntxchains, sc->nrxchains, sc->eeprom_domain,
567220724Sbschmidt		    macaddr, ":");
568220724Sbschmidt	}
569198429Srpaulo
570178676Ssam	ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211);
571178676Ssam	if (ifp == NULL) {
572178676Ssam		device_printf(dev, "can not allocate ifnet structure\n");
573178676Ssam		goto fail;
574178676Ssam	}
575220726Sbschmidt
576178676Ssam	ic = ifp->if_l2com;
577198429Srpaulo	ic->ic_ifp = ifp;
578178676Ssam	ic->ic_phytype = IEEE80211_T_OFDM;	/* not only, but not used */
579178676Ssam	ic->ic_opmode = IEEE80211_M_STA;	/* default to BSS mode */
580178676Ssam
581198429Srpaulo	/* Set device capabilities. */
582178676Ssam	ic->ic_caps =
583178957Ssam		  IEEE80211_C_STA		/* station mode supported */
584178957Ssam		| IEEE80211_C_MONITOR		/* monitor mode supported */
585178676Ssam		| IEEE80211_C_TXPMGT		/* tx power management */
586178676Ssam		| IEEE80211_C_SHSLOT		/* short slot time supported */
587178676Ssam		| IEEE80211_C_WPA
588178676Ssam		| IEEE80211_C_SHPREAMBLE	/* short preamble supported */
589201209Srpaulo		| IEEE80211_C_BGSCAN		/* background scanning */
590178676Ssam#if 0
591178676Ssam		| IEEE80211_C_IBSS		/* ibss/adhoc mode */
592178676Ssam#endif
593178676Ssam		| IEEE80211_C_WME		/* WME */
594178676Ssam		;
595201209Srpaulo#if 0	/* HT */
596178678Ssam	/* XXX disable until HT channel setup works */
597178676Ssam	ic->ic_htcaps =
598178676Ssam		  IEEE80211_HTCAP_SMPS_ENA	/* SM PS mode enabled */
599178676Ssam		| IEEE80211_HTCAP_CHWIDTH40	/* 40MHz channel width */
600178676Ssam		| IEEE80211_HTCAP_SHORTGI20	/* short GI in 20MHz */
601178676Ssam		| IEEE80211_HTCAP_SHORTGI40	/* short GI in 40MHz */
602178676Ssam		| IEEE80211_HTCAP_RXSTBC_2STREAM/* 1-2 spatial streams */
603178676Ssam		| IEEE80211_HTCAP_MAXAMSDU_3839	/* max A-MSDU length */
604178676Ssam		/* s/w capabilities */
605178676Ssam		| IEEE80211_HTC_HT		/* HT operation */
606178676Ssam		| IEEE80211_HTC_AMPDU		/* tx A-MPDU */
607178676Ssam		| IEEE80211_HTC_AMSDU		/* tx A-MSDU */
608178676Ssam		;
609201209Srpaulo
610201209Srpaulo	/* Set HT capabilities. */
611201209Srpaulo	ic->ic_htcaps =
612201209Srpaulo#if IWN_RBUF_SIZE == 8192
613201209Srpaulo	    IEEE80211_HTCAP_AMSDU7935 |
614178678Ssam#endif
615201209Srpaulo	    IEEE80211_HTCAP_CBW20_40 |
616201209Srpaulo	    IEEE80211_HTCAP_SGI20 |
617201209Srpaulo	    IEEE80211_HTCAP_SGI40;
618201209Srpaulo	if (sc->hw_type != IWN_HW_REV_TYPE_4965)
619201209Srpaulo		ic->ic_htcaps |= IEEE80211_HTCAP_GF;
620206444Sbschmidt	if (sc->hw_type == IWN_HW_REV_TYPE_6050)
621206444Sbschmidt		ic->ic_htcaps |= IEEE80211_HTCAP_SMPS_DYN;
622206444Sbschmidt	else
623206444Sbschmidt		ic->ic_htcaps |= IEEE80211_HTCAP_SMPS_DIS;
624201209Srpaulo#endif
625178676Ssam
626198429Srpaulo	/* Read MAC address, channels, etc from EEPROM. */
627220726Sbschmidt	if ((error = iwn_read_eeprom(sc, macaddr)) != 0) {
628198429Srpaulo		device_printf(dev, "could not read EEPROM, error %d\n",
629198429Srpaulo		    error);
630198429Srpaulo		goto fail;
631198429Srpaulo	}
632198429Srpaulo
633201209Srpaulo#if 0	/* HT */
634201209Srpaulo	/* Set supported HT rates. */
635201209Srpaulo	ic->ic_sup_mcs[0] = 0xff;
636201209Srpaulo	if (sc->nrxchains > 1)
637201209Srpaulo		ic->ic_sup_mcs[1] = 0xff;
638201209Srpaulo	if (sc->nrxchains > 2)
639201209Srpaulo		ic->ic_sup_mcs[2] = 0xff;
640201209Srpaulo#endif
641201209Srpaulo
642178676Ssam	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
643178676Ssam	ifp->if_softc = sc;
644178676Ssam	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
645178676Ssam	ifp->if_init = iwn_init;
646178676Ssam	ifp->if_ioctl = iwn_ioctl;
647178676Ssam	ifp->if_start = iwn_start;
648207554Ssobomax	IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen);
649207554Ssobomax	ifp->if_snd.ifq_drv_maxlen = ifqmaxlen;
650178676Ssam	IFQ_SET_READY(&ifp->if_snd);
651178676Ssam
652190526Ssam	ieee80211_ifattach(ic, macaddr);
653178676Ssam	ic->ic_vap_create = iwn_vap_create;
654178676Ssam	ic->ic_vap_delete = iwn_vap_delete;
655178676Ssam	ic->ic_raw_xmit = iwn_raw_xmit;
656178676Ssam	ic->ic_node_alloc = iwn_node_alloc;
657220723Sbschmidt#if 0	/* HT */
658220723Sbschmidt	ic->ic_ampdu_rx_start = iwn_ampdu_rx_start;
659220723Sbschmidt	ic->ic_ampdu_rx_stop = iwn_ampdu_rx_stop;
660220723Sbschmidt	ic->ic_ampdu_tx_start = iwn_ampdu_tx_start;
661220723Sbschmidt	ic->ic_ampdu_tx_stop = iwn_ampdu_tx_stop;
662220723Sbschmidt#endif
663220715Sbschmidt	ic->ic_newassoc = iwn_newassoc;
664220721Sbschmidt	ic->ic_wme.wme_update = iwn_updateedca;
665201209Srpaulo	ic->ic_update_mcast = iwn_update_mcast;
666198429Srpaulo	ic->ic_scan_start = iwn_scan_start;
667198429Srpaulo	ic->ic_scan_end = iwn_scan_end;
668198429Srpaulo	ic->ic_set_channel = iwn_set_channel;
669198429Srpaulo	ic->ic_scan_curchan = iwn_scan_curchan;
670198429Srpaulo	ic->ic_scan_mindwell = iwn_scan_mindwell;
671201209Srpaulo	ic->ic_setregdomain = iwn_setregdomain;
672178676Ssam
673198429Srpaulo	iwn_radiotap_attach(sc);
674220667Sbschmidt
675220667Sbschmidt	callout_init_mtx(&sc->calib_to, &sc->sc_mtx, 0);
676220667Sbschmidt	callout_init_mtx(&sc->watchdog_to, &sc->sc_mtx, 0);
677220726Sbschmidt	TASK_INIT(&sc->sc_reinit_task, 0, iwn_hw_reset, sc);
678220726Sbschmidt	TASK_INIT(&sc->sc_radioon_task, 0, iwn_radio_on, sc);
679220726Sbschmidt	TASK_INIT(&sc->sc_radiooff_task, 0, iwn_radio_off, sc);
680220667Sbschmidt
681178676Ssam	iwn_sysctlattach(sc);
682178676Ssam
683198429Srpaulo	/*
684198429Srpaulo	 * Hook our interrupt after all initialization is complete.
685198429Srpaulo	 */
686198429Srpaulo	error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET | INTR_MPSAFE,
687178676Ssam	    NULL, iwn_intr, sc, &sc->sc_ih);
688198429Srpaulo	if (error != 0) {
689220724Sbschmidt		device_printf(dev, "can't establish interrupt, error %d\n",
690198429Srpaulo		    error);
691198429Srpaulo		goto fail;
692198429Srpaulo	}
693178676Ssam
694220724Sbschmidt	if (bootverbose)
695220724Sbschmidt		ieee80211_announce(ic);
696178676Ssam	return 0;
697178676Ssamfail:
698220635Sbschmidt	iwn_detach(dev);
699178676Ssam	return error;
700178676Ssam}
701178676Ssam
702220728Sbschmidtstatic int
703220728Sbschmidtiwn4965_attach(struct iwn_softc *sc, uint16_t pid)
704178676Ssam{
705220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
706198429Srpaulo
707220728Sbschmidt	ops->load_firmware = iwn4965_load_firmware;
708220728Sbschmidt	ops->read_eeprom = iwn4965_read_eeprom;
709220728Sbschmidt	ops->post_alive = iwn4965_post_alive;
710220728Sbschmidt	ops->nic_config = iwn4965_nic_config;
711220728Sbschmidt	ops->update_sched = iwn4965_update_sched;
712220728Sbschmidt	ops->get_temperature = iwn4965_get_temperature;
713220728Sbschmidt	ops->get_rssi = iwn4965_get_rssi;
714220728Sbschmidt	ops->set_txpower = iwn4965_set_txpower;
715220728Sbschmidt	ops->init_gains = iwn4965_init_gains;
716220728Sbschmidt	ops->set_gains = iwn4965_set_gains;
717220728Sbschmidt	ops->add_node = iwn4965_add_node;
718220728Sbschmidt	ops->tx_done = iwn4965_tx_done;
719220728Sbschmidt#if 0	/* HT */
720220728Sbschmidt	ops->ampdu_tx_start = iwn4965_ampdu_tx_start;
721220728Sbschmidt	ops->ampdu_tx_stop = iwn4965_ampdu_tx_stop;
722220728Sbschmidt#endif
723220728Sbschmidt	sc->ntxqs = IWN4965_NTXQUEUES;
724220728Sbschmidt	sc->ndmachnls = IWN4965_NDMACHNLS;
725220728Sbschmidt	sc->broadcast_id = IWN4965_ID_BROADCAST;
726220728Sbschmidt	sc->rxonsz = IWN4965_RXONSZ;
727220728Sbschmidt	sc->schedsz = IWN4965_SCHEDSZ;
728220728Sbschmidt	sc->fw_text_maxsz = IWN4965_FW_TEXT_MAXSZ;
729220728Sbschmidt	sc->fw_data_maxsz = IWN4965_FW_DATA_MAXSZ;
730220728Sbschmidt	sc->fwsz = IWN4965_FWSZ;
731220728Sbschmidt	sc->sched_txfact_addr = IWN4965_SCHED_TXFACT;
732220728Sbschmidt	sc->limits = &iwn4965_sensitivity_limits;
733220728Sbschmidt	sc->fwname = "iwn4965fw";
734220728Sbschmidt	/* Override chains masks, ROM is known to be broken. */
735220728Sbschmidt	sc->txchainmask = IWN_ANT_AB;
736220728Sbschmidt	sc->rxchainmask = IWN_ANT_ABC;
737220728Sbschmidt
738220728Sbschmidt	return 0;
739220728Sbschmidt}
740220728Sbschmidt
741220728Sbschmidtstatic int
742220728Sbschmidtiwn5000_attach(struct iwn_softc *sc, uint16_t pid)
743220728Sbschmidt{
744220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
745220728Sbschmidt
746220728Sbschmidt	ops->load_firmware = iwn5000_load_firmware;
747220728Sbschmidt	ops->read_eeprom = iwn5000_read_eeprom;
748220728Sbschmidt	ops->post_alive = iwn5000_post_alive;
749220728Sbschmidt	ops->nic_config = iwn5000_nic_config;
750220728Sbschmidt	ops->update_sched = iwn5000_update_sched;
751220728Sbschmidt	ops->get_temperature = iwn5000_get_temperature;
752220728Sbschmidt	ops->get_rssi = iwn5000_get_rssi;
753220728Sbschmidt	ops->set_txpower = iwn5000_set_txpower;
754220728Sbschmidt	ops->init_gains = iwn5000_init_gains;
755220728Sbschmidt	ops->set_gains = iwn5000_set_gains;
756220728Sbschmidt	ops->add_node = iwn5000_add_node;
757220728Sbschmidt	ops->tx_done = iwn5000_tx_done;
758220728Sbschmidt#if 0	/* HT */
759220728Sbschmidt	ops->ampdu_tx_start = iwn5000_ampdu_tx_start;
760220728Sbschmidt	ops->ampdu_tx_stop = iwn5000_ampdu_tx_stop;
761220728Sbschmidt#endif
762220728Sbschmidt	sc->ntxqs = IWN5000_NTXQUEUES;
763220728Sbschmidt	sc->ndmachnls = IWN5000_NDMACHNLS;
764220728Sbschmidt	sc->broadcast_id = IWN5000_ID_BROADCAST;
765220728Sbschmidt	sc->rxonsz = IWN5000_RXONSZ;
766220728Sbschmidt	sc->schedsz = IWN5000_SCHEDSZ;
767220728Sbschmidt	sc->fw_text_maxsz = IWN5000_FW_TEXT_MAXSZ;
768220728Sbschmidt	sc->fw_data_maxsz = IWN5000_FW_DATA_MAXSZ;
769220728Sbschmidt	sc->fwsz = IWN5000_FWSZ;
770220728Sbschmidt	sc->sched_txfact_addr = IWN5000_SCHED_TXFACT;
771220866Sbschmidt	sc->reset_noise_gain = IWN5000_PHY_CALIB_RESET_NOISE_GAIN;
772220866Sbschmidt	sc->noise_gain = IWN5000_PHY_CALIB_NOISE_GAIN;
773220728Sbschmidt
774198429Srpaulo	switch (sc->hw_type) {
775198429Srpaulo	case IWN_HW_REV_TYPE_5100:
776201209Srpaulo		sc->limits = &iwn5000_sensitivity_limits;
777198439Srpaulo		sc->fwname = "iwn5000fw";
778220727Sbschmidt		/* Override chains masks, ROM is known to be broken. */
779201209Srpaulo		sc->txchainmask = IWN_ANT_B;
780201209Srpaulo		sc->rxchainmask = IWN_ANT_AB;
781198429Srpaulo		break;
782198429Srpaulo	case IWN_HW_REV_TYPE_5150:
783201209Srpaulo		sc->limits = &iwn5150_sensitivity_limits;
784198439Srpaulo		sc->fwname = "iwn5150fw";
785198429Srpaulo		break;
786198429Srpaulo	case IWN_HW_REV_TYPE_5300:
787198429Srpaulo	case IWN_HW_REV_TYPE_5350:
788201209Srpaulo		sc->limits = &iwn5000_sensitivity_limits;
789198439Srpaulo		sc->fwname = "iwn5000fw";
790198429Srpaulo		break;
791198429Srpaulo	case IWN_HW_REV_TYPE_1000:
792206444Sbschmidt		sc->limits = &iwn1000_sensitivity_limits;
793198439Srpaulo		sc->fwname = "iwn1000fw";
794198429Srpaulo		break;
795198429Srpaulo	case IWN_HW_REV_TYPE_6000:
796201209Srpaulo		sc->limits = &iwn6000_sensitivity_limits;
797198439Srpaulo		sc->fwname = "iwn6000fw";
798220728Sbschmidt		if (pid == 0x422c || pid == 0x4239) {
799201209Srpaulo			sc->sc_flags |= IWN_FLAG_INTERNAL_PA;
800220727Sbschmidt			/* Override chains masks, ROM is known to be broken. */
801201209Srpaulo			sc->txchainmask = IWN_ANT_BC;
802201209Srpaulo			sc->rxchainmask = IWN_ANT_BC;
803201209Srpaulo		}
804198429Srpaulo		break;
805198429Srpaulo	case IWN_HW_REV_TYPE_6050:
806201209Srpaulo		sc->limits = &iwn6000_sensitivity_limits;
807210109Sbschmidt		sc->fwname = "iwn6050fw";
808220867Sbschmidt		/* Override chains masks, ROM is known to be broken. */
809220867Sbschmidt		sc->txchainmask = IWN_ANT_AB;
810220867Sbschmidt		sc->rxchainmask = IWN_ANT_AB;
811198429Srpaulo		break;
812210109Sbschmidt	case IWN_HW_REV_TYPE_6005:
813210109Sbschmidt		sc->limits = &iwn6000_sensitivity_limits;
814220894Sbschmidt		if (pid != 0x0082 && pid != 0x0085) {
815220894Sbschmidt			sc->fwname = "iwn6000g2bfw";
816220891Sbschmidt			sc->sc_flags |= IWN_FLAG_ADV_BTCOEX;
817220894Sbschmidt		} else
818220894Sbschmidt			sc->fwname = "iwn6000g2afw";
819210109Sbschmidt		break;
820198429Srpaulo	default:
821198429Srpaulo		device_printf(sc->sc_dev, "adapter type %d not supported\n",
822198429Srpaulo		    sc->hw_type);
823220728Sbschmidt		return ENOTSUP;
824198429Srpaulo	}
825220728Sbschmidt	return 0;
826178676Ssam}
827178676Ssam
828178676Ssam/*
829198429Srpaulo * Attach the interface to 802.11 radiotap.
830178676Ssam */
831206477Sbschmidtstatic void
832198429Srpauloiwn_radiotap_attach(struct iwn_softc *sc)
833178676Ssam{
834178676Ssam	struct ifnet *ifp = sc->sc_ifp;
835178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
836178676Ssam
837198429Srpaulo	ieee80211_radiotap_attach(ic,
838198429Srpaulo	    &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap),
839198429Srpaulo		IWN_TX_RADIOTAP_PRESENT,
840198429Srpaulo	    &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap),
841198429Srpaulo		IWN_RX_RADIOTAP_PRESENT);
842178676Ssam}
843178676Ssam
844220723Sbschmidtstatic void
845220723Sbschmidtiwn_sysctlattach(struct iwn_softc *sc)
846220723Sbschmidt{
847220723Sbschmidt	struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev);
848220723Sbschmidt	struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev);
849220723Sbschmidt
850220723Sbschmidt#ifdef IWN_DEBUG
851220723Sbschmidt	sc->sc_debug = 0;
852220723Sbschmidt	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
853220723Sbschmidt	    "debug", CTLFLAG_RW, &sc->sc_debug, 0, "control debugging printfs");
854220723Sbschmidt#endif
855220723Sbschmidt}
856220723Sbschmidt
857178676Ssamstatic struct ieee80211vap *
858178676Ssamiwn_vap_create(struct ieee80211com *ic,
859220726Sbschmidt    const char name[IFNAMSIZ], int unit, int opmode, int flags,
860220726Sbschmidt    const uint8_t bssid[IEEE80211_ADDR_LEN],
861220726Sbschmidt    const uint8_t mac[IEEE80211_ADDR_LEN])
862178676Ssam{
863178676Ssam	struct iwn_vap *ivp;
864178676Ssam	struct ieee80211vap *vap;
865178676Ssam
866178676Ssam	if (!TAILQ_EMPTY(&ic->ic_vaps))		/* only one at a time */
867178676Ssam		return NULL;
868178676Ssam	ivp = (struct iwn_vap *) malloc(sizeof(struct iwn_vap),
869178676Ssam	    M_80211_VAP, M_NOWAIT | M_ZERO);
870178676Ssam	if (ivp == NULL)
871178676Ssam		return NULL;
872178676Ssam	vap = &ivp->iv_vap;
873178676Ssam	ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac);
874178676Ssam	vap->iv_bmissthreshold = 10;		/* override default */
875198429Srpaulo	/* Override with driver methods. */
876178676Ssam	ivp->iv_newstate = vap->iv_newstate;
877178676Ssam	vap->iv_newstate = iwn_newstate;
878178676Ssam
879206358Srpaulo	ieee80211_ratectl_init(vap);
880198429Srpaulo	/* Complete setup. */
881206476Sbschmidt	ieee80211_vap_attach(vap, iwn_media_change, ieee80211_media_status);
882178676Ssam	ic->ic_opmode = opmode;
883178676Ssam	return vap;
884178676Ssam}
885178676Ssam
886178676Ssamstatic void
887178676Ssamiwn_vap_delete(struct ieee80211vap *vap)
888178676Ssam{
889178676Ssam	struct iwn_vap *ivp = IWN_VAP(vap);
890178676Ssam
891206358Srpaulo	ieee80211_ratectl_deinit(vap);
892178676Ssam	ieee80211_vap_detach(vap);
893178676Ssam	free(ivp, M_80211_VAP);
894178676Ssam}
895178676Ssam
896206477Sbschmidtstatic int
897220635Sbschmidtiwn_detach(device_t dev)
898178676Ssam{
899178676Ssam	struct iwn_softc *sc = device_get_softc(dev);
900198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
901198429Srpaulo	struct ieee80211com *ic;
902220721Sbschmidt	int qid;
903178676Ssam
904198429Srpaulo	if (ifp != NULL) {
905198429Srpaulo		ic = ifp->if_l2com;
906198429Srpaulo
907198429Srpaulo		ieee80211_draintask(ic, &sc->sc_reinit_task);
908198429Srpaulo		ieee80211_draintask(ic, &sc->sc_radioon_task);
909198429Srpaulo		ieee80211_draintask(ic, &sc->sc_radiooff_task);
910198429Srpaulo
911198429Srpaulo		iwn_stop(sc);
912220667Sbschmidt		callout_drain(&sc->watchdog_to);
913220667Sbschmidt		callout_drain(&sc->calib_to);
914198429Srpaulo		ieee80211_ifdetach(ic);
915198429Srpaulo	}
916198429Srpaulo
917220725Sbschmidt	/* Uninstall interrupt handler. */
918220723Sbschmidt	if (sc->irq != NULL) {
919220723Sbschmidt		bus_teardown_intr(dev, sc->irq, sc->sc_ih);
920220723Sbschmidt		bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq);
921220723Sbschmidt		if (sc->irq_rid == 1)
922220723Sbschmidt			pci_release_msi(dev);
923220723Sbschmidt	}
924220723Sbschmidt
925201209Srpaulo	/* Free DMA resources. */
926198429Srpaulo	iwn_free_rx_ring(sc, &sc->rxq);
927220728Sbschmidt	for (qid = 0; qid < sc->ntxqs; qid++)
928220728Sbschmidt		iwn_free_tx_ring(sc, &sc->txq[qid]);
929198429Srpaulo	iwn_free_sched(sc);
930198429Srpaulo	iwn_free_kw(sc);
931201209Srpaulo	if (sc->ict != NULL)
932201209Srpaulo		iwn_free_ict(sc);
933198429Srpaulo	iwn_free_fwmem(sc);
934198429Srpaulo
935198429Srpaulo	if (sc->mem != NULL)
936198429Srpaulo		bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem);
937198429Srpaulo
938198429Srpaulo	if (ifp != NULL)
939198429Srpaulo		if_free(ifp);
940198429Srpaulo
941198429Srpaulo	IWN_LOCK_DESTROY(sc);
942178676Ssam	return 0;
943178676Ssam}
944178676Ssam
945178676Ssamstatic int
946220723Sbschmidtiwn_shutdown(device_t dev)
947220723Sbschmidt{
948220723Sbschmidt	struct iwn_softc *sc = device_get_softc(dev);
949220723Sbschmidt
950220723Sbschmidt	iwn_stop(sc);
951220723Sbschmidt	return 0;
952220723Sbschmidt}
953220723Sbschmidt
954220723Sbschmidtstatic int
955220723Sbschmidtiwn_suspend(device_t dev)
956220723Sbschmidt{
957220723Sbschmidt	struct iwn_softc *sc = device_get_softc(dev);
958220723Sbschmidt	struct ifnet *ifp = sc->sc_ifp;
959220723Sbschmidt	struct ieee80211com *ic = ifp->if_l2com;
960220723Sbschmidt	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
961220723Sbschmidt
962220723Sbschmidt	iwn_stop(sc);
963220723Sbschmidt	if (vap != NULL)
964220723Sbschmidt		ieee80211_stop(vap);
965220723Sbschmidt	return 0;
966220723Sbschmidt}
967220723Sbschmidt
968220723Sbschmidtstatic int
969220723Sbschmidtiwn_resume(device_t dev)
970220723Sbschmidt{
971220723Sbschmidt	struct iwn_softc *sc = device_get_softc(dev);
972220723Sbschmidt	struct ifnet *ifp = sc->sc_ifp;
973220723Sbschmidt	struct ieee80211com *ic = ifp->if_l2com;
974220723Sbschmidt	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
975220723Sbschmidt
976220723Sbschmidt	/* Clear device-specific "PCI retry timeout" register (41h). */
977220723Sbschmidt	pci_write_config(dev, 0x41, 0, 1);
978220723Sbschmidt
979220723Sbschmidt	if (ifp->if_flags & IFF_UP) {
980220723Sbschmidt		iwn_init(sc);
981220723Sbschmidt		if (vap != NULL)
982220723Sbschmidt			ieee80211_init(vap);
983220723Sbschmidt		if (ifp->if_drv_flags & IFF_DRV_RUNNING)
984220723Sbschmidt			iwn_start(ifp);
985220723Sbschmidt	}
986220723Sbschmidt	return 0;
987220723Sbschmidt}
988220723Sbschmidt
989220723Sbschmidtstatic int
990198429Srpauloiwn_nic_lock(struct iwn_softc *sc)
991178676Ssam{
992198429Srpaulo	int ntries;
993178676Ssam
994198429Srpaulo	/* Request exclusive access to NIC. */
995198429Srpaulo	IWN_SETBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_MAC_ACCESS_REQ);
996178676Ssam
997198429Srpaulo	/* Spin until we actually get the lock. */
998198429Srpaulo	for (ntries = 0; ntries < 1000; ntries++) {
999198429Srpaulo		if ((IWN_READ(sc, IWN_GP_CNTRL) &
1000220726Sbschmidt		     (IWN_GP_CNTRL_MAC_ACCESS_ENA | IWN_GP_CNTRL_SLEEP)) ==
1001198429Srpaulo		    IWN_GP_CNTRL_MAC_ACCESS_ENA)
1002198429Srpaulo			return 0;
1003198429Srpaulo		DELAY(10);
1004198429Srpaulo	}
1005198429Srpaulo	return ETIMEDOUT;
1006198429Srpaulo}
1007198429Srpaulo
1008198429Srpaulostatic __inline void
1009198429Srpauloiwn_nic_unlock(struct iwn_softc *sc)
1010198429Srpaulo{
1011198429Srpaulo	IWN_CLRBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_MAC_ACCESS_REQ);
1012198429Srpaulo}
1013198429Srpaulo
1014198429Srpaulostatic __inline uint32_t
1015198429Srpauloiwn_prph_read(struct iwn_softc *sc, uint32_t addr)
1016198429Srpaulo{
1017198429Srpaulo	IWN_WRITE(sc, IWN_PRPH_RADDR, IWN_PRPH_DWORD | addr);
1018201209Srpaulo	IWN_BARRIER_READ_WRITE(sc);
1019198429Srpaulo	return IWN_READ(sc, IWN_PRPH_RDATA);
1020198429Srpaulo}
1021198429Srpaulo
1022198429Srpaulostatic __inline void
1023198429Srpauloiwn_prph_write(struct iwn_softc *sc, uint32_t addr, uint32_t data)
1024198429Srpaulo{
1025198429Srpaulo	IWN_WRITE(sc, IWN_PRPH_WADDR, IWN_PRPH_DWORD | addr);
1026201209Srpaulo	IWN_BARRIER_WRITE(sc);
1027198429Srpaulo	IWN_WRITE(sc, IWN_PRPH_WDATA, data);
1028198429Srpaulo}
1029198429Srpaulo
1030198429Srpaulostatic __inline void
1031198429Srpauloiwn_prph_setbits(struct iwn_softc *sc, uint32_t addr, uint32_t mask)
1032198429Srpaulo{
1033198429Srpaulo	iwn_prph_write(sc, addr, iwn_prph_read(sc, addr) | mask);
1034198429Srpaulo}
1035198429Srpaulo
1036198429Srpaulostatic __inline void
1037198429Srpauloiwn_prph_clrbits(struct iwn_softc *sc, uint32_t addr, uint32_t mask)
1038198429Srpaulo{
1039198429Srpaulo	iwn_prph_write(sc, addr, iwn_prph_read(sc, addr) & ~mask);
1040198429Srpaulo}
1041198429Srpaulo
1042198429Srpaulostatic __inline void
1043198429Srpauloiwn_prph_write_region_4(struct iwn_softc *sc, uint32_t addr,
1044198429Srpaulo    const uint32_t *data, int count)
1045198429Srpaulo{
1046198429Srpaulo	for (; count > 0; count--, data++, addr += 4)
1047198429Srpaulo		iwn_prph_write(sc, addr, *data);
1048198429Srpaulo}
1049198429Srpaulo
1050198429Srpaulostatic __inline uint32_t
1051198429Srpauloiwn_mem_read(struct iwn_softc *sc, uint32_t addr)
1052198429Srpaulo{
1053198429Srpaulo	IWN_WRITE(sc, IWN_MEM_RADDR, addr);
1054201209Srpaulo	IWN_BARRIER_READ_WRITE(sc);
1055198429Srpaulo	return IWN_READ(sc, IWN_MEM_RDATA);
1056198429Srpaulo}
1057198429Srpaulo
1058198429Srpaulostatic __inline void
1059198429Srpauloiwn_mem_write(struct iwn_softc *sc, uint32_t addr, uint32_t data)
1060198429Srpaulo{
1061198429Srpaulo	IWN_WRITE(sc, IWN_MEM_WADDR, addr);
1062201209Srpaulo	IWN_BARRIER_WRITE(sc);
1063198429Srpaulo	IWN_WRITE(sc, IWN_MEM_WDATA, data);
1064198429Srpaulo}
1065198429Srpaulo
1066198429Srpaulostatic __inline void
1067198429Srpauloiwn_mem_write_2(struct iwn_softc *sc, uint32_t addr, uint16_t data)
1068198429Srpaulo{
1069198429Srpaulo	uint32_t tmp;
1070198429Srpaulo
1071198429Srpaulo	tmp = iwn_mem_read(sc, addr & ~3);
1072198429Srpaulo	if (addr & 3)
1073198429Srpaulo		tmp = (tmp & 0x0000ffff) | data << 16;
1074198429Srpaulo	else
1075198429Srpaulo		tmp = (tmp & 0xffff0000) | data;
1076198429Srpaulo	iwn_mem_write(sc, addr & ~3, tmp);
1077198429Srpaulo}
1078198429Srpaulo
1079198429Srpaulostatic __inline void
1080198429Srpauloiwn_mem_read_region_4(struct iwn_softc *sc, uint32_t addr, uint32_t *data,
1081198429Srpaulo    int count)
1082198429Srpaulo{
1083198429Srpaulo	for (; count > 0; count--, addr += 4)
1084198429Srpaulo		*data++ = iwn_mem_read(sc, addr);
1085198429Srpaulo}
1086198429Srpaulo
1087198429Srpaulostatic __inline void
1088198429Srpauloiwn_mem_set_region_4(struct iwn_softc *sc, uint32_t addr, uint32_t val,
1089198429Srpaulo    int count)
1090198429Srpaulo{
1091198429Srpaulo	for (; count > 0; count--, addr += 4)
1092198429Srpaulo		iwn_mem_write(sc, addr, val);
1093198429Srpaulo}
1094198429Srpaulo
1095206477Sbschmidtstatic int
1096198429Srpauloiwn_eeprom_lock(struct iwn_softc *sc)
1097198429Srpaulo{
1098198429Srpaulo	int i, ntries;
1099198429Srpaulo
1100198429Srpaulo	for (i = 0; i < 100; i++) {
1101198429Srpaulo		/* Request exclusive access to EEPROM. */
1102198429Srpaulo		IWN_SETBITS(sc, IWN_HW_IF_CONFIG,
1103198429Srpaulo		    IWN_HW_IF_CONFIG_EEPROM_LOCKED);
1104198429Srpaulo
1105198429Srpaulo		/* Spin until we actually get the lock. */
1106198429Srpaulo		for (ntries = 0; ntries < 100; ntries++) {
1107198429Srpaulo			if (IWN_READ(sc, IWN_HW_IF_CONFIG) &
1108198429Srpaulo			    IWN_HW_IF_CONFIG_EEPROM_LOCKED)
1109198429Srpaulo				return 0;
1110198429Srpaulo			DELAY(10);
1111198429Srpaulo		}
1112198429Srpaulo	}
1113198429Srpaulo	return ETIMEDOUT;
1114198429Srpaulo}
1115198429Srpaulo
1116198429Srpaulostatic __inline void
1117198429Srpauloiwn_eeprom_unlock(struct iwn_softc *sc)
1118198429Srpaulo{
1119198429Srpaulo	IWN_CLRBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_EEPROM_LOCKED);
1120198429Srpaulo}
1121198429Srpaulo
1122198429Srpaulo/*
1123198429Srpaulo * Initialize access by host to One Time Programmable ROM.
1124198429Srpaulo * NB: This kind of ROM can be found on 1000 or 6000 Series only.
1125198429Srpaulo */
1126206477Sbschmidtstatic int
1127198429Srpauloiwn_init_otprom(struct iwn_softc *sc)
1128198429Srpaulo{
1129203934Sbschmidt	uint16_t prev, base, next;
1130201209Srpaulo	int count, error;
1131198429Srpaulo
1132201209Srpaulo	/* Wait for clock stabilization before accessing prph. */
1133220726Sbschmidt	if ((error = iwn_clock_wait(sc)) != 0)
1134198429Srpaulo		return error;
1135198429Srpaulo
1136220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
1137198429Srpaulo		return error;
1138198429Srpaulo	iwn_prph_setbits(sc, IWN_APMG_PS, IWN_APMG_PS_RESET_REQ);
1139198429Srpaulo	DELAY(5);
1140198429Srpaulo	iwn_prph_clrbits(sc, IWN_APMG_PS, IWN_APMG_PS_RESET_REQ);
1141198429Srpaulo	iwn_nic_unlock(sc);
1142198429Srpaulo
1143201209Srpaulo	/* Set auto clock gate disable bit for HW with OTP shadow RAM. */
1144201209Srpaulo	if (sc->hw_type != IWN_HW_REV_TYPE_1000) {
1145201209Srpaulo		IWN_SETBITS(sc, IWN_DBG_LINK_PWR_MGMT,
1146201209Srpaulo		    IWN_RESET_LINK_PWR_MGMT_DIS);
1147201209Srpaulo	}
1148198429Srpaulo	IWN_CLRBITS(sc, IWN_EEPROM_GP, IWN_EEPROM_GP_IF_OWNER);
1149198429Srpaulo	/* Clear ECC status. */
1150198429Srpaulo	IWN_SETBITS(sc, IWN_OTP_GP,
1151198429Srpaulo	    IWN_OTP_GP_ECC_CORR_STTS | IWN_OTP_GP_ECC_UNCORR_STTS);
1152198429Srpaulo
1153201209Srpaulo	/*
1154203934Sbschmidt	 * Find the block before last block (contains the EEPROM image)
1155203934Sbschmidt	 * for HW without OTP shadow RAM.
1156201209Srpaulo	 */
1157201209Srpaulo	if (sc->hw_type == IWN_HW_REV_TYPE_1000) {
1158201209Srpaulo		/* Switch to absolute addressing mode. */
1159201209Srpaulo		IWN_CLRBITS(sc, IWN_OTP_GP, IWN_OTP_GP_RELATIVE_ACCESS);
1160203934Sbschmidt		base = prev = 0;
1161201209Srpaulo		for (count = 0; count < IWN1000_OTP_NBLOCKS; count++) {
1162201209Srpaulo			error = iwn_read_prom_data(sc, base, &next, 2);
1163201209Srpaulo			if (error != 0)
1164201209Srpaulo				return error;
1165201209Srpaulo			if (next == 0)	/* End of linked-list. */
1166201209Srpaulo				break;
1167203934Sbschmidt			prev = base;
1168201209Srpaulo			base = le16toh(next);
1169201209Srpaulo		}
1170203934Sbschmidt		if (count == 0 || count == IWN1000_OTP_NBLOCKS)
1171201209Srpaulo			return EIO;
1172201209Srpaulo		/* Skip "next" word. */
1173203934Sbschmidt		sc->prom_base = prev + 1;
1174201209Srpaulo	}
1175178676Ssam	return 0;
1176178676Ssam}
1177178676Ssam
1178206477Sbschmidtstatic int
1179198429Srpauloiwn_read_prom_data(struct iwn_softc *sc, uint32_t addr, void *data, int count)
1180198429Srpaulo{
1181220723Sbschmidt	uint8_t *out = data;
1182198429Srpaulo	uint32_t val, tmp;
1183198429Srpaulo	int ntries;
1184198429Srpaulo
1185201209Srpaulo	addr += sc->prom_base;
1186198429Srpaulo	for (; count > 0; count -= 2, addr++) {
1187198429Srpaulo		IWN_WRITE(sc, IWN_EEPROM, addr << 2);
1188201209Srpaulo		for (ntries = 0; ntries < 10; ntries++) {
1189198429Srpaulo			val = IWN_READ(sc, IWN_EEPROM);
1190198429Srpaulo			if (val & IWN_EEPROM_READ_VALID)
1191198429Srpaulo				break;
1192198429Srpaulo			DELAY(5);
1193198429Srpaulo		}
1194201209Srpaulo		if (ntries == 10) {
1195198429Srpaulo			device_printf(sc->sc_dev,
1196198429Srpaulo			    "timeout reading ROM at 0x%x\n", addr);
1197198429Srpaulo			return ETIMEDOUT;
1198198429Srpaulo		}
1199198429Srpaulo		if (sc->sc_flags & IWN_FLAG_HAS_OTPROM) {
1200198429Srpaulo			/* OTPROM, check for ECC errors. */
1201198429Srpaulo			tmp = IWN_READ(sc, IWN_OTP_GP);
1202198429Srpaulo			if (tmp & IWN_OTP_GP_ECC_UNCORR_STTS) {
1203198429Srpaulo				device_printf(sc->sc_dev,
1204198429Srpaulo				    "OTPROM ECC error at 0x%x\n", addr);
1205198429Srpaulo				return EIO;
1206198429Srpaulo			}
1207198429Srpaulo			if (tmp & IWN_OTP_GP_ECC_CORR_STTS) {
1208198429Srpaulo				/* Correctable ECC error, clear bit. */
1209198429Srpaulo				IWN_SETBITS(sc, IWN_OTP_GP,
1210198429Srpaulo				    IWN_OTP_GP_ECC_CORR_STTS);
1211198429Srpaulo			}
1212198429Srpaulo		}
1213198429Srpaulo		*out++ = val >> 16;
1214198429Srpaulo		if (count > 1)
1215198429Srpaulo			*out++ = val >> 24;
1216198429Srpaulo	}
1217198429Srpaulo	return 0;
1218198429Srpaulo}
1219198429Srpaulo
1220178676Ssamstatic void
1221178676Ssamiwn_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
1222178676Ssam{
1223198429Srpaulo	if (error != 0)
1224198429Srpaulo		return;
1225198429Srpaulo	KASSERT(nsegs == 1, ("too many DMA segments, %d should be 1", nsegs));
1226198429Srpaulo	*(bus_addr_t *)arg = segs[0].ds_addr;
1227178676Ssam}
1228178676Ssam
1229198429Srpaulostatic int
1230178676Ssamiwn_dma_contig_alloc(struct iwn_softc *sc, struct iwn_dma_info *dma,
1231220691Sbschmidt    void **kvap, bus_size_t size, bus_size_t alignment)
1232178676Ssam{
1233198429Srpaulo	int error;
1234178676Ssam
1235220723Sbschmidt	dma->tag = NULL;
1236178676Ssam	dma->size = size;
1237178676Ssam
1238198429Srpaulo	error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), alignment,
1239178676Ssam	    0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, size,
1240220691Sbschmidt	    1, size, BUS_DMA_NOWAIT, NULL, NULL, &dma->tag);
1241220711Sbschmidt	if (error != 0)
1242178676Ssam		goto fail;
1243220711Sbschmidt
1244178676Ssam	error = bus_dmamem_alloc(dma->tag, (void **)&dma->vaddr,
1245220691Sbschmidt	    BUS_DMA_NOWAIT | BUS_DMA_ZERO | BUS_DMA_COHERENT, &dma->map);
1246220711Sbschmidt	if (error != 0)
1247178676Ssam		goto fail;
1248220711Sbschmidt
1249220691Sbschmidt	error = bus_dmamap_load(dma->tag, dma->map, dma->vaddr, size,
1250220691Sbschmidt	    iwn_dma_map_addr, &dma->paddr, BUS_DMA_NOWAIT);
1251220711Sbschmidt	if (error != 0)
1252178676Ssam		goto fail;
1253178676Ssam
1254220704Sbschmidt	bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
1255220704Sbschmidt
1256178676Ssam	if (kvap != NULL)
1257178676Ssam		*kvap = dma->vaddr;
1258220726Sbschmidt
1259178676Ssam	return 0;
1260220726Sbschmidt
1261220726Sbschmidtfail:	iwn_dma_contig_free(dma);
1262178676Ssam	return error;
1263178676Ssam}
1264178676Ssam
1265206477Sbschmidtstatic void
1266178676Ssamiwn_dma_contig_free(struct iwn_dma_info *dma)
1267178676Ssam{
1268220701Sbschmidt	if (dma->map != NULL) {
1269220701Sbschmidt		if (dma->vaddr != NULL) {
1270220701Sbschmidt			bus_dmamap_sync(dma->tag, dma->map,
1271220701Sbschmidt			    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
1272220701Sbschmidt			bus_dmamap_unload(dma->tag, dma->map);
1273178676Ssam			bus_dmamem_free(dma->tag, &dma->vaddr, dma->map);
1274220701Sbschmidt			dma->vaddr = NULL;
1275178676Ssam		}
1276220701Sbschmidt		bus_dmamap_destroy(dma->tag, dma->map);
1277220701Sbschmidt		dma->map = NULL;
1278220701Sbschmidt	}
1279220701Sbschmidt	if (dma->tag != NULL) {
1280178676Ssam		bus_dma_tag_destroy(dma->tag);
1281220701Sbschmidt		dma->tag = NULL;
1282178676Ssam	}
1283178676Ssam}
1284178676Ssam
1285206477Sbschmidtstatic int
1286198429Srpauloiwn_alloc_sched(struct iwn_softc *sc)
1287178676Ssam{
1288198429Srpaulo	/* TX scheduler rings must be aligned on a 1KB boundary. */
1289220691Sbschmidt	return iwn_dma_contig_alloc(sc, &sc->sched_dma, (void **)&sc->sched,
1290220728Sbschmidt	    sc->schedsz, 1024);
1291178676Ssam}
1292178676Ssam
1293206477Sbschmidtstatic void
1294198429Srpauloiwn_free_sched(struct iwn_softc *sc)
1295178676Ssam{
1296198429Srpaulo	iwn_dma_contig_free(&sc->sched_dma);
1297178676Ssam}
1298178676Ssam
1299206477Sbschmidtstatic int
1300178676Ssamiwn_alloc_kw(struct iwn_softc *sc)
1301178676Ssam{
1302198429Srpaulo	/* "Keep Warm" page must be aligned on a 4KB boundary. */
1303220691Sbschmidt	return iwn_dma_contig_alloc(sc, &sc->kw_dma, NULL, 4096, 4096);
1304178676Ssam}
1305178676Ssam
1306206477Sbschmidtstatic void
1307178676Ssamiwn_free_kw(struct iwn_softc *sc)
1308178676Ssam{
1309178676Ssam	iwn_dma_contig_free(&sc->kw_dma);
1310178676Ssam}
1311178676Ssam
1312206477Sbschmidtstatic int
1313201209Srpauloiwn_alloc_ict(struct iwn_softc *sc)
1314201209Srpaulo{
1315201209Srpaulo	/* ICT table must be aligned on a 4KB boundary. */
1316220691Sbschmidt	return iwn_dma_contig_alloc(sc, &sc->ict_dma, (void **)&sc->ict,
1317220691Sbschmidt	    IWN_ICT_SIZE, 4096);
1318201209Srpaulo}
1319201209Srpaulo
1320206477Sbschmidtstatic void
1321201209Srpauloiwn_free_ict(struct iwn_softc *sc)
1322201209Srpaulo{
1323201209Srpaulo	iwn_dma_contig_free(&sc->ict_dma);
1324201209Srpaulo}
1325201209Srpaulo
1326206477Sbschmidtstatic int
1327178676Ssamiwn_alloc_fwmem(struct iwn_softc *sc)
1328178676Ssam{
1329198429Srpaulo	/* Must be aligned on a 16-byte boundary. */
1330220728Sbschmidt	return iwn_dma_contig_alloc(sc, &sc->fw_dma, NULL, sc->fwsz, 16);
1331178676Ssam}
1332178676Ssam
1333206477Sbschmidtstatic void
1334178676Ssamiwn_free_fwmem(struct iwn_softc *sc)
1335178676Ssam{
1336178676Ssam	iwn_dma_contig_free(&sc->fw_dma);
1337178676Ssam}
1338178676Ssam
1339206477Sbschmidtstatic int
1340178676Ssamiwn_alloc_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring)
1341178676Ssam{
1342198429Srpaulo	bus_size_t size;
1343178676Ssam	int i, error;
1344178676Ssam
1345178676Ssam	ring->cur = 0;
1346178676Ssam
1347198429Srpaulo	/* Allocate RX descriptors (256-byte aligned). */
1348198429Srpaulo	size = IWN_RX_RING_COUNT * sizeof (uint32_t);
1349220691Sbschmidt	error = iwn_dma_contig_alloc(sc, &ring->desc_dma, (void **)&ring->desc,
1350220691Sbschmidt	    size, 256);
1351178676Ssam	if (error != 0) {
1352178676Ssam		device_printf(sc->sc_dev,
1353220711Sbschmidt		    "%s: could not allocate RX ring DMA memory, error %d\n",
1354178676Ssam		    __func__, error);
1355178676Ssam		goto fail;
1356178676Ssam	}
1357178676Ssam
1358220702Sbschmidt	/* Allocate RX status area (16-byte aligned). */
1359220702Sbschmidt	error = iwn_dma_contig_alloc(sc, &ring->stat_dma, (void **)&ring->stat,
1360220702Sbschmidt	    sizeof (struct iwn_rx_status), 16);
1361198429Srpaulo	if (error != 0) {
1362198429Srpaulo		device_printf(sc->sc_dev,
1363220711Sbschmidt		    "%s: could not allocate RX status DMA memory, error %d\n",
1364178676Ssam		    __func__, error);
1365198429Srpaulo		goto fail;
1366198429Srpaulo	}
1367178676Ssam
1368220702Sbschmidt	/* Create RX buffer DMA tag. */
1369220702Sbschmidt	error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0,
1370220702Sbschmidt	    BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
1371220702Sbschmidt	    IWN_RBUF_SIZE, 1, IWN_RBUF_SIZE, BUS_DMA_NOWAIT, NULL, NULL,
1372220702Sbschmidt	    &ring->data_dmat);
1373198429Srpaulo	if (error != 0) {
1374198429Srpaulo		device_printf(sc->sc_dev,
1375220711Sbschmidt		    "%s: could not create RX buf DMA tag, error %d\n",
1376198429Srpaulo		    __func__, error);
1377198429Srpaulo		goto fail;
1378198429Srpaulo	}
1379198429Srpaulo
1380178676Ssam	/*
1381198429Srpaulo	 * Allocate and map RX buffers.
1382178676Ssam	 */
1383178676Ssam	for (i = 0; i < IWN_RX_RING_COUNT; i++) {
1384178676Ssam		struct iwn_rx_data *data = &ring->data[i];
1385178676Ssam		bus_addr_t paddr;
1386178676Ssam
1387201209Srpaulo		error = bus_dmamap_create(ring->data_dmat, 0, &data->map);
1388178676Ssam		if (error != 0) {
1389178676Ssam			device_printf(sc->sc_dev,
1390220711Sbschmidt			    "%s: could not create RX buf DMA map, error %d\n",
1391178676Ssam			    __func__, error);
1392178676Ssam			goto fail;
1393178676Ssam		}
1394198429Srpaulo
1395220692Sbschmidt		data->m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR,
1396220692Sbschmidt		    IWN_RBUF_SIZE);
1397198439Srpaulo		if (data->m == NULL) {
1398178676Ssam			device_printf(sc->sc_dev,
1399220711Sbschmidt			    "%s: could not allocate RX mbuf\n", __func__);
1400220710Sbschmidt			error = ENOBUFS;
1401178676Ssam			goto fail;
1402178676Ssam		}
1403198429Srpaulo
1404201209Srpaulo		error = bus_dmamap_load(ring->data_dmat, data->map,
1405220692Sbschmidt		    mtod(data->m, void *), IWN_RBUF_SIZE, iwn_dma_map_addr,
1406220692Sbschmidt		    &paddr, BUS_DMA_NOWAIT);
1407178676Ssam		if (error != 0 && error != EFBIG) {
1408178676Ssam			device_printf(sc->sc_dev,
1409220711Sbschmidt			    "%s: can't not map mbuf, error %d\n", __func__,
1410220711Sbschmidt			    error);
1411178676Ssam			goto fail;
1412178676Ssam		}
1413178676Ssam
1414198429Srpaulo		/* Set physical address of RX buffer (256-byte aligned). */
1415178676Ssam		ring->desc[i] = htole32(paddr >> 8);
1416178676Ssam	}
1417220726Sbschmidt
1418178676Ssam	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
1419178676Ssam	    BUS_DMASYNC_PREWRITE);
1420220726Sbschmidt
1421178676Ssam	return 0;
1422220726Sbschmidt
1423220726Sbschmidtfail:	iwn_free_rx_ring(sc, ring);
1424178676Ssam	return error;
1425178676Ssam}
1426178676Ssam
1427206477Sbschmidtstatic void
1428178676Ssamiwn_reset_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring)
1429178676Ssam{
1430178676Ssam	int ntries;
1431178676Ssam
1432198429Srpaulo	if (iwn_nic_lock(sc) == 0) {
1433198429Srpaulo		IWN_WRITE(sc, IWN_FH_RX_CONFIG, 0);
1434198429Srpaulo		for (ntries = 0; ntries < 1000; ntries++) {
1435198429Srpaulo			if (IWN_READ(sc, IWN_FH_RX_STATUS) &
1436198429Srpaulo			    IWN_FH_RX_STATUS_IDLE)
1437198429Srpaulo				break;
1438198429Srpaulo			DELAY(10);
1439198429Srpaulo		}
1440198429Srpaulo		iwn_nic_unlock(sc);
1441198429Srpaulo	}
1442178676Ssam	ring->cur = 0;
1443198429Srpaulo	sc->last_rx_valid = 0;
1444178676Ssam}
1445178676Ssam
1446206477Sbschmidtstatic void
1447178676Ssamiwn_free_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring)
1448178676Ssam{
1449178676Ssam	int i;
1450178676Ssam
1451178676Ssam	iwn_dma_contig_free(&ring->desc_dma);
1452198429Srpaulo	iwn_dma_contig_free(&ring->stat_dma);
1453178676Ssam
1454198429Srpaulo	for (i = 0; i < IWN_RX_RING_COUNT; i++) {
1455198429Srpaulo		struct iwn_rx_data *data = &ring->data[i];
1456198429Srpaulo
1457198429Srpaulo		if (data->m != NULL) {
1458201209Srpaulo			bus_dmamap_sync(ring->data_dmat, data->map,
1459198439Srpaulo			    BUS_DMASYNC_POSTREAD);
1460201209Srpaulo			bus_dmamap_unload(ring->data_dmat, data->map);
1461198429Srpaulo			m_freem(data->m);
1462220710Sbschmidt			data->m = NULL;
1463198429Srpaulo		}
1464201209Srpaulo		if (data->map != NULL)
1465201209Srpaulo			bus_dmamap_destroy(ring->data_dmat, data->map);
1466198429Srpaulo	}
1467220701Sbschmidt	if (ring->data_dmat != NULL) {
1468220701Sbschmidt		bus_dma_tag_destroy(ring->data_dmat);
1469220701Sbschmidt		ring->data_dmat = NULL;
1470220701Sbschmidt	}
1471178676Ssam}
1472178676Ssam
1473206477Sbschmidtstatic int
1474178676Ssamiwn_alloc_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring, int qid)
1475178676Ssam{
1476220723Sbschmidt	bus_addr_t paddr;
1477178676Ssam	bus_size_t size;
1478178676Ssam	int i, error;
1479178676Ssam
1480178676Ssam	ring->qid = qid;
1481178676Ssam	ring->queued = 0;
1482178676Ssam	ring->cur = 0;
1483178676Ssam
1484220725Sbschmidt	/* Allocate TX descriptors (256-byte aligned). */
1485220726Sbschmidt	size = IWN_TX_RING_COUNT * sizeof (struct iwn_tx_desc);
1486220691Sbschmidt	error = iwn_dma_contig_alloc(sc, &ring->desc_dma, (void **)&ring->desc,
1487220691Sbschmidt	    size, 256);
1488178676Ssam	if (error != 0) {
1489178676Ssam		device_printf(sc->sc_dev,
1490198429Srpaulo		    "%s: could not allocate TX ring DMA memory, error %d\n",
1491178676Ssam		    __func__, error);
1492178676Ssam		goto fail;
1493178676Ssam	}
1494198429Srpaulo	/*
1495198429Srpaulo	 * We only use rings 0 through 4 (4 EDCA + cmd) so there is no need
1496198429Srpaulo	 * to allocate commands space for other rings.
1497220725Sbschmidt	 * XXX Do we really need to allocate descriptors for other rings?
1498198429Srpaulo	 */
1499198429Srpaulo	if (qid > 4)
1500198429Srpaulo		return 0;
1501198429Srpaulo
1502220726Sbschmidt	size = IWN_TX_RING_COUNT * sizeof (struct iwn_tx_cmd);
1503220691Sbschmidt	error = iwn_dma_contig_alloc(sc, &ring->cmd_dma, (void **)&ring->cmd,
1504220691Sbschmidt	    size, 4);
1505178676Ssam	if (error != 0) {
1506178676Ssam		device_printf(sc->sc_dev,
1507198429Srpaulo		    "%s: could not allocate TX cmd DMA memory, error %d\n",
1508178676Ssam		    __func__, error);
1509178676Ssam		goto fail;
1510178676Ssam	}
1511178676Ssam
1512198429Srpaulo	error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0,
1513220726Sbschmidt	    BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES,
1514220726Sbschmidt	    IWN_MAX_SCATTER - 1, MCLBYTES, BUS_DMA_NOWAIT, NULL, NULL,
1515220726Sbschmidt	    &ring->data_dmat);
1516198429Srpaulo	if (error != 0) {
1517198429Srpaulo		device_printf(sc->sc_dev,
1518220711Sbschmidt		    "%s: could not create TX buf DMA tag, error %d\n",
1519178676Ssam		    __func__, error);
1520198429Srpaulo		goto fail;
1521198429Srpaulo	}
1522178676Ssam
1523198429Srpaulo	paddr = ring->cmd_dma.paddr;
1524178676Ssam	for (i = 0; i < IWN_TX_RING_COUNT; i++) {
1525178676Ssam		struct iwn_tx_data *data = &ring->data[i];
1526178676Ssam
1527198429Srpaulo		data->cmd_paddr = paddr;
1528198429Srpaulo		data->scratch_paddr = paddr + 12;
1529198429Srpaulo		paddr += sizeof (struct iwn_tx_cmd);
1530198429Srpaulo
1531201209Srpaulo		error = bus_dmamap_create(ring->data_dmat, 0, &data->map);
1532178676Ssam		if (error != 0) {
1533178676Ssam			device_printf(sc->sc_dev,
1534220711Sbschmidt			    "%s: could not create TX buf DMA map, error %d\n",
1535178676Ssam			    __func__, error);
1536178676Ssam			goto fail;
1537178676Ssam		}
1538178676Ssam	}
1539178676Ssam	return 0;
1540220726Sbschmidt
1541220726Sbschmidtfail:	iwn_free_tx_ring(sc, ring);
1542178676Ssam	return error;
1543178676Ssam}
1544178676Ssam
1545206477Sbschmidtstatic void
1546178676Ssamiwn_reset_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring)
1547178676Ssam{
1548198429Srpaulo	int i;
1549178676Ssam
1550178676Ssam	for (i = 0; i < IWN_TX_RING_COUNT; i++) {
1551178676Ssam		struct iwn_tx_data *data = &ring->data[i];
1552178676Ssam
1553178676Ssam		if (data->m != NULL) {
1554220704Sbschmidt			bus_dmamap_sync(ring->data_dmat, data->map,
1555220704Sbschmidt			    BUS_DMASYNC_POSTWRITE);
1556201209Srpaulo			bus_dmamap_unload(ring->data_dmat, data->map);
1557178676Ssam			m_freem(data->m);
1558178676Ssam			data->m = NULL;
1559178676Ssam		}
1560178676Ssam	}
1561198429Srpaulo	/* Clear TX descriptors. */
1562198429Srpaulo	memset(ring->desc, 0, ring->desc_dma.size);
1563198439Srpaulo	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
1564198439Srpaulo	    BUS_DMASYNC_PREWRITE);
1565198429Srpaulo	sc->qfullmsk &= ~(1 << ring->qid);
1566178676Ssam	ring->queued = 0;
1567178676Ssam	ring->cur = 0;
1568178676Ssam}
1569178676Ssam
1570206477Sbschmidtstatic void
1571178676Ssamiwn_free_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring)
1572178676Ssam{
1573178676Ssam	int i;
1574178676Ssam
1575178676Ssam	iwn_dma_contig_free(&ring->desc_dma);
1576178676Ssam	iwn_dma_contig_free(&ring->cmd_dma);
1577178676Ssam
1578201209Srpaulo	for (i = 0; i < IWN_TX_RING_COUNT; i++) {
1579201209Srpaulo		struct iwn_tx_data *data = &ring->data[i];
1580178676Ssam
1581201209Srpaulo		if (data->m != NULL) {
1582201209Srpaulo			bus_dmamap_sync(ring->data_dmat, data->map,
1583201209Srpaulo			    BUS_DMASYNC_POSTWRITE);
1584201209Srpaulo			bus_dmamap_unload(ring->data_dmat, data->map);
1585201209Srpaulo			m_freem(data->m);
1586178676Ssam		}
1587201209Srpaulo		if (data->map != NULL)
1588201209Srpaulo			bus_dmamap_destroy(ring->data_dmat, data->map);
1589178676Ssam	}
1590220701Sbschmidt	if (ring->data_dmat != NULL) {
1591220701Sbschmidt		bus_dma_tag_destroy(ring->data_dmat);
1592220701Sbschmidt		ring->data_dmat = NULL;
1593220701Sbschmidt	}
1594178676Ssam}
1595178676Ssam
1596206477Sbschmidtstatic void
1597201209Srpauloiwn5000_ict_reset(struct iwn_softc *sc)
1598201209Srpaulo{
1599201209Srpaulo	/* Disable interrupts. */
1600201209Srpaulo	IWN_WRITE(sc, IWN_INT_MASK, 0);
1601201209Srpaulo
1602201209Srpaulo	/* Reset ICT table. */
1603201209Srpaulo	memset(sc->ict, 0, IWN_ICT_SIZE);
1604201209Srpaulo	sc->ict_cur = 0;
1605201209Srpaulo
1606220725Sbschmidt	/* Set physical address of ICT table (4KB aligned). */
1607201209Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "%s: enabling ICT\n", __func__);
1608201209Srpaulo	IWN_WRITE(sc, IWN_DRAM_INT_TBL, IWN_DRAM_INT_TBL_ENABLE |
1609201209Srpaulo	    IWN_DRAM_INT_TBL_WRAP_CHECK | sc->ict_dma.paddr >> 12);
1610201209Srpaulo
1611201209Srpaulo	/* Enable periodic RX interrupt. */
1612201209Srpaulo	sc->int_mask |= IWN_INT_RX_PERIODIC;
1613201209Srpaulo	/* Switch to ICT interrupt mode in driver. */
1614201209Srpaulo	sc->sc_flags |= IWN_FLAG_USE_ICT;
1615201209Srpaulo
1616201209Srpaulo	/* Re-enable interrupts. */
1617201209Srpaulo	IWN_WRITE(sc, IWN_INT, 0xffffffff);
1618201209Srpaulo	IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask);
1619201209Srpaulo}
1620201209Srpaulo
1621206477Sbschmidtstatic int
1622198429Srpauloiwn_read_eeprom(struct iwn_softc *sc, uint8_t macaddr[IEEE80211_ADDR_LEN])
1623178676Ssam{
1624220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
1625220723Sbschmidt	uint16_t val;
1626198429Srpaulo	int error;
1627178676Ssam
1628198429Srpaulo	/* Check whether adapter has an EEPROM or an OTPROM. */
1629198429Srpaulo	if (sc->hw_type >= IWN_HW_REV_TYPE_1000 &&
1630198429Srpaulo	    (IWN_READ(sc, IWN_OTP_GP) & IWN_OTP_GP_DEV_SEL_OTP))
1631198429Srpaulo		sc->sc_flags |= IWN_FLAG_HAS_OTPROM;
1632198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "%s found\n",
1633198429Srpaulo	    (sc->sc_flags & IWN_FLAG_HAS_OTPROM) ? "OTPROM" : "EEPROM");
1634178676Ssam
1635201209Srpaulo	/* Adapter has to be powered on for EEPROM access to work. */
1636220726Sbschmidt	if ((error = iwn_apm_init(sc)) != 0) {
1637201209Srpaulo		device_printf(sc->sc_dev,
1638220726Sbschmidt		    "%s: could not power ON adapter, error %d\n", __func__,
1639220726Sbschmidt		    error);
1640201209Srpaulo		return error;
1641201209Srpaulo	}
1642201209Srpaulo
1643198429Srpaulo	if ((IWN_READ(sc, IWN_EEPROM_GP) & 0x7) == 0) {
1644198429Srpaulo		device_printf(sc->sc_dev, "%s: bad ROM signature\n", __func__);
1645198429Srpaulo		return EIO;
1646198429Srpaulo	}
1647220726Sbschmidt	if ((error = iwn_eeprom_lock(sc)) != 0) {
1648220726Sbschmidt		device_printf(sc->sc_dev, "%s: could not lock ROM, error %d\n",
1649198429Srpaulo		    __func__, error);
1650198429Srpaulo		return error;
1651198429Srpaulo	}
1652201209Srpaulo	if (sc->sc_flags & IWN_FLAG_HAS_OTPROM) {
1653220726Sbschmidt		if ((error = iwn_init_otprom(sc)) != 0) {
1654201209Srpaulo			device_printf(sc->sc_dev,
1655201209Srpaulo			    "%s: could not initialize OTPROM, error %d\n",
1656201209Srpaulo			    __func__, error);
1657201209Srpaulo			return error;
1658201209Srpaulo		}
1659198429Srpaulo	}
1660178676Ssam
1661220729Sbschmidt	iwn_read_prom_data(sc, IWN_EEPROM_SKU_CAP, &val, 2);
1662220729Sbschmidt	DPRINTF(sc, IWN_DEBUG_RESET, "SKU capabilities=0x%04x\n", le16toh(val));
1663220729Sbschmidt	/* Check if HT support is bonded out. */
1664220729Sbschmidt	if (val & htole16(IWN_EEPROM_SKU_CAP_11N))
1665220729Sbschmidt		sc->sc_flags |= IWN_FLAG_HAS_11N;
1666220729Sbschmidt
1667198429Srpaulo	iwn_read_prom_data(sc, IWN_EEPROM_RFCFG, &val, 2);
1668198429Srpaulo	sc->rfcfg = le16toh(val);
1669198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "radio config=0x%04x\n", sc->rfcfg);
1670220727Sbschmidt	/* Read Tx/Rx chains from ROM unless it's known to be broken. */
1671220727Sbschmidt	if (sc->txchainmask == 0)
1672220727Sbschmidt		sc->txchainmask = IWN_RFCFG_TXANTMSK(sc->rfcfg);
1673220727Sbschmidt	if (sc->rxchainmask == 0)
1674220727Sbschmidt		sc->rxchainmask = IWN_RFCFG_RXANTMSK(sc->rfcfg);
1675178676Ssam
1676198429Srpaulo	/* Read MAC address. */
1677198429Srpaulo	iwn_read_prom_data(sc, IWN_EEPROM_MAC, macaddr, 6);
1678178676Ssam
1679198429Srpaulo	/* Read adapter-specific information from EEPROM. */
1680220728Sbschmidt	ops->read_eeprom(sc);
1681178676Ssam
1682201209Srpaulo	iwn_apm_stop(sc);	/* Power OFF adapter. */
1683201209Srpaulo
1684198429Srpaulo	iwn_eeprom_unlock(sc);
1685198429Srpaulo	return 0;
1686178676Ssam}
1687178676Ssam
1688206477Sbschmidtstatic void
1689198429Srpauloiwn4965_read_eeprom(struct iwn_softc *sc)
1690178676Ssam{
1691201209Srpaulo	uint32_t addr;
1692220723Sbschmidt	uint16_t val;
1693198429Srpaulo	int i;
1694178676Ssam
1695220725Sbschmidt	/* Read regulatory domain (4 ASCII characters). */
1696198429Srpaulo	iwn_read_prom_data(sc, IWN4965_EEPROM_DOMAIN, sc->eeprom_domain, 4);
1697178676Ssam
1698220725Sbschmidt	/* Read the list of authorized channels (20MHz ones only). */
1699201209Srpaulo	for (i = 0; i < 5; i++) {
1700201209Srpaulo		addr = iwn4965_regulatory_bands[i];
1701201209Srpaulo		iwn_read_eeprom_channels(sc, i, addr);
1702201209Srpaulo	}
1703198429Srpaulo
1704198429Srpaulo	/* Read maximum allowed TX power for 2GHz and 5GHz bands. */
1705198429Srpaulo	iwn_read_prom_data(sc, IWN4965_EEPROM_MAXPOW, &val, 2);
1706198429Srpaulo	sc->maxpwr2GHz = val & 0xff;
1707198429Srpaulo	sc->maxpwr5GHz = val >> 8;
1708198429Srpaulo	/* Check that EEPROM values are within valid range. */
1709198429Srpaulo	if (sc->maxpwr5GHz < 20 || sc->maxpwr5GHz > 50)
1710198429Srpaulo		sc->maxpwr5GHz = 38;
1711198429Srpaulo	if (sc->maxpwr2GHz < 20 || sc->maxpwr2GHz > 50)
1712198429Srpaulo		sc->maxpwr2GHz = 38;
1713198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "maxpwr 2GHz=%d 5GHz=%d\n",
1714198429Srpaulo	    sc->maxpwr2GHz, sc->maxpwr5GHz);
1715198429Srpaulo
1716198429Srpaulo	/* Read samples for each TX power group. */
1717198429Srpaulo	iwn_read_prom_data(sc, IWN4965_EEPROM_BANDS, sc->bands,
1718198429Srpaulo	    sizeof sc->bands);
1719198429Srpaulo
1720198429Srpaulo	/* Read voltage at which samples were taken. */
1721198429Srpaulo	iwn_read_prom_data(sc, IWN4965_EEPROM_VOLTAGE, &val, 2);
1722198429Srpaulo	sc->eeprom_voltage = (int16_t)le16toh(val);
1723198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "voltage=%d (in 0.3V)\n",
1724198429Srpaulo	    sc->eeprom_voltage);
1725198429Srpaulo
1726198429Srpaulo#ifdef IWN_DEBUG
1727198429Srpaulo	/* Print samples. */
1728201209Srpaulo	if (sc->sc_debug & IWN_DEBUG_ANY) {
1729198429Srpaulo		for (i = 0; i < IWN_NBANDS; i++)
1730198429Srpaulo			iwn4965_print_power_group(sc, i);
1731178676Ssam	}
1732198429Srpaulo#endif
1733178676Ssam}
1734178676Ssam
1735198429Srpaulo#ifdef IWN_DEBUG
1736206477Sbschmidtstatic void
1737198429Srpauloiwn4965_print_power_group(struct iwn_softc *sc, int i)
1738178676Ssam{
1739198429Srpaulo	struct iwn4965_eeprom_band *band = &sc->bands[i];
1740198429Srpaulo	struct iwn4965_eeprom_chan_samples *chans = band->chans;
1741198429Srpaulo	int j, c;
1742178676Ssam
1743198429Srpaulo	printf("===band %d===\n", i);
1744198429Srpaulo	printf("chan lo=%d, chan hi=%d\n", band->lo, band->hi);
1745198429Srpaulo	printf("chan1 num=%d\n", chans[0].num);
1746198429Srpaulo	for (c = 0; c < 2; c++) {
1747198429Srpaulo		for (j = 0; j < IWN_NSAMPLES; j++) {
1748198429Srpaulo			printf("chain %d, sample %d: temp=%d gain=%d "
1749198429Srpaulo			    "power=%d pa_det=%d\n", c, j,
1750198429Srpaulo			    chans[0].samples[c][j].temp,
1751198429Srpaulo			    chans[0].samples[c][j].gain,
1752198429Srpaulo			    chans[0].samples[c][j].power,
1753198429Srpaulo			    chans[0].samples[c][j].pa_det);
1754198429Srpaulo		}
1755198429Srpaulo	}
1756198429Srpaulo	printf("chan2 num=%d\n", chans[1].num);
1757198429Srpaulo	for (c = 0; c < 2; c++) {
1758198429Srpaulo		for (j = 0; j < IWN_NSAMPLES; j++) {
1759198429Srpaulo			printf("chain %d, sample %d: temp=%d gain=%d "
1760198429Srpaulo			    "power=%d pa_det=%d\n", c, j,
1761198429Srpaulo			    chans[1].samples[c][j].temp,
1762198429Srpaulo			    chans[1].samples[c][j].gain,
1763198429Srpaulo			    chans[1].samples[c][j].power,
1764198429Srpaulo			    chans[1].samples[c][j].pa_det);
1765198429Srpaulo		}
1766198429Srpaulo	}
1767178676Ssam}
1768198429Srpaulo#endif
1769178676Ssam
1770206477Sbschmidtstatic void
1771198429Srpauloiwn5000_read_eeprom(struct iwn_softc *sc)
1772178676Ssam{
1773206444Sbschmidt	struct iwn5000_eeprom_calib_hdr hdr;
1774220674Sbschmidt	int32_t volt;
1775220723Sbschmidt	uint32_t base, addr;
1776220723Sbschmidt	uint16_t val;
1777198429Srpaulo	int i;
1778178676Ssam
1779220725Sbschmidt	/* Read regulatory domain (4 ASCII characters). */
1780198429Srpaulo	iwn_read_prom_data(sc, IWN5000_EEPROM_REG, &val, 2);
1781198429Srpaulo	base = le16toh(val);
1782198429Srpaulo	iwn_read_prom_data(sc, base + IWN5000_EEPROM_DOMAIN,
1783198429Srpaulo	    sc->eeprom_domain, 4);
1784178676Ssam
1785220725Sbschmidt	/* Read the list of authorized channels (20MHz ones only). */
1786201209Srpaulo	for (i = 0; i < 5; i++) {
1787198429Srpaulo		addr = base + iwn5000_regulatory_bands[i];
1788201209Srpaulo		iwn_read_eeprom_channels(sc, i, addr);
1789198429Srpaulo	}
1790178676Ssam
1791201209Srpaulo	/* Read enhanced TX power information for 6000 Series. */
1792201209Srpaulo	if (sc->hw_type >= IWN_HW_REV_TYPE_6000)
1793201209Srpaulo		iwn_read_eeprom_enhinfo(sc);
1794201209Srpaulo
1795198429Srpaulo	iwn_read_prom_data(sc, IWN5000_EEPROM_CAL, &val, 2);
1796198429Srpaulo	base = le16toh(val);
1797206444Sbschmidt	iwn_read_prom_data(sc, base, &hdr, sizeof hdr);
1798206444Sbschmidt	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
1799220726Sbschmidt	    "%s: calib version=%u pa type=%u voltage=%u\n", __func__,
1800220726Sbschmidt	    hdr.version, hdr.pa_type, le16toh(hdr.volt));
1801210108Sbschmidt	sc->calib_ver = hdr.version;
1802206444Sbschmidt
1803198429Srpaulo	if (sc->hw_type == IWN_HW_REV_TYPE_5150) {
1804201209Srpaulo		/* Compute temperature offset. */
1805198429Srpaulo		iwn_read_prom_data(sc, base + IWN5000_EEPROM_TEMP, &val, 2);
1806220674Sbschmidt		sc->eeprom_temp = le16toh(val);
1807198429Srpaulo		iwn_read_prom_data(sc, base + IWN5000_EEPROM_VOLT, &val, 2);
1808198429Srpaulo		volt = le16toh(val);
1809220674Sbschmidt		sc->temp_off = sc->eeprom_temp - (volt / -5);
1810201209Srpaulo		DPRINTF(sc, IWN_DEBUG_CALIBRATE, "temp=%d volt=%d offset=%dK\n",
1811220674Sbschmidt		    sc->eeprom_temp, volt, sc->temp_off);
1812220674Sbschmidt	} else {
1813220674Sbschmidt		/* Read crystal calibration. */
1814220674Sbschmidt		iwn_read_prom_data(sc, base + IWN5000_EEPROM_CRYSTAL,
1815220674Sbschmidt		    &sc->eeprom_crystal, sizeof (uint32_t));
1816220674Sbschmidt		DPRINTF(sc, IWN_DEBUG_CALIBRATE, "crystal calibration 0x%08x\n",
1817220674Sbschmidt		    le32toh(sc->eeprom_crystal));
1818178676Ssam	}
1819178676Ssam}
1820178676Ssam
1821201209Srpaulo/*
1822201209Srpaulo * Translate EEPROM flags to net80211.
1823201209Srpaulo */
1824201209Srpaulostatic uint32_t
1825201209Srpauloiwn_eeprom_channel_flags(struct iwn_eeprom_chan *channel)
1826201209Srpaulo{
1827201209Srpaulo	uint32_t nflags;
1828201209Srpaulo
1829201209Srpaulo	nflags = 0;
1830201209Srpaulo	if ((channel->flags & IWN_EEPROM_CHAN_ACTIVE) == 0)
1831201209Srpaulo		nflags |= IEEE80211_CHAN_PASSIVE;
1832201209Srpaulo	if ((channel->flags & IWN_EEPROM_CHAN_IBSS) == 0)
1833201209Srpaulo		nflags |= IEEE80211_CHAN_NOADHOC;
1834201209Srpaulo	if (channel->flags & IWN_EEPROM_CHAN_RADAR) {
1835201209Srpaulo		nflags |= IEEE80211_CHAN_DFS;
1836201209Srpaulo		/* XXX apparently IBSS may still be marked */
1837201209Srpaulo		nflags |= IEEE80211_CHAN_NOADHOC;
1838201209Srpaulo	}
1839201209Srpaulo
1840201209Srpaulo	return nflags;
1841201209Srpaulo}
1842201209Srpaulo
1843198429Srpaulostatic void
1844201209Srpauloiwn_read_eeprom_band(struct iwn_softc *sc, int n)
1845178676Ssam{
1846198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
1847198429Srpaulo	struct ieee80211com *ic = ifp->if_l2com;
1848201209Srpaulo	struct iwn_eeprom_chan *channels = sc->eeprom_channels[n];
1849201209Srpaulo	const struct iwn_chan_band *band = &iwn_bands[n];
1850198429Srpaulo	struct ieee80211_channel *c;
1851220687Sbschmidt	uint8_t chan;
1852220687Sbschmidt	int i, nflags;
1853178676Ssam
1854198429Srpaulo	for (i = 0; i < band->nchan; i++) {
1855198429Srpaulo		if (!(channels[i].flags & IWN_EEPROM_CHAN_VALID)) {
1856198429Srpaulo			DPRINTF(sc, IWN_DEBUG_RESET,
1857198429Srpaulo			    "skip chan %d flags 0x%x maxpwr %d\n",
1858198429Srpaulo			    band->chan[i], channels[i].flags,
1859198429Srpaulo			    channels[i].maxpwr);
1860198429Srpaulo			continue;
1861198429Srpaulo		}
1862198429Srpaulo		chan = band->chan[i];
1863201209Srpaulo		nflags = iwn_eeprom_channel_flags(&channels[i]);
1864178676Ssam
1865198429Srpaulo		c = &ic->ic_channels[ic->ic_nchans++];
1866198429Srpaulo		c->ic_ieee = chan;
1867198429Srpaulo		c->ic_maxregpower = channels[i].maxpwr;
1868198429Srpaulo		c->ic_maxpower = 2*c->ic_maxregpower;
1869206445Sbschmidt
1870201209Srpaulo		if (n == 0) {	/* 2GHz band */
1871220726Sbschmidt			c->ic_freq = ieee80211_ieee2mhz(chan, IEEE80211_CHAN_G);
1872198429Srpaulo			/* G =>'s B is supported */
1873198429Srpaulo			c->ic_flags = IEEE80211_CHAN_B | nflags;
1874198429Srpaulo			c = &ic->ic_channels[ic->ic_nchans++];
1875198429Srpaulo			c[0] = c[-1];
1876198429Srpaulo			c->ic_flags = IEEE80211_CHAN_G | nflags;
1877198429Srpaulo		} else {	/* 5GHz band */
1878220726Sbschmidt			c->ic_freq = ieee80211_ieee2mhz(chan, IEEE80211_CHAN_A);
1879198429Srpaulo			c->ic_flags = IEEE80211_CHAN_A | nflags;
1880178676Ssam		}
1881220723Sbschmidt
1882220723Sbschmidt		/* Save maximum allowed TX power for this channel. */
1883220723Sbschmidt		sc->maxpwr[chan] = channels[i].maxpwr;
1884220723Sbschmidt
1885220723Sbschmidt		DPRINTF(sc, IWN_DEBUG_RESET,
1886220726Sbschmidt		    "add chan %d flags 0x%x maxpwr %d\n", chan,
1887220726Sbschmidt		    channels[i].flags, channels[i].maxpwr);
1888220723Sbschmidt
1889201209Srpaulo#if 0	/* HT */
1890198429Srpaulo		/* XXX no constraints on using HT20 */
1891198429Srpaulo		/* add HT20, HT40 added separately */
1892198429Srpaulo		c = &ic->ic_channels[ic->ic_nchans++];
1893198429Srpaulo		c[0] = c[-1];
1894198429Srpaulo		c->ic_flags |= IEEE80211_CHAN_HT20;
1895198429Srpaulo		/* XXX NARROW =>'s 1/2 and 1/4 width? */
1896201209Srpaulo#endif
1897178676Ssam	}
1898178676Ssam}
1899178676Ssam
1900201209Srpaulo#if 0	/* HT */
1901198429Srpaulostatic void
1902201209Srpauloiwn_read_eeprom_ht40(struct iwn_softc *sc, int n)
1903178676Ssam{
1904198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
1905198429Srpaulo	struct ieee80211com *ic = ifp->if_l2com;
1906201209Srpaulo	struct iwn_eeprom_chan *channels = sc->eeprom_channels[n];
1907201209Srpaulo	const struct iwn_chan_band *band = &iwn_bands[n];
1908198429Srpaulo	struct ieee80211_channel *c, *cent, *extc;
1909198429Srpaulo	int i;
1910178676Ssam
1911198429Srpaulo	for (i = 0; i < band->nchan; i++) {
1912198429Srpaulo		if (!(channels[i].flags & IWN_EEPROM_CHAN_VALID) ||
1913198429Srpaulo		    !(channels[i].flags & IWN_EEPROM_CHAN_WIDE)) {
1914198429Srpaulo			DPRINTF(sc, IWN_DEBUG_RESET,
1915198429Srpaulo			    "skip chan %d flags 0x%x maxpwr %d\n",
1916198429Srpaulo			    band->chan[i], channels[i].flags,
1917198429Srpaulo			    channels[i].maxpwr);
1918198429Srpaulo			continue;
1919198429Srpaulo		}
1920198429Srpaulo		/*
1921198429Srpaulo		 * Each entry defines an HT40 channel pair; find the
1922198429Srpaulo		 * center channel, then the extension channel above.
1923198429Srpaulo		 */
1924198429Srpaulo		cent = ieee80211_find_channel_byieee(ic, band->chan[i],
1925201209Srpaulo		    band->flags & ~IEEE80211_CHAN_HT);
1926198429Srpaulo		if (cent == NULL) {	/* XXX shouldn't happen */
1927198429Srpaulo			device_printf(sc->sc_dev,
1928198429Srpaulo			    "%s: no entry for channel %d\n",
1929198429Srpaulo			    __func__, band->chan[i]);
1930198429Srpaulo			continue;
1931198429Srpaulo		}
1932198429Srpaulo		extc = ieee80211_find_channel(ic, cent->ic_freq+20,
1933201209Srpaulo		    band->flags & ~IEEE80211_CHAN_HT);
1934198429Srpaulo		if (extc == NULL) {
1935198429Srpaulo			DPRINTF(sc, IWN_DEBUG_RESET,
1936198429Srpaulo			    "skip chan %d, extension channel not found\n",
1937198429Srpaulo			    band->chan[i]);
1938198429Srpaulo			continue;
1939198429Srpaulo		}
1940178676Ssam
1941198429Srpaulo		DPRINTF(sc, IWN_DEBUG_RESET,
1942198429Srpaulo		    "add ht40 chan %d flags 0x%x maxpwr %d\n",
1943198429Srpaulo		    band->chan[i], channels[i].flags, channels[i].maxpwr);
1944178676Ssam
1945198429Srpaulo		c = &ic->ic_channels[ic->ic_nchans++];
1946198429Srpaulo		c[0] = cent[0];
1947198429Srpaulo		c->ic_extieee = extc->ic_ieee;
1948198429Srpaulo		c->ic_flags &= ~IEEE80211_CHAN_HT;
1949198429Srpaulo		c->ic_flags |= IEEE80211_CHAN_HT40U;
1950198429Srpaulo		c = &ic->ic_channels[ic->ic_nchans++];
1951198429Srpaulo		c[0] = extc[0];
1952198429Srpaulo		c->ic_extieee = cent->ic_ieee;
1953198429Srpaulo		c->ic_flags &= ~IEEE80211_CHAN_HT;
1954198429Srpaulo		c->ic_flags |= IEEE80211_CHAN_HT40D;
1955178676Ssam	}
1956198429Srpaulo}
1957201209Srpaulo#endif
1958178676Ssam
1959198429Srpaulostatic void
1960201209Srpauloiwn_read_eeprom_channels(struct iwn_softc *sc, int n, uint32_t addr)
1961198429Srpaulo{
1962198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
1963198429Srpaulo	struct ieee80211com *ic = ifp->if_l2com;
1964178676Ssam
1965201209Srpaulo	iwn_read_prom_data(sc, addr, &sc->eeprom_channels[n],
1966201209Srpaulo	    iwn_bands[n].nchan * sizeof (struct iwn_eeprom_chan));
1967201209Srpaulo
1968198429Srpaulo	if (n < 5)
1969201209Srpaulo		iwn_read_eeprom_band(sc, n);
1970201209Srpaulo#if 0	/* HT */
1971198429Srpaulo	else
1972201209Srpaulo		iwn_read_eeprom_ht40(sc, n);
1973201209Srpaulo#endif
1974198429Srpaulo	ieee80211_sort_channels(ic->ic_channels, ic->ic_nchans);
1975178676Ssam}
1976178676Ssam
1977220723Sbschmidtstatic struct iwn_eeprom_chan *
1978220723Sbschmidtiwn_find_eeprom_channel(struct iwn_softc *sc, struct ieee80211_channel *c)
1979220723Sbschmidt{
1980220723Sbschmidt	int i, j;
1981220723Sbschmidt
1982220723Sbschmidt	for (j = 0; j < 7; j++) {
1983220723Sbschmidt		for (i = 0; i < iwn_bands[j].nchan; i++) {
1984220723Sbschmidt			if (iwn_bands[j].chan[i] == c->ic_ieee)
1985220723Sbschmidt				return &sc->eeprom_channels[j][i];
1986220723Sbschmidt		}
1987220723Sbschmidt	}
1988220723Sbschmidt
1989220723Sbschmidt	return NULL;
1990220723Sbschmidt}
1991220723Sbschmidt
1992220723Sbschmidt/*
1993220723Sbschmidt * Enforce flags read from EEPROM.
1994220723Sbschmidt */
1995220723Sbschmidtstatic int
1996220723Sbschmidtiwn_setregdomain(struct ieee80211com *ic, struct ieee80211_regdomain *rd,
1997220723Sbschmidt    int nchan, struct ieee80211_channel chans[])
1998220723Sbschmidt{
1999220723Sbschmidt	struct iwn_softc *sc = ic->ic_ifp->if_softc;
2000220723Sbschmidt	int i;
2001220723Sbschmidt
2002220723Sbschmidt	for (i = 0; i < nchan; i++) {
2003220723Sbschmidt		struct ieee80211_channel *c = &chans[i];
2004220723Sbschmidt		struct iwn_eeprom_chan *channel;
2005220723Sbschmidt
2006220723Sbschmidt		channel = iwn_find_eeprom_channel(sc, c);
2007220723Sbschmidt		if (channel == NULL) {
2008220723Sbschmidt			if_printf(ic->ic_ifp,
2009220723Sbschmidt			    "%s: invalid channel %u freq %u/0x%x\n",
2010220723Sbschmidt			    __func__, c->ic_ieee, c->ic_freq, c->ic_flags);
2011220723Sbschmidt			return EINVAL;
2012220723Sbschmidt		}
2013220723Sbschmidt		c->ic_flags |= iwn_eeprom_channel_flags(channel);
2014220723Sbschmidt	}
2015220723Sbschmidt
2016220723Sbschmidt	return 0;
2017220723Sbschmidt}
2018220723Sbschmidt
2019201209Srpaulo#define nitems(_a)	(sizeof((_a)) / sizeof((_a)[0]))
2020201209Srpaulo
2021206477Sbschmidtstatic void
2022201209Srpauloiwn_read_eeprom_enhinfo(struct iwn_softc *sc)
2023201209Srpaulo{
2024201209Srpaulo	struct iwn_eeprom_enhinfo enhinfo[35];
2025201209Srpaulo	uint16_t val, base;
2026201209Srpaulo	int8_t maxpwr;
2027201209Srpaulo	int i;
2028201209Srpaulo
2029201209Srpaulo	iwn_read_prom_data(sc, IWN5000_EEPROM_REG, &val, 2);
2030201209Srpaulo	base = le16toh(val);
2031201209Srpaulo	iwn_read_prom_data(sc, base + IWN6000_EEPROM_ENHINFO,
2032201209Srpaulo	    enhinfo, sizeof enhinfo);
2033201209Srpaulo
2034201209Srpaulo	memset(sc->enh_maxpwr, 0, sizeof sc->enh_maxpwr);
2035201209Srpaulo	for (i = 0; i < nitems(enhinfo); i++) {
2036201209Srpaulo		if (enhinfo[i].chan == 0 || enhinfo[i].reserved != 0)
2037201209Srpaulo			continue;	/* Skip invalid entries. */
2038201209Srpaulo
2039201209Srpaulo		maxpwr = 0;
2040201209Srpaulo		if (sc->txchainmask & IWN_ANT_A)
2041201209Srpaulo			maxpwr = MAX(maxpwr, enhinfo[i].chain[0]);
2042201209Srpaulo		if (sc->txchainmask & IWN_ANT_B)
2043201209Srpaulo			maxpwr = MAX(maxpwr, enhinfo[i].chain[1]);
2044201209Srpaulo		if (sc->txchainmask & IWN_ANT_C)
2045201209Srpaulo			maxpwr = MAX(maxpwr, enhinfo[i].chain[2]);
2046201209Srpaulo		if (sc->ntxchains == 2)
2047201209Srpaulo			maxpwr = MAX(maxpwr, enhinfo[i].mimo2);
2048201209Srpaulo		else if (sc->ntxchains == 3)
2049201209Srpaulo			maxpwr = MAX(maxpwr, enhinfo[i].mimo3);
2050201209Srpaulo		maxpwr /= 2;	/* Convert half-dBm to dBm. */
2051201209Srpaulo
2052201209Srpaulo		DPRINTF(sc, IWN_DEBUG_RESET, "enhinfo %d, maxpwr=%d\n", i,
2053201209Srpaulo		    maxpwr);
2054201209Srpaulo		sc->enh_maxpwr[i] = maxpwr;
2055201209Srpaulo	}
2056201209Srpaulo}
2057201209Srpaulo
2058206477Sbschmidtstatic struct ieee80211_node *
2059198429Srpauloiwn_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN])
2060178676Ssam{
2061198429Srpaulo	return malloc(sizeof (struct iwn_node), M_80211_NODE,M_NOWAIT | M_ZERO);
2062198429Srpaulo}
2063178676Ssam
2064220715Sbschmidtstatic void
2065220715Sbschmidtiwn_newassoc(struct ieee80211_node *ni, int isnew)
2066220715Sbschmidt{
2067220715Sbschmidt	struct iwn_node *wn = (void *)ni;
2068220715Sbschmidt	int ridx, i;
2069220715Sbschmidt
2070220715Sbschmidt	for (i = 0; i < ni->ni_rates.rs_nrates; i++) {
2071220715Sbschmidt		ridx = iwn_plcp_signal(ni->ni_rates.rs_rates[i]);
2072220715Sbschmidt		wn->ridx[i] = ridx;
2073220715Sbschmidt	}
2074220715Sbschmidt}
2075220715Sbschmidt
2076206477Sbschmidtstatic int
2077198429Srpauloiwn_media_change(struct ifnet *ifp)
2078178676Ssam{
2079220726Sbschmidt	int error;
2080220726Sbschmidt
2081220726Sbschmidt	error = ieee80211_media_change(ifp);
2082198429Srpaulo	/* NB: only the fixed rate can change and that doesn't need a reset */
2083198429Srpaulo	return (error == ENETRESET ? 0 : error);
2084198429Srpaulo}
2085178676Ssam
2086206477Sbschmidtstatic int
2087198429Srpauloiwn_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
2088198429Srpaulo{
2089198429Srpaulo	struct iwn_vap *ivp = IWN_VAP(vap);
2090198429Srpaulo	struct ieee80211com *ic = vap->iv_ic;
2091198429Srpaulo	struct iwn_softc *sc = ic->ic_ifp->if_softc;
2092220688Sbschmidt	int error = 0;
2093178676Ssam
2094198429Srpaulo	DPRINTF(sc, IWN_DEBUG_STATE, "%s: %s -> %s\n", __func__,
2095220726Sbschmidt	    ieee80211_state_name[vap->iv_state], ieee80211_state_name[nstate]);
2096178676Ssam
2097198429Srpaulo	IEEE80211_UNLOCK(ic);
2098198429Srpaulo	IWN_LOCK(sc);
2099220667Sbschmidt	callout_stop(&sc->calib_to);
2100178676Ssam
2101210114Sbschmidt	switch (nstate) {
2102210114Sbschmidt	case IEEE80211_S_ASSOC:
2103210114Sbschmidt		if (vap->iv_state != IEEE80211_S_RUN)
2104210114Sbschmidt			break;
2105210114Sbschmidt		/* FALLTHROUGH */
2106210114Sbschmidt	case IEEE80211_S_AUTH:
2107210114Sbschmidt		if (vap->iv_state == IEEE80211_S_AUTH)
2108210114Sbschmidt			break;
2109210114Sbschmidt
2110210114Sbschmidt		/*
2111210114Sbschmidt		 * !AUTH -> AUTH transition requires state reset to handle
2112210114Sbschmidt		 * reassociations correctly.
2113210114Sbschmidt		 */
2114198439Srpaulo		sc->rxon.associd = 0;
2115198439Srpaulo		sc->rxon.filter &= ~htole32(IWN_FILTER_BSS);
2116220667Sbschmidt		sc->calib.state = IWN_CALIB_STATE_INIT;
2117220667Sbschmidt
2118220688Sbschmidt		if ((error = iwn_auth(sc, vap)) != 0) {
2119220688Sbschmidt			device_printf(sc->sc_dev,
2120220688Sbschmidt			    "%s: could not move to auth state\n", __func__);
2121220688Sbschmidt		}
2122210114Sbschmidt		break;
2123210114Sbschmidt
2124210114Sbschmidt	case IEEE80211_S_RUN:
2125198429Srpaulo		/*
2126210114Sbschmidt		 * RUN -> RUN transition; Just restart the timers.
2127210114Sbschmidt		 */
2128220667Sbschmidt		if (vap->iv_state == IEEE80211_S_RUN) {
2129220667Sbschmidt			sc->calib_cnt = 0;
2130210114Sbschmidt			break;
2131210114Sbschmidt		}
2132210114Sbschmidt
2133210114Sbschmidt		/*
2134198429Srpaulo		 * !RUN -> RUN requires setting the association id
2135198429Srpaulo		 * which is done with a firmware cmd.  We also defer
2136198429Srpaulo		 * starting the timers until that work is done.
2137198429Srpaulo		 */
2138220688Sbschmidt		if ((error = iwn_run(sc, vap)) != 0) {
2139220688Sbschmidt			device_printf(sc->sc_dev,
2140220688Sbschmidt			    "%s: could not move to run state\n", __func__);
2141220688Sbschmidt		}
2142210114Sbschmidt		break;
2143210114Sbschmidt
2144220667Sbschmidt	case IEEE80211_S_INIT:
2145220667Sbschmidt		sc->calib.state = IWN_CALIB_STATE_INIT;
2146220667Sbschmidt		break;
2147220667Sbschmidt
2148210114Sbschmidt	default:
2149210114Sbschmidt		break;
2150178676Ssam	}
2151198429Srpaulo	IWN_UNLOCK(sc);
2152198429Srpaulo	IEEE80211_LOCK(ic);
2153220688Sbschmidt	if (error != 0)
2154220688Sbschmidt		return error;
2155198429Srpaulo	return ivp->iv_newstate(vap, nstate, arg);
2156178676Ssam}
2157178676Ssam
2158220667Sbschmidtstatic void
2159220667Sbschmidtiwn_calib_timeout(void *arg)
2160220667Sbschmidt{
2161220667Sbschmidt	struct iwn_softc *sc = arg;
2162220667Sbschmidt
2163220667Sbschmidt	IWN_LOCK_ASSERT(sc);
2164220667Sbschmidt
2165220667Sbschmidt	/* Force automatic TX power calibration every 60 secs. */
2166220667Sbschmidt	if (++sc->calib_cnt >= 120) {
2167220667Sbschmidt		uint32_t flags = 0;
2168220667Sbschmidt
2169220667Sbschmidt		DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s\n",
2170220667Sbschmidt		    "sending request for statistics");
2171220667Sbschmidt		(void)iwn_cmd(sc, IWN_CMD_GET_STATISTICS, &flags,
2172220667Sbschmidt		    sizeof flags, 1);
2173220667Sbschmidt		sc->calib_cnt = 0;
2174220667Sbschmidt	}
2175220667Sbschmidt	callout_reset(&sc->calib_to, msecs_to_ticks(500), iwn_calib_timeout,
2176220667Sbschmidt	    sc);
2177220667Sbschmidt}
2178220667Sbschmidt
2179198429Srpaulo/*
2180198429Srpaulo * Process an RX_PHY firmware notification.  This is usually immediately
2181198429Srpaulo * followed by an MPDU_RX_DONE notification.
2182198429Srpaulo */
2183206477Sbschmidtstatic void
2184198429Srpauloiwn_rx_phy(struct iwn_softc *sc, struct iwn_rx_desc *desc,
2185198429Srpaulo    struct iwn_rx_data *data)
2186178676Ssam{
2187198429Srpaulo	struct iwn_rx_stat *stat = (struct iwn_rx_stat *)(desc + 1);
2188198429Srpaulo
2189198429Srpaulo	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: received PHY stats\n", __func__);
2190202986Srpaulo	bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD);
2191198429Srpaulo
2192198429Srpaulo	/* Save RX statistics, they will be used on MPDU_RX_DONE. */
2193198429Srpaulo	memcpy(&sc->last_rx_stat, stat, sizeof (*stat));
2194198429Srpaulo	sc->last_rx_valid = 1;
2195178676Ssam}
2196178676Ssam
2197198429Srpaulo/*
2198198429Srpaulo * Process an RX_DONE (4965AGN only) or MPDU_RX_DONE firmware notification.
2199198429Srpaulo * Each MPDU_RX_DONE notification must be preceded by an RX_PHY one.
2200198429Srpaulo */
2201206477Sbschmidtstatic void
2202198429Srpauloiwn_rx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc,
2203178676Ssam    struct iwn_rx_data *data)
2204178676Ssam{
2205220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
2206178676Ssam	struct ifnet *ifp = sc->sc_ifp;
2207178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
2208178676Ssam	struct iwn_rx_ring *ring = &sc->rxq;
2209178676Ssam	struct ieee80211_frame *wh;
2210178676Ssam	struct ieee80211_node *ni;
2211198429Srpaulo	struct mbuf *m, *m1;
2212178676Ssam	struct iwn_rx_stat *stat;
2213178676Ssam	caddr_t head;
2214178676Ssam	bus_addr_t paddr;
2215198429Srpaulo	uint32_t flags;
2216198429Srpaulo	int error, len, rssi, nf;
2217178676Ssam
2218198429Srpaulo	if (desc->type == IWN_MPDU_RX_DONE) {
2219198429Srpaulo		/* Check for prior RX_PHY notification. */
2220178676Ssam		if (!sc->last_rx_valid) {
2221178676Ssam			DPRINTF(sc, IWN_DEBUG_ANY,
2222201209Srpaulo			    "%s: missing RX_PHY\n", __func__);
2223178676Ssam			return;
2224178676Ssam		}
2225178676Ssam		sc->last_rx_valid = 0;
2226178676Ssam		stat = &sc->last_rx_stat;
2227178676Ssam	} else
2228178676Ssam		stat = (struct iwn_rx_stat *)(desc + 1);
2229178676Ssam
2230201209Srpaulo	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD);
2231198439Srpaulo
2232178676Ssam	if (stat->cfg_phy_len > IWN_STAT_MAXLEN) {
2233178676Ssam		device_printf(sc->sc_dev,
2234220724Sbschmidt		    "%s: invalid RX statistic header, len %d\n", __func__,
2235220724Sbschmidt		    stat->cfg_phy_len);
2236178676Ssam		return;
2237178676Ssam	}
2238198429Srpaulo	if (desc->type == IWN_MPDU_RX_DONE) {
2239198429Srpaulo		struct iwn_rx_mpdu *mpdu = (struct iwn_rx_mpdu *)(desc + 1);
2240198429Srpaulo		head = (caddr_t)(mpdu + 1);
2241198429Srpaulo		len = le16toh(mpdu->len);
2242178676Ssam	} else {
2243178676Ssam		head = (caddr_t)(stat + 1) + stat->cfg_phy_len;
2244178676Ssam		len = le16toh(stat->len);
2245178676Ssam	}
2246178676Ssam
2247198429Srpaulo	flags = le32toh(*(uint32_t *)(head + len));
2248198429Srpaulo
2249198429Srpaulo	/* Discard frames with a bad FCS early. */
2250198429Srpaulo	if ((flags & IWN_RX_NOERROR) != IWN_RX_NOERROR) {
2251220724Sbschmidt		DPRINTF(sc, IWN_DEBUG_RECV, "%s: RX flags error %x\n",
2252198429Srpaulo		    __func__, flags);
2253178676Ssam		ifp->if_ierrors++;
2254178676Ssam		return;
2255178676Ssam	}
2256198429Srpaulo	/* Discard frames that are too short. */
2257198429Srpaulo	if (len < sizeof (*wh)) {
2258178676Ssam		DPRINTF(sc, IWN_DEBUG_RECV, "%s: frame too short: %d\n",
2259178676Ssam		    __func__, len);
2260178676Ssam		ifp->if_ierrors++;
2261178676Ssam		return;
2262178676Ssam	}
2263178676Ssam
2264220692Sbschmidt	m1 = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, IWN_RBUF_SIZE);
2265198429Srpaulo	if (m1 == NULL) {
2266178676Ssam		DPRINTF(sc, IWN_DEBUG_ANY, "%s: no mbuf to restock ring\n",
2267178676Ssam		    __func__);
2268178676Ssam		ifp->if_ierrors++;
2269178676Ssam		return;
2270178676Ssam	}
2271201209Srpaulo	bus_dmamap_unload(ring->data_dmat, data->map);
2272201209Srpaulo
2273220692Sbschmidt	error = bus_dmamap_load(ring->data_dmat, data->map, mtod(m1, void *),
2274220692Sbschmidt	    IWN_RBUF_SIZE, iwn_dma_map_addr, &paddr, BUS_DMA_NOWAIT);
2275178676Ssam	if (error != 0 && error != EFBIG) {
2276178676Ssam		device_printf(sc->sc_dev,
2277178676Ssam		    "%s: bus_dmamap_load failed, error %d\n", __func__, error);
2278198429Srpaulo		m_freem(m1);
2279220693Sbschmidt
2280220693Sbschmidt		/* Try to reload the old mbuf. */
2281220693Sbschmidt		error = bus_dmamap_load(ring->data_dmat, data->map,
2282220693Sbschmidt		    mtod(data->m, void *), IWN_RBUF_SIZE, iwn_dma_map_addr,
2283220693Sbschmidt		    &paddr, BUS_DMA_NOWAIT);
2284220693Sbschmidt		if (error != 0 && error != EFBIG) {
2285220693Sbschmidt			panic("%s: could not load old RX mbuf", __func__);
2286220693Sbschmidt		}
2287220693Sbschmidt		/* Physical address may have changed. */
2288220693Sbschmidt		ring->desc[ring->cur] = htole32(paddr >> 8);
2289220693Sbschmidt		bus_dmamap_sync(ring->data_dmat, ring->desc_dma.map,
2290220693Sbschmidt		    BUS_DMASYNC_PREWRITE);
2291178676Ssam		ifp->if_ierrors++;
2292178676Ssam		return;
2293178676Ssam	}
2294178676Ssam
2295178676Ssam	m = data->m;
2296198429Srpaulo	data->m = m1;
2297198429Srpaulo	/* Update RX descriptor. */
2298198429Srpaulo	ring->desc[ring->cur] = htole32(paddr >> 8);
2299201209Srpaulo	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
2300201209Srpaulo	    BUS_DMASYNC_PREWRITE);
2301198429Srpaulo
2302198429Srpaulo	/* Finalize mbuf. */
2303178676Ssam	m->m_pkthdr.rcvif = ifp;
2304178676Ssam	m->m_data = head;
2305178676Ssam	m->m_pkthdr.len = m->m_len = len;
2306178676Ssam
2307198429Srpaulo	/* Grab a reference to the source node. */
2308178676Ssam	wh = mtod(m, struct ieee80211_frame *);
2309178676Ssam	ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh);
2310178676Ssam	nf = (ni != NULL && ni->ni_vap->iv_state == IEEE80211_S_RUN &&
2311178676Ssam	    (ic->ic_flags & IEEE80211_F_SCAN) == 0) ? sc->noise : -95;
2312178676Ssam
2313220728Sbschmidt	rssi = ops->get_rssi(sc, stat);
2314220689Sbschmidt
2315192468Ssam	if (ieee80211_radiotap_active(ic)) {
2316178676Ssam		struct iwn_rx_radiotap_header *tap = &sc->sc_rxtap;
2317178676Ssam
2318178676Ssam		tap->wr_flags = 0;
2319201209Srpaulo		if (stat->flags & htole16(IWN_STAT_FLAG_SHPREAMBLE))
2320192468Ssam			tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
2321220723Sbschmidt		tap->wr_dbm_antsignal = (int8_t)rssi;
2322220723Sbschmidt		tap->wr_dbm_antnoise = (int8_t)nf;
2323220723Sbschmidt		tap->wr_tsft = stat->tstamp;
2324201209Srpaulo		switch (stat->rate) {
2325201209Srpaulo		/* CCK rates. */
2326201209Srpaulo		case  10: tap->wr_rate =   2; break;
2327201209Srpaulo		case  20: tap->wr_rate =   4; break;
2328201209Srpaulo		case  55: tap->wr_rate =  11; break;
2329201209Srpaulo		case 110: tap->wr_rate =  22; break;
2330201209Srpaulo		/* OFDM rates. */
2331201209Srpaulo		case 0xd: tap->wr_rate =  12; break;
2332201209Srpaulo		case 0xf: tap->wr_rate =  18; break;
2333201209Srpaulo		case 0x5: tap->wr_rate =  24; break;
2334201209Srpaulo		case 0x7: tap->wr_rate =  36; break;
2335201209Srpaulo		case 0x9: tap->wr_rate =  48; break;
2336201209Srpaulo		case 0xb: tap->wr_rate =  72; break;
2337201209Srpaulo		case 0x1: tap->wr_rate =  96; break;
2338201209Srpaulo		case 0x3: tap->wr_rate = 108; break;
2339201209Srpaulo		/* Unknown rate: should not happen. */
2340201209Srpaulo		default:  tap->wr_rate =   0;
2341201209Srpaulo		}
2342178676Ssam	}
2343178676Ssam
2344178676Ssam	IWN_UNLOCK(sc);
2345178676Ssam
2346198429Srpaulo	/* Send the frame to the 802.11 layer. */
2347178676Ssam	if (ni != NULL) {
2348220726Sbschmidt		(void)ieee80211_input(ni, m, rssi - nf, nf);
2349198429Srpaulo		/* Node is no longer needed. */
2350178676Ssam		ieee80211_free_node(ni);
2351178676Ssam	} else
2352220726Sbschmidt		(void)ieee80211_input_all(ic, m, rssi - nf, nf);
2353178676Ssam
2354178676Ssam	IWN_LOCK(sc);
2355178676Ssam}
2356178676Ssam
2357201209Srpaulo#if 0	/* HT */
2358201209Srpaulo/* Process an incoming Compressed BlockAck. */
2359206477Sbschmidtstatic void
2360201209Srpauloiwn_rx_compressed_ba(struct iwn_softc *sc, struct iwn_rx_desc *desc,
2361201209Srpaulo    struct iwn_rx_data *data)
2362201209Srpaulo{
2363201209Srpaulo	struct iwn_compressed_ba *ba = (struct iwn_compressed_ba *)(desc + 1);
2364201209Srpaulo	struct iwn_tx_ring *txq;
2365201209Srpaulo
2366220704Sbschmidt	bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD);
2367220704Sbschmidt
2368201209Srpaulo	txq = &sc->txq[letoh16(ba->qid)];
2369201209Srpaulo	/* XXX TBD */
2370201209Srpaulo}
2371201209Srpaulo#endif
2372201209Srpaulo
2373198429Srpaulo/*
2374220674Sbschmidt * Process a CALIBRATION_RESULT notification sent by the initialization
2375220674Sbschmidt * firmware on response to a CMD_CALIB_CONFIG command (5000 only).
2376220674Sbschmidt */
2377220674Sbschmidtstatic void
2378220674Sbschmidtiwn5000_rx_calib_results(struct iwn_softc *sc, struct iwn_rx_desc *desc,
2379220674Sbschmidt    struct iwn_rx_data *data)
2380220674Sbschmidt{
2381220674Sbschmidt	struct iwn_phy_calib *calib = (struct iwn_phy_calib *)(desc + 1);
2382220674Sbschmidt	int len, idx = -1;
2383220674Sbschmidt
2384220674Sbschmidt	/* Runtime firmware should not send such a notification. */
2385220674Sbschmidt	if (sc->sc_flags & IWN_FLAG_CALIB_DONE)
2386220674Sbschmidt		return;
2387220674Sbschmidt
2388220674Sbschmidt	len = (le32toh(desc->len) & 0x3fff) - 4;
2389220674Sbschmidt	bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD);
2390220674Sbschmidt
2391220674Sbschmidt	switch (calib->code) {
2392220674Sbschmidt	case IWN5000_PHY_CALIB_DC:
2393220867Sbschmidt		if ((sc->sc_flags & IWN_FLAG_INTERNAL_PA) == 0 &&
2394220867Sbschmidt		    (sc->hw_type == IWN_HW_REV_TYPE_5150 ||
2395220867Sbschmidt		     sc->hw_type >= IWN_HW_REV_TYPE_6000))
2396220674Sbschmidt			idx = 0;
2397220674Sbschmidt		break;
2398220674Sbschmidt	case IWN5000_PHY_CALIB_LO:
2399220674Sbschmidt		idx = 1;
2400220674Sbschmidt		break;
2401220674Sbschmidt	case IWN5000_PHY_CALIB_TX_IQ:
2402220674Sbschmidt		idx = 2;
2403220674Sbschmidt		break;
2404220674Sbschmidt	case IWN5000_PHY_CALIB_TX_IQ_PERIODIC:
2405220674Sbschmidt		if (sc->hw_type < IWN_HW_REV_TYPE_6000 &&
2406220674Sbschmidt		    sc->hw_type != IWN_HW_REV_TYPE_5150)
2407220674Sbschmidt			idx = 3;
2408220674Sbschmidt		break;
2409220674Sbschmidt	case IWN5000_PHY_CALIB_BASE_BAND:
2410220674Sbschmidt		idx = 4;
2411220674Sbschmidt		break;
2412220674Sbschmidt	}
2413220674Sbschmidt	if (idx == -1)	/* Ignore other results. */
2414220674Sbschmidt		return;
2415220674Sbschmidt
2416220674Sbschmidt	/* Save calibration result. */
2417220674Sbschmidt	if (sc->calibcmd[idx].buf != NULL)
2418220674Sbschmidt		free(sc->calibcmd[idx].buf, M_DEVBUF);
2419220674Sbschmidt	sc->calibcmd[idx].buf = malloc(len, M_DEVBUF, M_NOWAIT);
2420220674Sbschmidt	if (sc->calibcmd[idx].buf == NULL) {
2421220674Sbschmidt		DPRINTF(sc, IWN_DEBUG_CALIBRATE,
2422220674Sbschmidt		    "not enough memory for calibration result %d\n",
2423220674Sbschmidt		    calib->code);
2424220674Sbschmidt		return;
2425220674Sbschmidt	}
2426220674Sbschmidt	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
2427220674Sbschmidt	    "saving calibration result code=%d len=%d\n", calib->code, len);
2428220674Sbschmidt	sc->calibcmd[idx].len = len;
2429220674Sbschmidt	memcpy(sc->calibcmd[idx].buf, calib, len);
2430220674Sbschmidt}
2431220674Sbschmidt
2432220674Sbschmidt/*
2433198429Srpaulo * Process an RX_STATISTICS or BEACON_STATISTICS firmware notification.
2434198429Srpaulo * The latter is sent by the firmware after each received beacon.
2435198429Srpaulo */
2436206477Sbschmidtstatic void
2437198429Srpauloiwn_rx_statistics(struct iwn_softc *sc, struct iwn_rx_desc *desc,
2438198429Srpaulo    struct iwn_rx_data *data)
2439198429Srpaulo{
2440220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
2441178676Ssam	struct ifnet *ifp = sc->sc_ifp;
2442178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
2443178676Ssam	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
2444178676Ssam	struct iwn_calib_state *calib = &sc->calib;
2445178676Ssam	struct iwn_stats *stats = (struct iwn_stats *)(desc + 1);
2446198429Srpaulo	int temp;
2447178676Ssam
2448220725Sbschmidt	/* Ignore statistics received during a scan. */
2449178676Ssam	if (vap->iv_state != IEEE80211_S_RUN ||
2450178676Ssam	    (ic->ic_flags & IEEE80211_F_SCAN))
2451178676Ssam		return;
2452178676Ssam
2453202986Srpaulo	bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD);
2454220724Sbschmidt
2455220724Sbschmidt	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: received statistics, cmd %d\n",
2456220724Sbschmidt	    __func__, desc->type);
2457220667Sbschmidt	sc->calib_cnt = 0;	/* Reset TX power calibration timeout. */
2458178676Ssam
2459198429Srpaulo	/* Test if temperature has changed. */
2460178676Ssam	if (stats->general.temp != sc->rawtemp) {
2461198429Srpaulo		/* Convert "raw" temperature to degC. */
2462178676Ssam		sc->rawtemp = stats->general.temp;
2463220728Sbschmidt		temp = ops->get_temperature(sc);
2464178676Ssam		DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: temperature %d\n",
2465178676Ssam		    __func__, temp);
2466178676Ssam
2467220725Sbschmidt		/* Update TX power if need be (4965AGN only). */
2468198429Srpaulo		if (sc->hw_type == IWN_HW_REV_TYPE_4965)
2469198429Srpaulo			iwn4965_power_calibration(sc, temp);
2470178676Ssam	}
2471178676Ssam
2472178676Ssam	if (desc->type != IWN_BEACON_STATISTICS)
2473198429Srpaulo		return;	/* Reply to a statistics request. */
2474178676Ssam
2475178676Ssam	sc->noise = iwn_get_noise(&stats->rx.general);
2476178676Ssam	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: noise %d\n", __func__, sc->noise);
2477178676Ssam
2478198429Srpaulo	/* Test that RSSI and noise are present in stats report. */
2479198429Srpaulo	if (le32toh(stats->rx.general.flags) != 1) {
2480178676Ssam		DPRINTF(sc, IWN_DEBUG_ANY, "%s\n",
2481178676Ssam		    "received statistics without RSSI");
2482178676Ssam		return;
2483178676Ssam	}
2484178676Ssam
2485178676Ssam	if (calib->state == IWN_CALIB_STATE_ASSOC)
2486198429Srpaulo		iwn_collect_noise(sc, &stats->rx.general);
2487178676Ssam	else if (calib->state == IWN_CALIB_STATE_RUN)
2488178676Ssam		iwn_tune_sensitivity(sc, &stats->rx);
2489178676Ssam}
2490178676Ssam
2491198429Srpaulo/*
2492198429Srpaulo * Process a TX_DONE firmware notification.  Unfortunately, the 4965AGN
2493198429Srpaulo * and 5000 adapters have different incompatible TX status formats.
2494198429Srpaulo */
2495206477Sbschmidtstatic void
2496198429Srpauloiwn4965_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc,
2497198429Srpaulo    struct iwn_rx_data *data)
2498178676Ssam{
2499198429Srpaulo	struct iwn4965_tx_stat *stat = (struct iwn4965_tx_stat *)(desc + 1);
2500207001Sbschmidt	struct iwn_tx_ring *ring = &sc->txq[desc->qid & 0xf];
2501198429Srpaulo
2502198429Srpaulo	DPRINTF(sc, IWN_DEBUG_XMIT, "%s: "
2503198429Srpaulo	    "qid %d idx %d retries %d nkill %d rate %x duration %d status %x\n",
2504201209Srpaulo	    __func__, desc->qid, desc->idx, stat->ackfailcnt,
2505201209Srpaulo	    stat->btkillcnt, stat->rate, le16toh(stat->duration),
2506198429Srpaulo	    le32toh(stat->status));
2507201209Srpaulo
2508207001Sbschmidt	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD);
2509201209Srpaulo	iwn_tx_done(sc, desc, stat->ackfailcnt, le32toh(stat->status) & 0xff);
2510198429Srpaulo}
2511198429Srpaulo
2512206477Sbschmidtstatic void
2513198429Srpauloiwn5000_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc,
2514198429Srpaulo    struct iwn_rx_data *data)
2515198429Srpaulo{
2516198429Srpaulo	struct iwn5000_tx_stat *stat = (struct iwn5000_tx_stat *)(desc + 1);
2517207001Sbschmidt	struct iwn_tx_ring *ring = &sc->txq[desc->qid & 0xf];
2518198429Srpaulo
2519198429Srpaulo	DPRINTF(sc, IWN_DEBUG_XMIT, "%s: "
2520198429Srpaulo	    "qid %d idx %d retries %d nkill %d rate %x duration %d status %x\n",
2521201209Srpaulo	    __func__, desc->qid, desc->idx, stat->ackfailcnt,
2522201209Srpaulo	    stat->btkillcnt, stat->rate, le16toh(stat->duration),
2523198429Srpaulo	    le32toh(stat->status));
2524198429Srpaulo
2525201209Srpaulo#ifdef notyet
2526198429Srpaulo	/* Reset TX scheduler slot. */
2527198429Srpaulo	iwn5000_reset_sched(sc, desc->qid & 0xf, desc->idx);
2528201209Srpaulo#endif
2529202986Srpaulo
2530207001Sbschmidt	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD);
2531201209Srpaulo	iwn_tx_done(sc, desc, stat->ackfailcnt, le16toh(stat->status) & 0xff);
2532198429Srpaulo}
2533198429Srpaulo
2534198429Srpaulo/*
2535198429Srpaulo * Adapter-independent backend for TX_DONE firmware notifications.
2536198429Srpaulo */
2537206477Sbschmidtstatic void
2538201209Srpauloiwn_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, int ackfailcnt,
2539198429Srpaulo    uint8_t status)
2540198429Srpaulo{
2541178676Ssam	struct ifnet *ifp = sc->sc_ifp;
2542178676Ssam	struct iwn_tx_ring *ring = &sc->txq[desc->qid & 0xf];
2543178676Ssam	struct iwn_tx_data *data = &ring->data[desc->idx];
2544178676Ssam	struct mbuf *m;
2545178676Ssam	struct ieee80211_node *ni;
2546206358Srpaulo	struct ieee80211vap *vap;
2547178676Ssam
2548178676Ssam	KASSERT(data->ni != NULL, ("no node"));
2549178676Ssam
2550198429Srpaulo	/* Unmap and free mbuf. */
2551201209Srpaulo	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTWRITE);
2552201209Srpaulo	bus_dmamap_unload(ring->data_dmat, data->map);
2553178676Ssam	m = data->m, data->m = NULL;
2554178676Ssam	ni = data->ni, data->ni = NULL;
2555206358Srpaulo	vap = ni->ni_vap;
2556178676Ssam
2557178676Ssam	if (m->m_flags & M_TXCB) {
2558178676Ssam		/*
2559178676Ssam		 * Channels marked for "radar" require traffic to be received
2560178676Ssam		 * to unlock before we can transmit.  Until traffic is seen
2561178676Ssam		 * any attempt to transmit is returned immediately with status
2562178676Ssam		 * set to IWN_TX_FAIL_TX_LOCKED.  Unfortunately this can easily
2563178676Ssam		 * happen on first authenticate after scanning.  To workaround
2564178676Ssam		 * this we ignore a failure of this sort in AUTH state so the
2565178676Ssam		 * 802.11 layer will fall back to using a timeout to wait for
2566178676Ssam		 * the AUTH reply.  This allows the firmware time to see
2567178676Ssam		 * traffic so a subsequent retry of AUTH succeeds.  It's
2568178676Ssam		 * unclear why the firmware does not maintain state for
2569178676Ssam		 * channels recently visited as this would allow immediate
2570178676Ssam		 * use of the channel after a scan (where we see traffic).
2571178676Ssam		 */
2572178676Ssam		if (status == IWN_TX_FAIL_TX_LOCKED &&
2573178676Ssam		    ni->ni_vap->iv_state == IEEE80211_S_AUTH)
2574178676Ssam			ieee80211_process_callback(ni, m, 0);
2575178676Ssam		else
2576178676Ssam			ieee80211_process_callback(ni, m,
2577178676Ssam			    (status & IWN_TX_FAIL) != 0);
2578178676Ssam	}
2579201209Srpaulo
2580201209Srpaulo	/*
2581201209Srpaulo	 * Update rate control statistics for the node.
2582201209Srpaulo	 */
2583220719Sbschmidt	if (status & IWN_TX_FAIL) {
2584201209Srpaulo		ifp->if_oerrors++;
2585206358Srpaulo		ieee80211_ratectl_tx_complete(vap, ni,
2586206358Srpaulo		    IEEE80211_RATECTL_TX_FAILURE, &ackfailcnt, NULL);
2587201209Srpaulo	} else {
2588220719Sbschmidt		ifp->if_opackets++;
2589206358Srpaulo		ieee80211_ratectl_tx_complete(vap, ni,
2590206358Srpaulo		    IEEE80211_RATECTL_TX_SUCCESS, &ackfailcnt, NULL);
2591201209Srpaulo	}
2592178676Ssam	m_freem(m);
2593178676Ssam	ieee80211_free_node(ni);
2594178676Ssam
2595178676Ssam	sc->sc_tx_timer = 0;
2596198429Srpaulo	if (--ring->queued < IWN_TX_RING_LOMARK) {
2597198429Srpaulo		sc->qfullmsk &= ~(1 << ring->qid);
2598198429Srpaulo		if (sc->qfullmsk == 0 &&
2599198429Srpaulo		    (ifp->if_drv_flags & IFF_DRV_OACTIVE)) {
2600198429Srpaulo			ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
2601198429Srpaulo			iwn_start_locked(ifp);
2602198429Srpaulo		}
2603198429Srpaulo	}
2604178676Ssam}
2605178676Ssam
2606198429Srpaulo/*
2607198429Srpaulo * Process a "command done" firmware notification.  This is where we wakeup
2608198429Srpaulo * processes waiting for a synchronous command completion.
2609198429Srpaulo */
2610206477Sbschmidtstatic void
2611198429Srpauloiwn_cmd_done(struct iwn_softc *sc, struct iwn_rx_desc *desc)
2612178676Ssam{
2613178676Ssam	struct iwn_tx_ring *ring = &sc->txq[4];
2614178676Ssam	struct iwn_tx_data *data;
2615178676Ssam
2616178676Ssam	if ((desc->qid & 0xf) != 4)
2617198429Srpaulo		return;	/* Not a command ack. */
2618178676Ssam
2619178676Ssam	data = &ring->data[desc->idx];
2620178676Ssam
2621198429Srpaulo	/* If the command was mapped in an mbuf, free it. */
2622178676Ssam	if (data->m != NULL) {
2623220704Sbschmidt		bus_dmamap_sync(ring->data_dmat, data->map,
2624220704Sbschmidt		    BUS_DMASYNC_POSTWRITE);
2625201209Srpaulo		bus_dmamap_unload(ring->data_dmat, data->map);
2626178676Ssam		m_freem(data->m);
2627178676Ssam		data->m = NULL;
2628178676Ssam	}
2629198439Srpaulo	wakeup(&ring->desc[desc->idx]);
2630178676Ssam}
2631178676Ssam
2632198429Srpaulo/*
2633198429Srpaulo * Process an INT_FH_RX or INT_SW_RX interrupt.
2634198429Srpaulo */
2635206477Sbschmidtstatic void
2636178676Ssamiwn_notif_intr(struct iwn_softc *sc)
2637178676Ssam{
2638220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
2639178676Ssam	struct ifnet *ifp = sc->sc_ifp;
2640178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
2641178676Ssam	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
2642178676Ssam	uint16_t hw;
2643178676Ssam
2644198429Srpaulo	bus_dmamap_sync(sc->rxq.stat_dma.tag, sc->rxq.stat_dma.map,
2645198429Srpaulo	    BUS_DMASYNC_POSTREAD);
2646198429Srpaulo
2647198429Srpaulo	hw = le16toh(sc->rxq.stat->closed_count) & 0xfff;
2648178676Ssam	while (sc->rxq.cur != hw) {
2649178676Ssam		struct iwn_rx_data *data = &sc->rxq.data[sc->rxq.cur];
2650198429Srpaulo		struct iwn_rx_desc *desc;
2651178676Ssam
2652201209Srpaulo		bus_dmamap_sync(sc->rxq.data_dmat, data->map,
2653201209Srpaulo		    BUS_DMASYNC_POSTREAD);
2654198429Srpaulo		desc = mtod(data->m, struct iwn_rx_desc *);
2655198429Srpaulo
2656178676Ssam		DPRINTF(sc, IWN_DEBUG_RECV,
2657178676Ssam		    "%s: qid %x idx %d flags %x type %d(%s) len %d\n",
2658198439Srpaulo		    __func__, desc->qid & 0xf, desc->idx, desc->flags,
2659178676Ssam		    desc->type, iwn_intr_str(desc->type),
2660178676Ssam		    le16toh(desc->len));
2661178676Ssam
2662198429Srpaulo		if (!(desc->qid & 0x80))	/* Reply to a command. */
2663198429Srpaulo			iwn_cmd_done(sc, desc);
2664178676Ssam
2665178676Ssam		switch (desc->type) {
2666198429Srpaulo		case IWN_RX_PHY:
2667198429Srpaulo			iwn_rx_phy(sc, desc, data);
2668178676Ssam			break;
2669178676Ssam
2670198429Srpaulo		case IWN_RX_DONE:		/* 4965AGN only. */
2671198429Srpaulo		case IWN_MPDU_RX_DONE:
2672198429Srpaulo			/* An 802.11 frame has been received. */
2673198429Srpaulo			iwn_rx_done(sc, desc, data);
2674178676Ssam			break;
2675178676Ssam
2676201209Srpaulo#if 0	/* HT */
2677201209Srpaulo		case IWN_RX_COMPRESSED_BA:
2678201209Srpaulo			/* A Compressed BlockAck has been received. */
2679201209Srpaulo			iwn_rx_compressed_ba(sc, desc, data);
2680201209Srpaulo			break;
2681201209Srpaulo#endif
2682201209Srpaulo
2683178676Ssam		case IWN_TX_DONE:
2684198429Srpaulo			/* An 802.11 frame has been transmitted. */
2685220728Sbschmidt			ops->tx_done(sc, desc, data);
2686178676Ssam			break;
2687178676Ssam
2688178676Ssam		case IWN_RX_STATISTICS:
2689178676Ssam		case IWN_BEACON_STATISTICS:
2690198429Srpaulo			iwn_rx_statistics(sc, desc, data);
2691178676Ssam			break;
2692178676Ssam
2693198429Srpaulo		case IWN_BEACON_MISSED:
2694198429Srpaulo		{
2695178676Ssam			struct iwn_beacon_missed *miss =
2696178676Ssam			    (struct iwn_beacon_missed *)(desc + 1);
2697202986Srpaulo			int misses;
2698178676Ssam
2699201209Srpaulo			bus_dmamap_sync(sc->rxq.data_dmat, data->map,
2700201209Srpaulo			    BUS_DMASYNC_POSTREAD);
2701202986Srpaulo			misses = le32toh(miss->consecutive);
2702201209Srpaulo
2703178676Ssam			DPRINTF(sc, IWN_DEBUG_STATE,
2704178676Ssam			    "%s: beacons missed %d/%d\n", __func__,
2705178676Ssam			    misses, le32toh(miss->total));
2706178676Ssam			/*
2707178676Ssam			 * If more than 5 consecutive beacons are missed,
2708178676Ssam			 * reinitialize the sensitivity state machine.
2709178676Ssam			 */
2710220660Sbschmidt			if (vap->iv_state == IEEE80211_S_RUN &&
2711220660Sbschmidt			    (ic->ic_flags & IEEE80211_F_SCAN) != 0) {
2712220660Sbschmidt				if (misses > 5)
2713220660Sbschmidt					(void)iwn_init_sensitivity(sc);
2714220660Sbschmidt				if (misses >= vap->iv_bmissthreshold) {
2715220660Sbschmidt					IWN_UNLOCK(sc);
2716220660Sbschmidt					ieee80211_beacon_miss(ic);
2717220660Sbschmidt					IWN_LOCK(sc);
2718220660Sbschmidt				}
2719201209Srpaulo			}
2720178676Ssam			break;
2721178676Ssam		}
2722198429Srpaulo		case IWN_UC_READY:
2723198429Srpaulo		{
2724178676Ssam			struct iwn_ucode_info *uc =
2725178676Ssam			    (struct iwn_ucode_info *)(desc + 1);
2726178676Ssam
2727198429Srpaulo			/* The microcontroller is ready. */
2728201209Srpaulo			bus_dmamap_sync(sc->rxq.data_dmat, data->map,
2729201209Srpaulo			    BUS_DMASYNC_POSTREAD);
2730178676Ssam			DPRINTF(sc, IWN_DEBUG_RESET,
2731178676Ssam			    "microcode alive notification version=%d.%d "
2732178676Ssam			    "subtype=%x alive=%x\n", uc->major, uc->minor,
2733178676Ssam			    uc->subtype, le32toh(uc->valid));
2734178676Ssam
2735178676Ssam			if (le32toh(uc->valid) != 1) {
2736178676Ssam				device_printf(sc->sc_dev,
2737198429Srpaulo				    "microcontroller initialization failed");
2738178676Ssam				break;
2739178676Ssam			}
2740178676Ssam			if (uc->subtype == IWN_UCODE_INIT) {
2741201209Srpaulo				/* Save microcontroller report. */
2742178676Ssam				memcpy(&sc->ucode_info, uc, sizeof (*uc));
2743178676Ssam			}
2744198429Srpaulo			/* Save the address of the error log in SRAM. */
2745198429Srpaulo			sc->errptr = le32toh(uc->errptr);
2746178676Ssam			break;
2747178676Ssam		}
2748198429Srpaulo		case IWN_STATE_CHANGED:
2749198429Srpaulo		{
2750178676Ssam			uint32_t *status = (uint32_t *)(desc + 1);
2751178676Ssam
2752178676Ssam			/*
2753178676Ssam			 * State change allows hardware switch change to be
2754178676Ssam			 * noted. However, we handle this in iwn_intr as we
2755178676Ssam			 * get both the enable/disble intr.
2756178676Ssam			 */
2757201209Srpaulo			bus_dmamap_sync(sc->rxq.data_dmat, data->map,
2758201209Srpaulo			    BUS_DMASYNC_POSTREAD);
2759178676Ssam			DPRINTF(sc, IWN_DEBUG_INTR, "state changed to %x\n",
2760178676Ssam			    le32toh(*status));
2761178676Ssam			break;
2762178676Ssam		}
2763198429Srpaulo		case IWN_START_SCAN:
2764198429Srpaulo		{
2765178676Ssam			struct iwn_start_scan *scan =
2766178676Ssam			    (struct iwn_start_scan *)(desc + 1);
2767178676Ssam
2768201209Srpaulo			bus_dmamap_sync(sc->rxq.data_dmat, data->map,
2769201209Srpaulo			    BUS_DMASYNC_POSTREAD);
2770178676Ssam			DPRINTF(sc, IWN_DEBUG_ANY,
2771178676Ssam			    "%s: scanning channel %d status %x\n",
2772178676Ssam			    __func__, scan->chan, le32toh(scan->status));
2773178676Ssam			break;
2774178676Ssam		}
2775198429Srpaulo		case IWN_STOP_SCAN:
2776198429Srpaulo		{
2777178676Ssam			struct iwn_stop_scan *scan =
2778178676Ssam			    (struct iwn_stop_scan *)(desc + 1);
2779178676Ssam
2780201209Srpaulo			bus_dmamap_sync(sc->rxq.data_dmat, data->map,
2781201209Srpaulo			    BUS_DMASYNC_POSTREAD);
2782178676Ssam			DPRINTF(sc, IWN_DEBUG_STATE,
2783178676Ssam			    "scan finished nchan=%d status=%d chan=%d\n",
2784178676Ssam			    scan->nchan, scan->status, scan->chan);
2785178676Ssam
2786201209Srpaulo			IWN_UNLOCK(sc);
2787191746Sthompsa			ieee80211_scan_next(vap);
2788201209Srpaulo			IWN_LOCK(sc);
2789178676Ssam			break;
2790178676Ssam		}
2791198429Srpaulo		case IWN5000_CALIBRATION_RESULT:
2792220674Sbschmidt			iwn5000_rx_calib_results(sc, desc, data);
2793198429Srpaulo			break;
2794198429Srpaulo
2795198429Srpaulo		case IWN5000_CALIBRATION_DONE:
2796201209Srpaulo			sc->sc_flags |= IWN_FLAG_CALIB_DONE;
2797198429Srpaulo			wakeup(sc);
2798198429Srpaulo			break;
2799178676Ssam		}
2800198429Srpaulo
2801178676Ssam		sc->rxq.cur = (sc->rxq.cur + 1) % IWN_RX_RING_COUNT;
2802178676Ssam	}
2803178676Ssam
2804198429Srpaulo	/* Tell the firmware what we have processed. */
2805178676Ssam	hw = (hw == 0) ? IWN_RX_RING_COUNT - 1 : hw - 1;
2806198429Srpaulo	IWN_WRITE(sc, IWN_FH_RX_WPTR, hw & ~7);
2807178676Ssam}
2808178676Ssam
2809198429Srpaulo/*
2810198429Srpaulo * Process an INT_WAKEUP interrupt raised when the microcontroller wakes up
2811198429Srpaulo * from power-down sleep mode.
2812198429Srpaulo */
2813206477Sbschmidtstatic void
2814198429Srpauloiwn_wakeup_intr(struct iwn_softc *sc)
2815198429Srpaulo{
2816198429Srpaulo	int qid;
2817198429Srpaulo
2818198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "%s: ucode wakeup from power-down sleep\n",
2819198429Srpaulo	    __func__);
2820198429Srpaulo
2821198429Srpaulo	/* Wakeup RX and TX rings. */
2822198429Srpaulo	IWN_WRITE(sc, IWN_FH_RX_WPTR, sc->rxq.cur & ~7);
2823220728Sbschmidt	for (qid = 0; qid < sc->ntxqs; qid++) {
2824198429Srpaulo		struct iwn_tx_ring *ring = &sc->txq[qid];
2825198429Srpaulo		IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | ring->cur);
2826198429Srpaulo	}
2827198429Srpaulo}
2828198429Srpaulo
2829206477Sbschmidtstatic void
2830191746Sthompsaiwn_rftoggle_intr(struct iwn_softc *sc)
2831191746Sthompsa{
2832191746Sthompsa	struct ifnet *ifp = sc->sc_ifp;
2833191746Sthompsa	struct ieee80211com *ic = ifp->if_l2com;
2834198429Srpaulo	uint32_t tmp = IWN_READ(sc, IWN_GP_CNTRL);
2835191746Sthompsa
2836191746Sthompsa	IWN_LOCK_ASSERT(sc);
2837191746Sthompsa
2838191746Sthompsa	device_printf(sc->sc_dev, "RF switch: radio %s\n",
2839198429Srpaulo	    (tmp & IWN_GP_CNTRL_RFKILL) ? "enabled" : "disabled");
2840198429Srpaulo	if (tmp & IWN_GP_CNTRL_RFKILL)
2841191746Sthompsa		ieee80211_runtask(ic, &sc->sc_radioon_task);
2842191746Sthompsa	else
2843191746Sthompsa		ieee80211_runtask(ic, &sc->sc_radiooff_task);
2844191746Sthompsa}
2845191746Sthompsa
2846198429Srpaulo/*
2847198429Srpaulo * Dump the error log of the firmware when a firmware panic occurs.  Although
2848198429Srpaulo * we can't debug the firmware because it is neither open source nor free, it
2849198429Srpaulo * can help us to identify certain classes of problems.
2850198429Srpaulo */
2851206477Sbschmidtstatic void
2852201209Srpauloiwn_fatal_intr(struct iwn_softc *sc)
2853191746Sthompsa{
2854198429Srpaulo	struct iwn_fw_dump dump;
2855198429Srpaulo	int i;
2856191746Sthompsa
2857191746Sthompsa	IWN_LOCK_ASSERT(sc);
2858191746Sthompsa
2859201209Srpaulo	/* Force a complete recalibration on next init. */
2860201209Srpaulo	sc->sc_flags &= ~IWN_FLAG_CALIB_DONE;
2861201209Srpaulo
2862198429Srpaulo	/* Check that the error log address is valid. */
2863198429Srpaulo	if (sc->errptr < IWN_FW_DATA_BASE ||
2864198429Srpaulo	    sc->errptr + sizeof (dump) >
2865220728Sbschmidt	    IWN_FW_DATA_BASE + sc->fw_data_maxsz) {
2866220726Sbschmidt		printf("%s: bad firmware error log address 0x%08x\n", __func__,
2867220726Sbschmidt		    sc->errptr);
2868198429Srpaulo		return;
2869198429Srpaulo	}
2870198429Srpaulo	if (iwn_nic_lock(sc) != 0) {
2871220726Sbschmidt		printf("%s: could not read firmware error log\n", __func__);
2872198429Srpaulo		return;
2873198429Srpaulo	}
2874198429Srpaulo	/* Read firmware error log from SRAM. */
2875198429Srpaulo	iwn_mem_read_region_4(sc, sc->errptr, (uint32_t *)&dump,
2876198429Srpaulo	    sizeof (dump) / sizeof (uint32_t));
2877198429Srpaulo	iwn_nic_unlock(sc);
2878198429Srpaulo
2879198429Srpaulo	if (dump.valid == 0) {
2880220726Sbschmidt		printf("%s: firmware error log is empty\n", __func__);
2881198429Srpaulo		return;
2882198429Srpaulo	}
2883198429Srpaulo	printf("firmware error log:\n");
2884198429Srpaulo	printf("  error type      = \"%s\" (0x%08X)\n",
2885198429Srpaulo	    (dump.id < nitems(iwn_fw_errmsg)) ?
2886198429Srpaulo		iwn_fw_errmsg[dump.id] : "UNKNOWN",
2887198429Srpaulo	    dump.id);
2888198429Srpaulo	printf("  program counter = 0x%08X\n", dump.pc);
2889198429Srpaulo	printf("  source line     = 0x%08X\n", dump.src_line);
2890198429Srpaulo	printf("  error data      = 0x%08X%08X\n",
2891198429Srpaulo	    dump.error_data[0], dump.error_data[1]);
2892198429Srpaulo	printf("  branch link     = 0x%08X%08X\n",
2893198429Srpaulo	    dump.branch_link[0], dump.branch_link[1]);
2894198429Srpaulo	printf("  interrupt link  = 0x%08X%08X\n",
2895198429Srpaulo	    dump.interrupt_link[0], dump.interrupt_link[1]);
2896198429Srpaulo	printf("  time            = %u\n", dump.time[0]);
2897198429Srpaulo
2898198429Srpaulo	/* Dump driver status (TX and RX rings) while we're here. */
2899198429Srpaulo	printf("driver status:\n");
2900220728Sbschmidt	for (i = 0; i < sc->ntxqs; i++) {
2901198429Srpaulo		struct iwn_tx_ring *ring = &sc->txq[i];
2902198429Srpaulo		printf("  tx ring %2d: qid=%-2d cur=%-3d queued=%-3d\n",
2903198429Srpaulo		    i, ring->qid, ring->cur, ring->queued);
2904198429Srpaulo	}
2905198429Srpaulo	printf("  rx ring: cur=%d\n", sc->rxq.cur);
2906191746Sthompsa}
2907191746Sthompsa
2908206477Sbschmidtstatic void
2909178676Ssamiwn_intr(void *arg)
2910178676Ssam{
2911178676Ssam	struct iwn_softc *sc = arg;
2912198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
2913201209Srpaulo	uint32_t r1, r2, tmp;
2914178676Ssam
2915178676Ssam	IWN_LOCK(sc);
2916178676Ssam
2917198429Srpaulo	/* Disable interrupts. */
2918201209Srpaulo	IWN_WRITE(sc, IWN_INT_MASK, 0);
2919178676Ssam
2920201209Srpaulo	/* Read interrupts from ICT (fast) or from registers (slow). */
2921201209Srpaulo	if (sc->sc_flags & IWN_FLAG_USE_ICT) {
2922201209Srpaulo		tmp = 0;
2923201209Srpaulo		while (sc->ict[sc->ict_cur] != 0) {
2924201209Srpaulo			tmp |= sc->ict[sc->ict_cur];
2925201209Srpaulo			sc->ict[sc->ict_cur] = 0;	/* Acknowledge. */
2926201209Srpaulo			sc->ict_cur = (sc->ict_cur + 1) % IWN_ICT_COUNT;
2927201209Srpaulo		}
2928201209Srpaulo		tmp = le32toh(tmp);
2929206444Sbschmidt		if (tmp == 0xffffffff)	/* Shouldn't happen. */
2930206444Sbschmidt			tmp = 0;
2931206444Sbschmidt		else if (tmp & 0xc0000)	/* Workaround a HW bug. */
2932206444Sbschmidt			tmp |= 0x8000;
2933201209Srpaulo		r1 = (tmp & 0xff00) << 16 | (tmp & 0xff);
2934201209Srpaulo		r2 = 0;	/* Unused. */
2935201209Srpaulo	} else {
2936201209Srpaulo		r1 = IWN_READ(sc, IWN_INT);
2937201209Srpaulo		if (r1 == 0xffffffff || (r1 & 0xfffffff0) == 0xa5a5a5a0)
2938201209Srpaulo			return;	/* Hardware gone! */
2939201209Srpaulo		r2 = IWN_READ(sc, IWN_FH_INT);
2940201209Srpaulo	}
2941178676Ssam
2942198429Srpaulo	DPRINTF(sc, IWN_DEBUG_INTR, "interrupt reg1=%x reg2=%x\n", r1, r2);
2943198429Srpaulo
2944201209Srpaulo	if (r1 == 0 && r2 == 0)
2945198429Srpaulo		goto done;	/* Interrupt not for us. */
2946178676Ssam
2947198429Srpaulo	/* Acknowledge interrupts. */
2948198429Srpaulo	IWN_WRITE(sc, IWN_INT, r1);
2949201209Srpaulo	if (!(sc->sc_flags & IWN_FLAG_USE_ICT))
2950201209Srpaulo		IWN_WRITE(sc, IWN_FH_INT, r2);
2951178676Ssam
2952198429Srpaulo	if (r1 & IWN_INT_RF_TOGGLED) {
2953191746Sthompsa		iwn_rftoggle_intr(sc);
2954201209Srpaulo		goto done;
2955198429Srpaulo	}
2956198429Srpaulo	if (r1 & IWN_INT_CT_REACHED) {
2957198429Srpaulo		device_printf(sc->sc_dev, "%s: critical temperature reached!\n",
2958198429Srpaulo		    __func__);
2959198429Srpaulo	}
2960198429Srpaulo	if (r1 & (IWN_INT_SW_ERR | IWN_INT_HW_ERR)) {
2961220724Sbschmidt		device_printf(sc->sc_dev, "%s: fatal firmware error\n",
2962220724Sbschmidt		    __func__);
2963220725Sbschmidt		/* Dump firmware error log and stop. */
2964201209Srpaulo		iwn_fatal_intr(sc);
2965201209Srpaulo		ifp->if_flags &= ~IFF_UP;
2966201209Srpaulo		iwn_stop_locked(sc);
2967178676Ssam		goto done;
2968178676Ssam	}
2969201209Srpaulo	if ((r1 & (IWN_INT_FH_RX | IWN_INT_SW_RX | IWN_INT_RX_PERIODIC)) ||
2970201209Srpaulo	    (r2 & IWN_FH_INT_RX)) {
2971201209Srpaulo		if (sc->sc_flags & IWN_FLAG_USE_ICT) {
2972201209Srpaulo			if (r1 & (IWN_INT_FH_RX | IWN_INT_SW_RX))
2973201209Srpaulo				IWN_WRITE(sc, IWN_FH_INT, IWN_FH_INT_RX);
2974201209Srpaulo			IWN_WRITE_1(sc, IWN_INT_PERIODIC,
2975201209Srpaulo			    IWN_INT_PERIODIC_DIS);
2976201209Srpaulo			iwn_notif_intr(sc);
2977201209Srpaulo			if (r1 & (IWN_INT_FH_RX | IWN_INT_SW_RX)) {
2978201209Srpaulo				IWN_WRITE_1(sc, IWN_INT_PERIODIC,
2979201209Srpaulo				    IWN_INT_PERIODIC_ENA);
2980201209Srpaulo			}
2981201209Srpaulo		} else
2982201209Srpaulo			iwn_notif_intr(sc);
2983201209Srpaulo	}
2984178676Ssam
2985201209Srpaulo	if ((r1 & IWN_INT_FH_TX) || (r2 & IWN_FH_INT_TX)) {
2986201209Srpaulo		if (sc->sc_flags & IWN_FLAG_USE_ICT)
2987201209Srpaulo			IWN_WRITE(sc, IWN_FH_INT, IWN_FH_INT_TX);
2988198429Srpaulo		wakeup(sc);	/* FH DMA transfer completed. */
2989201209Srpaulo	}
2990198429Srpaulo
2991198429Srpaulo	if (r1 & IWN_INT_ALIVE)
2992198429Srpaulo		wakeup(sc);	/* Firmware is alive. */
2993198429Srpaulo
2994198429Srpaulo	if (r1 & IWN_INT_WAKEUP)
2995198429Srpaulo		iwn_wakeup_intr(sc);
2996198429Srpaulo
2997201209Srpaulodone:
2998198429Srpaulo	/* Re-enable interrupts. */
2999201209Srpaulo	if (ifp->if_flags & IFF_UP)
3000201209Srpaulo		IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask);
3001198429Srpaulo
3002178676Ssam	IWN_UNLOCK(sc);
3003178676Ssam}
3004178676Ssam
3005198429Srpaulo/*
3006198429Srpaulo * Update TX scheduler ring when transmitting an 802.11 frame (4965AGN and
3007220725Sbschmidt * 5000 adapters use a slightly different format).
3008198429Srpaulo */
3009206477Sbschmidtstatic void
3010198429Srpauloiwn4965_update_sched(struct iwn_softc *sc, int qid, int idx, uint8_t id,
3011198429Srpaulo    uint16_t len)
3012178676Ssam{
3013198429Srpaulo	uint16_t *w = &sc->sched[qid * IWN4965_SCHED_COUNT + idx];
3014178676Ssam
3015198429Srpaulo	*w = htole16(len + 8);
3016198439Srpaulo	bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map,
3017198439Srpaulo	    BUS_DMASYNC_PREWRITE);
3018201209Srpaulo	if (idx < IWN_SCHED_WINSZ) {
3019198429Srpaulo		*(w + IWN_TX_RING_COUNT) = *w;
3020198439Srpaulo		bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map,
3021198439Srpaulo		    BUS_DMASYNC_PREWRITE);
3022198439Srpaulo	}
3023198429Srpaulo}
3024198429Srpaulo
3025206477Sbschmidtstatic void
3026198429Srpauloiwn5000_update_sched(struct iwn_softc *sc, int qid, int idx, uint8_t id,
3027198429Srpaulo    uint16_t len)
3028198429Srpaulo{
3029198429Srpaulo	uint16_t *w = &sc->sched[qid * IWN5000_SCHED_COUNT + idx];
3030198429Srpaulo
3031198429Srpaulo	*w = htole16(id << 12 | (len + 8));
3032198439Srpaulo	bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map,
3033198439Srpaulo	    BUS_DMASYNC_PREWRITE);
3034198439Srpaulo	if (idx < IWN_SCHED_WINSZ) {
3035198429Srpaulo		*(w + IWN_TX_RING_COUNT) = *w;
3036198439Srpaulo		bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map,
3037198439Srpaulo		    BUS_DMASYNC_PREWRITE);
3038198439Srpaulo	}
3039198429Srpaulo}
3040198429Srpaulo
3041206475Sbschmidt#ifdef notyet
3042206477Sbschmidtstatic void
3043198429Srpauloiwn5000_reset_sched(struct iwn_softc *sc, int qid, int idx)
3044198429Srpaulo{
3045198429Srpaulo	uint16_t *w = &sc->sched[qid * IWN5000_SCHED_COUNT + idx];
3046198429Srpaulo
3047198429Srpaulo	*w = (*w & htole16(0xf000)) | htole16(1);
3048198439Srpaulo	bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map,
3049198439Srpaulo	    BUS_DMASYNC_PREWRITE);
3050198439Srpaulo	if (idx < IWN_SCHED_WINSZ) {
3051198429Srpaulo		*(w + IWN_TX_RING_COUNT) = *w;
3052198439Srpaulo		bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map,
3053206443Sbschmidt		    BUS_DMASYNC_PREWRITE);
3054198439Srpaulo	}
3055198429Srpaulo}
3056206475Sbschmidt#endif
3057198429Srpaulo
3058201209Srpaulostatic uint8_t
3059198429Srpauloiwn_plcp_signal(int rate) {
3060198429Srpaulo	int i;
3061198429Srpaulo
3062198429Srpaulo	for (i = 0; i < IWN_RIDX_MAX + 1; i++) {
3063220715Sbschmidt		if ((rate & IEEE80211_RATE_VAL) == iwn_rates[i].rate)
3064201209Srpaulo			return i;
3065178676Ssam	}
3066198429Srpaulo
3067201209Srpaulo	return 0;
3068178676Ssam}
3069178676Ssam
3070206477Sbschmidtstatic int
3071220720Sbschmidtiwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
3072178676Ssam{
3073198429Srpaulo	const struct ieee80211_txparam *tp;
3074178676Ssam	struct ieee80211vap *vap = ni->ni_vap;
3075178676Ssam	struct ieee80211com *ic = ni->ni_ic;
3076198429Srpaulo	struct iwn_node *wn = (void *)ni;
3077220720Sbschmidt	struct iwn_tx_ring *ring;
3078178676Ssam	struct iwn_tx_desc *desc;
3079178676Ssam	struct iwn_tx_data *data;
3080178676Ssam	struct iwn_tx_cmd *cmd;
3081178676Ssam	struct iwn_cmd_data *tx;
3082220723Sbschmidt	const struct iwn_rate *rinfo;
3083178676Ssam	struct ieee80211_frame *wh;
3084198429Srpaulo	struct ieee80211_key *k = NULL;
3085220700Sbschmidt	struct mbuf *m1;
3086178676Ssam	uint32_t flags;
3087220720Sbschmidt	uint16_t qos;
3088178676Ssam	u_int hdrlen;
3089220723Sbschmidt	bus_dma_segment_t *seg, segs[IWN_MAX_SCATTER];
3090220723Sbschmidt	uint8_t tid, ridx, txant, type;
3091220720Sbschmidt	int ac, i, totlen, error, pad, nsegs = 0, rate;
3092178676Ssam
3093178676Ssam	IWN_LOCK_ASSERT(sc);
3094178676Ssam
3095198429Srpaulo	wh = mtod(m, struct ieee80211_frame *);
3096198429Srpaulo	hdrlen = ieee80211_anyhdrsize(wh);
3097178676Ssam	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
3098178676Ssam
3099220720Sbschmidt	/* Select EDCA Access Category and TX ring for this frame. */
3100220720Sbschmidt	if (IEEE80211_QOS_HAS_SEQ(wh)) {
3101220720Sbschmidt		qos = ((const struct ieee80211_qosframe *)wh)->i_qos[0];
3102220720Sbschmidt		tid = qos & IEEE80211_QOS_TID;
3103220720Sbschmidt	} else {
3104220720Sbschmidt		qos = 0;
3105220720Sbschmidt		tid = 0;
3106220720Sbschmidt	}
3107220720Sbschmidt	ac = M_WME_GETAC(m);
3108220720Sbschmidt
3109220720Sbschmidt	ring = &sc->txq[ac];
3110198429Srpaulo	desc = &ring->desc[ring->cur];
3111198429Srpaulo	data = &ring->data[ring->cur];
3112198429Srpaulo
3113198429Srpaulo	/* Choose a TX rate index. */
3114201209Srpaulo	tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)];
3115178676Ssam	if (type == IEEE80211_FC0_TYPE_MGT)
3116178676Ssam		rate = tp->mgmtrate;
3117198429Srpaulo	else if (IEEE80211_IS_MULTICAST(wh->i_addr1))
3118178676Ssam		rate = tp->mcastrate;
3119178676Ssam	else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE)
3120178676Ssam		rate = tp->ucastrate;
3121178676Ssam	else {
3122206358Srpaulo		/* XXX pass pktlen */
3123206358Srpaulo		(void) ieee80211_ratectl_rate(ni, NULL, 0);
3124178676Ssam		rate = ni->ni_txrate;
3125178676Ssam	}
3126201209Srpaulo	ridx = iwn_plcp_signal(rate);
3127201209Srpaulo	rinfo = &iwn_rates[ridx];
3128178676Ssam
3129198429Srpaulo	/* Encrypt the frame if need be. */
3130178676Ssam	if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
3131220725Sbschmidt		/* Retrieve key for TX. */
3132198429Srpaulo		k = ieee80211_crypto_encap(ni, m);
3133178676Ssam		if (k == NULL) {
3134198429Srpaulo			m_freem(m);
3135178676Ssam			return ENOBUFS;
3136178676Ssam		}
3137220725Sbschmidt		/* 802.11 header may have moved. */
3138198429Srpaulo		wh = mtod(m, struct ieee80211_frame *);
3139198429Srpaulo	}
3140198429Srpaulo	totlen = m->m_pkthdr.len;
3141178676Ssam
3142192468Ssam	if (ieee80211_radiotap_active_vap(vap)) {
3143178676Ssam		struct iwn_tx_radiotap_header *tap = &sc->sc_txtap;
3144178676Ssam
3145178676Ssam		tap->wt_flags = 0;
3146201209Srpaulo		tap->wt_rate = rinfo->rate;
3147178676Ssam		if (k != NULL)
3148178676Ssam			tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP;
3149178676Ssam
3150198429Srpaulo		ieee80211_radiotap_tx(vap, m);
3151178676Ssam	}
3152178676Ssam
3153198429Srpaulo	/* Prepare TX firmware command. */
3154198429Srpaulo	cmd = &ring->cmd[ring->cur];
3155198429Srpaulo	cmd->code = IWN_CMD_TX_DATA;
3156198429Srpaulo	cmd->flags = 0;
3157198429Srpaulo	cmd->qid = ring->qid;
3158198429Srpaulo	cmd->idx = ring->cur;
3159198429Srpaulo
3160198429Srpaulo	tx = (struct iwn_cmd_data *)cmd->data;
3161198429Srpaulo	/* NB: No need to clear tx, all fields are reinitialized here. */
3162198429Srpaulo	tx->scratch = 0;	/* clear "scratch" area */
3163198429Srpaulo
3164198429Srpaulo	flags = 0;
3165220720Sbschmidt	if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
3166220720Sbschmidt		/* Unicast frame, check if an ACK is expected. */
3167220720Sbschmidt		if (!qos || (qos & IEEE80211_QOS_ACKPOLICY) !=
3168220720Sbschmidt		    IEEE80211_QOS_ACKPOLICY_NOACK)
3169220720Sbschmidt			flags |= IWN_TX_NEED_ACK;
3170220720Sbschmidt	}
3171198429Srpaulo	if ((wh->i_fc[0] &
3172198429Srpaulo	    (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) ==
3173198429Srpaulo	    (IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_BAR))
3174198429Srpaulo		flags |= IWN_TX_IMM_BA;		/* Cannot happen yet. */
3175178676Ssam
3176198429Srpaulo	if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG)
3177198429Srpaulo		flags |= IWN_TX_MORE_FRAG;	/* Cannot happen yet. */
3178178676Ssam
3179198429Srpaulo	/* Check if frame must be protected using RTS/CTS or CTS-to-self. */
3180198429Srpaulo	if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
3181198429Srpaulo		/* NB: Group frames are sent using CCK in 802.11b/g. */
3182198429Srpaulo		if (totlen + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) {
3183198429Srpaulo			flags |= IWN_TX_NEED_RTS;
3184178676Ssam		} else if ((ic->ic_flags & IEEE80211_F_USEPROT) &&
3185201209Srpaulo		    ridx >= IWN_RIDX_OFDM6) {
3186178676Ssam			if (ic->ic_protmode == IEEE80211_PROT_CTSONLY)
3187198429Srpaulo				flags |= IWN_TX_NEED_CTS;
3188178676Ssam			else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS)
3189198429Srpaulo				flags |= IWN_TX_NEED_RTS;
3190178676Ssam		}
3191198429Srpaulo		if (flags & (IWN_TX_NEED_RTS | IWN_TX_NEED_CTS)) {
3192198429Srpaulo			if (sc->hw_type != IWN_HW_REV_TYPE_4965) {
3193198429Srpaulo				/* 5000 autoselects RTS/CTS or CTS-to-self. */
3194198429Srpaulo				flags &= ~(IWN_TX_NEED_RTS | IWN_TX_NEED_CTS);
3195198429Srpaulo				flags |= IWN_TX_NEED_PROTECTION;
3196198429Srpaulo			} else
3197198429Srpaulo				flags |= IWN_TX_FULL_TXOP;
3198198429Srpaulo		}
3199201209Srpaulo	}
3200178676Ssam
3201198429Srpaulo	if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
3202198429Srpaulo	    type != IEEE80211_FC0_TYPE_DATA)
3203220728Sbschmidt		tx->id = sc->broadcast_id;
3204198429Srpaulo	else
3205198429Srpaulo		tx->id = wn->id;
3206198429Srpaulo
3207178676Ssam	if (type == IEEE80211_FC0_TYPE_MGT) {
3208178676Ssam		uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
3209178676Ssam
3210198429Srpaulo		/* Tell HW to set timestamp in probe responses. */
3211178676Ssam		if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
3212178676Ssam			flags |= IWN_TX_INSERT_TSTAMP;
3213178676Ssam		if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ ||
3214178676Ssam		    subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ)
3215198429Srpaulo			tx->timeout = htole16(3);
3216178676Ssam		else
3217198429Srpaulo			tx->timeout = htole16(2);
3218178676Ssam	} else
3219198429Srpaulo		tx->timeout = htole16(0);
3220178676Ssam
3221178676Ssam	if (hdrlen & 3) {
3222201209Srpaulo		/* First segment length must be a multiple of 4. */
3223178676Ssam		flags |= IWN_TX_NEED_PADDING;
3224178676Ssam		pad = 4 - (hdrlen & 3);
3225178676Ssam	} else
3226178676Ssam		pad = 0;
3227178676Ssam
3228198429Srpaulo	tx->len = htole16(totlen);
3229220720Sbschmidt	tx->tid = tid;
3230201209Srpaulo	tx->rts_ntries = 60;
3231201209Srpaulo	tx->data_ntries = 15;
3232178676Ssam	tx->lifetime = htole32(IWN_LIFETIME_INFINITE);
3233198429Srpaulo	tx->plcp = rinfo->plcp;
3234198429Srpaulo	tx->rflags = rinfo->flags;
3235220728Sbschmidt	if (tx->id == sc->broadcast_id) {
3236201209Srpaulo		/* Group or management frame. */
3237201209Srpaulo		tx->linkq = 0;
3238198429Srpaulo		/* XXX Alternate between antenna A and B? */
3239201209Srpaulo		txant = IWN_LSB(sc->txchainmask);
3240198429Srpaulo		tx->rflags |= IWN_RFLAG_ANT(txant);
3241201209Srpaulo	} else {
3242220715Sbschmidt		tx->linkq = ni->ni_rates.rs_nrates - ridx - 1;
3243201209Srpaulo		flags |= IWN_TX_LINKQ;	/* enable MRR */
3244201209Srpaulo	}
3245198429Srpaulo	/* Set physical address of "scratch area". */
3246201209Srpaulo	tx->loaddr = htole32(IWN_LOADDR(data->scratch_paddr));
3247201209Srpaulo	tx->hiaddr = IWN_HIADDR(data->scratch_paddr);
3248178676Ssam
3249198429Srpaulo	/* Copy 802.11 header in TX command. */
3250178676Ssam	memcpy((uint8_t *)(tx + 1), wh, hdrlen);
3251178676Ssam
3252198429Srpaulo	/* Trim 802.11 header. */
3253198429Srpaulo	m_adj(m, hdrlen);
3254198429Srpaulo	tx->security = 0;
3255198429Srpaulo	tx->flags = htole32(flags);
3256198429Srpaulo
3257220700Sbschmidt	error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m, segs,
3258220700Sbschmidt	    &nsegs, BUS_DMA_NOWAIT);
3259220700Sbschmidt	if (error != 0) {
3260220700Sbschmidt		if (error != EFBIG) {
3261220700Sbschmidt			device_printf(sc->sc_dev,
3262220700Sbschmidt			    "%s: can't map mbuf (error %d)\n", __func__, error);
3263220700Sbschmidt			m_freem(m);
3264220700Sbschmidt			return error;
3265178676Ssam		}
3266220700Sbschmidt		/* Too many DMA segments, linearize mbuf. */
3267220700Sbschmidt		m1 = m_collapse(m, M_DONTWAIT, IWN_MAX_SCATTER);
3268220700Sbschmidt		if (m1 == NULL) {
3269220700Sbschmidt			device_printf(sc->sc_dev,
3270220700Sbschmidt			    "%s: could not defrag mbuf\n", __func__);
3271220700Sbschmidt			m_freem(m);
3272220700Sbschmidt			return ENOBUFS;
3273220700Sbschmidt		}
3274220700Sbschmidt		m = m1;
3275220700Sbschmidt
3276220700Sbschmidt		error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m,
3277220700Sbschmidt		    segs, &nsegs, BUS_DMA_NOWAIT);
3278178676Ssam		if (error != 0) {
3279178676Ssam			device_printf(sc->sc_dev,
3280220700Sbschmidt			    "%s: can't map mbuf (error %d)\n", __func__, error);
3281198429Srpaulo			m_freem(m);
3282178676Ssam			return error;
3283178676Ssam		}
3284178676Ssam	}
3285178676Ssam
3286198429Srpaulo	data->m = m;
3287178676Ssam	data->ni = ni;
3288178676Ssam
3289178676Ssam	DPRINTF(sc, IWN_DEBUG_XMIT, "%s: qid %d idx %d len %d nsegs %d\n",
3290198429Srpaulo	    __func__, ring->qid, ring->cur, m->m_pkthdr.len, nsegs);
3291178676Ssam
3292198429Srpaulo	/* Fill TX descriptor. */
3293220700Sbschmidt	desc->nsegs = 1;
3294220700Sbschmidt	if (m->m_len != 0)
3295220700Sbschmidt		desc->nsegs += nsegs;
3296198429Srpaulo	/* First DMA segment is used by the TX command. */
3297201209Srpaulo	desc->segs[0].addr = htole32(IWN_LOADDR(data->cmd_paddr));
3298201209Srpaulo	desc->segs[0].len  = htole16(IWN_HIADDR(data->cmd_paddr) |
3299198429Srpaulo	    (4 + sizeof (*tx) + hdrlen + pad) << 4);
3300198429Srpaulo	/* Other DMA segments are for data payload. */
3301220700Sbschmidt	seg = &segs[0];
3302178676Ssam	for (i = 1; i <= nsegs; i++) {
3303220700Sbschmidt		desc->segs[i].addr = htole32(IWN_LOADDR(seg->ds_addr));
3304220700Sbschmidt		desc->segs[i].len  = htole16(IWN_HIADDR(seg->ds_addr) |
3305220700Sbschmidt		    seg->ds_len << 4);
3306220700Sbschmidt		seg++;
3307178676Ssam	}
3308178676Ssam
3309201209Srpaulo	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE);
3310201209Srpaulo	bus_dmamap_sync(ring->data_dmat, ring->cmd_dma.map,
3311198429Srpaulo	    BUS_DMASYNC_PREWRITE);
3312198429Srpaulo	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
3313198429Srpaulo	    BUS_DMASYNC_PREWRITE);
3314178676Ssam
3315201209Srpaulo#ifdef notyet
3316198429Srpaulo	/* Update TX scheduler. */
3317220728Sbschmidt	ops->update_sched(sc, ring->qid, ring->cur, tx->id, totlen);
3318201209Srpaulo#endif
3319178676Ssam
3320198429Srpaulo	/* Kick TX ring. */
3321178676Ssam	ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT;
3322198429Srpaulo	IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur);
3323178676Ssam
3324198429Srpaulo	/* Mark TX ring as full if we reach a certain threshold. */
3325198429Srpaulo	if (++ring->queued > IWN_TX_RING_HIMARK)
3326198429Srpaulo		sc->qfullmsk |= 1 << ring->qid;
3327178676Ssam
3328178676Ssam	return 0;
3329178676Ssam}
3330178676Ssam
3331178676Ssamstatic int
3332201209Srpauloiwn_tx_data_raw(struct iwn_softc *sc, struct mbuf *m,
3333220720Sbschmidt    struct ieee80211_node *ni, const struct ieee80211_bpf_params *params)
3334178676Ssam{
3335198429Srpaulo	const struct iwn_rate *rinfo;
3336178676Ssam	struct ifnet *ifp = sc->sc_ifp;
3337198429Srpaulo	struct ieee80211vap *vap = ni->ni_vap;
3338198429Srpaulo	struct ieee80211com *ic = ifp->if_l2com;
3339198429Srpaulo	struct iwn_tx_cmd *cmd;
3340198429Srpaulo	struct iwn_cmd_data *tx;
3341198429Srpaulo	struct ieee80211_frame *wh;
3342220720Sbschmidt	struct iwn_tx_ring *ring;
3343178676Ssam	struct iwn_tx_desc *desc;
3344178676Ssam	struct iwn_tx_data *data;
3345220700Sbschmidt	struct mbuf *m1;
3346220700Sbschmidt	bus_dma_segment_t *seg, segs[IWN_MAX_SCATTER];
3347198429Srpaulo	uint32_t flags;
3348198429Srpaulo	u_int hdrlen;
3349220720Sbschmidt	int ac, totlen, error, pad, nsegs = 0, i, rate;
3350201209Srpaulo	uint8_t ridx, type, txant;
3351178676Ssam
3352198429Srpaulo	IWN_LOCK_ASSERT(sc);
3353178676Ssam
3354201209Srpaulo	wh = mtod(m, struct ieee80211_frame *);
3355198429Srpaulo	hdrlen = ieee80211_anyhdrsize(wh);
3356198429Srpaulo	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
3357198429Srpaulo
3358220720Sbschmidt	ac = params->ibp_pri & 3;
3359220720Sbschmidt
3360220720Sbschmidt	ring = &sc->txq[ac];
3361178676Ssam	desc = &ring->desc[ring->cur];
3362178676Ssam	data = &ring->data[ring->cur];
3363178676Ssam
3364198429Srpaulo	/* Choose a TX rate index. */
3365198429Srpaulo	rate = params->ibp_rate0;
3366198429Srpaulo	if (!ieee80211_isratevalid(ic->ic_rt, rate)) {
3367198429Srpaulo		/* XXX fall back to mcast/mgmt rate? */
3368201209Srpaulo		m_freem(m);
3369198429Srpaulo		return EINVAL;
3370198429Srpaulo	}
3371201209Srpaulo	ridx = iwn_plcp_signal(rate);
3372201209Srpaulo	rinfo = &iwn_rates[ridx];
3373198429Srpaulo
3374201209Srpaulo	totlen = m->m_pkthdr.len;
3375198429Srpaulo
3376201209Srpaulo	/* Prepare TX firmware command. */
3377198429Srpaulo	cmd = &ring->cmd[ring->cur];
3378198429Srpaulo	cmd->code = IWN_CMD_TX_DATA;
3379198429Srpaulo	cmd->flags = 0;
3380198429Srpaulo	cmd->qid = ring->qid;
3381198429Srpaulo	cmd->idx = ring->cur;
3382198429Srpaulo
3383198429Srpaulo	tx = (struct iwn_cmd_data *)cmd->data;
3384201209Srpaulo	/* NB: No need to clear tx, all fields are reinitialized here. */
3385198429Srpaulo	tx->scratch = 0;	/* clear "scratch" area */
3386198429Srpaulo
3387198429Srpaulo	flags = 0;
3388198429Srpaulo	if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0)
3389198429Srpaulo		flags |= IWN_TX_NEED_ACK;
3390198429Srpaulo	if (params->ibp_flags & IEEE80211_BPF_RTS) {
3391198429Srpaulo		if (sc->hw_type != IWN_HW_REV_TYPE_4965) {
3392198429Srpaulo			/* 5000 autoselects RTS/CTS or CTS-to-self. */
3393198429Srpaulo			flags &= ~IWN_TX_NEED_RTS;
3394198429Srpaulo			flags |= IWN_TX_NEED_PROTECTION;
3395198429Srpaulo		} else
3396198429Srpaulo			flags |= IWN_TX_NEED_RTS | IWN_TX_FULL_TXOP;
3397198429Srpaulo	}
3398198429Srpaulo	if (params->ibp_flags & IEEE80211_BPF_CTS) {
3399198429Srpaulo		if (sc->hw_type != IWN_HW_REV_TYPE_4965) {
3400198429Srpaulo			/* 5000 autoselects RTS/CTS or CTS-to-self. */
3401198429Srpaulo			flags &= ~IWN_TX_NEED_CTS;
3402198429Srpaulo			flags |= IWN_TX_NEED_PROTECTION;
3403198429Srpaulo		} else
3404198429Srpaulo			flags |= IWN_TX_NEED_CTS | IWN_TX_FULL_TXOP;
3405198429Srpaulo	}
3406198429Srpaulo	if (type == IEEE80211_FC0_TYPE_MGT) {
3407198429Srpaulo		uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
3408198429Srpaulo
3409220725Sbschmidt		/* Tell HW to set timestamp in probe responses. */
3410198429Srpaulo		if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
3411198429Srpaulo			flags |= IWN_TX_INSERT_TSTAMP;
3412198429Srpaulo
3413198429Srpaulo		if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ ||
3414198429Srpaulo		    subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ)
3415198429Srpaulo			tx->timeout = htole16(3);
3416198429Srpaulo		else
3417198429Srpaulo			tx->timeout = htole16(2);
3418198429Srpaulo	} else
3419198429Srpaulo		tx->timeout = htole16(0);
3420198429Srpaulo
3421198429Srpaulo	if (hdrlen & 3) {
3422201209Srpaulo		/* First segment length must be a multiple of 4. */
3423198429Srpaulo		flags |= IWN_TX_NEED_PADDING;
3424198429Srpaulo		pad = 4 - (hdrlen & 3);
3425198429Srpaulo	} else
3426198429Srpaulo		pad = 0;
3427198429Srpaulo
3428198429Srpaulo	if (ieee80211_radiotap_active_vap(vap)) {
3429198429Srpaulo		struct iwn_tx_radiotap_header *tap = &sc->sc_txtap;
3430198429Srpaulo
3431198429Srpaulo		tap->wt_flags = 0;
3432198429Srpaulo		tap->wt_rate = rate;
3433198429Srpaulo
3434201209Srpaulo		ieee80211_radiotap_tx(vap, m);
3435198429Srpaulo	}
3436198429Srpaulo
3437198429Srpaulo	tx->len = htole16(totlen);
3438198429Srpaulo	tx->tid = 0;
3439220728Sbschmidt	tx->id = sc->broadcast_id;
3440198429Srpaulo	tx->rts_ntries = params->ibp_try1;
3441198429Srpaulo	tx->data_ntries = params->ibp_try0;
3442198429Srpaulo	tx->lifetime = htole32(IWN_LIFETIME_INFINITE);
3443198429Srpaulo	tx->plcp = rinfo->plcp;
3444198429Srpaulo	tx->rflags = rinfo->flags;
3445201209Srpaulo	/* Group or management frame. */
3446201209Srpaulo	tx->linkq = 0;
3447201209Srpaulo	txant = IWN_LSB(sc->txchainmask);
3448201209Srpaulo	tx->rflags |= IWN_RFLAG_ANT(txant);
3449198429Srpaulo	/* Set physical address of "scratch area". */
3450220694Sbschmidt	tx->loaddr = htole32(IWN_LOADDR(data->scratch_paddr));
3451220694Sbschmidt	tx->hiaddr = IWN_HIADDR(data->scratch_paddr);
3452198429Srpaulo
3453198429Srpaulo	/* Copy 802.11 header in TX command. */
3454198429Srpaulo	memcpy((uint8_t *)(tx + 1), wh, hdrlen);
3455198429Srpaulo
3456198429Srpaulo	/* Trim 802.11 header. */
3457201209Srpaulo	m_adj(m, hdrlen);
3458198429Srpaulo	tx->security = 0;
3459198429Srpaulo	tx->flags = htole32(flags);
3460198429Srpaulo
3461220700Sbschmidt	error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m, segs,
3462220700Sbschmidt	    &nsegs, BUS_DMA_NOWAIT);
3463220700Sbschmidt	if (error != 0) {
3464220700Sbschmidt		if (error != EFBIG) {
3465220700Sbschmidt			device_printf(sc->sc_dev,
3466220700Sbschmidt			    "%s: can't map mbuf (error %d)\n", __func__, error);
3467220700Sbschmidt			m_freem(m);
3468220700Sbschmidt			return error;
3469178676Ssam		}
3470220700Sbschmidt		/* Too many DMA segments, linearize mbuf. */
3471220700Sbschmidt		m1 = m_collapse(m, M_DONTWAIT, IWN_MAX_SCATTER);
3472220700Sbschmidt		if (m1 == NULL) {
3473220700Sbschmidt			device_printf(sc->sc_dev,
3474220700Sbschmidt			    "%s: could not defrag mbuf\n", __func__);
3475220700Sbschmidt			m_freem(m);
3476220700Sbschmidt			return ENOBUFS;
3477220700Sbschmidt		}
3478220700Sbschmidt		m = m1;
3479220700Sbschmidt
3480220700Sbschmidt		error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m,
3481220700Sbschmidt		    segs, &nsegs, BUS_DMA_NOWAIT);
3482178676Ssam		if (error != 0) {
3483178676Ssam			device_printf(sc->sc_dev,
3484220700Sbschmidt			    "%s: can't map mbuf (error %d)\n", __func__, error);
3485201209Srpaulo			m_freem(m);
3486178676Ssam			return error;
3487178676Ssam		}
3488178676Ssam	}
3489178676Ssam
3490201209Srpaulo	data->m = m;
3491178676Ssam	data->ni = ni;
3492178676Ssam
3493178676Ssam	DPRINTF(sc, IWN_DEBUG_XMIT, "%s: qid %d idx %d len %d nsegs %d\n",
3494201209Srpaulo	    __func__, ring->qid, ring->cur, m->m_pkthdr.len, nsegs);
3495178676Ssam
3496198429Srpaulo	/* Fill TX descriptor. */
3497220700Sbschmidt	desc->nsegs = 1;
3498220700Sbschmidt	if (m->m_len != 0)
3499220700Sbschmidt		desc->nsegs += nsegs;
3500198429Srpaulo	/* First DMA segment is used by the TX command. */
3501201209Srpaulo	desc->segs[0].addr = htole32(IWN_LOADDR(data->cmd_paddr));
3502201209Srpaulo	desc->segs[0].len  = htole16(IWN_HIADDR(data->cmd_paddr) |
3503198429Srpaulo	    (4 + sizeof (*tx) + hdrlen + pad) << 4);
3504198429Srpaulo	/* Other DMA segments are for data payload. */
3505220700Sbschmidt	seg = &segs[0];
3506178676Ssam	for (i = 1; i <= nsegs; i++) {
3507220700Sbschmidt		desc->segs[i].addr = htole32(IWN_LOADDR(seg->ds_addr));
3508220700Sbschmidt		desc->segs[i].len  = htole16(IWN_HIADDR(seg->ds_addr) |
3509220700Sbschmidt		    seg->ds_len << 4);
3510220700Sbschmidt		seg++;
3511178676Ssam	}
3512178676Ssam
3513201209Srpaulo	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE);
3514201209Srpaulo	bus_dmamap_sync(ring->data_dmat, ring->cmd_dma.map,
3515201209Srpaulo	    BUS_DMASYNC_PREWRITE);
3516201209Srpaulo	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
3517201209Srpaulo	    BUS_DMASYNC_PREWRITE);
3518201209Srpaulo
3519201209Srpaulo#ifdef notyet
3520198429Srpaulo	/* Update TX scheduler. */
3521220728Sbschmidt	ops->update_sched(sc, ring->qid, ring->cur, tx->id, totlen);
3522201209Srpaulo#endif
3523178676Ssam
3524198429Srpaulo	/* Kick TX ring. */
3525178676Ssam	ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT;
3526198429Srpaulo	IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur);
3527178676Ssam
3528198429Srpaulo	/* Mark TX ring as full if we reach a certain threshold. */
3529198429Srpaulo	if (++ring->queued > IWN_TX_RING_HIMARK)
3530198429Srpaulo		sc->qfullmsk |= 1 << ring->qid;
3531178676Ssam
3532178676Ssam	return 0;
3533178676Ssam}
3534178676Ssam
3535178676Ssamstatic int
3536178676Ssamiwn_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
3537220720Sbschmidt    const struct ieee80211_bpf_params *params)
3538178676Ssam{
3539178676Ssam	struct ieee80211com *ic = ni->ni_ic;
3540178676Ssam	struct ifnet *ifp = ic->ic_ifp;
3541178676Ssam	struct iwn_softc *sc = ifp->if_softc;
3542198429Srpaulo	int error = 0;
3543178676Ssam
3544178676Ssam	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
3545178676Ssam		ieee80211_free_node(ni);
3546178676Ssam		m_freem(m);
3547178676Ssam		return ENETDOWN;
3548178676Ssam	}
3549178676Ssam
3550178676Ssam	IWN_LOCK(sc);
3551178676Ssam	if (params == NULL) {
3552178676Ssam		/*
3553178676Ssam		 * Legacy path; interpret frame contents to decide
3554178676Ssam		 * precisely how to send the frame.
3555178676Ssam		 */
3556220720Sbschmidt		error = iwn_tx_data(sc, m, ni);
3557178676Ssam	} else {
3558178676Ssam		/*
3559178676Ssam		 * Caller supplied explicit parameters to use in
3560178676Ssam		 * sending the frame.
3561178676Ssam		 */
3562220720Sbschmidt		error = iwn_tx_data_raw(sc, m, ni, params);
3563178676Ssam	}
3564178676Ssam	if (error != 0) {
3565178676Ssam		/* NB: m is reclaimed on tx failure */
3566178676Ssam		ieee80211_free_node(ni);
3567178676Ssam		ifp->if_oerrors++;
3568178676Ssam	}
3569220667Sbschmidt	sc->sc_tx_timer = 5;
3570220667Sbschmidt
3571178676Ssam	IWN_UNLOCK(sc);
3572178676Ssam	return error;
3573178676Ssam}
3574178676Ssam
3575206477Sbschmidtstatic void
3576198429Srpauloiwn_start(struct ifnet *ifp)
3577198429Srpaulo{
3578198429Srpaulo	struct iwn_softc *sc = ifp->if_softc;
3579198429Srpaulo
3580198429Srpaulo	IWN_LOCK(sc);
3581198429Srpaulo	iwn_start_locked(ifp);
3582198429Srpaulo	IWN_UNLOCK(sc);
3583198429Srpaulo}
3584198429Srpaulo
3585206477Sbschmidtstatic void
3586198429Srpauloiwn_start_locked(struct ifnet *ifp)
3587198429Srpaulo{
3588198429Srpaulo	struct iwn_softc *sc = ifp->if_softc;
3589198429Srpaulo	struct ieee80211_node *ni;
3590198429Srpaulo	struct mbuf *m;
3591198429Srpaulo
3592198429Srpaulo	IWN_LOCK_ASSERT(sc);
3593198429Srpaulo
3594220720Sbschmidt	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 ||
3595220720Sbschmidt	    (ifp->if_drv_flags & IFF_DRV_OACTIVE))
3596220720Sbschmidt		return;
3597220720Sbschmidt
3598198429Srpaulo	for (;;) {
3599198429Srpaulo		if (sc->qfullmsk != 0) {
3600198429Srpaulo			ifp->if_drv_flags |= IFF_DRV_OACTIVE;
3601198429Srpaulo			break;
3602198429Srpaulo		}
3603198429Srpaulo		IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
3604198429Srpaulo		if (m == NULL)
3605198429Srpaulo			break;
3606198429Srpaulo		ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
3607220720Sbschmidt		if (iwn_tx_data(sc, m, ni) != 0) {
3608220720Sbschmidt			ieee80211_free_node(ni);
3609198429Srpaulo			ifp->if_oerrors++;
3610220720Sbschmidt			continue;
3611198429Srpaulo		}
3612198429Srpaulo		sc->sc_tx_timer = 5;
3613198429Srpaulo	}
3614198429Srpaulo}
3615198429Srpaulo
3616178676Ssamstatic void
3617220667Sbschmidtiwn_watchdog(void *arg)
3618178676Ssam{
3619220667Sbschmidt	struct iwn_softc *sc = arg;
3620220667Sbschmidt	struct ifnet *ifp = sc->sc_ifp;
3621220667Sbschmidt	struct ieee80211com *ic = ifp->if_l2com;
3622178676Ssam
3623220667Sbschmidt	IWN_LOCK_ASSERT(sc);
3624220667Sbschmidt
3625220667Sbschmidt	KASSERT(ifp->if_drv_flags & IFF_DRV_RUNNING, ("not running"));
3626220667Sbschmidt
3627220668Sbschmidt	if (sc->sc_tx_timer > 0) {
3628220668Sbschmidt		if (--sc->sc_tx_timer == 0) {
3629220667Sbschmidt			if_printf(ifp, "device timeout\n");
3630220667Sbschmidt			ieee80211_runtask(ic, &sc->sc_reinit_task);
3631220667Sbschmidt			return;
3632220667Sbschmidt		}
3633178676Ssam	}
3634220667Sbschmidt	callout_reset(&sc->watchdog_to, hz, iwn_watchdog, sc);
3635178676Ssam}
3636178676Ssam
3637206477Sbschmidtstatic int
3638178676Ssamiwn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
3639178676Ssam{
3640178676Ssam	struct iwn_softc *sc = ifp->if_softc;
3641178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
3642201209Srpaulo	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
3643178676Ssam	struct ifreq *ifr = (struct ifreq *) data;
3644201209Srpaulo	int error = 0, startall = 0, stop = 0;
3645178676Ssam
3646178676Ssam	switch (cmd) {
3647220723Sbschmidt	case SIOCGIFADDR:
3648220723Sbschmidt		error = ether_ioctl(ifp, cmd, data);
3649220723Sbschmidt		break;
3650178676Ssam	case SIOCSIFFLAGS:
3651178704Sthompsa		IWN_LOCK(sc);
3652178676Ssam		if (ifp->if_flags & IFF_UP) {
3653178676Ssam			if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
3654178676Ssam				iwn_init_locked(sc);
3655201209Srpaulo				if (IWN_READ(sc, IWN_GP_CNTRL) & IWN_GP_CNTRL_RFKILL)
3656201209Srpaulo					startall = 1;
3657201209Srpaulo				else
3658201209Srpaulo					stop = 1;
3659178676Ssam			}
3660178676Ssam		} else {
3661178676Ssam			if (ifp->if_drv_flags & IFF_DRV_RUNNING)
3662178676Ssam				iwn_stop_locked(sc);
3663178676Ssam		}
3664178704Sthompsa		IWN_UNLOCK(sc);
3665178704Sthompsa		if (startall)
3666178704Sthompsa			ieee80211_start_all(ic);
3667201209Srpaulo		else if (vap != NULL && stop)
3668201209Srpaulo			ieee80211_stop(vap);
3669178676Ssam		break;
3670178676Ssam	case SIOCGIFMEDIA:
3671178676Ssam		error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd);
3672178676Ssam		break;
3673178704Sthompsa	default:
3674178704Sthompsa		error = EINVAL;
3675178704Sthompsa		break;
3676178676Ssam	}
3677178676Ssam	return error;
3678178676Ssam}
3679178676Ssam
3680178676Ssam/*
3681178676Ssam * Send a command to the firmware.
3682178676Ssam */
3683206477Sbschmidtstatic int
3684178676Ssamiwn_cmd(struct iwn_softc *sc, int code, const void *buf, int size, int async)
3685178676Ssam{
3686178676Ssam	struct iwn_tx_ring *ring = &sc->txq[4];
3687178676Ssam	struct iwn_tx_desc *desc;
3688198429Srpaulo	struct iwn_tx_data *data;
3689178676Ssam	struct iwn_tx_cmd *cmd;
3690198439Srpaulo	struct mbuf *m;
3691178676Ssam	bus_addr_t paddr;
3692198439Srpaulo	int totlen, error;
3693178676Ssam
3694178676Ssam	IWN_LOCK_ASSERT(sc);
3695178676Ssam
3696178676Ssam	desc = &ring->desc[ring->cur];
3697198429Srpaulo	data = &ring->data[ring->cur];
3698198429Srpaulo	totlen = 4 + size;
3699178676Ssam
3700198439Srpaulo	if (size > sizeof cmd->data) {
3701198439Srpaulo		/* Command is too large to fit in a descriptor. */
3702198439Srpaulo		if (totlen > MCLBYTES)
3703198439Srpaulo			return EINVAL;
3704201209Srpaulo		m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE);
3705198439Srpaulo		if (m == NULL)
3706198439Srpaulo			return ENOMEM;
3707198439Srpaulo		cmd = mtod(m, struct iwn_tx_cmd *);
3708201209Srpaulo		error = bus_dmamap_load(ring->data_dmat, data->map, cmd,
3709198439Srpaulo		    totlen, iwn_dma_map_addr, &paddr, BUS_DMA_NOWAIT);
3710198439Srpaulo		if (error != 0) {
3711198439Srpaulo			m_freem(m);
3712198439Srpaulo			return error;
3713198439Srpaulo		}
3714198439Srpaulo		data->m = m;
3715198439Srpaulo	} else {
3716198439Srpaulo		cmd = &ring->cmd[ring->cur];
3717198439Srpaulo		paddr = data->cmd_paddr;
3718198439Srpaulo	}
3719198439Srpaulo
3720178676Ssam	cmd->code = code;
3721178676Ssam	cmd->flags = 0;
3722178676Ssam	cmd->qid = ring->qid;
3723178676Ssam	cmd->idx = ring->cur;
3724178676Ssam	memcpy(cmd->data, buf, size);
3725178676Ssam
3726198429Srpaulo	desc->nsegs = 1;
3727198429Srpaulo	desc->segs[0].addr = htole32(IWN_LOADDR(paddr));
3728198429Srpaulo	desc->segs[0].len  = htole16(IWN_HIADDR(paddr) | totlen << 4);
3729178676Ssam
3730178676Ssam	DPRINTF(sc, IWN_DEBUG_CMD, "%s: %s (0x%x) flags %d qid %d idx %d\n",
3731178676Ssam	    __func__, iwn_intr_str(cmd->code), cmd->code,
3732178676Ssam	    cmd->flags, cmd->qid, cmd->idx);
3733178676Ssam
3734198439Srpaulo	if (size > sizeof cmd->data) {
3735201209Srpaulo		bus_dmamap_sync(ring->data_dmat, data->map,
3736198439Srpaulo		    BUS_DMASYNC_PREWRITE);
3737198439Srpaulo	} else {
3738201209Srpaulo		bus_dmamap_sync(ring->data_dmat, ring->cmd_dma.map,
3739198439Srpaulo		    BUS_DMASYNC_PREWRITE);
3740198439Srpaulo	}
3741198439Srpaulo	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
3742198439Srpaulo	    BUS_DMASYNC_PREWRITE);
3743198439Srpaulo
3744201209Srpaulo#ifdef notyet
3745198429Srpaulo	/* Update TX scheduler. */
3746220728Sbschmidt	ops->update_sched(sc, ring->qid, ring->cur, 0, 0);
3747201209Srpaulo#endif
3748198429Srpaulo
3749198429Srpaulo	/* Kick command ring. */
3750178676Ssam	ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT;
3751198429Srpaulo	IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur);
3752178676Ssam
3753198439Srpaulo	return async ? 0 : msleep(desc, &sc->sc_mtx, PCATCH, "iwncmd", hz);
3754178676Ssam}
3755178676Ssam
3756206477Sbschmidtstatic int
3757198429Srpauloiwn4965_add_node(struct iwn_softc *sc, struct iwn_node_info *node, int async)
3758198429Srpaulo{
3759198429Srpaulo	struct iwn4965_node_info hnode;
3760198429Srpaulo	caddr_t src, dst;
3761198429Srpaulo
3762198429Srpaulo	/*
3763198429Srpaulo	 * We use the node structure for 5000 Series internally (it is
3764198429Srpaulo	 * a superset of the one for 4965AGN). We thus copy the common
3765198429Srpaulo	 * fields before sending the command.
3766198429Srpaulo	 */
3767198429Srpaulo	src = (caddr_t)node;
3768198429Srpaulo	dst = (caddr_t)&hnode;
3769198429Srpaulo	memcpy(dst, src, 48);
3770198429Srpaulo	/* Skip TSC, RX MIC and TX MIC fields from ``src''. */
3771198429Srpaulo	memcpy(dst + 48, src + 72, 20);
3772198429Srpaulo	return iwn_cmd(sc, IWN_CMD_ADD_NODE, &hnode, sizeof hnode, async);
3773198429Srpaulo}
3774198429Srpaulo
3775206477Sbschmidtstatic int
3776198429Srpauloiwn5000_add_node(struct iwn_softc *sc, struct iwn_node_info *node, int async)
3777198429Srpaulo{
3778198429Srpaulo	/* Direct mapping. */
3779198429Srpaulo	return iwn_cmd(sc, IWN_CMD_ADD_NODE, node, sizeof (*node), async);
3780198429Srpaulo}
3781198429Srpaulo
3782206477Sbschmidtstatic int
3783220715Sbschmidtiwn_set_link_quality(struct iwn_softc *sc, struct ieee80211_node *ni)
3784178676Ssam{
3785220715Sbschmidt	struct iwn_node *wn = (void *)ni;
3786220715Sbschmidt	struct ieee80211_rateset *rs = &ni->ni_rates;
3787198429Srpaulo	struct iwn_cmd_link_quality linkq;
3788201209Srpaulo	const struct iwn_rate *rinfo;
3789220715Sbschmidt	uint8_t txant;
3790220715Sbschmidt	int i, txrate;
3791178676Ssam
3792198429Srpaulo	/* Use the first valid TX antenna. */
3793201209Srpaulo	txant = IWN_LSB(sc->txchainmask);
3794178676Ssam
3795198429Srpaulo	memset(&linkq, 0, sizeof linkq);
3796220715Sbschmidt	linkq.id = wn->id;
3797198429Srpaulo	linkq.antmsk_1stream = txant;
3798201209Srpaulo	linkq.antmsk_2stream = IWN_ANT_AB;
3799201209Srpaulo	linkq.ampdu_max = 31;
3800198429Srpaulo	linkq.ampdu_threshold = 3;
3801198429Srpaulo	linkq.ampdu_limit = htole16(4000);	/* 4ms */
3802198429Srpaulo
3803220715Sbschmidt	/* Start at highest available bit-rate. */
3804220715Sbschmidt	txrate = rs->rs_nrates - 1;
3805178676Ssam	for (i = 0; i < IWN_MAX_TX_RETRIES; i++) {
3806220715Sbschmidt		rinfo = &iwn_rates[wn->ridx[txrate]];
3807220715Sbschmidt		linkq.retry[i].plcp = rinfo->plcp;
3808220715Sbschmidt		linkq.retry[i].rflags = rinfo->flags;
3809198429Srpaulo		linkq.retry[i].rflags |= IWN_RFLAG_ANT(txant);
3810220715Sbschmidt		/* Next retry at immediate lower bit-rate. */
3811220715Sbschmidt		if (txrate > 0)
3812220715Sbschmidt			txrate--;
3813178676Ssam	}
3814220715Sbschmidt	return iwn_cmd(sc, IWN_CMD_LINK_QUALITY, &linkq, sizeof linkq, 1);
3815178676Ssam}
3816178676Ssam
3817178676Ssam/*
3818198429Srpaulo * Broadcast node is used to send group-addressed and management frames.
3819178676Ssam */
3820206477Sbschmidtstatic int
3821201209Srpauloiwn_add_broadcast_node(struct iwn_softc *sc, int async)
3822178676Ssam{
3823220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
3824198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
3825220715Sbschmidt	struct ieee80211com *ic = ifp->if_l2com;
3826178676Ssam	struct iwn_node_info node;
3827220715Sbschmidt	struct iwn_cmd_link_quality linkq;
3828220715Sbschmidt	const struct iwn_rate *rinfo;
3829220715Sbschmidt	uint8_t txant;
3830220715Sbschmidt	int i, error;
3831178676Ssam
3832178676Ssam	memset(&node, 0, sizeof node);
3833198429Srpaulo	IEEE80211_ADDR_COPY(node.macaddr, ifp->if_broadcastaddr);
3834220728Sbschmidt	node.id = sc->broadcast_id;
3835198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "%s: adding broadcast node\n", __func__);
3836220728Sbschmidt	if ((error = ops->add_node(sc, &node, async)) != 0)
3837198429Srpaulo		return error;
3838178676Ssam
3839220715Sbschmidt	/* Use the first valid TX antenna. */
3840220715Sbschmidt	txant = IWN_LSB(sc->txchainmask);
3841220715Sbschmidt
3842220715Sbschmidt	memset(&linkq, 0, sizeof linkq);
3843220728Sbschmidt	linkq.id = sc->broadcast_id;
3844220715Sbschmidt	linkq.antmsk_1stream = txant;
3845220715Sbschmidt	linkq.antmsk_2stream = IWN_ANT_AB;
3846220715Sbschmidt	linkq.ampdu_max = 64;
3847220715Sbschmidt	linkq.ampdu_threshold = 3;
3848220715Sbschmidt	linkq.ampdu_limit = htole16(4000);	/* 4ms */
3849220715Sbschmidt
3850220715Sbschmidt	/* Use lowest mandatory bit-rate. */
3851220715Sbschmidt	if (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan))
3852220715Sbschmidt		rinfo = &iwn_rates[IWN_RIDX_OFDM6];
3853220715Sbschmidt	else
3854220715Sbschmidt		rinfo = &iwn_rates[IWN_RIDX_CCK1];
3855220715Sbschmidt	linkq.retry[0].plcp = rinfo->plcp;
3856220715Sbschmidt	linkq.retry[0].rflags = rinfo->flags;
3857220715Sbschmidt	linkq.retry[0].rflags |= IWN_RFLAG_ANT(txant);
3858220715Sbschmidt	/* Use same bit-rate for all TX retries. */
3859220715Sbschmidt	for (i = 1; i < IWN_MAX_TX_RETRIES; i++) {
3860220715Sbschmidt		linkq.retry[i].plcp = linkq.retry[0].plcp;
3861220715Sbschmidt		linkq.retry[i].rflags = linkq.retry[0].rflags;
3862220715Sbschmidt	}
3863220715Sbschmidt	return iwn_cmd(sc, IWN_CMD_LINK_QUALITY, &linkq, sizeof linkq, async);
3864178676Ssam}
3865178676Ssam
3866206477Sbschmidtstatic int
3867220721Sbschmidtiwn_updateedca(struct ieee80211com *ic)
3868178676Ssam{
3869178676Ssam#define IWN_EXP2(x)	((1 << (x)) - 1)	/* CWmin = 2^ECWmin - 1 */
3870178676Ssam	struct iwn_softc *sc = ic->ic_ifp->if_softc;
3871178676Ssam	struct iwn_edca_params cmd;
3872220721Sbschmidt	int aci;
3873178676Ssam
3874178676Ssam	memset(&cmd, 0, sizeof cmd);
3875178676Ssam	cmd.flags = htole32(IWN_EDCA_UPDATE);
3876220721Sbschmidt	for (aci = 0; aci < WME_NUM_AC; aci++) {
3877220721Sbschmidt		const struct wmeParams *ac =
3878220721Sbschmidt		    &ic->ic_wme.wme_chanParams.cap_wmeParams[aci];
3879220721Sbschmidt		cmd.ac[aci].aifsn = ac->wmep_aifsn;
3880220721Sbschmidt		cmd.ac[aci].cwmin = htole16(IWN_EXP2(ac->wmep_logcwmin));
3881220721Sbschmidt		cmd.ac[aci].cwmax = htole16(IWN_EXP2(ac->wmep_logcwmax));
3882220721Sbschmidt		cmd.ac[aci].txoplimit =
3883220721Sbschmidt		    htole16(IEEE80211_TXOP_TO_US(ac->wmep_txopLimit));
3884178676Ssam	}
3885201209Srpaulo	IEEE80211_UNLOCK(ic);
3886178676Ssam	IWN_LOCK(sc);
3887220725Sbschmidt	(void)iwn_cmd(sc, IWN_CMD_EDCA_PARAMS, &cmd, sizeof cmd, 1);
3888178676Ssam	IWN_UNLOCK(sc);
3889201209Srpaulo	IEEE80211_LOCK(ic);
3890178676Ssam	return 0;
3891178676Ssam#undef IWN_EXP2
3892178676Ssam}
3893178676Ssam
3894201209Srpaulostatic void
3895201209Srpauloiwn_update_mcast(struct ifnet *ifp)
3896201209Srpaulo{
3897201209Srpaulo	/* Ignore */
3898201209Srpaulo}
3899201209Srpaulo
3900206477Sbschmidtstatic void
3901178676Ssamiwn_set_led(struct iwn_softc *sc, uint8_t which, uint8_t off, uint8_t on)
3902178676Ssam{
3903178676Ssam	struct iwn_cmd_led led;
3904178676Ssam
3905198429Srpaulo	/* Clear microcode LED ownership. */
3906198429Srpaulo	IWN_CLRBITS(sc, IWN_LED, IWN_LED_BSM_CTRL);
3907198429Srpaulo
3908178676Ssam	led.which = which;
3909198429Srpaulo	led.unit = htole32(10000);	/* on/off in unit of 100ms */
3910178676Ssam	led.off = off;
3911178676Ssam	led.on = on;
3912198429Srpaulo	(void)iwn_cmd(sc, IWN_CMD_SET_LED, &led, sizeof led, 1);
3913178676Ssam}
3914178676Ssam
3915178676Ssam/*
3916201209Srpaulo * Set the critical temperature at which the firmware will stop the radio
3917201209Srpaulo * and notify us.
3918178676Ssam */
3919206477Sbschmidtstatic int
3920178676Ssamiwn_set_critical_temp(struct iwn_softc *sc)
3921178676Ssam{
3922178676Ssam	struct iwn_critical_temp crit;
3923201209Srpaulo	int32_t temp;
3924178676Ssam
3925198429Srpaulo	IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_CTEMP_STOP_RF);
3926178676Ssam
3927201209Srpaulo	if (sc->hw_type == IWN_HW_REV_TYPE_5150)
3928201209Srpaulo		temp = (IWN_CTOK(110) - sc->temp_off) * -5;
3929201209Srpaulo	else if (sc->hw_type == IWN_HW_REV_TYPE_4965)
3930201209Srpaulo		temp = IWN_CTOK(110);
3931201209Srpaulo	else
3932201209Srpaulo		temp = 110;
3933178676Ssam	memset(&crit, 0, sizeof crit);
3934201209Srpaulo	crit.tempR = htole32(temp);
3935220726Sbschmidt	DPRINTF(sc, IWN_DEBUG_RESET, "setting critical temp to %d\n", temp);
3936178676Ssam	return iwn_cmd(sc, IWN_CMD_SET_CRITICAL_TEMP, &crit, sizeof crit, 0);
3937178676Ssam}
3938178676Ssam
3939206477Sbschmidtstatic int
3940198429Srpauloiwn_set_timing(struct iwn_softc *sc, struct ieee80211_node *ni)
3941178676Ssam{
3942198429Srpaulo	struct iwn_cmd_timing cmd;
3943178676Ssam	uint64_t val, mod;
3944178676Ssam
3945198429Srpaulo	memset(&cmd, 0, sizeof cmd);
3946198429Srpaulo	memcpy(&cmd.tstamp, ni->ni_tstamp.data, sizeof (uint64_t));
3947198429Srpaulo	cmd.bintval = htole16(ni->ni_intval);
3948198429Srpaulo	cmd.lintval = htole16(10);
3949178676Ssam
3950198429Srpaulo	/* Compute remaining time until next beacon. */
3951220634Sbschmidt	val = (uint64_t)ni->ni_intval * IEEE80211_DUR_TU;
3952198429Srpaulo	mod = le64toh(cmd.tstamp) % val;
3953198429Srpaulo	cmd.binitval = htole32((uint32_t)(val - mod));
3954178676Ssam
3955198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "timing bintval=%u tstamp=%ju, init=%u\n",
3956198429Srpaulo	    ni->ni_intval, le64toh(cmd.tstamp), (uint32_t)(val - mod));
3957178676Ssam
3958198429Srpaulo	return iwn_cmd(sc, IWN_CMD_TIMING, &cmd, sizeof cmd, 1);
3959178676Ssam}
3960178676Ssam
3961206477Sbschmidtstatic void
3962198429Srpauloiwn4965_power_calibration(struct iwn_softc *sc, int temp)
3963178676Ssam{
3964201882Skeramida	struct ifnet *ifp = sc->sc_ifp;
3965201882Skeramida	struct ieee80211com *ic = ifp->if_l2com;
3966201882Skeramida
3967220725Sbschmidt	/* Adjust TX power if need be (delta >= 3 degC). */
3968178676Ssam	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: temperature %d->%d\n",
3969178676Ssam	    __func__, sc->temp, temp);
3970198429Srpaulo	if (abs(temp - sc->temp) >= 3) {
3971198429Srpaulo		/* Record temperature of last calibration. */
3972198429Srpaulo		sc->temp = temp;
3973201882Skeramida		(void)iwn4965_set_txpower(sc, ic->ic_bsschan, 1);
3974178676Ssam	}
3975178676Ssam}
3976178676Ssam
3977178676Ssam/*
3978198429Srpaulo * Set TX power for current channel (each rate has its own power settings).
3979178676Ssam * This function takes into account the regulatory information from EEPROM,
3980178676Ssam * the current temperature and the current voltage.
3981178676Ssam */
3982206477Sbschmidtstatic int
3983201882Skeramidaiwn4965_set_txpower(struct iwn_softc *sc, struct ieee80211_channel *ch,
3984201882Skeramida    int async)
3985178676Ssam{
3986198429Srpaulo/* Fixed-point arithmetic division using a n-bit fractional part. */
3987178676Ssam#define fdivround(a, b, n)	\
3988178676Ssam	((((1 << n) * (a)) / (b) + (1 << n) / 2) / (1 << n))
3989198429Srpaulo/* Linear interpolation. */
3990178676Ssam#define interpolate(x, x1, y1, x2, y2, n)	\
3991178676Ssam	((y1) + fdivround(((int)(x) - (x1)) * ((y2) - (y1)), (x2) - (x1), n))
3992178676Ssam
3993178676Ssam	static const int tdiv[IWN_NATTEN_GROUPS] = { 9, 8, 8, 8, 6 };
3994178676Ssam	struct iwn_ucode_info *uc = &sc->ucode_info;
3995198429Srpaulo	struct iwn4965_cmd_txpower cmd;
3996198429Srpaulo	struct iwn4965_eeprom_chan_samples *chans;
3997220723Sbschmidt	const uint8_t *rf_gain, *dsp_gain;
3998178676Ssam	int32_t vdiff, tdiff;
3999178676Ssam	int i, c, grp, maxpwr;
4000198429Srpaulo	uint8_t chan;
4001178676Ssam
4002220687Sbschmidt	/* Retrieve current channel from last RXON. */
4003220687Sbschmidt	chan = sc->rxon.chan;
4004201209Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "setting TX power for channel %d\n",
4005201209Srpaulo	    chan);
4006178676Ssam
4007178676Ssam	memset(&cmd, 0, sizeof cmd);
4008178676Ssam	cmd.band = IEEE80211_IS_CHAN_5GHZ(ch) ? 0 : 1;
4009178676Ssam	cmd.chan = chan;
4010178676Ssam
4011178676Ssam	if (IEEE80211_IS_CHAN_5GHZ(ch)) {
4012178676Ssam		maxpwr   = sc->maxpwr5GHz;
4013198429Srpaulo		rf_gain  = iwn4965_rf_gain_5ghz;
4014198429Srpaulo		dsp_gain = iwn4965_dsp_gain_5ghz;
4015178676Ssam	} else {
4016178676Ssam		maxpwr   = sc->maxpwr2GHz;
4017198429Srpaulo		rf_gain  = iwn4965_rf_gain_2ghz;
4018198429Srpaulo		dsp_gain = iwn4965_dsp_gain_2ghz;
4019178676Ssam	}
4020178676Ssam
4021198429Srpaulo	/* Compute voltage compensation. */
4022178676Ssam	vdiff = ((int32_t)le32toh(uc->volt) - sc->eeprom_voltage) / 7;
4023178676Ssam	if (vdiff > 0)
4024178676Ssam		vdiff *= 2;
4025178676Ssam	if (abs(vdiff) > 2)
4026178676Ssam		vdiff = 0;
4027178676Ssam	DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
4028178676Ssam	    "%s: voltage compensation=%d (UCODE=%d, EEPROM=%d)\n",
4029178676Ssam	    __func__, vdiff, le32toh(uc->volt), sc->eeprom_voltage);
4030178676Ssam
4031201209Srpaulo	/* Get channel attenuation group. */
4032178676Ssam	if (chan <= 20)		/* 1-20 */
4033178676Ssam		grp = 4;
4034178676Ssam	else if (chan <= 43)	/* 34-43 */
4035178676Ssam		grp = 0;
4036178676Ssam	else if (chan <= 70)	/* 44-70 */
4037178676Ssam		grp = 1;
4038178676Ssam	else if (chan <= 124)	/* 71-124 */
4039178676Ssam		grp = 2;
4040178676Ssam	else			/* 125-200 */
4041178676Ssam		grp = 3;
4042178676Ssam	DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
4043178676Ssam	    "%s: chan %d, attenuation group=%d\n", __func__, chan, grp);
4044178676Ssam
4045201209Srpaulo	/* Get channel sub-band. */
4046178676Ssam	for (i = 0; i < IWN_NBANDS; i++)
4047178676Ssam		if (sc->bands[i].lo != 0 &&
4048178676Ssam		    sc->bands[i].lo <= chan && chan <= sc->bands[i].hi)
4049178676Ssam			break;
4050198429Srpaulo	if (i == IWN_NBANDS)	/* Can't happen in real-life. */
4051198429Srpaulo		return EINVAL;
4052178676Ssam	chans = sc->bands[i].chans;
4053178676Ssam	DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
4054178676Ssam	    "%s: chan %d sub-band=%d\n", __func__, chan, i);
4055178676Ssam
4056198429Srpaulo	for (c = 0; c < 2; c++) {
4057178676Ssam		uint8_t power, gain, temp;
4058178676Ssam		int maxchpwr, pwr, ridx, idx;
4059178676Ssam
4060178676Ssam		power = interpolate(chan,
4061178676Ssam		    chans[0].num, chans[0].samples[c][1].power,
4062178676Ssam		    chans[1].num, chans[1].samples[c][1].power, 1);
4063178676Ssam		gain  = interpolate(chan,
4064178676Ssam		    chans[0].num, chans[0].samples[c][1].gain,
4065178676Ssam		    chans[1].num, chans[1].samples[c][1].gain, 1);
4066178676Ssam		temp  = interpolate(chan,
4067178676Ssam		    chans[0].num, chans[0].samples[c][1].temp,
4068178676Ssam		    chans[1].num, chans[1].samples[c][1].temp, 1);
4069178676Ssam		DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
4070178676Ssam		    "%s: Tx chain %d: power=%d gain=%d temp=%d\n",
4071178676Ssam		    __func__, c, power, gain, temp);
4072178676Ssam
4073198429Srpaulo		/* Compute temperature compensation. */
4074178676Ssam		tdiff = ((sc->temp - temp) * 2) / tdiv[grp];
4075178676Ssam		DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
4076178676Ssam		    "%s: temperature compensation=%d (current=%d, EEPROM=%d)\n",
4077178676Ssam		    __func__, tdiff, sc->temp, temp);
4078178676Ssam
4079178676Ssam		for (ridx = 0; ridx <= IWN_RIDX_MAX; ridx++) {
4080201209Srpaulo			/* Convert dBm to half-dBm. */
4081198429Srpaulo			maxchpwr = sc->maxpwr[chan] * 2;
4082198429Srpaulo			if ((ridx / 8) & 1)
4083198429Srpaulo				maxchpwr -= 6;	/* MIMO 2T: -3dB */
4084178676Ssam
4085198429Srpaulo			pwr = maxpwr;
4086178676Ssam
4087198429Srpaulo			/* Adjust TX power based on rate. */
4088198429Srpaulo			if ((ridx % 8) == 5)
4089198429Srpaulo				pwr -= 15;	/* OFDM48: -7.5dB */
4090198429Srpaulo			else if ((ridx % 8) == 6)
4091198429Srpaulo				pwr -= 17;	/* OFDM54: -8.5dB */
4092198429Srpaulo			else if ((ridx % 8) == 7)
4093198429Srpaulo				pwr -= 20;	/* OFDM60: -10dB */
4094198429Srpaulo			else
4095198429Srpaulo				pwr -= 10;	/* Others: -5dB */
4096178676Ssam
4097201209Srpaulo			/* Do not exceed channel max TX power. */
4098178676Ssam			if (pwr > maxchpwr)
4099178676Ssam				pwr = maxchpwr;
4100178676Ssam
4101178676Ssam			idx = gain - (pwr - power) - tdiff - vdiff;
4102178676Ssam			if ((ridx / 8) & 1)	/* MIMO */
4103178676Ssam				idx += (int32_t)le32toh(uc->atten[grp][c]);
4104178676Ssam
4105178676Ssam			if (cmd.band == 0)
4106178676Ssam				idx += 9;	/* 5GHz */
4107178676Ssam			if (ridx == IWN_RIDX_MAX)
4108178676Ssam				idx += 5;	/* CCK */
4109178676Ssam
4110198429Srpaulo			/* Make sure idx stays in a valid range. */
4111178676Ssam			if (idx < 0)
4112178676Ssam				idx = 0;
4113198429Srpaulo			else if (idx > IWN4965_MAX_PWR_INDEX)
4114198429Srpaulo				idx = IWN4965_MAX_PWR_INDEX;
4115178676Ssam
4116178676Ssam			DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
4117178676Ssam			    "%s: Tx chain %d, rate idx %d: power=%d\n",
4118178676Ssam			    __func__, c, ridx, idx);
4119178676Ssam			cmd.power[ridx].rf_gain[c] = rf_gain[idx];
4120178676Ssam			cmd.power[ridx].dsp_gain[c] = dsp_gain[idx];
4121178676Ssam		}
4122178676Ssam	}
4123178676Ssam
4124178676Ssam	DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
4125178676Ssam	    "%s: set tx power for chan %d\n", __func__, chan);
4126178676Ssam	return iwn_cmd(sc, IWN_CMD_TXPOWER, &cmd, sizeof cmd, async);
4127178676Ssam
4128178676Ssam#undef interpolate
4129178676Ssam#undef fdivround
4130178676Ssam}
4131178676Ssam
4132206477Sbschmidtstatic int
4133201882Skeramidaiwn5000_set_txpower(struct iwn_softc *sc, struct ieee80211_channel *ch,
4134201882Skeramida    int async)
4135198429Srpaulo{
4136198429Srpaulo	struct iwn5000_cmd_txpower cmd;
4137198429Srpaulo
4138198429Srpaulo	/*
4139198429Srpaulo	 * TX power calibration is handled automatically by the firmware
4140198429Srpaulo	 * for 5000 Series.
4141198429Srpaulo	 */
4142198429Srpaulo	memset(&cmd, 0, sizeof cmd);
4143198429Srpaulo	cmd.global_limit = 2 * IWN5000_TXPOWER_MAX_DBM;	/* 16 dBm */
4144198429Srpaulo	cmd.flags = IWN5000_TXPOWER_NO_CLOSED;
4145198429Srpaulo	cmd.srv_limit = IWN5000_TXPOWER_AUTO;
4146198429Srpaulo	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: setting TX power\n", __func__);
4147198429Srpaulo	return iwn_cmd(sc, IWN_CMD_TXPOWER_DBM, &cmd, sizeof cmd, async);
4148198429Srpaulo}
4149198429Srpaulo
4150178676Ssam/*
4151198429Srpaulo * Retrieve the maximum RSSI (in dBm) among receivers.
4152178676Ssam */
4153206477Sbschmidtstatic int
4154198429Srpauloiwn4965_get_rssi(struct iwn_softc *sc, struct iwn_rx_stat *stat)
4155178676Ssam{
4156198429Srpaulo	struct iwn4965_rx_phystat *phy = (void *)stat->phybuf;
4157198429Srpaulo	uint8_t mask, agc;
4158198429Srpaulo	int rssi;
4159178676Ssam
4160201209Srpaulo	mask = (le16toh(phy->antenna) >> 4) & IWN_ANT_ABC;
4161198429Srpaulo	agc  = (le16toh(phy->agc) >> 7) & 0x7f;
4162178676Ssam
4163178676Ssam	rssi = 0;
4164220689Sbschmidt	if (mask & IWN_ANT_A)
4165220689Sbschmidt		rssi = MAX(rssi, phy->rssi[0]);
4166220689Sbschmidt	if (mask & IWN_ANT_B)
4167220689Sbschmidt		rssi = MAX(rssi, phy->rssi[2]);
4168220689Sbschmidt	if (mask & IWN_ANT_C)
4169220689Sbschmidt		rssi = MAX(rssi, phy->rssi[4]);
4170198429Srpaulo
4171220724Sbschmidt	DPRINTF(sc, IWN_DEBUG_RECV,
4172220724Sbschmidt	    "%s: agc %d mask 0x%x rssi %d %d %d result %d\n", __func__, agc,
4173220724Sbschmidt	    mask, phy->rssi[0], phy->rssi[2], phy->rssi[4],
4174178676Ssam	    rssi - agc - IWN_RSSI_TO_DBM);
4175178676Ssam	return rssi - agc - IWN_RSSI_TO_DBM;
4176178676Ssam}
4177178676Ssam
4178206477Sbschmidtstatic int
4179198429Srpauloiwn5000_get_rssi(struct iwn_softc *sc, struct iwn_rx_stat *stat)
4180198429Srpaulo{
4181198429Srpaulo	struct iwn5000_rx_phystat *phy = (void *)stat->phybuf;
4182220723Sbschmidt	uint8_t agc;
4183198429Srpaulo	int rssi;
4184198429Srpaulo
4185198429Srpaulo	agc = (le32toh(phy->agc) >> 9) & 0x7f;
4186198429Srpaulo
4187198429Srpaulo	rssi = MAX(le16toh(phy->rssi[0]) & 0xff,
4188198429Srpaulo		   le16toh(phy->rssi[1]) & 0xff);
4189198429Srpaulo	rssi = MAX(le16toh(phy->rssi[2]) & 0xff, rssi);
4190198429Srpaulo
4191220724Sbschmidt	DPRINTF(sc, IWN_DEBUG_RECV,
4192220724Sbschmidt	    "%s: agc %d rssi %d %d %d result %d\n", __func__, agc,
4193201822Strasz	    phy->rssi[0], phy->rssi[1], phy->rssi[2],
4194198429Srpaulo	    rssi - agc - IWN_RSSI_TO_DBM);
4195198429Srpaulo	return rssi - agc - IWN_RSSI_TO_DBM;
4196198429Srpaulo}
4197198429Srpaulo
4198178676Ssam/*
4199198429Srpaulo * Retrieve the average noise (in dBm) among receivers.
4200178676Ssam */
4201206477Sbschmidtstatic int
4202178676Ssamiwn_get_noise(const struct iwn_rx_general_stats *stats)
4203178676Ssam{
4204178676Ssam	int i, total, nbant, noise;
4205178676Ssam
4206178676Ssam	total = nbant = 0;
4207178676Ssam	for (i = 0; i < 3; i++) {
4208198429Srpaulo		if ((noise = le32toh(stats->noise[i]) & 0xff) == 0)
4209198429Srpaulo			continue;
4210198429Srpaulo		total += noise;
4211198429Srpaulo		nbant++;
4212178676Ssam	}
4213198429Srpaulo	/* There should be at least one antenna but check anyway. */
4214178676Ssam	return (nbant == 0) ? -127 : (total / nbant) - 107;
4215178676Ssam}
4216178676Ssam
4217178676Ssam/*
4218198429Srpaulo * Compute temperature (in degC) from last received statistics.
4219178676Ssam */
4220206477Sbschmidtstatic int
4221198429Srpauloiwn4965_get_temperature(struct iwn_softc *sc)
4222178676Ssam{
4223178676Ssam	struct iwn_ucode_info *uc = &sc->ucode_info;
4224178676Ssam	int32_t r1, r2, r3, r4, temp;
4225178676Ssam
4226178676Ssam	r1 = le32toh(uc->temp[0].chan20MHz);
4227178676Ssam	r2 = le32toh(uc->temp[1].chan20MHz);
4228178676Ssam	r3 = le32toh(uc->temp[2].chan20MHz);
4229178676Ssam	r4 = le32toh(sc->rawtemp);
4230178676Ssam
4231220725Sbschmidt	if (r1 == r3)	/* Prevents division by 0 (should not happen). */
4232178676Ssam		return 0;
4233178676Ssam
4234198429Srpaulo	/* Sign-extend 23-bit R4 value to 32-bit. */
4235220659Sbschmidt	r4 = ((r4 & 0xffffff) ^ 0x800000) - 0x800000;
4236198429Srpaulo	/* Compute temperature in Kelvin. */
4237178676Ssam	temp = (259 * (r4 - r2)) / (r3 - r1);
4238178676Ssam	temp = (temp * 97) / 100 + 8;
4239178676Ssam
4240201209Srpaulo	DPRINTF(sc, IWN_DEBUG_ANY, "temperature %dK/%dC\n", temp,
4241201209Srpaulo	    IWN_KTOC(temp));
4242178676Ssam	return IWN_KTOC(temp);
4243178676Ssam}
4244178676Ssam
4245206477Sbschmidtstatic int
4246198429Srpauloiwn5000_get_temperature(struct iwn_softc *sc)
4247198429Srpaulo{
4248201209Srpaulo	int32_t temp;
4249201209Srpaulo
4250198429Srpaulo	/*
4251198429Srpaulo	 * Temperature is not used by the driver for 5000 Series because
4252220725Sbschmidt	 * TX power calibration is handled by firmware.
4253198429Srpaulo	 */
4254201209Srpaulo	temp = le32toh(sc->rawtemp);
4255201209Srpaulo	if (sc->hw_type == IWN_HW_REV_TYPE_5150) {
4256201209Srpaulo		temp = (temp / -5) + sc->temp_off;
4257201209Srpaulo		temp = IWN_KTOC(temp);
4258201209Srpaulo	}
4259201209Srpaulo	return temp;
4260198429Srpaulo}
4261198429Srpaulo
4262178676Ssam/*
4263178676Ssam * Initialize sensitivity calibration state machine.
4264178676Ssam */
4265206477Sbschmidtstatic int
4266178676Ssamiwn_init_sensitivity(struct iwn_softc *sc)
4267178676Ssam{
4268220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
4269178676Ssam	struct iwn_calib_state *calib = &sc->calib;
4270198429Srpaulo	uint32_t flags;
4271178676Ssam	int error;
4272178676Ssam
4273198429Srpaulo	/* Reset calibration state machine. */
4274178676Ssam	memset(calib, 0, sizeof (*calib));
4275178676Ssam	calib->state = IWN_CALIB_STATE_INIT;
4276178676Ssam	calib->cck_state = IWN_CCK_STATE_HIFA;
4277198429Srpaulo	/* Set initial correlation values. */
4278201209Srpaulo	calib->ofdm_x1     = sc->limits->min_ofdm_x1;
4279201209Srpaulo	calib->ofdm_mrc_x1 = sc->limits->min_ofdm_mrc_x1;
4280206444Sbschmidt	calib->ofdm_x4     = sc->limits->min_ofdm_x4;
4281201209Srpaulo	calib->ofdm_mrc_x4 = sc->limits->min_ofdm_mrc_x4;
4282198429Srpaulo	calib->cck_x4      = 125;
4283201209Srpaulo	calib->cck_mrc_x4  = sc->limits->min_cck_mrc_x4;
4284201209Srpaulo	calib->energy_cck  = sc->limits->energy_cck;
4285178676Ssam
4286198429Srpaulo	/* Write initial sensitivity. */
4287220726Sbschmidt	if ((error = iwn_send_sensitivity(sc)) != 0)
4288178676Ssam		return error;
4289178676Ssam
4290198429Srpaulo	/* Write initial gains. */
4291220728Sbschmidt	if ((error = ops->init_gains(sc)) != 0)
4292198429Srpaulo		return error;
4293198429Srpaulo
4294198429Srpaulo	/* Request statistics at each beacon interval. */
4295198429Srpaulo	flags = 0;
4296220724Sbschmidt	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: sending request for statistics\n",
4297220724Sbschmidt	    __func__);
4298198429Srpaulo	return iwn_cmd(sc, IWN_CMD_GET_STATISTICS, &flags, sizeof flags, 1);
4299178676Ssam}
4300178676Ssam
4301178676Ssam/*
4302178676Ssam * Collect noise and RSSI statistics for the first 20 beacons received
4303178676Ssam * after association and use them to determine connected antennas and
4304198429Srpaulo * to set differential gains.
4305178676Ssam */
4306206477Sbschmidtstatic void
4307198429Srpauloiwn_collect_noise(struct iwn_softc *sc,
4308178676Ssam    const struct iwn_rx_general_stats *stats)
4309178676Ssam{
4310220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
4311178676Ssam	struct iwn_calib_state *calib = &sc->calib;
4312198429Srpaulo	uint32_t val;
4313198429Srpaulo	int i;
4314178676Ssam
4315198429Srpaulo	/* Accumulate RSSI and noise for all 3 antennas. */
4316178676Ssam	for (i = 0; i < 3; i++) {
4317178676Ssam		calib->rssi[i] += le32toh(stats->rssi[i]) & 0xff;
4318178676Ssam		calib->noise[i] += le32toh(stats->noise[i]) & 0xff;
4319178676Ssam	}
4320198429Srpaulo	/* NB: We update differential gains only once after 20 beacons. */
4321178676Ssam	if (++calib->nbeacons < 20)
4322178676Ssam		return;
4323178676Ssam
4324198429Srpaulo	/* Determine highest average RSSI. */
4325198429Srpaulo	val = MAX(calib->rssi[0], calib->rssi[1]);
4326198429Srpaulo	val = MAX(calib->rssi[2], val);
4327178676Ssam
4328198429Srpaulo	/* Determine which antennas are connected. */
4329210110Sbschmidt	sc->chainmask = sc->rxchainmask;
4330178676Ssam	for (i = 0; i < 3; i++)
4331210110Sbschmidt		if (val - calib->rssi[i] > 15 * 20)
4332210110Sbschmidt			sc->chainmask &= ~(1 << i);
4333210110Sbschmidt	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4334210110Sbschmidt	    "%s: RX chains mask: theoretical=0x%x, actual=0x%x\n",
4335210110Sbschmidt	    __func__, sc->rxchainmask, sc->chainmask);
4336210110Sbschmidt
4337198429Srpaulo	/* If none of the TX antennas are connected, keep at least one. */
4338201209Srpaulo	if ((sc->chainmask & sc->txchainmask) == 0)
4339201209Srpaulo		sc->chainmask |= IWN_LSB(sc->txchainmask);
4340178676Ssam
4341220728Sbschmidt	(void)ops->set_gains(sc);
4342198429Srpaulo	calib->state = IWN_CALIB_STATE_RUN;
4343198429Srpaulo
4344198429Srpaulo#ifdef notyet
4345198429Srpaulo	/* XXX Disable RX chains with no antennas connected. */
4346201209Srpaulo	sc->rxon.rxchain = htole16(IWN_RXCHAIN_SEL(sc->chainmask));
4347220728Sbschmidt	(void)iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, sc->rxonsz, 1);
4348198429Srpaulo#endif
4349198429Srpaulo
4350198429Srpaulo#if 0
4351198429Srpaulo	/* XXX: not yet */
4352198429Srpaulo	/* Enable power-saving mode if requested by user. */
4353198429Srpaulo	if (sc->sc_ic.ic_flags & IEEE80211_F_PMGTON)
4354198429Srpaulo		(void)iwn_set_pslevel(sc, 0, 3, 1);
4355198429Srpaulo#endif
4356198429Srpaulo}
4357198429Srpaulo
4358206477Sbschmidtstatic int
4359198429Srpauloiwn4965_init_gains(struct iwn_softc *sc)
4360198429Srpaulo{
4361198429Srpaulo	struct iwn_phy_calib_gain cmd;
4362198429Srpaulo
4363198429Srpaulo	memset(&cmd, 0, sizeof cmd);
4364198429Srpaulo	cmd.code = IWN4965_PHY_CALIB_DIFF_GAIN;
4365198429Srpaulo	/* Differential gains initially set to 0 for all 3 antennas. */
4366198429Srpaulo	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4367198429Srpaulo	    "%s: setting initial differential gains\n", __func__);
4368198429Srpaulo	return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1);
4369198429Srpaulo}
4370198429Srpaulo
4371206477Sbschmidtstatic int
4372198429Srpauloiwn5000_init_gains(struct iwn_softc *sc)
4373198429Srpaulo{
4374198429Srpaulo	struct iwn_phy_calib cmd;
4375198429Srpaulo
4376198429Srpaulo	memset(&cmd, 0, sizeof cmd);
4377220866Sbschmidt	cmd.code = sc->reset_noise_gain;
4378198429Srpaulo	cmd.ngroups = 1;
4379198429Srpaulo	cmd.isvalid = 1;
4380198429Srpaulo	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4381198429Srpaulo	    "%s: setting initial differential gains\n", __func__);
4382198429Srpaulo	return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1);
4383198429Srpaulo}
4384198429Srpaulo
4385206477Sbschmidtstatic int
4386198429Srpauloiwn4965_set_gains(struct iwn_softc *sc)
4387198429Srpaulo{
4388198429Srpaulo	struct iwn_calib_state *calib = &sc->calib;
4389198429Srpaulo	struct iwn_phy_calib_gain cmd;
4390198429Srpaulo	int i, delta, noise;
4391198429Srpaulo
4392198429Srpaulo	/* Get minimal noise among connected antennas. */
4393201209Srpaulo	noise = INT_MAX;	/* NB: There's at least one antenna. */
4394178676Ssam	for (i = 0; i < 3; i++)
4395201209Srpaulo		if (sc->chainmask & (1 << i))
4396198429Srpaulo			noise = MIN(calib->noise[i], noise);
4397178676Ssam
4398178676Ssam	memset(&cmd, 0, sizeof cmd);
4399198429Srpaulo	cmd.code = IWN4965_PHY_CALIB_DIFF_GAIN;
4400198429Srpaulo	/* Set differential gains for connected antennas. */
4401178676Ssam	for (i = 0; i < 3; i++) {
4402201209Srpaulo		if (sc->chainmask & (1 << i)) {
4403198429Srpaulo			/* Compute attenuation (in unit of 1.5dB). */
4404198429Srpaulo			delta = (noise - (int32_t)calib->noise[i]) / 30;
4405198429Srpaulo			/* NB: delta <= 0 */
4406198429Srpaulo			/* Limit to [-4.5dB,0]. */
4407198429Srpaulo			cmd.gain[i] = MIN(abs(delta), 3);
4408198429Srpaulo			if (delta < 0)
4409198429Srpaulo				cmd.gain[i] |= 1 << 2;	/* sign bit */
4410178676Ssam		}
4411178676Ssam	}
4412178676Ssam	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4413198429Srpaulo	    "setting differential gains Ant A/B/C: %x/%x/%x (%x)\n",
4414201209Srpaulo	    cmd.gain[0], cmd.gain[1], cmd.gain[2], sc->chainmask);
4415198429Srpaulo	return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1);
4416178676Ssam}
4417178676Ssam
4418206477Sbschmidtstatic int
4419198429Srpauloiwn5000_set_gains(struct iwn_softc *sc)
4420198429Srpaulo{
4421198429Srpaulo	struct iwn_calib_state *calib = &sc->calib;
4422198429Srpaulo	struct iwn_phy_calib_gain cmd;
4423220723Sbschmidt	int i, ant, div, delta;
4424198429Srpaulo
4425206444Sbschmidt	/* We collected 20 beacons and !=6050 need a 1.5 factor. */
4426206444Sbschmidt	div = (sc->hw_type == IWN_HW_REV_TYPE_6050) ? 20 : 30;
4427198429Srpaulo
4428198429Srpaulo	memset(&cmd, 0, sizeof cmd);
4429220866Sbschmidt	cmd.code = sc->noise_gain;
4430198429Srpaulo	cmd.ngroups = 1;
4431198429Srpaulo	cmd.isvalid = 1;
4432201209Srpaulo	/* Get first available RX antenna as referential. */
4433201209Srpaulo	ant = IWN_LSB(sc->rxchainmask);
4434201209Srpaulo	/* Set differential gains for other antennas. */
4435201209Srpaulo	for (i = ant + 1; i < 3; i++) {
4436201209Srpaulo		if (sc->chainmask & (1 << i)) {
4437201209Srpaulo			/* The delta is relative to antenna "ant". */
4438201209Srpaulo			delta = ((int32_t)calib->noise[ant] -
4439206444Sbschmidt			    (int32_t)calib->noise[i]) / div;
4440198429Srpaulo			/* Limit to [-4.5dB,+4.5dB]. */
4441198429Srpaulo			cmd.gain[i - 1] = MIN(abs(delta), 3);
4442198429Srpaulo			if (delta < 0)
4443198429Srpaulo				cmd.gain[i - 1] |= 1 << 2;	/* sign bit */
4444198429Srpaulo		}
4445198429Srpaulo	}
4446198429Srpaulo	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4447198429Srpaulo	    "setting differential gains Ant B/C: %x/%x (%x)\n",
4448201209Srpaulo	    cmd.gain[0], cmd.gain[1], sc->chainmask);
4449198429Srpaulo	return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1);
4450198429Srpaulo}
4451198429Srpaulo
4452178676Ssam/*
4453198429Srpaulo * Tune RF RX sensitivity based on the number of false alarms detected
4454178676Ssam * during the last beacon period.
4455178676Ssam */
4456206477Sbschmidtstatic void
4457178676Ssamiwn_tune_sensitivity(struct iwn_softc *sc, const struct iwn_rx_stats *stats)
4458178676Ssam{
4459198429Srpaulo#define inc(val, inc, max)			\
4460178676Ssam	if ((val) < (max)) {			\
4461178676Ssam		if ((val) < (max) - (inc))	\
4462178676Ssam			(val) += (inc);		\
4463178676Ssam		else				\
4464178676Ssam			(val) = (max);		\
4465178676Ssam		needs_update = 1;		\
4466178676Ssam	}
4467198429Srpaulo#define dec(val, dec, min)			\
4468178676Ssam	if ((val) > (min)) {			\
4469178676Ssam		if ((val) > (min) + (dec))	\
4470178676Ssam			(val) -= (dec);		\
4471178676Ssam		else				\
4472178676Ssam			(val) = (min);		\
4473178676Ssam		needs_update = 1;		\
4474178676Ssam	}
4475178676Ssam
4476201209Srpaulo	const struct iwn_sensitivity_limits *limits = sc->limits;
4477178676Ssam	struct iwn_calib_state *calib = &sc->calib;
4478178676Ssam	uint32_t val, rxena, fa;
4479178676Ssam	uint32_t energy[3], energy_min;
4480198439Srpaulo	uint8_t noise[3], noise_ref;
4481198429Srpaulo	int i, needs_update = 0;
4482178676Ssam
4483198429Srpaulo	/* Check that we've been enabled long enough. */
4484220726Sbschmidt	if ((rxena = le32toh(stats->general.load)) == 0)
4485178676Ssam		return;
4486178676Ssam
4487198429Srpaulo	/* Compute number of false alarms since last call for OFDM. */
4488178676Ssam	fa  = le32toh(stats->ofdm.bad_plcp) - calib->bad_plcp_ofdm;
4489178676Ssam	fa += le32toh(stats->ofdm.fa) - calib->fa_ofdm;
4490220634Sbschmidt	fa *= 200 * IEEE80211_DUR_TU;	/* 200TU */
4491178676Ssam
4492198429Srpaulo	/* Save counters values for next call. */
4493178676Ssam	calib->bad_plcp_ofdm = le32toh(stats->ofdm.bad_plcp);
4494178676Ssam	calib->fa_ofdm = le32toh(stats->ofdm.fa);
4495178676Ssam
4496178676Ssam	if (fa > 50 * rxena) {
4497198429Srpaulo		/* High false alarm count, decrease sensitivity. */
4498178676Ssam		DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4499178676Ssam		    "%s: OFDM high false alarm count: %u\n", __func__, fa);
4500198429Srpaulo		inc(calib->ofdm_x1,     1, limits->max_ofdm_x1);
4501198429Srpaulo		inc(calib->ofdm_mrc_x1, 1, limits->max_ofdm_mrc_x1);
4502198429Srpaulo		inc(calib->ofdm_x4,     1, limits->max_ofdm_x4);
4503198429Srpaulo		inc(calib->ofdm_mrc_x4, 1, limits->max_ofdm_mrc_x4);
4504178676Ssam
4505178676Ssam	} else if (fa < 5 * rxena) {
4506198429Srpaulo		/* Low false alarm count, increase sensitivity. */
4507178676Ssam		DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4508178676Ssam		    "%s: OFDM low false alarm count: %u\n", __func__, fa);
4509198429Srpaulo		dec(calib->ofdm_x1,     1, limits->min_ofdm_x1);
4510198429Srpaulo		dec(calib->ofdm_mrc_x1, 1, limits->min_ofdm_mrc_x1);
4511198429Srpaulo		dec(calib->ofdm_x4,     1, limits->min_ofdm_x4);
4512198429Srpaulo		dec(calib->ofdm_mrc_x4, 1, limits->min_ofdm_mrc_x4);
4513178676Ssam	}
4514178676Ssam
4515198429Srpaulo	/* Compute maximum noise among 3 receivers. */
4516178676Ssam	for (i = 0; i < 3; i++)
4517178676Ssam		noise[i] = (le32toh(stats->general.noise[i]) >> 8) & 0xff;
4518198429Srpaulo	val = MAX(noise[0], noise[1]);
4519198429Srpaulo	val = MAX(noise[2], val);
4520198429Srpaulo	/* Insert it into our samples table. */
4521178676Ssam	calib->noise_samples[calib->cur_noise_sample] = val;
4522178676Ssam	calib->cur_noise_sample = (calib->cur_noise_sample + 1) % 20;
4523178676Ssam
4524198429Srpaulo	/* Compute maximum noise among last 20 samples. */
4525178676Ssam	noise_ref = calib->noise_samples[0];
4526178676Ssam	for (i = 1; i < 20; i++)
4527198429Srpaulo		noise_ref = MAX(noise_ref, calib->noise_samples[i]);
4528178676Ssam
4529198429Srpaulo	/* Compute maximum energy among 3 receivers. */
4530178676Ssam	for (i = 0; i < 3; i++)
4531178676Ssam		energy[i] = le32toh(stats->general.energy[i]);
4532198429Srpaulo	val = MIN(energy[0], energy[1]);
4533198429Srpaulo	val = MIN(energy[2], val);
4534198429Srpaulo	/* Insert it into our samples table. */
4535178676Ssam	calib->energy_samples[calib->cur_energy_sample] = val;
4536178676Ssam	calib->cur_energy_sample = (calib->cur_energy_sample + 1) % 10;
4537178676Ssam
4538198429Srpaulo	/* Compute minimum energy among last 10 samples. */
4539178676Ssam	energy_min = calib->energy_samples[0];
4540178676Ssam	for (i = 1; i < 10; i++)
4541198429Srpaulo		energy_min = MAX(energy_min, calib->energy_samples[i]);
4542178676Ssam	energy_min += 6;
4543178676Ssam
4544198429Srpaulo	/* Compute number of false alarms since last call for CCK. */
4545178676Ssam	fa  = le32toh(stats->cck.bad_plcp) - calib->bad_plcp_cck;
4546178676Ssam	fa += le32toh(stats->cck.fa) - calib->fa_cck;
4547220634Sbschmidt	fa *= 200 * IEEE80211_DUR_TU;	/* 200TU */
4548178676Ssam
4549198429Srpaulo	/* Save counters values for next call. */
4550178676Ssam	calib->bad_plcp_cck = le32toh(stats->cck.bad_plcp);
4551178676Ssam	calib->fa_cck = le32toh(stats->cck.fa);
4552178676Ssam
4553178676Ssam	if (fa > 50 * rxena) {
4554198429Srpaulo		/* High false alarm count, decrease sensitivity. */
4555178676Ssam		DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4556178676Ssam		    "%s: CCK high false alarm count: %u\n", __func__, fa);
4557178676Ssam		calib->cck_state = IWN_CCK_STATE_HIFA;
4558178676Ssam		calib->low_fa = 0;
4559178676Ssam
4560198429Srpaulo		if (calib->cck_x4 > 160) {
4561178676Ssam			calib->noise_ref = noise_ref;
4562178676Ssam			if (calib->energy_cck > 2)
4563198429Srpaulo				dec(calib->energy_cck, 2, energy_min);
4564178676Ssam		}
4565198429Srpaulo		if (calib->cck_x4 < 160) {
4566198429Srpaulo			calib->cck_x4 = 161;
4567178676Ssam			needs_update = 1;
4568178676Ssam		} else
4569198429Srpaulo			inc(calib->cck_x4, 3, limits->max_cck_x4);
4570178676Ssam
4571198429Srpaulo		inc(calib->cck_mrc_x4, 3, limits->max_cck_mrc_x4);
4572178676Ssam
4573178676Ssam	} else if (fa < 5 * rxena) {
4574198429Srpaulo		/* Low false alarm count, increase sensitivity. */
4575178676Ssam		DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4576178676Ssam		    "%s: CCK low false alarm count: %u\n", __func__, fa);
4577178676Ssam		calib->cck_state = IWN_CCK_STATE_LOFA;
4578178676Ssam		calib->low_fa++;
4579178676Ssam
4580198429Srpaulo		if (calib->cck_state != IWN_CCK_STATE_INIT &&
4581198429Srpaulo		    (((int32_t)calib->noise_ref - (int32_t)noise_ref) > 2 ||
4582220726Sbschmidt		     calib->low_fa > 100)) {
4583198429Srpaulo			inc(calib->energy_cck, 2, limits->min_energy_cck);
4584198429Srpaulo			dec(calib->cck_x4,     3, limits->min_cck_x4);
4585198429Srpaulo			dec(calib->cck_mrc_x4, 3, limits->min_cck_mrc_x4);
4586178676Ssam		}
4587178676Ssam	} else {
4588198429Srpaulo		/* Not worth to increase or decrease sensitivity. */
4589178676Ssam		DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4590178676Ssam		    "%s: CCK normal false alarm count: %u\n", __func__, fa);
4591178676Ssam		calib->low_fa = 0;
4592178676Ssam		calib->noise_ref = noise_ref;
4593178676Ssam
4594178676Ssam		if (calib->cck_state == IWN_CCK_STATE_HIFA) {
4595198429Srpaulo			/* Previous interval had many false alarms. */
4596198429Srpaulo			dec(calib->energy_cck, 8, energy_min);
4597178676Ssam		}
4598178676Ssam		calib->cck_state = IWN_CCK_STATE_INIT;
4599178676Ssam	}
4600178676Ssam
4601178676Ssam	if (needs_update)
4602178676Ssam		(void)iwn_send_sensitivity(sc);
4603198429Srpaulo#undef dec
4604198429Srpaulo#undef inc
4605178676Ssam}
4606178676Ssam
4607206477Sbschmidtstatic int
4608178676Ssamiwn_send_sensitivity(struct iwn_softc *sc)
4609178676Ssam{
4610178676Ssam	struct iwn_calib_state *calib = &sc->calib;
4611220729Sbschmidt	struct iwn_enhanced_sensitivity_cmd cmd;
4612220729Sbschmidt	int len;
4613178676Ssam
4614178676Ssam	memset(&cmd, 0, sizeof cmd);
4615220729Sbschmidt	len = sizeof (struct iwn_sensitivity_cmd);
4616178676Ssam	cmd.which = IWN_SENSITIVITY_WORKTBL;
4617198429Srpaulo	/* OFDM modulation. */
4618220726Sbschmidt	cmd.corr_ofdm_x1       = htole16(calib->ofdm_x1);
4619220726Sbschmidt	cmd.corr_ofdm_mrc_x1   = htole16(calib->ofdm_mrc_x1);
4620220726Sbschmidt	cmd.corr_ofdm_x4       = htole16(calib->ofdm_x4);
4621220726Sbschmidt	cmd.corr_ofdm_mrc_x4   = htole16(calib->ofdm_mrc_x4);
4622220726Sbschmidt	cmd.energy_ofdm        = htole16(sc->limits->energy_ofdm);
4623220726Sbschmidt	cmd.energy_ofdm_th     = htole16(62);
4624198429Srpaulo	/* CCK modulation. */
4625220726Sbschmidt	cmd.corr_cck_x4        = htole16(calib->cck_x4);
4626220726Sbschmidt	cmd.corr_cck_mrc_x4    = htole16(calib->cck_mrc_x4);
4627220726Sbschmidt	cmd.energy_cck         = htole16(calib->energy_cck);
4628198429Srpaulo	/* Barker modulation: use default values. */
4629220726Sbschmidt	cmd.corr_barker        = htole16(190);
4630220726Sbschmidt	cmd.corr_barker_mrc    = htole16(390);
4631178676Ssam
4632202986Srpaulo	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4633178676Ssam	    "%s: set sensitivity %d/%d/%d/%d/%d/%d/%d\n", __func__,
4634198429Srpaulo	    calib->ofdm_x1, calib->ofdm_mrc_x1, calib->ofdm_x4,
4635198429Srpaulo	    calib->ofdm_mrc_x4, calib->cck_x4,
4636198429Srpaulo	    calib->cck_mrc_x4, calib->energy_cck);
4637220729Sbschmidt
4638220729Sbschmidt	if (!(sc->sc_flags & IWN_FLAG_ENH_SENS))
4639220729Sbschmidt		goto send;
4640220729Sbschmidt	/* Enhanced sensitivity settings. */
4641220729Sbschmidt	len = sizeof (struct iwn_enhanced_sensitivity_cmd);
4642220729Sbschmidt	cmd.ofdm_det_slope_mrc = htole16(668);
4643220729Sbschmidt	cmd.ofdm_det_icept_mrc = htole16(4);
4644220729Sbschmidt	cmd.ofdm_det_slope     = htole16(486);
4645220729Sbschmidt	cmd.ofdm_det_icept     = htole16(37);
4646220729Sbschmidt	cmd.cck_det_slope_mrc  = htole16(853);
4647220729Sbschmidt	cmd.cck_det_icept_mrc  = htole16(4);
4648220729Sbschmidt	cmd.cck_det_slope      = htole16(476);
4649220729Sbschmidt	cmd.cck_det_icept      = htole16(99);
4650220729Sbschmidtsend:
4651220729Sbschmidt	return iwn_cmd(sc, IWN_CMD_SET_SENSITIVITY, &cmd, len, 1);
4652178676Ssam}
4653178676Ssam
4654198429Srpaulo/*
4655198429Srpaulo * Set STA mode power saving level (between 0 and 5).
4656198429Srpaulo * Level 0 is CAM (Continuously Aware Mode), 5 is for maximum power saving.
4657198429Srpaulo */
4658206477Sbschmidtstatic int
4659198429Srpauloiwn_set_pslevel(struct iwn_softc *sc, int dtim, int level, int async)
4660198429Srpaulo{
4661220723Sbschmidt	struct iwn_pmgt_cmd cmd;
4662198429Srpaulo	const struct iwn_pmgt *pmgt;
4663198429Srpaulo	uint32_t max, skip_dtim;
4664220721Sbschmidt	uint32_t reg;
4665198429Srpaulo	int i;
4666198429Srpaulo
4667198429Srpaulo	/* Select which PS parameters to use. */
4668198429Srpaulo	if (dtim <= 2)
4669198429Srpaulo		pmgt = &iwn_pmgt[0][level];
4670198429Srpaulo	else if (dtim <= 10)
4671198429Srpaulo		pmgt = &iwn_pmgt[1][level];
4672198429Srpaulo	else
4673198429Srpaulo		pmgt = &iwn_pmgt[2][level];
4674198429Srpaulo
4675198429Srpaulo	memset(&cmd, 0, sizeof cmd);
4676198429Srpaulo	if (level != 0)	/* not CAM */
4677198429Srpaulo		cmd.flags |= htole16(IWN_PS_ALLOW_SLEEP);
4678198429Srpaulo	if (level == 5)
4679198429Srpaulo		cmd.flags |= htole16(IWN_PS_FAST_PD);
4680201209Srpaulo	/* Retrieve PCIe Active State Power Management (ASPM). */
4681220721Sbschmidt	reg = pci_read_config(sc->sc_dev, sc->sc_cap_off + 0x10, 1);
4682220721Sbschmidt	if (!(reg & 0x1))	/* L0s Entry disabled. */
4683198429Srpaulo		cmd.flags |= htole16(IWN_PS_PCI_PMGT);
4684198429Srpaulo	cmd.rxtimeout = htole32(pmgt->rxtimeout * 1024);
4685198429Srpaulo	cmd.txtimeout = htole32(pmgt->txtimeout * 1024);
4686198429Srpaulo
4687198429Srpaulo	if (dtim == 0) {
4688198429Srpaulo		dtim = 1;
4689198429Srpaulo		skip_dtim = 0;
4690198429Srpaulo	} else
4691198429Srpaulo		skip_dtim = pmgt->skip_dtim;
4692198429Srpaulo	if (skip_dtim != 0) {
4693198429Srpaulo		cmd.flags |= htole16(IWN_PS_SLEEP_OVER_DTIM);
4694198429Srpaulo		max = pmgt->intval[4];
4695198429Srpaulo		if (max == (uint32_t)-1)
4696198429Srpaulo			max = dtim * (skip_dtim + 1);
4697198429Srpaulo		else if (max > dtim)
4698198429Srpaulo			max = (max / dtim) * dtim;
4699198429Srpaulo	} else
4700198429Srpaulo		max = dtim;
4701198429Srpaulo	for (i = 0; i < 5; i++)
4702198429Srpaulo		cmd.intval[i] = htole32(MIN(max, pmgt->intval[i]));
4703198429Srpaulo
4704198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "setting power saving level to %d\n",
4705198429Srpaulo	    level);
4706198429Srpaulo	return iwn_cmd(sc, IWN_CMD_SET_POWER_MODE, &cmd, sizeof cmd, async);
4707198429Srpaulo}
4708198429Srpaulo
4709206477Sbschmidtstatic int
4710220662Sbschmidtiwn_send_btcoex(struct iwn_softc *sc)
4711220662Sbschmidt{
4712220662Sbschmidt	struct iwn_bluetooth cmd;
4713220662Sbschmidt
4714220662Sbschmidt	memset(&cmd, 0, sizeof cmd);
4715220662Sbschmidt	cmd.flags = IWN_BT_COEX_CHAN_ANN | IWN_BT_COEX_BT_PRIO;
4716220662Sbschmidt	cmd.lead_time = IWN_BT_LEAD_TIME_DEF;
4717220662Sbschmidt	cmd.max_kill = IWN_BT_MAX_KILL_DEF;
4718220662Sbschmidt	DPRINTF(sc, IWN_DEBUG_RESET, "%s: configuring bluetooth coexistence\n",
4719220662Sbschmidt	    __func__);
4720220662Sbschmidt	return iwn_cmd(sc, IWN_CMD_BT_COEX, &cmd, sizeof(cmd), 0);
4721220662Sbschmidt}
4722220662Sbschmidt
4723220662Sbschmidtstatic int
4724220891Sbschmidtiwn_send_advanced_btcoex(struct iwn_softc *sc)
4725220891Sbschmidt{
4726220891Sbschmidt	static const uint32_t btcoex_3wire[12] = {
4727220891Sbschmidt		0xaaaaaaaa, 0xaaaaaaaa, 0xaeaaaaaa, 0xaaaaaaaa,
4728220891Sbschmidt		0xcc00ff28, 0x0000aaaa, 0xcc00aaaa, 0x0000aaaa,
4729220891Sbschmidt		0xc0004000, 0x00004000, 0xf0005000, 0xf0005000,
4730220891Sbschmidt	};
4731220891Sbschmidt	struct iwn6000_btcoex_config btconfig;
4732220891Sbschmidt	struct iwn_btcoex_priotable btprio;
4733220891Sbschmidt	struct iwn_btcoex_prot btprot;
4734220891Sbschmidt	int error, i;
4735220891Sbschmidt
4736220891Sbschmidt	memset(&btconfig, 0, sizeof btconfig);
4737220891Sbschmidt	btconfig.flags = 145;
4738220891Sbschmidt	btconfig.max_kill = 5;
4739220891Sbschmidt	btconfig.bt3_t7_timer = 1;
4740220891Sbschmidt	btconfig.kill_ack = htole32(0xffff0000);
4741220891Sbschmidt	btconfig.kill_cts = htole32(0xffff0000);
4742220891Sbschmidt	btconfig.sample_time = 2;
4743220891Sbschmidt	btconfig.bt3_t2_timer = 0xc;
4744220891Sbschmidt	for (i = 0; i < 12; i++)
4745220891Sbschmidt		btconfig.lookup_table[i] = htole32(btcoex_3wire[i]);
4746220891Sbschmidt	btconfig.valid = htole16(0xff);
4747220891Sbschmidt	btconfig.prio_boost = 0xf0;
4748220891Sbschmidt	DPRINTF(sc, IWN_DEBUG_RESET,
4749220891Sbschmidt	    "%s: configuring advanced bluetooth coexistence\n", __func__);
4750220891Sbschmidt	error = iwn_cmd(sc, IWN_CMD_BT_COEX, &btconfig, sizeof(btconfig), 1);
4751220891Sbschmidt	if (error != 0)
4752220891Sbschmidt		return error;
4753220891Sbschmidt
4754220891Sbschmidt	memset(&btprio, 0, sizeof btprio);
4755220891Sbschmidt	btprio.calib_init1 = 0x6;
4756220891Sbschmidt	btprio.calib_init2 = 0x7;
4757220891Sbschmidt	btprio.calib_periodic_low1 = 0x2;
4758220891Sbschmidt	btprio.calib_periodic_low2 = 0x3;
4759220891Sbschmidt	btprio.calib_periodic_high1 = 0x4;
4760220891Sbschmidt	btprio.calib_periodic_high2 = 0x5;
4761220891Sbschmidt	btprio.dtim = 0x6;
4762220891Sbschmidt	btprio.scan52 = 0x8;
4763220891Sbschmidt	btprio.scan24 = 0xa;
4764220891Sbschmidt	error = iwn_cmd(sc, IWN_CMD_BT_COEX_PRIOTABLE, &btprio, sizeof(btprio),
4765220891Sbschmidt	    1);
4766220891Sbschmidt	if (error != 0)
4767220891Sbschmidt		return error;
4768220891Sbschmidt
4769220891Sbschmidt	/* Force BT state machine change. */
4770220891Sbschmidt	memset(&btprot, 0, sizeof btprio);
4771220891Sbschmidt	btprot.open = 1;
4772220891Sbschmidt	btprot.type = 1;
4773220891Sbschmidt	error = iwn_cmd(sc, IWN_CMD_BT_COEX_PROT, &btprot, sizeof(btprot), 1);
4774220891Sbschmidt	if (error != 0)
4775220891Sbschmidt		return error;
4776220891Sbschmidt	btprot.open = 0;
4777220891Sbschmidt	return iwn_cmd(sc, IWN_CMD_BT_COEX_PROT, &btprot, sizeof(btprot), 1);
4778220891Sbschmidt}
4779220891Sbschmidt
4780220891Sbschmidtstatic int
4781198429Srpauloiwn_config(struct iwn_softc *sc)
4782198429Srpaulo{
4783220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
4784198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
4785198429Srpaulo	struct ieee80211com *ic = ifp->if_l2com;
4786201209Srpaulo	uint32_t txmask;
4787220723Sbschmidt	uint16_t rxchain;
4788198429Srpaulo	int error;
4789198429Srpaulo
4790220676Sbschmidt	if (sc->hw_type == IWN_HW_REV_TYPE_6005) {
4791220676Sbschmidt		/* Set radio temperature sensor offset. */
4792220676Sbschmidt		error = iwn5000_temp_offset_calib(sc);
4793220676Sbschmidt		if (error != 0) {
4794220676Sbschmidt			device_printf(sc->sc_dev,
4795220676Sbschmidt			    "%s: could not set temperature offset\n", __func__);
4796220676Sbschmidt			return error;
4797220676Sbschmidt		}
4798220676Sbschmidt	}
4799220676Sbschmidt
4800220725Sbschmidt	/* Configure valid TX chains for >=5000 Series. */
4801201209Srpaulo	if (sc->hw_type != IWN_HW_REV_TYPE_4965) {
4802201209Srpaulo		txmask = htole32(sc->txchainmask);
4803201209Srpaulo		DPRINTF(sc, IWN_DEBUG_RESET,
4804201209Srpaulo		    "%s: configuring valid TX chains 0x%x\n", __func__, txmask);
4805201209Srpaulo		error = iwn_cmd(sc, IWN5000_CMD_TX_ANT_CONFIG, &txmask,
4806201209Srpaulo		    sizeof txmask, 0);
4807201209Srpaulo		if (error != 0) {
4808201209Srpaulo			device_printf(sc->sc_dev,
4809201209Srpaulo			    "%s: could not configure valid TX chains, "
4810201209Srpaulo			    "error %d\n", __func__, error);
4811201209Srpaulo			return error;
4812201209Srpaulo		}
4813198429Srpaulo	}
4814198429Srpaulo
4815198429Srpaulo	/* Configure bluetooth coexistence. */
4816220891Sbschmidt	if (sc->sc_flags & IWN_FLAG_ADV_BTCOEX)
4817220891Sbschmidt		error = iwn_send_advanced_btcoex(sc);
4818220891Sbschmidt	else
4819220891Sbschmidt		error = iwn_send_btcoex(sc);
4820198429Srpaulo	if (error != 0) {
4821198429Srpaulo		device_printf(sc->sc_dev,
4822198429Srpaulo		    "%s: could not configure bluetooth coexistence, error %d\n",
4823198429Srpaulo		    __func__, error);
4824198429Srpaulo		return error;
4825198429Srpaulo	}
4826198429Srpaulo
4827201209Srpaulo	/* Set mode, channel, RX filter and enable RX. */
4828198429Srpaulo	memset(&sc->rxon, 0, sizeof (struct iwn_rxon));
4829198429Srpaulo	IEEE80211_ADDR_COPY(sc->rxon.myaddr, IF_LLADDR(ifp));
4830198429Srpaulo	IEEE80211_ADDR_COPY(sc->rxon.wlap, IF_LLADDR(ifp));
4831198429Srpaulo	sc->rxon.chan = ieee80211_chan2ieee(ic, ic->ic_curchan);
4832198429Srpaulo	sc->rxon.flags = htole32(IWN_RXON_TSF | IWN_RXON_CTS_TO_SELF);
4833198429Srpaulo	if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan))
4834198429Srpaulo		sc->rxon.flags |= htole32(IWN_RXON_AUTO | IWN_RXON_24GHZ);
4835198429Srpaulo	switch (ic->ic_opmode) {
4836198429Srpaulo	case IEEE80211_M_STA:
4837198429Srpaulo		sc->rxon.mode = IWN_MODE_STA;
4838198429Srpaulo		sc->rxon.filter = htole32(IWN_FILTER_MULTICAST);
4839198429Srpaulo		break;
4840198429Srpaulo	case IEEE80211_M_MONITOR:
4841198429Srpaulo		sc->rxon.mode = IWN_MODE_MONITOR;
4842198429Srpaulo		sc->rxon.filter = htole32(IWN_FILTER_MULTICAST |
4843198429Srpaulo		    IWN_FILTER_CTL | IWN_FILTER_PROMISC);
4844198429Srpaulo		break;
4845198429Srpaulo	default:
4846198429Srpaulo		/* Should not get there. */
4847198429Srpaulo		break;
4848198429Srpaulo	}
4849198429Srpaulo	sc->rxon.cck_mask  = 0x0f;	/* not yet negotiated */
4850198429Srpaulo	sc->rxon.ofdm_mask = 0xff;	/* not yet negotiated */
4851198429Srpaulo	sc->rxon.ht_single_mask = 0xff;
4852198429Srpaulo	sc->rxon.ht_dual_mask = 0xff;
4853201209Srpaulo	sc->rxon.ht_triple_mask = 0xff;
4854201209Srpaulo	rxchain =
4855201209Srpaulo	    IWN_RXCHAIN_VALID(sc->rxchainmask) |
4856201209Srpaulo	    IWN_RXCHAIN_MIMO_COUNT(2) |
4857201209Srpaulo	    IWN_RXCHAIN_IDLE_COUNT(2);
4858198429Srpaulo	sc->rxon.rxchain = htole16(rxchain);
4859198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "%s: setting configuration\n", __func__);
4860220728Sbschmidt	error = iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, sc->rxonsz, 0);
4861198429Srpaulo	if (error != 0) {
4862220726Sbschmidt		device_printf(sc->sc_dev, "%s: RXON command failed\n",
4863220726Sbschmidt		    __func__);
4864198429Srpaulo		return error;
4865198429Srpaulo	}
4866198429Srpaulo
4867220726Sbschmidt	if ((error = iwn_add_broadcast_node(sc, 0)) != 0) {
4868220726Sbschmidt		device_printf(sc->sc_dev, "%s: could not add broadcast node\n",
4869220726Sbschmidt		    __func__);
4870201209Srpaulo		return error;
4871201209Srpaulo	}
4872201209Srpaulo
4873198429Srpaulo	/* Configuration has changed, set TX power accordingly. */
4874220728Sbschmidt	if ((error = ops->set_txpower(sc, ic->ic_curchan, 0)) != 0) {
4875220726Sbschmidt		device_printf(sc->sc_dev, "%s: could not set TX power\n",
4876220726Sbschmidt		    __func__);
4877198429Srpaulo		return error;
4878198429Srpaulo	}
4879198429Srpaulo
4880220726Sbschmidt	if ((error = iwn_set_critical_temp(sc)) != 0) {
4881198429Srpaulo		device_printf(sc->sc_dev,
4882220724Sbschmidt		    "%s: could not set critical temperature\n", __func__);
4883198429Srpaulo		return error;
4884198429Srpaulo	}
4885198429Srpaulo
4886201209Srpaulo	/* Set power saving level to CAM during initialization. */
4887220726Sbschmidt	if ((error = iwn_set_pslevel(sc, 0, 0, 0)) != 0) {
4888198429Srpaulo		device_printf(sc->sc_dev,
4889201209Srpaulo		    "%s: could not set power saving level\n", __func__);
4890198429Srpaulo		return error;
4891198429Srpaulo	}
4892198429Srpaulo	return 0;
4893198429Srpaulo}
4894198429Srpaulo
4895220634Sbschmidt/*
4896220634Sbschmidt * Add an ssid element to a frame.
4897220634Sbschmidt */
4898220634Sbschmidtstatic uint8_t *
4899220634Sbschmidtieee80211_add_ssid(uint8_t *frm, const uint8_t *ssid, u_int len)
4900220634Sbschmidt{
4901220634Sbschmidt	*frm++ = IEEE80211_ELEMID_SSID;
4902220634Sbschmidt	*frm++ = len;
4903220634Sbschmidt	memcpy(frm, ssid, len);
4904220634Sbschmidt	return frm + len;
4905220634Sbschmidt}
4906220634Sbschmidt
4907206477Sbschmidtstatic int
4908198429Srpauloiwn_scan(struct iwn_softc *sc)
4909198429Srpaulo{
4910198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
4911198429Srpaulo	struct ieee80211com *ic = ifp->if_l2com;
4912198429Srpaulo	struct ieee80211_scan_state *ss = ic->ic_scan;	/*XXX*/
4913198429Srpaulo	struct iwn_scan_hdr *hdr;
4914198429Srpaulo	struct iwn_cmd_data *tx;
4915198429Srpaulo	struct iwn_scan_essid *essid;
4916198429Srpaulo	struct iwn_scan_chan *chan;
4917198429Srpaulo	struct ieee80211_frame *wh;
4918198429Srpaulo	struct ieee80211_rateset *rs;
4919198429Srpaulo	struct ieee80211_channel *c;
4920220726Sbschmidt	uint8_t *buf, *frm;
4921220723Sbschmidt	uint16_t rxchain;
4922220726Sbschmidt	uint8_t txant;
4923220634Sbschmidt	int buflen, error;
4924198429Srpaulo
4925198429Srpaulo	buf = malloc(IWN_SCAN_MAXSZ, M_DEVBUF, M_NOWAIT | M_ZERO);
4926198429Srpaulo	if (buf == NULL) {
4927198429Srpaulo		device_printf(sc->sc_dev,
4928198429Srpaulo		    "%s: could not allocate buffer for scan command\n",
4929198429Srpaulo		    __func__);
4930198429Srpaulo		return ENOMEM;
4931198429Srpaulo	}
4932198429Srpaulo	hdr = (struct iwn_scan_hdr *)buf;
4933198429Srpaulo	/*
4934198429Srpaulo	 * Move to the next channel if no frames are received within 10ms
4935198429Srpaulo	 * after sending the probe request.
4936198429Srpaulo	 */
4937198429Srpaulo	hdr->quiet_time = htole16(10);		/* timeout in milliseconds */
4938198429Srpaulo	hdr->quiet_threshold = htole16(1);	/* min # of packets */
4939198429Srpaulo
4940198429Srpaulo	/* Select antennas for scanning. */
4941201209Srpaulo	rxchain =
4942201209Srpaulo	    IWN_RXCHAIN_VALID(sc->rxchainmask) |
4943201209Srpaulo	    IWN_RXCHAIN_FORCE_MIMO_SEL(sc->rxchainmask) |
4944201209Srpaulo	    IWN_RXCHAIN_DRIVER_FORCE;
4945198429Srpaulo	if (IEEE80211_IS_CHAN_A(ic->ic_curchan) &&
4946198429Srpaulo	    sc->hw_type == IWN_HW_REV_TYPE_4965) {
4947198429Srpaulo		/* Ant A must be avoided in 5GHz because of an HW bug. */
4948201209Srpaulo		rxchain |= IWN_RXCHAIN_FORCE_SEL(IWN_ANT_BC);
4949198429Srpaulo	} else	/* Use all available RX antennas. */
4950201209Srpaulo		rxchain |= IWN_RXCHAIN_FORCE_SEL(sc->rxchainmask);
4951198429Srpaulo	hdr->rxchain = htole16(rxchain);
4952198429Srpaulo	hdr->filter = htole32(IWN_FILTER_MULTICAST | IWN_FILTER_BEACON);
4953198429Srpaulo
4954198429Srpaulo	tx = (struct iwn_cmd_data *)(hdr + 1);
4955198429Srpaulo	tx->flags = htole32(IWN_TX_AUTO_SEQ);
4956220728Sbschmidt	tx->id = sc->broadcast_id;
4957198429Srpaulo	tx->lifetime = htole32(IWN_LIFETIME_INFINITE);
4958198429Srpaulo
4959198429Srpaulo	if (IEEE80211_IS_CHAN_A(ic->ic_curchan)) {
4960198429Srpaulo		/* Send probe requests at 6Mbps. */
4961198429Srpaulo		tx->plcp = iwn_rates[IWN_RIDX_OFDM6].plcp;
4962201209Srpaulo		rs = &ic->ic_sup_rates[IEEE80211_MODE_11A];
4963198429Srpaulo	} else {
4964198429Srpaulo		hdr->flags = htole32(IWN_RXON_24GHZ | IWN_RXON_AUTO);
4965198429Srpaulo		/* Send probe requests at 1Mbps. */
4966198429Srpaulo		tx->plcp = iwn_rates[IWN_RIDX_CCK1].plcp;
4967198429Srpaulo		tx->rflags = IWN_RFLAG_CCK;
4968201209Srpaulo		rs = &ic->ic_sup_rates[IEEE80211_MODE_11G];
4969198429Srpaulo	}
4970198429Srpaulo	/* Use the first valid TX antenna. */
4971201209Srpaulo	txant = IWN_LSB(sc->txchainmask);
4972198429Srpaulo	tx->rflags |= IWN_RFLAG_ANT(txant);
4973198429Srpaulo
4974198429Srpaulo	essid = (struct iwn_scan_essid *)(tx + 1);
4975198429Srpaulo	if (ss->ss_ssid[0].len != 0) {
4976198429Srpaulo		essid[0].id = IEEE80211_ELEMID_SSID;
4977198429Srpaulo		essid[0].len = ss->ss_ssid[0].len;
4978198429Srpaulo		memcpy(essid[0].data, ss->ss_ssid[0].ssid, ss->ss_ssid[0].len);
4979198429Srpaulo	}
4980198429Srpaulo	/*
4981198429Srpaulo	 * Build a probe request frame.  Most of the following code is a
4982198429Srpaulo	 * copy & paste of what is done in net80211.
4983198429Srpaulo	 */
4984198429Srpaulo	wh = (struct ieee80211_frame *)(essid + 20);
4985198429Srpaulo	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT |
4986198429Srpaulo	    IEEE80211_FC0_SUBTYPE_PROBE_REQ;
4987198429Srpaulo	wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
4988198429Srpaulo	IEEE80211_ADDR_COPY(wh->i_addr1, ifp->if_broadcastaddr);
4989198429Srpaulo	IEEE80211_ADDR_COPY(wh->i_addr2, IF_LLADDR(ifp));
4990198429Srpaulo	IEEE80211_ADDR_COPY(wh->i_addr3, ifp->if_broadcastaddr);
4991198429Srpaulo	*(uint16_t *)&wh->i_dur[0] = 0;	/* filled by HW */
4992198429Srpaulo	*(uint16_t *)&wh->i_seq[0] = 0;	/* filled by HW */
4993198429Srpaulo
4994198429Srpaulo	frm = (uint8_t *)(wh + 1);
4995220634Sbschmidt	frm = ieee80211_add_ssid(frm, NULL, 0);
4996220634Sbschmidt	frm = ieee80211_add_rates(frm, rs);
4997220634Sbschmidt	if (rs->rs_nrates > IEEE80211_RATE_SIZE)
4998220634Sbschmidt		frm = ieee80211_add_xrates(frm, rs);
4999220634Sbschmidt#if 0	/* HT */
5000220634Sbschmidt	if (ic->ic_flags & IEEE80211_F_HTON)
5001220634Sbschmidt		frm = ieee80211_add_htcaps(frm, ic);
5002220634Sbschmidt#endif
5003198429Srpaulo
5004198429Srpaulo	/* Set length of probe request. */
5005198429Srpaulo	tx->len = htole16(frm - (uint8_t *)wh);
5006198429Srpaulo
5007198429Srpaulo	c = ic->ic_curchan;
5008198429Srpaulo	chan = (struct iwn_scan_chan *)frm;
5009201209Srpaulo	chan->chan = htole16(ieee80211_chan2ieee(ic, c));
5010198429Srpaulo	chan->flags = 0;
5011198429Srpaulo	if (ss->ss_nssid > 0)
5012198429Srpaulo		chan->flags |= htole32(IWN_CHAN_NPBREQS(1));
5013198429Srpaulo	chan->dsp_gain = 0x6e;
5014201209Srpaulo	if (IEEE80211_IS_CHAN_5GHZ(c) &&
5015201209Srpaulo	    !(c->ic_flags & IEEE80211_CHAN_PASSIVE)) {
5016198429Srpaulo		chan->rf_gain = 0x3b;
5017198429Srpaulo		chan->active  = htole16(24);
5018198429Srpaulo		chan->passive = htole16(110);
5019201209Srpaulo		chan->flags |= htole32(IWN_CHAN_ACTIVE);
5020201209Srpaulo	} else if (IEEE80211_IS_CHAN_5GHZ(c)) {
5021201209Srpaulo		chan->rf_gain = 0x3b;
5022201209Srpaulo		chan->active  = htole16(24);
5023201209Srpaulo		if (sc->rxon.associd)
5024201209Srpaulo			chan->passive = htole16(78);
5025201209Srpaulo		else
5026201209Srpaulo			chan->passive = htole16(110);
5027207709Sbschmidt		hdr->crc_threshold = 0xffff;
5028201209Srpaulo	} else if (!(c->ic_flags & IEEE80211_CHAN_PASSIVE)) {
5029201209Srpaulo		chan->rf_gain = 0x28;
5030201209Srpaulo		chan->active  = htole16(36);
5031201209Srpaulo		chan->passive = htole16(120);
5032201209Srpaulo		chan->flags |= htole32(IWN_CHAN_ACTIVE);
5033198429Srpaulo	} else {
5034198429Srpaulo		chan->rf_gain = 0x28;
5035198429Srpaulo		chan->active  = htole16(36);
5036201209Srpaulo		if (sc->rxon.associd)
5037201209Srpaulo			chan->passive = htole16(88);
5038201209Srpaulo		else
5039201209Srpaulo			chan->passive = htole16(120);
5040207709Sbschmidt		hdr->crc_threshold = 0xffff;
5041198429Srpaulo	}
5042198429Srpaulo
5043201209Srpaulo	DPRINTF(sc, IWN_DEBUG_STATE,
5044201209Srpaulo	    "%s: chan %u flags 0x%x rf_gain 0x%x "
5045198429Srpaulo	    "dsp_gain 0x%x active 0x%x passive 0x%x\n", __func__,
5046198429Srpaulo	    chan->chan, chan->flags, chan->rf_gain, chan->dsp_gain,
5047198429Srpaulo	    chan->active, chan->passive);
5048198429Srpaulo
5049201209Srpaulo	hdr->nchan++;
5050201209Srpaulo	chan++;
5051198429Srpaulo	buflen = (uint8_t *)chan - buf;
5052198429Srpaulo	hdr->len = htole16(buflen);
5053198429Srpaulo
5054198429Srpaulo	DPRINTF(sc, IWN_DEBUG_STATE, "sending scan command nchan=%d\n",
5055198429Srpaulo	    hdr->nchan);
5056198429Srpaulo	error = iwn_cmd(sc, IWN_CMD_SCAN, buf, buflen, 1);
5057198429Srpaulo	free(buf, M_DEVBUF);
5058198429Srpaulo	return error;
5059198429Srpaulo}
5060198429Srpaulo
5061206477Sbschmidtstatic int
5062191746Sthompsaiwn_auth(struct iwn_softc *sc, struct ieee80211vap *vap)
5063178676Ssam{
5064220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
5065178676Ssam	struct ifnet *ifp = sc->sc_ifp;
5066178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
5067178676Ssam	struct ieee80211_node *ni = vap->iv_bss;
5068178676Ssam	int error;
5069178676Ssam
5070201209Srpaulo	/* Update adapter configuration. */
5071198429Srpaulo	IEEE80211_ADDR_COPY(sc->rxon.bssid, ni->ni_bssid);
5072220636Sbschmidt	sc->rxon.chan = ieee80211_chan2ieee(ic, ni->ni_chan);
5073198429Srpaulo	sc->rxon.flags = htole32(IWN_RXON_TSF | IWN_RXON_CTS_TO_SELF);
5074178676Ssam	if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
5075198429Srpaulo		sc->rxon.flags |= htole32(IWN_RXON_AUTO | IWN_RXON_24GHZ);
5076198429Srpaulo	if (ic->ic_flags & IEEE80211_F_SHSLOT)
5077198429Srpaulo		sc->rxon.flags |= htole32(IWN_RXON_SHSLOT);
5078198429Srpaulo	if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
5079198429Srpaulo		sc->rxon.flags |= htole32(IWN_RXON_SHPREAMBLE);
5080178676Ssam	if (IEEE80211_IS_CHAN_A(ni->ni_chan)) {
5081198429Srpaulo		sc->rxon.cck_mask  = 0;
5082198429Srpaulo		sc->rxon.ofdm_mask = 0x15;
5083178676Ssam	} else if (IEEE80211_IS_CHAN_B(ni->ni_chan)) {
5084198429Srpaulo		sc->rxon.cck_mask  = 0x03;
5085198429Srpaulo		sc->rxon.ofdm_mask = 0;
5086178676Ssam	} else {
5087220725Sbschmidt		/* Assume 802.11b/g. */
5088198429Srpaulo		sc->rxon.cck_mask  = 0x0f;
5089198429Srpaulo		sc->rxon.ofdm_mask = 0x15;
5090178676Ssam	}
5091220724Sbschmidt	DPRINTF(sc, IWN_DEBUG_STATE, "rxon chan %d flags %x cck %x ofdm %x\n",
5092220724Sbschmidt	    sc->rxon.chan, sc->rxon.flags, sc->rxon.cck_mask,
5093220724Sbschmidt	    sc->rxon.ofdm_mask);
5094220728Sbschmidt	error = iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, sc->rxonsz, 1);
5095178676Ssam	if (error != 0) {
5096220726Sbschmidt		device_printf(sc->sc_dev, "%s: RXON command failed, error %d\n",
5097220726Sbschmidt		    __func__, error);
5098178676Ssam		return error;
5099178676Ssam	}
5100178676Ssam
5101198429Srpaulo	/* Configuration has changed, set TX power accordingly. */
5102220728Sbschmidt	if ((error = ops->set_txpower(sc, ni->ni_chan, 1)) != 0) {
5103178676Ssam		device_printf(sc->sc_dev,
5104220724Sbschmidt		    "%s: could not set TX power, error %d\n", __func__, error);
5105178676Ssam		return error;
5106178676Ssam	}
5107178676Ssam	/*
5108201209Srpaulo	 * Reconfiguring RXON clears the firmware nodes table so we must
5109178676Ssam	 * add the broadcast node again.
5110178676Ssam	 */
5111220726Sbschmidt	if ((error = iwn_add_broadcast_node(sc, 1)) != 0) {
5112178676Ssam		device_printf(sc->sc_dev,
5113220726Sbschmidt		    "%s: could not add broadcast node, error %d\n", __func__,
5114220726Sbschmidt		    error);
5115178676Ssam		return error;
5116178676Ssam	}
5117178676Ssam	return 0;
5118178676Ssam}
5119178676Ssam
5120206477Sbschmidtstatic int
5121191746Sthompsaiwn_run(struct iwn_softc *sc, struct ieee80211vap *vap)
5122178676Ssam{
5123178676Ssam#define	MS(v,x)	(((v) & x) >> x##_S)
5124220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
5125178676Ssam	struct ifnet *ifp = sc->sc_ifp;
5126178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
5127178676Ssam	struct ieee80211_node *ni = vap->iv_bss;
5128178676Ssam	struct iwn_node_info node;
5129201209Srpaulo	int error;
5130178676Ssam
5131178676Ssam	if (ic->ic_opmode == IEEE80211_M_MONITOR) {
5132201209Srpaulo		/* Link LED blinks while monitoring. */
5133220674Sbschmidt		iwn_set_led(sc, IWN_LED_LINK, 5, 5);
5134178676Ssam		return 0;
5135178676Ssam	}
5136220726Sbschmidt	if ((error = iwn_set_timing(sc, ni)) != 0) {
5137198429Srpaulo		device_printf(sc->sc_dev,
5138198429Srpaulo		    "%s: could not set timing, error %d\n", __func__, error);
5139198429Srpaulo		return error;
5140198429Srpaulo	}
5141178676Ssam
5142201209Srpaulo	/* Update adapter configuration. */
5143201209Srpaulo	IEEE80211_ADDR_COPY(sc->rxon.bssid, ni->ni_bssid);
5144198429Srpaulo	sc->rxon.associd = htole16(IEEE80211_AID(ni->ni_associd));
5145220636Sbschmidt	sc->rxon.chan = ieee80211_chan2ieee(ic, ni->ni_chan);
5146220636Sbschmidt	sc->rxon.flags = htole32(IWN_RXON_TSF | IWN_RXON_CTS_TO_SELF);
5147201209Srpaulo	if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
5148201209Srpaulo		sc->rxon.flags |= htole32(IWN_RXON_AUTO | IWN_RXON_24GHZ);
5149178676Ssam	if (ic->ic_flags & IEEE80211_F_SHSLOT)
5150198429Srpaulo		sc->rxon.flags |= htole32(IWN_RXON_SHSLOT);
5151178676Ssam	if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
5152198429Srpaulo		sc->rxon.flags |= htole32(IWN_RXON_SHPREAMBLE);
5153201209Srpaulo	if (IEEE80211_IS_CHAN_A(ni->ni_chan)) {
5154201209Srpaulo		sc->rxon.cck_mask  = 0;
5155201209Srpaulo		sc->rxon.ofdm_mask = 0x15;
5156201209Srpaulo	} else if (IEEE80211_IS_CHAN_B(ni->ni_chan)) {
5157201209Srpaulo		sc->rxon.cck_mask  = 0x03;
5158201209Srpaulo		sc->rxon.ofdm_mask = 0;
5159201209Srpaulo	} else {
5160220725Sbschmidt		/* Assume 802.11b/g. */
5161201209Srpaulo		sc->rxon.cck_mask  = 0x0f;
5162201209Srpaulo		sc->rxon.ofdm_mask = 0x15;
5163201209Srpaulo	}
5164201209Srpaulo#if 0	/* HT */
5165178676Ssam	if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) {
5166198429Srpaulo		sc->rxon.flags &= ~htole32(IWN_RXON_HT);
5167178676Ssam		if (IEEE80211_IS_CHAN_HT40U(ni->ni_chan))
5168198429Srpaulo			sc->rxon.flags |= htole32(IWN_RXON_HT40U);
5169178676Ssam		else if (IEEE80211_IS_CHAN_HT40D(ni->ni_chan))
5170198429Srpaulo			sc->rxon.flags |= htole32(IWN_RXON_HT40D);
5171178676Ssam		else
5172198429Srpaulo			sc->rxon.flags |= htole32(IWN_RXON_HT20);
5173198429Srpaulo		sc->rxon.rxchain = htole16(
5174198429Srpaulo			  IWN_RXCHAIN_VALID(3)
5175198429Srpaulo			| IWN_RXCHAIN_MIMO_COUNT(3)
5176198429Srpaulo			| IWN_RXCHAIN_IDLE_COUNT(1)
5177178676Ssam			| IWN_RXCHAIN_MIMO_FORCE);
5178178676Ssam
5179178676Ssam		maxrxampdu = MS(ni->ni_htparam, IEEE80211_HTCAP_MAXRXAMPDU);
5180178676Ssam		ampdudensity = MS(ni->ni_htparam, IEEE80211_HTCAP_MPDUDENSITY);
5181178676Ssam	} else
5182178676Ssam		maxrxampdu = ampdudensity = 0;
5183201209Srpaulo#endif
5184198429Srpaulo	sc->rxon.filter |= htole32(IWN_FILTER_BSS);
5185220724Sbschmidt	DPRINTF(sc, IWN_DEBUG_STATE, "rxon chan %d flags %x\n",
5186220724Sbschmidt	    sc->rxon.chan, sc->rxon.flags);
5187220728Sbschmidt	error = iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, sc->rxonsz, 1);
5188178676Ssam	if (error != 0) {
5189178676Ssam		device_printf(sc->sc_dev,
5190220726Sbschmidt		    "%s: could not update configuration, error %d\n", __func__,
5191220726Sbschmidt		    error);
5192178676Ssam		return error;
5193178676Ssam	}
5194178676Ssam
5195198429Srpaulo	/* Configuration has changed, set TX power accordingly. */
5196220728Sbschmidt	if ((error = ops->set_txpower(sc, ni->ni_chan, 1)) != 0) {
5197178676Ssam		device_printf(sc->sc_dev,
5198220724Sbschmidt		    "%s: could not set TX power, error %d\n", __func__, error);
5199178676Ssam		return error;
5200178676Ssam	}
5201178676Ssam
5202220715Sbschmidt	/* Fake a join to initialize the TX rate. */
5203220715Sbschmidt	((struct iwn_node *)ni)->id = IWN_ID_BSS;
5204220715Sbschmidt	iwn_newassoc(ni, 1);
5205220715Sbschmidt
5206198429Srpaulo	/* Add BSS node. */
5207178676Ssam	memset(&node, 0, sizeof node);
5208178676Ssam	IEEE80211_ADDR_COPY(node.macaddr, ni->ni_macaddr);
5209178676Ssam	node.id = IWN_ID_BSS;
5210201209Srpaulo#ifdef notyet
5211201209Srpaulo	node.htflags = htole32(IWN_AMDPU_SIZE_FACTOR(3) |
5212201209Srpaulo	    IWN_AMDPU_DENSITY(5));	/* 2us */
5213201209Srpaulo#endif
5214220724Sbschmidt	DPRINTF(sc, IWN_DEBUG_STATE, "%s: adding BSS node\n", __func__);
5215220728Sbschmidt	error = ops->add_node(sc, &node, 1);
5216178676Ssam	if (error != 0) {
5217220724Sbschmidt		device_printf(sc->sc_dev,
5218220724Sbschmidt		    "%s: could not add BSS node, error %d\n", __func__, error);
5219178676Ssam		return error;
5220178676Ssam	}
5221220724Sbschmidt	DPRINTF(sc, IWN_DEBUG_STATE, "%s: setting link quality for node %d\n",
5222220724Sbschmidt	    __func__, node.id);
5223220726Sbschmidt	if ((error = iwn_set_link_quality(sc, ni)) != 0) {
5224178676Ssam		device_printf(sc->sc_dev,
5225220724Sbschmidt		    "%s: could not setup link quality for node %d, error %d\n",
5226178676Ssam		    __func__, node.id, error);
5227178676Ssam		return error;
5228178676Ssam	}
5229178676Ssam
5230220726Sbschmidt	if ((error = iwn_init_sensitivity(sc)) != 0) {
5231178676Ssam		device_printf(sc->sc_dev,
5232220726Sbschmidt		    "%s: could not set sensitivity, error %d\n", __func__,
5233220726Sbschmidt		    error);
5234178676Ssam		return error;
5235178676Ssam	}
5236198429Srpaulo	/* Start periodic calibration timer. */
5237178676Ssam	sc->calib.state = IWN_CALIB_STATE_ASSOC;
5238220667Sbschmidt	sc->calib_cnt = 0;
5239220667Sbschmidt	callout_reset(&sc->calib_to, msecs_to_ticks(500), iwn_calib_timeout,
5240220667Sbschmidt	    sc);
5241178676Ssam
5242198429Srpaulo	/* Link LED always on while associated. */
5243178676Ssam	iwn_set_led(sc, IWN_LED_LINK, 0, 1);
5244178676Ssam	return 0;
5245178676Ssam#undef MS
5246178676Ssam}
5247178676Ssam
5248201209Srpaulo#if 0	/* HT */
5249178676Ssam/*
5250201209Srpaulo * This function is called by upper layer when an ADDBA request is received
5251201209Srpaulo * from another STA and before the ADDBA response is sent.
5252201209Srpaulo */
5253206477Sbschmidtstatic int
5254201209Srpauloiwn_ampdu_rx_start(struct ieee80211com *ic, struct ieee80211_node *ni,
5255201209Srpaulo    uint8_t tid)
5256201209Srpaulo{
5257201209Srpaulo	struct ieee80211_rx_ba *ba = &ni->ni_rx_ba[tid];
5258201209Srpaulo	struct iwn_softc *sc = ic->ic_softc;
5259220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
5260201209Srpaulo	struct iwn_node *wn = (void *)ni;
5261201209Srpaulo	struct iwn_node_info node;
5262201209Srpaulo
5263201209Srpaulo	memset(&node, 0, sizeof node);
5264201209Srpaulo	node.id = wn->id;
5265201209Srpaulo	node.control = IWN_NODE_UPDATE;
5266201209Srpaulo	node.flags = IWN_FLAG_SET_ADDBA;
5267201209Srpaulo	node.addba_tid = tid;
5268201209Srpaulo	node.addba_ssn = htole16(ba->ba_winstart);
5269201209Srpaulo	DPRINTF(sc, IWN_DEBUG_RECV, "ADDBA RA=%d TID=%d SSN=%d\n",
5270220724Sbschmidt	    wn->id, tid, ba->ba_winstart);
5271220728Sbschmidt	return ops->add_node(sc, &node, 1);
5272201209Srpaulo}
5273201209Srpaulo
5274201209Srpaulo/*
5275201209Srpaulo * This function is called by upper layer on teardown of an HT-immediate
5276220725Sbschmidt * Block Ack agreement (eg. uppon receipt of a DELBA frame).
5277201209Srpaulo */
5278206477Sbschmidtstatic void
5279201209Srpauloiwn_ampdu_rx_stop(struct ieee80211com *ic, struct ieee80211_node *ni,
5280201209Srpaulo    uint8_t tid)
5281201209Srpaulo{
5282201209Srpaulo	struct iwn_softc *sc = ic->ic_softc;
5283220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
5284201209Srpaulo	struct iwn_node *wn = (void *)ni;
5285201209Srpaulo	struct iwn_node_info node;
5286201209Srpaulo
5287201209Srpaulo	memset(&node, 0, sizeof node);
5288201209Srpaulo	node.id = wn->id;
5289201209Srpaulo	node.control = IWN_NODE_UPDATE;
5290201209Srpaulo	node.flags = IWN_FLAG_SET_DELBA;
5291201209Srpaulo	node.delba_tid = tid;
5292201209Srpaulo	DPRINTF(sc, IWN_DEBUG_RECV, "DELBA RA=%d TID=%d\n", wn->id, tid);
5293220728Sbschmidt	(void)ops->add_node(sc, &node, 1);
5294201209Srpaulo}
5295201209Srpaulo
5296201209Srpaulo/*
5297201209Srpaulo * This function is called by upper layer when an ADDBA response is received
5298201209Srpaulo * from another STA.
5299201209Srpaulo */
5300206477Sbschmidtstatic int
5301201209Srpauloiwn_ampdu_tx_start(struct ieee80211com *ic, struct ieee80211_node *ni,
5302201209Srpaulo    uint8_t tid)
5303201209Srpaulo{
5304201209Srpaulo	struct ieee80211_tx_ba *ba = &ni->ni_tx_ba[tid];
5305201209Srpaulo	struct iwn_softc *sc = ic->ic_softc;
5306220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
5307201209Srpaulo	struct iwn_node *wn = (void *)ni;
5308201209Srpaulo	struct iwn_node_info node;
5309201209Srpaulo	int error;
5310201209Srpaulo
5311201209Srpaulo	/* Enable TX for the specified RA/TID. */
5312201209Srpaulo	wn->disable_tid &= ~(1 << tid);
5313201209Srpaulo	memset(&node, 0, sizeof node);
5314201209Srpaulo	node.id = wn->id;
5315201209Srpaulo	node.control = IWN_NODE_UPDATE;
5316201209Srpaulo	node.flags = IWN_FLAG_SET_DISABLE_TID;
5317201209Srpaulo	node.disable_tid = htole16(wn->disable_tid);
5318220728Sbschmidt	error = ops->add_node(sc, &node, 1);
5319201209Srpaulo	if (error != 0)
5320201209Srpaulo		return error;
5321201209Srpaulo
5322201209Srpaulo	if ((error = iwn_nic_lock(sc)) != 0)
5323201209Srpaulo		return error;
5324220728Sbschmidt	ops->ampdu_tx_start(sc, ni, tid, ba->ba_winstart);
5325201209Srpaulo	iwn_nic_unlock(sc);
5326201209Srpaulo	return 0;
5327201209Srpaulo}
5328201209Srpaulo
5329206477Sbschmidtstatic void
5330201209Srpauloiwn_ampdu_tx_stop(struct ieee80211com *ic, struct ieee80211_node *ni,
5331201209Srpaulo    uint8_t tid)
5332201209Srpaulo{
5333201209Srpaulo	struct ieee80211_tx_ba *ba = &ni->ni_tx_ba[tid];
5334201209Srpaulo	struct iwn_softc *sc = ic->ic_softc;
5335220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
5336201209Srpaulo
5337220726Sbschmidt	if (iwn_nic_lock(sc) != 0)
5338201209Srpaulo		return;
5339220728Sbschmidt	ops->ampdu_tx_stop(sc, tid, ba->ba_winstart);
5340201209Srpaulo	iwn_nic_unlock(sc);
5341201209Srpaulo}
5342201209Srpaulo
5343206477Sbschmidtstatic void
5344201209Srpauloiwn4965_ampdu_tx_start(struct iwn_softc *sc, struct ieee80211_node *ni,
5345201209Srpaulo    uint8_t tid, uint16_t ssn)
5346201209Srpaulo{
5347201209Srpaulo	struct iwn_node *wn = (void *)ni;
5348201209Srpaulo	int qid = 7 + tid;
5349201209Srpaulo
5350201209Srpaulo	/* Stop TX scheduler while we're changing its configuration. */
5351201209Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid),
5352201209Srpaulo	    IWN4965_TXQ_STATUS_CHGACT);
5353201209Srpaulo
5354201209Srpaulo	/* Assign RA/TID translation to the queue. */
5355201209Srpaulo	iwn_mem_write_2(sc, sc->sched_base + IWN4965_SCHED_TRANS_TBL(qid),
5356201209Srpaulo	    wn->id << 4 | tid);
5357201209Srpaulo
5358201209Srpaulo	/* Enable chain-building mode for the queue. */
5359201209Srpaulo	iwn_prph_setbits(sc, IWN4965_SCHED_QCHAIN_SEL, 1 << qid);
5360201209Srpaulo
5361201209Srpaulo	/* Set starting sequence number from the ADDBA request. */
5362201209Srpaulo	IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff));
5363201209Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_QUEUE_RDPTR(qid), ssn);
5364201209Srpaulo
5365201209Srpaulo	/* Set scheduler window size. */
5366201209Srpaulo	iwn_mem_write(sc, sc->sched_base + IWN4965_SCHED_QUEUE_OFFSET(qid),
5367201209Srpaulo	    IWN_SCHED_WINSZ);
5368201209Srpaulo	/* Set scheduler frame limit. */
5369201209Srpaulo	iwn_mem_write(sc, sc->sched_base + IWN4965_SCHED_QUEUE_OFFSET(qid) + 4,
5370201209Srpaulo	    IWN_SCHED_LIMIT << 16);
5371201209Srpaulo
5372201209Srpaulo	/* Enable interrupts for the queue. */
5373201209Srpaulo	iwn_prph_setbits(sc, IWN4965_SCHED_INTR_MASK, 1 << qid);
5374201209Srpaulo
5375201209Srpaulo	/* Mark the queue as active. */
5376201209Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid),
5377201209Srpaulo	    IWN4965_TXQ_STATUS_ACTIVE | IWN4965_TXQ_STATUS_AGGR_ENA |
5378201209Srpaulo	    iwn_tid2fifo[tid] << 1);
5379201209Srpaulo}
5380201209Srpaulo
5381206477Sbschmidtstatic void
5382201209Srpauloiwn4965_ampdu_tx_stop(struct iwn_softc *sc, uint8_t tid, uint16_t ssn)
5383201209Srpaulo{
5384201209Srpaulo	int qid = 7 + tid;
5385201209Srpaulo
5386201209Srpaulo	/* Stop TX scheduler while we're changing its configuration. */
5387201209Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid),
5388201209Srpaulo	    IWN4965_TXQ_STATUS_CHGACT);
5389201209Srpaulo
5390201209Srpaulo	/* Set starting sequence number from the ADDBA request. */
5391201209Srpaulo	IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff));
5392201209Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_QUEUE_RDPTR(qid), ssn);
5393201209Srpaulo
5394201209Srpaulo	/* Disable interrupts for the queue. */
5395201209Srpaulo	iwn_prph_clrbits(sc, IWN4965_SCHED_INTR_MASK, 1 << qid);
5396201209Srpaulo
5397201209Srpaulo	/* Mark the queue as inactive. */
5398201209Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid),
5399201209Srpaulo	    IWN4965_TXQ_STATUS_INACTIVE | iwn_tid2fifo[tid] << 1);
5400201209Srpaulo}
5401201209Srpaulo
5402206477Sbschmidtstatic void
5403201209Srpauloiwn5000_ampdu_tx_start(struct iwn_softc *sc, struct ieee80211_node *ni,
5404201209Srpaulo    uint8_t tid, uint16_t ssn)
5405201209Srpaulo{
5406201209Srpaulo	struct iwn_node *wn = (void *)ni;
5407201209Srpaulo	int qid = 10 + tid;
5408201209Srpaulo
5409201209Srpaulo	/* Stop TX scheduler while we're changing its configuration. */
5410201209Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid),
5411201209Srpaulo	    IWN5000_TXQ_STATUS_CHGACT);
5412201209Srpaulo
5413201209Srpaulo	/* Assign RA/TID translation to the queue. */
5414201209Srpaulo	iwn_mem_write_2(sc, sc->sched_base + IWN5000_SCHED_TRANS_TBL(qid),
5415201209Srpaulo	    wn->id << 4 | tid);
5416201209Srpaulo
5417201209Srpaulo	/* Enable chain-building mode for the queue. */
5418201209Srpaulo	iwn_prph_setbits(sc, IWN5000_SCHED_QCHAIN_SEL, 1 << qid);
5419201209Srpaulo
5420201209Srpaulo	/* Enable aggregation for the queue. */
5421201209Srpaulo	iwn_prph_setbits(sc, IWN5000_SCHED_AGGR_SEL, 1 << qid);
5422201209Srpaulo
5423201209Srpaulo	/* Set starting sequence number from the ADDBA request. */
5424201209Srpaulo	IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff));
5425201209Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_QUEUE_RDPTR(qid), ssn);
5426201209Srpaulo
5427201209Srpaulo	/* Set scheduler window size and frame limit. */
5428201209Srpaulo	iwn_mem_write(sc, sc->sched_base + IWN5000_SCHED_QUEUE_OFFSET(qid) + 4,
5429201209Srpaulo	    IWN_SCHED_LIMIT << 16 | IWN_SCHED_WINSZ);
5430201209Srpaulo
5431201209Srpaulo	/* Enable interrupts for the queue. */
5432201209Srpaulo	iwn_prph_setbits(sc, IWN5000_SCHED_INTR_MASK, 1 << qid);
5433201209Srpaulo
5434201209Srpaulo	/* Mark the queue as active. */
5435201209Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid),
5436201209Srpaulo	    IWN5000_TXQ_STATUS_ACTIVE | iwn_tid2fifo[tid]);
5437201209Srpaulo}
5438201209Srpaulo
5439206477Sbschmidtstatic void
5440201209Srpauloiwn5000_ampdu_tx_stop(struct iwn_softc *sc, uint8_t tid, uint16_t ssn)
5441201209Srpaulo{
5442201209Srpaulo	int qid = 10 + tid;
5443201209Srpaulo
5444201209Srpaulo	/* Stop TX scheduler while we're changing its configuration. */
5445201209Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid),
5446201209Srpaulo	    IWN5000_TXQ_STATUS_CHGACT);
5447201209Srpaulo
5448201209Srpaulo	/* Disable aggregation for the queue. */
5449201209Srpaulo	iwn_prph_clrbits(sc, IWN5000_SCHED_AGGR_SEL, 1 << qid);
5450201209Srpaulo
5451201209Srpaulo	/* Set starting sequence number from the ADDBA request. */
5452201209Srpaulo	IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff));
5453201209Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_QUEUE_RDPTR(qid), ssn);
5454201209Srpaulo
5455201209Srpaulo	/* Disable interrupts for the queue. */
5456201209Srpaulo	iwn_prph_clrbits(sc, IWN5000_SCHED_INTR_MASK, 1 << qid);
5457201209Srpaulo
5458201209Srpaulo	/* Mark the queue as inactive. */
5459201209Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid),
5460201209Srpaulo	    IWN5000_TXQ_STATUS_INACTIVE | iwn_tid2fifo[tid]);
5461201209Srpaulo}
5462201209Srpaulo#endif
5463201209Srpaulo
5464201209Srpaulo/*
5465220674Sbschmidt * Query calibration tables from the initialization firmware.  We do this
5466220674Sbschmidt * only once at first boot.  Called from a process context.
5467212853Sbschmidt */
5468212853Sbschmidtstatic int
5469220674Sbschmidtiwn5000_query_calibration(struct iwn_softc *sc)
5470212853Sbschmidt{
5471198429Srpaulo	struct iwn5000_calib_config cmd;
5472198429Srpaulo	int error;
5473178676Ssam
5474198429Srpaulo	memset(&cmd, 0, sizeof cmd);
5475220674Sbschmidt	cmd.ucode.once.enable = 0xffffffff;
5476220674Sbschmidt	cmd.ucode.once.start  = 0xffffffff;
5477220674Sbschmidt	cmd.ucode.once.send   = 0xffffffff;
5478220674Sbschmidt	cmd.ucode.flags       = 0xffffffff;
5479220674Sbschmidt	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: sending calibration query\n",
5480220674Sbschmidt	    __func__);
5481198429Srpaulo	error = iwn_cmd(sc, IWN5000_CMD_CALIB_CONFIG, &cmd, sizeof cmd, 0);
5482198429Srpaulo	if (error != 0)
5483198429Srpaulo		return error;
5484178676Ssam
5485198429Srpaulo	/* Wait at most two seconds for calibration to complete. */
5486201209Srpaulo	if (!(sc->sc_flags & IWN_FLAG_CALIB_DONE))
5487220674Sbschmidt		error = msleep(sc, &sc->sc_mtx, PCATCH, "iwncal", 2 * hz);
5488201209Srpaulo	return error;
5489198429Srpaulo}
5490198429Srpaulo
5491198429Srpaulo/*
5492220674Sbschmidt * Send calibration results to the runtime firmware.  These results were
5493220674Sbschmidt * obtained on first boot from the initialization firmware.
5494198429Srpaulo */
5495212854Sbschmidtstatic int
5496220674Sbschmidtiwn5000_send_calibration(struct iwn_softc *sc)
5497198429Srpaulo{
5498220674Sbschmidt	int idx, error;
5499198429Srpaulo
5500220674Sbschmidt	for (idx = 0; idx < 5; idx++) {
5501220674Sbschmidt		if (sc->calibcmd[idx].buf == NULL)
5502220674Sbschmidt			continue;	/* No results available. */
5503198429Srpaulo		DPRINTF(sc, IWN_DEBUG_CALIBRATE,
5504220674Sbschmidt		    "send calibration result idx=%d len=%d\n", idx,
5505220674Sbschmidt		    sc->calibcmd[idx].len);
5506220674Sbschmidt		error = iwn_cmd(sc, IWN_CMD_PHY_CALIB, sc->calibcmd[idx].buf,
5507220674Sbschmidt		    sc->calibcmd[idx].len, 0);
5508220674Sbschmidt		if (error != 0) {
5509220674Sbschmidt			device_printf(sc->sc_dev,
5510220674Sbschmidt			    "%s: could not send calibration result, error %d\n",
5511220674Sbschmidt			    __func__, error);
5512220674Sbschmidt			return error;
5513220674Sbschmidt		}
5514178676Ssam	}
5515220674Sbschmidt	return 0;
5516198429Srpaulo}
5517178676Ssam
5518206477Sbschmidtstatic int
5519201209Srpauloiwn5000_send_wimax_coex(struct iwn_softc *sc)
5520201209Srpaulo{
5521201209Srpaulo	struct iwn5000_wimax_coex wimax;
5522201209Srpaulo
5523201209Srpaulo#ifdef notyet
5524201209Srpaulo	if (sc->hw_type == IWN_HW_REV_TYPE_6050) {
5525201209Srpaulo		/* Enable WiMAX coexistence for combo adapters. */
5526201209Srpaulo		wimax.flags =
5527201209Srpaulo		    IWN_WIMAX_COEX_ASSOC_WA_UNMASK |
5528201209Srpaulo		    IWN_WIMAX_COEX_UNASSOC_WA_UNMASK |
5529201209Srpaulo		    IWN_WIMAX_COEX_STA_TABLE_VALID |
5530201209Srpaulo		    IWN_WIMAX_COEX_ENABLE;
5531201209Srpaulo		memcpy(wimax.events, iwn6050_wimax_events,
5532201209Srpaulo		    sizeof iwn6050_wimax_events);
5533201209Srpaulo	} else
5534201209Srpaulo#endif
5535201209Srpaulo	{
5536201209Srpaulo		/* Disable WiMAX coexistence. */
5537201209Srpaulo		wimax.flags = 0;
5538201209Srpaulo		memset(wimax.events, 0, sizeof wimax.events);
5539201209Srpaulo	}
5540201209Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "%s: Configuring WiMAX coexistence\n",
5541201209Srpaulo	    __func__);
5542201209Srpaulo	return iwn_cmd(sc, IWN5000_CMD_WIMAX_COEX, &wimax, sizeof wimax, 0);
5543201209Srpaulo}
5544201209Srpaulo
5545220674Sbschmidtstatic int
5546220674Sbschmidtiwn5000_crystal_calib(struct iwn_softc *sc)
5547220674Sbschmidt{
5548220674Sbschmidt	struct iwn5000_phy_calib_crystal cmd;
5549220674Sbschmidt
5550220674Sbschmidt	memset(&cmd, 0, sizeof cmd);
5551220674Sbschmidt	cmd.code = IWN5000_PHY_CALIB_CRYSTAL;
5552220674Sbschmidt	cmd.ngroups = 1;
5553220674Sbschmidt	cmd.isvalid = 1;
5554220674Sbschmidt	cmd.cap_pin[0] = le32toh(sc->eeprom_crystal) & 0xff;
5555220674Sbschmidt	cmd.cap_pin[1] = (le32toh(sc->eeprom_crystal) >> 16) & 0xff;
5556220674Sbschmidt	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "sending crystal calibration %d, %d\n",
5557220674Sbschmidt	    cmd.cap_pin[0], cmd.cap_pin[1]);
5558220674Sbschmidt	return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 0);
5559220674Sbschmidt}
5560220674Sbschmidt
5561220676Sbschmidtstatic int
5562220676Sbschmidtiwn5000_temp_offset_calib(struct iwn_softc *sc)
5563220676Sbschmidt{
5564220676Sbschmidt	struct iwn5000_phy_calib_temp_offset cmd;
5565220676Sbschmidt
5566220676Sbschmidt	memset(&cmd, 0, sizeof cmd);
5567220676Sbschmidt	cmd.code = IWN5000_PHY_CALIB_TEMP_OFFSET;
5568220676Sbschmidt	cmd.ngroups = 1;
5569220676Sbschmidt	cmd.isvalid = 1;
5570220676Sbschmidt	if (sc->eeprom_temp != 0)
5571220676Sbschmidt		cmd.offset = htole16(sc->eeprom_temp);
5572220676Sbschmidt	else
5573220676Sbschmidt		cmd.offset = htole16(IWN_DEFAULT_TEMP_OFFSET);
5574220676Sbschmidt	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "setting radio sensor offset to %d\n",
5575220676Sbschmidt	    le16toh(cmd.offset));
5576220676Sbschmidt	return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 0);
5577220676Sbschmidt}
5578220676Sbschmidt
5579198429Srpaulo/*
5580198429Srpaulo * This function is called after the runtime firmware notifies us of its
5581220725Sbschmidt * readiness (called in a process context).
5582198429Srpaulo */
5583206477Sbschmidtstatic int
5584198429Srpauloiwn4965_post_alive(struct iwn_softc *sc)
5585198429Srpaulo{
5586198429Srpaulo	int error, qid;
5587178676Ssam
5588198429Srpaulo	if ((error = iwn_nic_lock(sc)) != 0)
5589198429Srpaulo		return error;
5590178676Ssam
5591201209Srpaulo	/* Clear TX scheduler state in SRAM. */
5592198429Srpaulo	sc->sched_base = iwn_prph_read(sc, IWN_SCHED_SRAM_ADDR);
5593198429Srpaulo	iwn_mem_set_region_4(sc, sc->sched_base + IWN4965_SCHED_CTX_OFF, 0,
5594201209Srpaulo	    IWN4965_SCHED_CTX_LEN / sizeof (uint32_t));
5595178676Ssam
5596220725Sbschmidt	/* Set physical address of TX scheduler rings (1KB aligned). */
5597198429Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_DRAM_ADDR, sc->sched_dma.paddr >> 10);
5598178676Ssam
5599198429Srpaulo	IWN_SETBITS(sc, IWN_FH_TX_CHICKEN, IWN_FH_TX_CHICKEN_SCHED_RETRY);
5600178676Ssam
5601198429Srpaulo	/* Disable chain mode for all our 16 queues. */
5602198429Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_QCHAIN_SEL, 0);
5603198429Srpaulo
5604198429Srpaulo	for (qid = 0; qid < IWN4965_NTXQUEUES; qid++) {
5605198429Srpaulo		iwn_prph_write(sc, IWN4965_SCHED_QUEUE_RDPTR(qid), 0);
5606198429Srpaulo		IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | 0);
5607198429Srpaulo
5608198429Srpaulo		/* Set scheduler window size. */
5609198429Srpaulo		iwn_mem_write(sc, sc->sched_base +
5610198429Srpaulo		    IWN4965_SCHED_QUEUE_OFFSET(qid), IWN_SCHED_WINSZ);
5611198429Srpaulo		/* Set scheduler frame limit. */
5612198429Srpaulo		iwn_mem_write(sc, sc->sched_base +
5613198429Srpaulo		    IWN4965_SCHED_QUEUE_OFFSET(qid) + 4,
5614198429Srpaulo		    IWN_SCHED_LIMIT << 16);
5615178676Ssam	}
5616178676Ssam
5617198429Srpaulo	/* Enable interrupts for all our 16 queues. */
5618198429Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_INTR_MASK, 0xffff);
5619198429Srpaulo	/* Identify TX FIFO rings (0-7). */
5620198429Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_TXFACT, 0xff);
5621178676Ssam
5622198429Srpaulo	/* Mark TX rings (4 EDCA + cmd + 2 HCCA) as active. */
5623198429Srpaulo	for (qid = 0; qid < 7; qid++) {
5624198429Srpaulo		static uint8_t qid2fifo[] = { 3, 2, 1, 0, 4, 5, 6 };
5625198429Srpaulo		iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid),
5626198429Srpaulo		    IWN4965_TXQ_STATUS_ACTIVE | qid2fifo[qid] << 1);
5627198429Srpaulo	}
5628198429Srpaulo	iwn_nic_unlock(sc);
5629198429Srpaulo	return 0;
5630198429Srpaulo}
5631178676Ssam
5632198429Srpaulo/*
5633198429Srpaulo * This function is called after the initialization or runtime firmware
5634220725Sbschmidt * notifies us of its readiness (called in a process context).
5635198429Srpaulo */
5636206477Sbschmidtstatic int
5637198429Srpauloiwn5000_post_alive(struct iwn_softc *sc)
5638198429Srpaulo{
5639198429Srpaulo	int error, qid;
5640178676Ssam
5641201209Srpaulo	/* Switch to using ICT interrupt mode. */
5642201209Srpaulo	iwn5000_ict_reset(sc);
5643201209Srpaulo
5644220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
5645198429Srpaulo		return error;
5646178676Ssam
5647201209Srpaulo	/* Clear TX scheduler state in SRAM. */
5648198429Srpaulo	sc->sched_base = iwn_prph_read(sc, IWN_SCHED_SRAM_ADDR);
5649198429Srpaulo	iwn_mem_set_region_4(sc, sc->sched_base + IWN5000_SCHED_CTX_OFF, 0,
5650201209Srpaulo	    IWN5000_SCHED_CTX_LEN / sizeof (uint32_t));
5651178676Ssam
5652220725Sbschmidt	/* Set physical address of TX scheduler rings (1KB aligned). */
5653198429Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_DRAM_ADDR, sc->sched_dma.paddr >> 10);
5654178676Ssam
5655198429Srpaulo	IWN_SETBITS(sc, IWN_FH_TX_CHICKEN, IWN_FH_TX_CHICKEN_SCHED_RETRY);
5656178676Ssam
5657201209Srpaulo	/* Enable chain mode for all queues, except command queue. */
5658201209Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_QCHAIN_SEL, 0xfffef);
5659198429Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_AGGR_SEL, 0);
5660178676Ssam
5661198429Srpaulo	for (qid = 0; qid < IWN5000_NTXQUEUES; qid++) {
5662198429Srpaulo		iwn_prph_write(sc, IWN5000_SCHED_QUEUE_RDPTR(qid), 0);
5663198429Srpaulo		IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | 0);
5664198429Srpaulo
5665198429Srpaulo		iwn_mem_write(sc, sc->sched_base +
5666198429Srpaulo		    IWN5000_SCHED_QUEUE_OFFSET(qid), 0);
5667198429Srpaulo		/* Set scheduler window size and frame limit. */
5668198429Srpaulo		iwn_mem_write(sc, sc->sched_base +
5669198429Srpaulo		    IWN5000_SCHED_QUEUE_OFFSET(qid) + 4,
5670198429Srpaulo		    IWN_SCHED_LIMIT << 16 | IWN_SCHED_WINSZ);
5671178676Ssam	}
5672178676Ssam
5673198429Srpaulo	/* Enable interrupts for all our 20 queues. */
5674198429Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_INTR_MASK, 0xfffff);
5675198429Srpaulo	/* Identify TX FIFO rings (0-7). */
5676198429Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_TXFACT, 0xff);
5677178676Ssam
5678198429Srpaulo	/* Mark TX rings (4 EDCA + cmd + 2 HCCA) as active. */
5679198429Srpaulo	for (qid = 0; qid < 7; qid++) {
5680198429Srpaulo		static uint8_t qid2fifo[] = { 3, 2, 1, 0, 7, 5, 6 };
5681198429Srpaulo		iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid),
5682198429Srpaulo		    IWN5000_TXQ_STATUS_ACTIVE | qid2fifo[qid]);
5683198429Srpaulo	}
5684198429Srpaulo	iwn_nic_unlock(sc);
5685178676Ssam
5686201209Srpaulo	/* Configure WiMAX coexistence for combo adapters. */
5687201209Srpaulo	error = iwn5000_send_wimax_coex(sc);
5688178676Ssam	if (error != 0) {
5689178676Ssam		device_printf(sc->sc_dev,
5690198429Srpaulo		    "%s: could not configure WiMAX coexistence, error %d\n",
5691178676Ssam		    __func__, error);
5692178676Ssam		return error;
5693178676Ssam	}
5694220674Sbschmidt	if (sc->hw_type != IWN_HW_REV_TYPE_5150) {
5695220674Sbschmidt		/* Perform crystal calibration. */
5696220674Sbschmidt		error = iwn5000_crystal_calib(sc);
5697198429Srpaulo		if (error != 0) {
5698198429Srpaulo			device_printf(sc->sc_dev,
5699220674Sbschmidt			    "%s: crystal calibration failed, error %d\n",
5700220674Sbschmidt			    __func__, error);
5701198429Srpaulo			return error;
5702198429Srpaulo		}
5703220674Sbschmidt	}
5704220674Sbschmidt	if (!(sc->sc_flags & IWN_FLAG_CALIB_DONE)) {
5705220674Sbschmidt		/* Query calibration from the initialization firmware. */
5706220674Sbschmidt		if ((error = iwn5000_query_calibration(sc)) != 0) {
5707198429Srpaulo			device_printf(sc->sc_dev,
5708220674Sbschmidt			    "%s: could not query calibration, error %d\n",
5709198429Srpaulo			    __func__, error);
5710198429Srpaulo			return error;
5711198429Srpaulo		}
5712198429Srpaulo		/*
5713201209Srpaulo		 * We have the calibration results now, reboot with the
5714201209Srpaulo		 * runtime firmware (call ourselves recursively!)
5715198429Srpaulo		 */
5716198429Srpaulo		iwn_hw_stop(sc);
5717198429Srpaulo		error = iwn_hw_init(sc);
5718198429Srpaulo	} else {
5719220674Sbschmidt		/* Send calibration results to runtime firmware. */
5720220674Sbschmidt		error = iwn5000_send_calibration(sc);
5721198429Srpaulo	}
5722198429Srpaulo	return error;
5723198429Srpaulo}
5724178676Ssam
5725198429Srpaulo/*
5726198429Srpaulo * The firmware boot code is small and is intended to be copied directly into
5727220725Sbschmidt * the NIC internal memory (no DMA transfer).
5728198429Srpaulo */
5729206477Sbschmidtstatic int
5730198429Srpauloiwn4965_load_bootcode(struct iwn_softc *sc, const uint8_t *ucode, int size)
5731198429Srpaulo{
5732198429Srpaulo	int error, ntries;
5733198429Srpaulo
5734198429Srpaulo	size /= sizeof (uint32_t);
5735198429Srpaulo
5736220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
5737198429Srpaulo		return error;
5738198429Srpaulo
5739198429Srpaulo	/* Copy microcode image into NIC memory. */
5740198429Srpaulo	iwn_prph_write_region_4(sc, IWN_BSM_SRAM_BASE,
5741198429Srpaulo	    (const uint32_t *)ucode, size);
5742198429Srpaulo
5743198429Srpaulo	iwn_prph_write(sc, IWN_BSM_WR_MEM_SRC, 0);
5744198429Srpaulo	iwn_prph_write(sc, IWN_BSM_WR_MEM_DST, IWN_FW_TEXT_BASE);
5745198429Srpaulo	iwn_prph_write(sc, IWN_BSM_WR_DWCOUNT, size);
5746198429Srpaulo
5747198429Srpaulo	/* Start boot load now. */
5748198429Srpaulo	iwn_prph_write(sc, IWN_BSM_WR_CTRL, IWN_BSM_WR_CTRL_START);
5749198429Srpaulo
5750198429Srpaulo	/* Wait for transfer to complete. */
5751198429Srpaulo	for (ntries = 0; ntries < 1000; ntries++) {
5752198429Srpaulo		if (!(iwn_prph_read(sc, IWN_BSM_WR_CTRL) &
5753198429Srpaulo		    IWN_BSM_WR_CTRL_START))
5754198429Srpaulo			break;
5755198429Srpaulo		DELAY(10);
5756198429Srpaulo	}
5757198429Srpaulo	if (ntries == 1000) {
5758198429Srpaulo		device_printf(sc->sc_dev, "%s: could not load boot firmware\n",
5759198429Srpaulo		    __func__);
5760198429Srpaulo		iwn_nic_unlock(sc);
5761198429Srpaulo		return ETIMEDOUT;
5762198429Srpaulo	}
5763198429Srpaulo
5764198429Srpaulo	/* Enable boot after power up. */
5765198429Srpaulo	iwn_prph_write(sc, IWN_BSM_WR_CTRL, IWN_BSM_WR_CTRL_START_EN);
5766198429Srpaulo
5767198429Srpaulo	iwn_nic_unlock(sc);
5768198429Srpaulo	return 0;
5769178676Ssam}
5770178676Ssam
5771206477Sbschmidtstatic int
5772198429Srpauloiwn4965_load_firmware(struct iwn_softc *sc)
5773178676Ssam{
5774198429Srpaulo	struct iwn_fw_info *fw = &sc->fw;
5775198429Srpaulo	struct iwn_dma_info *dma = &sc->fw_dma;
5776178676Ssam	int error;
5777178676Ssam
5778198429Srpaulo	/* Copy initialization sections into pre-allocated DMA-safe memory. */
5779198429Srpaulo	memcpy(dma->vaddr, fw->init.data, fw->init.datasz);
5780220661Sbschmidt	bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
5781198429Srpaulo	memcpy(dma->vaddr + IWN4965_FW_DATA_MAXSZ,
5782198429Srpaulo	    fw->init.text, fw->init.textsz);
5783220661Sbschmidt	bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
5784198429Srpaulo
5785198429Srpaulo	/* Tell adapter where to find initialization sections. */
5786220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
5787198429Srpaulo		return error;
5788198429Srpaulo	iwn_prph_write(sc, IWN_BSM_DRAM_DATA_ADDR, dma->paddr >> 4);
5789198429Srpaulo	iwn_prph_write(sc, IWN_BSM_DRAM_DATA_SIZE, fw->init.datasz);
5790198429Srpaulo	iwn_prph_write(sc, IWN_BSM_DRAM_TEXT_ADDR,
5791198429Srpaulo	    (dma->paddr + IWN4965_FW_DATA_MAXSZ) >> 4);
5792198429Srpaulo	iwn_prph_write(sc, IWN_BSM_DRAM_TEXT_SIZE, fw->init.textsz);
5793198429Srpaulo	iwn_nic_unlock(sc);
5794198429Srpaulo
5795198429Srpaulo	/* Load firmware boot code. */
5796198429Srpaulo	error = iwn4965_load_bootcode(sc, fw->boot.text, fw->boot.textsz);
5797178676Ssam	if (error != 0) {
5798198429Srpaulo		device_printf(sc->sc_dev, "%s: could not load boot firmware\n",
5799198429Srpaulo		    __func__);
5800178676Ssam		return error;
5801178676Ssam	}
5802198429Srpaulo	/* Now press "execute". */
5803198429Srpaulo	IWN_WRITE(sc, IWN_RESET, 0);
5804178676Ssam
5805198429Srpaulo	/* Wait at most one second for first alive notification. */
5806220726Sbschmidt	if ((error = msleep(sc, &sc->sc_mtx, PCATCH, "iwninit", hz)) != 0) {
5807178676Ssam		device_printf(sc->sc_dev,
5808198429Srpaulo		    "%s: timeout waiting for adapter to initialize, error %d\n",
5809178676Ssam		    __func__, error);
5810178676Ssam		return error;
5811178676Ssam	}
5812178676Ssam
5813198429Srpaulo	/* Retrieve current temperature for initial TX power calibration. */
5814198429Srpaulo	sc->rawtemp = sc->ucode_info.temp[3].chan20MHz;
5815198429Srpaulo	sc->temp = iwn4965_get_temperature(sc);
5816178676Ssam
5817198429Srpaulo	/* Copy runtime sections into pre-allocated DMA-safe memory. */
5818198429Srpaulo	memcpy(dma->vaddr, fw->main.data, fw->main.datasz);
5819220661Sbschmidt	bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
5820198429Srpaulo	memcpy(dma->vaddr + IWN4965_FW_DATA_MAXSZ,
5821198429Srpaulo	    fw->main.text, fw->main.textsz);
5822220661Sbschmidt	bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
5823198429Srpaulo
5824198429Srpaulo	/* Tell adapter where to find runtime sections. */
5825220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
5826198429Srpaulo		return error;
5827198429Srpaulo	iwn_prph_write(sc, IWN_BSM_DRAM_DATA_ADDR, dma->paddr >> 4);
5828198429Srpaulo	iwn_prph_write(sc, IWN_BSM_DRAM_DATA_SIZE, fw->main.datasz);
5829198429Srpaulo	iwn_prph_write(sc, IWN_BSM_DRAM_TEXT_ADDR,
5830198429Srpaulo	    (dma->paddr + IWN4965_FW_DATA_MAXSZ) >> 4);
5831198429Srpaulo	iwn_prph_write(sc, IWN_BSM_DRAM_TEXT_SIZE,
5832198429Srpaulo	    IWN_FW_UPDATED | fw->main.textsz);
5833198429Srpaulo	iwn_nic_unlock(sc);
5834198429Srpaulo
5835198429Srpaulo	return 0;
5836198429Srpaulo}
5837198429Srpaulo
5838206477Sbschmidtstatic int
5839198429Srpauloiwn5000_load_firmware_section(struct iwn_softc *sc, uint32_t dst,
5840198429Srpaulo    const uint8_t *section, int size)
5841198429Srpaulo{
5842198429Srpaulo	struct iwn_dma_info *dma = &sc->fw_dma;
5843198429Srpaulo	int error;
5844198429Srpaulo
5845198429Srpaulo	/* Copy firmware section into pre-allocated DMA-safe memory. */
5846198429Srpaulo	memcpy(dma->vaddr, section, size);
5847220661Sbschmidt	bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
5848198429Srpaulo
5849220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
5850198429Srpaulo		return error;
5851198429Srpaulo
5852198429Srpaulo	IWN_WRITE(sc, IWN_FH_TX_CONFIG(IWN_SRVC_DMACHNL),
5853198429Srpaulo	    IWN_FH_TX_CONFIG_DMA_PAUSE);
5854198429Srpaulo
5855198429Srpaulo	IWN_WRITE(sc, IWN_FH_SRAM_ADDR(IWN_SRVC_DMACHNL), dst);
5856198429Srpaulo	IWN_WRITE(sc, IWN_FH_TFBD_CTRL0(IWN_SRVC_DMACHNL),
5857198429Srpaulo	    IWN_LOADDR(dma->paddr));
5858198429Srpaulo	IWN_WRITE(sc, IWN_FH_TFBD_CTRL1(IWN_SRVC_DMACHNL),
5859198429Srpaulo	    IWN_HIADDR(dma->paddr) << 28 | size);
5860198429Srpaulo	IWN_WRITE(sc, IWN_FH_TXBUF_STATUS(IWN_SRVC_DMACHNL),
5861198429Srpaulo	    IWN_FH_TXBUF_STATUS_TBNUM(1) |
5862198429Srpaulo	    IWN_FH_TXBUF_STATUS_TBIDX(1) |
5863198429Srpaulo	    IWN_FH_TXBUF_STATUS_TFBD_VALID);
5864198429Srpaulo
5865198429Srpaulo	/* Kick Flow Handler to start DMA transfer. */
5866198429Srpaulo	IWN_WRITE(sc, IWN_FH_TX_CONFIG(IWN_SRVC_DMACHNL),
5867198429Srpaulo	    IWN_FH_TX_CONFIG_DMA_ENA | IWN_FH_TX_CONFIG_CIRQ_HOST_ENDTFD);
5868198429Srpaulo
5869198429Srpaulo	iwn_nic_unlock(sc);
5870198429Srpaulo
5871198429Srpaulo	/* Wait at most five seconds for FH DMA transfer to complete. */
5872220661Sbschmidt	return msleep(sc, &sc->sc_mtx, PCATCH, "iwninit", 5 * hz);
5873198429Srpaulo}
5874198429Srpaulo
5875206477Sbschmidtstatic int
5876198429Srpauloiwn5000_load_firmware(struct iwn_softc *sc)
5877198429Srpaulo{
5878198429Srpaulo	struct iwn_fw_part *fw;
5879198429Srpaulo	int error;
5880198429Srpaulo
5881198429Srpaulo	/* Load the initialization firmware on first boot only. */
5882201209Srpaulo	fw = (sc->sc_flags & IWN_FLAG_CALIB_DONE) ?
5883201209Srpaulo	    &sc->fw.main : &sc->fw.init;
5884198429Srpaulo
5885198429Srpaulo	error = iwn5000_load_firmware_section(sc, IWN_FW_TEXT_BASE,
5886198429Srpaulo	    fw->text, fw->textsz);
5887178676Ssam	if (error != 0) {
5888178676Ssam		device_printf(sc->sc_dev,
5889198429Srpaulo		    "%s: could not load firmware %s section, error %d\n",
5890198429Srpaulo		    __func__, ".text", error);
5891178676Ssam		return error;
5892178676Ssam	}
5893198429Srpaulo	error = iwn5000_load_firmware_section(sc, IWN_FW_DATA_BASE,
5894198429Srpaulo	    fw->data, fw->datasz);
5895178676Ssam	if (error != 0) {
5896178676Ssam		device_printf(sc->sc_dev,
5897198429Srpaulo		    "%s: could not load firmware %s section, error %d\n",
5898198429Srpaulo		    __func__, ".data", error);
5899178676Ssam		return error;
5900178676Ssam	}
5901178676Ssam
5902198429Srpaulo	/* Now press "execute". */
5903198429Srpaulo	IWN_WRITE(sc, IWN_RESET, 0);
5904198429Srpaulo	return 0;
5905198429Srpaulo}
5906198429Srpaulo
5907210111Sbschmidt/*
5908210111Sbschmidt * Extract text and data sections from a legacy firmware image.
5909210111Sbschmidt */
5910206477Sbschmidtstatic int
5911210111Sbschmidtiwn_read_firmware_leg(struct iwn_softc *sc, struct iwn_fw_info *fw)
5912198429Srpaulo{
5913201209Srpaulo	const uint32_t *ptr;
5914210111Sbschmidt	size_t hdrlen = 24;
5915201209Srpaulo	uint32_t rev;
5916198429Srpaulo
5917220661Sbschmidt	ptr = (const uint32_t *)fw->data;
5918201209Srpaulo	rev = le32toh(*ptr++);
5919210111Sbschmidt
5920201209Srpaulo	/* Check firmware API version. */
5921201209Srpaulo	if (IWN_FW_API(rev) <= 1) {
5922201209Srpaulo		device_printf(sc->sc_dev,
5923201209Srpaulo		    "%s: bad firmware, need API version >=2\n", __func__);
5924201209Srpaulo		return EINVAL;
5925201209Srpaulo	}
5926201209Srpaulo	if (IWN_FW_API(rev) >= 3) {
5927201209Srpaulo		/* Skip build number (version 2 header). */
5928210111Sbschmidt		hdrlen += 4;
5929201209Srpaulo		ptr++;
5930201209Srpaulo	}
5931210111Sbschmidt	if (fw->size < hdrlen) {
5932220724Sbschmidt		device_printf(sc->sc_dev, "%s: firmware too short: %zu bytes\n",
5933210111Sbschmidt		    __func__, fw->size);
5934210111Sbschmidt		return EINVAL;
5935210111Sbschmidt	}
5936201209Srpaulo	fw->main.textsz = le32toh(*ptr++);
5937201209Srpaulo	fw->main.datasz = le32toh(*ptr++);
5938201209Srpaulo	fw->init.textsz = le32toh(*ptr++);
5939201209Srpaulo	fw->init.datasz = le32toh(*ptr++);
5940201209Srpaulo	fw->boot.textsz = le32toh(*ptr++);
5941198429Srpaulo
5942198429Srpaulo	/* Check that all firmware sections fit. */
5943210111Sbschmidt	if (fw->size < hdrlen + fw->main.textsz + fw->main.datasz +
5944210111Sbschmidt	    fw->init.textsz + fw->init.datasz + fw->boot.textsz) {
5945220724Sbschmidt		device_printf(sc->sc_dev, "%s: firmware too short: %zu bytes\n",
5946210111Sbschmidt		    __func__, fw->size);
5947198429Srpaulo		return EINVAL;
5948178676Ssam	}
5949198429Srpaulo
5950198429Srpaulo	/* Get pointers to firmware sections. */
5951201209Srpaulo	fw->main.text = (const uint8_t *)ptr;
5952198429Srpaulo	fw->main.data = fw->main.text + fw->main.textsz;
5953198429Srpaulo	fw->init.text = fw->main.data + fw->main.datasz;
5954198429Srpaulo	fw->init.data = fw->init.text + fw->init.textsz;
5955198429Srpaulo	fw->boot.text = fw->init.data + fw->init.datasz;
5956178676Ssam	return 0;
5957178676Ssam}
5958178676Ssam
5959210111Sbschmidt/*
5960210111Sbschmidt * Extract text and data sections from a TLV firmware image.
5961210111Sbschmidt */
5962220661Sbschmidtstatic int
5963210111Sbschmidtiwn_read_firmware_tlv(struct iwn_softc *sc, struct iwn_fw_info *fw,
5964210111Sbschmidt    uint16_t alt)
5965210111Sbschmidt{
5966210111Sbschmidt	const struct iwn_fw_tlv_hdr *hdr;
5967210111Sbschmidt	const struct iwn_fw_tlv *tlv;
5968210111Sbschmidt	const uint8_t *ptr, *end;
5969210111Sbschmidt	uint64_t altmask;
5970220866Sbschmidt	uint32_t len, tmp;
5971210111Sbschmidt
5972210111Sbschmidt	if (fw->size < sizeof (*hdr)) {
5973220724Sbschmidt		device_printf(sc->sc_dev, "%s: firmware too short: %zu bytes\n",
5974210111Sbschmidt		    __func__, fw->size);
5975210111Sbschmidt		return EINVAL;
5976210111Sbschmidt	}
5977210111Sbschmidt	hdr = (const struct iwn_fw_tlv_hdr *)fw->data;
5978210111Sbschmidt	if (hdr->signature != htole32(IWN_FW_SIGNATURE)) {
5979220724Sbschmidt		device_printf(sc->sc_dev, "%s: bad firmware signature 0x%08x\n",
5980210111Sbschmidt		    __func__, le32toh(hdr->signature));
5981210111Sbschmidt		return EINVAL;
5982210111Sbschmidt	}
5983220724Sbschmidt	DPRINTF(sc, IWN_DEBUG_RESET, "FW: \"%.64s\", build 0x%x\n", hdr->descr,
5984220724Sbschmidt	    le32toh(hdr->build));
5985210111Sbschmidt
5986210111Sbschmidt	/*
5987210111Sbschmidt	 * Select the closest supported alternative that is less than
5988210111Sbschmidt	 * or equal to the specified one.
5989210111Sbschmidt	 */
5990210111Sbschmidt	altmask = le64toh(hdr->altmask);
5991210111Sbschmidt	while (alt > 0 && !(altmask & (1ULL << alt)))
5992210111Sbschmidt		alt--;	/* Downgrade. */
5993220724Sbschmidt	DPRINTF(sc, IWN_DEBUG_RESET, "using alternative %d\n", alt);
5994210111Sbschmidt
5995210111Sbschmidt	ptr = (const uint8_t *)(hdr + 1);
5996210111Sbschmidt	end = (const uint8_t *)(fw->data + fw->size);
5997210111Sbschmidt
5998210111Sbschmidt	/* Parse type-length-value fields. */
5999210111Sbschmidt	while (ptr + sizeof (*tlv) <= end) {
6000210111Sbschmidt		tlv = (const struct iwn_fw_tlv *)ptr;
6001210111Sbschmidt		len = le32toh(tlv->len);
6002210111Sbschmidt
6003210111Sbschmidt		ptr += sizeof (*tlv);
6004210111Sbschmidt		if (ptr + len > end) {
6005210111Sbschmidt			device_printf(sc->sc_dev,
6006220724Sbschmidt			    "%s: firmware too short: %zu bytes\n", __func__,
6007220724Sbschmidt			    fw->size);
6008210111Sbschmidt			return EINVAL;
6009210111Sbschmidt		}
6010210111Sbschmidt		/* Skip other alternatives. */
6011210111Sbschmidt		if (tlv->alt != 0 && tlv->alt != htole16(alt))
6012210111Sbschmidt			goto next;
6013210111Sbschmidt
6014210111Sbschmidt		switch (le16toh(tlv->type)) {
6015210111Sbschmidt		case IWN_FW_TLV_MAIN_TEXT:
6016210111Sbschmidt			fw->main.text = ptr;
6017210111Sbschmidt			fw->main.textsz = len;
6018210111Sbschmidt			break;
6019210111Sbschmidt		case IWN_FW_TLV_MAIN_DATA:
6020210111Sbschmidt			fw->main.data = ptr;
6021210111Sbschmidt			fw->main.datasz = len;
6022210111Sbschmidt			break;
6023210111Sbschmidt		case IWN_FW_TLV_INIT_TEXT:
6024210111Sbschmidt			fw->init.text = ptr;
6025210111Sbschmidt			fw->init.textsz = len;
6026210111Sbschmidt			break;
6027210111Sbschmidt		case IWN_FW_TLV_INIT_DATA:
6028210111Sbschmidt			fw->init.data = ptr;
6029210111Sbschmidt			fw->init.datasz = len;
6030210111Sbschmidt			break;
6031210111Sbschmidt		case IWN_FW_TLV_BOOT_TEXT:
6032210111Sbschmidt			fw->boot.text = ptr;
6033210111Sbschmidt			fw->boot.textsz = len;
6034210111Sbschmidt			break;
6035220866Sbschmidt		case IWN_FW_TLV_ENH_SENS:
6036220866Sbschmidt			if (!len)
6037220866Sbschmidt				sc->sc_flags |= IWN_FLAG_ENH_SENS;
6038220866Sbschmidt			break;
6039220866Sbschmidt		case IWN_FW_TLV_PHY_CALIB:
6040220866Sbschmidt			tmp = htole32(*ptr);
6041220866Sbschmidt			if (tmp < 253) {
6042220866Sbschmidt				sc->reset_noise_gain = tmp;
6043220866Sbschmidt				sc->noise_gain = tmp + 1;
6044220866Sbschmidt			}
6045220866Sbschmidt			break;
6046210111Sbschmidt		default:
6047210111Sbschmidt			DPRINTF(sc, IWN_DEBUG_RESET,
6048220724Sbschmidt			    "TLV type %d not handled\n", le16toh(tlv->type));
6049210111Sbschmidt			break;
6050210111Sbschmidt		}
6051220726Sbschmidt next:		/* TLV fields are 32-bit aligned. */
6052210111Sbschmidt		ptr += (len + 3) & ~3;
6053210111Sbschmidt	}
6054210111Sbschmidt	return 0;
6055210111Sbschmidt}
6056210111Sbschmidt
6057206477Sbschmidtstatic int
6058210111Sbschmidtiwn_read_firmware(struct iwn_softc *sc)
6059210111Sbschmidt{
6060210111Sbschmidt	struct iwn_fw_info *fw = &sc->fw;
6061210111Sbschmidt	int error;
6062210111Sbschmidt
6063210111Sbschmidt	IWN_UNLOCK(sc);
6064210111Sbschmidt
6065210111Sbschmidt	memset(fw, 0, sizeof (*fw));
6066210111Sbschmidt
6067210111Sbschmidt	/* Read firmware image from filesystem. */
6068210111Sbschmidt	sc->fw_fp = firmware_get(sc->fwname);
6069210111Sbschmidt	if (sc->fw_fp == NULL) {
6070220724Sbschmidt		device_printf(sc->sc_dev, "%s: could not read firmware %s\n",
6071220724Sbschmidt		    __func__, sc->fwname);
6072210111Sbschmidt		IWN_LOCK(sc);
6073210111Sbschmidt		return EINVAL;
6074210111Sbschmidt	}
6075210111Sbschmidt	IWN_LOCK(sc);
6076210111Sbschmidt
6077210111Sbschmidt	fw->size = sc->fw_fp->datasize;
6078210111Sbschmidt	fw->data = (const uint8_t *)sc->fw_fp->data;
6079210111Sbschmidt	if (fw->size < sizeof (uint32_t)) {
6080220724Sbschmidt		device_printf(sc->sc_dev, "%s: firmware too short: %zu bytes\n",
6081210111Sbschmidt		    __func__, fw->size);
6082220661Sbschmidt		firmware_put(sc->fw_fp, FIRMWARE_UNLOAD);
6083220661Sbschmidt		sc->fw_fp = NULL;
6084210111Sbschmidt		return EINVAL;
6085210111Sbschmidt	}
6086210111Sbschmidt
6087210111Sbschmidt	/* Retrieve text and data sections. */
6088210111Sbschmidt	if (*(const uint32_t *)fw->data != 0)	/* Legacy image. */
6089210111Sbschmidt		error = iwn_read_firmware_leg(sc, fw);
6090210111Sbschmidt	else
6091210111Sbschmidt		error = iwn_read_firmware_tlv(sc, fw, 1);
6092210111Sbschmidt	if (error != 0) {
6093210111Sbschmidt		device_printf(sc->sc_dev,
6094220724Sbschmidt		    "%s: could not read firmware sections, error %d\n",
6095220724Sbschmidt		    __func__, error);
6096220661Sbschmidt		firmware_put(sc->fw_fp, FIRMWARE_UNLOAD);
6097220661Sbschmidt		sc->fw_fp = NULL;
6098210111Sbschmidt		return error;
6099210111Sbschmidt	}
6100210111Sbschmidt
6101210111Sbschmidt	/* Make sure text and data sections fit in hardware memory. */
6102220728Sbschmidt	if (fw->main.textsz > sc->fw_text_maxsz ||
6103220728Sbschmidt	    fw->main.datasz > sc->fw_data_maxsz ||
6104220728Sbschmidt	    fw->init.textsz > sc->fw_text_maxsz ||
6105220728Sbschmidt	    fw->init.datasz > sc->fw_data_maxsz ||
6106210111Sbschmidt	    fw->boot.textsz > IWN_FW_BOOT_TEXT_MAXSZ ||
6107210111Sbschmidt	    (fw->boot.textsz & 3) != 0) {
6108220724Sbschmidt		device_printf(sc->sc_dev, "%s: firmware sections too large\n",
6109220724Sbschmidt		    __func__);
6110220661Sbschmidt		firmware_put(sc->fw_fp, FIRMWARE_UNLOAD);
6111220661Sbschmidt		sc->fw_fp = NULL;
6112210111Sbschmidt		return EINVAL;
6113210111Sbschmidt	}
6114210111Sbschmidt
6115210111Sbschmidt	/* We can proceed with loading the firmware. */
6116210111Sbschmidt	return 0;
6117210111Sbschmidt}
6118210111Sbschmidt
6119210111Sbschmidtstatic int
6120198429Srpauloiwn_clock_wait(struct iwn_softc *sc)
6121198429Srpaulo{
6122198429Srpaulo	int ntries;
6123178676Ssam
6124198429Srpaulo	/* Set "initialization complete" bit. */
6125198429Srpaulo	IWN_SETBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_INIT_DONE);
6126198429Srpaulo
6127198429Srpaulo	/* Wait for clock stabilization. */
6128201209Srpaulo	for (ntries = 0; ntries < 2500; ntries++) {
6129198429Srpaulo		if (IWN_READ(sc, IWN_GP_CNTRL) & IWN_GP_CNTRL_MAC_CLOCK_READY)
6130198429Srpaulo			return 0;
6131201209Srpaulo		DELAY(10);
6132178676Ssam	}
6133198429Srpaulo	device_printf(sc->sc_dev,
6134198429Srpaulo	    "%s: timeout waiting for clock stabilization\n", __func__);
6135198429Srpaulo	return ETIMEDOUT;
6136198429Srpaulo}
6137178676Ssam
6138206477Sbschmidtstatic int
6139201209Srpauloiwn_apm_init(struct iwn_softc *sc)
6140198429Srpaulo{
6141220721Sbschmidt	uint32_t reg;
6142198429Srpaulo	int error;
6143178676Ssam
6144220725Sbschmidt	/* Disable L0s exit timer (NMI bug workaround). */
6145198429Srpaulo	IWN_SETBITS(sc, IWN_GIO_CHICKEN, IWN_GIO_CHICKEN_DIS_L0S_TIMER);
6146220725Sbschmidt	/* Don't wait for ICH L0s (ICH bug workaround). */
6147198429Srpaulo	IWN_SETBITS(sc, IWN_GIO_CHICKEN, IWN_GIO_CHICKEN_L1A_NO_L0S_RX);
6148178676Ssam
6149220725Sbschmidt	/* Set FH wait threshold to max (HW bug under stress workaround). */
6150198429Srpaulo	IWN_SETBITS(sc, IWN_DBG_HPET_MEM, 0xffff0000);
6151198429Srpaulo
6152201209Srpaulo	/* Enable HAP INTA to move adapter from L1a to L0s. */
6153198429Srpaulo	IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_HAP_WAKE_L1A);
6154198429Srpaulo
6155201209Srpaulo	/* Retrieve PCIe Active State Power Management (ASPM). */
6156220721Sbschmidt	reg = pci_read_config(sc->sc_dev, sc->sc_cap_off + 0x10, 1);
6157201209Srpaulo	/* Workaround for HW instability in PCIe L0->L0s->L1 transition. */
6158220721Sbschmidt	if (reg & 0x02)	/* L1 Entry enabled. */
6159201209Srpaulo		IWN_SETBITS(sc, IWN_GIO, IWN_GIO_L0S_ENA);
6160201209Srpaulo	else
6161201209Srpaulo		IWN_CLRBITS(sc, IWN_GIO, IWN_GIO_L0S_ENA);
6162201209Srpaulo
6163201209Srpaulo	if (sc->hw_type != IWN_HW_REV_TYPE_4965 &&
6164210109Sbschmidt	    sc->hw_type <= IWN_HW_REV_TYPE_1000)
6165198429Srpaulo		IWN_SETBITS(sc, IWN_ANA_PLL, IWN_ANA_PLL_INIT);
6166198429Srpaulo
6167201209Srpaulo	/* Wait for clock stabilization before accessing prph. */
6168220726Sbschmidt	if ((error = iwn_clock_wait(sc)) != 0)
6169198429Srpaulo		return error;
6170198429Srpaulo
6171220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
6172198429Srpaulo		return error;
6173201209Srpaulo	if (sc->hw_type == IWN_HW_REV_TYPE_4965) {
6174220725Sbschmidt		/* Enable DMA and BSM (Bootstrap State Machine). */
6175201209Srpaulo		iwn_prph_write(sc, IWN_APMG_CLK_EN,
6176201209Srpaulo		    IWN_APMG_CLK_CTRL_DMA_CLK_RQT |
6177201209Srpaulo		    IWN_APMG_CLK_CTRL_BSM_CLK_RQT);
6178201209Srpaulo	} else {
6179201209Srpaulo		/* Enable DMA. */
6180201209Srpaulo		iwn_prph_write(sc, IWN_APMG_CLK_EN,
6181201209Srpaulo		    IWN_APMG_CLK_CTRL_DMA_CLK_RQT);
6182201209Srpaulo	}
6183198429Srpaulo	DELAY(20);
6184201209Srpaulo	/* Disable L1-Active. */
6185198429Srpaulo	iwn_prph_setbits(sc, IWN_APMG_PCI_STT, IWN_APMG_PCI_STT_L1A_DIS);
6186198429Srpaulo	iwn_nic_unlock(sc);
6187198429Srpaulo
6188198429Srpaulo	return 0;
6189198429Srpaulo}
6190198429Srpaulo
6191206477Sbschmidtstatic void
6192198429Srpauloiwn_apm_stop_master(struct iwn_softc *sc)
6193178676Ssam{
6194178676Ssam	int ntries;
6195178676Ssam
6196201209Srpaulo	/* Stop busmaster DMA activity. */
6197198429Srpaulo	IWN_SETBITS(sc, IWN_RESET, IWN_RESET_STOP_MASTER);
6198178676Ssam	for (ntries = 0; ntries < 100; ntries++) {
6199198429Srpaulo		if (IWN_READ(sc, IWN_RESET) & IWN_RESET_MASTER_DISABLED)
6200198429Srpaulo			return;
6201178676Ssam		DELAY(10);
6202178676Ssam	}
6203220726Sbschmidt	device_printf(sc->sc_dev, "%s: timeout waiting for master\n", __func__);
6204178676Ssam}
6205178676Ssam
6206206477Sbschmidtstatic void
6207198429Srpauloiwn_apm_stop(struct iwn_softc *sc)
6208198429Srpaulo{
6209198429Srpaulo	iwn_apm_stop_master(sc);
6210198429Srpaulo
6211201209Srpaulo	/* Reset the entire device. */
6212198429Srpaulo	IWN_SETBITS(sc, IWN_RESET, IWN_RESET_SW);
6213198429Srpaulo	DELAY(10);
6214198429Srpaulo	/* Clear "initialization complete" bit. */
6215198429Srpaulo	IWN_CLRBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_INIT_DONE);
6216198429Srpaulo}
6217198429Srpaulo
6218206477Sbschmidtstatic int
6219198429Srpauloiwn4965_nic_config(struct iwn_softc *sc)
6220178676Ssam{
6221198429Srpaulo	if (IWN_RFCFG_TYPE(sc->rfcfg) == 1) {
6222198429Srpaulo		/*
6223198429Srpaulo		 * I don't believe this to be correct but this is what the
6224198429Srpaulo		 * vendor driver is doing. Probably the bits should not be
6225198429Srpaulo		 * shifted in IWN_RFCFG_*.
6226198429Srpaulo		 */
6227198429Srpaulo		IWN_SETBITS(sc, IWN_HW_IF_CONFIG,
6228198429Srpaulo		    IWN_RFCFG_TYPE(sc->rfcfg) |
6229198429Srpaulo		    IWN_RFCFG_STEP(sc->rfcfg) |
6230198429Srpaulo		    IWN_RFCFG_DASH(sc->rfcfg));
6231198429Srpaulo	}
6232198429Srpaulo	IWN_SETBITS(sc, IWN_HW_IF_CONFIG,
6233198429Srpaulo	    IWN_HW_IF_CONFIG_RADIO_SI | IWN_HW_IF_CONFIG_MAC_SI);
6234198429Srpaulo	return 0;
6235198429Srpaulo}
6236178676Ssam
6237206477Sbschmidtstatic int
6238198429Srpauloiwn5000_nic_config(struct iwn_softc *sc)
6239198429Srpaulo{
6240198429Srpaulo	uint32_t tmp;
6241198429Srpaulo	int error;
6242178676Ssam
6243198429Srpaulo	if (IWN_RFCFG_TYPE(sc->rfcfg) < 3) {
6244198429Srpaulo		IWN_SETBITS(sc, IWN_HW_IF_CONFIG,
6245198429Srpaulo		    IWN_RFCFG_TYPE(sc->rfcfg) |
6246198429Srpaulo		    IWN_RFCFG_STEP(sc->rfcfg) |
6247198429Srpaulo		    IWN_RFCFG_DASH(sc->rfcfg));
6248198429Srpaulo	}
6249198429Srpaulo	IWN_SETBITS(sc, IWN_HW_IF_CONFIG,
6250198429Srpaulo	    IWN_HW_IF_CONFIG_RADIO_SI | IWN_HW_IF_CONFIG_MAC_SI);
6251198429Srpaulo
6252220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
6253198429Srpaulo		return error;
6254198429Srpaulo	iwn_prph_setbits(sc, IWN_APMG_PS, IWN_APMG_PS_EARLY_PWROFF_DIS);
6255201209Srpaulo
6256201209Srpaulo	if (sc->hw_type == IWN_HW_REV_TYPE_1000) {
6257201209Srpaulo		/*
6258201209Srpaulo		 * Select first Switching Voltage Regulator (1.32V) to
6259201209Srpaulo		 * solve a stability issue related to noisy DC2DC line
6260201209Srpaulo		 * in the silicon of 1000 Series.
6261201209Srpaulo		 */
6262201209Srpaulo		tmp = iwn_prph_read(sc, IWN_APMG_DIGITAL_SVR);
6263201209Srpaulo		tmp &= ~IWN_APMG_DIGITAL_SVR_VOLTAGE_MASK;
6264201209Srpaulo		tmp |= IWN_APMG_DIGITAL_SVR_VOLTAGE_1_32;
6265201209Srpaulo		iwn_prph_write(sc, IWN_APMG_DIGITAL_SVR, tmp);
6266201209Srpaulo	}
6267198429Srpaulo	iwn_nic_unlock(sc);
6268201209Srpaulo
6269201209Srpaulo	if (sc->sc_flags & IWN_FLAG_INTERNAL_PA) {
6270201209Srpaulo		/* Use internal power amplifier only. */
6271201209Srpaulo		IWN_WRITE(sc, IWN_GP_DRIVER, IWN_GP_DRIVER_RADIO_2X2_IPA);
6272201209Srpaulo	}
6273220676Sbschmidt	if ((sc->hw_type == IWN_HW_REV_TYPE_6050 ||
6274220676Sbschmidt	     sc->hw_type == IWN_HW_REV_TYPE_6005) && sc->calib_ver >= 6) {
6275210108Sbschmidt		/* Indicate that ROM calibration version is >=6. */
6276210108Sbschmidt		IWN_SETBITS(sc, IWN_GP_DRIVER, IWN_GP_DRIVER_CALIB_VER6);
6277206444Sbschmidt	}
6278220729Sbschmidt	if (sc->hw_type == IWN_HW_REV_TYPE_6005)
6279220729Sbschmidt		IWN_SETBITS(sc, IWN_GP_DRIVER, IWN_GP_DRIVER_6050_1X2);
6280198429Srpaulo	return 0;
6281198429Srpaulo}
6282198429Srpaulo
6283198429Srpaulo/*
6284198429Srpaulo * Take NIC ownership over Intel Active Management Technology (AMT).
6285198429Srpaulo */
6286206477Sbschmidtstatic int
6287198429Srpauloiwn_hw_prepare(struct iwn_softc *sc)
6288198429Srpaulo{
6289198429Srpaulo	int ntries;
6290198429Srpaulo
6291201209Srpaulo	/* Check if hardware is ready. */
6292201209Srpaulo	IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_NIC_READY);
6293201209Srpaulo	for (ntries = 0; ntries < 5; ntries++) {
6294201209Srpaulo		if (IWN_READ(sc, IWN_HW_IF_CONFIG) &
6295201209Srpaulo		    IWN_HW_IF_CONFIG_NIC_READY)
6296201209Srpaulo			return 0;
6297201209Srpaulo		DELAY(10);
6298201209Srpaulo	}
6299201209Srpaulo
6300201209Srpaulo	/* Hardware not ready, force into ready state. */
6301198429Srpaulo	IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_PREPARE);
6302198429Srpaulo	for (ntries = 0; ntries < 15000; ntries++) {
6303198429Srpaulo		if (!(IWN_READ(sc, IWN_HW_IF_CONFIG) &
6304198429Srpaulo		    IWN_HW_IF_CONFIG_PREPARE_DONE))
6305178676Ssam			break;
6306178676Ssam		DELAY(10);
6307178676Ssam	}
6308198429Srpaulo	if (ntries == 15000)
6309178676Ssam		return ETIMEDOUT;
6310198429Srpaulo
6311201209Srpaulo	/* Hardware should be ready now. */
6312198429Srpaulo	IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_NIC_READY);
6313198429Srpaulo	for (ntries = 0; ntries < 5; ntries++) {
6314198429Srpaulo		if (IWN_READ(sc, IWN_HW_IF_CONFIG) &
6315198429Srpaulo		    IWN_HW_IF_CONFIG_NIC_READY)
6316198429Srpaulo			return 0;
6317198429Srpaulo		DELAY(10);
6318178676Ssam	}
6319198429Srpaulo	return ETIMEDOUT;
6320178676Ssam}
6321178676Ssam
6322206477Sbschmidtstatic int
6323198429Srpauloiwn_hw_init(struct iwn_softc *sc)
6324178676Ssam{
6325220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
6326198429Srpaulo	int error, chnl, qid;
6327178676Ssam
6328198429Srpaulo	/* Clear pending interrupts. */
6329198429Srpaulo	IWN_WRITE(sc, IWN_INT, 0xffffffff);
6330178676Ssam
6331220726Sbschmidt	if ((error = iwn_apm_init(sc)) != 0) {
6332198429Srpaulo		device_printf(sc->sc_dev,
6333220726Sbschmidt		    "%s: could not power ON adapter, error %d\n", __func__,
6334220726Sbschmidt		    error);
6335198429Srpaulo		return error;
6336178676Ssam	}
6337178676Ssam
6338198429Srpaulo	/* Select VMAIN power source. */
6339220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
6340198429Srpaulo		return error;
6341198429Srpaulo	iwn_prph_clrbits(sc, IWN_APMG_PS, IWN_APMG_PS_PWR_SRC_MASK);
6342198429Srpaulo	iwn_nic_unlock(sc);
6343178676Ssam
6344198429Srpaulo	/* Perform adapter-specific initialization. */
6345220728Sbschmidt	if ((error = ops->nic_config(sc)) != 0)
6346198429Srpaulo		return error;
6347178676Ssam
6348198429Srpaulo	/* Initialize RX ring. */
6349220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
6350198429Srpaulo		return error;
6351198429Srpaulo	IWN_WRITE(sc, IWN_FH_RX_CONFIG, 0);
6352198429Srpaulo	IWN_WRITE(sc, IWN_FH_RX_WPTR, 0);
6353220725Sbschmidt	/* Set physical address of RX ring (256-byte aligned). */
6354198429Srpaulo	IWN_WRITE(sc, IWN_FH_RX_BASE, sc->rxq.desc_dma.paddr >> 8);
6355220725Sbschmidt	/* Set physical address of RX status (16-byte aligned). */
6356198429Srpaulo	IWN_WRITE(sc, IWN_FH_STATUS_WPTR, sc->rxq.stat_dma.paddr >> 4);
6357198429Srpaulo	/* Enable RX. */
6358198429Srpaulo	IWN_WRITE(sc, IWN_FH_RX_CONFIG,
6359198429Srpaulo	    IWN_FH_RX_CONFIG_ENA           |
6360198429Srpaulo	    IWN_FH_RX_CONFIG_IGN_RXF_EMPTY |	/* HW bug workaround */
6361198429Srpaulo	    IWN_FH_RX_CONFIG_IRQ_DST_HOST  |
6362198429Srpaulo	    IWN_FH_RX_CONFIG_SINGLE_FRAME  |
6363198429Srpaulo	    IWN_FH_RX_CONFIG_RB_TIMEOUT(0) |
6364198429Srpaulo	    IWN_FH_RX_CONFIG_NRBD(IWN_RX_RING_COUNT_LOG));
6365198429Srpaulo	iwn_nic_unlock(sc);
6366198429Srpaulo	IWN_WRITE(sc, IWN_FH_RX_WPTR, (IWN_RX_RING_COUNT - 1) & ~7);
6367178676Ssam
6368220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
6369198429Srpaulo		return error;
6370178676Ssam
6371198429Srpaulo	/* Initialize TX scheduler. */
6372220728Sbschmidt	iwn_prph_write(sc, sc->sched_txfact_addr, 0);
6373178676Ssam
6374220725Sbschmidt	/* Set physical address of "keep warm" page (16-byte aligned). */
6375198429Srpaulo	IWN_WRITE(sc, IWN_FH_KW_ADDR, sc->kw_dma.paddr >> 4);
6376198429Srpaulo
6377198429Srpaulo	/* Initialize TX rings. */
6378220728Sbschmidt	for (qid = 0; qid < sc->ntxqs; qid++) {
6379198429Srpaulo		struct iwn_tx_ring *txq = &sc->txq[qid];
6380198429Srpaulo
6381220725Sbschmidt		/* Set physical address of TX ring (256-byte aligned). */
6382198429Srpaulo		IWN_WRITE(sc, IWN_FH_CBBC_QUEUE(qid),
6383198429Srpaulo		    txq->desc_dma.paddr >> 8);
6384178676Ssam	}
6385198429Srpaulo	iwn_nic_unlock(sc);
6386178676Ssam
6387198429Srpaulo	/* Enable DMA channels. */
6388220728Sbschmidt	for (chnl = 0; chnl < sc->ndmachnls; chnl++) {
6389198429Srpaulo		IWN_WRITE(sc, IWN_FH_TX_CONFIG(chnl),
6390198429Srpaulo		    IWN_FH_TX_CONFIG_DMA_ENA |
6391198429Srpaulo		    IWN_FH_TX_CONFIG_DMA_CREDIT_ENA);
6392198429Srpaulo	}
6393198429Srpaulo
6394198429Srpaulo	/* Clear "radio off" and "commands blocked" bits. */
6395198429Srpaulo	IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_RFKILL);
6396198429Srpaulo	IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_CMD_BLOCKED);
6397198429Srpaulo
6398198429Srpaulo	/* Clear pending interrupts. */
6399198429Srpaulo	IWN_WRITE(sc, IWN_INT, 0xffffffff);
6400198429Srpaulo	/* Enable interrupt coalescing. */
6401198429Srpaulo	IWN_WRITE(sc, IWN_INT_COALESCING, 512 / 8);
6402198429Srpaulo	/* Enable interrupts. */
6403201209Srpaulo	IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask);
6404198429Srpaulo
6405198429Srpaulo	/* _Really_ make sure "radio off" bit is cleared! */
6406198429Srpaulo	IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_RFKILL);
6407198429Srpaulo	IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_RFKILL);
6408198429Srpaulo
6409220729Sbschmidt	/* Enable shadow registers. */
6410220729Sbschmidt	if (sc->hw_type >= IWN_HW_REV_TYPE_6000)
6411220729Sbschmidt		IWN_SETBITS(sc, IWN_SHADOW_REG_CTRL, 0x800fffff);
6412220729Sbschmidt
6413220728Sbschmidt	if ((error = ops->load_firmware(sc)) != 0) {
6414178676Ssam		device_printf(sc->sc_dev,
6415220726Sbschmidt		    "%s: could not load firmware, error %d\n", __func__,
6416220726Sbschmidt		    error);
6417198429Srpaulo		return error;
6418178676Ssam	}
6419198429Srpaulo	/* Wait at most one second for firmware alive notification. */
6420220726Sbschmidt	if ((error = msleep(sc, &sc->sc_mtx, PCATCH, "iwninit", hz)) != 0) {
6421198429Srpaulo		device_printf(sc->sc_dev,
6422198429Srpaulo		    "%s: timeout waiting for adapter to initialize, error %d\n",
6423198429Srpaulo		    __func__, error);
6424198429Srpaulo		return error;
6425198429Srpaulo	}
6426198429Srpaulo	/* Do post-firmware initialization. */
6427220728Sbschmidt	return ops->post_alive(sc);
6428198429Srpaulo}
6429178676Ssam
6430206477Sbschmidtstatic void
6431198429Srpauloiwn_hw_stop(struct iwn_softc *sc)
6432198429Srpaulo{
6433198429Srpaulo	int chnl, qid, ntries;
6434178676Ssam
6435198429Srpaulo	IWN_WRITE(sc, IWN_RESET, IWN_RESET_NEVO);
6436178676Ssam
6437198429Srpaulo	/* Disable interrupts. */
6438201209Srpaulo	IWN_WRITE(sc, IWN_INT_MASK, 0);
6439198429Srpaulo	IWN_WRITE(sc, IWN_INT, 0xffffffff);
6440198429Srpaulo	IWN_WRITE(sc, IWN_FH_INT, 0xffffffff);
6441201209Srpaulo	sc->sc_flags &= ~IWN_FLAG_USE_ICT;
6442178676Ssam
6443198429Srpaulo	/* Make sure we no longer hold the NIC lock. */
6444198429Srpaulo	iwn_nic_unlock(sc);
6445178676Ssam
6446198429Srpaulo	/* Stop TX scheduler. */
6447220728Sbschmidt	iwn_prph_write(sc, sc->sched_txfact_addr, 0);
6448178676Ssam
6449198429Srpaulo	/* Stop all DMA channels. */
6450198429Srpaulo	if (iwn_nic_lock(sc) == 0) {
6451220728Sbschmidt		for (chnl = 0; chnl < sc->ndmachnls; chnl++) {
6452198429Srpaulo			IWN_WRITE(sc, IWN_FH_TX_CONFIG(chnl), 0);
6453198429Srpaulo			for (ntries = 0; ntries < 200; ntries++) {
6454220659Sbschmidt				if (IWN_READ(sc, IWN_FH_TX_STATUS) &
6455198429Srpaulo				    IWN_FH_TX_STATUS_IDLE(chnl))
6456198429Srpaulo					break;
6457198429Srpaulo				DELAY(10);
6458198429Srpaulo			}
6459198429Srpaulo		}
6460198429Srpaulo		iwn_nic_unlock(sc);
6461198429Srpaulo	}
6462178676Ssam
6463198429Srpaulo	/* Stop RX ring. */
6464198429Srpaulo	iwn_reset_rx_ring(sc, &sc->rxq);
6465178676Ssam
6466198429Srpaulo	/* Reset all TX rings. */
6467220728Sbschmidt	for (qid = 0; qid < sc->ntxqs; qid++)
6468198429Srpaulo		iwn_reset_tx_ring(sc, &sc->txq[qid]);
6469178676Ssam
6470198429Srpaulo	if (iwn_nic_lock(sc) == 0) {
6471201209Srpaulo		iwn_prph_write(sc, IWN_APMG_CLK_DIS,
6472201209Srpaulo		    IWN_APMG_CLK_CTRL_DMA_CLK_RQT);
6473198429Srpaulo		iwn_nic_unlock(sc);
6474178676Ssam	}
6475198429Srpaulo	DELAY(5);
6476198429Srpaulo	/* Power OFF adapter. */
6477198429Srpaulo	iwn_apm_stop(sc);
6478198429Srpaulo}
6479178676Ssam
6480206477Sbschmidtstatic void
6481220723Sbschmidtiwn_radio_on(void *arg0, int pending)
6482220723Sbschmidt{
6483220723Sbschmidt	struct iwn_softc *sc = arg0;
6484220723Sbschmidt	struct ifnet *ifp = sc->sc_ifp;
6485220723Sbschmidt	struct ieee80211com *ic = ifp->if_l2com;
6486220723Sbschmidt	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
6487220723Sbschmidt
6488220723Sbschmidt	if (vap != NULL) {
6489220723Sbschmidt		iwn_init(sc);
6490220723Sbschmidt		ieee80211_init(vap);
6491220723Sbschmidt	}
6492220723Sbschmidt}
6493220723Sbschmidt
6494220723Sbschmidtstatic void
6495220723Sbschmidtiwn_radio_off(void *arg0, int pending)
6496220723Sbschmidt{
6497220723Sbschmidt	struct iwn_softc *sc = arg0;
6498220723Sbschmidt	struct ifnet *ifp = sc->sc_ifp;
6499220723Sbschmidt	struct ieee80211com *ic = ifp->if_l2com;
6500220723Sbschmidt	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
6501220723Sbschmidt
6502220723Sbschmidt	iwn_stop(sc);
6503220723Sbschmidt	if (vap != NULL)
6504220723Sbschmidt		ieee80211_stop(vap);
6505220723Sbschmidt
6506220723Sbschmidt	/* Enable interrupts to get RF toggle notification. */
6507220723Sbschmidt	IWN_LOCK(sc);
6508220723Sbschmidt	IWN_WRITE(sc, IWN_INT, 0xffffffff);
6509220723Sbschmidt	IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask);
6510220723Sbschmidt	IWN_UNLOCK(sc);
6511220723Sbschmidt}
6512220723Sbschmidt
6513220723Sbschmidtstatic void
6514198429Srpauloiwn_init_locked(struct iwn_softc *sc)
6515198429Srpaulo{
6516198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
6517198429Srpaulo	int error;
6518178676Ssam
6519198429Srpaulo	IWN_LOCK_ASSERT(sc);
6520178676Ssam
6521220726Sbschmidt	if ((error = iwn_hw_prepare(sc)) != 0) {
6522220724Sbschmidt		device_printf(sc->sc_dev, "%s: hardware not ready, error %d\n",
6523198429Srpaulo		    __func__, error);
6524198429Srpaulo		goto fail;
6525198429Srpaulo	}
6526198429Srpaulo
6527201209Srpaulo	/* Initialize interrupt mask to default value. */
6528201209Srpaulo	sc->int_mask = IWN_INT_MASK_DEF;
6529201209Srpaulo	sc->sc_flags &= ~IWN_FLAG_USE_ICT;
6530201209Srpaulo
6531198429Srpaulo	/* Check that the radio is not disabled by hardware switch. */
6532198429Srpaulo	if (!(IWN_READ(sc, IWN_GP_CNTRL) & IWN_GP_CNTRL_RFKILL)) {
6533178676Ssam		device_printf(sc->sc_dev,
6534201209Srpaulo		    "radio is disabled by hardware switch\n");
6535201209Srpaulo		/* Enable interrupts to get RF toggle notifications. */
6536201209Srpaulo		IWN_WRITE(sc, IWN_INT, 0xffffffff);
6537201209Srpaulo		IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask);
6538201209Srpaulo		return;
6539178676Ssam	}
6540178676Ssam
6541198429Srpaulo	/* Read firmware images from the filesystem. */
6542220726Sbschmidt	if ((error = iwn_read_firmware(sc)) != 0) {
6543178676Ssam		device_printf(sc->sc_dev,
6544220726Sbschmidt		    "%s: could not read firmware, error %d\n", __func__,
6545220726Sbschmidt		    error);
6546198429Srpaulo		goto fail;
6547178676Ssam	}
6548178676Ssam
6549198429Srpaulo	/* Initialize hardware and upload firmware. */
6550198429Srpaulo	error = iwn_hw_init(sc);
6551201209Srpaulo	firmware_put(sc->fw_fp, FIRMWARE_UNLOAD);
6552201209Srpaulo	sc->fw_fp = NULL;
6553198429Srpaulo	if (error != 0) {
6554198429Srpaulo		device_printf(sc->sc_dev,
6555220726Sbschmidt		    "%s: could not initialize hardware, error %d\n", __func__,
6556220726Sbschmidt		    error);
6557198429Srpaulo		goto fail;
6558198429Srpaulo	}
6559178676Ssam
6560198429Srpaulo	/* Configure adapter now that it is ready. */
6561220726Sbschmidt	if ((error = iwn_config(sc)) != 0) {
6562178676Ssam		device_printf(sc->sc_dev,
6563220726Sbschmidt		    "%s: could not configure device, error %d\n", __func__,
6564220726Sbschmidt		    error);
6565198429Srpaulo		goto fail;
6566178676Ssam	}
6567178676Ssam
6568178676Ssam	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
6569178676Ssam	ifp->if_drv_flags |= IFF_DRV_RUNNING;
6570198429Srpaulo
6571220667Sbschmidt	callout_reset(&sc->watchdog_to, hz, iwn_watchdog, sc);
6572198429Srpaulo	return;
6573198429Srpaulo
6574220726Sbschmidtfail:	iwn_stop_locked(sc);
6575178676Ssam}
6576178676Ssam
6577206477Sbschmidtstatic void
6578178676Ssamiwn_init(void *arg)
6579178676Ssam{
6580178676Ssam	struct iwn_softc *sc = arg;
6581178676Ssam	struct ifnet *ifp = sc->sc_ifp;
6582178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
6583178676Ssam
6584178676Ssam	IWN_LOCK(sc);
6585178676Ssam	iwn_init_locked(sc);
6586178676Ssam	IWN_UNLOCK(sc);
6587178676Ssam
6588178676Ssam	if (ifp->if_drv_flags & IFF_DRV_RUNNING)
6589178676Ssam		ieee80211_start_all(ic);
6590178676Ssam}
6591178676Ssam
6592206477Sbschmidtstatic void
6593178676Ssamiwn_stop_locked(struct iwn_softc *sc)
6594178676Ssam{
6595178676Ssam	struct ifnet *ifp = sc->sc_ifp;
6596178676Ssam
6597178676Ssam	IWN_LOCK_ASSERT(sc);
6598178676Ssam
6599178676Ssam	sc->sc_tx_timer = 0;
6600220667Sbschmidt	callout_stop(&sc->watchdog_to);
6601220667Sbschmidt	callout_stop(&sc->calib_to);
6602178676Ssam	ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
6603178676Ssam
6604198429Srpaulo	/* Power OFF hardware. */
6605198429Srpaulo	iwn_hw_stop(sc);
6606198429Srpaulo}
6607178676Ssam
6608206477Sbschmidtstatic void
6609178676Ssamiwn_stop(struct iwn_softc *sc)
6610178676Ssam{
6611178676Ssam	IWN_LOCK(sc);
6612178676Ssam	iwn_stop_locked(sc);
6613178676Ssam	IWN_UNLOCK(sc);
6614178676Ssam}
6615178676Ssam
6616178676Ssam/*
6617178676Ssam * Callback from net80211 to start a scan.
6618178676Ssam */
6619178676Ssamstatic void
6620178676Ssamiwn_scan_start(struct ieee80211com *ic)
6621178676Ssam{
6622178676Ssam	struct ifnet *ifp = ic->ic_ifp;
6623178676Ssam	struct iwn_softc *sc = ifp->if_softc;
6624178676Ssam
6625191746Sthompsa	IWN_LOCK(sc);
6626191746Sthompsa	/* make the link LED blink while we're scanning */
6627191746Sthompsa	iwn_set_led(sc, IWN_LED_LINK, 20, 2);
6628191746Sthompsa	IWN_UNLOCK(sc);
6629178676Ssam}
6630178676Ssam
6631178676Ssam/*
6632178676Ssam * Callback from net80211 to terminate a scan.
6633178676Ssam */
6634178676Ssamstatic void
6635178676Ssamiwn_scan_end(struct ieee80211com *ic)
6636178676Ssam{
6637201209Srpaulo	struct ifnet *ifp = ic->ic_ifp;
6638201209Srpaulo	struct iwn_softc *sc = ifp->if_softc;
6639201209Srpaulo	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
6640201209Srpaulo
6641201209Srpaulo	IWN_LOCK(sc);
6642201209Srpaulo	if (vap->iv_state == IEEE80211_S_RUN) {
6643201209Srpaulo		/* Set link LED to ON status if we are associated */
6644201209Srpaulo		iwn_set_led(sc, IWN_LED_LINK, 0, 1);
6645201209Srpaulo	}
6646201209Srpaulo	IWN_UNLOCK(sc);
6647178676Ssam}
6648178676Ssam
6649178676Ssam/*
6650178676Ssam * Callback from net80211 to force a channel change.
6651178676Ssam */
6652178676Ssamstatic void
6653178676Ssamiwn_set_channel(struct ieee80211com *ic)
6654178676Ssam{
6655198429Srpaulo	const struct ieee80211_channel *c = ic->ic_curchan;
6656178676Ssam	struct ifnet *ifp = ic->ic_ifp;
6657178676Ssam	struct iwn_softc *sc = ifp->if_softc;
6658178676Ssam
6659191746Sthompsa	IWN_LOCK(sc);
6660201209Srpaulo	sc->sc_rxtap.wr_chan_freq = htole16(c->ic_freq);
6661201209Srpaulo	sc->sc_rxtap.wr_chan_flags = htole16(c->ic_flags);
6662201209Srpaulo	sc->sc_txtap.wt_chan_freq = htole16(c->ic_freq);
6663201209Srpaulo	sc->sc_txtap.wt_chan_flags = htole16(c->ic_flags);
6664191746Sthompsa	IWN_UNLOCK(sc);
6665178676Ssam}
6666178676Ssam
6667178676Ssam/*
6668178676Ssam * Callback from net80211 to start scanning of the current channel.
6669178676Ssam */
6670178676Ssamstatic void
6671178676Ssamiwn_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell)
6672178676Ssam{
6673178676Ssam	struct ieee80211vap *vap = ss->ss_vap;
6674178676Ssam	struct iwn_softc *sc = vap->iv_ic->ic_ifp->if_softc;
6675191746Sthompsa	int error;
6676178676Ssam
6677191746Sthompsa	IWN_LOCK(sc);
6678191746Sthompsa	error = iwn_scan(sc);
6679191746Sthompsa	IWN_UNLOCK(sc);
6680191746Sthompsa	if (error != 0)
6681191746Sthompsa		ieee80211_cancel_scan(vap);
6682178676Ssam}
6683178676Ssam
6684178676Ssam/*
6685178676Ssam * Callback from net80211 to handle the minimum dwell time being met.
6686178676Ssam * The intent is to terminate the scan but we just let the firmware
6687178676Ssam * notify us when it's finished as we have no safe way to abort it.
6688178676Ssam */
6689178676Ssamstatic void
6690178676Ssamiwn_scan_mindwell(struct ieee80211_scan_state *ss)
6691178676Ssam{
6692178676Ssam	/* NB: don't try to abort scan; wait for firmware to finish */
6693178676Ssam}
6694178676Ssam
6695178676Ssamstatic void
6696198429Srpauloiwn_hw_reset(void *arg0, int pending)
6697178676Ssam{
6698178676Ssam	struct iwn_softc *sc = arg0;
6699178676Ssam	struct ifnet *ifp = sc->sc_ifp;
6700178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
6701178676Ssam
6702201209Srpaulo	iwn_stop(sc);
6703191746Sthompsa	iwn_init(sc);
6704191746Sthompsa	ieee80211_notify_radio(ic, 1);
6705191746Sthompsa}
6706