if_iwn.c revision 220662
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 220662 2011-04-15 17:10:52Z 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
75178676Ssamstatic int	iwn_probe(device_t);
76178676Ssamstatic int	iwn_attach(device_t);
77206477Sbschmidtstatic const struct iwn_hal *iwn_hal_attach(struct iwn_softc *);
78206477Sbschmidtstatic void	iwn_radiotap_attach(struct iwn_softc *);
79178676Ssamstatic struct ieee80211vap *iwn_vap_create(struct ieee80211com *,
80178676Ssam		    const char name[IFNAMSIZ], int unit, int opmode,
81178676Ssam		    int flags, const uint8_t bssid[IEEE80211_ADDR_LEN],
82178676Ssam		    const uint8_t mac[IEEE80211_ADDR_LEN]);
83178676Ssamstatic void	iwn_vap_delete(struct ieee80211vap *);
84206474Sbschmidtstatic int	iwn_detach(device_t);
85206477Sbschmidtstatic int	iwn_nic_lock(struct iwn_softc *);
86206477Sbschmidtstatic int	iwn_eeprom_lock(struct iwn_softc *);
87206477Sbschmidtstatic int	iwn_init_otprom(struct iwn_softc *);
88206477Sbschmidtstatic int	iwn_read_prom_data(struct iwn_softc *, uint32_t, void *, int);
89206474Sbschmidtstatic void	iwn_dma_map_addr(void *, bus_dma_segment_t *, int, int);
90178676Ssamstatic int	iwn_dma_contig_alloc(struct iwn_softc *, struct iwn_dma_info *,
91178676Ssam		    void **, bus_size_t, bus_size_t, int);
92178676Ssamstatic void	iwn_dma_contig_free(struct iwn_dma_info *);
93206477Sbschmidtstatic int	iwn_alloc_sched(struct iwn_softc *);
94206477Sbschmidtstatic void	iwn_free_sched(struct iwn_softc *);
95206477Sbschmidtstatic int	iwn_alloc_kw(struct iwn_softc *);
96206477Sbschmidtstatic void	iwn_free_kw(struct iwn_softc *);
97206477Sbschmidtstatic int	iwn_alloc_ict(struct iwn_softc *);
98206477Sbschmidtstatic void	iwn_free_ict(struct iwn_softc *);
99206477Sbschmidtstatic int	iwn_alloc_fwmem(struct iwn_softc *);
100206477Sbschmidtstatic void	iwn_free_fwmem(struct iwn_softc *);
101206477Sbschmidtstatic int	iwn_alloc_rx_ring(struct iwn_softc *, struct iwn_rx_ring *);
102206477Sbschmidtstatic void	iwn_reset_rx_ring(struct iwn_softc *, struct iwn_rx_ring *);
103206477Sbschmidtstatic void	iwn_free_rx_ring(struct iwn_softc *, struct iwn_rx_ring *);
104206477Sbschmidtstatic int	iwn_alloc_tx_ring(struct iwn_softc *, struct iwn_tx_ring *,
105178676Ssam		    int);
106206477Sbschmidtstatic void	iwn_reset_tx_ring(struct iwn_softc *, struct iwn_tx_ring *);
107206477Sbschmidtstatic void	iwn_free_tx_ring(struct iwn_softc *, struct iwn_tx_ring *);
108206477Sbschmidtstatic void	iwn5000_ict_reset(struct iwn_softc *);
109206477Sbschmidtstatic int	iwn_read_eeprom(struct iwn_softc *,
110198429Srpaulo		    uint8_t macaddr[IEEE80211_ADDR_LEN]);
111206477Sbschmidtstatic void	iwn4965_read_eeprom(struct iwn_softc *);
112206477Sbschmidtstatic void	iwn4965_print_power_group(struct iwn_softc *, int);
113206477Sbschmidtstatic void	iwn5000_read_eeprom(struct iwn_softc *);
114206474Sbschmidtstatic uint32_t	iwn_eeprom_channel_flags(struct iwn_eeprom_chan *);
115206474Sbschmidtstatic void	iwn_read_eeprom_band(struct iwn_softc *, int);
116206474Sbschmidt#if 0	/* HT */
117206474Sbschmidtstatic void	iwn_read_eeprom_ht40(struct iwn_softc *, int);
118206474Sbschmidt#endif
119201209Srpaulostatic void	iwn_read_eeprom_channels(struct iwn_softc *, int,
120201209Srpaulo		    uint32_t);
121206477Sbschmidtstatic void	iwn_read_eeprom_enhinfo(struct iwn_softc *);
122206477Sbschmidtstatic struct ieee80211_node *iwn_node_alloc(struct ieee80211vap *,
123198429Srpaulo		    const uint8_t mac[IEEE80211_ADDR_LEN]);
124206477Sbschmidtstatic int	iwn_media_change(struct ifnet *);
125206477Sbschmidtstatic int	iwn_newstate(struct ieee80211vap *, enum ieee80211_state, int);
126206477Sbschmidtstatic void	iwn_rx_phy(struct iwn_softc *, struct iwn_rx_desc *,
127198429Srpaulo		    struct iwn_rx_data *);
128178676Ssamstatic void	iwn_timer_timeout(void *);
129178676Ssamstatic void	iwn_calib_reset(struct iwn_softc *);
130206477Sbschmidtstatic void	iwn_rx_done(struct iwn_softc *, struct iwn_rx_desc *,
131178676Ssam		    struct iwn_rx_data *);
132201209Srpaulo#if 0	/* HT */
133206477Sbschmidtstatic void	iwn_rx_compressed_ba(struct iwn_softc *, struct iwn_rx_desc *,
134201209Srpaulo		    struct iwn_rx_data *);
135201209Srpaulo#endif
136206477Sbschmidtstatic void	iwn_rx_statistics(struct iwn_softc *, struct iwn_rx_desc *,
137198429Srpaulo		    struct iwn_rx_data *);
138206477Sbschmidtstatic void	iwn4965_tx_done(struct iwn_softc *, struct iwn_rx_desc *,
139198429Srpaulo		    struct iwn_rx_data *);
140206477Sbschmidtstatic void	iwn5000_tx_done(struct iwn_softc *, struct iwn_rx_desc *,
141198429Srpaulo		    struct iwn_rx_data *);
142206477Sbschmidtstatic void	iwn_tx_done(struct iwn_softc *, struct iwn_rx_desc *, int,
143198429Srpaulo		    uint8_t);
144206477Sbschmidtstatic void	iwn_cmd_done(struct iwn_softc *, struct iwn_rx_desc *);
145206477Sbschmidtstatic void	iwn_notif_intr(struct iwn_softc *);
146206477Sbschmidtstatic void	iwn_wakeup_intr(struct iwn_softc *);
147206477Sbschmidtstatic void	iwn_rftoggle_intr(struct iwn_softc *);
148206477Sbschmidtstatic void	iwn_fatal_intr(struct iwn_softc *);
149206477Sbschmidtstatic void	iwn_intr(void *);
150206477Sbschmidtstatic void	iwn4965_update_sched(struct iwn_softc *, int, int, uint8_t,
151198429Srpaulo		    uint16_t);
152206477Sbschmidtstatic void	iwn5000_update_sched(struct iwn_softc *, int, int, uint8_t,
153198429Srpaulo		    uint16_t);
154206475Sbschmidt#ifdef notyet
155206477Sbschmidtstatic void	iwn5000_reset_sched(struct iwn_softc *, int, int);
156206475Sbschmidt#endif
157206474Sbschmidtstatic uint8_t	iwn_plcp_signal(int);
158206477Sbschmidtstatic int	iwn_tx_data(struct iwn_softc *, struct mbuf *,
159178676Ssam		    struct ieee80211_node *, struct iwn_tx_ring *);
160198429Srpaulostatic int	iwn_raw_xmit(struct ieee80211_node *, struct mbuf *,
161198429Srpaulo		    const struct ieee80211_bpf_params *);
162206477Sbschmidtstatic void	iwn_start(struct ifnet *);
163206477Sbschmidtstatic void	iwn_start_locked(struct ifnet *);
164198429Srpaulostatic void	iwn_watchdog(struct iwn_softc *sc);
165206477Sbschmidtstatic int	iwn_ioctl(struct ifnet *, u_long, caddr_t);
166206477Sbschmidtstatic int	iwn_cmd(struct iwn_softc *, int, const void *, int, int);
167206477Sbschmidtstatic int	iwn4965_add_node(struct iwn_softc *, struct iwn_node_info *,
168198429Srpaulo		    int);
169206477Sbschmidtstatic int	iwn5000_add_node(struct iwn_softc *, struct iwn_node_info *,
170198429Srpaulo		    int);
171206477Sbschmidtstatic int	iwn_set_link_quality(struct iwn_softc *, uint8_t, int);
172206477Sbschmidtstatic int	iwn_add_broadcast_node(struct iwn_softc *, int);
173206477Sbschmidtstatic int	iwn_wme_update(struct ieee80211com *);
174201209Srpaulostatic void	iwn_update_mcast(struct ifnet *);
175206477Sbschmidtstatic void	iwn_set_led(struct iwn_softc *, uint8_t, uint8_t, uint8_t);
176206477Sbschmidtstatic int	iwn_set_critical_temp(struct iwn_softc *);
177206477Sbschmidtstatic int	iwn_set_timing(struct iwn_softc *, struct ieee80211_node *);
178206477Sbschmidtstatic void	iwn4965_power_calibration(struct iwn_softc *, int);
179206477Sbschmidtstatic int	iwn4965_set_txpower(struct iwn_softc *,
180201882Skeramida		    struct ieee80211_channel *, int);
181206477Sbschmidtstatic int	iwn5000_set_txpower(struct iwn_softc *,
182201882Skeramida		    struct ieee80211_channel *, int);
183206477Sbschmidtstatic int	iwn4965_get_rssi(struct iwn_softc *, struct iwn_rx_stat *);
184206477Sbschmidtstatic int	iwn5000_get_rssi(struct iwn_softc *, struct iwn_rx_stat *);
185206477Sbschmidtstatic int	iwn_get_noise(const struct iwn_rx_general_stats *);
186206477Sbschmidtstatic int	iwn4965_get_temperature(struct iwn_softc *);
187206477Sbschmidtstatic int	iwn5000_get_temperature(struct iwn_softc *);
188206477Sbschmidtstatic int	iwn_init_sensitivity(struct iwn_softc *);
189206477Sbschmidtstatic void	iwn_collect_noise(struct iwn_softc *,
190178676Ssam		    const struct iwn_rx_general_stats *);
191206477Sbschmidtstatic int	iwn4965_init_gains(struct iwn_softc *);
192206477Sbschmidtstatic int	iwn5000_init_gains(struct iwn_softc *);
193206477Sbschmidtstatic int	iwn4965_set_gains(struct iwn_softc *);
194206477Sbschmidtstatic int	iwn5000_set_gains(struct iwn_softc *);
195206477Sbschmidtstatic void	iwn_tune_sensitivity(struct iwn_softc *,
196178676Ssam		    const struct iwn_rx_stats *);
197206477Sbschmidtstatic int	iwn_send_sensitivity(struct iwn_softc *);
198206477Sbschmidtstatic int	iwn_set_pslevel(struct iwn_softc *, int, int, int);
199220662Sbschmidtstatic int	iwn_send_btcoex(struct iwn_softc *);
200206477Sbschmidtstatic int	iwn_config(struct iwn_softc *);
201220634Sbschmidtstatic uint8_t	*ieee80211_add_ssid(uint8_t *, const uint8_t *, u_int);
202206477Sbschmidtstatic int	iwn_scan(struct iwn_softc *);
203206477Sbschmidtstatic int	iwn_auth(struct iwn_softc *, struct ieee80211vap *vap);
204206477Sbschmidtstatic int	iwn_run(struct iwn_softc *, struct ieee80211vap *vap);
205206474Sbschmidt#if 0	/* HT */
206206474Sbschmidtstatic int	iwn_ampdu_rx_start(struct ieee80211com *,
207206474Sbschmidt		    struct ieee80211_node *, uint8_t);
208206474Sbschmidtstatic void	iwn_ampdu_rx_stop(struct ieee80211com *,
209206474Sbschmidt		    struct ieee80211_node *, uint8_t);
210206474Sbschmidtstatic int	iwn_ampdu_tx_start(struct ieee80211com *,
211206474Sbschmidt		    struct ieee80211_node *, uint8_t);
212206474Sbschmidtstatic void	iwn_ampdu_tx_stop(struct ieee80211com *,
213206474Sbschmidt		    struct ieee80211_node *, uint8_t);
214206474Sbschmidtstatic void	iwn4965_ampdu_tx_start(struct iwn_softc *,
215206474Sbschmidt		    struct ieee80211_node *, uint8_t, uint16_t);
216206474Sbschmidtstatic void	iwn4965_ampdu_tx_stop(struct iwn_softc *, uint8_t, uint16_t);
217206474Sbschmidtstatic void	iwn5000_ampdu_tx_start(struct iwn_softc *,
218206474Sbschmidt		    struct ieee80211_node *, uint8_t, uint16_t);
219206474Sbschmidtstatic void	iwn5000_ampdu_tx_stop(struct iwn_softc *, uint8_t, uint16_t);
220206474Sbschmidt#endif
221212854Sbschmidtstatic int	iwn5000_send_calib_results(struct iwn_softc *);
222212854Sbschmidtstatic int	iwn5000_save_calib_result(struct iwn_softc *,
223212854Sbschmidt		    struct iwn_phy_calib *, int, int);
224212854Sbschmidtstatic void	iwn5000_free_calib_results(struct iwn_softc *);
225212854Sbschmidtstatic int	iwn5000_chrystal_calib(struct iwn_softc *);
226216195Sbschmidtstatic int	iwn5000_send_calib_query(struct iwn_softc *, uint32_t);
227212854Sbschmidtstatic int	iwn5000_rx_calib_result(struct iwn_softc *,
228212853Sbschmidt		    struct iwn_rx_desc *, struct iwn_rx_data *);
229206477Sbschmidtstatic int	iwn5000_send_wimax_coex(struct iwn_softc *);
230206477Sbschmidtstatic int	iwn4965_post_alive(struct iwn_softc *);
231206477Sbschmidtstatic int	iwn5000_post_alive(struct iwn_softc *);
232206477Sbschmidtstatic int	iwn4965_load_bootcode(struct iwn_softc *, const uint8_t *,
233198429Srpaulo		    int);
234206477Sbschmidtstatic int	iwn4965_load_firmware(struct iwn_softc *);
235206477Sbschmidtstatic int	iwn5000_load_firmware_section(struct iwn_softc *, uint32_t,
236198429Srpaulo		    const uint8_t *, int);
237206477Sbschmidtstatic int	iwn5000_load_firmware(struct iwn_softc *);
238210111Sbschmidtstatic int	iwn_read_firmware_leg(struct iwn_softc *,
239210111Sbschmidt		    struct iwn_fw_info *);
240210111Sbschmidtstatic int	iwn_read_firmware_tlv(struct iwn_softc *,
241210111Sbschmidt		    struct iwn_fw_info *, uint16_t);
242206477Sbschmidtstatic int	iwn_read_firmware(struct iwn_softc *);
243206477Sbschmidtstatic int	iwn_clock_wait(struct iwn_softc *);
244206477Sbschmidtstatic int	iwn_apm_init(struct iwn_softc *);
245206477Sbschmidtstatic void	iwn_apm_stop_master(struct iwn_softc *);
246206477Sbschmidtstatic void	iwn_apm_stop(struct iwn_softc *);
247206477Sbschmidtstatic int	iwn4965_nic_config(struct iwn_softc *);
248206477Sbschmidtstatic int	iwn5000_nic_config(struct iwn_softc *);
249206477Sbschmidtstatic int	iwn_hw_prepare(struct iwn_softc *);
250206477Sbschmidtstatic int	iwn_hw_init(struct iwn_softc *);
251206477Sbschmidtstatic void	iwn_hw_stop(struct iwn_softc *);
252206477Sbschmidtstatic void	iwn_init_locked(struct iwn_softc *);
253206477Sbschmidtstatic void	iwn_init(void *);
254206477Sbschmidtstatic void	iwn_stop_locked(struct iwn_softc *);
255206477Sbschmidtstatic void	iwn_stop(struct iwn_softc *);
256178676Ssamstatic void 	iwn_scan_start(struct ieee80211com *);
257178676Ssamstatic void 	iwn_scan_end(struct ieee80211com *);
258178676Ssamstatic void 	iwn_set_channel(struct ieee80211com *);
259178676Ssamstatic void 	iwn_scan_curchan(struct ieee80211_scan_state *, unsigned long);
260178676Ssamstatic void 	iwn_scan_mindwell(struct ieee80211_scan_state *);
261206474Sbschmidtstatic struct iwn_eeprom_chan *iwn_find_eeprom_channel(struct iwn_softc *,
262206474Sbschmidt		    struct ieee80211_channel *);
263201209Srpaulostatic int	iwn_setregdomain(struct ieee80211com *,
264201209Srpaulo		    struct ieee80211_regdomain *, int,
265201209Srpaulo		    struct ieee80211_channel []);
266198429Srpaulostatic void	iwn_hw_reset(void *, int);
267198429Srpaulostatic void	iwn_radio_on(void *, int);
268198429Srpaulostatic void	iwn_radio_off(void *, int);
269178676Ssamstatic void	iwn_sysctlattach(struct iwn_softc *);
270198429Srpaulostatic int	iwn_shutdown(device_t);
271198429Srpaulostatic int	iwn_suspend(device_t);
272198429Srpaulostatic int	iwn_resume(device_t);
273178676Ssam
274178676Ssam#define IWN_DEBUG
275178676Ssam#ifdef IWN_DEBUG
276178676Ssamenum {
277178676Ssam	IWN_DEBUG_XMIT		= 0x00000001,	/* basic xmit operation */
278178676Ssam	IWN_DEBUG_RECV		= 0x00000002,	/* basic recv operation */
279178676Ssam	IWN_DEBUG_STATE		= 0x00000004,	/* 802.11 state transitions */
280178676Ssam	IWN_DEBUG_TXPOW		= 0x00000008,	/* tx power processing */
281178676Ssam	IWN_DEBUG_RESET		= 0x00000010,	/* reset processing */
282178676Ssam	IWN_DEBUG_OPS		= 0x00000020,	/* iwn_ops processing */
283178676Ssam	IWN_DEBUG_BEACON 	= 0x00000040,	/* beacon handling */
284178676Ssam	IWN_DEBUG_WATCHDOG 	= 0x00000080,	/* watchdog timeout */
285178676Ssam	IWN_DEBUG_INTR		= 0x00000100,	/* ISR */
286178676Ssam	IWN_DEBUG_CALIBRATE	= 0x00000200,	/* periodic calibration */
287178676Ssam	IWN_DEBUG_NODE		= 0x00000400,	/* node management */
288178676Ssam	IWN_DEBUG_LED		= 0x00000800,	/* led management */
289178676Ssam	IWN_DEBUG_CMD		= 0x00001000,	/* cmd submission */
290178676Ssam	IWN_DEBUG_FATAL		= 0x80000000,	/* fatal errors */
291178676Ssam	IWN_DEBUG_ANY		= 0xffffffff
292178676Ssam};
293178676Ssam
294178676Ssam#define DPRINTF(sc, m, fmt, ...) do {			\
295178676Ssam	if (sc->sc_debug & (m))				\
296178676Ssam		printf(fmt, __VA_ARGS__);		\
297178676Ssam} while (0)
298178676Ssam
299178676Ssamstatic const char *iwn_intr_str(uint8_t);
300178676Ssam#else
301178676Ssam#define DPRINTF(sc, m, fmt, ...) do { (void) sc; } while (0)
302178676Ssam#endif
303178676Ssam
304178676Ssamstruct iwn_ident {
305178676Ssam	uint16_t	vendor;
306178676Ssam	uint16_t	device;
307178676Ssam	const char	*name;
308178676Ssam};
309178676Ssam
310178676Ssamstatic const struct iwn_ident iwn_ident_table [] = {
311198429Srpaulo	{ 0x8086, 0x4229, "Intel(R) PRO/Wireless 4965BGN" },
312198429Srpaulo	{ 0x8086, 0x422D, "Intel(R) PRO/Wireless 4965BGN" },
313198429Srpaulo	{ 0x8086, 0x4230, "Intel(R) PRO/Wireless 4965BGN" },
314198429Srpaulo	{ 0x8086, 0x4233, "Intel(R) PRO/Wireless 4965BGN" },
315198429Srpaulo	{ 0x8086, 0x4232, "Intel(R) PRO/Wireless 5100" },
316198429Srpaulo	{ 0x8086, 0x4237, "Intel(R) PRO/Wireless 5100" },
317198429Srpaulo	{ 0x8086, 0x423C, "Intel(R) PRO/Wireless 5150" },
318198429Srpaulo	{ 0x8086, 0x423D, "Intel(R) PRO/Wireless 5150" },
319198429Srpaulo	{ 0x8086, 0x4235, "Intel(R) PRO/Wireless 5300" },
320198429Srpaulo	{ 0x8086, 0x4236, "Intel(R) PRO/Wireless 5300" },
321198429Srpaulo	{ 0x8086, 0x423A, "Intel(R) PRO/Wireless 5350" },
322198429Srpaulo	{ 0x8086, 0x423B, "Intel(R) PRO/Wireless 5350" },
323198429Srpaulo	{ 0x8086, 0x0083, "Intel(R) PRO/Wireless 1000" },
324198429Srpaulo	{ 0x8086, 0x0084, "Intel(R) PRO/Wireless 1000" },
325198429Srpaulo	{ 0x8086, 0x008D, "Intel(R) PRO/Wireless 6000" },
326198429Srpaulo	{ 0x8086, 0x008E, "Intel(R) PRO/Wireless 6000" },
327198429Srpaulo	{ 0x8086, 0x4238, "Intel(R) PRO/Wireless 6000" },
328198429Srpaulo	{ 0x8086, 0x4239, "Intel(R) PRO/Wireless 6000" },
329198429Srpaulo	{ 0x8086, 0x422B, "Intel(R) PRO/Wireless 6000" },
330198429Srpaulo	{ 0x8086, 0x422C, "Intel(R) PRO/Wireless 6000" },
331210109Sbschmidt	{ 0x8086, 0x0087, "Intel(R) PRO/Wireless 6250" },
332210109Sbschmidt	{ 0x8086, 0x0089, "Intel(R) PRO/Wireless 6250" },
333210109Sbschmidt	{ 0x8086, 0x0082, "Intel(R) PRO/Wireless 6205a" },
334210109Sbschmidt	{ 0x8086, 0x0085, "Intel(R) PRO/Wireless 6205a" },
335210109Sbschmidt#ifdef notyet
336210109Sbschmidt	{ 0x8086, 0x008a, "Intel(R) PRO/Wireless 6205b" },
337210109Sbschmidt	{ 0x8086, 0x008b, "Intel(R) PRO/Wireless 6205b" },
338210109Sbschmidt	{ 0x8086, 0x008f, "Intel(R) PRO/Wireless 6205b" },
339210109Sbschmidt	{ 0x8086, 0x0090, "Intel(R) PRO/Wireless 6205b" },
340210109Sbschmidt	{ 0x8086, 0x0091, "Intel(R) PRO/Wireless 6205b" },
341210109Sbschmidt#endif
342198429Srpaulo	{ 0, 0, NULL }
343178676Ssam};
344178676Ssam
345198429Srpaulostatic const struct iwn_hal iwn4965_hal = {
346198429Srpaulo	iwn4965_load_firmware,
347198429Srpaulo	iwn4965_read_eeprom,
348198429Srpaulo	iwn4965_post_alive,
349198429Srpaulo	iwn4965_nic_config,
350198429Srpaulo	iwn4965_update_sched,
351198429Srpaulo	iwn4965_get_temperature,
352198429Srpaulo	iwn4965_get_rssi,
353198429Srpaulo	iwn4965_set_txpower,
354198429Srpaulo	iwn4965_init_gains,
355198429Srpaulo	iwn4965_set_gains,
356198429Srpaulo	iwn4965_add_node,
357198429Srpaulo	iwn4965_tx_done,
358201209Srpaulo#if 0	/* HT */
359201209Srpaulo	iwn4965_ampdu_tx_start,
360201209Srpaulo	iwn4965_ampdu_tx_stop,
361201209Srpaulo#endif
362198429Srpaulo	IWN4965_NTXQUEUES,
363198429Srpaulo	IWN4965_NDMACHNLS,
364198429Srpaulo	IWN4965_ID_BROADCAST,
365198429Srpaulo	IWN4965_RXONSZ,
366198429Srpaulo	IWN4965_SCHEDSZ,
367198429Srpaulo	IWN4965_FW_TEXT_MAXSZ,
368198429Srpaulo	IWN4965_FW_DATA_MAXSZ,
369198429Srpaulo	IWN4965_FWSZ,
370201209Srpaulo	IWN4965_SCHED_TXFACT
371198429Srpaulo};
372198429Srpaulo
373198429Srpaulostatic const struct iwn_hal iwn5000_hal = {
374198429Srpaulo	iwn5000_load_firmware,
375198429Srpaulo	iwn5000_read_eeprom,
376198429Srpaulo	iwn5000_post_alive,
377198429Srpaulo	iwn5000_nic_config,
378198429Srpaulo	iwn5000_update_sched,
379198429Srpaulo	iwn5000_get_temperature,
380198429Srpaulo	iwn5000_get_rssi,
381198429Srpaulo	iwn5000_set_txpower,
382198429Srpaulo	iwn5000_init_gains,
383198429Srpaulo	iwn5000_set_gains,
384198429Srpaulo	iwn5000_add_node,
385198429Srpaulo	iwn5000_tx_done,
386201209Srpaulo#if 0	/* HT */
387201209Srpaulo	iwn5000_ampdu_tx_start,
388201209Srpaulo	iwn5000_ampdu_tx_stop,
389201209Srpaulo#endif
390198429Srpaulo	IWN5000_NTXQUEUES,
391198429Srpaulo	IWN5000_NDMACHNLS,
392198429Srpaulo	IWN5000_ID_BROADCAST,
393198429Srpaulo	IWN5000_RXONSZ,
394198429Srpaulo	IWN5000_SCHEDSZ,
395198429Srpaulo	IWN5000_FW_TEXT_MAXSZ,
396198429Srpaulo	IWN5000_FW_DATA_MAXSZ,
397198429Srpaulo	IWN5000_FWSZ,
398201209Srpaulo	IWN5000_SCHED_TXFACT
399198429Srpaulo};
400198429Srpaulo
401178676Ssamstatic int
402178676Ssamiwn_probe(device_t dev)
403178676Ssam{
404198429Srpaulo	const struct iwn_ident *ident;
405178676Ssam
406198429Srpaulo	for (ident = iwn_ident_table; ident->name != NULL; ident++) {
407198429Srpaulo		if (pci_get_vendor(dev) == ident->vendor &&
408198429Srpaulo		    pci_get_device(dev) == ident->device) {
409198429Srpaulo			device_set_desc(dev, ident->name);
410198429Srpaulo			return 0;
411198429Srpaulo		}
412198429Srpaulo	}
413198429Srpaulo	return ENXIO;
414178676Ssam}
415178676Ssam
416178676Ssamstatic int
417178676Ssamiwn_attach(device_t dev)
418178676Ssam{
419178676Ssam	struct iwn_softc *sc = (struct iwn_softc *)device_get_softc(dev);
420178676Ssam	struct ieee80211com *ic;
421178676Ssam	struct ifnet *ifp;
422198429Srpaulo	const struct iwn_hal *hal;
423198429Srpaulo	uint32_t tmp;
424184233Smav	int i, error, result;
425190526Ssam	uint8_t macaddr[IEEE80211_ADDR_LEN];
426178676Ssam
427178676Ssam	sc->sc_dev = dev;
428178676Ssam
429198429Srpaulo	/*
430198429Srpaulo	 * Get the offset of the PCI Express Capability Structure in PCI
431198429Srpaulo	 * Configuration Space.
432198429Srpaulo	 */
433219902Sjhb	error = pci_find_cap(dev, PCIY_EXPRESS, &sc->sc_cap_off);
434198429Srpaulo	if (error != 0) {
435198429Srpaulo		device_printf(dev, "PCIe capability structure not found!\n");
436198429Srpaulo		return error;
437178676Ssam	}
438178676Ssam
439198429Srpaulo	/* Clear device-specific "PCI retry timeout" register (41h). */
440178676Ssam	pci_write_config(dev, 0x41, 0, 1);
441178676Ssam
442198429Srpaulo	/* Hardware bug workaround. */
443198429Srpaulo	tmp = pci_read_config(dev, PCIR_COMMAND, 1);
444198429Srpaulo	if (tmp & PCIM_CMD_INTxDIS) {
445198429Srpaulo		DPRINTF(sc, IWN_DEBUG_RESET, "%s: PCIe INTx Disable set\n",
446198429Srpaulo		    __func__);
447198429Srpaulo		tmp &= ~PCIM_CMD_INTxDIS;
448198429Srpaulo		pci_write_config(dev, PCIR_COMMAND, tmp, 1);
449198429Srpaulo	}
450198429Srpaulo
451198429Srpaulo	/* Enable bus-mastering. */
452178676Ssam	pci_enable_busmaster(dev);
453178676Ssam
454198429Srpaulo	sc->mem_rid = PCIR_BAR(0);
455178676Ssam	sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid,
456198429Srpaulo	    RF_ACTIVE);
457178676Ssam	if (sc->mem == NULL ) {
458178676Ssam		device_printf(dev, "could not allocate memory resources\n");
459198429Srpaulo		error = ENOMEM;
460178676Ssam		return error;
461178676Ssam	}
462178676Ssam
463178676Ssam	sc->sc_st = rman_get_bustag(sc->mem);
464178676Ssam	sc->sc_sh = rman_get_bushandle(sc->mem);
465178676Ssam	sc->irq_rid = 0;
466184233Smav	if ((result = pci_msi_count(dev)) == 1 &&
467184233Smav	    pci_alloc_msi(dev, &result) == 0)
468184233Smav		sc->irq_rid = 1;
469178676Ssam	sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irq_rid,
470198429Srpaulo	    RF_ACTIVE | RF_SHAREABLE);
471178676Ssam	if (sc->irq == NULL) {
472178676Ssam		device_printf(dev, "could not allocate interrupt resource\n");
473178676Ssam		error = ENOMEM;
474198429Srpaulo		goto fail;
475178676Ssam	}
476178676Ssam
477178676Ssam	IWN_LOCK_INIT(sc);
478178676Ssam	callout_init_mtx(&sc->sc_timer_to, &sc->sc_mtx, 0);
479198429Srpaulo	TASK_INIT(&sc->sc_reinit_task, 0, iwn_hw_reset, sc );
480198429Srpaulo	TASK_INIT(&sc->sc_radioon_task, 0, iwn_radio_on, sc );
481198429Srpaulo	TASK_INIT(&sc->sc_radiooff_task, 0, iwn_radio_off, sc );
482178676Ssam
483198429Srpaulo	/* Attach Hardware Abstraction Layer. */
484198429Srpaulo	hal = iwn_hal_attach(sc);
485198429Srpaulo	if (hal == NULL) {
486198429Srpaulo		error = ENXIO;	/* XXX: Wrong error code? */
487198429Srpaulo		goto fail;
488198429Srpaulo	}
489198429Srpaulo
490198429Srpaulo	error = iwn_hw_prepare(sc);
491178676Ssam	if (error != 0) {
492198429Srpaulo		device_printf(dev, "hardware not ready, error %d\n", error);
493178676Ssam		goto fail;
494178676Ssam	}
495178676Ssam
496198429Srpaulo	/* Allocate DMA memory for firmware transfers. */
497178676Ssam	error = iwn_alloc_fwmem(sc);
498178676Ssam	if (error != 0) {
499178676Ssam		device_printf(dev,
500198429Srpaulo		    "could not allocate memory for firmware, error %d\n",
501198429Srpaulo		    error);
502178676Ssam		goto fail;
503178676Ssam	}
504178676Ssam
505198429Srpaulo	/* Allocate "Keep Warm" page. */
506178676Ssam	error = iwn_alloc_kw(sc);
507178676Ssam	if (error != 0) {
508178676Ssam		device_printf(dev,
509198429Srpaulo		    "could not allocate \"Keep Warm\" page, error %d\n", error);
510178676Ssam		goto fail;
511178676Ssam	}
512178676Ssam
513201209Srpaulo	/* Allocate ICT table for 5000 Series. */
514201209Srpaulo	if (sc->hw_type != IWN_HW_REV_TYPE_4965 &&
515201209Srpaulo	    (error = iwn_alloc_ict(sc)) != 0) {
516201209Srpaulo		device_printf(dev,
517201209Srpaulo		    "%s: could not allocate ICT table, error %d\n",
518201209Srpaulo		    __func__, error);
519201209Srpaulo		goto fail;
520201209Srpaulo	}
521201209Srpaulo
522198429Srpaulo	/* Allocate TX scheduler "rings". */
523198429Srpaulo	error = iwn_alloc_sched(sc);
524178676Ssam	if (error != 0) {
525178676Ssam		device_printf(dev,
526198429Srpaulo		    "could not allocate TX scheduler rings, error %d\n",
527198429Srpaulo		    error);
528178676Ssam		goto fail;
529178676Ssam	}
530178676Ssam
531198429Srpaulo	/* Allocate TX rings (16 on 4965AGN, 20 on 5000). */
532198429Srpaulo	for (i = 0; i < hal->ntxqs; i++) {
533178676Ssam		error = iwn_alloc_tx_ring(sc, &sc->txq[i], i);
534178676Ssam		if (error != 0) {
535178676Ssam			device_printf(dev,
536178676Ssam			    "could not allocate Tx ring %d, error %d\n",
537178676Ssam			    i, error);
538178676Ssam			goto fail;
539178676Ssam		}
540178676Ssam	}
541178676Ssam
542198429Srpaulo	/* Allocate RX ring. */
543178676Ssam	error = iwn_alloc_rx_ring(sc, &sc->rxq);
544178676Ssam	if (error != 0 ){
545178676Ssam		device_printf(dev,
546178676Ssam		    "could not allocate Rx ring, error %d\n", error);
547178676Ssam		goto fail;
548178676Ssam	}
549178676Ssam
550198429Srpaulo	/* Clear pending interrupts. */
551198429Srpaulo	IWN_WRITE(sc, IWN_INT, 0xffffffff);
552198429Srpaulo
553201209Srpaulo	/* Count the number of available chains. */
554201209Srpaulo	sc->ntxchains =
555201209Srpaulo	    ((sc->txchainmask >> 2) & 1) +
556201209Srpaulo	    ((sc->txchainmask >> 1) & 1) +
557201209Srpaulo	    ((sc->txchainmask >> 0) & 1);
558201209Srpaulo	sc->nrxchains =
559201209Srpaulo	    ((sc->rxchainmask >> 2) & 1) +
560201209Srpaulo	    ((sc->rxchainmask >> 1) & 1) +
561201209Srpaulo	    ((sc->rxchainmask >> 0) & 1);
562198429Srpaulo
563178676Ssam	ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211);
564178676Ssam	if (ifp == NULL) {
565178676Ssam		device_printf(dev, "can not allocate ifnet structure\n");
566178676Ssam		goto fail;
567178676Ssam	}
568178676Ssam	ic = ifp->if_l2com;
569178676Ssam
570198429Srpaulo	ic->ic_ifp = ifp;
571178676Ssam	ic->ic_phytype = IEEE80211_T_OFDM;	/* not only, but not used */
572178676Ssam	ic->ic_opmode = IEEE80211_M_STA;	/* default to BSS mode */
573178676Ssam
574198429Srpaulo	/* Set device capabilities. */
575178676Ssam	ic->ic_caps =
576178957Ssam		  IEEE80211_C_STA		/* station mode supported */
577178957Ssam		| IEEE80211_C_MONITOR		/* monitor mode supported */
578178676Ssam		| IEEE80211_C_TXPMGT		/* tx power management */
579178676Ssam		| IEEE80211_C_SHSLOT		/* short slot time supported */
580178676Ssam		| IEEE80211_C_WPA
581178676Ssam		| IEEE80211_C_SHPREAMBLE	/* short preamble supported */
582201209Srpaulo		| IEEE80211_C_BGSCAN		/* background scanning */
583178676Ssam#if 0
584178676Ssam		| IEEE80211_C_IBSS		/* ibss/adhoc mode */
585178676Ssam#endif
586178676Ssam		| IEEE80211_C_WME		/* WME */
587178676Ssam		;
588201209Srpaulo#if 0	/* HT */
589178678Ssam	/* XXX disable until HT channel setup works */
590178676Ssam	ic->ic_htcaps =
591178676Ssam		  IEEE80211_HTCAP_SMPS_ENA	/* SM PS mode enabled */
592178676Ssam		| IEEE80211_HTCAP_CHWIDTH40	/* 40MHz channel width */
593178676Ssam		| IEEE80211_HTCAP_SHORTGI20	/* short GI in 20MHz */
594178676Ssam		| IEEE80211_HTCAP_SHORTGI40	/* short GI in 40MHz */
595178676Ssam		| IEEE80211_HTCAP_RXSTBC_2STREAM/* 1-2 spatial streams */
596178676Ssam		| IEEE80211_HTCAP_MAXAMSDU_3839	/* max A-MSDU length */
597178676Ssam		/* s/w capabilities */
598178676Ssam		| IEEE80211_HTC_HT		/* HT operation */
599178676Ssam		| IEEE80211_HTC_AMPDU		/* tx A-MPDU */
600178676Ssam		| IEEE80211_HTC_AMSDU		/* tx A-MSDU */
601178676Ssam		;
602201209Srpaulo
603201209Srpaulo	/* Set HT capabilities. */
604201209Srpaulo	ic->ic_htcaps =
605201209Srpaulo#if IWN_RBUF_SIZE == 8192
606201209Srpaulo	    IEEE80211_HTCAP_AMSDU7935 |
607178678Ssam#endif
608201209Srpaulo	    IEEE80211_HTCAP_CBW20_40 |
609201209Srpaulo	    IEEE80211_HTCAP_SGI20 |
610201209Srpaulo	    IEEE80211_HTCAP_SGI40;
611201209Srpaulo	if (sc->hw_type != IWN_HW_REV_TYPE_4965)
612201209Srpaulo		ic->ic_htcaps |= IEEE80211_HTCAP_GF;
613206444Sbschmidt	if (sc->hw_type == IWN_HW_REV_TYPE_6050)
614206444Sbschmidt		ic->ic_htcaps |= IEEE80211_HTCAP_SMPS_DYN;
615206444Sbschmidt	else
616206444Sbschmidt		ic->ic_htcaps |= IEEE80211_HTCAP_SMPS_DIS;
617201209Srpaulo#endif
618178676Ssam
619198429Srpaulo	/* Read MAC address, channels, etc from EEPROM. */
620198429Srpaulo	error = iwn_read_eeprom(sc, macaddr);
621198429Srpaulo	if (error != 0) {
622198429Srpaulo		device_printf(dev, "could not read EEPROM, error %d\n",
623198429Srpaulo		    error);
624198429Srpaulo		goto fail;
625198429Srpaulo	}
626198429Srpaulo
627198429Srpaulo	device_printf(sc->sc_dev, "MIMO %dT%dR, %.4s, address %6D\n",
628198429Srpaulo	    sc->ntxchains, sc->nrxchains, sc->eeprom_domain,
629198429Srpaulo	    macaddr, ":");
630198429Srpaulo
631201209Srpaulo#if 0	/* HT */
632201209Srpaulo	/* Set supported HT rates. */
633201209Srpaulo	ic->ic_sup_mcs[0] = 0xff;
634201209Srpaulo	if (sc->nrxchains > 1)
635201209Srpaulo		ic->ic_sup_mcs[1] = 0xff;
636201209Srpaulo	if (sc->nrxchains > 2)
637201209Srpaulo		ic->ic_sup_mcs[2] = 0xff;
638201209Srpaulo#endif
639201209Srpaulo
640178676Ssam	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
641178676Ssam	ifp->if_softc = sc;
642178676Ssam	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
643178676Ssam	ifp->if_init = iwn_init;
644178676Ssam	ifp->if_ioctl = iwn_ioctl;
645178676Ssam	ifp->if_start = iwn_start;
646207554Ssobomax	IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen);
647207554Ssobomax	ifp->if_snd.ifq_drv_maxlen = ifqmaxlen;
648178676Ssam	IFQ_SET_READY(&ifp->if_snd);
649178676Ssam
650190526Ssam	ieee80211_ifattach(ic, macaddr);
651178676Ssam	ic->ic_vap_create = iwn_vap_create;
652178676Ssam	ic->ic_vap_delete = iwn_vap_delete;
653178676Ssam	ic->ic_raw_xmit = iwn_raw_xmit;
654178676Ssam	ic->ic_node_alloc = iwn_node_alloc;
655198429Srpaulo	ic->ic_wme.wme_update = iwn_wme_update;
656201209Srpaulo	ic->ic_update_mcast = iwn_update_mcast;
657198429Srpaulo	ic->ic_scan_start = iwn_scan_start;
658198429Srpaulo	ic->ic_scan_end = iwn_scan_end;
659198429Srpaulo	ic->ic_set_channel = iwn_set_channel;
660198429Srpaulo	ic->ic_scan_curchan = iwn_scan_curchan;
661198429Srpaulo	ic->ic_scan_mindwell = iwn_scan_mindwell;
662201209Srpaulo	ic->ic_setregdomain = iwn_setregdomain;
663201209Srpaulo#if 0	/* HT */
664201209Srpaulo	ic->ic_ampdu_rx_start = iwn_ampdu_rx_start;
665201209Srpaulo	ic->ic_ampdu_rx_stop = iwn_ampdu_rx_stop;
666201209Srpaulo	ic->ic_ampdu_tx_start = iwn_ampdu_tx_start;
667201209Srpaulo	ic->ic_ampdu_tx_stop = iwn_ampdu_tx_stop;
668201209Srpaulo#endif
669178676Ssam
670198429Srpaulo	iwn_radiotap_attach(sc);
671178676Ssam	iwn_sysctlattach(sc);
672178676Ssam
673198429Srpaulo	/*
674198429Srpaulo	 * Hook our interrupt after all initialization is complete.
675198429Srpaulo	 */
676198429Srpaulo	error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET | INTR_MPSAFE,
677178676Ssam	    NULL, iwn_intr, sc, &sc->sc_ih);
678198429Srpaulo	if (error != 0) {
679198429Srpaulo		device_printf(dev, "could not set up interrupt, error %d\n",
680198429Srpaulo		    error);
681198429Srpaulo		goto fail;
682198429Srpaulo	}
683178676Ssam
684198429Srpaulo	ieee80211_announce(ic);
685178676Ssam	return 0;
686178676Ssamfail:
687220635Sbschmidt	iwn_detach(dev);
688178676Ssam	return error;
689178676Ssam}
690178676Ssam
691206477Sbschmidtstatic const struct iwn_hal *
692198429Srpauloiwn_hal_attach(struct iwn_softc *sc)
693178676Ssam{
694198429Srpaulo	sc->hw_type = (IWN_READ(sc, IWN_HW_REV) >> 4) & 0xf;
695198429Srpaulo
696198429Srpaulo	switch (sc->hw_type) {
697198429Srpaulo	case IWN_HW_REV_TYPE_4965:
698198429Srpaulo		sc->sc_hal = &iwn4965_hal;
699201209Srpaulo		sc->limits = &iwn4965_sensitivity_limits;
700198439Srpaulo		sc->fwname = "iwn4965fw";
701201209Srpaulo		sc->txchainmask = IWN_ANT_AB;
702201209Srpaulo		sc->rxchainmask = IWN_ANT_ABC;
703198429Srpaulo		break;
704198429Srpaulo	case IWN_HW_REV_TYPE_5100:
705198429Srpaulo		sc->sc_hal = &iwn5000_hal;
706201209Srpaulo		sc->limits = &iwn5000_sensitivity_limits;
707198439Srpaulo		sc->fwname = "iwn5000fw";
708201209Srpaulo		sc->txchainmask = IWN_ANT_B;
709201209Srpaulo		sc->rxchainmask = IWN_ANT_AB;
710212854Sbschmidt		sc->calib_init = IWN_CALIB_XTAL | IWN_CALIB_LO |
711212854Sbschmidt		    IWN_CALIB_TX_IQ | IWN_CALIB_TX_IQ_PERIODIC |
712212854Sbschmidt		    IWN_CALIB_BASE_BAND;
713198429Srpaulo		break;
714198429Srpaulo	case IWN_HW_REV_TYPE_5150:
715198429Srpaulo		sc->sc_hal = &iwn5000_hal;
716201209Srpaulo		sc->limits = &iwn5150_sensitivity_limits;
717198439Srpaulo		sc->fwname = "iwn5150fw";
718201209Srpaulo		sc->txchainmask = IWN_ANT_A;
719201209Srpaulo		sc->rxchainmask = IWN_ANT_AB;
720212854Sbschmidt		sc->calib_init = IWN_CALIB_DC | IWN_CALIB_LO |
721212854Sbschmidt		    IWN_CALIB_TX_IQ | IWN_CALIB_BASE_BAND;
722198429Srpaulo		break;
723198429Srpaulo	case IWN_HW_REV_TYPE_5300:
724198429Srpaulo	case IWN_HW_REV_TYPE_5350:
725198429Srpaulo		sc->sc_hal = &iwn5000_hal;
726201209Srpaulo		sc->limits = &iwn5000_sensitivity_limits;
727198439Srpaulo		sc->fwname = "iwn5000fw";
728201209Srpaulo		sc->txchainmask = IWN_ANT_ABC;
729201209Srpaulo		sc->rxchainmask = IWN_ANT_ABC;
730212854Sbschmidt		sc->calib_init = IWN_CALIB_XTAL | IWN_CALIB_LO |
731212854Sbschmidt		    IWN_CALIB_TX_IQ | IWN_CALIB_TX_IQ_PERIODIC |
732212854Sbschmidt		    IWN_CALIB_BASE_BAND;
733198429Srpaulo		break;
734198429Srpaulo	case IWN_HW_REV_TYPE_1000:
735198429Srpaulo		sc->sc_hal = &iwn5000_hal;
736206444Sbschmidt		sc->limits = &iwn1000_sensitivity_limits;
737198439Srpaulo		sc->fwname = "iwn1000fw";
738201209Srpaulo		sc->txchainmask = IWN_ANT_A;
739201209Srpaulo		sc->rxchainmask = IWN_ANT_AB;
740212854Sbschmidt		sc->calib_init = IWN_CALIB_XTAL | IWN_CALIB_LO |
741212854Sbschmidt		    IWN_CALIB_TX_IQ | IWN_CALIB_TX_IQ_PERIODIC |
742212854Sbschmidt		    IWN_CALIB_BASE_BAND;
743198429Srpaulo		break;
744198429Srpaulo	case IWN_HW_REV_TYPE_6000:
745198429Srpaulo		sc->sc_hal = &iwn5000_hal;
746201209Srpaulo		sc->limits = &iwn6000_sensitivity_limits;
747198439Srpaulo		sc->fwname = "iwn6000fw";
748201209Srpaulo		switch (pci_get_device(sc->sc_dev)) {
749201209Srpaulo		case 0x422C:
750201209Srpaulo		case 0x4239:
751201209Srpaulo			sc->sc_flags |= IWN_FLAG_INTERNAL_PA;
752201209Srpaulo			sc->txchainmask = IWN_ANT_BC;
753201209Srpaulo			sc->rxchainmask = IWN_ANT_BC;
754201209Srpaulo			break;
755201209Srpaulo		default:
756201209Srpaulo			sc->txchainmask = IWN_ANT_ABC;
757201209Srpaulo			sc->rxchainmask = IWN_ANT_ABC;
758216195Sbschmidt			sc->calib_runtime = IWN_CALIB_DC;
759201209Srpaulo			break;
760201209Srpaulo		}
761212854Sbschmidt		sc->calib_init = IWN_CALIB_XTAL | IWN_CALIB_LO |
762212854Sbschmidt		    IWN_CALIB_TX_IQ | IWN_CALIB_BASE_BAND;
763198429Srpaulo		break;
764198429Srpaulo	case IWN_HW_REV_TYPE_6050:
765198429Srpaulo		sc->sc_hal = &iwn5000_hal;
766201209Srpaulo		sc->limits = &iwn6000_sensitivity_limits;
767210109Sbschmidt		sc->fwname = "iwn6050fw";
768201209Srpaulo		sc->txchainmask = IWN_ANT_AB;
769201209Srpaulo		sc->rxchainmask = IWN_ANT_AB;
770216195Sbschmidt		sc->calib_init = IWN_CALIB_XTAL | IWN_CALIB_LO |
771212854Sbschmidt		    IWN_CALIB_TX_IQ | IWN_CALIB_BASE_BAND;
772216195Sbschmidt		sc->calib_runtime = IWN_CALIB_DC;
773198429Srpaulo		break;
774210109Sbschmidt	case IWN_HW_REV_TYPE_6005:
775210109Sbschmidt		sc->sc_hal = &iwn5000_hal;
776210109Sbschmidt		sc->limits = &iwn6000_sensitivity_limits;
777210109Sbschmidt		sc->fwname = "iwn6005fw";
778210109Sbschmidt		sc->txchainmask = IWN_ANT_AB;
779210109Sbschmidt		sc->rxchainmask = IWN_ANT_AB;
780212854Sbschmidt		sc->calib_init = IWN_CALIB_XTAL | IWN_CALIB_LO |
781212854Sbschmidt		    IWN_CALIB_TX_IQ | IWN_CALIB_BASE_BAND;
782216195Sbschmidt		sc->calib_runtime = IWN_CALIB_DC;
783210109Sbschmidt		break;
784198429Srpaulo	default:
785198429Srpaulo		device_printf(sc->sc_dev, "adapter type %d not supported\n",
786198429Srpaulo		    sc->hw_type);
787198429Srpaulo		return NULL;
788198429Srpaulo	}
789198429Srpaulo	return sc->sc_hal;
790178676Ssam}
791178676Ssam
792178676Ssam/*
793198429Srpaulo * Attach the interface to 802.11 radiotap.
794178676Ssam */
795206477Sbschmidtstatic void
796198429Srpauloiwn_radiotap_attach(struct iwn_softc *sc)
797178676Ssam{
798178676Ssam	struct ifnet *ifp = sc->sc_ifp;
799178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
800178676Ssam
801198429Srpaulo	ieee80211_radiotap_attach(ic,
802198429Srpaulo	    &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap),
803198429Srpaulo		IWN_TX_RADIOTAP_PRESENT,
804198429Srpaulo	    &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap),
805198429Srpaulo		IWN_RX_RADIOTAP_PRESENT);
806178676Ssam}
807178676Ssam
808178676Ssamstatic struct ieee80211vap *
809178676Ssamiwn_vap_create(struct ieee80211com *ic,
810178676Ssam	const char name[IFNAMSIZ], int unit, int opmode, int flags,
811178676Ssam	const uint8_t bssid[IEEE80211_ADDR_LEN],
812178676Ssam	const uint8_t mac[IEEE80211_ADDR_LEN])
813178676Ssam{
814178676Ssam	struct iwn_vap *ivp;
815178676Ssam	struct ieee80211vap *vap;
816178676Ssam
817178676Ssam	if (!TAILQ_EMPTY(&ic->ic_vaps))		/* only one at a time */
818178676Ssam		return NULL;
819178676Ssam	ivp = (struct iwn_vap *) malloc(sizeof(struct iwn_vap),
820178676Ssam	    M_80211_VAP, M_NOWAIT | M_ZERO);
821178676Ssam	if (ivp == NULL)
822178676Ssam		return NULL;
823178676Ssam	vap = &ivp->iv_vap;
824178676Ssam	ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac);
825178676Ssam	vap->iv_bmissthreshold = 10;		/* override default */
826198429Srpaulo	/* Override with driver methods. */
827178676Ssam	ivp->iv_newstate = vap->iv_newstate;
828178676Ssam	vap->iv_newstate = iwn_newstate;
829178676Ssam
830206358Srpaulo	ieee80211_ratectl_init(vap);
831198429Srpaulo	/* Complete setup. */
832206476Sbschmidt	ieee80211_vap_attach(vap, iwn_media_change, ieee80211_media_status);
833178676Ssam	ic->ic_opmode = opmode;
834178676Ssam	return vap;
835178676Ssam}
836178676Ssam
837178676Ssamstatic void
838178676Ssamiwn_vap_delete(struct ieee80211vap *vap)
839178676Ssam{
840178676Ssam	struct iwn_vap *ivp = IWN_VAP(vap);
841178676Ssam
842206358Srpaulo	ieee80211_ratectl_deinit(vap);
843178676Ssam	ieee80211_vap_detach(vap);
844178676Ssam	free(ivp, M_80211_VAP);
845178676Ssam}
846178676Ssam
847206477Sbschmidtstatic int
848220635Sbschmidtiwn_detach(device_t dev)
849178676Ssam{
850178676Ssam	struct iwn_softc *sc = device_get_softc(dev);
851198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
852198429Srpaulo	struct ieee80211com *ic;
853198429Srpaulo	int i;
854178676Ssam
855198429Srpaulo	if (ifp != NULL) {
856198429Srpaulo		ic = ifp->if_l2com;
857198429Srpaulo
858198429Srpaulo		ieee80211_draintask(ic, &sc->sc_reinit_task);
859198429Srpaulo		ieee80211_draintask(ic, &sc->sc_radioon_task);
860198429Srpaulo		ieee80211_draintask(ic, &sc->sc_radiooff_task);
861198429Srpaulo
862198429Srpaulo		iwn_stop(sc);
863198429Srpaulo		callout_drain(&sc->sc_timer_to);
864198429Srpaulo		ieee80211_ifdetach(ic);
865198429Srpaulo	}
866198429Srpaulo
867212854Sbschmidt	iwn5000_free_calib_results(sc);
868212854Sbschmidt
869201209Srpaulo	/* Free DMA resources. */
870198429Srpaulo	iwn_free_rx_ring(sc, &sc->rxq);
871198429Srpaulo	if (sc->sc_hal != NULL)
872198429Srpaulo		for (i = 0; i < sc->sc_hal->ntxqs; i++)
873198429Srpaulo			iwn_free_tx_ring(sc, &sc->txq[i]);
874198429Srpaulo	iwn_free_sched(sc);
875198429Srpaulo	iwn_free_kw(sc);
876201209Srpaulo	if (sc->ict != NULL)
877201209Srpaulo		iwn_free_ict(sc);
878198429Srpaulo	iwn_free_fwmem(sc);
879198429Srpaulo
880198429Srpaulo	if (sc->irq != NULL) {
881198429Srpaulo		bus_teardown_intr(dev, sc->irq, sc->sc_ih);
882198429Srpaulo		bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq);
883198429Srpaulo		if (sc->irq_rid == 1)
884198429Srpaulo			pci_release_msi(dev);
885198429Srpaulo	}
886198429Srpaulo
887198429Srpaulo	if (sc->mem != NULL)
888198429Srpaulo		bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem);
889198429Srpaulo
890198429Srpaulo	if (ifp != NULL)
891198429Srpaulo		if_free(ifp);
892198429Srpaulo
893198429Srpaulo	IWN_LOCK_DESTROY(sc);
894178676Ssam	return 0;
895178676Ssam}
896178676Ssam
897178676Ssamstatic int
898198429Srpauloiwn_nic_lock(struct iwn_softc *sc)
899178676Ssam{
900198429Srpaulo	int ntries;
901178676Ssam
902198429Srpaulo	/* Request exclusive access to NIC. */
903198429Srpaulo	IWN_SETBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_MAC_ACCESS_REQ);
904178676Ssam
905198429Srpaulo	/* Spin until we actually get the lock. */
906198429Srpaulo	for (ntries = 0; ntries < 1000; ntries++) {
907198429Srpaulo		if ((IWN_READ(sc, IWN_GP_CNTRL) &
908206443Sbschmidt		    (IWN_GP_CNTRL_MAC_ACCESS_ENA | IWN_GP_CNTRL_SLEEP)) ==
909198429Srpaulo		    IWN_GP_CNTRL_MAC_ACCESS_ENA)
910198429Srpaulo			return 0;
911198429Srpaulo		DELAY(10);
912198429Srpaulo	}
913198429Srpaulo	return ETIMEDOUT;
914198429Srpaulo}
915198429Srpaulo
916198429Srpaulostatic __inline void
917198429Srpauloiwn_nic_unlock(struct iwn_softc *sc)
918198429Srpaulo{
919198429Srpaulo	IWN_CLRBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_MAC_ACCESS_REQ);
920198429Srpaulo}
921198429Srpaulo
922198429Srpaulostatic __inline uint32_t
923198429Srpauloiwn_prph_read(struct iwn_softc *sc, uint32_t addr)
924198429Srpaulo{
925198429Srpaulo	IWN_WRITE(sc, IWN_PRPH_RADDR, IWN_PRPH_DWORD | addr);
926201209Srpaulo	IWN_BARRIER_READ_WRITE(sc);
927198429Srpaulo	return IWN_READ(sc, IWN_PRPH_RDATA);
928198429Srpaulo}
929198429Srpaulo
930198429Srpaulostatic __inline void
931198429Srpauloiwn_prph_write(struct iwn_softc *sc, uint32_t addr, uint32_t data)
932198429Srpaulo{
933198429Srpaulo	IWN_WRITE(sc, IWN_PRPH_WADDR, IWN_PRPH_DWORD | addr);
934201209Srpaulo	IWN_BARRIER_WRITE(sc);
935198429Srpaulo	IWN_WRITE(sc, IWN_PRPH_WDATA, data);
936198429Srpaulo}
937198429Srpaulo
938198429Srpaulostatic __inline void
939198429Srpauloiwn_prph_setbits(struct iwn_softc *sc, uint32_t addr, uint32_t mask)
940198429Srpaulo{
941198429Srpaulo	iwn_prph_write(sc, addr, iwn_prph_read(sc, addr) | mask);
942198429Srpaulo}
943198429Srpaulo
944198429Srpaulostatic __inline void
945198429Srpauloiwn_prph_clrbits(struct iwn_softc *sc, uint32_t addr, uint32_t mask)
946198429Srpaulo{
947198429Srpaulo	iwn_prph_write(sc, addr, iwn_prph_read(sc, addr) & ~mask);
948198429Srpaulo}
949198429Srpaulo
950198429Srpaulostatic __inline void
951198429Srpauloiwn_prph_write_region_4(struct iwn_softc *sc, uint32_t addr,
952198429Srpaulo    const uint32_t *data, int count)
953198429Srpaulo{
954198429Srpaulo	for (; count > 0; count--, data++, addr += 4)
955198429Srpaulo		iwn_prph_write(sc, addr, *data);
956198429Srpaulo}
957198429Srpaulo
958198429Srpaulostatic __inline uint32_t
959198429Srpauloiwn_mem_read(struct iwn_softc *sc, uint32_t addr)
960198429Srpaulo{
961198429Srpaulo	IWN_WRITE(sc, IWN_MEM_RADDR, addr);
962201209Srpaulo	IWN_BARRIER_READ_WRITE(sc);
963198429Srpaulo	return IWN_READ(sc, IWN_MEM_RDATA);
964198429Srpaulo}
965198429Srpaulo
966198429Srpaulostatic __inline void
967198429Srpauloiwn_mem_write(struct iwn_softc *sc, uint32_t addr, uint32_t data)
968198429Srpaulo{
969198429Srpaulo	IWN_WRITE(sc, IWN_MEM_WADDR, addr);
970201209Srpaulo	IWN_BARRIER_WRITE(sc);
971198429Srpaulo	IWN_WRITE(sc, IWN_MEM_WDATA, data);
972198429Srpaulo}
973198429Srpaulo
974198429Srpaulostatic __inline void
975198429Srpauloiwn_mem_write_2(struct iwn_softc *sc, uint32_t addr, uint16_t data)
976198429Srpaulo{
977198429Srpaulo	uint32_t tmp;
978198429Srpaulo
979198429Srpaulo	tmp = iwn_mem_read(sc, addr & ~3);
980198429Srpaulo	if (addr & 3)
981198429Srpaulo		tmp = (tmp & 0x0000ffff) | data << 16;
982198429Srpaulo	else
983198429Srpaulo		tmp = (tmp & 0xffff0000) | data;
984198429Srpaulo	iwn_mem_write(sc, addr & ~3, tmp);
985198429Srpaulo}
986198429Srpaulo
987198429Srpaulostatic __inline void
988198429Srpauloiwn_mem_read_region_4(struct iwn_softc *sc, uint32_t addr, uint32_t *data,
989198429Srpaulo    int count)
990198429Srpaulo{
991198429Srpaulo	for (; count > 0; count--, addr += 4)
992198429Srpaulo		*data++ = iwn_mem_read(sc, addr);
993198429Srpaulo}
994198429Srpaulo
995198429Srpaulostatic __inline void
996198429Srpauloiwn_mem_set_region_4(struct iwn_softc *sc, uint32_t addr, uint32_t val,
997198429Srpaulo    int count)
998198429Srpaulo{
999198429Srpaulo	for (; count > 0; count--, addr += 4)
1000198429Srpaulo		iwn_mem_write(sc, addr, val);
1001198429Srpaulo}
1002198429Srpaulo
1003206477Sbschmidtstatic int
1004198429Srpauloiwn_eeprom_lock(struct iwn_softc *sc)
1005198429Srpaulo{
1006198429Srpaulo	int i, ntries;
1007198429Srpaulo
1008198429Srpaulo	for (i = 0; i < 100; i++) {
1009198429Srpaulo		/* Request exclusive access to EEPROM. */
1010198429Srpaulo		IWN_SETBITS(sc, IWN_HW_IF_CONFIG,
1011198429Srpaulo		    IWN_HW_IF_CONFIG_EEPROM_LOCKED);
1012198429Srpaulo
1013198429Srpaulo		/* Spin until we actually get the lock. */
1014198429Srpaulo		for (ntries = 0; ntries < 100; ntries++) {
1015198429Srpaulo			if (IWN_READ(sc, IWN_HW_IF_CONFIG) &
1016198429Srpaulo			    IWN_HW_IF_CONFIG_EEPROM_LOCKED)
1017198429Srpaulo				return 0;
1018198429Srpaulo			DELAY(10);
1019198429Srpaulo		}
1020198429Srpaulo	}
1021198429Srpaulo	return ETIMEDOUT;
1022198429Srpaulo}
1023198429Srpaulo
1024198429Srpaulostatic __inline void
1025198429Srpauloiwn_eeprom_unlock(struct iwn_softc *sc)
1026198429Srpaulo{
1027198429Srpaulo	IWN_CLRBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_EEPROM_LOCKED);
1028198429Srpaulo}
1029198429Srpaulo
1030198429Srpaulo/*
1031198429Srpaulo * Initialize access by host to One Time Programmable ROM.
1032198429Srpaulo * NB: This kind of ROM can be found on 1000 or 6000 Series only.
1033198429Srpaulo */
1034206477Sbschmidtstatic int
1035198429Srpauloiwn_init_otprom(struct iwn_softc *sc)
1036198429Srpaulo{
1037203934Sbschmidt	uint16_t prev, base, next;
1038201209Srpaulo	int count, error;
1039198429Srpaulo
1040201209Srpaulo	/* Wait for clock stabilization before accessing prph. */
1041198429Srpaulo	error = iwn_clock_wait(sc);
1042198429Srpaulo	if (error != 0)
1043198429Srpaulo		return error;
1044198429Srpaulo
1045198429Srpaulo	error = iwn_nic_lock(sc);
1046198429Srpaulo	if (error != 0)
1047198429Srpaulo		return error;
1048198429Srpaulo	iwn_prph_setbits(sc, IWN_APMG_PS, IWN_APMG_PS_RESET_REQ);
1049198429Srpaulo	DELAY(5);
1050198429Srpaulo	iwn_prph_clrbits(sc, IWN_APMG_PS, IWN_APMG_PS_RESET_REQ);
1051198429Srpaulo	iwn_nic_unlock(sc);
1052198429Srpaulo
1053201209Srpaulo	/* Set auto clock gate disable bit for HW with OTP shadow RAM. */
1054201209Srpaulo	if (sc->hw_type != IWN_HW_REV_TYPE_1000) {
1055201209Srpaulo		IWN_SETBITS(sc, IWN_DBG_LINK_PWR_MGMT,
1056201209Srpaulo		    IWN_RESET_LINK_PWR_MGMT_DIS);
1057201209Srpaulo	}
1058198429Srpaulo	IWN_CLRBITS(sc, IWN_EEPROM_GP, IWN_EEPROM_GP_IF_OWNER);
1059198429Srpaulo	/* Clear ECC status. */
1060198429Srpaulo	IWN_SETBITS(sc, IWN_OTP_GP,
1061198429Srpaulo	    IWN_OTP_GP_ECC_CORR_STTS | IWN_OTP_GP_ECC_UNCORR_STTS);
1062198429Srpaulo
1063201209Srpaulo	/*
1064203934Sbschmidt	 * Find the block before last block (contains the EEPROM image)
1065203934Sbschmidt	 * for HW without OTP shadow RAM.
1066201209Srpaulo	 */
1067201209Srpaulo	if (sc->hw_type == IWN_HW_REV_TYPE_1000) {
1068201209Srpaulo		/* Switch to absolute addressing mode. */
1069201209Srpaulo		IWN_CLRBITS(sc, IWN_OTP_GP, IWN_OTP_GP_RELATIVE_ACCESS);
1070203934Sbschmidt		base = prev = 0;
1071201209Srpaulo		for (count = 0; count < IWN1000_OTP_NBLOCKS; count++) {
1072201209Srpaulo			error = iwn_read_prom_data(sc, base, &next, 2);
1073201209Srpaulo			if (error != 0)
1074201209Srpaulo				return error;
1075201209Srpaulo			if (next == 0)	/* End of linked-list. */
1076201209Srpaulo				break;
1077203934Sbschmidt			prev = base;
1078201209Srpaulo			base = le16toh(next);
1079201209Srpaulo		}
1080203934Sbschmidt		if (count == 0 || count == IWN1000_OTP_NBLOCKS)
1081201209Srpaulo			return EIO;
1082201209Srpaulo		/* Skip "next" word. */
1083203934Sbschmidt		sc->prom_base = prev + 1;
1084201209Srpaulo	}
1085178676Ssam	return 0;
1086178676Ssam}
1087178676Ssam
1088206477Sbschmidtstatic int
1089198429Srpauloiwn_read_prom_data(struct iwn_softc *sc, uint32_t addr, void *data, int count)
1090198429Srpaulo{
1091198429Srpaulo	uint32_t val, tmp;
1092198429Srpaulo	int ntries;
1093198429Srpaulo	uint8_t *out = data;
1094198429Srpaulo
1095201209Srpaulo	addr += sc->prom_base;
1096198429Srpaulo	for (; count > 0; count -= 2, addr++) {
1097198429Srpaulo		IWN_WRITE(sc, IWN_EEPROM, addr << 2);
1098201209Srpaulo		for (ntries = 0; ntries < 10; ntries++) {
1099198429Srpaulo			val = IWN_READ(sc, IWN_EEPROM);
1100198429Srpaulo			if (val & IWN_EEPROM_READ_VALID)
1101198429Srpaulo				break;
1102198429Srpaulo			DELAY(5);
1103198429Srpaulo		}
1104201209Srpaulo		if (ntries == 10) {
1105198429Srpaulo			device_printf(sc->sc_dev,
1106198429Srpaulo			    "timeout reading ROM at 0x%x\n", addr);
1107198429Srpaulo			return ETIMEDOUT;
1108198429Srpaulo		}
1109198429Srpaulo		if (sc->sc_flags & IWN_FLAG_HAS_OTPROM) {
1110198429Srpaulo			/* OTPROM, check for ECC errors. */
1111198429Srpaulo			tmp = IWN_READ(sc, IWN_OTP_GP);
1112198429Srpaulo			if (tmp & IWN_OTP_GP_ECC_UNCORR_STTS) {
1113198429Srpaulo				device_printf(sc->sc_dev,
1114198429Srpaulo				    "OTPROM ECC error at 0x%x\n", addr);
1115198429Srpaulo				return EIO;
1116198429Srpaulo			}
1117198429Srpaulo			if (tmp & IWN_OTP_GP_ECC_CORR_STTS) {
1118198429Srpaulo				/* Correctable ECC error, clear bit. */
1119198429Srpaulo				IWN_SETBITS(sc, IWN_OTP_GP,
1120198429Srpaulo				    IWN_OTP_GP_ECC_CORR_STTS);
1121198429Srpaulo			}
1122198429Srpaulo		}
1123198429Srpaulo		*out++ = val >> 16;
1124198429Srpaulo		if (count > 1)
1125198429Srpaulo			*out++ = val >> 24;
1126198429Srpaulo	}
1127198429Srpaulo	return 0;
1128198429Srpaulo}
1129198429Srpaulo
1130178676Ssamstatic void
1131178676Ssamiwn_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
1132178676Ssam{
1133198429Srpaulo	if (error != 0)
1134198429Srpaulo		return;
1135198429Srpaulo	KASSERT(nsegs == 1, ("too many DMA segments, %d should be 1", nsegs));
1136198429Srpaulo	*(bus_addr_t *)arg = segs[0].ds_addr;
1137178676Ssam}
1138178676Ssam
1139198429Srpaulostatic int
1140178676Ssamiwn_dma_contig_alloc(struct iwn_softc *sc, struct iwn_dma_info *dma,
1141178676Ssam	void **kvap, bus_size_t size, bus_size_t alignment, int flags)
1142178676Ssam{
1143198429Srpaulo	int error;
1144178676Ssam
1145178676Ssam	dma->size = size;
1146178676Ssam	dma->tag = NULL;
1147178676Ssam
1148198429Srpaulo	error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), alignment,
1149178676Ssam	    0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, size,
1150178676Ssam	    1, size, flags, NULL, NULL, &dma->tag);
1151178676Ssam	if (error != 0) {
1152178676Ssam		device_printf(sc->sc_dev,
1153178676Ssam		    "%s: bus_dma_tag_create failed, error %d\n",
1154178676Ssam		    __func__, error);
1155178676Ssam		goto fail;
1156178676Ssam	}
1157178676Ssam	error = bus_dmamem_alloc(dma->tag, (void **)&dma->vaddr,
1158178676Ssam	    flags | BUS_DMA_ZERO, &dma->map);
1159178676Ssam	if (error != 0) {
1160178676Ssam		device_printf(sc->sc_dev,
1161206443Sbschmidt		    "%s: bus_dmamem_alloc failed, error %d\n", __func__, error);
1162178676Ssam		goto fail;
1163178676Ssam	}
1164178676Ssam	error = bus_dmamap_load(dma->tag, dma->map, dma->vaddr,
1165178676Ssam	    size, iwn_dma_map_addr, &dma->paddr, flags);
1166178676Ssam	if (error != 0) {
1167178676Ssam		device_printf(sc->sc_dev,
1168178676Ssam		    "%s: bus_dmamap_load failed, error %d\n", __func__, error);
1169178676Ssam		goto fail;
1170178676Ssam	}
1171178676Ssam
1172178676Ssam	if (kvap != NULL)
1173178676Ssam		*kvap = dma->vaddr;
1174178676Ssam	return 0;
1175178676Ssamfail:
1176178676Ssam	iwn_dma_contig_free(dma);
1177178676Ssam	return error;
1178178676Ssam}
1179178676Ssam
1180206477Sbschmidtstatic void
1181178676Ssamiwn_dma_contig_free(struct iwn_dma_info *dma)
1182178676Ssam{
1183178676Ssam	if (dma->tag != NULL) {
1184178676Ssam		if (dma->map != NULL) {
1185178676Ssam			if (dma->paddr == 0) {
1186178676Ssam				bus_dmamap_sync(dma->tag, dma->map,
1187178676Ssam				    BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE);
1188178676Ssam				bus_dmamap_unload(dma->tag, dma->map);
1189178676Ssam			}
1190178676Ssam			bus_dmamem_free(dma->tag, &dma->vaddr, dma->map);
1191178676Ssam		}
1192178676Ssam		bus_dma_tag_destroy(dma->tag);
1193178676Ssam	}
1194178676Ssam}
1195178676Ssam
1196206477Sbschmidtstatic int
1197198429Srpauloiwn_alloc_sched(struct iwn_softc *sc)
1198178676Ssam{
1199198429Srpaulo	/* TX scheduler rings must be aligned on a 1KB boundary. */
1200198429Srpaulo	return iwn_dma_contig_alloc(sc, &sc->sched_dma,
1201198429Srpaulo	    (void **)&sc->sched, sc->sc_hal->schedsz, 1024, BUS_DMA_NOWAIT);
1202178676Ssam}
1203178676Ssam
1204206477Sbschmidtstatic void
1205198429Srpauloiwn_free_sched(struct iwn_softc *sc)
1206178676Ssam{
1207198429Srpaulo	iwn_dma_contig_free(&sc->sched_dma);
1208178676Ssam}
1209178676Ssam
1210206477Sbschmidtstatic int
1211178676Ssamiwn_alloc_kw(struct iwn_softc *sc)
1212178676Ssam{
1213198429Srpaulo	/* "Keep Warm" page must be aligned on a 4KB boundary. */
1214198429Srpaulo	return iwn_dma_contig_alloc(sc, &sc->kw_dma, NULL, 4096, 4096,
1215198429Srpaulo	    BUS_DMA_NOWAIT);
1216178676Ssam}
1217178676Ssam
1218206477Sbschmidtstatic void
1219178676Ssamiwn_free_kw(struct iwn_softc *sc)
1220178676Ssam{
1221178676Ssam	iwn_dma_contig_free(&sc->kw_dma);
1222178676Ssam}
1223178676Ssam
1224206477Sbschmidtstatic int
1225201209Srpauloiwn_alloc_ict(struct iwn_softc *sc)
1226201209Srpaulo{
1227201209Srpaulo	/* ICT table must be aligned on a 4KB boundary. */
1228201209Srpaulo	return iwn_dma_contig_alloc(sc, &sc->ict_dma,
1229201209Srpaulo	    (void **)&sc->ict, IWN_ICT_SIZE, 4096, BUS_DMA_NOWAIT);
1230201209Srpaulo}
1231201209Srpaulo
1232206477Sbschmidtstatic void
1233201209Srpauloiwn_free_ict(struct iwn_softc *sc)
1234201209Srpaulo{
1235201209Srpaulo	iwn_dma_contig_free(&sc->ict_dma);
1236201209Srpaulo}
1237201209Srpaulo
1238206477Sbschmidtstatic int
1239178676Ssamiwn_alloc_fwmem(struct iwn_softc *sc)
1240178676Ssam{
1241198429Srpaulo	/* Must be aligned on a 16-byte boundary. */
1242178676Ssam	return iwn_dma_contig_alloc(sc, &sc->fw_dma, NULL,
1243198429Srpaulo	    sc->sc_hal->fwsz, 16, BUS_DMA_NOWAIT);
1244178676Ssam}
1245178676Ssam
1246206477Sbschmidtstatic void
1247178676Ssamiwn_free_fwmem(struct iwn_softc *sc)
1248178676Ssam{
1249178676Ssam	iwn_dma_contig_free(&sc->fw_dma);
1250178676Ssam}
1251178676Ssam
1252206477Sbschmidtstatic int
1253178676Ssamiwn_alloc_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring)
1254178676Ssam{
1255198429Srpaulo	bus_size_t size;
1256178676Ssam	int i, error;
1257178676Ssam
1258178676Ssam	ring->cur = 0;
1259178676Ssam
1260198429Srpaulo	/* Allocate RX descriptors (256-byte aligned). */
1261198429Srpaulo	size = IWN_RX_RING_COUNT * sizeof (uint32_t);
1262178676Ssam	error = iwn_dma_contig_alloc(sc, &ring->desc_dma,
1263198429Srpaulo	    (void **)&ring->desc, size, 256, BUS_DMA_NOWAIT);
1264178676Ssam	if (error != 0) {
1265178676Ssam		device_printf(sc->sc_dev,
1266198429Srpaulo		    "%s: could not allocate Rx ring DMA memory, error %d\n",
1267178676Ssam		    __func__, error);
1268178676Ssam		goto fail;
1269178676Ssam	}
1270178676Ssam
1271198429Srpaulo	error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0,
1272178676Ssam	    BUS_SPACE_MAXADDR_32BIT,
1273198429Srpaulo	    BUS_SPACE_MAXADDR, NULL, NULL, MJUMPAGESIZE, 1,
1274201209Srpaulo	    MJUMPAGESIZE, BUS_DMA_NOWAIT, NULL, NULL, &ring->data_dmat);
1275198429Srpaulo	if (error != 0) {
1276198429Srpaulo		device_printf(sc->sc_dev,
1277178676Ssam		    "%s: bus_dma_tag_create_failed, error %d\n",
1278178676Ssam		    __func__, error);
1279198429Srpaulo		goto fail;
1280198429Srpaulo	}
1281178676Ssam
1282198429Srpaulo	/* Allocate RX status area (16-byte aligned). */
1283198429Srpaulo	error = iwn_dma_contig_alloc(sc, &ring->stat_dma,
1284198429Srpaulo	    (void **)&ring->stat, sizeof (struct iwn_rx_status),
1285198429Srpaulo	    16, BUS_DMA_NOWAIT);
1286198429Srpaulo	if (error != 0) {
1287198429Srpaulo		device_printf(sc->sc_dev,
1288198429Srpaulo		    "%s: could not allocate Rx status DMA memory, error %d\n",
1289198429Srpaulo		    __func__, error);
1290198429Srpaulo		goto fail;
1291198429Srpaulo	}
1292198429Srpaulo
1293178676Ssam	/*
1294198429Srpaulo	 * Allocate and map RX buffers.
1295178676Ssam	 */
1296178676Ssam	for (i = 0; i < IWN_RX_RING_COUNT; i++) {
1297178676Ssam		struct iwn_rx_data *data = &ring->data[i];
1298178676Ssam		bus_addr_t paddr;
1299178676Ssam
1300201209Srpaulo		error = bus_dmamap_create(ring->data_dmat, 0, &data->map);
1301178676Ssam		if (error != 0) {
1302178676Ssam			device_printf(sc->sc_dev,
1303178676Ssam			    "%s: bus_dmamap_create failed, error %d\n",
1304178676Ssam			    __func__, error);
1305178676Ssam			goto fail;
1306178676Ssam		}
1307198429Srpaulo
1308198439Srpaulo		data->m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE);
1309198439Srpaulo		if (data->m == NULL) {
1310178676Ssam			device_printf(sc->sc_dev,
1311206443Sbschmidt			    "%s: could not allocate rx mbuf\n", __func__);
1312178676Ssam			error = ENOMEM;
1313178676Ssam			goto fail;
1314178676Ssam		}
1315198429Srpaulo
1316198429Srpaulo		/* Map page. */
1317201209Srpaulo		error = bus_dmamap_load(ring->data_dmat, data->map,
1318198439Srpaulo		    mtod(data->m, caddr_t), MJUMPAGESIZE,
1319178676Ssam		    iwn_dma_map_addr, &paddr, BUS_DMA_NOWAIT);
1320178676Ssam		if (error != 0 && error != EFBIG) {
1321178676Ssam			device_printf(sc->sc_dev,
1322178676Ssam			    "%s: bus_dmamap_load failed, error %d\n",
1323178676Ssam			    __func__, error);
1324198439Srpaulo			m_freem(data->m);
1325178676Ssam			error = ENOMEM;	/* XXX unique code */
1326178676Ssam			goto fail;
1327178676Ssam		}
1328201209Srpaulo		bus_dmamap_sync(ring->data_dmat, data->map,
1329201209Srpaulo		    BUS_DMASYNC_PREWRITE);
1330178676Ssam
1331198429Srpaulo		/* Set physical address of RX buffer (256-byte aligned). */
1332178676Ssam		ring->desc[i] = htole32(paddr >> 8);
1333178676Ssam	}
1334178676Ssam	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
1335178676Ssam	    BUS_DMASYNC_PREWRITE);
1336178676Ssam	return 0;
1337178676Ssamfail:
1338178676Ssam	iwn_free_rx_ring(sc, ring);
1339178676Ssam	return error;
1340178676Ssam}
1341178676Ssam
1342206477Sbschmidtstatic void
1343178676Ssamiwn_reset_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring)
1344178676Ssam{
1345178676Ssam	int ntries;
1346178676Ssam
1347198429Srpaulo	if (iwn_nic_lock(sc) == 0) {
1348198429Srpaulo		IWN_WRITE(sc, IWN_FH_RX_CONFIG, 0);
1349198429Srpaulo		for (ntries = 0; ntries < 1000; ntries++) {
1350198429Srpaulo			if (IWN_READ(sc, IWN_FH_RX_STATUS) &
1351198429Srpaulo			    IWN_FH_RX_STATUS_IDLE)
1352198429Srpaulo				break;
1353198429Srpaulo			DELAY(10);
1354198429Srpaulo		}
1355198429Srpaulo		iwn_nic_unlock(sc);
1356178676Ssam#ifdef IWN_DEBUG
1357198429Srpaulo		if (ntries == 1000)
1358198429Srpaulo			DPRINTF(sc, IWN_DEBUG_ANY, "%s\n",
1359198429Srpaulo			    "timeout resetting Rx ring");
1360178676Ssam#endif
1361198429Srpaulo	}
1362178676Ssam	ring->cur = 0;
1363198429Srpaulo	sc->last_rx_valid = 0;
1364178676Ssam}
1365178676Ssam
1366206477Sbschmidtstatic void
1367178676Ssamiwn_free_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring)
1368178676Ssam{
1369178676Ssam	int i;
1370178676Ssam
1371178676Ssam	iwn_dma_contig_free(&ring->desc_dma);
1372198429Srpaulo	iwn_dma_contig_free(&ring->stat_dma);
1373178676Ssam
1374198429Srpaulo	for (i = 0; i < IWN_RX_RING_COUNT; i++) {
1375198429Srpaulo		struct iwn_rx_data *data = &ring->data[i];
1376198429Srpaulo
1377198429Srpaulo		if (data->m != NULL) {
1378201209Srpaulo			bus_dmamap_sync(ring->data_dmat, data->map,
1379198439Srpaulo			    BUS_DMASYNC_POSTREAD);
1380201209Srpaulo			bus_dmamap_unload(ring->data_dmat, data->map);
1381198429Srpaulo			m_freem(data->m);
1382198429Srpaulo		}
1383201209Srpaulo		if (data->map != NULL)
1384201209Srpaulo			bus_dmamap_destroy(ring->data_dmat, data->map);
1385198429Srpaulo	}
1386178676Ssam}
1387178676Ssam
1388206477Sbschmidtstatic int
1389178676Ssamiwn_alloc_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring, int qid)
1390178676Ssam{
1391178676Ssam	bus_size_t size;
1392198429Srpaulo	bus_addr_t paddr;
1393178676Ssam	int i, error;
1394178676Ssam
1395178676Ssam	ring->qid = qid;
1396178676Ssam	ring->queued = 0;
1397178676Ssam	ring->cur = 0;
1398178676Ssam
1399198429Srpaulo	/* Allocate TX descriptors (256-byte aligned.) */
1400178676Ssam	size = IWN_TX_RING_COUNT * sizeof(struct iwn_tx_desc);
1401178676Ssam	error = iwn_dma_contig_alloc(sc, &ring->desc_dma,
1402198429Srpaulo	    (void **)&ring->desc, size, 256, BUS_DMA_NOWAIT);
1403178676Ssam	if (error != 0) {
1404178676Ssam		device_printf(sc->sc_dev,
1405198429Srpaulo		    "%s: could not allocate TX ring DMA memory, error %d\n",
1406178676Ssam		    __func__, error);
1407178676Ssam		goto fail;
1408178676Ssam	}
1409178676Ssam
1410198429Srpaulo	/*
1411198429Srpaulo	 * We only use rings 0 through 4 (4 EDCA + cmd) so there is no need
1412198429Srpaulo	 * to allocate commands space for other rings.
1413198429Srpaulo	 */
1414198429Srpaulo	if (qid > 4)
1415198429Srpaulo		return 0;
1416198429Srpaulo
1417178676Ssam	size = IWN_TX_RING_COUNT * sizeof(struct iwn_tx_cmd);
1418178676Ssam	error = iwn_dma_contig_alloc(sc, &ring->cmd_dma,
1419178676Ssam	    (void **)&ring->cmd, size, 4, BUS_DMA_NOWAIT);
1420178676Ssam	if (error != 0) {
1421178676Ssam		device_printf(sc->sc_dev,
1422198429Srpaulo		    "%s: could not allocate TX cmd DMA memory, error %d\n",
1423178676Ssam		    __func__, error);
1424178676Ssam		goto fail;
1425178676Ssam	}
1426178676Ssam
1427198429Srpaulo	error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0,
1428178676Ssam	    BUS_SPACE_MAXADDR_32BIT,
1429198429Srpaulo	    BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, IWN_MAX_SCATTER - 1,
1430201209Srpaulo	    MCLBYTES, BUS_DMA_NOWAIT, NULL, NULL, &ring->data_dmat);
1431198429Srpaulo	if (error != 0) {
1432198429Srpaulo		device_printf(sc->sc_dev,
1433178676Ssam		    "%s: bus_dma_tag_create_failed, error %d\n",
1434178676Ssam		    __func__, error);
1435198429Srpaulo		goto fail;
1436198429Srpaulo	}
1437178676Ssam
1438198429Srpaulo	paddr = ring->cmd_dma.paddr;
1439178676Ssam	for (i = 0; i < IWN_TX_RING_COUNT; i++) {
1440178676Ssam		struct iwn_tx_data *data = &ring->data[i];
1441178676Ssam
1442198429Srpaulo		data->cmd_paddr = paddr;
1443198429Srpaulo		data->scratch_paddr = paddr + 12;
1444198429Srpaulo		paddr += sizeof (struct iwn_tx_cmd);
1445198429Srpaulo
1446201209Srpaulo		error = bus_dmamap_create(ring->data_dmat, 0, &data->map);
1447178676Ssam		if (error != 0) {
1448178676Ssam			device_printf(sc->sc_dev,
1449178676Ssam			    "%s: bus_dmamap_create failed, error %d\n",
1450178676Ssam			    __func__, error);
1451178676Ssam			goto fail;
1452178676Ssam		}
1453201209Srpaulo		bus_dmamap_sync(ring->data_dmat, data->map,
1454201209Srpaulo		    BUS_DMASYNC_PREWRITE);
1455178676Ssam	}
1456178676Ssam	return 0;
1457178676Ssamfail:
1458178676Ssam	iwn_free_tx_ring(sc, ring);
1459178676Ssam	return error;
1460178676Ssam}
1461178676Ssam
1462206477Sbschmidtstatic void
1463178676Ssamiwn_reset_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring)
1464178676Ssam{
1465198429Srpaulo	int i;
1466178676Ssam
1467178676Ssam	for (i = 0; i < IWN_TX_RING_COUNT; i++) {
1468178676Ssam		struct iwn_tx_data *data = &ring->data[i];
1469178676Ssam
1470178676Ssam		if (data->m != NULL) {
1471201209Srpaulo			bus_dmamap_unload(ring->data_dmat, data->map);
1472178676Ssam			m_freem(data->m);
1473178676Ssam			data->m = NULL;
1474178676Ssam		}
1475178676Ssam	}
1476198429Srpaulo	/* Clear TX descriptors. */
1477198429Srpaulo	memset(ring->desc, 0, ring->desc_dma.size);
1478198439Srpaulo	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
1479198439Srpaulo	    BUS_DMASYNC_PREWRITE);
1480198429Srpaulo	sc->qfullmsk &= ~(1 << ring->qid);
1481178676Ssam	ring->queued = 0;
1482178676Ssam	ring->cur = 0;
1483178676Ssam}
1484178676Ssam
1485206477Sbschmidtstatic void
1486178676Ssamiwn_free_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring)
1487178676Ssam{
1488178676Ssam	int i;
1489178676Ssam
1490178676Ssam	iwn_dma_contig_free(&ring->desc_dma);
1491178676Ssam	iwn_dma_contig_free(&ring->cmd_dma);
1492178676Ssam
1493201209Srpaulo	for (i = 0; i < IWN_TX_RING_COUNT; i++) {
1494201209Srpaulo		struct iwn_tx_data *data = &ring->data[i];
1495178676Ssam
1496201209Srpaulo		if (data->m != NULL) {
1497201209Srpaulo			bus_dmamap_sync(ring->data_dmat, data->map,
1498201209Srpaulo			    BUS_DMASYNC_POSTWRITE);
1499201209Srpaulo			bus_dmamap_unload(ring->data_dmat, data->map);
1500201209Srpaulo			m_freem(data->m);
1501178676Ssam		}
1502201209Srpaulo		if (data->map != NULL)
1503201209Srpaulo			bus_dmamap_destroy(ring->data_dmat, data->map);
1504178676Ssam	}
1505178676Ssam}
1506178676Ssam
1507206477Sbschmidtstatic void
1508201209Srpauloiwn5000_ict_reset(struct iwn_softc *sc)
1509201209Srpaulo{
1510201209Srpaulo	/* Disable interrupts. */
1511201209Srpaulo	IWN_WRITE(sc, IWN_INT_MASK, 0);
1512201209Srpaulo
1513201209Srpaulo	/* Reset ICT table. */
1514201209Srpaulo	memset(sc->ict, 0, IWN_ICT_SIZE);
1515201209Srpaulo	sc->ict_cur = 0;
1516201209Srpaulo
1517201209Srpaulo	/* Set physical address of ICT table (4KB aligned.) */
1518201209Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "%s: enabling ICT\n", __func__);
1519201209Srpaulo	IWN_WRITE(sc, IWN_DRAM_INT_TBL, IWN_DRAM_INT_TBL_ENABLE |
1520201209Srpaulo	    IWN_DRAM_INT_TBL_WRAP_CHECK | sc->ict_dma.paddr >> 12);
1521201209Srpaulo
1522201209Srpaulo	/* Enable periodic RX interrupt. */
1523201209Srpaulo	sc->int_mask |= IWN_INT_RX_PERIODIC;
1524201209Srpaulo	/* Switch to ICT interrupt mode in driver. */
1525201209Srpaulo	sc->sc_flags |= IWN_FLAG_USE_ICT;
1526201209Srpaulo
1527201209Srpaulo	/* Re-enable interrupts. */
1528201209Srpaulo	IWN_WRITE(sc, IWN_INT, 0xffffffff);
1529201209Srpaulo	IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask);
1530201209Srpaulo}
1531201209Srpaulo
1532206477Sbschmidtstatic int
1533198429Srpauloiwn_read_eeprom(struct iwn_softc *sc, uint8_t macaddr[IEEE80211_ADDR_LEN])
1534178676Ssam{
1535198429Srpaulo	const struct iwn_hal *hal = sc->sc_hal;
1536198429Srpaulo	int error;
1537198429Srpaulo	uint16_t val;
1538178676Ssam
1539198429Srpaulo	/* Check whether adapter has an EEPROM or an OTPROM. */
1540198429Srpaulo	if (sc->hw_type >= IWN_HW_REV_TYPE_1000 &&
1541198429Srpaulo	    (IWN_READ(sc, IWN_OTP_GP) & IWN_OTP_GP_DEV_SEL_OTP))
1542198429Srpaulo		sc->sc_flags |= IWN_FLAG_HAS_OTPROM;
1543198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "%s found\n",
1544198429Srpaulo	    (sc->sc_flags & IWN_FLAG_HAS_OTPROM) ? "OTPROM" : "EEPROM");
1545178676Ssam
1546201209Srpaulo	/* Adapter has to be powered on for EEPROM access to work. */
1547201209Srpaulo	error = iwn_apm_init(sc);
1548201209Srpaulo	if (error != 0) {
1549201209Srpaulo		device_printf(sc->sc_dev,
1550201209Srpaulo		    "%s: could not power ON adapter, error %d\n",
1551201209Srpaulo		    __func__, error);
1552201209Srpaulo		return error;
1553201209Srpaulo	}
1554201209Srpaulo
1555198429Srpaulo	if ((IWN_READ(sc, IWN_EEPROM_GP) & 0x7) == 0) {
1556198429Srpaulo		device_printf(sc->sc_dev, "%s: bad ROM signature\n", __func__);
1557198429Srpaulo		return EIO;
1558198429Srpaulo	}
1559198429Srpaulo	error = iwn_eeprom_lock(sc);
1560198429Srpaulo	if (error != 0) {
1561198429Srpaulo		device_printf(sc->sc_dev,
1562198429Srpaulo		    "%s: could not lock ROM, error %d\n",
1563198429Srpaulo		    __func__, error);
1564198429Srpaulo		return error;
1565198429Srpaulo	}
1566178676Ssam
1567201209Srpaulo	if (sc->sc_flags & IWN_FLAG_HAS_OTPROM) {
1568201209Srpaulo		error = iwn_init_otprom(sc);
1569201209Srpaulo		if (error != 0) {
1570201209Srpaulo			device_printf(sc->sc_dev,
1571201209Srpaulo			    "%s: could not initialize OTPROM, error %d\n",
1572201209Srpaulo			    __func__, error);
1573201209Srpaulo			return error;
1574201209Srpaulo		}
1575198429Srpaulo	}
1576178676Ssam
1577198429Srpaulo	iwn_read_prom_data(sc, IWN_EEPROM_RFCFG, &val, 2);
1578198429Srpaulo	sc->rfcfg = le16toh(val);
1579198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "radio config=0x%04x\n", sc->rfcfg);
1580178676Ssam
1581198429Srpaulo	/* Read MAC address. */
1582198429Srpaulo	iwn_read_prom_data(sc, IWN_EEPROM_MAC, macaddr, 6);
1583178676Ssam
1584198429Srpaulo	/* Read adapter-specific information from EEPROM. */
1585198429Srpaulo	hal->read_eeprom(sc);
1586178676Ssam
1587201209Srpaulo	iwn_apm_stop(sc);	/* Power OFF adapter. */
1588201209Srpaulo
1589198429Srpaulo	iwn_eeprom_unlock(sc);
1590198429Srpaulo	return 0;
1591178676Ssam}
1592178676Ssam
1593206477Sbschmidtstatic void
1594198429Srpauloiwn4965_read_eeprom(struct iwn_softc *sc)
1595178676Ssam{
1596201209Srpaulo	uint32_t addr;
1597198429Srpaulo	int i;
1598198429Srpaulo	uint16_t val;
1599178676Ssam
1600198429Srpaulo	/* Read regulatory domain (4 ASCII characters.) */
1601198429Srpaulo	iwn_read_prom_data(sc, IWN4965_EEPROM_DOMAIN, sc->eeprom_domain, 4);
1602178676Ssam
1603201209Srpaulo	/* Read the list of authorized channels (20MHz ones only.) */
1604201209Srpaulo	for (i = 0; i < 5; i++) {
1605201209Srpaulo		addr = iwn4965_regulatory_bands[i];
1606201209Srpaulo		iwn_read_eeprom_channels(sc, i, addr);
1607201209Srpaulo	}
1608198429Srpaulo
1609198429Srpaulo	/* Read maximum allowed TX power for 2GHz and 5GHz bands. */
1610198429Srpaulo	iwn_read_prom_data(sc, IWN4965_EEPROM_MAXPOW, &val, 2);
1611198429Srpaulo	sc->maxpwr2GHz = val & 0xff;
1612198429Srpaulo	sc->maxpwr5GHz = val >> 8;
1613198429Srpaulo	/* Check that EEPROM values are within valid range. */
1614198429Srpaulo	if (sc->maxpwr5GHz < 20 || sc->maxpwr5GHz > 50)
1615198429Srpaulo		sc->maxpwr5GHz = 38;
1616198429Srpaulo	if (sc->maxpwr2GHz < 20 || sc->maxpwr2GHz > 50)
1617198429Srpaulo		sc->maxpwr2GHz = 38;
1618198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "maxpwr 2GHz=%d 5GHz=%d\n",
1619198429Srpaulo	    sc->maxpwr2GHz, sc->maxpwr5GHz);
1620198429Srpaulo
1621198429Srpaulo	/* Read samples for each TX power group. */
1622198429Srpaulo	iwn_read_prom_data(sc, IWN4965_EEPROM_BANDS, sc->bands,
1623198429Srpaulo	    sizeof sc->bands);
1624198429Srpaulo
1625198429Srpaulo	/* Read voltage at which samples were taken. */
1626198429Srpaulo	iwn_read_prom_data(sc, IWN4965_EEPROM_VOLTAGE, &val, 2);
1627198429Srpaulo	sc->eeprom_voltage = (int16_t)le16toh(val);
1628198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "voltage=%d (in 0.3V)\n",
1629198429Srpaulo	    sc->eeprom_voltage);
1630198429Srpaulo
1631198429Srpaulo#ifdef IWN_DEBUG
1632198429Srpaulo	/* Print samples. */
1633201209Srpaulo	if (sc->sc_debug & IWN_DEBUG_ANY) {
1634198429Srpaulo		for (i = 0; i < IWN_NBANDS; i++)
1635198429Srpaulo			iwn4965_print_power_group(sc, i);
1636178676Ssam	}
1637198429Srpaulo#endif
1638178676Ssam}
1639178676Ssam
1640198429Srpaulo#ifdef IWN_DEBUG
1641206477Sbschmidtstatic void
1642198429Srpauloiwn4965_print_power_group(struct iwn_softc *sc, int i)
1643178676Ssam{
1644198429Srpaulo	struct iwn4965_eeprom_band *band = &sc->bands[i];
1645198429Srpaulo	struct iwn4965_eeprom_chan_samples *chans = band->chans;
1646198429Srpaulo	int j, c;
1647178676Ssam
1648198429Srpaulo	printf("===band %d===\n", i);
1649198429Srpaulo	printf("chan lo=%d, chan hi=%d\n", band->lo, band->hi);
1650198429Srpaulo	printf("chan1 num=%d\n", chans[0].num);
1651198429Srpaulo	for (c = 0; c < 2; c++) {
1652198429Srpaulo		for (j = 0; j < IWN_NSAMPLES; j++) {
1653198429Srpaulo			printf("chain %d, sample %d: temp=%d gain=%d "
1654198429Srpaulo			    "power=%d pa_det=%d\n", c, j,
1655198429Srpaulo			    chans[0].samples[c][j].temp,
1656198429Srpaulo			    chans[0].samples[c][j].gain,
1657198429Srpaulo			    chans[0].samples[c][j].power,
1658198429Srpaulo			    chans[0].samples[c][j].pa_det);
1659198429Srpaulo		}
1660198429Srpaulo	}
1661198429Srpaulo	printf("chan2 num=%d\n", chans[1].num);
1662198429Srpaulo	for (c = 0; c < 2; c++) {
1663198429Srpaulo		for (j = 0; j < IWN_NSAMPLES; j++) {
1664198429Srpaulo			printf("chain %d, sample %d: temp=%d gain=%d "
1665198429Srpaulo			    "power=%d pa_det=%d\n", c, j,
1666198429Srpaulo			    chans[1].samples[c][j].temp,
1667198429Srpaulo			    chans[1].samples[c][j].gain,
1668198429Srpaulo			    chans[1].samples[c][j].power,
1669198429Srpaulo			    chans[1].samples[c][j].pa_det);
1670198429Srpaulo		}
1671198429Srpaulo	}
1672178676Ssam}
1673198429Srpaulo#endif
1674178676Ssam
1675206477Sbschmidtstatic void
1676198429Srpauloiwn5000_read_eeprom(struct iwn_softc *sc)
1677178676Ssam{
1678206444Sbschmidt	struct iwn5000_eeprom_calib_hdr hdr;
1679201209Srpaulo	int32_t temp, volt;
1680198429Srpaulo	uint32_t addr, base;
1681198429Srpaulo	int i;
1682198429Srpaulo	uint16_t val;
1683178676Ssam
1684198429Srpaulo	/* Read regulatory domain (4 ASCII characters.) */
1685198429Srpaulo	iwn_read_prom_data(sc, IWN5000_EEPROM_REG, &val, 2);
1686198429Srpaulo	base = le16toh(val);
1687198429Srpaulo	iwn_read_prom_data(sc, base + IWN5000_EEPROM_DOMAIN,
1688198429Srpaulo	    sc->eeprom_domain, 4);
1689178676Ssam
1690201209Srpaulo	/* Read the list of authorized channels (20MHz ones only.) */
1691201209Srpaulo	for (i = 0; i < 5; i++) {
1692198429Srpaulo		addr = base + iwn5000_regulatory_bands[i];
1693201209Srpaulo		iwn_read_eeprom_channels(sc, i, addr);
1694198429Srpaulo	}
1695178676Ssam
1696201209Srpaulo	/* Read enhanced TX power information for 6000 Series. */
1697201209Srpaulo	if (sc->hw_type >= IWN_HW_REV_TYPE_6000)
1698201209Srpaulo		iwn_read_eeprom_enhinfo(sc);
1699201209Srpaulo
1700198429Srpaulo	iwn_read_prom_data(sc, IWN5000_EEPROM_CAL, &val, 2);
1701198429Srpaulo	base = le16toh(val);
1702206444Sbschmidt	iwn_read_prom_data(sc, base, &hdr, sizeof hdr);
1703206444Sbschmidt	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
1704206444Sbschmidt	    "%s: calib version=%u pa type=%u voltage=%u\n",
1705206444Sbschmidt	    __func__, hdr.version, hdr.pa_type, le16toh(hdr.volt));
1706210108Sbschmidt	sc->calib_ver = hdr.version;
1707206444Sbschmidt
1708198429Srpaulo	if (sc->hw_type == IWN_HW_REV_TYPE_5150) {
1709201209Srpaulo		/* Compute temperature offset. */
1710198429Srpaulo		iwn_read_prom_data(sc, base + IWN5000_EEPROM_TEMP, &val, 2);
1711198429Srpaulo		temp = le16toh(val);
1712198429Srpaulo		iwn_read_prom_data(sc, base + IWN5000_EEPROM_VOLT, &val, 2);
1713198429Srpaulo		volt = le16toh(val);
1714201209Srpaulo		sc->temp_off = temp - (volt / -5);
1715201209Srpaulo		DPRINTF(sc, IWN_DEBUG_CALIBRATE, "temp=%d volt=%d offset=%dK\n",
1716201209Srpaulo		    temp, volt, sc->temp_off);
1717178676Ssam	}
1718178676Ssam}
1719178676Ssam
1720201209Srpaulo/*
1721201209Srpaulo * Translate EEPROM flags to net80211.
1722201209Srpaulo */
1723201209Srpaulostatic uint32_t
1724201209Srpauloiwn_eeprom_channel_flags(struct iwn_eeprom_chan *channel)
1725201209Srpaulo{
1726201209Srpaulo	uint32_t nflags;
1727201209Srpaulo
1728201209Srpaulo	nflags = 0;
1729201209Srpaulo	if ((channel->flags & IWN_EEPROM_CHAN_ACTIVE) == 0)
1730201209Srpaulo		nflags |= IEEE80211_CHAN_PASSIVE;
1731201209Srpaulo	if ((channel->flags & IWN_EEPROM_CHAN_IBSS) == 0)
1732201209Srpaulo		nflags |= IEEE80211_CHAN_NOADHOC;
1733201209Srpaulo	if (channel->flags & IWN_EEPROM_CHAN_RADAR) {
1734201209Srpaulo		nflags |= IEEE80211_CHAN_DFS;
1735201209Srpaulo		/* XXX apparently IBSS may still be marked */
1736201209Srpaulo		nflags |= IEEE80211_CHAN_NOADHOC;
1737201209Srpaulo	}
1738201209Srpaulo
1739201209Srpaulo	return nflags;
1740201209Srpaulo}
1741201209Srpaulo
1742198429Srpaulostatic void
1743201209Srpauloiwn_read_eeprom_band(struct iwn_softc *sc, int n)
1744178676Ssam{
1745198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
1746198429Srpaulo	struct ieee80211com *ic = ifp->if_l2com;
1747201209Srpaulo	struct iwn_eeprom_chan *channels = sc->eeprom_channels[n];
1748201209Srpaulo	const struct iwn_chan_band *band = &iwn_bands[n];
1749198429Srpaulo	struct ieee80211_channel *c;
1750198429Srpaulo	int i, chan, nflags;
1751178676Ssam
1752198429Srpaulo	for (i = 0; i < band->nchan; i++) {
1753198429Srpaulo		if (!(channels[i].flags & IWN_EEPROM_CHAN_VALID)) {
1754198429Srpaulo			DPRINTF(sc, IWN_DEBUG_RESET,
1755198429Srpaulo			    "skip chan %d flags 0x%x maxpwr %d\n",
1756198429Srpaulo			    band->chan[i], channels[i].flags,
1757198429Srpaulo			    channels[i].maxpwr);
1758198429Srpaulo			continue;
1759198429Srpaulo		}
1760198429Srpaulo		chan = band->chan[i];
1761201209Srpaulo		nflags = iwn_eeprom_channel_flags(&channels[i]);
1762178676Ssam
1763198429Srpaulo		DPRINTF(sc, IWN_DEBUG_RESET,
1764198429Srpaulo		    "add chan %d flags 0x%x maxpwr %d\n",
1765198429Srpaulo		    chan, channels[i].flags, channels[i].maxpwr);
1766198429Srpaulo
1767198429Srpaulo		c = &ic->ic_channels[ic->ic_nchans++];
1768198429Srpaulo		c->ic_ieee = chan;
1769198429Srpaulo		c->ic_maxregpower = channels[i].maxpwr;
1770198429Srpaulo		c->ic_maxpower = 2*c->ic_maxregpower;
1771206445Sbschmidt
1772206445Sbschmidt		/* Save maximum allowed TX power for this channel. */
1773206445Sbschmidt		sc->maxpwr[chan] = channels[i].maxpwr;
1774206445Sbschmidt
1775201209Srpaulo		if (n == 0) {	/* 2GHz band */
1776201209Srpaulo			c->ic_freq = ieee80211_ieee2mhz(chan,
1777201209Srpaulo			    IEEE80211_CHAN_G);
1778201209Srpaulo
1779198429Srpaulo			/* G =>'s B is supported */
1780198429Srpaulo			c->ic_flags = IEEE80211_CHAN_B | nflags;
1781198429Srpaulo
1782198429Srpaulo			c = &ic->ic_channels[ic->ic_nchans++];
1783198429Srpaulo			c[0] = c[-1];
1784198429Srpaulo			c->ic_flags = IEEE80211_CHAN_G | nflags;
1785198429Srpaulo		} else {	/* 5GHz band */
1786201209Srpaulo			c->ic_freq = ieee80211_ieee2mhz(chan,
1787201209Srpaulo			    IEEE80211_CHAN_A);
1788198429Srpaulo			c->ic_flags = IEEE80211_CHAN_A | nflags;
1789178676Ssam		}
1790201209Srpaulo#if 0	/* HT */
1791198429Srpaulo		/* XXX no constraints on using HT20 */
1792198429Srpaulo		/* add HT20, HT40 added separately */
1793198429Srpaulo		c = &ic->ic_channels[ic->ic_nchans++];
1794198429Srpaulo		c[0] = c[-1];
1795198429Srpaulo		c->ic_flags |= IEEE80211_CHAN_HT20;
1796198429Srpaulo		/* XXX NARROW =>'s 1/2 and 1/4 width? */
1797201209Srpaulo#endif
1798178676Ssam	}
1799178676Ssam}
1800178676Ssam
1801201209Srpaulo#if 0	/* HT */
1802198429Srpaulostatic void
1803201209Srpauloiwn_read_eeprom_ht40(struct iwn_softc *sc, int n)
1804178676Ssam{
1805198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
1806198429Srpaulo	struct ieee80211com *ic = ifp->if_l2com;
1807201209Srpaulo	struct iwn_eeprom_chan *channels = sc->eeprom_channels[n];
1808201209Srpaulo	const struct iwn_chan_band *band = &iwn_bands[n];
1809198429Srpaulo	struct ieee80211_channel *c, *cent, *extc;
1810198429Srpaulo	int i;
1811178676Ssam
1812198429Srpaulo	for (i = 0; i < band->nchan; i++) {
1813198429Srpaulo		if (!(channels[i].flags & IWN_EEPROM_CHAN_VALID) ||
1814198429Srpaulo		    !(channels[i].flags & IWN_EEPROM_CHAN_WIDE)) {
1815198429Srpaulo			DPRINTF(sc, IWN_DEBUG_RESET,
1816198429Srpaulo			    "skip chan %d flags 0x%x maxpwr %d\n",
1817198429Srpaulo			    band->chan[i], channels[i].flags,
1818198429Srpaulo			    channels[i].maxpwr);
1819198429Srpaulo			continue;
1820198429Srpaulo		}
1821198429Srpaulo		/*
1822198429Srpaulo		 * Each entry defines an HT40 channel pair; find the
1823198429Srpaulo		 * center channel, then the extension channel above.
1824198429Srpaulo		 */
1825198429Srpaulo		cent = ieee80211_find_channel_byieee(ic, band->chan[i],
1826201209Srpaulo		    band->flags & ~IEEE80211_CHAN_HT);
1827198429Srpaulo		if (cent == NULL) {	/* XXX shouldn't happen */
1828198429Srpaulo			device_printf(sc->sc_dev,
1829198429Srpaulo			    "%s: no entry for channel %d\n",
1830198429Srpaulo			    __func__, band->chan[i]);
1831198429Srpaulo			continue;
1832198429Srpaulo		}
1833198429Srpaulo		extc = ieee80211_find_channel(ic, cent->ic_freq+20,
1834201209Srpaulo		    band->flags & ~IEEE80211_CHAN_HT);
1835198429Srpaulo		if (extc == NULL) {
1836198429Srpaulo			DPRINTF(sc, IWN_DEBUG_RESET,
1837198429Srpaulo			    "skip chan %d, extension channel not found\n",
1838198429Srpaulo			    band->chan[i]);
1839198429Srpaulo			continue;
1840198429Srpaulo		}
1841178676Ssam
1842198429Srpaulo		DPRINTF(sc, IWN_DEBUG_RESET,
1843198429Srpaulo		    "add ht40 chan %d flags 0x%x maxpwr %d\n",
1844198429Srpaulo		    band->chan[i], channels[i].flags, channels[i].maxpwr);
1845178676Ssam
1846198429Srpaulo		c = &ic->ic_channels[ic->ic_nchans++];
1847198429Srpaulo		c[0] = cent[0];
1848198429Srpaulo		c->ic_extieee = extc->ic_ieee;
1849198429Srpaulo		c->ic_flags &= ~IEEE80211_CHAN_HT;
1850198429Srpaulo		c->ic_flags |= IEEE80211_CHAN_HT40U;
1851198429Srpaulo		c = &ic->ic_channels[ic->ic_nchans++];
1852198429Srpaulo		c[0] = extc[0];
1853198429Srpaulo		c->ic_extieee = cent->ic_ieee;
1854198429Srpaulo		c->ic_flags &= ~IEEE80211_CHAN_HT;
1855198429Srpaulo		c->ic_flags |= IEEE80211_CHAN_HT40D;
1856178676Ssam	}
1857198429Srpaulo}
1858201209Srpaulo#endif
1859178676Ssam
1860198429Srpaulostatic void
1861201209Srpauloiwn_read_eeprom_channels(struct iwn_softc *sc, int n, uint32_t addr)
1862198429Srpaulo{
1863198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
1864198429Srpaulo	struct ieee80211com *ic = ifp->if_l2com;
1865178676Ssam
1866201209Srpaulo	iwn_read_prom_data(sc, addr, &sc->eeprom_channels[n],
1867201209Srpaulo	    iwn_bands[n].nchan * sizeof (struct iwn_eeprom_chan));
1868201209Srpaulo
1869198429Srpaulo	if (n < 5)
1870201209Srpaulo		iwn_read_eeprom_band(sc, n);
1871201209Srpaulo#if 0	/* HT */
1872198429Srpaulo	else
1873201209Srpaulo		iwn_read_eeprom_ht40(sc, n);
1874201209Srpaulo#endif
1875198429Srpaulo	ieee80211_sort_channels(ic->ic_channels, ic->ic_nchans);
1876178676Ssam}
1877178676Ssam
1878201209Srpaulo#define nitems(_a)	(sizeof((_a)) / sizeof((_a)[0]))
1879201209Srpaulo
1880206477Sbschmidtstatic void
1881201209Srpauloiwn_read_eeprom_enhinfo(struct iwn_softc *sc)
1882201209Srpaulo{
1883201209Srpaulo	struct iwn_eeprom_enhinfo enhinfo[35];
1884201209Srpaulo	uint16_t val, base;
1885201209Srpaulo	int8_t maxpwr;
1886201209Srpaulo	int i;
1887201209Srpaulo
1888201209Srpaulo	iwn_read_prom_data(sc, IWN5000_EEPROM_REG, &val, 2);
1889201209Srpaulo	base = le16toh(val);
1890201209Srpaulo	iwn_read_prom_data(sc, base + IWN6000_EEPROM_ENHINFO,
1891201209Srpaulo	    enhinfo, sizeof enhinfo);
1892201209Srpaulo
1893201209Srpaulo	memset(sc->enh_maxpwr, 0, sizeof sc->enh_maxpwr);
1894201209Srpaulo	for (i = 0; i < nitems(enhinfo); i++) {
1895201209Srpaulo		if (enhinfo[i].chan == 0 || enhinfo[i].reserved != 0)
1896201209Srpaulo			continue;	/* Skip invalid entries. */
1897201209Srpaulo
1898201209Srpaulo		maxpwr = 0;
1899201209Srpaulo		if (sc->txchainmask & IWN_ANT_A)
1900201209Srpaulo			maxpwr = MAX(maxpwr, enhinfo[i].chain[0]);
1901201209Srpaulo		if (sc->txchainmask & IWN_ANT_B)
1902201209Srpaulo			maxpwr = MAX(maxpwr, enhinfo[i].chain[1]);
1903201209Srpaulo		if (sc->txchainmask & IWN_ANT_C)
1904201209Srpaulo			maxpwr = MAX(maxpwr, enhinfo[i].chain[2]);
1905201209Srpaulo		if (sc->ntxchains == 2)
1906201209Srpaulo			maxpwr = MAX(maxpwr, enhinfo[i].mimo2);
1907201209Srpaulo		else if (sc->ntxchains == 3)
1908201209Srpaulo			maxpwr = MAX(maxpwr, enhinfo[i].mimo3);
1909201209Srpaulo		maxpwr /= 2;	/* Convert half-dBm to dBm. */
1910201209Srpaulo
1911201209Srpaulo		DPRINTF(sc, IWN_DEBUG_RESET, "enhinfo %d, maxpwr=%d\n", i,
1912201209Srpaulo		    maxpwr);
1913201209Srpaulo		sc->enh_maxpwr[i] = maxpwr;
1914201209Srpaulo	}
1915201209Srpaulo}
1916201209Srpaulo
1917206477Sbschmidtstatic struct ieee80211_node *
1918198429Srpauloiwn_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN])
1919178676Ssam{
1920198429Srpaulo	return malloc(sizeof (struct iwn_node), M_80211_NODE,M_NOWAIT | M_ZERO);
1921198429Srpaulo}
1922178676Ssam
1923206477Sbschmidtstatic int
1924198429Srpauloiwn_media_change(struct ifnet *ifp)
1925178676Ssam{
1926198429Srpaulo	int error = ieee80211_media_change(ifp);
1927198429Srpaulo	/* NB: only the fixed rate can change and that doesn't need a reset */
1928198429Srpaulo	return (error == ENETRESET ? 0 : error);
1929198429Srpaulo}
1930178676Ssam
1931206477Sbschmidtstatic int
1932198429Srpauloiwn_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
1933198429Srpaulo{
1934198429Srpaulo	struct iwn_vap *ivp = IWN_VAP(vap);
1935198429Srpaulo	struct ieee80211com *ic = vap->iv_ic;
1936198429Srpaulo	struct iwn_softc *sc = ic->ic_ifp->if_softc;
1937198429Srpaulo	int error;
1938178676Ssam
1939198429Srpaulo	DPRINTF(sc, IWN_DEBUG_STATE, "%s: %s -> %s\n", __func__,
1940198429Srpaulo		ieee80211_state_name[vap->iv_state],
1941198429Srpaulo		ieee80211_state_name[nstate]);
1942178676Ssam
1943198429Srpaulo	IEEE80211_UNLOCK(ic);
1944198429Srpaulo	IWN_LOCK(sc);
1945198429Srpaulo	callout_stop(&sc->sc_timer_to);
1946178676Ssam
1947210114Sbschmidt	switch (nstate) {
1948210114Sbschmidt	case IEEE80211_S_ASSOC:
1949210114Sbschmidt		if (vap->iv_state != IEEE80211_S_RUN)
1950210114Sbschmidt			break;
1951210114Sbschmidt		/* FALLTHROUGH */
1952210114Sbschmidt	case IEEE80211_S_AUTH:
1953210114Sbschmidt		if (vap->iv_state == IEEE80211_S_AUTH)
1954210114Sbschmidt			break;
1955210114Sbschmidt
1956210114Sbschmidt		/*
1957210114Sbschmidt		 * !AUTH -> AUTH transition requires state reset to handle
1958210114Sbschmidt		 * reassociations correctly.
1959210114Sbschmidt		 */
1960198439Srpaulo		sc->rxon.associd = 0;
1961198439Srpaulo		sc->rxon.filter &= ~htole32(IWN_FILTER_BSS);
1962198439Srpaulo		iwn_calib_reset(sc);
1963198439Srpaulo		error = iwn_auth(sc, vap);
1964210114Sbschmidt		break;
1965210114Sbschmidt
1966210114Sbschmidt	case IEEE80211_S_RUN:
1967198429Srpaulo		/*
1968210114Sbschmidt		 * RUN -> RUN transition; Just restart the timers.
1969210114Sbschmidt		 */
1970216237Sbschmidt		if (vap->iv_state == IEEE80211_S_RUN &&
1971216237Sbschmidt		    vap->iv_opmode != IEEE80211_M_MONITOR) {
1972210114Sbschmidt			iwn_calib_reset(sc);
1973210114Sbschmidt			break;
1974210114Sbschmidt		}
1975210114Sbschmidt
1976210114Sbschmidt		/*
1977198429Srpaulo		 * !RUN -> RUN requires setting the association id
1978198429Srpaulo		 * which is done with a firmware cmd.  We also defer
1979198429Srpaulo		 * starting the timers until that work is done.
1980198429Srpaulo		 */
1981198429Srpaulo		error = iwn_run(sc, vap);
1982210114Sbschmidt		break;
1983210114Sbschmidt
1984210114Sbschmidt	default:
1985210114Sbschmidt		break;
1986178676Ssam	}
1987198429Srpaulo	IWN_UNLOCK(sc);
1988198429Srpaulo	IEEE80211_LOCK(ic);
1989198429Srpaulo	return ivp->iv_newstate(vap, nstate, arg);
1990178676Ssam}
1991178676Ssam
1992198429Srpaulo/*
1993198429Srpaulo * Process an RX_PHY firmware notification.  This is usually immediately
1994198429Srpaulo * followed by an MPDU_RX_DONE notification.
1995198429Srpaulo */
1996206477Sbschmidtstatic void
1997198429Srpauloiwn_rx_phy(struct iwn_softc *sc, struct iwn_rx_desc *desc,
1998198429Srpaulo    struct iwn_rx_data *data)
1999178676Ssam{
2000198429Srpaulo	struct iwn_rx_stat *stat = (struct iwn_rx_stat *)(desc + 1);
2001198429Srpaulo
2002198429Srpaulo	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: received PHY stats\n", __func__);
2003202986Srpaulo	bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD);
2004198429Srpaulo
2005198429Srpaulo	/* Save RX statistics, they will be used on MPDU_RX_DONE. */
2006198429Srpaulo	memcpy(&sc->last_rx_stat, stat, sizeof (*stat));
2007198429Srpaulo	sc->last_rx_valid = 1;
2008178676Ssam}
2009178676Ssam
2010178676Ssamstatic void
2011178676Ssamiwn_timer_timeout(void *arg)
2012178676Ssam{
2013178676Ssam	struct iwn_softc *sc = arg;
2014201209Srpaulo	uint32_t flags = 0;
2015178676Ssam
2016178676Ssam	IWN_LOCK_ASSERT(sc);
2017178676Ssam
2018178676Ssam	if (sc->calib_cnt && --sc->calib_cnt == 0) {
2019178676Ssam		DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s\n",
2020178676Ssam		    "send statistics request");
2021201209Srpaulo		(void) iwn_cmd(sc, IWN_CMD_GET_STATISTICS, &flags,
2022201209Srpaulo		    sizeof flags, 1);
2023178676Ssam		sc->calib_cnt = 60;	/* do calibration every 60s */
2024178676Ssam	}
2025178676Ssam	iwn_watchdog(sc);		/* NB: piggyback tx watchdog */
2026178676Ssam	callout_reset(&sc->sc_timer_to, hz, iwn_timer_timeout, sc);
2027178676Ssam}
2028178676Ssam
2029178676Ssamstatic void
2030178676Ssamiwn_calib_reset(struct iwn_softc *sc)
2031178676Ssam{
2032178676Ssam	callout_reset(&sc->sc_timer_to, hz, iwn_timer_timeout, sc);
2033178676Ssam	sc->calib_cnt = 60;		/* do calibration every 60s */
2034178676Ssam}
2035178676Ssam
2036198429Srpaulo/*
2037198429Srpaulo * Process an RX_DONE (4965AGN only) or MPDU_RX_DONE firmware notification.
2038198429Srpaulo * Each MPDU_RX_DONE notification must be preceded by an RX_PHY one.
2039198429Srpaulo */
2040206477Sbschmidtstatic void
2041198429Srpauloiwn_rx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc,
2042178676Ssam    struct iwn_rx_data *data)
2043178676Ssam{
2044198429Srpaulo	const struct iwn_hal *hal = sc->sc_hal;
2045178676Ssam	struct ifnet *ifp = sc->sc_ifp;
2046178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
2047178676Ssam	struct iwn_rx_ring *ring = &sc->rxq;
2048178676Ssam	struct ieee80211_frame *wh;
2049178676Ssam	struct ieee80211_node *ni;
2050198429Srpaulo	struct mbuf *m, *m1;
2051178676Ssam	struct iwn_rx_stat *stat;
2052178676Ssam	caddr_t head;
2053178676Ssam	bus_addr_t paddr;
2054198429Srpaulo	uint32_t flags;
2055198429Srpaulo	int error, len, rssi, nf;
2056178676Ssam
2057198429Srpaulo	if (desc->type == IWN_MPDU_RX_DONE) {
2058198429Srpaulo		/* Check for prior RX_PHY notification. */
2059178676Ssam		if (!sc->last_rx_valid) {
2060178676Ssam			DPRINTF(sc, IWN_DEBUG_ANY,
2061201209Srpaulo			    "%s: missing RX_PHY\n", __func__);
2062178676Ssam			ifp->if_ierrors++;
2063178676Ssam			return;
2064178676Ssam		}
2065178676Ssam		sc->last_rx_valid = 0;
2066178676Ssam		stat = &sc->last_rx_stat;
2067178676Ssam	} else
2068178676Ssam		stat = (struct iwn_rx_stat *)(desc + 1);
2069178676Ssam
2070201209Srpaulo	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD);
2071198439Srpaulo
2072178676Ssam	if (stat->cfg_phy_len > IWN_STAT_MAXLEN) {
2073178676Ssam		device_printf(sc->sc_dev,
2074178676Ssam		    "%s: invalid rx statistic header, len %d\n",
2075178676Ssam		    __func__, stat->cfg_phy_len);
2076178676Ssam		ifp->if_ierrors++;
2077178676Ssam		return;
2078178676Ssam	}
2079198429Srpaulo	if (desc->type == IWN_MPDU_RX_DONE) {
2080198429Srpaulo		struct iwn_rx_mpdu *mpdu = (struct iwn_rx_mpdu *)(desc + 1);
2081198429Srpaulo		head = (caddr_t)(mpdu + 1);
2082198429Srpaulo		len = le16toh(mpdu->len);
2083178676Ssam	} else {
2084178676Ssam		head = (caddr_t)(stat + 1) + stat->cfg_phy_len;
2085178676Ssam		len = le16toh(stat->len);
2086178676Ssam	}
2087178676Ssam
2088198429Srpaulo	flags = le32toh(*(uint32_t *)(head + len));
2089198429Srpaulo
2090198429Srpaulo	/* Discard frames with a bad FCS early. */
2091198429Srpaulo	if ((flags & IWN_RX_NOERROR) != IWN_RX_NOERROR) {
2092178676Ssam		DPRINTF(sc, IWN_DEBUG_RECV, "%s: rx flags error %x\n",
2093198429Srpaulo		    __func__, flags);
2094178676Ssam		ifp->if_ierrors++;
2095178676Ssam		return;
2096178676Ssam	}
2097198429Srpaulo	/* Discard frames that are too short. */
2098198429Srpaulo	if (len < sizeof (*wh)) {
2099178676Ssam		DPRINTF(sc, IWN_DEBUG_RECV, "%s: frame too short: %d\n",
2100178676Ssam		    __func__, len);
2101178676Ssam		ifp->if_ierrors++;
2102178676Ssam		return;
2103178676Ssam	}
2104178676Ssam
2105178676Ssam	/* XXX don't need mbuf, just dma buffer */
2106198429Srpaulo	m1 = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE);
2107198429Srpaulo	if (m1 == NULL) {
2108178676Ssam		DPRINTF(sc, IWN_DEBUG_ANY, "%s: no mbuf to restock ring\n",
2109178676Ssam		    __func__);
2110178676Ssam		ifp->if_ierrors++;
2111178676Ssam		return;
2112178676Ssam	}
2113201209Srpaulo	bus_dmamap_unload(ring->data_dmat, data->map);
2114201209Srpaulo
2115201209Srpaulo	error = bus_dmamap_load(ring->data_dmat, data->map,
2116198429Srpaulo	    mtod(m1, caddr_t), MJUMPAGESIZE,
2117178676Ssam	    iwn_dma_map_addr, &paddr, BUS_DMA_NOWAIT);
2118178676Ssam	if (error != 0 && error != EFBIG) {
2119178676Ssam		device_printf(sc->sc_dev,
2120178676Ssam		    "%s: bus_dmamap_load failed, error %d\n", __func__, error);
2121198429Srpaulo		m_freem(m1);
2122178676Ssam		ifp->if_ierrors++;
2123178676Ssam		return;
2124178676Ssam	}
2125178676Ssam
2126178676Ssam	m = data->m;
2127198429Srpaulo	data->m = m1;
2128198429Srpaulo	/* Update RX descriptor. */
2129198429Srpaulo	ring->desc[ring->cur] = htole32(paddr >> 8);
2130201209Srpaulo	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
2131201209Srpaulo	    BUS_DMASYNC_PREWRITE);
2132198429Srpaulo
2133198429Srpaulo	/* Finalize mbuf. */
2134178676Ssam	m->m_pkthdr.rcvif = ifp;
2135178676Ssam	m->m_data = head;
2136178676Ssam	m->m_pkthdr.len = m->m_len = len;
2137178676Ssam
2138198429Srpaulo	rssi = hal->get_rssi(sc, stat);
2139178676Ssam
2140198429Srpaulo	/* Grab a reference to the source node. */
2141178676Ssam	wh = mtod(m, struct ieee80211_frame *);
2142178676Ssam	ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh);
2143178676Ssam	nf = (ni != NULL && ni->ni_vap->iv_state == IEEE80211_S_RUN &&
2144178676Ssam	    (ic->ic_flags & IEEE80211_F_SCAN) == 0) ? sc->noise : -95;
2145178676Ssam
2146192468Ssam	if (ieee80211_radiotap_active(ic)) {
2147178676Ssam		struct iwn_rx_radiotap_header *tap = &sc->sc_rxtap;
2148178676Ssam
2149192468Ssam		tap->wr_tsft = htole64(stat->tstamp);
2150178676Ssam		tap->wr_flags = 0;
2151201209Srpaulo		if (stat->flags & htole16(IWN_STAT_FLAG_SHPREAMBLE))
2152192468Ssam			tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
2153201209Srpaulo		switch (stat->rate) {
2154201209Srpaulo		/* CCK rates. */
2155201209Srpaulo		case  10: tap->wr_rate =   2; break;
2156201209Srpaulo		case  20: tap->wr_rate =   4; break;
2157201209Srpaulo		case  55: tap->wr_rate =  11; break;
2158201209Srpaulo		case 110: tap->wr_rate =  22; break;
2159201209Srpaulo		/* OFDM rates. */
2160201209Srpaulo		case 0xd: tap->wr_rate =  12; break;
2161201209Srpaulo		case 0xf: tap->wr_rate =  18; break;
2162201209Srpaulo		case 0x5: tap->wr_rate =  24; break;
2163201209Srpaulo		case 0x7: tap->wr_rate =  36; break;
2164201209Srpaulo		case 0x9: tap->wr_rate =  48; break;
2165201209Srpaulo		case 0xb: tap->wr_rate =  72; break;
2166201209Srpaulo		case 0x1: tap->wr_rate =  96; break;
2167201209Srpaulo		case 0x3: tap->wr_rate = 108; break;
2168201209Srpaulo		/* Unknown rate: should not happen. */
2169201209Srpaulo		default:  tap->wr_rate =   0;
2170201209Srpaulo		}
2171178676Ssam		tap->wr_dbm_antsignal = rssi;
2172178676Ssam		tap->wr_dbm_antnoise = nf;
2173178676Ssam	}
2174178676Ssam
2175178676Ssam	IWN_UNLOCK(sc);
2176178676Ssam
2177198429Srpaulo	/* Send the frame to the 802.11 layer. */
2178178676Ssam	if (ni != NULL) {
2179192468Ssam		(void) ieee80211_input(ni, m, rssi - nf, nf);
2180198429Srpaulo		/* Node is no longer needed. */
2181178676Ssam		ieee80211_free_node(ni);
2182178676Ssam	} else
2183192468Ssam		(void) ieee80211_input_all(ic, m, rssi - nf, nf);
2184178676Ssam
2185178676Ssam	IWN_LOCK(sc);
2186178676Ssam}
2187178676Ssam
2188201209Srpaulo#if 0	/* HT */
2189201209Srpaulo/* Process an incoming Compressed BlockAck. */
2190206477Sbschmidtstatic void
2191201209Srpauloiwn_rx_compressed_ba(struct iwn_softc *sc, struct iwn_rx_desc *desc,
2192201209Srpaulo    struct iwn_rx_data *data)
2193201209Srpaulo{
2194201209Srpaulo	struct iwn_compressed_ba *ba = (struct iwn_compressed_ba *)(desc + 1);
2195201209Srpaulo	struct iwn_tx_ring *txq;
2196201209Srpaulo
2197201209Srpaulo	txq = &sc->txq[letoh16(ba->qid)];
2198201209Srpaulo	/* XXX TBD */
2199201209Srpaulo}
2200201209Srpaulo#endif
2201201209Srpaulo
2202198429Srpaulo/*
2203198429Srpaulo * Process an RX_STATISTICS or BEACON_STATISTICS firmware notification.
2204198429Srpaulo * The latter is sent by the firmware after each received beacon.
2205198429Srpaulo */
2206206477Sbschmidtstatic void
2207198429Srpauloiwn_rx_statistics(struct iwn_softc *sc, struct iwn_rx_desc *desc,
2208198429Srpaulo    struct iwn_rx_data *data)
2209198429Srpaulo{
2210198429Srpaulo	const struct iwn_hal *hal = sc->sc_hal;
2211178676Ssam	struct ifnet *ifp = sc->sc_ifp;
2212178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
2213178676Ssam	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
2214178676Ssam	struct iwn_calib_state *calib = &sc->calib;
2215178676Ssam	struct iwn_stats *stats = (struct iwn_stats *)(desc + 1);
2216198429Srpaulo	int temp;
2217178676Ssam
2218198429Srpaulo	/* Beacon stats are meaningful only when associated and not scanning. */
2219178676Ssam	if (vap->iv_state != IEEE80211_S_RUN ||
2220178676Ssam	    (ic->ic_flags & IEEE80211_F_SCAN))
2221178676Ssam		return;
2222178676Ssam
2223202986Srpaulo	bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD);
2224178676Ssam	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: cmd %d\n", __func__, desc->type);
2225198429Srpaulo	iwn_calib_reset(sc);	/* Reset TX power calibration timeout. */
2226178676Ssam
2227198429Srpaulo	/* Test if temperature has changed. */
2228178676Ssam	if (stats->general.temp != sc->rawtemp) {
2229198429Srpaulo		/* Convert "raw" temperature to degC. */
2230178676Ssam		sc->rawtemp = stats->general.temp;
2231198429Srpaulo		temp = hal->get_temperature(sc);
2232178676Ssam		DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: temperature %d\n",
2233178676Ssam		    __func__, temp);
2234178676Ssam
2235198429Srpaulo		/* Update TX power if need be (4965AGN only.) */
2236198429Srpaulo		if (sc->hw_type == IWN_HW_REV_TYPE_4965)
2237198429Srpaulo			iwn4965_power_calibration(sc, temp);
2238178676Ssam	}
2239178676Ssam
2240178676Ssam	if (desc->type != IWN_BEACON_STATISTICS)
2241198429Srpaulo		return;	/* Reply to a statistics request. */
2242178676Ssam
2243178676Ssam	sc->noise = iwn_get_noise(&stats->rx.general);
2244178676Ssam	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: noise %d\n", __func__, sc->noise);
2245178676Ssam
2246198429Srpaulo	/* Test that RSSI and noise are present in stats report. */
2247198429Srpaulo	if (le32toh(stats->rx.general.flags) != 1) {
2248178676Ssam		DPRINTF(sc, IWN_DEBUG_ANY, "%s\n",
2249178676Ssam		    "received statistics without RSSI");
2250178676Ssam		return;
2251178676Ssam	}
2252178676Ssam
2253178676Ssam	if (calib->state == IWN_CALIB_STATE_ASSOC)
2254198429Srpaulo		iwn_collect_noise(sc, &stats->rx.general);
2255178676Ssam	else if (calib->state == IWN_CALIB_STATE_RUN)
2256178676Ssam		iwn_tune_sensitivity(sc, &stats->rx);
2257178676Ssam}
2258178676Ssam
2259198429Srpaulo/*
2260198429Srpaulo * Process a TX_DONE firmware notification.  Unfortunately, the 4965AGN
2261198429Srpaulo * and 5000 adapters have different incompatible TX status formats.
2262198429Srpaulo */
2263206477Sbschmidtstatic void
2264198429Srpauloiwn4965_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc,
2265198429Srpaulo    struct iwn_rx_data *data)
2266178676Ssam{
2267198429Srpaulo	struct iwn4965_tx_stat *stat = (struct iwn4965_tx_stat *)(desc + 1);
2268207001Sbschmidt	struct iwn_tx_ring *ring = &sc->txq[desc->qid & 0xf];
2269198429Srpaulo
2270198429Srpaulo	DPRINTF(sc, IWN_DEBUG_XMIT, "%s: "
2271198429Srpaulo	    "qid %d idx %d retries %d nkill %d rate %x duration %d status %x\n",
2272201209Srpaulo	    __func__, desc->qid, desc->idx, stat->ackfailcnt,
2273201209Srpaulo	    stat->btkillcnt, stat->rate, le16toh(stat->duration),
2274198429Srpaulo	    le32toh(stat->status));
2275201209Srpaulo
2276207001Sbschmidt	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD);
2277201209Srpaulo	iwn_tx_done(sc, desc, stat->ackfailcnt, le32toh(stat->status) & 0xff);
2278198429Srpaulo}
2279198429Srpaulo
2280206477Sbschmidtstatic void
2281198429Srpauloiwn5000_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc,
2282198429Srpaulo    struct iwn_rx_data *data)
2283198429Srpaulo{
2284198429Srpaulo	struct iwn5000_tx_stat *stat = (struct iwn5000_tx_stat *)(desc + 1);
2285207001Sbschmidt	struct iwn_tx_ring *ring = &sc->txq[desc->qid & 0xf];
2286198429Srpaulo
2287198429Srpaulo	DPRINTF(sc, IWN_DEBUG_XMIT, "%s: "
2288198429Srpaulo	    "qid %d idx %d retries %d nkill %d rate %x duration %d status %x\n",
2289201209Srpaulo	    __func__, desc->qid, desc->idx, stat->ackfailcnt,
2290201209Srpaulo	    stat->btkillcnt, stat->rate, le16toh(stat->duration),
2291198429Srpaulo	    le32toh(stat->status));
2292198429Srpaulo
2293201209Srpaulo#ifdef notyet
2294198429Srpaulo	/* Reset TX scheduler slot. */
2295198429Srpaulo	iwn5000_reset_sched(sc, desc->qid & 0xf, desc->idx);
2296201209Srpaulo#endif
2297202986Srpaulo
2298207001Sbschmidt	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD);
2299201209Srpaulo	iwn_tx_done(sc, desc, stat->ackfailcnt, le16toh(stat->status) & 0xff);
2300198429Srpaulo}
2301198429Srpaulo
2302198429Srpaulo/*
2303198429Srpaulo * Adapter-independent backend for TX_DONE firmware notifications.
2304198429Srpaulo */
2305206477Sbschmidtstatic void
2306201209Srpauloiwn_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, int ackfailcnt,
2307198429Srpaulo    uint8_t status)
2308198429Srpaulo{
2309178676Ssam	struct ifnet *ifp = sc->sc_ifp;
2310178676Ssam	struct iwn_tx_ring *ring = &sc->txq[desc->qid & 0xf];
2311178676Ssam	struct iwn_tx_data *data = &ring->data[desc->idx];
2312178676Ssam	struct mbuf *m;
2313178676Ssam	struct ieee80211_node *ni;
2314206358Srpaulo	struct ieee80211vap *vap;
2315178676Ssam
2316178676Ssam	KASSERT(data->ni != NULL, ("no node"));
2317178676Ssam
2318198429Srpaulo	/* Unmap and free mbuf. */
2319201209Srpaulo	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTWRITE);
2320201209Srpaulo	bus_dmamap_unload(ring->data_dmat, data->map);
2321178676Ssam	m = data->m, data->m = NULL;
2322178676Ssam	ni = data->ni, data->ni = NULL;
2323206358Srpaulo	vap = ni->ni_vap;
2324178676Ssam
2325178676Ssam	if (m->m_flags & M_TXCB) {
2326178676Ssam		/*
2327178676Ssam		 * Channels marked for "radar" require traffic to be received
2328178676Ssam		 * to unlock before we can transmit.  Until traffic is seen
2329178676Ssam		 * any attempt to transmit is returned immediately with status
2330178676Ssam		 * set to IWN_TX_FAIL_TX_LOCKED.  Unfortunately this can easily
2331178676Ssam		 * happen on first authenticate after scanning.  To workaround
2332178676Ssam		 * this we ignore a failure of this sort in AUTH state so the
2333178676Ssam		 * 802.11 layer will fall back to using a timeout to wait for
2334178676Ssam		 * the AUTH reply.  This allows the firmware time to see
2335178676Ssam		 * traffic so a subsequent retry of AUTH succeeds.  It's
2336178676Ssam		 * unclear why the firmware does not maintain state for
2337178676Ssam		 * channels recently visited as this would allow immediate
2338178676Ssam		 * use of the channel after a scan (where we see traffic).
2339178676Ssam		 */
2340178676Ssam		if (status == IWN_TX_FAIL_TX_LOCKED &&
2341178676Ssam		    ni->ni_vap->iv_state == IEEE80211_S_AUTH)
2342178676Ssam			ieee80211_process_callback(ni, m, 0);
2343178676Ssam		else
2344178676Ssam			ieee80211_process_callback(ni, m,
2345178676Ssam			    (status & IWN_TX_FAIL) != 0);
2346178676Ssam	}
2347201209Srpaulo
2348201209Srpaulo	/*
2349201209Srpaulo	 * Update rate control statistics for the node.
2350201209Srpaulo	 */
2351201209Srpaulo	if (status & 0x80) {
2352201209Srpaulo		ifp->if_oerrors++;
2353206358Srpaulo		ieee80211_ratectl_tx_complete(vap, ni,
2354206358Srpaulo		    IEEE80211_RATECTL_TX_FAILURE, &ackfailcnt, NULL);
2355201209Srpaulo	} else {
2356206358Srpaulo		ieee80211_ratectl_tx_complete(vap, ni,
2357206358Srpaulo		    IEEE80211_RATECTL_TX_SUCCESS, &ackfailcnt, NULL);
2358201209Srpaulo	}
2359178676Ssam	m_freem(m);
2360178676Ssam	ieee80211_free_node(ni);
2361178676Ssam
2362178676Ssam	sc->sc_tx_timer = 0;
2363198429Srpaulo	if (--ring->queued < IWN_TX_RING_LOMARK) {
2364198429Srpaulo		sc->qfullmsk &= ~(1 << ring->qid);
2365198429Srpaulo		if (sc->qfullmsk == 0 &&
2366198429Srpaulo		    (ifp->if_drv_flags & IFF_DRV_OACTIVE)) {
2367198429Srpaulo			ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
2368198429Srpaulo			iwn_start_locked(ifp);
2369198429Srpaulo		}
2370198429Srpaulo	}
2371178676Ssam}
2372178676Ssam
2373198429Srpaulo/*
2374198429Srpaulo * Process a "command done" firmware notification.  This is where we wakeup
2375198429Srpaulo * processes waiting for a synchronous command completion.
2376198429Srpaulo */
2377206477Sbschmidtstatic void
2378198429Srpauloiwn_cmd_done(struct iwn_softc *sc, struct iwn_rx_desc *desc)
2379178676Ssam{
2380178676Ssam	struct iwn_tx_ring *ring = &sc->txq[4];
2381178676Ssam	struct iwn_tx_data *data;
2382178676Ssam
2383178676Ssam	if ((desc->qid & 0xf) != 4)
2384198429Srpaulo		return;	/* Not a command ack. */
2385178676Ssam
2386178676Ssam	data = &ring->data[desc->idx];
2387178676Ssam
2388198429Srpaulo	/* If the command was mapped in an mbuf, free it. */
2389178676Ssam	if (data->m != NULL) {
2390201209Srpaulo		bus_dmamap_unload(ring->data_dmat, data->map);
2391178676Ssam		m_freem(data->m);
2392178676Ssam		data->m = NULL;
2393178676Ssam	}
2394198439Srpaulo	wakeup(&ring->desc[desc->idx]);
2395178676Ssam}
2396178676Ssam
2397198429Srpaulo/*
2398198429Srpaulo * Process an INT_FH_RX or INT_SW_RX interrupt.
2399198429Srpaulo */
2400206477Sbschmidtstatic void
2401178676Ssamiwn_notif_intr(struct iwn_softc *sc)
2402178676Ssam{
2403178676Ssam	struct ifnet *ifp = sc->sc_ifp;
2404178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
2405178676Ssam	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
2406178676Ssam	uint16_t hw;
2407178676Ssam
2408198429Srpaulo	bus_dmamap_sync(sc->rxq.stat_dma.tag, sc->rxq.stat_dma.map,
2409198429Srpaulo	    BUS_DMASYNC_POSTREAD);
2410198429Srpaulo
2411198429Srpaulo	hw = le16toh(sc->rxq.stat->closed_count) & 0xfff;
2412178676Ssam	while (sc->rxq.cur != hw) {
2413178676Ssam		struct iwn_rx_data *data = &sc->rxq.data[sc->rxq.cur];
2414198429Srpaulo		struct iwn_rx_desc *desc;
2415178676Ssam
2416201209Srpaulo		bus_dmamap_sync(sc->rxq.data_dmat, data->map,
2417201209Srpaulo		    BUS_DMASYNC_POSTREAD);
2418198429Srpaulo		desc = mtod(data->m, struct iwn_rx_desc *);
2419198429Srpaulo
2420178676Ssam		DPRINTF(sc, IWN_DEBUG_RECV,
2421178676Ssam		    "%s: qid %x idx %d flags %x type %d(%s) len %d\n",
2422198439Srpaulo		    __func__, desc->qid & 0xf, desc->idx, desc->flags,
2423178676Ssam		    desc->type, iwn_intr_str(desc->type),
2424178676Ssam		    le16toh(desc->len));
2425178676Ssam
2426198429Srpaulo		if (!(desc->qid & 0x80))	/* Reply to a command. */
2427198429Srpaulo			iwn_cmd_done(sc, desc);
2428178676Ssam
2429178676Ssam		switch (desc->type) {
2430198429Srpaulo		case IWN_RX_PHY:
2431198429Srpaulo			iwn_rx_phy(sc, desc, data);
2432178676Ssam			break;
2433178676Ssam
2434198429Srpaulo		case IWN_RX_DONE:		/* 4965AGN only. */
2435198429Srpaulo		case IWN_MPDU_RX_DONE:
2436198429Srpaulo			/* An 802.11 frame has been received. */
2437198429Srpaulo			iwn_rx_done(sc, desc, data);
2438178676Ssam			break;
2439178676Ssam
2440201209Srpaulo#if 0	/* HT */
2441201209Srpaulo		case IWN_RX_COMPRESSED_BA:
2442201209Srpaulo			/* A Compressed BlockAck has been received. */
2443201209Srpaulo			iwn_rx_compressed_ba(sc, desc, data);
2444201209Srpaulo			break;
2445201209Srpaulo#endif
2446201209Srpaulo
2447178676Ssam		case IWN_TX_DONE:
2448198429Srpaulo			/* An 802.11 frame has been transmitted. */
2449198429Srpaulo			sc->sc_hal->tx_done(sc, desc, data);
2450178676Ssam			break;
2451178676Ssam
2452178676Ssam		case IWN_RX_STATISTICS:
2453178676Ssam		case IWN_BEACON_STATISTICS:
2454198429Srpaulo			iwn_rx_statistics(sc, desc, data);
2455178676Ssam			break;
2456178676Ssam
2457198429Srpaulo		case IWN_BEACON_MISSED:
2458198429Srpaulo		{
2459178676Ssam			struct iwn_beacon_missed *miss =
2460178676Ssam			    (struct iwn_beacon_missed *)(desc + 1);
2461202986Srpaulo			int misses;
2462178676Ssam
2463201209Srpaulo			bus_dmamap_sync(sc->rxq.data_dmat, data->map,
2464201209Srpaulo			    BUS_DMASYNC_POSTREAD);
2465202986Srpaulo			misses = le32toh(miss->consecutive);
2466201209Srpaulo
2467178676Ssam			DPRINTF(sc, IWN_DEBUG_STATE,
2468178676Ssam			    "%s: beacons missed %d/%d\n", __func__,
2469178676Ssam			    misses, le32toh(miss->total));
2470178676Ssam			/*
2471178676Ssam			 * If more than 5 consecutive beacons are missed,
2472178676Ssam			 * reinitialize the sensitivity state machine.
2473178676Ssam			 */
2474220660Sbschmidt			if (vap->iv_state == IEEE80211_S_RUN &&
2475220660Sbschmidt			    (ic->ic_flags & IEEE80211_F_SCAN) != 0) {
2476220660Sbschmidt				if (misses > 5)
2477220660Sbschmidt					(void)iwn_init_sensitivity(sc);
2478220660Sbschmidt				if (misses >= vap->iv_bmissthreshold) {
2479220660Sbschmidt					IWN_UNLOCK(sc);
2480220660Sbschmidt					ieee80211_beacon_miss(ic);
2481220660Sbschmidt					IWN_LOCK(sc);
2482220660Sbschmidt				}
2483201209Srpaulo			}
2484178676Ssam			break;
2485178676Ssam		}
2486198429Srpaulo		case IWN_UC_READY:
2487198429Srpaulo		{
2488178676Ssam			struct iwn_ucode_info *uc =
2489178676Ssam			    (struct iwn_ucode_info *)(desc + 1);
2490178676Ssam
2491198429Srpaulo			/* The microcontroller is ready. */
2492201209Srpaulo			bus_dmamap_sync(sc->rxq.data_dmat, data->map,
2493201209Srpaulo			    BUS_DMASYNC_POSTREAD);
2494178676Ssam			DPRINTF(sc, IWN_DEBUG_RESET,
2495178676Ssam			    "microcode alive notification version=%d.%d "
2496178676Ssam			    "subtype=%x alive=%x\n", uc->major, uc->minor,
2497178676Ssam			    uc->subtype, le32toh(uc->valid));
2498178676Ssam
2499178676Ssam			if (le32toh(uc->valid) != 1) {
2500178676Ssam				device_printf(sc->sc_dev,
2501198429Srpaulo				    "microcontroller initialization failed");
2502178676Ssam				break;
2503178676Ssam			}
2504178676Ssam			if (uc->subtype == IWN_UCODE_INIT) {
2505201209Srpaulo				/* Save microcontroller report. */
2506178676Ssam				memcpy(&sc->ucode_info, uc, sizeof (*uc));
2507178676Ssam			}
2508198429Srpaulo			/* Save the address of the error log in SRAM. */
2509198429Srpaulo			sc->errptr = le32toh(uc->errptr);
2510178676Ssam			break;
2511178676Ssam		}
2512198429Srpaulo		case IWN_STATE_CHANGED:
2513198429Srpaulo		{
2514178676Ssam			uint32_t *status = (uint32_t *)(desc + 1);
2515178676Ssam
2516178676Ssam			/*
2517178676Ssam			 * State change allows hardware switch change to be
2518178676Ssam			 * noted. However, we handle this in iwn_intr as we
2519178676Ssam			 * get both the enable/disble intr.
2520178676Ssam			 */
2521201209Srpaulo			bus_dmamap_sync(sc->rxq.data_dmat, data->map,
2522201209Srpaulo			    BUS_DMASYNC_POSTREAD);
2523178676Ssam			DPRINTF(sc, IWN_DEBUG_INTR, "state changed to %x\n",
2524178676Ssam			    le32toh(*status));
2525178676Ssam			break;
2526178676Ssam		}
2527198429Srpaulo		case IWN_START_SCAN:
2528198429Srpaulo		{
2529178676Ssam			struct iwn_start_scan *scan =
2530178676Ssam			    (struct iwn_start_scan *)(desc + 1);
2531178676Ssam
2532201209Srpaulo			bus_dmamap_sync(sc->rxq.data_dmat, data->map,
2533201209Srpaulo			    BUS_DMASYNC_POSTREAD);
2534178676Ssam			DPRINTF(sc, IWN_DEBUG_ANY,
2535178676Ssam			    "%s: scanning channel %d status %x\n",
2536178676Ssam			    __func__, scan->chan, le32toh(scan->status));
2537178676Ssam			break;
2538178676Ssam		}
2539198429Srpaulo		case IWN_STOP_SCAN:
2540198429Srpaulo		{
2541178676Ssam			struct iwn_stop_scan *scan =
2542178676Ssam			    (struct iwn_stop_scan *)(desc + 1);
2543178676Ssam
2544201209Srpaulo			bus_dmamap_sync(sc->rxq.data_dmat, data->map,
2545201209Srpaulo			    BUS_DMASYNC_POSTREAD);
2546178676Ssam			DPRINTF(sc, IWN_DEBUG_STATE,
2547178676Ssam			    "scan finished nchan=%d status=%d chan=%d\n",
2548178676Ssam			    scan->nchan, scan->status, scan->chan);
2549178676Ssam
2550201209Srpaulo			IWN_UNLOCK(sc);
2551191746Sthompsa			ieee80211_scan_next(vap);
2552201209Srpaulo			IWN_LOCK(sc);
2553178676Ssam			break;
2554178676Ssam		}
2555198429Srpaulo		case IWN5000_CALIBRATION_RESULT:
2556212854Sbschmidt			iwn5000_rx_calib_result(sc, desc, data);
2557198429Srpaulo			break;
2558198429Srpaulo
2559198429Srpaulo		case IWN5000_CALIBRATION_DONE:
2560201209Srpaulo			sc->sc_flags |= IWN_FLAG_CALIB_DONE;
2561198429Srpaulo			wakeup(sc);
2562198429Srpaulo			break;
2563178676Ssam		}
2564198429Srpaulo
2565178676Ssam		sc->rxq.cur = (sc->rxq.cur + 1) % IWN_RX_RING_COUNT;
2566178676Ssam	}
2567178676Ssam
2568198429Srpaulo	/* Tell the firmware what we have processed. */
2569178676Ssam	hw = (hw == 0) ? IWN_RX_RING_COUNT - 1 : hw - 1;
2570198429Srpaulo	IWN_WRITE(sc, IWN_FH_RX_WPTR, hw & ~7);
2571178676Ssam}
2572178676Ssam
2573198429Srpaulo/*
2574198429Srpaulo * Process an INT_WAKEUP interrupt raised when the microcontroller wakes up
2575198429Srpaulo * from power-down sleep mode.
2576198429Srpaulo */
2577206477Sbschmidtstatic void
2578198429Srpauloiwn_wakeup_intr(struct iwn_softc *sc)
2579198429Srpaulo{
2580198429Srpaulo	int qid;
2581198429Srpaulo
2582198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "%s: ucode wakeup from power-down sleep\n",
2583198429Srpaulo	    __func__);
2584198429Srpaulo
2585198429Srpaulo	/* Wakeup RX and TX rings. */
2586198429Srpaulo	IWN_WRITE(sc, IWN_FH_RX_WPTR, sc->rxq.cur & ~7);
2587201209Srpaulo	for (qid = 0; qid < sc->sc_hal->ntxqs; qid++) {
2588198429Srpaulo		struct iwn_tx_ring *ring = &sc->txq[qid];
2589198429Srpaulo		IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | ring->cur);
2590198429Srpaulo	}
2591198429Srpaulo}
2592198429Srpaulo
2593206477Sbschmidtstatic void
2594191746Sthompsaiwn_rftoggle_intr(struct iwn_softc *sc)
2595191746Sthompsa{
2596191746Sthompsa	struct ifnet *ifp = sc->sc_ifp;
2597191746Sthompsa	struct ieee80211com *ic = ifp->if_l2com;
2598198429Srpaulo	uint32_t tmp = IWN_READ(sc, IWN_GP_CNTRL);
2599191746Sthompsa
2600191746Sthompsa	IWN_LOCK_ASSERT(sc);
2601191746Sthompsa
2602191746Sthompsa	device_printf(sc->sc_dev, "RF switch: radio %s\n",
2603198429Srpaulo	    (tmp & IWN_GP_CNTRL_RFKILL) ? "enabled" : "disabled");
2604198429Srpaulo	if (tmp & IWN_GP_CNTRL_RFKILL)
2605191746Sthompsa		ieee80211_runtask(ic, &sc->sc_radioon_task);
2606191746Sthompsa	else
2607191746Sthompsa		ieee80211_runtask(ic, &sc->sc_radiooff_task);
2608191746Sthompsa}
2609191746Sthompsa
2610198429Srpaulo/*
2611198429Srpaulo * Dump the error log of the firmware when a firmware panic occurs.  Although
2612198429Srpaulo * we can't debug the firmware because it is neither open source nor free, it
2613198429Srpaulo * can help us to identify certain classes of problems.
2614198429Srpaulo */
2615206477Sbschmidtstatic void
2616201209Srpauloiwn_fatal_intr(struct iwn_softc *sc)
2617191746Sthompsa{
2618198429Srpaulo	const struct iwn_hal *hal = sc->sc_hal;
2619198429Srpaulo	struct iwn_fw_dump dump;
2620198429Srpaulo	int i;
2621191746Sthompsa
2622191746Sthompsa	IWN_LOCK_ASSERT(sc);
2623191746Sthompsa
2624201209Srpaulo	/* Force a complete recalibration on next init. */
2625201209Srpaulo	sc->sc_flags &= ~IWN_FLAG_CALIB_DONE;
2626201209Srpaulo
2627198429Srpaulo	/* Check that the error log address is valid. */
2628198429Srpaulo	if (sc->errptr < IWN_FW_DATA_BASE ||
2629198429Srpaulo	    sc->errptr + sizeof (dump) >
2630198429Srpaulo	    IWN_FW_DATA_BASE + hal->fw_data_maxsz) {
2631198429Srpaulo		printf("%s: bad firmware error log address 0x%08x\n",
2632198429Srpaulo		    __func__, sc->errptr);
2633198429Srpaulo		return;
2634198429Srpaulo	}
2635198429Srpaulo	if (iwn_nic_lock(sc) != 0) {
2636198429Srpaulo		printf("%s: could not read firmware error log\n",
2637198429Srpaulo		    __func__);
2638198429Srpaulo		return;
2639198429Srpaulo	}
2640198429Srpaulo	/* Read firmware error log from SRAM. */
2641198429Srpaulo	iwn_mem_read_region_4(sc, sc->errptr, (uint32_t *)&dump,
2642198429Srpaulo	    sizeof (dump) / sizeof (uint32_t));
2643198429Srpaulo	iwn_nic_unlock(sc);
2644198429Srpaulo
2645198429Srpaulo	if (dump.valid == 0) {
2646198429Srpaulo		printf("%s: firmware error log is empty\n",
2647198429Srpaulo		    __func__);
2648198429Srpaulo		return;
2649198429Srpaulo	}
2650198429Srpaulo	printf("firmware error log:\n");
2651198429Srpaulo	printf("  error type      = \"%s\" (0x%08X)\n",
2652198429Srpaulo	    (dump.id < nitems(iwn_fw_errmsg)) ?
2653198429Srpaulo		iwn_fw_errmsg[dump.id] : "UNKNOWN",
2654198429Srpaulo	    dump.id);
2655198429Srpaulo	printf("  program counter = 0x%08X\n", dump.pc);
2656198429Srpaulo	printf("  source line     = 0x%08X\n", dump.src_line);
2657198429Srpaulo	printf("  error data      = 0x%08X%08X\n",
2658198429Srpaulo	    dump.error_data[0], dump.error_data[1]);
2659198429Srpaulo	printf("  branch link     = 0x%08X%08X\n",
2660198429Srpaulo	    dump.branch_link[0], dump.branch_link[1]);
2661198429Srpaulo	printf("  interrupt link  = 0x%08X%08X\n",
2662198429Srpaulo	    dump.interrupt_link[0], dump.interrupt_link[1]);
2663198429Srpaulo	printf("  time            = %u\n", dump.time[0]);
2664198429Srpaulo
2665198429Srpaulo	/* Dump driver status (TX and RX rings) while we're here. */
2666198429Srpaulo	printf("driver status:\n");
2667198429Srpaulo	for (i = 0; i < hal->ntxqs; i++) {
2668198429Srpaulo		struct iwn_tx_ring *ring = &sc->txq[i];
2669198429Srpaulo		printf("  tx ring %2d: qid=%-2d cur=%-3d queued=%-3d\n",
2670198429Srpaulo		    i, ring->qid, ring->cur, ring->queued);
2671198429Srpaulo	}
2672198429Srpaulo	printf("  rx ring: cur=%d\n", sc->rxq.cur);
2673191746Sthompsa}
2674191746Sthompsa
2675206477Sbschmidtstatic void
2676178676Ssamiwn_intr(void *arg)
2677178676Ssam{
2678178676Ssam	struct iwn_softc *sc = arg;
2679198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
2680201209Srpaulo	uint32_t r1, r2, tmp;
2681178676Ssam
2682178676Ssam	IWN_LOCK(sc);
2683178676Ssam
2684198429Srpaulo	/* Disable interrupts. */
2685201209Srpaulo	IWN_WRITE(sc, IWN_INT_MASK, 0);
2686178676Ssam
2687201209Srpaulo	/* Read interrupts from ICT (fast) or from registers (slow). */
2688201209Srpaulo	if (sc->sc_flags & IWN_FLAG_USE_ICT) {
2689201209Srpaulo		tmp = 0;
2690201209Srpaulo		while (sc->ict[sc->ict_cur] != 0) {
2691201209Srpaulo			tmp |= sc->ict[sc->ict_cur];
2692201209Srpaulo			sc->ict[sc->ict_cur] = 0;	/* Acknowledge. */
2693201209Srpaulo			sc->ict_cur = (sc->ict_cur + 1) % IWN_ICT_COUNT;
2694201209Srpaulo		}
2695201209Srpaulo		tmp = le32toh(tmp);
2696206444Sbschmidt		if (tmp == 0xffffffff)	/* Shouldn't happen. */
2697206444Sbschmidt			tmp = 0;
2698206444Sbschmidt		else if (tmp & 0xc0000)	/* Workaround a HW bug. */
2699206444Sbschmidt			tmp |= 0x8000;
2700201209Srpaulo		r1 = (tmp & 0xff00) << 16 | (tmp & 0xff);
2701201209Srpaulo		r2 = 0;	/* Unused. */
2702201209Srpaulo	} else {
2703201209Srpaulo		r1 = IWN_READ(sc, IWN_INT);
2704201209Srpaulo		if (r1 == 0xffffffff || (r1 & 0xfffffff0) == 0xa5a5a5a0)
2705201209Srpaulo			return;	/* Hardware gone! */
2706201209Srpaulo		r2 = IWN_READ(sc, IWN_FH_INT);
2707201209Srpaulo	}
2708178676Ssam
2709198429Srpaulo	DPRINTF(sc, IWN_DEBUG_INTR, "interrupt reg1=%x reg2=%x\n", r1, r2);
2710198429Srpaulo
2711201209Srpaulo	if (r1 == 0 && r2 == 0)
2712198429Srpaulo		goto done;	/* Interrupt not for us. */
2713178676Ssam
2714198429Srpaulo	/* Acknowledge interrupts. */
2715198429Srpaulo	IWN_WRITE(sc, IWN_INT, r1);
2716201209Srpaulo	if (!(sc->sc_flags & IWN_FLAG_USE_ICT))
2717201209Srpaulo		IWN_WRITE(sc, IWN_FH_INT, r2);
2718178676Ssam
2719198429Srpaulo	if (r1 & IWN_INT_RF_TOGGLED) {
2720191746Sthompsa		iwn_rftoggle_intr(sc);
2721201209Srpaulo		goto done;
2722198429Srpaulo	}
2723198429Srpaulo	if (r1 & IWN_INT_CT_REACHED) {
2724198429Srpaulo		device_printf(sc->sc_dev, "%s: critical temperature reached!\n",
2725198429Srpaulo		    __func__);
2726198429Srpaulo	}
2727198429Srpaulo	if (r1 & (IWN_INT_SW_ERR | IWN_INT_HW_ERR)) {
2728201209Srpaulo		iwn_fatal_intr(sc);
2729201209Srpaulo		ifp->if_flags &= ~IFF_UP;
2730201209Srpaulo		iwn_stop_locked(sc);
2731178676Ssam		goto done;
2732178676Ssam	}
2733201209Srpaulo	if ((r1 & (IWN_INT_FH_RX | IWN_INT_SW_RX | IWN_INT_RX_PERIODIC)) ||
2734201209Srpaulo	    (r2 & IWN_FH_INT_RX)) {
2735201209Srpaulo		if (sc->sc_flags & IWN_FLAG_USE_ICT) {
2736201209Srpaulo			if (r1 & (IWN_INT_FH_RX | IWN_INT_SW_RX))
2737201209Srpaulo				IWN_WRITE(sc, IWN_FH_INT, IWN_FH_INT_RX);
2738201209Srpaulo			IWN_WRITE_1(sc, IWN_INT_PERIODIC,
2739201209Srpaulo			    IWN_INT_PERIODIC_DIS);
2740201209Srpaulo			iwn_notif_intr(sc);
2741201209Srpaulo			if (r1 & (IWN_INT_FH_RX | IWN_INT_SW_RX)) {
2742201209Srpaulo				IWN_WRITE_1(sc, IWN_INT_PERIODIC,
2743201209Srpaulo				    IWN_INT_PERIODIC_ENA);
2744201209Srpaulo			}
2745201209Srpaulo		} else
2746201209Srpaulo			iwn_notif_intr(sc);
2747201209Srpaulo	}
2748178676Ssam
2749201209Srpaulo	if ((r1 & IWN_INT_FH_TX) || (r2 & IWN_FH_INT_TX)) {
2750201209Srpaulo		if (sc->sc_flags & IWN_FLAG_USE_ICT)
2751201209Srpaulo			IWN_WRITE(sc, IWN_FH_INT, IWN_FH_INT_TX);
2752198429Srpaulo		wakeup(sc);	/* FH DMA transfer completed. */
2753201209Srpaulo	}
2754198429Srpaulo
2755198429Srpaulo	if (r1 & IWN_INT_ALIVE)
2756198429Srpaulo		wakeup(sc);	/* Firmware is alive. */
2757198429Srpaulo
2758198429Srpaulo	if (r1 & IWN_INT_WAKEUP)
2759198429Srpaulo		iwn_wakeup_intr(sc);
2760198429Srpaulo
2761201209Srpaulodone:
2762198429Srpaulo	/* Re-enable interrupts. */
2763201209Srpaulo	if (ifp->if_flags & IFF_UP)
2764201209Srpaulo		IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask);
2765198429Srpaulo
2766178676Ssam	IWN_UNLOCK(sc);
2767178676Ssam}
2768178676Ssam
2769198429Srpaulo/*
2770198429Srpaulo * Update TX scheduler ring when transmitting an 802.11 frame (4965AGN and
2771198429Srpaulo * 5000 adapters use a slightly different format.)
2772198429Srpaulo */
2773206477Sbschmidtstatic void
2774198429Srpauloiwn4965_update_sched(struct iwn_softc *sc, int qid, int idx, uint8_t id,
2775198429Srpaulo    uint16_t len)
2776178676Ssam{
2777198429Srpaulo	uint16_t *w = &sc->sched[qid * IWN4965_SCHED_COUNT + idx];
2778178676Ssam
2779198429Srpaulo	*w = htole16(len + 8);
2780198439Srpaulo	bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map,
2781198439Srpaulo	    BUS_DMASYNC_PREWRITE);
2782201209Srpaulo	if (idx < IWN_SCHED_WINSZ) {
2783198429Srpaulo		*(w + IWN_TX_RING_COUNT) = *w;
2784198439Srpaulo		bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map,
2785198439Srpaulo		    BUS_DMASYNC_PREWRITE);
2786198439Srpaulo	}
2787198429Srpaulo}
2788198429Srpaulo
2789206477Sbschmidtstatic void
2790198429Srpauloiwn5000_update_sched(struct iwn_softc *sc, int qid, int idx, uint8_t id,
2791198429Srpaulo    uint16_t len)
2792198429Srpaulo{
2793198429Srpaulo	uint16_t *w = &sc->sched[qid * IWN5000_SCHED_COUNT + idx];
2794198429Srpaulo
2795198429Srpaulo	*w = htole16(id << 12 | (len + 8));
2796198439Srpaulo
2797198439Srpaulo	bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map,
2798198439Srpaulo	    BUS_DMASYNC_PREWRITE);
2799198439Srpaulo	if (idx < IWN_SCHED_WINSZ) {
2800198429Srpaulo		*(w + IWN_TX_RING_COUNT) = *w;
2801198439Srpaulo		bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map,
2802198439Srpaulo		    BUS_DMASYNC_PREWRITE);
2803198439Srpaulo	}
2804198429Srpaulo}
2805198429Srpaulo
2806206475Sbschmidt#ifdef notyet
2807206477Sbschmidtstatic void
2808198429Srpauloiwn5000_reset_sched(struct iwn_softc *sc, int qid, int idx)
2809198429Srpaulo{
2810198429Srpaulo	uint16_t *w = &sc->sched[qid * IWN5000_SCHED_COUNT + idx];
2811198429Srpaulo
2812198429Srpaulo	*w = (*w & htole16(0xf000)) | htole16(1);
2813198439Srpaulo	bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map,
2814198439Srpaulo	    BUS_DMASYNC_PREWRITE);
2815198439Srpaulo	if (idx < IWN_SCHED_WINSZ) {
2816198429Srpaulo		*(w + IWN_TX_RING_COUNT) = *w;
2817198439Srpaulo		bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map,
2818206443Sbschmidt		    BUS_DMASYNC_PREWRITE);
2819198439Srpaulo	}
2820198429Srpaulo}
2821206475Sbschmidt#endif
2822198429Srpaulo
2823201209Srpaulostatic uint8_t
2824198429Srpauloiwn_plcp_signal(int rate) {
2825198429Srpaulo	int i;
2826198429Srpaulo
2827198429Srpaulo	for (i = 0; i < IWN_RIDX_MAX + 1; i++) {
2828198429Srpaulo		if (rate == iwn_rates[i].rate)
2829201209Srpaulo			return i;
2830178676Ssam	}
2831198429Srpaulo
2832201209Srpaulo	return 0;
2833178676Ssam}
2834178676Ssam
2835206477Sbschmidtstatic int
2836198429Srpauloiwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni,
2837178676Ssam    struct iwn_tx_ring *ring)
2838178676Ssam{
2839198429Srpaulo	const struct iwn_hal *hal = sc->sc_hal;
2840198429Srpaulo	const struct ieee80211_txparam *tp;
2841198429Srpaulo	const struct iwn_rate *rinfo;
2842178676Ssam	struct ieee80211vap *vap = ni->ni_vap;
2843178676Ssam	struct ieee80211com *ic = ni->ni_ic;
2844198429Srpaulo	struct iwn_node *wn = (void *)ni;
2845178676Ssam	struct iwn_tx_desc *desc;
2846178676Ssam	struct iwn_tx_data *data;
2847178676Ssam	struct iwn_tx_cmd *cmd;
2848178676Ssam	struct iwn_cmd_data *tx;
2849178676Ssam	struct ieee80211_frame *wh;
2850198429Srpaulo	struct ieee80211_key *k = NULL;
2851198429Srpaulo	struct mbuf *mnew;
2852198429Srpaulo	bus_dma_segment_t segs[IWN_MAX_SCATTER];
2853178676Ssam	uint32_t flags;
2854178676Ssam	u_int hdrlen;
2855201209Srpaulo	int totlen, error, pad, nsegs = 0, i, rate;
2856201209Srpaulo	uint8_t ridx, type, txant;
2857178676Ssam
2858178676Ssam	IWN_LOCK_ASSERT(sc);
2859178676Ssam
2860198429Srpaulo	wh = mtod(m, struct ieee80211_frame *);
2861198429Srpaulo	hdrlen = ieee80211_anyhdrsize(wh);
2862178676Ssam	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
2863178676Ssam
2864198429Srpaulo	desc = &ring->desc[ring->cur];
2865198429Srpaulo	data = &ring->data[ring->cur];
2866198429Srpaulo
2867198429Srpaulo	/* Choose a TX rate index. */
2868201209Srpaulo	tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)];
2869178676Ssam	if (type == IEEE80211_FC0_TYPE_MGT)
2870178676Ssam		rate = tp->mgmtrate;
2871198429Srpaulo	else if (IEEE80211_IS_MULTICAST(wh->i_addr1))
2872178676Ssam		rate = tp->mcastrate;
2873178676Ssam	else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE)
2874178676Ssam		rate = tp->ucastrate;
2875178676Ssam	else {
2876206358Srpaulo		/* XXX pass pktlen */
2877206358Srpaulo		(void) ieee80211_ratectl_rate(ni, NULL, 0);
2878178676Ssam		rate = ni->ni_txrate;
2879178676Ssam	}
2880201209Srpaulo	ridx = iwn_plcp_signal(rate);
2881201209Srpaulo	rinfo = &iwn_rates[ridx];
2882178676Ssam
2883198429Srpaulo	/* Encrypt the frame if need be. */
2884178676Ssam	if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
2885198429Srpaulo		k = ieee80211_crypto_encap(ni, m);
2886178676Ssam		if (k == NULL) {
2887198429Srpaulo			m_freem(m);
2888178676Ssam			return ENOBUFS;
2889178676Ssam		}
2890198429Srpaulo		/* Packet header may have moved, reset our local pointer. */
2891198429Srpaulo		wh = mtod(m, struct ieee80211_frame *);
2892198429Srpaulo	}
2893198429Srpaulo	totlen = m->m_pkthdr.len;
2894178676Ssam
2895192468Ssam	if (ieee80211_radiotap_active_vap(vap)) {
2896178676Ssam		struct iwn_tx_radiotap_header *tap = &sc->sc_txtap;
2897178676Ssam
2898178676Ssam		tap->wt_flags = 0;
2899201209Srpaulo		tap->wt_rate = rinfo->rate;
2900178676Ssam		if (k != NULL)
2901178676Ssam			tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP;
2902178676Ssam
2903198429Srpaulo		ieee80211_radiotap_tx(vap, m);
2904178676Ssam	}
2905178676Ssam
2906198429Srpaulo	/* Prepare TX firmware command. */
2907198429Srpaulo	cmd = &ring->cmd[ring->cur];
2908198429Srpaulo	cmd->code = IWN_CMD_TX_DATA;
2909198429Srpaulo	cmd->flags = 0;
2910198429Srpaulo	cmd->qid = ring->qid;
2911198429Srpaulo	cmd->idx = ring->cur;
2912198429Srpaulo
2913198429Srpaulo	tx = (struct iwn_cmd_data *)cmd->data;
2914198429Srpaulo	/* NB: No need to clear tx, all fields are reinitialized here. */
2915198429Srpaulo	tx->scratch = 0;	/* clear "scratch" area */
2916198429Srpaulo
2917198429Srpaulo	flags = 0;
2918198429Srpaulo	if (!IEEE80211_IS_MULTICAST(wh->i_addr1))
2919178676Ssam		flags |= IWN_TX_NEED_ACK;
2920198429Srpaulo	if ((wh->i_fc[0] &
2921198429Srpaulo	    (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) ==
2922198429Srpaulo	    (IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_BAR))
2923198429Srpaulo		flags |= IWN_TX_IMM_BA;		/* Cannot happen yet. */
2924178676Ssam
2925198429Srpaulo	if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG)
2926198429Srpaulo		flags |= IWN_TX_MORE_FRAG;	/* Cannot happen yet. */
2927178676Ssam
2928198429Srpaulo	/* Check if frame must be protected using RTS/CTS or CTS-to-self. */
2929198429Srpaulo	if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
2930198429Srpaulo		/* NB: Group frames are sent using CCK in 802.11b/g. */
2931198429Srpaulo		if (totlen + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) {
2932198429Srpaulo			flags |= IWN_TX_NEED_RTS;
2933178676Ssam		} else if ((ic->ic_flags & IEEE80211_F_USEPROT) &&
2934201209Srpaulo		    ridx >= IWN_RIDX_OFDM6) {
2935178676Ssam			if (ic->ic_protmode == IEEE80211_PROT_CTSONLY)
2936198429Srpaulo				flags |= IWN_TX_NEED_CTS;
2937178676Ssam			else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS)
2938198429Srpaulo				flags |= IWN_TX_NEED_RTS;
2939178676Ssam		}
2940198429Srpaulo		if (flags & (IWN_TX_NEED_RTS | IWN_TX_NEED_CTS)) {
2941198429Srpaulo			if (sc->hw_type != IWN_HW_REV_TYPE_4965) {
2942198429Srpaulo				/* 5000 autoselects RTS/CTS or CTS-to-self. */
2943198429Srpaulo				flags &= ~(IWN_TX_NEED_RTS | IWN_TX_NEED_CTS);
2944198429Srpaulo				flags |= IWN_TX_NEED_PROTECTION;
2945198429Srpaulo			} else
2946198429Srpaulo				flags |= IWN_TX_FULL_TXOP;
2947198429Srpaulo		}
2948201209Srpaulo	}
2949178676Ssam
2950198429Srpaulo	if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
2951198429Srpaulo	    type != IEEE80211_FC0_TYPE_DATA)
2952198429Srpaulo		tx->id = hal->broadcast_id;
2953198429Srpaulo	else
2954198429Srpaulo		tx->id = wn->id;
2955198429Srpaulo
2956178676Ssam	if (type == IEEE80211_FC0_TYPE_MGT) {
2957178676Ssam		uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
2958178676Ssam
2959198429Srpaulo		/* Tell HW to set timestamp in probe responses. */
2960178676Ssam		if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
2961178676Ssam			flags |= IWN_TX_INSERT_TSTAMP;
2962178676Ssam
2963178676Ssam		if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ ||
2964178676Ssam		    subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ)
2965198429Srpaulo			tx->timeout = htole16(3);
2966178676Ssam		else
2967198429Srpaulo			tx->timeout = htole16(2);
2968178676Ssam	} else
2969198429Srpaulo		tx->timeout = htole16(0);
2970178676Ssam
2971178676Ssam	if (hdrlen & 3) {
2972201209Srpaulo		/* First segment length must be a multiple of 4. */
2973178676Ssam		flags |= IWN_TX_NEED_PADDING;
2974178676Ssam		pad = 4 - (hdrlen & 3);
2975178676Ssam	} else
2976178676Ssam		pad = 0;
2977178676Ssam
2978198429Srpaulo	tx->len = htole16(totlen);
2979198429Srpaulo	tx->tid = 0;
2980201209Srpaulo	tx->rts_ntries = 60;
2981201209Srpaulo	tx->data_ntries = 15;
2982178676Ssam	tx->lifetime = htole32(IWN_LIFETIME_INFINITE);
2983198429Srpaulo	tx->plcp = rinfo->plcp;
2984198429Srpaulo	tx->rflags = rinfo->flags;
2985198429Srpaulo	if (tx->id == hal->broadcast_id) {
2986201209Srpaulo		/* Group or management frame. */
2987201209Srpaulo		tx->linkq = 0;
2988198429Srpaulo		/* XXX Alternate between antenna A and B? */
2989201209Srpaulo		txant = IWN_LSB(sc->txchainmask);
2990198429Srpaulo		tx->rflags |= IWN_RFLAG_ANT(txant);
2991201209Srpaulo	} else {
2992209169Sbschmidt		tx->linkq = IWN_RIDX_OFDM54 - ridx;
2993201209Srpaulo		flags |= IWN_TX_LINKQ;	/* enable MRR */
2994201209Srpaulo	}
2995178676Ssam
2996198429Srpaulo	/* Set physical address of "scratch area". */
2997201209Srpaulo	tx->loaddr = htole32(IWN_LOADDR(data->scratch_paddr));
2998201209Srpaulo	tx->hiaddr = IWN_HIADDR(data->scratch_paddr);
2999178676Ssam
3000198429Srpaulo	/* Copy 802.11 header in TX command. */
3001178676Ssam	memcpy((uint8_t *)(tx + 1), wh, hdrlen);
3002178676Ssam
3003198429Srpaulo	/* Trim 802.11 header. */
3004198429Srpaulo	m_adj(m, hdrlen);
3005198429Srpaulo	tx->security = 0;
3006198429Srpaulo	tx->flags = htole32(flags);
3007198429Srpaulo
3008201209Srpaulo	if (m->m_len > 0) {
3009201209Srpaulo		error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map,
3010201209Srpaulo		    m, segs, &nsegs, BUS_DMA_NOWAIT);
3011178676Ssam		if (error == EFBIG) {
3012178676Ssam			/* too many fragments, linearize */
3013198429Srpaulo			mnew = m_collapse(m, M_DONTWAIT, IWN_MAX_SCATTER);
3014178676Ssam			if (mnew == NULL) {
3015178676Ssam				device_printf(sc->sc_dev,
3016178676Ssam				    "%s: could not defrag mbuf\n", __func__);
3017198429Srpaulo				m_freem(m);
3018178676Ssam				return ENOBUFS;
3019178676Ssam			}
3020198429Srpaulo			m = mnew;
3021201209Srpaulo			error = bus_dmamap_load_mbuf_sg(ring->data_dmat,
3022198429Srpaulo			    data->map, m, segs, &nsegs, BUS_DMA_NOWAIT);
3023178676Ssam		}
3024178676Ssam		if (error != 0) {
3025178676Ssam			device_printf(sc->sc_dev,
3026178676Ssam			    "%s: bus_dmamap_load_mbuf_sg failed, error %d\n",
3027206443Sbschmidt			    __func__, error);
3028198429Srpaulo			m_freem(m);
3029178676Ssam			return error;
3030178676Ssam		}
3031178676Ssam	}
3032178676Ssam
3033198429Srpaulo	data->m = m;
3034178676Ssam	data->ni = ni;
3035178676Ssam
3036178676Ssam	DPRINTF(sc, IWN_DEBUG_XMIT, "%s: qid %d idx %d len %d nsegs %d\n",
3037198429Srpaulo	    __func__, ring->qid, ring->cur, m->m_pkthdr.len, nsegs);
3038178676Ssam
3039198429Srpaulo	/* Fill TX descriptor. */
3040198429Srpaulo	desc->nsegs = 1 + nsegs;
3041198429Srpaulo	/* First DMA segment is used by the TX command. */
3042201209Srpaulo	desc->segs[0].addr = htole32(IWN_LOADDR(data->cmd_paddr));
3043201209Srpaulo	desc->segs[0].len  = htole16(IWN_HIADDR(data->cmd_paddr) |
3044198429Srpaulo	    (4 + sizeof (*tx) + hdrlen + pad) << 4);
3045198429Srpaulo	/* Other DMA segments are for data payload. */
3046178676Ssam	for (i = 1; i <= nsegs; i++) {
3047198429Srpaulo		desc->segs[i].addr = htole32(IWN_LOADDR(segs[i - 1].ds_addr));
3048198429Srpaulo		desc->segs[i].len  = htole16(IWN_HIADDR(segs[i - 1].ds_addr) |
3049198429Srpaulo		    segs[i - 1].ds_len << 4);
3050178676Ssam	}
3051178676Ssam
3052201209Srpaulo	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE);
3053201209Srpaulo	bus_dmamap_sync(ring->data_dmat, ring->cmd_dma.map,
3054198429Srpaulo	    BUS_DMASYNC_PREWRITE);
3055198429Srpaulo	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
3056198429Srpaulo	    BUS_DMASYNC_PREWRITE);
3057178676Ssam
3058201209Srpaulo#ifdef notyet
3059198429Srpaulo	/* Update TX scheduler. */
3060198429Srpaulo	hal->update_sched(sc, ring->qid, ring->cur, tx->id, totlen);
3061201209Srpaulo#endif
3062178676Ssam
3063198429Srpaulo	/* Kick TX ring. */
3064178676Ssam	ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT;
3065198429Srpaulo	IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur);
3066178676Ssam
3067198429Srpaulo	/* Mark TX ring as full if we reach a certain threshold. */
3068198429Srpaulo	if (++ring->queued > IWN_TX_RING_HIMARK)
3069198429Srpaulo		sc->qfullmsk |= 1 << ring->qid;
3070178676Ssam
3071178676Ssam	return 0;
3072178676Ssam}
3073178676Ssam
3074178676Ssamstatic int
3075201209Srpauloiwn_tx_data_raw(struct iwn_softc *sc, struct mbuf *m,
3076198429Srpaulo    struct ieee80211_node *ni, struct iwn_tx_ring *ring,
3077198429Srpaulo    const struct ieee80211_bpf_params *params)
3078178676Ssam{
3079198429Srpaulo	const struct iwn_hal *hal = sc->sc_hal;
3080198429Srpaulo	const struct iwn_rate *rinfo;
3081178676Ssam	struct ifnet *ifp = sc->sc_ifp;
3082198429Srpaulo	struct ieee80211vap *vap = ni->ni_vap;
3083198429Srpaulo	struct ieee80211com *ic = ifp->if_l2com;
3084198429Srpaulo	struct iwn_tx_cmd *cmd;
3085198429Srpaulo	struct iwn_cmd_data *tx;
3086198429Srpaulo	struct ieee80211_frame *wh;
3087178676Ssam	struct iwn_tx_desc *desc;
3088178676Ssam	struct iwn_tx_data *data;
3089198429Srpaulo	struct mbuf *mnew;
3090178676Ssam	bus_addr_t paddr;
3091178676Ssam	bus_dma_segment_t segs[IWN_MAX_SCATTER];
3092198429Srpaulo	uint32_t flags;
3093198429Srpaulo	u_int hdrlen;
3094201209Srpaulo	int totlen, error, pad, nsegs = 0, i, rate;
3095201209Srpaulo	uint8_t ridx, type, txant;
3096178676Ssam
3097198429Srpaulo	IWN_LOCK_ASSERT(sc);
3098178676Ssam
3099201209Srpaulo	wh = mtod(m, struct ieee80211_frame *);
3100198429Srpaulo	hdrlen = ieee80211_anyhdrsize(wh);
3101198429Srpaulo	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
3102198429Srpaulo
3103178676Ssam	desc = &ring->desc[ring->cur];
3104178676Ssam	data = &ring->data[ring->cur];
3105178676Ssam
3106198429Srpaulo	/* Choose a TX rate index. */
3107198429Srpaulo	rate = params->ibp_rate0;
3108198429Srpaulo	if (!ieee80211_isratevalid(ic->ic_rt, rate)) {
3109198429Srpaulo		/* XXX fall back to mcast/mgmt rate? */
3110201209Srpaulo		m_freem(m);
3111198429Srpaulo		return EINVAL;
3112198429Srpaulo	}
3113201209Srpaulo	ridx = iwn_plcp_signal(rate);
3114201209Srpaulo	rinfo = &iwn_rates[ridx];
3115198429Srpaulo
3116201209Srpaulo	totlen = m->m_pkthdr.len;
3117198429Srpaulo
3118201209Srpaulo	/* Prepare TX firmware command. */
3119198429Srpaulo	cmd = &ring->cmd[ring->cur];
3120198429Srpaulo	cmd->code = IWN_CMD_TX_DATA;
3121198429Srpaulo	cmd->flags = 0;
3122198429Srpaulo	cmd->qid = ring->qid;
3123198429Srpaulo	cmd->idx = ring->cur;
3124198429Srpaulo
3125198429Srpaulo	tx = (struct iwn_cmd_data *)cmd->data;
3126201209Srpaulo	/* NB: No need to clear tx, all fields are reinitialized here. */
3127198429Srpaulo	tx->scratch = 0;	/* clear "scratch" area */
3128198429Srpaulo
3129198429Srpaulo	flags = 0;
3130198429Srpaulo	if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0)
3131198429Srpaulo		flags |= IWN_TX_NEED_ACK;
3132198429Srpaulo	if (params->ibp_flags & IEEE80211_BPF_RTS) {
3133198429Srpaulo		if (sc->hw_type != IWN_HW_REV_TYPE_4965) {
3134198429Srpaulo			/* 5000 autoselects RTS/CTS or CTS-to-self. */
3135198429Srpaulo			flags &= ~IWN_TX_NEED_RTS;
3136198429Srpaulo			flags |= IWN_TX_NEED_PROTECTION;
3137198429Srpaulo		} else
3138198429Srpaulo			flags |= IWN_TX_NEED_RTS | IWN_TX_FULL_TXOP;
3139198429Srpaulo	}
3140198429Srpaulo	if (params->ibp_flags & IEEE80211_BPF_CTS) {
3141198429Srpaulo		if (sc->hw_type != IWN_HW_REV_TYPE_4965) {
3142198429Srpaulo			/* 5000 autoselects RTS/CTS or CTS-to-self. */
3143198429Srpaulo			flags &= ~IWN_TX_NEED_CTS;
3144198429Srpaulo			flags |= IWN_TX_NEED_PROTECTION;
3145198429Srpaulo		} else
3146198429Srpaulo			flags |= IWN_TX_NEED_CTS | IWN_TX_FULL_TXOP;
3147198429Srpaulo	}
3148198429Srpaulo	if (type == IEEE80211_FC0_TYPE_MGT) {
3149198429Srpaulo		uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
3150198429Srpaulo
3151198429Srpaulo		if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
3152198429Srpaulo			flags |= IWN_TX_INSERT_TSTAMP;
3153198429Srpaulo
3154198429Srpaulo		if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ ||
3155198429Srpaulo		    subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ)
3156198429Srpaulo			tx->timeout = htole16(3);
3157198429Srpaulo		else
3158198429Srpaulo			tx->timeout = htole16(2);
3159198429Srpaulo	} else
3160198429Srpaulo		tx->timeout = htole16(0);
3161198429Srpaulo
3162198429Srpaulo	if (hdrlen & 3) {
3163201209Srpaulo		/* First segment length must be a multiple of 4. */
3164198429Srpaulo		flags |= IWN_TX_NEED_PADDING;
3165198429Srpaulo		pad = 4 - (hdrlen & 3);
3166198429Srpaulo	} else
3167198429Srpaulo		pad = 0;
3168198429Srpaulo
3169198429Srpaulo	if (ieee80211_radiotap_active_vap(vap)) {
3170198429Srpaulo		struct iwn_tx_radiotap_header *tap = &sc->sc_txtap;
3171198429Srpaulo
3172198429Srpaulo		tap->wt_flags = 0;
3173198429Srpaulo		tap->wt_rate = rate;
3174198429Srpaulo
3175201209Srpaulo		ieee80211_radiotap_tx(vap, m);
3176198429Srpaulo	}
3177198429Srpaulo
3178198429Srpaulo	tx->len = htole16(totlen);
3179198429Srpaulo	tx->tid = 0;
3180198429Srpaulo	tx->id = hal->broadcast_id;
3181198429Srpaulo	tx->rts_ntries = params->ibp_try1;
3182198429Srpaulo	tx->data_ntries = params->ibp_try0;
3183198429Srpaulo	tx->lifetime = htole32(IWN_LIFETIME_INFINITE);
3184198429Srpaulo	tx->plcp = rinfo->plcp;
3185198429Srpaulo	tx->rflags = rinfo->flags;
3186201209Srpaulo	/* Group or management frame. */
3187201209Srpaulo	tx->linkq = 0;
3188201209Srpaulo	txant = IWN_LSB(sc->txchainmask);
3189201209Srpaulo	tx->rflags |= IWN_RFLAG_ANT(txant);
3190198429Srpaulo	/* Set physical address of "scratch area". */
3191198429Srpaulo	paddr = ring->cmd_dma.paddr + ring->cur * sizeof (struct iwn_tx_cmd);
3192198429Srpaulo	tx->loaddr = htole32(IWN_LOADDR(paddr));
3193198429Srpaulo	tx->hiaddr = IWN_HIADDR(paddr);
3194198429Srpaulo
3195198429Srpaulo	/* Copy 802.11 header in TX command. */
3196198429Srpaulo	memcpy((uint8_t *)(tx + 1), wh, hdrlen);
3197198429Srpaulo
3198198429Srpaulo	/* Trim 802.11 header. */
3199201209Srpaulo	m_adj(m, hdrlen);
3200198429Srpaulo	tx->security = 0;
3201198429Srpaulo	tx->flags = htole32(flags);
3202198429Srpaulo
3203201209Srpaulo	if (m->m_len > 0) {
3204201209Srpaulo		error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map,
3205201209Srpaulo		    m, segs, &nsegs, BUS_DMA_NOWAIT);
3206178676Ssam		if (error == EFBIG) {
3207198429Srpaulo			/* Too many fragments, linearize. */
3208201209Srpaulo			mnew = m_collapse(m, M_DONTWAIT, IWN_MAX_SCATTER);
3209178676Ssam			if (mnew == NULL) {
3210178676Ssam				device_printf(sc->sc_dev,
3211178676Ssam				    "%s: could not defrag mbuf\n", __func__);
3212201209Srpaulo				m_freem(m);
3213178676Ssam				return ENOBUFS;
3214178676Ssam			}
3215201209Srpaulo			m = mnew;
3216201209Srpaulo			error = bus_dmamap_load_mbuf_sg(ring->data_dmat,
3217201209Srpaulo			    data->map, m, segs, &nsegs, BUS_DMA_NOWAIT);
3218178676Ssam		}
3219178676Ssam		if (error != 0) {
3220178676Ssam			device_printf(sc->sc_dev,
3221178676Ssam			    "%s: bus_dmamap_load_mbuf_sg failed, error %d\n",
3222206443Sbschmidt			    __func__, error);
3223201209Srpaulo			m_freem(m);
3224178676Ssam			return error;
3225178676Ssam		}
3226178676Ssam	}
3227178676Ssam
3228201209Srpaulo	data->m = m;
3229178676Ssam	data->ni = ni;
3230178676Ssam
3231178676Ssam	DPRINTF(sc, IWN_DEBUG_XMIT, "%s: qid %d idx %d len %d nsegs %d\n",
3232201209Srpaulo	    __func__, ring->qid, ring->cur, m->m_pkthdr.len, nsegs);
3233178676Ssam
3234198429Srpaulo	/* Fill TX descriptor. */
3235198429Srpaulo	desc->nsegs = 1 + nsegs;
3236198429Srpaulo	/* First DMA segment is used by the TX command. */
3237201209Srpaulo	desc->segs[0].addr = htole32(IWN_LOADDR(data->cmd_paddr));
3238201209Srpaulo	desc->segs[0].len  = htole16(IWN_HIADDR(data->cmd_paddr) |
3239198429Srpaulo	    (4 + sizeof (*tx) + hdrlen + pad) << 4);
3240198429Srpaulo	/* Other DMA segments are for data payload. */
3241178676Ssam	for (i = 1; i <= nsegs; i++) {
3242198429Srpaulo		desc->segs[i].addr = htole32(IWN_LOADDR(segs[i - 1].ds_addr));
3243198429Srpaulo		desc->segs[i].len  = htole16(IWN_HIADDR(segs[i - 1].ds_addr) |
3244198429Srpaulo		    segs[i - 1].ds_len << 4);
3245178676Ssam	}
3246178676Ssam
3247201209Srpaulo	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE);
3248201209Srpaulo	bus_dmamap_sync(ring->data_dmat, ring->cmd_dma.map,
3249201209Srpaulo	    BUS_DMASYNC_PREWRITE);
3250201209Srpaulo	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
3251201209Srpaulo	    BUS_DMASYNC_PREWRITE);
3252201209Srpaulo
3253201209Srpaulo#ifdef notyet
3254198429Srpaulo	/* Update TX scheduler. */
3255198429Srpaulo	hal->update_sched(sc, ring->qid, ring->cur, tx->id, totlen);
3256201209Srpaulo#endif
3257178676Ssam
3258198429Srpaulo	/* Kick TX ring. */
3259178676Ssam	ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT;
3260198429Srpaulo	IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur);
3261178676Ssam
3262198429Srpaulo	/* Mark TX ring as full if we reach a certain threshold. */
3263198429Srpaulo	if (++ring->queued > IWN_TX_RING_HIMARK)
3264198429Srpaulo		sc->qfullmsk |= 1 << ring->qid;
3265178676Ssam
3266178676Ssam	return 0;
3267178676Ssam}
3268178676Ssam
3269178676Ssamstatic int
3270178676Ssamiwn_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
3271178676Ssam	const struct ieee80211_bpf_params *params)
3272178676Ssam{
3273178676Ssam	struct ieee80211com *ic = ni->ni_ic;
3274178676Ssam	struct ifnet *ifp = ic->ic_ifp;
3275178676Ssam	struct iwn_softc *sc = ifp->if_softc;
3276178676Ssam	struct iwn_tx_ring *txq;
3277198429Srpaulo	int error = 0;
3278178676Ssam
3279178676Ssam	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
3280178676Ssam		ieee80211_free_node(ni);
3281178676Ssam		m_freem(m);
3282178676Ssam		return ENETDOWN;
3283178676Ssam	}
3284178676Ssam
3285178676Ssam	IWN_LOCK(sc);
3286178676Ssam	if (params == NULL)
3287178676Ssam		txq = &sc->txq[M_WME_GETAC(m)];
3288178676Ssam	else
3289178676Ssam		txq = &sc->txq[params->ibp_pri & 3];
3290198429Srpaulo
3291178676Ssam	if (params == NULL) {
3292178676Ssam		/*
3293178676Ssam		 * Legacy path; interpret frame contents to decide
3294178676Ssam		 * precisely how to send the frame.
3295178676Ssam		 */
3296178676Ssam		error = iwn_tx_data(sc, m, ni, txq);
3297178676Ssam	} else {
3298178676Ssam		/*
3299178676Ssam		 * Caller supplied explicit parameters to use in
3300178676Ssam		 * sending the frame.
3301178676Ssam		 */
3302178676Ssam		error = iwn_tx_data_raw(sc, m, ni, txq, params);
3303178676Ssam	}
3304178676Ssam	if (error != 0) {
3305178676Ssam		/* NB: m is reclaimed on tx failure */
3306178676Ssam		ieee80211_free_node(ni);
3307178676Ssam		ifp->if_oerrors++;
3308178676Ssam	}
3309178676Ssam	IWN_UNLOCK(sc);
3310178676Ssam	return error;
3311178676Ssam}
3312178676Ssam
3313206477Sbschmidtstatic void
3314198429Srpauloiwn_start(struct ifnet *ifp)
3315198429Srpaulo{
3316198429Srpaulo	struct iwn_softc *sc = ifp->if_softc;
3317198429Srpaulo
3318198429Srpaulo	IWN_LOCK(sc);
3319198429Srpaulo	iwn_start_locked(ifp);
3320198429Srpaulo	IWN_UNLOCK(sc);
3321198429Srpaulo}
3322198429Srpaulo
3323206477Sbschmidtstatic void
3324198429Srpauloiwn_start_locked(struct ifnet *ifp)
3325198429Srpaulo{
3326198429Srpaulo	struct iwn_softc *sc = ifp->if_softc;
3327198429Srpaulo	struct ieee80211_node *ni;
3328198429Srpaulo	struct iwn_tx_ring *txq;
3329198429Srpaulo	struct mbuf *m;
3330198429Srpaulo	int pri;
3331198429Srpaulo
3332198429Srpaulo	IWN_LOCK_ASSERT(sc);
3333198429Srpaulo
3334198429Srpaulo	for (;;) {
3335198429Srpaulo		if (sc->qfullmsk != 0) {
3336198429Srpaulo			ifp->if_drv_flags |= IFF_DRV_OACTIVE;
3337198429Srpaulo			break;
3338198429Srpaulo		}
3339198429Srpaulo		IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
3340198429Srpaulo		if (m == NULL)
3341198429Srpaulo			break;
3342198429Srpaulo		ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
3343198429Srpaulo		pri = M_WME_GETAC(m);
3344198429Srpaulo		txq = &sc->txq[pri];
3345198429Srpaulo		if (iwn_tx_data(sc, m, ni, txq) != 0) {
3346198429Srpaulo			ifp->if_oerrors++;
3347198429Srpaulo			ieee80211_free_node(ni);
3348198429Srpaulo			break;
3349198429Srpaulo		}
3350198429Srpaulo		sc->sc_tx_timer = 5;
3351198429Srpaulo	}
3352198429Srpaulo}
3353198429Srpaulo
3354178676Ssamstatic void
3355178676Ssamiwn_watchdog(struct iwn_softc *sc)
3356178676Ssam{
3357178676Ssam	if (sc->sc_tx_timer > 0 && --sc->sc_tx_timer == 0) {
3358178676Ssam		struct ifnet *ifp = sc->sc_ifp;
3359191746Sthompsa		struct ieee80211com *ic = ifp->if_l2com;
3360178676Ssam
3361178676Ssam		if_printf(ifp, "device timeout\n");
3362191746Sthompsa		ieee80211_runtask(ic, &sc->sc_reinit_task);
3363178676Ssam	}
3364178676Ssam}
3365178676Ssam
3366206477Sbschmidtstatic int
3367178676Ssamiwn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
3368178676Ssam{
3369178676Ssam	struct iwn_softc *sc = ifp->if_softc;
3370178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
3371201209Srpaulo	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
3372178676Ssam	struct ifreq *ifr = (struct ifreq *) data;
3373201209Srpaulo	int error = 0, startall = 0, stop = 0;
3374178676Ssam
3375178676Ssam	switch (cmd) {
3376178676Ssam	case SIOCSIFFLAGS:
3377178704Sthompsa		IWN_LOCK(sc);
3378178676Ssam		if (ifp->if_flags & IFF_UP) {
3379178676Ssam			if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
3380178676Ssam				iwn_init_locked(sc);
3381201209Srpaulo				if (IWN_READ(sc, IWN_GP_CNTRL) & IWN_GP_CNTRL_RFKILL)
3382201209Srpaulo					startall = 1;
3383201209Srpaulo				else
3384201209Srpaulo					stop = 1;
3385178676Ssam			}
3386178676Ssam		} else {
3387178676Ssam			if (ifp->if_drv_flags & IFF_DRV_RUNNING)
3388178676Ssam				iwn_stop_locked(sc);
3389178676Ssam		}
3390178704Sthompsa		IWN_UNLOCK(sc);
3391178704Sthompsa		if (startall)
3392178704Sthompsa			ieee80211_start_all(ic);
3393201209Srpaulo		else if (vap != NULL && stop)
3394201209Srpaulo			ieee80211_stop(vap);
3395178676Ssam		break;
3396178676Ssam	case SIOCGIFMEDIA:
3397178676Ssam		error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd);
3398178676Ssam		break;
3399178704Sthompsa	case SIOCGIFADDR:
3400178676Ssam		error = ether_ioctl(ifp, cmd, data);
3401178676Ssam		break;
3402178704Sthompsa	default:
3403178704Sthompsa		error = EINVAL;
3404178704Sthompsa		break;
3405178676Ssam	}
3406178676Ssam	return error;
3407178676Ssam}
3408178676Ssam
3409178676Ssam/*
3410178676Ssam * Send a command to the firmware.
3411178676Ssam */
3412206477Sbschmidtstatic int
3413178676Ssamiwn_cmd(struct iwn_softc *sc, int code, const void *buf, int size, int async)
3414178676Ssam{
3415178676Ssam	struct iwn_tx_ring *ring = &sc->txq[4];
3416178676Ssam	struct iwn_tx_desc *desc;
3417198429Srpaulo	struct iwn_tx_data *data;
3418178676Ssam	struct iwn_tx_cmd *cmd;
3419198439Srpaulo	struct mbuf *m;
3420178676Ssam	bus_addr_t paddr;
3421198439Srpaulo	int totlen, error;
3422178676Ssam
3423178676Ssam	IWN_LOCK_ASSERT(sc);
3424178676Ssam
3425178676Ssam	desc = &ring->desc[ring->cur];
3426198429Srpaulo	data = &ring->data[ring->cur];
3427198429Srpaulo	totlen = 4 + size;
3428178676Ssam
3429198439Srpaulo	if (size > sizeof cmd->data) {
3430198439Srpaulo		/* Command is too large to fit in a descriptor. */
3431198439Srpaulo		if (totlen > MCLBYTES)
3432198439Srpaulo			return EINVAL;
3433201209Srpaulo		m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE);
3434198439Srpaulo		if (m == NULL)
3435198439Srpaulo			return ENOMEM;
3436198439Srpaulo		cmd = mtod(m, struct iwn_tx_cmd *);
3437201209Srpaulo		error = bus_dmamap_load(ring->data_dmat, data->map, cmd,
3438198439Srpaulo		    totlen, iwn_dma_map_addr, &paddr, BUS_DMA_NOWAIT);
3439198439Srpaulo		if (error != 0) {
3440198439Srpaulo			m_freem(m);
3441198439Srpaulo			return error;
3442198439Srpaulo		}
3443198439Srpaulo		data->m = m;
3444198439Srpaulo	} else {
3445198439Srpaulo		cmd = &ring->cmd[ring->cur];
3446198439Srpaulo		paddr = data->cmd_paddr;
3447198439Srpaulo	}
3448198439Srpaulo
3449178676Ssam	cmd->code = code;
3450178676Ssam	cmd->flags = 0;
3451178676Ssam	cmd->qid = ring->qid;
3452178676Ssam	cmd->idx = ring->cur;
3453178676Ssam	memcpy(cmd->data, buf, size);
3454178676Ssam
3455198429Srpaulo	desc->nsegs = 1;
3456198429Srpaulo	desc->segs[0].addr = htole32(IWN_LOADDR(paddr));
3457198429Srpaulo	desc->segs[0].len  = htole16(IWN_HIADDR(paddr) | totlen << 4);
3458178676Ssam
3459178676Ssam	DPRINTF(sc, IWN_DEBUG_CMD, "%s: %s (0x%x) flags %d qid %d idx %d\n",
3460178676Ssam	    __func__, iwn_intr_str(cmd->code), cmd->code,
3461178676Ssam	    cmd->flags, cmd->qid, cmd->idx);
3462178676Ssam
3463198439Srpaulo	if (size > sizeof cmd->data) {
3464201209Srpaulo		bus_dmamap_sync(ring->data_dmat, data->map,
3465198439Srpaulo		    BUS_DMASYNC_PREWRITE);
3466198439Srpaulo	} else {
3467201209Srpaulo		bus_dmamap_sync(ring->data_dmat, ring->cmd_dma.map,
3468198439Srpaulo		    BUS_DMASYNC_PREWRITE);
3469198439Srpaulo	}
3470198439Srpaulo	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
3471198439Srpaulo	    BUS_DMASYNC_PREWRITE);
3472198439Srpaulo
3473201209Srpaulo#ifdef notyet
3474198429Srpaulo	/* Update TX scheduler. */
3475201209Srpaulo	sc->sc_hal->update_sched(sc, ring->qid, ring->cur, 0, 0);
3476201209Srpaulo#endif
3477198429Srpaulo
3478198429Srpaulo	/* Kick command ring. */
3479178676Ssam	ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT;
3480198429Srpaulo	IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur);
3481178676Ssam
3482198439Srpaulo	return async ? 0 : msleep(desc, &sc->sc_mtx, PCATCH, "iwncmd", hz);
3483178676Ssam}
3484178676Ssam
3485206477Sbschmidtstatic int
3486198429Srpauloiwn4965_add_node(struct iwn_softc *sc, struct iwn_node_info *node, int async)
3487198429Srpaulo{
3488198429Srpaulo	struct iwn4965_node_info hnode;
3489198429Srpaulo	caddr_t src, dst;
3490198429Srpaulo
3491198429Srpaulo	/*
3492198429Srpaulo	 * We use the node structure for 5000 Series internally (it is
3493198429Srpaulo	 * a superset of the one for 4965AGN). We thus copy the common
3494198429Srpaulo	 * fields before sending the command.
3495198429Srpaulo	 */
3496198429Srpaulo	src = (caddr_t)node;
3497198429Srpaulo	dst = (caddr_t)&hnode;
3498198429Srpaulo	memcpy(dst, src, 48);
3499198429Srpaulo	/* Skip TSC, RX MIC and TX MIC fields from ``src''. */
3500198429Srpaulo	memcpy(dst + 48, src + 72, 20);
3501198429Srpaulo	return iwn_cmd(sc, IWN_CMD_ADD_NODE, &hnode, sizeof hnode, async);
3502198429Srpaulo}
3503198429Srpaulo
3504206477Sbschmidtstatic int
3505198429Srpauloiwn5000_add_node(struct iwn_softc *sc, struct iwn_node_info *node, int async)
3506198429Srpaulo{
3507198429Srpaulo	/* Direct mapping. */
3508198429Srpaulo	return iwn_cmd(sc, IWN_CMD_ADD_NODE, node, sizeof (*node), async);
3509198429Srpaulo}
3510198429Srpaulo
3511201209Srpaulo#if 0	/* HT */
3512178676Ssamstatic const uint8_t iwn_ridx_to_plcp[] = {
3513178676Ssam	10, 20, 55, 110, /* CCK */
3514178676Ssam	0xd, 0xf, 0x5, 0x7, 0x9, 0xb, 0x1, 0x3, 0x3 /* OFDM R1-R4 */
3515178676Ssam};
3516178676Ssamstatic const uint8_t iwn_siso_mcs_to_plcp[] = {
3517178676Ssam	0, 0, 0, 0, 			/* CCK */
3518178676Ssam	0, 0, 1, 2, 3, 4, 5, 6, 7	/* HT */
3519178676Ssam};
3520178676Ssamstatic const uint8_t iwn_mimo_mcs_to_plcp[] = {
3521178676Ssam	0, 0, 0, 0, 			/* CCK */
3522178676Ssam	8, 8, 9, 10, 11, 12, 13, 14, 15	/* HT */
3523178676Ssam};
3524201209Srpaulo#endif
3525178676Ssamstatic const uint8_t iwn_prev_ridx[] = {
3526178676Ssam	/* NB: allow fallback from CCK11 to OFDM9 and from OFDM6 to CCK5 */
3527178676Ssam	0, 0, 1, 5,			/* CCK */
3528178676Ssam	2, 4, 3, 6, 7, 8, 9, 10, 10	/* OFDM */
3529178676Ssam};
3530178676Ssam
3531178676Ssam/*
3532178676Ssam * Configure hardware link parameters for the specified
3533178676Ssam * node operating on the specified channel.
3534178676Ssam */
3535206477Sbschmidtstatic int
3536201209Srpauloiwn_set_link_quality(struct iwn_softc *sc, uint8_t id, int async)
3537178676Ssam{
3538201209Srpaulo	struct ifnet *ifp = sc->sc_ifp;
3539201209Srpaulo	struct ieee80211com *ic = ifp->if_l2com;
3540198429Srpaulo	struct iwn_cmd_link_quality linkq;
3541201209Srpaulo	const struct iwn_rate *rinfo;
3542201209Srpaulo	int i;
3543201209Srpaulo	uint8_t txant, ridx;
3544178676Ssam
3545198429Srpaulo	/* Use the first valid TX antenna. */
3546201209Srpaulo	txant = IWN_LSB(sc->txchainmask);
3547178676Ssam
3548198429Srpaulo	memset(&linkq, 0, sizeof linkq);
3549198429Srpaulo	linkq.id = id;
3550198429Srpaulo	linkq.antmsk_1stream = txant;
3551201209Srpaulo	linkq.antmsk_2stream = IWN_ANT_AB;
3552201209Srpaulo	linkq.ampdu_max = 31;
3553198429Srpaulo	linkq.ampdu_threshold = 3;
3554198429Srpaulo	linkq.ampdu_limit = htole16(4000);	/* 4ms */
3555198429Srpaulo
3556201209Srpaulo#if 0	/* HT */
3557198429Srpaulo	if (IEEE80211_IS_CHAN_HT(c))
3558198429Srpaulo		linkq.mimo = 1;
3559201209Srpaulo#endif
3560198429Srpaulo
3561178676Ssam	if (id == IWN_ID_BSS)
3562198429Srpaulo		ridx = IWN_RIDX_OFDM54;
3563201209Srpaulo	else if (IEEE80211_IS_CHAN_A(ic->ic_curchan))
3564198429Srpaulo		ridx = IWN_RIDX_OFDM6;
3565178676Ssam	else
3566198429Srpaulo		ridx = IWN_RIDX_CCK1;
3567198429Srpaulo
3568178676Ssam	for (i = 0; i < IWN_MAX_TX_RETRIES; i++) {
3569201209Srpaulo		rinfo = &iwn_rates[ridx];
3570201209Srpaulo#if 0	/* HT */
3571178676Ssam		if (IEEE80211_IS_CHAN_HT40(c)) {
3572198429Srpaulo			linkq.retry[i].plcp = iwn_mimo_mcs_to_plcp[ridx]
3573198429Srpaulo					 | IWN_RIDX_MCS;
3574198429Srpaulo			linkq.retry[i].rflags = IWN_RFLAG_HT
3575198429Srpaulo					 | IWN_RFLAG_HT40;
3576178676Ssam			/* XXX shortGI */
3577178676Ssam		} else if (IEEE80211_IS_CHAN_HT(c)) {
3578198429Srpaulo			linkq.retry[i].plcp = iwn_siso_mcs_to_plcp[ridx]
3579198429Srpaulo					 | IWN_RIDX_MCS;
3580198429Srpaulo			linkq.retry[i].rflags = IWN_RFLAG_HT;
3581178676Ssam			/* XXX shortGI */
3582201209Srpaulo		} else
3583201209Srpaulo#endif
3584201209Srpaulo		{
3585201209Srpaulo			linkq.retry[i].plcp = rinfo->plcp;
3586201209Srpaulo			linkq.retry[i].rflags = rinfo->flags;
3587178676Ssam		}
3588198429Srpaulo		linkq.retry[i].rflags |= IWN_RFLAG_ANT(txant);
3589178676Ssam		ridx = iwn_prev_ridx[ridx];
3590178676Ssam	}
3591178676Ssam#ifdef IWN_DEBUG
3592178676Ssam	if (sc->sc_debug & IWN_DEBUG_STATE) {
3593178676Ssam		printf("%s: set link quality for node %d, mimo %d ssmask %d\n",
3594198429Srpaulo		    __func__, id, linkq.mimo, linkq.antmsk_1stream);
3595178676Ssam		printf("%s:", __func__);
3596178676Ssam		for (i = 0; i < IWN_MAX_TX_RETRIES; i++)
3597198429Srpaulo			printf(" %d:%x", linkq.retry[i].plcp,
3598198429Srpaulo			    linkq.retry[i].rflags);
3599178676Ssam		printf("\n");
3600178676Ssam	}
3601178676Ssam#endif
3602198429Srpaulo	return iwn_cmd(sc, IWN_CMD_LINK_QUALITY, &linkq, sizeof linkq, async);
3603178676Ssam}
3604178676Ssam
3605178676Ssam/*
3606198429Srpaulo * Broadcast node is used to send group-addressed and management frames.
3607178676Ssam */
3608206477Sbschmidtstatic int
3609201209Srpauloiwn_add_broadcast_node(struct iwn_softc *sc, int async)
3610178676Ssam{
3611198429Srpaulo	const struct iwn_hal *hal = sc->sc_hal;
3612198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
3613178676Ssam	struct iwn_node_info node;
3614198429Srpaulo	int error;
3615178676Ssam
3616178676Ssam	memset(&node, 0, sizeof node);
3617198429Srpaulo	IEEE80211_ADDR_COPY(node.macaddr, ifp->if_broadcastaddr);
3618198429Srpaulo	node.id = hal->broadcast_id;
3619198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "%s: adding broadcast node\n", __func__);
3620198429Srpaulo	error = hal->add_node(sc, &node, async);
3621198429Srpaulo	if (error != 0)
3622198429Srpaulo		return error;
3623178676Ssam
3624201209Srpaulo	error = iwn_set_link_quality(sc, hal->broadcast_id, async);
3625201209Srpaulo	return error;
3626178676Ssam}
3627178676Ssam
3628206477Sbschmidtstatic int
3629178676Ssamiwn_wme_update(struct ieee80211com *ic)
3630178676Ssam{
3631178676Ssam#define IWN_EXP2(x)	((1 << (x)) - 1)	/* CWmin = 2^ECWmin - 1 */
3632178676Ssam	struct iwn_softc *sc = ic->ic_ifp->if_softc;
3633178676Ssam	struct iwn_edca_params cmd;
3634178676Ssam	int i;
3635178676Ssam
3636178676Ssam	memset(&cmd, 0, sizeof cmd);
3637178676Ssam	cmd.flags = htole32(IWN_EDCA_UPDATE);
3638178676Ssam	for (i = 0; i < WME_NUM_AC; i++) {
3639178676Ssam		const struct wmeParams *wmep =
3640178676Ssam		    &ic->ic_wme.wme_chanParams.cap_wmeParams[i];
3641178676Ssam		cmd.ac[i].aifsn = wmep->wmep_aifsn;
3642178676Ssam		cmd.ac[i].cwmin = htole16(IWN_EXP2(wmep->wmep_logcwmin));
3643178676Ssam		cmd.ac[i].cwmax = htole16(IWN_EXP2(wmep->wmep_logcwmax));
3644178676Ssam		cmd.ac[i].txoplimit =
3645220634Sbschmidt		    htole16(IEEE80211_TXOP_TO_US(wmep->wmep_txopLimit));
3646178676Ssam	}
3647201209Srpaulo	IEEE80211_UNLOCK(ic);
3648178676Ssam	IWN_LOCK(sc);
3649178676Ssam	(void) iwn_cmd(sc, IWN_CMD_EDCA_PARAMS, &cmd, sizeof cmd, 1 /*async*/);
3650178676Ssam	IWN_UNLOCK(sc);
3651201209Srpaulo	IEEE80211_LOCK(ic);
3652178676Ssam	return 0;
3653178676Ssam#undef IWN_EXP2
3654178676Ssam}
3655178676Ssam
3656201209Srpaulostatic void
3657201209Srpauloiwn_update_mcast(struct ifnet *ifp)
3658201209Srpaulo{
3659201209Srpaulo	/* Ignore */
3660201209Srpaulo}
3661201209Srpaulo
3662206477Sbschmidtstatic void
3663178676Ssamiwn_set_led(struct iwn_softc *sc, uint8_t which, uint8_t off, uint8_t on)
3664178676Ssam{
3665178676Ssam	struct iwn_cmd_led led;
3666178676Ssam
3667198429Srpaulo	/* Clear microcode LED ownership. */
3668198429Srpaulo	IWN_CLRBITS(sc, IWN_LED, IWN_LED_BSM_CTRL);
3669198429Srpaulo
3670178676Ssam	led.which = which;
3671198429Srpaulo	led.unit = htole32(10000);	/* on/off in unit of 100ms */
3672178676Ssam	led.off = off;
3673178676Ssam	led.on = on;
3674198429Srpaulo	(void)iwn_cmd(sc, IWN_CMD_SET_LED, &led, sizeof led, 1);
3675178676Ssam}
3676178676Ssam
3677178676Ssam/*
3678201209Srpaulo * Set the critical temperature at which the firmware will stop the radio
3679201209Srpaulo * and notify us.
3680178676Ssam */
3681206477Sbschmidtstatic int
3682178676Ssamiwn_set_critical_temp(struct iwn_softc *sc)
3683178676Ssam{
3684178676Ssam	struct iwn_critical_temp crit;
3685201209Srpaulo	int32_t temp;
3686178676Ssam
3687198429Srpaulo	IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_CTEMP_STOP_RF);
3688178676Ssam
3689201209Srpaulo	if (sc->hw_type == IWN_HW_REV_TYPE_5150)
3690201209Srpaulo		temp = (IWN_CTOK(110) - sc->temp_off) * -5;
3691201209Srpaulo	else if (sc->hw_type == IWN_HW_REV_TYPE_4965)
3692201209Srpaulo		temp = IWN_CTOK(110);
3693201209Srpaulo	else
3694201209Srpaulo		temp = 110;
3695178676Ssam	memset(&crit, 0, sizeof crit);
3696201209Srpaulo	crit.tempR = htole32(temp);
3697201209Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "setting critical temp to %d\n",
3698201209Srpaulo	    temp);
3699178676Ssam	return iwn_cmd(sc, IWN_CMD_SET_CRITICAL_TEMP, &crit, sizeof crit, 0);
3700178676Ssam}
3701178676Ssam
3702206477Sbschmidtstatic int
3703198429Srpauloiwn_set_timing(struct iwn_softc *sc, struct ieee80211_node *ni)
3704178676Ssam{
3705198429Srpaulo	struct iwn_cmd_timing cmd;
3706178676Ssam	uint64_t val, mod;
3707178676Ssam
3708198429Srpaulo	memset(&cmd, 0, sizeof cmd);
3709198429Srpaulo	memcpy(&cmd.tstamp, ni->ni_tstamp.data, sizeof (uint64_t));
3710198429Srpaulo	cmd.bintval = htole16(ni->ni_intval);
3711198429Srpaulo	cmd.lintval = htole16(10);
3712178676Ssam
3713198429Srpaulo	/* Compute remaining time until next beacon. */
3714220634Sbschmidt	val = (uint64_t)ni->ni_intval * IEEE80211_DUR_TU;
3715198429Srpaulo	mod = le64toh(cmd.tstamp) % val;
3716198429Srpaulo	cmd.binitval = htole32((uint32_t)(val - mod));
3717178676Ssam
3718198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "timing bintval=%u tstamp=%ju, init=%u\n",
3719198429Srpaulo	    ni->ni_intval, le64toh(cmd.tstamp), (uint32_t)(val - mod));
3720178676Ssam
3721198429Srpaulo	return iwn_cmd(sc, IWN_CMD_TIMING, &cmd, sizeof cmd, 1);
3722178676Ssam}
3723178676Ssam
3724206477Sbschmidtstatic void
3725198429Srpauloiwn4965_power_calibration(struct iwn_softc *sc, int temp)
3726178676Ssam{
3727201882Skeramida	struct ifnet *ifp = sc->sc_ifp;
3728201882Skeramida	struct ieee80211com *ic = ifp->if_l2com;
3729201882Skeramida
3730198429Srpaulo	/* Adjust TX power if need be (delta >= 3 degC.) */
3731178676Ssam	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: temperature %d->%d\n",
3732178676Ssam	    __func__, sc->temp, temp);
3733198429Srpaulo	if (abs(temp - sc->temp) >= 3) {
3734198429Srpaulo		/* Record temperature of last calibration. */
3735198429Srpaulo		sc->temp = temp;
3736201882Skeramida		(void)iwn4965_set_txpower(sc, ic->ic_bsschan, 1);
3737178676Ssam	}
3738178676Ssam}
3739178676Ssam
3740178676Ssam/*
3741198429Srpaulo * Set TX power for current channel (each rate has its own power settings).
3742178676Ssam * This function takes into account the regulatory information from EEPROM,
3743178676Ssam * the current temperature and the current voltage.
3744178676Ssam */
3745206477Sbschmidtstatic int
3746201882Skeramidaiwn4965_set_txpower(struct iwn_softc *sc, struct ieee80211_channel *ch,
3747201882Skeramida    int async)
3748178676Ssam{
3749198429Srpaulo/* Fixed-point arithmetic division using a n-bit fractional part. */
3750178676Ssam#define fdivround(a, b, n)	\
3751178676Ssam	((((1 << n) * (a)) / (b) + (1 << n) / 2) / (1 << n))
3752198429Srpaulo/* Linear interpolation. */
3753178676Ssam#define interpolate(x, x1, y1, x2, y2, n)	\
3754178676Ssam	((y1) + fdivround(((int)(x) - (x1)) * ((y2) - (y1)), (x2) - (x1), n))
3755178676Ssam
3756178676Ssam	static const int tdiv[IWN_NATTEN_GROUPS] = { 9, 8, 8, 8, 6 };
3757178676Ssam	struct ifnet *ifp = sc->sc_ifp;
3758178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
3759178676Ssam	struct iwn_ucode_info *uc = &sc->ucode_info;
3760198429Srpaulo	struct iwn4965_cmd_txpower cmd;
3761198429Srpaulo	struct iwn4965_eeprom_chan_samples *chans;
3762178676Ssam	int32_t vdiff, tdiff;
3763178676Ssam	int i, c, grp, maxpwr;
3764198429Srpaulo	const uint8_t *rf_gain, *dsp_gain;
3765198429Srpaulo	uint8_t chan;
3766178676Ssam
3767201882Skeramida	/* Retrieve channel number. */
3768201882Skeramida	chan = ieee80211_chan2ieee(ic, ch);
3769201209Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "setting TX power for channel %d\n",
3770201209Srpaulo	    chan);
3771178676Ssam
3772178676Ssam	memset(&cmd, 0, sizeof cmd);
3773178676Ssam	cmd.band = IEEE80211_IS_CHAN_5GHZ(ch) ? 0 : 1;
3774178676Ssam	cmd.chan = chan;
3775178676Ssam
3776178676Ssam	if (IEEE80211_IS_CHAN_5GHZ(ch)) {
3777178676Ssam		maxpwr   = sc->maxpwr5GHz;
3778198429Srpaulo		rf_gain  = iwn4965_rf_gain_5ghz;
3779198429Srpaulo		dsp_gain = iwn4965_dsp_gain_5ghz;
3780178676Ssam	} else {
3781178676Ssam		maxpwr   = sc->maxpwr2GHz;
3782198429Srpaulo		rf_gain  = iwn4965_rf_gain_2ghz;
3783198429Srpaulo		dsp_gain = iwn4965_dsp_gain_2ghz;
3784178676Ssam	}
3785178676Ssam
3786198429Srpaulo	/* Compute voltage compensation. */
3787178676Ssam	vdiff = ((int32_t)le32toh(uc->volt) - sc->eeprom_voltage) / 7;
3788178676Ssam	if (vdiff > 0)
3789178676Ssam		vdiff *= 2;
3790178676Ssam	if (abs(vdiff) > 2)
3791178676Ssam		vdiff = 0;
3792178676Ssam	DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
3793178676Ssam	    "%s: voltage compensation=%d (UCODE=%d, EEPROM=%d)\n",
3794178676Ssam	    __func__, vdiff, le32toh(uc->volt), sc->eeprom_voltage);
3795178676Ssam
3796201209Srpaulo	/* Get channel attenuation group. */
3797178676Ssam	if (chan <= 20)		/* 1-20 */
3798178676Ssam		grp = 4;
3799178676Ssam	else if (chan <= 43)	/* 34-43 */
3800178676Ssam		grp = 0;
3801178676Ssam	else if (chan <= 70)	/* 44-70 */
3802178676Ssam		grp = 1;
3803178676Ssam	else if (chan <= 124)	/* 71-124 */
3804178676Ssam		grp = 2;
3805178676Ssam	else			/* 125-200 */
3806178676Ssam		grp = 3;
3807178676Ssam	DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
3808178676Ssam	    "%s: chan %d, attenuation group=%d\n", __func__, chan, grp);
3809178676Ssam
3810201209Srpaulo	/* Get channel sub-band. */
3811178676Ssam	for (i = 0; i < IWN_NBANDS; i++)
3812178676Ssam		if (sc->bands[i].lo != 0 &&
3813178676Ssam		    sc->bands[i].lo <= chan && chan <= sc->bands[i].hi)
3814178676Ssam			break;
3815198429Srpaulo	if (i == IWN_NBANDS)	/* Can't happen in real-life. */
3816198429Srpaulo		return EINVAL;
3817178676Ssam	chans = sc->bands[i].chans;
3818178676Ssam	DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
3819178676Ssam	    "%s: chan %d sub-band=%d\n", __func__, chan, i);
3820178676Ssam
3821198429Srpaulo	for (c = 0; c < 2; c++) {
3822178676Ssam		uint8_t power, gain, temp;
3823178676Ssam		int maxchpwr, pwr, ridx, idx;
3824178676Ssam
3825178676Ssam		power = interpolate(chan,
3826178676Ssam		    chans[0].num, chans[0].samples[c][1].power,
3827178676Ssam		    chans[1].num, chans[1].samples[c][1].power, 1);
3828178676Ssam		gain  = interpolate(chan,
3829178676Ssam		    chans[0].num, chans[0].samples[c][1].gain,
3830178676Ssam		    chans[1].num, chans[1].samples[c][1].gain, 1);
3831178676Ssam		temp  = interpolate(chan,
3832178676Ssam		    chans[0].num, chans[0].samples[c][1].temp,
3833178676Ssam		    chans[1].num, chans[1].samples[c][1].temp, 1);
3834178676Ssam		DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
3835178676Ssam		    "%s: Tx chain %d: power=%d gain=%d temp=%d\n",
3836178676Ssam		    __func__, c, power, gain, temp);
3837178676Ssam
3838198429Srpaulo		/* Compute temperature compensation. */
3839178676Ssam		tdiff = ((sc->temp - temp) * 2) / tdiv[grp];
3840178676Ssam		DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
3841178676Ssam		    "%s: temperature compensation=%d (current=%d, EEPROM=%d)\n",
3842178676Ssam		    __func__, tdiff, sc->temp, temp);
3843178676Ssam
3844178676Ssam		for (ridx = 0; ridx <= IWN_RIDX_MAX; ridx++) {
3845201209Srpaulo			/* Convert dBm to half-dBm. */
3846198429Srpaulo			maxchpwr = sc->maxpwr[chan] * 2;
3847198429Srpaulo			if ((ridx / 8) & 1)
3848198429Srpaulo				maxchpwr -= 6;	/* MIMO 2T: -3dB */
3849178676Ssam
3850198429Srpaulo			pwr = maxpwr;
3851178676Ssam
3852198429Srpaulo			/* Adjust TX power based on rate. */
3853198429Srpaulo			if ((ridx % 8) == 5)
3854198429Srpaulo				pwr -= 15;	/* OFDM48: -7.5dB */
3855198429Srpaulo			else if ((ridx % 8) == 6)
3856198429Srpaulo				pwr -= 17;	/* OFDM54: -8.5dB */
3857198429Srpaulo			else if ((ridx % 8) == 7)
3858198429Srpaulo				pwr -= 20;	/* OFDM60: -10dB */
3859198429Srpaulo			else
3860198429Srpaulo				pwr -= 10;	/* Others: -5dB */
3861178676Ssam
3862201209Srpaulo			/* Do not exceed channel max TX power. */
3863178676Ssam			if (pwr > maxchpwr)
3864178676Ssam				pwr = maxchpwr;
3865178676Ssam
3866178676Ssam			idx = gain - (pwr - power) - tdiff - vdiff;
3867178676Ssam			if ((ridx / 8) & 1)	/* MIMO */
3868178676Ssam				idx += (int32_t)le32toh(uc->atten[grp][c]);
3869178676Ssam
3870178676Ssam			if (cmd.band == 0)
3871178676Ssam				idx += 9;	/* 5GHz */
3872178676Ssam			if (ridx == IWN_RIDX_MAX)
3873178676Ssam				idx += 5;	/* CCK */
3874178676Ssam
3875198429Srpaulo			/* Make sure idx stays in a valid range. */
3876178676Ssam			if (idx < 0)
3877178676Ssam				idx = 0;
3878198429Srpaulo			else if (idx > IWN4965_MAX_PWR_INDEX)
3879198429Srpaulo				idx = IWN4965_MAX_PWR_INDEX;
3880178676Ssam
3881178676Ssam			DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
3882178676Ssam			    "%s: Tx chain %d, rate idx %d: power=%d\n",
3883178676Ssam			    __func__, c, ridx, idx);
3884178676Ssam			cmd.power[ridx].rf_gain[c] = rf_gain[idx];
3885178676Ssam			cmd.power[ridx].dsp_gain[c] = dsp_gain[idx];
3886178676Ssam		}
3887178676Ssam	}
3888178676Ssam
3889178676Ssam	DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
3890178676Ssam	    "%s: set tx power for chan %d\n", __func__, chan);
3891178676Ssam	return iwn_cmd(sc, IWN_CMD_TXPOWER, &cmd, sizeof cmd, async);
3892178676Ssam
3893178676Ssam#undef interpolate
3894178676Ssam#undef fdivround
3895178676Ssam}
3896178676Ssam
3897206477Sbschmidtstatic int
3898201882Skeramidaiwn5000_set_txpower(struct iwn_softc *sc, struct ieee80211_channel *ch,
3899201882Skeramida    int async)
3900198429Srpaulo{
3901198429Srpaulo	struct iwn5000_cmd_txpower cmd;
3902198429Srpaulo
3903198429Srpaulo	/*
3904198429Srpaulo	 * TX power calibration is handled automatically by the firmware
3905198429Srpaulo	 * for 5000 Series.
3906198429Srpaulo	 */
3907198429Srpaulo	memset(&cmd, 0, sizeof cmd);
3908198429Srpaulo	cmd.global_limit = 2 * IWN5000_TXPOWER_MAX_DBM;	/* 16 dBm */
3909198429Srpaulo	cmd.flags = IWN5000_TXPOWER_NO_CLOSED;
3910198429Srpaulo	cmd.srv_limit = IWN5000_TXPOWER_AUTO;
3911198429Srpaulo	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: setting TX power\n", __func__);
3912198429Srpaulo	return iwn_cmd(sc, IWN_CMD_TXPOWER_DBM, &cmd, sizeof cmd, async);
3913198429Srpaulo}
3914198429Srpaulo
3915178676Ssam/*
3916198429Srpaulo * Retrieve the maximum RSSI (in dBm) among receivers.
3917178676Ssam */
3918206477Sbschmidtstatic int
3919198429Srpauloiwn4965_get_rssi(struct iwn_softc *sc, struct iwn_rx_stat *stat)
3920178676Ssam{
3921198429Srpaulo	struct iwn4965_rx_phystat *phy = (void *)stat->phybuf;
3922198429Srpaulo	uint8_t mask, agc;
3923198429Srpaulo	int rssi;
3924178676Ssam
3925201209Srpaulo	mask = (le16toh(phy->antenna) >> 4) & IWN_ANT_ABC;
3926198429Srpaulo	agc  = (le16toh(phy->agc) >> 7) & 0x7f;
3927178676Ssam
3928178676Ssam	rssi = 0;
3929178676Ssam#if 0
3930198429Srpaulo	if (mask & IWN_ANT_A)	/* Ant A */
3931198429Srpaulo		rssi = max(rssi, phy->rssi[0]);
3932198429Srpaulo	if (mask & IWN_ATH_B)	/* Ant B */
3933198429Srpaulo		rssi = max(rssi, phy->rssi[2]);
3934198429Srpaulo	if (mask & IWN_ANT_C)	/* Ant C */
3935198429Srpaulo		rssi = max(rssi, phy->rssi[4]);
3936178676Ssam#else
3937198429Srpaulo	rssi = max(rssi, phy->rssi[0]);
3938198429Srpaulo	rssi = max(rssi, phy->rssi[2]);
3939198429Srpaulo	rssi = max(rssi, phy->rssi[4]);
3940178676Ssam#endif
3941198429Srpaulo
3942178676Ssam	DPRINTF(sc, IWN_DEBUG_RECV, "%s: agc %d mask 0x%x rssi %d %d %d "
3943178676Ssam	    "result %d\n", __func__, agc, mask,
3944198429Srpaulo	    phy->rssi[0], phy->rssi[2], phy->rssi[4],
3945178676Ssam	    rssi - agc - IWN_RSSI_TO_DBM);
3946178676Ssam	return rssi - agc - IWN_RSSI_TO_DBM;
3947178676Ssam}
3948178676Ssam
3949206477Sbschmidtstatic int
3950198429Srpauloiwn5000_get_rssi(struct iwn_softc *sc, struct iwn_rx_stat *stat)
3951198429Srpaulo{
3952198429Srpaulo	struct iwn5000_rx_phystat *phy = (void *)stat->phybuf;
3953198429Srpaulo	int rssi;
3954198429Srpaulo	uint8_t agc;
3955198429Srpaulo
3956198429Srpaulo	agc = (le32toh(phy->agc) >> 9) & 0x7f;
3957198429Srpaulo
3958198429Srpaulo	rssi = MAX(le16toh(phy->rssi[0]) & 0xff,
3959198429Srpaulo		   le16toh(phy->rssi[1]) & 0xff);
3960198429Srpaulo	rssi = MAX(le16toh(phy->rssi[2]) & 0xff, rssi);
3961198429Srpaulo
3962198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RECV, "%s: agc %d rssi %d %d %d "
3963198429Srpaulo	    "result %d\n", __func__, agc,
3964201822Strasz	    phy->rssi[0], phy->rssi[1], phy->rssi[2],
3965198429Srpaulo	    rssi - agc - IWN_RSSI_TO_DBM);
3966198429Srpaulo	return rssi - agc - IWN_RSSI_TO_DBM;
3967198429Srpaulo}
3968198429Srpaulo
3969178676Ssam/*
3970198429Srpaulo * Retrieve the average noise (in dBm) among receivers.
3971178676Ssam */
3972206477Sbschmidtstatic int
3973178676Ssamiwn_get_noise(const struct iwn_rx_general_stats *stats)
3974178676Ssam{
3975178676Ssam	int i, total, nbant, noise;
3976178676Ssam
3977178676Ssam	total = nbant = 0;
3978178676Ssam	for (i = 0; i < 3; i++) {
3979198429Srpaulo		if ((noise = le32toh(stats->noise[i]) & 0xff) == 0)
3980198429Srpaulo			continue;
3981198429Srpaulo		total += noise;
3982198429Srpaulo		nbant++;
3983178676Ssam	}
3984198429Srpaulo	/* There should be at least one antenna but check anyway. */
3985178676Ssam	return (nbant == 0) ? -127 : (total / nbant) - 107;
3986178676Ssam}
3987178676Ssam
3988178676Ssam/*
3989198429Srpaulo * Compute temperature (in degC) from last received statistics.
3990178676Ssam */
3991206477Sbschmidtstatic int
3992198429Srpauloiwn4965_get_temperature(struct iwn_softc *sc)
3993178676Ssam{
3994178676Ssam	struct iwn_ucode_info *uc = &sc->ucode_info;
3995178676Ssam	int32_t r1, r2, r3, r4, temp;
3996178676Ssam
3997178676Ssam	r1 = le32toh(uc->temp[0].chan20MHz);
3998178676Ssam	r2 = le32toh(uc->temp[1].chan20MHz);
3999178676Ssam	r3 = le32toh(uc->temp[2].chan20MHz);
4000178676Ssam	r4 = le32toh(sc->rawtemp);
4001178676Ssam
4002198429Srpaulo	if (r1 == r3)	/* Prevents division by 0 (should not happen.) */
4003178676Ssam		return 0;
4004178676Ssam
4005198429Srpaulo	/* Sign-extend 23-bit R4 value to 32-bit. */
4006220659Sbschmidt	r4 = ((r4 & 0xffffff) ^ 0x800000) - 0x800000;
4007198429Srpaulo	/* Compute temperature in Kelvin. */
4008178676Ssam	temp = (259 * (r4 - r2)) / (r3 - r1);
4009178676Ssam	temp = (temp * 97) / 100 + 8;
4010178676Ssam
4011201209Srpaulo	DPRINTF(sc, IWN_DEBUG_ANY, "temperature %dK/%dC\n", temp,
4012201209Srpaulo	    IWN_KTOC(temp));
4013178676Ssam	return IWN_KTOC(temp);
4014178676Ssam}
4015178676Ssam
4016206477Sbschmidtstatic int
4017198429Srpauloiwn5000_get_temperature(struct iwn_softc *sc)
4018198429Srpaulo{
4019201209Srpaulo	int32_t temp;
4020201209Srpaulo
4021198429Srpaulo	/*
4022198429Srpaulo	 * Temperature is not used by the driver for 5000 Series because
4023198429Srpaulo	 * TX power calibration is handled by firmware.  We export it to
4024198429Srpaulo	 * users through the sensor framework though.
4025198429Srpaulo	 */
4026201209Srpaulo	temp = le32toh(sc->rawtemp);
4027201209Srpaulo	if (sc->hw_type == IWN_HW_REV_TYPE_5150) {
4028201209Srpaulo		temp = (temp / -5) + sc->temp_off;
4029201209Srpaulo		temp = IWN_KTOC(temp);
4030201209Srpaulo	}
4031201209Srpaulo	return temp;
4032198429Srpaulo}
4033198429Srpaulo
4034178676Ssam/*
4035178676Ssam * Initialize sensitivity calibration state machine.
4036178676Ssam */
4037206477Sbschmidtstatic int
4038178676Ssamiwn_init_sensitivity(struct iwn_softc *sc)
4039178676Ssam{
4040198429Srpaulo	const struct iwn_hal *hal = sc->sc_hal;
4041178676Ssam	struct iwn_calib_state *calib = &sc->calib;
4042198429Srpaulo	uint32_t flags;
4043178676Ssam	int error;
4044178676Ssam
4045198429Srpaulo	/* Reset calibration state machine. */
4046178676Ssam	memset(calib, 0, sizeof (*calib));
4047178676Ssam	calib->state = IWN_CALIB_STATE_INIT;
4048178676Ssam	calib->cck_state = IWN_CCK_STATE_HIFA;
4049198429Srpaulo	/* Set initial correlation values. */
4050201209Srpaulo	calib->ofdm_x1     = sc->limits->min_ofdm_x1;
4051201209Srpaulo	calib->ofdm_mrc_x1 = sc->limits->min_ofdm_mrc_x1;
4052206444Sbschmidt	calib->ofdm_x4     = sc->limits->min_ofdm_x4;
4053201209Srpaulo	calib->ofdm_mrc_x4 = sc->limits->min_ofdm_mrc_x4;
4054198429Srpaulo	calib->cck_x4      = 125;
4055201209Srpaulo	calib->cck_mrc_x4  = sc->limits->min_cck_mrc_x4;
4056201209Srpaulo	calib->energy_cck  = sc->limits->energy_cck;
4057178676Ssam
4058198429Srpaulo	/* Write initial sensitivity. */
4059178676Ssam	error = iwn_send_sensitivity(sc);
4060178676Ssam	if (error != 0)
4061178676Ssam		return error;
4062178676Ssam
4063198429Srpaulo	/* Write initial gains. */
4064198429Srpaulo	error = hal->init_gains(sc);
4065198429Srpaulo	if (error != 0)
4066198429Srpaulo		return error;
4067198429Srpaulo
4068198429Srpaulo	/* Request statistics at each beacon interval. */
4069198429Srpaulo	flags = 0;
4070178676Ssam	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: calibrate phy\n", __func__);
4071198429Srpaulo	return iwn_cmd(sc, IWN_CMD_GET_STATISTICS, &flags, sizeof flags, 1);
4072178676Ssam}
4073178676Ssam
4074178676Ssam/*
4075178676Ssam * Collect noise and RSSI statistics for the first 20 beacons received
4076178676Ssam * after association and use them to determine connected antennas and
4077198429Srpaulo * to set differential gains.
4078178676Ssam */
4079206477Sbschmidtstatic void
4080198429Srpauloiwn_collect_noise(struct iwn_softc *sc,
4081178676Ssam    const struct iwn_rx_general_stats *stats)
4082178676Ssam{
4083198429Srpaulo	const struct iwn_hal *hal = sc->sc_hal;
4084178676Ssam	struct iwn_calib_state *calib = &sc->calib;
4085198429Srpaulo	uint32_t val;
4086198429Srpaulo	int i;
4087178676Ssam
4088198429Srpaulo	/* Accumulate RSSI and noise for all 3 antennas. */
4089178676Ssam	for (i = 0; i < 3; i++) {
4090178676Ssam		calib->rssi[i] += le32toh(stats->rssi[i]) & 0xff;
4091178676Ssam		calib->noise[i] += le32toh(stats->noise[i]) & 0xff;
4092178676Ssam	}
4093198429Srpaulo	/* NB: We update differential gains only once after 20 beacons. */
4094178676Ssam	if (++calib->nbeacons < 20)
4095178676Ssam		return;
4096178676Ssam
4097198429Srpaulo	/* Determine highest average RSSI. */
4098198429Srpaulo	val = MAX(calib->rssi[0], calib->rssi[1]);
4099198429Srpaulo	val = MAX(calib->rssi[2], val);
4100178676Ssam
4101198429Srpaulo	/* Determine which antennas are connected. */
4102210110Sbschmidt	sc->chainmask = sc->rxchainmask;
4103178676Ssam	for (i = 0; i < 3; i++)
4104210110Sbschmidt		if (val - calib->rssi[i] > 15 * 20)
4105210110Sbschmidt			sc->chainmask &= ~(1 << i);
4106210110Sbschmidt	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4107210110Sbschmidt	    "%s: RX chains mask: theoretical=0x%x, actual=0x%x\n",
4108210110Sbschmidt	    __func__, sc->rxchainmask, sc->chainmask);
4109210110Sbschmidt
4110198429Srpaulo	/* If none of the TX antennas are connected, keep at least one. */
4111201209Srpaulo	if ((sc->chainmask & sc->txchainmask) == 0)
4112201209Srpaulo		sc->chainmask |= IWN_LSB(sc->txchainmask);
4113178676Ssam
4114198429Srpaulo	(void)hal->set_gains(sc);
4115198429Srpaulo	calib->state = IWN_CALIB_STATE_RUN;
4116198429Srpaulo
4117198429Srpaulo#ifdef notyet
4118198429Srpaulo	/* XXX Disable RX chains with no antennas connected. */
4119201209Srpaulo	sc->rxon.rxchain = htole16(IWN_RXCHAIN_SEL(sc->chainmask));
4120201209Srpaulo	(void)iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, hal->rxonsz, 1);
4121198429Srpaulo#endif
4122198429Srpaulo
4123198429Srpaulo#if 0
4124198429Srpaulo	/* XXX: not yet */
4125198429Srpaulo	/* Enable power-saving mode if requested by user. */
4126198429Srpaulo	if (sc->sc_ic.ic_flags & IEEE80211_F_PMGTON)
4127198429Srpaulo		(void)iwn_set_pslevel(sc, 0, 3, 1);
4128198429Srpaulo#endif
4129198429Srpaulo}
4130198429Srpaulo
4131206477Sbschmidtstatic int
4132198429Srpauloiwn4965_init_gains(struct iwn_softc *sc)
4133198429Srpaulo{
4134198429Srpaulo	struct iwn_phy_calib_gain cmd;
4135198429Srpaulo
4136198429Srpaulo	memset(&cmd, 0, sizeof cmd);
4137198429Srpaulo	cmd.code = IWN4965_PHY_CALIB_DIFF_GAIN;
4138198429Srpaulo	/* Differential gains initially set to 0 for all 3 antennas. */
4139198429Srpaulo	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4140198429Srpaulo	    "%s: setting initial differential gains\n", __func__);
4141198429Srpaulo	return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1);
4142198429Srpaulo}
4143198429Srpaulo
4144206477Sbschmidtstatic int
4145198429Srpauloiwn5000_init_gains(struct iwn_softc *sc)
4146198429Srpaulo{
4147198429Srpaulo	struct iwn_phy_calib cmd;
4148198429Srpaulo
4149198429Srpaulo	memset(&cmd, 0, sizeof cmd);
4150198429Srpaulo	cmd.code = IWN5000_PHY_CALIB_RESET_NOISE_GAIN;
4151198429Srpaulo	cmd.ngroups = 1;
4152198429Srpaulo	cmd.isvalid = 1;
4153198429Srpaulo	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4154198429Srpaulo	    "%s: setting initial differential gains\n", __func__);
4155198429Srpaulo	return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1);
4156198429Srpaulo}
4157198429Srpaulo
4158206477Sbschmidtstatic int
4159198429Srpauloiwn4965_set_gains(struct iwn_softc *sc)
4160198429Srpaulo{
4161198429Srpaulo	struct iwn_calib_state *calib = &sc->calib;
4162198429Srpaulo	struct iwn_phy_calib_gain cmd;
4163198429Srpaulo	int i, delta, noise;
4164198429Srpaulo
4165198429Srpaulo	/* Get minimal noise among connected antennas. */
4166201209Srpaulo	noise = INT_MAX;	/* NB: There's at least one antenna. */
4167178676Ssam	for (i = 0; i < 3; i++)
4168201209Srpaulo		if (sc->chainmask & (1 << i))
4169198429Srpaulo			noise = MIN(calib->noise[i], noise);
4170178676Ssam
4171178676Ssam	memset(&cmd, 0, sizeof cmd);
4172198429Srpaulo	cmd.code = IWN4965_PHY_CALIB_DIFF_GAIN;
4173198429Srpaulo	/* Set differential gains for connected antennas. */
4174178676Ssam	for (i = 0; i < 3; i++) {
4175201209Srpaulo		if (sc->chainmask & (1 << i)) {
4176198429Srpaulo			/* Compute attenuation (in unit of 1.5dB). */
4177198429Srpaulo			delta = (noise - (int32_t)calib->noise[i]) / 30;
4178198429Srpaulo			/* NB: delta <= 0 */
4179198429Srpaulo			/* Limit to [-4.5dB,0]. */
4180198429Srpaulo			cmd.gain[i] = MIN(abs(delta), 3);
4181198429Srpaulo			if (delta < 0)
4182198429Srpaulo				cmd.gain[i] |= 1 << 2;	/* sign bit */
4183178676Ssam		}
4184178676Ssam	}
4185178676Ssam	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4186198429Srpaulo	    "setting differential gains Ant A/B/C: %x/%x/%x (%x)\n",
4187201209Srpaulo	    cmd.gain[0], cmd.gain[1], cmd.gain[2], sc->chainmask);
4188198429Srpaulo	return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1);
4189178676Ssam}
4190178676Ssam
4191206477Sbschmidtstatic int
4192198429Srpauloiwn5000_set_gains(struct iwn_softc *sc)
4193198429Srpaulo{
4194198429Srpaulo	struct iwn_calib_state *calib = &sc->calib;
4195198429Srpaulo	struct iwn_phy_calib_gain cmd;
4196206444Sbschmidt	int i, ant, delta, div;
4197198429Srpaulo
4198206444Sbschmidt	/* We collected 20 beacons and !=6050 need a 1.5 factor. */
4199206444Sbschmidt	div = (sc->hw_type == IWN_HW_REV_TYPE_6050) ? 20 : 30;
4200198429Srpaulo
4201198429Srpaulo	memset(&cmd, 0, sizeof cmd);
4202198429Srpaulo	cmd.code = IWN5000_PHY_CALIB_NOISE_GAIN;
4203198429Srpaulo	cmd.ngroups = 1;
4204198429Srpaulo	cmd.isvalid = 1;
4205201209Srpaulo	/* Get first available RX antenna as referential. */
4206201209Srpaulo	ant = IWN_LSB(sc->rxchainmask);
4207201209Srpaulo	/* Set differential gains for other antennas. */
4208201209Srpaulo	for (i = ant + 1; i < 3; i++) {
4209201209Srpaulo		if (sc->chainmask & (1 << i)) {
4210201209Srpaulo			/* The delta is relative to antenna "ant". */
4211201209Srpaulo			delta = ((int32_t)calib->noise[ant] -
4212206444Sbschmidt			    (int32_t)calib->noise[i]) / div;
4213198429Srpaulo			/* Limit to [-4.5dB,+4.5dB]. */
4214198429Srpaulo			cmd.gain[i - 1] = MIN(abs(delta), 3);
4215198429Srpaulo			if (delta < 0)
4216198429Srpaulo				cmd.gain[i - 1] |= 1 << 2;	/* sign bit */
4217198429Srpaulo		}
4218198429Srpaulo	}
4219198429Srpaulo	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4220198429Srpaulo	    "setting differential gains Ant B/C: %x/%x (%x)\n",
4221201209Srpaulo	    cmd.gain[0], cmd.gain[1], sc->chainmask);
4222198429Srpaulo	return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1);
4223198429Srpaulo}
4224198429Srpaulo
4225178676Ssam/*
4226198429Srpaulo * Tune RF RX sensitivity based on the number of false alarms detected
4227178676Ssam * during the last beacon period.
4228178676Ssam */
4229206477Sbschmidtstatic void
4230178676Ssamiwn_tune_sensitivity(struct iwn_softc *sc, const struct iwn_rx_stats *stats)
4231178676Ssam{
4232198429Srpaulo#define inc(val, inc, max)			\
4233178676Ssam	if ((val) < (max)) {			\
4234178676Ssam		if ((val) < (max) - (inc))	\
4235178676Ssam			(val) += (inc);		\
4236178676Ssam		else				\
4237178676Ssam			(val) = (max);		\
4238178676Ssam		needs_update = 1;		\
4239178676Ssam	}
4240198429Srpaulo#define dec(val, dec, min)			\
4241178676Ssam	if ((val) > (min)) {			\
4242178676Ssam		if ((val) > (min) + (dec))	\
4243178676Ssam			(val) -= (dec);		\
4244178676Ssam		else				\
4245178676Ssam			(val) = (min);		\
4246178676Ssam		needs_update = 1;		\
4247178676Ssam	}
4248178676Ssam
4249201209Srpaulo	const struct iwn_sensitivity_limits *limits = sc->limits;
4250178676Ssam	struct iwn_calib_state *calib = &sc->calib;
4251178676Ssam	uint32_t val, rxena, fa;
4252178676Ssam	uint32_t energy[3], energy_min;
4253198439Srpaulo	uint8_t noise[3], noise_ref;
4254198429Srpaulo	int i, needs_update = 0;
4255178676Ssam
4256198429Srpaulo	/* Check that we've been enabled long enough. */
4257198439Srpaulo	rxena = le32toh(stats->general.load);
4258198439Srpaulo	if (rxena == 0)
4259178676Ssam		return;
4260178676Ssam
4261198429Srpaulo	/* Compute number of false alarms since last call for OFDM. */
4262178676Ssam	fa  = le32toh(stats->ofdm.bad_plcp) - calib->bad_plcp_ofdm;
4263178676Ssam	fa += le32toh(stats->ofdm.fa) - calib->fa_ofdm;
4264220634Sbschmidt	fa *= 200 * IEEE80211_DUR_TU;	/* 200TU */
4265178676Ssam
4266198429Srpaulo	/* Save counters values for next call. */
4267178676Ssam	calib->bad_plcp_ofdm = le32toh(stats->ofdm.bad_plcp);
4268178676Ssam	calib->fa_ofdm = le32toh(stats->ofdm.fa);
4269178676Ssam
4270178676Ssam	if (fa > 50 * rxena) {
4271198429Srpaulo		/* High false alarm count, decrease sensitivity. */
4272178676Ssam		DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4273178676Ssam		    "%s: OFDM high false alarm count: %u\n", __func__, fa);
4274198429Srpaulo		inc(calib->ofdm_x1,     1, limits->max_ofdm_x1);
4275198429Srpaulo		inc(calib->ofdm_mrc_x1, 1, limits->max_ofdm_mrc_x1);
4276198429Srpaulo		inc(calib->ofdm_x4,     1, limits->max_ofdm_x4);
4277198429Srpaulo		inc(calib->ofdm_mrc_x4, 1, limits->max_ofdm_mrc_x4);
4278178676Ssam
4279178676Ssam	} else if (fa < 5 * rxena) {
4280198429Srpaulo		/* Low false alarm count, increase sensitivity. */
4281178676Ssam		DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4282178676Ssam		    "%s: OFDM low false alarm count: %u\n", __func__, fa);
4283198429Srpaulo		dec(calib->ofdm_x1,     1, limits->min_ofdm_x1);
4284198429Srpaulo		dec(calib->ofdm_mrc_x1, 1, limits->min_ofdm_mrc_x1);
4285198429Srpaulo		dec(calib->ofdm_x4,     1, limits->min_ofdm_x4);
4286198429Srpaulo		dec(calib->ofdm_mrc_x4, 1, limits->min_ofdm_mrc_x4);
4287178676Ssam	}
4288178676Ssam
4289198429Srpaulo	/* Compute maximum noise among 3 receivers. */
4290178676Ssam	for (i = 0; i < 3; i++)
4291178676Ssam		noise[i] = (le32toh(stats->general.noise[i]) >> 8) & 0xff;
4292198429Srpaulo	val = MAX(noise[0], noise[1]);
4293198429Srpaulo	val = MAX(noise[2], val);
4294198429Srpaulo	/* Insert it into our samples table. */
4295178676Ssam	calib->noise_samples[calib->cur_noise_sample] = val;
4296178676Ssam	calib->cur_noise_sample = (calib->cur_noise_sample + 1) % 20;
4297178676Ssam
4298198429Srpaulo	/* Compute maximum noise among last 20 samples. */
4299178676Ssam	noise_ref = calib->noise_samples[0];
4300178676Ssam	for (i = 1; i < 20; i++)
4301198429Srpaulo		noise_ref = MAX(noise_ref, calib->noise_samples[i]);
4302178676Ssam
4303198429Srpaulo	/* Compute maximum energy among 3 receivers. */
4304178676Ssam	for (i = 0; i < 3; i++)
4305178676Ssam		energy[i] = le32toh(stats->general.energy[i]);
4306198429Srpaulo	val = MIN(energy[0], energy[1]);
4307198429Srpaulo	val = MIN(energy[2], val);
4308198429Srpaulo	/* Insert it into our samples table. */
4309178676Ssam	calib->energy_samples[calib->cur_energy_sample] = val;
4310178676Ssam	calib->cur_energy_sample = (calib->cur_energy_sample + 1) % 10;
4311178676Ssam
4312198429Srpaulo	/* Compute minimum energy among last 10 samples. */
4313178676Ssam	energy_min = calib->energy_samples[0];
4314178676Ssam	for (i = 1; i < 10; i++)
4315198429Srpaulo		energy_min = MAX(energy_min, calib->energy_samples[i]);
4316178676Ssam	energy_min += 6;
4317178676Ssam
4318198429Srpaulo	/* Compute number of false alarms since last call for CCK. */
4319178676Ssam	fa  = le32toh(stats->cck.bad_plcp) - calib->bad_plcp_cck;
4320178676Ssam	fa += le32toh(stats->cck.fa) - calib->fa_cck;
4321220634Sbschmidt	fa *= 200 * IEEE80211_DUR_TU;	/* 200TU */
4322178676Ssam
4323198429Srpaulo	/* Save counters values for next call. */
4324178676Ssam	calib->bad_plcp_cck = le32toh(stats->cck.bad_plcp);
4325178676Ssam	calib->fa_cck = le32toh(stats->cck.fa);
4326178676Ssam
4327178676Ssam	if (fa > 50 * rxena) {
4328198429Srpaulo		/* High false alarm count, decrease sensitivity. */
4329178676Ssam		DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4330178676Ssam		    "%s: CCK high false alarm count: %u\n", __func__, fa);
4331178676Ssam		calib->cck_state = IWN_CCK_STATE_HIFA;
4332178676Ssam		calib->low_fa = 0;
4333178676Ssam
4334198429Srpaulo		if (calib->cck_x4 > 160) {
4335178676Ssam			calib->noise_ref = noise_ref;
4336178676Ssam			if (calib->energy_cck > 2)
4337198429Srpaulo				dec(calib->energy_cck, 2, energy_min);
4338178676Ssam		}
4339198429Srpaulo		if (calib->cck_x4 < 160) {
4340198429Srpaulo			calib->cck_x4 = 161;
4341178676Ssam			needs_update = 1;
4342178676Ssam		} else
4343198429Srpaulo			inc(calib->cck_x4, 3, limits->max_cck_x4);
4344178676Ssam
4345198429Srpaulo		inc(calib->cck_mrc_x4, 3, limits->max_cck_mrc_x4);
4346178676Ssam
4347178676Ssam	} else if (fa < 5 * rxena) {
4348198429Srpaulo		/* Low false alarm count, increase sensitivity. */
4349178676Ssam		DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4350178676Ssam		    "%s: CCK low false alarm count: %u\n", __func__, fa);
4351178676Ssam		calib->cck_state = IWN_CCK_STATE_LOFA;
4352178676Ssam		calib->low_fa++;
4353178676Ssam
4354198429Srpaulo		if (calib->cck_state != IWN_CCK_STATE_INIT &&
4355198429Srpaulo		    (((int32_t)calib->noise_ref - (int32_t)noise_ref) > 2 ||
4356206443Sbschmidt		    calib->low_fa > 100)) {
4357198429Srpaulo			inc(calib->energy_cck, 2, limits->min_energy_cck);
4358198429Srpaulo			dec(calib->cck_x4,     3, limits->min_cck_x4);
4359198429Srpaulo			dec(calib->cck_mrc_x4, 3, limits->min_cck_mrc_x4);
4360178676Ssam		}
4361178676Ssam	} else {
4362198429Srpaulo		/* Not worth to increase or decrease sensitivity. */
4363178676Ssam		DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4364178676Ssam		    "%s: CCK normal false alarm count: %u\n", __func__, fa);
4365178676Ssam		calib->low_fa = 0;
4366178676Ssam		calib->noise_ref = noise_ref;
4367178676Ssam
4368178676Ssam		if (calib->cck_state == IWN_CCK_STATE_HIFA) {
4369198429Srpaulo			/* Previous interval had many false alarms. */
4370198429Srpaulo			dec(calib->energy_cck, 8, energy_min);
4371178676Ssam		}
4372178676Ssam		calib->cck_state = IWN_CCK_STATE_INIT;
4373178676Ssam	}
4374178676Ssam
4375178676Ssam	if (needs_update)
4376178676Ssam		(void)iwn_send_sensitivity(sc);
4377198429Srpaulo#undef dec
4378198429Srpaulo#undef inc
4379178676Ssam}
4380178676Ssam
4381206477Sbschmidtstatic int
4382178676Ssamiwn_send_sensitivity(struct iwn_softc *sc)
4383178676Ssam{
4384178676Ssam	struct iwn_calib_state *calib = &sc->calib;
4385178676Ssam	struct iwn_sensitivity_cmd cmd;
4386178676Ssam
4387178676Ssam	memset(&cmd, 0, sizeof cmd);
4388178676Ssam	cmd.which = IWN_SENSITIVITY_WORKTBL;
4389198429Srpaulo	/* OFDM modulation. */
4390198429Srpaulo	cmd.corr_ofdm_x1     = htole16(calib->ofdm_x1);
4391198429Srpaulo	cmd.corr_ofdm_mrc_x1 = htole16(calib->ofdm_mrc_x1);
4392198429Srpaulo	cmd.corr_ofdm_x4     = htole16(calib->ofdm_x4);
4393198429Srpaulo	cmd.corr_ofdm_mrc_x4 = htole16(calib->ofdm_mrc_x4);
4394201209Srpaulo	cmd.energy_ofdm      = htole16(sc->limits->energy_ofdm);
4395178676Ssam	cmd.energy_ofdm_th   = htole16(62);
4396198429Srpaulo	/* CCK modulation. */
4397198429Srpaulo	cmd.corr_cck_x4      = htole16(calib->cck_x4);
4398198429Srpaulo	cmd.corr_cck_mrc_x4  = htole16(calib->cck_mrc_x4);
4399178676Ssam	cmd.energy_cck       = htole16(calib->energy_cck);
4400198429Srpaulo	/* Barker modulation: use default values. */
4401178676Ssam	cmd.corr_barker      = htole16(190);
4402178676Ssam	cmd.corr_barker_mrc  = htole16(390);
4403178676Ssam
4404202986Srpaulo	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4405178676Ssam	    "%s: set sensitivity %d/%d/%d/%d/%d/%d/%d\n", __func__,
4406198429Srpaulo	    calib->ofdm_x1, calib->ofdm_mrc_x1, calib->ofdm_x4,
4407198429Srpaulo	    calib->ofdm_mrc_x4, calib->cck_x4,
4408198429Srpaulo	    calib->cck_mrc_x4, calib->energy_cck);
4409198429Srpaulo	return iwn_cmd(sc, IWN_CMD_SET_SENSITIVITY, &cmd, sizeof cmd, 1);
4410178676Ssam}
4411178676Ssam
4412198429Srpaulo/*
4413198429Srpaulo * Set STA mode power saving level (between 0 and 5).
4414198429Srpaulo * Level 0 is CAM (Continuously Aware Mode), 5 is for maximum power saving.
4415198429Srpaulo */
4416206477Sbschmidtstatic int
4417198429Srpauloiwn_set_pslevel(struct iwn_softc *sc, int dtim, int level, int async)
4418198429Srpaulo{
4419198429Srpaulo	const struct iwn_pmgt *pmgt;
4420198429Srpaulo	struct iwn_pmgt_cmd cmd;
4421198429Srpaulo	uint32_t max, skip_dtim;
4422198429Srpaulo	uint32_t tmp;
4423198429Srpaulo	int i;
4424198429Srpaulo
4425198429Srpaulo	/* Select which PS parameters to use. */
4426198429Srpaulo	if (dtim <= 2)
4427198429Srpaulo		pmgt = &iwn_pmgt[0][level];
4428198429Srpaulo	else if (dtim <= 10)
4429198429Srpaulo		pmgt = &iwn_pmgt[1][level];
4430198429Srpaulo	else
4431198429Srpaulo		pmgt = &iwn_pmgt[2][level];
4432198429Srpaulo
4433198429Srpaulo	memset(&cmd, 0, sizeof cmd);
4434198429Srpaulo	if (level != 0)	/* not CAM */
4435198429Srpaulo		cmd.flags |= htole16(IWN_PS_ALLOW_SLEEP);
4436198429Srpaulo	if (level == 5)
4437198429Srpaulo		cmd.flags |= htole16(IWN_PS_FAST_PD);
4438201209Srpaulo	/* Retrieve PCIe Active State Power Management (ASPM). */
4439198429Srpaulo	tmp = pci_read_config(sc->sc_dev, sc->sc_cap_off + 0x10, 1);
4440198429Srpaulo	if (!(tmp & 0x1))	/* L0s Entry disabled. */
4441198429Srpaulo		cmd.flags |= htole16(IWN_PS_PCI_PMGT);
4442198429Srpaulo	cmd.rxtimeout = htole32(pmgt->rxtimeout * 1024);
4443198429Srpaulo	cmd.txtimeout = htole32(pmgt->txtimeout * 1024);
4444198429Srpaulo
4445198429Srpaulo	if (dtim == 0) {
4446198429Srpaulo		dtim = 1;
4447198429Srpaulo		skip_dtim = 0;
4448198429Srpaulo	} else
4449198429Srpaulo		skip_dtim = pmgt->skip_dtim;
4450198429Srpaulo	if (skip_dtim != 0) {
4451198429Srpaulo		cmd.flags |= htole16(IWN_PS_SLEEP_OVER_DTIM);
4452198429Srpaulo		max = pmgt->intval[4];
4453198429Srpaulo		if (max == (uint32_t)-1)
4454198429Srpaulo			max = dtim * (skip_dtim + 1);
4455198429Srpaulo		else if (max > dtim)
4456198429Srpaulo			max = (max / dtim) * dtim;
4457198429Srpaulo	} else
4458198429Srpaulo		max = dtim;
4459198429Srpaulo	for (i = 0; i < 5; i++)
4460198429Srpaulo		cmd.intval[i] = htole32(MIN(max, pmgt->intval[i]));
4461198429Srpaulo
4462198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "setting power saving level to %d\n",
4463198429Srpaulo	    level);
4464198429Srpaulo	return iwn_cmd(sc, IWN_CMD_SET_POWER_MODE, &cmd, sizeof cmd, async);
4465198429Srpaulo}
4466198429Srpaulo
4467206477Sbschmidtstatic int
4468220662Sbschmidtiwn_send_btcoex(struct iwn_softc *sc)
4469220662Sbschmidt{
4470220662Sbschmidt	struct iwn_bluetooth cmd;
4471220662Sbschmidt
4472220662Sbschmidt	memset(&cmd, 0, sizeof cmd);
4473220662Sbschmidt	cmd.flags = IWN_BT_COEX_CHAN_ANN | IWN_BT_COEX_BT_PRIO;
4474220662Sbschmidt	cmd.lead_time = IWN_BT_LEAD_TIME_DEF;
4475220662Sbschmidt	cmd.max_kill = IWN_BT_MAX_KILL_DEF;
4476220662Sbschmidt	DPRINTF(sc, IWN_DEBUG_RESET, "%s: configuring bluetooth coexistence\n",
4477220662Sbschmidt	    __func__);
4478220662Sbschmidt	return iwn_cmd(sc, IWN_CMD_BT_COEX, &cmd, sizeof(cmd), 0);
4479220662Sbschmidt}
4480220662Sbschmidt
4481220662Sbschmidtstatic int
4482198429Srpauloiwn_config(struct iwn_softc *sc)
4483198429Srpaulo{
4484198429Srpaulo	const struct iwn_hal *hal = sc->sc_hal;
4485198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
4486198429Srpaulo	struct ieee80211com *ic = ifp->if_l2com;
4487201209Srpaulo	uint32_t txmask;
4488198429Srpaulo	int error;
4489198429Srpaulo	uint16_t rxchain;
4490198429Srpaulo
4491201209Srpaulo	/* Configure valid TX chains for 5000 Series. */
4492201209Srpaulo	if (sc->hw_type != IWN_HW_REV_TYPE_4965) {
4493201209Srpaulo		txmask = htole32(sc->txchainmask);
4494201209Srpaulo		DPRINTF(sc, IWN_DEBUG_RESET,
4495201209Srpaulo		    "%s: configuring valid TX chains 0x%x\n", __func__, txmask);
4496201209Srpaulo		error = iwn_cmd(sc, IWN5000_CMD_TX_ANT_CONFIG, &txmask,
4497201209Srpaulo		    sizeof txmask, 0);
4498201209Srpaulo		if (error != 0) {
4499201209Srpaulo			device_printf(sc->sc_dev,
4500201209Srpaulo			    "%s: could not configure valid TX chains, "
4501201209Srpaulo			    "error %d\n", __func__, error);
4502201209Srpaulo			return error;
4503201209Srpaulo		}
4504198429Srpaulo	}
4505198429Srpaulo
4506198429Srpaulo	/* Configure bluetooth coexistence. */
4507220662Sbschmidt	error = iwn_send_btcoex(sc);
4508198429Srpaulo	if (error != 0) {
4509198429Srpaulo		device_printf(sc->sc_dev,
4510198429Srpaulo		    "%s: could not configure bluetooth coexistence, error %d\n",
4511198429Srpaulo		    __func__, error);
4512198429Srpaulo		return error;
4513198429Srpaulo	}
4514198429Srpaulo
4515201209Srpaulo	/* Set mode, channel, RX filter and enable RX. */
4516198429Srpaulo	memset(&sc->rxon, 0, sizeof (struct iwn_rxon));
4517198429Srpaulo	IEEE80211_ADDR_COPY(sc->rxon.myaddr, IF_LLADDR(ifp));
4518198429Srpaulo	IEEE80211_ADDR_COPY(sc->rxon.wlap, IF_LLADDR(ifp));
4519198429Srpaulo	sc->rxon.chan = ieee80211_chan2ieee(ic, ic->ic_curchan);
4520198429Srpaulo	sc->rxon.flags = htole32(IWN_RXON_TSF | IWN_RXON_CTS_TO_SELF);
4521198429Srpaulo	if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan))
4522198429Srpaulo		sc->rxon.flags |= htole32(IWN_RXON_AUTO | IWN_RXON_24GHZ);
4523198429Srpaulo	switch (ic->ic_opmode) {
4524198429Srpaulo	case IEEE80211_M_STA:
4525198429Srpaulo		sc->rxon.mode = IWN_MODE_STA;
4526198429Srpaulo		sc->rxon.filter = htole32(IWN_FILTER_MULTICAST);
4527198429Srpaulo		break;
4528198429Srpaulo	case IEEE80211_M_MONITOR:
4529198429Srpaulo		sc->rxon.mode = IWN_MODE_MONITOR;
4530198429Srpaulo		sc->rxon.filter = htole32(IWN_FILTER_MULTICAST |
4531198429Srpaulo		    IWN_FILTER_CTL | IWN_FILTER_PROMISC);
4532198429Srpaulo		break;
4533198429Srpaulo	default:
4534198429Srpaulo		/* Should not get there. */
4535198429Srpaulo		break;
4536198429Srpaulo	}
4537198429Srpaulo	sc->rxon.cck_mask  = 0x0f;	/* not yet negotiated */
4538198429Srpaulo	sc->rxon.ofdm_mask = 0xff;	/* not yet negotiated */
4539198429Srpaulo	sc->rxon.ht_single_mask = 0xff;
4540198429Srpaulo	sc->rxon.ht_dual_mask = 0xff;
4541201209Srpaulo	sc->rxon.ht_triple_mask = 0xff;
4542201209Srpaulo	rxchain =
4543201209Srpaulo	    IWN_RXCHAIN_VALID(sc->rxchainmask) |
4544201209Srpaulo	    IWN_RXCHAIN_MIMO_COUNT(2) |
4545201209Srpaulo	    IWN_RXCHAIN_IDLE_COUNT(2);
4546198429Srpaulo	sc->rxon.rxchain = htole16(rxchain);
4547198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "%s: setting configuration\n", __func__);
4548201209Srpaulo	error = iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, hal->rxonsz, 0);
4549198429Srpaulo	if (error != 0) {
4550198429Srpaulo		device_printf(sc->sc_dev,
4551201209Srpaulo		    "%s: RXON command failed\n", __func__);
4552198429Srpaulo		return error;
4553198429Srpaulo	}
4554198429Srpaulo
4555201209Srpaulo	error = iwn_add_broadcast_node(sc, 0);
4556201209Srpaulo	if (error != 0) {
4557201209Srpaulo		device_printf(sc->sc_dev,
4558201209Srpaulo		    "%s: could not add broadcast node\n", __func__);
4559201209Srpaulo		return error;
4560201209Srpaulo	}
4561201209Srpaulo
4562198429Srpaulo	/* Configuration has changed, set TX power accordingly. */
4563201882Skeramida	error = hal->set_txpower(sc, ic->ic_curchan, 0);
4564198429Srpaulo	if (error != 0) {
4565198429Srpaulo		device_printf(sc->sc_dev,
4566198429Srpaulo		    "%s: could not set TX power\n", __func__);
4567198429Srpaulo		return error;
4568198429Srpaulo	}
4569198429Srpaulo
4570201209Srpaulo	error = iwn_set_critical_temp(sc);
4571198429Srpaulo	if (error != 0) {
4572198429Srpaulo		device_printf(sc->sc_dev,
4573201209Srpaulo		    "%s: ccould not set critical temperature\n", __func__);
4574198429Srpaulo		return error;
4575198429Srpaulo	}
4576198429Srpaulo
4577201209Srpaulo	/* Set power saving level to CAM during initialization. */
4578201209Srpaulo	error = iwn_set_pslevel(sc, 0, 0, 0);
4579198429Srpaulo	if (error != 0) {
4580198429Srpaulo		device_printf(sc->sc_dev,
4581201209Srpaulo		    "%s: could not set power saving level\n", __func__);
4582198429Srpaulo		return error;
4583198429Srpaulo	}
4584198429Srpaulo	return 0;
4585198429Srpaulo}
4586198429Srpaulo
4587220634Sbschmidt/*
4588220634Sbschmidt * Add an ssid element to a frame.
4589220634Sbschmidt */
4590220634Sbschmidtstatic uint8_t *
4591220634Sbschmidtieee80211_add_ssid(uint8_t *frm, const uint8_t *ssid, u_int len)
4592220634Sbschmidt{
4593220634Sbschmidt	*frm++ = IEEE80211_ELEMID_SSID;
4594220634Sbschmidt	*frm++ = len;
4595220634Sbschmidt	memcpy(frm, ssid, len);
4596220634Sbschmidt	return frm + len;
4597220634Sbschmidt}
4598220634Sbschmidt
4599206477Sbschmidtstatic int
4600198429Srpauloiwn_scan(struct iwn_softc *sc)
4601198429Srpaulo{
4602198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
4603198429Srpaulo	struct ieee80211com *ic = ifp->if_l2com;
4604198429Srpaulo	struct ieee80211_scan_state *ss = ic->ic_scan;	/*XXX*/
4605198429Srpaulo	struct iwn_scan_hdr *hdr;
4606198429Srpaulo	struct iwn_cmd_data *tx;
4607198429Srpaulo	struct iwn_scan_essid *essid;
4608198429Srpaulo	struct iwn_scan_chan *chan;
4609198429Srpaulo	struct ieee80211_frame *wh;
4610198429Srpaulo	struct ieee80211_rateset *rs;
4611198429Srpaulo	struct ieee80211_channel *c;
4612220634Sbschmidt	int buflen, error;
4613198429Srpaulo	uint16_t rxchain;
4614198429Srpaulo	uint8_t *buf, *frm, txant;
4615198429Srpaulo
4616198429Srpaulo	buf = malloc(IWN_SCAN_MAXSZ, M_DEVBUF, M_NOWAIT | M_ZERO);
4617198429Srpaulo	if (buf == NULL) {
4618198429Srpaulo		device_printf(sc->sc_dev,
4619198429Srpaulo		    "%s: could not allocate buffer for scan command\n",
4620198429Srpaulo		    __func__);
4621198429Srpaulo		return ENOMEM;
4622198429Srpaulo	}
4623198429Srpaulo	hdr = (struct iwn_scan_hdr *)buf;
4624198429Srpaulo
4625198429Srpaulo	/*
4626198429Srpaulo	 * Move to the next channel if no frames are received within 10ms
4627198429Srpaulo	 * after sending the probe request.
4628198429Srpaulo	 */
4629198429Srpaulo	hdr->quiet_time = htole16(10);		/* timeout in milliseconds */
4630198429Srpaulo	hdr->quiet_threshold = htole16(1);	/* min # of packets */
4631198429Srpaulo
4632198429Srpaulo	/* Select antennas for scanning. */
4633201209Srpaulo	rxchain =
4634201209Srpaulo	    IWN_RXCHAIN_VALID(sc->rxchainmask) |
4635201209Srpaulo	    IWN_RXCHAIN_FORCE_MIMO_SEL(sc->rxchainmask) |
4636201209Srpaulo	    IWN_RXCHAIN_DRIVER_FORCE;
4637198429Srpaulo	if (IEEE80211_IS_CHAN_A(ic->ic_curchan) &&
4638198429Srpaulo	    sc->hw_type == IWN_HW_REV_TYPE_4965) {
4639198429Srpaulo		/* Ant A must be avoided in 5GHz because of an HW bug. */
4640201209Srpaulo		rxchain |= IWN_RXCHAIN_FORCE_SEL(IWN_ANT_BC);
4641198429Srpaulo	} else	/* Use all available RX antennas. */
4642201209Srpaulo		rxchain |= IWN_RXCHAIN_FORCE_SEL(sc->rxchainmask);
4643198429Srpaulo	hdr->rxchain = htole16(rxchain);
4644198429Srpaulo	hdr->filter = htole32(IWN_FILTER_MULTICAST | IWN_FILTER_BEACON);
4645198429Srpaulo
4646198429Srpaulo	tx = (struct iwn_cmd_data *)(hdr + 1);
4647198429Srpaulo	tx->flags = htole32(IWN_TX_AUTO_SEQ);
4648198429Srpaulo	tx->id = sc->sc_hal->broadcast_id;
4649198429Srpaulo	tx->lifetime = htole32(IWN_LIFETIME_INFINITE);
4650198429Srpaulo
4651198429Srpaulo	if (IEEE80211_IS_CHAN_A(ic->ic_curchan)) {
4652198429Srpaulo		/* Send probe requests at 6Mbps. */
4653198429Srpaulo		tx->plcp = iwn_rates[IWN_RIDX_OFDM6].plcp;
4654201209Srpaulo		rs = &ic->ic_sup_rates[IEEE80211_MODE_11A];
4655198429Srpaulo	} else {
4656198429Srpaulo		hdr->flags = htole32(IWN_RXON_24GHZ | IWN_RXON_AUTO);
4657198429Srpaulo		/* Send probe requests at 1Mbps. */
4658198429Srpaulo		tx->plcp = iwn_rates[IWN_RIDX_CCK1].plcp;
4659198429Srpaulo		tx->rflags = IWN_RFLAG_CCK;
4660201209Srpaulo		rs = &ic->ic_sup_rates[IEEE80211_MODE_11G];
4661198429Srpaulo	}
4662198429Srpaulo	/* Use the first valid TX antenna. */
4663201209Srpaulo	txant = IWN_LSB(sc->txchainmask);
4664198429Srpaulo	tx->rflags |= IWN_RFLAG_ANT(txant);
4665198429Srpaulo
4666198429Srpaulo	essid = (struct iwn_scan_essid *)(tx + 1);
4667198429Srpaulo	if (ss->ss_ssid[0].len != 0) {
4668198429Srpaulo		essid[0].id = IEEE80211_ELEMID_SSID;
4669198429Srpaulo		essid[0].len = ss->ss_ssid[0].len;
4670198429Srpaulo		memcpy(essid[0].data, ss->ss_ssid[0].ssid, ss->ss_ssid[0].len);
4671198429Srpaulo	}
4672201209Srpaulo
4673198429Srpaulo	/*
4674198429Srpaulo	 * Build a probe request frame.  Most of the following code is a
4675198429Srpaulo	 * copy & paste of what is done in net80211.
4676198429Srpaulo	 */
4677198429Srpaulo	wh = (struct ieee80211_frame *)(essid + 20);
4678198429Srpaulo	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT |
4679198429Srpaulo	    IEEE80211_FC0_SUBTYPE_PROBE_REQ;
4680198429Srpaulo	wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
4681198429Srpaulo	IEEE80211_ADDR_COPY(wh->i_addr1, ifp->if_broadcastaddr);
4682198429Srpaulo	IEEE80211_ADDR_COPY(wh->i_addr2, IF_LLADDR(ifp));
4683198429Srpaulo	IEEE80211_ADDR_COPY(wh->i_addr3, ifp->if_broadcastaddr);
4684198429Srpaulo	*(uint16_t *)&wh->i_dur[0] = 0;	/* filled by HW */
4685198429Srpaulo	*(uint16_t *)&wh->i_seq[0] = 0;	/* filled by HW */
4686198429Srpaulo
4687198429Srpaulo	frm = (uint8_t *)(wh + 1);
4688220634Sbschmidt	frm = ieee80211_add_ssid(frm, NULL, 0);
4689220634Sbschmidt	frm = ieee80211_add_rates(frm, rs);
4690220634Sbschmidt	if (rs->rs_nrates > IEEE80211_RATE_SIZE)
4691220634Sbschmidt		frm = ieee80211_add_xrates(frm, rs);
4692220634Sbschmidt#if 0	/* HT */
4693220634Sbschmidt	if (ic->ic_flags & IEEE80211_F_HTON)
4694220634Sbschmidt		frm = ieee80211_add_htcaps(frm, ic);
4695220634Sbschmidt#endif
4696198429Srpaulo
4697198429Srpaulo	/* Set length of probe request. */
4698198429Srpaulo	tx->len = htole16(frm - (uint8_t *)wh);
4699198429Srpaulo
4700198429Srpaulo	c = ic->ic_curchan;
4701198429Srpaulo	chan = (struct iwn_scan_chan *)frm;
4702201209Srpaulo	chan->chan = htole16(ieee80211_chan2ieee(ic, c));
4703198429Srpaulo	chan->flags = 0;
4704198429Srpaulo	if (ss->ss_nssid > 0)
4705198429Srpaulo		chan->flags |= htole32(IWN_CHAN_NPBREQS(1));
4706198429Srpaulo	chan->dsp_gain = 0x6e;
4707201209Srpaulo	if (IEEE80211_IS_CHAN_5GHZ(c) &&
4708201209Srpaulo	    !(c->ic_flags & IEEE80211_CHAN_PASSIVE)) {
4709198429Srpaulo		chan->rf_gain = 0x3b;
4710198429Srpaulo		chan->active  = htole16(24);
4711198429Srpaulo		chan->passive = htole16(110);
4712201209Srpaulo		chan->flags |= htole32(IWN_CHAN_ACTIVE);
4713201209Srpaulo	} else if (IEEE80211_IS_CHAN_5GHZ(c)) {
4714201209Srpaulo		chan->rf_gain = 0x3b;
4715201209Srpaulo		chan->active  = htole16(24);
4716201209Srpaulo		if (sc->rxon.associd)
4717201209Srpaulo			chan->passive = htole16(78);
4718201209Srpaulo		else
4719201209Srpaulo			chan->passive = htole16(110);
4720207709Sbschmidt		hdr->crc_threshold = 0xffff;
4721201209Srpaulo	} else if (!(c->ic_flags & IEEE80211_CHAN_PASSIVE)) {
4722201209Srpaulo		chan->rf_gain = 0x28;
4723201209Srpaulo		chan->active  = htole16(36);
4724201209Srpaulo		chan->passive = htole16(120);
4725201209Srpaulo		chan->flags |= htole32(IWN_CHAN_ACTIVE);
4726198429Srpaulo	} else {
4727198429Srpaulo		chan->rf_gain = 0x28;
4728198429Srpaulo		chan->active  = htole16(36);
4729201209Srpaulo		if (sc->rxon.associd)
4730201209Srpaulo			chan->passive = htole16(88);
4731201209Srpaulo		else
4732201209Srpaulo			chan->passive = htole16(120);
4733207709Sbschmidt		hdr->crc_threshold = 0xffff;
4734198429Srpaulo	}
4735198429Srpaulo
4736201209Srpaulo	DPRINTF(sc, IWN_DEBUG_STATE,
4737201209Srpaulo	    "%s: chan %u flags 0x%x rf_gain 0x%x "
4738198429Srpaulo	    "dsp_gain 0x%x active 0x%x passive 0x%x\n", __func__,
4739198429Srpaulo	    chan->chan, chan->flags, chan->rf_gain, chan->dsp_gain,
4740198429Srpaulo	    chan->active, chan->passive);
4741198429Srpaulo
4742201209Srpaulo	hdr->nchan++;
4743201209Srpaulo	chan++;
4744198429Srpaulo	buflen = (uint8_t *)chan - buf;
4745198429Srpaulo	hdr->len = htole16(buflen);
4746198429Srpaulo
4747198429Srpaulo	DPRINTF(sc, IWN_DEBUG_STATE, "sending scan command nchan=%d\n",
4748198429Srpaulo	    hdr->nchan);
4749198429Srpaulo	error = iwn_cmd(sc, IWN_CMD_SCAN, buf, buflen, 1);
4750198429Srpaulo	free(buf, M_DEVBUF);
4751198429Srpaulo	return error;
4752198429Srpaulo}
4753198429Srpaulo
4754206477Sbschmidtstatic int
4755191746Sthompsaiwn_auth(struct iwn_softc *sc, struct ieee80211vap *vap)
4756178676Ssam{
4757198429Srpaulo	const struct iwn_hal *hal = sc->sc_hal;
4758178676Ssam	struct ifnet *ifp = sc->sc_ifp;
4759178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
4760178676Ssam	struct ieee80211_node *ni = vap->iv_bss;
4761178676Ssam	int error;
4762178676Ssam
4763178676Ssam	sc->calib.state = IWN_CALIB_STATE_INIT;
4764178676Ssam
4765201209Srpaulo	/* Update adapter configuration. */
4766198429Srpaulo	IEEE80211_ADDR_COPY(sc->rxon.bssid, ni->ni_bssid);
4767220636Sbschmidt	sc->rxon.chan = ieee80211_chan2ieee(ic, ni->ni_chan);
4768198429Srpaulo	sc->rxon.flags = htole32(IWN_RXON_TSF | IWN_RXON_CTS_TO_SELF);
4769178676Ssam	if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
4770198429Srpaulo		sc->rxon.flags |= htole32(IWN_RXON_AUTO | IWN_RXON_24GHZ);
4771198429Srpaulo	if (ic->ic_flags & IEEE80211_F_SHSLOT)
4772198429Srpaulo		sc->rxon.flags |= htole32(IWN_RXON_SHSLOT);
4773198429Srpaulo	if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
4774198429Srpaulo		sc->rxon.flags |= htole32(IWN_RXON_SHPREAMBLE);
4775178676Ssam	if (IEEE80211_IS_CHAN_A(ni->ni_chan)) {
4776198429Srpaulo		sc->rxon.cck_mask  = 0;
4777198429Srpaulo		sc->rxon.ofdm_mask = 0x15;
4778178676Ssam	} else if (IEEE80211_IS_CHAN_B(ni->ni_chan)) {
4779198429Srpaulo		sc->rxon.cck_mask  = 0x03;
4780198429Srpaulo		sc->rxon.ofdm_mask = 0;
4781178676Ssam	} else {
4782178676Ssam		/* XXX assume 802.11b/g */
4783198429Srpaulo		sc->rxon.cck_mask  = 0x0f;
4784198429Srpaulo		sc->rxon.ofdm_mask = 0x15;
4785178676Ssam	}
4786178676Ssam	DPRINTF(sc, IWN_DEBUG_STATE,
4787198429Srpaulo	    "%s: config chan %d mode %d flags 0x%x cck 0x%x ofdm 0x%x "
4788198429Srpaulo	    "ht_single 0x%x ht_dual 0x%x rxchain 0x%x "
4789198429Srpaulo	    "myaddr %6D wlap %6D bssid %6D associd %d filter 0x%x\n",
4790198429Srpaulo	    __func__,
4791198429Srpaulo	    le16toh(sc->rxon.chan), sc->rxon.mode, le32toh(sc->rxon.flags),
4792198429Srpaulo	    sc->rxon.cck_mask, sc->rxon.ofdm_mask,
4793198429Srpaulo	    sc->rxon.ht_single_mask, sc->rxon.ht_dual_mask,
4794198429Srpaulo	    le16toh(sc->rxon.rxchain),
4795198429Srpaulo	    sc->rxon.myaddr, ":", sc->rxon.wlap, ":", sc->rxon.bssid, ":",
4796198429Srpaulo	    le16toh(sc->rxon.associd), le32toh(sc->rxon.filter));
4797201209Srpaulo	error = iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, hal->rxonsz, 1);
4798178676Ssam	if (error != 0) {
4799178676Ssam		device_printf(sc->sc_dev,
4800201209Srpaulo		    "%s: RXON command failed, error %d\n", __func__, error);
4801178676Ssam		return error;
4802178676Ssam	}
4803178676Ssam
4804198429Srpaulo	/* Configuration has changed, set TX power accordingly. */
4805201882Skeramida	error = hal->set_txpower(sc, ni->ni_chan, 1);
4806201209Srpaulo	if (error != 0) {
4807178676Ssam		device_printf(sc->sc_dev,
4808178676Ssam		    "%s: could not set Tx power, error %d\n", __func__, error);
4809178676Ssam		return error;
4810178676Ssam	}
4811178676Ssam	/*
4812201209Srpaulo	 * Reconfiguring RXON clears the firmware nodes table so we must
4813178676Ssam	 * add the broadcast node again.
4814178676Ssam	 */
4815201209Srpaulo	error = iwn_add_broadcast_node(sc, 1);
4816178676Ssam	if (error != 0) {
4817178676Ssam		device_printf(sc->sc_dev,
4818201209Srpaulo		    "%s: could not add broadcast node, error %d\n",
4819178676Ssam		    __func__, error);
4820178676Ssam		return error;
4821178676Ssam	}
4822178676Ssam	return 0;
4823178676Ssam}
4824178676Ssam
4825178676Ssam/*
4826178676Ssam * Configure the adapter for associated state.
4827178676Ssam */
4828206477Sbschmidtstatic int
4829191746Sthompsaiwn_run(struct iwn_softc *sc, struct ieee80211vap *vap)
4830178676Ssam{
4831178676Ssam#define	MS(v,x)	(((v) & x) >> x##_S)
4832198429Srpaulo	const struct iwn_hal *hal = sc->sc_hal;
4833178676Ssam	struct ifnet *ifp = sc->sc_ifp;
4834178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
4835178676Ssam	struct ieee80211_node *ni = vap->iv_bss;
4836178676Ssam	struct iwn_node_info node;
4837201209Srpaulo	int error;
4838178676Ssam
4839178676Ssam	if (ic->ic_opmode == IEEE80211_M_MONITOR) {
4840201209Srpaulo		/* Link LED blinks while monitoring. */
4841216237Sbschmidt		iwn_set_led(sc, IWN_LED_LINK, 20, 20);
4842178676Ssam		return 0;
4843178676Ssam	}
4844198429Srpaulo	error = iwn_set_timing(sc, ni);
4845198429Srpaulo	if (error != 0) {
4846198429Srpaulo		device_printf(sc->sc_dev,
4847198429Srpaulo		    "%s: could not set timing, error %d\n", __func__, error);
4848198429Srpaulo		return error;
4849198429Srpaulo	}
4850178676Ssam
4851201209Srpaulo	/* Update adapter configuration. */
4852201209Srpaulo	IEEE80211_ADDR_COPY(sc->rxon.bssid, ni->ni_bssid);
4853198429Srpaulo	sc->rxon.associd = htole16(IEEE80211_AID(ni->ni_associd));
4854220636Sbschmidt	sc->rxon.chan = ieee80211_chan2ieee(ic, ni->ni_chan);
4855220636Sbschmidt	sc->rxon.flags = htole32(IWN_RXON_TSF | IWN_RXON_CTS_TO_SELF);
4856201209Srpaulo	if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
4857201209Srpaulo		sc->rxon.flags |= htole32(IWN_RXON_AUTO | IWN_RXON_24GHZ);
4858178676Ssam	if (ic->ic_flags & IEEE80211_F_SHSLOT)
4859198429Srpaulo		sc->rxon.flags |= htole32(IWN_RXON_SHSLOT);
4860178676Ssam	if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
4861198429Srpaulo		sc->rxon.flags |= htole32(IWN_RXON_SHPREAMBLE);
4862201209Srpaulo	if (IEEE80211_IS_CHAN_A(ni->ni_chan)) {
4863201209Srpaulo		sc->rxon.cck_mask  = 0;
4864201209Srpaulo		sc->rxon.ofdm_mask = 0x15;
4865201209Srpaulo	} else if (IEEE80211_IS_CHAN_B(ni->ni_chan)) {
4866201209Srpaulo		sc->rxon.cck_mask  = 0x03;
4867201209Srpaulo		sc->rxon.ofdm_mask = 0;
4868201209Srpaulo	} else {
4869201209Srpaulo		/* XXX assume 802.11b/g */
4870201209Srpaulo		sc->rxon.cck_mask  = 0x0f;
4871201209Srpaulo		sc->rxon.ofdm_mask = 0x15;
4872201209Srpaulo	}
4873201209Srpaulo#if 0	/* HT */
4874178676Ssam	if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) {
4875198429Srpaulo		sc->rxon.flags &= ~htole32(IWN_RXON_HT);
4876178676Ssam		if (IEEE80211_IS_CHAN_HT40U(ni->ni_chan))
4877198429Srpaulo			sc->rxon.flags |= htole32(IWN_RXON_HT40U);
4878178676Ssam		else if (IEEE80211_IS_CHAN_HT40D(ni->ni_chan))
4879198429Srpaulo			sc->rxon.flags |= htole32(IWN_RXON_HT40D);
4880178676Ssam		else
4881198429Srpaulo			sc->rxon.flags |= htole32(IWN_RXON_HT20);
4882198429Srpaulo		sc->rxon.rxchain = htole16(
4883198429Srpaulo			  IWN_RXCHAIN_VALID(3)
4884198429Srpaulo			| IWN_RXCHAIN_MIMO_COUNT(3)
4885198429Srpaulo			| IWN_RXCHAIN_IDLE_COUNT(1)
4886178676Ssam			| IWN_RXCHAIN_MIMO_FORCE);
4887178676Ssam
4888178676Ssam		maxrxampdu = MS(ni->ni_htparam, IEEE80211_HTCAP_MAXRXAMPDU);
4889178676Ssam		ampdudensity = MS(ni->ni_htparam, IEEE80211_HTCAP_MPDUDENSITY);
4890178676Ssam	} else
4891178676Ssam		maxrxampdu = ampdudensity = 0;
4892201209Srpaulo#endif
4893198429Srpaulo	sc->rxon.filter |= htole32(IWN_FILTER_BSS);
4894178676Ssam
4895178676Ssam	DPRINTF(sc, IWN_DEBUG_STATE,
4896198429Srpaulo	    "%s: config chan %d mode %d flags 0x%x cck 0x%x ofdm 0x%x "
4897198429Srpaulo	    "ht_single 0x%x ht_dual 0x%x rxchain 0x%x "
4898198429Srpaulo	    "myaddr %6D wlap %6D bssid %6D associd %d filter 0x%x\n",
4899198429Srpaulo	    __func__,
4900198429Srpaulo	    le16toh(sc->rxon.chan), sc->rxon.mode, le32toh(sc->rxon.flags),
4901198429Srpaulo	    sc->rxon.cck_mask, sc->rxon.ofdm_mask,
4902198429Srpaulo	    sc->rxon.ht_single_mask, sc->rxon.ht_dual_mask,
4903198429Srpaulo	    le16toh(sc->rxon.rxchain),
4904198429Srpaulo	    sc->rxon.myaddr, ":", sc->rxon.wlap, ":", sc->rxon.bssid, ":",
4905198429Srpaulo	    le16toh(sc->rxon.associd), le32toh(sc->rxon.filter));
4906201209Srpaulo	error = iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, hal->rxonsz, 1);
4907178676Ssam	if (error != 0) {
4908178676Ssam		device_printf(sc->sc_dev,
4909178676Ssam		    "%s: could not update configuration, error %d\n",
4910178676Ssam		    __func__, error);
4911178676Ssam		return error;
4912178676Ssam	}
4913178676Ssam
4914198429Srpaulo	/* Configuration has changed, set TX power accordingly. */
4915201882Skeramida	error = hal->set_txpower(sc, ni->ni_chan, 1);
4916178676Ssam	if (error != 0) {
4917178676Ssam		device_printf(sc->sc_dev,
4918178676Ssam		    "%s: could not set Tx power, error %d\n", __func__, error);
4919178676Ssam		return error;
4920178676Ssam	}
4921178676Ssam
4922198429Srpaulo	/* Add BSS node. */
4923178676Ssam	memset(&node, 0, sizeof node);
4924178676Ssam	IEEE80211_ADDR_COPY(node.macaddr, ni->ni_macaddr);
4925178676Ssam	node.id = IWN_ID_BSS;
4926201209Srpaulo#ifdef notyet
4927201209Srpaulo	node.htflags = htole32(IWN_AMDPU_SIZE_FACTOR(3) |
4928201209Srpaulo	    IWN_AMDPU_DENSITY(5));	/* 2us */
4929201209Srpaulo#endif
4930178676Ssam	DPRINTF(sc, IWN_DEBUG_STATE, "%s: add BSS node, id %d htflags 0x%x\n",
4931178676Ssam	    __func__, node.id, le32toh(node.htflags));
4932198429Srpaulo	error = hal->add_node(sc, &node, 1);
4933178676Ssam	if (error != 0) {
4934198429Srpaulo		device_printf(sc->sc_dev, "could not add BSS node\n");
4935178676Ssam		return error;
4936178676Ssam	}
4937201209Srpaulo	DPRINTF(sc, IWN_DEBUG_STATE, "setting link quality for node %d\n",
4938201209Srpaulo	    node.id);
4939201209Srpaulo	error = iwn_set_link_quality(sc, node.id, 1);
4940178676Ssam	if (error != 0) {
4941178676Ssam		device_printf(sc->sc_dev,
4942178676Ssam		    "%s: could not setup MRR for node %d, error %d\n",
4943178676Ssam		    __func__, node.id, error);
4944178676Ssam		return error;
4945178676Ssam	}
4946178676Ssam
4947178676Ssam	error = iwn_init_sensitivity(sc);
4948178676Ssam	if (error != 0) {
4949178676Ssam		device_printf(sc->sc_dev,
4950178676Ssam		    "%s: could not set sensitivity, error %d\n",
4951178676Ssam		    __func__, error);
4952178676Ssam		return error;
4953178676Ssam	}
4954178676Ssam
4955198429Srpaulo	/* Start periodic calibration timer. */
4956178676Ssam	sc->calib.state = IWN_CALIB_STATE_ASSOC;
4957178676Ssam	iwn_calib_reset(sc);
4958178676Ssam
4959198429Srpaulo	/* Link LED always on while associated. */
4960178676Ssam	iwn_set_led(sc, IWN_LED_LINK, 0, 1);
4961178676Ssam
4962178676Ssam	return 0;
4963178676Ssam#undef MS
4964178676Ssam}
4965178676Ssam
4966201209Srpaulo#if 0	/* HT */
4967178676Ssam/*
4968201209Srpaulo * This function is called by upper layer when an ADDBA request is received
4969201209Srpaulo * from another STA and before the ADDBA response is sent.
4970201209Srpaulo */
4971206477Sbschmidtstatic int
4972201209Srpauloiwn_ampdu_rx_start(struct ieee80211com *ic, struct ieee80211_node *ni,
4973201209Srpaulo    uint8_t tid)
4974201209Srpaulo{
4975201209Srpaulo	struct ieee80211_rx_ba *ba = &ni->ni_rx_ba[tid];
4976201209Srpaulo	struct iwn_softc *sc = ic->ic_softc;
4977201209Srpaulo	struct iwn_node *wn = (void *)ni;
4978201209Srpaulo	struct iwn_node_info node;
4979201209Srpaulo
4980201209Srpaulo	memset(&node, 0, sizeof node);
4981201209Srpaulo	node.id = wn->id;
4982201209Srpaulo	node.control = IWN_NODE_UPDATE;
4983201209Srpaulo	node.flags = IWN_FLAG_SET_ADDBA;
4984201209Srpaulo	node.addba_tid = tid;
4985201209Srpaulo	node.addba_ssn = htole16(ba->ba_winstart);
4986201209Srpaulo	DPRINTF(sc, IWN_DEBUG_RECV, "ADDBA RA=%d TID=%d SSN=%d\n",
4987201209Srpaulo	    wn->id, tid, ba->ba_winstart));
4988201209Srpaulo	return sc->sc_hal->add_node(sc, &node, 1);
4989201209Srpaulo}
4990201209Srpaulo
4991201209Srpaulo/*
4992201209Srpaulo * This function is called by upper layer on teardown of an HT-immediate
4993201209Srpaulo * Block Ack agreement (eg. uppon receipt of a DELBA frame.)
4994201209Srpaulo */
4995206477Sbschmidtstatic void
4996201209Srpauloiwn_ampdu_rx_stop(struct ieee80211com *ic, struct ieee80211_node *ni,
4997201209Srpaulo    uint8_t tid)
4998201209Srpaulo{
4999201209Srpaulo	struct iwn_softc *sc = ic->ic_softc;
5000201209Srpaulo	struct iwn_node *wn = (void *)ni;
5001201209Srpaulo	struct iwn_node_info node;
5002201209Srpaulo
5003201209Srpaulo	memset(&node, 0, sizeof node);
5004201209Srpaulo	node.id = wn->id;
5005201209Srpaulo	node.control = IWN_NODE_UPDATE;
5006201209Srpaulo	node.flags = IWN_FLAG_SET_DELBA;
5007201209Srpaulo	node.delba_tid = tid;
5008201209Srpaulo	DPRINTF(sc, IWN_DEBUG_RECV, "DELBA RA=%d TID=%d\n", wn->id, tid);
5009201209Srpaulo	(void)sc->sc_hal->add_node(sc, &node, 1);
5010201209Srpaulo}
5011201209Srpaulo
5012201209Srpaulo/*
5013201209Srpaulo * This function is called by upper layer when an ADDBA response is received
5014201209Srpaulo * from another STA.
5015201209Srpaulo */
5016206477Sbschmidtstatic int
5017201209Srpauloiwn_ampdu_tx_start(struct ieee80211com *ic, struct ieee80211_node *ni,
5018201209Srpaulo    uint8_t tid)
5019201209Srpaulo{
5020201209Srpaulo	struct ieee80211_tx_ba *ba = &ni->ni_tx_ba[tid];
5021201209Srpaulo	struct iwn_softc *sc = ic->ic_softc;
5022201209Srpaulo	const struct iwn_hal *hal = sc->sc_hal;
5023201209Srpaulo	struct iwn_node *wn = (void *)ni;
5024201209Srpaulo	struct iwn_node_info node;
5025201209Srpaulo	int error;
5026201209Srpaulo
5027201209Srpaulo	/* Enable TX for the specified RA/TID. */
5028201209Srpaulo	wn->disable_tid &= ~(1 << tid);
5029201209Srpaulo	memset(&node, 0, sizeof node);
5030201209Srpaulo	node.id = wn->id;
5031201209Srpaulo	node.control = IWN_NODE_UPDATE;
5032201209Srpaulo	node.flags = IWN_FLAG_SET_DISABLE_TID;
5033201209Srpaulo	node.disable_tid = htole16(wn->disable_tid);
5034201209Srpaulo	error = hal->add_node(sc, &node, 1);
5035201209Srpaulo	if (error != 0)
5036201209Srpaulo		return error;
5037201209Srpaulo
5038201209Srpaulo	if ((error = iwn_nic_lock(sc)) != 0)
5039201209Srpaulo		return error;
5040201209Srpaulo	hal->ampdu_tx_start(sc, ni, tid, ba->ba_winstart);
5041201209Srpaulo	iwn_nic_unlock(sc);
5042201209Srpaulo	return 0;
5043201209Srpaulo}
5044201209Srpaulo
5045206477Sbschmidtstatic void
5046201209Srpauloiwn_ampdu_tx_stop(struct ieee80211com *ic, struct ieee80211_node *ni,
5047201209Srpaulo    uint8_t tid)
5048201209Srpaulo{
5049201209Srpaulo	struct ieee80211_tx_ba *ba = &ni->ni_tx_ba[tid];
5050201209Srpaulo	struct iwn_softc *sc = ic->ic_softc;
5051201209Srpaulo	int error;
5052201209Srpaulo
5053201209Srpaulo	error = iwn_nic_lock(sc);
5054201209Srpaulo	if (error != 0)
5055201209Srpaulo		return;
5056201209Srpaulo	sc->sc_hal->ampdu_tx_stop(sc, tid, ba->ba_winstart);
5057201209Srpaulo	iwn_nic_unlock(sc);
5058201209Srpaulo}
5059201209Srpaulo
5060206477Sbschmidtstatic void
5061201209Srpauloiwn4965_ampdu_tx_start(struct iwn_softc *sc, struct ieee80211_node *ni,
5062201209Srpaulo    uint8_t tid, uint16_t ssn)
5063201209Srpaulo{
5064201209Srpaulo	struct iwn_node *wn = (void *)ni;
5065201209Srpaulo	int qid = 7 + tid;
5066201209Srpaulo
5067201209Srpaulo	/* Stop TX scheduler while we're changing its configuration. */
5068201209Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid),
5069201209Srpaulo	    IWN4965_TXQ_STATUS_CHGACT);
5070201209Srpaulo
5071201209Srpaulo	/* Assign RA/TID translation to the queue. */
5072201209Srpaulo	iwn_mem_write_2(sc, sc->sched_base + IWN4965_SCHED_TRANS_TBL(qid),
5073201209Srpaulo	    wn->id << 4 | tid);
5074201209Srpaulo
5075201209Srpaulo	/* Enable chain-building mode for the queue. */
5076201209Srpaulo	iwn_prph_setbits(sc, IWN4965_SCHED_QCHAIN_SEL, 1 << qid);
5077201209Srpaulo
5078201209Srpaulo	/* Set starting sequence number from the ADDBA request. */
5079201209Srpaulo	IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff));
5080201209Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_QUEUE_RDPTR(qid), ssn);
5081201209Srpaulo
5082201209Srpaulo	/* Set scheduler window size. */
5083201209Srpaulo	iwn_mem_write(sc, sc->sched_base + IWN4965_SCHED_QUEUE_OFFSET(qid),
5084201209Srpaulo	    IWN_SCHED_WINSZ);
5085201209Srpaulo	/* Set scheduler frame limit. */
5086201209Srpaulo	iwn_mem_write(sc, sc->sched_base + IWN4965_SCHED_QUEUE_OFFSET(qid) + 4,
5087201209Srpaulo	    IWN_SCHED_LIMIT << 16);
5088201209Srpaulo
5089201209Srpaulo	/* Enable interrupts for the queue. */
5090201209Srpaulo	iwn_prph_setbits(sc, IWN4965_SCHED_INTR_MASK, 1 << qid);
5091201209Srpaulo
5092201209Srpaulo	/* Mark the queue as active. */
5093201209Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid),
5094201209Srpaulo	    IWN4965_TXQ_STATUS_ACTIVE | IWN4965_TXQ_STATUS_AGGR_ENA |
5095201209Srpaulo	    iwn_tid2fifo[tid] << 1);
5096201209Srpaulo}
5097201209Srpaulo
5098206477Sbschmidtstatic void
5099201209Srpauloiwn4965_ampdu_tx_stop(struct iwn_softc *sc, uint8_t tid, uint16_t ssn)
5100201209Srpaulo{
5101201209Srpaulo	int qid = 7 + tid;
5102201209Srpaulo
5103201209Srpaulo	/* Stop TX scheduler while we're changing its configuration. */
5104201209Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid),
5105201209Srpaulo	    IWN4965_TXQ_STATUS_CHGACT);
5106201209Srpaulo
5107201209Srpaulo	/* Set starting sequence number from the ADDBA request. */
5108201209Srpaulo	IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff));
5109201209Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_QUEUE_RDPTR(qid), ssn);
5110201209Srpaulo
5111201209Srpaulo	/* Disable interrupts for the queue. */
5112201209Srpaulo	iwn_prph_clrbits(sc, IWN4965_SCHED_INTR_MASK, 1 << qid);
5113201209Srpaulo
5114201209Srpaulo	/* Mark the queue as inactive. */
5115201209Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid),
5116201209Srpaulo	    IWN4965_TXQ_STATUS_INACTIVE | iwn_tid2fifo[tid] << 1);
5117201209Srpaulo}
5118201209Srpaulo
5119206477Sbschmidtstatic void
5120201209Srpauloiwn5000_ampdu_tx_start(struct iwn_softc *sc, struct ieee80211_node *ni,
5121201209Srpaulo    uint8_t tid, uint16_t ssn)
5122201209Srpaulo{
5123201209Srpaulo	struct iwn_node *wn = (void *)ni;
5124201209Srpaulo	int qid = 10 + tid;
5125201209Srpaulo
5126201209Srpaulo	/* Stop TX scheduler while we're changing its configuration. */
5127201209Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid),
5128201209Srpaulo	    IWN5000_TXQ_STATUS_CHGACT);
5129201209Srpaulo
5130201209Srpaulo	/* Assign RA/TID translation to the queue. */
5131201209Srpaulo	iwn_mem_write_2(sc, sc->sched_base + IWN5000_SCHED_TRANS_TBL(qid),
5132201209Srpaulo	    wn->id << 4 | tid);
5133201209Srpaulo
5134201209Srpaulo	/* Enable chain-building mode for the queue. */
5135201209Srpaulo	iwn_prph_setbits(sc, IWN5000_SCHED_QCHAIN_SEL, 1 << qid);
5136201209Srpaulo
5137201209Srpaulo	/* Enable aggregation for the queue. */
5138201209Srpaulo	iwn_prph_setbits(sc, IWN5000_SCHED_AGGR_SEL, 1 << qid);
5139201209Srpaulo
5140201209Srpaulo	/* Set starting sequence number from the ADDBA request. */
5141201209Srpaulo	IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff));
5142201209Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_QUEUE_RDPTR(qid), ssn);
5143201209Srpaulo
5144201209Srpaulo	/* Set scheduler window size and frame limit. */
5145201209Srpaulo	iwn_mem_write(sc, sc->sched_base + IWN5000_SCHED_QUEUE_OFFSET(qid) + 4,
5146201209Srpaulo	    IWN_SCHED_LIMIT << 16 | IWN_SCHED_WINSZ);
5147201209Srpaulo
5148201209Srpaulo	/* Enable interrupts for the queue. */
5149201209Srpaulo	iwn_prph_setbits(sc, IWN5000_SCHED_INTR_MASK, 1 << qid);
5150201209Srpaulo
5151201209Srpaulo	/* Mark the queue as active. */
5152201209Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid),
5153201209Srpaulo	    IWN5000_TXQ_STATUS_ACTIVE | iwn_tid2fifo[tid]);
5154201209Srpaulo}
5155201209Srpaulo
5156206477Sbschmidtstatic void
5157201209Srpauloiwn5000_ampdu_tx_stop(struct iwn_softc *sc, uint8_t tid, uint16_t ssn)
5158201209Srpaulo{
5159201209Srpaulo	int qid = 10 + tid;
5160201209Srpaulo
5161201209Srpaulo	/* Stop TX scheduler while we're changing its configuration. */
5162201209Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid),
5163201209Srpaulo	    IWN5000_TXQ_STATUS_CHGACT);
5164201209Srpaulo
5165201209Srpaulo	/* Disable aggregation for the queue. */
5166201209Srpaulo	iwn_prph_clrbits(sc, IWN5000_SCHED_AGGR_SEL, 1 << qid);
5167201209Srpaulo
5168201209Srpaulo	/* Set starting sequence number from the ADDBA request. */
5169201209Srpaulo	IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff));
5170201209Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_QUEUE_RDPTR(qid), ssn);
5171201209Srpaulo
5172201209Srpaulo	/* Disable interrupts for the queue. */
5173201209Srpaulo	iwn_prph_clrbits(sc, IWN5000_SCHED_INTR_MASK, 1 << qid);
5174201209Srpaulo
5175201209Srpaulo	/* Mark the queue as inactive. */
5176201209Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid),
5177201209Srpaulo	    IWN5000_TXQ_STATUS_INACTIVE | iwn_tid2fifo[tid]);
5178201209Srpaulo}
5179201209Srpaulo#endif
5180201209Srpaulo
5181201209Srpaulo/*
5182212853Sbschmidt * Send calibration results to the runtime firmware.  These results were
5183212854Sbschmidt * obtained on first boot from the initialization firmware, or by reading
5184212854Sbschmidt * the EEPROM for crystal calibration.
5185212853Sbschmidt */
5186212853Sbschmidtstatic int
5187212854Sbschmidtiwn5000_send_calib_results(struct iwn_softc *sc)
5188212853Sbschmidt{
5189212854Sbschmidt	struct iwn_calib_info *calib_result;
5190212853Sbschmidt	int idx, error;
5191212853Sbschmidt
5192212854Sbschmidt	for (idx = 0; idx < IWN_CALIB_NUM; idx++) {
5193212854Sbschmidt		calib_result = &sc->calib_results[idx];
5194212854Sbschmidt
5195212854Sbschmidt		/* No support for this type of calibration. */
5196212854Sbschmidt		if ((sc->calib_init & (1 << idx)) == 0)
5197212854Sbschmidt			continue;
5198212854Sbschmidt
5199212854Sbschmidt		/* No calibration result available. */
5200212854Sbschmidt		if (calib_result->buf == NULL)
5201212854Sbschmidt			continue;
5202212854Sbschmidt
5203212853Sbschmidt		DPRINTF(sc, IWN_DEBUG_CALIBRATE,
5204212854Sbschmidt		    "%s: send calibration result idx=%d, len=%d\n",
5205212854Sbschmidt		    __func__, idx, calib_result->len);
5206212854Sbschmidt
5207212854Sbschmidt		error = iwn_cmd(sc, IWN_CMD_PHY_CALIB, calib_result->buf,
5208212854Sbschmidt		    calib_result->len, 0);
5209212853Sbschmidt		if (error != 0) {
5210212853Sbschmidt			device_printf(sc->sc_dev,
5211212854Sbschmidt			    "%s: could not send calibration result "
5212212854Sbschmidt			    "idx=%d, error=%d\n",
5213212854Sbschmidt			    __func__, idx, error);
5214212853Sbschmidt			return error;
5215212853Sbschmidt		}
5216212853Sbschmidt	}
5217212853Sbschmidt	return 0;
5218212853Sbschmidt}
5219212853Sbschmidt
5220212853Sbschmidt/*
5221212854Sbschmidt * Save calibration result at the given index.  The index determines
5222212854Sbschmidt * in which order the results are sent to the runtime firmware.
5223178676Ssam */
5224206477Sbschmidtstatic int
5225212854Sbschmidtiwn5000_save_calib_result(struct iwn_softc *sc, struct iwn_phy_calib *calib,
5226212854Sbschmidt    int len, int idx)
5227178676Ssam{
5228212854Sbschmidt	struct iwn_calib_info *calib_result = &sc->calib_results[idx];
5229212854Sbschmidt
5230212854Sbschmidt	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
5231212854Sbschmidt	    "%s: saving calibration result code=%d, idx=%d, len=%d\n",
5232212854Sbschmidt	    __func__, calib->code, idx, len);
5233212854Sbschmidt
5234212854Sbschmidt	if (calib_result->buf != NULL)
5235212854Sbschmidt		free(calib_result->buf, M_DEVBUF);
5236212854Sbschmidt
5237212854Sbschmidt	calib_result->buf = malloc(len, M_DEVBUF, M_NOWAIT);
5238212854Sbschmidt	if (calib_result->buf == NULL) {
5239212854Sbschmidt		device_printf(sc->sc_dev,
5240212854Sbschmidt		    "%s: not enough memory for calibration result "
5241212854Sbschmidt		    "code=%d, len=%d\n", __func__, calib->code, len);
5242212854Sbschmidt		return ENOMEM;
5243212854Sbschmidt	}
5244212854Sbschmidt
5245212854Sbschmidt	calib_result->len = len;
5246212854Sbschmidt	memcpy(calib_result->buf, calib, len);
5247212854Sbschmidt	return 0;
5248212854Sbschmidt}
5249212854Sbschmidt
5250212854Sbschmidtstatic void
5251212854Sbschmidtiwn5000_free_calib_results(struct iwn_softc *sc)
5252212854Sbschmidt{
5253212854Sbschmidt	struct iwn_calib_info *calib_result;
5254212854Sbschmidt	int idx;
5255212854Sbschmidt
5256212854Sbschmidt	for (idx = 0; idx < IWN_CALIB_NUM; idx++) {
5257212854Sbschmidt		calib_result = &sc->calib_results[idx];
5258212854Sbschmidt
5259212854Sbschmidt		if (calib_result->buf != NULL)
5260212854Sbschmidt			free(calib_result->buf, M_DEVBUF);
5261212854Sbschmidt
5262212854Sbschmidt		calib_result->buf = NULL;
5263212854Sbschmidt		calib_result->len = 0;
5264212854Sbschmidt	}
5265212854Sbschmidt}
5266212854Sbschmidt
5267212854Sbschmidt/*
5268212854Sbschmidt * Obtain the crystal calibration result from the EEPROM.
5269212854Sbschmidt */
5270212854Sbschmidtstatic int
5271212854Sbschmidtiwn5000_chrystal_calib(struct iwn_softc *sc)
5272212854Sbschmidt{
5273212854Sbschmidt	struct iwn5000_phy_calib_crystal cmd;
5274212854Sbschmidt	uint32_t base, crystal;
5275212854Sbschmidt	uint16_t val;
5276212854Sbschmidt
5277212854Sbschmidt	/* Read crystal calibration. */
5278212854Sbschmidt	iwn_read_prom_data(sc, IWN5000_EEPROM_CAL, &val, 2);
5279212854Sbschmidt	base = le16toh(val);
5280212854Sbschmidt	iwn_read_prom_data(sc, base + IWN5000_EEPROM_CRYSTAL, &crystal,
5281212854Sbschmidt	    sizeof(uint32_t));
5282212854Sbschmidt	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: crystal calibration=0x%08x\n",
5283212854Sbschmidt	    __func__, le32toh(crystal));
5284212854Sbschmidt
5285212854Sbschmidt	memset(&cmd, 0, sizeof cmd);
5286212854Sbschmidt	cmd.code = IWN5000_PHY_CALIB_CRYSTAL;
5287212854Sbschmidt	cmd.ngroups = 1;
5288212854Sbschmidt	cmd.isvalid = 1;
5289212854Sbschmidt	cmd.cap_pin[0] = le32toh(crystal) & 0xff;
5290212854Sbschmidt	cmd.cap_pin[1] = (le32toh(crystal) >> 16) & 0xff;
5291212854Sbschmidt
5292212854Sbschmidt	return iwn5000_save_calib_result(sc, (struct iwn_phy_calib *)&cmd,
5293212854Sbschmidt	    sizeof cmd, IWN_CALIB_IDX_XTAL);
5294212854Sbschmidt}
5295212854Sbschmidt
5296212854Sbschmidt/*
5297212854Sbschmidt * Query calibration results from the initialization firmware.  We do this
5298212854Sbschmidt * only once at first boot.
5299212854Sbschmidt */
5300212854Sbschmidtstatic int
5301216195Sbschmidtiwn5000_send_calib_query(struct iwn_softc *sc, uint32_t cfg)
5302212854Sbschmidt{
5303212854Sbschmidt#define	CALIB_INIT_CFG	0xffffffff;
5304198429Srpaulo	struct iwn5000_calib_config cmd;
5305198429Srpaulo	int error;
5306178676Ssam
5307198429Srpaulo	memset(&cmd, 0, sizeof cmd);
5308212854Sbschmidt	cmd.ucode.once.enable = CALIB_INIT_CFG;
5309216195Sbschmidt	if (cfg == 0) {
5310216195Sbschmidt		cmd.ucode.once.start  = CALIB_INIT_CFG;
5311216195Sbschmidt		cmd.ucode.once.send   = CALIB_INIT_CFG;
5312216195Sbschmidt		cmd.ucode.flags       = CALIB_INIT_CFG;
5313216195Sbschmidt	} else
5314216195Sbschmidt		cmd.ucode.once.start  = cfg;
5315212854Sbschmidt
5316216195Sbschmidt	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
5317216195Sbschmidt	    "%s: query calibration results, cfg %x\n", __func__, cfg);
5318212854Sbschmidt
5319198429Srpaulo	error = iwn_cmd(sc, IWN5000_CMD_CALIB_CONFIG, &cmd, sizeof cmd, 0);
5320198429Srpaulo	if (error != 0)
5321198429Srpaulo		return error;
5322178676Ssam
5323198429Srpaulo	/* Wait at most two seconds for calibration to complete. */
5324201209Srpaulo	if (!(sc->sc_flags & IWN_FLAG_CALIB_DONE))
5325201209Srpaulo		error = msleep(sc, &sc->sc_mtx, PCATCH, "iwninit", 2 * hz);
5326212854Sbschmidt
5327201209Srpaulo	return error;
5328212854Sbschmidt#undef	CALIB_INIT_CFG
5329198429Srpaulo}
5330198429Srpaulo
5331198429Srpaulo/*
5332212853Sbschmidt * Process a CALIBRATION_RESULT notification sent by the initialization
5333212854Sbschmidt * firmware on response to a CMD_CALIB_CONFIG command.
5334198429Srpaulo */
5335212854Sbschmidtstatic int
5336212854Sbschmidtiwn5000_rx_calib_result(struct iwn_softc *sc, struct iwn_rx_desc *desc,
5337212853Sbschmidt    struct iwn_rx_data *data)
5338198429Srpaulo{
5339212854Sbschmidt#define	FRAME_SIZE_MASK		0x3fff
5340212853Sbschmidt	struct iwn_phy_calib *calib = (struct iwn_phy_calib *)(desc + 1);
5341212854Sbschmidt	int len, idx;
5342198429Srpaulo
5343212853Sbschmidt	bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD);
5344212854Sbschmidt	len = (le32toh(desc->len) & FRAME_SIZE_MASK);
5345212853Sbschmidt
5346212855Sbschmidt	/* Remove length field itself. */
5347212854Sbschmidt	len -= 4;
5348212854Sbschmidt
5349212854Sbschmidt	/*
5350212854Sbschmidt	 * Determine the order in which the results will be send to the
5351212854Sbschmidt	 * runtime firmware.
5352212854Sbschmidt	 */
5353212853Sbschmidt	switch (calib->code) {
5354212853Sbschmidt	case IWN5000_PHY_CALIB_DC:
5355212854Sbschmidt		idx = IWN_CALIB_IDX_DC;
5356212853Sbschmidt		break;
5357212853Sbschmidt	case IWN5000_PHY_CALIB_LO:
5358212854Sbschmidt		idx = IWN_CALIB_IDX_LO;
5359212853Sbschmidt		break;
5360212853Sbschmidt	case IWN5000_PHY_CALIB_TX_IQ:
5361212854Sbschmidt		idx = IWN_CALIB_IDX_TX_IQ;
5362212853Sbschmidt		break;
5363212853Sbschmidt	case IWN5000_PHY_CALIB_TX_IQ_PERIODIC:
5364212854Sbschmidt		idx = IWN_CALIB_IDX_TX_IQ_PERIODIC;
5365212853Sbschmidt		break;
5366212853Sbschmidt	case IWN5000_PHY_CALIB_BASE_BAND:
5367212854Sbschmidt		idx = IWN_CALIB_IDX_BASE_BAND;
5368212853Sbschmidt		break;
5369212854Sbschmidt	default:
5370198429Srpaulo		DPRINTF(sc, IWN_DEBUG_CALIBRATE,
5371212854Sbschmidt		   "%s: unknown calibration code=%d\n", __func__, calib->code);
5372212854Sbschmidt		return EINVAL;
5373178676Ssam	}
5374212854Sbschmidt	return iwn5000_save_calib_result(sc, calib, len, idx);
5375212854Sbschmidt#undef	FRAME_SIZE_MASK
5376198429Srpaulo}
5377178676Ssam
5378206477Sbschmidtstatic int
5379201209Srpauloiwn5000_send_wimax_coex(struct iwn_softc *sc)
5380201209Srpaulo{
5381201209Srpaulo	struct iwn5000_wimax_coex wimax;
5382201209Srpaulo
5383201209Srpaulo#ifdef notyet
5384201209Srpaulo	if (sc->hw_type == IWN_HW_REV_TYPE_6050) {
5385201209Srpaulo		/* Enable WiMAX coexistence for combo adapters. */
5386201209Srpaulo		wimax.flags =
5387201209Srpaulo		    IWN_WIMAX_COEX_ASSOC_WA_UNMASK |
5388201209Srpaulo		    IWN_WIMAX_COEX_UNASSOC_WA_UNMASK |
5389201209Srpaulo		    IWN_WIMAX_COEX_STA_TABLE_VALID |
5390201209Srpaulo		    IWN_WIMAX_COEX_ENABLE;
5391201209Srpaulo		memcpy(wimax.events, iwn6050_wimax_events,
5392201209Srpaulo		    sizeof iwn6050_wimax_events);
5393201209Srpaulo	} else
5394201209Srpaulo#endif
5395201209Srpaulo	{
5396201209Srpaulo		/* Disable WiMAX coexistence. */
5397201209Srpaulo		wimax.flags = 0;
5398201209Srpaulo		memset(wimax.events, 0, sizeof wimax.events);
5399201209Srpaulo	}
5400201209Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "%s: Configuring WiMAX coexistence\n",
5401201209Srpaulo	    __func__);
5402201209Srpaulo	return iwn_cmd(sc, IWN5000_CMD_WIMAX_COEX, &wimax, sizeof wimax, 0);
5403201209Srpaulo}
5404201209Srpaulo
5405198429Srpaulo/*
5406198429Srpaulo * This function is called after the runtime firmware notifies us of its
5407198429Srpaulo * readiness (called in a process context.)
5408198429Srpaulo */
5409206477Sbschmidtstatic int
5410198429Srpauloiwn4965_post_alive(struct iwn_softc *sc)
5411198429Srpaulo{
5412198429Srpaulo	int error, qid;
5413178676Ssam
5414198429Srpaulo	if ((error = iwn_nic_lock(sc)) != 0)
5415198429Srpaulo		return error;
5416178676Ssam
5417201209Srpaulo	/* Clear TX scheduler state in SRAM. */
5418198429Srpaulo	sc->sched_base = iwn_prph_read(sc, IWN_SCHED_SRAM_ADDR);
5419198429Srpaulo	iwn_mem_set_region_4(sc, sc->sched_base + IWN4965_SCHED_CTX_OFF, 0,
5420201209Srpaulo	    IWN4965_SCHED_CTX_LEN / sizeof (uint32_t));
5421178676Ssam
5422198429Srpaulo	/* Set physical address of TX scheduler rings (1KB aligned.) */
5423198429Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_DRAM_ADDR, sc->sched_dma.paddr >> 10);
5424178676Ssam
5425198429Srpaulo	IWN_SETBITS(sc, IWN_FH_TX_CHICKEN, IWN_FH_TX_CHICKEN_SCHED_RETRY);
5426178676Ssam
5427198429Srpaulo	/* Disable chain mode for all our 16 queues. */
5428198429Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_QCHAIN_SEL, 0);
5429198429Srpaulo
5430198429Srpaulo	for (qid = 0; qid < IWN4965_NTXQUEUES; qid++) {
5431198429Srpaulo		iwn_prph_write(sc, IWN4965_SCHED_QUEUE_RDPTR(qid), 0);
5432198429Srpaulo		IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | 0);
5433198429Srpaulo
5434198429Srpaulo		/* Set scheduler window size. */
5435198429Srpaulo		iwn_mem_write(sc, sc->sched_base +
5436198429Srpaulo		    IWN4965_SCHED_QUEUE_OFFSET(qid), IWN_SCHED_WINSZ);
5437198429Srpaulo		/* Set scheduler frame limit. */
5438198429Srpaulo		iwn_mem_write(sc, sc->sched_base +
5439198429Srpaulo		    IWN4965_SCHED_QUEUE_OFFSET(qid) + 4,
5440198429Srpaulo		    IWN_SCHED_LIMIT << 16);
5441178676Ssam	}
5442178676Ssam
5443198429Srpaulo	/* Enable interrupts for all our 16 queues. */
5444198429Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_INTR_MASK, 0xffff);
5445198429Srpaulo	/* Identify TX FIFO rings (0-7). */
5446198429Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_TXFACT, 0xff);
5447178676Ssam
5448198429Srpaulo	/* Mark TX rings (4 EDCA + cmd + 2 HCCA) as active. */
5449198429Srpaulo	for (qid = 0; qid < 7; qid++) {
5450198429Srpaulo		static uint8_t qid2fifo[] = { 3, 2, 1, 0, 4, 5, 6 };
5451198429Srpaulo		iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid),
5452198429Srpaulo		    IWN4965_TXQ_STATUS_ACTIVE | qid2fifo[qid] << 1);
5453198429Srpaulo	}
5454198429Srpaulo	iwn_nic_unlock(sc);
5455198429Srpaulo	return 0;
5456198429Srpaulo}
5457178676Ssam
5458198429Srpaulo/*
5459198429Srpaulo * This function is called after the initialization or runtime firmware
5460198429Srpaulo * notifies us of its readiness (called in a process context.)
5461198429Srpaulo */
5462206477Sbschmidtstatic int
5463198429Srpauloiwn5000_post_alive(struct iwn_softc *sc)
5464198429Srpaulo{
5465198429Srpaulo	int error, qid;
5466178676Ssam
5467201209Srpaulo	/* Switch to using ICT interrupt mode. */
5468201209Srpaulo	iwn5000_ict_reset(sc);
5469201209Srpaulo
5470201209Srpaulo	error = iwn_nic_lock(sc);
5471201209Srpaulo	if (error != 0)
5472198429Srpaulo		return error;
5473178676Ssam
5474201209Srpaulo	/* Clear TX scheduler state in SRAM. */
5475198429Srpaulo	sc->sched_base = iwn_prph_read(sc, IWN_SCHED_SRAM_ADDR);
5476198429Srpaulo	iwn_mem_set_region_4(sc, sc->sched_base + IWN5000_SCHED_CTX_OFF, 0,
5477201209Srpaulo	    IWN5000_SCHED_CTX_LEN / sizeof (uint32_t));
5478178676Ssam
5479198429Srpaulo	/* Set physical address of TX scheduler rings (1KB aligned.) */
5480198429Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_DRAM_ADDR, sc->sched_dma.paddr >> 10);
5481178676Ssam
5482198429Srpaulo	IWN_SETBITS(sc, IWN_FH_TX_CHICKEN, IWN_FH_TX_CHICKEN_SCHED_RETRY);
5483178676Ssam
5484201209Srpaulo	/* Enable chain mode for all queues, except command queue. */
5485201209Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_QCHAIN_SEL, 0xfffef);
5486198429Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_AGGR_SEL, 0);
5487178676Ssam
5488198429Srpaulo	for (qid = 0; qid < IWN5000_NTXQUEUES; qid++) {
5489198429Srpaulo		iwn_prph_write(sc, IWN5000_SCHED_QUEUE_RDPTR(qid), 0);
5490198429Srpaulo		IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | 0);
5491198429Srpaulo
5492198429Srpaulo		iwn_mem_write(sc, sc->sched_base +
5493198429Srpaulo		    IWN5000_SCHED_QUEUE_OFFSET(qid), 0);
5494198429Srpaulo		/* Set scheduler window size and frame limit. */
5495198429Srpaulo		iwn_mem_write(sc, sc->sched_base +
5496198429Srpaulo		    IWN5000_SCHED_QUEUE_OFFSET(qid) + 4,
5497198429Srpaulo		    IWN_SCHED_LIMIT << 16 | IWN_SCHED_WINSZ);
5498178676Ssam	}
5499178676Ssam
5500198429Srpaulo	/* Enable interrupts for all our 20 queues. */
5501198429Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_INTR_MASK, 0xfffff);
5502198429Srpaulo	/* Identify TX FIFO rings (0-7). */
5503198429Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_TXFACT, 0xff);
5504178676Ssam
5505198429Srpaulo	/* Mark TX rings (4 EDCA + cmd + 2 HCCA) as active. */
5506198429Srpaulo	for (qid = 0; qid < 7; qid++) {
5507198429Srpaulo		static uint8_t qid2fifo[] = { 3, 2, 1, 0, 7, 5, 6 };
5508198429Srpaulo		iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid),
5509198429Srpaulo		    IWN5000_TXQ_STATUS_ACTIVE | qid2fifo[qid]);
5510198429Srpaulo	}
5511198429Srpaulo	iwn_nic_unlock(sc);
5512178676Ssam
5513201209Srpaulo	/* Configure WiMAX coexistence for combo adapters. */
5514201209Srpaulo	error = iwn5000_send_wimax_coex(sc);
5515178676Ssam	if (error != 0) {
5516178676Ssam		device_printf(sc->sc_dev,
5517198429Srpaulo		    "%s: could not configure WiMAX coexistence, error %d\n",
5518178676Ssam		    __func__, error);
5519178676Ssam		return error;
5520178676Ssam	}
5521178676Ssam
5522212854Sbschmidt	if (!(sc->sc_flags & IWN_FLAG_CALIB_DONE)) {
5523212854Sbschmidt		/*
5524212854Sbschmidt		 * Start calibration by setting and sending the chrystal
5525212854Sbschmidt		 * calibration first, this must be done before we are able
5526212854Sbschmidt		 * to query the other calibration results.
5527212854Sbschmidt		 */
5528212854Sbschmidt		error = iwn5000_chrystal_calib(sc);
5529198429Srpaulo		if (error != 0) {
5530198429Srpaulo			device_printf(sc->sc_dev,
5531212854Sbschmidt			    "%s: could not set chrystal calibration, "
5532212854Sbschmidt			    "error=%d\n", __func__, error);
5533198429Srpaulo			return error;
5534198429Srpaulo		}
5535212854Sbschmidt		error = iwn5000_send_calib_results(sc);
5536201209Srpaulo		if (error != 0) {
5537198429Srpaulo			device_printf(sc->sc_dev,
5538212854Sbschmidt			    "%s: could not send chrystal calibration, "
5539212854Sbschmidt			    "error=%d\n", __func__, error);
5540212854Sbschmidt			return error;
5541212854Sbschmidt		}
5542212854Sbschmidt
5543212854Sbschmidt		/*
5544212854Sbschmidt		 * Query other calibration results from the initialization
5545212854Sbschmidt		 * firmware.
5546212854Sbschmidt		 */
5547216195Sbschmidt		error = iwn5000_send_calib_query(sc, 0);
5548212854Sbschmidt		if (error != 0) {
5549212854Sbschmidt			device_printf(sc->sc_dev,
5550212854Sbschmidt			    "%s: could not query calibration, error=%d\n",
5551198429Srpaulo			    __func__, error);
5552198429Srpaulo			return error;
5553198429Srpaulo		}
5554212854Sbschmidt
5555198429Srpaulo		/*
5556201209Srpaulo		 * We have the calibration results now, reboot with the
5557201209Srpaulo		 * runtime firmware (call ourselves recursively!)
5558198429Srpaulo		 */
5559198429Srpaulo		iwn_hw_stop(sc);
5560198429Srpaulo		error = iwn_hw_init(sc);
5561198429Srpaulo	} else {
5562212854Sbschmidt		/*
5563212854Sbschmidt		 * Send calibration results obtained from the initialization
5564212854Sbschmidt		 * firmware to the runtime firmware.
5565212854Sbschmidt		 */
5566212854Sbschmidt		error = iwn5000_send_calib_results(sc);
5567216195Sbschmidt
5568216195Sbschmidt		/*
5569216195Sbschmidt		 * Tell the runtime firmware to do certain calibration types.
5570216195Sbschmidt		 */
5571216195Sbschmidt		if (sc->calib_runtime != 0) {
5572216195Sbschmidt			error = iwn5000_send_calib_query(sc, sc->calib_runtime);
5573216195Sbschmidt			if (error != 0) {
5574216195Sbschmidt				device_printf(sc->sc_dev,
5575216195Sbschmidt				    "%s: could not send query calibration, "
5576216195Sbschmidt				    "error=%d, cfg=%x\n", __func__, error,
5577216195Sbschmidt				    sc->calib_runtime);
5578216195Sbschmidt			}
5579216195Sbschmidt		}
5580198429Srpaulo	}
5581198429Srpaulo	return error;
5582198429Srpaulo}
5583178676Ssam
5584198429Srpaulo/*
5585198429Srpaulo * The firmware boot code is small and is intended to be copied directly into
5586198429Srpaulo * the NIC internal memory (no DMA transfer.)
5587198429Srpaulo */
5588206477Sbschmidtstatic int
5589198429Srpauloiwn4965_load_bootcode(struct iwn_softc *sc, const uint8_t *ucode, int size)
5590198429Srpaulo{
5591198429Srpaulo	int error, ntries;
5592198429Srpaulo
5593198429Srpaulo	size /= sizeof (uint32_t);
5594198429Srpaulo
5595198429Srpaulo	error = iwn_nic_lock(sc);
5596198429Srpaulo	if (error != 0)
5597198429Srpaulo		return error;
5598198429Srpaulo
5599198429Srpaulo	/* Copy microcode image into NIC memory. */
5600198429Srpaulo	iwn_prph_write_region_4(sc, IWN_BSM_SRAM_BASE,
5601198429Srpaulo	    (const uint32_t *)ucode, size);
5602198429Srpaulo
5603198429Srpaulo	iwn_prph_write(sc, IWN_BSM_WR_MEM_SRC, 0);
5604198429Srpaulo	iwn_prph_write(sc, IWN_BSM_WR_MEM_DST, IWN_FW_TEXT_BASE);
5605198429Srpaulo	iwn_prph_write(sc, IWN_BSM_WR_DWCOUNT, size);
5606198429Srpaulo
5607198429Srpaulo	/* Start boot load now. */
5608198429Srpaulo	iwn_prph_write(sc, IWN_BSM_WR_CTRL, IWN_BSM_WR_CTRL_START);
5609198429Srpaulo
5610198429Srpaulo	/* Wait for transfer to complete. */
5611198429Srpaulo	for (ntries = 0; ntries < 1000; ntries++) {
5612198429Srpaulo		if (!(iwn_prph_read(sc, IWN_BSM_WR_CTRL) &
5613198429Srpaulo		    IWN_BSM_WR_CTRL_START))
5614198429Srpaulo			break;
5615198429Srpaulo		DELAY(10);
5616198429Srpaulo	}
5617198429Srpaulo	if (ntries == 1000) {
5618198429Srpaulo		device_printf(sc->sc_dev, "%s: could not load boot firmware\n",
5619198429Srpaulo		    __func__);
5620198429Srpaulo		iwn_nic_unlock(sc);
5621198429Srpaulo		return ETIMEDOUT;
5622198429Srpaulo	}
5623198429Srpaulo
5624198429Srpaulo	/* Enable boot after power up. */
5625198429Srpaulo	iwn_prph_write(sc, IWN_BSM_WR_CTRL, IWN_BSM_WR_CTRL_START_EN);
5626198429Srpaulo
5627198429Srpaulo	iwn_nic_unlock(sc);
5628198429Srpaulo	return 0;
5629178676Ssam}
5630178676Ssam
5631206477Sbschmidtstatic int
5632198429Srpauloiwn4965_load_firmware(struct iwn_softc *sc)
5633178676Ssam{
5634198429Srpaulo	struct iwn_fw_info *fw = &sc->fw;
5635198429Srpaulo	struct iwn_dma_info *dma = &sc->fw_dma;
5636178676Ssam	int error;
5637178676Ssam
5638198429Srpaulo	/* Copy initialization sections into pre-allocated DMA-safe memory. */
5639198429Srpaulo	memcpy(dma->vaddr, fw->init.data, fw->init.datasz);
5640220661Sbschmidt	bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
5641198429Srpaulo	memcpy(dma->vaddr + IWN4965_FW_DATA_MAXSZ,
5642198429Srpaulo	    fw->init.text, fw->init.textsz);
5643220661Sbschmidt	bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
5644198429Srpaulo
5645198429Srpaulo	/* Tell adapter where to find initialization sections. */
5646198429Srpaulo	error = iwn_nic_lock(sc);
5647198429Srpaulo	if (error != 0)
5648198429Srpaulo		return error;
5649198429Srpaulo	iwn_prph_write(sc, IWN_BSM_DRAM_DATA_ADDR, dma->paddr >> 4);
5650198429Srpaulo	iwn_prph_write(sc, IWN_BSM_DRAM_DATA_SIZE, fw->init.datasz);
5651198429Srpaulo	iwn_prph_write(sc, IWN_BSM_DRAM_TEXT_ADDR,
5652198429Srpaulo	    (dma->paddr + IWN4965_FW_DATA_MAXSZ) >> 4);
5653198429Srpaulo	iwn_prph_write(sc, IWN_BSM_DRAM_TEXT_SIZE, fw->init.textsz);
5654198429Srpaulo	iwn_nic_unlock(sc);
5655198429Srpaulo
5656198429Srpaulo	/* Load firmware boot code. */
5657198429Srpaulo	error = iwn4965_load_bootcode(sc, fw->boot.text, fw->boot.textsz);
5658178676Ssam	if (error != 0) {
5659198429Srpaulo		device_printf(sc->sc_dev, "%s: could not load boot firmware\n",
5660198429Srpaulo		    __func__);
5661178676Ssam		return error;
5662178676Ssam	}
5663198429Srpaulo	/* Now press "execute". */
5664198429Srpaulo	IWN_WRITE(sc, IWN_RESET, 0);
5665178676Ssam
5666198429Srpaulo	/* Wait at most one second for first alive notification. */
5667198429Srpaulo	error = msleep(sc, &sc->sc_mtx, PCATCH, "iwninit", hz);
5668198429Srpaulo	if (error) {
5669178676Ssam		device_printf(sc->sc_dev,
5670198429Srpaulo		    "%s: timeout waiting for adapter to initialize, error %d\n",
5671178676Ssam		    __func__, error);
5672178676Ssam		return error;
5673178676Ssam	}
5674178676Ssam
5675198429Srpaulo	/* Retrieve current temperature for initial TX power calibration. */
5676198429Srpaulo	sc->rawtemp = sc->ucode_info.temp[3].chan20MHz;
5677198429Srpaulo	sc->temp = iwn4965_get_temperature(sc);
5678178676Ssam
5679198429Srpaulo	/* Copy runtime sections into pre-allocated DMA-safe memory. */
5680198429Srpaulo	memcpy(dma->vaddr, fw->main.data, fw->main.datasz);
5681220661Sbschmidt	bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
5682198429Srpaulo	memcpy(dma->vaddr + IWN4965_FW_DATA_MAXSZ,
5683198429Srpaulo	    fw->main.text, fw->main.textsz);
5684220661Sbschmidt	bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
5685198429Srpaulo
5686198429Srpaulo	/* Tell adapter where to find runtime sections. */
5687198429Srpaulo	error = iwn_nic_lock(sc);
5688198429Srpaulo	if (error != 0)
5689198429Srpaulo		return error;
5690198429Srpaulo
5691198429Srpaulo	iwn_prph_write(sc, IWN_BSM_DRAM_DATA_ADDR, dma->paddr >> 4);
5692198429Srpaulo	iwn_prph_write(sc, IWN_BSM_DRAM_DATA_SIZE, fw->main.datasz);
5693198429Srpaulo	iwn_prph_write(sc, IWN_BSM_DRAM_TEXT_ADDR,
5694198429Srpaulo	    (dma->paddr + IWN4965_FW_DATA_MAXSZ) >> 4);
5695198429Srpaulo	iwn_prph_write(sc, IWN_BSM_DRAM_TEXT_SIZE,
5696198429Srpaulo	    IWN_FW_UPDATED | fw->main.textsz);
5697198429Srpaulo	iwn_nic_unlock(sc);
5698198429Srpaulo
5699198429Srpaulo	return 0;
5700198429Srpaulo}
5701198429Srpaulo
5702206477Sbschmidtstatic int
5703198429Srpauloiwn5000_load_firmware_section(struct iwn_softc *sc, uint32_t dst,
5704198429Srpaulo    const uint8_t *section, int size)
5705198429Srpaulo{
5706198429Srpaulo	struct iwn_dma_info *dma = &sc->fw_dma;
5707198429Srpaulo	int error;
5708198429Srpaulo
5709198429Srpaulo	/* Copy firmware section into pre-allocated DMA-safe memory. */
5710198429Srpaulo	memcpy(dma->vaddr, section, size);
5711220661Sbschmidt	bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
5712198429Srpaulo
5713198429Srpaulo	error = iwn_nic_lock(sc);
5714198429Srpaulo	if (error != 0)
5715198429Srpaulo		return error;
5716198429Srpaulo
5717198429Srpaulo	IWN_WRITE(sc, IWN_FH_TX_CONFIG(IWN_SRVC_DMACHNL),
5718198429Srpaulo	    IWN_FH_TX_CONFIG_DMA_PAUSE);
5719198429Srpaulo
5720198429Srpaulo	IWN_WRITE(sc, IWN_FH_SRAM_ADDR(IWN_SRVC_DMACHNL), dst);
5721198429Srpaulo	IWN_WRITE(sc, IWN_FH_TFBD_CTRL0(IWN_SRVC_DMACHNL),
5722198429Srpaulo	    IWN_LOADDR(dma->paddr));
5723198429Srpaulo	IWN_WRITE(sc, IWN_FH_TFBD_CTRL1(IWN_SRVC_DMACHNL),
5724198429Srpaulo	    IWN_HIADDR(dma->paddr) << 28 | size);
5725198429Srpaulo	IWN_WRITE(sc, IWN_FH_TXBUF_STATUS(IWN_SRVC_DMACHNL),
5726198429Srpaulo	    IWN_FH_TXBUF_STATUS_TBNUM(1) |
5727198429Srpaulo	    IWN_FH_TXBUF_STATUS_TBIDX(1) |
5728198429Srpaulo	    IWN_FH_TXBUF_STATUS_TFBD_VALID);
5729198429Srpaulo
5730198429Srpaulo	/* Kick Flow Handler to start DMA transfer. */
5731198429Srpaulo	IWN_WRITE(sc, IWN_FH_TX_CONFIG(IWN_SRVC_DMACHNL),
5732198429Srpaulo	    IWN_FH_TX_CONFIG_DMA_ENA | IWN_FH_TX_CONFIG_CIRQ_HOST_ENDTFD);
5733198429Srpaulo
5734198429Srpaulo	iwn_nic_unlock(sc);
5735198429Srpaulo
5736198429Srpaulo	/* Wait at most five seconds for FH DMA transfer to complete. */
5737220661Sbschmidt	return msleep(sc, &sc->sc_mtx, PCATCH, "iwninit", 5 * hz);
5738198429Srpaulo}
5739198429Srpaulo
5740206477Sbschmidtstatic int
5741198429Srpauloiwn5000_load_firmware(struct iwn_softc *sc)
5742198429Srpaulo{
5743198429Srpaulo	struct iwn_fw_part *fw;
5744198429Srpaulo	int error;
5745198429Srpaulo
5746198429Srpaulo	/* Load the initialization firmware on first boot only. */
5747201209Srpaulo	fw = (sc->sc_flags & IWN_FLAG_CALIB_DONE) ?
5748201209Srpaulo	    &sc->fw.main : &sc->fw.init;
5749198429Srpaulo
5750198429Srpaulo	error = iwn5000_load_firmware_section(sc, IWN_FW_TEXT_BASE,
5751198429Srpaulo	    fw->text, fw->textsz);
5752178676Ssam	if (error != 0) {
5753178676Ssam		device_printf(sc->sc_dev,
5754198429Srpaulo		    "%s: could not load firmware %s section, error %d\n",
5755198429Srpaulo		    __func__, ".text", error);
5756178676Ssam		return error;
5757178676Ssam	}
5758198429Srpaulo	error = iwn5000_load_firmware_section(sc, IWN_FW_DATA_BASE,
5759198429Srpaulo	    fw->data, fw->datasz);
5760178676Ssam	if (error != 0) {
5761178676Ssam		device_printf(sc->sc_dev,
5762198429Srpaulo		    "%s: could not load firmware %s section, error %d\n",
5763198429Srpaulo		    __func__, ".data", error);
5764178676Ssam		return error;
5765178676Ssam	}
5766178676Ssam
5767198429Srpaulo	/* Now press "execute". */
5768198429Srpaulo	IWN_WRITE(sc, IWN_RESET, 0);
5769198429Srpaulo	return 0;
5770198429Srpaulo}
5771198429Srpaulo
5772210111Sbschmidt/*
5773210111Sbschmidt * Extract text and data sections from a legacy firmware image.
5774210111Sbschmidt */
5775206477Sbschmidtstatic int
5776210111Sbschmidtiwn_read_firmware_leg(struct iwn_softc *sc, struct iwn_fw_info *fw)
5777198429Srpaulo{
5778201209Srpaulo	const uint32_t *ptr;
5779210111Sbschmidt	size_t hdrlen = 24;
5780201209Srpaulo	uint32_t rev;
5781198429Srpaulo
5782220661Sbschmidt	ptr = (const uint32_t *)fw->data;
5783201209Srpaulo	rev = le32toh(*ptr++);
5784210111Sbschmidt
5785201209Srpaulo	/* Check firmware API version. */
5786201209Srpaulo	if (IWN_FW_API(rev) <= 1) {
5787201209Srpaulo		device_printf(sc->sc_dev,
5788201209Srpaulo		    "%s: bad firmware, need API version >=2\n", __func__);
5789201209Srpaulo		return EINVAL;
5790201209Srpaulo	}
5791201209Srpaulo	if (IWN_FW_API(rev) >= 3) {
5792201209Srpaulo		/* Skip build number (version 2 header). */
5793210111Sbschmidt		hdrlen += 4;
5794201209Srpaulo		ptr++;
5795201209Srpaulo	}
5796210111Sbschmidt	if (fw->size < hdrlen) {
5797210111Sbschmidt		device_printf(sc->sc_dev,
5798210111Sbschmidt		    "%s: firmware file too short: %zu bytes\n",
5799210111Sbschmidt		    __func__, fw->size);
5800210111Sbschmidt		return EINVAL;
5801210111Sbschmidt	}
5802201209Srpaulo	fw->main.textsz = le32toh(*ptr++);
5803201209Srpaulo	fw->main.datasz = le32toh(*ptr++);
5804201209Srpaulo	fw->init.textsz = le32toh(*ptr++);
5805201209Srpaulo	fw->init.datasz = le32toh(*ptr++);
5806201209Srpaulo	fw->boot.textsz = le32toh(*ptr++);
5807198429Srpaulo
5808198429Srpaulo	/* Check that all firmware sections fit. */
5809210111Sbschmidt	if (fw->size < hdrlen + fw->main.textsz + fw->main.datasz +
5810210111Sbschmidt	    fw->init.textsz + fw->init.datasz + fw->boot.textsz) {
5811178676Ssam		device_printf(sc->sc_dev,
5812198429Srpaulo		    "%s: firmware file too short: %zu bytes\n",
5813210111Sbschmidt		    __func__, fw->size);
5814198429Srpaulo		return EINVAL;
5815178676Ssam	}
5816198429Srpaulo
5817198429Srpaulo	/* Get pointers to firmware sections. */
5818201209Srpaulo	fw->main.text = (const uint8_t *)ptr;
5819198429Srpaulo	fw->main.data = fw->main.text + fw->main.textsz;
5820198429Srpaulo	fw->init.text = fw->main.data + fw->main.datasz;
5821198429Srpaulo	fw->init.data = fw->init.text + fw->init.textsz;
5822198429Srpaulo	fw->boot.text = fw->init.data + fw->init.datasz;
5823198429Srpaulo
5824178676Ssam	return 0;
5825178676Ssam}
5826178676Ssam
5827210111Sbschmidt/*
5828210111Sbschmidt * Extract text and data sections from a TLV firmware image.
5829210111Sbschmidt */
5830220661Sbschmidtstatic int
5831210111Sbschmidtiwn_read_firmware_tlv(struct iwn_softc *sc, struct iwn_fw_info *fw,
5832210111Sbschmidt    uint16_t alt)
5833210111Sbschmidt{
5834210111Sbschmidt	const struct iwn_fw_tlv_hdr *hdr;
5835210111Sbschmidt	const struct iwn_fw_tlv *tlv;
5836210111Sbschmidt	const uint8_t *ptr, *end;
5837210111Sbschmidt	uint64_t altmask;
5838210111Sbschmidt	uint32_t len;
5839210111Sbschmidt
5840210111Sbschmidt	if (fw->size < sizeof (*hdr)) {
5841210111Sbschmidt		device_printf(sc->sc_dev,
5842210111Sbschmidt		    "%s: firmware file too short: %zu bytes\n",
5843210111Sbschmidt		    __func__, fw->size);
5844210111Sbschmidt		return EINVAL;
5845210111Sbschmidt	}
5846210111Sbschmidt	hdr = (const struct iwn_fw_tlv_hdr *)fw->data;
5847210111Sbschmidt	if (hdr->signature != htole32(IWN_FW_SIGNATURE)) {
5848210111Sbschmidt		device_printf(sc->sc_dev,
5849210111Sbschmidt		    "%s: bad firmware file signature 0x%08x\n",
5850210111Sbschmidt		    __func__, le32toh(hdr->signature));
5851210111Sbschmidt		return EINVAL;
5852210111Sbschmidt	}
5853210111Sbschmidt
5854210111Sbschmidt	/*
5855210111Sbschmidt	 * Select the closest supported alternative that is less than
5856210111Sbschmidt	 * or equal to the specified one.
5857210111Sbschmidt	 */
5858210111Sbschmidt	altmask = le64toh(hdr->altmask);
5859210111Sbschmidt	while (alt > 0 && !(altmask & (1ULL << alt)))
5860210111Sbschmidt		alt--;	/* Downgrade. */
5861210111Sbschmidt
5862210111Sbschmidt	ptr = (const uint8_t *)(hdr + 1);
5863210111Sbschmidt	end = (const uint8_t *)(fw->data + fw->size);
5864210111Sbschmidt
5865210111Sbschmidt	/* Parse type-length-value fields. */
5866210111Sbschmidt	while (ptr + sizeof (*tlv) <= end) {
5867210111Sbschmidt		tlv = (const struct iwn_fw_tlv *)ptr;
5868210111Sbschmidt		len = le32toh(tlv->len);
5869210111Sbschmidt
5870210111Sbschmidt		ptr += sizeof (*tlv);
5871210111Sbschmidt		if (ptr + len > end) {
5872210111Sbschmidt			device_printf(sc->sc_dev,
5873210111Sbschmidt			    "%s: firmware file too short: %zu bytes\n",
5874210111Sbschmidt			    __func__, fw->size);
5875210111Sbschmidt			return EINVAL;
5876210111Sbschmidt		}
5877210111Sbschmidt		/* Skip other alternatives. */
5878210111Sbschmidt		if (tlv->alt != 0 && tlv->alt != htole16(alt))
5879210111Sbschmidt			goto next;
5880210111Sbschmidt
5881210111Sbschmidt		switch (le16toh(tlv->type)) {
5882210111Sbschmidt		case IWN_FW_TLV_MAIN_TEXT:
5883210111Sbschmidt			fw->main.text = ptr;
5884210111Sbschmidt			fw->main.textsz = len;
5885210111Sbschmidt			break;
5886210111Sbschmidt		case IWN_FW_TLV_MAIN_DATA:
5887210111Sbschmidt			fw->main.data = ptr;
5888210111Sbschmidt			fw->main.datasz = len;
5889210111Sbschmidt			break;
5890210111Sbschmidt		case IWN_FW_TLV_INIT_TEXT:
5891210111Sbschmidt			fw->init.text = ptr;
5892210111Sbschmidt			fw->init.textsz = len;
5893210111Sbschmidt			break;
5894210111Sbschmidt		case IWN_FW_TLV_INIT_DATA:
5895210111Sbschmidt			fw->init.data = ptr;
5896210111Sbschmidt			fw->init.datasz = len;
5897210111Sbschmidt			break;
5898210111Sbschmidt		case IWN_FW_TLV_BOOT_TEXT:
5899210111Sbschmidt			fw->boot.text = ptr;
5900210111Sbschmidt			fw->boot.textsz = len;
5901210111Sbschmidt			break;
5902210111Sbschmidt		default:
5903210111Sbschmidt			DPRINTF(sc, IWN_DEBUG_RESET,
5904210111Sbschmidt			    "%s: TLV type %d not handled\n",
5905210111Sbschmidt			    __func__, le16toh(tlv->type));
5906210111Sbschmidt			break;
5907210111Sbschmidt		}
5908210111Sbschmidtnext:		/* TLV fields are 32-bit aligned. */
5909210111Sbschmidt		ptr += (len + 3) & ~3;
5910210111Sbschmidt	}
5911210111Sbschmidt	return 0;
5912210111Sbschmidt}
5913210111Sbschmidt
5914206477Sbschmidtstatic int
5915210111Sbschmidtiwn_read_firmware(struct iwn_softc *sc)
5916210111Sbschmidt{
5917210111Sbschmidt	const struct iwn_hal *hal = sc->sc_hal;
5918210111Sbschmidt	struct iwn_fw_info *fw = &sc->fw;
5919210111Sbschmidt	int error;
5920210111Sbschmidt
5921210111Sbschmidt	IWN_UNLOCK(sc);
5922210111Sbschmidt
5923210111Sbschmidt	memset(fw, 0, sizeof (*fw));
5924210111Sbschmidt
5925210111Sbschmidt	/* Read firmware image from filesystem. */
5926210111Sbschmidt	sc->fw_fp = firmware_get(sc->fwname);
5927210111Sbschmidt	if (sc->fw_fp == NULL) {
5928210111Sbschmidt		device_printf(sc->sc_dev,
5929210111Sbschmidt		    "%s: could not load firmare image \"%s\"\n", __func__,
5930210111Sbschmidt		    sc->fwname);
5931210111Sbschmidt		IWN_LOCK(sc);
5932210111Sbschmidt		return EINVAL;
5933210111Sbschmidt	}
5934210111Sbschmidt	IWN_LOCK(sc);
5935210111Sbschmidt
5936210111Sbschmidt	fw->size = sc->fw_fp->datasize;
5937210111Sbschmidt	fw->data = (const uint8_t *)sc->fw_fp->data;
5938210111Sbschmidt	if (fw->size < sizeof (uint32_t)) {
5939210111Sbschmidt		device_printf(sc->sc_dev,
5940210111Sbschmidt		    "%s: firmware file too short: %zu bytes\n",
5941210111Sbschmidt		    __func__, fw->size);
5942220661Sbschmidt		firmware_put(sc->fw_fp, FIRMWARE_UNLOAD);
5943220661Sbschmidt		sc->fw_fp = NULL;
5944210111Sbschmidt		return EINVAL;
5945210111Sbschmidt	}
5946210111Sbschmidt
5947210111Sbschmidt	/* Retrieve text and data sections. */
5948210111Sbschmidt	if (*(const uint32_t *)fw->data != 0)	/* Legacy image. */
5949210111Sbschmidt		error = iwn_read_firmware_leg(sc, fw);
5950210111Sbschmidt	else
5951210111Sbschmidt		error = iwn_read_firmware_tlv(sc, fw, 1);
5952210111Sbschmidt	if (error != 0) {
5953210111Sbschmidt		device_printf(sc->sc_dev,
5954210111Sbschmidt		    "%s: could not read firmware sections\n", __func__);
5955220661Sbschmidt		firmware_put(sc->fw_fp, FIRMWARE_UNLOAD);
5956220661Sbschmidt		sc->fw_fp = NULL;
5957210111Sbschmidt		return error;
5958210111Sbschmidt	}
5959210111Sbschmidt
5960210111Sbschmidt	/* Make sure text and data sections fit in hardware memory. */
5961210111Sbschmidt	if (fw->main.textsz > hal->fw_text_maxsz ||
5962210111Sbschmidt	    fw->main.datasz > hal->fw_data_maxsz ||
5963210111Sbschmidt	    fw->init.textsz > hal->fw_text_maxsz ||
5964210111Sbschmidt	    fw->init.datasz > hal->fw_data_maxsz ||
5965210111Sbschmidt	    fw->boot.textsz > IWN_FW_BOOT_TEXT_MAXSZ ||
5966210111Sbschmidt	    (fw->boot.textsz & 3) != 0) {
5967210111Sbschmidt		device_printf(sc->sc_dev,
5968210111Sbschmidt		    "%s: firmware sections too large\n", __func__);
5969220661Sbschmidt		firmware_put(sc->fw_fp, FIRMWARE_UNLOAD);
5970220661Sbschmidt		sc->fw_fp = NULL;
5971210111Sbschmidt		return EINVAL;
5972210111Sbschmidt	}
5973210111Sbschmidt
5974210111Sbschmidt	/* We can proceed with loading the firmware. */
5975210111Sbschmidt	return 0;
5976210111Sbschmidt}
5977210111Sbschmidt
5978210111Sbschmidtstatic int
5979198429Srpauloiwn_clock_wait(struct iwn_softc *sc)
5980198429Srpaulo{
5981198429Srpaulo	int ntries;
5982178676Ssam
5983198429Srpaulo	/* Set "initialization complete" bit. */
5984198429Srpaulo	IWN_SETBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_INIT_DONE);
5985198429Srpaulo
5986198429Srpaulo	/* Wait for clock stabilization. */
5987201209Srpaulo	for (ntries = 0; ntries < 2500; ntries++) {
5988198429Srpaulo		if (IWN_READ(sc, IWN_GP_CNTRL) & IWN_GP_CNTRL_MAC_CLOCK_READY)
5989198429Srpaulo			return 0;
5990201209Srpaulo		DELAY(10);
5991178676Ssam	}
5992198429Srpaulo	device_printf(sc->sc_dev,
5993198429Srpaulo	    "%s: timeout waiting for clock stabilization\n", __func__);
5994198429Srpaulo	return ETIMEDOUT;
5995198429Srpaulo}
5996178676Ssam
5997206477Sbschmidtstatic int
5998201209Srpauloiwn_apm_init(struct iwn_softc *sc)
5999198429Srpaulo{
6000201209Srpaulo	uint32_t tmp;
6001198429Srpaulo	int error;
6002178676Ssam
6003201209Srpaulo	/* Disable L0s exit timer (NMI bug workaround.) */
6004198429Srpaulo	IWN_SETBITS(sc, IWN_GIO_CHICKEN, IWN_GIO_CHICKEN_DIS_L0S_TIMER);
6005201209Srpaulo	/* Don't wait for ICH L0s (ICH bug workaround.) */
6006198429Srpaulo	IWN_SETBITS(sc, IWN_GIO_CHICKEN, IWN_GIO_CHICKEN_L1A_NO_L0S_RX);
6007178676Ssam
6008201209Srpaulo	/* Set FH wait threshold to max (HW bug under stress workaround.) */
6009198429Srpaulo	IWN_SETBITS(sc, IWN_DBG_HPET_MEM, 0xffff0000);
6010198429Srpaulo
6011201209Srpaulo	/* Enable HAP INTA to move adapter from L1a to L0s. */
6012198429Srpaulo	IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_HAP_WAKE_L1A);
6013198429Srpaulo
6014201209Srpaulo	/* Retrieve PCIe Active State Power Management (ASPM). */
6015201209Srpaulo	tmp = pci_read_config(sc->sc_dev, sc->sc_cap_off + 0x10, 1);
6016201209Srpaulo	/* Workaround for HW instability in PCIe L0->L0s->L1 transition. */
6017201209Srpaulo	if (tmp & 0x02)	/* L1 Entry enabled. */
6018201209Srpaulo		IWN_SETBITS(sc, IWN_GIO, IWN_GIO_L0S_ENA);
6019201209Srpaulo	else
6020201209Srpaulo		IWN_CLRBITS(sc, IWN_GIO, IWN_GIO_L0S_ENA);
6021201209Srpaulo
6022201209Srpaulo	if (sc->hw_type != IWN_HW_REV_TYPE_4965 &&
6023210109Sbschmidt	    sc->hw_type <= IWN_HW_REV_TYPE_1000)
6024198429Srpaulo		IWN_SETBITS(sc, IWN_ANA_PLL, IWN_ANA_PLL_INIT);
6025198429Srpaulo
6026201209Srpaulo	/* Wait for clock stabilization before accessing prph. */
6027198429Srpaulo	error = iwn_clock_wait(sc);
6028198429Srpaulo	if (error != 0)
6029198429Srpaulo		return error;
6030198429Srpaulo
6031198429Srpaulo	error = iwn_nic_lock(sc);
6032198429Srpaulo	if (error != 0)
6033198429Srpaulo		return error;
6034198429Srpaulo
6035201209Srpaulo	if (sc->hw_type == IWN_HW_REV_TYPE_4965) {
6036201209Srpaulo		/* Enable DMA and BSM (Bootstrap State Machine.) */
6037201209Srpaulo		iwn_prph_write(sc, IWN_APMG_CLK_EN,
6038201209Srpaulo		    IWN_APMG_CLK_CTRL_DMA_CLK_RQT |
6039201209Srpaulo		    IWN_APMG_CLK_CTRL_BSM_CLK_RQT);
6040201209Srpaulo	} else {
6041201209Srpaulo		/* Enable DMA. */
6042201209Srpaulo		iwn_prph_write(sc, IWN_APMG_CLK_EN,
6043201209Srpaulo		    IWN_APMG_CLK_CTRL_DMA_CLK_RQT);
6044201209Srpaulo	}
6045198429Srpaulo	DELAY(20);
6046198429Srpaulo
6047201209Srpaulo	/* Disable L1-Active. */
6048198429Srpaulo	iwn_prph_setbits(sc, IWN_APMG_PCI_STT, IWN_APMG_PCI_STT_L1A_DIS);
6049198429Srpaulo	iwn_nic_unlock(sc);
6050198429Srpaulo
6051198429Srpaulo	return 0;
6052198429Srpaulo}
6053198429Srpaulo
6054206477Sbschmidtstatic void
6055198429Srpauloiwn_apm_stop_master(struct iwn_softc *sc)
6056178676Ssam{
6057178676Ssam	int ntries;
6058178676Ssam
6059201209Srpaulo	/* Stop busmaster DMA activity. */
6060198429Srpaulo	IWN_SETBITS(sc, IWN_RESET, IWN_RESET_STOP_MASTER);
6061178676Ssam	for (ntries = 0; ntries < 100; ntries++) {
6062198429Srpaulo		if (IWN_READ(sc, IWN_RESET) & IWN_RESET_MASTER_DISABLED)
6063198429Srpaulo			return;
6064178676Ssam		DELAY(10);
6065178676Ssam	}
6066198429Srpaulo	device_printf(sc->sc_dev, "%s: timeout waiting for master\n",
6067198429Srpaulo	    __func__);
6068178676Ssam}
6069178676Ssam
6070206477Sbschmidtstatic void
6071198429Srpauloiwn_apm_stop(struct iwn_softc *sc)
6072198429Srpaulo{
6073198429Srpaulo	iwn_apm_stop_master(sc);
6074198429Srpaulo
6075201209Srpaulo	/* Reset the entire device. */
6076198429Srpaulo	IWN_SETBITS(sc, IWN_RESET, IWN_RESET_SW);
6077198429Srpaulo	DELAY(10);
6078198429Srpaulo	/* Clear "initialization complete" bit. */
6079198429Srpaulo	IWN_CLRBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_INIT_DONE);
6080198429Srpaulo}
6081198429Srpaulo
6082206477Sbschmidtstatic int
6083198429Srpauloiwn4965_nic_config(struct iwn_softc *sc)
6084178676Ssam{
6085198429Srpaulo	if (IWN_RFCFG_TYPE(sc->rfcfg) == 1) {
6086198429Srpaulo		/*
6087198429Srpaulo		 * I don't believe this to be correct but this is what the
6088198429Srpaulo		 * vendor driver is doing. Probably the bits should not be
6089198429Srpaulo		 * shifted in IWN_RFCFG_*.
6090198429Srpaulo		 */
6091198429Srpaulo		IWN_SETBITS(sc, IWN_HW_IF_CONFIG,
6092198429Srpaulo		    IWN_RFCFG_TYPE(sc->rfcfg) |
6093198429Srpaulo		    IWN_RFCFG_STEP(sc->rfcfg) |
6094198429Srpaulo		    IWN_RFCFG_DASH(sc->rfcfg));
6095198429Srpaulo	}
6096198429Srpaulo	IWN_SETBITS(sc, IWN_HW_IF_CONFIG,
6097198429Srpaulo	    IWN_HW_IF_CONFIG_RADIO_SI | IWN_HW_IF_CONFIG_MAC_SI);
6098198429Srpaulo	return 0;
6099198429Srpaulo}
6100178676Ssam
6101206477Sbschmidtstatic int
6102198429Srpauloiwn5000_nic_config(struct iwn_softc *sc)
6103198429Srpaulo{
6104198429Srpaulo	uint32_t tmp;
6105198429Srpaulo	int error;
6106178676Ssam
6107198429Srpaulo	if (IWN_RFCFG_TYPE(sc->rfcfg) < 3) {
6108198429Srpaulo		IWN_SETBITS(sc, IWN_HW_IF_CONFIG,
6109198429Srpaulo		    IWN_RFCFG_TYPE(sc->rfcfg) |
6110198429Srpaulo		    IWN_RFCFG_STEP(sc->rfcfg) |
6111198429Srpaulo		    IWN_RFCFG_DASH(sc->rfcfg));
6112198429Srpaulo	}
6113198429Srpaulo	IWN_SETBITS(sc, IWN_HW_IF_CONFIG,
6114198429Srpaulo	    IWN_HW_IF_CONFIG_RADIO_SI | IWN_HW_IF_CONFIG_MAC_SI);
6115198429Srpaulo
6116198429Srpaulo	error = iwn_nic_lock(sc);
6117198429Srpaulo	if (error != 0)
6118198429Srpaulo		return error;
6119198429Srpaulo	iwn_prph_setbits(sc, IWN_APMG_PS, IWN_APMG_PS_EARLY_PWROFF_DIS);
6120201209Srpaulo
6121201209Srpaulo	if (sc->hw_type == IWN_HW_REV_TYPE_1000) {
6122201209Srpaulo		/*
6123201209Srpaulo		 * Select first Switching Voltage Regulator (1.32V) to
6124201209Srpaulo		 * solve a stability issue related to noisy DC2DC line
6125201209Srpaulo		 * in the silicon of 1000 Series.
6126201209Srpaulo		 */
6127201209Srpaulo		tmp = iwn_prph_read(sc, IWN_APMG_DIGITAL_SVR);
6128201209Srpaulo		tmp &= ~IWN_APMG_DIGITAL_SVR_VOLTAGE_MASK;
6129201209Srpaulo		tmp |= IWN_APMG_DIGITAL_SVR_VOLTAGE_1_32;
6130201209Srpaulo		iwn_prph_write(sc, IWN_APMG_DIGITAL_SVR, tmp);
6131201209Srpaulo	}
6132198429Srpaulo	iwn_nic_unlock(sc);
6133201209Srpaulo
6134201209Srpaulo	if (sc->sc_flags & IWN_FLAG_INTERNAL_PA) {
6135201209Srpaulo		/* Use internal power amplifier only. */
6136201209Srpaulo		IWN_WRITE(sc, IWN_GP_DRIVER, IWN_GP_DRIVER_RADIO_2X2_IPA);
6137201209Srpaulo	}
6138210108Sbschmidt	if (sc->hw_type == IWN_HW_REV_TYPE_6050 && sc->calib_ver >= 6) {
6139210108Sbschmidt		/* Indicate that ROM calibration version is >=6. */
6140210108Sbschmidt		IWN_SETBITS(sc, IWN_GP_DRIVER, IWN_GP_DRIVER_CALIB_VER6);
6141206444Sbschmidt	}
6142198429Srpaulo	return 0;
6143198429Srpaulo}
6144198429Srpaulo
6145198429Srpaulo/*
6146198429Srpaulo * Take NIC ownership over Intel Active Management Technology (AMT).
6147198429Srpaulo */
6148206477Sbschmidtstatic int
6149198429Srpauloiwn_hw_prepare(struct iwn_softc *sc)
6150198429Srpaulo{
6151198429Srpaulo	int ntries;
6152198429Srpaulo
6153201209Srpaulo	/* Check if hardware is ready. */
6154201209Srpaulo	IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_NIC_READY);
6155201209Srpaulo	for (ntries = 0; ntries < 5; ntries++) {
6156201209Srpaulo		if (IWN_READ(sc, IWN_HW_IF_CONFIG) &
6157201209Srpaulo		    IWN_HW_IF_CONFIG_NIC_READY)
6158201209Srpaulo			return 0;
6159201209Srpaulo		DELAY(10);
6160201209Srpaulo	}
6161201209Srpaulo
6162201209Srpaulo	/* Hardware not ready, force into ready state. */
6163198429Srpaulo	IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_PREPARE);
6164198429Srpaulo	for (ntries = 0; ntries < 15000; ntries++) {
6165198429Srpaulo		if (!(IWN_READ(sc, IWN_HW_IF_CONFIG) &
6166198429Srpaulo		    IWN_HW_IF_CONFIG_PREPARE_DONE))
6167178676Ssam			break;
6168178676Ssam		DELAY(10);
6169178676Ssam	}
6170198429Srpaulo	if (ntries == 15000)
6171178676Ssam		return ETIMEDOUT;
6172198429Srpaulo
6173201209Srpaulo	/* Hardware should be ready now. */
6174198429Srpaulo	IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_NIC_READY);
6175198429Srpaulo	for (ntries = 0; ntries < 5; ntries++) {
6176198429Srpaulo		if (IWN_READ(sc, IWN_HW_IF_CONFIG) &
6177198429Srpaulo		    IWN_HW_IF_CONFIG_NIC_READY)
6178198429Srpaulo			return 0;
6179198429Srpaulo		DELAY(10);
6180178676Ssam	}
6181198429Srpaulo	return ETIMEDOUT;
6182178676Ssam}
6183178676Ssam
6184206477Sbschmidtstatic int
6185198429Srpauloiwn_hw_init(struct iwn_softc *sc)
6186178676Ssam{
6187198429Srpaulo	const struct iwn_hal *hal = sc->sc_hal;
6188198429Srpaulo	int error, chnl, qid;
6189178676Ssam
6190198429Srpaulo	/* Clear pending interrupts. */
6191198429Srpaulo	IWN_WRITE(sc, IWN_INT, 0xffffffff);
6192178676Ssam
6193201209Srpaulo	error = iwn_apm_init(sc);
6194198429Srpaulo	if (error != 0) {
6195198429Srpaulo		device_printf(sc->sc_dev,
6196198429Srpaulo		    "%s: could not power ON adapter, error %d\n",
6197198429Srpaulo		    __func__, error);
6198198429Srpaulo		return error;
6199178676Ssam	}
6200178676Ssam
6201198429Srpaulo	/* Select VMAIN power source. */
6202198429Srpaulo	error = iwn_nic_lock(sc);
6203198429Srpaulo	if (error != 0)
6204198429Srpaulo		return error;
6205198429Srpaulo	iwn_prph_clrbits(sc, IWN_APMG_PS, IWN_APMG_PS_PWR_SRC_MASK);
6206198429Srpaulo	iwn_nic_unlock(sc);
6207178676Ssam
6208198429Srpaulo	/* Perform adapter-specific initialization. */
6209198429Srpaulo	error = hal->nic_config(sc);
6210198429Srpaulo	if (error != 0)
6211198429Srpaulo		return error;
6212178676Ssam
6213198429Srpaulo	/* Initialize RX ring. */
6214198429Srpaulo	error = iwn_nic_lock(sc);
6215198429Srpaulo	if (error != 0)
6216198429Srpaulo		return error;
6217198429Srpaulo	IWN_WRITE(sc, IWN_FH_RX_CONFIG, 0);
6218198429Srpaulo	IWN_WRITE(sc, IWN_FH_RX_WPTR, 0);
6219198429Srpaulo	/* Set physical address of RX ring (256-byte aligned.) */
6220198429Srpaulo	IWN_WRITE(sc, IWN_FH_RX_BASE, sc->rxq.desc_dma.paddr >> 8);
6221198429Srpaulo	/* Set physical address of RX status (16-byte aligned.) */
6222198429Srpaulo	IWN_WRITE(sc, IWN_FH_STATUS_WPTR, sc->rxq.stat_dma.paddr >> 4);
6223198429Srpaulo	/* Enable RX. */
6224198429Srpaulo	IWN_WRITE(sc, IWN_FH_RX_CONFIG,
6225198429Srpaulo	    IWN_FH_RX_CONFIG_ENA           |
6226198429Srpaulo	    IWN_FH_RX_CONFIG_IGN_RXF_EMPTY |	/* HW bug workaround */
6227198429Srpaulo	    IWN_FH_RX_CONFIG_IRQ_DST_HOST  |
6228198429Srpaulo	    IWN_FH_RX_CONFIG_SINGLE_FRAME  |
6229198429Srpaulo	    IWN_FH_RX_CONFIG_RB_TIMEOUT(0) |
6230198429Srpaulo	    IWN_FH_RX_CONFIG_NRBD(IWN_RX_RING_COUNT_LOG));
6231198429Srpaulo	iwn_nic_unlock(sc);
6232198429Srpaulo	IWN_WRITE(sc, IWN_FH_RX_WPTR, (IWN_RX_RING_COUNT - 1) & ~7);
6233178676Ssam
6234198429Srpaulo	error = iwn_nic_lock(sc);
6235198429Srpaulo	if (error != 0)
6236198429Srpaulo		return error;
6237178676Ssam
6238198429Srpaulo	/* Initialize TX scheduler. */
6239198429Srpaulo	iwn_prph_write(sc, hal->sched_txfact_addr, 0);
6240178676Ssam
6241198429Srpaulo	/* Set physical address of "keep warm" page (16-byte aligned.) */
6242198429Srpaulo	IWN_WRITE(sc, IWN_FH_KW_ADDR, sc->kw_dma.paddr >> 4);
6243198429Srpaulo
6244198429Srpaulo	/* Initialize TX rings. */
6245198429Srpaulo	for (qid = 0; qid < hal->ntxqs; qid++) {
6246198429Srpaulo		struct iwn_tx_ring *txq = &sc->txq[qid];
6247198429Srpaulo
6248198429Srpaulo		/* Set physical address of TX ring (256-byte aligned.) */
6249198429Srpaulo		IWN_WRITE(sc, IWN_FH_CBBC_QUEUE(qid),
6250198429Srpaulo		    txq->desc_dma.paddr >> 8);
6251178676Ssam	}
6252198429Srpaulo	iwn_nic_unlock(sc);
6253178676Ssam
6254198429Srpaulo	/* Enable DMA channels. */
6255198429Srpaulo	for (chnl = 0; chnl < hal->ndmachnls; chnl++) {
6256198429Srpaulo		IWN_WRITE(sc, IWN_FH_TX_CONFIG(chnl),
6257198429Srpaulo		    IWN_FH_TX_CONFIG_DMA_ENA |
6258198429Srpaulo		    IWN_FH_TX_CONFIG_DMA_CREDIT_ENA);
6259198429Srpaulo	}
6260198429Srpaulo
6261198429Srpaulo	/* Clear "radio off" and "commands blocked" bits. */
6262198429Srpaulo	IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_RFKILL);
6263198429Srpaulo	IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_CMD_BLOCKED);
6264198429Srpaulo
6265198429Srpaulo	/* Clear pending interrupts. */
6266198429Srpaulo	IWN_WRITE(sc, IWN_INT, 0xffffffff);
6267198429Srpaulo	/* Enable interrupt coalescing. */
6268198429Srpaulo	IWN_WRITE(sc, IWN_INT_COALESCING, 512 / 8);
6269198429Srpaulo	/* Enable interrupts. */
6270201209Srpaulo	IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask);
6271198429Srpaulo
6272198429Srpaulo	/* _Really_ make sure "radio off" bit is cleared! */
6273198429Srpaulo	IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_RFKILL);
6274198429Srpaulo	IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_RFKILL);
6275198429Srpaulo
6276198429Srpaulo	error = hal->load_firmware(sc);
6277178676Ssam	if (error != 0) {
6278178676Ssam		device_printf(sc->sc_dev,
6279198429Srpaulo		    "%s: could not load firmware, error %d\n",
6280198429Srpaulo		    __func__, error);
6281198429Srpaulo		return error;
6282178676Ssam	}
6283198429Srpaulo	/* Wait at most one second for firmware alive notification. */
6284198429Srpaulo	error = msleep(sc, &sc->sc_mtx, PCATCH, "iwninit", hz);
6285198429Srpaulo	if (error != 0) {
6286198429Srpaulo		device_printf(sc->sc_dev,
6287198429Srpaulo		    "%s: timeout waiting for adapter to initialize, error %d\n",
6288198429Srpaulo		    __func__, error);
6289198429Srpaulo		return error;
6290198429Srpaulo	}
6291198429Srpaulo	/* Do post-firmware initialization. */
6292198429Srpaulo	return hal->post_alive(sc);
6293198429Srpaulo}
6294178676Ssam
6295206477Sbschmidtstatic void
6296198429Srpauloiwn_hw_stop(struct iwn_softc *sc)
6297198429Srpaulo{
6298198429Srpaulo	const struct iwn_hal *hal = sc->sc_hal;
6299198429Srpaulo	int chnl, qid, ntries;
6300178676Ssam
6301198429Srpaulo	IWN_WRITE(sc, IWN_RESET, IWN_RESET_NEVO);
6302178676Ssam
6303198429Srpaulo	/* Disable interrupts. */
6304201209Srpaulo	IWN_WRITE(sc, IWN_INT_MASK, 0);
6305198429Srpaulo	IWN_WRITE(sc, IWN_INT, 0xffffffff);
6306198429Srpaulo	IWN_WRITE(sc, IWN_FH_INT, 0xffffffff);
6307201209Srpaulo	sc->sc_flags &= ~IWN_FLAG_USE_ICT;
6308178676Ssam
6309198429Srpaulo	/* Make sure we no longer hold the NIC lock. */
6310198429Srpaulo	iwn_nic_unlock(sc);
6311178676Ssam
6312198429Srpaulo	/* Stop TX scheduler. */
6313198429Srpaulo	iwn_prph_write(sc, hal->sched_txfact_addr, 0);
6314178676Ssam
6315198429Srpaulo	/* Stop all DMA channels. */
6316198429Srpaulo	if (iwn_nic_lock(sc) == 0) {
6317198429Srpaulo		for (chnl = 0; chnl < hal->ndmachnls; chnl++) {
6318198429Srpaulo			IWN_WRITE(sc, IWN_FH_TX_CONFIG(chnl), 0);
6319198429Srpaulo			for (ntries = 0; ntries < 200; ntries++) {
6320220659Sbschmidt				if (IWN_READ(sc, IWN_FH_TX_STATUS) &
6321198429Srpaulo				    IWN_FH_TX_STATUS_IDLE(chnl))
6322198429Srpaulo					break;
6323198429Srpaulo				DELAY(10);
6324198429Srpaulo			}
6325198429Srpaulo		}
6326198429Srpaulo		iwn_nic_unlock(sc);
6327198429Srpaulo	}
6328178676Ssam
6329198429Srpaulo	/* Stop RX ring. */
6330198429Srpaulo	iwn_reset_rx_ring(sc, &sc->rxq);
6331178676Ssam
6332198429Srpaulo	/* Reset all TX rings. */
6333198429Srpaulo	for (qid = 0; qid < hal->ntxqs; qid++)
6334198429Srpaulo		iwn_reset_tx_ring(sc, &sc->txq[qid]);
6335178676Ssam
6336198429Srpaulo	if (iwn_nic_lock(sc) == 0) {
6337201209Srpaulo		iwn_prph_write(sc, IWN_APMG_CLK_DIS,
6338201209Srpaulo		    IWN_APMG_CLK_CTRL_DMA_CLK_RQT);
6339198429Srpaulo		iwn_nic_unlock(sc);
6340178676Ssam	}
6341198429Srpaulo	DELAY(5);
6342178676Ssam
6343198429Srpaulo	/* Power OFF adapter. */
6344198429Srpaulo	iwn_apm_stop(sc);
6345198429Srpaulo}
6346178676Ssam
6347206477Sbschmidtstatic void
6348198429Srpauloiwn_init_locked(struct iwn_softc *sc)
6349198429Srpaulo{
6350198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
6351198429Srpaulo	int error;
6352178676Ssam
6353198429Srpaulo	IWN_LOCK_ASSERT(sc);
6354178676Ssam
6355198429Srpaulo	error = iwn_hw_prepare(sc);
6356198429Srpaulo	if (error != 0) {
6357198429Srpaulo		device_printf(sc->sc_dev, "%s: hardware not ready, eror %d\n",
6358198429Srpaulo		    __func__, error);
6359198429Srpaulo		goto fail;
6360198429Srpaulo	}
6361198429Srpaulo
6362201209Srpaulo	/* Initialize interrupt mask to default value. */
6363201209Srpaulo	sc->int_mask = IWN_INT_MASK_DEF;
6364201209Srpaulo	sc->sc_flags &= ~IWN_FLAG_USE_ICT;
6365201209Srpaulo
6366198429Srpaulo	/* Check that the radio is not disabled by hardware switch. */
6367198429Srpaulo	if (!(IWN_READ(sc, IWN_GP_CNTRL) & IWN_GP_CNTRL_RFKILL)) {
6368178676Ssam		device_printf(sc->sc_dev,
6369201209Srpaulo		    "radio is disabled by hardware switch\n");
6370201209Srpaulo
6371201209Srpaulo		/* Enable interrupts to get RF toggle notifications. */
6372201209Srpaulo		IWN_WRITE(sc, IWN_INT, 0xffffffff);
6373201209Srpaulo		IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask);
6374201209Srpaulo		return;
6375178676Ssam	}
6376178676Ssam
6377198429Srpaulo	/* Read firmware images from the filesystem. */
6378198429Srpaulo	error = iwn_read_firmware(sc);
6379178676Ssam	if (error != 0) {
6380178676Ssam		device_printf(sc->sc_dev,
6381198429Srpaulo		    "%s: could not read firmware, error %d\n",
6382198429Srpaulo		    __func__, error);
6383198429Srpaulo		goto fail;
6384178676Ssam	}
6385178676Ssam
6386198429Srpaulo	/* Initialize hardware and upload firmware. */
6387198429Srpaulo	error = iwn_hw_init(sc);
6388201209Srpaulo	firmware_put(sc->fw_fp, FIRMWARE_UNLOAD);
6389201209Srpaulo	sc->fw_fp = NULL;
6390198429Srpaulo	if (error != 0) {
6391198429Srpaulo		device_printf(sc->sc_dev,
6392198429Srpaulo		    "%s: could not initialize hardware, error %d\n",
6393198429Srpaulo		    __func__, error);
6394198429Srpaulo		goto fail;
6395198429Srpaulo	}
6396178676Ssam
6397198429Srpaulo	/* Configure adapter now that it is ready. */
6398178676Ssam	error = iwn_config(sc);
6399178676Ssam	if (error != 0) {
6400178676Ssam		device_printf(sc->sc_dev,
6401178676Ssam		    "%s: could not configure device, error %d\n",
6402178676Ssam		    __func__, error);
6403198429Srpaulo		goto fail;
6404178676Ssam	}
6405178676Ssam
6406178676Ssam	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
6407178676Ssam	ifp->if_drv_flags |= IFF_DRV_RUNNING;
6408198429Srpaulo
6409198429Srpaulo	return;
6410198429Srpaulo
6411198429Srpaulofail:
6412198429Srpaulo	iwn_stop_locked(sc);
6413178676Ssam}
6414178676Ssam
6415206477Sbschmidtstatic void
6416178676Ssamiwn_init(void *arg)
6417178676Ssam{
6418178676Ssam	struct iwn_softc *sc = arg;
6419178676Ssam	struct ifnet *ifp = sc->sc_ifp;
6420178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
6421178676Ssam
6422178676Ssam	IWN_LOCK(sc);
6423178676Ssam	iwn_init_locked(sc);
6424178676Ssam	IWN_UNLOCK(sc);
6425178676Ssam
6426178676Ssam	if (ifp->if_drv_flags & IFF_DRV_RUNNING)
6427178676Ssam		ieee80211_start_all(ic);
6428178676Ssam}
6429178676Ssam
6430206477Sbschmidtstatic void
6431178676Ssamiwn_stop_locked(struct iwn_softc *sc)
6432178676Ssam{
6433178676Ssam	struct ifnet *ifp = sc->sc_ifp;
6434178676Ssam
6435178676Ssam	IWN_LOCK_ASSERT(sc);
6436178676Ssam
6437178676Ssam	sc->sc_tx_timer = 0;
6438178676Ssam	callout_stop(&sc->sc_timer_to);
6439178676Ssam	ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
6440178676Ssam
6441198429Srpaulo	/* Power OFF hardware. */
6442198429Srpaulo	iwn_hw_stop(sc);
6443198429Srpaulo}
6444178676Ssam
6445206477Sbschmidtstatic void
6446178676Ssamiwn_stop(struct iwn_softc *sc)
6447178676Ssam{
6448178676Ssam	IWN_LOCK(sc);
6449178676Ssam	iwn_stop_locked(sc);
6450178676Ssam	IWN_UNLOCK(sc);
6451178676Ssam}
6452178676Ssam
6453178676Ssam/*
6454178676Ssam * Callback from net80211 to start a scan.
6455178676Ssam */
6456178676Ssamstatic void
6457178676Ssamiwn_scan_start(struct ieee80211com *ic)
6458178676Ssam{
6459178676Ssam	struct ifnet *ifp = ic->ic_ifp;
6460178676Ssam	struct iwn_softc *sc = ifp->if_softc;
6461178676Ssam
6462191746Sthompsa	IWN_LOCK(sc);
6463191746Sthompsa	/* make the link LED blink while we're scanning */
6464191746Sthompsa	iwn_set_led(sc, IWN_LED_LINK, 20, 2);
6465191746Sthompsa	IWN_UNLOCK(sc);
6466178676Ssam}
6467178676Ssam
6468178676Ssam/*
6469178676Ssam * Callback from net80211 to terminate a scan.
6470178676Ssam */
6471178676Ssamstatic void
6472178676Ssamiwn_scan_end(struct ieee80211com *ic)
6473178676Ssam{
6474201209Srpaulo	struct ifnet *ifp = ic->ic_ifp;
6475201209Srpaulo	struct iwn_softc *sc = ifp->if_softc;
6476201209Srpaulo	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
6477201209Srpaulo
6478201209Srpaulo	IWN_LOCK(sc);
6479201209Srpaulo	if (vap->iv_state == IEEE80211_S_RUN) {
6480201209Srpaulo		/* Set link LED to ON status if we are associated */
6481201209Srpaulo		iwn_set_led(sc, IWN_LED_LINK, 0, 1);
6482201209Srpaulo	}
6483201209Srpaulo	IWN_UNLOCK(sc);
6484178676Ssam}
6485178676Ssam
6486178676Ssam/*
6487178676Ssam * Callback from net80211 to force a channel change.
6488178676Ssam */
6489178676Ssamstatic void
6490178676Ssamiwn_set_channel(struct ieee80211com *ic)
6491178676Ssam{
6492198429Srpaulo	const struct ieee80211_channel *c = ic->ic_curchan;
6493178676Ssam	struct ifnet *ifp = ic->ic_ifp;
6494178676Ssam	struct iwn_softc *sc = ifp->if_softc;
6495178676Ssam
6496191746Sthompsa	IWN_LOCK(sc);
6497201209Srpaulo	sc->sc_rxtap.wr_chan_freq = htole16(c->ic_freq);
6498201209Srpaulo	sc->sc_rxtap.wr_chan_flags = htole16(c->ic_flags);
6499201209Srpaulo	sc->sc_txtap.wt_chan_freq = htole16(c->ic_freq);
6500201209Srpaulo	sc->sc_txtap.wt_chan_flags = htole16(c->ic_flags);
6501191746Sthompsa	IWN_UNLOCK(sc);
6502178676Ssam}
6503178676Ssam
6504178676Ssam/*
6505178676Ssam * Callback from net80211 to start scanning of the current channel.
6506178676Ssam */
6507178676Ssamstatic void
6508178676Ssamiwn_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell)
6509178676Ssam{
6510178676Ssam	struct ieee80211vap *vap = ss->ss_vap;
6511178676Ssam	struct iwn_softc *sc = vap->iv_ic->ic_ifp->if_softc;
6512191746Sthompsa	int error;
6513178676Ssam
6514191746Sthompsa	IWN_LOCK(sc);
6515191746Sthompsa	error = iwn_scan(sc);
6516191746Sthompsa	IWN_UNLOCK(sc);
6517191746Sthompsa	if (error != 0)
6518191746Sthompsa		ieee80211_cancel_scan(vap);
6519178676Ssam}
6520178676Ssam
6521178676Ssam/*
6522178676Ssam * Callback from net80211 to handle the minimum dwell time being met.
6523178676Ssam * The intent is to terminate the scan but we just let the firmware
6524178676Ssam * notify us when it's finished as we have no safe way to abort it.
6525178676Ssam */
6526178676Ssamstatic void
6527178676Ssamiwn_scan_mindwell(struct ieee80211_scan_state *ss)
6528178676Ssam{
6529178676Ssam	/* NB: don't try to abort scan; wait for firmware to finish */
6530178676Ssam}
6531178676Ssam
6532201209Srpaulostatic struct iwn_eeprom_chan *
6533201209Srpauloiwn_find_eeprom_channel(struct iwn_softc *sc, struct ieee80211_channel *c)
6534201209Srpaulo{
6535201209Srpaulo	int i, j;
6536201209Srpaulo
6537201209Srpaulo	for (j = 0; j < 7; j++) {
6538201209Srpaulo		for (i = 0; i < iwn_bands[j].nchan; i++) {
6539201209Srpaulo			if (iwn_bands[j].chan[i] == c->ic_ieee)
6540201209Srpaulo				return &sc->eeprom_channels[j][i];
6541201209Srpaulo		}
6542201209Srpaulo	}
6543201209Srpaulo
6544201209Srpaulo	return NULL;
6545201209Srpaulo}
6546201209Srpaulo
6547201209Srpaulo/*
6548201209Srpaulo * Enforce flags read from EEPROM.
6549201209Srpaulo */
6550201209Srpaulostatic int
6551201209Srpauloiwn_setregdomain(struct ieee80211com *ic, struct ieee80211_regdomain *rd,
6552201209Srpaulo    int nchan, struct ieee80211_channel chans[])
6553201209Srpaulo{
6554201209Srpaulo	struct iwn_softc *sc = ic->ic_ifp->if_softc;
6555201209Srpaulo	int i;
6556201209Srpaulo
6557201209Srpaulo	for (i = 0; i < nchan; i++) {
6558201209Srpaulo		struct ieee80211_channel *c = &chans[i];
6559201209Srpaulo		struct iwn_eeprom_chan *channel;
6560201209Srpaulo
6561201209Srpaulo		channel = iwn_find_eeprom_channel(sc, c);
6562201209Srpaulo		if (channel == NULL) {
6563201209Srpaulo			if_printf(ic->ic_ifp,
6564201209Srpaulo			    "%s: invalid channel %u freq %u/0x%x\n",
6565201209Srpaulo			    __func__, c->ic_ieee, c->ic_freq, c->ic_flags);
6566201209Srpaulo			return EINVAL;
6567201209Srpaulo		}
6568201209Srpaulo		c->ic_flags |= iwn_eeprom_channel_flags(channel);
6569201209Srpaulo	}
6570201209Srpaulo
6571201209Srpaulo	return 0;
6572201209Srpaulo}
6573201209Srpaulo
6574178676Ssamstatic void
6575198429Srpauloiwn_hw_reset(void *arg0, int pending)
6576178676Ssam{
6577178676Ssam	struct iwn_softc *sc = arg0;
6578178676Ssam	struct ifnet *ifp = sc->sc_ifp;
6579178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
6580178676Ssam
6581201209Srpaulo	iwn_stop(sc);
6582191746Sthompsa	iwn_init(sc);
6583191746Sthompsa	ieee80211_notify_radio(ic, 1);
6584191746Sthompsa}
6585178676Ssam
6586191746Sthompsastatic void
6587198429Srpauloiwn_radio_on(void *arg0, int pending)
6588191746Sthompsa{
6589191746Sthompsa	struct iwn_softc *sc = arg0;
6590201209Srpaulo	struct ifnet *ifp = sc->sc_ifp;
6591201209Srpaulo	struct ieee80211com *ic = ifp->if_l2com;
6592201209Srpaulo	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
6593178676Ssam
6594201209Srpaulo	if (vap != NULL) {
6595201209Srpaulo		iwn_init(sc);
6596201209Srpaulo		ieee80211_init(vap);
6597201209Srpaulo	}
6598178676Ssam}
6599178676Ssam
6600191746Sthompsastatic void
6601198429Srpauloiwn_radio_off(void *arg0, int pending)
6602178676Ssam{
6603191746Sthompsa	struct iwn_softc *sc = arg0;
6604191746Sthompsa	struct ifnet *ifp = sc->sc_ifp;
6605191746Sthompsa	struct ieee80211com *ic = ifp->if_l2com;
6606201209Srpaulo	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
6607191746Sthompsa
6608201209Srpaulo	iwn_stop(sc);
6609201209Srpaulo	if (vap != NULL)
6610201209Srpaulo		ieee80211_stop(vap);
6611201209Srpaulo
6612201209Srpaulo	/* Enable interrupts to get RF toggle notification. */
6613191746Sthompsa	IWN_LOCK(sc);
6614201209Srpaulo	IWN_WRITE(sc, IWN_INT, 0xffffffff);
6615201209Srpaulo	IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask);
6616191746Sthompsa	IWN_UNLOCK(sc);
6617178676Ssam}
6618178676Ssam
6619178676Ssamstatic void
6620178676Ssamiwn_sysctlattach(struct iwn_softc *sc)
6621178676Ssam{
6622178676Ssam	struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev);
6623178676Ssam	struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev);
6624178676Ssam
6625178676Ssam#ifdef IWN_DEBUG
6626178676Ssam	sc->sc_debug = 0;
6627178676Ssam	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
6628178676Ssam	    "debug", CTLFLAG_RW, &sc->sc_debug, 0, "control debugging printfs");
6629178676Ssam#endif
6630178676Ssam}
6631178676Ssam
6632198429Srpaulostatic int
6633198429Srpauloiwn_shutdown(device_t dev)
6634198429Srpaulo{
6635198429Srpaulo	struct iwn_softc *sc = device_get_softc(dev);
6636198429Srpaulo
6637198429Srpaulo	iwn_stop(sc);
6638198429Srpaulo	return 0;
6639198429Srpaulo}
6640198429Srpaulo
6641198429Srpaulostatic int
6642198429Srpauloiwn_suspend(device_t dev)
6643198429Srpaulo{
6644198429Srpaulo	struct iwn_softc *sc = device_get_softc(dev);
6645201209Srpaulo	struct ifnet *ifp = sc->sc_ifp;
6646201209Srpaulo	struct ieee80211com *ic = ifp->if_l2com;
6647201209Srpaulo	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
6648198429Srpaulo
6649198429Srpaulo	iwn_stop(sc);
6650201209Srpaulo	if (vap != NULL)
6651201209Srpaulo		ieee80211_stop(vap);
6652198429Srpaulo	return 0;
6653198429Srpaulo}
6654198429Srpaulo
6655198429Srpaulostatic int
6656198429Srpauloiwn_resume(device_t dev)
6657198429Srpaulo{
6658198429Srpaulo	struct iwn_softc *sc = device_get_softc(dev);
6659198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
6660201209Srpaulo	struct ieee80211com *ic = ifp->if_l2com;
6661201209Srpaulo	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
6662198429Srpaulo
6663201209Srpaulo	/* Clear device-specific "PCI retry timeout" register (41h). */
6664198429Srpaulo	pci_write_config(dev, 0x41, 0, 1);
6665198429Srpaulo
6666201209Srpaulo	if (ifp->if_flags & IFF_UP) {
6667198429Srpaulo		iwn_init(sc);
6668201209Srpaulo		if (vap != NULL)
6669201209Srpaulo			ieee80211_init(vap);
6670201209Srpaulo		if (ifp->if_drv_flags & IFF_DRV_RUNNING)
6671201209Srpaulo			iwn_start(ifp);
6672201209Srpaulo	}
6673198429Srpaulo	return 0;
6674198429Srpaulo}
6675198429Srpaulo
6676178676Ssam#ifdef IWN_DEBUG
6677178676Ssamstatic const char *
6678178676Ssamiwn_intr_str(uint8_t cmd)
6679178676Ssam{
6680178676Ssam	switch (cmd) {
6681178676Ssam	/* Notifications */
6682178676Ssam	case IWN_UC_READY:		return "UC_READY";
6683178676Ssam	case IWN_ADD_NODE_DONE:		return "ADD_NODE_DONE";
6684178676Ssam	case IWN_TX_DONE:		return "TX_DONE";
6685178676Ssam	case IWN_START_SCAN:		return "START_SCAN";
6686178676Ssam	case IWN_STOP_SCAN:		return "STOP_SCAN";
6687178676Ssam	case IWN_RX_STATISTICS:		return "RX_STATS";
6688178676Ssam	case IWN_BEACON_STATISTICS:	return "BEACON_STATS";
6689178676Ssam	case IWN_STATE_CHANGED:		return "STATE_CHANGED";
6690178676Ssam	case IWN_BEACON_MISSED:		return "BEACON_MISSED";
6691198429Srpaulo	case IWN_RX_PHY:		return "RX_PHY";
6692198429Srpaulo	case IWN_MPDU_RX_DONE:		return "MPDU_RX_DONE";
6693178676Ssam	case IWN_RX_DONE:		return "RX_DONE";
6694178676Ssam
6695178676Ssam	/* Command Notifications */
6696201209Srpaulo	case IWN_CMD_RXON:		return "IWN_CMD_RXON";
6697201209Srpaulo	case IWN_CMD_RXON_ASSOC:	return "IWN_CMD_RXON_ASSOC";
6698178676Ssam	case IWN_CMD_EDCA_PARAMS:	return "IWN_CMD_EDCA_PARAMS";
6699198429Srpaulo	case IWN_CMD_TIMING:		return "IWN_CMD_TIMING";
6700198429Srpaulo	case IWN_CMD_LINK_QUALITY:	return "IWN_CMD_LINK_QUALITY";
6701178676Ssam	case IWN_CMD_SET_LED:		return "IWN_CMD_SET_LED";
6702198429Srpaulo	case IWN5000_CMD_WIMAX_COEX:	return "IWN5000_CMD_WIMAX_COEX";
6703198429Srpaulo	case IWN5000_CMD_CALIB_CONFIG:	return "IWN5000_CMD_CALIB_CONFIG";
6704202986Srpaulo	case IWN5000_CMD_CALIB_RESULT:	return "IWN5000_CMD_CALIB_RESULT";
6705202986Srpaulo	case IWN5000_CMD_CALIB_COMPLETE: return "IWN5000_CMD_CALIB_COMPLETE";
6706178676Ssam	case IWN_CMD_SET_POWER_MODE:	return "IWN_CMD_SET_POWER_MODE";
6707178676Ssam	case IWN_CMD_SCAN:		return "IWN_CMD_SCAN";
6708202986Srpaulo	case IWN_CMD_SCAN_RESULTS:	return "IWN_CMD_SCAN_RESULTS";
6709178676Ssam	case IWN_CMD_TXPOWER:		return "IWN_CMD_TXPOWER";
6710198429Srpaulo	case IWN_CMD_TXPOWER_DBM:	return "IWN_CMD_TXPOWER_DBM";
6711202986Srpaulo	case IWN5000_CMD_TX_ANT_CONFIG:	return "IWN5000_CMD_TX_ANT_CONFIG";
6712198429Srpaulo	case IWN_CMD_BT_COEX:		return "IWN_CMD_BT_COEX";
6713178676Ssam	case IWN_CMD_SET_CRITICAL_TEMP:	return "IWN_CMD_SET_CRITICAL_TEMP";
6714198429Srpaulo	case IWN_CMD_SET_SENSITIVITY:	return "IWN_CMD_SET_SENSITIVITY";
6715198429Srpaulo	case IWN_CMD_PHY_CALIB:		return "IWN_CMD_PHY_CALIB";
6716178676Ssam	}
6717178676Ssam	return "UNKNOWN INTR NOTIF/CMD";
6718178676Ssam}
6719178676Ssam#endif /* IWN_DEBUG */
6720178676Ssam
6721178676Ssamstatic device_method_t iwn_methods[] = {
6722198429Srpaulo	/* Device interface */
6723198429Srpaulo	DEVMETHOD(device_probe,		iwn_probe),
6724198429Srpaulo	DEVMETHOD(device_attach,	iwn_attach),
6725198429Srpaulo	DEVMETHOD(device_detach,	iwn_detach),
6726198429Srpaulo	DEVMETHOD(device_shutdown,	iwn_shutdown),
6727198429Srpaulo	DEVMETHOD(device_suspend,	iwn_suspend),
6728198429Srpaulo	DEVMETHOD(device_resume,	iwn_resume),
6729198429Srpaulo	{ 0, 0 }
6730178676Ssam};
6731178676Ssam
6732178676Ssamstatic driver_t iwn_driver = {
6733198429Srpaulo	"iwn",
6734198429Srpaulo	iwn_methods,
6735198429Srpaulo	sizeof (struct iwn_softc)
6736178676Ssam};
6737178676Ssamstatic devclass_t iwn_devclass;
6738198429Srpaulo
6739178676SsamDRIVER_MODULE(iwn, pci, iwn_driver, iwn_devclass, 0, 0);
6740178676SsamMODULE_DEPEND(iwn, pci, 1, 1, 1);
6741178676SsamMODULE_DEPEND(iwn, firmware, 1, 1, 1);
6742178676SsamMODULE_DEPEND(iwn, wlan, 1, 1, 1);
6743