1178676Ssam/*-
2254204Sadrian * Copyright (c) 2013 Cedric GROSS <c.gross@kreiz-it.fr>
3254204Sadrian * Copyright (c) 2011 Intel Corporation
4198429Srpaulo * Copyright (c) 2007-2009
5178676Ssam *	Damien Bergamini <damien.bergamini@free.fr>
6178676Ssam * Copyright (c) 2008
7178676Ssam *	Benjamin Close <benjsc@FreeBSD.org>
8178676Ssam * Copyright (c) 2008 Sam Leffler, Errno Consulting
9178676Ssam *
10178676Ssam * Permission to use, copy, modify, and distribute this software for any
11178676Ssam * purpose with or without fee is hereby granted, provided that the above
12178676Ssam * copyright notice and this permission notice appear in all copies.
13178676Ssam *
14178676Ssam * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
15178676Ssam * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16178676Ssam * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
17178676Ssam * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18178676Ssam * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
19178676Ssam * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
20178676Ssam * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21178676Ssam */
22178676Ssam
23178676Ssam/*
24201209Srpaulo * Driver for Intel WiFi Link 4965 and 1000/5000/6000 Series 802.11 network
25201209Srpaulo * adapters.
26178676Ssam */
27178676Ssam
28178676Ssam#include <sys/cdefs.h>
29178676Ssam__FBSDID("$FreeBSD: releng/10.2/sys/dev/iwn/if_iwn.c 281480 2015-04-13 01:01:17Z eadler $");
30178676Ssam
31243692Sadrian#include "opt_wlan.h"
32253868Sadrian#include "opt_iwn.h"
33243692Sadrian
34178676Ssam#include <sys/param.h>
35178676Ssam#include <sys/sockio.h>
36178676Ssam#include <sys/sysctl.h>
37178676Ssam#include <sys/mbuf.h>
38178676Ssam#include <sys/kernel.h>
39178676Ssam#include <sys/socket.h>
40178676Ssam#include <sys/systm.h>
41178676Ssam#include <sys/malloc.h>
42178676Ssam#include <sys/bus.h>
43178676Ssam#include <sys/rman.h>
44178676Ssam#include <sys/endian.h>
45178676Ssam#include <sys/firmware.h>
46178676Ssam#include <sys/limits.h>
47178676Ssam#include <sys/module.h>
48178676Ssam#include <sys/queue.h>
49178676Ssam#include <sys/taskqueue.h>
50178676Ssam
51178676Ssam#include <machine/bus.h>
52178676Ssam#include <machine/resource.h>
53178676Ssam#include <machine/clock.h>
54178676Ssam
55178676Ssam#include <dev/pci/pcireg.h>
56178676Ssam#include <dev/pci/pcivar.h>
57178676Ssam
58178676Ssam#include <net/bpf.h>
59178676Ssam#include <net/if.h>
60178676Ssam#include <net/if_arp.h>
61178676Ssam#include <net/ethernet.h>
62178676Ssam#include <net/if_dl.h>
63178676Ssam#include <net/if_media.h>
64178676Ssam#include <net/if_types.h>
65178676Ssam
66178676Ssam#include <netinet/in.h>
67178676Ssam#include <netinet/in_systm.h>
68178676Ssam#include <netinet/in_var.h>
69178676Ssam#include <netinet/if_ether.h>
70178676Ssam#include <netinet/ip.h>
71178676Ssam
72178676Ssam#include <net80211/ieee80211_var.h>
73178676Ssam#include <net80211/ieee80211_radiotap.h>
74178676Ssam#include <net80211/ieee80211_regdomain.h>
75206358Srpaulo#include <net80211/ieee80211_ratectl.h>
76178676Ssam
77178676Ssam#include <dev/iwn/if_iwnreg.h>
78178676Ssam#include <dev/iwn/if_iwnvar.h>
79253897Sadrian#include <dev/iwn/if_iwn_devid.h>
80178676Ssam
81220723Sbschmidtstruct iwn_ident {
82220723Sbschmidt	uint16_t	vendor;
83220723Sbschmidt	uint16_t	device;
84220723Sbschmidt	const char	*name;
85220723Sbschmidt};
86220723Sbschmidt
87220895Sbschmidtstatic const struct iwn_ident iwn_ident_table[] = {
88253897Sadrian	{ 0x8086, IWN_DID_6x05_1, "Intel Centrino Advanced-N 6205"		},
89253897Sadrian	{ 0x8086, IWN_DID_1000_1, "Intel Centrino Wireless-N 1000"		},
90253897Sadrian	{ 0x8086, IWN_DID_1000_2, "Intel Centrino Wireless-N 1000"		},
91253897Sadrian	{ 0x8086, IWN_DID_6x05_2, "Intel Centrino Advanced-N 6205"		},
92253897Sadrian	{ 0x8086, IWN_DID_6050_1, "Intel Centrino Advanced-N + WiMAX 6250"	},
93253897Sadrian	{ 0x8086, IWN_DID_6050_2, "Intel Centrino Advanced-N + WiMAX 6250"	},
94253897Sadrian	{ 0x8086, IWN_DID_x030_1, "Intel Centrino Wireless-N 1030"		},
95253897Sadrian	{ 0x8086, IWN_DID_x030_2, "Intel Centrino Wireless-N 1030"		},
96253897Sadrian	{ 0x8086, IWN_DID_x030_3, "Intel Centrino Advanced-N 6230"		},
97253897Sadrian	{ 0x8086, IWN_DID_x030_4, "Intel Centrino Advanced-N 6230"		},
98253897Sadrian	{ 0x8086, IWN_DID_6150_1, "Intel Centrino Wireless-N + WiMAX 6150"	},
99253897Sadrian	{ 0x8086, IWN_DID_6150_2, "Intel Centrino Wireless-N + WiMAX 6150"	},
100253897Sadrian	{ 0x8086, IWN_DID_2x30_1, "Intel Centrino Wireless-N 2230"		},
101253897Sadrian	{ 0x8086, IWN_DID_2x30_2, "Intel Centrino Wireless-N 2230"		},
102253897Sadrian	{ 0x8086, IWN_DID_130_1, "Intel Centrino Wireless-N 130"		},
103253897Sadrian	{ 0x8086, IWN_DID_130_2, "Intel Centrino Wireless-N 130"		},
104253897Sadrian	{ 0x8086, IWN_DID_100_1, "Intel Centrino Wireless-N 100"		},
105253897Sadrian	{ 0x8086, IWN_DID_100_2, "Intel Centrino Wireless-N 100"		},
106253897Sadrian	{ 0x8086, IWN_DID_4965_1, "Intel Wireless WiFi Link 4965"		},
107253897Sadrian	{ 0x8086, IWN_DID_6x00_1, "Intel Centrino Ultimate-N 6300"		},
108253897Sadrian	{ 0x8086, IWN_DID_6x00_2, "Intel Centrino Advanced-N 6200"		},
109253897Sadrian	{ 0x8086, IWN_DID_4965_2, "Intel Wireless WiFi Link 4965"		},
110253897Sadrian	{ 0x8086, IWN_DID_4965_3, "Intel Wireless WiFi Link 4965"		},
111253897Sadrian	{ 0x8086, IWN_DID_5x00_1, "Intel WiFi Link 5100"			},
112253897Sadrian	{ 0x8086, IWN_DID_4965_4, "Intel Wireless WiFi Link 4965"		},
113253897Sadrian	{ 0x8086, IWN_DID_5x00_3, "Intel Ultimate N WiFi Link 5300"		},
114253897Sadrian	{ 0x8086, IWN_DID_5x00_4, "Intel Ultimate N WiFi Link 5300"		},
115253897Sadrian	{ 0x8086, IWN_DID_5x00_2, "Intel WiFi Link 5100"			},
116253897Sadrian	{ 0x8086, IWN_DID_6x00_3, "Intel Centrino Ultimate-N 6300"		},
117253897Sadrian	{ 0x8086, IWN_DID_6x00_4, "Intel Centrino Advanced-N 6200"		},
118253897Sadrian	{ 0x8086, IWN_DID_5x50_1, "Intel WiMAX/WiFi Link 5350"			},
119253897Sadrian	{ 0x8086, IWN_DID_5x50_2, "Intel WiMAX/WiFi Link 5350"			},
120253897Sadrian	{ 0x8086, IWN_DID_5x50_3, "Intel WiMAX/WiFi Link 5150"			},
121253897Sadrian	{ 0x8086, IWN_DID_5x50_4, "Intel WiMAX/WiFi Link 5150"			},
122220723Sbschmidt	{ 0, 0, NULL }
123220723Sbschmidt};
124220723Sbschmidt
125178676Ssamstatic int	iwn_probe(device_t);
126178676Ssamstatic int	iwn_attach(device_t);
127220728Sbschmidtstatic int	iwn4965_attach(struct iwn_softc *, uint16_t);
128220728Sbschmidtstatic int	iwn5000_attach(struct iwn_softc *, uint16_t);
129206477Sbschmidtstatic void	iwn_radiotap_attach(struct iwn_softc *);
130220723Sbschmidtstatic void	iwn_sysctlattach(struct iwn_softc *);
131178676Ssamstatic struct ieee80211vap *iwn_vap_create(struct ieee80211com *,
132228621Sbschmidt		    const char [IFNAMSIZ], int, enum ieee80211_opmode, int,
133228621Sbschmidt		    const uint8_t [IEEE80211_ADDR_LEN],
134228621Sbschmidt		    const uint8_t [IEEE80211_ADDR_LEN]);
135178676Ssamstatic void	iwn_vap_delete(struct ieee80211vap *);
136206474Sbschmidtstatic int	iwn_detach(device_t);
137220723Sbschmidtstatic int	iwn_shutdown(device_t);
138220723Sbschmidtstatic int	iwn_suspend(device_t);
139220723Sbschmidtstatic int	iwn_resume(device_t);
140206477Sbschmidtstatic int	iwn_nic_lock(struct iwn_softc *);
141206477Sbschmidtstatic int	iwn_eeprom_lock(struct iwn_softc *);
142206477Sbschmidtstatic int	iwn_init_otprom(struct iwn_softc *);
143206477Sbschmidtstatic int	iwn_read_prom_data(struct iwn_softc *, uint32_t, void *, int);
144206474Sbschmidtstatic void	iwn_dma_map_addr(void *, bus_dma_segment_t *, int, int);
145178676Ssamstatic int	iwn_dma_contig_alloc(struct iwn_softc *, struct iwn_dma_info *,
146220691Sbschmidt		    void **, bus_size_t, bus_size_t);
147178676Ssamstatic void	iwn_dma_contig_free(struct iwn_dma_info *);
148206477Sbschmidtstatic int	iwn_alloc_sched(struct iwn_softc *);
149206477Sbschmidtstatic void	iwn_free_sched(struct iwn_softc *);
150206477Sbschmidtstatic int	iwn_alloc_kw(struct iwn_softc *);
151206477Sbschmidtstatic void	iwn_free_kw(struct iwn_softc *);
152206477Sbschmidtstatic int	iwn_alloc_ict(struct iwn_softc *);
153206477Sbschmidtstatic void	iwn_free_ict(struct iwn_softc *);
154206477Sbschmidtstatic int	iwn_alloc_fwmem(struct iwn_softc *);
155206477Sbschmidtstatic void	iwn_free_fwmem(struct iwn_softc *);
156206477Sbschmidtstatic int	iwn_alloc_rx_ring(struct iwn_softc *, struct iwn_rx_ring *);
157206477Sbschmidtstatic void	iwn_reset_rx_ring(struct iwn_softc *, struct iwn_rx_ring *);
158206477Sbschmidtstatic void	iwn_free_rx_ring(struct iwn_softc *, struct iwn_rx_ring *);
159206477Sbschmidtstatic int	iwn_alloc_tx_ring(struct iwn_softc *, struct iwn_tx_ring *,
160178676Ssam		    int);
161206477Sbschmidtstatic void	iwn_reset_tx_ring(struct iwn_softc *, struct iwn_tx_ring *);
162206477Sbschmidtstatic void	iwn_free_tx_ring(struct iwn_softc *, struct iwn_tx_ring *);
163206477Sbschmidtstatic void	iwn5000_ict_reset(struct iwn_softc *);
164206477Sbschmidtstatic int	iwn_read_eeprom(struct iwn_softc *,
165198429Srpaulo		    uint8_t macaddr[IEEE80211_ADDR_LEN]);
166206477Sbschmidtstatic void	iwn4965_read_eeprom(struct iwn_softc *);
167253866Sadrian#ifdef	IWN_DEBUG
168206477Sbschmidtstatic void	iwn4965_print_power_group(struct iwn_softc *, int);
169253866Sadrian#endif
170206477Sbschmidtstatic void	iwn5000_read_eeprom(struct iwn_softc *);
171206474Sbschmidtstatic uint32_t	iwn_eeprom_channel_flags(struct iwn_eeprom_chan *);
172206474Sbschmidtstatic void	iwn_read_eeprom_band(struct iwn_softc *, int);
173206474Sbschmidtstatic void	iwn_read_eeprom_ht40(struct iwn_softc *, int);
174220726Sbschmidtstatic void	iwn_read_eeprom_channels(struct iwn_softc *, int, uint32_t);
175220723Sbschmidtstatic struct iwn_eeprom_chan *iwn_find_eeprom_channel(struct iwn_softc *,
176220723Sbschmidt		    struct ieee80211_channel *);
177220723Sbschmidtstatic int	iwn_setregdomain(struct ieee80211com *,
178220723Sbschmidt		    struct ieee80211_regdomain *, int,
179220726Sbschmidt		    struct ieee80211_channel[]);
180206477Sbschmidtstatic void	iwn_read_eeprom_enhinfo(struct iwn_softc *);
181206477Sbschmidtstatic struct ieee80211_node *iwn_node_alloc(struct ieee80211vap *,
182198429Srpaulo		    const uint8_t mac[IEEE80211_ADDR_LEN]);
183220715Sbschmidtstatic void	iwn_newassoc(struct ieee80211_node *, int);
184206477Sbschmidtstatic int	iwn_media_change(struct ifnet *);
185206477Sbschmidtstatic int	iwn_newstate(struct ieee80211vap *, enum ieee80211_state, int);
186220667Sbschmidtstatic void	iwn_calib_timeout(void *);
187206477Sbschmidtstatic void	iwn_rx_phy(struct iwn_softc *, struct iwn_rx_desc *,
188198429Srpaulo		    struct iwn_rx_data *);
189206477Sbschmidtstatic void	iwn_rx_done(struct iwn_softc *, struct iwn_rx_desc *,
190178676Ssam		    struct iwn_rx_data *);
191206477Sbschmidtstatic void	iwn_rx_compressed_ba(struct iwn_softc *, struct iwn_rx_desc *,
192201209Srpaulo		    struct iwn_rx_data *);
193220674Sbschmidtstatic void	iwn5000_rx_calib_results(struct iwn_softc *,
194220674Sbschmidt		    struct iwn_rx_desc *, struct iwn_rx_data *);
195206477Sbschmidtstatic void	iwn_rx_statistics(struct iwn_softc *, struct iwn_rx_desc *,
196198429Srpaulo		    struct iwn_rx_data *);
197206477Sbschmidtstatic void	iwn4965_tx_done(struct iwn_softc *, struct iwn_rx_desc *,
198198429Srpaulo		    struct iwn_rx_data *);
199206477Sbschmidtstatic void	iwn5000_tx_done(struct iwn_softc *, struct iwn_rx_desc *,
200198429Srpaulo		    struct iwn_rx_data *);
201206477Sbschmidtstatic void	iwn_tx_done(struct iwn_softc *, struct iwn_rx_desc *, int,
202198429Srpaulo		    uint8_t);
203221651Sbschmidtstatic void	iwn_ampdu_tx_done(struct iwn_softc *, int, int, int, void *);
204206477Sbschmidtstatic void	iwn_cmd_done(struct iwn_softc *, struct iwn_rx_desc *);
205206477Sbschmidtstatic void	iwn_notif_intr(struct iwn_softc *);
206206477Sbschmidtstatic void	iwn_wakeup_intr(struct iwn_softc *);
207206477Sbschmidtstatic void	iwn_rftoggle_intr(struct iwn_softc *);
208206477Sbschmidtstatic void	iwn_fatal_intr(struct iwn_softc *);
209206477Sbschmidtstatic void	iwn_intr(void *);
210206477Sbschmidtstatic void	iwn4965_update_sched(struct iwn_softc *, int, int, uint8_t,
211198429Srpaulo		    uint16_t);
212206477Sbschmidtstatic void	iwn5000_update_sched(struct iwn_softc *, int, int, uint8_t,
213198429Srpaulo		    uint16_t);
214206475Sbschmidt#ifdef notyet
215206477Sbschmidtstatic void	iwn5000_reset_sched(struct iwn_softc *, int, int);
216206475Sbschmidt#endif
217206477Sbschmidtstatic int	iwn_tx_data(struct iwn_softc *, struct mbuf *,
218220720Sbschmidt		    struct ieee80211_node *);
219220720Sbschmidtstatic int	iwn_tx_data_raw(struct iwn_softc *, struct mbuf *,
220220720Sbschmidt		    struct ieee80211_node *,
221220720Sbschmidt		    const struct ieee80211_bpf_params *params);
222198429Srpaulostatic int	iwn_raw_xmit(struct ieee80211_node *, struct mbuf *,
223198429Srpaulo		    const struct ieee80211_bpf_params *);
224206477Sbschmidtstatic void	iwn_start(struct ifnet *);
225206477Sbschmidtstatic void	iwn_start_locked(struct ifnet *);
226220667Sbschmidtstatic void	iwn_watchdog(void *);
227206477Sbschmidtstatic int	iwn_ioctl(struct ifnet *, u_long, caddr_t);
228206477Sbschmidtstatic int	iwn_cmd(struct iwn_softc *, int, const void *, int, int);
229206477Sbschmidtstatic int	iwn4965_add_node(struct iwn_softc *, struct iwn_node_info *,
230198429Srpaulo		    int);
231206477Sbschmidtstatic int	iwn5000_add_node(struct iwn_softc *, struct iwn_node_info *,
232198429Srpaulo		    int);
233220715Sbschmidtstatic int	iwn_set_link_quality(struct iwn_softc *,
234220715Sbschmidt		    struct ieee80211_node *);
235206477Sbschmidtstatic int	iwn_add_broadcast_node(struct iwn_softc *, int);
236220721Sbschmidtstatic int	iwn_updateedca(struct ieee80211com *);
237201209Srpaulostatic void	iwn_update_mcast(struct ifnet *);
238206477Sbschmidtstatic void	iwn_set_led(struct iwn_softc *, uint8_t, uint8_t, uint8_t);
239206477Sbschmidtstatic int	iwn_set_critical_temp(struct iwn_softc *);
240206477Sbschmidtstatic int	iwn_set_timing(struct iwn_softc *, struct ieee80211_node *);
241206477Sbschmidtstatic void	iwn4965_power_calibration(struct iwn_softc *, int);
242206477Sbschmidtstatic int	iwn4965_set_txpower(struct iwn_softc *,
243201882Skeramida		    struct ieee80211_channel *, int);
244206477Sbschmidtstatic int	iwn5000_set_txpower(struct iwn_softc *,
245201882Skeramida		    struct ieee80211_channel *, int);
246206477Sbschmidtstatic int	iwn4965_get_rssi(struct iwn_softc *, struct iwn_rx_stat *);
247206477Sbschmidtstatic int	iwn5000_get_rssi(struct iwn_softc *, struct iwn_rx_stat *);
248206477Sbschmidtstatic int	iwn_get_noise(const struct iwn_rx_general_stats *);
249206477Sbschmidtstatic int	iwn4965_get_temperature(struct iwn_softc *);
250206477Sbschmidtstatic int	iwn5000_get_temperature(struct iwn_softc *);
251206477Sbschmidtstatic int	iwn_init_sensitivity(struct iwn_softc *);
252206477Sbschmidtstatic void	iwn_collect_noise(struct iwn_softc *,
253178676Ssam		    const struct iwn_rx_general_stats *);
254206477Sbschmidtstatic int	iwn4965_init_gains(struct iwn_softc *);
255206477Sbschmidtstatic int	iwn5000_init_gains(struct iwn_softc *);
256206477Sbschmidtstatic int	iwn4965_set_gains(struct iwn_softc *);
257206477Sbschmidtstatic int	iwn5000_set_gains(struct iwn_softc *);
258206477Sbschmidtstatic void	iwn_tune_sensitivity(struct iwn_softc *,
259178676Ssam		    const struct iwn_rx_stats *);
260206477Sbschmidtstatic int	iwn_send_sensitivity(struct iwn_softc *);
261206477Sbschmidtstatic int	iwn_set_pslevel(struct iwn_softc *, int, int, int);
262220662Sbschmidtstatic int	iwn_send_btcoex(struct iwn_softc *);
263220891Sbschmidtstatic int	iwn_send_advanced_btcoex(struct iwn_softc *);
264227805Sbschmidtstatic int	iwn5000_runtime_calib(struct iwn_softc *);
265206477Sbschmidtstatic int	iwn_config(struct iwn_softc *);
266220634Sbschmidtstatic uint8_t	*ieee80211_add_ssid(uint8_t *, const uint8_t *, u_int);
267206477Sbschmidtstatic int	iwn_scan(struct iwn_softc *);
268206477Sbschmidtstatic int	iwn_auth(struct iwn_softc *, struct ieee80211vap *vap);
269206477Sbschmidtstatic int	iwn_run(struct iwn_softc *, struct ieee80211vap *vap);
270221650Sbschmidtstatic int	iwn_ampdu_rx_start(struct ieee80211_node *,
271221650Sbschmidt		    struct ieee80211_rx_ampdu *, int, int, int);
272221650Sbschmidtstatic void	iwn_ampdu_rx_stop(struct ieee80211_node *,
273221650Sbschmidt		    struct ieee80211_rx_ampdu *);
274221651Sbschmidtstatic int	iwn_addba_request(struct ieee80211_node *,
275221651Sbschmidt		    struct ieee80211_tx_ampdu *, int, int, int);
276221651Sbschmidtstatic int	iwn_addba_response(struct ieee80211_node *,
277221651Sbschmidt		    struct ieee80211_tx_ampdu *, int, int, int);
278206474Sbschmidtstatic int	iwn_ampdu_tx_start(struct ieee80211com *,
279206474Sbschmidt		    struct ieee80211_node *, uint8_t);
280221651Sbschmidtstatic void	iwn_ampdu_tx_stop(struct ieee80211_node *,
281221651Sbschmidt		    struct ieee80211_tx_ampdu *);
282206474Sbschmidtstatic void	iwn4965_ampdu_tx_start(struct iwn_softc *,
283221651Sbschmidt		    struct ieee80211_node *, int, uint8_t, uint16_t);
284221651Sbschmidtstatic void	iwn4965_ampdu_tx_stop(struct iwn_softc *, int,
285220726Sbschmidt		    uint8_t, uint16_t);
286206474Sbschmidtstatic void	iwn5000_ampdu_tx_start(struct iwn_softc *,
287221651Sbschmidt		    struct ieee80211_node *, int, uint8_t, uint16_t);
288221651Sbschmidtstatic void	iwn5000_ampdu_tx_stop(struct iwn_softc *, int,
289220726Sbschmidt		    uint8_t, uint16_t);
290220674Sbschmidtstatic int	iwn5000_query_calibration(struct iwn_softc *);
291220674Sbschmidtstatic int	iwn5000_send_calibration(struct iwn_softc *);
292206477Sbschmidtstatic int	iwn5000_send_wimax_coex(struct iwn_softc *);
293220677Sbschmidtstatic int	iwn5000_crystal_calib(struct iwn_softc *);
294220676Sbschmidtstatic int	iwn5000_temp_offset_calib(struct iwn_softc *);
295206477Sbschmidtstatic int	iwn4965_post_alive(struct iwn_softc *);
296206477Sbschmidtstatic int	iwn5000_post_alive(struct iwn_softc *);
297206477Sbschmidtstatic int	iwn4965_load_bootcode(struct iwn_softc *, const uint8_t *,
298198429Srpaulo		    int);
299206477Sbschmidtstatic int	iwn4965_load_firmware(struct iwn_softc *);
300206477Sbschmidtstatic int	iwn5000_load_firmware_section(struct iwn_softc *, uint32_t,
301198429Srpaulo		    const uint8_t *, int);
302206477Sbschmidtstatic int	iwn5000_load_firmware(struct iwn_softc *);
303210111Sbschmidtstatic int	iwn_read_firmware_leg(struct iwn_softc *,
304210111Sbschmidt		    struct iwn_fw_info *);
305210111Sbschmidtstatic int	iwn_read_firmware_tlv(struct iwn_softc *,
306210111Sbschmidt		    struct iwn_fw_info *, uint16_t);
307206477Sbschmidtstatic int	iwn_read_firmware(struct iwn_softc *);
308206477Sbschmidtstatic int	iwn_clock_wait(struct iwn_softc *);
309206477Sbschmidtstatic int	iwn_apm_init(struct iwn_softc *);
310206477Sbschmidtstatic void	iwn_apm_stop_master(struct iwn_softc *);
311206477Sbschmidtstatic void	iwn_apm_stop(struct iwn_softc *);
312206477Sbschmidtstatic int	iwn4965_nic_config(struct iwn_softc *);
313206477Sbschmidtstatic int	iwn5000_nic_config(struct iwn_softc *);
314206477Sbschmidtstatic int	iwn_hw_prepare(struct iwn_softc *);
315206477Sbschmidtstatic int	iwn_hw_init(struct iwn_softc *);
316206477Sbschmidtstatic void	iwn_hw_stop(struct iwn_softc *);
317220723Sbschmidtstatic void	iwn_radio_on(void *, int);
318220723Sbschmidtstatic void	iwn_radio_off(void *, int);
319206477Sbschmidtstatic void	iwn_init_locked(struct iwn_softc *);
320206477Sbschmidtstatic void	iwn_init(void *);
321206477Sbschmidtstatic void	iwn_stop_locked(struct iwn_softc *);
322206477Sbschmidtstatic void	iwn_stop(struct iwn_softc *);
323220726Sbschmidtstatic void	iwn_scan_start(struct ieee80211com *);
324220726Sbschmidtstatic void	iwn_scan_end(struct ieee80211com *);
325220726Sbschmidtstatic void	iwn_set_channel(struct ieee80211com *);
326220726Sbschmidtstatic void	iwn_scan_curchan(struct ieee80211_scan_state *, unsigned long);
327220726Sbschmidtstatic void	iwn_scan_mindwell(struct ieee80211_scan_state *);
328198429Srpaulostatic void	iwn_hw_reset(void *, int);
329253866Sadrian#ifdef	IWN_DEBUG
330253866Sadrianstatic char	*iwn_get_csr_string(int);
331253866Sadrianstatic void	iwn_debug_register(struct iwn_softc *);
332253866Sadrian#endif
333178676Ssam
334253866Sadrian#ifdef	IWN_DEBUG
335178676Ssamenum {
336178676Ssam	IWN_DEBUG_XMIT		= 0x00000001,	/* basic xmit operation */
337178676Ssam	IWN_DEBUG_RECV		= 0x00000002,	/* basic recv operation */
338178676Ssam	IWN_DEBUG_STATE		= 0x00000004,	/* 802.11 state transitions */
339178676Ssam	IWN_DEBUG_TXPOW		= 0x00000008,	/* tx power processing */
340178676Ssam	IWN_DEBUG_RESET		= 0x00000010,	/* reset processing */
341178676Ssam	IWN_DEBUG_OPS		= 0x00000020,	/* iwn_ops processing */
342178676Ssam	IWN_DEBUG_BEACON 	= 0x00000040,	/* beacon handling */
343178676Ssam	IWN_DEBUG_WATCHDOG 	= 0x00000080,	/* watchdog timeout */
344178676Ssam	IWN_DEBUG_INTR		= 0x00000100,	/* ISR */
345178676Ssam	IWN_DEBUG_CALIBRATE	= 0x00000200,	/* periodic calibration */
346178676Ssam	IWN_DEBUG_NODE		= 0x00000400,	/* node management */
347178676Ssam	IWN_DEBUG_LED		= 0x00000800,	/* led management */
348178676Ssam	IWN_DEBUG_CMD		= 0x00001000,	/* cmd submission */
349252727Sadrian	IWN_DEBUG_TXRATE	= 0x00002000,	/* TX rate debugging */
350252727Sadrian	IWN_DEBUG_PWRSAVE	= 0x00004000,	/* Power save operations */
351253866Sadrian	IWN_DEBUG_REGISTER	= 0x20000000,	/* print chipset register */
352253705Sadrian	IWN_DEBUG_TRACE		= 0x40000000,	/* Print begin and start driver function */
353178676Ssam	IWN_DEBUG_FATAL		= 0x80000000,	/* fatal errors */
354178676Ssam	IWN_DEBUG_ANY		= 0xffffffff
355178676Ssam};
356178676Ssam
357178676Ssam#define DPRINTF(sc, m, fmt, ...) do {			\
358178676Ssam	if (sc->sc_debug & (m))				\
359178676Ssam		printf(fmt, __VA_ARGS__);		\
360178676Ssam} while (0)
361178676Ssam
362220723Sbschmidtstatic const char *
363220723Sbschmidtiwn_intr_str(uint8_t cmd)
364220723Sbschmidt{
365220723Sbschmidt	switch (cmd) {
366220723Sbschmidt	/* Notifications */
367220723Sbschmidt	case IWN_UC_READY:		return "UC_READY";
368220723Sbschmidt	case IWN_ADD_NODE_DONE:		return "ADD_NODE_DONE";
369220723Sbschmidt	case IWN_TX_DONE:		return "TX_DONE";
370220723Sbschmidt	case IWN_START_SCAN:		return "START_SCAN";
371220723Sbschmidt	case IWN_STOP_SCAN:		return "STOP_SCAN";
372220723Sbschmidt	case IWN_RX_STATISTICS:		return "RX_STATS";
373220723Sbschmidt	case IWN_BEACON_STATISTICS:	return "BEACON_STATS";
374220723Sbschmidt	case IWN_STATE_CHANGED:		return "STATE_CHANGED";
375220723Sbschmidt	case IWN_BEACON_MISSED:		return "BEACON_MISSED";
376220723Sbschmidt	case IWN_RX_PHY:		return "RX_PHY";
377220723Sbschmidt	case IWN_MPDU_RX_DONE:		return "MPDU_RX_DONE";
378220723Sbschmidt	case IWN_RX_DONE:		return "RX_DONE";
379220723Sbschmidt
380220723Sbschmidt	/* Command Notifications */
381220723Sbschmidt	case IWN_CMD_RXON:		return "IWN_CMD_RXON";
382220723Sbschmidt	case IWN_CMD_RXON_ASSOC:	return "IWN_CMD_RXON_ASSOC";
383220723Sbschmidt	case IWN_CMD_EDCA_PARAMS:	return "IWN_CMD_EDCA_PARAMS";
384220723Sbschmidt	case IWN_CMD_TIMING:		return "IWN_CMD_TIMING";
385220723Sbschmidt	case IWN_CMD_LINK_QUALITY:	return "IWN_CMD_LINK_QUALITY";
386220723Sbschmidt	case IWN_CMD_SET_LED:		return "IWN_CMD_SET_LED";
387220723Sbschmidt	case IWN5000_CMD_WIMAX_COEX:	return "IWN5000_CMD_WIMAX_COEX";
388220723Sbschmidt	case IWN5000_CMD_CALIB_CONFIG:	return "IWN5000_CMD_CALIB_CONFIG";
389220723Sbschmidt	case IWN5000_CMD_CALIB_RESULT:	return "IWN5000_CMD_CALIB_RESULT";
390220723Sbschmidt	case IWN5000_CMD_CALIB_COMPLETE: return "IWN5000_CMD_CALIB_COMPLETE";
391220723Sbschmidt	case IWN_CMD_SET_POWER_MODE:	return "IWN_CMD_SET_POWER_MODE";
392220723Sbschmidt	case IWN_CMD_SCAN:		return "IWN_CMD_SCAN";
393220723Sbschmidt	case IWN_CMD_SCAN_RESULTS:	return "IWN_CMD_SCAN_RESULTS";
394220723Sbschmidt	case IWN_CMD_TXPOWER:		return "IWN_CMD_TXPOWER";
395220723Sbschmidt	case IWN_CMD_TXPOWER_DBM:	return "IWN_CMD_TXPOWER_DBM";
396220723Sbschmidt	case IWN5000_CMD_TX_ANT_CONFIG:	return "IWN5000_CMD_TX_ANT_CONFIG";
397220723Sbschmidt	case IWN_CMD_BT_COEX:		return "IWN_CMD_BT_COEX";
398220723Sbschmidt	case IWN_CMD_SET_CRITICAL_TEMP:	return "IWN_CMD_SET_CRITICAL_TEMP";
399220723Sbschmidt	case IWN_CMD_SET_SENSITIVITY:	return "IWN_CMD_SET_SENSITIVITY";
400220723Sbschmidt	case IWN_CMD_PHY_CALIB:		return "IWN_CMD_PHY_CALIB";
401220723Sbschmidt	}
402220723Sbschmidt	return "UNKNOWN INTR NOTIF/CMD";
403220723Sbschmidt}
404178676Ssam#else
405178676Ssam#define DPRINTF(sc, m, fmt, ...) do { (void) sc; } while (0)
406178676Ssam#endif
407178676Ssam
408220723Sbschmidtstatic device_method_t iwn_methods[] = {
409220723Sbschmidt	/* Device interface */
410220723Sbschmidt	DEVMETHOD(device_probe,		iwn_probe),
411220723Sbschmidt	DEVMETHOD(device_attach,	iwn_attach),
412220723Sbschmidt	DEVMETHOD(device_detach,	iwn_detach),
413220723Sbschmidt	DEVMETHOD(device_shutdown,	iwn_shutdown),
414220723Sbschmidt	DEVMETHOD(device_suspend,	iwn_suspend),
415220723Sbschmidt	DEVMETHOD(device_resume,	iwn_resume),
416264945Smarius
417264945Smarius	DEVMETHOD_END
418178676Ssam};
419178676Ssam
420220723Sbschmidtstatic driver_t iwn_driver = {
421220723Sbschmidt	"iwn",
422220723Sbschmidt	iwn_methods,
423220726Sbschmidt	sizeof(struct iwn_softc)
424178676Ssam};
425220723Sbschmidtstatic devclass_t iwn_devclass;
426178676Ssam
427264945SmariusDRIVER_MODULE(iwn, pci, iwn_driver, iwn_devclass, NULL, NULL);
428220726Sbschmidt
429222543SbschmidtMODULE_VERSION(iwn, 1);
430222543Sbschmidt
431220723SbschmidtMODULE_DEPEND(iwn, firmware, 1, 1, 1);
432220723SbschmidtMODULE_DEPEND(iwn, pci, 1, 1, 1);
433220723SbschmidtMODULE_DEPEND(iwn, wlan, 1, 1, 1);
434220723Sbschmidt
435178676Ssamstatic int
436178676Ssamiwn_probe(device_t dev)
437178676Ssam{
438198429Srpaulo	const struct iwn_ident *ident;
439178676Ssam
440198429Srpaulo	for (ident = iwn_ident_table; ident->name != NULL; ident++) {
441198429Srpaulo		if (pci_get_vendor(dev) == ident->vendor &&
442198429Srpaulo		    pci_get_device(dev) == ident->device) {
443198429Srpaulo			device_set_desc(dev, ident->name);
444264948Smarius			return (BUS_PROBE_DEFAULT);
445198429Srpaulo		}
446198429Srpaulo	}
447198429Srpaulo	return ENXIO;
448178676Ssam}
449178676Ssam
450178676Ssamstatic int
451178676Ssamiwn_attach(device_t dev)
452178676Ssam{
453178676Ssam	struct iwn_softc *sc = (struct iwn_softc *)device_get_softc(dev);
454178676Ssam	struct ieee80211com *ic;
455178676Ssam	struct ifnet *ifp;
456264945Smarius	int i, error, rid;
457190526Ssam	uint8_t macaddr[IEEE80211_ADDR_LEN];
458178676Ssam
459178676Ssam	sc->sc_dev = dev;
460178676Ssam
461253612Sadrian#ifdef	IWN_DEBUG
462253612Sadrian	error = resource_int_value(device_get_name(sc->sc_dev),
463253612Sadrian	    device_get_unit(sc->sc_dev), "debug", &(sc->sc_debug));
464253612Sadrian	if (error != 0)
465253612Sadrian		sc->sc_debug = 0;
466253612Sadrian#else
467253612Sadrian	sc->sc_debug = 0;
468253612Sadrian#endif
469253612Sadrian
470253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: begin\n",__func__);
471253705Sadrian
472198429Srpaulo	/*
473198429Srpaulo	 * Get the offset of the PCI Express Capability Structure in PCI
474198429Srpaulo	 * Configuration Space.
475198429Srpaulo	 */
476219902Sjhb	error = pci_find_cap(dev, PCIY_EXPRESS, &sc->sc_cap_off);
477198429Srpaulo	if (error != 0) {
478198429Srpaulo		device_printf(dev, "PCIe capability structure not found!\n");
479198429Srpaulo		return error;
480178676Ssam	}
481178676Ssam
482198429Srpaulo	/* Clear device-specific "PCI retry timeout" register (41h). */
483178676Ssam	pci_write_config(dev, 0x41, 0, 1);
484178676Ssam
485198429Srpaulo	/* Enable bus-mastering. */
486178676Ssam	pci_enable_busmaster(dev);
487178676Ssam
488264945Smarius	rid = PCIR_BAR(0);
489264945Smarius	sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
490198429Srpaulo	    RF_ACTIVE);
491220726Sbschmidt	if (sc->mem == NULL) {
492220724Sbschmidt		device_printf(dev, "can't map mem space\n");
493198429Srpaulo		error = ENOMEM;
494178676Ssam		return error;
495178676Ssam	}
496178676Ssam	sc->sc_st = rman_get_bustag(sc->mem);
497178676Ssam	sc->sc_sh = rman_get_bushandle(sc->mem);
498220726Sbschmidt
499264945Smarius	i = 1;
500264945Smarius	rid = 0;
501264945Smarius	if (pci_alloc_msi(dev, &i) == 0)
502264945Smarius		rid = 1;
503220725Sbschmidt	/* Install interrupt handler. */
504264945Smarius	sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE |
505264945Smarius	    (rid != 0 ? 0 : RF_SHAREABLE));
506178676Ssam	if (sc->irq == NULL) {
507220724Sbschmidt		device_printf(dev, "can't map interrupt\n");
508178676Ssam		error = ENOMEM;
509198429Srpaulo		goto fail;
510178676Ssam	}
511178676Ssam
512178676Ssam	IWN_LOCK_INIT(sc);
513178676Ssam
514220728Sbschmidt	/* Read hardware revision and attach. */
515253897Sadrian	sc->hw_type = (IWN_READ(sc, IWN_HW_REV) >> IWN_HW_REV_TYPE_SHIFT)
516253897Sadrian	    & IWN_HW_REV_TYPE_MASK;
517254204Sadrian	sc->subdevice_id = pci_get_subdevice(dev);
518220728Sbschmidt	if (sc->hw_type == IWN_HW_REV_TYPE_4965)
519220728Sbschmidt		error = iwn4965_attach(sc, pci_get_device(dev));
520220728Sbschmidt	else
521220728Sbschmidt		error = iwn5000_attach(sc, pci_get_device(dev));
522220728Sbschmidt	if (error != 0) {
523220728Sbschmidt		device_printf(dev, "could not attach device, error %d\n",
524220728Sbschmidt		    error);
525198429Srpaulo		goto fail;
526198429Srpaulo	}
527198429Srpaulo
528220726Sbschmidt	if ((error = iwn_hw_prepare(sc)) != 0) {
529198429Srpaulo		device_printf(dev, "hardware not ready, error %d\n", error);
530178676Ssam		goto fail;
531178676Ssam	}
532178676Ssam
533198429Srpaulo	/* Allocate DMA memory for firmware transfers. */
534220726Sbschmidt	if ((error = iwn_alloc_fwmem(sc)) != 0) {
535178676Ssam		device_printf(dev,
536198429Srpaulo		    "could not allocate memory for firmware, error %d\n",
537198429Srpaulo		    error);
538178676Ssam		goto fail;
539178676Ssam	}
540178676Ssam
541198429Srpaulo	/* Allocate "Keep Warm" page. */
542220726Sbschmidt	if ((error = iwn_alloc_kw(sc)) != 0) {
543178676Ssam		device_printf(dev,
544220724Sbschmidt		    "could not allocate keep warm page, error %d\n", error);
545178676Ssam		goto fail;
546178676Ssam	}
547178676Ssam
548201209Srpaulo	/* Allocate ICT table for 5000 Series. */
549201209Srpaulo	if (sc->hw_type != IWN_HW_REV_TYPE_4965 &&
550201209Srpaulo	    (error = iwn_alloc_ict(sc)) != 0) {
551220724Sbschmidt		device_printf(dev, "could not allocate ICT table, error %d\n",
552220724Sbschmidt		    error);
553201209Srpaulo		goto fail;
554201209Srpaulo	}
555201209Srpaulo
556198429Srpaulo	/* Allocate TX scheduler "rings". */
557220726Sbschmidt	if ((error = iwn_alloc_sched(sc)) != 0) {
558178676Ssam		device_printf(dev,
559220726Sbschmidt		    "could not allocate TX scheduler rings, error %d\n", error);
560178676Ssam		goto fail;
561178676Ssam	}
562178676Ssam
563220725Sbschmidt	/* Allocate TX rings (16 on 4965AGN, 20 on >=5000). */
564220728Sbschmidt	for (i = 0; i < sc->ntxqs; i++) {
565220726Sbschmidt		if ((error = iwn_alloc_tx_ring(sc, &sc->txq[i], i)) != 0) {
566178676Ssam			device_printf(dev,
567220724Sbschmidt			    "could not allocate TX ring %d, error %d\n", i,
568220724Sbschmidt			    error);
569178676Ssam			goto fail;
570178676Ssam		}
571178676Ssam	}
572178676Ssam
573198429Srpaulo	/* Allocate RX ring. */
574220726Sbschmidt	if ((error = iwn_alloc_rx_ring(sc, &sc->rxq)) != 0) {
575220724Sbschmidt		device_printf(dev, "could not allocate RX ring, error %d\n",
576220724Sbschmidt		    error);
577178676Ssam		goto fail;
578178676Ssam	}
579178676Ssam
580198429Srpaulo	/* Clear pending interrupts. */
581198429Srpaulo	IWN_WRITE(sc, IWN_INT, 0xffffffff);
582198429Srpaulo
583178676Ssam	ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211);
584178676Ssam	if (ifp == NULL) {
585178676Ssam		device_printf(dev, "can not allocate ifnet structure\n");
586178676Ssam		goto fail;
587178676Ssam	}
588220726Sbschmidt
589178676Ssam	ic = ifp->if_l2com;
590198429Srpaulo	ic->ic_ifp = ifp;
591178676Ssam	ic->ic_phytype = IEEE80211_T_OFDM;	/* not only, but not used */
592178676Ssam	ic->ic_opmode = IEEE80211_M_STA;	/* default to BSS mode */
593178676Ssam
594198429Srpaulo	/* Set device capabilities. */
595178676Ssam	ic->ic_caps =
596178957Ssam		  IEEE80211_C_STA		/* station mode supported */
597178957Ssam		| IEEE80211_C_MONITOR		/* monitor mode supported */
598222679Sbschmidt		| IEEE80211_C_BGSCAN		/* background scanning */
599178676Ssam		| IEEE80211_C_TXPMGT		/* tx power management */
600178676Ssam		| IEEE80211_C_SHSLOT		/* short slot time supported */
601178676Ssam		| IEEE80211_C_WPA
602178676Ssam		| IEEE80211_C_SHPREAMBLE	/* short preamble supported */
603178676Ssam#if 0
604178676Ssam		| IEEE80211_C_IBSS		/* ibss/adhoc mode */
605178676Ssam#endif
606178676Ssam		| IEEE80211_C_WME		/* WME */
607252717Sadrian		| IEEE80211_C_PMGT		/* Station-side power mgmt */
608178676Ssam		;
609221640Sbschmidt
610221642Sbschmidt	/* Read MAC address, channels, etc from EEPROM. */
611221642Sbschmidt	if ((error = iwn_read_eeprom(sc, macaddr)) != 0) {
612221642Sbschmidt		device_printf(dev, "could not read EEPROM, error %d\n",
613221642Sbschmidt		    error);
614221642Sbschmidt		goto fail;
615221642Sbschmidt	}
616221642Sbschmidt
617221642Sbschmidt	/* Count the number of available chains. */
618221642Sbschmidt	sc->ntxchains =
619221642Sbschmidt	    ((sc->txchainmask >> 2) & 1) +
620221642Sbschmidt	    ((sc->txchainmask >> 1) & 1) +
621221642Sbschmidt	    ((sc->txchainmask >> 0) & 1);
622221642Sbschmidt	sc->nrxchains =
623221642Sbschmidt	    ((sc->rxchainmask >> 2) & 1) +
624221642Sbschmidt	    ((sc->rxchainmask >> 1) & 1) +
625221642Sbschmidt	    ((sc->rxchainmask >> 0) & 1);
626221642Sbschmidt	if (bootverbose) {
627221642Sbschmidt		device_printf(dev, "MIMO %dT%dR, %.4s, address %6D\n",
628221642Sbschmidt		    sc->ntxchains, sc->nrxchains, sc->eeprom_domain,
629221642Sbschmidt		    macaddr, ":");
630221642Sbschmidt	}
631221642Sbschmidt
632221657Sbschmidt	if (sc->sc_flags & IWN_FLAG_HAS_11N) {
633221657Sbschmidt		ic->ic_rxstream = sc->nrxchains;
634221657Sbschmidt		ic->ic_txstream = sc->ntxchains;
635254085Sadrian
636254085Sadrian		/*
637254085Sadrian		 * The NICs we currently support cap out at 2x2 support
638254085Sadrian		 * separate from the chains being used.
639254085Sadrian		 *
640254085Sadrian		 * This is a total hack to work around that until some
641254085Sadrian		 * per-device method is implemented to return the
642254085Sadrian		 * actual stream support.
643254085Sadrian		 */
644254085Sadrian		if (ic->ic_rxstream > 2)
645254085Sadrian			ic->ic_rxstream = 2;
646254085Sadrian		if (ic->ic_txstream > 2)
647254085Sadrian			ic->ic_txstream = 2;
648254085Sadrian
649221657Sbschmidt		ic->ic_htcaps =
650221657Sbschmidt			  IEEE80211_HTCAP_SMPS_OFF	/* SMPS mode disabled */
651221657Sbschmidt			| IEEE80211_HTCAP_SHORTGI20	/* short GI in 20MHz */
652221657Sbschmidt			| IEEE80211_HTCAP_CHWIDTH40	/* 40MHz channel width*/
653221657Sbschmidt			| IEEE80211_HTCAP_SHORTGI40	/* short GI in 40MHz */
654222687Sbschmidt#ifdef notyet
655221657Sbschmidt			| IEEE80211_HTCAP_GREENFIELD
656201209Srpaulo#if IWN_RBUF_SIZE == 8192
657221657Sbschmidt			| IEEE80211_HTCAP_MAXAMSDU_7935	/* max A-MSDU length */
658221657Sbschmidt#else
659221657Sbschmidt			| IEEE80211_HTCAP_MAXAMSDU_3839	/* max A-MSDU length */
660178678Ssam#endif
661201209Srpaulo#endif
662221657Sbschmidt			/* s/w capabilities */
663221657Sbschmidt			| IEEE80211_HTC_HT		/* HT operation */
664221657Sbschmidt			| IEEE80211_HTC_AMPDU		/* tx A-MPDU */
665221657Sbschmidt#ifdef notyet
666221657Sbschmidt			| IEEE80211_HTC_AMSDU		/* tx A-MSDU */
667201209Srpaulo#endif
668221657Sbschmidt			;
669221657Sbschmidt	}
670201209Srpaulo
671178676Ssam	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
672178676Ssam	ifp->if_softc = sc;
673178676Ssam	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
674178676Ssam	ifp->if_init = iwn_init;
675178676Ssam	ifp->if_ioctl = iwn_ioctl;
676178676Ssam	ifp->if_start = iwn_start;
677207554Ssobomax	IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen);
678207554Ssobomax	ifp->if_snd.ifq_drv_maxlen = ifqmaxlen;
679178676Ssam	IFQ_SET_READY(&ifp->if_snd);
680178676Ssam
681190526Ssam	ieee80211_ifattach(ic, macaddr);
682178676Ssam	ic->ic_vap_create = iwn_vap_create;
683178676Ssam	ic->ic_vap_delete = iwn_vap_delete;
684178676Ssam	ic->ic_raw_xmit = iwn_raw_xmit;
685178676Ssam	ic->ic_node_alloc = iwn_node_alloc;
686221650Sbschmidt	sc->sc_ampdu_rx_start = ic->ic_ampdu_rx_start;
687220723Sbschmidt	ic->ic_ampdu_rx_start = iwn_ampdu_rx_start;
688221650Sbschmidt	sc->sc_ampdu_rx_stop = ic->ic_ampdu_rx_stop;
689220723Sbschmidt	ic->ic_ampdu_rx_stop = iwn_ampdu_rx_stop;
690221651Sbschmidt	sc->sc_addba_request = ic->ic_addba_request;
691221651Sbschmidt	ic->ic_addba_request = iwn_addba_request;
692221651Sbschmidt	sc->sc_addba_response = ic->ic_addba_response;
693221651Sbschmidt	ic->ic_addba_response = iwn_addba_response;
694221651Sbschmidt	sc->sc_addba_stop = ic->ic_addba_stop;
695221651Sbschmidt	ic->ic_addba_stop = iwn_ampdu_tx_stop;
696220715Sbschmidt	ic->ic_newassoc = iwn_newassoc;
697220721Sbschmidt	ic->ic_wme.wme_update = iwn_updateedca;
698201209Srpaulo	ic->ic_update_mcast = iwn_update_mcast;
699198429Srpaulo	ic->ic_scan_start = iwn_scan_start;
700198429Srpaulo	ic->ic_scan_end = iwn_scan_end;
701198429Srpaulo	ic->ic_set_channel = iwn_set_channel;
702198429Srpaulo	ic->ic_scan_curchan = iwn_scan_curchan;
703198429Srpaulo	ic->ic_scan_mindwell = iwn_scan_mindwell;
704201209Srpaulo	ic->ic_setregdomain = iwn_setregdomain;
705178676Ssam
706198429Srpaulo	iwn_radiotap_attach(sc);
707220667Sbschmidt
708220667Sbschmidt	callout_init_mtx(&sc->calib_to, &sc->sc_mtx, 0);
709220667Sbschmidt	callout_init_mtx(&sc->watchdog_to, &sc->sc_mtx, 0);
710220726Sbschmidt	TASK_INIT(&sc->sc_reinit_task, 0, iwn_hw_reset, sc);
711220726Sbschmidt	TASK_INIT(&sc->sc_radioon_task, 0, iwn_radio_on, sc);
712220726Sbschmidt	TASK_INIT(&sc->sc_radiooff_task, 0, iwn_radio_off, sc);
713220667Sbschmidt
714178676Ssam	iwn_sysctlattach(sc);
715178676Ssam
716198429Srpaulo	/*
717198429Srpaulo	 * Hook our interrupt after all initialization is complete.
718198429Srpaulo	 */
719198429Srpaulo	error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET | INTR_MPSAFE,
720178676Ssam	    NULL, iwn_intr, sc, &sc->sc_ih);
721198429Srpaulo	if (error != 0) {
722220724Sbschmidt		device_printf(dev, "can't establish interrupt, error %d\n",
723198429Srpaulo		    error);
724198429Srpaulo		goto fail;
725198429Srpaulo	}
726178676Ssam
727220724Sbschmidt	if (bootverbose)
728220724Sbschmidt		ieee80211_announce(ic);
729253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
730178676Ssam	return 0;
731178676Ssamfail:
732220635Sbschmidt	iwn_detach(dev);
733253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end in error\n",__func__);
734178676Ssam	return error;
735178676Ssam}
736178676Ssam
737220728Sbschmidtstatic int
738220728Sbschmidtiwn4965_attach(struct iwn_softc *sc, uint16_t pid)
739178676Ssam{
740220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
741198429Srpaulo
742253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
743220728Sbschmidt	ops->load_firmware = iwn4965_load_firmware;
744220728Sbschmidt	ops->read_eeprom = iwn4965_read_eeprom;
745220728Sbschmidt	ops->post_alive = iwn4965_post_alive;
746220728Sbschmidt	ops->nic_config = iwn4965_nic_config;
747220728Sbschmidt	ops->update_sched = iwn4965_update_sched;
748220728Sbschmidt	ops->get_temperature = iwn4965_get_temperature;
749220728Sbschmidt	ops->get_rssi = iwn4965_get_rssi;
750220728Sbschmidt	ops->set_txpower = iwn4965_set_txpower;
751220728Sbschmidt	ops->init_gains = iwn4965_init_gains;
752220728Sbschmidt	ops->set_gains = iwn4965_set_gains;
753220728Sbschmidt	ops->add_node = iwn4965_add_node;
754220728Sbschmidt	ops->tx_done = iwn4965_tx_done;
755220728Sbschmidt	ops->ampdu_tx_start = iwn4965_ampdu_tx_start;
756220728Sbschmidt	ops->ampdu_tx_stop = iwn4965_ampdu_tx_stop;
757220728Sbschmidt	sc->ntxqs = IWN4965_NTXQUEUES;
758221651Sbschmidt	sc->firstaggqueue = IWN4965_FIRSTAGGQUEUE;
759220728Sbschmidt	sc->ndmachnls = IWN4965_NDMACHNLS;
760220728Sbschmidt	sc->broadcast_id = IWN4965_ID_BROADCAST;
761220728Sbschmidt	sc->rxonsz = IWN4965_RXONSZ;
762220728Sbschmidt	sc->schedsz = IWN4965_SCHEDSZ;
763220728Sbschmidt	sc->fw_text_maxsz = IWN4965_FW_TEXT_MAXSZ;
764220728Sbschmidt	sc->fw_data_maxsz = IWN4965_FW_DATA_MAXSZ;
765220728Sbschmidt	sc->fwsz = IWN4965_FWSZ;
766220728Sbschmidt	sc->sched_txfact_addr = IWN4965_SCHED_TXFACT;
767220728Sbschmidt	sc->limits = &iwn4965_sensitivity_limits;
768220728Sbschmidt	sc->fwname = "iwn4965fw";
769220728Sbschmidt	/* Override chains masks, ROM is known to be broken. */
770220728Sbschmidt	sc->txchainmask = IWN_ANT_AB;
771220728Sbschmidt	sc->rxchainmask = IWN_ANT_ABC;
772220728Sbschmidt
773253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "%s: end\n",__func__);
774253705Sadrian
775220728Sbschmidt	return 0;
776220728Sbschmidt}
777220728Sbschmidt
778220728Sbschmidtstatic int
779220728Sbschmidtiwn5000_attach(struct iwn_softc *sc, uint16_t pid)
780220728Sbschmidt{
781220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
782220728Sbschmidt
783253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
784253705Sadrian
785220728Sbschmidt	ops->load_firmware = iwn5000_load_firmware;
786220728Sbschmidt	ops->read_eeprom = iwn5000_read_eeprom;
787220728Sbschmidt	ops->post_alive = iwn5000_post_alive;
788220728Sbschmidt	ops->nic_config = iwn5000_nic_config;
789220728Sbschmidt	ops->update_sched = iwn5000_update_sched;
790220728Sbschmidt	ops->get_temperature = iwn5000_get_temperature;
791220728Sbschmidt	ops->get_rssi = iwn5000_get_rssi;
792220728Sbschmidt	ops->set_txpower = iwn5000_set_txpower;
793220728Sbschmidt	ops->init_gains = iwn5000_init_gains;
794220728Sbschmidt	ops->set_gains = iwn5000_set_gains;
795220728Sbschmidt	ops->add_node = iwn5000_add_node;
796220728Sbschmidt	ops->tx_done = iwn5000_tx_done;
797220728Sbschmidt	ops->ampdu_tx_start = iwn5000_ampdu_tx_start;
798220728Sbschmidt	ops->ampdu_tx_stop = iwn5000_ampdu_tx_stop;
799220728Sbschmidt	sc->ntxqs = IWN5000_NTXQUEUES;
800221651Sbschmidt	sc->firstaggqueue = IWN5000_FIRSTAGGQUEUE;
801220728Sbschmidt	sc->ndmachnls = IWN5000_NDMACHNLS;
802220728Sbschmidt	sc->broadcast_id = IWN5000_ID_BROADCAST;
803220728Sbschmidt	sc->rxonsz = IWN5000_RXONSZ;
804220728Sbschmidt	sc->schedsz = IWN5000_SCHEDSZ;
805220728Sbschmidt	sc->fw_text_maxsz = IWN5000_FW_TEXT_MAXSZ;
806220728Sbschmidt	sc->fw_data_maxsz = IWN5000_FW_DATA_MAXSZ;
807220728Sbschmidt	sc->fwsz = IWN5000_FWSZ;
808220728Sbschmidt	sc->sched_txfact_addr = IWN5000_SCHED_TXFACT;
809220866Sbschmidt	sc->reset_noise_gain = IWN5000_PHY_CALIB_RESET_NOISE_GAIN;
810220866Sbschmidt	sc->noise_gain = IWN5000_PHY_CALIB_NOISE_GAIN;
811220728Sbschmidt
812198429Srpaulo	switch (sc->hw_type) {
813198429Srpaulo	case IWN_HW_REV_TYPE_5100:
814201209Srpaulo		sc->limits = &iwn5000_sensitivity_limits;
815198439Srpaulo		sc->fwname = "iwn5000fw";
816220727Sbschmidt		/* Override chains masks, ROM is known to be broken. */
817201209Srpaulo		sc->txchainmask = IWN_ANT_B;
818201209Srpaulo		sc->rxchainmask = IWN_ANT_AB;
819198429Srpaulo		break;
820198429Srpaulo	case IWN_HW_REV_TYPE_5150:
821201209Srpaulo		sc->limits = &iwn5150_sensitivity_limits;
822198439Srpaulo		sc->fwname = "iwn5150fw";
823198429Srpaulo		break;
824198429Srpaulo	case IWN_HW_REV_TYPE_5300:
825198429Srpaulo	case IWN_HW_REV_TYPE_5350:
826201209Srpaulo		sc->limits = &iwn5000_sensitivity_limits;
827198439Srpaulo		sc->fwname = "iwn5000fw";
828198429Srpaulo		break;
829198429Srpaulo	case IWN_HW_REV_TYPE_1000:
830206444Sbschmidt		sc->limits = &iwn1000_sensitivity_limits;
831198439Srpaulo		sc->fwname = "iwn1000fw";
832198429Srpaulo		break;
833198429Srpaulo	case IWN_HW_REV_TYPE_6000:
834201209Srpaulo		sc->limits = &iwn6000_sensitivity_limits;
835198439Srpaulo		sc->fwname = "iwn6000fw";
836220728Sbschmidt		if (pid == 0x422c || pid == 0x4239) {
837201209Srpaulo			sc->sc_flags |= IWN_FLAG_INTERNAL_PA;
838220727Sbschmidt			/* Override chains masks, ROM is known to be broken. */
839201209Srpaulo			sc->txchainmask = IWN_ANT_BC;
840201209Srpaulo			sc->rxchainmask = IWN_ANT_BC;
841201209Srpaulo		}
842198429Srpaulo		break;
843198429Srpaulo	case IWN_HW_REV_TYPE_6050:
844201209Srpaulo		sc->limits = &iwn6000_sensitivity_limits;
845210109Sbschmidt		sc->fwname = "iwn6050fw";
846220867Sbschmidt		/* Override chains masks, ROM is known to be broken. */
847220867Sbschmidt		sc->txchainmask = IWN_ANT_AB;
848220867Sbschmidt		sc->rxchainmask = IWN_ANT_AB;
849198429Srpaulo		break;
850210109Sbschmidt	case IWN_HW_REV_TYPE_6005:
851210109Sbschmidt		sc->limits = &iwn6000_sensitivity_limits;
852220894Sbschmidt		if (pid != 0x0082 && pid != 0x0085) {
853220894Sbschmidt			sc->fwname = "iwn6000g2bfw";
854220891Sbschmidt			sc->sc_flags |= IWN_FLAG_ADV_BTCOEX;
855220894Sbschmidt		} else
856220894Sbschmidt			sc->fwname = "iwn6000g2afw";
857210109Sbschmidt		break;
858198429Srpaulo	default:
859198429Srpaulo		device_printf(sc->sc_dev, "adapter type %d not supported\n",
860198429Srpaulo		    sc->hw_type);
861253705Sadrian		DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end in error\n",__func__);
862220728Sbschmidt		return ENOTSUP;
863198429Srpaulo	}
864220728Sbschmidt	return 0;
865178676Ssam}
866178676Ssam
867178676Ssam/*
868198429Srpaulo * Attach the interface to 802.11 radiotap.
869178676Ssam */
870206477Sbschmidtstatic void
871198429Srpauloiwn_radiotap_attach(struct iwn_softc *sc)
872178676Ssam{
873178676Ssam	struct ifnet *ifp = sc->sc_ifp;
874178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
875253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
876198429Srpaulo	ieee80211_radiotap_attach(ic,
877198429Srpaulo	    &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap),
878198429Srpaulo		IWN_TX_RADIOTAP_PRESENT,
879198429Srpaulo	    &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap),
880198429Srpaulo		IWN_RX_RADIOTAP_PRESENT);
881253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s end\n", __func__);
882178676Ssam}
883178676Ssam
884220723Sbschmidtstatic void
885220723Sbschmidtiwn_sysctlattach(struct iwn_softc *sc)
886220723Sbschmidt{
887253612Sadrian#ifdef	IWN_DEBUG
888220723Sbschmidt	struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev);
889220723Sbschmidt	struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev);
890220723Sbschmidt
891220723Sbschmidt	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
892253612Sadrian	    "debug", CTLFLAG_RW, &sc->sc_debug, sc->sc_debug,
893253612Sadrian		"control debugging printfs");
894220723Sbschmidt#endif
895220723Sbschmidt}
896220723Sbschmidt
897178676Ssamstatic struct ieee80211vap *
898228621Sbschmidtiwn_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit,
899228621Sbschmidt    enum ieee80211_opmode opmode, int flags,
900220726Sbschmidt    const uint8_t bssid[IEEE80211_ADDR_LEN],
901220726Sbschmidt    const uint8_t mac[IEEE80211_ADDR_LEN])
902178676Ssam{
903178676Ssam	struct iwn_vap *ivp;
904178676Ssam	struct ieee80211vap *vap;
905254204Sadrian	uint8_t mac1[IEEE80211_ADDR_LEN];
906254204Sadrian	struct iwn_softc *sc = ic->ic_ifp->if_softc;
907178676Ssam
908178676Ssam	if (!TAILQ_EMPTY(&ic->ic_vaps))		/* only one at a time */
909178676Ssam		return NULL;
910254204Sadrian
911254204Sadrian	IEEE80211_ADDR_COPY(mac1, mac);
912254204Sadrian
913178676Ssam	ivp = (struct iwn_vap *) malloc(sizeof(struct iwn_vap),
914178676Ssam	    M_80211_VAP, M_NOWAIT | M_ZERO);
915178676Ssam	if (ivp == NULL)
916178676Ssam		return NULL;
917178676Ssam	vap = &ivp->iv_vap;
918254204Sadrian	ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac1);
919254204Sadrian	ivp->ctx = IWN_RXON_BSS_CTX;
920254204Sadrian	IEEE80211_ADDR_COPY(ivp->macaddr, mac1);
921178676Ssam	vap->iv_bmissthreshold = 10;		/* override default */
922198429Srpaulo	/* Override with driver methods. */
923178676Ssam	ivp->iv_newstate = vap->iv_newstate;
924178676Ssam	vap->iv_newstate = iwn_newstate;
925254204Sadrian	sc->ivap[IWN_RXON_BSS_CTX] = vap;
926178676Ssam
927206358Srpaulo	ieee80211_ratectl_init(vap);
928198429Srpaulo	/* Complete setup. */
929206476Sbschmidt	ieee80211_vap_attach(vap, iwn_media_change, ieee80211_media_status);
930178676Ssam	ic->ic_opmode = opmode;
931178676Ssam	return vap;
932178676Ssam}
933178676Ssam
934178676Ssamstatic void
935178676Ssamiwn_vap_delete(struct ieee80211vap *vap)
936178676Ssam{
937178676Ssam	struct iwn_vap *ivp = IWN_VAP(vap);
938178676Ssam
939206358Srpaulo	ieee80211_ratectl_deinit(vap);
940178676Ssam	ieee80211_vap_detach(vap);
941178676Ssam	free(ivp, M_80211_VAP);
942178676Ssam}
943178676Ssam
944206477Sbschmidtstatic int
945220635Sbschmidtiwn_detach(device_t dev)
946178676Ssam{
947178676Ssam	struct iwn_softc *sc = device_get_softc(dev);
948198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
949198429Srpaulo	struct ieee80211com *ic;
950220721Sbschmidt	int qid;
951178676Ssam
952253866Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
953253866Sadrian
954198429Srpaulo	if (ifp != NULL) {
955198429Srpaulo		ic = ifp->if_l2com;
956198429Srpaulo
957198429Srpaulo		ieee80211_draintask(ic, &sc->sc_reinit_task);
958198429Srpaulo		ieee80211_draintask(ic, &sc->sc_radioon_task);
959198429Srpaulo		ieee80211_draintask(ic, &sc->sc_radiooff_task);
960198429Srpaulo
961198429Srpaulo		iwn_stop(sc);
962220667Sbschmidt		callout_drain(&sc->watchdog_to);
963220667Sbschmidt		callout_drain(&sc->calib_to);
964198429Srpaulo		ieee80211_ifdetach(ic);
965198429Srpaulo	}
966198429Srpaulo
967220725Sbschmidt	/* Uninstall interrupt handler. */
968220723Sbschmidt	if (sc->irq != NULL) {
969220723Sbschmidt		bus_teardown_intr(dev, sc->irq, sc->sc_ih);
970264945Smarius		bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(sc->irq),
971264945Smarius		    sc->irq);
972264945Smarius		pci_release_msi(dev);
973220723Sbschmidt	}
974220723Sbschmidt
975201209Srpaulo	/* Free DMA resources. */
976198429Srpaulo	iwn_free_rx_ring(sc, &sc->rxq);
977220728Sbschmidt	for (qid = 0; qid < sc->ntxqs; qid++)
978220728Sbschmidt		iwn_free_tx_ring(sc, &sc->txq[qid]);
979198429Srpaulo	iwn_free_sched(sc);
980198429Srpaulo	iwn_free_kw(sc);
981201209Srpaulo	if (sc->ict != NULL)
982201209Srpaulo		iwn_free_ict(sc);
983198429Srpaulo	iwn_free_fwmem(sc);
984198429Srpaulo
985198429Srpaulo	if (sc->mem != NULL)
986264945Smarius		bus_release_resource(dev, SYS_RES_MEMORY,
987264945Smarius		    rman_get_rid(sc->mem), sc->mem);
988198429Srpaulo
989198429Srpaulo	if (ifp != NULL)
990198429Srpaulo		if_free(ifp);
991198429Srpaulo
992253866Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n", __func__);
993198429Srpaulo	IWN_LOCK_DESTROY(sc);
994178676Ssam	return 0;
995178676Ssam}
996178676Ssam
997178676Ssamstatic int
998220723Sbschmidtiwn_shutdown(device_t dev)
999220723Sbschmidt{
1000220723Sbschmidt	struct iwn_softc *sc = device_get_softc(dev);
1001220723Sbschmidt
1002220723Sbschmidt	iwn_stop(sc);
1003220723Sbschmidt	return 0;
1004220723Sbschmidt}
1005220723Sbschmidt
1006220723Sbschmidtstatic int
1007220723Sbschmidtiwn_suspend(device_t dev)
1008220723Sbschmidt{
1009220723Sbschmidt	struct iwn_softc *sc = device_get_softc(dev);
1010233387Sbschmidt	struct ieee80211com *ic = sc->sc_ifp->if_l2com;
1011220723Sbschmidt
1012233387Sbschmidt	ieee80211_suspend_all(ic);
1013220723Sbschmidt	return 0;
1014220723Sbschmidt}
1015220723Sbschmidt
1016220723Sbschmidtstatic int
1017220723Sbschmidtiwn_resume(device_t dev)
1018220723Sbschmidt{
1019220723Sbschmidt	struct iwn_softc *sc = device_get_softc(dev);
1020233387Sbschmidt	struct ieee80211com *ic = sc->sc_ifp->if_l2com;
1021220723Sbschmidt
1022220723Sbschmidt	/* Clear device-specific "PCI retry timeout" register (41h). */
1023220723Sbschmidt	pci_write_config(dev, 0x41, 0, 1);
1024220723Sbschmidt
1025233387Sbschmidt	ieee80211_resume_all(ic);
1026220723Sbschmidt	return 0;
1027220723Sbschmidt}
1028220723Sbschmidt
1029220723Sbschmidtstatic int
1030198429Srpauloiwn_nic_lock(struct iwn_softc *sc)
1031178676Ssam{
1032198429Srpaulo	int ntries;
1033178676Ssam
1034198429Srpaulo	/* Request exclusive access to NIC. */
1035198429Srpaulo	IWN_SETBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_MAC_ACCESS_REQ);
1036178676Ssam
1037198429Srpaulo	/* Spin until we actually get the lock. */
1038198429Srpaulo	for (ntries = 0; ntries < 1000; ntries++) {
1039198429Srpaulo		if ((IWN_READ(sc, IWN_GP_CNTRL) &
1040220726Sbschmidt		     (IWN_GP_CNTRL_MAC_ACCESS_ENA | IWN_GP_CNTRL_SLEEP)) ==
1041198429Srpaulo		    IWN_GP_CNTRL_MAC_ACCESS_ENA)
1042198429Srpaulo			return 0;
1043198429Srpaulo		DELAY(10);
1044198429Srpaulo	}
1045198429Srpaulo	return ETIMEDOUT;
1046198429Srpaulo}
1047198429Srpaulo
1048198429Srpaulostatic __inline void
1049198429Srpauloiwn_nic_unlock(struct iwn_softc *sc)
1050198429Srpaulo{
1051198429Srpaulo	IWN_CLRBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_MAC_ACCESS_REQ);
1052198429Srpaulo}
1053198429Srpaulo
1054198429Srpaulostatic __inline uint32_t
1055198429Srpauloiwn_prph_read(struct iwn_softc *sc, uint32_t addr)
1056198429Srpaulo{
1057198429Srpaulo	IWN_WRITE(sc, IWN_PRPH_RADDR, IWN_PRPH_DWORD | addr);
1058201209Srpaulo	IWN_BARRIER_READ_WRITE(sc);
1059198429Srpaulo	return IWN_READ(sc, IWN_PRPH_RDATA);
1060198429Srpaulo}
1061198429Srpaulo
1062198429Srpaulostatic __inline void
1063198429Srpauloiwn_prph_write(struct iwn_softc *sc, uint32_t addr, uint32_t data)
1064198429Srpaulo{
1065198429Srpaulo	IWN_WRITE(sc, IWN_PRPH_WADDR, IWN_PRPH_DWORD | addr);
1066201209Srpaulo	IWN_BARRIER_WRITE(sc);
1067198429Srpaulo	IWN_WRITE(sc, IWN_PRPH_WDATA, data);
1068198429Srpaulo}
1069198429Srpaulo
1070198429Srpaulostatic __inline void
1071198429Srpauloiwn_prph_setbits(struct iwn_softc *sc, uint32_t addr, uint32_t mask)
1072198429Srpaulo{
1073198429Srpaulo	iwn_prph_write(sc, addr, iwn_prph_read(sc, addr) | mask);
1074198429Srpaulo}
1075198429Srpaulo
1076198429Srpaulostatic __inline void
1077198429Srpauloiwn_prph_clrbits(struct iwn_softc *sc, uint32_t addr, uint32_t mask)
1078198429Srpaulo{
1079198429Srpaulo	iwn_prph_write(sc, addr, iwn_prph_read(sc, addr) & ~mask);
1080198429Srpaulo}
1081198429Srpaulo
1082198429Srpaulostatic __inline void
1083198429Srpauloiwn_prph_write_region_4(struct iwn_softc *sc, uint32_t addr,
1084198429Srpaulo    const uint32_t *data, int count)
1085198429Srpaulo{
1086198429Srpaulo	for (; count > 0; count--, data++, addr += 4)
1087198429Srpaulo		iwn_prph_write(sc, addr, *data);
1088198429Srpaulo}
1089198429Srpaulo
1090198429Srpaulostatic __inline uint32_t
1091198429Srpauloiwn_mem_read(struct iwn_softc *sc, uint32_t addr)
1092198429Srpaulo{
1093198429Srpaulo	IWN_WRITE(sc, IWN_MEM_RADDR, addr);
1094201209Srpaulo	IWN_BARRIER_READ_WRITE(sc);
1095198429Srpaulo	return IWN_READ(sc, IWN_MEM_RDATA);
1096198429Srpaulo}
1097198429Srpaulo
1098198429Srpaulostatic __inline void
1099198429Srpauloiwn_mem_write(struct iwn_softc *sc, uint32_t addr, uint32_t data)
1100198429Srpaulo{
1101198429Srpaulo	IWN_WRITE(sc, IWN_MEM_WADDR, addr);
1102201209Srpaulo	IWN_BARRIER_WRITE(sc);
1103198429Srpaulo	IWN_WRITE(sc, IWN_MEM_WDATA, data);
1104198429Srpaulo}
1105198429Srpaulo
1106198429Srpaulostatic __inline void
1107198429Srpauloiwn_mem_write_2(struct iwn_softc *sc, uint32_t addr, uint16_t data)
1108198429Srpaulo{
1109198429Srpaulo	uint32_t tmp;
1110198429Srpaulo
1111198429Srpaulo	tmp = iwn_mem_read(sc, addr & ~3);
1112198429Srpaulo	if (addr & 3)
1113198429Srpaulo		tmp = (tmp & 0x0000ffff) | data << 16;
1114198429Srpaulo	else
1115198429Srpaulo		tmp = (tmp & 0xffff0000) | data;
1116198429Srpaulo	iwn_mem_write(sc, addr & ~3, tmp);
1117198429Srpaulo}
1118198429Srpaulo
1119198429Srpaulostatic __inline void
1120198429Srpauloiwn_mem_read_region_4(struct iwn_softc *sc, uint32_t addr, uint32_t *data,
1121198429Srpaulo    int count)
1122198429Srpaulo{
1123198429Srpaulo	for (; count > 0; count--, addr += 4)
1124198429Srpaulo		*data++ = iwn_mem_read(sc, addr);
1125198429Srpaulo}
1126198429Srpaulo
1127198429Srpaulostatic __inline void
1128198429Srpauloiwn_mem_set_region_4(struct iwn_softc *sc, uint32_t addr, uint32_t val,
1129198429Srpaulo    int count)
1130198429Srpaulo{
1131198429Srpaulo	for (; count > 0; count--, addr += 4)
1132198429Srpaulo		iwn_mem_write(sc, addr, val);
1133198429Srpaulo}
1134198429Srpaulo
1135206477Sbschmidtstatic int
1136198429Srpauloiwn_eeprom_lock(struct iwn_softc *sc)
1137198429Srpaulo{
1138198429Srpaulo	int i, ntries;
1139198429Srpaulo
1140198429Srpaulo	for (i = 0; i < 100; i++) {
1141198429Srpaulo		/* Request exclusive access to EEPROM. */
1142198429Srpaulo		IWN_SETBITS(sc, IWN_HW_IF_CONFIG,
1143198429Srpaulo		    IWN_HW_IF_CONFIG_EEPROM_LOCKED);
1144198429Srpaulo
1145198429Srpaulo		/* Spin until we actually get the lock. */
1146198429Srpaulo		for (ntries = 0; ntries < 100; ntries++) {
1147198429Srpaulo			if (IWN_READ(sc, IWN_HW_IF_CONFIG) &
1148198429Srpaulo			    IWN_HW_IF_CONFIG_EEPROM_LOCKED)
1149198429Srpaulo				return 0;
1150198429Srpaulo			DELAY(10);
1151198429Srpaulo		}
1152198429Srpaulo	}
1153253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s end timeout\n", __func__);
1154198429Srpaulo	return ETIMEDOUT;
1155198429Srpaulo}
1156198429Srpaulo
1157198429Srpaulostatic __inline void
1158198429Srpauloiwn_eeprom_unlock(struct iwn_softc *sc)
1159198429Srpaulo{
1160198429Srpaulo	IWN_CLRBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_EEPROM_LOCKED);
1161198429Srpaulo}
1162198429Srpaulo
1163198429Srpaulo/*
1164198429Srpaulo * Initialize access by host to One Time Programmable ROM.
1165198429Srpaulo * NB: This kind of ROM can be found on 1000 or 6000 Series only.
1166198429Srpaulo */
1167206477Sbschmidtstatic int
1168198429Srpauloiwn_init_otprom(struct iwn_softc *sc)
1169198429Srpaulo{
1170203934Sbschmidt	uint16_t prev, base, next;
1171201209Srpaulo	int count, error;
1172198429Srpaulo
1173253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
1174253705Sadrian
1175201209Srpaulo	/* Wait for clock stabilization before accessing prph. */
1176220726Sbschmidt	if ((error = iwn_clock_wait(sc)) != 0)
1177198429Srpaulo		return error;
1178198429Srpaulo
1179220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
1180198429Srpaulo		return error;
1181198429Srpaulo	iwn_prph_setbits(sc, IWN_APMG_PS, IWN_APMG_PS_RESET_REQ);
1182198429Srpaulo	DELAY(5);
1183198429Srpaulo	iwn_prph_clrbits(sc, IWN_APMG_PS, IWN_APMG_PS_RESET_REQ);
1184198429Srpaulo	iwn_nic_unlock(sc);
1185198429Srpaulo
1186201209Srpaulo	/* Set auto clock gate disable bit for HW with OTP shadow RAM. */
1187201209Srpaulo	if (sc->hw_type != IWN_HW_REV_TYPE_1000) {
1188201209Srpaulo		IWN_SETBITS(sc, IWN_DBG_LINK_PWR_MGMT,
1189201209Srpaulo		    IWN_RESET_LINK_PWR_MGMT_DIS);
1190201209Srpaulo	}
1191198429Srpaulo	IWN_CLRBITS(sc, IWN_EEPROM_GP, IWN_EEPROM_GP_IF_OWNER);
1192198429Srpaulo	/* Clear ECC status. */
1193198429Srpaulo	IWN_SETBITS(sc, IWN_OTP_GP,
1194198429Srpaulo	    IWN_OTP_GP_ECC_CORR_STTS | IWN_OTP_GP_ECC_UNCORR_STTS);
1195198429Srpaulo
1196201209Srpaulo	/*
1197203934Sbschmidt	 * Find the block before last block (contains the EEPROM image)
1198203934Sbschmidt	 * for HW without OTP shadow RAM.
1199201209Srpaulo	 */
1200201209Srpaulo	if (sc->hw_type == IWN_HW_REV_TYPE_1000) {
1201201209Srpaulo		/* Switch to absolute addressing mode. */
1202201209Srpaulo		IWN_CLRBITS(sc, IWN_OTP_GP, IWN_OTP_GP_RELATIVE_ACCESS);
1203203934Sbschmidt		base = prev = 0;
1204201209Srpaulo		for (count = 0; count < IWN1000_OTP_NBLOCKS; count++) {
1205201209Srpaulo			error = iwn_read_prom_data(sc, base, &next, 2);
1206201209Srpaulo			if (error != 0)
1207201209Srpaulo				return error;
1208201209Srpaulo			if (next == 0)	/* End of linked-list. */
1209201209Srpaulo				break;
1210203934Sbschmidt			prev = base;
1211201209Srpaulo			base = le16toh(next);
1212201209Srpaulo		}
1213203934Sbschmidt		if (count == 0 || count == IWN1000_OTP_NBLOCKS)
1214201209Srpaulo			return EIO;
1215201209Srpaulo		/* Skip "next" word. */
1216203934Sbschmidt		sc->prom_base = prev + 1;
1217201209Srpaulo	}
1218253705Sadrian
1219253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s end\n", __func__);
1220253705Sadrian
1221178676Ssam	return 0;
1222178676Ssam}
1223178676Ssam
1224206477Sbschmidtstatic int
1225198429Srpauloiwn_read_prom_data(struct iwn_softc *sc, uint32_t addr, void *data, int count)
1226198429Srpaulo{
1227220723Sbschmidt	uint8_t *out = data;
1228198429Srpaulo	uint32_t val, tmp;
1229198429Srpaulo	int ntries;
1230198429Srpaulo
1231253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
1232253705Sadrian
1233201209Srpaulo	addr += sc->prom_base;
1234198429Srpaulo	for (; count > 0; count -= 2, addr++) {
1235198429Srpaulo		IWN_WRITE(sc, IWN_EEPROM, addr << 2);
1236201209Srpaulo		for (ntries = 0; ntries < 10; ntries++) {
1237198429Srpaulo			val = IWN_READ(sc, IWN_EEPROM);
1238198429Srpaulo			if (val & IWN_EEPROM_READ_VALID)
1239198429Srpaulo				break;
1240198429Srpaulo			DELAY(5);
1241198429Srpaulo		}
1242201209Srpaulo		if (ntries == 10) {
1243198429Srpaulo			device_printf(sc->sc_dev,
1244198429Srpaulo			    "timeout reading ROM at 0x%x\n", addr);
1245198429Srpaulo			return ETIMEDOUT;
1246198429Srpaulo		}
1247198429Srpaulo		if (sc->sc_flags & IWN_FLAG_HAS_OTPROM) {
1248198429Srpaulo			/* OTPROM, check for ECC errors. */
1249198429Srpaulo			tmp = IWN_READ(sc, IWN_OTP_GP);
1250198429Srpaulo			if (tmp & IWN_OTP_GP_ECC_UNCORR_STTS) {
1251198429Srpaulo				device_printf(sc->sc_dev,
1252198429Srpaulo				    "OTPROM ECC error at 0x%x\n", addr);
1253198429Srpaulo				return EIO;
1254198429Srpaulo			}
1255198429Srpaulo			if (tmp & IWN_OTP_GP_ECC_CORR_STTS) {
1256198429Srpaulo				/* Correctable ECC error, clear bit. */
1257198429Srpaulo				IWN_SETBITS(sc, IWN_OTP_GP,
1258198429Srpaulo				    IWN_OTP_GP_ECC_CORR_STTS);
1259198429Srpaulo			}
1260198429Srpaulo		}
1261198429Srpaulo		*out++ = val >> 16;
1262198429Srpaulo		if (count > 1)
1263198429Srpaulo			*out++ = val >> 24;
1264198429Srpaulo	}
1265253705Sadrian
1266253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s end\n", __func__);
1267253705Sadrian
1268198429Srpaulo	return 0;
1269198429Srpaulo}
1270198429Srpaulo
1271178676Ssamstatic void
1272178676Ssamiwn_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
1273178676Ssam{
1274198429Srpaulo	if (error != 0)
1275198429Srpaulo		return;
1276198429Srpaulo	KASSERT(nsegs == 1, ("too many DMA segments, %d should be 1", nsegs));
1277198429Srpaulo	*(bus_addr_t *)arg = segs[0].ds_addr;
1278178676Ssam}
1279178676Ssam
1280198429Srpaulostatic int
1281178676Ssamiwn_dma_contig_alloc(struct iwn_softc *sc, struct iwn_dma_info *dma,
1282220691Sbschmidt    void **kvap, bus_size_t size, bus_size_t alignment)
1283178676Ssam{
1284198429Srpaulo	int error;
1285178676Ssam
1286220723Sbschmidt	dma->tag = NULL;
1287178676Ssam	dma->size = size;
1288178676Ssam
1289198429Srpaulo	error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), alignment,
1290178676Ssam	    0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, size,
1291220691Sbschmidt	    1, size, BUS_DMA_NOWAIT, NULL, NULL, &dma->tag);
1292220711Sbschmidt	if (error != 0)
1293178676Ssam		goto fail;
1294220711Sbschmidt
1295178676Ssam	error = bus_dmamem_alloc(dma->tag, (void **)&dma->vaddr,
1296220691Sbschmidt	    BUS_DMA_NOWAIT | BUS_DMA_ZERO | BUS_DMA_COHERENT, &dma->map);
1297220711Sbschmidt	if (error != 0)
1298178676Ssam		goto fail;
1299220711Sbschmidt
1300220691Sbschmidt	error = bus_dmamap_load(dma->tag, dma->map, dma->vaddr, size,
1301220691Sbschmidt	    iwn_dma_map_addr, &dma->paddr, BUS_DMA_NOWAIT);
1302220711Sbschmidt	if (error != 0)
1303178676Ssam		goto fail;
1304178676Ssam
1305220704Sbschmidt	bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
1306220704Sbschmidt
1307178676Ssam	if (kvap != NULL)
1308178676Ssam		*kvap = dma->vaddr;
1309220726Sbschmidt
1310178676Ssam	return 0;
1311220726Sbschmidt
1312220726Sbschmidtfail:	iwn_dma_contig_free(dma);
1313178676Ssam	return error;
1314178676Ssam}
1315178676Ssam
1316206477Sbschmidtstatic void
1317178676Ssamiwn_dma_contig_free(struct iwn_dma_info *dma)
1318178676Ssam{
1319220701Sbschmidt	if (dma->map != NULL) {
1320220701Sbschmidt		if (dma->vaddr != NULL) {
1321220701Sbschmidt			bus_dmamap_sync(dma->tag, dma->map,
1322220701Sbschmidt			    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
1323220701Sbschmidt			bus_dmamap_unload(dma->tag, dma->map);
1324243622Sbschmidt			bus_dmamem_free(dma->tag, dma->vaddr, dma->map);
1325220701Sbschmidt			dma->vaddr = NULL;
1326178676Ssam		}
1327220701Sbschmidt		bus_dmamap_destroy(dma->tag, dma->map);
1328220701Sbschmidt		dma->map = NULL;
1329220701Sbschmidt	}
1330220701Sbschmidt	if (dma->tag != NULL) {
1331178676Ssam		bus_dma_tag_destroy(dma->tag);
1332220701Sbschmidt		dma->tag = NULL;
1333178676Ssam	}
1334178676Ssam}
1335178676Ssam
1336206477Sbschmidtstatic int
1337198429Srpauloiwn_alloc_sched(struct iwn_softc *sc)
1338178676Ssam{
1339198429Srpaulo	/* TX scheduler rings must be aligned on a 1KB boundary. */
1340220691Sbschmidt	return iwn_dma_contig_alloc(sc, &sc->sched_dma, (void **)&sc->sched,
1341220728Sbschmidt	    sc->schedsz, 1024);
1342178676Ssam}
1343178676Ssam
1344206477Sbschmidtstatic void
1345198429Srpauloiwn_free_sched(struct iwn_softc *sc)
1346178676Ssam{
1347198429Srpaulo	iwn_dma_contig_free(&sc->sched_dma);
1348178676Ssam}
1349178676Ssam
1350206477Sbschmidtstatic int
1351178676Ssamiwn_alloc_kw(struct iwn_softc *sc)
1352178676Ssam{
1353198429Srpaulo	/* "Keep Warm" page must be aligned on a 4KB boundary. */
1354220691Sbschmidt	return iwn_dma_contig_alloc(sc, &sc->kw_dma, NULL, 4096, 4096);
1355178676Ssam}
1356178676Ssam
1357206477Sbschmidtstatic void
1358178676Ssamiwn_free_kw(struct iwn_softc *sc)
1359178676Ssam{
1360178676Ssam	iwn_dma_contig_free(&sc->kw_dma);
1361178676Ssam}
1362178676Ssam
1363206477Sbschmidtstatic int
1364201209Srpauloiwn_alloc_ict(struct iwn_softc *sc)
1365201209Srpaulo{
1366201209Srpaulo	/* ICT table must be aligned on a 4KB boundary. */
1367220691Sbschmidt	return iwn_dma_contig_alloc(sc, &sc->ict_dma, (void **)&sc->ict,
1368220691Sbschmidt	    IWN_ICT_SIZE, 4096);
1369201209Srpaulo}
1370201209Srpaulo
1371206477Sbschmidtstatic void
1372201209Srpauloiwn_free_ict(struct iwn_softc *sc)
1373201209Srpaulo{
1374201209Srpaulo	iwn_dma_contig_free(&sc->ict_dma);
1375201209Srpaulo}
1376201209Srpaulo
1377206477Sbschmidtstatic int
1378178676Ssamiwn_alloc_fwmem(struct iwn_softc *sc)
1379178676Ssam{
1380198429Srpaulo	/* Must be aligned on a 16-byte boundary. */
1381220728Sbschmidt	return iwn_dma_contig_alloc(sc, &sc->fw_dma, NULL, sc->fwsz, 16);
1382178676Ssam}
1383178676Ssam
1384206477Sbschmidtstatic void
1385178676Ssamiwn_free_fwmem(struct iwn_softc *sc)
1386178676Ssam{
1387178676Ssam	iwn_dma_contig_free(&sc->fw_dma);
1388178676Ssam}
1389178676Ssam
1390206477Sbschmidtstatic int
1391178676Ssamiwn_alloc_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring)
1392178676Ssam{
1393198429Srpaulo	bus_size_t size;
1394178676Ssam	int i, error;
1395178676Ssam
1396178676Ssam	ring->cur = 0;
1397178676Ssam
1398253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
1399253705Sadrian
1400198429Srpaulo	/* Allocate RX descriptors (256-byte aligned). */
1401198429Srpaulo	size = IWN_RX_RING_COUNT * sizeof (uint32_t);
1402220691Sbschmidt	error = iwn_dma_contig_alloc(sc, &ring->desc_dma, (void **)&ring->desc,
1403220691Sbschmidt	    size, 256);
1404178676Ssam	if (error != 0) {
1405178676Ssam		device_printf(sc->sc_dev,
1406220711Sbschmidt		    "%s: could not allocate RX ring DMA memory, error %d\n",
1407178676Ssam		    __func__, error);
1408178676Ssam		goto fail;
1409178676Ssam	}
1410178676Ssam
1411220702Sbschmidt	/* Allocate RX status area (16-byte aligned). */
1412220702Sbschmidt	error = iwn_dma_contig_alloc(sc, &ring->stat_dma, (void **)&ring->stat,
1413220702Sbschmidt	    sizeof (struct iwn_rx_status), 16);
1414198429Srpaulo	if (error != 0) {
1415198429Srpaulo		device_printf(sc->sc_dev,
1416220711Sbschmidt		    "%s: could not allocate RX status DMA memory, error %d\n",
1417178676Ssam		    __func__, error);
1418198429Srpaulo		goto fail;
1419198429Srpaulo	}
1420178676Ssam
1421220702Sbschmidt	/* Create RX buffer DMA tag. */
1422220702Sbschmidt	error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0,
1423220702Sbschmidt	    BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
1424220702Sbschmidt	    IWN_RBUF_SIZE, 1, IWN_RBUF_SIZE, BUS_DMA_NOWAIT, NULL, NULL,
1425220702Sbschmidt	    &ring->data_dmat);
1426198429Srpaulo	if (error != 0) {
1427198429Srpaulo		device_printf(sc->sc_dev,
1428220711Sbschmidt		    "%s: could not create RX buf DMA tag, error %d\n",
1429198429Srpaulo		    __func__, error);
1430198429Srpaulo		goto fail;
1431198429Srpaulo	}
1432198429Srpaulo
1433178676Ssam	/*
1434198429Srpaulo	 * Allocate and map RX buffers.
1435178676Ssam	 */
1436178676Ssam	for (i = 0; i < IWN_RX_RING_COUNT; i++) {
1437178676Ssam		struct iwn_rx_data *data = &ring->data[i];
1438178676Ssam		bus_addr_t paddr;
1439178676Ssam
1440201209Srpaulo		error = bus_dmamap_create(ring->data_dmat, 0, &data->map);
1441178676Ssam		if (error != 0) {
1442178676Ssam			device_printf(sc->sc_dev,
1443220711Sbschmidt			    "%s: could not create RX buf DMA map, error %d\n",
1444178676Ssam			    __func__, error);
1445178676Ssam			goto fail;
1446178676Ssam		}
1447198429Srpaulo
1448243857Sglebius		data->m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR,
1449220692Sbschmidt		    IWN_RBUF_SIZE);
1450198439Srpaulo		if (data->m == NULL) {
1451178676Ssam			device_printf(sc->sc_dev,
1452220711Sbschmidt			    "%s: could not allocate RX mbuf\n", __func__);
1453220710Sbschmidt			error = ENOBUFS;
1454178676Ssam			goto fail;
1455178676Ssam		}
1456198429Srpaulo
1457201209Srpaulo		error = bus_dmamap_load(ring->data_dmat, data->map,
1458220692Sbschmidt		    mtod(data->m, void *), IWN_RBUF_SIZE, iwn_dma_map_addr,
1459220692Sbschmidt		    &paddr, BUS_DMA_NOWAIT);
1460178676Ssam		if (error != 0 && error != EFBIG) {
1461178676Ssam			device_printf(sc->sc_dev,
1462220711Sbschmidt			    "%s: can't not map mbuf, error %d\n", __func__,
1463220711Sbschmidt			    error);
1464178676Ssam			goto fail;
1465178676Ssam		}
1466178676Ssam
1467198429Srpaulo		/* Set physical address of RX buffer (256-byte aligned). */
1468178676Ssam		ring->desc[i] = htole32(paddr >> 8);
1469178676Ssam	}
1470220726Sbschmidt
1471178676Ssam	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
1472178676Ssam	    BUS_DMASYNC_PREWRITE);
1473220726Sbschmidt
1474253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
1475253705Sadrian
1476178676Ssam	return 0;
1477220726Sbschmidt
1478220726Sbschmidtfail:	iwn_free_rx_ring(sc, ring);
1479253705Sadrian
1480253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end in error\n",__func__);
1481253705Sadrian
1482178676Ssam	return error;
1483178676Ssam}
1484178676Ssam
1485206477Sbschmidtstatic void
1486178676Ssamiwn_reset_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring)
1487178676Ssam{
1488178676Ssam	int ntries;
1489178676Ssam
1490253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
1491253705Sadrian
1492198429Srpaulo	if (iwn_nic_lock(sc) == 0) {
1493198429Srpaulo		IWN_WRITE(sc, IWN_FH_RX_CONFIG, 0);
1494198429Srpaulo		for (ntries = 0; ntries < 1000; ntries++) {
1495198429Srpaulo			if (IWN_READ(sc, IWN_FH_RX_STATUS) &
1496198429Srpaulo			    IWN_FH_RX_STATUS_IDLE)
1497198429Srpaulo				break;
1498198429Srpaulo			DELAY(10);
1499198429Srpaulo		}
1500198429Srpaulo		iwn_nic_unlock(sc);
1501198429Srpaulo	}
1502178676Ssam	ring->cur = 0;
1503198429Srpaulo	sc->last_rx_valid = 0;
1504178676Ssam}
1505178676Ssam
1506206477Sbschmidtstatic void
1507178676Ssamiwn_free_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring)
1508178676Ssam{
1509178676Ssam	int i;
1510178676Ssam
1511253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s \n", __func__);
1512253705Sadrian
1513178676Ssam	iwn_dma_contig_free(&ring->desc_dma);
1514198429Srpaulo	iwn_dma_contig_free(&ring->stat_dma);
1515178676Ssam
1516198429Srpaulo	for (i = 0; i < IWN_RX_RING_COUNT; i++) {
1517198429Srpaulo		struct iwn_rx_data *data = &ring->data[i];
1518198429Srpaulo
1519198429Srpaulo		if (data->m != NULL) {
1520201209Srpaulo			bus_dmamap_sync(ring->data_dmat, data->map,
1521198439Srpaulo			    BUS_DMASYNC_POSTREAD);
1522201209Srpaulo			bus_dmamap_unload(ring->data_dmat, data->map);
1523198429Srpaulo			m_freem(data->m);
1524220710Sbschmidt			data->m = NULL;
1525198429Srpaulo		}
1526201209Srpaulo		if (data->map != NULL)
1527201209Srpaulo			bus_dmamap_destroy(ring->data_dmat, data->map);
1528198429Srpaulo	}
1529220701Sbschmidt	if (ring->data_dmat != NULL) {
1530220701Sbschmidt		bus_dma_tag_destroy(ring->data_dmat);
1531220701Sbschmidt		ring->data_dmat = NULL;
1532220701Sbschmidt	}
1533178676Ssam}
1534178676Ssam
1535206477Sbschmidtstatic int
1536178676Ssamiwn_alloc_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring, int qid)
1537178676Ssam{
1538220723Sbschmidt	bus_addr_t paddr;
1539178676Ssam	bus_size_t size;
1540178676Ssam	int i, error;
1541178676Ssam
1542178676Ssam	ring->qid = qid;
1543178676Ssam	ring->queued = 0;
1544178676Ssam	ring->cur = 0;
1545178676Ssam
1546253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
1547253705Sadrian
1548220725Sbschmidt	/* Allocate TX descriptors (256-byte aligned). */
1549220726Sbschmidt	size = IWN_TX_RING_COUNT * sizeof (struct iwn_tx_desc);
1550220691Sbschmidt	error = iwn_dma_contig_alloc(sc, &ring->desc_dma, (void **)&ring->desc,
1551220691Sbschmidt	    size, 256);
1552178676Ssam	if (error != 0) {
1553178676Ssam		device_printf(sc->sc_dev,
1554198429Srpaulo		    "%s: could not allocate TX ring DMA memory, error %d\n",
1555178676Ssam		    __func__, error);
1556178676Ssam		goto fail;
1557178676Ssam	}
1558198429Srpaulo
1559220726Sbschmidt	size = IWN_TX_RING_COUNT * sizeof (struct iwn_tx_cmd);
1560220691Sbschmidt	error = iwn_dma_contig_alloc(sc, &ring->cmd_dma, (void **)&ring->cmd,
1561220691Sbschmidt	    size, 4);
1562178676Ssam	if (error != 0) {
1563178676Ssam		device_printf(sc->sc_dev,
1564198429Srpaulo		    "%s: could not allocate TX cmd DMA memory, error %d\n",
1565178676Ssam		    __func__, error);
1566178676Ssam		goto fail;
1567178676Ssam	}
1568178676Ssam
1569198429Srpaulo	error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0,
1570220726Sbschmidt	    BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES,
1571220726Sbschmidt	    IWN_MAX_SCATTER - 1, MCLBYTES, BUS_DMA_NOWAIT, NULL, NULL,
1572220726Sbschmidt	    &ring->data_dmat);
1573198429Srpaulo	if (error != 0) {
1574198429Srpaulo		device_printf(sc->sc_dev,
1575220711Sbschmidt		    "%s: could not create TX buf DMA tag, error %d\n",
1576178676Ssam		    __func__, error);
1577198429Srpaulo		goto fail;
1578198429Srpaulo	}
1579178676Ssam
1580198429Srpaulo	paddr = ring->cmd_dma.paddr;
1581178676Ssam	for (i = 0; i < IWN_TX_RING_COUNT; i++) {
1582178676Ssam		struct iwn_tx_data *data = &ring->data[i];
1583178676Ssam
1584198429Srpaulo		data->cmd_paddr = paddr;
1585198429Srpaulo		data->scratch_paddr = paddr + 12;
1586198429Srpaulo		paddr += sizeof (struct iwn_tx_cmd);
1587198429Srpaulo
1588201209Srpaulo		error = bus_dmamap_create(ring->data_dmat, 0, &data->map);
1589178676Ssam		if (error != 0) {
1590178676Ssam			device_printf(sc->sc_dev,
1591220711Sbschmidt			    "%s: could not create TX buf DMA map, error %d\n",
1592178676Ssam			    __func__, error);
1593178676Ssam			goto fail;
1594178676Ssam		}
1595178676Ssam	}
1596253705Sadrian
1597253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s end\n", __func__);
1598253705Sadrian
1599178676Ssam	return 0;
1600220726Sbschmidt
1601220726Sbschmidtfail:	iwn_free_tx_ring(sc, ring);
1602253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s end in error\n", __func__);
1603178676Ssam	return error;
1604178676Ssam}
1605178676Ssam
1606206477Sbschmidtstatic void
1607178676Ssamiwn_reset_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring)
1608178676Ssam{
1609198429Srpaulo	int i;
1610178676Ssam
1611253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->doing %s \n", __func__);
1612253705Sadrian
1613178676Ssam	for (i = 0; i < IWN_TX_RING_COUNT; i++) {
1614178676Ssam		struct iwn_tx_data *data = &ring->data[i];
1615178676Ssam
1616178676Ssam		if (data->m != NULL) {
1617220704Sbschmidt			bus_dmamap_sync(ring->data_dmat, data->map,
1618220704Sbschmidt			    BUS_DMASYNC_POSTWRITE);
1619201209Srpaulo			bus_dmamap_unload(ring->data_dmat, data->map);
1620178676Ssam			m_freem(data->m);
1621178676Ssam			data->m = NULL;
1622178676Ssam		}
1623178676Ssam	}
1624198429Srpaulo	/* Clear TX descriptors. */
1625198429Srpaulo	memset(ring->desc, 0, ring->desc_dma.size);
1626198439Srpaulo	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
1627198439Srpaulo	    BUS_DMASYNC_PREWRITE);
1628198429Srpaulo	sc->qfullmsk &= ~(1 << ring->qid);
1629178676Ssam	ring->queued = 0;
1630178676Ssam	ring->cur = 0;
1631178676Ssam}
1632178676Ssam
1633206477Sbschmidtstatic void
1634178676Ssamiwn_free_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring)
1635178676Ssam{
1636178676Ssam	int i;
1637178676Ssam
1638253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s \n", __func__);
1639253705Sadrian
1640178676Ssam	iwn_dma_contig_free(&ring->desc_dma);
1641178676Ssam	iwn_dma_contig_free(&ring->cmd_dma);
1642178676Ssam
1643201209Srpaulo	for (i = 0; i < IWN_TX_RING_COUNT; i++) {
1644201209Srpaulo		struct iwn_tx_data *data = &ring->data[i];
1645178676Ssam
1646201209Srpaulo		if (data->m != NULL) {
1647201209Srpaulo			bus_dmamap_sync(ring->data_dmat, data->map,
1648201209Srpaulo			    BUS_DMASYNC_POSTWRITE);
1649201209Srpaulo			bus_dmamap_unload(ring->data_dmat, data->map);
1650201209Srpaulo			m_freem(data->m);
1651178676Ssam		}
1652201209Srpaulo		if (data->map != NULL)
1653201209Srpaulo			bus_dmamap_destroy(ring->data_dmat, data->map);
1654178676Ssam	}
1655220701Sbschmidt	if (ring->data_dmat != NULL) {
1656220701Sbschmidt		bus_dma_tag_destroy(ring->data_dmat);
1657220701Sbschmidt		ring->data_dmat = NULL;
1658220701Sbschmidt	}
1659178676Ssam}
1660178676Ssam
1661206477Sbschmidtstatic void
1662201209Srpauloiwn5000_ict_reset(struct iwn_softc *sc)
1663201209Srpaulo{
1664201209Srpaulo	/* Disable interrupts. */
1665201209Srpaulo	IWN_WRITE(sc, IWN_INT_MASK, 0);
1666201209Srpaulo
1667201209Srpaulo	/* Reset ICT table. */
1668201209Srpaulo	memset(sc->ict, 0, IWN_ICT_SIZE);
1669201209Srpaulo	sc->ict_cur = 0;
1670201209Srpaulo
1671220725Sbschmidt	/* Set physical address of ICT table (4KB aligned). */
1672201209Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "%s: enabling ICT\n", __func__);
1673201209Srpaulo	IWN_WRITE(sc, IWN_DRAM_INT_TBL, IWN_DRAM_INT_TBL_ENABLE |
1674201209Srpaulo	    IWN_DRAM_INT_TBL_WRAP_CHECK | sc->ict_dma.paddr >> 12);
1675201209Srpaulo
1676201209Srpaulo	/* Enable periodic RX interrupt. */
1677201209Srpaulo	sc->int_mask |= IWN_INT_RX_PERIODIC;
1678201209Srpaulo	/* Switch to ICT interrupt mode in driver. */
1679201209Srpaulo	sc->sc_flags |= IWN_FLAG_USE_ICT;
1680201209Srpaulo
1681201209Srpaulo	/* Re-enable interrupts. */
1682201209Srpaulo	IWN_WRITE(sc, IWN_INT, 0xffffffff);
1683201209Srpaulo	IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask);
1684201209Srpaulo}
1685201209Srpaulo
1686206477Sbschmidtstatic int
1687198429Srpauloiwn_read_eeprom(struct iwn_softc *sc, uint8_t macaddr[IEEE80211_ADDR_LEN])
1688178676Ssam{
1689220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
1690220723Sbschmidt	uint16_t val;
1691198429Srpaulo	int error;
1692178676Ssam
1693253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
1694253705Sadrian
1695198429Srpaulo	/* Check whether adapter has an EEPROM or an OTPROM. */
1696198429Srpaulo	if (sc->hw_type >= IWN_HW_REV_TYPE_1000 &&
1697198429Srpaulo	    (IWN_READ(sc, IWN_OTP_GP) & IWN_OTP_GP_DEV_SEL_OTP))
1698198429Srpaulo		sc->sc_flags |= IWN_FLAG_HAS_OTPROM;
1699198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "%s found\n",
1700198429Srpaulo	    (sc->sc_flags & IWN_FLAG_HAS_OTPROM) ? "OTPROM" : "EEPROM");
1701178676Ssam
1702201209Srpaulo	/* Adapter has to be powered on for EEPROM access to work. */
1703220726Sbschmidt	if ((error = iwn_apm_init(sc)) != 0) {
1704201209Srpaulo		device_printf(sc->sc_dev,
1705220726Sbschmidt		    "%s: could not power ON adapter, error %d\n", __func__,
1706220726Sbschmidt		    error);
1707201209Srpaulo		return error;
1708201209Srpaulo	}
1709201209Srpaulo
1710198429Srpaulo	if ((IWN_READ(sc, IWN_EEPROM_GP) & 0x7) == 0) {
1711198429Srpaulo		device_printf(sc->sc_dev, "%s: bad ROM signature\n", __func__);
1712198429Srpaulo		return EIO;
1713198429Srpaulo	}
1714220726Sbschmidt	if ((error = iwn_eeprom_lock(sc)) != 0) {
1715220726Sbschmidt		device_printf(sc->sc_dev, "%s: could not lock ROM, error %d\n",
1716198429Srpaulo		    __func__, error);
1717198429Srpaulo		return error;
1718198429Srpaulo	}
1719201209Srpaulo	if (sc->sc_flags & IWN_FLAG_HAS_OTPROM) {
1720220726Sbschmidt		if ((error = iwn_init_otprom(sc)) != 0) {
1721201209Srpaulo			device_printf(sc->sc_dev,
1722201209Srpaulo			    "%s: could not initialize OTPROM, error %d\n",
1723201209Srpaulo			    __func__, error);
1724201209Srpaulo			return error;
1725201209Srpaulo		}
1726198429Srpaulo	}
1727178676Ssam
1728220729Sbschmidt	iwn_read_prom_data(sc, IWN_EEPROM_SKU_CAP, &val, 2);
1729220729Sbschmidt	DPRINTF(sc, IWN_DEBUG_RESET, "SKU capabilities=0x%04x\n", le16toh(val));
1730220729Sbschmidt	/* Check if HT support is bonded out. */
1731220729Sbschmidt	if (val & htole16(IWN_EEPROM_SKU_CAP_11N))
1732220729Sbschmidt		sc->sc_flags |= IWN_FLAG_HAS_11N;
1733220729Sbschmidt
1734198429Srpaulo	iwn_read_prom_data(sc, IWN_EEPROM_RFCFG, &val, 2);
1735198429Srpaulo	sc->rfcfg = le16toh(val);
1736198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "radio config=0x%04x\n", sc->rfcfg);
1737220727Sbschmidt	/* Read Tx/Rx chains from ROM unless it's known to be broken. */
1738220727Sbschmidt	if (sc->txchainmask == 0)
1739220727Sbschmidt		sc->txchainmask = IWN_RFCFG_TXANTMSK(sc->rfcfg);
1740220727Sbschmidt	if (sc->rxchainmask == 0)
1741220727Sbschmidt		sc->rxchainmask = IWN_RFCFG_RXANTMSK(sc->rfcfg);
1742178676Ssam
1743198429Srpaulo	/* Read MAC address. */
1744198429Srpaulo	iwn_read_prom_data(sc, IWN_EEPROM_MAC, macaddr, 6);
1745178676Ssam
1746198429Srpaulo	/* Read adapter-specific information from EEPROM. */
1747220728Sbschmidt	ops->read_eeprom(sc);
1748178676Ssam
1749201209Srpaulo	iwn_apm_stop(sc);	/* Power OFF adapter. */
1750201209Srpaulo
1751198429Srpaulo	iwn_eeprom_unlock(sc);
1752253705Sadrian
1753253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s end\n", __func__);
1754253705Sadrian
1755198429Srpaulo	return 0;
1756178676Ssam}
1757178676Ssam
1758206477Sbschmidtstatic void
1759198429Srpauloiwn4965_read_eeprom(struct iwn_softc *sc)
1760178676Ssam{
1761201209Srpaulo	uint32_t addr;
1762220723Sbschmidt	uint16_t val;
1763198429Srpaulo	int i;
1764178676Ssam
1765253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
1766253705Sadrian
1767220725Sbschmidt	/* Read regulatory domain (4 ASCII characters). */
1768198429Srpaulo	iwn_read_prom_data(sc, IWN4965_EEPROM_DOMAIN, sc->eeprom_domain, 4);
1769178676Ssam
1770220725Sbschmidt	/* Read the list of authorized channels (20MHz ones only). */
1771221636Sbschmidt	for (i = 0; i < 7; i++) {
1772201209Srpaulo		addr = iwn4965_regulatory_bands[i];
1773201209Srpaulo		iwn_read_eeprom_channels(sc, i, addr);
1774201209Srpaulo	}
1775198429Srpaulo
1776198429Srpaulo	/* Read maximum allowed TX power for 2GHz and 5GHz bands. */
1777198429Srpaulo	iwn_read_prom_data(sc, IWN4965_EEPROM_MAXPOW, &val, 2);
1778198429Srpaulo	sc->maxpwr2GHz = val & 0xff;
1779198429Srpaulo	sc->maxpwr5GHz = val >> 8;
1780198429Srpaulo	/* Check that EEPROM values are within valid range. */
1781198429Srpaulo	if (sc->maxpwr5GHz < 20 || sc->maxpwr5GHz > 50)
1782198429Srpaulo		sc->maxpwr5GHz = 38;
1783198429Srpaulo	if (sc->maxpwr2GHz < 20 || sc->maxpwr2GHz > 50)
1784198429Srpaulo		sc->maxpwr2GHz = 38;
1785198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "maxpwr 2GHz=%d 5GHz=%d\n",
1786198429Srpaulo	    sc->maxpwr2GHz, sc->maxpwr5GHz);
1787198429Srpaulo
1788198429Srpaulo	/* Read samples for each TX power group. */
1789198429Srpaulo	iwn_read_prom_data(sc, IWN4965_EEPROM_BANDS, sc->bands,
1790198429Srpaulo	    sizeof sc->bands);
1791198429Srpaulo
1792198429Srpaulo	/* Read voltage at which samples were taken. */
1793198429Srpaulo	iwn_read_prom_data(sc, IWN4965_EEPROM_VOLTAGE, &val, 2);
1794198429Srpaulo	sc->eeprom_voltage = (int16_t)le16toh(val);
1795198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "voltage=%d (in 0.3V)\n",
1796198429Srpaulo	    sc->eeprom_voltage);
1797198429Srpaulo
1798198429Srpaulo#ifdef IWN_DEBUG
1799198429Srpaulo	/* Print samples. */
1800201209Srpaulo	if (sc->sc_debug & IWN_DEBUG_ANY) {
1801198429Srpaulo		for (i = 0; i < IWN_NBANDS; i++)
1802198429Srpaulo			iwn4965_print_power_group(sc, i);
1803178676Ssam	}
1804198429Srpaulo#endif
1805253705Sadrian
1806253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s end\n", __func__);
1807178676Ssam}
1808178676Ssam
1809198429Srpaulo#ifdef IWN_DEBUG
1810206477Sbschmidtstatic void
1811198429Srpauloiwn4965_print_power_group(struct iwn_softc *sc, int i)
1812178676Ssam{
1813198429Srpaulo	struct iwn4965_eeprom_band *band = &sc->bands[i];
1814198429Srpaulo	struct iwn4965_eeprom_chan_samples *chans = band->chans;
1815198429Srpaulo	int j, c;
1816178676Ssam
1817198429Srpaulo	printf("===band %d===\n", i);
1818198429Srpaulo	printf("chan lo=%d, chan hi=%d\n", band->lo, band->hi);
1819198429Srpaulo	printf("chan1 num=%d\n", chans[0].num);
1820198429Srpaulo	for (c = 0; c < 2; c++) {
1821198429Srpaulo		for (j = 0; j < IWN_NSAMPLES; j++) {
1822198429Srpaulo			printf("chain %d, sample %d: temp=%d gain=%d "
1823198429Srpaulo			    "power=%d pa_det=%d\n", c, j,
1824198429Srpaulo			    chans[0].samples[c][j].temp,
1825198429Srpaulo			    chans[0].samples[c][j].gain,
1826198429Srpaulo			    chans[0].samples[c][j].power,
1827198429Srpaulo			    chans[0].samples[c][j].pa_det);
1828198429Srpaulo		}
1829198429Srpaulo	}
1830198429Srpaulo	printf("chan2 num=%d\n", chans[1].num);
1831198429Srpaulo	for (c = 0; c < 2; c++) {
1832198429Srpaulo		for (j = 0; j < IWN_NSAMPLES; j++) {
1833198429Srpaulo			printf("chain %d, sample %d: temp=%d gain=%d "
1834198429Srpaulo			    "power=%d pa_det=%d\n", c, j,
1835198429Srpaulo			    chans[1].samples[c][j].temp,
1836198429Srpaulo			    chans[1].samples[c][j].gain,
1837198429Srpaulo			    chans[1].samples[c][j].power,
1838198429Srpaulo			    chans[1].samples[c][j].pa_det);
1839198429Srpaulo		}
1840198429Srpaulo	}
1841178676Ssam}
1842198429Srpaulo#endif
1843178676Ssam
1844206477Sbschmidtstatic void
1845198429Srpauloiwn5000_read_eeprom(struct iwn_softc *sc)
1846178676Ssam{
1847206444Sbschmidt	struct iwn5000_eeprom_calib_hdr hdr;
1848220674Sbschmidt	int32_t volt;
1849220723Sbschmidt	uint32_t base, addr;
1850220723Sbschmidt	uint16_t val;
1851198429Srpaulo	int i;
1852178676Ssam
1853253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
1854253705Sadrian
1855220725Sbschmidt	/* Read regulatory domain (4 ASCII characters). */
1856198429Srpaulo	iwn_read_prom_data(sc, IWN5000_EEPROM_REG, &val, 2);
1857198429Srpaulo	base = le16toh(val);
1858198429Srpaulo	iwn_read_prom_data(sc, base + IWN5000_EEPROM_DOMAIN,
1859198429Srpaulo	    sc->eeprom_domain, 4);
1860178676Ssam
1861220725Sbschmidt	/* Read the list of authorized channels (20MHz ones only). */
1862221636Sbschmidt	for (i = 0; i < 7; i++) {
1863221635Sbschmidt		if (sc->hw_type >= IWN_HW_REV_TYPE_6000)
1864221635Sbschmidt			addr = base + iwn6000_regulatory_bands[i];
1865221635Sbschmidt		else
1866221635Sbschmidt			addr = base + iwn5000_regulatory_bands[i];
1867201209Srpaulo		iwn_read_eeprom_channels(sc, i, addr);
1868198429Srpaulo	}
1869178676Ssam
1870201209Srpaulo	/* Read enhanced TX power information for 6000 Series. */
1871201209Srpaulo	if (sc->hw_type >= IWN_HW_REV_TYPE_6000)
1872201209Srpaulo		iwn_read_eeprom_enhinfo(sc);
1873201209Srpaulo
1874198429Srpaulo	iwn_read_prom_data(sc, IWN5000_EEPROM_CAL, &val, 2);
1875198429Srpaulo	base = le16toh(val);
1876206444Sbschmidt	iwn_read_prom_data(sc, base, &hdr, sizeof hdr);
1877206444Sbschmidt	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
1878220726Sbschmidt	    "%s: calib version=%u pa type=%u voltage=%u\n", __func__,
1879220726Sbschmidt	    hdr.version, hdr.pa_type, le16toh(hdr.volt));
1880210108Sbschmidt	sc->calib_ver = hdr.version;
1881206444Sbschmidt
1882198429Srpaulo	if (sc->hw_type == IWN_HW_REV_TYPE_5150) {
1883201209Srpaulo		/* Compute temperature offset. */
1884198429Srpaulo		iwn_read_prom_data(sc, base + IWN5000_EEPROM_TEMP, &val, 2);
1885220674Sbschmidt		sc->eeprom_temp = le16toh(val);
1886198429Srpaulo		iwn_read_prom_data(sc, base + IWN5000_EEPROM_VOLT, &val, 2);
1887198429Srpaulo		volt = le16toh(val);
1888220674Sbschmidt		sc->temp_off = sc->eeprom_temp - (volt / -5);
1889201209Srpaulo		DPRINTF(sc, IWN_DEBUG_CALIBRATE, "temp=%d volt=%d offset=%dK\n",
1890220674Sbschmidt		    sc->eeprom_temp, volt, sc->temp_off);
1891220674Sbschmidt	} else {
1892220674Sbschmidt		/* Read crystal calibration. */
1893220674Sbschmidt		iwn_read_prom_data(sc, base + IWN5000_EEPROM_CRYSTAL,
1894220674Sbschmidt		    &sc->eeprom_crystal, sizeof (uint32_t));
1895220674Sbschmidt		DPRINTF(sc, IWN_DEBUG_CALIBRATE, "crystal calibration 0x%08x\n",
1896220674Sbschmidt		    le32toh(sc->eeprom_crystal));
1897178676Ssam	}
1898253705Sadrian
1899253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s end\n", __func__);
1900253705Sadrian
1901178676Ssam}
1902178676Ssam
1903201209Srpaulo/*
1904201209Srpaulo * Translate EEPROM flags to net80211.
1905201209Srpaulo */
1906201209Srpaulostatic uint32_t
1907201209Srpauloiwn_eeprom_channel_flags(struct iwn_eeprom_chan *channel)
1908201209Srpaulo{
1909201209Srpaulo	uint32_t nflags;
1910201209Srpaulo
1911201209Srpaulo	nflags = 0;
1912201209Srpaulo	if ((channel->flags & IWN_EEPROM_CHAN_ACTIVE) == 0)
1913201209Srpaulo		nflags |= IEEE80211_CHAN_PASSIVE;
1914201209Srpaulo	if ((channel->flags & IWN_EEPROM_CHAN_IBSS) == 0)
1915201209Srpaulo		nflags |= IEEE80211_CHAN_NOADHOC;
1916201209Srpaulo	if (channel->flags & IWN_EEPROM_CHAN_RADAR) {
1917201209Srpaulo		nflags |= IEEE80211_CHAN_DFS;
1918201209Srpaulo		/* XXX apparently IBSS may still be marked */
1919201209Srpaulo		nflags |= IEEE80211_CHAN_NOADHOC;
1920201209Srpaulo	}
1921201209Srpaulo
1922201209Srpaulo	return nflags;
1923201209Srpaulo}
1924201209Srpaulo
1925198429Srpaulostatic void
1926201209Srpauloiwn_read_eeprom_band(struct iwn_softc *sc, int n)
1927178676Ssam{
1928198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
1929198429Srpaulo	struct ieee80211com *ic = ifp->if_l2com;
1930201209Srpaulo	struct iwn_eeprom_chan *channels = sc->eeprom_channels[n];
1931201209Srpaulo	const struct iwn_chan_band *band = &iwn_bands[n];
1932198429Srpaulo	struct ieee80211_channel *c;
1933220687Sbschmidt	uint8_t chan;
1934220687Sbschmidt	int i, nflags;
1935178676Ssam
1936253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
1937253705Sadrian
1938198429Srpaulo	for (i = 0; i < band->nchan; i++) {
1939198429Srpaulo		if (!(channels[i].flags & IWN_EEPROM_CHAN_VALID)) {
1940198429Srpaulo			DPRINTF(sc, IWN_DEBUG_RESET,
1941198429Srpaulo			    "skip chan %d flags 0x%x maxpwr %d\n",
1942198429Srpaulo			    band->chan[i], channels[i].flags,
1943198429Srpaulo			    channels[i].maxpwr);
1944198429Srpaulo			continue;
1945198429Srpaulo		}
1946198429Srpaulo		chan = band->chan[i];
1947201209Srpaulo		nflags = iwn_eeprom_channel_flags(&channels[i]);
1948178676Ssam
1949198429Srpaulo		c = &ic->ic_channels[ic->ic_nchans++];
1950198429Srpaulo		c->ic_ieee = chan;
1951198429Srpaulo		c->ic_maxregpower = channels[i].maxpwr;
1952198429Srpaulo		c->ic_maxpower = 2*c->ic_maxregpower;
1953206445Sbschmidt
1954201209Srpaulo		if (n == 0) {	/* 2GHz band */
1955220726Sbschmidt			c->ic_freq = ieee80211_ieee2mhz(chan, IEEE80211_CHAN_G);
1956198429Srpaulo			/* G =>'s B is supported */
1957198429Srpaulo			c->ic_flags = IEEE80211_CHAN_B | nflags;
1958198429Srpaulo			c = &ic->ic_channels[ic->ic_nchans++];
1959198429Srpaulo			c[0] = c[-1];
1960198429Srpaulo			c->ic_flags = IEEE80211_CHAN_G | nflags;
1961198429Srpaulo		} else {	/* 5GHz band */
1962220726Sbschmidt			c->ic_freq = ieee80211_ieee2mhz(chan, IEEE80211_CHAN_A);
1963198429Srpaulo			c->ic_flags = IEEE80211_CHAN_A | nflags;
1964178676Ssam		}
1965220723Sbschmidt
1966220723Sbschmidt		/* Save maximum allowed TX power for this channel. */
1967220723Sbschmidt		sc->maxpwr[chan] = channels[i].maxpwr;
1968220723Sbschmidt
1969220723Sbschmidt		DPRINTF(sc, IWN_DEBUG_RESET,
1970220726Sbschmidt		    "add chan %d flags 0x%x maxpwr %d\n", chan,
1971220726Sbschmidt		    channels[i].flags, channels[i].maxpwr);
1972220723Sbschmidt
1973221636Sbschmidt		if (sc->sc_flags & IWN_FLAG_HAS_11N) {
1974221636Sbschmidt			/* add HT20, HT40 added separately */
1975221636Sbschmidt			c = &ic->ic_channels[ic->ic_nchans++];
1976221636Sbschmidt			c[0] = c[-1];
1977221636Sbschmidt			c->ic_flags |= IEEE80211_CHAN_HT20;
1978221636Sbschmidt		}
1979178676Ssam	}
1980253705Sadrian
1981253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s end\n", __func__);
1982253705Sadrian
1983178676Ssam}
1984178676Ssam
1985198429Srpaulostatic void
1986201209Srpauloiwn_read_eeprom_ht40(struct iwn_softc *sc, int n)
1987178676Ssam{
1988198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
1989198429Srpaulo	struct ieee80211com *ic = ifp->if_l2com;
1990201209Srpaulo	struct iwn_eeprom_chan *channels = sc->eeprom_channels[n];
1991201209Srpaulo	const struct iwn_chan_band *band = &iwn_bands[n];
1992198429Srpaulo	struct ieee80211_channel *c, *cent, *extc;
1993221636Sbschmidt	uint8_t chan;
1994221636Sbschmidt	int i, nflags;
1995178676Ssam
1996253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s start\n", __func__);
1997253705Sadrian
1998253705Sadrian	if (!(sc->sc_flags & IWN_FLAG_HAS_11N)) {
1999253705Sadrian		DPRINTF(sc, IWN_DEBUG_TRACE, "->%s end no 11n\n", __func__);
2000221636Sbschmidt		return;
2001253705Sadrian	}
2002221636Sbschmidt
2003198429Srpaulo	for (i = 0; i < band->nchan; i++) {
2004221636Sbschmidt		if (!(channels[i].flags & IWN_EEPROM_CHAN_VALID)) {
2005198429Srpaulo			DPRINTF(sc, IWN_DEBUG_RESET,
2006198429Srpaulo			    "skip chan %d flags 0x%x maxpwr %d\n",
2007198429Srpaulo			    band->chan[i], channels[i].flags,
2008198429Srpaulo			    channels[i].maxpwr);
2009198429Srpaulo			continue;
2010198429Srpaulo		}
2011221636Sbschmidt		chan = band->chan[i];
2012221636Sbschmidt		nflags = iwn_eeprom_channel_flags(&channels[i]);
2013221636Sbschmidt
2014198429Srpaulo		/*
2015198429Srpaulo		 * Each entry defines an HT40 channel pair; find the
2016198429Srpaulo		 * center channel, then the extension channel above.
2017198429Srpaulo		 */
2018221636Sbschmidt		cent = ieee80211_find_channel_byieee(ic, chan,
2019221636Sbschmidt		    (n == 5 ? IEEE80211_CHAN_G : IEEE80211_CHAN_A));
2020198429Srpaulo		if (cent == NULL) {	/* XXX shouldn't happen */
2021198429Srpaulo			device_printf(sc->sc_dev,
2022221636Sbschmidt			    "%s: no entry for channel %d\n", __func__, chan);
2023198429Srpaulo			continue;
2024198429Srpaulo		}
2025198429Srpaulo		extc = ieee80211_find_channel(ic, cent->ic_freq+20,
2026221636Sbschmidt		    (n == 5 ? IEEE80211_CHAN_G : IEEE80211_CHAN_A));
2027198429Srpaulo		if (extc == NULL) {
2028198429Srpaulo			DPRINTF(sc, IWN_DEBUG_RESET,
2029221636Sbschmidt			    "%s: skip chan %d, extension channel not found\n",
2030221636Sbschmidt			    __func__, chan);
2031198429Srpaulo			continue;
2032198429Srpaulo		}
2033178676Ssam
2034198429Srpaulo		DPRINTF(sc, IWN_DEBUG_RESET,
2035198429Srpaulo		    "add ht40 chan %d flags 0x%x maxpwr %d\n",
2036221636Sbschmidt		    chan, channels[i].flags, channels[i].maxpwr);
2037178676Ssam
2038198429Srpaulo		c = &ic->ic_channels[ic->ic_nchans++];
2039198429Srpaulo		c[0] = cent[0];
2040198429Srpaulo		c->ic_extieee = extc->ic_ieee;
2041198429Srpaulo		c->ic_flags &= ~IEEE80211_CHAN_HT;
2042221636Sbschmidt		c->ic_flags |= IEEE80211_CHAN_HT40U | nflags;
2043198429Srpaulo		c = &ic->ic_channels[ic->ic_nchans++];
2044198429Srpaulo		c[0] = extc[0];
2045198429Srpaulo		c->ic_extieee = cent->ic_ieee;
2046198429Srpaulo		c->ic_flags &= ~IEEE80211_CHAN_HT;
2047221636Sbschmidt		c->ic_flags |= IEEE80211_CHAN_HT40D | nflags;
2048178676Ssam	}
2049253705Sadrian
2050253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s end\n", __func__);
2051253705Sadrian
2052198429Srpaulo}
2053178676Ssam
2054198429Srpaulostatic void
2055201209Srpauloiwn_read_eeprom_channels(struct iwn_softc *sc, int n, uint32_t addr)
2056198429Srpaulo{
2057198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
2058198429Srpaulo	struct ieee80211com *ic = ifp->if_l2com;
2059178676Ssam
2060201209Srpaulo	iwn_read_prom_data(sc, addr, &sc->eeprom_channels[n],
2061201209Srpaulo	    iwn_bands[n].nchan * sizeof (struct iwn_eeprom_chan));
2062201209Srpaulo
2063198429Srpaulo	if (n < 5)
2064201209Srpaulo		iwn_read_eeprom_band(sc, n);
2065198429Srpaulo	else
2066201209Srpaulo		iwn_read_eeprom_ht40(sc, n);
2067198429Srpaulo	ieee80211_sort_channels(ic->ic_channels, ic->ic_nchans);
2068178676Ssam}
2069178676Ssam
2070220723Sbschmidtstatic struct iwn_eeprom_chan *
2071220723Sbschmidtiwn_find_eeprom_channel(struct iwn_softc *sc, struct ieee80211_channel *c)
2072220723Sbschmidt{
2073221636Sbschmidt	int band, chan, i, j;
2074220723Sbschmidt
2075221636Sbschmidt	if (IEEE80211_IS_CHAN_HT40(c)) {
2076221636Sbschmidt		band = IEEE80211_IS_CHAN_5GHZ(c) ? 6 : 5;
2077221636Sbschmidt		if (IEEE80211_IS_CHAN_HT40D(c))
2078221636Sbschmidt			chan = c->ic_extieee;
2079221636Sbschmidt		else
2080221636Sbschmidt			chan = c->ic_ieee;
2081221636Sbschmidt		for (i = 0; i < iwn_bands[band].nchan; i++) {
2082221636Sbschmidt			if (iwn_bands[band].chan[i] == chan)
2083221636Sbschmidt				return &sc->eeprom_channels[band][i];
2084220723Sbschmidt		}
2085221636Sbschmidt	} else {
2086221636Sbschmidt		for (j = 0; j < 5; j++) {
2087221636Sbschmidt			for (i = 0; i < iwn_bands[j].nchan; i++) {
2088221636Sbschmidt				if (iwn_bands[j].chan[i] == c->ic_ieee)
2089221636Sbschmidt					return &sc->eeprom_channels[j][i];
2090221636Sbschmidt			}
2091221636Sbschmidt		}
2092220723Sbschmidt	}
2093220723Sbschmidt	return NULL;
2094220723Sbschmidt}
2095220723Sbschmidt
2096220723Sbschmidt/*
2097220723Sbschmidt * Enforce flags read from EEPROM.
2098220723Sbschmidt */
2099220723Sbschmidtstatic int
2100220723Sbschmidtiwn_setregdomain(struct ieee80211com *ic, struct ieee80211_regdomain *rd,
2101220723Sbschmidt    int nchan, struct ieee80211_channel chans[])
2102220723Sbschmidt{
2103220723Sbschmidt	struct iwn_softc *sc = ic->ic_ifp->if_softc;
2104220723Sbschmidt	int i;
2105220723Sbschmidt
2106220723Sbschmidt	for (i = 0; i < nchan; i++) {
2107220723Sbschmidt		struct ieee80211_channel *c = &chans[i];
2108220723Sbschmidt		struct iwn_eeprom_chan *channel;
2109220723Sbschmidt
2110220723Sbschmidt		channel = iwn_find_eeprom_channel(sc, c);
2111220723Sbschmidt		if (channel == NULL) {
2112220723Sbschmidt			if_printf(ic->ic_ifp,
2113220723Sbschmidt			    "%s: invalid channel %u freq %u/0x%x\n",
2114220723Sbschmidt			    __func__, c->ic_ieee, c->ic_freq, c->ic_flags);
2115220723Sbschmidt			return EINVAL;
2116220723Sbschmidt		}
2117220723Sbschmidt		c->ic_flags |= iwn_eeprom_channel_flags(channel);
2118220723Sbschmidt	}
2119220723Sbschmidt
2120220723Sbschmidt	return 0;
2121220723Sbschmidt}
2122220723Sbschmidt
2123206477Sbschmidtstatic void
2124201209Srpauloiwn_read_eeprom_enhinfo(struct iwn_softc *sc)
2125201209Srpaulo{
2126201209Srpaulo	struct iwn_eeprom_enhinfo enhinfo[35];
2127221637Sbschmidt	struct ifnet *ifp = sc->sc_ifp;
2128221637Sbschmidt	struct ieee80211com *ic = ifp->if_l2com;
2129221637Sbschmidt	struct ieee80211_channel *c;
2130201209Srpaulo	uint16_t val, base;
2131201209Srpaulo	int8_t maxpwr;
2132221637Sbschmidt	uint8_t flags;
2133221637Sbschmidt	int i, j;
2134201209Srpaulo
2135253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
2136253705Sadrian
2137201209Srpaulo	iwn_read_prom_data(sc, IWN5000_EEPROM_REG, &val, 2);
2138201209Srpaulo	base = le16toh(val);
2139201209Srpaulo	iwn_read_prom_data(sc, base + IWN6000_EEPROM_ENHINFO,
2140201209Srpaulo	    enhinfo, sizeof enhinfo);
2141201209Srpaulo
2142201209Srpaulo	for (i = 0; i < nitems(enhinfo); i++) {
2143221637Sbschmidt		flags = enhinfo[i].flags;
2144221637Sbschmidt		if (!(flags & IWN_ENHINFO_VALID))
2145201209Srpaulo			continue;	/* Skip invalid entries. */
2146201209Srpaulo
2147201209Srpaulo		maxpwr = 0;
2148201209Srpaulo		if (sc->txchainmask & IWN_ANT_A)
2149201209Srpaulo			maxpwr = MAX(maxpwr, enhinfo[i].chain[0]);
2150201209Srpaulo		if (sc->txchainmask & IWN_ANT_B)
2151201209Srpaulo			maxpwr = MAX(maxpwr, enhinfo[i].chain[1]);
2152201209Srpaulo		if (sc->txchainmask & IWN_ANT_C)
2153201209Srpaulo			maxpwr = MAX(maxpwr, enhinfo[i].chain[2]);
2154201209Srpaulo		if (sc->ntxchains == 2)
2155201209Srpaulo			maxpwr = MAX(maxpwr, enhinfo[i].mimo2);
2156201209Srpaulo		else if (sc->ntxchains == 3)
2157201209Srpaulo			maxpwr = MAX(maxpwr, enhinfo[i].mimo3);
2158201209Srpaulo
2159221637Sbschmidt		for (j = 0; j < ic->ic_nchans; j++) {
2160221637Sbschmidt			c = &ic->ic_channels[j];
2161221637Sbschmidt			if ((flags & IWN_ENHINFO_5GHZ)) {
2162221637Sbschmidt				if (!IEEE80211_IS_CHAN_A(c))
2163221637Sbschmidt					continue;
2164221637Sbschmidt			} else if ((flags & IWN_ENHINFO_OFDM)) {
2165221637Sbschmidt				if (!IEEE80211_IS_CHAN_G(c))
2166221637Sbschmidt					continue;
2167221637Sbschmidt			} else if (!IEEE80211_IS_CHAN_B(c))
2168221637Sbschmidt				continue;
2169221637Sbschmidt			if ((flags & IWN_ENHINFO_HT40)) {
2170221637Sbschmidt				if (!IEEE80211_IS_CHAN_HT40(c))
2171221637Sbschmidt					continue;
2172221637Sbschmidt			} else {
2173221637Sbschmidt				if (IEEE80211_IS_CHAN_HT40(c))
2174221637Sbschmidt					continue;
2175221637Sbschmidt			}
2176221637Sbschmidt			if (enhinfo[i].chan != 0 &&
2177221637Sbschmidt			    enhinfo[i].chan != c->ic_ieee)
2178221637Sbschmidt				continue;
2179221637Sbschmidt
2180221637Sbschmidt			DPRINTF(sc, IWN_DEBUG_RESET,
2181221637Sbschmidt			    "channel %d(%x), maxpwr %d\n", c->ic_ieee,
2182221637Sbschmidt			    c->ic_flags, maxpwr / 2);
2183221637Sbschmidt			c->ic_maxregpower = maxpwr / 2;
2184221637Sbschmidt			c->ic_maxpower = maxpwr;
2185221637Sbschmidt		}
2186201209Srpaulo	}
2187253705Sadrian
2188253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s end\n", __func__);
2189253705Sadrian
2190201209Srpaulo}
2191201209Srpaulo
2192206477Sbschmidtstatic struct ieee80211_node *
2193198429Srpauloiwn_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN])
2194178676Ssam{
2195198429Srpaulo	return malloc(sizeof (struct iwn_node), M_80211_NODE,M_NOWAIT | M_ZERO);
2196198429Srpaulo}
2197178676Ssam
2198221648Sbschmidtstatic __inline int
2199221648Sbschmidtrate2plcp(int rate)
2200221648Sbschmidt{
2201221648Sbschmidt	switch (rate & 0xff) {
2202221648Sbschmidt	case 12:	return 0xd;
2203221648Sbschmidt	case 18:	return 0xf;
2204221648Sbschmidt	case 24:	return 0x5;
2205221648Sbschmidt	case 36:	return 0x7;
2206221648Sbschmidt	case 48:	return 0x9;
2207221648Sbschmidt	case 72:	return 0xb;
2208221648Sbschmidt	case 96:	return 0x1;
2209221648Sbschmidt	case 108:	return 0x3;
2210221648Sbschmidt	case 2:		return 10;
2211221648Sbschmidt	case 4:		return 20;
2212221648Sbschmidt	case 11:	return 55;
2213221648Sbschmidt	case 22:	return 110;
2214221648Sbschmidt	}
2215221648Sbschmidt	return 0;
2216221648Sbschmidt}
2217221648Sbschmidt
2218252727Sadrian/*
2219252727Sadrian * Calculate the required PLCP value from the given rate,
2220252727Sadrian * to the given node.
2221252727Sadrian *
2222252727Sadrian * This will take the node configuration (eg 11n, rate table
2223252727Sadrian * setup, etc) into consideration.
2224252727Sadrian */
2225252727Sadrianstatic uint32_t
2226252727Sadrianiwn_rate_to_plcp(struct iwn_softc *sc, struct ieee80211_node *ni,
2227252727Sadrian    uint8_t rate)
2228220715Sbschmidt{
2229222933Sbschmidt#define	RV(v)	((v) & IEEE80211_RATE_VAL)
2230221648Sbschmidt	struct ieee80211com *ic = ni->ni_ic;
2231221649Sbschmidt	uint8_t txant1, txant2;
2232252727Sadrian	uint32_t plcp = 0;
2233252727Sadrian	int ridx;
2234220715Sbschmidt
2235221648Sbschmidt	/* Use the first valid TX antenna. */
2236221649Sbschmidt	txant1 = IWN_LSB(sc->txchainmask);
2237221649Sbschmidt	txant2 = IWN_LSB(sc->txchainmask & ~txant1);
2238221648Sbschmidt
2239252727Sadrian	/*
2240252727Sadrian	 * If it's an MCS rate, let's set the plcp correctly
2241252727Sadrian	 * and set the relevant flags based on the node config.
2242252727Sadrian	 */
2243221649Sbschmidt	if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) {
2244252727Sadrian		/*
2245252727Sadrian		 * Set the initial PLCP value to be between 0->31 for
2246252727Sadrian		 * MCS 0 -> MCS 31, then set the "I'm an MCS rate!"
2247252727Sadrian		 * flag.
2248252727Sadrian		 */
2249252727Sadrian		plcp = RV(rate) | IWN_RFLAG_MCS;
2250252727Sadrian
2251252727Sadrian		/*
2252252727Sadrian		 * XXX the following should only occur if both
2253252727Sadrian		 * the local configuration _and_ the remote node
2254252727Sadrian		 * advertise these capabilities.  Thus this code
2255252727Sadrian		 * may need fixing!
2256252727Sadrian		 */
2257252727Sadrian
2258252727Sadrian		/*
2259252727Sadrian		 * Set the channel width and guard interval.
2260252727Sadrian		 */
2261252727Sadrian		if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) {
2262252727Sadrian			plcp |= IWN_RFLAG_HT40;
2263252727Sadrian			if (ni->ni_htcap & IEEE80211_HTCAP_SHORTGI40)
2264221649Sbschmidt				plcp |= IWN_RFLAG_SGI;
2265252727Sadrian		} else if (ni->ni_htcap & IEEE80211_HTCAP_SHORTGI20) {
2266252727Sadrian			plcp |= IWN_RFLAG_SGI;
2267221649Sbschmidt		}
2268252727Sadrian
2269252727Sadrian		/*
2270252727Sadrian		 * If it's a two stream rate, enable TX on both
2271252727Sadrian		 * antennas.
2272252727Sadrian		 *
2273252727Sadrian		 * XXX three stream rates?
2274252727Sadrian		 */
2275252727Sadrian		if (rate > 0x87)
2276252727Sadrian			plcp |= IWN_RFLAG_ANT(txant1 | txant2);
2277252727Sadrian		else
2278252727Sadrian			plcp |= IWN_RFLAG_ANT(txant1);
2279221649Sbschmidt	} else {
2280252727Sadrian		/*
2281252727Sadrian		 * Set the initial PLCP - fine for both
2282252727Sadrian		 * OFDM and CCK rates.
2283252727Sadrian		 */
2284252727Sadrian		plcp = rate2plcp(rate);
2285252727Sadrian
2286252727Sadrian		/* Set CCK flag if it's CCK */
2287252727Sadrian
2288252727Sadrian		/* XXX It would be nice to have a method
2289252727Sadrian		 * to map the ridx -> phy table entry
2290252727Sadrian		 * so we could just query that, rather than
2291252727Sadrian		 * this hack to check against IWN_RIDX_OFDM6.
2292252727Sadrian		 */
2293252727Sadrian		ridx = ieee80211_legacy_rate_lookup(ic->ic_rt,
2294252727Sadrian		    rate & IEEE80211_RATE_VAL);
2295252727Sadrian		if (ridx < IWN_RIDX_OFDM6 &&
2296252727Sadrian		    IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
2297252727Sadrian			plcp |= IWN_RFLAG_CCK;
2298252727Sadrian
2299252727Sadrian		/* Set antenna configuration */
2300252727Sadrian		plcp |= IWN_RFLAG_ANT(txant1);
2301220715Sbschmidt	}
2302252727Sadrian
2303252727Sadrian	DPRINTF(sc, IWN_DEBUG_TXRATE, "%s: rate=0x%02x, plcp=0x%08x\n",
2304252727Sadrian	    __func__,
2305252727Sadrian	    rate,
2306252727Sadrian	    plcp);
2307252727Sadrian
2308252727Sadrian	return (htole32(plcp));
2309222933Sbschmidt#undef	RV
2310220715Sbschmidt}
2311220715Sbschmidt
2312252727Sadrianstatic void
2313252727Sadrianiwn_newassoc(struct ieee80211_node *ni, int isnew)
2314252727Sadrian{
2315252727Sadrian	/* Doesn't do anything at the moment */
2316252727Sadrian}
2317252727Sadrian
2318206477Sbschmidtstatic int
2319198429Srpauloiwn_media_change(struct ifnet *ifp)
2320178676Ssam{
2321220726Sbschmidt	int error;
2322220726Sbschmidt
2323220726Sbschmidt	error = ieee80211_media_change(ifp);
2324198429Srpaulo	/* NB: only the fixed rate can change and that doesn't need a reset */
2325198429Srpaulo	return (error == ENETRESET ? 0 : error);
2326198429Srpaulo}
2327178676Ssam
2328206477Sbschmidtstatic int
2329198429Srpauloiwn_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
2330198429Srpaulo{
2331198429Srpaulo	struct iwn_vap *ivp = IWN_VAP(vap);
2332198429Srpaulo	struct ieee80211com *ic = vap->iv_ic;
2333198429Srpaulo	struct iwn_softc *sc = ic->ic_ifp->if_softc;
2334220688Sbschmidt	int error = 0;
2335178676Ssam
2336253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
2337253705Sadrian
2338198429Srpaulo	DPRINTF(sc, IWN_DEBUG_STATE, "%s: %s -> %s\n", __func__,
2339220726Sbschmidt	    ieee80211_state_name[vap->iv_state], ieee80211_state_name[nstate]);
2340178676Ssam
2341198429Srpaulo	IEEE80211_UNLOCK(ic);
2342198429Srpaulo	IWN_LOCK(sc);
2343220667Sbschmidt	callout_stop(&sc->calib_to);
2344178676Ssam
2345254204Sadrian	sc->rxon = &sc->rx_on[IWN_RXON_BSS_CTX];
2346254204Sadrian
2347210114Sbschmidt	switch (nstate) {
2348210114Sbschmidt	case IEEE80211_S_ASSOC:
2349210114Sbschmidt		if (vap->iv_state != IEEE80211_S_RUN)
2350210114Sbschmidt			break;
2351210114Sbschmidt		/* FALLTHROUGH */
2352210114Sbschmidt	case IEEE80211_S_AUTH:
2353210114Sbschmidt		if (vap->iv_state == IEEE80211_S_AUTH)
2354210114Sbschmidt			break;
2355210114Sbschmidt
2356210114Sbschmidt		/*
2357210114Sbschmidt		 * !AUTH -> AUTH transition requires state reset to handle
2358210114Sbschmidt		 * reassociations correctly.
2359210114Sbschmidt		 */
2360254204Sadrian		sc->rxon->associd = 0;
2361254204Sadrian		sc->rxon->filter &= ~htole32(IWN_FILTER_BSS);
2362220667Sbschmidt		sc->calib.state = IWN_CALIB_STATE_INIT;
2363220667Sbschmidt
2364220688Sbschmidt		if ((error = iwn_auth(sc, vap)) != 0) {
2365220688Sbschmidt			device_printf(sc->sc_dev,
2366220688Sbschmidt			    "%s: could not move to auth state\n", __func__);
2367220688Sbschmidt		}
2368210114Sbschmidt		break;
2369210114Sbschmidt
2370210114Sbschmidt	case IEEE80211_S_RUN:
2371198429Srpaulo		/*
2372210114Sbschmidt		 * RUN -> RUN transition; Just restart the timers.
2373210114Sbschmidt		 */
2374220667Sbschmidt		if (vap->iv_state == IEEE80211_S_RUN) {
2375220667Sbschmidt			sc->calib_cnt = 0;
2376210114Sbschmidt			break;
2377210114Sbschmidt		}
2378210114Sbschmidt
2379210114Sbschmidt		/*
2380198429Srpaulo		 * !RUN -> RUN requires setting the association id
2381198429Srpaulo		 * which is done with a firmware cmd.  We also defer
2382198429Srpaulo		 * starting the timers until that work is done.
2383198429Srpaulo		 */
2384220688Sbschmidt		if ((error = iwn_run(sc, vap)) != 0) {
2385220688Sbschmidt			device_printf(sc->sc_dev,
2386220688Sbschmidt			    "%s: could not move to run state\n", __func__);
2387220688Sbschmidt		}
2388210114Sbschmidt		break;
2389210114Sbschmidt
2390220667Sbschmidt	case IEEE80211_S_INIT:
2391220667Sbschmidt		sc->calib.state = IWN_CALIB_STATE_INIT;
2392220667Sbschmidt		break;
2393220667Sbschmidt
2394210114Sbschmidt	default:
2395210114Sbschmidt		break;
2396178676Ssam	}
2397198429Srpaulo	IWN_UNLOCK(sc);
2398198429Srpaulo	IEEE80211_LOCK(ic);
2399253705Sadrian	if (error != 0){
2400253705Sadrian		DPRINTF(sc, IWN_DEBUG_TRACE, "->%s end in error\n", __func__);
2401220688Sbschmidt		return error;
2402253705Sadrian	}
2403253705Sadrian
2404253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
2405253705Sadrian
2406198429Srpaulo	return ivp->iv_newstate(vap, nstate, arg);
2407178676Ssam}
2408178676Ssam
2409220667Sbschmidtstatic void
2410220667Sbschmidtiwn_calib_timeout(void *arg)
2411220667Sbschmidt{
2412220667Sbschmidt	struct iwn_softc *sc = arg;
2413220667Sbschmidt
2414220667Sbschmidt	IWN_LOCK_ASSERT(sc);
2415220667Sbschmidt
2416220667Sbschmidt	/* Force automatic TX power calibration every 60 secs. */
2417220667Sbschmidt	if (++sc->calib_cnt >= 120) {
2418220667Sbschmidt		uint32_t flags = 0;
2419220667Sbschmidt
2420220667Sbschmidt		DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s\n",
2421220667Sbschmidt		    "sending request for statistics");
2422220667Sbschmidt		(void)iwn_cmd(sc, IWN_CMD_GET_STATISTICS, &flags,
2423220667Sbschmidt		    sizeof flags, 1);
2424220667Sbschmidt		sc->calib_cnt = 0;
2425220667Sbschmidt	}
2426220667Sbschmidt	callout_reset(&sc->calib_to, msecs_to_ticks(500), iwn_calib_timeout,
2427220667Sbschmidt	    sc);
2428220667Sbschmidt}
2429220667Sbschmidt
2430198429Srpaulo/*
2431198429Srpaulo * Process an RX_PHY firmware notification.  This is usually immediately
2432198429Srpaulo * followed by an MPDU_RX_DONE notification.
2433198429Srpaulo */
2434206477Sbschmidtstatic void
2435198429Srpauloiwn_rx_phy(struct iwn_softc *sc, struct iwn_rx_desc *desc,
2436198429Srpaulo    struct iwn_rx_data *data)
2437178676Ssam{
2438198429Srpaulo	struct iwn_rx_stat *stat = (struct iwn_rx_stat *)(desc + 1);
2439198429Srpaulo
2440198429Srpaulo	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: received PHY stats\n", __func__);
2441202986Srpaulo	bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD);
2442198429Srpaulo
2443198429Srpaulo	/* Save RX statistics, they will be used on MPDU_RX_DONE. */
2444198429Srpaulo	memcpy(&sc->last_rx_stat, stat, sizeof (*stat));
2445198429Srpaulo	sc->last_rx_valid = 1;
2446178676Ssam}
2447178676Ssam
2448198429Srpaulo/*
2449198429Srpaulo * Process an RX_DONE (4965AGN only) or MPDU_RX_DONE firmware notification.
2450198429Srpaulo * Each MPDU_RX_DONE notification must be preceded by an RX_PHY one.
2451198429Srpaulo */
2452206477Sbschmidtstatic void
2453198429Srpauloiwn_rx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc,
2454178676Ssam    struct iwn_rx_data *data)
2455178676Ssam{
2456220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
2457178676Ssam	struct ifnet *ifp = sc->sc_ifp;
2458178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
2459178676Ssam	struct iwn_rx_ring *ring = &sc->rxq;
2460178676Ssam	struct ieee80211_frame *wh;
2461178676Ssam	struct ieee80211_node *ni;
2462198429Srpaulo	struct mbuf *m, *m1;
2463178676Ssam	struct iwn_rx_stat *stat;
2464178676Ssam	caddr_t head;
2465178676Ssam	bus_addr_t paddr;
2466198429Srpaulo	uint32_t flags;
2467198429Srpaulo	int error, len, rssi, nf;
2468178676Ssam
2469253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
2470253705Sadrian
2471198429Srpaulo	if (desc->type == IWN_MPDU_RX_DONE) {
2472198429Srpaulo		/* Check for prior RX_PHY notification. */
2473178676Ssam		if (!sc->last_rx_valid) {
2474178676Ssam			DPRINTF(sc, IWN_DEBUG_ANY,
2475201209Srpaulo			    "%s: missing RX_PHY\n", __func__);
2476178676Ssam			return;
2477178676Ssam		}
2478178676Ssam		stat = &sc->last_rx_stat;
2479178676Ssam	} else
2480178676Ssam		stat = (struct iwn_rx_stat *)(desc + 1);
2481178676Ssam
2482201209Srpaulo	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD);
2483198439Srpaulo
2484178676Ssam	if (stat->cfg_phy_len > IWN_STAT_MAXLEN) {
2485178676Ssam		device_printf(sc->sc_dev,
2486220724Sbschmidt		    "%s: invalid RX statistic header, len %d\n", __func__,
2487220724Sbschmidt		    stat->cfg_phy_len);
2488178676Ssam		return;
2489178676Ssam	}
2490198429Srpaulo	if (desc->type == IWN_MPDU_RX_DONE) {
2491198429Srpaulo		struct iwn_rx_mpdu *mpdu = (struct iwn_rx_mpdu *)(desc + 1);
2492198429Srpaulo		head = (caddr_t)(mpdu + 1);
2493198429Srpaulo		len = le16toh(mpdu->len);
2494178676Ssam	} else {
2495178676Ssam		head = (caddr_t)(stat + 1) + stat->cfg_phy_len;
2496178676Ssam		len = le16toh(stat->len);
2497178676Ssam	}
2498178676Ssam
2499198429Srpaulo	flags = le32toh(*(uint32_t *)(head + len));
2500198429Srpaulo
2501198429Srpaulo	/* Discard frames with a bad FCS early. */
2502198429Srpaulo	if ((flags & IWN_RX_NOERROR) != IWN_RX_NOERROR) {
2503220724Sbschmidt		DPRINTF(sc, IWN_DEBUG_RECV, "%s: RX flags error %x\n",
2504198429Srpaulo		    __func__, flags);
2505178676Ssam		ifp->if_ierrors++;
2506178676Ssam		return;
2507178676Ssam	}
2508198429Srpaulo	/* Discard frames that are too short. */
2509198429Srpaulo	if (len < sizeof (*wh)) {
2510178676Ssam		DPRINTF(sc, IWN_DEBUG_RECV, "%s: frame too short: %d\n",
2511178676Ssam		    __func__, len);
2512178676Ssam		ifp->if_ierrors++;
2513178676Ssam		return;
2514178676Ssam	}
2515178676Ssam
2516243857Sglebius	m1 = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, IWN_RBUF_SIZE);
2517198429Srpaulo	if (m1 == NULL) {
2518178676Ssam		DPRINTF(sc, IWN_DEBUG_ANY, "%s: no mbuf to restock ring\n",
2519178676Ssam		    __func__);
2520178676Ssam		ifp->if_ierrors++;
2521178676Ssam		return;
2522178676Ssam	}
2523201209Srpaulo	bus_dmamap_unload(ring->data_dmat, data->map);
2524201209Srpaulo
2525220692Sbschmidt	error = bus_dmamap_load(ring->data_dmat, data->map, mtod(m1, void *),
2526220692Sbschmidt	    IWN_RBUF_SIZE, iwn_dma_map_addr, &paddr, BUS_DMA_NOWAIT);
2527178676Ssam	if (error != 0 && error != EFBIG) {
2528178676Ssam		device_printf(sc->sc_dev,
2529178676Ssam		    "%s: bus_dmamap_load failed, error %d\n", __func__, error);
2530198429Srpaulo		m_freem(m1);
2531220693Sbschmidt
2532220693Sbschmidt		/* Try to reload the old mbuf. */
2533220693Sbschmidt		error = bus_dmamap_load(ring->data_dmat, data->map,
2534220693Sbschmidt		    mtod(data->m, void *), IWN_RBUF_SIZE, iwn_dma_map_addr,
2535220693Sbschmidt		    &paddr, BUS_DMA_NOWAIT);
2536220693Sbschmidt		if (error != 0 && error != EFBIG) {
2537220693Sbschmidt			panic("%s: could not load old RX mbuf", __func__);
2538220693Sbschmidt		}
2539220693Sbschmidt		/* Physical address may have changed. */
2540220693Sbschmidt		ring->desc[ring->cur] = htole32(paddr >> 8);
2541220693Sbschmidt		bus_dmamap_sync(ring->data_dmat, ring->desc_dma.map,
2542220693Sbschmidt		    BUS_DMASYNC_PREWRITE);
2543178676Ssam		ifp->if_ierrors++;
2544178676Ssam		return;
2545178676Ssam	}
2546178676Ssam
2547178676Ssam	m = data->m;
2548198429Srpaulo	data->m = m1;
2549198429Srpaulo	/* Update RX descriptor. */
2550198429Srpaulo	ring->desc[ring->cur] = htole32(paddr >> 8);
2551201209Srpaulo	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
2552201209Srpaulo	    BUS_DMASYNC_PREWRITE);
2553198429Srpaulo
2554198429Srpaulo	/* Finalize mbuf. */
2555178676Ssam	m->m_pkthdr.rcvif = ifp;
2556178676Ssam	m->m_data = head;
2557178676Ssam	m->m_pkthdr.len = m->m_len = len;
2558178676Ssam
2559198429Srpaulo	/* Grab a reference to the source node. */
2560178676Ssam	wh = mtod(m, struct ieee80211_frame *);
2561178676Ssam	ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh);
2562178676Ssam	nf = (ni != NULL && ni->ni_vap->iv_state == IEEE80211_S_RUN &&
2563178676Ssam	    (ic->ic_flags & IEEE80211_F_SCAN) == 0) ? sc->noise : -95;
2564178676Ssam
2565220728Sbschmidt	rssi = ops->get_rssi(sc, stat);
2566220689Sbschmidt
2567192468Ssam	if (ieee80211_radiotap_active(ic)) {
2568178676Ssam		struct iwn_rx_radiotap_header *tap = &sc->sc_rxtap;
2569178676Ssam
2570178676Ssam		tap->wr_flags = 0;
2571201209Srpaulo		if (stat->flags & htole16(IWN_STAT_FLAG_SHPREAMBLE))
2572192468Ssam			tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
2573220723Sbschmidt		tap->wr_dbm_antsignal = (int8_t)rssi;
2574220723Sbschmidt		tap->wr_dbm_antnoise = (int8_t)nf;
2575220723Sbschmidt		tap->wr_tsft = stat->tstamp;
2576201209Srpaulo		switch (stat->rate) {
2577201209Srpaulo		/* CCK rates. */
2578201209Srpaulo		case  10: tap->wr_rate =   2; break;
2579201209Srpaulo		case  20: tap->wr_rate =   4; break;
2580201209Srpaulo		case  55: tap->wr_rate =  11; break;
2581201209Srpaulo		case 110: tap->wr_rate =  22; break;
2582201209Srpaulo		/* OFDM rates. */
2583201209Srpaulo		case 0xd: tap->wr_rate =  12; break;
2584201209Srpaulo		case 0xf: tap->wr_rate =  18; break;
2585201209Srpaulo		case 0x5: tap->wr_rate =  24; break;
2586201209Srpaulo		case 0x7: tap->wr_rate =  36; break;
2587201209Srpaulo		case 0x9: tap->wr_rate =  48; break;
2588201209Srpaulo		case 0xb: tap->wr_rate =  72; break;
2589201209Srpaulo		case 0x1: tap->wr_rate =  96; break;
2590201209Srpaulo		case 0x3: tap->wr_rate = 108; break;
2591201209Srpaulo		/* Unknown rate: should not happen. */
2592201209Srpaulo		default:  tap->wr_rate =   0;
2593201209Srpaulo		}
2594178676Ssam	}
2595178676Ssam
2596178676Ssam	IWN_UNLOCK(sc);
2597178676Ssam
2598198429Srpaulo	/* Send the frame to the 802.11 layer. */
2599178676Ssam	if (ni != NULL) {
2600221650Sbschmidt		if (ni->ni_flags & IEEE80211_NODE_HT)
2601221650Sbschmidt			m->m_flags |= M_AMPDU;
2602220726Sbschmidt		(void)ieee80211_input(ni, m, rssi - nf, nf);
2603198429Srpaulo		/* Node is no longer needed. */
2604178676Ssam		ieee80211_free_node(ni);
2605178676Ssam	} else
2606220726Sbschmidt		(void)ieee80211_input_all(ic, m, rssi - nf, nf);
2607178676Ssam
2608178676Ssam	IWN_LOCK(sc);
2609253705Sadrian
2610253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
2611253705Sadrian
2612178676Ssam}
2613178676Ssam
2614201209Srpaulo/* Process an incoming Compressed BlockAck. */
2615206477Sbschmidtstatic void
2616201209Srpauloiwn_rx_compressed_ba(struct iwn_softc *sc, struct iwn_rx_desc *desc,
2617201209Srpaulo    struct iwn_rx_data *data)
2618201209Srpaulo{
2619237649Sbschmidt	struct iwn_ops *ops = &sc->ops;
2620221651Sbschmidt	struct ifnet *ifp = sc->sc_ifp;
2621221651Sbschmidt	struct iwn_node *wn;
2622221651Sbschmidt	struct ieee80211_node *ni;
2623201209Srpaulo	struct iwn_compressed_ba *ba = (struct iwn_compressed_ba *)(desc + 1);
2624201209Srpaulo	struct iwn_tx_ring *txq;
2625237647Sbschmidt	struct iwn_tx_data *txdata;
2626221651Sbschmidt	struct ieee80211_tx_ampdu *tap;
2627237647Sbschmidt	struct mbuf *m;
2628221651Sbschmidt	uint64_t bitmap;
2629237649Sbschmidt	uint16_t ssn;
2630221651Sbschmidt	uint8_t tid;
2631237649Sbschmidt	int ackfailcnt = 0, i, lastidx, qid, *res, shift;
2632201209Srpaulo
2633253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
2634253705Sadrian
2635220704Sbschmidt	bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD);
2636220704Sbschmidt
2637237647Sbschmidt	qid = le16toh(ba->qid);
2638237647Sbschmidt	txq = &sc->txq[ba->qid];
2639237647Sbschmidt	tap = sc->qid2tap[ba->qid];
2640234324Sadrian	tid = tap->txa_tid;
2641237647Sbschmidt	wn = (void *)tap->txa_ni;
2642221651Sbschmidt
2643237649Sbschmidt	res = NULL;
2644237649Sbschmidt	ssn = 0;
2645237649Sbschmidt	if (!IEEE80211_AMPDU_RUNNING(tap)) {
2646237649Sbschmidt		res = tap->txa_private;
2647237649Sbschmidt		ssn = tap->txa_start & 0xfff;
2648237649Sbschmidt	}
2649237649Sbschmidt
2650237647Sbschmidt	for (lastidx = le16toh(ba->ssn) & 0xff; txq->read != lastidx;) {
2651237647Sbschmidt		txdata = &txq->data[txq->read];
2652237647Sbschmidt
2653237647Sbschmidt		/* Unmap and free mbuf. */
2654237647Sbschmidt		bus_dmamap_sync(txq->data_dmat, txdata->map,
2655237647Sbschmidt		    BUS_DMASYNC_POSTWRITE);
2656237647Sbschmidt		bus_dmamap_unload(txq->data_dmat, txdata->map);
2657237647Sbschmidt		m = txdata->m, txdata->m = NULL;
2658237647Sbschmidt		ni = txdata->ni, txdata->ni = NULL;
2659237647Sbschmidt
2660237647Sbschmidt		KASSERT(ni != NULL, ("no node"));
2661237647Sbschmidt		KASSERT(m != NULL, ("no mbuf"));
2662237647Sbschmidt
2663255023Sadrian		ieee80211_tx_complete(ni, m, 1);
2664237647Sbschmidt
2665237647Sbschmidt		txq->queued--;
2666237647Sbschmidt		txq->read = (txq->read + 1) % IWN_TX_RING_COUNT;
2667237647Sbschmidt	}
2668237647Sbschmidt
2669237649Sbschmidt	if (txq->queued == 0 && res != NULL) {
2670237649Sbschmidt		iwn_nic_lock(sc);
2671237649Sbschmidt		ops->ampdu_tx_stop(sc, qid, tid, ssn);
2672237649Sbschmidt		iwn_nic_unlock(sc);
2673237649Sbschmidt		sc->qid2tap[qid] = NULL;
2674237649Sbschmidt		free(res, M_DEVBUF);
2675237649Sbschmidt		return;
2676237649Sbschmidt	}
2677237649Sbschmidt
2678221651Sbschmidt	if (wn->agg[tid].bitmap == 0)
2679221651Sbschmidt		return;
2680221651Sbschmidt
2681221651Sbschmidt	shift = wn->agg[tid].startidx - ((le16toh(ba->seq) >> 4) & 0xff);
2682221651Sbschmidt	if (shift < 0)
2683221651Sbschmidt		shift += 0x100;
2684221651Sbschmidt
2685221651Sbschmidt	if (wn->agg[tid].nframes > (64 - shift))
2686221651Sbschmidt		return;
2687221651Sbschmidt
2688237647Sbschmidt	ni = tap->txa_ni;
2689221651Sbschmidt	bitmap = (le64toh(ba->bitmap) >> shift) & wn->agg[tid].bitmap;
2690221651Sbschmidt	for (i = 0; bitmap; i++) {
2691221651Sbschmidt		if ((bitmap & 1) == 0) {
2692221651Sbschmidt			ifp->if_oerrors++;
2693221651Sbschmidt			ieee80211_ratectl_tx_complete(ni->ni_vap, ni,
2694221651Sbschmidt			    IEEE80211_RATECTL_TX_FAILURE, &ackfailcnt, NULL);
2695221651Sbschmidt		} else {
2696221651Sbschmidt			ifp->if_opackets++;
2697221651Sbschmidt			ieee80211_ratectl_tx_complete(ni->ni_vap, ni,
2698221651Sbschmidt			    IEEE80211_RATECTL_TX_SUCCESS, &ackfailcnt, NULL);
2699221651Sbschmidt		}
2700221651Sbschmidt		bitmap >>= 1;
2701221651Sbschmidt	}
2702253705Sadrian
2703253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
2704253705Sadrian
2705201209Srpaulo}
2706201209Srpaulo
2707198429Srpaulo/*
2708220674Sbschmidt * Process a CALIBRATION_RESULT notification sent by the initialization
2709220674Sbschmidt * firmware on response to a CMD_CALIB_CONFIG command (5000 only).
2710220674Sbschmidt */
2711220674Sbschmidtstatic void
2712220674Sbschmidtiwn5000_rx_calib_results(struct iwn_softc *sc, struct iwn_rx_desc *desc,
2713220674Sbschmidt    struct iwn_rx_data *data)
2714220674Sbschmidt{
2715220674Sbschmidt	struct iwn_phy_calib *calib = (struct iwn_phy_calib *)(desc + 1);
2716220674Sbschmidt	int len, idx = -1;
2717220674Sbschmidt
2718253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
2719253705Sadrian
2720220674Sbschmidt	/* Runtime firmware should not send such a notification. */
2721253705Sadrian	if (sc->sc_flags & IWN_FLAG_CALIB_DONE){
2722253705Sadrian		DPRINTF(sc, IWN_DEBUG_TRACE, "->%s received after clib done\n",
2723253705Sadrian	    __func__);
2724220674Sbschmidt		return;
2725253705Sadrian	}
2726220674Sbschmidt	len = (le32toh(desc->len) & 0x3fff) - 4;
2727220674Sbschmidt	bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD);
2728220674Sbschmidt
2729220674Sbschmidt	switch (calib->code) {
2730220674Sbschmidt	case IWN5000_PHY_CALIB_DC:
2731220867Sbschmidt		if ((sc->sc_flags & IWN_FLAG_INTERNAL_PA) == 0 &&
2732220867Sbschmidt		    (sc->hw_type == IWN_HW_REV_TYPE_5150 ||
2733227805Sbschmidt		     sc->hw_type >= IWN_HW_REV_TYPE_6000) &&
2734227805Sbschmidt		     sc->hw_type != IWN_HW_REV_TYPE_6050)
2735220674Sbschmidt			idx = 0;
2736220674Sbschmidt		break;
2737220674Sbschmidt	case IWN5000_PHY_CALIB_LO:
2738220674Sbschmidt		idx = 1;
2739220674Sbschmidt		break;
2740220674Sbschmidt	case IWN5000_PHY_CALIB_TX_IQ:
2741220674Sbschmidt		idx = 2;
2742220674Sbschmidt		break;
2743220674Sbschmidt	case IWN5000_PHY_CALIB_TX_IQ_PERIODIC:
2744220674Sbschmidt		if (sc->hw_type < IWN_HW_REV_TYPE_6000 &&
2745220674Sbschmidt		    sc->hw_type != IWN_HW_REV_TYPE_5150)
2746220674Sbschmidt			idx = 3;
2747220674Sbschmidt		break;
2748220674Sbschmidt	case IWN5000_PHY_CALIB_BASE_BAND:
2749220674Sbschmidt		idx = 4;
2750220674Sbschmidt		break;
2751220674Sbschmidt	}
2752220674Sbschmidt	if (idx == -1)	/* Ignore other results. */
2753220674Sbschmidt		return;
2754220674Sbschmidt
2755220674Sbschmidt	/* Save calibration result. */
2756220674Sbschmidt	if (sc->calibcmd[idx].buf != NULL)
2757220674Sbschmidt		free(sc->calibcmd[idx].buf, M_DEVBUF);
2758220674Sbschmidt	sc->calibcmd[idx].buf = malloc(len, M_DEVBUF, M_NOWAIT);
2759220674Sbschmidt	if (sc->calibcmd[idx].buf == NULL) {
2760220674Sbschmidt		DPRINTF(sc, IWN_DEBUG_CALIBRATE,
2761220674Sbschmidt		    "not enough memory for calibration result %d\n",
2762220674Sbschmidt		    calib->code);
2763220674Sbschmidt		return;
2764220674Sbschmidt	}
2765220674Sbschmidt	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
2766220674Sbschmidt	    "saving calibration result code=%d len=%d\n", calib->code, len);
2767220674Sbschmidt	sc->calibcmd[idx].len = len;
2768220674Sbschmidt	memcpy(sc->calibcmd[idx].buf, calib, len);
2769220674Sbschmidt}
2770220674Sbschmidt
2771220674Sbschmidt/*
2772198429Srpaulo * Process an RX_STATISTICS or BEACON_STATISTICS firmware notification.
2773198429Srpaulo * The latter is sent by the firmware after each received beacon.
2774198429Srpaulo */
2775206477Sbschmidtstatic void
2776198429Srpauloiwn_rx_statistics(struct iwn_softc *sc, struct iwn_rx_desc *desc,
2777198429Srpaulo    struct iwn_rx_data *data)
2778198429Srpaulo{
2779220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
2780178676Ssam	struct ifnet *ifp = sc->sc_ifp;
2781178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
2782178676Ssam	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
2783178676Ssam	struct iwn_calib_state *calib = &sc->calib;
2784178676Ssam	struct iwn_stats *stats = (struct iwn_stats *)(desc + 1);
2785198429Srpaulo	int temp;
2786178676Ssam
2787253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
2788253705Sadrian
2789220725Sbschmidt	/* Ignore statistics received during a scan. */
2790178676Ssam	if (vap->iv_state != IEEE80211_S_RUN ||
2791253705Sadrian	    (ic->ic_flags & IEEE80211_F_SCAN)){
2792253705Sadrian		DPRINTF(sc, IWN_DEBUG_TRACE, "->%s received during calib\n",
2793253705Sadrian	    __func__);
2794178676Ssam		return;
2795253705Sadrian	}
2796178676Ssam
2797202986Srpaulo	bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD);
2798220724Sbschmidt
2799220724Sbschmidt	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: received statistics, cmd %d\n",
2800220724Sbschmidt	    __func__, desc->type);
2801220667Sbschmidt	sc->calib_cnt = 0;	/* Reset TX power calibration timeout. */
2802178676Ssam
2803198429Srpaulo	/* Test if temperature has changed. */
2804178676Ssam	if (stats->general.temp != sc->rawtemp) {
2805198429Srpaulo		/* Convert "raw" temperature to degC. */
2806178676Ssam		sc->rawtemp = stats->general.temp;
2807220728Sbschmidt		temp = ops->get_temperature(sc);
2808178676Ssam		DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: temperature %d\n",
2809178676Ssam		    __func__, temp);
2810178676Ssam
2811220725Sbschmidt		/* Update TX power if need be (4965AGN only). */
2812198429Srpaulo		if (sc->hw_type == IWN_HW_REV_TYPE_4965)
2813198429Srpaulo			iwn4965_power_calibration(sc, temp);
2814178676Ssam	}
2815178676Ssam
2816178676Ssam	if (desc->type != IWN_BEACON_STATISTICS)
2817198429Srpaulo		return;	/* Reply to a statistics request. */
2818178676Ssam
2819178676Ssam	sc->noise = iwn_get_noise(&stats->rx.general);
2820178676Ssam	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: noise %d\n", __func__, sc->noise);
2821178676Ssam
2822198429Srpaulo	/* Test that RSSI and noise are present in stats report. */
2823198429Srpaulo	if (le32toh(stats->rx.general.flags) != 1) {
2824178676Ssam		DPRINTF(sc, IWN_DEBUG_ANY, "%s\n",
2825178676Ssam		    "received statistics without RSSI");
2826178676Ssam		return;
2827178676Ssam	}
2828178676Ssam
2829178676Ssam	if (calib->state == IWN_CALIB_STATE_ASSOC)
2830198429Srpaulo		iwn_collect_noise(sc, &stats->rx.general);
2831178676Ssam	else if (calib->state == IWN_CALIB_STATE_RUN)
2832178676Ssam		iwn_tune_sensitivity(sc, &stats->rx);
2833253705Sadrian
2834253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
2835178676Ssam}
2836178676Ssam
2837198429Srpaulo/*
2838198429Srpaulo * Process a TX_DONE firmware notification.  Unfortunately, the 4965AGN
2839198429Srpaulo * and 5000 adapters have different incompatible TX status formats.
2840198429Srpaulo */
2841206477Sbschmidtstatic void
2842198429Srpauloiwn4965_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc,
2843198429Srpaulo    struct iwn_rx_data *data)
2844178676Ssam{
2845198429Srpaulo	struct iwn4965_tx_stat *stat = (struct iwn4965_tx_stat *)(desc + 1);
2846221651Sbschmidt	struct iwn_tx_ring *ring;
2847221651Sbschmidt	int qid;
2848198429Srpaulo
2849221651Sbschmidt	qid = desc->qid & 0xf;
2850221651Sbschmidt	ring = &sc->txq[qid];
2851221651Sbschmidt
2852198429Srpaulo	DPRINTF(sc, IWN_DEBUG_XMIT, "%s: "
2853198429Srpaulo	    "qid %d idx %d retries %d nkill %d rate %x duration %d status %x\n",
2854201209Srpaulo	    __func__, desc->qid, desc->idx, stat->ackfailcnt,
2855201209Srpaulo	    stat->btkillcnt, stat->rate, le16toh(stat->duration),
2856198429Srpaulo	    le32toh(stat->status));
2857201209Srpaulo
2858207001Sbschmidt	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD);
2859221651Sbschmidt	if (qid >= sc->firstaggqueue) {
2860221651Sbschmidt		iwn_ampdu_tx_done(sc, qid, desc->idx, stat->nframes,
2861221651Sbschmidt		    &stat->status);
2862221651Sbschmidt	} else {
2863221651Sbschmidt		iwn_tx_done(sc, desc, stat->ackfailcnt,
2864221651Sbschmidt		    le32toh(stat->status) & 0xff);
2865221651Sbschmidt	}
2866198429Srpaulo}
2867198429Srpaulo
2868206477Sbschmidtstatic void
2869198429Srpauloiwn5000_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc,
2870198429Srpaulo    struct iwn_rx_data *data)
2871198429Srpaulo{
2872198429Srpaulo	struct iwn5000_tx_stat *stat = (struct iwn5000_tx_stat *)(desc + 1);
2873221651Sbschmidt	struct iwn_tx_ring *ring;
2874221651Sbschmidt	int qid;
2875198429Srpaulo
2876221651Sbschmidt	qid = desc->qid & 0xf;
2877221651Sbschmidt	ring = &sc->txq[qid];
2878221651Sbschmidt
2879198429Srpaulo	DPRINTF(sc, IWN_DEBUG_XMIT, "%s: "
2880198429Srpaulo	    "qid %d idx %d retries %d nkill %d rate %x duration %d status %x\n",
2881201209Srpaulo	    __func__, desc->qid, desc->idx, stat->ackfailcnt,
2882201209Srpaulo	    stat->btkillcnt, stat->rate, le16toh(stat->duration),
2883198429Srpaulo	    le32toh(stat->status));
2884198429Srpaulo
2885201209Srpaulo#ifdef notyet
2886198429Srpaulo	/* Reset TX scheduler slot. */
2887198429Srpaulo	iwn5000_reset_sched(sc, desc->qid & 0xf, desc->idx);
2888201209Srpaulo#endif
2889202986Srpaulo
2890207001Sbschmidt	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD);
2891221651Sbschmidt	if (qid >= sc->firstaggqueue) {
2892221651Sbschmidt		iwn_ampdu_tx_done(sc, qid, desc->idx, stat->nframes,
2893221651Sbschmidt		    &stat->status);
2894221651Sbschmidt	} else {
2895221651Sbschmidt		iwn_tx_done(sc, desc, stat->ackfailcnt,
2896221651Sbschmidt		    le16toh(stat->status) & 0xff);
2897221651Sbschmidt	}
2898198429Srpaulo}
2899198429Srpaulo
2900198429Srpaulo/*
2901198429Srpaulo * Adapter-independent backend for TX_DONE firmware notifications.
2902198429Srpaulo */
2903206477Sbschmidtstatic void
2904201209Srpauloiwn_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, int ackfailcnt,
2905198429Srpaulo    uint8_t status)
2906198429Srpaulo{
2907178676Ssam	struct ifnet *ifp = sc->sc_ifp;
2908178676Ssam	struct iwn_tx_ring *ring = &sc->txq[desc->qid & 0xf];
2909178676Ssam	struct iwn_tx_data *data = &ring->data[desc->idx];
2910178676Ssam	struct mbuf *m;
2911178676Ssam	struct ieee80211_node *ni;
2912206358Srpaulo	struct ieee80211vap *vap;
2913178676Ssam
2914178676Ssam	KASSERT(data->ni != NULL, ("no node"));
2915178676Ssam
2916253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
2917253705Sadrian
2918198429Srpaulo	/* Unmap and free mbuf. */
2919201209Srpaulo	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTWRITE);
2920201209Srpaulo	bus_dmamap_unload(ring->data_dmat, data->map);
2921178676Ssam	m = data->m, data->m = NULL;
2922178676Ssam	ni = data->ni, data->ni = NULL;
2923206358Srpaulo	vap = ni->ni_vap;
2924178676Ssam
2925201209Srpaulo	/*
2926201209Srpaulo	 * Update rate control statistics for the node.
2927201209Srpaulo	 */
2928220719Sbschmidt	if (status & IWN_TX_FAIL) {
2929201209Srpaulo		ifp->if_oerrors++;
2930206358Srpaulo		ieee80211_ratectl_tx_complete(vap, ni,
2931206358Srpaulo		    IEEE80211_RATECTL_TX_FAILURE, &ackfailcnt, NULL);
2932201209Srpaulo	} else {
2933220719Sbschmidt		ifp->if_opackets++;
2934206358Srpaulo		ieee80211_ratectl_tx_complete(vap, ni,
2935206358Srpaulo		    IEEE80211_RATECTL_TX_SUCCESS, &ackfailcnt, NULL);
2936201209Srpaulo	}
2937178676Ssam
2938255023Sadrian	/*
2939255023Sadrian	 * Channels marked for "radar" require traffic to be received
2940255023Sadrian	 * to unlock before we can transmit.  Until traffic is seen
2941255023Sadrian	 * any attempt to transmit is returned immediately with status
2942255023Sadrian	 * set to IWN_TX_FAIL_TX_LOCKED.  Unfortunately this can easily
2943255023Sadrian	 * happen on first authenticate after scanning.  To workaround
2944255023Sadrian	 * this we ignore a failure of this sort in AUTH state so the
2945255023Sadrian	 * 802.11 layer will fall back to using a timeout to wait for
2946255023Sadrian	 * the AUTH reply.  This allows the firmware time to see
2947255023Sadrian	 * traffic so a subsequent retry of AUTH succeeds.  It's
2948255023Sadrian	 * unclear why the firmware does not maintain state for
2949255023Sadrian	 * channels recently visited as this would allow immediate
2950255023Sadrian	 * use of the channel after a scan (where we see traffic).
2951255023Sadrian	 */
2952255023Sadrian	if (status == IWN_TX_FAIL_TX_LOCKED &&
2953255023Sadrian	    ni->ni_vap->iv_state == IEEE80211_S_AUTH)
2954255023Sadrian		ieee80211_tx_complete(ni, m, 0);
2955255023Sadrian	else
2956255023Sadrian		ieee80211_tx_complete(ni, m,
2957255023Sadrian		    (status & IWN_TX_FAIL) != 0);
2958255023Sadrian
2959178676Ssam	sc->sc_tx_timer = 0;
2960198429Srpaulo	if (--ring->queued < IWN_TX_RING_LOMARK) {
2961198429Srpaulo		sc->qfullmsk &= ~(1 << ring->qid);
2962198429Srpaulo		if (sc->qfullmsk == 0 &&
2963198429Srpaulo		    (ifp->if_drv_flags & IFF_DRV_OACTIVE)) {
2964198429Srpaulo			ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
2965198429Srpaulo			iwn_start_locked(ifp);
2966198429Srpaulo		}
2967198429Srpaulo	}
2968253705Sadrian
2969253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
2970253705Sadrian
2971178676Ssam}
2972178676Ssam
2973198429Srpaulo/*
2974198429Srpaulo * Process a "command done" firmware notification.  This is where we wakeup
2975198429Srpaulo * processes waiting for a synchronous command completion.
2976198429Srpaulo */
2977206477Sbschmidtstatic void
2978198429Srpauloiwn_cmd_done(struct iwn_softc *sc, struct iwn_rx_desc *desc)
2979178676Ssam{
2980178676Ssam	struct iwn_tx_ring *ring = &sc->txq[4];
2981178676Ssam	struct iwn_tx_data *data;
2982178676Ssam
2983178676Ssam	if ((desc->qid & 0xf) != 4)
2984198429Srpaulo		return;	/* Not a command ack. */
2985178676Ssam
2986178676Ssam	data = &ring->data[desc->idx];
2987178676Ssam
2988198429Srpaulo	/* If the command was mapped in an mbuf, free it. */
2989178676Ssam	if (data->m != NULL) {
2990220704Sbschmidt		bus_dmamap_sync(ring->data_dmat, data->map,
2991220704Sbschmidt		    BUS_DMASYNC_POSTWRITE);
2992201209Srpaulo		bus_dmamap_unload(ring->data_dmat, data->map);
2993178676Ssam		m_freem(data->m);
2994178676Ssam		data->m = NULL;
2995178676Ssam	}
2996198439Srpaulo	wakeup(&ring->desc[desc->idx]);
2997178676Ssam}
2998178676Ssam
2999221651Sbschmidtstatic void
3000221651Sbschmidtiwn_ampdu_tx_done(struct iwn_softc *sc, int qid, int idx, int nframes,
3001221651Sbschmidt    void *stat)
3002221651Sbschmidt{
3003237649Sbschmidt	struct iwn_ops *ops = &sc->ops;
3004221651Sbschmidt	struct ifnet *ifp = sc->sc_ifp;
3005221651Sbschmidt	struct iwn_tx_ring *ring = &sc->txq[qid];
3006221651Sbschmidt	struct iwn_tx_data *data;
3007221651Sbschmidt	struct mbuf *m;
3008221651Sbschmidt	struct iwn_node *wn;
3009221651Sbschmidt	struct ieee80211_node *ni;
3010221651Sbschmidt	struct ieee80211_tx_ampdu *tap;
3011221651Sbschmidt	uint64_t bitmap;
3012221651Sbschmidt	uint32_t *status = stat;
3013221651Sbschmidt	uint16_t *aggstatus = stat;
3014237649Sbschmidt	uint16_t ssn;
3015221651Sbschmidt	uint8_t tid;
3016237649Sbschmidt	int bit, i, lastidx, *res, seqno, shift, start;
3017221651Sbschmidt
3018253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
3019253705Sadrian
3020221651Sbschmidt#ifdef NOT_YET
3021221651Sbschmidt	if (nframes == 1) {
3022221651Sbschmidt		if ((*status & 0xff) != 1 && (*status & 0xff) != 2)
3023221651Sbschmidt			printf("ieee80211_send_bar()\n");
3024221651Sbschmidt	}
3025221651Sbschmidt#endif
3026221651Sbschmidt
3027221651Sbschmidt	bitmap = 0;
3028221651Sbschmidt	start = idx;
3029221651Sbschmidt	for (i = 0; i < nframes; i++) {
3030221651Sbschmidt		if (le16toh(aggstatus[i * 2]) & 0xc)
3031221651Sbschmidt			continue;
3032221651Sbschmidt
3033221651Sbschmidt		idx = le16toh(aggstatus[2*i + 1]) & 0xff;
3034221651Sbschmidt		bit = idx - start;
3035221651Sbschmidt		shift = 0;
3036221651Sbschmidt		if (bit >= 64) {
3037221651Sbschmidt			shift = 0x100 - idx + start;
3038221651Sbschmidt			bit = 0;
3039221651Sbschmidt			start = idx;
3040221651Sbschmidt		} else if (bit <= -64)
3041221651Sbschmidt			bit = 0x100 - start + idx;
3042221651Sbschmidt		else if (bit < 0) {
3043221651Sbschmidt			shift = start - idx;
3044221651Sbschmidt			start = idx;
3045221651Sbschmidt			bit = 0;
3046221651Sbschmidt		}
3047221651Sbschmidt		bitmap = bitmap << shift;
3048221651Sbschmidt		bitmap |= 1ULL << bit;
3049221651Sbschmidt	}
3050221651Sbschmidt	tap = sc->qid2tap[qid];
3051237649Sbschmidt	tid = tap->txa_tid;
3052237649Sbschmidt	wn = (void *)tap->txa_ni;
3053237649Sbschmidt	wn->agg[tid].bitmap = bitmap;
3054237649Sbschmidt	wn->agg[tid].startidx = start;
3055237649Sbschmidt	wn->agg[tid].nframes = nframes;
3056237649Sbschmidt
3057237649Sbschmidt	res = NULL;
3058237649Sbschmidt	ssn = 0;
3059237649Sbschmidt	if (!IEEE80211_AMPDU_RUNNING(tap)) {
3060237649Sbschmidt		res = tap->txa_private;
3061237649Sbschmidt		ssn = tap->txa_start & 0xfff;
3062230620Sbschmidt	}
3063221651Sbschmidt
3064221651Sbschmidt	seqno = le32toh(*(status + nframes)) & 0xfff;
3065221651Sbschmidt	for (lastidx = (seqno & 0xff); ring->read != lastidx;) {
3066221651Sbschmidt		data = &ring->data[ring->read];
3067221651Sbschmidt
3068221651Sbschmidt		/* Unmap and free mbuf. */
3069221651Sbschmidt		bus_dmamap_sync(ring->data_dmat, data->map,
3070221651Sbschmidt		    BUS_DMASYNC_POSTWRITE);
3071221651Sbschmidt		bus_dmamap_unload(ring->data_dmat, data->map);
3072221651Sbschmidt		m = data->m, data->m = NULL;
3073221651Sbschmidt		ni = data->ni, data->ni = NULL;
3074221651Sbschmidt
3075237647Sbschmidt		KASSERT(ni != NULL, ("no node"));
3076237647Sbschmidt		KASSERT(m != NULL, ("no mbuf"));
3077237647Sbschmidt
3078255023Sadrian		ieee80211_tx_complete(ni, m, 1);
3079221651Sbschmidt
3080221651Sbschmidt		ring->queued--;
3081221651Sbschmidt		ring->read = (ring->read + 1) % IWN_TX_RING_COUNT;
3082221651Sbschmidt	}
3083221651Sbschmidt
3084237649Sbschmidt	if (ring->queued == 0 && res != NULL) {
3085237649Sbschmidt		iwn_nic_lock(sc);
3086237649Sbschmidt		ops->ampdu_tx_stop(sc, qid, tid, ssn);
3087237649Sbschmidt		iwn_nic_unlock(sc);
3088237649Sbschmidt		sc->qid2tap[qid] = NULL;
3089237649Sbschmidt		free(res, M_DEVBUF);
3090237649Sbschmidt		return;
3091237649Sbschmidt	}
3092237649Sbschmidt
3093221651Sbschmidt	sc->sc_tx_timer = 0;
3094221651Sbschmidt	if (ring->queued < IWN_TX_RING_LOMARK) {
3095221651Sbschmidt		sc->qfullmsk &= ~(1 << ring->qid);
3096221651Sbschmidt		if (sc->qfullmsk == 0 &&
3097221651Sbschmidt		    (ifp->if_drv_flags & IFF_DRV_OACTIVE)) {
3098221651Sbschmidt			ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
3099221651Sbschmidt			iwn_start_locked(ifp);
3100221651Sbschmidt		}
3101221651Sbschmidt	}
3102253705Sadrian
3103253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
3104253705Sadrian
3105221651Sbschmidt}
3106221651Sbschmidt
3107198429Srpaulo/*
3108198429Srpaulo * Process an INT_FH_RX or INT_SW_RX interrupt.
3109198429Srpaulo */
3110206477Sbschmidtstatic void
3111178676Ssamiwn_notif_intr(struct iwn_softc *sc)
3112178676Ssam{
3113220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
3114178676Ssam	struct ifnet *ifp = sc->sc_ifp;
3115178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
3116178676Ssam	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
3117178676Ssam	uint16_t hw;
3118178676Ssam
3119198429Srpaulo	bus_dmamap_sync(sc->rxq.stat_dma.tag, sc->rxq.stat_dma.map,
3120198429Srpaulo	    BUS_DMASYNC_POSTREAD);
3121198429Srpaulo
3122198429Srpaulo	hw = le16toh(sc->rxq.stat->closed_count) & 0xfff;
3123178676Ssam	while (sc->rxq.cur != hw) {
3124178676Ssam		struct iwn_rx_data *data = &sc->rxq.data[sc->rxq.cur];
3125198429Srpaulo		struct iwn_rx_desc *desc;
3126178676Ssam
3127201209Srpaulo		bus_dmamap_sync(sc->rxq.data_dmat, data->map,
3128201209Srpaulo		    BUS_DMASYNC_POSTREAD);
3129198429Srpaulo		desc = mtod(data->m, struct iwn_rx_desc *);
3130198429Srpaulo
3131178676Ssam		DPRINTF(sc, IWN_DEBUG_RECV,
3132178676Ssam		    "%s: qid %x idx %d flags %x type %d(%s) len %d\n",
3133198439Srpaulo		    __func__, desc->qid & 0xf, desc->idx, desc->flags,
3134178676Ssam		    desc->type, iwn_intr_str(desc->type),
3135178676Ssam		    le16toh(desc->len));
3136178676Ssam
3137198429Srpaulo		if (!(desc->qid & 0x80))	/* Reply to a command. */
3138198429Srpaulo			iwn_cmd_done(sc, desc);
3139178676Ssam
3140178676Ssam		switch (desc->type) {
3141198429Srpaulo		case IWN_RX_PHY:
3142198429Srpaulo			iwn_rx_phy(sc, desc, data);
3143178676Ssam			break;
3144178676Ssam
3145198429Srpaulo		case IWN_RX_DONE:		/* 4965AGN only. */
3146198429Srpaulo		case IWN_MPDU_RX_DONE:
3147198429Srpaulo			/* An 802.11 frame has been received. */
3148198429Srpaulo			iwn_rx_done(sc, desc, data);
3149178676Ssam			break;
3150178676Ssam
3151201209Srpaulo		case IWN_RX_COMPRESSED_BA:
3152201209Srpaulo			/* A Compressed BlockAck has been received. */
3153201209Srpaulo			iwn_rx_compressed_ba(sc, desc, data);
3154201209Srpaulo			break;
3155201209Srpaulo
3156178676Ssam		case IWN_TX_DONE:
3157198429Srpaulo			/* An 802.11 frame has been transmitted. */
3158220728Sbschmidt			ops->tx_done(sc, desc, data);
3159178676Ssam			break;
3160178676Ssam
3161178676Ssam		case IWN_RX_STATISTICS:
3162178676Ssam		case IWN_BEACON_STATISTICS:
3163198429Srpaulo			iwn_rx_statistics(sc, desc, data);
3164178676Ssam			break;
3165178676Ssam
3166198429Srpaulo		case IWN_BEACON_MISSED:
3167198429Srpaulo		{
3168178676Ssam			struct iwn_beacon_missed *miss =
3169178676Ssam			    (struct iwn_beacon_missed *)(desc + 1);
3170202986Srpaulo			int misses;
3171178676Ssam
3172201209Srpaulo			bus_dmamap_sync(sc->rxq.data_dmat, data->map,
3173201209Srpaulo			    BUS_DMASYNC_POSTREAD);
3174202986Srpaulo			misses = le32toh(miss->consecutive);
3175201209Srpaulo
3176178676Ssam			DPRINTF(sc, IWN_DEBUG_STATE,
3177178676Ssam			    "%s: beacons missed %d/%d\n", __func__,
3178178676Ssam			    misses, le32toh(miss->total));
3179178676Ssam			/*
3180178676Ssam			 * If more than 5 consecutive beacons are missed,
3181178676Ssam			 * reinitialize the sensitivity state machine.
3182178676Ssam			 */
3183220660Sbschmidt			if (vap->iv_state == IEEE80211_S_RUN &&
3184226346Sbschmidt			    (ic->ic_flags & IEEE80211_F_SCAN) == 0) {
3185220660Sbschmidt				if (misses > 5)
3186220660Sbschmidt					(void)iwn_init_sensitivity(sc);
3187220660Sbschmidt				if (misses >= vap->iv_bmissthreshold) {
3188220660Sbschmidt					IWN_UNLOCK(sc);
3189220660Sbschmidt					ieee80211_beacon_miss(ic);
3190220660Sbschmidt					IWN_LOCK(sc);
3191220660Sbschmidt				}
3192201209Srpaulo			}
3193178676Ssam			break;
3194178676Ssam		}
3195198429Srpaulo		case IWN_UC_READY:
3196198429Srpaulo		{
3197178676Ssam			struct iwn_ucode_info *uc =
3198178676Ssam			    (struct iwn_ucode_info *)(desc + 1);
3199178676Ssam
3200198429Srpaulo			/* The microcontroller is ready. */
3201201209Srpaulo			bus_dmamap_sync(sc->rxq.data_dmat, data->map,
3202201209Srpaulo			    BUS_DMASYNC_POSTREAD);
3203178676Ssam			DPRINTF(sc, IWN_DEBUG_RESET,
3204178676Ssam			    "microcode alive notification version=%d.%d "
3205178676Ssam			    "subtype=%x alive=%x\n", uc->major, uc->minor,
3206178676Ssam			    uc->subtype, le32toh(uc->valid));
3207178676Ssam
3208178676Ssam			if (le32toh(uc->valid) != 1) {
3209178676Ssam				device_printf(sc->sc_dev,
3210198429Srpaulo				    "microcontroller initialization failed");
3211178676Ssam				break;
3212178676Ssam			}
3213178676Ssam			if (uc->subtype == IWN_UCODE_INIT) {
3214201209Srpaulo				/* Save microcontroller report. */
3215178676Ssam				memcpy(&sc->ucode_info, uc, sizeof (*uc));
3216178676Ssam			}
3217198429Srpaulo			/* Save the address of the error log in SRAM. */
3218198429Srpaulo			sc->errptr = le32toh(uc->errptr);
3219178676Ssam			break;
3220178676Ssam		}
3221198429Srpaulo		case IWN_STATE_CHANGED:
3222198429Srpaulo		{
3223178676Ssam			/*
3224178676Ssam			 * State change allows hardware switch change to be
3225178676Ssam			 * noted. However, we handle this in iwn_intr as we
3226178676Ssam			 * get both the enable/disble intr.
3227178676Ssam			 */
3228201209Srpaulo			bus_dmamap_sync(sc->rxq.data_dmat, data->map,
3229201209Srpaulo			    BUS_DMASYNC_POSTREAD);
3230253866Sadrian#ifdef	IWN_DEBUG
3231253866Sadrian			uint32_t *status = (uint32_t *)(desc + 1);
3232178676Ssam			DPRINTF(sc, IWN_DEBUG_INTR, "state changed to %x\n",
3233178676Ssam			    le32toh(*status));
3234253866Sadrian#endif
3235178676Ssam			break;
3236178676Ssam		}
3237198429Srpaulo		case IWN_START_SCAN:
3238198429Srpaulo		{
3239253866Sadrian			bus_dmamap_sync(sc->rxq.data_dmat, data->map,
3240253866Sadrian			    BUS_DMASYNC_POSTREAD);
3241253866Sadrian#ifdef	IWN_DEBUG
3242178676Ssam			struct iwn_start_scan *scan =
3243178676Ssam			    (struct iwn_start_scan *)(desc + 1);
3244178676Ssam			DPRINTF(sc, IWN_DEBUG_ANY,
3245178676Ssam			    "%s: scanning channel %d status %x\n",
3246178676Ssam			    __func__, scan->chan, le32toh(scan->status));
3247253866Sadrian#endif
3248178676Ssam			break;
3249178676Ssam		}
3250198429Srpaulo		case IWN_STOP_SCAN:
3251198429Srpaulo		{
3252253866Sadrian			bus_dmamap_sync(sc->rxq.data_dmat, data->map,
3253253866Sadrian			    BUS_DMASYNC_POSTREAD);
3254253866Sadrian#ifdef	IWN_DEBUG
3255178676Ssam			struct iwn_stop_scan *scan =
3256178676Ssam			    (struct iwn_stop_scan *)(desc + 1);
3257178676Ssam			DPRINTF(sc, IWN_DEBUG_STATE,
3258178676Ssam			    "scan finished nchan=%d status=%d chan=%d\n",
3259178676Ssam			    scan->nchan, scan->status, scan->chan);
3260253866Sadrian#endif
3261178676Ssam
3262201209Srpaulo			IWN_UNLOCK(sc);
3263191746Sthompsa			ieee80211_scan_next(vap);
3264201209Srpaulo			IWN_LOCK(sc);
3265178676Ssam			break;
3266178676Ssam		}
3267198429Srpaulo		case IWN5000_CALIBRATION_RESULT:
3268220674Sbschmidt			iwn5000_rx_calib_results(sc, desc, data);
3269198429Srpaulo			break;
3270198429Srpaulo
3271198429Srpaulo		case IWN5000_CALIBRATION_DONE:
3272201209Srpaulo			sc->sc_flags |= IWN_FLAG_CALIB_DONE;
3273198429Srpaulo			wakeup(sc);
3274198429Srpaulo			break;
3275178676Ssam		}
3276198429Srpaulo
3277178676Ssam		sc->rxq.cur = (sc->rxq.cur + 1) % IWN_RX_RING_COUNT;
3278178676Ssam	}
3279178676Ssam
3280198429Srpaulo	/* Tell the firmware what we have processed. */
3281178676Ssam	hw = (hw == 0) ? IWN_RX_RING_COUNT - 1 : hw - 1;
3282198429Srpaulo	IWN_WRITE(sc, IWN_FH_RX_WPTR, hw & ~7);
3283178676Ssam}
3284178676Ssam
3285198429Srpaulo/*
3286198429Srpaulo * Process an INT_WAKEUP interrupt raised when the microcontroller wakes up
3287198429Srpaulo * from power-down sleep mode.
3288198429Srpaulo */
3289206477Sbschmidtstatic void
3290198429Srpauloiwn_wakeup_intr(struct iwn_softc *sc)
3291198429Srpaulo{
3292198429Srpaulo	int qid;
3293198429Srpaulo
3294198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "%s: ucode wakeup from power-down sleep\n",
3295198429Srpaulo	    __func__);
3296198429Srpaulo
3297198429Srpaulo	/* Wakeup RX and TX rings. */
3298198429Srpaulo	IWN_WRITE(sc, IWN_FH_RX_WPTR, sc->rxq.cur & ~7);
3299220728Sbschmidt	for (qid = 0; qid < sc->ntxqs; qid++) {
3300198429Srpaulo		struct iwn_tx_ring *ring = &sc->txq[qid];
3301198429Srpaulo		IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | ring->cur);
3302198429Srpaulo	}
3303198429Srpaulo}
3304198429Srpaulo
3305206477Sbschmidtstatic void
3306191746Sthompsaiwn_rftoggle_intr(struct iwn_softc *sc)
3307191746Sthompsa{
3308191746Sthompsa	struct ifnet *ifp = sc->sc_ifp;
3309191746Sthompsa	struct ieee80211com *ic = ifp->if_l2com;
3310198429Srpaulo	uint32_t tmp = IWN_READ(sc, IWN_GP_CNTRL);
3311191746Sthompsa
3312191746Sthompsa	IWN_LOCK_ASSERT(sc);
3313191746Sthompsa
3314191746Sthompsa	device_printf(sc->sc_dev, "RF switch: radio %s\n",
3315198429Srpaulo	    (tmp & IWN_GP_CNTRL_RFKILL) ? "enabled" : "disabled");
3316198429Srpaulo	if (tmp & IWN_GP_CNTRL_RFKILL)
3317191746Sthompsa		ieee80211_runtask(ic, &sc->sc_radioon_task);
3318191746Sthompsa	else
3319191746Sthompsa		ieee80211_runtask(ic, &sc->sc_radiooff_task);
3320191746Sthompsa}
3321191746Sthompsa
3322198429Srpaulo/*
3323198429Srpaulo * Dump the error log of the firmware when a firmware panic occurs.  Although
3324198429Srpaulo * we can't debug the firmware because it is neither open source nor free, it
3325198429Srpaulo * can help us to identify certain classes of problems.
3326198429Srpaulo */
3327206477Sbschmidtstatic void
3328201209Srpauloiwn_fatal_intr(struct iwn_softc *sc)
3329191746Sthompsa{
3330198429Srpaulo	struct iwn_fw_dump dump;
3331198429Srpaulo	int i;
3332191746Sthompsa
3333191746Sthompsa	IWN_LOCK_ASSERT(sc);
3334191746Sthompsa
3335201209Srpaulo	/* Force a complete recalibration on next init. */
3336201209Srpaulo	sc->sc_flags &= ~IWN_FLAG_CALIB_DONE;
3337201209Srpaulo
3338198429Srpaulo	/* Check that the error log address is valid. */
3339198429Srpaulo	if (sc->errptr < IWN_FW_DATA_BASE ||
3340198429Srpaulo	    sc->errptr + sizeof (dump) >
3341220728Sbschmidt	    IWN_FW_DATA_BASE + sc->fw_data_maxsz) {
3342220726Sbschmidt		printf("%s: bad firmware error log address 0x%08x\n", __func__,
3343220726Sbschmidt		    sc->errptr);
3344198429Srpaulo		return;
3345198429Srpaulo	}
3346198429Srpaulo	if (iwn_nic_lock(sc) != 0) {
3347220726Sbschmidt		printf("%s: could not read firmware error log\n", __func__);
3348198429Srpaulo		return;
3349198429Srpaulo	}
3350198429Srpaulo	/* Read firmware error log from SRAM. */
3351198429Srpaulo	iwn_mem_read_region_4(sc, sc->errptr, (uint32_t *)&dump,
3352198429Srpaulo	    sizeof (dump) / sizeof (uint32_t));
3353198429Srpaulo	iwn_nic_unlock(sc);
3354198429Srpaulo
3355198429Srpaulo	if (dump.valid == 0) {
3356220726Sbschmidt		printf("%s: firmware error log is empty\n", __func__);
3357198429Srpaulo		return;
3358198429Srpaulo	}
3359198429Srpaulo	printf("firmware error log:\n");
3360198429Srpaulo	printf("  error type      = \"%s\" (0x%08X)\n",
3361198429Srpaulo	    (dump.id < nitems(iwn_fw_errmsg)) ?
3362198429Srpaulo		iwn_fw_errmsg[dump.id] : "UNKNOWN",
3363198429Srpaulo	    dump.id);
3364198429Srpaulo	printf("  program counter = 0x%08X\n", dump.pc);
3365198429Srpaulo	printf("  source line     = 0x%08X\n", dump.src_line);
3366198429Srpaulo	printf("  error data      = 0x%08X%08X\n",
3367198429Srpaulo	    dump.error_data[0], dump.error_data[1]);
3368198429Srpaulo	printf("  branch link     = 0x%08X%08X\n",
3369198429Srpaulo	    dump.branch_link[0], dump.branch_link[1]);
3370198429Srpaulo	printf("  interrupt link  = 0x%08X%08X\n",
3371198429Srpaulo	    dump.interrupt_link[0], dump.interrupt_link[1]);
3372198429Srpaulo	printf("  time            = %u\n", dump.time[0]);
3373198429Srpaulo
3374198429Srpaulo	/* Dump driver status (TX and RX rings) while we're here. */
3375198429Srpaulo	printf("driver status:\n");
3376220728Sbschmidt	for (i = 0; i < sc->ntxqs; i++) {
3377198429Srpaulo		struct iwn_tx_ring *ring = &sc->txq[i];
3378198429Srpaulo		printf("  tx ring %2d: qid=%-2d cur=%-3d queued=%-3d\n",
3379198429Srpaulo		    i, ring->qid, ring->cur, ring->queued);
3380198429Srpaulo	}
3381198429Srpaulo	printf("  rx ring: cur=%d\n", sc->rxq.cur);
3382191746Sthompsa}
3383191746Sthompsa
3384206477Sbschmidtstatic void
3385178676Ssamiwn_intr(void *arg)
3386178676Ssam{
3387178676Ssam	struct iwn_softc *sc = arg;
3388198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
3389201209Srpaulo	uint32_t r1, r2, tmp;
3390178676Ssam
3391178676Ssam	IWN_LOCK(sc);
3392178676Ssam
3393198429Srpaulo	/* Disable interrupts. */
3394201209Srpaulo	IWN_WRITE(sc, IWN_INT_MASK, 0);
3395178676Ssam
3396201209Srpaulo	/* Read interrupts from ICT (fast) or from registers (slow). */
3397201209Srpaulo	if (sc->sc_flags & IWN_FLAG_USE_ICT) {
3398201209Srpaulo		tmp = 0;
3399201209Srpaulo		while (sc->ict[sc->ict_cur] != 0) {
3400201209Srpaulo			tmp |= sc->ict[sc->ict_cur];
3401201209Srpaulo			sc->ict[sc->ict_cur] = 0;	/* Acknowledge. */
3402201209Srpaulo			sc->ict_cur = (sc->ict_cur + 1) % IWN_ICT_COUNT;
3403201209Srpaulo		}
3404201209Srpaulo		tmp = le32toh(tmp);
3405206444Sbschmidt		if (tmp == 0xffffffff)	/* Shouldn't happen. */
3406206444Sbschmidt			tmp = 0;
3407206444Sbschmidt		else if (tmp & 0xc0000)	/* Workaround a HW bug. */
3408206444Sbschmidt			tmp |= 0x8000;
3409201209Srpaulo		r1 = (tmp & 0xff00) << 16 | (tmp & 0xff);
3410201209Srpaulo		r2 = 0;	/* Unused. */
3411201209Srpaulo	} else {
3412201209Srpaulo		r1 = IWN_READ(sc, IWN_INT);
3413201209Srpaulo		if (r1 == 0xffffffff || (r1 & 0xfffffff0) == 0xa5a5a5a0)
3414201209Srpaulo			return;	/* Hardware gone! */
3415201209Srpaulo		r2 = IWN_READ(sc, IWN_FH_INT);
3416201209Srpaulo	}
3417178676Ssam
3418253705Sadrian	DPRINTF(sc, IWN_DEBUG_INTR, "interrupt reg1=0x%08x reg2=0x%08x\n"
3419253705Sadrian    , r1, r2);
3420198429Srpaulo
3421201209Srpaulo	if (r1 == 0 && r2 == 0)
3422198429Srpaulo		goto done;	/* Interrupt not for us. */
3423178676Ssam
3424198429Srpaulo	/* Acknowledge interrupts. */
3425198429Srpaulo	IWN_WRITE(sc, IWN_INT, r1);
3426201209Srpaulo	if (!(sc->sc_flags & IWN_FLAG_USE_ICT))
3427201209Srpaulo		IWN_WRITE(sc, IWN_FH_INT, r2);
3428178676Ssam
3429198429Srpaulo	if (r1 & IWN_INT_RF_TOGGLED) {
3430191746Sthompsa		iwn_rftoggle_intr(sc);
3431201209Srpaulo		goto done;
3432198429Srpaulo	}
3433198429Srpaulo	if (r1 & IWN_INT_CT_REACHED) {
3434198429Srpaulo		device_printf(sc->sc_dev, "%s: critical temperature reached!\n",
3435198429Srpaulo		    __func__);
3436198429Srpaulo	}
3437198429Srpaulo	if (r1 & (IWN_INT_SW_ERR | IWN_INT_HW_ERR)) {
3438220724Sbschmidt		device_printf(sc->sc_dev, "%s: fatal firmware error\n",
3439220724Sbschmidt		    __func__);
3440253866Sadrian#ifdef	IWN_DEBUG
3441253866Sadrian		iwn_debug_register(sc);
3442253866Sadrian#endif
3443220725Sbschmidt		/* Dump firmware error log and stop. */
3444201209Srpaulo		iwn_fatal_intr(sc);
3445201209Srpaulo		ifp->if_flags &= ~IFF_UP;
3446201209Srpaulo		iwn_stop_locked(sc);
3447178676Ssam		goto done;
3448178676Ssam	}
3449201209Srpaulo	if ((r1 & (IWN_INT_FH_RX | IWN_INT_SW_RX | IWN_INT_RX_PERIODIC)) ||
3450201209Srpaulo	    (r2 & IWN_FH_INT_RX)) {
3451201209Srpaulo		if (sc->sc_flags & IWN_FLAG_USE_ICT) {
3452201209Srpaulo			if (r1 & (IWN_INT_FH_RX | IWN_INT_SW_RX))
3453201209Srpaulo				IWN_WRITE(sc, IWN_FH_INT, IWN_FH_INT_RX);
3454201209Srpaulo			IWN_WRITE_1(sc, IWN_INT_PERIODIC,
3455201209Srpaulo			    IWN_INT_PERIODIC_DIS);
3456201209Srpaulo			iwn_notif_intr(sc);
3457201209Srpaulo			if (r1 & (IWN_INT_FH_RX | IWN_INT_SW_RX)) {
3458201209Srpaulo				IWN_WRITE_1(sc, IWN_INT_PERIODIC,
3459201209Srpaulo				    IWN_INT_PERIODIC_ENA);
3460201209Srpaulo			}
3461201209Srpaulo		} else
3462201209Srpaulo			iwn_notif_intr(sc);
3463201209Srpaulo	}
3464178676Ssam
3465201209Srpaulo	if ((r1 & IWN_INT_FH_TX) || (r2 & IWN_FH_INT_TX)) {
3466201209Srpaulo		if (sc->sc_flags & IWN_FLAG_USE_ICT)
3467201209Srpaulo			IWN_WRITE(sc, IWN_FH_INT, IWN_FH_INT_TX);
3468198429Srpaulo		wakeup(sc);	/* FH DMA transfer completed. */
3469201209Srpaulo	}
3470198429Srpaulo
3471198429Srpaulo	if (r1 & IWN_INT_ALIVE)
3472198429Srpaulo		wakeup(sc);	/* Firmware is alive. */
3473198429Srpaulo
3474198429Srpaulo	if (r1 & IWN_INT_WAKEUP)
3475198429Srpaulo		iwn_wakeup_intr(sc);
3476198429Srpaulo
3477201209Srpaulodone:
3478198429Srpaulo	/* Re-enable interrupts. */
3479201209Srpaulo	if (ifp->if_flags & IFF_UP)
3480201209Srpaulo		IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask);
3481198429Srpaulo
3482178676Ssam	IWN_UNLOCK(sc);
3483178676Ssam}
3484178676Ssam
3485198429Srpaulo/*
3486198429Srpaulo * Update TX scheduler ring when transmitting an 802.11 frame (4965AGN and
3487220725Sbschmidt * 5000 adapters use a slightly different format).
3488198429Srpaulo */
3489206477Sbschmidtstatic void
3490198429Srpauloiwn4965_update_sched(struct iwn_softc *sc, int qid, int idx, uint8_t id,
3491198429Srpaulo    uint16_t len)
3492178676Ssam{
3493198429Srpaulo	uint16_t *w = &sc->sched[qid * IWN4965_SCHED_COUNT + idx];
3494178676Ssam
3495253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
3496253705Sadrian
3497198429Srpaulo	*w = htole16(len + 8);
3498198439Srpaulo	bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map,
3499198439Srpaulo	    BUS_DMASYNC_PREWRITE);
3500201209Srpaulo	if (idx < IWN_SCHED_WINSZ) {
3501198429Srpaulo		*(w + IWN_TX_RING_COUNT) = *w;
3502198439Srpaulo		bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map,
3503198439Srpaulo		    BUS_DMASYNC_PREWRITE);
3504198439Srpaulo	}
3505198429Srpaulo}
3506198429Srpaulo
3507206477Sbschmidtstatic void
3508198429Srpauloiwn5000_update_sched(struct iwn_softc *sc, int qid, int idx, uint8_t id,
3509198429Srpaulo    uint16_t len)
3510198429Srpaulo{
3511198429Srpaulo	uint16_t *w = &sc->sched[qid * IWN5000_SCHED_COUNT + idx];
3512198429Srpaulo
3513253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
3514253705Sadrian
3515198429Srpaulo	*w = htole16(id << 12 | (len + 8));
3516198439Srpaulo	bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map,
3517198439Srpaulo	    BUS_DMASYNC_PREWRITE);
3518198439Srpaulo	if (idx < IWN_SCHED_WINSZ) {
3519198429Srpaulo		*(w + IWN_TX_RING_COUNT) = *w;
3520198439Srpaulo		bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map,
3521198439Srpaulo		    BUS_DMASYNC_PREWRITE);
3522198439Srpaulo	}
3523198429Srpaulo}
3524198429Srpaulo
3525206475Sbschmidt#ifdef notyet
3526206477Sbschmidtstatic void
3527198429Srpauloiwn5000_reset_sched(struct iwn_softc *sc, int qid, int idx)
3528198429Srpaulo{
3529198429Srpaulo	uint16_t *w = &sc->sched[qid * IWN5000_SCHED_COUNT + idx];
3530198429Srpaulo
3531253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
3532253705Sadrian
3533198429Srpaulo	*w = (*w & htole16(0xf000)) | htole16(1);
3534198439Srpaulo	bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map,
3535198439Srpaulo	    BUS_DMASYNC_PREWRITE);
3536198439Srpaulo	if (idx < IWN_SCHED_WINSZ) {
3537198429Srpaulo		*(w + IWN_TX_RING_COUNT) = *w;
3538198439Srpaulo		bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map,
3539206443Sbschmidt		    BUS_DMASYNC_PREWRITE);
3540198439Srpaulo	}
3541198429Srpaulo}
3542206475Sbschmidt#endif
3543198429Srpaulo
3544206477Sbschmidtstatic int
3545220720Sbschmidtiwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
3546178676Ssam{
3547221651Sbschmidt	struct iwn_ops *ops = &sc->ops;
3548198429Srpaulo	const struct ieee80211_txparam *tp;
3549178676Ssam	struct ieee80211vap *vap = ni->ni_vap;
3550178676Ssam	struct ieee80211com *ic = ni->ni_ic;
3551198429Srpaulo	struct iwn_node *wn = (void *)ni;
3552220720Sbschmidt	struct iwn_tx_ring *ring;
3553178676Ssam	struct iwn_tx_desc *desc;
3554178676Ssam	struct iwn_tx_data *data;
3555178676Ssam	struct iwn_tx_cmd *cmd;
3556178676Ssam	struct iwn_cmd_data *tx;
3557178676Ssam	struct ieee80211_frame *wh;
3558198429Srpaulo	struct ieee80211_key *k = NULL;
3559220700Sbschmidt	struct mbuf *m1;
3560178676Ssam	uint32_t flags;
3561220720Sbschmidt	uint16_t qos;
3562178676Ssam	u_int hdrlen;
3563220723Sbschmidt	bus_dma_segment_t *seg, segs[IWN_MAX_SCATTER];
3564220723Sbschmidt	uint8_t tid, ridx, txant, type;
3565220720Sbschmidt	int ac, i, totlen, error, pad, nsegs = 0, rate;
3566178676Ssam
3567253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
3568253705Sadrian
3569178676Ssam	IWN_LOCK_ASSERT(sc);
3570178676Ssam
3571198429Srpaulo	wh = mtod(m, struct ieee80211_frame *);
3572198429Srpaulo	hdrlen = ieee80211_anyhdrsize(wh);
3573178676Ssam	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
3574178676Ssam
3575220720Sbschmidt	/* Select EDCA Access Category and TX ring for this frame. */
3576220720Sbschmidt	if (IEEE80211_QOS_HAS_SEQ(wh)) {
3577220720Sbschmidt		qos = ((const struct ieee80211_qosframe *)wh)->i_qos[0];
3578220720Sbschmidt		tid = qos & IEEE80211_QOS_TID;
3579220720Sbschmidt	} else {
3580220720Sbschmidt		qos = 0;
3581220720Sbschmidt		tid = 0;
3582220720Sbschmidt	}
3583220720Sbschmidt	ac = M_WME_GETAC(m);
3584234321Sbschmidt	if (m->m_flags & M_AMPDU_MPDU) {
3585221651Sbschmidt		struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[ac];
3586221651Sbschmidt
3587235686Sbschmidt		if (!IEEE80211_AMPDU_RUNNING(tap)) {
3588235686Sbschmidt			m_freem(m);
3589235686Sbschmidt			return EINVAL;
3590235686Sbschmidt		}
3591235686Sbschmidt
3592234321Sbschmidt		ac = *(int *)tap->txa_private;
3593221651Sbschmidt		*(uint16_t *)wh->i_seq =
3594221651Sbschmidt		    htole16(ni->ni_txseqs[tid] << IEEE80211_SEQ_SEQ_SHIFT);
3595221651Sbschmidt		ni->ni_txseqs[tid]++;
3596221651Sbschmidt	}
3597234321Sbschmidt	ring = &sc->txq[ac];
3598198429Srpaulo	desc = &ring->desc[ring->cur];
3599198429Srpaulo	data = &ring->data[ring->cur];
3600198429Srpaulo
3601198429Srpaulo	/* Choose a TX rate index. */
3602201209Srpaulo	tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)];
3603178676Ssam	if (type == IEEE80211_FC0_TYPE_MGT)
3604178676Ssam		rate = tp->mgmtrate;
3605198429Srpaulo	else if (IEEE80211_IS_MULTICAST(wh->i_addr1))
3606178676Ssam		rate = tp->mcastrate;
3607178676Ssam	else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE)
3608178676Ssam		rate = tp->ucastrate;
3609178676Ssam	else {
3610206358Srpaulo		/* XXX pass pktlen */
3611206358Srpaulo		(void) ieee80211_ratectl_rate(ni, NULL, 0);
3612178676Ssam		rate = ni->ni_txrate;
3613178676Ssam	}
3614252727Sadrian	ridx = ieee80211_legacy_rate_lookup(ic->ic_rt,
3615252727Sadrian	    rate & IEEE80211_RATE_VAL);
3616178676Ssam
3617198429Srpaulo	/* Encrypt the frame if need be. */
3618262007Skevlo	if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
3619220725Sbschmidt		/* Retrieve key for TX. */
3620198429Srpaulo		k = ieee80211_crypto_encap(ni, m);
3621178676Ssam		if (k == NULL) {
3622198429Srpaulo			m_freem(m);
3623178676Ssam			return ENOBUFS;
3624178676Ssam		}
3625220725Sbschmidt		/* 802.11 header may have moved. */
3626198429Srpaulo		wh = mtod(m, struct ieee80211_frame *);
3627198429Srpaulo	}
3628198429Srpaulo	totlen = m->m_pkthdr.len;
3629178676Ssam
3630192468Ssam	if (ieee80211_radiotap_active_vap(vap)) {
3631178676Ssam		struct iwn_tx_radiotap_header *tap = &sc->sc_txtap;
3632178676Ssam
3633178676Ssam		tap->wt_flags = 0;
3634221648Sbschmidt		tap->wt_rate = rate;
3635178676Ssam		if (k != NULL)
3636178676Ssam			tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP;
3637178676Ssam
3638198429Srpaulo		ieee80211_radiotap_tx(vap, m);
3639178676Ssam	}
3640178676Ssam
3641198429Srpaulo	/* Prepare TX firmware command. */
3642198429Srpaulo	cmd = &ring->cmd[ring->cur];
3643198429Srpaulo	cmd->code = IWN_CMD_TX_DATA;
3644198429Srpaulo	cmd->flags = 0;
3645198429Srpaulo	cmd->qid = ring->qid;
3646198429Srpaulo	cmd->idx = ring->cur;
3647198429Srpaulo
3648198429Srpaulo	tx = (struct iwn_cmd_data *)cmd->data;
3649198429Srpaulo	/* NB: No need to clear tx, all fields are reinitialized here. */
3650198429Srpaulo	tx->scratch = 0;	/* clear "scratch" area */
3651198429Srpaulo
3652198429Srpaulo	flags = 0;
3653220720Sbschmidt	if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
3654220720Sbschmidt		/* Unicast frame, check if an ACK is expected. */
3655220720Sbschmidt		if (!qos || (qos & IEEE80211_QOS_ACKPOLICY) !=
3656220720Sbschmidt		    IEEE80211_QOS_ACKPOLICY_NOACK)
3657220720Sbschmidt			flags |= IWN_TX_NEED_ACK;
3658220720Sbschmidt	}
3659198429Srpaulo	if ((wh->i_fc[0] &
3660198429Srpaulo	    (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) ==
3661198429Srpaulo	    (IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_BAR))
3662198429Srpaulo		flags |= IWN_TX_IMM_BA;		/* Cannot happen yet. */
3663178676Ssam
3664198429Srpaulo	if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG)
3665198429Srpaulo		flags |= IWN_TX_MORE_FRAG;	/* Cannot happen yet. */
3666178676Ssam
3667198429Srpaulo	/* Check if frame must be protected using RTS/CTS or CTS-to-self. */
3668198429Srpaulo	if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
3669198429Srpaulo		/* NB: Group frames are sent using CCK in 802.11b/g. */
3670198429Srpaulo		if (totlen + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) {
3671198429Srpaulo			flags |= IWN_TX_NEED_RTS;
3672178676Ssam		} else if ((ic->ic_flags & IEEE80211_F_USEPROT) &&
3673201209Srpaulo		    ridx >= IWN_RIDX_OFDM6) {
3674178676Ssam			if (ic->ic_protmode == IEEE80211_PROT_CTSONLY)
3675198429Srpaulo				flags |= IWN_TX_NEED_CTS;
3676178676Ssam			else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS)
3677198429Srpaulo				flags |= IWN_TX_NEED_RTS;
3678178676Ssam		}
3679198429Srpaulo		if (flags & (IWN_TX_NEED_RTS | IWN_TX_NEED_CTS)) {
3680198429Srpaulo			if (sc->hw_type != IWN_HW_REV_TYPE_4965) {
3681198429Srpaulo				/* 5000 autoselects RTS/CTS or CTS-to-self. */
3682198429Srpaulo				flags &= ~(IWN_TX_NEED_RTS | IWN_TX_NEED_CTS);
3683198429Srpaulo				flags |= IWN_TX_NEED_PROTECTION;
3684198429Srpaulo			} else
3685198429Srpaulo				flags |= IWN_TX_FULL_TXOP;
3686198429Srpaulo		}
3687201209Srpaulo	}
3688178676Ssam
3689198429Srpaulo	if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
3690198429Srpaulo	    type != IEEE80211_FC0_TYPE_DATA)
3691220728Sbschmidt		tx->id = sc->broadcast_id;
3692198429Srpaulo	else
3693198429Srpaulo		tx->id = wn->id;
3694198429Srpaulo
3695178676Ssam	if (type == IEEE80211_FC0_TYPE_MGT) {
3696178676Ssam		uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
3697178676Ssam
3698198429Srpaulo		/* Tell HW to set timestamp in probe responses. */
3699178676Ssam		if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
3700178676Ssam			flags |= IWN_TX_INSERT_TSTAMP;
3701178676Ssam		if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ ||
3702178676Ssam		    subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ)
3703198429Srpaulo			tx->timeout = htole16(3);
3704178676Ssam		else
3705198429Srpaulo			tx->timeout = htole16(2);
3706178676Ssam	} else
3707198429Srpaulo		tx->timeout = htole16(0);
3708178676Ssam
3709178676Ssam	if (hdrlen & 3) {
3710201209Srpaulo		/* First segment length must be a multiple of 4. */
3711178676Ssam		flags |= IWN_TX_NEED_PADDING;
3712178676Ssam		pad = 4 - (hdrlen & 3);
3713178676Ssam	} else
3714178676Ssam		pad = 0;
3715178676Ssam
3716198429Srpaulo	tx->len = htole16(totlen);
3717220720Sbschmidt	tx->tid = tid;
3718201209Srpaulo	tx->rts_ntries = 60;
3719201209Srpaulo	tx->data_ntries = 15;
3720178676Ssam	tx->lifetime = htole32(IWN_LIFETIME_INFINITE);
3721252727Sadrian	tx->rate = iwn_rate_to_plcp(sc, ni, rate);
3722220728Sbschmidt	if (tx->id == sc->broadcast_id) {
3723201209Srpaulo		/* Group or management frame. */
3724201209Srpaulo		tx->linkq = 0;
3725198429Srpaulo		/* XXX Alternate between antenna A and B? */
3726201209Srpaulo		txant = IWN_LSB(sc->txchainmask);
3727221648Sbschmidt		tx->rate |= htole32(IWN_RFLAG_ANT(txant));
3728201209Srpaulo	} else {
3729220715Sbschmidt		tx->linkq = ni->ni_rates.rs_nrates - ridx - 1;
3730201209Srpaulo		flags |= IWN_TX_LINKQ;	/* enable MRR */
3731201209Srpaulo	}
3732198429Srpaulo	/* Set physical address of "scratch area". */
3733201209Srpaulo	tx->loaddr = htole32(IWN_LOADDR(data->scratch_paddr));
3734201209Srpaulo	tx->hiaddr = IWN_HIADDR(data->scratch_paddr);
3735178676Ssam
3736198429Srpaulo	/* Copy 802.11 header in TX command. */
3737178676Ssam	memcpy((uint8_t *)(tx + 1), wh, hdrlen);
3738178676Ssam
3739198429Srpaulo	/* Trim 802.11 header. */
3740198429Srpaulo	m_adj(m, hdrlen);
3741198429Srpaulo	tx->security = 0;
3742198429Srpaulo	tx->flags = htole32(flags);
3743198429Srpaulo
3744220700Sbschmidt	error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m, segs,
3745220700Sbschmidt	    &nsegs, BUS_DMA_NOWAIT);
3746220700Sbschmidt	if (error != 0) {
3747220700Sbschmidt		if (error != EFBIG) {
3748220700Sbschmidt			device_printf(sc->sc_dev,
3749220700Sbschmidt			    "%s: can't map mbuf (error %d)\n", __func__, error);
3750220700Sbschmidt			m_freem(m);
3751220700Sbschmidt			return error;
3752178676Ssam		}
3753220700Sbschmidt		/* Too many DMA segments, linearize mbuf. */
3754243857Sglebius		m1 = m_collapse(m, M_NOWAIT, IWN_MAX_SCATTER);
3755220700Sbschmidt		if (m1 == NULL) {
3756220700Sbschmidt			device_printf(sc->sc_dev,
3757220700Sbschmidt			    "%s: could not defrag mbuf\n", __func__);
3758220700Sbschmidt			m_freem(m);
3759220700Sbschmidt			return ENOBUFS;
3760220700Sbschmidt		}
3761220700Sbschmidt		m = m1;
3762220700Sbschmidt
3763220700Sbschmidt		error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m,
3764220700Sbschmidt		    segs, &nsegs, BUS_DMA_NOWAIT);
3765178676Ssam		if (error != 0) {
3766178676Ssam			device_printf(sc->sc_dev,
3767220700Sbschmidt			    "%s: can't map mbuf (error %d)\n", __func__, error);
3768198429Srpaulo			m_freem(m);
3769178676Ssam			return error;
3770178676Ssam		}
3771178676Ssam	}
3772178676Ssam
3773198429Srpaulo	data->m = m;
3774178676Ssam	data->ni = ni;
3775178676Ssam
3776178676Ssam	DPRINTF(sc, IWN_DEBUG_XMIT, "%s: qid %d idx %d len %d nsegs %d\n",
3777198429Srpaulo	    __func__, ring->qid, ring->cur, m->m_pkthdr.len, nsegs);
3778178676Ssam
3779198429Srpaulo	/* Fill TX descriptor. */
3780220700Sbschmidt	desc->nsegs = 1;
3781220700Sbschmidt	if (m->m_len != 0)
3782220700Sbschmidt		desc->nsegs += nsegs;
3783198429Srpaulo	/* First DMA segment is used by the TX command. */
3784201209Srpaulo	desc->segs[0].addr = htole32(IWN_LOADDR(data->cmd_paddr));
3785201209Srpaulo	desc->segs[0].len  = htole16(IWN_HIADDR(data->cmd_paddr) |
3786198429Srpaulo	    (4 + sizeof (*tx) + hdrlen + pad) << 4);
3787198429Srpaulo	/* Other DMA segments are for data payload. */
3788220700Sbschmidt	seg = &segs[0];
3789178676Ssam	for (i = 1; i <= nsegs; i++) {
3790220700Sbschmidt		desc->segs[i].addr = htole32(IWN_LOADDR(seg->ds_addr));
3791220700Sbschmidt		desc->segs[i].len  = htole16(IWN_HIADDR(seg->ds_addr) |
3792220700Sbschmidt		    seg->ds_len << 4);
3793220700Sbschmidt		seg++;
3794178676Ssam	}
3795178676Ssam
3796201209Srpaulo	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE);
3797201209Srpaulo	bus_dmamap_sync(ring->data_dmat, ring->cmd_dma.map,
3798198429Srpaulo	    BUS_DMASYNC_PREWRITE);
3799198429Srpaulo	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
3800198429Srpaulo	    BUS_DMASYNC_PREWRITE);
3801178676Ssam
3802198429Srpaulo	/* Update TX scheduler. */
3803221945Sbschmidt	if (ring->qid >= sc->firstaggqueue)
3804221945Sbschmidt		ops->update_sched(sc, ring->qid, ring->cur, tx->id, totlen);
3805178676Ssam
3806198429Srpaulo	/* Kick TX ring. */
3807178676Ssam	ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT;
3808198429Srpaulo	IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur);
3809178676Ssam
3810198429Srpaulo	/* Mark TX ring as full if we reach a certain threshold. */
3811198429Srpaulo	if (++ring->queued > IWN_TX_RING_HIMARK)
3812198429Srpaulo		sc->qfullmsk |= 1 << ring->qid;
3813178676Ssam
3814253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
3815253705Sadrian
3816178676Ssam	return 0;
3817178676Ssam}
3818178676Ssam
3819178676Ssamstatic int
3820201209Srpauloiwn_tx_data_raw(struct iwn_softc *sc, struct mbuf *m,
3821220720Sbschmidt    struct ieee80211_node *ni, const struct ieee80211_bpf_params *params)
3822178676Ssam{
3823221651Sbschmidt	struct iwn_ops *ops = &sc->ops;
3824178676Ssam	struct ifnet *ifp = sc->sc_ifp;
3825198429Srpaulo	struct ieee80211vap *vap = ni->ni_vap;
3826198429Srpaulo	struct ieee80211com *ic = ifp->if_l2com;
3827198429Srpaulo	struct iwn_tx_cmd *cmd;
3828198429Srpaulo	struct iwn_cmd_data *tx;
3829198429Srpaulo	struct ieee80211_frame *wh;
3830220720Sbschmidt	struct iwn_tx_ring *ring;
3831178676Ssam	struct iwn_tx_desc *desc;
3832178676Ssam	struct iwn_tx_data *data;
3833220700Sbschmidt	struct mbuf *m1;
3834220700Sbschmidt	bus_dma_segment_t *seg, segs[IWN_MAX_SCATTER];
3835198429Srpaulo	uint32_t flags;
3836198429Srpaulo	u_int hdrlen;
3837220720Sbschmidt	int ac, totlen, error, pad, nsegs = 0, i, rate;
3838201209Srpaulo	uint8_t ridx, type, txant;
3839178676Ssam
3840253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
3841253705Sadrian
3842198429Srpaulo	IWN_LOCK_ASSERT(sc);
3843178676Ssam
3844201209Srpaulo	wh = mtod(m, struct ieee80211_frame *);
3845198429Srpaulo	hdrlen = ieee80211_anyhdrsize(wh);
3846198429Srpaulo	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
3847198429Srpaulo
3848220720Sbschmidt	ac = params->ibp_pri & 3;
3849220720Sbschmidt
3850220720Sbschmidt	ring = &sc->txq[ac];
3851178676Ssam	desc = &ring->desc[ring->cur];
3852178676Ssam	data = &ring->data[ring->cur];
3853178676Ssam
3854198429Srpaulo	/* Choose a TX rate index. */
3855198429Srpaulo	rate = params->ibp_rate0;
3856252727Sadrian	ridx = ieee80211_legacy_rate_lookup(ic->ic_rt,
3857252727Sadrian	    rate & IEEE80211_RATE_VAL);
3858221648Sbschmidt	if (ridx == (uint8_t)-1) {
3859198429Srpaulo		/* XXX fall back to mcast/mgmt rate? */
3860201209Srpaulo		m_freem(m);
3861198429Srpaulo		return EINVAL;
3862198429Srpaulo	}
3863198429Srpaulo
3864201209Srpaulo	totlen = m->m_pkthdr.len;
3865198429Srpaulo
3866201209Srpaulo	/* Prepare TX firmware command. */
3867198429Srpaulo	cmd = &ring->cmd[ring->cur];
3868198429Srpaulo	cmd->code = IWN_CMD_TX_DATA;
3869198429Srpaulo	cmd->flags = 0;
3870198429Srpaulo	cmd->qid = ring->qid;
3871198429Srpaulo	cmd->idx = ring->cur;
3872198429Srpaulo
3873198429Srpaulo	tx = (struct iwn_cmd_data *)cmd->data;
3874201209Srpaulo	/* NB: No need to clear tx, all fields are reinitialized here. */
3875198429Srpaulo	tx->scratch = 0;	/* clear "scratch" area */
3876198429Srpaulo
3877198429Srpaulo	flags = 0;
3878198429Srpaulo	if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0)
3879198429Srpaulo		flags |= IWN_TX_NEED_ACK;
3880198429Srpaulo	if (params->ibp_flags & IEEE80211_BPF_RTS) {
3881198429Srpaulo		if (sc->hw_type != IWN_HW_REV_TYPE_4965) {
3882198429Srpaulo			/* 5000 autoselects RTS/CTS or CTS-to-self. */
3883198429Srpaulo			flags &= ~IWN_TX_NEED_RTS;
3884198429Srpaulo			flags |= IWN_TX_NEED_PROTECTION;
3885198429Srpaulo		} else
3886198429Srpaulo			flags |= IWN_TX_NEED_RTS | IWN_TX_FULL_TXOP;
3887198429Srpaulo	}
3888198429Srpaulo	if (params->ibp_flags & IEEE80211_BPF_CTS) {
3889198429Srpaulo		if (sc->hw_type != IWN_HW_REV_TYPE_4965) {
3890198429Srpaulo			/* 5000 autoselects RTS/CTS or CTS-to-self. */
3891198429Srpaulo			flags &= ~IWN_TX_NEED_CTS;
3892198429Srpaulo			flags |= IWN_TX_NEED_PROTECTION;
3893198429Srpaulo		} else
3894198429Srpaulo			flags |= IWN_TX_NEED_CTS | IWN_TX_FULL_TXOP;
3895198429Srpaulo	}
3896198429Srpaulo	if (type == IEEE80211_FC0_TYPE_MGT) {
3897198429Srpaulo		uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
3898198429Srpaulo
3899220725Sbschmidt		/* Tell HW to set timestamp in probe responses. */
3900198429Srpaulo		if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
3901198429Srpaulo			flags |= IWN_TX_INSERT_TSTAMP;
3902198429Srpaulo
3903198429Srpaulo		if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ ||
3904198429Srpaulo		    subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ)
3905198429Srpaulo			tx->timeout = htole16(3);
3906198429Srpaulo		else
3907198429Srpaulo			tx->timeout = htole16(2);
3908198429Srpaulo	} else
3909198429Srpaulo		tx->timeout = htole16(0);
3910198429Srpaulo
3911198429Srpaulo	if (hdrlen & 3) {
3912201209Srpaulo		/* First segment length must be a multiple of 4. */
3913198429Srpaulo		flags |= IWN_TX_NEED_PADDING;
3914198429Srpaulo		pad = 4 - (hdrlen & 3);
3915198429Srpaulo	} else
3916198429Srpaulo		pad = 0;
3917198429Srpaulo
3918198429Srpaulo	if (ieee80211_radiotap_active_vap(vap)) {
3919198429Srpaulo		struct iwn_tx_radiotap_header *tap = &sc->sc_txtap;
3920198429Srpaulo
3921198429Srpaulo		tap->wt_flags = 0;
3922198429Srpaulo		tap->wt_rate = rate;
3923198429Srpaulo
3924201209Srpaulo		ieee80211_radiotap_tx(vap, m);
3925198429Srpaulo	}
3926198429Srpaulo
3927198429Srpaulo	tx->len = htole16(totlen);
3928198429Srpaulo	tx->tid = 0;
3929220728Sbschmidt	tx->id = sc->broadcast_id;
3930198429Srpaulo	tx->rts_ntries = params->ibp_try1;
3931198429Srpaulo	tx->data_ntries = params->ibp_try0;
3932198429Srpaulo	tx->lifetime = htole32(IWN_LIFETIME_INFINITE);
3933252727Sadrian
3934252727Sadrian	/* XXX should just use  iwn_rate_to_plcp() */
3935221648Sbschmidt	tx->rate = htole32(rate2plcp(rate));
3936221648Sbschmidt	if (ridx < IWN_RIDX_OFDM6 &&
3937221648Sbschmidt	    IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
3938221648Sbschmidt		tx->rate |= htole32(IWN_RFLAG_CCK);
3939252727Sadrian
3940201209Srpaulo	/* Group or management frame. */
3941201209Srpaulo	tx->linkq = 0;
3942201209Srpaulo	txant = IWN_LSB(sc->txchainmask);
3943221648Sbschmidt	tx->rate |= htole32(IWN_RFLAG_ANT(txant));
3944252727Sadrian
3945198429Srpaulo	/* Set physical address of "scratch area". */
3946220694Sbschmidt	tx->loaddr = htole32(IWN_LOADDR(data->scratch_paddr));
3947220694Sbschmidt	tx->hiaddr = IWN_HIADDR(data->scratch_paddr);
3948198429Srpaulo
3949198429Srpaulo	/* Copy 802.11 header in TX command. */
3950198429Srpaulo	memcpy((uint8_t *)(tx + 1), wh, hdrlen);
3951198429Srpaulo
3952198429Srpaulo	/* Trim 802.11 header. */
3953201209Srpaulo	m_adj(m, hdrlen);
3954198429Srpaulo	tx->security = 0;
3955198429Srpaulo	tx->flags = htole32(flags);
3956198429Srpaulo
3957220700Sbschmidt	error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m, segs,
3958220700Sbschmidt	    &nsegs, BUS_DMA_NOWAIT);
3959220700Sbschmidt	if (error != 0) {
3960220700Sbschmidt		if (error != EFBIG) {
3961220700Sbschmidt			device_printf(sc->sc_dev,
3962220700Sbschmidt			    "%s: can't map mbuf (error %d)\n", __func__, error);
3963220700Sbschmidt			m_freem(m);
3964220700Sbschmidt			return error;
3965178676Ssam		}
3966220700Sbschmidt		/* Too many DMA segments, linearize mbuf. */
3967243857Sglebius		m1 = m_collapse(m, M_NOWAIT, IWN_MAX_SCATTER);
3968220700Sbschmidt		if (m1 == NULL) {
3969220700Sbschmidt			device_printf(sc->sc_dev,
3970220700Sbschmidt			    "%s: could not defrag mbuf\n", __func__);
3971220700Sbschmidt			m_freem(m);
3972220700Sbschmidt			return ENOBUFS;
3973220700Sbschmidt		}
3974220700Sbschmidt		m = m1;
3975220700Sbschmidt
3976220700Sbschmidt		error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m,
3977220700Sbschmidt		    segs, &nsegs, BUS_DMA_NOWAIT);
3978178676Ssam		if (error != 0) {
3979178676Ssam			device_printf(sc->sc_dev,
3980220700Sbschmidt			    "%s: can't map mbuf (error %d)\n", __func__, error);
3981201209Srpaulo			m_freem(m);
3982178676Ssam			return error;
3983178676Ssam		}
3984178676Ssam	}
3985178676Ssam
3986201209Srpaulo	data->m = m;
3987178676Ssam	data->ni = ni;
3988178676Ssam
3989178676Ssam	DPRINTF(sc, IWN_DEBUG_XMIT, "%s: qid %d idx %d len %d nsegs %d\n",
3990201209Srpaulo	    __func__, ring->qid, ring->cur, m->m_pkthdr.len, nsegs);
3991178676Ssam
3992198429Srpaulo	/* Fill TX descriptor. */
3993220700Sbschmidt	desc->nsegs = 1;
3994220700Sbschmidt	if (m->m_len != 0)
3995220700Sbschmidt		desc->nsegs += nsegs;
3996198429Srpaulo	/* First DMA segment is used by the TX command. */
3997201209Srpaulo	desc->segs[0].addr = htole32(IWN_LOADDR(data->cmd_paddr));
3998201209Srpaulo	desc->segs[0].len  = htole16(IWN_HIADDR(data->cmd_paddr) |
3999198429Srpaulo	    (4 + sizeof (*tx) + hdrlen + pad) << 4);
4000198429Srpaulo	/* Other DMA segments are for data payload. */
4001220700Sbschmidt	seg = &segs[0];
4002178676Ssam	for (i = 1; i <= nsegs; i++) {
4003220700Sbschmidt		desc->segs[i].addr = htole32(IWN_LOADDR(seg->ds_addr));
4004220700Sbschmidt		desc->segs[i].len  = htole16(IWN_HIADDR(seg->ds_addr) |
4005220700Sbschmidt		    seg->ds_len << 4);
4006220700Sbschmidt		seg++;
4007178676Ssam	}
4008178676Ssam
4009201209Srpaulo	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE);
4010201209Srpaulo	bus_dmamap_sync(ring->data_dmat, ring->cmd_dma.map,
4011201209Srpaulo	    BUS_DMASYNC_PREWRITE);
4012201209Srpaulo	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
4013201209Srpaulo	    BUS_DMASYNC_PREWRITE);
4014201209Srpaulo
4015198429Srpaulo	/* Update TX scheduler. */
4016221945Sbschmidt	if (ring->qid >= sc->firstaggqueue)
4017221945Sbschmidt		ops->update_sched(sc, ring->qid, ring->cur, tx->id, totlen);
4018178676Ssam
4019198429Srpaulo	/* Kick TX ring. */
4020178676Ssam	ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT;
4021198429Srpaulo	IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur);
4022178676Ssam
4023198429Srpaulo	/* Mark TX ring as full if we reach a certain threshold. */
4024198429Srpaulo	if (++ring->queued > IWN_TX_RING_HIMARK)
4025198429Srpaulo		sc->qfullmsk |= 1 << ring->qid;
4026178676Ssam
4027253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
4028253705Sadrian
4029178676Ssam	return 0;
4030178676Ssam}
4031178676Ssam
4032178676Ssamstatic int
4033178676Ssamiwn_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
4034220720Sbschmidt    const struct ieee80211_bpf_params *params)
4035178676Ssam{
4036178676Ssam	struct ieee80211com *ic = ni->ni_ic;
4037178676Ssam	struct ifnet *ifp = ic->ic_ifp;
4038178676Ssam	struct iwn_softc *sc = ifp->if_softc;
4039198429Srpaulo	int error = 0;
4040178676Ssam
4041253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
4042253705Sadrian
4043178676Ssam	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
4044178676Ssam		ieee80211_free_node(ni);
4045178676Ssam		m_freem(m);
4046178676Ssam		return ENETDOWN;
4047178676Ssam	}
4048178676Ssam
4049178676Ssam	IWN_LOCK(sc);
4050178676Ssam	if (params == NULL) {
4051178676Ssam		/*
4052178676Ssam		 * Legacy path; interpret frame contents to decide
4053178676Ssam		 * precisely how to send the frame.
4054178676Ssam		 */
4055220720Sbschmidt		error = iwn_tx_data(sc, m, ni);
4056178676Ssam	} else {
4057178676Ssam		/*
4058178676Ssam		 * Caller supplied explicit parameters to use in
4059178676Ssam		 * sending the frame.
4060178676Ssam		 */
4061220720Sbschmidt		error = iwn_tx_data_raw(sc, m, ni, params);
4062178676Ssam	}
4063178676Ssam	if (error != 0) {
4064178676Ssam		/* NB: m is reclaimed on tx failure */
4065178676Ssam		ieee80211_free_node(ni);
4066178676Ssam		ifp->if_oerrors++;
4067178676Ssam	}
4068220667Sbschmidt	sc->sc_tx_timer = 5;
4069220667Sbschmidt
4070178676Ssam	IWN_UNLOCK(sc);
4071253705Sadrian
4072253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
4073253705Sadrian
4074178676Ssam	return error;
4075178676Ssam}
4076178676Ssam
4077206477Sbschmidtstatic void
4078198429Srpauloiwn_start(struct ifnet *ifp)
4079198429Srpaulo{
4080198429Srpaulo	struct iwn_softc *sc = ifp->if_softc;
4081198429Srpaulo
4082198429Srpaulo	IWN_LOCK(sc);
4083198429Srpaulo	iwn_start_locked(ifp);
4084198429Srpaulo	IWN_UNLOCK(sc);
4085198429Srpaulo}
4086198429Srpaulo
4087206477Sbschmidtstatic void
4088198429Srpauloiwn_start_locked(struct ifnet *ifp)
4089198429Srpaulo{
4090198429Srpaulo	struct iwn_softc *sc = ifp->if_softc;
4091198429Srpaulo	struct ieee80211_node *ni;
4092198429Srpaulo	struct mbuf *m;
4093198429Srpaulo
4094198429Srpaulo	IWN_LOCK_ASSERT(sc);
4095198429Srpaulo
4096220720Sbschmidt	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 ||
4097220720Sbschmidt	    (ifp->if_drv_flags & IFF_DRV_OACTIVE))
4098220720Sbschmidt		return;
4099220720Sbschmidt
4100198429Srpaulo	for (;;) {
4101198429Srpaulo		if (sc->qfullmsk != 0) {
4102198429Srpaulo			ifp->if_drv_flags |= IFF_DRV_OACTIVE;
4103198429Srpaulo			break;
4104198429Srpaulo		}
4105198429Srpaulo		IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
4106198429Srpaulo		if (m == NULL)
4107198429Srpaulo			break;
4108198429Srpaulo		ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
4109220720Sbschmidt		if (iwn_tx_data(sc, m, ni) != 0) {
4110220720Sbschmidt			ieee80211_free_node(ni);
4111198429Srpaulo			ifp->if_oerrors++;
4112220720Sbschmidt			continue;
4113198429Srpaulo		}
4114198429Srpaulo		sc->sc_tx_timer = 5;
4115198429Srpaulo	}
4116198429Srpaulo}
4117198429Srpaulo
4118178676Ssamstatic void
4119220667Sbschmidtiwn_watchdog(void *arg)
4120178676Ssam{
4121220667Sbschmidt	struct iwn_softc *sc = arg;
4122220667Sbschmidt	struct ifnet *ifp = sc->sc_ifp;
4123220667Sbschmidt	struct ieee80211com *ic = ifp->if_l2com;
4124178676Ssam
4125220667Sbschmidt	IWN_LOCK_ASSERT(sc);
4126220667Sbschmidt
4127220667Sbschmidt	KASSERT(ifp->if_drv_flags & IFF_DRV_RUNNING, ("not running"));
4128220667Sbschmidt
4129253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
4130253705Sadrian
4131220668Sbschmidt	if (sc->sc_tx_timer > 0) {
4132220668Sbschmidt		if (--sc->sc_tx_timer == 0) {
4133220667Sbschmidt			if_printf(ifp, "device timeout\n");
4134220667Sbschmidt			ieee80211_runtask(ic, &sc->sc_reinit_task);
4135220667Sbschmidt			return;
4136220667Sbschmidt		}
4137178676Ssam	}
4138220667Sbschmidt	callout_reset(&sc->watchdog_to, hz, iwn_watchdog, sc);
4139178676Ssam}
4140178676Ssam
4141206477Sbschmidtstatic int
4142178676Ssamiwn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
4143178676Ssam{
4144178676Ssam	struct iwn_softc *sc = ifp->if_softc;
4145178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
4146201209Srpaulo	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
4147178676Ssam	struct ifreq *ifr = (struct ifreq *) data;
4148201209Srpaulo	int error = 0, startall = 0, stop = 0;
4149178676Ssam
4150178676Ssam	switch (cmd) {
4151220723Sbschmidt	case SIOCGIFADDR:
4152220723Sbschmidt		error = ether_ioctl(ifp, cmd, data);
4153220723Sbschmidt		break;
4154178676Ssam	case SIOCSIFFLAGS:
4155178704Sthompsa		IWN_LOCK(sc);
4156178676Ssam		if (ifp->if_flags & IFF_UP) {
4157178676Ssam			if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
4158178676Ssam				iwn_init_locked(sc);
4159201209Srpaulo				if (IWN_READ(sc, IWN_GP_CNTRL) & IWN_GP_CNTRL_RFKILL)
4160201209Srpaulo					startall = 1;
4161201209Srpaulo				else
4162201209Srpaulo					stop = 1;
4163178676Ssam			}
4164178676Ssam		} else {
4165178676Ssam			if (ifp->if_drv_flags & IFF_DRV_RUNNING)
4166178676Ssam				iwn_stop_locked(sc);
4167178676Ssam		}
4168178704Sthompsa		IWN_UNLOCK(sc);
4169178704Sthompsa		if (startall)
4170178704Sthompsa			ieee80211_start_all(ic);
4171201209Srpaulo		else if (vap != NULL && stop)
4172201209Srpaulo			ieee80211_stop(vap);
4173178676Ssam		break;
4174178676Ssam	case SIOCGIFMEDIA:
4175178676Ssam		error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd);
4176178676Ssam		break;
4177178704Sthompsa	default:
4178178704Sthompsa		error = EINVAL;
4179178704Sthompsa		break;
4180178676Ssam	}
4181178676Ssam	return error;
4182178676Ssam}
4183178676Ssam
4184178676Ssam/*
4185178676Ssam * Send a command to the firmware.
4186178676Ssam */
4187206477Sbschmidtstatic int
4188178676Ssamiwn_cmd(struct iwn_softc *sc, int code, const void *buf, int size, int async)
4189178676Ssam{
4190178676Ssam	struct iwn_tx_ring *ring = &sc->txq[4];
4191178676Ssam	struct iwn_tx_desc *desc;
4192198429Srpaulo	struct iwn_tx_data *data;
4193178676Ssam	struct iwn_tx_cmd *cmd;
4194198439Srpaulo	struct mbuf *m;
4195178676Ssam	bus_addr_t paddr;
4196198439Srpaulo	int totlen, error;
4197178676Ssam
4198253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
4199253705Sadrian
4200221650Sbschmidt	if (async == 0)
4201221650Sbschmidt		IWN_LOCK_ASSERT(sc);
4202178676Ssam
4203178676Ssam	desc = &ring->desc[ring->cur];
4204198429Srpaulo	data = &ring->data[ring->cur];
4205198429Srpaulo	totlen = 4 + size;
4206178676Ssam
4207198439Srpaulo	if (size > sizeof cmd->data) {
4208198439Srpaulo		/* Command is too large to fit in a descriptor. */
4209198439Srpaulo		if (totlen > MCLBYTES)
4210198439Srpaulo			return EINVAL;
4211243857Sglebius		m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE);
4212198439Srpaulo		if (m == NULL)
4213198439Srpaulo			return ENOMEM;
4214198439Srpaulo		cmd = mtod(m, struct iwn_tx_cmd *);
4215201209Srpaulo		error = bus_dmamap_load(ring->data_dmat, data->map, cmd,
4216198439Srpaulo		    totlen, iwn_dma_map_addr, &paddr, BUS_DMA_NOWAIT);
4217198439Srpaulo		if (error != 0) {
4218198439Srpaulo			m_freem(m);
4219198439Srpaulo			return error;
4220198439Srpaulo		}
4221198439Srpaulo		data->m = m;
4222198439Srpaulo	} else {
4223198439Srpaulo		cmd = &ring->cmd[ring->cur];
4224198439Srpaulo		paddr = data->cmd_paddr;
4225198439Srpaulo	}
4226198439Srpaulo
4227178676Ssam	cmd->code = code;
4228178676Ssam	cmd->flags = 0;
4229178676Ssam	cmd->qid = ring->qid;
4230178676Ssam	cmd->idx = ring->cur;
4231178676Ssam	memcpy(cmd->data, buf, size);
4232178676Ssam
4233198429Srpaulo	desc->nsegs = 1;
4234198429Srpaulo	desc->segs[0].addr = htole32(IWN_LOADDR(paddr));
4235198429Srpaulo	desc->segs[0].len  = htole16(IWN_HIADDR(paddr) | totlen << 4);
4236178676Ssam
4237178676Ssam	DPRINTF(sc, IWN_DEBUG_CMD, "%s: %s (0x%x) flags %d qid %d idx %d\n",
4238178676Ssam	    __func__, iwn_intr_str(cmd->code), cmd->code,
4239178676Ssam	    cmd->flags, cmd->qid, cmd->idx);
4240178676Ssam
4241198439Srpaulo	if (size > sizeof cmd->data) {
4242201209Srpaulo		bus_dmamap_sync(ring->data_dmat, data->map,
4243198439Srpaulo		    BUS_DMASYNC_PREWRITE);
4244198439Srpaulo	} else {
4245201209Srpaulo		bus_dmamap_sync(ring->data_dmat, ring->cmd_dma.map,
4246198439Srpaulo		    BUS_DMASYNC_PREWRITE);
4247198439Srpaulo	}
4248198439Srpaulo	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
4249198439Srpaulo	    BUS_DMASYNC_PREWRITE);
4250198439Srpaulo
4251198429Srpaulo	/* Kick command ring. */
4252178676Ssam	ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT;
4253198429Srpaulo	IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur);
4254178676Ssam
4255253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
4256253705Sadrian
4257198439Srpaulo	return async ? 0 : msleep(desc, &sc->sc_mtx, PCATCH, "iwncmd", hz);
4258178676Ssam}
4259178676Ssam
4260206477Sbschmidtstatic int
4261198429Srpauloiwn4965_add_node(struct iwn_softc *sc, struct iwn_node_info *node, int async)
4262198429Srpaulo{
4263198429Srpaulo	struct iwn4965_node_info hnode;
4264198429Srpaulo	caddr_t src, dst;
4265198429Srpaulo
4266253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
4267253705Sadrian
4268198429Srpaulo	/*
4269198429Srpaulo	 * We use the node structure for 5000 Series internally (it is
4270198429Srpaulo	 * a superset of the one for 4965AGN). We thus copy the common
4271198429Srpaulo	 * fields before sending the command.
4272198429Srpaulo	 */
4273198429Srpaulo	src = (caddr_t)node;
4274198429Srpaulo	dst = (caddr_t)&hnode;
4275198429Srpaulo	memcpy(dst, src, 48);
4276198429Srpaulo	/* Skip TSC, RX MIC and TX MIC fields from ``src''. */
4277198429Srpaulo	memcpy(dst + 48, src + 72, 20);
4278198429Srpaulo	return iwn_cmd(sc, IWN_CMD_ADD_NODE, &hnode, sizeof hnode, async);
4279198429Srpaulo}
4280198429Srpaulo
4281206477Sbschmidtstatic int
4282198429Srpauloiwn5000_add_node(struct iwn_softc *sc, struct iwn_node_info *node, int async)
4283198429Srpaulo{
4284253705Sadrian
4285253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
4286253705Sadrian
4287198429Srpaulo	/* Direct mapping. */
4288198429Srpaulo	return iwn_cmd(sc, IWN_CMD_ADD_NODE, node, sizeof (*node), async);
4289198429Srpaulo}
4290198429Srpaulo
4291206477Sbschmidtstatic int
4292220715Sbschmidtiwn_set_link_quality(struct iwn_softc *sc, struct ieee80211_node *ni)
4293178676Ssam{
4294222933Sbschmidt#define	RV(v)	((v) & IEEE80211_RATE_VAL)
4295220715Sbschmidt	struct iwn_node *wn = (void *)ni;
4296220715Sbschmidt	struct ieee80211_rateset *rs = &ni->ni_rates;
4297198429Srpaulo	struct iwn_cmd_link_quality linkq;
4298220715Sbschmidt	uint8_t txant;
4299221648Sbschmidt	int i, rate, txrate;
4300178676Ssam
4301253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
4302253705Sadrian
4303198429Srpaulo	/* Use the first valid TX antenna. */
4304201209Srpaulo	txant = IWN_LSB(sc->txchainmask);
4305178676Ssam
4306198429Srpaulo	memset(&linkq, 0, sizeof linkq);
4307220715Sbschmidt	linkq.id = wn->id;
4308198429Srpaulo	linkq.antmsk_1stream = txant;
4309201209Srpaulo	linkq.antmsk_2stream = IWN_ANT_AB;
4310221651Sbschmidt	linkq.ampdu_max = 64;
4311198429Srpaulo	linkq.ampdu_threshold = 3;
4312198429Srpaulo	linkq.ampdu_limit = htole16(4000);	/* 4ms */
4313198429Srpaulo
4314220715Sbschmidt	/* Start at highest available bit-rate. */
4315221649Sbschmidt	if (IEEE80211_IS_CHAN_HT(ni->ni_chan))
4316221649Sbschmidt		txrate = ni->ni_htrates.rs_nrates - 1;
4317221649Sbschmidt	else
4318221649Sbschmidt		txrate = rs->rs_nrates - 1;
4319178676Ssam	for (i = 0; i < IWN_MAX_TX_RETRIES; i++) {
4320252727Sadrian		uint32_t plcp;
4321252727Sadrian
4322221649Sbschmidt		if (IEEE80211_IS_CHAN_HT(ni->ni_chan))
4323221649Sbschmidt			rate = IEEE80211_RATE_MCS | txrate;
4324221649Sbschmidt		else
4325222933Sbschmidt			rate = RV(rs->rs_rates[txrate]);
4326221648Sbschmidt
4327252727Sadrian		/* Do rate -> PLCP config mapping */
4328252727Sadrian		plcp = iwn_rate_to_plcp(sc, ni, rate);
4329252727Sadrian		linkq.retry[i] = plcp;
4330252727Sadrian
4331252727Sadrian		/* Special case for dual-stream rates? */
4332252727Sadrian		if ((le32toh(plcp) & IWN_RFLAG_MCS) &&
4333252727Sadrian		    RV(le32toh(plcp)) > 7)
4334221649Sbschmidt			linkq.mimo = i + 1;
4335221649Sbschmidt
4336220715Sbschmidt		/* Next retry at immediate lower bit-rate. */
4337220715Sbschmidt		if (txrate > 0)
4338220715Sbschmidt			txrate--;
4339178676Ssam	}
4340253705Sadrian
4341253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
4342253705Sadrian
4343220715Sbschmidt	return iwn_cmd(sc, IWN_CMD_LINK_QUALITY, &linkq, sizeof linkq, 1);
4344222933Sbschmidt#undef	RV
4345178676Ssam}
4346178676Ssam
4347178676Ssam/*
4348198429Srpaulo * Broadcast node is used to send group-addressed and management frames.
4349178676Ssam */
4350206477Sbschmidtstatic int
4351201209Srpauloiwn_add_broadcast_node(struct iwn_softc *sc, int async)
4352178676Ssam{
4353220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
4354198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
4355220715Sbschmidt	struct ieee80211com *ic = ifp->if_l2com;
4356178676Ssam	struct iwn_node_info node;
4357220715Sbschmidt	struct iwn_cmd_link_quality linkq;
4358220715Sbschmidt	uint8_t txant;
4359220715Sbschmidt	int i, error;
4360178676Ssam
4361253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
4362253705Sadrian
4363254204Sadrian	sc->rxon = &sc->rx_on[IWN_RXON_BSS_CTX];
4364254204Sadrian
4365178676Ssam	memset(&node, 0, sizeof node);
4366198429Srpaulo	IEEE80211_ADDR_COPY(node.macaddr, ifp->if_broadcastaddr);
4367220728Sbschmidt	node.id = sc->broadcast_id;
4368198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "%s: adding broadcast node\n", __func__);
4369220728Sbschmidt	if ((error = ops->add_node(sc, &node, async)) != 0)
4370198429Srpaulo		return error;
4371178676Ssam
4372220715Sbschmidt	/* Use the first valid TX antenna. */
4373220715Sbschmidt	txant = IWN_LSB(sc->txchainmask);
4374220715Sbschmidt
4375220715Sbschmidt	memset(&linkq, 0, sizeof linkq);
4376220728Sbschmidt	linkq.id = sc->broadcast_id;
4377220715Sbschmidt	linkq.antmsk_1stream = txant;
4378220715Sbschmidt	linkq.antmsk_2stream = IWN_ANT_AB;
4379220715Sbschmidt	linkq.ampdu_max = 64;
4380220715Sbschmidt	linkq.ampdu_threshold = 3;
4381220715Sbschmidt	linkq.ampdu_limit = htole16(4000);	/* 4ms */
4382220715Sbschmidt
4383220715Sbschmidt	/* Use lowest mandatory bit-rate. */
4384220715Sbschmidt	if (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan))
4385221648Sbschmidt		linkq.retry[0] = htole32(0xd);
4386220715Sbschmidt	else
4387221648Sbschmidt		linkq.retry[0] = htole32(10 | IWN_RFLAG_CCK);
4388221648Sbschmidt	linkq.retry[0] |= htole32(IWN_RFLAG_ANT(txant));
4389220715Sbschmidt	/* Use same bit-rate for all TX retries. */
4390220715Sbschmidt	for (i = 1; i < IWN_MAX_TX_RETRIES; i++) {
4391221648Sbschmidt		linkq.retry[i] = linkq.retry[0];
4392220715Sbschmidt	}
4393253705Sadrian
4394253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
4395253705Sadrian
4396220715Sbschmidt	return iwn_cmd(sc, IWN_CMD_LINK_QUALITY, &linkq, sizeof linkq, async);
4397178676Ssam}
4398178676Ssam
4399206477Sbschmidtstatic int
4400220721Sbschmidtiwn_updateedca(struct ieee80211com *ic)
4401178676Ssam{
4402178676Ssam#define IWN_EXP2(x)	((1 << (x)) - 1)	/* CWmin = 2^ECWmin - 1 */
4403178676Ssam	struct iwn_softc *sc = ic->ic_ifp->if_softc;
4404178676Ssam	struct iwn_edca_params cmd;
4405220721Sbschmidt	int aci;
4406178676Ssam
4407253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
4408253705Sadrian
4409178676Ssam	memset(&cmd, 0, sizeof cmd);
4410178676Ssam	cmd.flags = htole32(IWN_EDCA_UPDATE);
4411220721Sbschmidt	for (aci = 0; aci < WME_NUM_AC; aci++) {
4412220721Sbschmidt		const struct wmeParams *ac =
4413220721Sbschmidt		    &ic->ic_wme.wme_chanParams.cap_wmeParams[aci];
4414220721Sbschmidt		cmd.ac[aci].aifsn = ac->wmep_aifsn;
4415220721Sbschmidt		cmd.ac[aci].cwmin = htole16(IWN_EXP2(ac->wmep_logcwmin));
4416220721Sbschmidt		cmd.ac[aci].cwmax = htole16(IWN_EXP2(ac->wmep_logcwmax));
4417220721Sbschmidt		cmd.ac[aci].txoplimit =
4418220721Sbschmidt		    htole16(IEEE80211_TXOP_TO_US(ac->wmep_txopLimit));
4419178676Ssam	}
4420201209Srpaulo	IEEE80211_UNLOCK(ic);
4421178676Ssam	IWN_LOCK(sc);
4422220725Sbschmidt	(void)iwn_cmd(sc, IWN_CMD_EDCA_PARAMS, &cmd, sizeof cmd, 1);
4423178676Ssam	IWN_UNLOCK(sc);
4424201209Srpaulo	IEEE80211_LOCK(ic);
4425253705Sadrian
4426253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
4427253705Sadrian
4428178676Ssam	return 0;
4429178676Ssam#undef IWN_EXP2
4430178676Ssam}
4431178676Ssam
4432201209Srpaulostatic void
4433201209Srpauloiwn_update_mcast(struct ifnet *ifp)
4434201209Srpaulo{
4435201209Srpaulo	/* Ignore */
4436201209Srpaulo}
4437201209Srpaulo
4438206477Sbschmidtstatic void
4439178676Ssamiwn_set_led(struct iwn_softc *sc, uint8_t which, uint8_t off, uint8_t on)
4440178676Ssam{
4441178676Ssam	struct iwn_cmd_led led;
4442178676Ssam
4443253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
4444253705Sadrian
4445198429Srpaulo	/* Clear microcode LED ownership. */
4446198429Srpaulo	IWN_CLRBITS(sc, IWN_LED, IWN_LED_BSM_CTRL);
4447198429Srpaulo
4448178676Ssam	led.which = which;
4449198429Srpaulo	led.unit = htole32(10000);	/* on/off in unit of 100ms */
4450178676Ssam	led.off = off;
4451178676Ssam	led.on = on;
4452198429Srpaulo	(void)iwn_cmd(sc, IWN_CMD_SET_LED, &led, sizeof led, 1);
4453178676Ssam}
4454178676Ssam
4455178676Ssam/*
4456201209Srpaulo * Set the critical temperature at which the firmware will stop the radio
4457201209Srpaulo * and notify us.
4458178676Ssam */
4459206477Sbschmidtstatic int
4460178676Ssamiwn_set_critical_temp(struct iwn_softc *sc)
4461178676Ssam{
4462178676Ssam	struct iwn_critical_temp crit;
4463201209Srpaulo	int32_t temp;
4464178676Ssam
4465253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
4466253705Sadrian
4467198429Srpaulo	IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_CTEMP_STOP_RF);
4468178676Ssam
4469201209Srpaulo	if (sc->hw_type == IWN_HW_REV_TYPE_5150)
4470201209Srpaulo		temp = (IWN_CTOK(110) - sc->temp_off) * -5;
4471201209Srpaulo	else if (sc->hw_type == IWN_HW_REV_TYPE_4965)
4472201209Srpaulo		temp = IWN_CTOK(110);
4473201209Srpaulo	else
4474201209Srpaulo		temp = 110;
4475178676Ssam	memset(&crit, 0, sizeof crit);
4476201209Srpaulo	crit.tempR = htole32(temp);
4477220726Sbschmidt	DPRINTF(sc, IWN_DEBUG_RESET, "setting critical temp to %d\n", temp);
4478178676Ssam	return iwn_cmd(sc, IWN_CMD_SET_CRITICAL_TEMP, &crit, sizeof crit, 0);
4479178676Ssam}
4480178676Ssam
4481206477Sbschmidtstatic int
4482198429Srpauloiwn_set_timing(struct iwn_softc *sc, struct ieee80211_node *ni)
4483178676Ssam{
4484198429Srpaulo	struct iwn_cmd_timing cmd;
4485178676Ssam	uint64_t val, mod;
4486178676Ssam
4487253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
4488253705Sadrian
4489198429Srpaulo	memset(&cmd, 0, sizeof cmd);
4490198429Srpaulo	memcpy(&cmd.tstamp, ni->ni_tstamp.data, sizeof (uint64_t));
4491198429Srpaulo	cmd.bintval = htole16(ni->ni_intval);
4492198429Srpaulo	cmd.lintval = htole16(10);
4493178676Ssam
4494198429Srpaulo	/* Compute remaining time until next beacon. */
4495220634Sbschmidt	val = (uint64_t)ni->ni_intval * IEEE80211_DUR_TU;
4496198429Srpaulo	mod = le64toh(cmd.tstamp) % val;
4497198429Srpaulo	cmd.binitval = htole32((uint32_t)(val - mod));
4498178676Ssam
4499198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "timing bintval=%u tstamp=%ju, init=%u\n",
4500198429Srpaulo	    ni->ni_intval, le64toh(cmd.tstamp), (uint32_t)(val - mod));
4501178676Ssam
4502198429Srpaulo	return iwn_cmd(sc, IWN_CMD_TIMING, &cmd, sizeof cmd, 1);
4503178676Ssam}
4504178676Ssam
4505206477Sbschmidtstatic void
4506198429Srpauloiwn4965_power_calibration(struct iwn_softc *sc, int temp)
4507178676Ssam{
4508201882Skeramida	struct ifnet *ifp = sc->sc_ifp;
4509201882Skeramida	struct ieee80211com *ic = ifp->if_l2com;
4510201882Skeramida
4511253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
4512253705Sadrian
4513220725Sbschmidt	/* Adjust TX power if need be (delta >= 3 degC). */
4514178676Ssam	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: temperature %d->%d\n",
4515178676Ssam	    __func__, sc->temp, temp);
4516198429Srpaulo	if (abs(temp - sc->temp) >= 3) {
4517198429Srpaulo		/* Record temperature of last calibration. */
4518198429Srpaulo		sc->temp = temp;
4519201882Skeramida		(void)iwn4965_set_txpower(sc, ic->ic_bsschan, 1);
4520178676Ssam	}
4521178676Ssam}
4522178676Ssam
4523178676Ssam/*
4524198429Srpaulo * Set TX power for current channel (each rate has its own power settings).
4525178676Ssam * This function takes into account the regulatory information from EEPROM,
4526178676Ssam * the current temperature and the current voltage.
4527178676Ssam */
4528206477Sbschmidtstatic int
4529201882Skeramidaiwn4965_set_txpower(struct iwn_softc *sc, struct ieee80211_channel *ch,
4530201882Skeramida    int async)
4531178676Ssam{
4532198429Srpaulo/* Fixed-point arithmetic division using a n-bit fractional part. */
4533178676Ssam#define fdivround(a, b, n)	\
4534178676Ssam	((((1 << n) * (a)) / (b) + (1 << n) / 2) / (1 << n))
4535198429Srpaulo/* Linear interpolation. */
4536178676Ssam#define interpolate(x, x1, y1, x2, y2, n)	\
4537178676Ssam	((y1) + fdivround(((int)(x) - (x1)) * ((y2) - (y1)), (x2) - (x1), n))
4538178676Ssam
4539178676Ssam	static const int tdiv[IWN_NATTEN_GROUPS] = { 9, 8, 8, 8, 6 };
4540178676Ssam	struct iwn_ucode_info *uc = &sc->ucode_info;
4541198429Srpaulo	struct iwn4965_cmd_txpower cmd;
4542198429Srpaulo	struct iwn4965_eeprom_chan_samples *chans;
4543220723Sbschmidt	const uint8_t *rf_gain, *dsp_gain;
4544178676Ssam	int32_t vdiff, tdiff;
4545178676Ssam	int i, c, grp, maxpwr;
4546198429Srpaulo	uint8_t chan;
4547178676Ssam
4548254204Sadrian	sc->rxon = &sc->rx_on[IWN_RXON_BSS_CTX];
4549220687Sbschmidt	/* Retrieve current channel from last RXON. */
4550254204Sadrian	chan = sc->rxon->chan;
4551201209Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "setting TX power for channel %d\n",
4552201209Srpaulo	    chan);
4553178676Ssam
4554178676Ssam	memset(&cmd, 0, sizeof cmd);
4555178676Ssam	cmd.band = IEEE80211_IS_CHAN_5GHZ(ch) ? 0 : 1;
4556178676Ssam	cmd.chan = chan;
4557178676Ssam
4558178676Ssam	if (IEEE80211_IS_CHAN_5GHZ(ch)) {
4559178676Ssam		maxpwr   = sc->maxpwr5GHz;
4560198429Srpaulo		rf_gain  = iwn4965_rf_gain_5ghz;
4561198429Srpaulo		dsp_gain = iwn4965_dsp_gain_5ghz;
4562178676Ssam	} else {
4563178676Ssam		maxpwr   = sc->maxpwr2GHz;
4564198429Srpaulo		rf_gain  = iwn4965_rf_gain_2ghz;
4565198429Srpaulo		dsp_gain = iwn4965_dsp_gain_2ghz;
4566178676Ssam	}
4567178676Ssam
4568198429Srpaulo	/* Compute voltage compensation. */
4569178676Ssam	vdiff = ((int32_t)le32toh(uc->volt) - sc->eeprom_voltage) / 7;
4570178676Ssam	if (vdiff > 0)
4571178676Ssam		vdiff *= 2;
4572178676Ssam	if (abs(vdiff) > 2)
4573178676Ssam		vdiff = 0;
4574178676Ssam	DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
4575178676Ssam	    "%s: voltage compensation=%d (UCODE=%d, EEPROM=%d)\n",
4576178676Ssam	    __func__, vdiff, le32toh(uc->volt), sc->eeprom_voltage);
4577178676Ssam
4578201209Srpaulo	/* Get channel attenuation group. */
4579178676Ssam	if (chan <= 20)		/* 1-20 */
4580178676Ssam		grp = 4;
4581178676Ssam	else if (chan <= 43)	/* 34-43 */
4582178676Ssam		grp = 0;
4583178676Ssam	else if (chan <= 70)	/* 44-70 */
4584178676Ssam		grp = 1;
4585178676Ssam	else if (chan <= 124)	/* 71-124 */
4586178676Ssam		grp = 2;
4587178676Ssam	else			/* 125-200 */
4588178676Ssam		grp = 3;
4589178676Ssam	DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
4590178676Ssam	    "%s: chan %d, attenuation group=%d\n", __func__, chan, grp);
4591178676Ssam
4592201209Srpaulo	/* Get channel sub-band. */
4593178676Ssam	for (i = 0; i < IWN_NBANDS; i++)
4594178676Ssam		if (sc->bands[i].lo != 0 &&
4595178676Ssam		    sc->bands[i].lo <= chan && chan <= sc->bands[i].hi)
4596178676Ssam			break;
4597198429Srpaulo	if (i == IWN_NBANDS)	/* Can't happen in real-life. */
4598198429Srpaulo		return EINVAL;
4599178676Ssam	chans = sc->bands[i].chans;
4600178676Ssam	DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
4601178676Ssam	    "%s: chan %d sub-band=%d\n", __func__, chan, i);
4602178676Ssam
4603198429Srpaulo	for (c = 0; c < 2; c++) {
4604178676Ssam		uint8_t power, gain, temp;
4605178676Ssam		int maxchpwr, pwr, ridx, idx;
4606178676Ssam
4607178676Ssam		power = interpolate(chan,
4608178676Ssam		    chans[0].num, chans[0].samples[c][1].power,
4609178676Ssam		    chans[1].num, chans[1].samples[c][1].power, 1);
4610178676Ssam		gain  = interpolate(chan,
4611178676Ssam		    chans[0].num, chans[0].samples[c][1].gain,
4612178676Ssam		    chans[1].num, chans[1].samples[c][1].gain, 1);
4613178676Ssam		temp  = interpolate(chan,
4614178676Ssam		    chans[0].num, chans[0].samples[c][1].temp,
4615178676Ssam		    chans[1].num, chans[1].samples[c][1].temp, 1);
4616178676Ssam		DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
4617178676Ssam		    "%s: Tx chain %d: power=%d gain=%d temp=%d\n",
4618178676Ssam		    __func__, c, power, gain, temp);
4619178676Ssam
4620198429Srpaulo		/* Compute temperature compensation. */
4621178676Ssam		tdiff = ((sc->temp - temp) * 2) / tdiv[grp];
4622178676Ssam		DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
4623178676Ssam		    "%s: temperature compensation=%d (current=%d, EEPROM=%d)\n",
4624178676Ssam		    __func__, tdiff, sc->temp, temp);
4625178676Ssam
4626178676Ssam		for (ridx = 0; ridx <= IWN_RIDX_MAX; ridx++) {
4627201209Srpaulo			/* Convert dBm to half-dBm. */
4628198429Srpaulo			maxchpwr = sc->maxpwr[chan] * 2;
4629198429Srpaulo			if ((ridx / 8) & 1)
4630198429Srpaulo				maxchpwr -= 6;	/* MIMO 2T: -3dB */
4631178676Ssam
4632198429Srpaulo			pwr = maxpwr;
4633178676Ssam
4634198429Srpaulo			/* Adjust TX power based on rate. */
4635198429Srpaulo			if ((ridx % 8) == 5)
4636198429Srpaulo				pwr -= 15;	/* OFDM48: -7.5dB */
4637198429Srpaulo			else if ((ridx % 8) == 6)
4638198429Srpaulo				pwr -= 17;	/* OFDM54: -8.5dB */
4639198429Srpaulo			else if ((ridx % 8) == 7)
4640198429Srpaulo				pwr -= 20;	/* OFDM60: -10dB */
4641198429Srpaulo			else
4642198429Srpaulo				pwr -= 10;	/* Others: -5dB */
4643178676Ssam
4644201209Srpaulo			/* Do not exceed channel max TX power. */
4645178676Ssam			if (pwr > maxchpwr)
4646178676Ssam				pwr = maxchpwr;
4647178676Ssam
4648178676Ssam			idx = gain - (pwr - power) - tdiff - vdiff;
4649178676Ssam			if ((ridx / 8) & 1)	/* MIMO */
4650178676Ssam				idx += (int32_t)le32toh(uc->atten[grp][c]);
4651178676Ssam
4652178676Ssam			if (cmd.band == 0)
4653178676Ssam				idx += 9;	/* 5GHz */
4654178676Ssam			if (ridx == IWN_RIDX_MAX)
4655178676Ssam				idx += 5;	/* CCK */
4656178676Ssam
4657198429Srpaulo			/* Make sure idx stays in a valid range. */
4658178676Ssam			if (idx < 0)
4659178676Ssam				idx = 0;
4660198429Srpaulo			else if (idx > IWN4965_MAX_PWR_INDEX)
4661198429Srpaulo				idx = IWN4965_MAX_PWR_INDEX;
4662178676Ssam
4663178676Ssam			DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
4664178676Ssam			    "%s: Tx chain %d, rate idx %d: power=%d\n",
4665178676Ssam			    __func__, c, ridx, idx);
4666178676Ssam			cmd.power[ridx].rf_gain[c] = rf_gain[idx];
4667178676Ssam			cmd.power[ridx].dsp_gain[c] = dsp_gain[idx];
4668178676Ssam		}
4669178676Ssam	}
4670178676Ssam
4671178676Ssam	DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
4672178676Ssam	    "%s: set tx power for chan %d\n", __func__, chan);
4673178676Ssam	return iwn_cmd(sc, IWN_CMD_TXPOWER, &cmd, sizeof cmd, async);
4674178676Ssam
4675178676Ssam#undef interpolate
4676178676Ssam#undef fdivround
4677178676Ssam}
4678178676Ssam
4679206477Sbschmidtstatic int
4680201882Skeramidaiwn5000_set_txpower(struct iwn_softc *sc, struct ieee80211_channel *ch,
4681201882Skeramida    int async)
4682198429Srpaulo{
4683198429Srpaulo	struct iwn5000_cmd_txpower cmd;
4684198429Srpaulo
4685253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
4686253705Sadrian
4687198429Srpaulo	/*
4688198429Srpaulo	 * TX power calibration is handled automatically by the firmware
4689198429Srpaulo	 * for 5000 Series.
4690198429Srpaulo	 */
4691198429Srpaulo	memset(&cmd, 0, sizeof cmd);
4692198429Srpaulo	cmd.global_limit = 2 * IWN5000_TXPOWER_MAX_DBM;	/* 16 dBm */
4693198429Srpaulo	cmd.flags = IWN5000_TXPOWER_NO_CLOSED;
4694198429Srpaulo	cmd.srv_limit = IWN5000_TXPOWER_AUTO;
4695198429Srpaulo	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: setting TX power\n", __func__);
4696198429Srpaulo	return iwn_cmd(sc, IWN_CMD_TXPOWER_DBM, &cmd, sizeof cmd, async);
4697198429Srpaulo}
4698198429Srpaulo
4699178676Ssam/*
4700198429Srpaulo * Retrieve the maximum RSSI (in dBm) among receivers.
4701178676Ssam */
4702206477Sbschmidtstatic int
4703198429Srpauloiwn4965_get_rssi(struct iwn_softc *sc, struct iwn_rx_stat *stat)
4704178676Ssam{
4705198429Srpaulo	struct iwn4965_rx_phystat *phy = (void *)stat->phybuf;
4706198429Srpaulo	uint8_t mask, agc;
4707198429Srpaulo	int rssi;
4708178676Ssam
4709253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
4710253705Sadrian
4711201209Srpaulo	mask = (le16toh(phy->antenna) >> 4) & IWN_ANT_ABC;
4712198429Srpaulo	agc  = (le16toh(phy->agc) >> 7) & 0x7f;
4713178676Ssam
4714178676Ssam	rssi = 0;
4715220689Sbschmidt	if (mask & IWN_ANT_A)
4716220689Sbschmidt		rssi = MAX(rssi, phy->rssi[0]);
4717220689Sbschmidt	if (mask & IWN_ANT_B)
4718220689Sbschmidt		rssi = MAX(rssi, phy->rssi[2]);
4719220689Sbschmidt	if (mask & IWN_ANT_C)
4720220689Sbschmidt		rssi = MAX(rssi, phy->rssi[4]);
4721198429Srpaulo
4722220724Sbschmidt	DPRINTF(sc, IWN_DEBUG_RECV,
4723220724Sbschmidt	    "%s: agc %d mask 0x%x rssi %d %d %d result %d\n", __func__, agc,
4724220724Sbschmidt	    mask, phy->rssi[0], phy->rssi[2], phy->rssi[4],
4725178676Ssam	    rssi - agc - IWN_RSSI_TO_DBM);
4726178676Ssam	return rssi - agc - IWN_RSSI_TO_DBM;
4727178676Ssam}
4728178676Ssam
4729206477Sbschmidtstatic int
4730198429Srpauloiwn5000_get_rssi(struct iwn_softc *sc, struct iwn_rx_stat *stat)
4731198429Srpaulo{
4732198429Srpaulo	struct iwn5000_rx_phystat *phy = (void *)stat->phybuf;
4733220723Sbschmidt	uint8_t agc;
4734198429Srpaulo	int rssi;
4735198429Srpaulo
4736253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
4737253705Sadrian
4738198429Srpaulo	agc = (le32toh(phy->agc) >> 9) & 0x7f;
4739198429Srpaulo
4740198429Srpaulo	rssi = MAX(le16toh(phy->rssi[0]) & 0xff,
4741198429Srpaulo		   le16toh(phy->rssi[1]) & 0xff);
4742198429Srpaulo	rssi = MAX(le16toh(phy->rssi[2]) & 0xff, rssi);
4743198429Srpaulo
4744220724Sbschmidt	DPRINTF(sc, IWN_DEBUG_RECV,
4745220724Sbschmidt	    "%s: agc %d rssi %d %d %d result %d\n", __func__, agc,
4746201822Strasz	    phy->rssi[0], phy->rssi[1], phy->rssi[2],
4747198429Srpaulo	    rssi - agc - IWN_RSSI_TO_DBM);
4748198429Srpaulo	return rssi - agc - IWN_RSSI_TO_DBM;
4749198429Srpaulo}
4750198429Srpaulo
4751178676Ssam/*
4752198429Srpaulo * Retrieve the average noise (in dBm) among receivers.
4753178676Ssam */
4754206477Sbschmidtstatic int
4755178676Ssamiwn_get_noise(const struct iwn_rx_general_stats *stats)
4756178676Ssam{
4757178676Ssam	int i, total, nbant, noise;
4758178676Ssam
4759178676Ssam	total = nbant = 0;
4760178676Ssam	for (i = 0; i < 3; i++) {
4761198429Srpaulo		if ((noise = le32toh(stats->noise[i]) & 0xff) == 0)
4762198429Srpaulo			continue;
4763198429Srpaulo		total += noise;
4764198429Srpaulo		nbant++;
4765178676Ssam	}
4766198429Srpaulo	/* There should be at least one antenna but check anyway. */
4767178676Ssam	return (nbant == 0) ? -127 : (total / nbant) - 107;
4768178676Ssam}
4769178676Ssam
4770178676Ssam/*
4771198429Srpaulo * Compute temperature (in degC) from last received statistics.
4772178676Ssam */
4773206477Sbschmidtstatic int
4774198429Srpauloiwn4965_get_temperature(struct iwn_softc *sc)
4775178676Ssam{
4776178676Ssam	struct iwn_ucode_info *uc = &sc->ucode_info;
4777178676Ssam	int32_t r1, r2, r3, r4, temp;
4778178676Ssam
4779253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
4780253705Sadrian
4781178676Ssam	r1 = le32toh(uc->temp[0].chan20MHz);
4782178676Ssam	r2 = le32toh(uc->temp[1].chan20MHz);
4783178676Ssam	r3 = le32toh(uc->temp[2].chan20MHz);
4784178676Ssam	r4 = le32toh(sc->rawtemp);
4785178676Ssam
4786220725Sbschmidt	if (r1 == r3)	/* Prevents division by 0 (should not happen). */
4787178676Ssam		return 0;
4788178676Ssam
4789198429Srpaulo	/* Sign-extend 23-bit R4 value to 32-bit. */
4790220659Sbschmidt	r4 = ((r4 & 0xffffff) ^ 0x800000) - 0x800000;
4791198429Srpaulo	/* Compute temperature in Kelvin. */
4792178676Ssam	temp = (259 * (r4 - r2)) / (r3 - r1);
4793178676Ssam	temp = (temp * 97) / 100 + 8;
4794178676Ssam
4795201209Srpaulo	DPRINTF(sc, IWN_DEBUG_ANY, "temperature %dK/%dC\n", temp,
4796201209Srpaulo	    IWN_KTOC(temp));
4797178676Ssam	return IWN_KTOC(temp);
4798178676Ssam}
4799178676Ssam
4800206477Sbschmidtstatic int
4801198429Srpauloiwn5000_get_temperature(struct iwn_softc *sc)
4802198429Srpaulo{
4803201209Srpaulo	int32_t temp;
4804201209Srpaulo
4805253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
4806253705Sadrian
4807198429Srpaulo	/*
4808198429Srpaulo	 * Temperature is not used by the driver for 5000 Series because
4809220725Sbschmidt	 * TX power calibration is handled by firmware.
4810198429Srpaulo	 */
4811201209Srpaulo	temp = le32toh(sc->rawtemp);
4812201209Srpaulo	if (sc->hw_type == IWN_HW_REV_TYPE_5150) {
4813201209Srpaulo		temp = (temp / -5) + sc->temp_off;
4814201209Srpaulo		temp = IWN_KTOC(temp);
4815201209Srpaulo	}
4816201209Srpaulo	return temp;
4817198429Srpaulo}
4818198429Srpaulo
4819178676Ssam/*
4820178676Ssam * Initialize sensitivity calibration state machine.
4821178676Ssam */
4822206477Sbschmidtstatic int
4823178676Ssamiwn_init_sensitivity(struct iwn_softc *sc)
4824178676Ssam{
4825220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
4826178676Ssam	struct iwn_calib_state *calib = &sc->calib;
4827198429Srpaulo	uint32_t flags;
4828178676Ssam	int error;
4829178676Ssam
4830253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
4831253705Sadrian
4832198429Srpaulo	/* Reset calibration state machine. */
4833178676Ssam	memset(calib, 0, sizeof (*calib));
4834178676Ssam	calib->state = IWN_CALIB_STATE_INIT;
4835178676Ssam	calib->cck_state = IWN_CCK_STATE_HIFA;
4836198429Srpaulo	/* Set initial correlation values. */
4837201209Srpaulo	calib->ofdm_x1     = sc->limits->min_ofdm_x1;
4838201209Srpaulo	calib->ofdm_mrc_x1 = sc->limits->min_ofdm_mrc_x1;
4839206444Sbschmidt	calib->ofdm_x4     = sc->limits->min_ofdm_x4;
4840201209Srpaulo	calib->ofdm_mrc_x4 = sc->limits->min_ofdm_mrc_x4;
4841198429Srpaulo	calib->cck_x4      = 125;
4842201209Srpaulo	calib->cck_mrc_x4  = sc->limits->min_cck_mrc_x4;
4843201209Srpaulo	calib->energy_cck  = sc->limits->energy_cck;
4844178676Ssam
4845198429Srpaulo	/* Write initial sensitivity. */
4846220726Sbschmidt	if ((error = iwn_send_sensitivity(sc)) != 0)
4847178676Ssam		return error;
4848178676Ssam
4849198429Srpaulo	/* Write initial gains. */
4850220728Sbschmidt	if ((error = ops->init_gains(sc)) != 0)
4851198429Srpaulo		return error;
4852198429Srpaulo
4853198429Srpaulo	/* Request statistics at each beacon interval. */
4854198429Srpaulo	flags = 0;
4855220724Sbschmidt	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: sending request for statistics\n",
4856220724Sbschmidt	    __func__);
4857198429Srpaulo	return iwn_cmd(sc, IWN_CMD_GET_STATISTICS, &flags, sizeof flags, 1);
4858178676Ssam}
4859178676Ssam
4860178676Ssam/*
4861178676Ssam * Collect noise and RSSI statistics for the first 20 beacons received
4862178676Ssam * after association and use them to determine connected antennas and
4863198429Srpaulo * to set differential gains.
4864178676Ssam */
4865206477Sbschmidtstatic void
4866198429Srpauloiwn_collect_noise(struct iwn_softc *sc,
4867178676Ssam    const struct iwn_rx_general_stats *stats)
4868178676Ssam{
4869220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
4870178676Ssam	struct iwn_calib_state *calib = &sc->calib;
4871252717Sadrian	struct ifnet *ifp = sc->sc_ifp;
4872252717Sadrian	struct ieee80211com *ic = ifp->if_l2com;
4873198429Srpaulo	uint32_t val;
4874198429Srpaulo	int i;
4875178676Ssam
4876253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
4877253705Sadrian
4878198429Srpaulo	/* Accumulate RSSI and noise for all 3 antennas. */
4879178676Ssam	for (i = 0; i < 3; i++) {
4880178676Ssam		calib->rssi[i] += le32toh(stats->rssi[i]) & 0xff;
4881178676Ssam		calib->noise[i] += le32toh(stats->noise[i]) & 0xff;
4882178676Ssam	}
4883198429Srpaulo	/* NB: We update differential gains only once after 20 beacons. */
4884178676Ssam	if (++calib->nbeacons < 20)
4885178676Ssam		return;
4886178676Ssam
4887198429Srpaulo	/* Determine highest average RSSI. */
4888198429Srpaulo	val = MAX(calib->rssi[0], calib->rssi[1]);
4889198429Srpaulo	val = MAX(calib->rssi[2], val);
4890178676Ssam
4891198429Srpaulo	/* Determine which antennas are connected. */
4892210110Sbschmidt	sc->chainmask = sc->rxchainmask;
4893178676Ssam	for (i = 0; i < 3; i++)
4894210110Sbschmidt		if (val - calib->rssi[i] > 15 * 20)
4895210110Sbschmidt			sc->chainmask &= ~(1 << i);
4896210110Sbschmidt	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4897210110Sbschmidt	    "%s: RX chains mask: theoretical=0x%x, actual=0x%x\n",
4898210110Sbschmidt	    __func__, sc->rxchainmask, sc->chainmask);
4899210110Sbschmidt
4900198429Srpaulo	/* If none of the TX antennas are connected, keep at least one. */
4901201209Srpaulo	if ((sc->chainmask & sc->txchainmask) == 0)
4902201209Srpaulo		sc->chainmask |= IWN_LSB(sc->txchainmask);
4903178676Ssam
4904220728Sbschmidt	(void)ops->set_gains(sc);
4905198429Srpaulo	calib->state = IWN_CALIB_STATE_RUN;
4906198429Srpaulo
4907198429Srpaulo#ifdef notyet
4908198429Srpaulo	/* XXX Disable RX chains with no antennas connected. */
4909254204Sadrian	sc->rxon->rxchain = htole16(IWN_RXCHAIN_SEL(sc->chainmask));
4910254204Sadrian	(void)iwn_cmd(sc, IWN_CMD_RXON, sc->rxon, sc->rxonsz, 1);
4911198429Srpaulo#endif
4912198429Srpaulo
4913198429Srpaulo	/* Enable power-saving mode if requested by user. */
4914252717Sadrian	if (ic->ic_flags & IEEE80211_F_PMGTON)
4915198429Srpaulo		(void)iwn_set_pslevel(sc, 0, 3, 1);
4916253705Sadrian
4917253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
4918253705Sadrian
4919198429Srpaulo}
4920198429Srpaulo
4921206477Sbschmidtstatic int
4922198429Srpauloiwn4965_init_gains(struct iwn_softc *sc)
4923198429Srpaulo{
4924198429Srpaulo	struct iwn_phy_calib_gain cmd;
4925198429Srpaulo
4926253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
4927253705Sadrian
4928198429Srpaulo	memset(&cmd, 0, sizeof cmd);
4929198429Srpaulo	cmd.code = IWN4965_PHY_CALIB_DIFF_GAIN;
4930198429Srpaulo	/* Differential gains initially set to 0 for all 3 antennas. */
4931198429Srpaulo	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4932198429Srpaulo	    "%s: setting initial differential gains\n", __func__);
4933198429Srpaulo	return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1);
4934198429Srpaulo}
4935198429Srpaulo
4936206477Sbschmidtstatic int
4937198429Srpauloiwn5000_init_gains(struct iwn_softc *sc)
4938198429Srpaulo{
4939198429Srpaulo	struct iwn_phy_calib cmd;
4940198429Srpaulo
4941253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
4942253705Sadrian
4943198429Srpaulo	memset(&cmd, 0, sizeof cmd);
4944220866Sbschmidt	cmd.code = sc->reset_noise_gain;
4945198429Srpaulo	cmd.ngroups = 1;
4946198429Srpaulo	cmd.isvalid = 1;
4947198429Srpaulo	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4948198429Srpaulo	    "%s: setting initial differential gains\n", __func__);
4949198429Srpaulo	return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1);
4950198429Srpaulo}
4951198429Srpaulo
4952206477Sbschmidtstatic int
4953198429Srpauloiwn4965_set_gains(struct iwn_softc *sc)
4954198429Srpaulo{
4955198429Srpaulo	struct iwn_calib_state *calib = &sc->calib;
4956198429Srpaulo	struct iwn_phy_calib_gain cmd;
4957198429Srpaulo	int i, delta, noise;
4958198429Srpaulo
4959253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
4960253705Sadrian
4961198429Srpaulo	/* Get minimal noise among connected antennas. */
4962201209Srpaulo	noise = INT_MAX;	/* NB: There's at least one antenna. */
4963178676Ssam	for (i = 0; i < 3; i++)
4964201209Srpaulo		if (sc->chainmask & (1 << i))
4965198429Srpaulo			noise = MIN(calib->noise[i], noise);
4966178676Ssam
4967178676Ssam	memset(&cmd, 0, sizeof cmd);
4968198429Srpaulo	cmd.code = IWN4965_PHY_CALIB_DIFF_GAIN;
4969198429Srpaulo	/* Set differential gains for connected antennas. */
4970178676Ssam	for (i = 0; i < 3; i++) {
4971201209Srpaulo		if (sc->chainmask & (1 << i)) {
4972198429Srpaulo			/* Compute attenuation (in unit of 1.5dB). */
4973198429Srpaulo			delta = (noise - (int32_t)calib->noise[i]) / 30;
4974198429Srpaulo			/* NB: delta <= 0 */
4975198429Srpaulo			/* Limit to [-4.5dB,0]. */
4976198429Srpaulo			cmd.gain[i] = MIN(abs(delta), 3);
4977198429Srpaulo			if (delta < 0)
4978198429Srpaulo				cmd.gain[i] |= 1 << 2;	/* sign bit */
4979178676Ssam		}
4980178676Ssam	}
4981178676Ssam	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4982198429Srpaulo	    "setting differential gains Ant A/B/C: %x/%x/%x (%x)\n",
4983201209Srpaulo	    cmd.gain[0], cmd.gain[1], cmd.gain[2], sc->chainmask);
4984198429Srpaulo	return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1);
4985178676Ssam}
4986178676Ssam
4987206477Sbschmidtstatic int
4988198429Srpauloiwn5000_set_gains(struct iwn_softc *sc)
4989198429Srpaulo{
4990198429Srpaulo	struct iwn_calib_state *calib = &sc->calib;
4991198429Srpaulo	struct iwn_phy_calib_gain cmd;
4992220723Sbschmidt	int i, ant, div, delta;
4993198429Srpaulo
4994253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
4995253705Sadrian
4996206444Sbschmidt	/* We collected 20 beacons and !=6050 need a 1.5 factor. */
4997206444Sbschmidt	div = (sc->hw_type == IWN_HW_REV_TYPE_6050) ? 20 : 30;
4998198429Srpaulo
4999198429Srpaulo	memset(&cmd, 0, sizeof cmd);
5000220866Sbschmidt	cmd.code = sc->noise_gain;
5001198429Srpaulo	cmd.ngroups = 1;
5002198429Srpaulo	cmd.isvalid = 1;
5003201209Srpaulo	/* Get first available RX antenna as referential. */
5004201209Srpaulo	ant = IWN_LSB(sc->rxchainmask);
5005201209Srpaulo	/* Set differential gains for other antennas. */
5006201209Srpaulo	for (i = ant + 1; i < 3; i++) {
5007201209Srpaulo		if (sc->chainmask & (1 << i)) {
5008201209Srpaulo			/* The delta is relative to antenna "ant". */
5009201209Srpaulo			delta = ((int32_t)calib->noise[ant] -
5010206444Sbschmidt			    (int32_t)calib->noise[i]) / div;
5011198429Srpaulo			/* Limit to [-4.5dB,+4.5dB]. */
5012198429Srpaulo			cmd.gain[i - 1] = MIN(abs(delta), 3);
5013198429Srpaulo			if (delta < 0)
5014198429Srpaulo				cmd.gain[i - 1] |= 1 << 2;	/* sign bit */
5015198429Srpaulo		}
5016198429Srpaulo	}
5017198429Srpaulo	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
5018198429Srpaulo	    "setting differential gains Ant B/C: %x/%x (%x)\n",
5019201209Srpaulo	    cmd.gain[0], cmd.gain[1], sc->chainmask);
5020198429Srpaulo	return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1);
5021198429Srpaulo}
5022198429Srpaulo
5023178676Ssam/*
5024198429Srpaulo * Tune RF RX sensitivity based on the number of false alarms detected
5025178676Ssam * during the last beacon period.
5026178676Ssam */
5027206477Sbschmidtstatic void
5028178676Ssamiwn_tune_sensitivity(struct iwn_softc *sc, const struct iwn_rx_stats *stats)
5029178676Ssam{
5030198429Srpaulo#define inc(val, inc, max)			\
5031178676Ssam	if ((val) < (max)) {			\
5032178676Ssam		if ((val) < (max) - (inc))	\
5033178676Ssam			(val) += (inc);		\
5034178676Ssam		else				\
5035178676Ssam			(val) = (max);		\
5036178676Ssam		needs_update = 1;		\
5037178676Ssam	}
5038198429Srpaulo#define dec(val, dec, min)			\
5039178676Ssam	if ((val) > (min)) {			\
5040178676Ssam		if ((val) > (min) + (dec))	\
5041178676Ssam			(val) -= (dec);		\
5042178676Ssam		else				\
5043178676Ssam			(val) = (min);		\
5044178676Ssam		needs_update = 1;		\
5045178676Ssam	}
5046178676Ssam
5047201209Srpaulo	const struct iwn_sensitivity_limits *limits = sc->limits;
5048178676Ssam	struct iwn_calib_state *calib = &sc->calib;
5049178676Ssam	uint32_t val, rxena, fa;
5050178676Ssam	uint32_t energy[3], energy_min;
5051198439Srpaulo	uint8_t noise[3], noise_ref;
5052198429Srpaulo	int i, needs_update = 0;
5053178676Ssam
5054253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
5055253705Sadrian
5056198429Srpaulo	/* Check that we've been enabled long enough. */
5057253705Sadrian	if ((rxena = le32toh(stats->general.load)) == 0){
5058253705Sadrian		DPRINTF(sc, IWN_DEBUG_TRACE, "->%s end not so long\n", __func__);
5059178676Ssam		return;
5060253705Sadrian	}
5061178676Ssam
5062198429Srpaulo	/* Compute number of false alarms since last call for OFDM. */
5063178676Ssam	fa  = le32toh(stats->ofdm.bad_plcp) - calib->bad_plcp_ofdm;
5064178676Ssam	fa += le32toh(stats->ofdm.fa) - calib->fa_ofdm;
5065220634Sbschmidt	fa *= 200 * IEEE80211_DUR_TU;	/* 200TU */
5066178676Ssam
5067198429Srpaulo	/* Save counters values for next call. */
5068178676Ssam	calib->bad_plcp_ofdm = le32toh(stats->ofdm.bad_plcp);
5069178676Ssam	calib->fa_ofdm = le32toh(stats->ofdm.fa);
5070178676Ssam
5071178676Ssam	if (fa > 50 * rxena) {
5072198429Srpaulo		/* High false alarm count, decrease sensitivity. */
5073178676Ssam		DPRINTF(sc, IWN_DEBUG_CALIBRATE,
5074178676Ssam		    "%s: OFDM high false alarm count: %u\n", __func__, fa);
5075198429Srpaulo		inc(calib->ofdm_x1,     1, limits->max_ofdm_x1);
5076198429Srpaulo		inc(calib->ofdm_mrc_x1, 1, limits->max_ofdm_mrc_x1);
5077198429Srpaulo		inc(calib->ofdm_x4,     1, limits->max_ofdm_x4);
5078198429Srpaulo		inc(calib->ofdm_mrc_x4, 1, limits->max_ofdm_mrc_x4);
5079178676Ssam
5080178676Ssam	} else if (fa < 5 * rxena) {
5081198429Srpaulo		/* Low false alarm count, increase sensitivity. */
5082178676Ssam		DPRINTF(sc, IWN_DEBUG_CALIBRATE,
5083178676Ssam		    "%s: OFDM low false alarm count: %u\n", __func__, fa);
5084198429Srpaulo		dec(calib->ofdm_x1,     1, limits->min_ofdm_x1);
5085198429Srpaulo		dec(calib->ofdm_mrc_x1, 1, limits->min_ofdm_mrc_x1);
5086198429Srpaulo		dec(calib->ofdm_x4,     1, limits->min_ofdm_x4);
5087198429Srpaulo		dec(calib->ofdm_mrc_x4, 1, limits->min_ofdm_mrc_x4);
5088178676Ssam	}
5089178676Ssam
5090198429Srpaulo	/* Compute maximum noise among 3 receivers. */
5091178676Ssam	for (i = 0; i < 3; i++)
5092178676Ssam		noise[i] = (le32toh(stats->general.noise[i]) >> 8) & 0xff;
5093198429Srpaulo	val = MAX(noise[0], noise[1]);
5094198429Srpaulo	val = MAX(noise[2], val);
5095198429Srpaulo	/* Insert it into our samples table. */
5096178676Ssam	calib->noise_samples[calib->cur_noise_sample] = val;
5097178676Ssam	calib->cur_noise_sample = (calib->cur_noise_sample + 1) % 20;
5098178676Ssam
5099198429Srpaulo	/* Compute maximum noise among last 20 samples. */
5100178676Ssam	noise_ref = calib->noise_samples[0];
5101178676Ssam	for (i = 1; i < 20; i++)
5102198429Srpaulo		noise_ref = MAX(noise_ref, calib->noise_samples[i]);
5103178676Ssam
5104198429Srpaulo	/* Compute maximum energy among 3 receivers. */
5105178676Ssam	for (i = 0; i < 3; i++)
5106178676Ssam		energy[i] = le32toh(stats->general.energy[i]);
5107198429Srpaulo	val = MIN(energy[0], energy[1]);
5108198429Srpaulo	val = MIN(energy[2], val);
5109198429Srpaulo	/* Insert it into our samples table. */
5110178676Ssam	calib->energy_samples[calib->cur_energy_sample] = val;
5111178676Ssam	calib->cur_energy_sample = (calib->cur_energy_sample + 1) % 10;
5112178676Ssam
5113198429Srpaulo	/* Compute minimum energy among last 10 samples. */
5114178676Ssam	energy_min = calib->energy_samples[0];
5115178676Ssam	for (i = 1; i < 10; i++)
5116198429Srpaulo		energy_min = MAX(energy_min, calib->energy_samples[i]);
5117178676Ssam	energy_min += 6;
5118178676Ssam
5119198429Srpaulo	/* Compute number of false alarms since last call for CCK. */
5120178676Ssam	fa  = le32toh(stats->cck.bad_plcp) - calib->bad_plcp_cck;
5121178676Ssam	fa += le32toh(stats->cck.fa) - calib->fa_cck;
5122220634Sbschmidt	fa *= 200 * IEEE80211_DUR_TU;	/* 200TU */
5123178676Ssam
5124198429Srpaulo	/* Save counters values for next call. */
5125178676Ssam	calib->bad_plcp_cck = le32toh(stats->cck.bad_plcp);
5126178676Ssam	calib->fa_cck = le32toh(stats->cck.fa);
5127178676Ssam
5128178676Ssam	if (fa > 50 * rxena) {
5129198429Srpaulo		/* High false alarm count, decrease sensitivity. */
5130178676Ssam		DPRINTF(sc, IWN_DEBUG_CALIBRATE,
5131178676Ssam		    "%s: CCK high false alarm count: %u\n", __func__, fa);
5132178676Ssam		calib->cck_state = IWN_CCK_STATE_HIFA;
5133178676Ssam		calib->low_fa = 0;
5134178676Ssam
5135198429Srpaulo		if (calib->cck_x4 > 160) {
5136178676Ssam			calib->noise_ref = noise_ref;
5137178676Ssam			if (calib->energy_cck > 2)
5138198429Srpaulo				dec(calib->energy_cck, 2, energy_min);
5139178676Ssam		}
5140198429Srpaulo		if (calib->cck_x4 < 160) {
5141198429Srpaulo			calib->cck_x4 = 161;
5142178676Ssam			needs_update = 1;
5143178676Ssam		} else
5144198429Srpaulo			inc(calib->cck_x4, 3, limits->max_cck_x4);
5145178676Ssam
5146198429Srpaulo		inc(calib->cck_mrc_x4, 3, limits->max_cck_mrc_x4);
5147178676Ssam
5148178676Ssam	} else if (fa < 5 * rxena) {
5149198429Srpaulo		/* Low false alarm count, increase sensitivity. */
5150178676Ssam		DPRINTF(sc, IWN_DEBUG_CALIBRATE,
5151178676Ssam		    "%s: CCK low false alarm count: %u\n", __func__, fa);
5152178676Ssam		calib->cck_state = IWN_CCK_STATE_LOFA;
5153178676Ssam		calib->low_fa++;
5154178676Ssam
5155198429Srpaulo		if (calib->cck_state != IWN_CCK_STATE_INIT &&
5156198429Srpaulo		    (((int32_t)calib->noise_ref - (int32_t)noise_ref) > 2 ||
5157220726Sbschmidt		     calib->low_fa > 100)) {
5158198429Srpaulo			inc(calib->energy_cck, 2, limits->min_energy_cck);
5159198429Srpaulo			dec(calib->cck_x4,     3, limits->min_cck_x4);
5160198429Srpaulo			dec(calib->cck_mrc_x4, 3, limits->min_cck_mrc_x4);
5161178676Ssam		}
5162178676Ssam	} else {
5163198429Srpaulo		/* Not worth to increase or decrease sensitivity. */
5164178676Ssam		DPRINTF(sc, IWN_DEBUG_CALIBRATE,
5165178676Ssam		    "%s: CCK normal false alarm count: %u\n", __func__, fa);
5166178676Ssam		calib->low_fa = 0;
5167178676Ssam		calib->noise_ref = noise_ref;
5168178676Ssam
5169178676Ssam		if (calib->cck_state == IWN_CCK_STATE_HIFA) {
5170198429Srpaulo			/* Previous interval had many false alarms. */
5171198429Srpaulo			dec(calib->energy_cck, 8, energy_min);
5172178676Ssam		}
5173178676Ssam		calib->cck_state = IWN_CCK_STATE_INIT;
5174178676Ssam	}
5175178676Ssam
5176178676Ssam	if (needs_update)
5177178676Ssam		(void)iwn_send_sensitivity(sc);
5178253705Sadrian
5179253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
5180253705Sadrian
5181198429Srpaulo#undef dec
5182198429Srpaulo#undef inc
5183178676Ssam}
5184178676Ssam
5185206477Sbschmidtstatic int
5186178676Ssamiwn_send_sensitivity(struct iwn_softc *sc)
5187178676Ssam{
5188178676Ssam	struct iwn_calib_state *calib = &sc->calib;
5189220729Sbschmidt	struct iwn_enhanced_sensitivity_cmd cmd;
5190220729Sbschmidt	int len;
5191178676Ssam
5192178676Ssam	memset(&cmd, 0, sizeof cmd);
5193220729Sbschmidt	len = sizeof (struct iwn_sensitivity_cmd);
5194178676Ssam	cmd.which = IWN_SENSITIVITY_WORKTBL;
5195198429Srpaulo	/* OFDM modulation. */
5196220726Sbschmidt	cmd.corr_ofdm_x1       = htole16(calib->ofdm_x1);
5197220726Sbschmidt	cmd.corr_ofdm_mrc_x1   = htole16(calib->ofdm_mrc_x1);
5198220726Sbschmidt	cmd.corr_ofdm_x4       = htole16(calib->ofdm_x4);
5199220726Sbschmidt	cmd.corr_ofdm_mrc_x4   = htole16(calib->ofdm_mrc_x4);
5200220726Sbschmidt	cmd.energy_ofdm        = htole16(sc->limits->energy_ofdm);
5201220726Sbschmidt	cmd.energy_ofdm_th     = htole16(62);
5202198429Srpaulo	/* CCK modulation. */
5203220726Sbschmidt	cmd.corr_cck_x4        = htole16(calib->cck_x4);
5204220726Sbschmidt	cmd.corr_cck_mrc_x4    = htole16(calib->cck_mrc_x4);
5205220726Sbschmidt	cmd.energy_cck         = htole16(calib->energy_cck);
5206198429Srpaulo	/* Barker modulation: use default values. */
5207220726Sbschmidt	cmd.corr_barker        = htole16(190);
5208220726Sbschmidt	cmd.corr_barker_mrc    = htole16(390);
5209178676Ssam
5210202986Srpaulo	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
5211178676Ssam	    "%s: set sensitivity %d/%d/%d/%d/%d/%d/%d\n", __func__,
5212198429Srpaulo	    calib->ofdm_x1, calib->ofdm_mrc_x1, calib->ofdm_x4,
5213198429Srpaulo	    calib->ofdm_mrc_x4, calib->cck_x4,
5214198429Srpaulo	    calib->cck_mrc_x4, calib->energy_cck);
5215220729Sbschmidt
5216220729Sbschmidt	if (!(sc->sc_flags & IWN_FLAG_ENH_SENS))
5217220729Sbschmidt		goto send;
5218220729Sbschmidt	/* Enhanced sensitivity settings. */
5219220729Sbschmidt	len = sizeof (struct iwn_enhanced_sensitivity_cmd);
5220220729Sbschmidt	cmd.ofdm_det_slope_mrc = htole16(668);
5221220729Sbschmidt	cmd.ofdm_det_icept_mrc = htole16(4);
5222220729Sbschmidt	cmd.ofdm_det_slope     = htole16(486);
5223220729Sbschmidt	cmd.ofdm_det_icept     = htole16(37);
5224220729Sbschmidt	cmd.cck_det_slope_mrc  = htole16(853);
5225220729Sbschmidt	cmd.cck_det_icept_mrc  = htole16(4);
5226220729Sbschmidt	cmd.cck_det_slope      = htole16(476);
5227220729Sbschmidt	cmd.cck_det_icept      = htole16(99);
5228220729Sbschmidtsend:
5229220729Sbschmidt	return iwn_cmd(sc, IWN_CMD_SET_SENSITIVITY, &cmd, len, 1);
5230178676Ssam}
5231178676Ssam
5232198429Srpaulo/*
5233198429Srpaulo * Set STA mode power saving level (between 0 and 5).
5234198429Srpaulo * Level 0 is CAM (Continuously Aware Mode), 5 is for maximum power saving.
5235198429Srpaulo */
5236206477Sbschmidtstatic int
5237198429Srpauloiwn_set_pslevel(struct iwn_softc *sc, int dtim, int level, int async)
5238198429Srpaulo{
5239220723Sbschmidt	struct iwn_pmgt_cmd cmd;
5240198429Srpaulo	const struct iwn_pmgt *pmgt;
5241198429Srpaulo	uint32_t max, skip_dtim;
5242220721Sbschmidt	uint32_t reg;
5243198429Srpaulo	int i;
5244198429Srpaulo
5245252727Sadrian	DPRINTF(sc, IWN_DEBUG_PWRSAVE,
5246252727Sadrian	    "%s: dtim=%d, level=%d, async=%d\n",
5247252727Sadrian	    __func__,
5248252727Sadrian	    dtim,
5249252727Sadrian	    level,
5250252727Sadrian	    async);
5251252727Sadrian
5252198429Srpaulo	/* Select which PS parameters to use. */
5253198429Srpaulo	if (dtim <= 2)
5254198429Srpaulo		pmgt = &iwn_pmgt[0][level];
5255198429Srpaulo	else if (dtim <= 10)
5256198429Srpaulo		pmgt = &iwn_pmgt[1][level];
5257198429Srpaulo	else
5258198429Srpaulo		pmgt = &iwn_pmgt[2][level];
5259198429Srpaulo
5260198429Srpaulo	memset(&cmd, 0, sizeof cmd);
5261198429Srpaulo	if (level != 0)	/* not CAM */
5262198429Srpaulo		cmd.flags |= htole16(IWN_PS_ALLOW_SLEEP);
5263198429Srpaulo	if (level == 5)
5264198429Srpaulo		cmd.flags |= htole16(IWN_PS_FAST_PD);
5265201209Srpaulo	/* Retrieve PCIe Active State Power Management (ASPM). */
5266220721Sbschmidt	reg = pci_read_config(sc->sc_dev, sc->sc_cap_off + 0x10, 1);
5267220721Sbschmidt	if (!(reg & 0x1))	/* L0s Entry disabled. */
5268198429Srpaulo		cmd.flags |= htole16(IWN_PS_PCI_PMGT);
5269198429Srpaulo	cmd.rxtimeout = htole32(pmgt->rxtimeout * 1024);
5270198429Srpaulo	cmd.txtimeout = htole32(pmgt->txtimeout * 1024);
5271198429Srpaulo
5272198429Srpaulo	if (dtim == 0) {
5273198429Srpaulo		dtim = 1;
5274198429Srpaulo		skip_dtim = 0;
5275198429Srpaulo	} else
5276198429Srpaulo		skip_dtim = pmgt->skip_dtim;
5277198429Srpaulo	if (skip_dtim != 0) {
5278198429Srpaulo		cmd.flags |= htole16(IWN_PS_SLEEP_OVER_DTIM);
5279198429Srpaulo		max = pmgt->intval[4];
5280198429Srpaulo		if (max == (uint32_t)-1)
5281198429Srpaulo			max = dtim * (skip_dtim + 1);
5282198429Srpaulo		else if (max > dtim)
5283198429Srpaulo			max = (max / dtim) * dtim;
5284198429Srpaulo	} else
5285198429Srpaulo		max = dtim;
5286198429Srpaulo	for (i = 0; i < 5; i++)
5287198429Srpaulo		cmd.intval[i] = htole32(MIN(max, pmgt->intval[i]));
5288198429Srpaulo
5289198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "setting power saving level to %d\n",
5290198429Srpaulo	    level);
5291198429Srpaulo	return iwn_cmd(sc, IWN_CMD_SET_POWER_MODE, &cmd, sizeof cmd, async);
5292198429Srpaulo}
5293198429Srpaulo
5294206477Sbschmidtstatic int
5295220662Sbschmidtiwn_send_btcoex(struct iwn_softc *sc)
5296220662Sbschmidt{
5297220662Sbschmidt	struct iwn_bluetooth cmd;
5298220662Sbschmidt
5299220662Sbschmidt	memset(&cmd, 0, sizeof cmd);
5300220662Sbschmidt	cmd.flags = IWN_BT_COEX_CHAN_ANN | IWN_BT_COEX_BT_PRIO;
5301220662Sbschmidt	cmd.lead_time = IWN_BT_LEAD_TIME_DEF;
5302220662Sbschmidt	cmd.max_kill = IWN_BT_MAX_KILL_DEF;
5303220662Sbschmidt	DPRINTF(sc, IWN_DEBUG_RESET, "%s: configuring bluetooth coexistence\n",
5304220662Sbschmidt	    __func__);
5305220662Sbschmidt	return iwn_cmd(sc, IWN_CMD_BT_COEX, &cmd, sizeof(cmd), 0);
5306220662Sbschmidt}
5307220662Sbschmidt
5308220662Sbschmidtstatic int
5309220891Sbschmidtiwn_send_advanced_btcoex(struct iwn_softc *sc)
5310220891Sbschmidt{
5311220891Sbschmidt	static const uint32_t btcoex_3wire[12] = {
5312220891Sbschmidt		0xaaaaaaaa, 0xaaaaaaaa, 0xaeaaaaaa, 0xaaaaaaaa,
5313220891Sbschmidt		0xcc00ff28, 0x0000aaaa, 0xcc00aaaa, 0x0000aaaa,
5314220891Sbschmidt		0xc0004000, 0x00004000, 0xf0005000, 0xf0005000,
5315220891Sbschmidt	};
5316220891Sbschmidt	struct iwn6000_btcoex_config btconfig;
5317220891Sbschmidt	struct iwn_btcoex_priotable btprio;
5318220891Sbschmidt	struct iwn_btcoex_prot btprot;
5319220891Sbschmidt	int error, i;
5320220891Sbschmidt
5321220891Sbschmidt	memset(&btconfig, 0, sizeof btconfig);
5322220891Sbschmidt	btconfig.flags = 145;
5323220891Sbschmidt	btconfig.max_kill = 5;
5324220891Sbschmidt	btconfig.bt3_t7_timer = 1;
5325220891Sbschmidt	btconfig.kill_ack = htole32(0xffff0000);
5326220891Sbschmidt	btconfig.kill_cts = htole32(0xffff0000);
5327220891Sbschmidt	btconfig.sample_time = 2;
5328220891Sbschmidt	btconfig.bt3_t2_timer = 0xc;
5329220891Sbschmidt	for (i = 0; i < 12; i++)
5330220891Sbschmidt		btconfig.lookup_table[i] = htole32(btcoex_3wire[i]);
5331220891Sbschmidt	btconfig.valid = htole16(0xff);
5332220891Sbschmidt	btconfig.prio_boost = 0xf0;
5333220891Sbschmidt	DPRINTF(sc, IWN_DEBUG_RESET,
5334220891Sbschmidt	    "%s: configuring advanced bluetooth coexistence\n", __func__);
5335220891Sbschmidt	error = iwn_cmd(sc, IWN_CMD_BT_COEX, &btconfig, sizeof(btconfig), 1);
5336220891Sbschmidt	if (error != 0)
5337220891Sbschmidt		return error;
5338220891Sbschmidt
5339220891Sbschmidt	memset(&btprio, 0, sizeof btprio);
5340220891Sbschmidt	btprio.calib_init1 = 0x6;
5341220891Sbschmidt	btprio.calib_init2 = 0x7;
5342220891Sbschmidt	btprio.calib_periodic_low1 = 0x2;
5343220891Sbschmidt	btprio.calib_periodic_low2 = 0x3;
5344220891Sbschmidt	btprio.calib_periodic_high1 = 0x4;
5345220891Sbschmidt	btprio.calib_periodic_high2 = 0x5;
5346220891Sbschmidt	btprio.dtim = 0x6;
5347220891Sbschmidt	btprio.scan52 = 0x8;
5348220891Sbschmidt	btprio.scan24 = 0xa;
5349220891Sbschmidt	error = iwn_cmd(sc, IWN_CMD_BT_COEX_PRIOTABLE, &btprio, sizeof(btprio),
5350220891Sbschmidt	    1);
5351220891Sbschmidt	if (error != 0)
5352220891Sbschmidt		return error;
5353220891Sbschmidt
5354220891Sbschmidt	/* Force BT state machine change. */
5355254206Sadrian	memset(&btprot, 0, sizeof btprot);
5356220891Sbschmidt	btprot.open = 1;
5357220891Sbschmidt	btprot.type = 1;
5358220891Sbschmidt	error = iwn_cmd(sc, IWN_CMD_BT_COEX_PROT, &btprot, sizeof(btprot), 1);
5359220891Sbschmidt	if (error != 0)
5360220891Sbschmidt		return error;
5361220891Sbschmidt	btprot.open = 0;
5362220891Sbschmidt	return iwn_cmd(sc, IWN_CMD_BT_COEX_PROT, &btprot, sizeof(btprot), 1);
5363220891Sbschmidt}
5364220891Sbschmidt
5365220891Sbschmidtstatic int
5366227805Sbschmidtiwn5000_runtime_calib(struct iwn_softc *sc)
5367227805Sbschmidt{
5368227805Sbschmidt	struct iwn5000_calib_config cmd;
5369227805Sbschmidt
5370227805Sbschmidt	memset(&cmd, 0, sizeof cmd);
5371227805Sbschmidt	cmd.ucode.once.enable = 0xffffffff;
5372227805Sbschmidt	cmd.ucode.once.start = IWN5000_CALIB_DC;
5373227805Sbschmidt	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
5374227805Sbschmidt	    "%s: configuring runtime calibration\n", __func__);
5375227805Sbschmidt	return iwn_cmd(sc, IWN5000_CMD_CALIB_CONFIG, &cmd, sizeof(cmd), 0);
5376227805Sbschmidt}
5377227805Sbschmidt
5378227805Sbschmidtstatic int
5379198429Srpauloiwn_config(struct iwn_softc *sc)
5380198429Srpaulo{
5381220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
5382198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
5383198429Srpaulo	struct ieee80211com *ic = ifp->if_l2com;
5384201209Srpaulo	uint32_t txmask;
5385220723Sbschmidt	uint16_t rxchain;
5386198429Srpaulo	int error;
5387198429Srpaulo
5388253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
5389253705Sadrian
5390220676Sbschmidt	if (sc->hw_type == IWN_HW_REV_TYPE_6005) {
5391220676Sbschmidt		/* Set radio temperature sensor offset. */
5392220676Sbschmidt		error = iwn5000_temp_offset_calib(sc);
5393220676Sbschmidt		if (error != 0) {
5394220676Sbschmidt			device_printf(sc->sc_dev,
5395220676Sbschmidt			    "%s: could not set temperature offset\n", __func__);
5396220676Sbschmidt			return error;
5397220676Sbschmidt		}
5398220676Sbschmidt	}
5399220676Sbschmidt
5400227805Sbschmidt	if (sc->hw_type == IWN_HW_REV_TYPE_6050) {
5401227805Sbschmidt		/* Configure runtime DC calibration. */
5402227805Sbschmidt		error = iwn5000_runtime_calib(sc);
5403227805Sbschmidt		if (error != 0) {
5404227805Sbschmidt			device_printf(sc->sc_dev,
5405227805Sbschmidt			    "%s: could not configure runtime calibration\n",
5406227805Sbschmidt			    __func__);
5407227805Sbschmidt			return error;
5408227805Sbschmidt		}
5409227805Sbschmidt	}
5410227805Sbschmidt
5411220725Sbschmidt	/* Configure valid TX chains for >=5000 Series. */
5412201209Srpaulo	if (sc->hw_type != IWN_HW_REV_TYPE_4965) {
5413201209Srpaulo		txmask = htole32(sc->txchainmask);
5414201209Srpaulo		DPRINTF(sc, IWN_DEBUG_RESET,
5415201209Srpaulo		    "%s: configuring valid TX chains 0x%x\n", __func__, txmask);
5416201209Srpaulo		error = iwn_cmd(sc, IWN5000_CMD_TX_ANT_CONFIG, &txmask,
5417201209Srpaulo		    sizeof txmask, 0);
5418201209Srpaulo		if (error != 0) {
5419201209Srpaulo			device_printf(sc->sc_dev,
5420201209Srpaulo			    "%s: could not configure valid TX chains, "
5421201209Srpaulo			    "error %d\n", __func__, error);
5422201209Srpaulo			return error;
5423201209Srpaulo		}
5424198429Srpaulo	}
5425198429Srpaulo
5426198429Srpaulo	/* Configure bluetooth coexistence. */
5427220891Sbschmidt	if (sc->sc_flags & IWN_FLAG_ADV_BTCOEX)
5428220891Sbschmidt		error = iwn_send_advanced_btcoex(sc);
5429220891Sbschmidt	else
5430220891Sbschmidt		error = iwn_send_btcoex(sc);
5431198429Srpaulo	if (error != 0) {
5432198429Srpaulo		device_printf(sc->sc_dev,
5433198429Srpaulo		    "%s: could not configure bluetooth coexistence, error %d\n",
5434198429Srpaulo		    __func__, error);
5435198429Srpaulo		return error;
5436198429Srpaulo	}
5437198429Srpaulo
5438201209Srpaulo	/* Set mode, channel, RX filter and enable RX. */
5439254204Sadrian	sc->rxon = &sc->rx_on[IWN_RXON_BSS_CTX];
5440254204Sadrian	memset(sc->rxon, 0, sizeof (struct iwn_rxon));
5441254204Sadrian	IEEE80211_ADDR_COPY(sc->rxon->myaddr, IF_LLADDR(ifp));
5442254204Sadrian	IEEE80211_ADDR_COPY(sc->rxon->wlap, IF_LLADDR(ifp));
5443254204Sadrian	sc->rxon->chan = ieee80211_chan2ieee(ic, ic->ic_curchan);
5444254204Sadrian	sc->rxon->flags = htole32(IWN_RXON_TSF | IWN_RXON_CTS_TO_SELF);
5445198429Srpaulo	if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan))
5446254204Sadrian		sc->rxon->flags |= htole32(IWN_RXON_AUTO | IWN_RXON_24GHZ);
5447198429Srpaulo	switch (ic->ic_opmode) {
5448198429Srpaulo	case IEEE80211_M_STA:
5449254204Sadrian		sc->rxon->mode = IWN_MODE_STA;
5450254204Sadrian		sc->rxon->filter = htole32(IWN_FILTER_MULTICAST);
5451198429Srpaulo		break;
5452198429Srpaulo	case IEEE80211_M_MONITOR:
5453254204Sadrian		sc->rxon->mode = IWN_MODE_MONITOR;
5454254204Sadrian		sc->rxon->filter = htole32(IWN_FILTER_MULTICAST |
5455198429Srpaulo		    IWN_FILTER_CTL | IWN_FILTER_PROMISC);
5456198429Srpaulo		break;
5457198429Srpaulo	default:
5458198429Srpaulo		/* Should not get there. */
5459198429Srpaulo		break;
5460198429Srpaulo	}
5461254204Sadrian	sc->rxon->cck_mask  = 0x0f;	/* not yet negotiated */
5462254204Sadrian	sc->rxon->ofdm_mask = 0xff;	/* not yet negotiated */
5463254204Sadrian	sc->rxon->ht_single_mask = 0xff;
5464254204Sadrian	sc->rxon->ht_dual_mask = 0xff;
5465254204Sadrian	sc->rxon->ht_triple_mask = 0xff;
5466201209Srpaulo	rxchain =
5467201209Srpaulo	    IWN_RXCHAIN_VALID(sc->rxchainmask) |
5468201209Srpaulo	    IWN_RXCHAIN_MIMO_COUNT(2) |
5469201209Srpaulo	    IWN_RXCHAIN_IDLE_COUNT(2);
5470254204Sadrian	sc->rxon->rxchain = htole16(rxchain);
5471198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "%s: setting configuration\n", __func__);
5472254204Sadrian	error = iwn_cmd(sc, IWN_CMD_RXON, sc->rxon, sc->rxonsz, 0);
5473198429Srpaulo	if (error != 0) {
5474220726Sbschmidt		device_printf(sc->sc_dev, "%s: RXON command failed\n",
5475220726Sbschmidt		    __func__);
5476198429Srpaulo		return error;
5477198429Srpaulo	}
5478198429Srpaulo
5479220726Sbschmidt	if ((error = iwn_add_broadcast_node(sc, 0)) != 0) {
5480220726Sbschmidt		device_printf(sc->sc_dev, "%s: could not add broadcast node\n",
5481220726Sbschmidt		    __func__);
5482201209Srpaulo		return error;
5483201209Srpaulo	}
5484201209Srpaulo
5485198429Srpaulo	/* Configuration has changed, set TX power accordingly. */
5486220728Sbschmidt	if ((error = ops->set_txpower(sc, ic->ic_curchan, 0)) != 0) {
5487220726Sbschmidt		device_printf(sc->sc_dev, "%s: could not set TX power\n",
5488220726Sbschmidt		    __func__);
5489198429Srpaulo		return error;
5490198429Srpaulo	}
5491198429Srpaulo
5492220726Sbschmidt	if ((error = iwn_set_critical_temp(sc)) != 0) {
5493198429Srpaulo		device_printf(sc->sc_dev,
5494220724Sbschmidt		    "%s: could not set critical temperature\n", __func__);
5495198429Srpaulo		return error;
5496198429Srpaulo	}
5497198429Srpaulo
5498201209Srpaulo	/* Set power saving level to CAM during initialization. */
5499220726Sbschmidt	if ((error = iwn_set_pslevel(sc, 0, 0, 0)) != 0) {
5500198429Srpaulo		device_printf(sc->sc_dev,
5501201209Srpaulo		    "%s: could not set power saving level\n", __func__);
5502198429Srpaulo		return error;
5503198429Srpaulo	}
5504253705Sadrian
5505253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
5506253705Sadrian
5507198429Srpaulo	return 0;
5508198429Srpaulo}
5509198429Srpaulo
5510220634Sbschmidt/*
5511220634Sbschmidt * Add an ssid element to a frame.
5512220634Sbschmidt */
5513220634Sbschmidtstatic uint8_t *
5514220634Sbschmidtieee80211_add_ssid(uint8_t *frm, const uint8_t *ssid, u_int len)
5515220634Sbschmidt{
5516220634Sbschmidt	*frm++ = IEEE80211_ELEMID_SSID;
5517220634Sbschmidt	*frm++ = len;
5518220634Sbschmidt	memcpy(frm, ssid, len);
5519220634Sbschmidt	return frm + len;
5520220634Sbschmidt}
5521220634Sbschmidt
5522206477Sbschmidtstatic int
5523198429Srpauloiwn_scan(struct iwn_softc *sc)
5524198429Srpaulo{
5525198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
5526198429Srpaulo	struct ieee80211com *ic = ifp->if_l2com;
5527198429Srpaulo	struct ieee80211_scan_state *ss = ic->ic_scan;	/*XXX*/
5528221641Sbschmidt	struct ieee80211_node *ni = ss->ss_vap->iv_bss;
5529198429Srpaulo	struct iwn_scan_hdr *hdr;
5530198429Srpaulo	struct iwn_cmd_data *tx;
5531198429Srpaulo	struct iwn_scan_essid *essid;
5532198429Srpaulo	struct iwn_scan_chan *chan;
5533198429Srpaulo	struct ieee80211_frame *wh;
5534198429Srpaulo	struct ieee80211_rateset *rs;
5535198429Srpaulo	struct ieee80211_channel *c;
5536220726Sbschmidt	uint8_t *buf, *frm;
5537220723Sbschmidt	uint16_t rxchain;
5538220726Sbschmidt	uint8_t txant;
5539220634Sbschmidt	int buflen, error;
5540198429Srpaulo
5541253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
5542253705Sadrian
5543254204Sadrian	sc->rxon = &sc->rx_on[IWN_RXON_BSS_CTX];
5544198429Srpaulo	buf = malloc(IWN_SCAN_MAXSZ, M_DEVBUF, M_NOWAIT | M_ZERO);
5545198429Srpaulo	if (buf == NULL) {
5546198429Srpaulo		device_printf(sc->sc_dev,
5547198429Srpaulo		    "%s: could not allocate buffer for scan command\n",
5548198429Srpaulo		    __func__);
5549198429Srpaulo		return ENOMEM;
5550198429Srpaulo	}
5551198429Srpaulo	hdr = (struct iwn_scan_hdr *)buf;
5552198429Srpaulo	/*
5553198429Srpaulo	 * Move to the next channel if no frames are received within 10ms
5554198429Srpaulo	 * after sending the probe request.
5555198429Srpaulo	 */
5556198429Srpaulo	hdr->quiet_time = htole16(10);		/* timeout in milliseconds */
5557198429Srpaulo	hdr->quiet_threshold = htole16(1);	/* min # of packets */
5558198429Srpaulo
5559198429Srpaulo	/* Select antennas for scanning. */
5560201209Srpaulo	rxchain =
5561201209Srpaulo	    IWN_RXCHAIN_VALID(sc->rxchainmask) |
5562201209Srpaulo	    IWN_RXCHAIN_FORCE_MIMO_SEL(sc->rxchainmask) |
5563201209Srpaulo	    IWN_RXCHAIN_DRIVER_FORCE;
5564198429Srpaulo	if (IEEE80211_IS_CHAN_A(ic->ic_curchan) &&
5565198429Srpaulo	    sc->hw_type == IWN_HW_REV_TYPE_4965) {
5566198429Srpaulo		/* Ant A must be avoided in 5GHz because of an HW bug. */
5567222679Sbschmidt		rxchain |= IWN_RXCHAIN_FORCE_SEL(IWN_ANT_B);
5568198429Srpaulo	} else	/* Use all available RX antennas. */
5569201209Srpaulo		rxchain |= IWN_RXCHAIN_FORCE_SEL(sc->rxchainmask);
5570198429Srpaulo	hdr->rxchain = htole16(rxchain);
5571198429Srpaulo	hdr->filter = htole32(IWN_FILTER_MULTICAST | IWN_FILTER_BEACON);
5572198429Srpaulo
5573198429Srpaulo	tx = (struct iwn_cmd_data *)(hdr + 1);
5574198429Srpaulo	tx->flags = htole32(IWN_TX_AUTO_SEQ);
5575220728Sbschmidt	tx->id = sc->broadcast_id;
5576198429Srpaulo	tx->lifetime = htole32(IWN_LIFETIME_INFINITE);
5577198429Srpaulo
5578222679Sbschmidt	if (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan)) {
5579198429Srpaulo		/* Send probe requests at 6Mbps. */
5580221648Sbschmidt		tx->rate = htole32(0xd);
5581201209Srpaulo		rs = &ic->ic_sup_rates[IEEE80211_MODE_11A];
5582198429Srpaulo	} else {
5583198429Srpaulo		hdr->flags = htole32(IWN_RXON_24GHZ | IWN_RXON_AUTO);
5584222679Sbschmidt		if (sc->hw_type == IWN_HW_REV_TYPE_4965 &&
5585254204Sadrian		    sc->rxon->associd && sc->rxon->chan > 14)
5586222679Sbschmidt			tx->rate = htole32(0xd);
5587222679Sbschmidt		else {
5588222679Sbschmidt			/* Send probe requests at 1Mbps. */
5589222679Sbschmidt			tx->rate = htole32(10 | IWN_RFLAG_CCK);
5590222679Sbschmidt		}
5591201209Srpaulo		rs = &ic->ic_sup_rates[IEEE80211_MODE_11G];
5592198429Srpaulo	}
5593198429Srpaulo	/* Use the first valid TX antenna. */
5594201209Srpaulo	txant = IWN_LSB(sc->txchainmask);
5595221648Sbschmidt	tx->rate |= htole32(IWN_RFLAG_ANT(txant));
5596198429Srpaulo
5597198429Srpaulo	essid = (struct iwn_scan_essid *)(tx + 1);
5598198429Srpaulo	if (ss->ss_ssid[0].len != 0) {
5599198429Srpaulo		essid[0].id = IEEE80211_ELEMID_SSID;
5600198429Srpaulo		essid[0].len = ss->ss_ssid[0].len;
5601198429Srpaulo		memcpy(essid[0].data, ss->ss_ssid[0].ssid, ss->ss_ssid[0].len);
5602198429Srpaulo	}
5603198429Srpaulo	/*
5604198429Srpaulo	 * Build a probe request frame.  Most of the following code is a
5605198429Srpaulo	 * copy & paste of what is done in net80211.
5606198429Srpaulo	 */
5607198429Srpaulo	wh = (struct ieee80211_frame *)(essid + 20);
5608198429Srpaulo	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT |
5609198429Srpaulo	    IEEE80211_FC0_SUBTYPE_PROBE_REQ;
5610198429Srpaulo	wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
5611198429Srpaulo	IEEE80211_ADDR_COPY(wh->i_addr1, ifp->if_broadcastaddr);
5612198429Srpaulo	IEEE80211_ADDR_COPY(wh->i_addr2, IF_LLADDR(ifp));
5613198429Srpaulo	IEEE80211_ADDR_COPY(wh->i_addr3, ifp->if_broadcastaddr);
5614198429Srpaulo	*(uint16_t *)&wh->i_dur[0] = 0;	/* filled by HW */
5615198429Srpaulo	*(uint16_t *)&wh->i_seq[0] = 0;	/* filled by HW */
5616198429Srpaulo
5617198429Srpaulo	frm = (uint8_t *)(wh + 1);
5618220634Sbschmidt	frm = ieee80211_add_ssid(frm, NULL, 0);
5619220634Sbschmidt	frm = ieee80211_add_rates(frm, rs);
5620220634Sbschmidt	if (rs->rs_nrates > IEEE80211_RATE_SIZE)
5621220634Sbschmidt		frm = ieee80211_add_xrates(frm, rs);
5622221641Sbschmidt	if (ic->ic_htcaps & IEEE80211_HTC_HT)
5623221641Sbschmidt		frm = ieee80211_add_htcap(frm, ni);
5624198429Srpaulo
5625198429Srpaulo	/* Set length of probe request. */
5626198429Srpaulo	tx->len = htole16(frm - (uint8_t *)wh);
5627198429Srpaulo
5628198429Srpaulo	c = ic->ic_curchan;
5629198429Srpaulo	chan = (struct iwn_scan_chan *)frm;
5630201209Srpaulo	chan->chan = htole16(ieee80211_chan2ieee(ic, c));
5631198429Srpaulo	chan->flags = 0;
5632198429Srpaulo	if (ss->ss_nssid > 0)
5633198429Srpaulo		chan->flags |= htole32(IWN_CHAN_NPBREQS(1));
5634198429Srpaulo	chan->dsp_gain = 0x6e;
5635201209Srpaulo	if (IEEE80211_IS_CHAN_5GHZ(c) &&
5636201209Srpaulo	    !(c->ic_flags & IEEE80211_CHAN_PASSIVE)) {
5637198429Srpaulo		chan->rf_gain = 0x3b;
5638198429Srpaulo		chan->active  = htole16(24);
5639198429Srpaulo		chan->passive = htole16(110);
5640201209Srpaulo		chan->flags |= htole32(IWN_CHAN_ACTIVE);
5641201209Srpaulo	} else if (IEEE80211_IS_CHAN_5GHZ(c)) {
5642201209Srpaulo		chan->rf_gain = 0x3b;
5643201209Srpaulo		chan->active  = htole16(24);
5644254204Sadrian		if (sc->rxon->associd)
5645201209Srpaulo			chan->passive = htole16(78);
5646201209Srpaulo		else
5647201209Srpaulo			chan->passive = htole16(110);
5648207709Sbschmidt		hdr->crc_threshold = 0xffff;
5649201209Srpaulo	} else if (!(c->ic_flags & IEEE80211_CHAN_PASSIVE)) {
5650201209Srpaulo		chan->rf_gain = 0x28;
5651201209Srpaulo		chan->active  = htole16(36);
5652201209Srpaulo		chan->passive = htole16(120);
5653201209Srpaulo		chan->flags |= htole32(IWN_CHAN_ACTIVE);
5654198429Srpaulo	} else {
5655198429Srpaulo		chan->rf_gain = 0x28;
5656198429Srpaulo		chan->active  = htole16(36);
5657254204Sadrian		if (sc->rxon->associd)
5658201209Srpaulo			chan->passive = htole16(88);
5659201209Srpaulo		else
5660201209Srpaulo			chan->passive = htole16(120);
5661207709Sbschmidt		hdr->crc_threshold = 0xffff;
5662198429Srpaulo	}
5663198429Srpaulo
5664201209Srpaulo	DPRINTF(sc, IWN_DEBUG_STATE,
5665201209Srpaulo	    "%s: chan %u flags 0x%x rf_gain 0x%x "
5666198429Srpaulo	    "dsp_gain 0x%x active 0x%x passive 0x%x\n", __func__,
5667198429Srpaulo	    chan->chan, chan->flags, chan->rf_gain, chan->dsp_gain,
5668198429Srpaulo	    chan->active, chan->passive);
5669198429Srpaulo
5670201209Srpaulo	hdr->nchan++;
5671201209Srpaulo	chan++;
5672198429Srpaulo	buflen = (uint8_t *)chan - buf;
5673198429Srpaulo	hdr->len = htole16(buflen);
5674198429Srpaulo
5675198429Srpaulo	DPRINTF(sc, IWN_DEBUG_STATE, "sending scan command nchan=%d\n",
5676198429Srpaulo	    hdr->nchan);
5677198429Srpaulo	error = iwn_cmd(sc, IWN_CMD_SCAN, buf, buflen, 1);
5678198429Srpaulo	free(buf, M_DEVBUF);
5679253705Sadrian
5680253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
5681253705Sadrian
5682198429Srpaulo	return error;
5683198429Srpaulo}
5684198429Srpaulo
5685206477Sbschmidtstatic int
5686191746Sthompsaiwn_auth(struct iwn_softc *sc, struct ieee80211vap *vap)
5687178676Ssam{
5688220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
5689178676Ssam	struct ifnet *ifp = sc->sc_ifp;
5690178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
5691178676Ssam	struct ieee80211_node *ni = vap->iv_bss;
5692178676Ssam	int error;
5693178676Ssam
5694253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
5695253705Sadrian
5696254204Sadrian	sc->rxon = &sc->rx_on[IWN_RXON_BSS_CTX];
5697201209Srpaulo	/* Update adapter configuration. */
5698254204Sadrian	IEEE80211_ADDR_COPY(sc->rxon->bssid, ni->ni_bssid);
5699254204Sadrian	sc->rxon->chan = ieee80211_chan2ieee(ic, ni->ni_chan);
5700254204Sadrian	sc->rxon->flags = htole32(IWN_RXON_TSF | IWN_RXON_CTS_TO_SELF);
5701178676Ssam	if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
5702254204Sadrian		sc->rxon->flags |= htole32(IWN_RXON_AUTO | IWN_RXON_24GHZ);
5703198429Srpaulo	if (ic->ic_flags & IEEE80211_F_SHSLOT)
5704254204Sadrian		sc->rxon->flags |= htole32(IWN_RXON_SHSLOT);
5705198429Srpaulo	if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
5706254204Sadrian		sc->rxon->flags |= htole32(IWN_RXON_SHPREAMBLE);
5707178676Ssam	if (IEEE80211_IS_CHAN_A(ni->ni_chan)) {
5708254204Sadrian		sc->rxon->cck_mask  = 0;
5709254204Sadrian		sc->rxon->ofdm_mask = 0x15;
5710178676Ssam	} else if (IEEE80211_IS_CHAN_B(ni->ni_chan)) {
5711254204Sadrian		sc->rxon->cck_mask  = 0x03;
5712254204Sadrian		sc->rxon->ofdm_mask = 0;
5713178676Ssam	} else {
5714220725Sbschmidt		/* Assume 802.11b/g. */
5715254204Sadrian		sc->rxon->cck_mask  = 0x0f;
5716254204Sadrian		sc->rxon->ofdm_mask = 0x15;
5717178676Ssam	}
5718220724Sbschmidt	DPRINTF(sc, IWN_DEBUG_STATE, "rxon chan %d flags %x cck %x ofdm %x\n",
5719254204Sadrian	    sc->rxon->chan, sc->rxon->flags, sc->rxon->cck_mask,
5720254204Sadrian	    sc->rxon->ofdm_mask);
5721254204Sadrian	error = iwn_cmd(sc, IWN_CMD_RXON, sc->rxon, sc->rxonsz, 1);
5722178676Ssam	if (error != 0) {
5723220726Sbschmidt		device_printf(sc->sc_dev, "%s: RXON command failed, error %d\n",
5724220726Sbschmidt		    __func__, error);
5725178676Ssam		return error;
5726178676Ssam	}
5727178676Ssam
5728198429Srpaulo	/* Configuration has changed, set TX power accordingly. */
5729220728Sbschmidt	if ((error = ops->set_txpower(sc, ni->ni_chan, 1)) != 0) {
5730178676Ssam		device_printf(sc->sc_dev,
5731220724Sbschmidt		    "%s: could not set TX power, error %d\n", __func__, error);
5732178676Ssam		return error;
5733178676Ssam	}
5734178676Ssam	/*
5735201209Srpaulo	 * Reconfiguring RXON clears the firmware nodes table so we must
5736178676Ssam	 * add the broadcast node again.
5737178676Ssam	 */
5738220726Sbschmidt	if ((error = iwn_add_broadcast_node(sc, 1)) != 0) {
5739178676Ssam		device_printf(sc->sc_dev,
5740220726Sbschmidt		    "%s: could not add broadcast node, error %d\n", __func__,
5741220726Sbschmidt		    error);
5742178676Ssam		return error;
5743178676Ssam	}
5744253705Sadrian
5745253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
5746253705Sadrian
5747178676Ssam	return 0;
5748178676Ssam}
5749178676Ssam
5750206477Sbschmidtstatic int
5751191746Sthompsaiwn_run(struct iwn_softc *sc, struct ieee80211vap *vap)
5752178676Ssam{
5753220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
5754178676Ssam	struct ifnet *ifp = sc->sc_ifp;
5755178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
5756178676Ssam	struct ieee80211_node *ni = vap->iv_bss;
5757178676Ssam	struct iwn_node_info node;
5758221653Sbschmidt	uint32_t htflags = 0;
5759201209Srpaulo	int error;
5760178676Ssam
5761253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
5762253705Sadrian
5763254204Sadrian	sc->rxon = &sc->rx_on[IWN_RXON_BSS_CTX];
5764178676Ssam	if (ic->ic_opmode == IEEE80211_M_MONITOR) {
5765201209Srpaulo		/* Link LED blinks while monitoring. */
5766220674Sbschmidt		iwn_set_led(sc, IWN_LED_LINK, 5, 5);
5767178676Ssam		return 0;
5768178676Ssam	}
5769220726Sbschmidt	if ((error = iwn_set_timing(sc, ni)) != 0) {
5770198429Srpaulo		device_printf(sc->sc_dev,
5771198429Srpaulo		    "%s: could not set timing, error %d\n", __func__, error);
5772198429Srpaulo		return error;
5773198429Srpaulo	}
5774178676Ssam
5775201209Srpaulo	/* Update adapter configuration. */
5776254204Sadrian	IEEE80211_ADDR_COPY(sc->rxon->bssid, ni->ni_bssid);
5777254204Sadrian	sc->rxon->associd = htole16(IEEE80211_AID(ni->ni_associd));
5778254204Sadrian	sc->rxon->chan = ieee80211_chan2ieee(ic, ni->ni_chan);
5779254204Sadrian	sc->rxon->flags = htole32(IWN_RXON_TSF | IWN_RXON_CTS_TO_SELF);
5780201209Srpaulo	if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
5781254204Sadrian		sc->rxon->flags |= htole32(IWN_RXON_AUTO | IWN_RXON_24GHZ);
5782178676Ssam	if (ic->ic_flags & IEEE80211_F_SHSLOT)
5783254204Sadrian		sc->rxon->flags |= htole32(IWN_RXON_SHSLOT);
5784178676Ssam	if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
5785254204Sadrian		sc->rxon->flags |= htole32(IWN_RXON_SHPREAMBLE);
5786201209Srpaulo	if (IEEE80211_IS_CHAN_A(ni->ni_chan)) {
5787254204Sadrian		sc->rxon->cck_mask  = 0;
5788254204Sadrian		sc->rxon->ofdm_mask = 0x15;
5789201209Srpaulo	} else if (IEEE80211_IS_CHAN_B(ni->ni_chan)) {
5790254204Sadrian		sc->rxon->cck_mask  = 0x03;
5791254204Sadrian		sc->rxon->ofdm_mask = 0;
5792201209Srpaulo	} else {
5793220725Sbschmidt		/* Assume 802.11b/g. */
5794254204Sadrian		sc->rxon->cck_mask  = 0x0f;
5795254204Sadrian		sc->rxon->ofdm_mask = 0x15;
5796201209Srpaulo	}
5797178676Ssam	if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) {
5798221653Sbschmidt		htflags |= IWN_RXON_HT_PROTMODE(ic->ic_curhtprotmode);
5799221653Sbschmidt		if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) {
5800221653Sbschmidt			switch (ic->ic_curhtprotmode) {
5801221653Sbschmidt			case IEEE80211_HTINFO_OPMODE_HT20PR:
5802221653Sbschmidt				htflags |= IWN_RXON_HT_MODEPURE40;
5803221653Sbschmidt				break;
5804221653Sbschmidt			default:
5805221653Sbschmidt				htflags |= IWN_RXON_HT_MODEMIXED;
5806221653Sbschmidt				break;
5807221653Sbschmidt			}
5808221653Sbschmidt		}
5809221653Sbschmidt		if (IEEE80211_IS_CHAN_HT40D(ni->ni_chan))
5810221653Sbschmidt			htflags |= IWN_RXON_HT_HT40MINUS;
5811221653Sbschmidt	}
5812254204Sadrian	sc->rxon->flags |= htole32(htflags);
5813254204Sadrian	sc->rxon->filter |= htole32(IWN_FILTER_BSS);
5814220724Sbschmidt	DPRINTF(sc, IWN_DEBUG_STATE, "rxon chan %d flags %x\n",
5815254204Sadrian	    sc->rxon->chan, sc->rxon->flags);
5816254204Sadrian	error = iwn_cmd(sc, IWN_CMD_RXON, sc->rxon, sc->rxonsz, 1);
5817178676Ssam	if (error != 0) {
5818178676Ssam		device_printf(sc->sc_dev,
5819220726Sbschmidt		    "%s: could not update configuration, error %d\n", __func__,
5820220726Sbschmidt		    error);
5821178676Ssam		return error;
5822178676Ssam	}
5823178676Ssam
5824198429Srpaulo	/* Configuration has changed, set TX power accordingly. */
5825220728Sbschmidt	if ((error = ops->set_txpower(sc, ni->ni_chan, 1)) != 0) {
5826178676Ssam		device_printf(sc->sc_dev,
5827220724Sbschmidt		    "%s: could not set TX power, error %d\n", __func__, error);
5828178676Ssam		return error;
5829178676Ssam	}
5830178676Ssam
5831220715Sbschmidt	/* Fake a join to initialize the TX rate. */
5832220715Sbschmidt	((struct iwn_node *)ni)->id = IWN_ID_BSS;
5833220715Sbschmidt	iwn_newassoc(ni, 1);
5834220715Sbschmidt
5835198429Srpaulo	/* Add BSS node. */
5836178676Ssam	memset(&node, 0, sizeof node);
5837178676Ssam	IEEE80211_ADDR_COPY(node.macaddr, ni->ni_macaddr);
5838178676Ssam	node.id = IWN_ID_BSS;
5839221653Sbschmidt	if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) {
5840221653Sbschmidt		switch (ni->ni_htcap & IEEE80211_HTCAP_SMPS) {
5841221653Sbschmidt		case IEEE80211_HTCAP_SMPS_ENA:
5842221653Sbschmidt			node.htflags |= htole32(IWN_SMPS_MIMO_DIS);
5843221653Sbschmidt			break;
5844221653Sbschmidt		case IEEE80211_HTCAP_SMPS_DYNAMIC:
5845221653Sbschmidt			node.htflags |= htole32(IWN_SMPS_MIMO_PROT);
5846221653Sbschmidt			break;
5847221653Sbschmidt		}
5848221653Sbschmidt		node.htflags |= htole32(IWN_AMDPU_SIZE_FACTOR(3) |
5849221653Sbschmidt		    IWN_AMDPU_DENSITY(5));	/* 4us */
5850221653Sbschmidt		if (IEEE80211_IS_CHAN_HT40(ni->ni_chan))
5851221653Sbschmidt			node.htflags |= htole32(IWN_NODE_HT40);
5852221653Sbschmidt	}
5853220724Sbschmidt	DPRINTF(sc, IWN_DEBUG_STATE, "%s: adding BSS node\n", __func__);
5854220728Sbschmidt	error = ops->add_node(sc, &node, 1);
5855178676Ssam	if (error != 0) {
5856220724Sbschmidt		device_printf(sc->sc_dev,
5857220724Sbschmidt		    "%s: could not add BSS node, error %d\n", __func__, error);
5858178676Ssam		return error;
5859178676Ssam	}
5860220724Sbschmidt	DPRINTF(sc, IWN_DEBUG_STATE, "%s: setting link quality for node %d\n",
5861220724Sbschmidt	    __func__, node.id);
5862220726Sbschmidt	if ((error = iwn_set_link_quality(sc, ni)) != 0) {
5863178676Ssam		device_printf(sc->sc_dev,
5864220724Sbschmidt		    "%s: could not setup link quality for node %d, error %d\n",
5865178676Ssam		    __func__, node.id, error);
5866178676Ssam		return error;
5867178676Ssam	}
5868178676Ssam
5869220726Sbschmidt	if ((error = iwn_init_sensitivity(sc)) != 0) {
5870178676Ssam		device_printf(sc->sc_dev,
5871220726Sbschmidt		    "%s: could not set sensitivity, error %d\n", __func__,
5872220726Sbschmidt		    error);
5873178676Ssam		return error;
5874178676Ssam	}
5875198429Srpaulo	/* Start periodic calibration timer. */
5876178676Ssam	sc->calib.state = IWN_CALIB_STATE_ASSOC;
5877220667Sbschmidt	sc->calib_cnt = 0;
5878220667Sbschmidt	callout_reset(&sc->calib_to, msecs_to_ticks(500), iwn_calib_timeout,
5879220667Sbschmidt	    sc);
5880178676Ssam
5881198429Srpaulo	/* Link LED always on while associated. */
5882178676Ssam	iwn_set_led(sc, IWN_LED_LINK, 0, 1);
5883253705Sadrian
5884253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
5885253705Sadrian
5886178676Ssam	return 0;
5887178676Ssam}
5888178676Ssam
5889178676Ssam/*
5890201209Srpaulo * This function is called by upper layer when an ADDBA request is received
5891201209Srpaulo * from another STA and before the ADDBA response is sent.
5892201209Srpaulo */
5893206477Sbschmidtstatic int
5894221650Sbschmidtiwn_ampdu_rx_start(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap,
5895221650Sbschmidt    int baparamset, int batimeout, int baseqctl)
5896201209Srpaulo{
5897221650Sbschmidt#define MS(_v, _f)	(((_v) & _f) >> _f##_S)
5898221650Sbschmidt	struct iwn_softc *sc = ni->ni_ic->ic_ifp->if_softc;
5899220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
5900201209Srpaulo	struct iwn_node *wn = (void *)ni;
5901201209Srpaulo	struct iwn_node_info node;
5902221650Sbschmidt	uint16_t ssn;
5903221650Sbschmidt	uint8_t tid;
5904221650Sbschmidt	int error;
5905201209Srpaulo
5906253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
5907253705Sadrian
5908221650Sbschmidt	tid = MS(le16toh(baparamset), IEEE80211_BAPS_TID);
5909221650Sbschmidt	ssn = MS(le16toh(baseqctl), IEEE80211_BASEQ_START);
5910221650Sbschmidt
5911201209Srpaulo	memset(&node, 0, sizeof node);
5912201209Srpaulo	node.id = wn->id;
5913201209Srpaulo	node.control = IWN_NODE_UPDATE;
5914201209Srpaulo	node.flags = IWN_FLAG_SET_ADDBA;
5915201209Srpaulo	node.addba_tid = tid;
5916221650Sbschmidt	node.addba_ssn = htole16(ssn);
5917201209Srpaulo	DPRINTF(sc, IWN_DEBUG_RECV, "ADDBA RA=%d TID=%d SSN=%d\n",
5918221650Sbschmidt	    wn->id, tid, ssn);
5919221650Sbschmidt	error = ops->add_node(sc, &node, 1);
5920221650Sbschmidt	if (error != 0)
5921221650Sbschmidt		return error;
5922221650Sbschmidt	return sc->sc_ampdu_rx_start(ni, rap, baparamset, batimeout, baseqctl);
5923221650Sbschmidt#undef MS
5924201209Srpaulo}
5925201209Srpaulo
5926201209Srpaulo/*
5927201209Srpaulo * This function is called by upper layer on teardown of an HT-immediate
5928220725Sbschmidt * Block Ack agreement (eg. uppon receipt of a DELBA frame).
5929201209Srpaulo */
5930206477Sbschmidtstatic void
5931221650Sbschmidtiwn_ampdu_rx_stop(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap)
5932201209Srpaulo{
5933221650Sbschmidt	struct ieee80211com *ic = ni->ni_ic;
5934221650Sbschmidt	struct iwn_softc *sc = ic->ic_ifp->if_softc;
5935220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
5936201209Srpaulo	struct iwn_node *wn = (void *)ni;
5937201209Srpaulo	struct iwn_node_info node;
5938221650Sbschmidt	uint8_t tid;
5939201209Srpaulo
5940253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
5941253705Sadrian
5942221650Sbschmidt	/* XXX: tid as an argument */
5943221650Sbschmidt	for (tid = 0; tid < WME_NUM_TID; tid++) {
5944221650Sbschmidt		if (&ni->ni_rx_ampdu[tid] == rap)
5945221650Sbschmidt			break;
5946221650Sbschmidt	}
5947221650Sbschmidt
5948201209Srpaulo	memset(&node, 0, sizeof node);
5949201209Srpaulo	node.id = wn->id;
5950201209Srpaulo	node.control = IWN_NODE_UPDATE;
5951201209Srpaulo	node.flags = IWN_FLAG_SET_DELBA;
5952201209Srpaulo	node.delba_tid = tid;
5953201209Srpaulo	DPRINTF(sc, IWN_DEBUG_RECV, "DELBA RA=%d TID=%d\n", wn->id, tid);
5954220728Sbschmidt	(void)ops->add_node(sc, &node, 1);
5955221650Sbschmidt	sc->sc_ampdu_rx_stop(ni, rap);
5956201209Srpaulo}
5957201209Srpaulo
5958221651Sbschmidtstatic int
5959221651Sbschmidtiwn_addba_request(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap,
5960221651Sbschmidt    int dialogtoken, int baparamset, int batimeout)
5961221651Sbschmidt{
5962221651Sbschmidt	struct iwn_softc *sc = ni->ni_ic->ic_ifp->if_softc;
5963221651Sbschmidt	int qid;
5964221651Sbschmidt
5965253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
5966253705Sadrian
5967221651Sbschmidt	for (qid = sc->firstaggqueue; qid < sc->ntxqs; qid++) {
5968221651Sbschmidt		if (sc->qid2tap[qid] == NULL)
5969221651Sbschmidt			break;
5970221651Sbschmidt	}
5971221651Sbschmidt	if (qid == sc->ntxqs) {
5972221651Sbschmidt		DPRINTF(sc, IWN_DEBUG_XMIT, "%s: not free aggregation queue\n",
5973221651Sbschmidt		    __func__);
5974221651Sbschmidt		return 0;
5975221651Sbschmidt	}
5976221651Sbschmidt	tap->txa_private = malloc(sizeof(int), M_DEVBUF, M_NOWAIT);
5977221651Sbschmidt	if (tap->txa_private == NULL) {
5978221651Sbschmidt		device_printf(sc->sc_dev,
5979221651Sbschmidt		    "%s: failed to alloc TX aggregation structure\n", __func__);
5980221651Sbschmidt		return 0;
5981221651Sbschmidt	}
5982221651Sbschmidt	sc->qid2tap[qid] = tap;
5983221651Sbschmidt	*(int *)tap->txa_private = qid;
5984221651Sbschmidt	return sc->sc_addba_request(ni, tap, dialogtoken, baparamset,
5985221651Sbschmidt	    batimeout);
5986221651Sbschmidt}
5987221651Sbschmidt
5988221651Sbschmidtstatic int
5989221651Sbschmidtiwn_addba_response(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap,
5990221651Sbschmidt    int code, int baparamset, int batimeout)
5991221651Sbschmidt{
5992221651Sbschmidt	struct iwn_softc *sc = ni->ni_ic->ic_ifp->if_softc;
5993221651Sbschmidt	int qid = *(int *)tap->txa_private;
5994234324Sadrian	uint8_t tid = tap->txa_tid;
5995221651Sbschmidt	int ret;
5996221651Sbschmidt
5997253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
5998253705Sadrian
5999221651Sbschmidt	if (code == IEEE80211_STATUS_SUCCESS) {
6000221651Sbschmidt		ni->ni_txseqs[tid] = tap->txa_start & 0xfff;
6001221651Sbschmidt		ret = iwn_ampdu_tx_start(ni->ni_ic, ni, tid);
6002221651Sbschmidt		if (ret != 1)
6003221651Sbschmidt			return ret;
6004221651Sbschmidt	} else {
6005221651Sbschmidt		sc->qid2tap[qid] = NULL;
6006221651Sbschmidt		free(tap->txa_private, M_DEVBUF);
6007221651Sbschmidt		tap->txa_private = NULL;
6008221651Sbschmidt	}
6009221651Sbschmidt	return sc->sc_addba_response(ni, tap, code, baparamset, batimeout);
6010221651Sbschmidt}
6011221651Sbschmidt
6012201209Srpaulo/*
6013201209Srpaulo * This function is called by upper layer when an ADDBA response is received
6014201209Srpaulo * from another STA.
6015201209Srpaulo */
6016206477Sbschmidtstatic int
6017201209Srpauloiwn_ampdu_tx_start(struct ieee80211com *ic, struct ieee80211_node *ni,
6018201209Srpaulo    uint8_t tid)
6019201209Srpaulo{
6020234324Sadrian	struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[tid];
6021221651Sbschmidt	struct iwn_softc *sc = ni->ni_ic->ic_ifp->if_softc;
6022220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
6023201209Srpaulo	struct iwn_node *wn = (void *)ni;
6024201209Srpaulo	struct iwn_node_info node;
6025221651Sbschmidt	int error, qid;
6026201209Srpaulo
6027253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
6028253705Sadrian
6029201209Srpaulo	/* Enable TX for the specified RA/TID. */
6030201209Srpaulo	wn->disable_tid &= ~(1 << tid);
6031201209Srpaulo	memset(&node, 0, sizeof node);
6032201209Srpaulo	node.id = wn->id;
6033201209Srpaulo	node.control = IWN_NODE_UPDATE;
6034201209Srpaulo	node.flags = IWN_FLAG_SET_DISABLE_TID;
6035201209Srpaulo	node.disable_tid = htole16(wn->disable_tid);
6036220728Sbschmidt	error = ops->add_node(sc, &node, 1);
6037201209Srpaulo	if (error != 0)
6038221651Sbschmidt		return 0;
6039201209Srpaulo
6040201209Srpaulo	if ((error = iwn_nic_lock(sc)) != 0)
6041221651Sbschmidt		return 0;
6042221651Sbschmidt	qid = *(int *)tap->txa_private;
6043237649Sbschmidt	DPRINTF(sc, IWN_DEBUG_XMIT, "%s: ra=%d tid=%d ssn=%d qid=%d\n",
6044237649Sbschmidt	    __func__, wn->id, tid, tap->txa_start, qid);
6045221651Sbschmidt	ops->ampdu_tx_start(sc, ni, qid, tid, tap->txa_start & 0xfff);
6046201209Srpaulo	iwn_nic_unlock(sc);
6047221651Sbschmidt
6048221651Sbschmidt	iwn_set_link_quality(sc, ni);
6049221651Sbschmidt	return 1;
6050201209Srpaulo}
6051201209Srpaulo
6052206477Sbschmidtstatic void
6053221651Sbschmidtiwn_ampdu_tx_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap)
6054201209Srpaulo{
6055221651Sbschmidt	struct iwn_softc *sc = ni->ni_ic->ic_ifp->if_softc;
6056220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
6057234324Sadrian	uint8_t tid = tap->txa_tid;
6058221651Sbschmidt	int qid;
6059201209Srpaulo
6060253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
6061253705Sadrian
6062237649Sbschmidt	sc->sc_addba_stop(ni, tap);
6063237649Sbschmidt
6064221651Sbschmidt	if (tap->txa_private == NULL)
6065221651Sbschmidt		return;
6066221651Sbschmidt
6067221651Sbschmidt	qid = *(int *)tap->txa_private;
6068237649Sbschmidt	if (sc->txq[qid].queued != 0)
6069237649Sbschmidt		return;
6070220726Sbschmidt	if (iwn_nic_lock(sc) != 0)
6071201209Srpaulo		return;
6072221651Sbschmidt	ops->ampdu_tx_stop(sc, qid, tid, tap->txa_start & 0xfff);
6073201209Srpaulo	iwn_nic_unlock(sc);
6074221651Sbschmidt	sc->qid2tap[qid] = NULL;
6075221651Sbschmidt	free(tap->txa_private, M_DEVBUF);
6076221651Sbschmidt	tap->txa_private = NULL;
6077201209Srpaulo}
6078201209Srpaulo
6079206477Sbschmidtstatic void
6080201209Srpauloiwn4965_ampdu_tx_start(struct iwn_softc *sc, struct ieee80211_node *ni,
6081221651Sbschmidt    int qid, uint8_t tid, uint16_t ssn)
6082201209Srpaulo{
6083201209Srpaulo	struct iwn_node *wn = (void *)ni;
6084201209Srpaulo
6085253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
6086253705Sadrian
6087201209Srpaulo	/* Stop TX scheduler while we're changing its configuration. */
6088201209Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid),
6089201209Srpaulo	    IWN4965_TXQ_STATUS_CHGACT);
6090201209Srpaulo
6091201209Srpaulo	/* Assign RA/TID translation to the queue. */
6092201209Srpaulo	iwn_mem_write_2(sc, sc->sched_base + IWN4965_SCHED_TRANS_TBL(qid),
6093201209Srpaulo	    wn->id << 4 | tid);
6094201209Srpaulo
6095201209Srpaulo	/* Enable chain-building mode for the queue. */
6096201209Srpaulo	iwn_prph_setbits(sc, IWN4965_SCHED_QCHAIN_SEL, 1 << qid);
6097201209Srpaulo
6098201209Srpaulo	/* Set starting sequence number from the ADDBA request. */
6099221651Sbschmidt	sc->txq[qid].cur = sc->txq[qid].read = (ssn & 0xff);
6100201209Srpaulo	IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff));
6101201209Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_QUEUE_RDPTR(qid), ssn);
6102201209Srpaulo
6103201209Srpaulo	/* Set scheduler window size. */
6104201209Srpaulo	iwn_mem_write(sc, sc->sched_base + IWN4965_SCHED_QUEUE_OFFSET(qid),
6105201209Srpaulo	    IWN_SCHED_WINSZ);
6106201209Srpaulo	/* Set scheduler frame limit. */
6107201209Srpaulo	iwn_mem_write(sc, sc->sched_base + IWN4965_SCHED_QUEUE_OFFSET(qid) + 4,
6108201209Srpaulo	    IWN_SCHED_LIMIT << 16);
6109201209Srpaulo
6110201209Srpaulo	/* Enable interrupts for the queue. */
6111201209Srpaulo	iwn_prph_setbits(sc, IWN4965_SCHED_INTR_MASK, 1 << qid);
6112201209Srpaulo
6113201209Srpaulo	/* Mark the queue as active. */
6114201209Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid),
6115201209Srpaulo	    IWN4965_TXQ_STATUS_ACTIVE | IWN4965_TXQ_STATUS_AGGR_ENA |
6116201209Srpaulo	    iwn_tid2fifo[tid] << 1);
6117201209Srpaulo}
6118201209Srpaulo
6119206477Sbschmidtstatic void
6120221651Sbschmidtiwn4965_ampdu_tx_stop(struct iwn_softc *sc, int qid, uint8_t tid, uint16_t ssn)
6121201209Srpaulo{
6122253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
6123253705Sadrian
6124201209Srpaulo	/* Stop TX scheduler while we're changing its configuration. */
6125201209Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid),
6126201209Srpaulo	    IWN4965_TXQ_STATUS_CHGACT);
6127201209Srpaulo
6128201209Srpaulo	/* Set starting sequence number from the ADDBA request. */
6129201209Srpaulo	IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff));
6130201209Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_QUEUE_RDPTR(qid), ssn);
6131201209Srpaulo
6132201209Srpaulo	/* Disable interrupts for the queue. */
6133201209Srpaulo	iwn_prph_clrbits(sc, IWN4965_SCHED_INTR_MASK, 1 << qid);
6134201209Srpaulo
6135201209Srpaulo	/* Mark the queue as inactive. */
6136201209Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid),
6137201209Srpaulo	    IWN4965_TXQ_STATUS_INACTIVE | iwn_tid2fifo[tid] << 1);
6138201209Srpaulo}
6139201209Srpaulo
6140206477Sbschmidtstatic void
6141201209Srpauloiwn5000_ampdu_tx_start(struct iwn_softc *sc, struct ieee80211_node *ni,
6142221651Sbschmidt    int qid, uint8_t tid, uint16_t ssn)
6143201209Srpaulo{
6144253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
6145253705Sadrian
6146201209Srpaulo	struct iwn_node *wn = (void *)ni;
6147201209Srpaulo
6148201209Srpaulo	/* Stop TX scheduler while we're changing its configuration. */
6149201209Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid),
6150201209Srpaulo	    IWN5000_TXQ_STATUS_CHGACT);
6151201209Srpaulo
6152201209Srpaulo	/* Assign RA/TID translation to the queue. */
6153201209Srpaulo	iwn_mem_write_2(sc, sc->sched_base + IWN5000_SCHED_TRANS_TBL(qid),
6154201209Srpaulo	    wn->id << 4 | tid);
6155201209Srpaulo
6156201209Srpaulo	/* Enable chain-building mode for the queue. */
6157201209Srpaulo	iwn_prph_setbits(sc, IWN5000_SCHED_QCHAIN_SEL, 1 << qid);
6158201209Srpaulo
6159201209Srpaulo	/* Enable aggregation for the queue. */
6160201209Srpaulo	iwn_prph_setbits(sc, IWN5000_SCHED_AGGR_SEL, 1 << qid);
6161201209Srpaulo
6162201209Srpaulo	/* Set starting sequence number from the ADDBA request. */
6163221651Sbschmidt	sc->txq[qid].cur = sc->txq[qid].read = (ssn & 0xff);
6164201209Srpaulo	IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff));
6165201209Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_QUEUE_RDPTR(qid), ssn);
6166201209Srpaulo
6167201209Srpaulo	/* Set scheduler window size and frame limit. */
6168201209Srpaulo	iwn_mem_write(sc, sc->sched_base + IWN5000_SCHED_QUEUE_OFFSET(qid) + 4,
6169201209Srpaulo	    IWN_SCHED_LIMIT << 16 | IWN_SCHED_WINSZ);
6170201209Srpaulo
6171201209Srpaulo	/* Enable interrupts for the queue. */
6172201209Srpaulo	iwn_prph_setbits(sc, IWN5000_SCHED_INTR_MASK, 1 << qid);
6173201209Srpaulo
6174201209Srpaulo	/* Mark the queue as active. */
6175201209Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid),
6176201209Srpaulo	    IWN5000_TXQ_STATUS_ACTIVE | iwn_tid2fifo[tid]);
6177201209Srpaulo}
6178201209Srpaulo
6179206477Sbschmidtstatic void
6180221651Sbschmidtiwn5000_ampdu_tx_stop(struct iwn_softc *sc, int qid, uint8_t tid, uint16_t ssn)
6181201209Srpaulo{
6182253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
6183253705Sadrian
6184201209Srpaulo	/* Stop TX scheduler while we're changing its configuration. */
6185201209Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid),
6186201209Srpaulo	    IWN5000_TXQ_STATUS_CHGACT);
6187201209Srpaulo
6188201209Srpaulo	/* Disable aggregation for the queue. */
6189201209Srpaulo	iwn_prph_clrbits(sc, IWN5000_SCHED_AGGR_SEL, 1 << qid);
6190201209Srpaulo
6191201209Srpaulo	/* Set starting sequence number from the ADDBA request. */
6192201209Srpaulo	IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff));
6193201209Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_QUEUE_RDPTR(qid), ssn);
6194201209Srpaulo
6195201209Srpaulo	/* Disable interrupts for the queue. */
6196201209Srpaulo	iwn_prph_clrbits(sc, IWN5000_SCHED_INTR_MASK, 1 << qid);
6197201209Srpaulo
6198201209Srpaulo	/* Mark the queue as inactive. */
6199201209Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid),
6200201209Srpaulo	    IWN5000_TXQ_STATUS_INACTIVE | iwn_tid2fifo[tid]);
6201201209Srpaulo}
6202201209Srpaulo
6203201209Srpaulo/*
6204220674Sbschmidt * Query calibration tables from the initialization firmware.  We do this
6205220674Sbschmidt * only once at first boot.  Called from a process context.
6206212853Sbschmidt */
6207212853Sbschmidtstatic int
6208220674Sbschmidtiwn5000_query_calibration(struct iwn_softc *sc)
6209212853Sbschmidt{
6210198429Srpaulo	struct iwn5000_calib_config cmd;
6211198429Srpaulo	int error;
6212178676Ssam
6213198429Srpaulo	memset(&cmd, 0, sizeof cmd);
6214220674Sbschmidt	cmd.ucode.once.enable = 0xffffffff;
6215220674Sbschmidt	cmd.ucode.once.start  = 0xffffffff;
6216220674Sbschmidt	cmd.ucode.once.send   = 0xffffffff;
6217220674Sbschmidt	cmd.ucode.flags       = 0xffffffff;
6218220674Sbschmidt	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: sending calibration query\n",
6219220674Sbschmidt	    __func__);
6220198429Srpaulo	error = iwn_cmd(sc, IWN5000_CMD_CALIB_CONFIG, &cmd, sizeof cmd, 0);
6221198429Srpaulo	if (error != 0)
6222198429Srpaulo		return error;
6223178676Ssam
6224198429Srpaulo	/* Wait at most two seconds for calibration to complete. */
6225201209Srpaulo	if (!(sc->sc_flags & IWN_FLAG_CALIB_DONE))
6226220674Sbschmidt		error = msleep(sc, &sc->sc_mtx, PCATCH, "iwncal", 2 * hz);
6227201209Srpaulo	return error;
6228198429Srpaulo}
6229198429Srpaulo
6230198429Srpaulo/*
6231220674Sbschmidt * Send calibration results to the runtime firmware.  These results were
6232220674Sbschmidt * obtained on first boot from the initialization firmware.
6233198429Srpaulo */
6234212854Sbschmidtstatic int
6235220674Sbschmidtiwn5000_send_calibration(struct iwn_softc *sc)
6236198429Srpaulo{
6237220674Sbschmidt	int idx, error;
6238198429Srpaulo
6239220674Sbschmidt	for (idx = 0; idx < 5; idx++) {
6240220674Sbschmidt		if (sc->calibcmd[idx].buf == NULL)
6241220674Sbschmidt			continue;	/* No results available. */
6242198429Srpaulo		DPRINTF(sc, IWN_DEBUG_CALIBRATE,
6243220674Sbschmidt		    "send calibration result idx=%d len=%d\n", idx,
6244220674Sbschmidt		    sc->calibcmd[idx].len);
6245220674Sbschmidt		error = iwn_cmd(sc, IWN_CMD_PHY_CALIB, sc->calibcmd[idx].buf,
6246220674Sbschmidt		    sc->calibcmd[idx].len, 0);
6247220674Sbschmidt		if (error != 0) {
6248220674Sbschmidt			device_printf(sc->sc_dev,
6249220674Sbschmidt			    "%s: could not send calibration result, error %d\n",
6250220674Sbschmidt			    __func__, error);
6251220674Sbschmidt			return error;
6252220674Sbschmidt		}
6253178676Ssam	}
6254220674Sbschmidt	return 0;
6255198429Srpaulo}
6256178676Ssam
6257206477Sbschmidtstatic int
6258201209Srpauloiwn5000_send_wimax_coex(struct iwn_softc *sc)
6259201209Srpaulo{
6260201209Srpaulo	struct iwn5000_wimax_coex wimax;
6261201209Srpaulo
6262201209Srpaulo#ifdef notyet
6263201209Srpaulo	if (sc->hw_type == IWN_HW_REV_TYPE_6050) {
6264201209Srpaulo		/* Enable WiMAX coexistence for combo adapters. */
6265201209Srpaulo		wimax.flags =
6266201209Srpaulo		    IWN_WIMAX_COEX_ASSOC_WA_UNMASK |
6267201209Srpaulo		    IWN_WIMAX_COEX_UNASSOC_WA_UNMASK |
6268201209Srpaulo		    IWN_WIMAX_COEX_STA_TABLE_VALID |
6269201209Srpaulo		    IWN_WIMAX_COEX_ENABLE;
6270201209Srpaulo		memcpy(wimax.events, iwn6050_wimax_events,
6271201209Srpaulo		    sizeof iwn6050_wimax_events);
6272201209Srpaulo	} else
6273201209Srpaulo#endif
6274201209Srpaulo	{
6275201209Srpaulo		/* Disable WiMAX coexistence. */
6276201209Srpaulo		wimax.flags = 0;
6277201209Srpaulo		memset(wimax.events, 0, sizeof wimax.events);
6278201209Srpaulo	}
6279201209Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "%s: Configuring WiMAX coexistence\n",
6280201209Srpaulo	    __func__);
6281201209Srpaulo	return iwn_cmd(sc, IWN5000_CMD_WIMAX_COEX, &wimax, sizeof wimax, 0);
6282201209Srpaulo}
6283201209Srpaulo
6284220674Sbschmidtstatic int
6285220674Sbschmidtiwn5000_crystal_calib(struct iwn_softc *sc)
6286220674Sbschmidt{
6287220674Sbschmidt	struct iwn5000_phy_calib_crystal cmd;
6288220674Sbschmidt
6289220674Sbschmidt	memset(&cmd, 0, sizeof cmd);
6290220674Sbschmidt	cmd.code = IWN5000_PHY_CALIB_CRYSTAL;
6291220674Sbschmidt	cmd.ngroups = 1;
6292220674Sbschmidt	cmd.isvalid = 1;
6293220674Sbschmidt	cmd.cap_pin[0] = le32toh(sc->eeprom_crystal) & 0xff;
6294220674Sbschmidt	cmd.cap_pin[1] = (le32toh(sc->eeprom_crystal) >> 16) & 0xff;
6295220674Sbschmidt	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "sending crystal calibration %d, %d\n",
6296220674Sbschmidt	    cmd.cap_pin[0], cmd.cap_pin[1]);
6297220674Sbschmidt	return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 0);
6298220674Sbschmidt}
6299220674Sbschmidt
6300220676Sbschmidtstatic int
6301220676Sbschmidtiwn5000_temp_offset_calib(struct iwn_softc *sc)
6302220676Sbschmidt{
6303220676Sbschmidt	struct iwn5000_phy_calib_temp_offset cmd;
6304220676Sbschmidt
6305220676Sbschmidt	memset(&cmd, 0, sizeof cmd);
6306220676Sbschmidt	cmd.code = IWN5000_PHY_CALIB_TEMP_OFFSET;
6307220676Sbschmidt	cmd.ngroups = 1;
6308220676Sbschmidt	cmd.isvalid = 1;
6309220676Sbschmidt	if (sc->eeprom_temp != 0)
6310220676Sbschmidt		cmd.offset = htole16(sc->eeprom_temp);
6311220676Sbschmidt	else
6312220676Sbschmidt		cmd.offset = htole16(IWN_DEFAULT_TEMP_OFFSET);
6313220676Sbschmidt	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "setting radio sensor offset to %d\n",
6314220676Sbschmidt	    le16toh(cmd.offset));
6315220676Sbschmidt	return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 0);
6316220676Sbschmidt}
6317220676Sbschmidt
6318198429Srpaulo/*
6319198429Srpaulo * This function is called after the runtime firmware notifies us of its
6320220725Sbschmidt * readiness (called in a process context).
6321198429Srpaulo */
6322206477Sbschmidtstatic int
6323198429Srpauloiwn4965_post_alive(struct iwn_softc *sc)
6324198429Srpaulo{
6325198429Srpaulo	int error, qid;
6326178676Ssam
6327198429Srpaulo	if ((error = iwn_nic_lock(sc)) != 0)
6328198429Srpaulo		return error;
6329178676Ssam
6330253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
6331253705Sadrian
6332201209Srpaulo	/* Clear TX scheduler state in SRAM. */
6333198429Srpaulo	sc->sched_base = iwn_prph_read(sc, IWN_SCHED_SRAM_ADDR);
6334198429Srpaulo	iwn_mem_set_region_4(sc, sc->sched_base + IWN4965_SCHED_CTX_OFF, 0,
6335201209Srpaulo	    IWN4965_SCHED_CTX_LEN / sizeof (uint32_t));
6336178676Ssam
6337220725Sbschmidt	/* Set physical address of TX scheduler rings (1KB aligned). */
6338198429Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_DRAM_ADDR, sc->sched_dma.paddr >> 10);
6339178676Ssam
6340198429Srpaulo	IWN_SETBITS(sc, IWN_FH_TX_CHICKEN, IWN_FH_TX_CHICKEN_SCHED_RETRY);
6341178676Ssam
6342198429Srpaulo	/* Disable chain mode for all our 16 queues. */
6343198429Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_QCHAIN_SEL, 0);
6344198429Srpaulo
6345198429Srpaulo	for (qid = 0; qid < IWN4965_NTXQUEUES; qid++) {
6346198429Srpaulo		iwn_prph_write(sc, IWN4965_SCHED_QUEUE_RDPTR(qid), 0);
6347198429Srpaulo		IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | 0);
6348198429Srpaulo
6349198429Srpaulo		/* Set scheduler window size. */
6350198429Srpaulo		iwn_mem_write(sc, sc->sched_base +
6351198429Srpaulo		    IWN4965_SCHED_QUEUE_OFFSET(qid), IWN_SCHED_WINSZ);
6352198429Srpaulo		/* Set scheduler frame limit. */
6353198429Srpaulo		iwn_mem_write(sc, sc->sched_base +
6354198429Srpaulo		    IWN4965_SCHED_QUEUE_OFFSET(qid) + 4,
6355198429Srpaulo		    IWN_SCHED_LIMIT << 16);
6356178676Ssam	}
6357178676Ssam
6358198429Srpaulo	/* Enable interrupts for all our 16 queues. */
6359198429Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_INTR_MASK, 0xffff);
6360198429Srpaulo	/* Identify TX FIFO rings (0-7). */
6361198429Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_TXFACT, 0xff);
6362178676Ssam
6363198429Srpaulo	/* Mark TX rings (4 EDCA + cmd + 2 HCCA) as active. */
6364198429Srpaulo	for (qid = 0; qid < 7; qid++) {
6365198429Srpaulo		static uint8_t qid2fifo[] = { 3, 2, 1, 0, 4, 5, 6 };
6366198429Srpaulo		iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid),
6367198429Srpaulo		    IWN4965_TXQ_STATUS_ACTIVE | qid2fifo[qid] << 1);
6368198429Srpaulo	}
6369198429Srpaulo	iwn_nic_unlock(sc);
6370198429Srpaulo	return 0;
6371198429Srpaulo}
6372178676Ssam
6373198429Srpaulo/*
6374198429Srpaulo * This function is called after the initialization or runtime firmware
6375220725Sbschmidt * notifies us of its readiness (called in a process context).
6376198429Srpaulo */
6377206477Sbschmidtstatic int
6378198429Srpauloiwn5000_post_alive(struct iwn_softc *sc)
6379198429Srpaulo{
6380198429Srpaulo	int error, qid;
6381178676Ssam
6382253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
6383253705Sadrian
6384201209Srpaulo	/* Switch to using ICT interrupt mode. */
6385201209Srpaulo	iwn5000_ict_reset(sc);
6386201209Srpaulo
6387253705Sadrian	if ((error = iwn_nic_lock(sc)) != 0){
6388253705Sadrian		DPRINTF(sc, IWN_DEBUG_TRACE, "->%s end in error\n", __func__);
6389198429Srpaulo		return error;
6390253705Sadrian	}
6391178676Ssam
6392201209Srpaulo	/* Clear TX scheduler state in SRAM. */
6393198429Srpaulo	sc->sched_base = iwn_prph_read(sc, IWN_SCHED_SRAM_ADDR);
6394198429Srpaulo	iwn_mem_set_region_4(sc, sc->sched_base + IWN5000_SCHED_CTX_OFF, 0,
6395201209Srpaulo	    IWN5000_SCHED_CTX_LEN / sizeof (uint32_t));
6396178676Ssam
6397220725Sbschmidt	/* Set physical address of TX scheduler rings (1KB aligned). */
6398198429Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_DRAM_ADDR, sc->sched_dma.paddr >> 10);
6399178676Ssam
6400198429Srpaulo	IWN_SETBITS(sc, IWN_FH_TX_CHICKEN, IWN_FH_TX_CHICKEN_SCHED_RETRY);
6401178676Ssam
6402201209Srpaulo	/* Enable chain mode for all queues, except command queue. */
6403201209Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_QCHAIN_SEL, 0xfffef);
6404198429Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_AGGR_SEL, 0);
6405178676Ssam
6406198429Srpaulo	for (qid = 0; qid < IWN5000_NTXQUEUES; qid++) {
6407198429Srpaulo		iwn_prph_write(sc, IWN5000_SCHED_QUEUE_RDPTR(qid), 0);
6408198429Srpaulo		IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | 0);
6409198429Srpaulo
6410198429Srpaulo		iwn_mem_write(sc, sc->sched_base +
6411198429Srpaulo		    IWN5000_SCHED_QUEUE_OFFSET(qid), 0);
6412198429Srpaulo		/* Set scheduler window size and frame limit. */
6413198429Srpaulo		iwn_mem_write(sc, sc->sched_base +
6414198429Srpaulo		    IWN5000_SCHED_QUEUE_OFFSET(qid) + 4,
6415198429Srpaulo		    IWN_SCHED_LIMIT << 16 | IWN_SCHED_WINSZ);
6416178676Ssam	}
6417178676Ssam
6418198429Srpaulo	/* Enable interrupts for all our 20 queues. */
6419198429Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_INTR_MASK, 0xfffff);
6420198429Srpaulo	/* Identify TX FIFO rings (0-7). */
6421198429Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_TXFACT, 0xff);
6422178676Ssam
6423198429Srpaulo	/* Mark TX rings (4 EDCA + cmd + 2 HCCA) as active. */
6424198429Srpaulo	for (qid = 0; qid < 7; qid++) {
6425198429Srpaulo		static uint8_t qid2fifo[] = { 3, 2, 1, 0, 7, 5, 6 };
6426198429Srpaulo		iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid),
6427198429Srpaulo		    IWN5000_TXQ_STATUS_ACTIVE | qid2fifo[qid]);
6428198429Srpaulo	}
6429198429Srpaulo	iwn_nic_unlock(sc);
6430178676Ssam
6431201209Srpaulo	/* Configure WiMAX coexistence for combo adapters. */
6432201209Srpaulo	error = iwn5000_send_wimax_coex(sc);
6433178676Ssam	if (error != 0) {
6434178676Ssam		device_printf(sc->sc_dev,
6435198429Srpaulo		    "%s: could not configure WiMAX coexistence, error %d\n",
6436178676Ssam		    __func__, error);
6437178676Ssam		return error;
6438178676Ssam	}
6439220674Sbschmidt	if (sc->hw_type != IWN_HW_REV_TYPE_5150) {
6440220674Sbschmidt		/* Perform crystal calibration. */
6441220674Sbschmidt		error = iwn5000_crystal_calib(sc);
6442198429Srpaulo		if (error != 0) {
6443198429Srpaulo			device_printf(sc->sc_dev,
6444220674Sbschmidt			    "%s: crystal calibration failed, error %d\n",
6445220674Sbschmidt			    __func__, error);
6446198429Srpaulo			return error;
6447198429Srpaulo		}
6448220674Sbschmidt	}
6449220674Sbschmidt	if (!(sc->sc_flags & IWN_FLAG_CALIB_DONE)) {
6450220674Sbschmidt		/* Query calibration from the initialization firmware. */
6451220674Sbschmidt		if ((error = iwn5000_query_calibration(sc)) != 0) {
6452198429Srpaulo			device_printf(sc->sc_dev,
6453220674Sbschmidt			    "%s: could not query calibration, error %d\n",
6454198429Srpaulo			    __func__, error);
6455198429Srpaulo			return error;
6456198429Srpaulo		}
6457198429Srpaulo		/*
6458201209Srpaulo		 * We have the calibration results now, reboot with the
6459201209Srpaulo		 * runtime firmware (call ourselves recursively!)
6460198429Srpaulo		 */
6461198429Srpaulo		iwn_hw_stop(sc);
6462198429Srpaulo		error = iwn_hw_init(sc);
6463198429Srpaulo	} else {
6464220674Sbschmidt		/* Send calibration results to runtime firmware. */
6465220674Sbschmidt		error = iwn5000_send_calibration(sc);
6466198429Srpaulo	}
6467253705Sadrian
6468253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
6469253705Sadrian
6470198429Srpaulo	return error;
6471198429Srpaulo}
6472178676Ssam
6473198429Srpaulo/*
6474198429Srpaulo * The firmware boot code is small and is intended to be copied directly into
6475220725Sbschmidt * the NIC internal memory (no DMA transfer).
6476198429Srpaulo */
6477206477Sbschmidtstatic int
6478198429Srpauloiwn4965_load_bootcode(struct iwn_softc *sc, const uint8_t *ucode, int size)
6479198429Srpaulo{
6480198429Srpaulo	int error, ntries;
6481198429Srpaulo
6482198429Srpaulo	size /= sizeof (uint32_t);
6483198429Srpaulo
6484220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
6485198429Srpaulo		return error;
6486198429Srpaulo
6487198429Srpaulo	/* Copy microcode image into NIC memory. */
6488198429Srpaulo	iwn_prph_write_region_4(sc, IWN_BSM_SRAM_BASE,
6489198429Srpaulo	    (const uint32_t *)ucode, size);
6490198429Srpaulo
6491198429Srpaulo	iwn_prph_write(sc, IWN_BSM_WR_MEM_SRC, 0);
6492198429Srpaulo	iwn_prph_write(sc, IWN_BSM_WR_MEM_DST, IWN_FW_TEXT_BASE);
6493198429Srpaulo	iwn_prph_write(sc, IWN_BSM_WR_DWCOUNT, size);
6494198429Srpaulo
6495198429Srpaulo	/* Start boot load now. */
6496198429Srpaulo	iwn_prph_write(sc, IWN_BSM_WR_CTRL, IWN_BSM_WR_CTRL_START);
6497198429Srpaulo
6498198429Srpaulo	/* Wait for transfer to complete. */
6499198429Srpaulo	for (ntries = 0; ntries < 1000; ntries++) {
6500198429Srpaulo		if (!(iwn_prph_read(sc, IWN_BSM_WR_CTRL) &
6501198429Srpaulo		    IWN_BSM_WR_CTRL_START))
6502198429Srpaulo			break;
6503198429Srpaulo		DELAY(10);
6504198429Srpaulo	}
6505198429Srpaulo	if (ntries == 1000) {
6506198429Srpaulo		device_printf(sc->sc_dev, "%s: could not load boot firmware\n",
6507198429Srpaulo		    __func__);
6508198429Srpaulo		iwn_nic_unlock(sc);
6509198429Srpaulo		return ETIMEDOUT;
6510198429Srpaulo	}
6511198429Srpaulo
6512198429Srpaulo	/* Enable boot after power up. */
6513198429Srpaulo	iwn_prph_write(sc, IWN_BSM_WR_CTRL, IWN_BSM_WR_CTRL_START_EN);
6514198429Srpaulo
6515198429Srpaulo	iwn_nic_unlock(sc);
6516198429Srpaulo	return 0;
6517178676Ssam}
6518178676Ssam
6519206477Sbschmidtstatic int
6520198429Srpauloiwn4965_load_firmware(struct iwn_softc *sc)
6521178676Ssam{
6522198429Srpaulo	struct iwn_fw_info *fw = &sc->fw;
6523198429Srpaulo	struct iwn_dma_info *dma = &sc->fw_dma;
6524178676Ssam	int error;
6525178676Ssam
6526198429Srpaulo	/* Copy initialization sections into pre-allocated DMA-safe memory. */
6527198429Srpaulo	memcpy(dma->vaddr, fw->init.data, fw->init.datasz);
6528220661Sbschmidt	bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
6529198429Srpaulo	memcpy(dma->vaddr + IWN4965_FW_DATA_MAXSZ,
6530198429Srpaulo	    fw->init.text, fw->init.textsz);
6531220661Sbschmidt	bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
6532198429Srpaulo
6533198429Srpaulo	/* Tell adapter where to find initialization sections. */
6534220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
6535198429Srpaulo		return error;
6536198429Srpaulo	iwn_prph_write(sc, IWN_BSM_DRAM_DATA_ADDR, dma->paddr >> 4);
6537198429Srpaulo	iwn_prph_write(sc, IWN_BSM_DRAM_DATA_SIZE, fw->init.datasz);
6538198429Srpaulo	iwn_prph_write(sc, IWN_BSM_DRAM_TEXT_ADDR,
6539198429Srpaulo	    (dma->paddr + IWN4965_FW_DATA_MAXSZ) >> 4);
6540198429Srpaulo	iwn_prph_write(sc, IWN_BSM_DRAM_TEXT_SIZE, fw->init.textsz);
6541198429Srpaulo	iwn_nic_unlock(sc);
6542198429Srpaulo
6543198429Srpaulo	/* Load firmware boot code. */
6544198429Srpaulo	error = iwn4965_load_bootcode(sc, fw->boot.text, fw->boot.textsz);
6545178676Ssam	if (error != 0) {
6546198429Srpaulo		device_printf(sc->sc_dev, "%s: could not load boot firmware\n",
6547198429Srpaulo		    __func__);
6548178676Ssam		return error;
6549178676Ssam	}
6550198429Srpaulo	/* Now press "execute". */
6551198429Srpaulo	IWN_WRITE(sc, IWN_RESET, 0);
6552178676Ssam
6553198429Srpaulo	/* Wait at most one second for first alive notification. */
6554220726Sbschmidt	if ((error = msleep(sc, &sc->sc_mtx, PCATCH, "iwninit", hz)) != 0) {
6555178676Ssam		device_printf(sc->sc_dev,
6556198429Srpaulo		    "%s: timeout waiting for adapter to initialize, error %d\n",
6557178676Ssam		    __func__, error);
6558178676Ssam		return error;
6559178676Ssam	}
6560178676Ssam
6561198429Srpaulo	/* Retrieve current temperature for initial TX power calibration. */
6562198429Srpaulo	sc->rawtemp = sc->ucode_info.temp[3].chan20MHz;
6563198429Srpaulo	sc->temp = iwn4965_get_temperature(sc);
6564178676Ssam
6565198429Srpaulo	/* Copy runtime sections into pre-allocated DMA-safe memory. */
6566198429Srpaulo	memcpy(dma->vaddr, fw->main.data, fw->main.datasz);
6567220661Sbschmidt	bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
6568198429Srpaulo	memcpy(dma->vaddr + IWN4965_FW_DATA_MAXSZ,
6569198429Srpaulo	    fw->main.text, fw->main.textsz);
6570220661Sbschmidt	bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
6571198429Srpaulo
6572198429Srpaulo	/* Tell adapter where to find runtime sections. */
6573220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
6574198429Srpaulo		return error;
6575198429Srpaulo	iwn_prph_write(sc, IWN_BSM_DRAM_DATA_ADDR, dma->paddr >> 4);
6576198429Srpaulo	iwn_prph_write(sc, IWN_BSM_DRAM_DATA_SIZE, fw->main.datasz);
6577198429Srpaulo	iwn_prph_write(sc, IWN_BSM_DRAM_TEXT_ADDR,
6578198429Srpaulo	    (dma->paddr + IWN4965_FW_DATA_MAXSZ) >> 4);
6579198429Srpaulo	iwn_prph_write(sc, IWN_BSM_DRAM_TEXT_SIZE,
6580198429Srpaulo	    IWN_FW_UPDATED | fw->main.textsz);
6581198429Srpaulo	iwn_nic_unlock(sc);
6582198429Srpaulo
6583198429Srpaulo	return 0;
6584198429Srpaulo}
6585198429Srpaulo
6586206477Sbschmidtstatic int
6587198429Srpauloiwn5000_load_firmware_section(struct iwn_softc *sc, uint32_t dst,
6588198429Srpaulo    const uint8_t *section, int size)
6589198429Srpaulo{
6590198429Srpaulo	struct iwn_dma_info *dma = &sc->fw_dma;
6591198429Srpaulo	int error;
6592198429Srpaulo
6593253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
6594253705Sadrian
6595198429Srpaulo	/* Copy firmware section into pre-allocated DMA-safe memory. */
6596198429Srpaulo	memcpy(dma->vaddr, section, size);
6597220661Sbschmidt	bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
6598198429Srpaulo
6599220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
6600198429Srpaulo		return error;
6601198429Srpaulo
6602198429Srpaulo	IWN_WRITE(sc, IWN_FH_TX_CONFIG(IWN_SRVC_DMACHNL),
6603198429Srpaulo	    IWN_FH_TX_CONFIG_DMA_PAUSE);
6604198429Srpaulo
6605198429Srpaulo	IWN_WRITE(sc, IWN_FH_SRAM_ADDR(IWN_SRVC_DMACHNL), dst);
6606198429Srpaulo	IWN_WRITE(sc, IWN_FH_TFBD_CTRL0(IWN_SRVC_DMACHNL),
6607198429Srpaulo	    IWN_LOADDR(dma->paddr));
6608198429Srpaulo	IWN_WRITE(sc, IWN_FH_TFBD_CTRL1(IWN_SRVC_DMACHNL),
6609198429Srpaulo	    IWN_HIADDR(dma->paddr) << 28 | size);
6610198429Srpaulo	IWN_WRITE(sc, IWN_FH_TXBUF_STATUS(IWN_SRVC_DMACHNL),
6611198429Srpaulo	    IWN_FH_TXBUF_STATUS_TBNUM(1) |
6612198429Srpaulo	    IWN_FH_TXBUF_STATUS_TBIDX(1) |
6613198429Srpaulo	    IWN_FH_TXBUF_STATUS_TFBD_VALID);
6614198429Srpaulo
6615198429Srpaulo	/* Kick Flow Handler to start DMA transfer. */
6616198429Srpaulo	IWN_WRITE(sc, IWN_FH_TX_CONFIG(IWN_SRVC_DMACHNL),
6617198429Srpaulo	    IWN_FH_TX_CONFIG_DMA_ENA | IWN_FH_TX_CONFIG_CIRQ_HOST_ENDTFD);
6618198429Srpaulo
6619198429Srpaulo	iwn_nic_unlock(sc);
6620198429Srpaulo
6621198429Srpaulo	/* Wait at most five seconds for FH DMA transfer to complete. */
6622220661Sbschmidt	return msleep(sc, &sc->sc_mtx, PCATCH, "iwninit", 5 * hz);
6623198429Srpaulo}
6624198429Srpaulo
6625206477Sbschmidtstatic int
6626198429Srpauloiwn5000_load_firmware(struct iwn_softc *sc)
6627198429Srpaulo{
6628198429Srpaulo	struct iwn_fw_part *fw;
6629198429Srpaulo	int error;
6630198429Srpaulo
6631253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
6632253705Sadrian
6633198429Srpaulo	/* Load the initialization firmware on first boot only. */
6634201209Srpaulo	fw = (sc->sc_flags & IWN_FLAG_CALIB_DONE) ?
6635201209Srpaulo	    &sc->fw.main : &sc->fw.init;
6636198429Srpaulo
6637198429Srpaulo	error = iwn5000_load_firmware_section(sc, IWN_FW_TEXT_BASE,
6638198429Srpaulo	    fw->text, fw->textsz);
6639178676Ssam	if (error != 0) {
6640178676Ssam		device_printf(sc->sc_dev,
6641198429Srpaulo		    "%s: could not load firmware %s section, error %d\n",
6642198429Srpaulo		    __func__, ".text", error);
6643178676Ssam		return error;
6644178676Ssam	}
6645198429Srpaulo	error = iwn5000_load_firmware_section(sc, IWN_FW_DATA_BASE,
6646198429Srpaulo	    fw->data, fw->datasz);
6647178676Ssam	if (error != 0) {
6648178676Ssam		device_printf(sc->sc_dev,
6649198429Srpaulo		    "%s: could not load firmware %s section, error %d\n",
6650198429Srpaulo		    __func__, ".data", error);
6651178676Ssam		return error;
6652178676Ssam	}
6653178676Ssam
6654198429Srpaulo	/* Now press "execute". */
6655198429Srpaulo	IWN_WRITE(sc, IWN_RESET, 0);
6656198429Srpaulo	return 0;
6657198429Srpaulo}
6658198429Srpaulo
6659210111Sbschmidt/*
6660210111Sbschmidt * Extract text and data sections from a legacy firmware image.
6661210111Sbschmidt */
6662206477Sbschmidtstatic int
6663210111Sbschmidtiwn_read_firmware_leg(struct iwn_softc *sc, struct iwn_fw_info *fw)
6664198429Srpaulo{
6665201209Srpaulo	const uint32_t *ptr;
6666210111Sbschmidt	size_t hdrlen = 24;
6667201209Srpaulo	uint32_t rev;
6668198429Srpaulo
6669220661Sbschmidt	ptr = (const uint32_t *)fw->data;
6670201209Srpaulo	rev = le32toh(*ptr++);
6671210111Sbschmidt
6672201209Srpaulo	/* Check firmware API version. */
6673201209Srpaulo	if (IWN_FW_API(rev) <= 1) {
6674201209Srpaulo		device_printf(sc->sc_dev,
6675201209Srpaulo		    "%s: bad firmware, need API version >=2\n", __func__);
6676201209Srpaulo		return EINVAL;
6677201209Srpaulo	}
6678201209Srpaulo	if (IWN_FW_API(rev) >= 3) {
6679201209Srpaulo		/* Skip build number (version 2 header). */
6680210111Sbschmidt		hdrlen += 4;
6681201209Srpaulo		ptr++;
6682201209Srpaulo	}
6683210111Sbschmidt	if (fw->size < hdrlen) {
6684220724Sbschmidt		device_printf(sc->sc_dev, "%s: firmware too short: %zu bytes\n",
6685210111Sbschmidt		    __func__, fw->size);
6686210111Sbschmidt		return EINVAL;
6687210111Sbschmidt	}
6688201209Srpaulo	fw->main.textsz = le32toh(*ptr++);
6689201209Srpaulo	fw->main.datasz = le32toh(*ptr++);
6690201209Srpaulo	fw->init.textsz = le32toh(*ptr++);
6691201209Srpaulo	fw->init.datasz = le32toh(*ptr++);
6692201209Srpaulo	fw->boot.textsz = le32toh(*ptr++);
6693198429Srpaulo
6694198429Srpaulo	/* Check that all firmware sections fit. */
6695210111Sbschmidt	if (fw->size < hdrlen + fw->main.textsz + fw->main.datasz +
6696210111Sbschmidt	    fw->init.textsz + fw->init.datasz + fw->boot.textsz) {
6697220724Sbschmidt		device_printf(sc->sc_dev, "%s: firmware too short: %zu bytes\n",
6698210111Sbschmidt		    __func__, fw->size);
6699198429Srpaulo		return EINVAL;
6700178676Ssam	}
6701198429Srpaulo
6702198429Srpaulo	/* Get pointers to firmware sections. */
6703201209Srpaulo	fw->main.text = (const uint8_t *)ptr;
6704198429Srpaulo	fw->main.data = fw->main.text + fw->main.textsz;
6705198429Srpaulo	fw->init.text = fw->main.data + fw->main.datasz;
6706198429Srpaulo	fw->init.data = fw->init.text + fw->init.textsz;
6707198429Srpaulo	fw->boot.text = fw->init.data + fw->init.datasz;
6708178676Ssam	return 0;
6709178676Ssam}
6710178676Ssam
6711210111Sbschmidt/*
6712210111Sbschmidt * Extract text and data sections from a TLV firmware image.
6713210111Sbschmidt */
6714220661Sbschmidtstatic int
6715210111Sbschmidtiwn_read_firmware_tlv(struct iwn_softc *sc, struct iwn_fw_info *fw,
6716210111Sbschmidt    uint16_t alt)
6717210111Sbschmidt{
6718210111Sbschmidt	const struct iwn_fw_tlv_hdr *hdr;
6719210111Sbschmidt	const struct iwn_fw_tlv *tlv;
6720210111Sbschmidt	const uint8_t *ptr, *end;
6721210111Sbschmidt	uint64_t altmask;
6722220866Sbschmidt	uint32_t len, tmp;
6723210111Sbschmidt
6724210111Sbschmidt	if (fw->size < sizeof (*hdr)) {
6725220724Sbschmidt		device_printf(sc->sc_dev, "%s: firmware too short: %zu bytes\n",
6726210111Sbschmidt		    __func__, fw->size);
6727210111Sbschmidt		return EINVAL;
6728210111Sbschmidt	}
6729210111Sbschmidt	hdr = (const struct iwn_fw_tlv_hdr *)fw->data;
6730210111Sbschmidt	if (hdr->signature != htole32(IWN_FW_SIGNATURE)) {
6731220724Sbschmidt		device_printf(sc->sc_dev, "%s: bad firmware signature 0x%08x\n",
6732210111Sbschmidt		    __func__, le32toh(hdr->signature));
6733210111Sbschmidt		return EINVAL;
6734210111Sbschmidt	}
6735220724Sbschmidt	DPRINTF(sc, IWN_DEBUG_RESET, "FW: \"%.64s\", build 0x%x\n", hdr->descr,
6736220724Sbschmidt	    le32toh(hdr->build));
6737210111Sbschmidt
6738210111Sbschmidt	/*
6739210111Sbschmidt	 * Select the closest supported alternative that is less than
6740210111Sbschmidt	 * or equal to the specified one.
6741210111Sbschmidt	 */
6742210111Sbschmidt	altmask = le64toh(hdr->altmask);
6743210111Sbschmidt	while (alt > 0 && !(altmask & (1ULL << alt)))
6744210111Sbschmidt		alt--;	/* Downgrade. */
6745220724Sbschmidt	DPRINTF(sc, IWN_DEBUG_RESET, "using alternative %d\n", alt);
6746210111Sbschmidt
6747210111Sbschmidt	ptr = (const uint8_t *)(hdr + 1);
6748210111Sbschmidt	end = (const uint8_t *)(fw->data + fw->size);
6749210111Sbschmidt
6750210111Sbschmidt	/* Parse type-length-value fields. */
6751210111Sbschmidt	while (ptr + sizeof (*tlv) <= end) {
6752210111Sbschmidt		tlv = (const struct iwn_fw_tlv *)ptr;
6753210111Sbschmidt		len = le32toh(tlv->len);
6754210111Sbschmidt
6755210111Sbschmidt		ptr += sizeof (*tlv);
6756210111Sbschmidt		if (ptr + len > end) {
6757210111Sbschmidt			device_printf(sc->sc_dev,
6758220724Sbschmidt			    "%s: firmware too short: %zu bytes\n", __func__,
6759220724Sbschmidt			    fw->size);
6760210111Sbschmidt			return EINVAL;
6761210111Sbschmidt		}
6762210111Sbschmidt		/* Skip other alternatives. */
6763210111Sbschmidt		if (tlv->alt != 0 && tlv->alt != htole16(alt))
6764210111Sbschmidt			goto next;
6765210111Sbschmidt
6766210111Sbschmidt		switch (le16toh(tlv->type)) {
6767210111Sbschmidt		case IWN_FW_TLV_MAIN_TEXT:
6768210111Sbschmidt			fw->main.text = ptr;
6769210111Sbschmidt			fw->main.textsz = len;
6770210111Sbschmidt			break;
6771210111Sbschmidt		case IWN_FW_TLV_MAIN_DATA:
6772210111Sbschmidt			fw->main.data = ptr;
6773210111Sbschmidt			fw->main.datasz = len;
6774210111Sbschmidt			break;
6775210111Sbschmidt		case IWN_FW_TLV_INIT_TEXT:
6776210111Sbschmidt			fw->init.text = ptr;
6777210111Sbschmidt			fw->init.textsz = len;
6778210111Sbschmidt			break;
6779210111Sbschmidt		case IWN_FW_TLV_INIT_DATA:
6780210111Sbschmidt			fw->init.data = ptr;
6781210111Sbschmidt			fw->init.datasz = len;
6782210111Sbschmidt			break;
6783210111Sbschmidt		case IWN_FW_TLV_BOOT_TEXT:
6784210111Sbschmidt			fw->boot.text = ptr;
6785210111Sbschmidt			fw->boot.textsz = len;
6786210111Sbschmidt			break;
6787220866Sbschmidt		case IWN_FW_TLV_ENH_SENS:
6788220866Sbschmidt			if (!len)
6789220866Sbschmidt				sc->sc_flags |= IWN_FLAG_ENH_SENS;
6790220866Sbschmidt			break;
6791220866Sbschmidt		case IWN_FW_TLV_PHY_CALIB:
6792220866Sbschmidt			tmp = htole32(*ptr);
6793220866Sbschmidt			if (tmp < 253) {
6794220866Sbschmidt				sc->reset_noise_gain = tmp;
6795220866Sbschmidt				sc->noise_gain = tmp + 1;
6796220866Sbschmidt			}
6797220866Sbschmidt			break;
6798254204Sadrian		case IWN_FW_TLV_PAN:
6799254204Sadrian			sc->sc_flags |= IWN_FLAG_PAN_SUPPORT;
6800254204Sadrian			DPRINTF(sc, IWN_DEBUG_RESET,
6801254204Sadrian			    "PAN Support found: %d\n", 1);
6802254204Sadrian			break;
6803254204Sadrian		case IWN_FW_TLV_FLAGS :
6804254204Sadrian			sc->tlv_feature_flags = htole32(*ptr);
6805254204Sadrian			break;
6806254204Sadrian		case IWN_FW_TLV_PBREQ_MAXLEN:
6807254204Sadrian		case IWN_FW_TLV_RUNT_EVTLOG_PTR:
6808254204Sadrian		case IWN_FW_TLV_RUNT_EVTLOG_SIZE:
6809254204Sadrian		case IWN_FW_TLV_RUNT_ERRLOG_PTR:
6810254204Sadrian		case IWN_FW_TLV_INIT_EVTLOG_PTR:
6811254204Sadrian		case IWN_FW_TLV_INIT_EVTLOG_SIZE:
6812254204Sadrian		case IWN_FW_TLV_INIT_ERRLOG_PTR:
6813254204Sadrian		case IWN_FW_TLV_WOWLAN_INST:
6814254204Sadrian		case IWN_FW_TLV_WOWLAN_DATA:
6815254204Sadrian			DPRINTF(sc, IWN_DEBUG_RESET,
6816281480Seadler			    "TLV type %d recognized but not handled\n",
6817254204Sadrian			    le16toh(tlv->type));
6818254204Sadrian			break;
6819210111Sbschmidt		default:
6820210111Sbschmidt			DPRINTF(sc, IWN_DEBUG_RESET,
6821220724Sbschmidt			    "TLV type %d not handled\n", le16toh(tlv->type));
6822210111Sbschmidt			break;
6823210111Sbschmidt		}
6824220726Sbschmidt next:		/* TLV fields are 32-bit aligned. */
6825210111Sbschmidt		ptr += (len + 3) & ~3;
6826210111Sbschmidt	}
6827210111Sbschmidt	return 0;
6828210111Sbschmidt}
6829210111Sbschmidt
6830206477Sbschmidtstatic int
6831210111Sbschmidtiwn_read_firmware(struct iwn_softc *sc)
6832210111Sbschmidt{
6833210111Sbschmidt	struct iwn_fw_info *fw = &sc->fw;
6834210111Sbschmidt	int error;
6835210111Sbschmidt
6836253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
6837253705Sadrian
6838210111Sbschmidt	IWN_UNLOCK(sc);
6839210111Sbschmidt
6840210111Sbschmidt	memset(fw, 0, sizeof (*fw));
6841210111Sbschmidt
6842210111Sbschmidt	/* Read firmware image from filesystem. */
6843210111Sbschmidt	sc->fw_fp = firmware_get(sc->fwname);
6844210111Sbschmidt	if (sc->fw_fp == NULL) {
6845220724Sbschmidt		device_printf(sc->sc_dev, "%s: could not read firmware %s\n",
6846220724Sbschmidt		    __func__, sc->fwname);
6847210111Sbschmidt		IWN_LOCK(sc);
6848210111Sbschmidt		return EINVAL;
6849210111Sbschmidt	}
6850210111Sbschmidt	IWN_LOCK(sc);
6851210111Sbschmidt
6852210111Sbschmidt	fw->size = sc->fw_fp->datasize;
6853210111Sbschmidt	fw->data = (const uint8_t *)sc->fw_fp->data;
6854210111Sbschmidt	if (fw->size < sizeof (uint32_t)) {
6855220724Sbschmidt		device_printf(sc->sc_dev, "%s: firmware too short: %zu bytes\n",
6856210111Sbschmidt		    __func__, fw->size);
6857220661Sbschmidt		firmware_put(sc->fw_fp, FIRMWARE_UNLOAD);
6858220661Sbschmidt		sc->fw_fp = NULL;
6859210111Sbschmidt		return EINVAL;
6860210111Sbschmidt	}
6861210111Sbschmidt
6862210111Sbschmidt	/* Retrieve text and data sections. */
6863210111Sbschmidt	if (*(const uint32_t *)fw->data != 0)	/* Legacy image. */
6864210111Sbschmidt		error = iwn_read_firmware_leg(sc, fw);
6865210111Sbschmidt	else
6866210111Sbschmidt		error = iwn_read_firmware_tlv(sc, fw, 1);
6867210111Sbschmidt	if (error != 0) {
6868210111Sbschmidt		device_printf(sc->sc_dev,
6869220724Sbschmidt		    "%s: could not read firmware sections, error %d\n",
6870220724Sbschmidt		    __func__, error);
6871220661Sbschmidt		firmware_put(sc->fw_fp, FIRMWARE_UNLOAD);
6872220661Sbschmidt		sc->fw_fp = NULL;
6873210111Sbschmidt		return error;
6874210111Sbschmidt	}
6875210111Sbschmidt
6876210111Sbschmidt	/* Make sure text and data sections fit in hardware memory. */
6877220728Sbschmidt	if (fw->main.textsz > sc->fw_text_maxsz ||
6878220728Sbschmidt	    fw->main.datasz > sc->fw_data_maxsz ||
6879220728Sbschmidt	    fw->init.textsz > sc->fw_text_maxsz ||
6880220728Sbschmidt	    fw->init.datasz > sc->fw_data_maxsz ||
6881210111Sbschmidt	    fw->boot.textsz > IWN_FW_BOOT_TEXT_MAXSZ ||
6882210111Sbschmidt	    (fw->boot.textsz & 3) != 0) {
6883220724Sbschmidt		device_printf(sc->sc_dev, "%s: firmware sections too large\n",
6884220724Sbschmidt		    __func__);
6885220661Sbschmidt		firmware_put(sc->fw_fp, FIRMWARE_UNLOAD);
6886220661Sbschmidt		sc->fw_fp = NULL;
6887210111Sbschmidt		return EINVAL;
6888210111Sbschmidt	}
6889210111Sbschmidt
6890210111Sbschmidt	/* We can proceed with loading the firmware. */
6891210111Sbschmidt	return 0;
6892210111Sbschmidt}
6893210111Sbschmidt
6894210111Sbschmidtstatic int
6895198429Srpauloiwn_clock_wait(struct iwn_softc *sc)
6896198429Srpaulo{
6897198429Srpaulo	int ntries;
6898178676Ssam
6899198429Srpaulo	/* Set "initialization complete" bit. */
6900198429Srpaulo	IWN_SETBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_INIT_DONE);
6901198429Srpaulo
6902198429Srpaulo	/* Wait for clock stabilization. */
6903201209Srpaulo	for (ntries = 0; ntries < 2500; ntries++) {
6904198429Srpaulo		if (IWN_READ(sc, IWN_GP_CNTRL) & IWN_GP_CNTRL_MAC_CLOCK_READY)
6905198429Srpaulo			return 0;
6906201209Srpaulo		DELAY(10);
6907178676Ssam	}
6908198429Srpaulo	device_printf(sc->sc_dev,
6909198429Srpaulo	    "%s: timeout waiting for clock stabilization\n", __func__);
6910198429Srpaulo	return ETIMEDOUT;
6911198429Srpaulo}
6912178676Ssam
6913206477Sbschmidtstatic int
6914201209Srpauloiwn_apm_init(struct iwn_softc *sc)
6915198429Srpaulo{
6916220721Sbschmidt	uint32_t reg;
6917198429Srpaulo	int error;
6918178676Ssam
6919253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
6920253705Sadrian
6921220725Sbschmidt	/* Disable L0s exit timer (NMI bug workaround). */
6922198429Srpaulo	IWN_SETBITS(sc, IWN_GIO_CHICKEN, IWN_GIO_CHICKEN_DIS_L0S_TIMER);
6923220725Sbschmidt	/* Don't wait for ICH L0s (ICH bug workaround). */
6924198429Srpaulo	IWN_SETBITS(sc, IWN_GIO_CHICKEN, IWN_GIO_CHICKEN_L1A_NO_L0S_RX);
6925178676Ssam
6926220725Sbschmidt	/* Set FH wait threshold to max (HW bug under stress workaround). */
6927198429Srpaulo	IWN_SETBITS(sc, IWN_DBG_HPET_MEM, 0xffff0000);
6928198429Srpaulo
6929201209Srpaulo	/* Enable HAP INTA to move adapter from L1a to L0s. */
6930198429Srpaulo	IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_HAP_WAKE_L1A);
6931198429Srpaulo
6932201209Srpaulo	/* Retrieve PCIe Active State Power Management (ASPM). */
6933220721Sbschmidt	reg = pci_read_config(sc->sc_dev, sc->sc_cap_off + 0x10, 1);
6934201209Srpaulo	/* Workaround for HW instability in PCIe L0->L0s->L1 transition. */
6935220721Sbschmidt	if (reg & 0x02)	/* L1 Entry enabled. */
6936201209Srpaulo		IWN_SETBITS(sc, IWN_GIO, IWN_GIO_L0S_ENA);
6937201209Srpaulo	else
6938201209Srpaulo		IWN_CLRBITS(sc, IWN_GIO, IWN_GIO_L0S_ENA);
6939201209Srpaulo
6940201209Srpaulo	if (sc->hw_type != IWN_HW_REV_TYPE_4965 &&
6941210109Sbschmidt	    sc->hw_type <= IWN_HW_REV_TYPE_1000)
6942198429Srpaulo		IWN_SETBITS(sc, IWN_ANA_PLL, IWN_ANA_PLL_INIT);
6943198429Srpaulo
6944201209Srpaulo	/* Wait for clock stabilization before accessing prph. */
6945220726Sbschmidt	if ((error = iwn_clock_wait(sc)) != 0)
6946198429Srpaulo		return error;
6947198429Srpaulo
6948220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
6949198429Srpaulo		return error;
6950201209Srpaulo	if (sc->hw_type == IWN_HW_REV_TYPE_4965) {
6951220725Sbschmidt		/* Enable DMA and BSM (Bootstrap State Machine). */
6952201209Srpaulo		iwn_prph_write(sc, IWN_APMG_CLK_EN,
6953201209Srpaulo		    IWN_APMG_CLK_CTRL_DMA_CLK_RQT |
6954201209Srpaulo		    IWN_APMG_CLK_CTRL_BSM_CLK_RQT);
6955201209Srpaulo	} else {
6956201209Srpaulo		/* Enable DMA. */
6957201209Srpaulo		iwn_prph_write(sc, IWN_APMG_CLK_EN,
6958201209Srpaulo		    IWN_APMG_CLK_CTRL_DMA_CLK_RQT);
6959201209Srpaulo	}
6960198429Srpaulo	DELAY(20);
6961201209Srpaulo	/* Disable L1-Active. */
6962198429Srpaulo	iwn_prph_setbits(sc, IWN_APMG_PCI_STT, IWN_APMG_PCI_STT_L1A_DIS);
6963198429Srpaulo	iwn_nic_unlock(sc);
6964198429Srpaulo
6965198429Srpaulo	return 0;
6966198429Srpaulo}
6967198429Srpaulo
6968206477Sbschmidtstatic void
6969198429Srpauloiwn_apm_stop_master(struct iwn_softc *sc)
6970178676Ssam{
6971178676Ssam	int ntries;
6972178676Ssam
6973201209Srpaulo	/* Stop busmaster DMA activity. */
6974198429Srpaulo	IWN_SETBITS(sc, IWN_RESET, IWN_RESET_STOP_MASTER);
6975178676Ssam	for (ntries = 0; ntries < 100; ntries++) {
6976198429Srpaulo		if (IWN_READ(sc, IWN_RESET) & IWN_RESET_MASTER_DISABLED)
6977198429Srpaulo			return;
6978178676Ssam		DELAY(10);
6979178676Ssam	}
6980220726Sbschmidt	device_printf(sc->sc_dev, "%s: timeout waiting for master\n", __func__);
6981178676Ssam}
6982178676Ssam
6983206477Sbschmidtstatic void
6984198429Srpauloiwn_apm_stop(struct iwn_softc *sc)
6985198429Srpaulo{
6986198429Srpaulo	iwn_apm_stop_master(sc);
6987198429Srpaulo
6988201209Srpaulo	/* Reset the entire device. */
6989198429Srpaulo	IWN_SETBITS(sc, IWN_RESET, IWN_RESET_SW);
6990198429Srpaulo	DELAY(10);
6991198429Srpaulo	/* Clear "initialization complete" bit. */
6992198429Srpaulo	IWN_CLRBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_INIT_DONE);
6993198429Srpaulo}
6994198429Srpaulo
6995206477Sbschmidtstatic int
6996198429Srpauloiwn4965_nic_config(struct iwn_softc *sc)
6997178676Ssam{
6998253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
6999253705Sadrian
7000198429Srpaulo	if (IWN_RFCFG_TYPE(sc->rfcfg) == 1) {
7001198429Srpaulo		/*
7002198429Srpaulo		 * I don't believe this to be correct but this is what the
7003198429Srpaulo		 * vendor driver is doing. Probably the bits should not be
7004198429Srpaulo		 * shifted in IWN_RFCFG_*.
7005198429Srpaulo		 */
7006198429Srpaulo		IWN_SETBITS(sc, IWN_HW_IF_CONFIG,
7007198429Srpaulo		    IWN_RFCFG_TYPE(sc->rfcfg) |
7008198429Srpaulo		    IWN_RFCFG_STEP(sc->rfcfg) |
7009198429Srpaulo		    IWN_RFCFG_DASH(sc->rfcfg));
7010198429Srpaulo	}
7011198429Srpaulo	IWN_SETBITS(sc, IWN_HW_IF_CONFIG,
7012198429Srpaulo	    IWN_HW_IF_CONFIG_RADIO_SI | IWN_HW_IF_CONFIG_MAC_SI);
7013198429Srpaulo	return 0;
7014198429Srpaulo}
7015178676Ssam
7016206477Sbschmidtstatic int
7017198429Srpauloiwn5000_nic_config(struct iwn_softc *sc)
7018198429Srpaulo{
7019198429Srpaulo	uint32_t tmp;
7020198429Srpaulo	int error;
7021178676Ssam
7022253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
7023253705Sadrian
7024198429Srpaulo	if (IWN_RFCFG_TYPE(sc->rfcfg) < 3) {
7025198429Srpaulo		IWN_SETBITS(sc, IWN_HW_IF_CONFIG,
7026198429Srpaulo		    IWN_RFCFG_TYPE(sc->rfcfg) |
7027198429Srpaulo		    IWN_RFCFG_STEP(sc->rfcfg) |
7028198429Srpaulo		    IWN_RFCFG_DASH(sc->rfcfg));
7029198429Srpaulo	}
7030198429Srpaulo	IWN_SETBITS(sc, IWN_HW_IF_CONFIG,
7031198429Srpaulo	    IWN_HW_IF_CONFIG_RADIO_SI | IWN_HW_IF_CONFIG_MAC_SI);
7032198429Srpaulo
7033220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
7034198429Srpaulo		return error;
7035198429Srpaulo	iwn_prph_setbits(sc, IWN_APMG_PS, IWN_APMG_PS_EARLY_PWROFF_DIS);
7036201209Srpaulo
7037201209Srpaulo	if (sc->hw_type == IWN_HW_REV_TYPE_1000) {
7038201209Srpaulo		/*
7039201209Srpaulo		 * Select first Switching Voltage Regulator (1.32V) to
7040201209Srpaulo		 * solve a stability issue related to noisy DC2DC line
7041201209Srpaulo		 * in the silicon of 1000 Series.
7042201209Srpaulo		 */
7043201209Srpaulo		tmp = iwn_prph_read(sc, IWN_APMG_DIGITAL_SVR);
7044201209Srpaulo		tmp &= ~IWN_APMG_DIGITAL_SVR_VOLTAGE_MASK;
7045201209Srpaulo		tmp |= IWN_APMG_DIGITAL_SVR_VOLTAGE_1_32;
7046201209Srpaulo		iwn_prph_write(sc, IWN_APMG_DIGITAL_SVR, tmp);
7047201209Srpaulo	}
7048198429Srpaulo	iwn_nic_unlock(sc);
7049201209Srpaulo
7050201209Srpaulo	if (sc->sc_flags & IWN_FLAG_INTERNAL_PA) {
7051201209Srpaulo		/* Use internal power amplifier only. */
7052201209Srpaulo		IWN_WRITE(sc, IWN_GP_DRIVER, IWN_GP_DRIVER_RADIO_2X2_IPA);
7053201209Srpaulo	}
7054220676Sbschmidt	if ((sc->hw_type == IWN_HW_REV_TYPE_6050 ||
7055220676Sbschmidt	     sc->hw_type == IWN_HW_REV_TYPE_6005) && sc->calib_ver >= 6) {
7056210108Sbschmidt		/* Indicate that ROM calibration version is >=6. */
7057210108Sbschmidt		IWN_SETBITS(sc, IWN_GP_DRIVER, IWN_GP_DRIVER_CALIB_VER6);
7058206444Sbschmidt	}
7059220729Sbschmidt	if (sc->hw_type == IWN_HW_REV_TYPE_6005)
7060220729Sbschmidt		IWN_SETBITS(sc, IWN_GP_DRIVER, IWN_GP_DRIVER_6050_1X2);
7061198429Srpaulo	return 0;
7062198429Srpaulo}
7063198429Srpaulo
7064198429Srpaulo/*
7065198429Srpaulo * Take NIC ownership over Intel Active Management Technology (AMT).
7066198429Srpaulo */
7067206477Sbschmidtstatic int
7068198429Srpauloiwn_hw_prepare(struct iwn_softc *sc)
7069198429Srpaulo{
7070198429Srpaulo	int ntries;
7071198429Srpaulo
7072253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
7073253705Sadrian
7074201209Srpaulo	/* Check if hardware is ready. */
7075201209Srpaulo	IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_NIC_READY);
7076201209Srpaulo	for (ntries = 0; ntries < 5; ntries++) {
7077201209Srpaulo		if (IWN_READ(sc, IWN_HW_IF_CONFIG) &
7078201209Srpaulo		    IWN_HW_IF_CONFIG_NIC_READY)
7079201209Srpaulo			return 0;
7080201209Srpaulo		DELAY(10);
7081201209Srpaulo	}
7082201209Srpaulo
7083201209Srpaulo	/* Hardware not ready, force into ready state. */
7084198429Srpaulo	IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_PREPARE);
7085198429Srpaulo	for (ntries = 0; ntries < 15000; ntries++) {
7086198429Srpaulo		if (!(IWN_READ(sc, IWN_HW_IF_CONFIG) &
7087198429Srpaulo		    IWN_HW_IF_CONFIG_PREPARE_DONE))
7088178676Ssam			break;
7089178676Ssam		DELAY(10);
7090178676Ssam	}
7091198429Srpaulo	if (ntries == 15000)
7092178676Ssam		return ETIMEDOUT;
7093198429Srpaulo
7094201209Srpaulo	/* Hardware should be ready now. */
7095198429Srpaulo	IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_NIC_READY);
7096198429Srpaulo	for (ntries = 0; ntries < 5; ntries++) {
7097198429Srpaulo		if (IWN_READ(sc, IWN_HW_IF_CONFIG) &
7098198429Srpaulo		    IWN_HW_IF_CONFIG_NIC_READY)
7099198429Srpaulo			return 0;
7100198429Srpaulo		DELAY(10);
7101178676Ssam	}
7102198429Srpaulo	return ETIMEDOUT;
7103178676Ssam}
7104178676Ssam
7105206477Sbschmidtstatic int
7106198429Srpauloiwn_hw_init(struct iwn_softc *sc)
7107178676Ssam{
7108220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
7109198429Srpaulo	int error, chnl, qid;
7110178676Ssam
7111253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
7112253705Sadrian
7113198429Srpaulo	/* Clear pending interrupts. */
7114198429Srpaulo	IWN_WRITE(sc, IWN_INT, 0xffffffff);
7115178676Ssam
7116220726Sbschmidt	if ((error = iwn_apm_init(sc)) != 0) {
7117198429Srpaulo		device_printf(sc->sc_dev,
7118220726Sbschmidt		    "%s: could not power ON adapter, error %d\n", __func__,
7119220726Sbschmidt		    error);
7120198429Srpaulo		return error;
7121178676Ssam	}
7122178676Ssam
7123198429Srpaulo	/* Select VMAIN power source. */
7124220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
7125198429Srpaulo		return error;
7126198429Srpaulo	iwn_prph_clrbits(sc, IWN_APMG_PS, IWN_APMG_PS_PWR_SRC_MASK);
7127198429Srpaulo	iwn_nic_unlock(sc);
7128178676Ssam
7129198429Srpaulo	/* Perform adapter-specific initialization. */
7130220728Sbschmidt	if ((error = ops->nic_config(sc)) != 0)
7131198429Srpaulo		return error;
7132178676Ssam
7133198429Srpaulo	/* Initialize RX ring. */
7134220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
7135198429Srpaulo		return error;
7136198429Srpaulo	IWN_WRITE(sc, IWN_FH_RX_CONFIG, 0);
7137198429Srpaulo	IWN_WRITE(sc, IWN_FH_RX_WPTR, 0);
7138220725Sbschmidt	/* Set physical address of RX ring (256-byte aligned). */
7139198429Srpaulo	IWN_WRITE(sc, IWN_FH_RX_BASE, sc->rxq.desc_dma.paddr >> 8);
7140220725Sbschmidt	/* Set physical address of RX status (16-byte aligned). */
7141198429Srpaulo	IWN_WRITE(sc, IWN_FH_STATUS_WPTR, sc->rxq.stat_dma.paddr >> 4);
7142198429Srpaulo	/* Enable RX. */
7143198429Srpaulo	IWN_WRITE(sc, IWN_FH_RX_CONFIG,
7144198429Srpaulo	    IWN_FH_RX_CONFIG_ENA           |
7145198429Srpaulo	    IWN_FH_RX_CONFIG_IGN_RXF_EMPTY |	/* HW bug workaround */
7146198429Srpaulo	    IWN_FH_RX_CONFIG_IRQ_DST_HOST  |
7147198429Srpaulo	    IWN_FH_RX_CONFIG_SINGLE_FRAME  |
7148198429Srpaulo	    IWN_FH_RX_CONFIG_RB_TIMEOUT(0) |
7149198429Srpaulo	    IWN_FH_RX_CONFIG_NRBD(IWN_RX_RING_COUNT_LOG));
7150198429Srpaulo	iwn_nic_unlock(sc);
7151198429Srpaulo	IWN_WRITE(sc, IWN_FH_RX_WPTR, (IWN_RX_RING_COUNT - 1) & ~7);
7152178676Ssam
7153220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
7154198429Srpaulo		return error;
7155178676Ssam
7156198429Srpaulo	/* Initialize TX scheduler. */
7157220728Sbschmidt	iwn_prph_write(sc, sc->sched_txfact_addr, 0);
7158178676Ssam
7159220725Sbschmidt	/* Set physical address of "keep warm" page (16-byte aligned). */
7160198429Srpaulo	IWN_WRITE(sc, IWN_FH_KW_ADDR, sc->kw_dma.paddr >> 4);
7161198429Srpaulo
7162198429Srpaulo	/* Initialize TX rings. */
7163220728Sbschmidt	for (qid = 0; qid < sc->ntxqs; qid++) {
7164198429Srpaulo		struct iwn_tx_ring *txq = &sc->txq[qid];
7165198429Srpaulo
7166220725Sbschmidt		/* Set physical address of TX ring (256-byte aligned). */
7167198429Srpaulo		IWN_WRITE(sc, IWN_FH_CBBC_QUEUE(qid),
7168198429Srpaulo		    txq->desc_dma.paddr >> 8);
7169178676Ssam	}
7170198429Srpaulo	iwn_nic_unlock(sc);
7171178676Ssam
7172198429Srpaulo	/* Enable DMA channels. */
7173220728Sbschmidt	for (chnl = 0; chnl < sc->ndmachnls; chnl++) {
7174198429Srpaulo		IWN_WRITE(sc, IWN_FH_TX_CONFIG(chnl),
7175198429Srpaulo		    IWN_FH_TX_CONFIG_DMA_ENA |
7176198429Srpaulo		    IWN_FH_TX_CONFIG_DMA_CREDIT_ENA);
7177198429Srpaulo	}
7178198429Srpaulo
7179198429Srpaulo	/* Clear "radio off" and "commands blocked" bits. */
7180198429Srpaulo	IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_RFKILL);
7181198429Srpaulo	IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_CMD_BLOCKED);
7182198429Srpaulo
7183198429Srpaulo	/* Clear pending interrupts. */
7184198429Srpaulo	IWN_WRITE(sc, IWN_INT, 0xffffffff);
7185198429Srpaulo	/* Enable interrupt coalescing. */
7186198429Srpaulo	IWN_WRITE(sc, IWN_INT_COALESCING, 512 / 8);
7187198429Srpaulo	/* Enable interrupts. */
7188201209Srpaulo	IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask);
7189198429Srpaulo
7190198429Srpaulo	/* _Really_ make sure "radio off" bit is cleared! */
7191198429Srpaulo	IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_RFKILL);
7192198429Srpaulo	IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_RFKILL);
7193198429Srpaulo
7194220729Sbschmidt	/* Enable shadow registers. */
7195220729Sbschmidt	if (sc->hw_type >= IWN_HW_REV_TYPE_6000)
7196220729Sbschmidt		IWN_SETBITS(sc, IWN_SHADOW_REG_CTRL, 0x800fffff);
7197220729Sbschmidt
7198220728Sbschmidt	if ((error = ops->load_firmware(sc)) != 0) {
7199178676Ssam		device_printf(sc->sc_dev,
7200220726Sbschmidt		    "%s: could not load firmware, error %d\n", __func__,
7201220726Sbschmidt		    error);
7202198429Srpaulo		return error;
7203178676Ssam	}
7204198429Srpaulo	/* Wait at most one second for firmware alive notification. */
7205220726Sbschmidt	if ((error = msleep(sc, &sc->sc_mtx, PCATCH, "iwninit", hz)) != 0) {
7206198429Srpaulo		device_printf(sc->sc_dev,
7207198429Srpaulo		    "%s: timeout waiting for adapter to initialize, error %d\n",
7208198429Srpaulo		    __func__, error);
7209198429Srpaulo		return error;
7210198429Srpaulo	}
7211198429Srpaulo	/* Do post-firmware initialization. */
7212253705Sadrian
7213253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
7214253705Sadrian
7215220728Sbschmidt	return ops->post_alive(sc);
7216198429Srpaulo}
7217178676Ssam
7218206477Sbschmidtstatic void
7219198429Srpauloiwn_hw_stop(struct iwn_softc *sc)
7220198429Srpaulo{
7221198429Srpaulo	int chnl, qid, ntries;
7222178676Ssam
7223253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
7224253705Sadrian
7225198429Srpaulo	IWN_WRITE(sc, IWN_RESET, IWN_RESET_NEVO);
7226178676Ssam
7227198429Srpaulo	/* Disable interrupts. */
7228201209Srpaulo	IWN_WRITE(sc, IWN_INT_MASK, 0);
7229198429Srpaulo	IWN_WRITE(sc, IWN_INT, 0xffffffff);
7230198429Srpaulo	IWN_WRITE(sc, IWN_FH_INT, 0xffffffff);
7231201209Srpaulo	sc->sc_flags &= ~IWN_FLAG_USE_ICT;
7232178676Ssam
7233198429Srpaulo	/* Make sure we no longer hold the NIC lock. */
7234198429Srpaulo	iwn_nic_unlock(sc);
7235178676Ssam
7236198429Srpaulo	/* Stop TX scheduler. */
7237220728Sbschmidt	iwn_prph_write(sc, sc->sched_txfact_addr, 0);
7238178676Ssam
7239198429Srpaulo	/* Stop all DMA channels. */
7240198429Srpaulo	if (iwn_nic_lock(sc) == 0) {
7241220728Sbschmidt		for (chnl = 0; chnl < sc->ndmachnls; chnl++) {
7242198429Srpaulo			IWN_WRITE(sc, IWN_FH_TX_CONFIG(chnl), 0);
7243198429Srpaulo			for (ntries = 0; ntries < 200; ntries++) {
7244220659Sbschmidt				if (IWN_READ(sc, IWN_FH_TX_STATUS) &
7245198429Srpaulo				    IWN_FH_TX_STATUS_IDLE(chnl))
7246198429Srpaulo					break;
7247198429Srpaulo				DELAY(10);
7248198429Srpaulo			}
7249198429Srpaulo		}
7250198429Srpaulo		iwn_nic_unlock(sc);
7251198429Srpaulo	}
7252178676Ssam
7253198429Srpaulo	/* Stop RX ring. */
7254198429Srpaulo	iwn_reset_rx_ring(sc, &sc->rxq);
7255178676Ssam
7256198429Srpaulo	/* Reset all TX rings. */
7257220728Sbschmidt	for (qid = 0; qid < sc->ntxqs; qid++)
7258198429Srpaulo		iwn_reset_tx_ring(sc, &sc->txq[qid]);
7259178676Ssam
7260198429Srpaulo	if (iwn_nic_lock(sc) == 0) {
7261201209Srpaulo		iwn_prph_write(sc, IWN_APMG_CLK_DIS,
7262201209Srpaulo		    IWN_APMG_CLK_CTRL_DMA_CLK_RQT);
7263198429Srpaulo		iwn_nic_unlock(sc);
7264178676Ssam	}
7265198429Srpaulo	DELAY(5);
7266198429Srpaulo	/* Power OFF adapter. */
7267198429Srpaulo	iwn_apm_stop(sc);
7268198429Srpaulo}
7269178676Ssam
7270206477Sbschmidtstatic void
7271220723Sbschmidtiwn_radio_on(void *arg0, int pending)
7272220723Sbschmidt{
7273220723Sbschmidt	struct iwn_softc *sc = arg0;
7274220723Sbschmidt	struct ifnet *ifp = sc->sc_ifp;
7275220723Sbschmidt	struct ieee80211com *ic = ifp->if_l2com;
7276220723Sbschmidt	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
7277220723Sbschmidt
7278253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
7279253705Sadrian
7280220723Sbschmidt	if (vap != NULL) {
7281220723Sbschmidt		iwn_init(sc);
7282220723Sbschmidt		ieee80211_init(vap);
7283220723Sbschmidt	}
7284220723Sbschmidt}
7285220723Sbschmidt
7286220723Sbschmidtstatic void
7287220723Sbschmidtiwn_radio_off(void *arg0, int pending)
7288220723Sbschmidt{
7289220723Sbschmidt	struct iwn_softc *sc = arg0;
7290220723Sbschmidt	struct ifnet *ifp = sc->sc_ifp;
7291220723Sbschmidt	struct ieee80211com *ic = ifp->if_l2com;
7292220723Sbschmidt	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
7293220723Sbschmidt
7294253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
7295253705Sadrian
7296220723Sbschmidt	iwn_stop(sc);
7297220723Sbschmidt	if (vap != NULL)
7298220723Sbschmidt		ieee80211_stop(vap);
7299220723Sbschmidt
7300220723Sbschmidt	/* Enable interrupts to get RF toggle notification. */
7301220723Sbschmidt	IWN_LOCK(sc);
7302220723Sbschmidt	IWN_WRITE(sc, IWN_INT, 0xffffffff);
7303220723Sbschmidt	IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask);
7304220723Sbschmidt	IWN_UNLOCK(sc);
7305220723Sbschmidt}
7306220723Sbschmidt
7307220723Sbschmidtstatic void
7308198429Srpauloiwn_init_locked(struct iwn_softc *sc)
7309198429Srpaulo{
7310198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
7311198429Srpaulo	int error;
7312178676Ssam
7313253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
7314253705Sadrian
7315198429Srpaulo	IWN_LOCK_ASSERT(sc);
7316178676Ssam
7317220726Sbschmidt	if ((error = iwn_hw_prepare(sc)) != 0) {
7318220724Sbschmidt		device_printf(sc->sc_dev, "%s: hardware not ready, error %d\n",
7319198429Srpaulo		    __func__, error);
7320198429Srpaulo		goto fail;
7321198429Srpaulo	}
7322198429Srpaulo
7323201209Srpaulo	/* Initialize interrupt mask to default value. */
7324201209Srpaulo	sc->int_mask = IWN_INT_MASK_DEF;
7325201209Srpaulo	sc->sc_flags &= ~IWN_FLAG_USE_ICT;
7326201209Srpaulo
7327198429Srpaulo	/* Check that the radio is not disabled by hardware switch. */
7328198429Srpaulo	if (!(IWN_READ(sc, IWN_GP_CNTRL) & IWN_GP_CNTRL_RFKILL)) {
7329178676Ssam		device_printf(sc->sc_dev,
7330201209Srpaulo		    "radio is disabled by hardware switch\n");
7331201209Srpaulo		/* Enable interrupts to get RF toggle notifications. */
7332201209Srpaulo		IWN_WRITE(sc, IWN_INT, 0xffffffff);
7333201209Srpaulo		IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask);
7334201209Srpaulo		return;
7335178676Ssam	}
7336178676Ssam
7337198429Srpaulo	/* Read firmware images from the filesystem. */
7338220726Sbschmidt	if ((error = iwn_read_firmware(sc)) != 0) {
7339178676Ssam		device_printf(sc->sc_dev,
7340220726Sbschmidt		    "%s: could not read firmware, error %d\n", __func__,
7341220726Sbschmidt		    error);
7342198429Srpaulo		goto fail;
7343178676Ssam	}
7344178676Ssam
7345198429Srpaulo	/* Initialize hardware and upload firmware. */
7346198429Srpaulo	error = iwn_hw_init(sc);
7347201209Srpaulo	firmware_put(sc->fw_fp, FIRMWARE_UNLOAD);
7348201209Srpaulo	sc->fw_fp = NULL;
7349198429Srpaulo	if (error != 0) {
7350198429Srpaulo		device_printf(sc->sc_dev,
7351220726Sbschmidt		    "%s: could not initialize hardware, error %d\n", __func__,
7352220726Sbschmidt		    error);
7353198429Srpaulo		goto fail;
7354198429Srpaulo	}
7355178676Ssam
7356198429Srpaulo	/* Configure adapter now that it is ready. */
7357220726Sbschmidt	if ((error = iwn_config(sc)) != 0) {
7358178676Ssam		device_printf(sc->sc_dev,
7359220726Sbschmidt		    "%s: could not configure device, error %d\n", __func__,
7360220726Sbschmidt		    error);
7361198429Srpaulo		goto fail;
7362178676Ssam	}
7363178676Ssam
7364178676Ssam	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
7365178676Ssam	ifp->if_drv_flags |= IFF_DRV_RUNNING;
7366198429Srpaulo
7367220667Sbschmidt	callout_reset(&sc->watchdog_to, hz, iwn_watchdog, sc);
7368253705Sadrian
7369253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
7370253705Sadrian
7371198429Srpaulo	return;
7372198429Srpaulo
7373220726Sbschmidtfail:	iwn_stop_locked(sc);
7374253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end in error\n",__func__);
7375178676Ssam}
7376178676Ssam
7377206477Sbschmidtstatic void
7378178676Ssamiwn_init(void *arg)
7379178676Ssam{
7380178676Ssam	struct iwn_softc *sc = arg;
7381178676Ssam	struct ifnet *ifp = sc->sc_ifp;
7382178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
7383178676Ssam
7384178676Ssam	IWN_LOCK(sc);
7385178676Ssam	iwn_init_locked(sc);
7386178676Ssam	IWN_UNLOCK(sc);
7387178676Ssam
7388178676Ssam	if (ifp->if_drv_flags & IFF_DRV_RUNNING)
7389178676Ssam		ieee80211_start_all(ic);
7390178676Ssam}
7391178676Ssam
7392206477Sbschmidtstatic void
7393178676Ssamiwn_stop_locked(struct iwn_softc *sc)
7394178676Ssam{
7395178676Ssam	struct ifnet *ifp = sc->sc_ifp;
7396178676Ssam
7397178676Ssam	IWN_LOCK_ASSERT(sc);
7398178676Ssam
7399178676Ssam	sc->sc_tx_timer = 0;
7400220667Sbschmidt	callout_stop(&sc->watchdog_to);
7401220667Sbschmidt	callout_stop(&sc->calib_to);
7402178676Ssam	ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
7403178676Ssam
7404198429Srpaulo	/* Power OFF hardware. */
7405198429Srpaulo	iwn_hw_stop(sc);
7406198429Srpaulo}
7407178676Ssam
7408206477Sbschmidtstatic void
7409178676Ssamiwn_stop(struct iwn_softc *sc)
7410178676Ssam{
7411178676Ssam	IWN_LOCK(sc);
7412178676Ssam	iwn_stop_locked(sc);
7413178676Ssam	IWN_UNLOCK(sc);
7414178676Ssam}
7415178676Ssam
7416178676Ssam/*
7417178676Ssam * Callback from net80211 to start a scan.
7418178676Ssam */
7419178676Ssamstatic void
7420178676Ssamiwn_scan_start(struct ieee80211com *ic)
7421178676Ssam{
7422178676Ssam	struct ifnet *ifp = ic->ic_ifp;
7423178676Ssam	struct iwn_softc *sc = ifp->if_softc;
7424178676Ssam
7425191746Sthompsa	IWN_LOCK(sc);
7426191746Sthompsa	/* make the link LED blink while we're scanning */
7427191746Sthompsa	iwn_set_led(sc, IWN_LED_LINK, 20, 2);
7428191746Sthompsa	IWN_UNLOCK(sc);
7429178676Ssam}
7430178676Ssam
7431178676Ssam/*
7432178676Ssam * Callback from net80211 to terminate a scan.
7433178676Ssam */
7434178676Ssamstatic void
7435178676Ssamiwn_scan_end(struct ieee80211com *ic)
7436178676Ssam{
7437201209Srpaulo	struct ifnet *ifp = ic->ic_ifp;
7438201209Srpaulo	struct iwn_softc *sc = ifp->if_softc;
7439201209Srpaulo	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
7440201209Srpaulo
7441201209Srpaulo	IWN_LOCK(sc);
7442201209Srpaulo	if (vap->iv_state == IEEE80211_S_RUN) {
7443201209Srpaulo		/* Set link LED to ON status if we are associated */
7444201209Srpaulo		iwn_set_led(sc, IWN_LED_LINK, 0, 1);
7445201209Srpaulo	}
7446201209Srpaulo	IWN_UNLOCK(sc);
7447178676Ssam}
7448178676Ssam
7449178676Ssam/*
7450178676Ssam * Callback from net80211 to force a channel change.
7451178676Ssam */
7452178676Ssamstatic void
7453178676Ssamiwn_set_channel(struct ieee80211com *ic)
7454178676Ssam{
7455198429Srpaulo	const struct ieee80211_channel *c = ic->ic_curchan;
7456178676Ssam	struct ifnet *ifp = ic->ic_ifp;
7457178676Ssam	struct iwn_softc *sc = ifp->if_softc;
7458225686Sadrian	int error;
7459178676Ssam
7460253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
7461253705Sadrian
7462191746Sthompsa	IWN_LOCK(sc);
7463201209Srpaulo	sc->sc_rxtap.wr_chan_freq = htole16(c->ic_freq);
7464201209Srpaulo	sc->sc_rxtap.wr_chan_flags = htole16(c->ic_flags);
7465201209Srpaulo	sc->sc_txtap.wt_chan_freq = htole16(c->ic_freq);
7466201209Srpaulo	sc->sc_txtap.wt_chan_flags = htole16(c->ic_flags);
7467225686Sadrian
7468225686Sadrian	/*
7469225686Sadrian	 * Only need to set the channel in Monitor mode. AP scanning and auth
7470225686Sadrian	 * are already taken care of by their respective firmware commands.
7471225686Sadrian	 */
7472225686Sadrian	if (ic->ic_opmode == IEEE80211_M_MONITOR) {
7473225686Sadrian		error = iwn_config(sc);
7474225686Sadrian		if (error != 0)
7475225686Sadrian		device_printf(sc->sc_dev,
7476225686Sadrian		    "%s: error %d settting channel\n", __func__, error);
7477225686Sadrian	}
7478191746Sthompsa	IWN_UNLOCK(sc);
7479178676Ssam}
7480178676Ssam
7481178676Ssam/*
7482178676Ssam * Callback from net80211 to start scanning of the current channel.
7483178676Ssam */
7484178676Ssamstatic void
7485178676Ssamiwn_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell)
7486178676Ssam{
7487178676Ssam	struct ieee80211vap *vap = ss->ss_vap;
7488178676Ssam	struct iwn_softc *sc = vap->iv_ic->ic_ifp->if_softc;
7489191746Sthompsa	int error;
7490178676Ssam
7491191746Sthompsa	IWN_LOCK(sc);
7492191746Sthompsa	error = iwn_scan(sc);
7493191746Sthompsa	IWN_UNLOCK(sc);
7494191746Sthompsa	if (error != 0)
7495191746Sthompsa		ieee80211_cancel_scan(vap);
7496178676Ssam}
7497178676Ssam
7498178676Ssam/*
7499178676Ssam * Callback from net80211 to handle the minimum dwell time being met.
7500178676Ssam * The intent is to terminate the scan but we just let the firmware
7501178676Ssam * notify us when it's finished as we have no safe way to abort it.
7502178676Ssam */
7503178676Ssamstatic void
7504178676Ssamiwn_scan_mindwell(struct ieee80211_scan_state *ss)
7505178676Ssam{
7506178676Ssam	/* NB: don't try to abort scan; wait for firmware to finish */
7507178676Ssam}
7508178676Ssam
7509178676Ssamstatic void
7510198429Srpauloiwn_hw_reset(void *arg0, int pending)
7511178676Ssam{
7512178676Ssam	struct iwn_softc *sc = arg0;
7513178676Ssam	struct ifnet *ifp = sc->sc_ifp;
7514178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
7515178676Ssam
7516253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
7517253705Sadrian
7518201209Srpaulo	iwn_stop(sc);
7519191746Sthompsa	iwn_init(sc);
7520191746Sthompsa	ieee80211_notify_radio(ic, 1);
7521191746Sthompsa}
7522253866Sadrian#ifdef	IWN_DEBUG
7523253866Sadrian#define	IWN_DESC(x) case x:	return #x
7524253866Sadrian#define	COUNTOF(array) (sizeof(array) / sizeof(array[0]))
7525253866Sadrian
7526253866Sadrian/*
7527253937Shiren * Translate CSR code to string
7528253866Sadrian */
7529253866Sadrianstatic char *iwn_get_csr_string(int csr)
7530253866Sadrian{
7531253866Sadrian	switch (csr) {
7532253866Sadrian		IWN_DESC(IWN_HW_IF_CONFIG);
7533253866Sadrian		IWN_DESC(IWN_INT_COALESCING);
7534253866Sadrian		IWN_DESC(IWN_INT);
7535253866Sadrian		IWN_DESC(IWN_INT_MASK);
7536253866Sadrian		IWN_DESC(IWN_FH_INT);
7537253866Sadrian		IWN_DESC(IWN_GPIO_IN);
7538253866Sadrian		IWN_DESC(IWN_RESET);
7539253866Sadrian		IWN_DESC(IWN_GP_CNTRL);
7540253866Sadrian		IWN_DESC(IWN_HW_REV);
7541253866Sadrian		IWN_DESC(IWN_EEPROM);
7542253866Sadrian		IWN_DESC(IWN_EEPROM_GP);
7543253866Sadrian		IWN_DESC(IWN_OTP_GP);
7544253866Sadrian		IWN_DESC(IWN_GIO);
7545253866Sadrian		IWN_DESC(IWN_GP_UCODE);
7546253866Sadrian		IWN_DESC(IWN_GP_DRIVER);
7547253866Sadrian		IWN_DESC(IWN_UCODE_GP1);
7548253866Sadrian		IWN_DESC(IWN_UCODE_GP2);
7549253866Sadrian		IWN_DESC(IWN_LED);
7550253866Sadrian		IWN_DESC(IWN_DRAM_INT_TBL);
7551253866Sadrian		IWN_DESC(IWN_GIO_CHICKEN);
7552253866Sadrian		IWN_DESC(IWN_ANA_PLL);
7553253866Sadrian		IWN_DESC(IWN_HW_REV_WA);
7554253866Sadrian		IWN_DESC(IWN_DBG_HPET_MEM);
7555253866Sadrian	default:
7556253866Sadrian		return "UNKNOWN CSR";
7557253866Sadrian	}
7558253866Sadrian}
7559253866Sadrian
7560253866Sadrian/*
7561253866Sadrian * This function print firmware register
7562253866Sadrian */
7563253866Sadrianstatic void
7564253866Sadrianiwn_debug_register(struct iwn_softc *sc)
7565253866Sadrian{
7566253866Sadrian	int i;
7567253866Sadrian	static const uint32_t csr_tbl[] = {
7568253866Sadrian		IWN_HW_IF_CONFIG,
7569253866Sadrian		IWN_INT_COALESCING,
7570253866Sadrian		IWN_INT,
7571253866Sadrian		IWN_INT_MASK,
7572253866Sadrian		IWN_FH_INT,
7573253866Sadrian		IWN_GPIO_IN,
7574253866Sadrian		IWN_RESET,
7575253866Sadrian		IWN_GP_CNTRL,
7576253866Sadrian		IWN_HW_REV,
7577253866Sadrian		IWN_EEPROM,
7578253866Sadrian		IWN_EEPROM_GP,
7579253866Sadrian		IWN_OTP_GP,
7580253866Sadrian		IWN_GIO,
7581253866Sadrian		IWN_GP_UCODE,
7582253866Sadrian		IWN_GP_DRIVER,
7583253866Sadrian		IWN_UCODE_GP1,
7584253866Sadrian		IWN_UCODE_GP2,
7585253866Sadrian		IWN_LED,
7586253866Sadrian		IWN_DRAM_INT_TBL,
7587253866Sadrian		IWN_GIO_CHICKEN,
7588253866Sadrian		IWN_ANA_PLL,
7589253866Sadrian		IWN_HW_REV_WA,
7590253866Sadrian		IWN_DBG_HPET_MEM,
7591253866Sadrian	};
7592253866Sadrian	DPRINTF(sc, IWN_DEBUG_REGISTER,
7593253866Sadrian	    "CSR values: (2nd byte of IWN_INT_COALESCING is IWN_INT_PERIODIC)%s",
7594253866Sadrian	    "\n");
7595253866Sadrian	for (i = 0; i <  COUNTOF(csr_tbl); i++){
7596253866Sadrian		DPRINTF(sc, IWN_DEBUG_REGISTER,"  %10s: 0x%08x ",
7597253866Sadrian			iwn_get_csr_string(csr_tbl[i]), IWN_READ(sc, csr_tbl[i]));
7598253866Sadrian		if ((i+1) % 3 == 0)
7599253866Sadrian			DPRINTF(sc, IWN_DEBUG_REGISTER,"%s","\n");
7600253866Sadrian	}
7601253866Sadrian	DPRINTF(sc, IWN_DEBUG_REGISTER,"%s","\n");
7602253866Sadrian}
7603253866Sadrian#endif
7604