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$");
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),
416220723Sbschmidt	{ 0, 0 }
417178676Ssam};
418178676Ssam
419220723Sbschmidtstatic driver_t iwn_driver = {
420220723Sbschmidt	"iwn",
421220723Sbschmidt	iwn_methods,
422220726Sbschmidt	sizeof(struct iwn_softc)
423178676Ssam};
424220723Sbschmidtstatic devclass_t iwn_devclass;
425178676Ssam
426220723SbschmidtDRIVER_MODULE(iwn, pci, iwn_driver, iwn_devclass, 0, 0);
427220726Sbschmidt
428222543SbschmidtMODULE_VERSION(iwn, 1);
429222543Sbschmidt
430220723SbschmidtMODULE_DEPEND(iwn, firmware, 1, 1, 1);
431220723SbschmidtMODULE_DEPEND(iwn, pci, 1, 1, 1);
432220723SbschmidtMODULE_DEPEND(iwn, wlan, 1, 1, 1);
433220723Sbschmidt
434178676Ssamstatic int
435178676Ssamiwn_probe(device_t dev)
436178676Ssam{
437198429Srpaulo	const struct iwn_ident *ident;
438178676Ssam
439198429Srpaulo	for (ident = iwn_ident_table; ident->name != NULL; ident++) {
440198429Srpaulo		if (pci_get_vendor(dev) == ident->vendor &&
441198429Srpaulo		    pci_get_device(dev) == ident->device) {
442198429Srpaulo			device_set_desc(dev, ident->name);
443198429Srpaulo			return 0;
444198429Srpaulo		}
445198429Srpaulo	}
446198429Srpaulo	return ENXIO;
447178676Ssam}
448178676Ssam
449178676Ssamstatic int
450178676Ssamiwn_attach(device_t dev)
451178676Ssam{
452178676Ssam	struct iwn_softc *sc = (struct iwn_softc *)device_get_softc(dev);
453178676Ssam	struct ieee80211com *ic;
454178676Ssam	struct ifnet *ifp;
455220721Sbschmidt	uint32_t reg;
456184233Smav	int i, error, result;
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	/* Hardware bug workaround. */
486254263Sscottl	reg = pci_read_config(dev, PCIR_COMMAND, 2);
487220721Sbschmidt	if (reg & PCIM_CMD_INTxDIS) {
488198429Srpaulo		DPRINTF(sc, IWN_DEBUG_RESET, "%s: PCIe INTx Disable set\n",
489198429Srpaulo		    __func__);
490220721Sbschmidt		reg &= ~PCIM_CMD_INTxDIS;
491254263Sscottl		pci_write_config(dev, PCIR_COMMAND, reg, 2);
492198429Srpaulo	}
493198429Srpaulo
494198429Srpaulo	/* Enable bus-mastering. */
495178676Ssam	pci_enable_busmaster(dev);
496178676Ssam
497198429Srpaulo	sc->mem_rid = PCIR_BAR(0);
498178676Ssam	sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid,
499198429Srpaulo	    RF_ACTIVE);
500220726Sbschmidt	if (sc->mem == NULL) {
501220724Sbschmidt		device_printf(dev, "can't map mem space\n");
502198429Srpaulo		error = ENOMEM;
503178676Ssam		return error;
504178676Ssam	}
505178676Ssam	sc->sc_st = rman_get_bustag(sc->mem);
506178676Ssam	sc->sc_sh = rman_get_bushandle(sc->mem);
507220726Sbschmidt
508178676Ssam	sc->irq_rid = 0;
509184233Smav	if ((result = pci_msi_count(dev)) == 1 &&
510184233Smav	    pci_alloc_msi(dev, &result) == 0)
511184233Smav		sc->irq_rid = 1;
512220725Sbschmidt	/* Install interrupt handler. */
513178676Ssam	sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irq_rid,
514198429Srpaulo	    RF_ACTIVE | RF_SHAREABLE);
515178676Ssam	if (sc->irq == NULL) {
516220724Sbschmidt		device_printf(dev, "can't map interrupt\n");
517178676Ssam		error = ENOMEM;
518198429Srpaulo		goto fail;
519178676Ssam	}
520178676Ssam
521178676Ssam	IWN_LOCK_INIT(sc);
522178676Ssam
523220728Sbschmidt	/* Read hardware revision and attach. */
524253897Sadrian	sc->hw_type = (IWN_READ(sc, IWN_HW_REV) >> IWN_HW_REV_TYPE_SHIFT)
525253897Sadrian	    & IWN_HW_REV_TYPE_MASK;
526254204Sadrian	sc->subdevice_id = pci_get_subdevice(dev);
527220728Sbschmidt	if (sc->hw_type == IWN_HW_REV_TYPE_4965)
528220728Sbschmidt		error = iwn4965_attach(sc, pci_get_device(dev));
529220728Sbschmidt	else
530220728Sbschmidt		error = iwn5000_attach(sc, pci_get_device(dev));
531220728Sbschmidt	if (error != 0) {
532220728Sbschmidt		device_printf(dev, "could not attach device, error %d\n",
533220728Sbschmidt		    error);
534198429Srpaulo		goto fail;
535198429Srpaulo	}
536198429Srpaulo
537220726Sbschmidt	if ((error = iwn_hw_prepare(sc)) != 0) {
538198429Srpaulo		device_printf(dev, "hardware not ready, error %d\n", error);
539178676Ssam		goto fail;
540178676Ssam	}
541178676Ssam
542198429Srpaulo	/* Allocate DMA memory for firmware transfers. */
543220726Sbschmidt	if ((error = iwn_alloc_fwmem(sc)) != 0) {
544178676Ssam		device_printf(dev,
545198429Srpaulo		    "could not allocate memory for firmware, error %d\n",
546198429Srpaulo		    error);
547178676Ssam		goto fail;
548178676Ssam	}
549178676Ssam
550198429Srpaulo	/* Allocate "Keep Warm" page. */
551220726Sbschmidt	if ((error = iwn_alloc_kw(sc)) != 0) {
552178676Ssam		device_printf(dev,
553220724Sbschmidt		    "could not allocate keep warm page, error %d\n", error);
554178676Ssam		goto fail;
555178676Ssam	}
556178676Ssam
557201209Srpaulo	/* Allocate ICT table for 5000 Series. */
558201209Srpaulo	if (sc->hw_type != IWN_HW_REV_TYPE_4965 &&
559201209Srpaulo	    (error = iwn_alloc_ict(sc)) != 0) {
560220724Sbschmidt		device_printf(dev, "could not allocate ICT table, error %d\n",
561220724Sbschmidt		    error);
562201209Srpaulo		goto fail;
563201209Srpaulo	}
564201209Srpaulo
565198429Srpaulo	/* Allocate TX scheduler "rings". */
566220726Sbschmidt	if ((error = iwn_alloc_sched(sc)) != 0) {
567178676Ssam		device_printf(dev,
568220726Sbschmidt		    "could not allocate TX scheduler rings, error %d\n", error);
569178676Ssam		goto fail;
570178676Ssam	}
571178676Ssam
572220725Sbschmidt	/* Allocate TX rings (16 on 4965AGN, 20 on >=5000). */
573220728Sbschmidt	for (i = 0; i < sc->ntxqs; i++) {
574220726Sbschmidt		if ((error = iwn_alloc_tx_ring(sc, &sc->txq[i], i)) != 0) {
575178676Ssam			device_printf(dev,
576220724Sbschmidt			    "could not allocate TX ring %d, error %d\n", i,
577220724Sbschmidt			    error);
578178676Ssam			goto fail;
579178676Ssam		}
580178676Ssam	}
581178676Ssam
582198429Srpaulo	/* Allocate RX ring. */
583220726Sbschmidt	if ((error = iwn_alloc_rx_ring(sc, &sc->rxq)) != 0) {
584220724Sbschmidt		device_printf(dev, "could not allocate RX ring, error %d\n",
585220724Sbschmidt		    error);
586178676Ssam		goto fail;
587178676Ssam	}
588178676Ssam
589198429Srpaulo	/* Clear pending interrupts. */
590198429Srpaulo	IWN_WRITE(sc, IWN_INT, 0xffffffff);
591198429Srpaulo
592178676Ssam	ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211);
593178676Ssam	if (ifp == NULL) {
594178676Ssam		device_printf(dev, "can not allocate ifnet structure\n");
595178676Ssam		goto fail;
596178676Ssam	}
597220726Sbschmidt
598178676Ssam	ic = ifp->if_l2com;
599198429Srpaulo	ic->ic_ifp = ifp;
600178676Ssam	ic->ic_phytype = IEEE80211_T_OFDM;	/* not only, but not used */
601178676Ssam	ic->ic_opmode = IEEE80211_M_STA;	/* default to BSS mode */
602178676Ssam
603198429Srpaulo	/* Set device capabilities. */
604178676Ssam	ic->ic_caps =
605178957Ssam		  IEEE80211_C_STA		/* station mode supported */
606178957Ssam		| IEEE80211_C_MONITOR		/* monitor mode supported */
607222679Sbschmidt		| IEEE80211_C_BGSCAN		/* background scanning */
608178676Ssam		| IEEE80211_C_TXPMGT		/* tx power management */
609178676Ssam		| IEEE80211_C_SHSLOT		/* short slot time supported */
610178676Ssam		| IEEE80211_C_WPA
611178676Ssam		| IEEE80211_C_SHPREAMBLE	/* short preamble supported */
612178676Ssam#if 0
613178676Ssam		| IEEE80211_C_IBSS		/* ibss/adhoc mode */
614178676Ssam#endif
615178676Ssam		| IEEE80211_C_WME		/* WME */
616252717Sadrian		| IEEE80211_C_PMGT		/* Station-side power mgmt */
617178676Ssam		;
618221640Sbschmidt
619221642Sbschmidt	/* Read MAC address, channels, etc from EEPROM. */
620221642Sbschmidt	if ((error = iwn_read_eeprom(sc, macaddr)) != 0) {
621221642Sbschmidt		device_printf(dev, "could not read EEPROM, error %d\n",
622221642Sbschmidt		    error);
623221642Sbschmidt		goto fail;
624221642Sbschmidt	}
625221642Sbschmidt
626221642Sbschmidt	/* Count the number of available chains. */
627221642Sbschmidt	sc->ntxchains =
628221642Sbschmidt	    ((sc->txchainmask >> 2) & 1) +
629221642Sbschmidt	    ((sc->txchainmask >> 1) & 1) +
630221642Sbschmidt	    ((sc->txchainmask >> 0) & 1);
631221642Sbschmidt	sc->nrxchains =
632221642Sbschmidt	    ((sc->rxchainmask >> 2) & 1) +
633221642Sbschmidt	    ((sc->rxchainmask >> 1) & 1) +
634221642Sbschmidt	    ((sc->rxchainmask >> 0) & 1);
635221642Sbschmidt	if (bootverbose) {
636221642Sbschmidt		device_printf(dev, "MIMO %dT%dR, %.4s, address %6D\n",
637221642Sbschmidt		    sc->ntxchains, sc->nrxchains, sc->eeprom_domain,
638221642Sbschmidt		    macaddr, ":");
639221642Sbschmidt	}
640221642Sbschmidt
641221657Sbschmidt	if (sc->sc_flags & IWN_FLAG_HAS_11N) {
642221657Sbschmidt		ic->ic_rxstream = sc->nrxchains;
643221657Sbschmidt		ic->ic_txstream = sc->ntxchains;
644254085Sadrian
645254085Sadrian		/*
646254085Sadrian		 * The NICs we currently support cap out at 2x2 support
647254085Sadrian		 * separate from the chains being used.
648254085Sadrian		 *
649254085Sadrian		 * This is a total hack to work around that until some
650254085Sadrian		 * per-device method is implemented to return the
651254085Sadrian		 * actual stream support.
652254085Sadrian		 */
653254085Sadrian		if (ic->ic_rxstream > 2)
654254085Sadrian			ic->ic_rxstream = 2;
655254085Sadrian		if (ic->ic_txstream > 2)
656254085Sadrian			ic->ic_txstream = 2;
657254085Sadrian
658221657Sbschmidt		ic->ic_htcaps =
659221657Sbschmidt			  IEEE80211_HTCAP_SMPS_OFF	/* SMPS mode disabled */
660221657Sbschmidt			| IEEE80211_HTCAP_SHORTGI20	/* short GI in 20MHz */
661221657Sbschmidt			| IEEE80211_HTCAP_CHWIDTH40	/* 40MHz channel width*/
662221657Sbschmidt			| IEEE80211_HTCAP_SHORTGI40	/* short GI in 40MHz */
663222687Sbschmidt#ifdef notyet
664221657Sbschmidt			| IEEE80211_HTCAP_GREENFIELD
665201209Srpaulo#if IWN_RBUF_SIZE == 8192
666221657Sbschmidt			| IEEE80211_HTCAP_MAXAMSDU_7935	/* max A-MSDU length */
667221657Sbschmidt#else
668221657Sbschmidt			| IEEE80211_HTCAP_MAXAMSDU_3839	/* max A-MSDU length */
669178678Ssam#endif
670201209Srpaulo#endif
671221657Sbschmidt			/* s/w capabilities */
672221657Sbschmidt			| IEEE80211_HTC_HT		/* HT operation */
673221657Sbschmidt			| IEEE80211_HTC_AMPDU		/* tx A-MPDU */
674221657Sbschmidt#ifdef notyet
675221657Sbschmidt			| IEEE80211_HTC_AMSDU		/* tx A-MSDU */
676201209Srpaulo#endif
677221657Sbschmidt			;
678221657Sbschmidt	}
679201209Srpaulo
680178676Ssam	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
681178676Ssam	ifp->if_softc = sc;
682178676Ssam	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
683178676Ssam	ifp->if_init = iwn_init;
684178676Ssam	ifp->if_ioctl = iwn_ioctl;
685178676Ssam	ifp->if_start = iwn_start;
686207554Ssobomax	IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen);
687207554Ssobomax	ifp->if_snd.ifq_drv_maxlen = ifqmaxlen;
688178676Ssam	IFQ_SET_READY(&ifp->if_snd);
689178676Ssam
690190526Ssam	ieee80211_ifattach(ic, macaddr);
691178676Ssam	ic->ic_vap_create = iwn_vap_create;
692178676Ssam	ic->ic_vap_delete = iwn_vap_delete;
693178676Ssam	ic->ic_raw_xmit = iwn_raw_xmit;
694178676Ssam	ic->ic_node_alloc = iwn_node_alloc;
695221650Sbschmidt	sc->sc_ampdu_rx_start = ic->ic_ampdu_rx_start;
696220723Sbschmidt	ic->ic_ampdu_rx_start = iwn_ampdu_rx_start;
697221650Sbschmidt	sc->sc_ampdu_rx_stop = ic->ic_ampdu_rx_stop;
698220723Sbschmidt	ic->ic_ampdu_rx_stop = iwn_ampdu_rx_stop;
699221651Sbschmidt	sc->sc_addba_request = ic->ic_addba_request;
700221651Sbschmidt	ic->ic_addba_request = iwn_addba_request;
701221651Sbschmidt	sc->sc_addba_response = ic->ic_addba_response;
702221651Sbschmidt	ic->ic_addba_response = iwn_addba_response;
703221651Sbschmidt	sc->sc_addba_stop = ic->ic_addba_stop;
704221651Sbschmidt	ic->ic_addba_stop = iwn_ampdu_tx_stop;
705220715Sbschmidt	ic->ic_newassoc = iwn_newassoc;
706220721Sbschmidt	ic->ic_wme.wme_update = iwn_updateedca;
707201209Srpaulo	ic->ic_update_mcast = iwn_update_mcast;
708198429Srpaulo	ic->ic_scan_start = iwn_scan_start;
709198429Srpaulo	ic->ic_scan_end = iwn_scan_end;
710198429Srpaulo	ic->ic_set_channel = iwn_set_channel;
711198429Srpaulo	ic->ic_scan_curchan = iwn_scan_curchan;
712198429Srpaulo	ic->ic_scan_mindwell = iwn_scan_mindwell;
713201209Srpaulo	ic->ic_setregdomain = iwn_setregdomain;
714178676Ssam
715198429Srpaulo	iwn_radiotap_attach(sc);
716220667Sbschmidt
717220667Sbschmidt	callout_init_mtx(&sc->calib_to, &sc->sc_mtx, 0);
718220667Sbschmidt	callout_init_mtx(&sc->watchdog_to, &sc->sc_mtx, 0);
719220726Sbschmidt	TASK_INIT(&sc->sc_reinit_task, 0, iwn_hw_reset, sc);
720220726Sbschmidt	TASK_INIT(&sc->sc_radioon_task, 0, iwn_radio_on, sc);
721220726Sbschmidt	TASK_INIT(&sc->sc_radiooff_task, 0, iwn_radio_off, sc);
722220667Sbschmidt
723178676Ssam	iwn_sysctlattach(sc);
724178676Ssam
725198429Srpaulo	/*
726198429Srpaulo	 * Hook our interrupt after all initialization is complete.
727198429Srpaulo	 */
728198429Srpaulo	error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET | INTR_MPSAFE,
729178676Ssam	    NULL, iwn_intr, sc, &sc->sc_ih);
730198429Srpaulo	if (error != 0) {
731220724Sbschmidt		device_printf(dev, "can't establish interrupt, error %d\n",
732198429Srpaulo		    error);
733198429Srpaulo		goto fail;
734198429Srpaulo	}
735178676Ssam
736220724Sbschmidt	if (bootverbose)
737220724Sbschmidt		ieee80211_announce(ic);
738253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
739178676Ssam	return 0;
740178676Ssamfail:
741220635Sbschmidt	iwn_detach(dev);
742253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end in error\n",__func__);
743178676Ssam	return error;
744178676Ssam}
745178676Ssam
746220728Sbschmidtstatic int
747220728Sbschmidtiwn4965_attach(struct iwn_softc *sc, uint16_t pid)
748178676Ssam{
749220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
750198429Srpaulo
751253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
752220728Sbschmidt	ops->load_firmware = iwn4965_load_firmware;
753220728Sbschmidt	ops->read_eeprom = iwn4965_read_eeprom;
754220728Sbschmidt	ops->post_alive = iwn4965_post_alive;
755220728Sbschmidt	ops->nic_config = iwn4965_nic_config;
756220728Sbschmidt	ops->update_sched = iwn4965_update_sched;
757220728Sbschmidt	ops->get_temperature = iwn4965_get_temperature;
758220728Sbschmidt	ops->get_rssi = iwn4965_get_rssi;
759220728Sbschmidt	ops->set_txpower = iwn4965_set_txpower;
760220728Sbschmidt	ops->init_gains = iwn4965_init_gains;
761220728Sbschmidt	ops->set_gains = iwn4965_set_gains;
762220728Sbschmidt	ops->add_node = iwn4965_add_node;
763220728Sbschmidt	ops->tx_done = iwn4965_tx_done;
764220728Sbschmidt	ops->ampdu_tx_start = iwn4965_ampdu_tx_start;
765220728Sbschmidt	ops->ampdu_tx_stop = iwn4965_ampdu_tx_stop;
766220728Sbschmidt	sc->ntxqs = IWN4965_NTXQUEUES;
767221651Sbschmidt	sc->firstaggqueue = IWN4965_FIRSTAGGQUEUE;
768220728Sbschmidt	sc->ndmachnls = IWN4965_NDMACHNLS;
769220728Sbschmidt	sc->broadcast_id = IWN4965_ID_BROADCAST;
770220728Sbschmidt	sc->rxonsz = IWN4965_RXONSZ;
771220728Sbschmidt	sc->schedsz = IWN4965_SCHEDSZ;
772220728Sbschmidt	sc->fw_text_maxsz = IWN4965_FW_TEXT_MAXSZ;
773220728Sbschmidt	sc->fw_data_maxsz = IWN4965_FW_DATA_MAXSZ;
774220728Sbschmidt	sc->fwsz = IWN4965_FWSZ;
775220728Sbschmidt	sc->sched_txfact_addr = IWN4965_SCHED_TXFACT;
776220728Sbschmidt	sc->limits = &iwn4965_sensitivity_limits;
777220728Sbschmidt	sc->fwname = "iwn4965fw";
778220728Sbschmidt	/* Override chains masks, ROM is known to be broken. */
779220728Sbschmidt	sc->txchainmask = IWN_ANT_AB;
780220728Sbschmidt	sc->rxchainmask = IWN_ANT_ABC;
781220728Sbschmidt
782253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "%s: end\n",__func__);
783253705Sadrian
784220728Sbschmidt	return 0;
785220728Sbschmidt}
786220728Sbschmidt
787220728Sbschmidtstatic int
788220728Sbschmidtiwn5000_attach(struct iwn_softc *sc, uint16_t pid)
789220728Sbschmidt{
790220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
791220728Sbschmidt
792253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
793253705Sadrian
794220728Sbschmidt	ops->load_firmware = iwn5000_load_firmware;
795220728Sbschmidt	ops->read_eeprom = iwn5000_read_eeprom;
796220728Sbschmidt	ops->post_alive = iwn5000_post_alive;
797220728Sbschmidt	ops->nic_config = iwn5000_nic_config;
798220728Sbschmidt	ops->update_sched = iwn5000_update_sched;
799220728Sbschmidt	ops->get_temperature = iwn5000_get_temperature;
800220728Sbschmidt	ops->get_rssi = iwn5000_get_rssi;
801220728Sbschmidt	ops->set_txpower = iwn5000_set_txpower;
802220728Sbschmidt	ops->init_gains = iwn5000_init_gains;
803220728Sbschmidt	ops->set_gains = iwn5000_set_gains;
804220728Sbschmidt	ops->add_node = iwn5000_add_node;
805220728Sbschmidt	ops->tx_done = iwn5000_tx_done;
806220728Sbschmidt	ops->ampdu_tx_start = iwn5000_ampdu_tx_start;
807220728Sbschmidt	ops->ampdu_tx_stop = iwn5000_ampdu_tx_stop;
808220728Sbschmidt	sc->ntxqs = IWN5000_NTXQUEUES;
809221651Sbschmidt	sc->firstaggqueue = IWN5000_FIRSTAGGQUEUE;
810220728Sbschmidt	sc->ndmachnls = IWN5000_NDMACHNLS;
811220728Sbschmidt	sc->broadcast_id = IWN5000_ID_BROADCAST;
812220728Sbschmidt	sc->rxonsz = IWN5000_RXONSZ;
813220728Sbschmidt	sc->schedsz = IWN5000_SCHEDSZ;
814220728Sbschmidt	sc->fw_text_maxsz = IWN5000_FW_TEXT_MAXSZ;
815220728Sbschmidt	sc->fw_data_maxsz = IWN5000_FW_DATA_MAXSZ;
816220728Sbschmidt	sc->fwsz = IWN5000_FWSZ;
817220728Sbschmidt	sc->sched_txfact_addr = IWN5000_SCHED_TXFACT;
818220866Sbschmidt	sc->reset_noise_gain = IWN5000_PHY_CALIB_RESET_NOISE_GAIN;
819220866Sbschmidt	sc->noise_gain = IWN5000_PHY_CALIB_NOISE_GAIN;
820220728Sbschmidt
821198429Srpaulo	switch (sc->hw_type) {
822198429Srpaulo	case IWN_HW_REV_TYPE_5100:
823201209Srpaulo		sc->limits = &iwn5000_sensitivity_limits;
824198439Srpaulo		sc->fwname = "iwn5000fw";
825220727Sbschmidt		/* Override chains masks, ROM is known to be broken. */
826201209Srpaulo		sc->txchainmask = IWN_ANT_B;
827201209Srpaulo		sc->rxchainmask = IWN_ANT_AB;
828198429Srpaulo		break;
829198429Srpaulo	case IWN_HW_REV_TYPE_5150:
830201209Srpaulo		sc->limits = &iwn5150_sensitivity_limits;
831198439Srpaulo		sc->fwname = "iwn5150fw";
832198429Srpaulo		break;
833198429Srpaulo	case IWN_HW_REV_TYPE_5300:
834198429Srpaulo	case IWN_HW_REV_TYPE_5350:
835201209Srpaulo		sc->limits = &iwn5000_sensitivity_limits;
836198439Srpaulo		sc->fwname = "iwn5000fw";
837198429Srpaulo		break;
838198429Srpaulo	case IWN_HW_REV_TYPE_1000:
839206444Sbschmidt		sc->limits = &iwn1000_sensitivity_limits;
840198439Srpaulo		sc->fwname = "iwn1000fw";
841198429Srpaulo		break;
842198429Srpaulo	case IWN_HW_REV_TYPE_6000:
843201209Srpaulo		sc->limits = &iwn6000_sensitivity_limits;
844198439Srpaulo		sc->fwname = "iwn6000fw";
845220728Sbschmidt		if (pid == 0x422c || pid == 0x4239) {
846201209Srpaulo			sc->sc_flags |= IWN_FLAG_INTERNAL_PA;
847220727Sbschmidt			/* Override chains masks, ROM is known to be broken. */
848201209Srpaulo			sc->txchainmask = IWN_ANT_BC;
849201209Srpaulo			sc->rxchainmask = IWN_ANT_BC;
850201209Srpaulo		}
851198429Srpaulo		break;
852198429Srpaulo	case IWN_HW_REV_TYPE_6050:
853201209Srpaulo		sc->limits = &iwn6000_sensitivity_limits;
854210109Sbschmidt		sc->fwname = "iwn6050fw";
855220867Sbschmidt		/* Override chains masks, ROM is known to be broken. */
856220867Sbschmidt		sc->txchainmask = IWN_ANT_AB;
857220867Sbschmidt		sc->rxchainmask = IWN_ANT_AB;
858198429Srpaulo		break;
859210109Sbschmidt	case IWN_HW_REV_TYPE_6005:
860210109Sbschmidt		sc->limits = &iwn6000_sensitivity_limits;
861220894Sbschmidt		if (pid != 0x0082 && pid != 0x0085) {
862220894Sbschmidt			sc->fwname = "iwn6000g2bfw";
863220891Sbschmidt			sc->sc_flags |= IWN_FLAG_ADV_BTCOEX;
864220894Sbschmidt		} else
865220894Sbschmidt			sc->fwname = "iwn6000g2afw";
866210109Sbschmidt		break;
867198429Srpaulo	default:
868198429Srpaulo		device_printf(sc->sc_dev, "adapter type %d not supported\n",
869198429Srpaulo		    sc->hw_type);
870253705Sadrian		DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end in error\n",__func__);
871220728Sbschmidt		return ENOTSUP;
872198429Srpaulo	}
873220728Sbschmidt	return 0;
874178676Ssam}
875178676Ssam
876178676Ssam/*
877198429Srpaulo * Attach the interface to 802.11 radiotap.
878178676Ssam */
879206477Sbschmidtstatic void
880198429Srpauloiwn_radiotap_attach(struct iwn_softc *sc)
881178676Ssam{
882178676Ssam	struct ifnet *ifp = sc->sc_ifp;
883178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
884253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
885198429Srpaulo	ieee80211_radiotap_attach(ic,
886198429Srpaulo	    &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap),
887198429Srpaulo		IWN_TX_RADIOTAP_PRESENT,
888198429Srpaulo	    &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap),
889198429Srpaulo		IWN_RX_RADIOTAP_PRESENT);
890253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s end\n", __func__);
891178676Ssam}
892178676Ssam
893220723Sbschmidtstatic void
894220723Sbschmidtiwn_sysctlattach(struct iwn_softc *sc)
895220723Sbschmidt{
896253612Sadrian#ifdef	IWN_DEBUG
897220723Sbschmidt	struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev);
898220723Sbschmidt	struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev);
899220723Sbschmidt
900220723Sbschmidt	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
901253612Sadrian	    "debug", CTLFLAG_RW, &sc->sc_debug, sc->sc_debug,
902253612Sadrian		"control debugging printfs");
903220723Sbschmidt#endif
904220723Sbschmidt}
905220723Sbschmidt
906178676Ssamstatic struct ieee80211vap *
907228621Sbschmidtiwn_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit,
908228621Sbschmidt    enum ieee80211_opmode opmode, int flags,
909220726Sbschmidt    const uint8_t bssid[IEEE80211_ADDR_LEN],
910220726Sbschmidt    const uint8_t mac[IEEE80211_ADDR_LEN])
911178676Ssam{
912178676Ssam	struct iwn_vap *ivp;
913178676Ssam	struct ieee80211vap *vap;
914254204Sadrian	uint8_t mac1[IEEE80211_ADDR_LEN];
915254204Sadrian	struct iwn_softc *sc = ic->ic_ifp->if_softc;
916178676Ssam
917178676Ssam	if (!TAILQ_EMPTY(&ic->ic_vaps))		/* only one at a time */
918178676Ssam		return NULL;
919254204Sadrian
920254204Sadrian	IEEE80211_ADDR_COPY(mac1, mac);
921254204Sadrian
922178676Ssam	ivp = (struct iwn_vap *) malloc(sizeof(struct iwn_vap),
923178676Ssam	    M_80211_VAP, M_NOWAIT | M_ZERO);
924178676Ssam	if (ivp == NULL)
925178676Ssam		return NULL;
926178676Ssam	vap = &ivp->iv_vap;
927254204Sadrian	ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac1);
928254204Sadrian	ivp->ctx = IWN_RXON_BSS_CTX;
929254204Sadrian	IEEE80211_ADDR_COPY(ivp->macaddr, mac1);
930178676Ssam	vap->iv_bmissthreshold = 10;		/* override default */
931198429Srpaulo	/* Override with driver methods. */
932178676Ssam	ivp->iv_newstate = vap->iv_newstate;
933178676Ssam	vap->iv_newstate = iwn_newstate;
934254204Sadrian	sc->ivap[IWN_RXON_BSS_CTX] = vap;
935178676Ssam
936206358Srpaulo	ieee80211_ratectl_init(vap);
937198429Srpaulo	/* Complete setup. */
938206476Sbschmidt	ieee80211_vap_attach(vap, iwn_media_change, ieee80211_media_status);
939178676Ssam	ic->ic_opmode = opmode;
940178676Ssam	return vap;
941178676Ssam}
942178676Ssam
943178676Ssamstatic void
944178676Ssamiwn_vap_delete(struct ieee80211vap *vap)
945178676Ssam{
946178676Ssam	struct iwn_vap *ivp = IWN_VAP(vap);
947178676Ssam
948206358Srpaulo	ieee80211_ratectl_deinit(vap);
949178676Ssam	ieee80211_vap_detach(vap);
950178676Ssam	free(ivp, M_80211_VAP);
951178676Ssam}
952178676Ssam
953206477Sbschmidtstatic int
954220635Sbschmidtiwn_detach(device_t dev)
955178676Ssam{
956178676Ssam	struct iwn_softc *sc = device_get_softc(dev);
957198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
958198429Srpaulo	struct ieee80211com *ic;
959220721Sbschmidt	int qid;
960178676Ssam
961253866Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
962253866Sadrian
963198429Srpaulo	if (ifp != NULL) {
964198429Srpaulo		ic = ifp->if_l2com;
965198429Srpaulo
966198429Srpaulo		ieee80211_draintask(ic, &sc->sc_reinit_task);
967198429Srpaulo		ieee80211_draintask(ic, &sc->sc_radioon_task);
968198429Srpaulo		ieee80211_draintask(ic, &sc->sc_radiooff_task);
969198429Srpaulo
970198429Srpaulo		iwn_stop(sc);
971220667Sbschmidt		callout_drain(&sc->watchdog_to);
972220667Sbschmidt		callout_drain(&sc->calib_to);
973198429Srpaulo		ieee80211_ifdetach(ic);
974198429Srpaulo	}
975198429Srpaulo
976220725Sbschmidt	/* Uninstall interrupt handler. */
977220723Sbschmidt	if (sc->irq != NULL) {
978220723Sbschmidt		bus_teardown_intr(dev, sc->irq, sc->sc_ih);
979220723Sbschmidt		bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq);
980220723Sbschmidt		if (sc->irq_rid == 1)
981220723Sbschmidt			pci_release_msi(dev);
982220723Sbschmidt	}
983220723Sbschmidt
984201209Srpaulo	/* Free DMA resources. */
985198429Srpaulo	iwn_free_rx_ring(sc, &sc->rxq);
986220728Sbschmidt	for (qid = 0; qid < sc->ntxqs; qid++)
987220728Sbschmidt		iwn_free_tx_ring(sc, &sc->txq[qid]);
988198429Srpaulo	iwn_free_sched(sc);
989198429Srpaulo	iwn_free_kw(sc);
990201209Srpaulo	if (sc->ict != NULL)
991201209Srpaulo		iwn_free_ict(sc);
992198429Srpaulo	iwn_free_fwmem(sc);
993198429Srpaulo
994198429Srpaulo	if (sc->mem != NULL)
995198429Srpaulo		bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem);
996198429Srpaulo
997198429Srpaulo	if (ifp != NULL)
998198429Srpaulo		if_free(ifp);
999198429Srpaulo
1000253866Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n", __func__);
1001198429Srpaulo	IWN_LOCK_DESTROY(sc);
1002178676Ssam	return 0;
1003178676Ssam}
1004178676Ssam
1005178676Ssamstatic int
1006220723Sbschmidtiwn_shutdown(device_t dev)
1007220723Sbschmidt{
1008220723Sbschmidt	struct iwn_softc *sc = device_get_softc(dev);
1009220723Sbschmidt
1010220723Sbschmidt	iwn_stop(sc);
1011220723Sbschmidt	return 0;
1012220723Sbschmidt}
1013220723Sbschmidt
1014220723Sbschmidtstatic int
1015220723Sbschmidtiwn_suspend(device_t dev)
1016220723Sbschmidt{
1017220723Sbschmidt	struct iwn_softc *sc = device_get_softc(dev);
1018233387Sbschmidt	struct ieee80211com *ic = sc->sc_ifp->if_l2com;
1019220723Sbschmidt
1020233387Sbschmidt	ieee80211_suspend_all(ic);
1021220723Sbschmidt	return 0;
1022220723Sbschmidt}
1023220723Sbschmidt
1024220723Sbschmidtstatic int
1025220723Sbschmidtiwn_resume(device_t dev)
1026220723Sbschmidt{
1027220723Sbschmidt	struct iwn_softc *sc = device_get_softc(dev);
1028233387Sbschmidt	struct ieee80211com *ic = sc->sc_ifp->if_l2com;
1029220723Sbschmidt
1030220723Sbschmidt	/* Clear device-specific "PCI retry timeout" register (41h). */
1031220723Sbschmidt	pci_write_config(dev, 0x41, 0, 1);
1032220723Sbschmidt
1033233387Sbschmidt	ieee80211_resume_all(ic);
1034220723Sbschmidt	return 0;
1035220723Sbschmidt}
1036220723Sbschmidt
1037220723Sbschmidtstatic int
1038198429Srpauloiwn_nic_lock(struct iwn_softc *sc)
1039178676Ssam{
1040198429Srpaulo	int ntries;
1041178676Ssam
1042198429Srpaulo	/* Request exclusive access to NIC. */
1043198429Srpaulo	IWN_SETBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_MAC_ACCESS_REQ);
1044178676Ssam
1045198429Srpaulo	/* Spin until we actually get the lock. */
1046198429Srpaulo	for (ntries = 0; ntries < 1000; ntries++) {
1047198429Srpaulo		if ((IWN_READ(sc, IWN_GP_CNTRL) &
1048220726Sbschmidt		     (IWN_GP_CNTRL_MAC_ACCESS_ENA | IWN_GP_CNTRL_SLEEP)) ==
1049198429Srpaulo		    IWN_GP_CNTRL_MAC_ACCESS_ENA)
1050198429Srpaulo			return 0;
1051198429Srpaulo		DELAY(10);
1052198429Srpaulo	}
1053198429Srpaulo	return ETIMEDOUT;
1054198429Srpaulo}
1055198429Srpaulo
1056198429Srpaulostatic __inline void
1057198429Srpauloiwn_nic_unlock(struct iwn_softc *sc)
1058198429Srpaulo{
1059198429Srpaulo	IWN_CLRBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_MAC_ACCESS_REQ);
1060198429Srpaulo}
1061198429Srpaulo
1062198429Srpaulostatic __inline uint32_t
1063198429Srpauloiwn_prph_read(struct iwn_softc *sc, uint32_t addr)
1064198429Srpaulo{
1065198429Srpaulo	IWN_WRITE(sc, IWN_PRPH_RADDR, IWN_PRPH_DWORD | addr);
1066201209Srpaulo	IWN_BARRIER_READ_WRITE(sc);
1067198429Srpaulo	return IWN_READ(sc, IWN_PRPH_RDATA);
1068198429Srpaulo}
1069198429Srpaulo
1070198429Srpaulostatic __inline void
1071198429Srpauloiwn_prph_write(struct iwn_softc *sc, uint32_t addr, uint32_t data)
1072198429Srpaulo{
1073198429Srpaulo	IWN_WRITE(sc, IWN_PRPH_WADDR, IWN_PRPH_DWORD | addr);
1074201209Srpaulo	IWN_BARRIER_WRITE(sc);
1075198429Srpaulo	IWN_WRITE(sc, IWN_PRPH_WDATA, data);
1076198429Srpaulo}
1077198429Srpaulo
1078198429Srpaulostatic __inline void
1079198429Srpauloiwn_prph_setbits(struct iwn_softc *sc, uint32_t addr, uint32_t mask)
1080198429Srpaulo{
1081198429Srpaulo	iwn_prph_write(sc, addr, iwn_prph_read(sc, addr) | mask);
1082198429Srpaulo}
1083198429Srpaulo
1084198429Srpaulostatic __inline void
1085198429Srpauloiwn_prph_clrbits(struct iwn_softc *sc, uint32_t addr, uint32_t mask)
1086198429Srpaulo{
1087198429Srpaulo	iwn_prph_write(sc, addr, iwn_prph_read(sc, addr) & ~mask);
1088198429Srpaulo}
1089198429Srpaulo
1090198429Srpaulostatic __inline void
1091198429Srpauloiwn_prph_write_region_4(struct iwn_softc *sc, uint32_t addr,
1092198429Srpaulo    const uint32_t *data, int count)
1093198429Srpaulo{
1094198429Srpaulo	for (; count > 0; count--, data++, addr += 4)
1095198429Srpaulo		iwn_prph_write(sc, addr, *data);
1096198429Srpaulo}
1097198429Srpaulo
1098198429Srpaulostatic __inline uint32_t
1099198429Srpauloiwn_mem_read(struct iwn_softc *sc, uint32_t addr)
1100198429Srpaulo{
1101198429Srpaulo	IWN_WRITE(sc, IWN_MEM_RADDR, addr);
1102201209Srpaulo	IWN_BARRIER_READ_WRITE(sc);
1103198429Srpaulo	return IWN_READ(sc, IWN_MEM_RDATA);
1104198429Srpaulo}
1105198429Srpaulo
1106198429Srpaulostatic __inline void
1107198429Srpauloiwn_mem_write(struct iwn_softc *sc, uint32_t addr, uint32_t data)
1108198429Srpaulo{
1109198429Srpaulo	IWN_WRITE(sc, IWN_MEM_WADDR, addr);
1110201209Srpaulo	IWN_BARRIER_WRITE(sc);
1111198429Srpaulo	IWN_WRITE(sc, IWN_MEM_WDATA, data);
1112198429Srpaulo}
1113198429Srpaulo
1114198429Srpaulostatic __inline void
1115198429Srpauloiwn_mem_write_2(struct iwn_softc *sc, uint32_t addr, uint16_t data)
1116198429Srpaulo{
1117198429Srpaulo	uint32_t tmp;
1118198429Srpaulo
1119198429Srpaulo	tmp = iwn_mem_read(sc, addr & ~3);
1120198429Srpaulo	if (addr & 3)
1121198429Srpaulo		tmp = (tmp & 0x0000ffff) | data << 16;
1122198429Srpaulo	else
1123198429Srpaulo		tmp = (tmp & 0xffff0000) | data;
1124198429Srpaulo	iwn_mem_write(sc, addr & ~3, tmp);
1125198429Srpaulo}
1126198429Srpaulo
1127198429Srpaulostatic __inline void
1128198429Srpauloiwn_mem_read_region_4(struct iwn_softc *sc, uint32_t addr, uint32_t *data,
1129198429Srpaulo    int count)
1130198429Srpaulo{
1131198429Srpaulo	for (; count > 0; count--, addr += 4)
1132198429Srpaulo		*data++ = iwn_mem_read(sc, addr);
1133198429Srpaulo}
1134198429Srpaulo
1135198429Srpaulostatic __inline void
1136198429Srpauloiwn_mem_set_region_4(struct iwn_softc *sc, uint32_t addr, uint32_t val,
1137198429Srpaulo    int count)
1138198429Srpaulo{
1139198429Srpaulo	for (; count > 0; count--, addr += 4)
1140198429Srpaulo		iwn_mem_write(sc, addr, val);
1141198429Srpaulo}
1142198429Srpaulo
1143206477Sbschmidtstatic int
1144198429Srpauloiwn_eeprom_lock(struct iwn_softc *sc)
1145198429Srpaulo{
1146198429Srpaulo	int i, ntries;
1147198429Srpaulo
1148198429Srpaulo	for (i = 0; i < 100; i++) {
1149198429Srpaulo		/* Request exclusive access to EEPROM. */
1150198429Srpaulo		IWN_SETBITS(sc, IWN_HW_IF_CONFIG,
1151198429Srpaulo		    IWN_HW_IF_CONFIG_EEPROM_LOCKED);
1152198429Srpaulo
1153198429Srpaulo		/* Spin until we actually get the lock. */
1154198429Srpaulo		for (ntries = 0; ntries < 100; ntries++) {
1155198429Srpaulo			if (IWN_READ(sc, IWN_HW_IF_CONFIG) &
1156198429Srpaulo			    IWN_HW_IF_CONFIG_EEPROM_LOCKED)
1157198429Srpaulo				return 0;
1158198429Srpaulo			DELAY(10);
1159198429Srpaulo		}
1160198429Srpaulo	}
1161253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s end timeout\n", __func__);
1162198429Srpaulo	return ETIMEDOUT;
1163198429Srpaulo}
1164198429Srpaulo
1165198429Srpaulostatic __inline void
1166198429Srpauloiwn_eeprom_unlock(struct iwn_softc *sc)
1167198429Srpaulo{
1168198429Srpaulo	IWN_CLRBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_EEPROM_LOCKED);
1169198429Srpaulo}
1170198429Srpaulo
1171198429Srpaulo/*
1172198429Srpaulo * Initialize access by host to One Time Programmable ROM.
1173198429Srpaulo * NB: This kind of ROM can be found on 1000 or 6000 Series only.
1174198429Srpaulo */
1175206477Sbschmidtstatic int
1176198429Srpauloiwn_init_otprom(struct iwn_softc *sc)
1177198429Srpaulo{
1178203934Sbschmidt	uint16_t prev, base, next;
1179201209Srpaulo	int count, error;
1180198429Srpaulo
1181253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
1182253705Sadrian
1183201209Srpaulo	/* Wait for clock stabilization before accessing prph. */
1184220726Sbschmidt	if ((error = iwn_clock_wait(sc)) != 0)
1185198429Srpaulo		return error;
1186198429Srpaulo
1187220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
1188198429Srpaulo		return error;
1189198429Srpaulo	iwn_prph_setbits(sc, IWN_APMG_PS, IWN_APMG_PS_RESET_REQ);
1190198429Srpaulo	DELAY(5);
1191198429Srpaulo	iwn_prph_clrbits(sc, IWN_APMG_PS, IWN_APMG_PS_RESET_REQ);
1192198429Srpaulo	iwn_nic_unlock(sc);
1193198429Srpaulo
1194201209Srpaulo	/* Set auto clock gate disable bit for HW with OTP shadow RAM. */
1195201209Srpaulo	if (sc->hw_type != IWN_HW_REV_TYPE_1000) {
1196201209Srpaulo		IWN_SETBITS(sc, IWN_DBG_LINK_PWR_MGMT,
1197201209Srpaulo		    IWN_RESET_LINK_PWR_MGMT_DIS);
1198201209Srpaulo	}
1199198429Srpaulo	IWN_CLRBITS(sc, IWN_EEPROM_GP, IWN_EEPROM_GP_IF_OWNER);
1200198429Srpaulo	/* Clear ECC status. */
1201198429Srpaulo	IWN_SETBITS(sc, IWN_OTP_GP,
1202198429Srpaulo	    IWN_OTP_GP_ECC_CORR_STTS | IWN_OTP_GP_ECC_UNCORR_STTS);
1203198429Srpaulo
1204201209Srpaulo	/*
1205203934Sbschmidt	 * Find the block before last block (contains the EEPROM image)
1206203934Sbschmidt	 * for HW without OTP shadow RAM.
1207201209Srpaulo	 */
1208201209Srpaulo	if (sc->hw_type == IWN_HW_REV_TYPE_1000) {
1209201209Srpaulo		/* Switch to absolute addressing mode. */
1210201209Srpaulo		IWN_CLRBITS(sc, IWN_OTP_GP, IWN_OTP_GP_RELATIVE_ACCESS);
1211203934Sbschmidt		base = prev = 0;
1212201209Srpaulo		for (count = 0; count < IWN1000_OTP_NBLOCKS; count++) {
1213201209Srpaulo			error = iwn_read_prom_data(sc, base, &next, 2);
1214201209Srpaulo			if (error != 0)
1215201209Srpaulo				return error;
1216201209Srpaulo			if (next == 0)	/* End of linked-list. */
1217201209Srpaulo				break;
1218203934Sbschmidt			prev = base;
1219201209Srpaulo			base = le16toh(next);
1220201209Srpaulo		}
1221203934Sbschmidt		if (count == 0 || count == IWN1000_OTP_NBLOCKS)
1222201209Srpaulo			return EIO;
1223201209Srpaulo		/* Skip "next" word. */
1224203934Sbschmidt		sc->prom_base = prev + 1;
1225201209Srpaulo	}
1226253705Sadrian
1227253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s end\n", __func__);
1228253705Sadrian
1229178676Ssam	return 0;
1230178676Ssam}
1231178676Ssam
1232206477Sbschmidtstatic int
1233198429Srpauloiwn_read_prom_data(struct iwn_softc *sc, uint32_t addr, void *data, int count)
1234198429Srpaulo{
1235220723Sbschmidt	uint8_t *out = data;
1236198429Srpaulo	uint32_t val, tmp;
1237198429Srpaulo	int ntries;
1238198429Srpaulo
1239253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
1240253705Sadrian
1241201209Srpaulo	addr += sc->prom_base;
1242198429Srpaulo	for (; count > 0; count -= 2, addr++) {
1243198429Srpaulo		IWN_WRITE(sc, IWN_EEPROM, addr << 2);
1244201209Srpaulo		for (ntries = 0; ntries < 10; ntries++) {
1245198429Srpaulo			val = IWN_READ(sc, IWN_EEPROM);
1246198429Srpaulo			if (val & IWN_EEPROM_READ_VALID)
1247198429Srpaulo				break;
1248198429Srpaulo			DELAY(5);
1249198429Srpaulo		}
1250201209Srpaulo		if (ntries == 10) {
1251198429Srpaulo			device_printf(sc->sc_dev,
1252198429Srpaulo			    "timeout reading ROM at 0x%x\n", addr);
1253198429Srpaulo			return ETIMEDOUT;
1254198429Srpaulo		}
1255198429Srpaulo		if (sc->sc_flags & IWN_FLAG_HAS_OTPROM) {
1256198429Srpaulo			/* OTPROM, check for ECC errors. */
1257198429Srpaulo			tmp = IWN_READ(sc, IWN_OTP_GP);
1258198429Srpaulo			if (tmp & IWN_OTP_GP_ECC_UNCORR_STTS) {
1259198429Srpaulo				device_printf(sc->sc_dev,
1260198429Srpaulo				    "OTPROM ECC error at 0x%x\n", addr);
1261198429Srpaulo				return EIO;
1262198429Srpaulo			}
1263198429Srpaulo			if (tmp & IWN_OTP_GP_ECC_CORR_STTS) {
1264198429Srpaulo				/* Correctable ECC error, clear bit. */
1265198429Srpaulo				IWN_SETBITS(sc, IWN_OTP_GP,
1266198429Srpaulo				    IWN_OTP_GP_ECC_CORR_STTS);
1267198429Srpaulo			}
1268198429Srpaulo		}
1269198429Srpaulo		*out++ = val >> 16;
1270198429Srpaulo		if (count > 1)
1271198429Srpaulo			*out++ = val >> 24;
1272198429Srpaulo	}
1273253705Sadrian
1274253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s end\n", __func__);
1275253705Sadrian
1276198429Srpaulo	return 0;
1277198429Srpaulo}
1278198429Srpaulo
1279178676Ssamstatic void
1280178676Ssamiwn_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
1281178676Ssam{
1282198429Srpaulo	if (error != 0)
1283198429Srpaulo		return;
1284198429Srpaulo	KASSERT(nsegs == 1, ("too many DMA segments, %d should be 1", nsegs));
1285198429Srpaulo	*(bus_addr_t *)arg = segs[0].ds_addr;
1286178676Ssam}
1287178676Ssam
1288198429Srpaulostatic int
1289178676Ssamiwn_dma_contig_alloc(struct iwn_softc *sc, struct iwn_dma_info *dma,
1290220691Sbschmidt    void **kvap, bus_size_t size, bus_size_t alignment)
1291178676Ssam{
1292198429Srpaulo	int error;
1293178676Ssam
1294220723Sbschmidt	dma->tag = NULL;
1295178676Ssam	dma->size = size;
1296178676Ssam
1297198429Srpaulo	error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), alignment,
1298178676Ssam	    0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, size,
1299220691Sbschmidt	    1, size, BUS_DMA_NOWAIT, NULL, NULL, &dma->tag);
1300220711Sbschmidt	if (error != 0)
1301178676Ssam		goto fail;
1302220711Sbschmidt
1303178676Ssam	error = bus_dmamem_alloc(dma->tag, (void **)&dma->vaddr,
1304220691Sbschmidt	    BUS_DMA_NOWAIT | BUS_DMA_ZERO | BUS_DMA_COHERENT, &dma->map);
1305220711Sbschmidt	if (error != 0)
1306178676Ssam		goto fail;
1307220711Sbschmidt
1308220691Sbschmidt	error = bus_dmamap_load(dma->tag, dma->map, dma->vaddr, size,
1309220691Sbschmidt	    iwn_dma_map_addr, &dma->paddr, BUS_DMA_NOWAIT);
1310220711Sbschmidt	if (error != 0)
1311178676Ssam		goto fail;
1312178676Ssam
1313220704Sbschmidt	bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
1314220704Sbschmidt
1315178676Ssam	if (kvap != NULL)
1316178676Ssam		*kvap = dma->vaddr;
1317220726Sbschmidt
1318178676Ssam	return 0;
1319220726Sbschmidt
1320220726Sbschmidtfail:	iwn_dma_contig_free(dma);
1321178676Ssam	return error;
1322178676Ssam}
1323178676Ssam
1324206477Sbschmidtstatic void
1325178676Ssamiwn_dma_contig_free(struct iwn_dma_info *dma)
1326178676Ssam{
1327220701Sbschmidt	if (dma->map != NULL) {
1328220701Sbschmidt		if (dma->vaddr != NULL) {
1329220701Sbschmidt			bus_dmamap_sync(dma->tag, dma->map,
1330220701Sbschmidt			    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
1331220701Sbschmidt			bus_dmamap_unload(dma->tag, dma->map);
1332243622Sbschmidt			bus_dmamem_free(dma->tag, dma->vaddr, dma->map);
1333220701Sbschmidt			dma->vaddr = NULL;
1334178676Ssam		}
1335220701Sbschmidt		bus_dmamap_destroy(dma->tag, dma->map);
1336220701Sbschmidt		dma->map = NULL;
1337220701Sbschmidt	}
1338220701Sbschmidt	if (dma->tag != NULL) {
1339178676Ssam		bus_dma_tag_destroy(dma->tag);
1340220701Sbschmidt		dma->tag = NULL;
1341178676Ssam	}
1342178676Ssam}
1343178676Ssam
1344206477Sbschmidtstatic int
1345198429Srpauloiwn_alloc_sched(struct iwn_softc *sc)
1346178676Ssam{
1347198429Srpaulo	/* TX scheduler rings must be aligned on a 1KB boundary. */
1348220691Sbschmidt	return iwn_dma_contig_alloc(sc, &sc->sched_dma, (void **)&sc->sched,
1349220728Sbschmidt	    sc->schedsz, 1024);
1350178676Ssam}
1351178676Ssam
1352206477Sbschmidtstatic void
1353198429Srpauloiwn_free_sched(struct iwn_softc *sc)
1354178676Ssam{
1355198429Srpaulo	iwn_dma_contig_free(&sc->sched_dma);
1356178676Ssam}
1357178676Ssam
1358206477Sbschmidtstatic int
1359178676Ssamiwn_alloc_kw(struct iwn_softc *sc)
1360178676Ssam{
1361198429Srpaulo	/* "Keep Warm" page must be aligned on a 4KB boundary. */
1362220691Sbschmidt	return iwn_dma_contig_alloc(sc, &sc->kw_dma, NULL, 4096, 4096);
1363178676Ssam}
1364178676Ssam
1365206477Sbschmidtstatic void
1366178676Ssamiwn_free_kw(struct iwn_softc *sc)
1367178676Ssam{
1368178676Ssam	iwn_dma_contig_free(&sc->kw_dma);
1369178676Ssam}
1370178676Ssam
1371206477Sbschmidtstatic int
1372201209Srpauloiwn_alloc_ict(struct iwn_softc *sc)
1373201209Srpaulo{
1374201209Srpaulo	/* ICT table must be aligned on a 4KB boundary. */
1375220691Sbschmidt	return iwn_dma_contig_alloc(sc, &sc->ict_dma, (void **)&sc->ict,
1376220691Sbschmidt	    IWN_ICT_SIZE, 4096);
1377201209Srpaulo}
1378201209Srpaulo
1379206477Sbschmidtstatic void
1380201209Srpauloiwn_free_ict(struct iwn_softc *sc)
1381201209Srpaulo{
1382201209Srpaulo	iwn_dma_contig_free(&sc->ict_dma);
1383201209Srpaulo}
1384201209Srpaulo
1385206477Sbschmidtstatic int
1386178676Ssamiwn_alloc_fwmem(struct iwn_softc *sc)
1387178676Ssam{
1388198429Srpaulo	/* Must be aligned on a 16-byte boundary. */
1389220728Sbschmidt	return iwn_dma_contig_alloc(sc, &sc->fw_dma, NULL, sc->fwsz, 16);
1390178676Ssam}
1391178676Ssam
1392206477Sbschmidtstatic void
1393178676Ssamiwn_free_fwmem(struct iwn_softc *sc)
1394178676Ssam{
1395178676Ssam	iwn_dma_contig_free(&sc->fw_dma);
1396178676Ssam}
1397178676Ssam
1398206477Sbschmidtstatic int
1399178676Ssamiwn_alloc_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring)
1400178676Ssam{
1401198429Srpaulo	bus_size_t size;
1402178676Ssam	int i, error;
1403178676Ssam
1404178676Ssam	ring->cur = 0;
1405178676Ssam
1406253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
1407253705Sadrian
1408198429Srpaulo	/* Allocate RX descriptors (256-byte aligned). */
1409198429Srpaulo	size = IWN_RX_RING_COUNT * sizeof (uint32_t);
1410220691Sbschmidt	error = iwn_dma_contig_alloc(sc, &ring->desc_dma, (void **)&ring->desc,
1411220691Sbschmidt	    size, 256);
1412178676Ssam	if (error != 0) {
1413178676Ssam		device_printf(sc->sc_dev,
1414220711Sbschmidt		    "%s: could not allocate RX ring DMA memory, error %d\n",
1415178676Ssam		    __func__, error);
1416178676Ssam		goto fail;
1417178676Ssam	}
1418178676Ssam
1419220702Sbschmidt	/* Allocate RX status area (16-byte aligned). */
1420220702Sbschmidt	error = iwn_dma_contig_alloc(sc, &ring->stat_dma, (void **)&ring->stat,
1421220702Sbschmidt	    sizeof (struct iwn_rx_status), 16);
1422198429Srpaulo	if (error != 0) {
1423198429Srpaulo		device_printf(sc->sc_dev,
1424220711Sbschmidt		    "%s: could not allocate RX status DMA memory, error %d\n",
1425178676Ssam		    __func__, error);
1426198429Srpaulo		goto fail;
1427198429Srpaulo	}
1428178676Ssam
1429220702Sbschmidt	/* Create RX buffer DMA tag. */
1430220702Sbschmidt	error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0,
1431220702Sbschmidt	    BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
1432220702Sbschmidt	    IWN_RBUF_SIZE, 1, IWN_RBUF_SIZE, BUS_DMA_NOWAIT, NULL, NULL,
1433220702Sbschmidt	    &ring->data_dmat);
1434198429Srpaulo	if (error != 0) {
1435198429Srpaulo		device_printf(sc->sc_dev,
1436220711Sbschmidt		    "%s: could not create RX buf DMA tag, error %d\n",
1437198429Srpaulo		    __func__, error);
1438198429Srpaulo		goto fail;
1439198429Srpaulo	}
1440198429Srpaulo
1441178676Ssam	/*
1442198429Srpaulo	 * Allocate and map RX buffers.
1443178676Ssam	 */
1444178676Ssam	for (i = 0; i < IWN_RX_RING_COUNT; i++) {
1445178676Ssam		struct iwn_rx_data *data = &ring->data[i];
1446178676Ssam		bus_addr_t paddr;
1447178676Ssam
1448201209Srpaulo		error = bus_dmamap_create(ring->data_dmat, 0, &data->map);
1449178676Ssam		if (error != 0) {
1450178676Ssam			device_printf(sc->sc_dev,
1451220711Sbschmidt			    "%s: could not create RX buf DMA map, error %d\n",
1452178676Ssam			    __func__, error);
1453178676Ssam			goto fail;
1454178676Ssam		}
1455198429Srpaulo
1456243857Sglebius		data->m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR,
1457220692Sbschmidt		    IWN_RBUF_SIZE);
1458198439Srpaulo		if (data->m == NULL) {
1459178676Ssam			device_printf(sc->sc_dev,
1460220711Sbschmidt			    "%s: could not allocate RX mbuf\n", __func__);
1461220710Sbschmidt			error = ENOBUFS;
1462178676Ssam			goto fail;
1463178676Ssam		}
1464198429Srpaulo
1465201209Srpaulo		error = bus_dmamap_load(ring->data_dmat, data->map,
1466220692Sbschmidt		    mtod(data->m, void *), IWN_RBUF_SIZE, iwn_dma_map_addr,
1467220692Sbschmidt		    &paddr, BUS_DMA_NOWAIT);
1468178676Ssam		if (error != 0 && error != EFBIG) {
1469178676Ssam			device_printf(sc->sc_dev,
1470220711Sbschmidt			    "%s: can't not map mbuf, error %d\n", __func__,
1471220711Sbschmidt			    error);
1472178676Ssam			goto fail;
1473178676Ssam		}
1474178676Ssam
1475198429Srpaulo		/* Set physical address of RX buffer (256-byte aligned). */
1476178676Ssam		ring->desc[i] = htole32(paddr >> 8);
1477178676Ssam	}
1478220726Sbschmidt
1479178676Ssam	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
1480178676Ssam	    BUS_DMASYNC_PREWRITE);
1481220726Sbschmidt
1482253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
1483253705Sadrian
1484178676Ssam	return 0;
1485220726Sbschmidt
1486220726Sbschmidtfail:	iwn_free_rx_ring(sc, ring);
1487253705Sadrian
1488253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end in error\n",__func__);
1489253705Sadrian
1490178676Ssam	return error;
1491178676Ssam}
1492178676Ssam
1493206477Sbschmidtstatic void
1494178676Ssamiwn_reset_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring)
1495178676Ssam{
1496178676Ssam	int ntries;
1497178676Ssam
1498253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
1499253705Sadrian
1500198429Srpaulo	if (iwn_nic_lock(sc) == 0) {
1501198429Srpaulo		IWN_WRITE(sc, IWN_FH_RX_CONFIG, 0);
1502198429Srpaulo		for (ntries = 0; ntries < 1000; ntries++) {
1503198429Srpaulo			if (IWN_READ(sc, IWN_FH_RX_STATUS) &
1504198429Srpaulo			    IWN_FH_RX_STATUS_IDLE)
1505198429Srpaulo				break;
1506198429Srpaulo			DELAY(10);
1507198429Srpaulo		}
1508198429Srpaulo		iwn_nic_unlock(sc);
1509198429Srpaulo	}
1510178676Ssam	ring->cur = 0;
1511198429Srpaulo	sc->last_rx_valid = 0;
1512178676Ssam}
1513178676Ssam
1514206477Sbschmidtstatic void
1515178676Ssamiwn_free_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring)
1516178676Ssam{
1517178676Ssam	int i;
1518178676Ssam
1519253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s \n", __func__);
1520253705Sadrian
1521178676Ssam	iwn_dma_contig_free(&ring->desc_dma);
1522198429Srpaulo	iwn_dma_contig_free(&ring->stat_dma);
1523178676Ssam
1524198429Srpaulo	for (i = 0; i < IWN_RX_RING_COUNT; i++) {
1525198429Srpaulo		struct iwn_rx_data *data = &ring->data[i];
1526198429Srpaulo
1527198429Srpaulo		if (data->m != NULL) {
1528201209Srpaulo			bus_dmamap_sync(ring->data_dmat, data->map,
1529198439Srpaulo			    BUS_DMASYNC_POSTREAD);
1530201209Srpaulo			bus_dmamap_unload(ring->data_dmat, data->map);
1531198429Srpaulo			m_freem(data->m);
1532220710Sbschmidt			data->m = NULL;
1533198429Srpaulo		}
1534201209Srpaulo		if (data->map != NULL)
1535201209Srpaulo			bus_dmamap_destroy(ring->data_dmat, data->map);
1536198429Srpaulo	}
1537220701Sbschmidt	if (ring->data_dmat != NULL) {
1538220701Sbschmidt		bus_dma_tag_destroy(ring->data_dmat);
1539220701Sbschmidt		ring->data_dmat = NULL;
1540220701Sbschmidt	}
1541178676Ssam}
1542178676Ssam
1543206477Sbschmidtstatic int
1544178676Ssamiwn_alloc_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring, int qid)
1545178676Ssam{
1546220723Sbschmidt	bus_addr_t paddr;
1547178676Ssam	bus_size_t size;
1548178676Ssam	int i, error;
1549178676Ssam
1550178676Ssam	ring->qid = qid;
1551178676Ssam	ring->queued = 0;
1552178676Ssam	ring->cur = 0;
1553178676Ssam
1554253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
1555253705Sadrian
1556220725Sbschmidt	/* Allocate TX descriptors (256-byte aligned). */
1557220726Sbschmidt	size = IWN_TX_RING_COUNT * sizeof (struct iwn_tx_desc);
1558220691Sbschmidt	error = iwn_dma_contig_alloc(sc, &ring->desc_dma, (void **)&ring->desc,
1559220691Sbschmidt	    size, 256);
1560178676Ssam	if (error != 0) {
1561178676Ssam		device_printf(sc->sc_dev,
1562198429Srpaulo		    "%s: could not allocate TX ring DMA memory, error %d\n",
1563178676Ssam		    __func__, error);
1564178676Ssam		goto fail;
1565178676Ssam	}
1566198429Srpaulo
1567220726Sbschmidt	size = IWN_TX_RING_COUNT * sizeof (struct iwn_tx_cmd);
1568220691Sbschmidt	error = iwn_dma_contig_alloc(sc, &ring->cmd_dma, (void **)&ring->cmd,
1569220691Sbschmidt	    size, 4);
1570178676Ssam	if (error != 0) {
1571178676Ssam		device_printf(sc->sc_dev,
1572198429Srpaulo		    "%s: could not allocate TX cmd DMA memory, error %d\n",
1573178676Ssam		    __func__, error);
1574178676Ssam		goto fail;
1575178676Ssam	}
1576178676Ssam
1577198429Srpaulo	error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0,
1578220726Sbschmidt	    BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES,
1579220726Sbschmidt	    IWN_MAX_SCATTER - 1, MCLBYTES, BUS_DMA_NOWAIT, NULL, NULL,
1580220726Sbschmidt	    &ring->data_dmat);
1581198429Srpaulo	if (error != 0) {
1582198429Srpaulo		device_printf(sc->sc_dev,
1583220711Sbschmidt		    "%s: could not create TX buf DMA tag, error %d\n",
1584178676Ssam		    __func__, error);
1585198429Srpaulo		goto fail;
1586198429Srpaulo	}
1587178676Ssam
1588198429Srpaulo	paddr = ring->cmd_dma.paddr;
1589178676Ssam	for (i = 0; i < IWN_TX_RING_COUNT; i++) {
1590178676Ssam		struct iwn_tx_data *data = &ring->data[i];
1591178676Ssam
1592198429Srpaulo		data->cmd_paddr = paddr;
1593198429Srpaulo		data->scratch_paddr = paddr + 12;
1594198429Srpaulo		paddr += sizeof (struct iwn_tx_cmd);
1595198429Srpaulo
1596201209Srpaulo		error = bus_dmamap_create(ring->data_dmat, 0, &data->map);
1597178676Ssam		if (error != 0) {
1598178676Ssam			device_printf(sc->sc_dev,
1599220711Sbschmidt			    "%s: could not create TX buf DMA map, error %d\n",
1600178676Ssam			    __func__, error);
1601178676Ssam			goto fail;
1602178676Ssam		}
1603178676Ssam	}
1604253705Sadrian
1605253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s end\n", __func__);
1606253705Sadrian
1607178676Ssam	return 0;
1608220726Sbschmidt
1609220726Sbschmidtfail:	iwn_free_tx_ring(sc, ring);
1610253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s end in error\n", __func__);
1611178676Ssam	return error;
1612178676Ssam}
1613178676Ssam
1614206477Sbschmidtstatic void
1615178676Ssamiwn_reset_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring)
1616178676Ssam{
1617198429Srpaulo	int i;
1618178676Ssam
1619253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->doing %s \n", __func__);
1620253705Sadrian
1621178676Ssam	for (i = 0; i < IWN_TX_RING_COUNT; i++) {
1622178676Ssam		struct iwn_tx_data *data = &ring->data[i];
1623178676Ssam
1624178676Ssam		if (data->m != NULL) {
1625220704Sbschmidt			bus_dmamap_sync(ring->data_dmat, data->map,
1626220704Sbschmidt			    BUS_DMASYNC_POSTWRITE);
1627201209Srpaulo			bus_dmamap_unload(ring->data_dmat, data->map);
1628178676Ssam			m_freem(data->m);
1629178676Ssam			data->m = NULL;
1630178676Ssam		}
1631178676Ssam	}
1632198429Srpaulo	/* Clear TX descriptors. */
1633198429Srpaulo	memset(ring->desc, 0, ring->desc_dma.size);
1634198439Srpaulo	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
1635198439Srpaulo	    BUS_DMASYNC_PREWRITE);
1636198429Srpaulo	sc->qfullmsk &= ~(1 << ring->qid);
1637178676Ssam	ring->queued = 0;
1638178676Ssam	ring->cur = 0;
1639178676Ssam}
1640178676Ssam
1641206477Sbschmidtstatic void
1642178676Ssamiwn_free_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring)
1643178676Ssam{
1644178676Ssam	int i;
1645178676Ssam
1646253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s \n", __func__);
1647253705Sadrian
1648178676Ssam	iwn_dma_contig_free(&ring->desc_dma);
1649178676Ssam	iwn_dma_contig_free(&ring->cmd_dma);
1650178676Ssam
1651201209Srpaulo	for (i = 0; i < IWN_TX_RING_COUNT; i++) {
1652201209Srpaulo		struct iwn_tx_data *data = &ring->data[i];
1653178676Ssam
1654201209Srpaulo		if (data->m != NULL) {
1655201209Srpaulo			bus_dmamap_sync(ring->data_dmat, data->map,
1656201209Srpaulo			    BUS_DMASYNC_POSTWRITE);
1657201209Srpaulo			bus_dmamap_unload(ring->data_dmat, data->map);
1658201209Srpaulo			m_freem(data->m);
1659178676Ssam		}
1660201209Srpaulo		if (data->map != NULL)
1661201209Srpaulo			bus_dmamap_destroy(ring->data_dmat, data->map);
1662178676Ssam	}
1663220701Sbschmidt	if (ring->data_dmat != NULL) {
1664220701Sbschmidt		bus_dma_tag_destroy(ring->data_dmat);
1665220701Sbschmidt		ring->data_dmat = NULL;
1666220701Sbschmidt	}
1667178676Ssam}
1668178676Ssam
1669206477Sbschmidtstatic void
1670201209Srpauloiwn5000_ict_reset(struct iwn_softc *sc)
1671201209Srpaulo{
1672201209Srpaulo	/* Disable interrupts. */
1673201209Srpaulo	IWN_WRITE(sc, IWN_INT_MASK, 0);
1674201209Srpaulo
1675201209Srpaulo	/* Reset ICT table. */
1676201209Srpaulo	memset(sc->ict, 0, IWN_ICT_SIZE);
1677201209Srpaulo	sc->ict_cur = 0;
1678201209Srpaulo
1679220725Sbschmidt	/* Set physical address of ICT table (4KB aligned). */
1680201209Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "%s: enabling ICT\n", __func__);
1681201209Srpaulo	IWN_WRITE(sc, IWN_DRAM_INT_TBL, IWN_DRAM_INT_TBL_ENABLE |
1682201209Srpaulo	    IWN_DRAM_INT_TBL_WRAP_CHECK | sc->ict_dma.paddr >> 12);
1683201209Srpaulo
1684201209Srpaulo	/* Enable periodic RX interrupt. */
1685201209Srpaulo	sc->int_mask |= IWN_INT_RX_PERIODIC;
1686201209Srpaulo	/* Switch to ICT interrupt mode in driver. */
1687201209Srpaulo	sc->sc_flags |= IWN_FLAG_USE_ICT;
1688201209Srpaulo
1689201209Srpaulo	/* Re-enable interrupts. */
1690201209Srpaulo	IWN_WRITE(sc, IWN_INT, 0xffffffff);
1691201209Srpaulo	IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask);
1692201209Srpaulo}
1693201209Srpaulo
1694206477Sbschmidtstatic int
1695198429Srpauloiwn_read_eeprom(struct iwn_softc *sc, uint8_t macaddr[IEEE80211_ADDR_LEN])
1696178676Ssam{
1697220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
1698220723Sbschmidt	uint16_t val;
1699198429Srpaulo	int error;
1700178676Ssam
1701253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
1702253705Sadrian
1703198429Srpaulo	/* Check whether adapter has an EEPROM or an OTPROM. */
1704198429Srpaulo	if (sc->hw_type >= IWN_HW_REV_TYPE_1000 &&
1705198429Srpaulo	    (IWN_READ(sc, IWN_OTP_GP) & IWN_OTP_GP_DEV_SEL_OTP))
1706198429Srpaulo		sc->sc_flags |= IWN_FLAG_HAS_OTPROM;
1707198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "%s found\n",
1708198429Srpaulo	    (sc->sc_flags & IWN_FLAG_HAS_OTPROM) ? "OTPROM" : "EEPROM");
1709178676Ssam
1710201209Srpaulo	/* Adapter has to be powered on for EEPROM access to work. */
1711220726Sbschmidt	if ((error = iwn_apm_init(sc)) != 0) {
1712201209Srpaulo		device_printf(sc->sc_dev,
1713220726Sbschmidt		    "%s: could not power ON adapter, error %d\n", __func__,
1714220726Sbschmidt		    error);
1715201209Srpaulo		return error;
1716201209Srpaulo	}
1717201209Srpaulo
1718198429Srpaulo	if ((IWN_READ(sc, IWN_EEPROM_GP) & 0x7) == 0) {
1719198429Srpaulo		device_printf(sc->sc_dev, "%s: bad ROM signature\n", __func__);
1720198429Srpaulo		return EIO;
1721198429Srpaulo	}
1722220726Sbschmidt	if ((error = iwn_eeprom_lock(sc)) != 0) {
1723220726Sbschmidt		device_printf(sc->sc_dev, "%s: could not lock ROM, error %d\n",
1724198429Srpaulo		    __func__, error);
1725198429Srpaulo		return error;
1726198429Srpaulo	}
1727201209Srpaulo	if (sc->sc_flags & IWN_FLAG_HAS_OTPROM) {
1728220726Sbschmidt		if ((error = iwn_init_otprom(sc)) != 0) {
1729201209Srpaulo			device_printf(sc->sc_dev,
1730201209Srpaulo			    "%s: could not initialize OTPROM, error %d\n",
1731201209Srpaulo			    __func__, error);
1732201209Srpaulo			return error;
1733201209Srpaulo		}
1734198429Srpaulo	}
1735178676Ssam
1736220729Sbschmidt	iwn_read_prom_data(sc, IWN_EEPROM_SKU_CAP, &val, 2);
1737220729Sbschmidt	DPRINTF(sc, IWN_DEBUG_RESET, "SKU capabilities=0x%04x\n", le16toh(val));
1738220729Sbschmidt	/* Check if HT support is bonded out. */
1739220729Sbschmidt	if (val & htole16(IWN_EEPROM_SKU_CAP_11N))
1740220729Sbschmidt		sc->sc_flags |= IWN_FLAG_HAS_11N;
1741220729Sbschmidt
1742198429Srpaulo	iwn_read_prom_data(sc, IWN_EEPROM_RFCFG, &val, 2);
1743198429Srpaulo	sc->rfcfg = le16toh(val);
1744198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "radio config=0x%04x\n", sc->rfcfg);
1745220727Sbschmidt	/* Read Tx/Rx chains from ROM unless it's known to be broken. */
1746220727Sbschmidt	if (sc->txchainmask == 0)
1747220727Sbschmidt		sc->txchainmask = IWN_RFCFG_TXANTMSK(sc->rfcfg);
1748220727Sbschmidt	if (sc->rxchainmask == 0)
1749220727Sbschmidt		sc->rxchainmask = IWN_RFCFG_RXANTMSK(sc->rfcfg);
1750178676Ssam
1751198429Srpaulo	/* Read MAC address. */
1752198429Srpaulo	iwn_read_prom_data(sc, IWN_EEPROM_MAC, macaddr, 6);
1753178676Ssam
1754198429Srpaulo	/* Read adapter-specific information from EEPROM. */
1755220728Sbschmidt	ops->read_eeprom(sc);
1756178676Ssam
1757201209Srpaulo	iwn_apm_stop(sc);	/* Power OFF adapter. */
1758201209Srpaulo
1759198429Srpaulo	iwn_eeprom_unlock(sc);
1760253705Sadrian
1761253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s end\n", __func__);
1762253705Sadrian
1763198429Srpaulo	return 0;
1764178676Ssam}
1765178676Ssam
1766206477Sbschmidtstatic void
1767198429Srpauloiwn4965_read_eeprom(struct iwn_softc *sc)
1768178676Ssam{
1769201209Srpaulo	uint32_t addr;
1770220723Sbschmidt	uint16_t val;
1771198429Srpaulo	int i;
1772178676Ssam
1773253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
1774253705Sadrian
1775220725Sbschmidt	/* Read regulatory domain (4 ASCII characters). */
1776198429Srpaulo	iwn_read_prom_data(sc, IWN4965_EEPROM_DOMAIN, sc->eeprom_domain, 4);
1777178676Ssam
1778220725Sbschmidt	/* Read the list of authorized channels (20MHz ones only). */
1779221636Sbschmidt	for (i = 0; i < 7; i++) {
1780201209Srpaulo		addr = iwn4965_regulatory_bands[i];
1781201209Srpaulo		iwn_read_eeprom_channels(sc, i, addr);
1782201209Srpaulo	}
1783198429Srpaulo
1784198429Srpaulo	/* Read maximum allowed TX power for 2GHz and 5GHz bands. */
1785198429Srpaulo	iwn_read_prom_data(sc, IWN4965_EEPROM_MAXPOW, &val, 2);
1786198429Srpaulo	sc->maxpwr2GHz = val & 0xff;
1787198429Srpaulo	sc->maxpwr5GHz = val >> 8;
1788198429Srpaulo	/* Check that EEPROM values are within valid range. */
1789198429Srpaulo	if (sc->maxpwr5GHz < 20 || sc->maxpwr5GHz > 50)
1790198429Srpaulo		sc->maxpwr5GHz = 38;
1791198429Srpaulo	if (sc->maxpwr2GHz < 20 || sc->maxpwr2GHz > 50)
1792198429Srpaulo		sc->maxpwr2GHz = 38;
1793198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "maxpwr 2GHz=%d 5GHz=%d\n",
1794198429Srpaulo	    sc->maxpwr2GHz, sc->maxpwr5GHz);
1795198429Srpaulo
1796198429Srpaulo	/* Read samples for each TX power group. */
1797198429Srpaulo	iwn_read_prom_data(sc, IWN4965_EEPROM_BANDS, sc->bands,
1798198429Srpaulo	    sizeof sc->bands);
1799198429Srpaulo
1800198429Srpaulo	/* Read voltage at which samples were taken. */
1801198429Srpaulo	iwn_read_prom_data(sc, IWN4965_EEPROM_VOLTAGE, &val, 2);
1802198429Srpaulo	sc->eeprom_voltage = (int16_t)le16toh(val);
1803198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "voltage=%d (in 0.3V)\n",
1804198429Srpaulo	    sc->eeprom_voltage);
1805198429Srpaulo
1806198429Srpaulo#ifdef IWN_DEBUG
1807198429Srpaulo	/* Print samples. */
1808201209Srpaulo	if (sc->sc_debug & IWN_DEBUG_ANY) {
1809198429Srpaulo		for (i = 0; i < IWN_NBANDS; i++)
1810198429Srpaulo			iwn4965_print_power_group(sc, i);
1811178676Ssam	}
1812198429Srpaulo#endif
1813253705Sadrian
1814253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s end\n", __func__);
1815178676Ssam}
1816178676Ssam
1817198429Srpaulo#ifdef IWN_DEBUG
1818206477Sbschmidtstatic void
1819198429Srpauloiwn4965_print_power_group(struct iwn_softc *sc, int i)
1820178676Ssam{
1821198429Srpaulo	struct iwn4965_eeprom_band *band = &sc->bands[i];
1822198429Srpaulo	struct iwn4965_eeprom_chan_samples *chans = band->chans;
1823198429Srpaulo	int j, c;
1824178676Ssam
1825198429Srpaulo	printf("===band %d===\n", i);
1826198429Srpaulo	printf("chan lo=%d, chan hi=%d\n", band->lo, band->hi);
1827198429Srpaulo	printf("chan1 num=%d\n", chans[0].num);
1828198429Srpaulo	for (c = 0; c < 2; c++) {
1829198429Srpaulo		for (j = 0; j < IWN_NSAMPLES; j++) {
1830198429Srpaulo			printf("chain %d, sample %d: temp=%d gain=%d "
1831198429Srpaulo			    "power=%d pa_det=%d\n", c, j,
1832198429Srpaulo			    chans[0].samples[c][j].temp,
1833198429Srpaulo			    chans[0].samples[c][j].gain,
1834198429Srpaulo			    chans[0].samples[c][j].power,
1835198429Srpaulo			    chans[0].samples[c][j].pa_det);
1836198429Srpaulo		}
1837198429Srpaulo	}
1838198429Srpaulo	printf("chan2 num=%d\n", chans[1].num);
1839198429Srpaulo	for (c = 0; c < 2; c++) {
1840198429Srpaulo		for (j = 0; j < IWN_NSAMPLES; j++) {
1841198429Srpaulo			printf("chain %d, sample %d: temp=%d gain=%d "
1842198429Srpaulo			    "power=%d pa_det=%d\n", c, j,
1843198429Srpaulo			    chans[1].samples[c][j].temp,
1844198429Srpaulo			    chans[1].samples[c][j].gain,
1845198429Srpaulo			    chans[1].samples[c][j].power,
1846198429Srpaulo			    chans[1].samples[c][j].pa_det);
1847198429Srpaulo		}
1848198429Srpaulo	}
1849178676Ssam}
1850198429Srpaulo#endif
1851178676Ssam
1852206477Sbschmidtstatic void
1853198429Srpauloiwn5000_read_eeprom(struct iwn_softc *sc)
1854178676Ssam{
1855206444Sbschmidt	struct iwn5000_eeprom_calib_hdr hdr;
1856220674Sbschmidt	int32_t volt;
1857220723Sbschmidt	uint32_t base, addr;
1858220723Sbschmidt	uint16_t val;
1859198429Srpaulo	int i;
1860178676Ssam
1861253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
1862253705Sadrian
1863220725Sbschmidt	/* Read regulatory domain (4 ASCII characters). */
1864198429Srpaulo	iwn_read_prom_data(sc, IWN5000_EEPROM_REG, &val, 2);
1865198429Srpaulo	base = le16toh(val);
1866198429Srpaulo	iwn_read_prom_data(sc, base + IWN5000_EEPROM_DOMAIN,
1867198429Srpaulo	    sc->eeprom_domain, 4);
1868178676Ssam
1869220725Sbschmidt	/* Read the list of authorized channels (20MHz ones only). */
1870221636Sbschmidt	for (i = 0; i < 7; i++) {
1871221635Sbschmidt		if (sc->hw_type >= IWN_HW_REV_TYPE_6000)
1872221635Sbschmidt			addr = base + iwn6000_regulatory_bands[i];
1873221635Sbschmidt		else
1874221635Sbschmidt			addr = base + iwn5000_regulatory_bands[i];
1875201209Srpaulo		iwn_read_eeprom_channels(sc, i, addr);
1876198429Srpaulo	}
1877178676Ssam
1878201209Srpaulo	/* Read enhanced TX power information for 6000 Series. */
1879201209Srpaulo	if (sc->hw_type >= IWN_HW_REV_TYPE_6000)
1880201209Srpaulo		iwn_read_eeprom_enhinfo(sc);
1881201209Srpaulo
1882198429Srpaulo	iwn_read_prom_data(sc, IWN5000_EEPROM_CAL, &val, 2);
1883198429Srpaulo	base = le16toh(val);
1884206444Sbschmidt	iwn_read_prom_data(sc, base, &hdr, sizeof hdr);
1885206444Sbschmidt	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
1886220726Sbschmidt	    "%s: calib version=%u pa type=%u voltage=%u\n", __func__,
1887220726Sbschmidt	    hdr.version, hdr.pa_type, le16toh(hdr.volt));
1888210108Sbschmidt	sc->calib_ver = hdr.version;
1889206444Sbschmidt
1890198429Srpaulo	if (sc->hw_type == IWN_HW_REV_TYPE_5150) {
1891201209Srpaulo		/* Compute temperature offset. */
1892198429Srpaulo		iwn_read_prom_data(sc, base + IWN5000_EEPROM_TEMP, &val, 2);
1893220674Sbschmidt		sc->eeprom_temp = le16toh(val);
1894198429Srpaulo		iwn_read_prom_data(sc, base + IWN5000_EEPROM_VOLT, &val, 2);
1895198429Srpaulo		volt = le16toh(val);
1896220674Sbschmidt		sc->temp_off = sc->eeprom_temp - (volt / -5);
1897201209Srpaulo		DPRINTF(sc, IWN_DEBUG_CALIBRATE, "temp=%d volt=%d offset=%dK\n",
1898220674Sbschmidt		    sc->eeprom_temp, volt, sc->temp_off);
1899220674Sbschmidt	} else {
1900220674Sbschmidt		/* Read crystal calibration. */
1901220674Sbschmidt		iwn_read_prom_data(sc, base + IWN5000_EEPROM_CRYSTAL,
1902220674Sbschmidt		    &sc->eeprom_crystal, sizeof (uint32_t));
1903220674Sbschmidt		DPRINTF(sc, IWN_DEBUG_CALIBRATE, "crystal calibration 0x%08x\n",
1904220674Sbschmidt		    le32toh(sc->eeprom_crystal));
1905178676Ssam	}
1906253705Sadrian
1907253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s end\n", __func__);
1908253705Sadrian
1909178676Ssam}
1910178676Ssam
1911201209Srpaulo/*
1912201209Srpaulo * Translate EEPROM flags to net80211.
1913201209Srpaulo */
1914201209Srpaulostatic uint32_t
1915201209Srpauloiwn_eeprom_channel_flags(struct iwn_eeprom_chan *channel)
1916201209Srpaulo{
1917201209Srpaulo	uint32_t nflags;
1918201209Srpaulo
1919201209Srpaulo	nflags = 0;
1920201209Srpaulo	if ((channel->flags & IWN_EEPROM_CHAN_ACTIVE) == 0)
1921201209Srpaulo		nflags |= IEEE80211_CHAN_PASSIVE;
1922201209Srpaulo	if ((channel->flags & IWN_EEPROM_CHAN_IBSS) == 0)
1923201209Srpaulo		nflags |= IEEE80211_CHAN_NOADHOC;
1924201209Srpaulo	if (channel->flags & IWN_EEPROM_CHAN_RADAR) {
1925201209Srpaulo		nflags |= IEEE80211_CHAN_DFS;
1926201209Srpaulo		/* XXX apparently IBSS may still be marked */
1927201209Srpaulo		nflags |= IEEE80211_CHAN_NOADHOC;
1928201209Srpaulo	}
1929201209Srpaulo
1930201209Srpaulo	return nflags;
1931201209Srpaulo}
1932201209Srpaulo
1933198429Srpaulostatic void
1934201209Srpauloiwn_read_eeprom_band(struct iwn_softc *sc, int n)
1935178676Ssam{
1936198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
1937198429Srpaulo	struct ieee80211com *ic = ifp->if_l2com;
1938201209Srpaulo	struct iwn_eeprom_chan *channels = sc->eeprom_channels[n];
1939201209Srpaulo	const struct iwn_chan_band *band = &iwn_bands[n];
1940198429Srpaulo	struct ieee80211_channel *c;
1941220687Sbschmidt	uint8_t chan;
1942220687Sbschmidt	int i, nflags;
1943178676Ssam
1944253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
1945253705Sadrian
1946198429Srpaulo	for (i = 0; i < band->nchan; i++) {
1947198429Srpaulo		if (!(channels[i].flags & IWN_EEPROM_CHAN_VALID)) {
1948198429Srpaulo			DPRINTF(sc, IWN_DEBUG_RESET,
1949198429Srpaulo			    "skip chan %d flags 0x%x maxpwr %d\n",
1950198429Srpaulo			    band->chan[i], channels[i].flags,
1951198429Srpaulo			    channels[i].maxpwr);
1952198429Srpaulo			continue;
1953198429Srpaulo		}
1954198429Srpaulo		chan = band->chan[i];
1955201209Srpaulo		nflags = iwn_eeprom_channel_flags(&channels[i]);
1956178676Ssam
1957198429Srpaulo		c = &ic->ic_channels[ic->ic_nchans++];
1958198429Srpaulo		c->ic_ieee = chan;
1959198429Srpaulo		c->ic_maxregpower = channels[i].maxpwr;
1960198429Srpaulo		c->ic_maxpower = 2*c->ic_maxregpower;
1961206445Sbschmidt
1962201209Srpaulo		if (n == 0) {	/* 2GHz band */
1963220726Sbschmidt			c->ic_freq = ieee80211_ieee2mhz(chan, IEEE80211_CHAN_G);
1964198429Srpaulo			/* G =>'s B is supported */
1965198429Srpaulo			c->ic_flags = IEEE80211_CHAN_B | nflags;
1966198429Srpaulo			c = &ic->ic_channels[ic->ic_nchans++];
1967198429Srpaulo			c[0] = c[-1];
1968198429Srpaulo			c->ic_flags = IEEE80211_CHAN_G | nflags;
1969198429Srpaulo		} else {	/* 5GHz band */
1970220726Sbschmidt			c->ic_freq = ieee80211_ieee2mhz(chan, IEEE80211_CHAN_A);
1971198429Srpaulo			c->ic_flags = IEEE80211_CHAN_A | nflags;
1972178676Ssam		}
1973220723Sbschmidt
1974220723Sbschmidt		/* Save maximum allowed TX power for this channel. */
1975220723Sbschmidt		sc->maxpwr[chan] = channels[i].maxpwr;
1976220723Sbschmidt
1977220723Sbschmidt		DPRINTF(sc, IWN_DEBUG_RESET,
1978220726Sbschmidt		    "add chan %d flags 0x%x maxpwr %d\n", chan,
1979220726Sbschmidt		    channels[i].flags, channels[i].maxpwr);
1980220723Sbschmidt
1981221636Sbschmidt		if (sc->sc_flags & IWN_FLAG_HAS_11N) {
1982221636Sbschmidt			/* add HT20, HT40 added separately */
1983221636Sbschmidt			c = &ic->ic_channels[ic->ic_nchans++];
1984221636Sbschmidt			c[0] = c[-1];
1985221636Sbschmidt			c->ic_flags |= IEEE80211_CHAN_HT20;
1986221636Sbschmidt		}
1987178676Ssam	}
1988253705Sadrian
1989253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s end\n", __func__);
1990253705Sadrian
1991178676Ssam}
1992178676Ssam
1993198429Srpaulostatic void
1994201209Srpauloiwn_read_eeprom_ht40(struct iwn_softc *sc, int n)
1995178676Ssam{
1996198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
1997198429Srpaulo	struct ieee80211com *ic = ifp->if_l2com;
1998201209Srpaulo	struct iwn_eeprom_chan *channels = sc->eeprom_channels[n];
1999201209Srpaulo	const struct iwn_chan_band *band = &iwn_bands[n];
2000198429Srpaulo	struct ieee80211_channel *c, *cent, *extc;
2001221636Sbschmidt	uint8_t chan;
2002221636Sbschmidt	int i, nflags;
2003178676Ssam
2004253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s start\n", __func__);
2005253705Sadrian
2006253705Sadrian	if (!(sc->sc_flags & IWN_FLAG_HAS_11N)) {
2007253705Sadrian		DPRINTF(sc, IWN_DEBUG_TRACE, "->%s end no 11n\n", __func__);
2008221636Sbschmidt		return;
2009253705Sadrian	}
2010221636Sbschmidt
2011198429Srpaulo	for (i = 0; i < band->nchan; i++) {
2012221636Sbschmidt		if (!(channels[i].flags & IWN_EEPROM_CHAN_VALID)) {
2013198429Srpaulo			DPRINTF(sc, IWN_DEBUG_RESET,
2014198429Srpaulo			    "skip chan %d flags 0x%x maxpwr %d\n",
2015198429Srpaulo			    band->chan[i], channels[i].flags,
2016198429Srpaulo			    channels[i].maxpwr);
2017198429Srpaulo			continue;
2018198429Srpaulo		}
2019221636Sbschmidt		chan = band->chan[i];
2020221636Sbschmidt		nflags = iwn_eeprom_channel_flags(&channels[i]);
2021221636Sbschmidt
2022198429Srpaulo		/*
2023198429Srpaulo		 * Each entry defines an HT40 channel pair; find the
2024198429Srpaulo		 * center channel, then the extension channel above.
2025198429Srpaulo		 */
2026221636Sbschmidt		cent = ieee80211_find_channel_byieee(ic, chan,
2027221636Sbschmidt		    (n == 5 ? IEEE80211_CHAN_G : IEEE80211_CHAN_A));
2028198429Srpaulo		if (cent == NULL) {	/* XXX shouldn't happen */
2029198429Srpaulo			device_printf(sc->sc_dev,
2030221636Sbschmidt			    "%s: no entry for channel %d\n", __func__, chan);
2031198429Srpaulo			continue;
2032198429Srpaulo		}
2033198429Srpaulo		extc = ieee80211_find_channel(ic, cent->ic_freq+20,
2034221636Sbschmidt		    (n == 5 ? IEEE80211_CHAN_G : IEEE80211_CHAN_A));
2035198429Srpaulo		if (extc == NULL) {
2036198429Srpaulo			DPRINTF(sc, IWN_DEBUG_RESET,
2037221636Sbschmidt			    "%s: skip chan %d, extension channel not found\n",
2038221636Sbschmidt			    __func__, chan);
2039198429Srpaulo			continue;
2040198429Srpaulo		}
2041178676Ssam
2042198429Srpaulo		DPRINTF(sc, IWN_DEBUG_RESET,
2043198429Srpaulo		    "add ht40 chan %d flags 0x%x maxpwr %d\n",
2044221636Sbschmidt		    chan, channels[i].flags, channels[i].maxpwr);
2045178676Ssam
2046198429Srpaulo		c = &ic->ic_channels[ic->ic_nchans++];
2047198429Srpaulo		c[0] = cent[0];
2048198429Srpaulo		c->ic_extieee = extc->ic_ieee;
2049198429Srpaulo		c->ic_flags &= ~IEEE80211_CHAN_HT;
2050221636Sbschmidt		c->ic_flags |= IEEE80211_CHAN_HT40U | nflags;
2051198429Srpaulo		c = &ic->ic_channels[ic->ic_nchans++];
2052198429Srpaulo		c[0] = extc[0];
2053198429Srpaulo		c->ic_extieee = cent->ic_ieee;
2054198429Srpaulo		c->ic_flags &= ~IEEE80211_CHAN_HT;
2055221636Sbschmidt		c->ic_flags |= IEEE80211_CHAN_HT40D | nflags;
2056178676Ssam	}
2057253705Sadrian
2058253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s end\n", __func__);
2059253705Sadrian
2060198429Srpaulo}
2061178676Ssam
2062198429Srpaulostatic void
2063201209Srpauloiwn_read_eeprom_channels(struct iwn_softc *sc, int n, uint32_t addr)
2064198429Srpaulo{
2065198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
2066198429Srpaulo	struct ieee80211com *ic = ifp->if_l2com;
2067178676Ssam
2068201209Srpaulo	iwn_read_prom_data(sc, addr, &sc->eeprom_channels[n],
2069201209Srpaulo	    iwn_bands[n].nchan * sizeof (struct iwn_eeprom_chan));
2070201209Srpaulo
2071198429Srpaulo	if (n < 5)
2072201209Srpaulo		iwn_read_eeprom_band(sc, n);
2073198429Srpaulo	else
2074201209Srpaulo		iwn_read_eeprom_ht40(sc, n);
2075198429Srpaulo	ieee80211_sort_channels(ic->ic_channels, ic->ic_nchans);
2076178676Ssam}
2077178676Ssam
2078220723Sbschmidtstatic struct iwn_eeprom_chan *
2079220723Sbschmidtiwn_find_eeprom_channel(struct iwn_softc *sc, struct ieee80211_channel *c)
2080220723Sbschmidt{
2081221636Sbschmidt	int band, chan, i, j;
2082220723Sbschmidt
2083221636Sbschmidt	if (IEEE80211_IS_CHAN_HT40(c)) {
2084221636Sbschmidt		band = IEEE80211_IS_CHAN_5GHZ(c) ? 6 : 5;
2085221636Sbschmidt		if (IEEE80211_IS_CHAN_HT40D(c))
2086221636Sbschmidt			chan = c->ic_extieee;
2087221636Sbschmidt		else
2088221636Sbschmidt			chan = c->ic_ieee;
2089221636Sbschmidt		for (i = 0; i < iwn_bands[band].nchan; i++) {
2090221636Sbschmidt			if (iwn_bands[band].chan[i] == chan)
2091221636Sbschmidt				return &sc->eeprom_channels[band][i];
2092220723Sbschmidt		}
2093221636Sbschmidt	} else {
2094221636Sbschmidt		for (j = 0; j < 5; j++) {
2095221636Sbschmidt			for (i = 0; i < iwn_bands[j].nchan; i++) {
2096221636Sbschmidt				if (iwn_bands[j].chan[i] == c->ic_ieee)
2097221636Sbschmidt					return &sc->eeprom_channels[j][i];
2098221636Sbschmidt			}
2099221636Sbschmidt		}
2100220723Sbschmidt	}
2101220723Sbschmidt	return NULL;
2102220723Sbschmidt}
2103220723Sbschmidt
2104220723Sbschmidt/*
2105220723Sbschmidt * Enforce flags read from EEPROM.
2106220723Sbschmidt */
2107220723Sbschmidtstatic int
2108220723Sbschmidtiwn_setregdomain(struct ieee80211com *ic, struct ieee80211_regdomain *rd,
2109220723Sbschmidt    int nchan, struct ieee80211_channel chans[])
2110220723Sbschmidt{
2111220723Sbschmidt	struct iwn_softc *sc = ic->ic_ifp->if_softc;
2112220723Sbschmidt	int i;
2113220723Sbschmidt
2114220723Sbschmidt	for (i = 0; i < nchan; i++) {
2115220723Sbschmidt		struct ieee80211_channel *c = &chans[i];
2116220723Sbschmidt		struct iwn_eeprom_chan *channel;
2117220723Sbschmidt
2118220723Sbschmidt		channel = iwn_find_eeprom_channel(sc, c);
2119220723Sbschmidt		if (channel == NULL) {
2120220723Sbschmidt			if_printf(ic->ic_ifp,
2121220723Sbschmidt			    "%s: invalid channel %u freq %u/0x%x\n",
2122220723Sbschmidt			    __func__, c->ic_ieee, c->ic_freq, c->ic_flags);
2123220723Sbschmidt			return EINVAL;
2124220723Sbschmidt		}
2125220723Sbschmidt		c->ic_flags |= iwn_eeprom_channel_flags(channel);
2126220723Sbschmidt	}
2127220723Sbschmidt
2128220723Sbschmidt	return 0;
2129220723Sbschmidt}
2130220723Sbschmidt
2131206477Sbschmidtstatic void
2132201209Srpauloiwn_read_eeprom_enhinfo(struct iwn_softc *sc)
2133201209Srpaulo{
2134201209Srpaulo	struct iwn_eeprom_enhinfo enhinfo[35];
2135221637Sbschmidt	struct ifnet *ifp = sc->sc_ifp;
2136221637Sbschmidt	struct ieee80211com *ic = ifp->if_l2com;
2137221637Sbschmidt	struct ieee80211_channel *c;
2138201209Srpaulo	uint16_t val, base;
2139201209Srpaulo	int8_t maxpwr;
2140221637Sbschmidt	uint8_t flags;
2141221637Sbschmidt	int i, j;
2142201209Srpaulo
2143253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
2144253705Sadrian
2145201209Srpaulo	iwn_read_prom_data(sc, IWN5000_EEPROM_REG, &val, 2);
2146201209Srpaulo	base = le16toh(val);
2147201209Srpaulo	iwn_read_prom_data(sc, base + IWN6000_EEPROM_ENHINFO,
2148201209Srpaulo	    enhinfo, sizeof enhinfo);
2149201209Srpaulo
2150201209Srpaulo	for (i = 0; i < nitems(enhinfo); i++) {
2151221637Sbschmidt		flags = enhinfo[i].flags;
2152221637Sbschmidt		if (!(flags & IWN_ENHINFO_VALID))
2153201209Srpaulo			continue;	/* Skip invalid entries. */
2154201209Srpaulo
2155201209Srpaulo		maxpwr = 0;
2156201209Srpaulo		if (sc->txchainmask & IWN_ANT_A)
2157201209Srpaulo			maxpwr = MAX(maxpwr, enhinfo[i].chain[0]);
2158201209Srpaulo		if (sc->txchainmask & IWN_ANT_B)
2159201209Srpaulo			maxpwr = MAX(maxpwr, enhinfo[i].chain[1]);
2160201209Srpaulo		if (sc->txchainmask & IWN_ANT_C)
2161201209Srpaulo			maxpwr = MAX(maxpwr, enhinfo[i].chain[2]);
2162201209Srpaulo		if (sc->ntxchains == 2)
2163201209Srpaulo			maxpwr = MAX(maxpwr, enhinfo[i].mimo2);
2164201209Srpaulo		else if (sc->ntxchains == 3)
2165201209Srpaulo			maxpwr = MAX(maxpwr, enhinfo[i].mimo3);
2166201209Srpaulo
2167221637Sbschmidt		for (j = 0; j < ic->ic_nchans; j++) {
2168221637Sbschmidt			c = &ic->ic_channels[j];
2169221637Sbschmidt			if ((flags & IWN_ENHINFO_5GHZ)) {
2170221637Sbschmidt				if (!IEEE80211_IS_CHAN_A(c))
2171221637Sbschmidt					continue;
2172221637Sbschmidt			} else if ((flags & IWN_ENHINFO_OFDM)) {
2173221637Sbschmidt				if (!IEEE80211_IS_CHAN_G(c))
2174221637Sbschmidt					continue;
2175221637Sbschmidt			} else if (!IEEE80211_IS_CHAN_B(c))
2176221637Sbschmidt				continue;
2177221637Sbschmidt			if ((flags & IWN_ENHINFO_HT40)) {
2178221637Sbschmidt				if (!IEEE80211_IS_CHAN_HT40(c))
2179221637Sbschmidt					continue;
2180221637Sbschmidt			} else {
2181221637Sbschmidt				if (IEEE80211_IS_CHAN_HT40(c))
2182221637Sbschmidt					continue;
2183221637Sbschmidt			}
2184221637Sbschmidt			if (enhinfo[i].chan != 0 &&
2185221637Sbschmidt			    enhinfo[i].chan != c->ic_ieee)
2186221637Sbschmidt				continue;
2187221637Sbschmidt
2188221637Sbschmidt			DPRINTF(sc, IWN_DEBUG_RESET,
2189221637Sbschmidt			    "channel %d(%x), maxpwr %d\n", c->ic_ieee,
2190221637Sbschmidt			    c->ic_flags, maxpwr / 2);
2191221637Sbschmidt			c->ic_maxregpower = maxpwr / 2;
2192221637Sbschmidt			c->ic_maxpower = maxpwr;
2193221637Sbschmidt		}
2194201209Srpaulo	}
2195253705Sadrian
2196253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s end\n", __func__);
2197253705Sadrian
2198201209Srpaulo}
2199201209Srpaulo
2200206477Sbschmidtstatic struct ieee80211_node *
2201198429Srpauloiwn_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN])
2202178676Ssam{
2203198429Srpaulo	return malloc(sizeof (struct iwn_node), M_80211_NODE,M_NOWAIT | M_ZERO);
2204198429Srpaulo}
2205178676Ssam
2206221648Sbschmidtstatic __inline int
2207221648Sbschmidtrate2plcp(int rate)
2208221648Sbschmidt{
2209221648Sbschmidt	switch (rate & 0xff) {
2210221648Sbschmidt	case 12:	return 0xd;
2211221648Sbschmidt	case 18:	return 0xf;
2212221648Sbschmidt	case 24:	return 0x5;
2213221648Sbschmidt	case 36:	return 0x7;
2214221648Sbschmidt	case 48:	return 0x9;
2215221648Sbschmidt	case 72:	return 0xb;
2216221648Sbschmidt	case 96:	return 0x1;
2217221648Sbschmidt	case 108:	return 0x3;
2218221648Sbschmidt	case 2:		return 10;
2219221648Sbschmidt	case 4:		return 20;
2220221648Sbschmidt	case 11:	return 55;
2221221648Sbschmidt	case 22:	return 110;
2222221648Sbschmidt	}
2223221648Sbschmidt	return 0;
2224221648Sbschmidt}
2225221648Sbschmidt
2226252727Sadrian/*
2227252727Sadrian * Calculate the required PLCP value from the given rate,
2228252727Sadrian * to the given node.
2229252727Sadrian *
2230252727Sadrian * This will take the node configuration (eg 11n, rate table
2231252727Sadrian * setup, etc) into consideration.
2232252727Sadrian */
2233252727Sadrianstatic uint32_t
2234252727Sadrianiwn_rate_to_plcp(struct iwn_softc *sc, struct ieee80211_node *ni,
2235252727Sadrian    uint8_t rate)
2236220715Sbschmidt{
2237222933Sbschmidt#define	RV(v)	((v) & IEEE80211_RATE_VAL)
2238221648Sbschmidt	struct ieee80211com *ic = ni->ni_ic;
2239221649Sbschmidt	uint8_t txant1, txant2;
2240252727Sadrian	uint32_t plcp = 0;
2241252727Sadrian	int ridx;
2242220715Sbschmidt
2243221648Sbschmidt	/* Use the first valid TX antenna. */
2244221649Sbschmidt	txant1 = IWN_LSB(sc->txchainmask);
2245221649Sbschmidt	txant2 = IWN_LSB(sc->txchainmask & ~txant1);
2246221648Sbschmidt
2247252727Sadrian	/*
2248252727Sadrian	 * If it's an MCS rate, let's set the plcp correctly
2249252727Sadrian	 * and set the relevant flags based on the node config.
2250252727Sadrian	 */
2251221649Sbschmidt	if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) {
2252252727Sadrian		/*
2253252727Sadrian		 * Set the initial PLCP value to be between 0->31 for
2254252727Sadrian		 * MCS 0 -> MCS 31, then set the "I'm an MCS rate!"
2255252727Sadrian		 * flag.
2256252727Sadrian		 */
2257252727Sadrian		plcp = RV(rate) | IWN_RFLAG_MCS;
2258252727Sadrian
2259252727Sadrian		/*
2260252727Sadrian		 * XXX the following should only occur if both
2261252727Sadrian		 * the local configuration _and_ the remote node
2262252727Sadrian		 * advertise these capabilities.  Thus this code
2263252727Sadrian		 * may need fixing!
2264252727Sadrian		 */
2265252727Sadrian
2266252727Sadrian		/*
2267252727Sadrian		 * Set the channel width and guard interval.
2268252727Sadrian		 */
2269252727Sadrian		if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) {
2270252727Sadrian			plcp |= IWN_RFLAG_HT40;
2271252727Sadrian			if (ni->ni_htcap & IEEE80211_HTCAP_SHORTGI40)
2272221649Sbschmidt				plcp |= IWN_RFLAG_SGI;
2273252727Sadrian		} else if (ni->ni_htcap & IEEE80211_HTCAP_SHORTGI20) {
2274252727Sadrian			plcp |= IWN_RFLAG_SGI;
2275221649Sbschmidt		}
2276252727Sadrian
2277252727Sadrian		/*
2278252727Sadrian		 * If it's a two stream rate, enable TX on both
2279252727Sadrian		 * antennas.
2280252727Sadrian		 *
2281252727Sadrian		 * XXX three stream rates?
2282252727Sadrian		 */
2283252727Sadrian		if (rate > 0x87)
2284252727Sadrian			plcp |= IWN_RFLAG_ANT(txant1 | txant2);
2285252727Sadrian		else
2286252727Sadrian			plcp |= IWN_RFLAG_ANT(txant1);
2287221649Sbschmidt	} else {
2288252727Sadrian		/*
2289252727Sadrian		 * Set the initial PLCP - fine for both
2290252727Sadrian		 * OFDM and CCK rates.
2291252727Sadrian		 */
2292252727Sadrian		plcp = rate2plcp(rate);
2293252727Sadrian
2294252727Sadrian		/* Set CCK flag if it's CCK */
2295252727Sadrian
2296252727Sadrian		/* XXX It would be nice to have a method
2297252727Sadrian		 * to map the ridx -> phy table entry
2298252727Sadrian		 * so we could just query that, rather than
2299252727Sadrian		 * this hack to check against IWN_RIDX_OFDM6.
2300252727Sadrian		 */
2301252727Sadrian		ridx = ieee80211_legacy_rate_lookup(ic->ic_rt,
2302252727Sadrian		    rate & IEEE80211_RATE_VAL);
2303252727Sadrian		if (ridx < IWN_RIDX_OFDM6 &&
2304252727Sadrian		    IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
2305252727Sadrian			plcp |= IWN_RFLAG_CCK;
2306252727Sadrian
2307252727Sadrian		/* Set antenna configuration */
2308252727Sadrian		plcp |= IWN_RFLAG_ANT(txant1);
2309220715Sbschmidt	}
2310252727Sadrian
2311252727Sadrian	DPRINTF(sc, IWN_DEBUG_TXRATE, "%s: rate=0x%02x, plcp=0x%08x\n",
2312252727Sadrian	    __func__,
2313252727Sadrian	    rate,
2314252727Sadrian	    plcp);
2315252727Sadrian
2316252727Sadrian	return (htole32(plcp));
2317222933Sbschmidt#undef	RV
2318220715Sbschmidt}
2319220715Sbschmidt
2320252727Sadrianstatic void
2321252727Sadrianiwn_newassoc(struct ieee80211_node *ni, int isnew)
2322252727Sadrian{
2323252727Sadrian	/* Doesn't do anything at the moment */
2324252727Sadrian}
2325252727Sadrian
2326206477Sbschmidtstatic int
2327198429Srpauloiwn_media_change(struct ifnet *ifp)
2328178676Ssam{
2329220726Sbschmidt	int error;
2330220726Sbschmidt
2331220726Sbschmidt	error = ieee80211_media_change(ifp);
2332198429Srpaulo	/* NB: only the fixed rate can change and that doesn't need a reset */
2333198429Srpaulo	return (error == ENETRESET ? 0 : error);
2334198429Srpaulo}
2335178676Ssam
2336206477Sbschmidtstatic int
2337198429Srpauloiwn_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
2338198429Srpaulo{
2339198429Srpaulo	struct iwn_vap *ivp = IWN_VAP(vap);
2340198429Srpaulo	struct ieee80211com *ic = vap->iv_ic;
2341198429Srpaulo	struct iwn_softc *sc = ic->ic_ifp->if_softc;
2342220688Sbschmidt	int error = 0;
2343178676Ssam
2344253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
2345253705Sadrian
2346198429Srpaulo	DPRINTF(sc, IWN_DEBUG_STATE, "%s: %s -> %s\n", __func__,
2347220726Sbschmidt	    ieee80211_state_name[vap->iv_state], ieee80211_state_name[nstate]);
2348178676Ssam
2349198429Srpaulo	IEEE80211_UNLOCK(ic);
2350198429Srpaulo	IWN_LOCK(sc);
2351220667Sbschmidt	callout_stop(&sc->calib_to);
2352178676Ssam
2353254204Sadrian	sc->rxon = &sc->rx_on[IWN_RXON_BSS_CTX];
2354254204Sadrian
2355210114Sbschmidt	switch (nstate) {
2356210114Sbschmidt	case IEEE80211_S_ASSOC:
2357210114Sbschmidt		if (vap->iv_state != IEEE80211_S_RUN)
2358210114Sbschmidt			break;
2359210114Sbschmidt		/* FALLTHROUGH */
2360210114Sbschmidt	case IEEE80211_S_AUTH:
2361210114Sbschmidt		if (vap->iv_state == IEEE80211_S_AUTH)
2362210114Sbschmidt			break;
2363210114Sbschmidt
2364210114Sbschmidt		/*
2365210114Sbschmidt		 * !AUTH -> AUTH transition requires state reset to handle
2366210114Sbschmidt		 * reassociations correctly.
2367210114Sbschmidt		 */
2368254204Sadrian		sc->rxon->associd = 0;
2369254204Sadrian		sc->rxon->filter &= ~htole32(IWN_FILTER_BSS);
2370220667Sbschmidt		sc->calib.state = IWN_CALIB_STATE_INIT;
2371220667Sbschmidt
2372220688Sbschmidt		if ((error = iwn_auth(sc, vap)) != 0) {
2373220688Sbschmidt			device_printf(sc->sc_dev,
2374220688Sbschmidt			    "%s: could not move to auth state\n", __func__);
2375220688Sbschmidt		}
2376210114Sbschmidt		break;
2377210114Sbschmidt
2378210114Sbschmidt	case IEEE80211_S_RUN:
2379198429Srpaulo		/*
2380210114Sbschmidt		 * RUN -> RUN transition; Just restart the timers.
2381210114Sbschmidt		 */
2382220667Sbschmidt		if (vap->iv_state == IEEE80211_S_RUN) {
2383220667Sbschmidt			sc->calib_cnt = 0;
2384210114Sbschmidt			break;
2385210114Sbschmidt		}
2386210114Sbschmidt
2387210114Sbschmidt		/*
2388198429Srpaulo		 * !RUN -> RUN requires setting the association id
2389198429Srpaulo		 * which is done with a firmware cmd.  We also defer
2390198429Srpaulo		 * starting the timers until that work is done.
2391198429Srpaulo		 */
2392220688Sbschmidt		if ((error = iwn_run(sc, vap)) != 0) {
2393220688Sbschmidt			device_printf(sc->sc_dev,
2394220688Sbschmidt			    "%s: could not move to run state\n", __func__);
2395220688Sbschmidt		}
2396210114Sbschmidt		break;
2397210114Sbschmidt
2398220667Sbschmidt	case IEEE80211_S_INIT:
2399220667Sbschmidt		sc->calib.state = IWN_CALIB_STATE_INIT;
2400220667Sbschmidt		break;
2401220667Sbschmidt
2402210114Sbschmidt	default:
2403210114Sbschmidt		break;
2404178676Ssam	}
2405198429Srpaulo	IWN_UNLOCK(sc);
2406198429Srpaulo	IEEE80211_LOCK(ic);
2407253705Sadrian	if (error != 0){
2408253705Sadrian		DPRINTF(sc, IWN_DEBUG_TRACE, "->%s end in error\n", __func__);
2409220688Sbschmidt		return error;
2410253705Sadrian	}
2411253705Sadrian
2412253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
2413253705Sadrian
2414198429Srpaulo	return ivp->iv_newstate(vap, nstate, arg);
2415178676Ssam}
2416178676Ssam
2417220667Sbschmidtstatic void
2418220667Sbschmidtiwn_calib_timeout(void *arg)
2419220667Sbschmidt{
2420220667Sbschmidt	struct iwn_softc *sc = arg;
2421220667Sbschmidt
2422220667Sbschmidt	IWN_LOCK_ASSERT(sc);
2423220667Sbschmidt
2424220667Sbschmidt	/* Force automatic TX power calibration every 60 secs. */
2425220667Sbschmidt	if (++sc->calib_cnt >= 120) {
2426220667Sbschmidt		uint32_t flags = 0;
2427220667Sbschmidt
2428220667Sbschmidt		DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s\n",
2429220667Sbschmidt		    "sending request for statistics");
2430220667Sbschmidt		(void)iwn_cmd(sc, IWN_CMD_GET_STATISTICS, &flags,
2431220667Sbschmidt		    sizeof flags, 1);
2432220667Sbschmidt		sc->calib_cnt = 0;
2433220667Sbschmidt	}
2434220667Sbschmidt	callout_reset(&sc->calib_to, msecs_to_ticks(500), iwn_calib_timeout,
2435220667Sbschmidt	    sc);
2436220667Sbschmidt}
2437220667Sbschmidt
2438198429Srpaulo/*
2439198429Srpaulo * Process an RX_PHY firmware notification.  This is usually immediately
2440198429Srpaulo * followed by an MPDU_RX_DONE notification.
2441198429Srpaulo */
2442206477Sbschmidtstatic void
2443198429Srpauloiwn_rx_phy(struct iwn_softc *sc, struct iwn_rx_desc *desc,
2444198429Srpaulo    struct iwn_rx_data *data)
2445178676Ssam{
2446198429Srpaulo	struct iwn_rx_stat *stat = (struct iwn_rx_stat *)(desc + 1);
2447198429Srpaulo
2448198429Srpaulo	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: received PHY stats\n", __func__);
2449202986Srpaulo	bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD);
2450198429Srpaulo
2451198429Srpaulo	/* Save RX statistics, they will be used on MPDU_RX_DONE. */
2452198429Srpaulo	memcpy(&sc->last_rx_stat, stat, sizeof (*stat));
2453198429Srpaulo	sc->last_rx_valid = 1;
2454178676Ssam}
2455178676Ssam
2456198429Srpaulo/*
2457198429Srpaulo * Process an RX_DONE (4965AGN only) or MPDU_RX_DONE firmware notification.
2458198429Srpaulo * Each MPDU_RX_DONE notification must be preceded by an RX_PHY one.
2459198429Srpaulo */
2460206477Sbschmidtstatic void
2461198429Srpauloiwn_rx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc,
2462178676Ssam    struct iwn_rx_data *data)
2463178676Ssam{
2464220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
2465178676Ssam	struct ifnet *ifp = sc->sc_ifp;
2466178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
2467178676Ssam	struct iwn_rx_ring *ring = &sc->rxq;
2468178676Ssam	struct ieee80211_frame *wh;
2469178676Ssam	struct ieee80211_node *ni;
2470198429Srpaulo	struct mbuf *m, *m1;
2471178676Ssam	struct iwn_rx_stat *stat;
2472178676Ssam	caddr_t head;
2473178676Ssam	bus_addr_t paddr;
2474198429Srpaulo	uint32_t flags;
2475198429Srpaulo	int error, len, rssi, nf;
2476178676Ssam
2477253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
2478253705Sadrian
2479198429Srpaulo	if (desc->type == IWN_MPDU_RX_DONE) {
2480198429Srpaulo		/* Check for prior RX_PHY notification. */
2481178676Ssam		if (!sc->last_rx_valid) {
2482178676Ssam			DPRINTF(sc, IWN_DEBUG_ANY,
2483201209Srpaulo			    "%s: missing RX_PHY\n", __func__);
2484178676Ssam			return;
2485178676Ssam		}
2486178676Ssam		stat = &sc->last_rx_stat;
2487178676Ssam	} else
2488178676Ssam		stat = (struct iwn_rx_stat *)(desc + 1);
2489178676Ssam
2490201209Srpaulo	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD);
2491198439Srpaulo
2492178676Ssam	if (stat->cfg_phy_len > IWN_STAT_MAXLEN) {
2493178676Ssam		device_printf(sc->sc_dev,
2494220724Sbschmidt		    "%s: invalid RX statistic header, len %d\n", __func__,
2495220724Sbschmidt		    stat->cfg_phy_len);
2496178676Ssam		return;
2497178676Ssam	}
2498198429Srpaulo	if (desc->type == IWN_MPDU_RX_DONE) {
2499198429Srpaulo		struct iwn_rx_mpdu *mpdu = (struct iwn_rx_mpdu *)(desc + 1);
2500198429Srpaulo		head = (caddr_t)(mpdu + 1);
2501198429Srpaulo		len = le16toh(mpdu->len);
2502178676Ssam	} else {
2503178676Ssam		head = (caddr_t)(stat + 1) + stat->cfg_phy_len;
2504178676Ssam		len = le16toh(stat->len);
2505178676Ssam	}
2506178676Ssam
2507198429Srpaulo	flags = le32toh(*(uint32_t *)(head + len));
2508198429Srpaulo
2509198429Srpaulo	/* Discard frames with a bad FCS early. */
2510198429Srpaulo	if ((flags & IWN_RX_NOERROR) != IWN_RX_NOERROR) {
2511220724Sbschmidt		DPRINTF(sc, IWN_DEBUG_RECV, "%s: RX flags error %x\n",
2512198429Srpaulo		    __func__, flags);
2513178676Ssam		ifp->if_ierrors++;
2514178676Ssam		return;
2515178676Ssam	}
2516198429Srpaulo	/* Discard frames that are too short. */
2517198429Srpaulo	if (len < sizeof (*wh)) {
2518178676Ssam		DPRINTF(sc, IWN_DEBUG_RECV, "%s: frame too short: %d\n",
2519178676Ssam		    __func__, len);
2520178676Ssam		ifp->if_ierrors++;
2521178676Ssam		return;
2522178676Ssam	}
2523178676Ssam
2524243857Sglebius	m1 = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, IWN_RBUF_SIZE);
2525198429Srpaulo	if (m1 == NULL) {
2526178676Ssam		DPRINTF(sc, IWN_DEBUG_ANY, "%s: no mbuf to restock ring\n",
2527178676Ssam		    __func__);
2528178676Ssam		ifp->if_ierrors++;
2529178676Ssam		return;
2530178676Ssam	}
2531201209Srpaulo	bus_dmamap_unload(ring->data_dmat, data->map);
2532201209Srpaulo
2533220692Sbschmidt	error = bus_dmamap_load(ring->data_dmat, data->map, mtod(m1, void *),
2534220692Sbschmidt	    IWN_RBUF_SIZE, iwn_dma_map_addr, &paddr, BUS_DMA_NOWAIT);
2535178676Ssam	if (error != 0 && error != EFBIG) {
2536178676Ssam		device_printf(sc->sc_dev,
2537178676Ssam		    "%s: bus_dmamap_load failed, error %d\n", __func__, error);
2538198429Srpaulo		m_freem(m1);
2539220693Sbschmidt
2540220693Sbschmidt		/* Try to reload the old mbuf. */
2541220693Sbschmidt		error = bus_dmamap_load(ring->data_dmat, data->map,
2542220693Sbschmidt		    mtod(data->m, void *), IWN_RBUF_SIZE, iwn_dma_map_addr,
2543220693Sbschmidt		    &paddr, BUS_DMA_NOWAIT);
2544220693Sbschmidt		if (error != 0 && error != EFBIG) {
2545220693Sbschmidt			panic("%s: could not load old RX mbuf", __func__);
2546220693Sbschmidt		}
2547220693Sbschmidt		/* Physical address may have changed. */
2548220693Sbschmidt		ring->desc[ring->cur] = htole32(paddr >> 8);
2549220693Sbschmidt		bus_dmamap_sync(ring->data_dmat, ring->desc_dma.map,
2550220693Sbschmidt		    BUS_DMASYNC_PREWRITE);
2551178676Ssam		ifp->if_ierrors++;
2552178676Ssam		return;
2553178676Ssam	}
2554178676Ssam
2555178676Ssam	m = data->m;
2556198429Srpaulo	data->m = m1;
2557198429Srpaulo	/* Update RX descriptor. */
2558198429Srpaulo	ring->desc[ring->cur] = htole32(paddr >> 8);
2559201209Srpaulo	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
2560201209Srpaulo	    BUS_DMASYNC_PREWRITE);
2561198429Srpaulo
2562198429Srpaulo	/* Finalize mbuf. */
2563178676Ssam	m->m_pkthdr.rcvif = ifp;
2564178676Ssam	m->m_data = head;
2565178676Ssam	m->m_pkthdr.len = m->m_len = len;
2566178676Ssam
2567198429Srpaulo	/* Grab a reference to the source node. */
2568178676Ssam	wh = mtod(m, struct ieee80211_frame *);
2569178676Ssam	ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh);
2570178676Ssam	nf = (ni != NULL && ni->ni_vap->iv_state == IEEE80211_S_RUN &&
2571178676Ssam	    (ic->ic_flags & IEEE80211_F_SCAN) == 0) ? sc->noise : -95;
2572178676Ssam
2573220728Sbschmidt	rssi = ops->get_rssi(sc, stat);
2574220689Sbschmidt
2575192468Ssam	if (ieee80211_radiotap_active(ic)) {
2576178676Ssam		struct iwn_rx_radiotap_header *tap = &sc->sc_rxtap;
2577178676Ssam
2578178676Ssam		tap->wr_flags = 0;
2579201209Srpaulo		if (stat->flags & htole16(IWN_STAT_FLAG_SHPREAMBLE))
2580192468Ssam			tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
2581220723Sbschmidt		tap->wr_dbm_antsignal = (int8_t)rssi;
2582220723Sbschmidt		tap->wr_dbm_antnoise = (int8_t)nf;
2583220723Sbschmidt		tap->wr_tsft = stat->tstamp;
2584201209Srpaulo		switch (stat->rate) {
2585201209Srpaulo		/* CCK rates. */
2586201209Srpaulo		case  10: tap->wr_rate =   2; break;
2587201209Srpaulo		case  20: tap->wr_rate =   4; break;
2588201209Srpaulo		case  55: tap->wr_rate =  11; break;
2589201209Srpaulo		case 110: tap->wr_rate =  22; break;
2590201209Srpaulo		/* OFDM rates. */
2591201209Srpaulo		case 0xd: tap->wr_rate =  12; break;
2592201209Srpaulo		case 0xf: tap->wr_rate =  18; break;
2593201209Srpaulo		case 0x5: tap->wr_rate =  24; break;
2594201209Srpaulo		case 0x7: tap->wr_rate =  36; break;
2595201209Srpaulo		case 0x9: tap->wr_rate =  48; break;
2596201209Srpaulo		case 0xb: tap->wr_rate =  72; break;
2597201209Srpaulo		case 0x1: tap->wr_rate =  96; break;
2598201209Srpaulo		case 0x3: tap->wr_rate = 108; break;
2599201209Srpaulo		/* Unknown rate: should not happen. */
2600201209Srpaulo		default:  tap->wr_rate =   0;
2601201209Srpaulo		}
2602178676Ssam	}
2603178676Ssam
2604178676Ssam	IWN_UNLOCK(sc);
2605178676Ssam
2606198429Srpaulo	/* Send the frame to the 802.11 layer. */
2607178676Ssam	if (ni != NULL) {
2608221650Sbschmidt		if (ni->ni_flags & IEEE80211_NODE_HT)
2609221650Sbschmidt			m->m_flags |= M_AMPDU;
2610220726Sbschmidt		(void)ieee80211_input(ni, m, rssi - nf, nf);
2611198429Srpaulo		/* Node is no longer needed. */
2612178676Ssam		ieee80211_free_node(ni);
2613178676Ssam	} else
2614220726Sbschmidt		(void)ieee80211_input_all(ic, m, rssi - nf, nf);
2615178676Ssam
2616178676Ssam	IWN_LOCK(sc);
2617253705Sadrian
2618253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
2619253705Sadrian
2620178676Ssam}
2621178676Ssam
2622201209Srpaulo/* Process an incoming Compressed BlockAck. */
2623206477Sbschmidtstatic void
2624201209Srpauloiwn_rx_compressed_ba(struct iwn_softc *sc, struct iwn_rx_desc *desc,
2625201209Srpaulo    struct iwn_rx_data *data)
2626201209Srpaulo{
2627237649Sbschmidt	struct iwn_ops *ops = &sc->ops;
2628221651Sbschmidt	struct ifnet *ifp = sc->sc_ifp;
2629221651Sbschmidt	struct iwn_node *wn;
2630221651Sbschmidt	struct ieee80211_node *ni;
2631201209Srpaulo	struct iwn_compressed_ba *ba = (struct iwn_compressed_ba *)(desc + 1);
2632201209Srpaulo	struct iwn_tx_ring *txq;
2633237647Sbschmidt	struct iwn_tx_data *txdata;
2634221651Sbschmidt	struct ieee80211_tx_ampdu *tap;
2635237647Sbschmidt	struct mbuf *m;
2636221651Sbschmidt	uint64_t bitmap;
2637237649Sbschmidt	uint16_t ssn;
2638221651Sbschmidt	uint8_t tid;
2639237649Sbschmidt	int ackfailcnt = 0, i, lastidx, qid, *res, shift;
2640201209Srpaulo
2641253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
2642253705Sadrian
2643220704Sbschmidt	bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD);
2644220704Sbschmidt
2645237647Sbschmidt	qid = le16toh(ba->qid);
2646237647Sbschmidt	txq = &sc->txq[ba->qid];
2647237647Sbschmidt	tap = sc->qid2tap[ba->qid];
2648234324Sadrian	tid = tap->txa_tid;
2649237647Sbschmidt	wn = (void *)tap->txa_ni;
2650221651Sbschmidt
2651237649Sbschmidt	res = NULL;
2652237649Sbschmidt	ssn = 0;
2653237649Sbschmidt	if (!IEEE80211_AMPDU_RUNNING(tap)) {
2654237649Sbschmidt		res = tap->txa_private;
2655237649Sbschmidt		ssn = tap->txa_start & 0xfff;
2656237649Sbschmidt	}
2657237649Sbschmidt
2658237647Sbschmidt	for (lastidx = le16toh(ba->ssn) & 0xff; txq->read != lastidx;) {
2659237647Sbschmidt		txdata = &txq->data[txq->read];
2660237647Sbschmidt
2661237647Sbschmidt		/* Unmap and free mbuf. */
2662237647Sbschmidt		bus_dmamap_sync(txq->data_dmat, txdata->map,
2663237647Sbschmidt		    BUS_DMASYNC_POSTWRITE);
2664237647Sbschmidt		bus_dmamap_unload(txq->data_dmat, txdata->map);
2665237647Sbschmidt		m = txdata->m, txdata->m = NULL;
2666237647Sbschmidt		ni = txdata->ni, txdata->ni = NULL;
2667237647Sbschmidt
2668237647Sbschmidt		KASSERT(ni != NULL, ("no node"));
2669237647Sbschmidt		KASSERT(m != NULL, ("no mbuf"));
2670237647Sbschmidt
2671255023Sadrian		ieee80211_tx_complete(ni, m, 1);
2672237647Sbschmidt
2673237647Sbschmidt		txq->queued--;
2674237647Sbschmidt		txq->read = (txq->read + 1) % IWN_TX_RING_COUNT;
2675237647Sbschmidt	}
2676237647Sbschmidt
2677237649Sbschmidt	if (txq->queued == 0 && res != NULL) {
2678237649Sbschmidt		iwn_nic_lock(sc);
2679237649Sbschmidt		ops->ampdu_tx_stop(sc, qid, tid, ssn);
2680237649Sbschmidt		iwn_nic_unlock(sc);
2681237649Sbschmidt		sc->qid2tap[qid] = NULL;
2682237649Sbschmidt		free(res, M_DEVBUF);
2683237649Sbschmidt		return;
2684237649Sbschmidt	}
2685237649Sbschmidt
2686221651Sbschmidt	if (wn->agg[tid].bitmap == 0)
2687221651Sbschmidt		return;
2688221651Sbschmidt
2689221651Sbschmidt	shift = wn->agg[tid].startidx - ((le16toh(ba->seq) >> 4) & 0xff);
2690221651Sbschmidt	if (shift < 0)
2691221651Sbschmidt		shift += 0x100;
2692221651Sbschmidt
2693221651Sbschmidt	if (wn->agg[tid].nframes > (64 - shift))
2694221651Sbschmidt		return;
2695221651Sbschmidt
2696237647Sbschmidt	ni = tap->txa_ni;
2697221651Sbschmidt	bitmap = (le64toh(ba->bitmap) >> shift) & wn->agg[tid].bitmap;
2698221651Sbschmidt	for (i = 0; bitmap; i++) {
2699221651Sbschmidt		if ((bitmap & 1) == 0) {
2700221651Sbschmidt			ifp->if_oerrors++;
2701221651Sbschmidt			ieee80211_ratectl_tx_complete(ni->ni_vap, ni,
2702221651Sbschmidt			    IEEE80211_RATECTL_TX_FAILURE, &ackfailcnt, NULL);
2703221651Sbschmidt		} else {
2704221651Sbschmidt			ifp->if_opackets++;
2705221651Sbschmidt			ieee80211_ratectl_tx_complete(ni->ni_vap, ni,
2706221651Sbschmidt			    IEEE80211_RATECTL_TX_SUCCESS, &ackfailcnt, NULL);
2707221651Sbschmidt		}
2708221651Sbschmidt		bitmap >>= 1;
2709221651Sbschmidt	}
2710253705Sadrian
2711253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
2712253705Sadrian
2713201209Srpaulo}
2714201209Srpaulo
2715198429Srpaulo/*
2716220674Sbschmidt * Process a CALIBRATION_RESULT notification sent by the initialization
2717220674Sbschmidt * firmware on response to a CMD_CALIB_CONFIG command (5000 only).
2718220674Sbschmidt */
2719220674Sbschmidtstatic void
2720220674Sbschmidtiwn5000_rx_calib_results(struct iwn_softc *sc, struct iwn_rx_desc *desc,
2721220674Sbschmidt    struct iwn_rx_data *data)
2722220674Sbschmidt{
2723220674Sbschmidt	struct iwn_phy_calib *calib = (struct iwn_phy_calib *)(desc + 1);
2724220674Sbschmidt	int len, idx = -1;
2725220674Sbschmidt
2726253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
2727253705Sadrian
2728220674Sbschmidt	/* Runtime firmware should not send such a notification. */
2729253705Sadrian	if (sc->sc_flags & IWN_FLAG_CALIB_DONE){
2730253705Sadrian		DPRINTF(sc, IWN_DEBUG_TRACE, "->%s received after clib done\n",
2731253705Sadrian	    __func__);
2732220674Sbschmidt		return;
2733253705Sadrian	}
2734220674Sbschmidt	len = (le32toh(desc->len) & 0x3fff) - 4;
2735220674Sbschmidt	bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD);
2736220674Sbschmidt
2737220674Sbschmidt	switch (calib->code) {
2738220674Sbschmidt	case IWN5000_PHY_CALIB_DC:
2739220867Sbschmidt		if ((sc->sc_flags & IWN_FLAG_INTERNAL_PA) == 0 &&
2740220867Sbschmidt		    (sc->hw_type == IWN_HW_REV_TYPE_5150 ||
2741227805Sbschmidt		     sc->hw_type >= IWN_HW_REV_TYPE_6000) &&
2742227805Sbschmidt		     sc->hw_type != IWN_HW_REV_TYPE_6050)
2743220674Sbschmidt			idx = 0;
2744220674Sbschmidt		break;
2745220674Sbschmidt	case IWN5000_PHY_CALIB_LO:
2746220674Sbschmidt		idx = 1;
2747220674Sbschmidt		break;
2748220674Sbschmidt	case IWN5000_PHY_CALIB_TX_IQ:
2749220674Sbschmidt		idx = 2;
2750220674Sbschmidt		break;
2751220674Sbschmidt	case IWN5000_PHY_CALIB_TX_IQ_PERIODIC:
2752220674Sbschmidt		if (sc->hw_type < IWN_HW_REV_TYPE_6000 &&
2753220674Sbschmidt		    sc->hw_type != IWN_HW_REV_TYPE_5150)
2754220674Sbschmidt			idx = 3;
2755220674Sbschmidt		break;
2756220674Sbschmidt	case IWN5000_PHY_CALIB_BASE_BAND:
2757220674Sbschmidt		idx = 4;
2758220674Sbschmidt		break;
2759220674Sbschmidt	}
2760220674Sbschmidt	if (idx == -1)	/* Ignore other results. */
2761220674Sbschmidt		return;
2762220674Sbschmidt
2763220674Sbschmidt	/* Save calibration result. */
2764220674Sbschmidt	if (sc->calibcmd[idx].buf != NULL)
2765220674Sbschmidt		free(sc->calibcmd[idx].buf, M_DEVBUF);
2766220674Sbschmidt	sc->calibcmd[idx].buf = malloc(len, M_DEVBUF, M_NOWAIT);
2767220674Sbschmidt	if (sc->calibcmd[idx].buf == NULL) {
2768220674Sbschmidt		DPRINTF(sc, IWN_DEBUG_CALIBRATE,
2769220674Sbschmidt		    "not enough memory for calibration result %d\n",
2770220674Sbschmidt		    calib->code);
2771220674Sbschmidt		return;
2772220674Sbschmidt	}
2773220674Sbschmidt	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
2774220674Sbschmidt	    "saving calibration result code=%d len=%d\n", calib->code, len);
2775220674Sbschmidt	sc->calibcmd[idx].len = len;
2776220674Sbschmidt	memcpy(sc->calibcmd[idx].buf, calib, len);
2777220674Sbschmidt}
2778220674Sbschmidt
2779220674Sbschmidt/*
2780198429Srpaulo * Process an RX_STATISTICS or BEACON_STATISTICS firmware notification.
2781198429Srpaulo * The latter is sent by the firmware after each received beacon.
2782198429Srpaulo */
2783206477Sbschmidtstatic void
2784198429Srpauloiwn_rx_statistics(struct iwn_softc *sc, struct iwn_rx_desc *desc,
2785198429Srpaulo    struct iwn_rx_data *data)
2786198429Srpaulo{
2787220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
2788178676Ssam	struct ifnet *ifp = sc->sc_ifp;
2789178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
2790178676Ssam	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
2791178676Ssam	struct iwn_calib_state *calib = &sc->calib;
2792178676Ssam	struct iwn_stats *stats = (struct iwn_stats *)(desc + 1);
2793198429Srpaulo	int temp;
2794178676Ssam
2795253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
2796253705Sadrian
2797220725Sbschmidt	/* Ignore statistics received during a scan. */
2798178676Ssam	if (vap->iv_state != IEEE80211_S_RUN ||
2799253705Sadrian	    (ic->ic_flags & IEEE80211_F_SCAN)){
2800253705Sadrian		DPRINTF(sc, IWN_DEBUG_TRACE, "->%s received during calib\n",
2801253705Sadrian	    __func__);
2802178676Ssam		return;
2803253705Sadrian	}
2804178676Ssam
2805202986Srpaulo	bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD);
2806220724Sbschmidt
2807220724Sbschmidt	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: received statistics, cmd %d\n",
2808220724Sbschmidt	    __func__, desc->type);
2809220667Sbschmidt	sc->calib_cnt = 0;	/* Reset TX power calibration timeout. */
2810178676Ssam
2811198429Srpaulo	/* Test if temperature has changed. */
2812178676Ssam	if (stats->general.temp != sc->rawtemp) {
2813198429Srpaulo		/* Convert "raw" temperature to degC. */
2814178676Ssam		sc->rawtemp = stats->general.temp;
2815220728Sbschmidt		temp = ops->get_temperature(sc);
2816178676Ssam		DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: temperature %d\n",
2817178676Ssam		    __func__, temp);
2818178676Ssam
2819220725Sbschmidt		/* Update TX power if need be (4965AGN only). */
2820198429Srpaulo		if (sc->hw_type == IWN_HW_REV_TYPE_4965)
2821198429Srpaulo			iwn4965_power_calibration(sc, temp);
2822178676Ssam	}
2823178676Ssam
2824178676Ssam	if (desc->type != IWN_BEACON_STATISTICS)
2825198429Srpaulo		return;	/* Reply to a statistics request. */
2826178676Ssam
2827178676Ssam	sc->noise = iwn_get_noise(&stats->rx.general);
2828178676Ssam	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: noise %d\n", __func__, sc->noise);
2829178676Ssam
2830198429Srpaulo	/* Test that RSSI and noise are present in stats report. */
2831198429Srpaulo	if (le32toh(stats->rx.general.flags) != 1) {
2832178676Ssam		DPRINTF(sc, IWN_DEBUG_ANY, "%s\n",
2833178676Ssam		    "received statistics without RSSI");
2834178676Ssam		return;
2835178676Ssam	}
2836178676Ssam
2837178676Ssam	if (calib->state == IWN_CALIB_STATE_ASSOC)
2838198429Srpaulo		iwn_collect_noise(sc, &stats->rx.general);
2839178676Ssam	else if (calib->state == IWN_CALIB_STATE_RUN)
2840178676Ssam		iwn_tune_sensitivity(sc, &stats->rx);
2841253705Sadrian
2842253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
2843178676Ssam}
2844178676Ssam
2845198429Srpaulo/*
2846198429Srpaulo * Process a TX_DONE firmware notification.  Unfortunately, the 4965AGN
2847198429Srpaulo * and 5000 adapters have different incompatible TX status formats.
2848198429Srpaulo */
2849206477Sbschmidtstatic void
2850198429Srpauloiwn4965_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc,
2851198429Srpaulo    struct iwn_rx_data *data)
2852178676Ssam{
2853198429Srpaulo	struct iwn4965_tx_stat *stat = (struct iwn4965_tx_stat *)(desc + 1);
2854221651Sbschmidt	struct iwn_tx_ring *ring;
2855221651Sbschmidt	int qid;
2856198429Srpaulo
2857221651Sbschmidt	qid = desc->qid & 0xf;
2858221651Sbschmidt	ring = &sc->txq[qid];
2859221651Sbschmidt
2860198429Srpaulo	DPRINTF(sc, IWN_DEBUG_XMIT, "%s: "
2861198429Srpaulo	    "qid %d idx %d retries %d nkill %d rate %x duration %d status %x\n",
2862201209Srpaulo	    __func__, desc->qid, desc->idx, stat->ackfailcnt,
2863201209Srpaulo	    stat->btkillcnt, stat->rate, le16toh(stat->duration),
2864198429Srpaulo	    le32toh(stat->status));
2865201209Srpaulo
2866207001Sbschmidt	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD);
2867221651Sbschmidt	if (qid >= sc->firstaggqueue) {
2868221651Sbschmidt		iwn_ampdu_tx_done(sc, qid, desc->idx, stat->nframes,
2869221651Sbschmidt		    &stat->status);
2870221651Sbschmidt	} else {
2871221651Sbschmidt		iwn_tx_done(sc, desc, stat->ackfailcnt,
2872221651Sbschmidt		    le32toh(stat->status) & 0xff);
2873221651Sbschmidt	}
2874198429Srpaulo}
2875198429Srpaulo
2876206477Sbschmidtstatic void
2877198429Srpauloiwn5000_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc,
2878198429Srpaulo    struct iwn_rx_data *data)
2879198429Srpaulo{
2880198429Srpaulo	struct iwn5000_tx_stat *stat = (struct iwn5000_tx_stat *)(desc + 1);
2881221651Sbschmidt	struct iwn_tx_ring *ring;
2882221651Sbschmidt	int qid;
2883198429Srpaulo
2884221651Sbschmidt	qid = desc->qid & 0xf;
2885221651Sbschmidt	ring = &sc->txq[qid];
2886221651Sbschmidt
2887198429Srpaulo	DPRINTF(sc, IWN_DEBUG_XMIT, "%s: "
2888198429Srpaulo	    "qid %d idx %d retries %d nkill %d rate %x duration %d status %x\n",
2889201209Srpaulo	    __func__, desc->qid, desc->idx, stat->ackfailcnt,
2890201209Srpaulo	    stat->btkillcnt, stat->rate, le16toh(stat->duration),
2891198429Srpaulo	    le32toh(stat->status));
2892198429Srpaulo
2893201209Srpaulo#ifdef notyet
2894198429Srpaulo	/* Reset TX scheduler slot. */
2895198429Srpaulo	iwn5000_reset_sched(sc, desc->qid & 0xf, desc->idx);
2896201209Srpaulo#endif
2897202986Srpaulo
2898207001Sbschmidt	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD);
2899221651Sbschmidt	if (qid >= sc->firstaggqueue) {
2900221651Sbschmidt		iwn_ampdu_tx_done(sc, qid, desc->idx, stat->nframes,
2901221651Sbschmidt		    &stat->status);
2902221651Sbschmidt	} else {
2903221651Sbschmidt		iwn_tx_done(sc, desc, stat->ackfailcnt,
2904221651Sbschmidt		    le16toh(stat->status) & 0xff);
2905221651Sbschmidt	}
2906198429Srpaulo}
2907198429Srpaulo
2908198429Srpaulo/*
2909198429Srpaulo * Adapter-independent backend for TX_DONE firmware notifications.
2910198429Srpaulo */
2911206477Sbschmidtstatic void
2912201209Srpauloiwn_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, int ackfailcnt,
2913198429Srpaulo    uint8_t status)
2914198429Srpaulo{
2915178676Ssam	struct ifnet *ifp = sc->sc_ifp;
2916178676Ssam	struct iwn_tx_ring *ring = &sc->txq[desc->qid & 0xf];
2917178676Ssam	struct iwn_tx_data *data = &ring->data[desc->idx];
2918178676Ssam	struct mbuf *m;
2919178676Ssam	struct ieee80211_node *ni;
2920206358Srpaulo	struct ieee80211vap *vap;
2921178676Ssam
2922178676Ssam	KASSERT(data->ni != NULL, ("no node"));
2923178676Ssam
2924253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
2925253705Sadrian
2926198429Srpaulo	/* Unmap and free mbuf. */
2927201209Srpaulo	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTWRITE);
2928201209Srpaulo	bus_dmamap_unload(ring->data_dmat, data->map);
2929178676Ssam	m = data->m, data->m = NULL;
2930178676Ssam	ni = data->ni, data->ni = NULL;
2931206358Srpaulo	vap = ni->ni_vap;
2932178676Ssam
2933201209Srpaulo	/*
2934201209Srpaulo	 * Update rate control statistics for the node.
2935201209Srpaulo	 */
2936220719Sbschmidt	if (status & IWN_TX_FAIL) {
2937201209Srpaulo		ifp->if_oerrors++;
2938206358Srpaulo		ieee80211_ratectl_tx_complete(vap, ni,
2939206358Srpaulo		    IEEE80211_RATECTL_TX_FAILURE, &ackfailcnt, NULL);
2940201209Srpaulo	} else {
2941220719Sbschmidt		ifp->if_opackets++;
2942206358Srpaulo		ieee80211_ratectl_tx_complete(vap, ni,
2943206358Srpaulo		    IEEE80211_RATECTL_TX_SUCCESS, &ackfailcnt, NULL);
2944201209Srpaulo	}
2945178676Ssam
2946255023Sadrian	/*
2947255023Sadrian	 * Channels marked for "radar" require traffic to be received
2948255023Sadrian	 * to unlock before we can transmit.  Until traffic is seen
2949255023Sadrian	 * any attempt to transmit is returned immediately with status
2950255023Sadrian	 * set to IWN_TX_FAIL_TX_LOCKED.  Unfortunately this can easily
2951255023Sadrian	 * happen on first authenticate after scanning.  To workaround
2952255023Sadrian	 * this we ignore a failure of this sort in AUTH state so the
2953255023Sadrian	 * 802.11 layer will fall back to using a timeout to wait for
2954255023Sadrian	 * the AUTH reply.  This allows the firmware time to see
2955255023Sadrian	 * traffic so a subsequent retry of AUTH succeeds.  It's
2956255023Sadrian	 * unclear why the firmware does not maintain state for
2957255023Sadrian	 * channels recently visited as this would allow immediate
2958255023Sadrian	 * use of the channel after a scan (where we see traffic).
2959255023Sadrian	 */
2960255023Sadrian	if (status == IWN_TX_FAIL_TX_LOCKED &&
2961255023Sadrian	    ni->ni_vap->iv_state == IEEE80211_S_AUTH)
2962255023Sadrian		ieee80211_tx_complete(ni, m, 0);
2963255023Sadrian	else
2964255023Sadrian		ieee80211_tx_complete(ni, m,
2965255023Sadrian		    (status & IWN_TX_FAIL) != 0);
2966255023Sadrian
2967178676Ssam	sc->sc_tx_timer = 0;
2968198429Srpaulo	if (--ring->queued < IWN_TX_RING_LOMARK) {
2969198429Srpaulo		sc->qfullmsk &= ~(1 << ring->qid);
2970198429Srpaulo		if (sc->qfullmsk == 0 &&
2971198429Srpaulo		    (ifp->if_drv_flags & IFF_DRV_OACTIVE)) {
2972198429Srpaulo			ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
2973198429Srpaulo			iwn_start_locked(ifp);
2974198429Srpaulo		}
2975198429Srpaulo	}
2976253705Sadrian
2977253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
2978253705Sadrian
2979178676Ssam}
2980178676Ssam
2981198429Srpaulo/*
2982198429Srpaulo * Process a "command done" firmware notification.  This is where we wakeup
2983198429Srpaulo * processes waiting for a synchronous command completion.
2984198429Srpaulo */
2985206477Sbschmidtstatic void
2986198429Srpauloiwn_cmd_done(struct iwn_softc *sc, struct iwn_rx_desc *desc)
2987178676Ssam{
2988178676Ssam	struct iwn_tx_ring *ring = &sc->txq[4];
2989178676Ssam	struct iwn_tx_data *data;
2990178676Ssam
2991178676Ssam	if ((desc->qid & 0xf) != 4)
2992198429Srpaulo		return;	/* Not a command ack. */
2993178676Ssam
2994178676Ssam	data = &ring->data[desc->idx];
2995178676Ssam
2996198429Srpaulo	/* If the command was mapped in an mbuf, free it. */
2997178676Ssam	if (data->m != NULL) {
2998220704Sbschmidt		bus_dmamap_sync(ring->data_dmat, data->map,
2999220704Sbschmidt		    BUS_DMASYNC_POSTWRITE);
3000201209Srpaulo		bus_dmamap_unload(ring->data_dmat, data->map);
3001178676Ssam		m_freem(data->m);
3002178676Ssam		data->m = NULL;
3003178676Ssam	}
3004198439Srpaulo	wakeup(&ring->desc[desc->idx]);
3005178676Ssam}
3006178676Ssam
3007221651Sbschmidtstatic void
3008221651Sbschmidtiwn_ampdu_tx_done(struct iwn_softc *sc, int qid, int idx, int nframes,
3009221651Sbschmidt    void *stat)
3010221651Sbschmidt{
3011237649Sbschmidt	struct iwn_ops *ops = &sc->ops;
3012221651Sbschmidt	struct ifnet *ifp = sc->sc_ifp;
3013221651Sbschmidt	struct iwn_tx_ring *ring = &sc->txq[qid];
3014221651Sbschmidt	struct iwn_tx_data *data;
3015221651Sbschmidt	struct mbuf *m;
3016221651Sbschmidt	struct iwn_node *wn;
3017221651Sbschmidt	struct ieee80211_node *ni;
3018221651Sbschmidt	struct ieee80211_tx_ampdu *tap;
3019221651Sbschmidt	uint64_t bitmap;
3020221651Sbschmidt	uint32_t *status = stat;
3021221651Sbschmidt	uint16_t *aggstatus = stat;
3022237649Sbschmidt	uint16_t ssn;
3023221651Sbschmidt	uint8_t tid;
3024237649Sbschmidt	int bit, i, lastidx, *res, seqno, shift, start;
3025221651Sbschmidt
3026253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
3027253705Sadrian
3028221651Sbschmidt#ifdef NOT_YET
3029221651Sbschmidt	if (nframes == 1) {
3030221651Sbschmidt		if ((*status & 0xff) != 1 && (*status & 0xff) != 2)
3031221651Sbschmidt			printf("ieee80211_send_bar()\n");
3032221651Sbschmidt	}
3033221651Sbschmidt#endif
3034221651Sbschmidt
3035221651Sbschmidt	bitmap = 0;
3036221651Sbschmidt	start = idx;
3037221651Sbschmidt	for (i = 0; i < nframes; i++) {
3038221651Sbschmidt		if (le16toh(aggstatus[i * 2]) & 0xc)
3039221651Sbschmidt			continue;
3040221651Sbschmidt
3041221651Sbschmidt		idx = le16toh(aggstatus[2*i + 1]) & 0xff;
3042221651Sbschmidt		bit = idx - start;
3043221651Sbschmidt		shift = 0;
3044221651Sbschmidt		if (bit >= 64) {
3045221651Sbschmidt			shift = 0x100 - idx + start;
3046221651Sbschmidt			bit = 0;
3047221651Sbschmidt			start = idx;
3048221651Sbschmidt		} else if (bit <= -64)
3049221651Sbschmidt			bit = 0x100 - start + idx;
3050221651Sbschmidt		else if (bit < 0) {
3051221651Sbschmidt			shift = start - idx;
3052221651Sbschmidt			start = idx;
3053221651Sbschmidt			bit = 0;
3054221651Sbschmidt		}
3055221651Sbschmidt		bitmap = bitmap << shift;
3056221651Sbschmidt		bitmap |= 1ULL << bit;
3057221651Sbschmidt	}
3058221651Sbschmidt	tap = sc->qid2tap[qid];
3059237649Sbschmidt	tid = tap->txa_tid;
3060237649Sbschmidt	wn = (void *)tap->txa_ni;
3061237649Sbschmidt	wn->agg[tid].bitmap = bitmap;
3062237649Sbschmidt	wn->agg[tid].startidx = start;
3063237649Sbschmidt	wn->agg[tid].nframes = nframes;
3064237649Sbschmidt
3065237649Sbschmidt	res = NULL;
3066237649Sbschmidt	ssn = 0;
3067237649Sbschmidt	if (!IEEE80211_AMPDU_RUNNING(tap)) {
3068237649Sbschmidt		res = tap->txa_private;
3069237649Sbschmidt		ssn = tap->txa_start & 0xfff;
3070230620Sbschmidt	}
3071221651Sbschmidt
3072221651Sbschmidt	seqno = le32toh(*(status + nframes)) & 0xfff;
3073221651Sbschmidt	for (lastidx = (seqno & 0xff); ring->read != lastidx;) {
3074221651Sbschmidt		data = &ring->data[ring->read];
3075221651Sbschmidt
3076221651Sbschmidt		/* Unmap and free mbuf. */
3077221651Sbschmidt		bus_dmamap_sync(ring->data_dmat, data->map,
3078221651Sbschmidt		    BUS_DMASYNC_POSTWRITE);
3079221651Sbschmidt		bus_dmamap_unload(ring->data_dmat, data->map);
3080221651Sbschmidt		m = data->m, data->m = NULL;
3081221651Sbschmidt		ni = data->ni, data->ni = NULL;
3082221651Sbschmidt
3083237647Sbschmidt		KASSERT(ni != NULL, ("no node"));
3084237647Sbschmidt		KASSERT(m != NULL, ("no mbuf"));
3085237647Sbschmidt
3086255023Sadrian		ieee80211_tx_complete(ni, m, 1);
3087221651Sbschmidt
3088221651Sbschmidt		ring->queued--;
3089221651Sbschmidt		ring->read = (ring->read + 1) % IWN_TX_RING_COUNT;
3090221651Sbschmidt	}
3091221651Sbschmidt
3092237649Sbschmidt	if (ring->queued == 0 && res != NULL) {
3093237649Sbschmidt		iwn_nic_lock(sc);
3094237649Sbschmidt		ops->ampdu_tx_stop(sc, qid, tid, ssn);
3095237649Sbschmidt		iwn_nic_unlock(sc);
3096237649Sbschmidt		sc->qid2tap[qid] = NULL;
3097237649Sbschmidt		free(res, M_DEVBUF);
3098237649Sbschmidt		return;
3099237649Sbschmidt	}
3100237649Sbschmidt
3101221651Sbschmidt	sc->sc_tx_timer = 0;
3102221651Sbschmidt	if (ring->queued < IWN_TX_RING_LOMARK) {
3103221651Sbschmidt		sc->qfullmsk &= ~(1 << ring->qid);
3104221651Sbschmidt		if (sc->qfullmsk == 0 &&
3105221651Sbschmidt		    (ifp->if_drv_flags & IFF_DRV_OACTIVE)) {
3106221651Sbschmidt			ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
3107221651Sbschmidt			iwn_start_locked(ifp);
3108221651Sbschmidt		}
3109221651Sbschmidt	}
3110253705Sadrian
3111253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
3112253705Sadrian
3113221651Sbschmidt}
3114221651Sbschmidt
3115198429Srpaulo/*
3116198429Srpaulo * Process an INT_FH_RX or INT_SW_RX interrupt.
3117198429Srpaulo */
3118206477Sbschmidtstatic void
3119178676Ssamiwn_notif_intr(struct iwn_softc *sc)
3120178676Ssam{
3121220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
3122178676Ssam	struct ifnet *ifp = sc->sc_ifp;
3123178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
3124178676Ssam	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
3125178676Ssam	uint16_t hw;
3126178676Ssam
3127198429Srpaulo	bus_dmamap_sync(sc->rxq.stat_dma.tag, sc->rxq.stat_dma.map,
3128198429Srpaulo	    BUS_DMASYNC_POSTREAD);
3129198429Srpaulo
3130198429Srpaulo	hw = le16toh(sc->rxq.stat->closed_count) & 0xfff;
3131178676Ssam	while (sc->rxq.cur != hw) {
3132178676Ssam		struct iwn_rx_data *data = &sc->rxq.data[sc->rxq.cur];
3133198429Srpaulo		struct iwn_rx_desc *desc;
3134178676Ssam
3135201209Srpaulo		bus_dmamap_sync(sc->rxq.data_dmat, data->map,
3136201209Srpaulo		    BUS_DMASYNC_POSTREAD);
3137198429Srpaulo		desc = mtod(data->m, struct iwn_rx_desc *);
3138198429Srpaulo
3139178676Ssam		DPRINTF(sc, IWN_DEBUG_RECV,
3140178676Ssam		    "%s: qid %x idx %d flags %x type %d(%s) len %d\n",
3141198439Srpaulo		    __func__, desc->qid & 0xf, desc->idx, desc->flags,
3142178676Ssam		    desc->type, iwn_intr_str(desc->type),
3143178676Ssam		    le16toh(desc->len));
3144178676Ssam
3145198429Srpaulo		if (!(desc->qid & 0x80))	/* Reply to a command. */
3146198429Srpaulo			iwn_cmd_done(sc, desc);
3147178676Ssam
3148178676Ssam		switch (desc->type) {
3149198429Srpaulo		case IWN_RX_PHY:
3150198429Srpaulo			iwn_rx_phy(sc, desc, data);
3151178676Ssam			break;
3152178676Ssam
3153198429Srpaulo		case IWN_RX_DONE:		/* 4965AGN only. */
3154198429Srpaulo		case IWN_MPDU_RX_DONE:
3155198429Srpaulo			/* An 802.11 frame has been received. */
3156198429Srpaulo			iwn_rx_done(sc, desc, data);
3157178676Ssam			break;
3158178676Ssam
3159201209Srpaulo		case IWN_RX_COMPRESSED_BA:
3160201209Srpaulo			/* A Compressed BlockAck has been received. */
3161201209Srpaulo			iwn_rx_compressed_ba(sc, desc, data);
3162201209Srpaulo			break;
3163201209Srpaulo
3164178676Ssam		case IWN_TX_DONE:
3165198429Srpaulo			/* An 802.11 frame has been transmitted. */
3166220728Sbschmidt			ops->tx_done(sc, desc, data);
3167178676Ssam			break;
3168178676Ssam
3169178676Ssam		case IWN_RX_STATISTICS:
3170178676Ssam		case IWN_BEACON_STATISTICS:
3171198429Srpaulo			iwn_rx_statistics(sc, desc, data);
3172178676Ssam			break;
3173178676Ssam
3174198429Srpaulo		case IWN_BEACON_MISSED:
3175198429Srpaulo		{
3176178676Ssam			struct iwn_beacon_missed *miss =
3177178676Ssam			    (struct iwn_beacon_missed *)(desc + 1);
3178202986Srpaulo			int misses;
3179178676Ssam
3180201209Srpaulo			bus_dmamap_sync(sc->rxq.data_dmat, data->map,
3181201209Srpaulo			    BUS_DMASYNC_POSTREAD);
3182202986Srpaulo			misses = le32toh(miss->consecutive);
3183201209Srpaulo
3184178676Ssam			DPRINTF(sc, IWN_DEBUG_STATE,
3185178676Ssam			    "%s: beacons missed %d/%d\n", __func__,
3186178676Ssam			    misses, le32toh(miss->total));
3187178676Ssam			/*
3188178676Ssam			 * If more than 5 consecutive beacons are missed,
3189178676Ssam			 * reinitialize the sensitivity state machine.
3190178676Ssam			 */
3191220660Sbschmidt			if (vap->iv_state == IEEE80211_S_RUN &&
3192226346Sbschmidt			    (ic->ic_flags & IEEE80211_F_SCAN) == 0) {
3193220660Sbschmidt				if (misses > 5)
3194220660Sbschmidt					(void)iwn_init_sensitivity(sc);
3195220660Sbschmidt				if (misses >= vap->iv_bmissthreshold) {
3196220660Sbschmidt					IWN_UNLOCK(sc);
3197220660Sbschmidt					ieee80211_beacon_miss(ic);
3198220660Sbschmidt					IWN_LOCK(sc);
3199220660Sbschmidt				}
3200201209Srpaulo			}
3201178676Ssam			break;
3202178676Ssam		}
3203198429Srpaulo		case IWN_UC_READY:
3204198429Srpaulo		{
3205178676Ssam			struct iwn_ucode_info *uc =
3206178676Ssam			    (struct iwn_ucode_info *)(desc + 1);
3207178676Ssam
3208198429Srpaulo			/* The microcontroller is ready. */
3209201209Srpaulo			bus_dmamap_sync(sc->rxq.data_dmat, data->map,
3210201209Srpaulo			    BUS_DMASYNC_POSTREAD);
3211178676Ssam			DPRINTF(sc, IWN_DEBUG_RESET,
3212178676Ssam			    "microcode alive notification version=%d.%d "
3213178676Ssam			    "subtype=%x alive=%x\n", uc->major, uc->minor,
3214178676Ssam			    uc->subtype, le32toh(uc->valid));
3215178676Ssam
3216178676Ssam			if (le32toh(uc->valid) != 1) {
3217178676Ssam				device_printf(sc->sc_dev,
3218198429Srpaulo				    "microcontroller initialization failed");
3219178676Ssam				break;
3220178676Ssam			}
3221178676Ssam			if (uc->subtype == IWN_UCODE_INIT) {
3222201209Srpaulo				/* Save microcontroller report. */
3223178676Ssam				memcpy(&sc->ucode_info, uc, sizeof (*uc));
3224178676Ssam			}
3225198429Srpaulo			/* Save the address of the error log in SRAM. */
3226198429Srpaulo			sc->errptr = le32toh(uc->errptr);
3227178676Ssam			break;
3228178676Ssam		}
3229198429Srpaulo		case IWN_STATE_CHANGED:
3230198429Srpaulo		{
3231178676Ssam			/*
3232178676Ssam			 * State change allows hardware switch change to be
3233178676Ssam			 * noted. However, we handle this in iwn_intr as we
3234178676Ssam			 * get both the enable/disble intr.
3235178676Ssam			 */
3236201209Srpaulo			bus_dmamap_sync(sc->rxq.data_dmat, data->map,
3237201209Srpaulo			    BUS_DMASYNC_POSTREAD);
3238253866Sadrian#ifdef	IWN_DEBUG
3239253866Sadrian			uint32_t *status = (uint32_t *)(desc + 1);
3240178676Ssam			DPRINTF(sc, IWN_DEBUG_INTR, "state changed to %x\n",
3241178676Ssam			    le32toh(*status));
3242253866Sadrian#endif
3243178676Ssam			break;
3244178676Ssam		}
3245198429Srpaulo		case IWN_START_SCAN:
3246198429Srpaulo		{
3247253866Sadrian			bus_dmamap_sync(sc->rxq.data_dmat, data->map,
3248253866Sadrian			    BUS_DMASYNC_POSTREAD);
3249253866Sadrian#ifdef	IWN_DEBUG
3250178676Ssam			struct iwn_start_scan *scan =
3251178676Ssam			    (struct iwn_start_scan *)(desc + 1);
3252178676Ssam			DPRINTF(sc, IWN_DEBUG_ANY,
3253178676Ssam			    "%s: scanning channel %d status %x\n",
3254178676Ssam			    __func__, scan->chan, le32toh(scan->status));
3255253866Sadrian#endif
3256178676Ssam			break;
3257178676Ssam		}
3258198429Srpaulo		case IWN_STOP_SCAN:
3259198429Srpaulo		{
3260253866Sadrian			bus_dmamap_sync(sc->rxq.data_dmat, data->map,
3261253866Sadrian			    BUS_DMASYNC_POSTREAD);
3262253866Sadrian#ifdef	IWN_DEBUG
3263178676Ssam			struct iwn_stop_scan *scan =
3264178676Ssam			    (struct iwn_stop_scan *)(desc + 1);
3265178676Ssam			DPRINTF(sc, IWN_DEBUG_STATE,
3266178676Ssam			    "scan finished nchan=%d status=%d chan=%d\n",
3267178676Ssam			    scan->nchan, scan->status, scan->chan);
3268253866Sadrian#endif
3269178676Ssam
3270201209Srpaulo			IWN_UNLOCK(sc);
3271191746Sthompsa			ieee80211_scan_next(vap);
3272201209Srpaulo			IWN_LOCK(sc);
3273178676Ssam			break;
3274178676Ssam		}
3275198429Srpaulo		case IWN5000_CALIBRATION_RESULT:
3276220674Sbschmidt			iwn5000_rx_calib_results(sc, desc, data);
3277198429Srpaulo			break;
3278198429Srpaulo
3279198429Srpaulo		case IWN5000_CALIBRATION_DONE:
3280201209Srpaulo			sc->sc_flags |= IWN_FLAG_CALIB_DONE;
3281198429Srpaulo			wakeup(sc);
3282198429Srpaulo			break;
3283178676Ssam		}
3284198429Srpaulo
3285178676Ssam		sc->rxq.cur = (sc->rxq.cur + 1) % IWN_RX_RING_COUNT;
3286178676Ssam	}
3287178676Ssam
3288198429Srpaulo	/* Tell the firmware what we have processed. */
3289178676Ssam	hw = (hw == 0) ? IWN_RX_RING_COUNT - 1 : hw - 1;
3290198429Srpaulo	IWN_WRITE(sc, IWN_FH_RX_WPTR, hw & ~7);
3291178676Ssam}
3292178676Ssam
3293198429Srpaulo/*
3294198429Srpaulo * Process an INT_WAKEUP interrupt raised when the microcontroller wakes up
3295198429Srpaulo * from power-down sleep mode.
3296198429Srpaulo */
3297206477Sbschmidtstatic void
3298198429Srpauloiwn_wakeup_intr(struct iwn_softc *sc)
3299198429Srpaulo{
3300198429Srpaulo	int qid;
3301198429Srpaulo
3302198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "%s: ucode wakeup from power-down sleep\n",
3303198429Srpaulo	    __func__);
3304198429Srpaulo
3305198429Srpaulo	/* Wakeup RX and TX rings. */
3306198429Srpaulo	IWN_WRITE(sc, IWN_FH_RX_WPTR, sc->rxq.cur & ~7);
3307220728Sbschmidt	for (qid = 0; qid < sc->ntxqs; qid++) {
3308198429Srpaulo		struct iwn_tx_ring *ring = &sc->txq[qid];
3309198429Srpaulo		IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | ring->cur);
3310198429Srpaulo	}
3311198429Srpaulo}
3312198429Srpaulo
3313206477Sbschmidtstatic void
3314191746Sthompsaiwn_rftoggle_intr(struct iwn_softc *sc)
3315191746Sthompsa{
3316191746Sthompsa	struct ifnet *ifp = sc->sc_ifp;
3317191746Sthompsa	struct ieee80211com *ic = ifp->if_l2com;
3318198429Srpaulo	uint32_t tmp = IWN_READ(sc, IWN_GP_CNTRL);
3319191746Sthompsa
3320191746Sthompsa	IWN_LOCK_ASSERT(sc);
3321191746Sthompsa
3322191746Sthompsa	device_printf(sc->sc_dev, "RF switch: radio %s\n",
3323198429Srpaulo	    (tmp & IWN_GP_CNTRL_RFKILL) ? "enabled" : "disabled");
3324198429Srpaulo	if (tmp & IWN_GP_CNTRL_RFKILL)
3325191746Sthompsa		ieee80211_runtask(ic, &sc->sc_radioon_task);
3326191746Sthompsa	else
3327191746Sthompsa		ieee80211_runtask(ic, &sc->sc_radiooff_task);
3328191746Sthompsa}
3329191746Sthompsa
3330198429Srpaulo/*
3331198429Srpaulo * Dump the error log of the firmware when a firmware panic occurs.  Although
3332198429Srpaulo * we can't debug the firmware because it is neither open source nor free, it
3333198429Srpaulo * can help us to identify certain classes of problems.
3334198429Srpaulo */
3335206477Sbschmidtstatic void
3336201209Srpauloiwn_fatal_intr(struct iwn_softc *sc)
3337191746Sthompsa{
3338198429Srpaulo	struct iwn_fw_dump dump;
3339198429Srpaulo	int i;
3340191746Sthompsa
3341191746Sthompsa	IWN_LOCK_ASSERT(sc);
3342191746Sthompsa
3343201209Srpaulo	/* Force a complete recalibration on next init. */
3344201209Srpaulo	sc->sc_flags &= ~IWN_FLAG_CALIB_DONE;
3345201209Srpaulo
3346198429Srpaulo	/* Check that the error log address is valid. */
3347198429Srpaulo	if (sc->errptr < IWN_FW_DATA_BASE ||
3348198429Srpaulo	    sc->errptr + sizeof (dump) >
3349220728Sbschmidt	    IWN_FW_DATA_BASE + sc->fw_data_maxsz) {
3350220726Sbschmidt		printf("%s: bad firmware error log address 0x%08x\n", __func__,
3351220726Sbschmidt		    sc->errptr);
3352198429Srpaulo		return;
3353198429Srpaulo	}
3354198429Srpaulo	if (iwn_nic_lock(sc) != 0) {
3355220726Sbschmidt		printf("%s: could not read firmware error log\n", __func__);
3356198429Srpaulo		return;
3357198429Srpaulo	}
3358198429Srpaulo	/* Read firmware error log from SRAM. */
3359198429Srpaulo	iwn_mem_read_region_4(sc, sc->errptr, (uint32_t *)&dump,
3360198429Srpaulo	    sizeof (dump) / sizeof (uint32_t));
3361198429Srpaulo	iwn_nic_unlock(sc);
3362198429Srpaulo
3363198429Srpaulo	if (dump.valid == 0) {
3364220726Sbschmidt		printf("%s: firmware error log is empty\n", __func__);
3365198429Srpaulo		return;
3366198429Srpaulo	}
3367198429Srpaulo	printf("firmware error log:\n");
3368198429Srpaulo	printf("  error type      = \"%s\" (0x%08X)\n",
3369198429Srpaulo	    (dump.id < nitems(iwn_fw_errmsg)) ?
3370198429Srpaulo		iwn_fw_errmsg[dump.id] : "UNKNOWN",
3371198429Srpaulo	    dump.id);
3372198429Srpaulo	printf("  program counter = 0x%08X\n", dump.pc);
3373198429Srpaulo	printf("  source line     = 0x%08X\n", dump.src_line);
3374198429Srpaulo	printf("  error data      = 0x%08X%08X\n",
3375198429Srpaulo	    dump.error_data[0], dump.error_data[1]);
3376198429Srpaulo	printf("  branch link     = 0x%08X%08X\n",
3377198429Srpaulo	    dump.branch_link[0], dump.branch_link[1]);
3378198429Srpaulo	printf("  interrupt link  = 0x%08X%08X\n",
3379198429Srpaulo	    dump.interrupt_link[0], dump.interrupt_link[1]);
3380198429Srpaulo	printf("  time            = %u\n", dump.time[0]);
3381198429Srpaulo
3382198429Srpaulo	/* Dump driver status (TX and RX rings) while we're here. */
3383198429Srpaulo	printf("driver status:\n");
3384220728Sbschmidt	for (i = 0; i < sc->ntxqs; i++) {
3385198429Srpaulo		struct iwn_tx_ring *ring = &sc->txq[i];
3386198429Srpaulo		printf("  tx ring %2d: qid=%-2d cur=%-3d queued=%-3d\n",
3387198429Srpaulo		    i, ring->qid, ring->cur, ring->queued);
3388198429Srpaulo	}
3389198429Srpaulo	printf("  rx ring: cur=%d\n", sc->rxq.cur);
3390191746Sthompsa}
3391191746Sthompsa
3392206477Sbschmidtstatic void
3393178676Ssamiwn_intr(void *arg)
3394178676Ssam{
3395178676Ssam	struct iwn_softc *sc = arg;
3396198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
3397201209Srpaulo	uint32_t r1, r2, tmp;
3398178676Ssam
3399178676Ssam	IWN_LOCK(sc);
3400178676Ssam
3401198429Srpaulo	/* Disable interrupts. */
3402201209Srpaulo	IWN_WRITE(sc, IWN_INT_MASK, 0);
3403178676Ssam
3404201209Srpaulo	/* Read interrupts from ICT (fast) or from registers (slow). */
3405201209Srpaulo	if (sc->sc_flags & IWN_FLAG_USE_ICT) {
3406201209Srpaulo		tmp = 0;
3407201209Srpaulo		while (sc->ict[sc->ict_cur] != 0) {
3408201209Srpaulo			tmp |= sc->ict[sc->ict_cur];
3409201209Srpaulo			sc->ict[sc->ict_cur] = 0;	/* Acknowledge. */
3410201209Srpaulo			sc->ict_cur = (sc->ict_cur + 1) % IWN_ICT_COUNT;
3411201209Srpaulo		}
3412201209Srpaulo		tmp = le32toh(tmp);
3413206444Sbschmidt		if (tmp == 0xffffffff)	/* Shouldn't happen. */
3414206444Sbschmidt			tmp = 0;
3415206444Sbschmidt		else if (tmp & 0xc0000)	/* Workaround a HW bug. */
3416206444Sbschmidt			tmp |= 0x8000;
3417201209Srpaulo		r1 = (tmp & 0xff00) << 16 | (tmp & 0xff);
3418201209Srpaulo		r2 = 0;	/* Unused. */
3419201209Srpaulo	} else {
3420201209Srpaulo		r1 = IWN_READ(sc, IWN_INT);
3421201209Srpaulo		if (r1 == 0xffffffff || (r1 & 0xfffffff0) == 0xa5a5a5a0)
3422201209Srpaulo			return;	/* Hardware gone! */
3423201209Srpaulo		r2 = IWN_READ(sc, IWN_FH_INT);
3424201209Srpaulo	}
3425178676Ssam
3426253705Sadrian	DPRINTF(sc, IWN_DEBUG_INTR, "interrupt reg1=0x%08x reg2=0x%08x\n"
3427253705Sadrian    , r1, r2);
3428198429Srpaulo
3429201209Srpaulo	if (r1 == 0 && r2 == 0)
3430198429Srpaulo		goto done;	/* Interrupt not for us. */
3431178676Ssam
3432198429Srpaulo	/* Acknowledge interrupts. */
3433198429Srpaulo	IWN_WRITE(sc, IWN_INT, r1);
3434201209Srpaulo	if (!(sc->sc_flags & IWN_FLAG_USE_ICT))
3435201209Srpaulo		IWN_WRITE(sc, IWN_FH_INT, r2);
3436178676Ssam
3437198429Srpaulo	if (r1 & IWN_INT_RF_TOGGLED) {
3438191746Sthompsa		iwn_rftoggle_intr(sc);
3439201209Srpaulo		goto done;
3440198429Srpaulo	}
3441198429Srpaulo	if (r1 & IWN_INT_CT_REACHED) {
3442198429Srpaulo		device_printf(sc->sc_dev, "%s: critical temperature reached!\n",
3443198429Srpaulo		    __func__);
3444198429Srpaulo	}
3445198429Srpaulo	if (r1 & (IWN_INT_SW_ERR | IWN_INT_HW_ERR)) {
3446220724Sbschmidt		device_printf(sc->sc_dev, "%s: fatal firmware error\n",
3447220724Sbschmidt		    __func__);
3448253866Sadrian#ifdef	IWN_DEBUG
3449253866Sadrian		iwn_debug_register(sc);
3450253866Sadrian#endif
3451220725Sbschmidt		/* Dump firmware error log and stop. */
3452201209Srpaulo		iwn_fatal_intr(sc);
3453201209Srpaulo		ifp->if_flags &= ~IFF_UP;
3454201209Srpaulo		iwn_stop_locked(sc);
3455178676Ssam		goto done;
3456178676Ssam	}
3457201209Srpaulo	if ((r1 & (IWN_INT_FH_RX | IWN_INT_SW_RX | IWN_INT_RX_PERIODIC)) ||
3458201209Srpaulo	    (r2 & IWN_FH_INT_RX)) {
3459201209Srpaulo		if (sc->sc_flags & IWN_FLAG_USE_ICT) {
3460201209Srpaulo			if (r1 & (IWN_INT_FH_RX | IWN_INT_SW_RX))
3461201209Srpaulo				IWN_WRITE(sc, IWN_FH_INT, IWN_FH_INT_RX);
3462201209Srpaulo			IWN_WRITE_1(sc, IWN_INT_PERIODIC,
3463201209Srpaulo			    IWN_INT_PERIODIC_DIS);
3464201209Srpaulo			iwn_notif_intr(sc);
3465201209Srpaulo			if (r1 & (IWN_INT_FH_RX | IWN_INT_SW_RX)) {
3466201209Srpaulo				IWN_WRITE_1(sc, IWN_INT_PERIODIC,
3467201209Srpaulo				    IWN_INT_PERIODIC_ENA);
3468201209Srpaulo			}
3469201209Srpaulo		} else
3470201209Srpaulo			iwn_notif_intr(sc);
3471201209Srpaulo	}
3472178676Ssam
3473201209Srpaulo	if ((r1 & IWN_INT_FH_TX) || (r2 & IWN_FH_INT_TX)) {
3474201209Srpaulo		if (sc->sc_flags & IWN_FLAG_USE_ICT)
3475201209Srpaulo			IWN_WRITE(sc, IWN_FH_INT, IWN_FH_INT_TX);
3476198429Srpaulo		wakeup(sc);	/* FH DMA transfer completed. */
3477201209Srpaulo	}
3478198429Srpaulo
3479198429Srpaulo	if (r1 & IWN_INT_ALIVE)
3480198429Srpaulo		wakeup(sc);	/* Firmware is alive. */
3481198429Srpaulo
3482198429Srpaulo	if (r1 & IWN_INT_WAKEUP)
3483198429Srpaulo		iwn_wakeup_intr(sc);
3484198429Srpaulo
3485201209Srpaulodone:
3486198429Srpaulo	/* Re-enable interrupts. */
3487201209Srpaulo	if (ifp->if_flags & IFF_UP)
3488201209Srpaulo		IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask);
3489198429Srpaulo
3490178676Ssam	IWN_UNLOCK(sc);
3491178676Ssam}
3492178676Ssam
3493198429Srpaulo/*
3494198429Srpaulo * Update TX scheduler ring when transmitting an 802.11 frame (4965AGN and
3495220725Sbschmidt * 5000 adapters use a slightly different format).
3496198429Srpaulo */
3497206477Sbschmidtstatic void
3498198429Srpauloiwn4965_update_sched(struct iwn_softc *sc, int qid, int idx, uint8_t id,
3499198429Srpaulo    uint16_t len)
3500178676Ssam{
3501198429Srpaulo	uint16_t *w = &sc->sched[qid * IWN4965_SCHED_COUNT + idx];
3502178676Ssam
3503253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
3504253705Sadrian
3505198429Srpaulo	*w = htole16(len + 8);
3506198439Srpaulo	bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map,
3507198439Srpaulo	    BUS_DMASYNC_PREWRITE);
3508201209Srpaulo	if (idx < IWN_SCHED_WINSZ) {
3509198429Srpaulo		*(w + IWN_TX_RING_COUNT) = *w;
3510198439Srpaulo		bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map,
3511198439Srpaulo		    BUS_DMASYNC_PREWRITE);
3512198439Srpaulo	}
3513198429Srpaulo}
3514198429Srpaulo
3515206477Sbschmidtstatic void
3516198429Srpauloiwn5000_update_sched(struct iwn_softc *sc, int qid, int idx, uint8_t id,
3517198429Srpaulo    uint16_t len)
3518198429Srpaulo{
3519198429Srpaulo	uint16_t *w = &sc->sched[qid * IWN5000_SCHED_COUNT + idx];
3520198429Srpaulo
3521253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
3522253705Sadrian
3523198429Srpaulo	*w = htole16(id << 12 | (len + 8));
3524198439Srpaulo	bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map,
3525198439Srpaulo	    BUS_DMASYNC_PREWRITE);
3526198439Srpaulo	if (idx < IWN_SCHED_WINSZ) {
3527198429Srpaulo		*(w + IWN_TX_RING_COUNT) = *w;
3528198439Srpaulo		bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map,
3529198439Srpaulo		    BUS_DMASYNC_PREWRITE);
3530198439Srpaulo	}
3531198429Srpaulo}
3532198429Srpaulo
3533206475Sbschmidt#ifdef notyet
3534206477Sbschmidtstatic void
3535198429Srpauloiwn5000_reset_sched(struct iwn_softc *sc, int qid, int idx)
3536198429Srpaulo{
3537198429Srpaulo	uint16_t *w = &sc->sched[qid * IWN5000_SCHED_COUNT + idx];
3538198429Srpaulo
3539253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
3540253705Sadrian
3541198429Srpaulo	*w = (*w & htole16(0xf000)) | htole16(1);
3542198439Srpaulo	bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map,
3543198439Srpaulo	    BUS_DMASYNC_PREWRITE);
3544198439Srpaulo	if (idx < IWN_SCHED_WINSZ) {
3545198429Srpaulo		*(w + IWN_TX_RING_COUNT) = *w;
3546198439Srpaulo		bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map,
3547206443Sbschmidt		    BUS_DMASYNC_PREWRITE);
3548198439Srpaulo	}
3549198429Srpaulo}
3550206475Sbschmidt#endif
3551198429Srpaulo
3552206477Sbschmidtstatic int
3553220720Sbschmidtiwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
3554178676Ssam{
3555221651Sbschmidt	struct iwn_ops *ops = &sc->ops;
3556198429Srpaulo	const struct ieee80211_txparam *tp;
3557178676Ssam	struct ieee80211vap *vap = ni->ni_vap;
3558178676Ssam	struct ieee80211com *ic = ni->ni_ic;
3559198429Srpaulo	struct iwn_node *wn = (void *)ni;
3560220720Sbschmidt	struct iwn_tx_ring *ring;
3561178676Ssam	struct iwn_tx_desc *desc;
3562178676Ssam	struct iwn_tx_data *data;
3563178676Ssam	struct iwn_tx_cmd *cmd;
3564178676Ssam	struct iwn_cmd_data *tx;
3565178676Ssam	struct ieee80211_frame *wh;
3566198429Srpaulo	struct ieee80211_key *k = NULL;
3567220700Sbschmidt	struct mbuf *m1;
3568178676Ssam	uint32_t flags;
3569220720Sbschmidt	uint16_t qos;
3570178676Ssam	u_int hdrlen;
3571220723Sbschmidt	bus_dma_segment_t *seg, segs[IWN_MAX_SCATTER];
3572220723Sbschmidt	uint8_t tid, ridx, txant, type;
3573220720Sbschmidt	int ac, i, totlen, error, pad, nsegs = 0, rate;
3574178676Ssam
3575253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
3576253705Sadrian
3577178676Ssam	IWN_LOCK_ASSERT(sc);
3578178676Ssam
3579198429Srpaulo	wh = mtod(m, struct ieee80211_frame *);
3580198429Srpaulo	hdrlen = ieee80211_anyhdrsize(wh);
3581178676Ssam	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
3582178676Ssam
3583220720Sbschmidt	/* Select EDCA Access Category and TX ring for this frame. */
3584220720Sbschmidt	if (IEEE80211_QOS_HAS_SEQ(wh)) {
3585220720Sbschmidt		qos = ((const struct ieee80211_qosframe *)wh)->i_qos[0];
3586220720Sbschmidt		tid = qos & IEEE80211_QOS_TID;
3587220720Sbschmidt	} else {
3588220720Sbschmidt		qos = 0;
3589220720Sbschmidt		tid = 0;
3590220720Sbschmidt	}
3591220720Sbschmidt	ac = M_WME_GETAC(m);
3592234321Sbschmidt	if (m->m_flags & M_AMPDU_MPDU) {
3593221651Sbschmidt		struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[ac];
3594221651Sbschmidt
3595235686Sbschmidt		if (!IEEE80211_AMPDU_RUNNING(tap)) {
3596235686Sbschmidt			m_freem(m);
3597235686Sbschmidt			return EINVAL;
3598235686Sbschmidt		}
3599235686Sbschmidt
3600234321Sbschmidt		ac = *(int *)tap->txa_private;
3601221651Sbschmidt		*(uint16_t *)wh->i_seq =
3602221651Sbschmidt		    htole16(ni->ni_txseqs[tid] << IEEE80211_SEQ_SEQ_SHIFT);
3603221651Sbschmidt		ni->ni_txseqs[tid]++;
3604221651Sbschmidt	}
3605234321Sbschmidt	ring = &sc->txq[ac];
3606198429Srpaulo	desc = &ring->desc[ring->cur];
3607198429Srpaulo	data = &ring->data[ring->cur];
3608198429Srpaulo
3609198429Srpaulo	/* Choose a TX rate index. */
3610201209Srpaulo	tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)];
3611178676Ssam	if (type == IEEE80211_FC0_TYPE_MGT)
3612178676Ssam		rate = tp->mgmtrate;
3613198429Srpaulo	else if (IEEE80211_IS_MULTICAST(wh->i_addr1))
3614178676Ssam		rate = tp->mcastrate;
3615178676Ssam	else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE)
3616178676Ssam		rate = tp->ucastrate;
3617178676Ssam	else {
3618206358Srpaulo		/* XXX pass pktlen */
3619206358Srpaulo		(void) ieee80211_ratectl_rate(ni, NULL, 0);
3620178676Ssam		rate = ni->ni_txrate;
3621178676Ssam	}
3622252727Sadrian	ridx = ieee80211_legacy_rate_lookup(ic->ic_rt,
3623252727Sadrian	    rate & IEEE80211_RATE_VAL);
3624178676Ssam
3625198429Srpaulo	/* Encrypt the frame if need be. */
3626178676Ssam	if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
3627220725Sbschmidt		/* Retrieve key for TX. */
3628198429Srpaulo		k = ieee80211_crypto_encap(ni, m);
3629178676Ssam		if (k == NULL) {
3630198429Srpaulo			m_freem(m);
3631178676Ssam			return ENOBUFS;
3632178676Ssam		}
3633220725Sbschmidt		/* 802.11 header may have moved. */
3634198429Srpaulo		wh = mtod(m, struct ieee80211_frame *);
3635198429Srpaulo	}
3636198429Srpaulo	totlen = m->m_pkthdr.len;
3637178676Ssam
3638192468Ssam	if (ieee80211_radiotap_active_vap(vap)) {
3639178676Ssam		struct iwn_tx_radiotap_header *tap = &sc->sc_txtap;
3640178676Ssam
3641178676Ssam		tap->wt_flags = 0;
3642221648Sbschmidt		tap->wt_rate = rate;
3643178676Ssam		if (k != NULL)
3644178676Ssam			tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP;
3645178676Ssam
3646198429Srpaulo		ieee80211_radiotap_tx(vap, m);
3647178676Ssam	}
3648178676Ssam
3649198429Srpaulo	/* Prepare TX firmware command. */
3650198429Srpaulo	cmd = &ring->cmd[ring->cur];
3651198429Srpaulo	cmd->code = IWN_CMD_TX_DATA;
3652198429Srpaulo	cmd->flags = 0;
3653198429Srpaulo	cmd->qid = ring->qid;
3654198429Srpaulo	cmd->idx = ring->cur;
3655198429Srpaulo
3656198429Srpaulo	tx = (struct iwn_cmd_data *)cmd->data;
3657198429Srpaulo	/* NB: No need to clear tx, all fields are reinitialized here. */
3658198429Srpaulo	tx->scratch = 0;	/* clear "scratch" area */
3659198429Srpaulo
3660198429Srpaulo	flags = 0;
3661220720Sbschmidt	if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
3662220720Sbschmidt		/* Unicast frame, check if an ACK is expected. */
3663220720Sbschmidt		if (!qos || (qos & IEEE80211_QOS_ACKPOLICY) !=
3664220720Sbschmidt		    IEEE80211_QOS_ACKPOLICY_NOACK)
3665220720Sbschmidt			flags |= IWN_TX_NEED_ACK;
3666220720Sbschmidt	}
3667198429Srpaulo	if ((wh->i_fc[0] &
3668198429Srpaulo	    (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) ==
3669198429Srpaulo	    (IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_BAR))
3670198429Srpaulo		flags |= IWN_TX_IMM_BA;		/* Cannot happen yet. */
3671178676Ssam
3672198429Srpaulo	if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG)
3673198429Srpaulo		flags |= IWN_TX_MORE_FRAG;	/* Cannot happen yet. */
3674178676Ssam
3675198429Srpaulo	/* Check if frame must be protected using RTS/CTS or CTS-to-self. */
3676198429Srpaulo	if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
3677198429Srpaulo		/* NB: Group frames are sent using CCK in 802.11b/g. */
3678198429Srpaulo		if (totlen + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) {
3679198429Srpaulo			flags |= IWN_TX_NEED_RTS;
3680178676Ssam		} else if ((ic->ic_flags & IEEE80211_F_USEPROT) &&
3681201209Srpaulo		    ridx >= IWN_RIDX_OFDM6) {
3682178676Ssam			if (ic->ic_protmode == IEEE80211_PROT_CTSONLY)
3683198429Srpaulo				flags |= IWN_TX_NEED_CTS;
3684178676Ssam			else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS)
3685198429Srpaulo				flags |= IWN_TX_NEED_RTS;
3686178676Ssam		}
3687198429Srpaulo		if (flags & (IWN_TX_NEED_RTS | IWN_TX_NEED_CTS)) {
3688198429Srpaulo			if (sc->hw_type != IWN_HW_REV_TYPE_4965) {
3689198429Srpaulo				/* 5000 autoselects RTS/CTS or CTS-to-self. */
3690198429Srpaulo				flags &= ~(IWN_TX_NEED_RTS | IWN_TX_NEED_CTS);
3691198429Srpaulo				flags |= IWN_TX_NEED_PROTECTION;
3692198429Srpaulo			} else
3693198429Srpaulo				flags |= IWN_TX_FULL_TXOP;
3694198429Srpaulo		}
3695201209Srpaulo	}
3696178676Ssam
3697198429Srpaulo	if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
3698198429Srpaulo	    type != IEEE80211_FC0_TYPE_DATA)
3699220728Sbschmidt		tx->id = sc->broadcast_id;
3700198429Srpaulo	else
3701198429Srpaulo		tx->id = wn->id;
3702198429Srpaulo
3703178676Ssam	if (type == IEEE80211_FC0_TYPE_MGT) {
3704178676Ssam		uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
3705178676Ssam
3706198429Srpaulo		/* Tell HW to set timestamp in probe responses. */
3707178676Ssam		if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
3708178676Ssam			flags |= IWN_TX_INSERT_TSTAMP;
3709178676Ssam		if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ ||
3710178676Ssam		    subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ)
3711198429Srpaulo			tx->timeout = htole16(3);
3712178676Ssam		else
3713198429Srpaulo			tx->timeout = htole16(2);
3714178676Ssam	} else
3715198429Srpaulo		tx->timeout = htole16(0);
3716178676Ssam
3717178676Ssam	if (hdrlen & 3) {
3718201209Srpaulo		/* First segment length must be a multiple of 4. */
3719178676Ssam		flags |= IWN_TX_NEED_PADDING;
3720178676Ssam		pad = 4 - (hdrlen & 3);
3721178676Ssam	} else
3722178676Ssam		pad = 0;
3723178676Ssam
3724198429Srpaulo	tx->len = htole16(totlen);
3725220720Sbschmidt	tx->tid = tid;
3726201209Srpaulo	tx->rts_ntries = 60;
3727201209Srpaulo	tx->data_ntries = 15;
3728178676Ssam	tx->lifetime = htole32(IWN_LIFETIME_INFINITE);
3729252727Sadrian	tx->rate = iwn_rate_to_plcp(sc, ni, rate);
3730220728Sbschmidt	if (tx->id == sc->broadcast_id) {
3731201209Srpaulo		/* Group or management frame. */
3732201209Srpaulo		tx->linkq = 0;
3733198429Srpaulo		/* XXX Alternate between antenna A and B? */
3734201209Srpaulo		txant = IWN_LSB(sc->txchainmask);
3735221648Sbschmidt		tx->rate |= htole32(IWN_RFLAG_ANT(txant));
3736201209Srpaulo	} else {
3737220715Sbschmidt		tx->linkq = ni->ni_rates.rs_nrates - ridx - 1;
3738201209Srpaulo		flags |= IWN_TX_LINKQ;	/* enable MRR */
3739201209Srpaulo	}
3740198429Srpaulo	/* Set physical address of "scratch area". */
3741201209Srpaulo	tx->loaddr = htole32(IWN_LOADDR(data->scratch_paddr));
3742201209Srpaulo	tx->hiaddr = IWN_HIADDR(data->scratch_paddr);
3743178676Ssam
3744198429Srpaulo	/* Copy 802.11 header in TX command. */
3745178676Ssam	memcpy((uint8_t *)(tx + 1), wh, hdrlen);
3746178676Ssam
3747198429Srpaulo	/* Trim 802.11 header. */
3748198429Srpaulo	m_adj(m, hdrlen);
3749198429Srpaulo	tx->security = 0;
3750198429Srpaulo	tx->flags = htole32(flags);
3751198429Srpaulo
3752220700Sbschmidt	error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m, segs,
3753220700Sbschmidt	    &nsegs, BUS_DMA_NOWAIT);
3754220700Sbschmidt	if (error != 0) {
3755220700Sbschmidt		if (error != EFBIG) {
3756220700Sbschmidt			device_printf(sc->sc_dev,
3757220700Sbschmidt			    "%s: can't map mbuf (error %d)\n", __func__, error);
3758220700Sbschmidt			m_freem(m);
3759220700Sbschmidt			return error;
3760178676Ssam		}
3761220700Sbschmidt		/* Too many DMA segments, linearize mbuf. */
3762243857Sglebius		m1 = m_collapse(m, M_NOWAIT, IWN_MAX_SCATTER);
3763220700Sbschmidt		if (m1 == NULL) {
3764220700Sbschmidt			device_printf(sc->sc_dev,
3765220700Sbschmidt			    "%s: could not defrag mbuf\n", __func__);
3766220700Sbschmidt			m_freem(m);
3767220700Sbschmidt			return ENOBUFS;
3768220700Sbschmidt		}
3769220700Sbschmidt		m = m1;
3770220700Sbschmidt
3771220700Sbschmidt		error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m,
3772220700Sbschmidt		    segs, &nsegs, BUS_DMA_NOWAIT);
3773178676Ssam		if (error != 0) {
3774178676Ssam			device_printf(sc->sc_dev,
3775220700Sbschmidt			    "%s: can't map mbuf (error %d)\n", __func__, error);
3776198429Srpaulo			m_freem(m);
3777178676Ssam			return error;
3778178676Ssam		}
3779178676Ssam	}
3780178676Ssam
3781198429Srpaulo	data->m = m;
3782178676Ssam	data->ni = ni;
3783178676Ssam
3784178676Ssam	DPRINTF(sc, IWN_DEBUG_XMIT, "%s: qid %d idx %d len %d nsegs %d\n",
3785198429Srpaulo	    __func__, ring->qid, ring->cur, m->m_pkthdr.len, nsegs);
3786178676Ssam
3787198429Srpaulo	/* Fill TX descriptor. */
3788220700Sbschmidt	desc->nsegs = 1;
3789220700Sbschmidt	if (m->m_len != 0)
3790220700Sbschmidt		desc->nsegs += nsegs;
3791198429Srpaulo	/* First DMA segment is used by the TX command. */
3792201209Srpaulo	desc->segs[0].addr = htole32(IWN_LOADDR(data->cmd_paddr));
3793201209Srpaulo	desc->segs[0].len  = htole16(IWN_HIADDR(data->cmd_paddr) |
3794198429Srpaulo	    (4 + sizeof (*tx) + hdrlen + pad) << 4);
3795198429Srpaulo	/* Other DMA segments are for data payload. */
3796220700Sbschmidt	seg = &segs[0];
3797178676Ssam	for (i = 1; i <= nsegs; i++) {
3798220700Sbschmidt		desc->segs[i].addr = htole32(IWN_LOADDR(seg->ds_addr));
3799220700Sbschmidt		desc->segs[i].len  = htole16(IWN_HIADDR(seg->ds_addr) |
3800220700Sbschmidt		    seg->ds_len << 4);
3801220700Sbschmidt		seg++;
3802178676Ssam	}
3803178676Ssam
3804201209Srpaulo	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE);
3805201209Srpaulo	bus_dmamap_sync(ring->data_dmat, ring->cmd_dma.map,
3806198429Srpaulo	    BUS_DMASYNC_PREWRITE);
3807198429Srpaulo	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
3808198429Srpaulo	    BUS_DMASYNC_PREWRITE);
3809178676Ssam
3810198429Srpaulo	/* Update TX scheduler. */
3811221945Sbschmidt	if (ring->qid >= sc->firstaggqueue)
3812221945Sbschmidt		ops->update_sched(sc, ring->qid, ring->cur, tx->id, totlen);
3813178676Ssam
3814198429Srpaulo	/* Kick TX ring. */
3815178676Ssam	ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT;
3816198429Srpaulo	IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur);
3817178676Ssam
3818198429Srpaulo	/* Mark TX ring as full if we reach a certain threshold. */
3819198429Srpaulo	if (++ring->queued > IWN_TX_RING_HIMARK)
3820198429Srpaulo		sc->qfullmsk |= 1 << ring->qid;
3821178676Ssam
3822253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
3823253705Sadrian
3824178676Ssam	return 0;
3825178676Ssam}
3826178676Ssam
3827178676Ssamstatic int
3828201209Srpauloiwn_tx_data_raw(struct iwn_softc *sc, struct mbuf *m,
3829220720Sbschmidt    struct ieee80211_node *ni, const struct ieee80211_bpf_params *params)
3830178676Ssam{
3831221651Sbschmidt	struct iwn_ops *ops = &sc->ops;
3832178676Ssam	struct ifnet *ifp = sc->sc_ifp;
3833198429Srpaulo	struct ieee80211vap *vap = ni->ni_vap;
3834198429Srpaulo	struct ieee80211com *ic = ifp->if_l2com;
3835198429Srpaulo	struct iwn_tx_cmd *cmd;
3836198429Srpaulo	struct iwn_cmd_data *tx;
3837198429Srpaulo	struct ieee80211_frame *wh;
3838220720Sbschmidt	struct iwn_tx_ring *ring;
3839178676Ssam	struct iwn_tx_desc *desc;
3840178676Ssam	struct iwn_tx_data *data;
3841220700Sbschmidt	struct mbuf *m1;
3842220700Sbschmidt	bus_dma_segment_t *seg, segs[IWN_MAX_SCATTER];
3843198429Srpaulo	uint32_t flags;
3844198429Srpaulo	u_int hdrlen;
3845220720Sbschmidt	int ac, totlen, error, pad, nsegs = 0, i, rate;
3846201209Srpaulo	uint8_t ridx, type, txant;
3847178676Ssam
3848253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
3849253705Sadrian
3850198429Srpaulo	IWN_LOCK_ASSERT(sc);
3851178676Ssam
3852201209Srpaulo	wh = mtod(m, struct ieee80211_frame *);
3853198429Srpaulo	hdrlen = ieee80211_anyhdrsize(wh);
3854198429Srpaulo	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
3855198429Srpaulo
3856220720Sbschmidt	ac = params->ibp_pri & 3;
3857220720Sbschmidt
3858220720Sbschmidt	ring = &sc->txq[ac];
3859178676Ssam	desc = &ring->desc[ring->cur];
3860178676Ssam	data = &ring->data[ring->cur];
3861178676Ssam
3862198429Srpaulo	/* Choose a TX rate index. */
3863198429Srpaulo	rate = params->ibp_rate0;
3864252727Sadrian	ridx = ieee80211_legacy_rate_lookup(ic->ic_rt,
3865252727Sadrian	    rate & IEEE80211_RATE_VAL);
3866221648Sbschmidt	if (ridx == (uint8_t)-1) {
3867198429Srpaulo		/* XXX fall back to mcast/mgmt rate? */
3868201209Srpaulo		m_freem(m);
3869198429Srpaulo		return EINVAL;
3870198429Srpaulo	}
3871198429Srpaulo
3872201209Srpaulo	totlen = m->m_pkthdr.len;
3873198429Srpaulo
3874201209Srpaulo	/* Prepare TX firmware command. */
3875198429Srpaulo	cmd = &ring->cmd[ring->cur];
3876198429Srpaulo	cmd->code = IWN_CMD_TX_DATA;
3877198429Srpaulo	cmd->flags = 0;
3878198429Srpaulo	cmd->qid = ring->qid;
3879198429Srpaulo	cmd->idx = ring->cur;
3880198429Srpaulo
3881198429Srpaulo	tx = (struct iwn_cmd_data *)cmd->data;
3882201209Srpaulo	/* NB: No need to clear tx, all fields are reinitialized here. */
3883198429Srpaulo	tx->scratch = 0;	/* clear "scratch" area */
3884198429Srpaulo
3885198429Srpaulo	flags = 0;
3886198429Srpaulo	if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0)
3887198429Srpaulo		flags |= IWN_TX_NEED_ACK;
3888198429Srpaulo	if (params->ibp_flags & IEEE80211_BPF_RTS) {
3889198429Srpaulo		if (sc->hw_type != IWN_HW_REV_TYPE_4965) {
3890198429Srpaulo			/* 5000 autoselects RTS/CTS or CTS-to-self. */
3891198429Srpaulo			flags &= ~IWN_TX_NEED_RTS;
3892198429Srpaulo			flags |= IWN_TX_NEED_PROTECTION;
3893198429Srpaulo		} else
3894198429Srpaulo			flags |= IWN_TX_NEED_RTS | IWN_TX_FULL_TXOP;
3895198429Srpaulo	}
3896198429Srpaulo	if (params->ibp_flags & IEEE80211_BPF_CTS) {
3897198429Srpaulo		if (sc->hw_type != IWN_HW_REV_TYPE_4965) {
3898198429Srpaulo			/* 5000 autoselects RTS/CTS or CTS-to-self. */
3899198429Srpaulo			flags &= ~IWN_TX_NEED_CTS;
3900198429Srpaulo			flags |= IWN_TX_NEED_PROTECTION;
3901198429Srpaulo		} else
3902198429Srpaulo			flags |= IWN_TX_NEED_CTS | IWN_TX_FULL_TXOP;
3903198429Srpaulo	}
3904198429Srpaulo	if (type == IEEE80211_FC0_TYPE_MGT) {
3905198429Srpaulo		uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
3906198429Srpaulo
3907220725Sbschmidt		/* Tell HW to set timestamp in probe responses. */
3908198429Srpaulo		if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
3909198429Srpaulo			flags |= IWN_TX_INSERT_TSTAMP;
3910198429Srpaulo
3911198429Srpaulo		if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ ||
3912198429Srpaulo		    subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ)
3913198429Srpaulo			tx->timeout = htole16(3);
3914198429Srpaulo		else
3915198429Srpaulo			tx->timeout = htole16(2);
3916198429Srpaulo	} else
3917198429Srpaulo		tx->timeout = htole16(0);
3918198429Srpaulo
3919198429Srpaulo	if (hdrlen & 3) {
3920201209Srpaulo		/* First segment length must be a multiple of 4. */
3921198429Srpaulo		flags |= IWN_TX_NEED_PADDING;
3922198429Srpaulo		pad = 4 - (hdrlen & 3);
3923198429Srpaulo	} else
3924198429Srpaulo		pad = 0;
3925198429Srpaulo
3926198429Srpaulo	if (ieee80211_radiotap_active_vap(vap)) {
3927198429Srpaulo		struct iwn_tx_radiotap_header *tap = &sc->sc_txtap;
3928198429Srpaulo
3929198429Srpaulo		tap->wt_flags = 0;
3930198429Srpaulo		tap->wt_rate = rate;
3931198429Srpaulo
3932201209Srpaulo		ieee80211_radiotap_tx(vap, m);
3933198429Srpaulo	}
3934198429Srpaulo
3935198429Srpaulo	tx->len = htole16(totlen);
3936198429Srpaulo	tx->tid = 0;
3937220728Sbschmidt	tx->id = sc->broadcast_id;
3938198429Srpaulo	tx->rts_ntries = params->ibp_try1;
3939198429Srpaulo	tx->data_ntries = params->ibp_try0;
3940198429Srpaulo	tx->lifetime = htole32(IWN_LIFETIME_INFINITE);
3941252727Sadrian
3942252727Sadrian	/* XXX should just use  iwn_rate_to_plcp() */
3943221648Sbschmidt	tx->rate = htole32(rate2plcp(rate));
3944221648Sbschmidt	if (ridx < IWN_RIDX_OFDM6 &&
3945221648Sbschmidt	    IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
3946221648Sbschmidt		tx->rate |= htole32(IWN_RFLAG_CCK);
3947252727Sadrian
3948201209Srpaulo	/* Group or management frame. */
3949201209Srpaulo	tx->linkq = 0;
3950201209Srpaulo	txant = IWN_LSB(sc->txchainmask);
3951221648Sbschmidt	tx->rate |= htole32(IWN_RFLAG_ANT(txant));
3952252727Sadrian
3953198429Srpaulo	/* Set physical address of "scratch area". */
3954220694Sbschmidt	tx->loaddr = htole32(IWN_LOADDR(data->scratch_paddr));
3955220694Sbschmidt	tx->hiaddr = IWN_HIADDR(data->scratch_paddr);
3956198429Srpaulo
3957198429Srpaulo	/* Copy 802.11 header in TX command. */
3958198429Srpaulo	memcpy((uint8_t *)(tx + 1), wh, hdrlen);
3959198429Srpaulo
3960198429Srpaulo	/* Trim 802.11 header. */
3961201209Srpaulo	m_adj(m, hdrlen);
3962198429Srpaulo	tx->security = 0;
3963198429Srpaulo	tx->flags = htole32(flags);
3964198429Srpaulo
3965220700Sbschmidt	error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m, segs,
3966220700Sbschmidt	    &nsegs, BUS_DMA_NOWAIT);
3967220700Sbschmidt	if (error != 0) {
3968220700Sbschmidt		if (error != EFBIG) {
3969220700Sbschmidt			device_printf(sc->sc_dev,
3970220700Sbschmidt			    "%s: can't map mbuf (error %d)\n", __func__, error);
3971220700Sbschmidt			m_freem(m);
3972220700Sbschmidt			return error;
3973178676Ssam		}
3974220700Sbschmidt		/* Too many DMA segments, linearize mbuf. */
3975243857Sglebius		m1 = m_collapse(m, M_NOWAIT, IWN_MAX_SCATTER);
3976220700Sbschmidt		if (m1 == NULL) {
3977220700Sbschmidt			device_printf(sc->sc_dev,
3978220700Sbschmidt			    "%s: could not defrag mbuf\n", __func__);
3979220700Sbschmidt			m_freem(m);
3980220700Sbschmidt			return ENOBUFS;
3981220700Sbschmidt		}
3982220700Sbschmidt		m = m1;
3983220700Sbschmidt
3984220700Sbschmidt		error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m,
3985220700Sbschmidt		    segs, &nsegs, BUS_DMA_NOWAIT);
3986178676Ssam		if (error != 0) {
3987178676Ssam			device_printf(sc->sc_dev,
3988220700Sbschmidt			    "%s: can't map mbuf (error %d)\n", __func__, error);
3989201209Srpaulo			m_freem(m);
3990178676Ssam			return error;
3991178676Ssam		}
3992178676Ssam	}
3993178676Ssam
3994201209Srpaulo	data->m = m;
3995178676Ssam	data->ni = ni;
3996178676Ssam
3997178676Ssam	DPRINTF(sc, IWN_DEBUG_XMIT, "%s: qid %d idx %d len %d nsegs %d\n",
3998201209Srpaulo	    __func__, ring->qid, ring->cur, m->m_pkthdr.len, nsegs);
3999178676Ssam
4000198429Srpaulo	/* Fill TX descriptor. */
4001220700Sbschmidt	desc->nsegs = 1;
4002220700Sbschmidt	if (m->m_len != 0)
4003220700Sbschmidt		desc->nsegs += nsegs;
4004198429Srpaulo	/* First DMA segment is used by the TX command. */
4005201209Srpaulo	desc->segs[0].addr = htole32(IWN_LOADDR(data->cmd_paddr));
4006201209Srpaulo	desc->segs[0].len  = htole16(IWN_HIADDR(data->cmd_paddr) |
4007198429Srpaulo	    (4 + sizeof (*tx) + hdrlen + pad) << 4);
4008198429Srpaulo	/* Other DMA segments are for data payload. */
4009220700Sbschmidt	seg = &segs[0];
4010178676Ssam	for (i = 1; i <= nsegs; i++) {
4011220700Sbschmidt		desc->segs[i].addr = htole32(IWN_LOADDR(seg->ds_addr));
4012220700Sbschmidt		desc->segs[i].len  = htole16(IWN_HIADDR(seg->ds_addr) |
4013220700Sbschmidt		    seg->ds_len << 4);
4014220700Sbschmidt		seg++;
4015178676Ssam	}
4016178676Ssam
4017201209Srpaulo	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE);
4018201209Srpaulo	bus_dmamap_sync(ring->data_dmat, ring->cmd_dma.map,
4019201209Srpaulo	    BUS_DMASYNC_PREWRITE);
4020201209Srpaulo	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
4021201209Srpaulo	    BUS_DMASYNC_PREWRITE);
4022201209Srpaulo
4023198429Srpaulo	/* Update TX scheduler. */
4024221945Sbschmidt	if (ring->qid >= sc->firstaggqueue)
4025221945Sbschmidt		ops->update_sched(sc, ring->qid, ring->cur, tx->id, totlen);
4026178676Ssam
4027198429Srpaulo	/* Kick TX ring. */
4028178676Ssam	ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT;
4029198429Srpaulo	IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur);
4030178676Ssam
4031198429Srpaulo	/* Mark TX ring as full if we reach a certain threshold. */
4032198429Srpaulo	if (++ring->queued > IWN_TX_RING_HIMARK)
4033198429Srpaulo		sc->qfullmsk |= 1 << ring->qid;
4034178676Ssam
4035253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
4036253705Sadrian
4037178676Ssam	return 0;
4038178676Ssam}
4039178676Ssam
4040178676Ssamstatic int
4041178676Ssamiwn_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
4042220720Sbschmidt    const struct ieee80211_bpf_params *params)
4043178676Ssam{
4044178676Ssam	struct ieee80211com *ic = ni->ni_ic;
4045178676Ssam	struct ifnet *ifp = ic->ic_ifp;
4046178676Ssam	struct iwn_softc *sc = ifp->if_softc;
4047198429Srpaulo	int error = 0;
4048178676Ssam
4049253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
4050253705Sadrian
4051178676Ssam	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
4052178676Ssam		ieee80211_free_node(ni);
4053178676Ssam		m_freem(m);
4054178676Ssam		return ENETDOWN;
4055178676Ssam	}
4056178676Ssam
4057178676Ssam	IWN_LOCK(sc);
4058178676Ssam	if (params == NULL) {
4059178676Ssam		/*
4060178676Ssam		 * Legacy path; interpret frame contents to decide
4061178676Ssam		 * precisely how to send the frame.
4062178676Ssam		 */
4063220720Sbschmidt		error = iwn_tx_data(sc, m, ni);
4064178676Ssam	} else {
4065178676Ssam		/*
4066178676Ssam		 * Caller supplied explicit parameters to use in
4067178676Ssam		 * sending the frame.
4068178676Ssam		 */
4069220720Sbschmidt		error = iwn_tx_data_raw(sc, m, ni, params);
4070178676Ssam	}
4071178676Ssam	if (error != 0) {
4072178676Ssam		/* NB: m is reclaimed on tx failure */
4073178676Ssam		ieee80211_free_node(ni);
4074178676Ssam		ifp->if_oerrors++;
4075178676Ssam	}
4076220667Sbschmidt	sc->sc_tx_timer = 5;
4077220667Sbschmidt
4078178676Ssam	IWN_UNLOCK(sc);
4079253705Sadrian
4080253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
4081253705Sadrian
4082178676Ssam	return error;
4083178676Ssam}
4084178676Ssam
4085206477Sbschmidtstatic void
4086198429Srpauloiwn_start(struct ifnet *ifp)
4087198429Srpaulo{
4088198429Srpaulo	struct iwn_softc *sc = ifp->if_softc;
4089198429Srpaulo
4090198429Srpaulo	IWN_LOCK(sc);
4091198429Srpaulo	iwn_start_locked(ifp);
4092198429Srpaulo	IWN_UNLOCK(sc);
4093198429Srpaulo}
4094198429Srpaulo
4095206477Sbschmidtstatic void
4096198429Srpauloiwn_start_locked(struct ifnet *ifp)
4097198429Srpaulo{
4098198429Srpaulo	struct iwn_softc *sc = ifp->if_softc;
4099198429Srpaulo	struct ieee80211_node *ni;
4100198429Srpaulo	struct mbuf *m;
4101198429Srpaulo
4102198429Srpaulo	IWN_LOCK_ASSERT(sc);
4103198429Srpaulo
4104220720Sbschmidt	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 ||
4105220720Sbschmidt	    (ifp->if_drv_flags & IFF_DRV_OACTIVE))
4106220720Sbschmidt		return;
4107220720Sbschmidt
4108198429Srpaulo	for (;;) {
4109198429Srpaulo		if (sc->qfullmsk != 0) {
4110198429Srpaulo			ifp->if_drv_flags |= IFF_DRV_OACTIVE;
4111198429Srpaulo			break;
4112198429Srpaulo		}
4113198429Srpaulo		IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
4114198429Srpaulo		if (m == NULL)
4115198429Srpaulo			break;
4116198429Srpaulo		ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
4117220720Sbschmidt		if (iwn_tx_data(sc, m, ni) != 0) {
4118220720Sbschmidt			ieee80211_free_node(ni);
4119198429Srpaulo			ifp->if_oerrors++;
4120220720Sbschmidt			continue;
4121198429Srpaulo		}
4122198429Srpaulo		sc->sc_tx_timer = 5;
4123198429Srpaulo	}
4124198429Srpaulo}
4125198429Srpaulo
4126178676Ssamstatic void
4127220667Sbschmidtiwn_watchdog(void *arg)
4128178676Ssam{
4129220667Sbschmidt	struct iwn_softc *sc = arg;
4130220667Sbschmidt	struct ifnet *ifp = sc->sc_ifp;
4131220667Sbschmidt	struct ieee80211com *ic = ifp->if_l2com;
4132178676Ssam
4133220667Sbschmidt	IWN_LOCK_ASSERT(sc);
4134220667Sbschmidt
4135220667Sbschmidt	KASSERT(ifp->if_drv_flags & IFF_DRV_RUNNING, ("not running"));
4136220667Sbschmidt
4137253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
4138253705Sadrian
4139220668Sbschmidt	if (sc->sc_tx_timer > 0) {
4140220668Sbschmidt		if (--sc->sc_tx_timer == 0) {
4141220667Sbschmidt			if_printf(ifp, "device timeout\n");
4142220667Sbschmidt			ieee80211_runtask(ic, &sc->sc_reinit_task);
4143220667Sbschmidt			return;
4144220667Sbschmidt		}
4145178676Ssam	}
4146220667Sbschmidt	callout_reset(&sc->watchdog_to, hz, iwn_watchdog, sc);
4147178676Ssam}
4148178676Ssam
4149206477Sbschmidtstatic int
4150178676Ssamiwn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
4151178676Ssam{
4152178676Ssam	struct iwn_softc *sc = ifp->if_softc;
4153178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
4154201209Srpaulo	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
4155178676Ssam	struct ifreq *ifr = (struct ifreq *) data;
4156201209Srpaulo	int error = 0, startall = 0, stop = 0;
4157178676Ssam
4158178676Ssam	switch (cmd) {
4159220723Sbschmidt	case SIOCGIFADDR:
4160220723Sbschmidt		error = ether_ioctl(ifp, cmd, data);
4161220723Sbschmidt		break;
4162178676Ssam	case SIOCSIFFLAGS:
4163178704Sthompsa		IWN_LOCK(sc);
4164178676Ssam		if (ifp->if_flags & IFF_UP) {
4165178676Ssam			if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
4166178676Ssam				iwn_init_locked(sc);
4167201209Srpaulo				if (IWN_READ(sc, IWN_GP_CNTRL) & IWN_GP_CNTRL_RFKILL)
4168201209Srpaulo					startall = 1;
4169201209Srpaulo				else
4170201209Srpaulo					stop = 1;
4171178676Ssam			}
4172178676Ssam		} else {
4173178676Ssam			if (ifp->if_drv_flags & IFF_DRV_RUNNING)
4174178676Ssam				iwn_stop_locked(sc);
4175178676Ssam		}
4176178704Sthompsa		IWN_UNLOCK(sc);
4177178704Sthompsa		if (startall)
4178178704Sthompsa			ieee80211_start_all(ic);
4179201209Srpaulo		else if (vap != NULL && stop)
4180201209Srpaulo			ieee80211_stop(vap);
4181178676Ssam		break;
4182178676Ssam	case SIOCGIFMEDIA:
4183178676Ssam		error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd);
4184178676Ssam		break;
4185178704Sthompsa	default:
4186178704Sthompsa		error = EINVAL;
4187178704Sthompsa		break;
4188178676Ssam	}
4189178676Ssam	return error;
4190178676Ssam}
4191178676Ssam
4192178676Ssam/*
4193178676Ssam * Send a command to the firmware.
4194178676Ssam */
4195206477Sbschmidtstatic int
4196178676Ssamiwn_cmd(struct iwn_softc *sc, int code, const void *buf, int size, int async)
4197178676Ssam{
4198178676Ssam	struct iwn_tx_ring *ring = &sc->txq[4];
4199178676Ssam	struct iwn_tx_desc *desc;
4200198429Srpaulo	struct iwn_tx_data *data;
4201178676Ssam	struct iwn_tx_cmd *cmd;
4202198439Srpaulo	struct mbuf *m;
4203178676Ssam	bus_addr_t paddr;
4204198439Srpaulo	int totlen, error;
4205178676Ssam
4206253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
4207253705Sadrian
4208221650Sbschmidt	if (async == 0)
4209221650Sbschmidt		IWN_LOCK_ASSERT(sc);
4210178676Ssam
4211178676Ssam	desc = &ring->desc[ring->cur];
4212198429Srpaulo	data = &ring->data[ring->cur];
4213198429Srpaulo	totlen = 4 + size;
4214178676Ssam
4215198439Srpaulo	if (size > sizeof cmd->data) {
4216198439Srpaulo		/* Command is too large to fit in a descriptor. */
4217198439Srpaulo		if (totlen > MCLBYTES)
4218198439Srpaulo			return EINVAL;
4219243857Sglebius		m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE);
4220198439Srpaulo		if (m == NULL)
4221198439Srpaulo			return ENOMEM;
4222198439Srpaulo		cmd = mtod(m, struct iwn_tx_cmd *);
4223201209Srpaulo		error = bus_dmamap_load(ring->data_dmat, data->map, cmd,
4224198439Srpaulo		    totlen, iwn_dma_map_addr, &paddr, BUS_DMA_NOWAIT);
4225198439Srpaulo		if (error != 0) {
4226198439Srpaulo			m_freem(m);
4227198439Srpaulo			return error;
4228198439Srpaulo		}
4229198439Srpaulo		data->m = m;
4230198439Srpaulo	} else {
4231198439Srpaulo		cmd = &ring->cmd[ring->cur];
4232198439Srpaulo		paddr = data->cmd_paddr;
4233198439Srpaulo	}
4234198439Srpaulo
4235178676Ssam	cmd->code = code;
4236178676Ssam	cmd->flags = 0;
4237178676Ssam	cmd->qid = ring->qid;
4238178676Ssam	cmd->idx = ring->cur;
4239178676Ssam	memcpy(cmd->data, buf, size);
4240178676Ssam
4241198429Srpaulo	desc->nsegs = 1;
4242198429Srpaulo	desc->segs[0].addr = htole32(IWN_LOADDR(paddr));
4243198429Srpaulo	desc->segs[0].len  = htole16(IWN_HIADDR(paddr) | totlen << 4);
4244178676Ssam
4245178676Ssam	DPRINTF(sc, IWN_DEBUG_CMD, "%s: %s (0x%x) flags %d qid %d idx %d\n",
4246178676Ssam	    __func__, iwn_intr_str(cmd->code), cmd->code,
4247178676Ssam	    cmd->flags, cmd->qid, cmd->idx);
4248178676Ssam
4249198439Srpaulo	if (size > sizeof cmd->data) {
4250201209Srpaulo		bus_dmamap_sync(ring->data_dmat, data->map,
4251198439Srpaulo		    BUS_DMASYNC_PREWRITE);
4252198439Srpaulo	} else {
4253201209Srpaulo		bus_dmamap_sync(ring->data_dmat, ring->cmd_dma.map,
4254198439Srpaulo		    BUS_DMASYNC_PREWRITE);
4255198439Srpaulo	}
4256198439Srpaulo	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
4257198439Srpaulo	    BUS_DMASYNC_PREWRITE);
4258198439Srpaulo
4259198429Srpaulo	/* Kick command ring. */
4260178676Ssam	ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT;
4261198429Srpaulo	IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur);
4262178676Ssam
4263253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
4264253705Sadrian
4265198439Srpaulo	return async ? 0 : msleep(desc, &sc->sc_mtx, PCATCH, "iwncmd", hz);
4266178676Ssam}
4267178676Ssam
4268206477Sbschmidtstatic int
4269198429Srpauloiwn4965_add_node(struct iwn_softc *sc, struct iwn_node_info *node, int async)
4270198429Srpaulo{
4271198429Srpaulo	struct iwn4965_node_info hnode;
4272198429Srpaulo	caddr_t src, dst;
4273198429Srpaulo
4274253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
4275253705Sadrian
4276198429Srpaulo	/*
4277198429Srpaulo	 * We use the node structure for 5000 Series internally (it is
4278198429Srpaulo	 * a superset of the one for 4965AGN). We thus copy the common
4279198429Srpaulo	 * fields before sending the command.
4280198429Srpaulo	 */
4281198429Srpaulo	src = (caddr_t)node;
4282198429Srpaulo	dst = (caddr_t)&hnode;
4283198429Srpaulo	memcpy(dst, src, 48);
4284198429Srpaulo	/* Skip TSC, RX MIC and TX MIC fields from ``src''. */
4285198429Srpaulo	memcpy(dst + 48, src + 72, 20);
4286198429Srpaulo	return iwn_cmd(sc, IWN_CMD_ADD_NODE, &hnode, sizeof hnode, async);
4287198429Srpaulo}
4288198429Srpaulo
4289206477Sbschmidtstatic int
4290198429Srpauloiwn5000_add_node(struct iwn_softc *sc, struct iwn_node_info *node, int async)
4291198429Srpaulo{
4292253705Sadrian
4293253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
4294253705Sadrian
4295198429Srpaulo	/* Direct mapping. */
4296198429Srpaulo	return iwn_cmd(sc, IWN_CMD_ADD_NODE, node, sizeof (*node), async);
4297198429Srpaulo}
4298198429Srpaulo
4299206477Sbschmidtstatic int
4300220715Sbschmidtiwn_set_link_quality(struct iwn_softc *sc, struct ieee80211_node *ni)
4301178676Ssam{
4302222933Sbschmidt#define	RV(v)	((v) & IEEE80211_RATE_VAL)
4303220715Sbschmidt	struct iwn_node *wn = (void *)ni;
4304220715Sbschmidt	struct ieee80211_rateset *rs = &ni->ni_rates;
4305198429Srpaulo	struct iwn_cmd_link_quality linkq;
4306220715Sbschmidt	uint8_t txant;
4307221648Sbschmidt	int i, rate, txrate;
4308178676Ssam
4309253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
4310253705Sadrian
4311198429Srpaulo	/* Use the first valid TX antenna. */
4312201209Srpaulo	txant = IWN_LSB(sc->txchainmask);
4313178676Ssam
4314198429Srpaulo	memset(&linkq, 0, sizeof linkq);
4315220715Sbschmidt	linkq.id = wn->id;
4316198429Srpaulo	linkq.antmsk_1stream = txant;
4317201209Srpaulo	linkq.antmsk_2stream = IWN_ANT_AB;
4318221651Sbschmidt	linkq.ampdu_max = 64;
4319198429Srpaulo	linkq.ampdu_threshold = 3;
4320198429Srpaulo	linkq.ampdu_limit = htole16(4000);	/* 4ms */
4321198429Srpaulo
4322220715Sbschmidt	/* Start at highest available bit-rate. */
4323221649Sbschmidt	if (IEEE80211_IS_CHAN_HT(ni->ni_chan))
4324221649Sbschmidt		txrate = ni->ni_htrates.rs_nrates - 1;
4325221649Sbschmidt	else
4326221649Sbschmidt		txrate = rs->rs_nrates - 1;
4327178676Ssam	for (i = 0; i < IWN_MAX_TX_RETRIES; i++) {
4328252727Sadrian		uint32_t plcp;
4329252727Sadrian
4330221649Sbschmidt		if (IEEE80211_IS_CHAN_HT(ni->ni_chan))
4331221649Sbschmidt			rate = IEEE80211_RATE_MCS | txrate;
4332221649Sbschmidt		else
4333222933Sbschmidt			rate = RV(rs->rs_rates[txrate]);
4334221648Sbschmidt
4335252727Sadrian		/* Do rate -> PLCP config mapping */
4336252727Sadrian		plcp = iwn_rate_to_plcp(sc, ni, rate);
4337252727Sadrian		linkq.retry[i] = plcp;
4338252727Sadrian
4339252727Sadrian		/* Special case for dual-stream rates? */
4340252727Sadrian		if ((le32toh(plcp) & IWN_RFLAG_MCS) &&
4341252727Sadrian		    RV(le32toh(plcp)) > 7)
4342221649Sbschmidt			linkq.mimo = i + 1;
4343221649Sbschmidt
4344220715Sbschmidt		/* Next retry at immediate lower bit-rate. */
4345220715Sbschmidt		if (txrate > 0)
4346220715Sbschmidt			txrate--;
4347178676Ssam	}
4348253705Sadrian
4349253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
4350253705Sadrian
4351220715Sbschmidt	return iwn_cmd(sc, IWN_CMD_LINK_QUALITY, &linkq, sizeof linkq, 1);
4352222933Sbschmidt#undef	RV
4353178676Ssam}
4354178676Ssam
4355178676Ssam/*
4356198429Srpaulo * Broadcast node is used to send group-addressed and management frames.
4357178676Ssam */
4358206477Sbschmidtstatic int
4359201209Srpauloiwn_add_broadcast_node(struct iwn_softc *sc, int async)
4360178676Ssam{
4361220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
4362198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
4363220715Sbschmidt	struct ieee80211com *ic = ifp->if_l2com;
4364178676Ssam	struct iwn_node_info node;
4365220715Sbschmidt	struct iwn_cmd_link_quality linkq;
4366220715Sbschmidt	uint8_t txant;
4367220715Sbschmidt	int i, error;
4368178676Ssam
4369253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
4370253705Sadrian
4371254204Sadrian	sc->rxon = &sc->rx_on[IWN_RXON_BSS_CTX];
4372254204Sadrian
4373178676Ssam	memset(&node, 0, sizeof node);
4374198429Srpaulo	IEEE80211_ADDR_COPY(node.macaddr, ifp->if_broadcastaddr);
4375220728Sbschmidt	node.id = sc->broadcast_id;
4376198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "%s: adding broadcast node\n", __func__);
4377220728Sbschmidt	if ((error = ops->add_node(sc, &node, async)) != 0)
4378198429Srpaulo		return error;
4379178676Ssam
4380220715Sbschmidt	/* Use the first valid TX antenna. */
4381220715Sbschmidt	txant = IWN_LSB(sc->txchainmask);
4382220715Sbschmidt
4383220715Sbschmidt	memset(&linkq, 0, sizeof linkq);
4384220728Sbschmidt	linkq.id = sc->broadcast_id;
4385220715Sbschmidt	linkq.antmsk_1stream = txant;
4386220715Sbschmidt	linkq.antmsk_2stream = IWN_ANT_AB;
4387220715Sbschmidt	linkq.ampdu_max = 64;
4388220715Sbschmidt	linkq.ampdu_threshold = 3;
4389220715Sbschmidt	linkq.ampdu_limit = htole16(4000);	/* 4ms */
4390220715Sbschmidt
4391220715Sbschmidt	/* Use lowest mandatory bit-rate. */
4392220715Sbschmidt	if (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan))
4393221648Sbschmidt		linkq.retry[0] = htole32(0xd);
4394220715Sbschmidt	else
4395221648Sbschmidt		linkq.retry[0] = htole32(10 | IWN_RFLAG_CCK);
4396221648Sbschmidt	linkq.retry[0] |= htole32(IWN_RFLAG_ANT(txant));
4397220715Sbschmidt	/* Use same bit-rate for all TX retries. */
4398220715Sbschmidt	for (i = 1; i < IWN_MAX_TX_RETRIES; i++) {
4399221648Sbschmidt		linkq.retry[i] = linkq.retry[0];
4400220715Sbschmidt	}
4401253705Sadrian
4402253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
4403253705Sadrian
4404220715Sbschmidt	return iwn_cmd(sc, IWN_CMD_LINK_QUALITY, &linkq, sizeof linkq, async);
4405178676Ssam}
4406178676Ssam
4407206477Sbschmidtstatic int
4408220721Sbschmidtiwn_updateedca(struct ieee80211com *ic)
4409178676Ssam{
4410178676Ssam#define IWN_EXP2(x)	((1 << (x)) - 1)	/* CWmin = 2^ECWmin - 1 */
4411178676Ssam	struct iwn_softc *sc = ic->ic_ifp->if_softc;
4412178676Ssam	struct iwn_edca_params cmd;
4413220721Sbschmidt	int aci;
4414178676Ssam
4415253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
4416253705Sadrian
4417178676Ssam	memset(&cmd, 0, sizeof cmd);
4418178676Ssam	cmd.flags = htole32(IWN_EDCA_UPDATE);
4419220721Sbschmidt	for (aci = 0; aci < WME_NUM_AC; aci++) {
4420220721Sbschmidt		const struct wmeParams *ac =
4421220721Sbschmidt		    &ic->ic_wme.wme_chanParams.cap_wmeParams[aci];
4422220721Sbschmidt		cmd.ac[aci].aifsn = ac->wmep_aifsn;
4423220721Sbschmidt		cmd.ac[aci].cwmin = htole16(IWN_EXP2(ac->wmep_logcwmin));
4424220721Sbschmidt		cmd.ac[aci].cwmax = htole16(IWN_EXP2(ac->wmep_logcwmax));
4425220721Sbschmidt		cmd.ac[aci].txoplimit =
4426220721Sbschmidt		    htole16(IEEE80211_TXOP_TO_US(ac->wmep_txopLimit));
4427178676Ssam	}
4428201209Srpaulo	IEEE80211_UNLOCK(ic);
4429178676Ssam	IWN_LOCK(sc);
4430220725Sbschmidt	(void)iwn_cmd(sc, IWN_CMD_EDCA_PARAMS, &cmd, sizeof cmd, 1);
4431178676Ssam	IWN_UNLOCK(sc);
4432201209Srpaulo	IEEE80211_LOCK(ic);
4433253705Sadrian
4434253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
4435253705Sadrian
4436178676Ssam	return 0;
4437178676Ssam#undef IWN_EXP2
4438178676Ssam}
4439178676Ssam
4440201209Srpaulostatic void
4441201209Srpauloiwn_update_mcast(struct ifnet *ifp)
4442201209Srpaulo{
4443201209Srpaulo	/* Ignore */
4444201209Srpaulo}
4445201209Srpaulo
4446206477Sbschmidtstatic void
4447178676Ssamiwn_set_led(struct iwn_softc *sc, uint8_t which, uint8_t off, uint8_t on)
4448178676Ssam{
4449178676Ssam	struct iwn_cmd_led led;
4450178676Ssam
4451253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
4452253705Sadrian
4453198429Srpaulo	/* Clear microcode LED ownership. */
4454198429Srpaulo	IWN_CLRBITS(sc, IWN_LED, IWN_LED_BSM_CTRL);
4455198429Srpaulo
4456178676Ssam	led.which = which;
4457198429Srpaulo	led.unit = htole32(10000);	/* on/off in unit of 100ms */
4458178676Ssam	led.off = off;
4459178676Ssam	led.on = on;
4460198429Srpaulo	(void)iwn_cmd(sc, IWN_CMD_SET_LED, &led, sizeof led, 1);
4461178676Ssam}
4462178676Ssam
4463178676Ssam/*
4464201209Srpaulo * Set the critical temperature at which the firmware will stop the radio
4465201209Srpaulo * and notify us.
4466178676Ssam */
4467206477Sbschmidtstatic int
4468178676Ssamiwn_set_critical_temp(struct iwn_softc *sc)
4469178676Ssam{
4470178676Ssam	struct iwn_critical_temp crit;
4471201209Srpaulo	int32_t temp;
4472178676Ssam
4473253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
4474253705Sadrian
4475198429Srpaulo	IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_CTEMP_STOP_RF);
4476178676Ssam
4477201209Srpaulo	if (sc->hw_type == IWN_HW_REV_TYPE_5150)
4478201209Srpaulo		temp = (IWN_CTOK(110) - sc->temp_off) * -5;
4479201209Srpaulo	else if (sc->hw_type == IWN_HW_REV_TYPE_4965)
4480201209Srpaulo		temp = IWN_CTOK(110);
4481201209Srpaulo	else
4482201209Srpaulo		temp = 110;
4483178676Ssam	memset(&crit, 0, sizeof crit);
4484201209Srpaulo	crit.tempR = htole32(temp);
4485220726Sbschmidt	DPRINTF(sc, IWN_DEBUG_RESET, "setting critical temp to %d\n", temp);
4486178676Ssam	return iwn_cmd(sc, IWN_CMD_SET_CRITICAL_TEMP, &crit, sizeof crit, 0);
4487178676Ssam}
4488178676Ssam
4489206477Sbschmidtstatic int
4490198429Srpauloiwn_set_timing(struct iwn_softc *sc, struct ieee80211_node *ni)
4491178676Ssam{
4492198429Srpaulo	struct iwn_cmd_timing cmd;
4493178676Ssam	uint64_t val, mod;
4494178676Ssam
4495253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
4496253705Sadrian
4497198429Srpaulo	memset(&cmd, 0, sizeof cmd);
4498198429Srpaulo	memcpy(&cmd.tstamp, ni->ni_tstamp.data, sizeof (uint64_t));
4499198429Srpaulo	cmd.bintval = htole16(ni->ni_intval);
4500198429Srpaulo	cmd.lintval = htole16(10);
4501178676Ssam
4502198429Srpaulo	/* Compute remaining time until next beacon. */
4503220634Sbschmidt	val = (uint64_t)ni->ni_intval * IEEE80211_DUR_TU;
4504198429Srpaulo	mod = le64toh(cmd.tstamp) % val;
4505198429Srpaulo	cmd.binitval = htole32((uint32_t)(val - mod));
4506178676Ssam
4507198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "timing bintval=%u tstamp=%ju, init=%u\n",
4508198429Srpaulo	    ni->ni_intval, le64toh(cmd.tstamp), (uint32_t)(val - mod));
4509178676Ssam
4510198429Srpaulo	return iwn_cmd(sc, IWN_CMD_TIMING, &cmd, sizeof cmd, 1);
4511178676Ssam}
4512178676Ssam
4513206477Sbschmidtstatic void
4514198429Srpauloiwn4965_power_calibration(struct iwn_softc *sc, int temp)
4515178676Ssam{
4516201882Skeramida	struct ifnet *ifp = sc->sc_ifp;
4517201882Skeramida	struct ieee80211com *ic = ifp->if_l2com;
4518201882Skeramida
4519253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
4520253705Sadrian
4521220725Sbschmidt	/* Adjust TX power if need be (delta >= 3 degC). */
4522178676Ssam	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: temperature %d->%d\n",
4523178676Ssam	    __func__, sc->temp, temp);
4524198429Srpaulo	if (abs(temp - sc->temp) >= 3) {
4525198429Srpaulo		/* Record temperature of last calibration. */
4526198429Srpaulo		sc->temp = temp;
4527201882Skeramida		(void)iwn4965_set_txpower(sc, ic->ic_bsschan, 1);
4528178676Ssam	}
4529178676Ssam}
4530178676Ssam
4531178676Ssam/*
4532198429Srpaulo * Set TX power for current channel (each rate has its own power settings).
4533178676Ssam * This function takes into account the regulatory information from EEPROM,
4534178676Ssam * the current temperature and the current voltage.
4535178676Ssam */
4536206477Sbschmidtstatic int
4537201882Skeramidaiwn4965_set_txpower(struct iwn_softc *sc, struct ieee80211_channel *ch,
4538201882Skeramida    int async)
4539178676Ssam{
4540198429Srpaulo/* Fixed-point arithmetic division using a n-bit fractional part. */
4541178676Ssam#define fdivround(a, b, n)	\
4542178676Ssam	((((1 << n) * (a)) / (b) + (1 << n) / 2) / (1 << n))
4543198429Srpaulo/* Linear interpolation. */
4544178676Ssam#define interpolate(x, x1, y1, x2, y2, n)	\
4545178676Ssam	((y1) + fdivround(((int)(x) - (x1)) * ((y2) - (y1)), (x2) - (x1), n))
4546178676Ssam
4547178676Ssam	static const int tdiv[IWN_NATTEN_GROUPS] = { 9, 8, 8, 8, 6 };
4548178676Ssam	struct iwn_ucode_info *uc = &sc->ucode_info;
4549198429Srpaulo	struct iwn4965_cmd_txpower cmd;
4550198429Srpaulo	struct iwn4965_eeprom_chan_samples *chans;
4551220723Sbschmidt	const uint8_t *rf_gain, *dsp_gain;
4552178676Ssam	int32_t vdiff, tdiff;
4553178676Ssam	int i, c, grp, maxpwr;
4554198429Srpaulo	uint8_t chan;
4555178676Ssam
4556254204Sadrian	sc->rxon = &sc->rx_on[IWN_RXON_BSS_CTX];
4557220687Sbschmidt	/* Retrieve current channel from last RXON. */
4558254204Sadrian	chan = sc->rxon->chan;
4559201209Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "setting TX power for channel %d\n",
4560201209Srpaulo	    chan);
4561178676Ssam
4562178676Ssam	memset(&cmd, 0, sizeof cmd);
4563178676Ssam	cmd.band = IEEE80211_IS_CHAN_5GHZ(ch) ? 0 : 1;
4564178676Ssam	cmd.chan = chan;
4565178676Ssam
4566178676Ssam	if (IEEE80211_IS_CHAN_5GHZ(ch)) {
4567178676Ssam		maxpwr   = sc->maxpwr5GHz;
4568198429Srpaulo		rf_gain  = iwn4965_rf_gain_5ghz;
4569198429Srpaulo		dsp_gain = iwn4965_dsp_gain_5ghz;
4570178676Ssam	} else {
4571178676Ssam		maxpwr   = sc->maxpwr2GHz;
4572198429Srpaulo		rf_gain  = iwn4965_rf_gain_2ghz;
4573198429Srpaulo		dsp_gain = iwn4965_dsp_gain_2ghz;
4574178676Ssam	}
4575178676Ssam
4576198429Srpaulo	/* Compute voltage compensation. */
4577178676Ssam	vdiff = ((int32_t)le32toh(uc->volt) - sc->eeprom_voltage) / 7;
4578178676Ssam	if (vdiff > 0)
4579178676Ssam		vdiff *= 2;
4580178676Ssam	if (abs(vdiff) > 2)
4581178676Ssam		vdiff = 0;
4582178676Ssam	DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
4583178676Ssam	    "%s: voltage compensation=%d (UCODE=%d, EEPROM=%d)\n",
4584178676Ssam	    __func__, vdiff, le32toh(uc->volt), sc->eeprom_voltage);
4585178676Ssam
4586201209Srpaulo	/* Get channel attenuation group. */
4587178676Ssam	if (chan <= 20)		/* 1-20 */
4588178676Ssam		grp = 4;
4589178676Ssam	else if (chan <= 43)	/* 34-43 */
4590178676Ssam		grp = 0;
4591178676Ssam	else if (chan <= 70)	/* 44-70 */
4592178676Ssam		grp = 1;
4593178676Ssam	else if (chan <= 124)	/* 71-124 */
4594178676Ssam		grp = 2;
4595178676Ssam	else			/* 125-200 */
4596178676Ssam		grp = 3;
4597178676Ssam	DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
4598178676Ssam	    "%s: chan %d, attenuation group=%d\n", __func__, chan, grp);
4599178676Ssam
4600201209Srpaulo	/* Get channel sub-band. */
4601178676Ssam	for (i = 0; i < IWN_NBANDS; i++)
4602178676Ssam		if (sc->bands[i].lo != 0 &&
4603178676Ssam		    sc->bands[i].lo <= chan && chan <= sc->bands[i].hi)
4604178676Ssam			break;
4605198429Srpaulo	if (i == IWN_NBANDS)	/* Can't happen in real-life. */
4606198429Srpaulo		return EINVAL;
4607178676Ssam	chans = sc->bands[i].chans;
4608178676Ssam	DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
4609178676Ssam	    "%s: chan %d sub-band=%d\n", __func__, chan, i);
4610178676Ssam
4611198429Srpaulo	for (c = 0; c < 2; c++) {
4612178676Ssam		uint8_t power, gain, temp;
4613178676Ssam		int maxchpwr, pwr, ridx, idx;
4614178676Ssam
4615178676Ssam		power = interpolate(chan,
4616178676Ssam		    chans[0].num, chans[0].samples[c][1].power,
4617178676Ssam		    chans[1].num, chans[1].samples[c][1].power, 1);
4618178676Ssam		gain  = interpolate(chan,
4619178676Ssam		    chans[0].num, chans[0].samples[c][1].gain,
4620178676Ssam		    chans[1].num, chans[1].samples[c][1].gain, 1);
4621178676Ssam		temp  = interpolate(chan,
4622178676Ssam		    chans[0].num, chans[0].samples[c][1].temp,
4623178676Ssam		    chans[1].num, chans[1].samples[c][1].temp, 1);
4624178676Ssam		DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
4625178676Ssam		    "%s: Tx chain %d: power=%d gain=%d temp=%d\n",
4626178676Ssam		    __func__, c, power, gain, temp);
4627178676Ssam
4628198429Srpaulo		/* Compute temperature compensation. */
4629178676Ssam		tdiff = ((sc->temp - temp) * 2) / tdiv[grp];
4630178676Ssam		DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
4631178676Ssam		    "%s: temperature compensation=%d (current=%d, EEPROM=%d)\n",
4632178676Ssam		    __func__, tdiff, sc->temp, temp);
4633178676Ssam
4634178676Ssam		for (ridx = 0; ridx <= IWN_RIDX_MAX; ridx++) {
4635201209Srpaulo			/* Convert dBm to half-dBm. */
4636198429Srpaulo			maxchpwr = sc->maxpwr[chan] * 2;
4637198429Srpaulo			if ((ridx / 8) & 1)
4638198429Srpaulo				maxchpwr -= 6;	/* MIMO 2T: -3dB */
4639178676Ssam
4640198429Srpaulo			pwr = maxpwr;
4641178676Ssam
4642198429Srpaulo			/* Adjust TX power based on rate. */
4643198429Srpaulo			if ((ridx % 8) == 5)
4644198429Srpaulo				pwr -= 15;	/* OFDM48: -7.5dB */
4645198429Srpaulo			else if ((ridx % 8) == 6)
4646198429Srpaulo				pwr -= 17;	/* OFDM54: -8.5dB */
4647198429Srpaulo			else if ((ridx % 8) == 7)
4648198429Srpaulo				pwr -= 20;	/* OFDM60: -10dB */
4649198429Srpaulo			else
4650198429Srpaulo				pwr -= 10;	/* Others: -5dB */
4651178676Ssam
4652201209Srpaulo			/* Do not exceed channel max TX power. */
4653178676Ssam			if (pwr > maxchpwr)
4654178676Ssam				pwr = maxchpwr;
4655178676Ssam
4656178676Ssam			idx = gain - (pwr - power) - tdiff - vdiff;
4657178676Ssam			if ((ridx / 8) & 1)	/* MIMO */
4658178676Ssam				idx += (int32_t)le32toh(uc->atten[grp][c]);
4659178676Ssam
4660178676Ssam			if (cmd.band == 0)
4661178676Ssam				idx += 9;	/* 5GHz */
4662178676Ssam			if (ridx == IWN_RIDX_MAX)
4663178676Ssam				idx += 5;	/* CCK */
4664178676Ssam
4665198429Srpaulo			/* Make sure idx stays in a valid range. */
4666178676Ssam			if (idx < 0)
4667178676Ssam				idx = 0;
4668198429Srpaulo			else if (idx > IWN4965_MAX_PWR_INDEX)
4669198429Srpaulo				idx = IWN4965_MAX_PWR_INDEX;
4670178676Ssam
4671178676Ssam			DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
4672178676Ssam			    "%s: Tx chain %d, rate idx %d: power=%d\n",
4673178676Ssam			    __func__, c, ridx, idx);
4674178676Ssam			cmd.power[ridx].rf_gain[c] = rf_gain[idx];
4675178676Ssam			cmd.power[ridx].dsp_gain[c] = dsp_gain[idx];
4676178676Ssam		}
4677178676Ssam	}
4678178676Ssam
4679178676Ssam	DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
4680178676Ssam	    "%s: set tx power for chan %d\n", __func__, chan);
4681178676Ssam	return iwn_cmd(sc, IWN_CMD_TXPOWER, &cmd, sizeof cmd, async);
4682178676Ssam
4683178676Ssam#undef interpolate
4684178676Ssam#undef fdivround
4685178676Ssam}
4686178676Ssam
4687206477Sbschmidtstatic int
4688201882Skeramidaiwn5000_set_txpower(struct iwn_softc *sc, struct ieee80211_channel *ch,
4689201882Skeramida    int async)
4690198429Srpaulo{
4691198429Srpaulo	struct iwn5000_cmd_txpower cmd;
4692198429Srpaulo
4693253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
4694253705Sadrian
4695198429Srpaulo	/*
4696198429Srpaulo	 * TX power calibration is handled automatically by the firmware
4697198429Srpaulo	 * for 5000 Series.
4698198429Srpaulo	 */
4699198429Srpaulo	memset(&cmd, 0, sizeof cmd);
4700198429Srpaulo	cmd.global_limit = 2 * IWN5000_TXPOWER_MAX_DBM;	/* 16 dBm */
4701198429Srpaulo	cmd.flags = IWN5000_TXPOWER_NO_CLOSED;
4702198429Srpaulo	cmd.srv_limit = IWN5000_TXPOWER_AUTO;
4703198429Srpaulo	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: setting TX power\n", __func__);
4704198429Srpaulo	return iwn_cmd(sc, IWN_CMD_TXPOWER_DBM, &cmd, sizeof cmd, async);
4705198429Srpaulo}
4706198429Srpaulo
4707178676Ssam/*
4708198429Srpaulo * Retrieve the maximum RSSI (in dBm) among receivers.
4709178676Ssam */
4710206477Sbschmidtstatic int
4711198429Srpauloiwn4965_get_rssi(struct iwn_softc *sc, struct iwn_rx_stat *stat)
4712178676Ssam{
4713198429Srpaulo	struct iwn4965_rx_phystat *phy = (void *)stat->phybuf;
4714198429Srpaulo	uint8_t mask, agc;
4715198429Srpaulo	int rssi;
4716178676Ssam
4717253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
4718253705Sadrian
4719201209Srpaulo	mask = (le16toh(phy->antenna) >> 4) & IWN_ANT_ABC;
4720198429Srpaulo	agc  = (le16toh(phy->agc) >> 7) & 0x7f;
4721178676Ssam
4722178676Ssam	rssi = 0;
4723220689Sbschmidt	if (mask & IWN_ANT_A)
4724220689Sbschmidt		rssi = MAX(rssi, phy->rssi[0]);
4725220689Sbschmidt	if (mask & IWN_ANT_B)
4726220689Sbschmidt		rssi = MAX(rssi, phy->rssi[2]);
4727220689Sbschmidt	if (mask & IWN_ANT_C)
4728220689Sbschmidt		rssi = MAX(rssi, phy->rssi[4]);
4729198429Srpaulo
4730220724Sbschmidt	DPRINTF(sc, IWN_DEBUG_RECV,
4731220724Sbschmidt	    "%s: agc %d mask 0x%x rssi %d %d %d result %d\n", __func__, agc,
4732220724Sbschmidt	    mask, phy->rssi[0], phy->rssi[2], phy->rssi[4],
4733178676Ssam	    rssi - agc - IWN_RSSI_TO_DBM);
4734178676Ssam	return rssi - agc - IWN_RSSI_TO_DBM;
4735178676Ssam}
4736178676Ssam
4737206477Sbschmidtstatic int
4738198429Srpauloiwn5000_get_rssi(struct iwn_softc *sc, struct iwn_rx_stat *stat)
4739198429Srpaulo{
4740198429Srpaulo	struct iwn5000_rx_phystat *phy = (void *)stat->phybuf;
4741220723Sbschmidt	uint8_t agc;
4742198429Srpaulo	int rssi;
4743198429Srpaulo
4744253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
4745253705Sadrian
4746198429Srpaulo	agc = (le32toh(phy->agc) >> 9) & 0x7f;
4747198429Srpaulo
4748198429Srpaulo	rssi = MAX(le16toh(phy->rssi[0]) & 0xff,
4749198429Srpaulo		   le16toh(phy->rssi[1]) & 0xff);
4750198429Srpaulo	rssi = MAX(le16toh(phy->rssi[2]) & 0xff, rssi);
4751198429Srpaulo
4752220724Sbschmidt	DPRINTF(sc, IWN_DEBUG_RECV,
4753220724Sbschmidt	    "%s: agc %d rssi %d %d %d result %d\n", __func__, agc,
4754201822Strasz	    phy->rssi[0], phy->rssi[1], phy->rssi[2],
4755198429Srpaulo	    rssi - agc - IWN_RSSI_TO_DBM);
4756198429Srpaulo	return rssi - agc - IWN_RSSI_TO_DBM;
4757198429Srpaulo}
4758198429Srpaulo
4759178676Ssam/*
4760198429Srpaulo * Retrieve the average noise (in dBm) among receivers.
4761178676Ssam */
4762206477Sbschmidtstatic int
4763178676Ssamiwn_get_noise(const struct iwn_rx_general_stats *stats)
4764178676Ssam{
4765178676Ssam	int i, total, nbant, noise;
4766178676Ssam
4767178676Ssam	total = nbant = 0;
4768178676Ssam	for (i = 0; i < 3; i++) {
4769198429Srpaulo		if ((noise = le32toh(stats->noise[i]) & 0xff) == 0)
4770198429Srpaulo			continue;
4771198429Srpaulo		total += noise;
4772198429Srpaulo		nbant++;
4773178676Ssam	}
4774198429Srpaulo	/* There should be at least one antenna but check anyway. */
4775178676Ssam	return (nbant == 0) ? -127 : (total / nbant) - 107;
4776178676Ssam}
4777178676Ssam
4778178676Ssam/*
4779198429Srpaulo * Compute temperature (in degC) from last received statistics.
4780178676Ssam */
4781206477Sbschmidtstatic int
4782198429Srpauloiwn4965_get_temperature(struct iwn_softc *sc)
4783178676Ssam{
4784178676Ssam	struct iwn_ucode_info *uc = &sc->ucode_info;
4785178676Ssam	int32_t r1, r2, r3, r4, temp;
4786178676Ssam
4787253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
4788253705Sadrian
4789178676Ssam	r1 = le32toh(uc->temp[0].chan20MHz);
4790178676Ssam	r2 = le32toh(uc->temp[1].chan20MHz);
4791178676Ssam	r3 = le32toh(uc->temp[2].chan20MHz);
4792178676Ssam	r4 = le32toh(sc->rawtemp);
4793178676Ssam
4794220725Sbschmidt	if (r1 == r3)	/* Prevents division by 0 (should not happen). */
4795178676Ssam		return 0;
4796178676Ssam
4797198429Srpaulo	/* Sign-extend 23-bit R4 value to 32-bit. */
4798220659Sbschmidt	r4 = ((r4 & 0xffffff) ^ 0x800000) - 0x800000;
4799198429Srpaulo	/* Compute temperature in Kelvin. */
4800178676Ssam	temp = (259 * (r4 - r2)) / (r3 - r1);
4801178676Ssam	temp = (temp * 97) / 100 + 8;
4802178676Ssam
4803201209Srpaulo	DPRINTF(sc, IWN_DEBUG_ANY, "temperature %dK/%dC\n", temp,
4804201209Srpaulo	    IWN_KTOC(temp));
4805178676Ssam	return IWN_KTOC(temp);
4806178676Ssam}
4807178676Ssam
4808206477Sbschmidtstatic int
4809198429Srpauloiwn5000_get_temperature(struct iwn_softc *sc)
4810198429Srpaulo{
4811201209Srpaulo	int32_t temp;
4812201209Srpaulo
4813253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
4814253705Sadrian
4815198429Srpaulo	/*
4816198429Srpaulo	 * Temperature is not used by the driver for 5000 Series because
4817220725Sbschmidt	 * TX power calibration is handled by firmware.
4818198429Srpaulo	 */
4819201209Srpaulo	temp = le32toh(sc->rawtemp);
4820201209Srpaulo	if (sc->hw_type == IWN_HW_REV_TYPE_5150) {
4821201209Srpaulo		temp = (temp / -5) + sc->temp_off;
4822201209Srpaulo		temp = IWN_KTOC(temp);
4823201209Srpaulo	}
4824201209Srpaulo	return temp;
4825198429Srpaulo}
4826198429Srpaulo
4827178676Ssam/*
4828178676Ssam * Initialize sensitivity calibration state machine.
4829178676Ssam */
4830206477Sbschmidtstatic int
4831178676Ssamiwn_init_sensitivity(struct iwn_softc *sc)
4832178676Ssam{
4833220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
4834178676Ssam	struct iwn_calib_state *calib = &sc->calib;
4835198429Srpaulo	uint32_t flags;
4836178676Ssam	int error;
4837178676Ssam
4838253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
4839253705Sadrian
4840198429Srpaulo	/* Reset calibration state machine. */
4841178676Ssam	memset(calib, 0, sizeof (*calib));
4842178676Ssam	calib->state = IWN_CALIB_STATE_INIT;
4843178676Ssam	calib->cck_state = IWN_CCK_STATE_HIFA;
4844198429Srpaulo	/* Set initial correlation values. */
4845201209Srpaulo	calib->ofdm_x1     = sc->limits->min_ofdm_x1;
4846201209Srpaulo	calib->ofdm_mrc_x1 = sc->limits->min_ofdm_mrc_x1;
4847206444Sbschmidt	calib->ofdm_x4     = sc->limits->min_ofdm_x4;
4848201209Srpaulo	calib->ofdm_mrc_x4 = sc->limits->min_ofdm_mrc_x4;
4849198429Srpaulo	calib->cck_x4      = 125;
4850201209Srpaulo	calib->cck_mrc_x4  = sc->limits->min_cck_mrc_x4;
4851201209Srpaulo	calib->energy_cck  = sc->limits->energy_cck;
4852178676Ssam
4853198429Srpaulo	/* Write initial sensitivity. */
4854220726Sbschmidt	if ((error = iwn_send_sensitivity(sc)) != 0)
4855178676Ssam		return error;
4856178676Ssam
4857198429Srpaulo	/* Write initial gains. */
4858220728Sbschmidt	if ((error = ops->init_gains(sc)) != 0)
4859198429Srpaulo		return error;
4860198429Srpaulo
4861198429Srpaulo	/* Request statistics at each beacon interval. */
4862198429Srpaulo	flags = 0;
4863220724Sbschmidt	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: sending request for statistics\n",
4864220724Sbschmidt	    __func__);
4865198429Srpaulo	return iwn_cmd(sc, IWN_CMD_GET_STATISTICS, &flags, sizeof flags, 1);
4866178676Ssam}
4867178676Ssam
4868178676Ssam/*
4869178676Ssam * Collect noise and RSSI statistics for the first 20 beacons received
4870178676Ssam * after association and use them to determine connected antennas and
4871198429Srpaulo * to set differential gains.
4872178676Ssam */
4873206477Sbschmidtstatic void
4874198429Srpauloiwn_collect_noise(struct iwn_softc *sc,
4875178676Ssam    const struct iwn_rx_general_stats *stats)
4876178676Ssam{
4877220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
4878178676Ssam	struct iwn_calib_state *calib = &sc->calib;
4879252717Sadrian	struct ifnet *ifp = sc->sc_ifp;
4880252717Sadrian	struct ieee80211com *ic = ifp->if_l2com;
4881198429Srpaulo	uint32_t val;
4882198429Srpaulo	int i;
4883178676Ssam
4884253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
4885253705Sadrian
4886198429Srpaulo	/* Accumulate RSSI and noise for all 3 antennas. */
4887178676Ssam	for (i = 0; i < 3; i++) {
4888178676Ssam		calib->rssi[i] += le32toh(stats->rssi[i]) & 0xff;
4889178676Ssam		calib->noise[i] += le32toh(stats->noise[i]) & 0xff;
4890178676Ssam	}
4891198429Srpaulo	/* NB: We update differential gains only once after 20 beacons. */
4892178676Ssam	if (++calib->nbeacons < 20)
4893178676Ssam		return;
4894178676Ssam
4895198429Srpaulo	/* Determine highest average RSSI. */
4896198429Srpaulo	val = MAX(calib->rssi[0], calib->rssi[1]);
4897198429Srpaulo	val = MAX(calib->rssi[2], val);
4898178676Ssam
4899198429Srpaulo	/* Determine which antennas are connected. */
4900210110Sbschmidt	sc->chainmask = sc->rxchainmask;
4901178676Ssam	for (i = 0; i < 3; i++)
4902210110Sbschmidt		if (val - calib->rssi[i] > 15 * 20)
4903210110Sbschmidt			sc->chainmask &= ~(1 << i);
4904210110Sbschmidt	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4905210110Sbschmidt	    "%s: RX chains mask: theoretical=0x%x, actual=0x%x\n",
4906210110Sbschmidt	    __func__, sc->rxchainmask, sc->chainmask);
4907210110Sbschmidt
4908198429Srpaulo	/* If none of the TX antennas are connected, keep at least one. */
4909201209Srpaulo	if ((sc->chainmask & sc->txchainmask) == 0)
4910201209Srpaulo		sc->chainmask |= IWN_LSB(sc->txchainmask);
4911178676Ssam
4912220728Sbschmidt	(void)ops->set_gains(sc);
4913198429Srpaulo	calib->state = IWN_CALIB_STATE_RUN;
4914198429Srpaulo
4915198429Srpaulo#ifdef notyet
4916198429Srpaulo	/* XXX Disable RX chains with no antennas connected. */
4917254204Sadrian	sc->rxon->rxchain = htole16(IWN_RXCHAIN_SEL(sc->chainmask));
4918254204Sadrian	(void)iwn_cmd(sc, IWN_CMD_RXON, sc->rxon, sc->rxonsz, 1);
4919198429Srpaulo#endif
4920198429Srpaulo
4921198429Srpaulo	/* Enable power-saving mode if requested by user. */
4922252717Sadrian	if (ic->ic_flags & IEEE80211_F_PMGTON)
4923198429Srpaulo		(void)iwn_set_pslevel(sc, 0, 3, 1);
4924253705Sadrian
4925253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
4926253705Sadrian
4927198429Srpaulo}
4928198429Srpaulo
4929206477Sbschmidtstatic int
4930198429Srpauloiwn4965_init_gains(struct iwn_softc *sc)
4931198429Srpaulo{
4932198429Srpaulo	struct iwn_phy_calib_gain cmd;
4933198429Srpaulo
4934253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
4935253705Sadrian
4936198429Srpaulo	memset(&cmd, 0, sizeof cmd);
4937198429Srpaulo	cmd.code = IWN4965_PHY_CALIB_DIFF_GAIN;
4938198429Srpaulo	/* Differential gains initially set to 0 for all 3 antennas. */
4939198429Srpaulo	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4940198429Srpaulo	    "%s: setting initial differential gains\n", __func__);
4941198429Srpaulo	return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1);
4942198429Srpaulo}
4943198429Srpaulo
4944206477Sbschmidtstatic int
4945198429Srpauloiwn5000_init_gains(struct iwn_softc *sc)
4946198429Srpaulo{
4947198429Srpaulo	struct iwn_phy_calib cmd;
4948198429Srpaulo
4949253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
4950253705Sadrian
4951198429Srpaulo	memset(&cmd, 0, sizeof cmd);
4952220866Sbschmidt	cmd.code = sc->reset_noise_gain;
4953198429Srpaulo	cmd.ngroups = 1;
4954198429Srpaulo	cmd.isvalid = 1;
4955198429Srpaulo	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4956198429Srpaulo	    "%s: setting initial differential gains\n", __func__);
4957198429Srpaulo	return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1);
4958198429Srpaulo}
4959198429Srpaulo
4960206477Sbschmidtstatic int
4961198429Srpauloiwn4965_set_gains(struct iwn_softc *sc)
4962198429Srpaulo{
4963198429Srpaulo	struct iwn_calib_state *calib = &sc->calib;
4964198429Srpaulo	struct iwn_phy_calib_gain cmd;
4965198429Srpaulo	int i, delta, noise;
4966198429Srpaulo
4967253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
4968253705Sadrian
4969198429Srpaulo	/* Get minimal noise among connected antennas. */
4970201209Srpaulo	noise = INT_MAX;	/* NB: There's at least one antenna. */
4971178676Ssam	for (i = 0; i < 3; i++)
4972201209Srpaulo		if (sc->chainmask & (1 << i))
4973198429Srpaulo			noise = MIN(calib->noise[i], noise);
4974178676Ssam
4975178676Ssam	memset(&cmd, 0, sizeof cmd);
4976198429Srpaulo	cmd.code = IWN4965_PHY_CALIB_DIFF_GAIN;
4977198429Srpaulo	/* Set differential gains for connected antennas. */
4978178676Ssam	for (i = 0; i < 3; i++) {
4979201209Srpaulo		if (sc->chainmask & (1 << i)) {
4980198429Srpaulo			/* Compute attenuation (in unit of 1.5dB). */
4981198429Srpaulo			delta = (noise - (int32_t)calib->noise[i]) / 30;
4982198429Srpaulo			/* NB: delta <= 0 */
4983198429Srpaulo			/* Limit to [-4.5dB,0]. */
4984198429Srpaulo			cmd.gain[i] = MIN(abs(delta), 3);
4985198429Srpaulo			if (delta < 0)
4986198429Srpaulo				cmd.gain[i] |= 1 << 2;	/* sign bit */
4987178676Ssam		}
4988178676Ssam	}
4989178676Ssam	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4990198429Srpaulo	    "setting differential gains Ant A/B/C: %x/%x/%x (%x)\n",
4991201209Srpaulo	    cmd.gain[0], cmd.gain[1], cmd.gain[2], sc->chainmask);
4992198429Srpaulo	return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1);
4993178676Ssam}
4994178676Ssam
4995206477Sbschmidtstatic int
4996198429Srpauloiwn5000_set_gains(struct iwn_softc *sc)
4997198429Srpaulo{
4998198429Srpaulo	struct iwn_calib_state *calib = &sc->calib;
4999198429Srpaulo	struct iwn_phy_calib_gain cmd;
5000220723Sbschmidt	int i, ant, div, delta;
5001198429Srpaulo
5002253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
5003253705Sadrian
5004206444Sbschmidt	/* We collected 20 beacons and !=6050 need a 1.5 factor. */
5005206444Sbschmidt	div = (sc->hw_type == IWN_HW_REV_TYPE_6050) ? 20 : 30;
5006198429Srpaulo
5007198429Srpaulo	memset(&cmd, 0, sizeof cmd);
5008220866Sbschmidt	cmd.code = sc->noise_gain;
5009198429Srpaulo	cmd.ngroups = 1;
5010198429Srpaulo	cmd.isvalid = 1;
5011201209Srpaulo	/* Get first available RX antenna as referential. */
5012201209Srpaulo	ant = IWN_LSB(sc->rxchainmask);
5013201209Srpaulo	/* Set differential gains for other antennas. */
5014201209Srpaulo	for (i = ant + 1; i < 3; i++) {
5015201209Srpaulo		if (sc->chainmask & (1 << i)) {
5016201209Srpaulo			/* The delta is relative to antenna "ant". */
5017201209Srpaulo			delta = ((int32_t)calib->noise[ant] -
5018206444Sbschmidt			    (int32_t)calib->noise[i]) / div;
5019198429Srpaulo			/* Limit to [-4.5dB,+4.5dB]. */
5020198429Srpaulo			cmd.gain[i - 1] = MIN(abs(delta), 3);
5021198429Srpaulo			if (delta < 0)
5022198429Srpaulo				cmd.gain[i - 1] |= 1 << 2;	/* sign bit */
5023198429Srpaulo		}
5024198429Srpaulo	}
5025198429Srpaulo	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
5026198429Srpaulo	    "setting differential gains Ant B/C: %x/%x (%x)\n",
5027201209Srpaulo	    cmd.gain[0], cmd.gain[1], sc->chainmask);
5028198429Srpaulo	return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1);
5029198429Srpaulo}
5030198429Srpaulo
5031178676Ssam/*
5032198429Srpaulo * Tune RF RX sensitivity based on the number of false alarms detected
5033178676Ssam * during the last beacon period.
5034178676Ssam */
5035206477Sbschmidtstatic void
5036178676Ssamiwn_tune_sensitivity(struct iwn_softc *sc, const struct iwn_rx_stats *stats)
5037178676Ssam{
5038198429Srpaulo#define inc(val, inc, max)			\
5039178676Ssam	if ((val) < (max)) {			\
5040178676Ssam		if ((val) < (max) - (inc))	\
5041178676Ssam			(val) += (inc);		\
5042178676Ssam		else				\
5043178676Ssam			(val) = (max);		\
5044178676Ssam		needs_update = 1;		\
5045178676Ssam	}
5046198429Srpaulo#define dec(val, dec, min)			\
5047178676Ssam	if ((val) > (min)) {			\
5048178676Ssam		if ((val) > (min) + (dec))	\
5049178676Ssam			(val) -= (dec);		\
5050178676Ssam		else				\
5051178676Ssam			(val) = (min);		\
5052178676Ssam		needs_update = 1;		\
5053178676Ssam	}
5054178676Ssam
5055201209Srpaulo	const struct iwn_sensitivity_limits *limits = sc->limits;
5056178676Ssam	struct iwn_calib_state *calib = &sc->calib;
5057178676Ssam	uint32_t val, rxena, fa;
5058178676Ssam	uint32_t energy[3], energy_min;
5059198439Srpaulo	uint8_t noise[3], noise_ref;
5060198429Srpaulo	int i, needs_update = 0;
5061178676Ssam
5062253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
5063253705Sadrian
5064198429Srpaulo	/* Check that we've been enabled long enough. */
5065253705Sadrian	if ((rxena = le32toh(stats->general.load)) == 0){
5066253705Sadrian		DPRINTF(sc, IWN_DEBUG_TRACE, "->%s end not so long\n", __func__);
5067178676Ssam		return;
5068253705Sadrian	}
5069178676Ssam
5070198429Srpaulo	/* Compute number of false alarms since last call for OFDM. */
5071178676Ssam	fa  = le32toh(stats->ofdm.bad_plcp) - calib->bad_plcp_ofdm;
5072178676Ssam	fa += le32toh(stats->ofdm.fa) - calib->fa_ofdm;
5073220634Sbschmidt	fa *= 200 * IEEE80211_DUR_TU;	/* 200TU */
5074178676Ssam
5075198429Srpaulo	/* Save counters values for next call. */
5076178676Ssam	calib->bad_plcp_ofdm = le32toh(stats->ofdm.bad_plcp);
5077178676Ssam	calib->fa_ofdm = le32toh(stats->ofdm.fa);
5078178676Ssam
5079178676Ssam	if (fa > 50 * rxena) {
5080198429Srpaulo		/* High false alarm count, decrease sensitivity. */
5081178676Ssam		DPRINTF(sc, IWN_DEBUG_CALIBRATE,
5082178676Ssam		    "%s: OFDM high false alarm count: %u\n", __func__, fa);
5083198429Srpaulo		inc(calib->ofdm_x1,     1, limits->max_ofdm_x1);
5084198429Srpaulo		inc(calib->ofdm_mrc_x1, 1, limits->max_ofdm_mrc_x1);
5085198429Srpaulo		inc(calib->ofdm_x4,     1, limits->max_ofdm_x4);
5086198429Srpaulo		inc(calib->ofdm_mrc_x4, 1, limits->max_ofdm_mrc_x4);
5087178676Ssam
5088178676Ssam	} else if (fa < 5 * rxena) {
5089198429Srpaulo		/* Low false alarm count, increase sensitivity. */
5090178676Ssam		DPRINTF(sc, IWN_DEBUG_CALIBRATE,
5091178676Ssam		    "%s: OFDM low false alarm count: %u\n", __func__, fa);
5092198429Srpaulo		dec(calib->ofdm_x1,     1, limits->min_ofdm_x1);
5093198429Srpaulo		dec(calib->ofdm_mrc_x1, 1, limits->min_ofdm_mrc_x1);
5094198429Srpaulo		dec(calib->ofdm_x4,     1, limits->min_ofdm_x4);
5095198429Srpaulo		dec(calib->ofdm_mrc_x4, 1, limits->min_ofdm_mrc_x4);
5096178676Ssam	}
5097178676Ssam
5098198429Srpaulo	/* Compute maximum noise among 3 receivers. */
5099178676Ssam	for (i = 0; i < 3; i++)
5100178676Ssam		noise[i] = (le32toh(stats->general.noise[i]) >> 8) & 0xff;
5101198429Srpaulo	val = MAX(noise[0], noise[1]);
5102198429Srpaulo	val = MAX(noise[2], val);
5103198429Srpaulo	/* Insert it into our samples table. */
5104178676Ssam	calib->noise_samples[calib->cur_noise_sample] = val;
5105178676Ssam	calib->cur_noise_sample = (calib->cur_noise_sample + 1) % 20;
5106178676Ssam
5107198429Srpaulo	/* Compute maximum noise among last 20 samples. */
5108178676Ssam	noise_ref = calib->noise_samples[0];
5109178676Ssam	for (i = 1; i < 20; i++)
5110198429Srpaulo		noise_ref = MAX(noise_ref, calib->noise_samples[i]);
5111178676Ssam
5112198429Srpaulo	/* Compute maximum energy among 3 receivers. */
5113178676Ssam	for (i = 0; i < 3; i++)
5114178676Ssam		energy[i] = le32toh(stats->general.energy[i]);
5115198429Srpaulo	val = MIN(energy[0], energy[1]);
5116198429Srpaulo	val = MIN(energy[2], val);
5117198429Srpaulo	/* Insert it into our samples table. */
5118178676Ssam	calib->energy_samples[calib->cur_energy_sample] = val;
5119178676Ssam	calib->cur_energy_sample = (calib->cur_energy_sample + 1) % 10;
5120178676Ssam
5121198429Srpaulo	/* Compute minimum energy among last 10 samples. */
5122178676Ssam	energy_min = calib->energy_samples[0];
5123178676Ssam	for (i = 1; i < 10; i++)
5124198429Srpaulo		energy_min = MAX(energy_min, calib->energy_samples[i]);
5125178676Ssam	energy_min += 6;
5126178676Ssam
5127198429Srpaulo	/* Compute number of false alarms since last call for CCK. */
5128178676Ssam	fa  = le32toh(stats->cck.bad_plcp) - calib->bad_plcp_cck;
5129178676Ssam	fa += le32toh(stats->cck.fa) - calib->fa_cck;
5130220634Sbschmidt	fa *= 200 * IEEE80211_DUR_TU;	/* 200TU */
5131178676Ssam
5132198429Srpaulo	/* Save counters values for next call. */
5133178676Ssam	calib->bad_plcp_cck = le32toh(stats->cck.bad_plcp);
5134178676Ssam	calib->fa_cck = le32toh(stats->cck.fa);
5135178676Ssam
5136178676Ssam	if (fa > 50 * rxena) {
5137198429Srpaulo		/* High false alarm count, decrease sensitivity. */
5138178676Ssam		DPRINTF(sc, IWN_DEBUG_CALIBRATE,
5139178676Ssam		    "%s: CCK high false alarm count: %u\n", __func__, fa);
5140178676Ssam		calib->cck_state = IWN_CCK_STATE_HIFA;
5141178676Ssam		calib->low_fa = 0;
5142178676Ssam
5143198429Srpaulo		if (calib->cck_x4 > 160) {
5144178676Ssam			calib->noise_ref = noise_ref;
5145178676Ssam			if (calib->energy_cck > 2)
5146198429Srpaulo				dec(calib->energy_cck, 2, energy_min);
5147178676Ssam		}
5148198429Srpaulo		if (calib->cck_x4 < 160) {
5149198429Srpaulo			calib->cck_x4 = 161;
5150178676Ssam			needs_update = 1;
5151178676Ssam		} else
5152198429Srpaulo			inc(calib->cck_x4, 3, limits->max_cck_x4);
5153178676Ssam
5154198429Srpaulo		inc(calib->cck_mrc_x4, 3, limits->max_cck_mrc_x4);
5155178676Ssam
5156178676Ssam	} else if (fa < 5 * rxena) {
5157198429Srpaulo		/* Low false alarm count, increase sensitivity. */
5158178676Ssam		DPRINTF(sc, IWN_DEBUG_CALIBRATE,
5159178676Ssam		    "%s: CCK low false alarm count: %u\n", __func__, fa);
5160178676Ssam		calib->cck_state = IWN_CCK_STATE_LOFA;
5161178676Ssam		calib->low_fa++;
5162178676Ssam
5163198429Srpaulo		if (calib->cck_state != IWN_CCK_STATE_INIT &&
5164198429Srpaulo		    (((int32_t)calib->noise_ref - (int32_t)noise_ref) > 2 ||
5165220726Sbschmidt		     calib->low_fa > 100)) {
5166198429Srpaulo			inc(calib->energy_cck, 2, limits->min_energy_cck);
5167198429Srpaulo			dec(calib->cck_x4,     3, limits->min_cck_x4);
5168198429Srpaulo			dec(calib->cck_mrc_x4, 3, limits->min_cck_mrc_x4);
5169178676Ssam		}
5170178676Ssam	} else {
5171198429Srpaulo		/* Not worth to increase or decrease sensitivity. */
5172178676Ssam		DPRINTF(sc, IWN_DEBUG_CALIBRATE,
5173178676Ssam		    "%s: CCK normal false alarm count: %u\n", __func__, fa);
5174178676Ssam		calib->low_fa = 0;
5175178676Ssam		calib->noise_ref = noise_ref;
5176178676Ssam
5177178676Ssam		if (calib->cck_state == IWN_CCK_STATE_HIFA) {
5178198429Srpaulo			/* Previous interval had many false alarms. */
5179198429Srpaulo			dec(calib->energy_cck, 8, energy_min);
5180178676Ssam		}
5181178676Ssam		calib->cck_state = IWN_CCK_STATE_INIT;
5182178676Ssam	}
5183178676Ssam
5184178676Ssam	if (needs_update)
5185178676Ssam		(void)iwn_send_sensitivity(sc);
5186253705Sadrian
5187253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
5188253705Sadrian
5189198429Srpaulo#undef dec
5190198429Srpaulo#undef inc
5191178676Ssam}
5192178676Ssam
5193206477Sbschmidtstatic int
5194178676Ssamiwn_send_sensitivity(struct iwn_softc *sc)
5195178676Ssam{
5196178676Ssam	struct iwn_calib_state *calib = &sc->calib;
5197220729Sbschmidt	struct iwn_enhanced_sensitivity_cmd cmd;
5198220729Sbschmidt	int len;
5199178676Ssam
5200178676Ssam	memset(&cmd, 0, sizeof cmd);
5201220729Sbschmidt	len = sizeof (struct iwn_sensitivity_cmd);
5202178676Ssam	cmd.which = IWN_SENSITIVITY_WORKTBL;
5203198429Srpaulo	/* OFDM modulation. */
5204220726Sbschmidt	cmd.corr_ofdm_x1       = htole16(calib->ofdm_x1);
5205220726Sbschmidt	cmd.corr_ofdm_mrc_x1   = htole16(calib->ofdm_mrc_x1);
5206220726Sbschmidt	cmd.corr_ofdm_x4       = htole16(calib->ofdm_x4);
5207220726Sbschmidt	cmd.corr_ofdm_mrc_x4   = htole16(calib->ofdm_mrc_x4);
5208220726Sbschmidt	cmd.energy_ofdm        = htole16(sc->limits->energy_ofdm);
5209220726Sbschmidt	cmd.energy_ofdm_th     = htole16(62);
5210198429Srpaulo	/* CCK modulation. */
5211220726Sbschmidt	cmd.corr_cck_x4        = htole16(calib->cck_x4);
5212220726Sbschmidt	cmd.corr_cck_mrc_x4    = htole16(calib->cck_mrc_x4);
5213220726Sbschmidt	cmd.energy_cck         = htole16(calib->energy_cck);
5214198429Srpaulo	/* Barker modulation: use default values. */
5215220726Sbschmidt	cmd.corr_barker        = htole16(190);
5216220726Sbschmidt	cmd.corr_barker_mrc    = htole16(390);
5217178676Ssam
5218202986Srpaulo	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
5219178676Ssam	    "%s: set sensitivity %d/%d/%d/%d/%d/%d/%d\n", __func__,
5220198429Srpaulo	    calib->ofdm_x1, calib->ofdm_mrc_x1, calib->ofdm_x4,
5221198429Srpaulo	    calib->ofdm_mrc_x4, calib->cck_x4,
5222198429Srpaulo	    calib->cck_mrc_x4, calib->energy_cck);
5223220729Sbschmidt
5224220729Sbschmidt	if (!(sc->sc_flags & IWN_FLAG_ENH_SENS))
5225220729Sbschmidt		goto send;
5226220729Sbschmidt	/* Enhanced sensitivity settings. */
5227220729Sbschmidt	len = sizeof (struct iwn_enhanced_sensitivity_cmd);
5228220729Sbschmidt	cmd.ofdm_det_slope_mrc = htole16(668);
5229220729Sbschmidt	cmd.ofdm_det_icept_mrc = htole16(4);
5230220729Sbschmidt	cmd.ofdm_det_slope     = htole16(486);
5231220729Sbschmidt	cmd.ofdm_det_icept     = htole16(37);
5232220729Sbschmidt	cmd.cck_det_slope_mrc  = htole16(853);
5233220729Sbschmidt	cmd.cck_det_icept_mrc  = htole16(4);
5234220729Sbschmidt	cmd.cck_det_slope      = htole16(476);
5235220729Sbschmidt	cmd.cck_det_icept      = htole16(99);
5236220729Sbschmidtsend:
5237220729Sbschmidt	return iwn_cmd(sc, IWN_CMD_SET_SENSITIVITY, &cmd, len, 1);
5238178676Ssam}
5239178676Ssam
5240198429Srpaulo/*
5241198429Srpaulo * Set STA mode power saving level (between 0 and 5).
5242198429Srpaulo * Level 0 is CAM (Continuously Aware Mode), 5 is for maximum power saving.
5243198429Srpaulo */
5244206477Sbschmidtstatic int
5245198429Srpauloiwn_set_pslevel(struct iwn_softc *sc, int dtim, int level, int async)
5246198429Srpaulo{
5247220723Sbschmidt	struct iwn_pmgt_cmd cmd;
5248198429Srpaulo	const struct iwn_pmgt *pmgt;
5249198429Srpaulo	uint32_t max, skip_dtim;
5250220721Sbschmidt	uint32_t reg;
5251198429Srpaulo	int i;
5252198429Srpaulo
5253252727Sadrian	DPRINTF(sc, IWN_DEBUG_PWRSAVE,
5254252727Sadrian	    "%s: dtim=%d, level=%d, async=%d\n",
5255252727Sadrian	    __func__,
5256252727Sadrian	    dtim,
5257252727Sadrian	    level,
5258252727Sadrian	    async);
5259252727Sadrian
5260198429Srpaulo	/* Select which PS parameters to use. */
5261198429Srpaulo	if (dtim <= 2)
5262198429Srpaulo		pmgt = &iwn_pmgt[0][level];
5263198429Srpaulo	else if (dtim <= 10)
5264198429Srpaulo		pmgt = &iwn_pmgt[1][level];
5265198429Srpaulo	else
5266198429Srpaulo		pmgt = &iwn_pmgt[2][level];
5267198429Srpaulo
5268198429Srpaulo	memset(&cmd, 0, sizeof cmd);
5269198429Srpaulo	if (level != 0)	/* not CAM */
5270198429Srpaulo		cmd.flags |= htole16(IWN_PS_ALLOW_SLEEP);
5271198429Srpaulo	if (level == 5)
5272198429Srpaulo		cmd.flags |= htole16(IWN_PS_FAST_PD);
5273201209Srpaulo	/* Retrieve PCIe Active State Power Management (ASPM). */
5274220721Sbschmidt	reg = pci_read_config(sc->sc_dev, sc->sc_cap_off + 0x10, 1);
5275220721Sbschmidt	if (!(reg & 0x1))	/* L0s Entry disabled. */
5276198429Srpaulo		cmd.flags |= htole16(IWN_PS_PCI_PMGT);
5277198429Srpaulo	cmd.rxtimeout = htole32(pmgt->rxtimeout * 1024);
5278198429Srpaulo	cmd.txtimeout = htole32(pmgt->txtimeout * 1024);
5279198429Srpaulo
5280198429Srpaulo	if (dtim == 0) {
5281198429Srpaulo		dtim = 1;
5282198429Srpaulo		skip_dtim = 0;
5283198429Srpaulo	} else
5284198429Srpaulo		skip_dtim = pmgt->skip_dtim;
5285198429Srpaulo	if (skip_dtim != 0) {
5286198429Srpaulo		cmd.flags |= htole16(IWN_PS_SLEEP_OVER_DTIM);
5287198429Srpaulo		max = pmgt->intval[4];
5288198429Srpaulo		if (max == (uint32_t)-1)
5289198429Srpaulo			max = dtim * (skip_dtim + 1);
5290198429Srpaulo		else if (max > dtim)
5291198429Srpaulo			max = (max / dtim) * dtim;
5292198429Srpaulo	} else
5293198429Srpaulo		max = dtim;
5294198429Srpaulo	for (i = 0; i < 5; i++)
5295198429Srpaulo		cmd.intval[i] = htole32(MIN(max, pmgt->intval[i]));
5296198429Srpaulo
5297198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "setting power saving level to %d\n",
5298198429Srpaulo	    level);
5299198429Srpaulo	return iwn_cmd(sc, IWN_CMD_SET_POWER_MODE, &cmd, sizeof cmd, async);
5300198429Srpaulo}
5301198429Srpaulo
5302206477Sbschmidtstatic int
5303220662Sbschmidtiwn_send_btcoex(struct iwn_softc *sc)
5304220662Sbschmidt{
5305220662Sbschmidt	struct iwn_bluetooth cmd;
5306220662Sbschmidt
5307220662Sbschmidt	memset(&cmd, 0, sizeof cmd);
5308220662Sbschmidt	cmd.flags = IWN_BT_COEX_CHAN_ANN | IWN_BT_COEX_BT_PRIO;
5309220662Sbschmidt	cmd.lead_time = IWN_BT_LEAD_TIME_DEF;
5310220662Sbschmidt	cmd.max_kill = IWN_BT_MAX_KILL_DEF;
5311220662Sbschmidt	DPRINTF(sc, IWN_DEBUG_RESET, "%s: configuring bluetooth coexistence\n",
5312220662Sbschmidt	    __func__);
5313220662Sbschmidt	return iwn_cmd(sc, IWN_CMD_BT_COEX, &cmd, sizeof(cmd), 0);
5314220662Sbschmidt}
5315220662Sbschmidt
5316220662Sbschmidtstatic int
5317220891Sbschmidtiwn_send_advanced_btcoex(struct iwn_softc *sc)
5318220891Sbschmidt{
5319220891Sbschmidt	static const uint32_t btcoex_3wire[12] = {
5320220891Sbschmidt		0xaaaaaaaa, 0xaaaaaaaa, 0xaeaaaaaa, 0xaaaaaaaa,
5321220891Sbschmidt		0xcc00ff28, 0x0000aaaa, 0xcc00aaaa, 0x0000aaaa,
5322220891Sbschmidt		0xc0004000, 0x00004000, 0xf0005000, 0xf0005000,
5323220891Sbschmidt	};
5324220891Sbschmidt	struct iwn6000_btcoex_config btconfig;
5325220891Sbschmidt	struct iwn_btcoex_priotable btprio;
5326220891Sbschmidt	struct iwn_btcoex_prot btprot;
5327220891Sbschmidt	int error, i;
5328220891Sbschmidt
5329220891Sbschmidt	memset(&btconfig, 0, sizeof btconfig);
5330220891Sbschmidt	btconfig.flags = 145;
5331220891Sbschmidt	btconfig.max_kill = 5;
5332220891Sbschmidt	btconfig.bt3_t7_timer = 1;
5333220891Sbschmidt	btconfig.kill_ack = htole32(0xffff0000);
5334220891Sbschmidt	btconfig.kill_cts = htole32(0xffff0000);
5335220891Sbschmidt	btconfig.sample_time = 2;
5336220891Sbschmidt	btconfig.bt3_t2_timer = 0xc;
5337220891Sbschmidt	for (i = 0; i < 12; i++)
5338220891Sbschmidt		btconfig.lookup_table[i] = htole32(btcoex_3wire[i]);
5339220891Sbschmidt	btconfig.valid = htole16(0xff);
5340220891Sbschmidt	btconfig.prio_boost = 0xf0;
5341220891Sbschmidt	DPRINTF(sc, IWN_DEBUG_RESET,
5342220891Sbschmidt	    "%s: configuring advanced bluetooth coexistence\n", __func__);
5343220891Sbschmidt	error = iwn_cmd(sc, IWN_CMD_BT_COEX, &btconfig, sizeof(btconfig), 1);
5344220891Sbschmidt	if (error != 0)
5345220891Sbschmidt		return error;
5346220891Sbschmidt
5347220891Sbschmidt	memset(&btprio, 0, sizeof btprio);
5348220891Sbschmidt	btprio.calib_init1 = 0x6;
5349220891Sbschmidt	btprio.calib_init2 = 0x7;
5350220891Sbschmidt	btprio.calib_periodic_low1 = 0x2;
5351220891Sbschmidt	btprio.calib_periodic_low2 = 0x3;
5352220891Sbschmidt	btprio.calib_periodic_high1 = 0x4;
5353220891Sbschmidt	btprio.calib_periodic_high2 = 0x5;
5354220891Sbschmidt	btprio.dtim = 0x6;
5355220891Sbschmidt	btprio.scan52 = 0x8;
5356220891Sbschmidt	btprio.scan24 = 0xa;
5357220891Sbschmidt	error = iwn_cmd(sc, IWN_CMD_BT_COEX_PRIOTABLE, &btprio, sizeof(btprio),
5358220891Sbschmidt	    1);
5359220891Sbschmidt	if (error != 0)
5360220891Sbschmidt		return error;
5361220891Sbschmidt
5362220891Sbschmidt	/* Force BT state machine change. */
5363254206Sadrian	memset(&btprot, 0, sizeof btprot);
5364220891Sbschmidt	btprot.open = 1;
5365220891Sbschmidt	btprot.type = 1;
5366220891Sbschmidt	error = iwn_cmd(sc, IWN_CMD_BT_COEX_PROT, &btprot, sizeof(btprot), 1);
5367220891Sbschmidt	if (error != 0)
5368220891Sbschmidt		return error;
5369220891Sbschmidt	btprot.open = 0;
5370220891Sbschmidt	return iwn_cmd(sc, IWN_CMD_BT_COEX_PROT, &btprot, sizeof(btprot), 1);
5371220891Sbschmidt}
5372220891Sbschmidt
5373220891Sbschmidtstatic int
5374227805Sbschmidtiwn5000_runtime_calib(struct iwn_softc *sc)
5375227805Sbschmidt{
5376227805Sbschmidt	struct iwn5000_calib_config cmd;
5377227805Sbschmidt
5378227805Sbschmidt	memset(&cmd, 0, sizeof cmd);
5379227805Sbschmidt	cmd.ucode.once.enable = 0xffffffff;
5380227805Sbschmidt	cmd.ucode.once.start = IWN5000_CALIB_DC;
5381227805Sbschmidt	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
5382227805Sbschmidt	    "%s: configuring runtime calibration\n", __func__);
5383227805Sbschmidt	return iwn_cmd(sc, IWN5000_CMD_CALIB_CONFIG, &cmd, sizeof(cmd), 0);
5384227805Sbschmidt}
5385227805Sbschmidt
5386227805Sbschmidtstatic int
5387198429Srpauloiwn_config(struct iwn_softc *sc)
5388198429Srpaulo{
5389220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
5390198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
5391198429Srpaulo	struct ieee80211com *ic = ifp->if_l2com;
5392201209Srpaulo	uint32_t txmask;
5393220723Sbschmidt	uint16_t rxchain;
5394198429Srpaulo	int error;
5395198429Srpaulo
5396253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
5397253705Sadrian
5398220676Sbschmidt	if (sc->hw_type == IWN_HW_REV_TYPE_6005) {
5399220676Sbschmidt		/* Set radio temperature sensor offset. */
5400220676Sbschmidt		error = iwn5000_temp_offset_calib(sc);
5401220676Sbschmidt		if (error != 0) {
5402220676Sbschmidt			device_printf(sc->sc_dev,
5403220676Sbschmidt			    "%s: could not set temperature offset\n", __func__);
5404220676Sbschmidt			return error;
5405220676Sbschmidt		}
5406220676Sbschmidt	}
5407220676Sbschmidt
5408227805Sbschmidt	if (sc->hw_type == IWN_HW_REV_TYPE_6050) {
5409227805Sbschmidt		/* Configure runtime DC calibration. */
5410227805Sbschmidt		error = iwn5000_runtime_calib(sc);
5411227805Sbschmidt		if (error != 0) {
5412227805Sbschmidt			device_printf(sc->sc_dev,
5413227805Sbschmidt			    "%s: could not configure runtime calibration\n",
5414227805Sbschmidt			    __func__);
5415227805Sbschmidt			return error;
5416227805Sbschmidt		}
5417227805Sbschmidt	}
5418227805Sbschmidt
5419220725Sbschmidt	/* Configure valid TX chains for >=5000 Series. */
5420201209Srpaulo	if (sc->hw_type != IWN_HW_REV_TYPE_4965) {
5421201209Srpaulo		txmask = htole32(sc->txchainmask);
5422201209Srpaulo		DPRINTF(sc, IWN_DEBUG_RESET,
5423201209Srpaulo		    "%s: configuring valid TX chains 0x%x\n", __func__, txmask);
5424201209Srpaulo		error = iwn_cmd(sc, IWN5000_CMD_TX_ANT_CONFIG, &txmask,
5425201209Srpaulo		    sizeof txmask, 0);
5426201209Srpaulo		if (error != 0) {
5427201209Srpaulo			device_printf(sc->sc_dev,
5428201209Srpaulo			    "%s: could not configure valid TX chains, "
5429201209Srpaulo			    "error %d\n", __func__, error);
5430201209Srpaulo			return error;
5431201209Srpaulo		}
5432198429Srpaulo	}
5433198429Srpaulo
5434198429Srpaulo	/* Configure bluetooth coexistence. */
5435220891Sbschmidt	if (sc->sc_flags & IWN_FLAG_ADV_BTCOEX)
5436220891Sbschmidt		error = iwn_send_advanced_btcoex(sc);
5437220891Sbschmidt	else
5438220891Sbschmidt		error = iwn_send_btcoex(sc);
5439198429Srpaulo	if (error != 0) {
5440198429Srpaulo		device_printf(sc->sc_dev,
5441198429Srpaulo		    "%s: could not configure bluetooth coexistence, error %d\n",
5442198429Srpaulo		    __func__, error);
5443198429Srpaulo		return error;
5444198429Srpaulo	}
5445198429Srpaulo
5446201209Srpaulo	/* Set mode, channel, RX filter and enable RX. */
5447254204Sadrian	sc->rxon = &sc->rx_on[IWN_RXON_BSS_CTX];
5448254204Sadrian	memset(sc->rxon, 0, sizeof (struct iwn_rxon));
5449254204Sadrian	IEEE80211_ADDR_COPY(sc->rxon->myaddr, IF_LLADDR(ifp));
5450254204Sadrian	IEEE80211_ADDR_COPY(sc->rxon->wlap, IF_LLADDR(ifp));
5451254204Sadrian	sc->rxon->chan = ieee80211_chan2ieee(ic, ic->ic_curchan);
5452254204Sadrian	sc->rxon->flags = htole32(IWN_RXON_TSF | IWN_RXON_CTS_TO_SELF);
5453198429Srpaulo	if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan))
5454254204Sadrian		sc->rxon->flags |= htole32(IWN_RXON_AUTO | IWN_RXON_24GHZ);
5455198429Srpaulo	switch (ic->ic_opmode) {
5456198429Srpaulo	case IEEE80211_M_STA:
5457254204Sadrian		sc->rxon->mode = IWN_MODE_STA;
5458254204Sadrian		sc->rxon->filter = htole32(IWN_FILTER_MULTICAST);
5459198429Srpaulo		break;
5460198429Srpaulo	case IEEE80211_M_MONITOR:
5461254204Sadrian		sc->rxon->mode = IWN_MODE_MONITOR;
5462254204Sadrian		sc->rxon->filter = htole32(IWN_FILTER_MULTICAST |
5463198429Srpaulo		    IWN_FILTER_CTL | IWN_FILTER_PROMISC);
5464198429Srpaulo		break;
5465198429Srpaulo	default:
5466198429Srpaulo		/* Should not get there. */
5467198429Srpaulo		break;
5468198429Srpaulo	}
5469254204Sadrian	sc->rxon->cck_mask  = 0x0f;	/* not yet negotiated */
5470254204Sadrian	sc->rxon->ofdm_mask = 0xff;	/* not yet negotiated */
5471254204Sadrian	sc->rxon->ht_single_mask = 0xff;
5472254204Sadrian	sc->rxon->ht_dual_mask = 0xff;
5473254204Sadrian	sc->rxon->ht_triple_mask = 0xff;
5474201209Srpaulo	rxchain =
5475201209Srpaulo	    IWN_RXCHAIN_VALID(sc->rxchainmask) |
5476201209Srpaulo	    IWN_RXCHAIN_MIMO_COUNT(2) |
5477201209Srpaulo	    IWN_RXCHAIN_IDLE_COUNT(2);
5478254204Sadrian	sc->rxon->rxchain = htole16(rxchain);
5479198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "%s: setting configuration\n", __func__);
5480254204Sadrian	error = iwn_cmd(sc, IWN_CMD_RXON, sc->rxon, sc->rxonsz, 0);
5481198429Srpaulo	if (error != 0) {
5482220726Sbschmidt		device_printf(sc->sc_dev, "%s: RXON command failed\n",
5483220726Sbschmidt		    __func__);
5484198429Srpaulo		return error;
5485198429Srpaulo	}
5486198429Srpaulo
5487220726Sbschmidt	if ((error = iwn_add_broadcast_node(sc, 0)) != 0) {
5488220726Sbschmidt		device_printf(sc->sc_dev, "%s: could not add broadcast node\n",
5489220726Sbschmidt		    __func__);
5490201209Srpaulo		return error;
5491201209Srpaulo	}
5492201209Srpaulo
5493198429Srpaulo	/* Configuration has changed, set TX power accordingly. */
5494220728Sbschmidt	if ((error = ops->set_txpower(sc, ic->ic_curchan, 0)) != 0) {
5495220726Sbschmidt		device_printf(sc->sc_dev, "%s: could not set TX power\n",
5496220726Sbschmidt		    __func__);
5497198429Srpaulo		return error;
5498198429Srpaulo	}
5499198429Srpaulo
5500220726Sbschmidt	if ((error = iwn_set_critical_temp(sc)) != 0) {
5501198429Srpaulo		device_printf(sc->sc_dev,
5502220724Sbschmidt		    "%s: could not set critical temperature\n", __func__);
5503198429Srpaulo		return error;
5504198429Srpaulo	}
5505198429Srpaulo
5506201209Srpaulo	/* Set power saving level to CAM during initialization. */
5507220726Sbschmidt	if ((error = iwn_set_pslevel(sc, 0, 0, 0)) != 0) {
5508198429Srpaulo		device_printf(sc->sc_dev,
5509201209Srpaulo		    "%s: could not set power saving level\n", __func__);
5510198429Srpaulo		return error;
5511198429Srpaulo	}
5512253705Sadrian
5513253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
5514253705Sadrian
5515198429Srpaulo	return 0;
5516198429Srpaulo}
5517198429Srpaulo
5518220634Sbschmidt/*
5519220634Sbschmidt * Add an ssid element to a frame.
5520220634Sbschmidt */
5521220634Sbschmidtstatic uint8_t *
5522220634Sbschmidtieee80211_add_ssid(uint8_t *frm, const uint8_t *ssid, u_int len)
5523220634Sbschmidt{
5524220634Sbschmidt	*frm++ = IEEE80211_ELEMID_SSID;
5525220634Sbschmidt	*frm++ = len;
5526220634Sbschmidt	memcpy(frm, ssid, len);
5527220634Sbschmidt	return frm + len;
5528220634Sbschmidt}
5529220634Sbschmidt
5530206477Sbschmidtstatic int
5531198429Srpauloiwn_scan(struct iwn_softc *sc)
5532198429Srpaulo{
5533198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
5534198429Srpaulo	struct ieee80211com *ic = ifp->if_l2com;
5535198429Srpaulo	struct ieee80211_scan_state *ss = ic->ic_scan;	/*XXX*/
5536221641Sbschmidt	struct ieee80211_node *ni = ss->ss_vap->iv_bss;
5537198429Srpaulo	struct iwn_scan_hdr *hdr;
5538198429Srpaulo	struct iwn_cmd_data *tx;
5539198429Srpaulo	struct iwn_scan_essid *essid;
5540198429Srpaulo	struct iwn_scan_chan *chan;
5541198429Srpaulo	struct ieee80211_frame *wh;
5542198429Srpaulo	struct ieee80211_rateset *rs;
5543198429Srpaulo	struct ieee80211_channel *c;
5544220726Sbschmidt	uint8_t *buf, *frm;
5545220723Sbschmidt	uint16_t rxchain;
5546220726Sbschmidt	uint8_t txant;
5547220634Sbschmidt	int buflen, error;
5548198429Srpaulo
5549253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
5550253705Sadrian
5551254204Sadrian	sc->rxon = &sc->rx_on[IWN_RXON_BSS_CTX];
5552198429Srpaulo	buf = malloc(IWN_SCAN_MAXSZ, M_DEVBUF, M_NOWAIT | M_ZERO);
5553198429Srpaulo	if (buf == NULL) {
5554198429Srpaulo		device_printf(sc->sc_dev,
5555198429Srpaulo		    "%s: could not allocate buffer for scan command\n",
5556198429Srpaulo		    __func__);
5557198429Srpaulo		return ENOMEM;
5558198429Srpaulo	}
5559198429Srpaulo	hdr = (struct iwn_scan_hdr *)buf;
5560198429Srpaulo	/*
5561198429Srpaulo	 * Move to the next channel if no frames are received within 10ms
5562198429Srpaulo	 * after sending the probe request.
5563198429Srpaulo	 */
5564198429Srpaulo	hdr->quiet_time = htole16(10);		/* timeout in milliseconds */
5565198429Srpaulo	hdr->quiet_threshold = htole16(1);	/* min # of packets */
5566198429Srpaulo
5567198429Srpaulo	/* Select antennas for scanning. */
5568201209Srpaulo	rxchain =
5569201209Srpaulo	    IWN_RXCHAIN_VALID(sc->rxchainmask) |
5570201209Srpaulo	    IWN_RXCHAIN_FORCE_MIMO_SEL(sc->rxchainmask) |
5571201209Srpaulo	    IWN_RXCHAIN_DRIVER_FORCE;
5572198429Srpaulo	if (IEEE80211_IS_CHAN_A(ic->ic_curchan) &&
5573198429Srpaulo	    sc->hw_type == IWN_HW_REV_TYPE_4965) {
5574198429Srpaulo		/* Ant A must be avoided in 5GHz because of an HW bug. */
5575222679Sbschmidt		rxchain |= IWN_RXCHAIN_FORCE_SEL(IWN_ANT_B);
5576198429Srpaulo	} else	/* Use all available RX antennas. */
5577201209Srpaulo		rxchain |= IWN_RXCHAIN_FORCE_SEL(sc->rxchainmask);
5578198429Srpaulo	hdr->rxchain = htole16(rxchain);
5579198429Srpaulo	hdr->filter = htole32(IWN_FILTER_MULTICAST | IWN_FILTER_BEACON);
5580198429Srpaulo
5581198429Srpaulo	tx = (struct iwn_cmd_data *)(hdr + 1);
5582198429Srpaulo	tx->flags = htole32(IWN_TX_AUTO_SEQ);
5583220728Sbschmidt	tx->id = sc->broadcast_id;
5584198429Srpaulo	tx->lifetime = htole32(IWN_LIFETIME_INFINITE);
5585198429Srpaulo
5586222679Sbschmidt	if (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan)) {
5587198429Srpaulo		/* Send probe requests at 6Mbps. */
5588221648Sbschmidt		tx->rate = htole32(0xd);
5589201209Srpaulo		rs = &ic->ic_sup_rates[IEEE80211_MODE_11A];
5590198429Srpaulo	} else {
5591198429Srpaulo		hdr->flags = htole32(IWN_RXON_24GHZ | IWN_RXON_AUTO);
5592222679Sbschmidt		if (sc->hw_type == IWN_HW_REV_TYPE_4965 &&
5593254204Sadrian		    sc->rxon->associd && sc->rxon->chan > 14)
5594222679Sbschmidt			tx->rate = htole32(0xd);
5595222679Sbschmidt		else {
5596222679Sbschmidt			/* Send probe requests at 1Mbps. */
5597222679Sbschmidt			tx->rate = htole32(10 | IWN_RFLAG_CCK);
5598222679Sbschmidt		}
5599201209Srpaulo		rs = &ic->ic_sup_rates[IEEE80211_MODE_11G];
5600198429Srpaulo	}
5601198429Srpaulo	/* Use the first valid TX antenna. */
5602201209Srpaulo	txant = IWN_LSB(sc->txchainmask);
5603221648Sbschmidt	tx->rate |= htole32(IWN_RFLAG_ANT(txant));
5604198429Srpaulo
5605198429Srpaulo	essid = (struct iwn_scan_essid *)(tx + 1);
5606198429Srpaulo	if (ss->ss_ssid[0].len != 0) {
5607198429Srpaulo		essid[0].id = IEEE80211_ELEMID_SSID;
5608198429Srpaulo		essid[0].len = ss->ss_ssid[0].len;
5609198429Srpaulo		memcpy(essid[0].data, ss->ss_ssid[0].ssid, ss->ss_ssid[0].len);
5610198429Srpaulo	}
5611198429Srpaulo	/*
5612198429Srpaulo	 * Build a probe request frame.  Most of the following code is a
5613198429Srpaulo	 * copy & paste of what is done in net80211.
5614198429Srpaulo	 */
5615198429Srpaulo	wh = (struct ieee80211_frame *)(essid + 20);
5616198429Srpaulo	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT |
5617198429Srpaulo	    IEEE80211_FC0_SUBTYPE_PROBE_REQ;
5618198429Srpaulo	wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
5619198429Srpaulo	IEEE80211_ADDR_COPY(wh->i_addr1, ifp->if_broadcastaddr);
5620198429Srpaulo	IEEE80211_ADDR_COPY(wh->i_addr2, IF_LLADDR(ifp));
5621198429Srpaulo	IEEE80211_ADDR_COPY(wh->i_addr3, ifp->if_broadcastaddr);
5622198429Srpaulo	*(uint16_t *)&wh->i_dur[0] = 0;	/* filled by HW */
5623198429Srpaulo	*(uint16_t *)&wh->i_seq[0] = 0;	/* filled by HW */
5624198429Srpaulo
5625198429Srpaulo	frm = (uint8_t *)(wh + 1);
5626220634Sbschmidt	frm = ieee80211_add_ssid(frm, NULL, 0);
5627220634Sbschmidt	frm = ieee80211_add_rates(frm, rs);
5628220634Sbschmidt	if (rs->rs_nrates > IEEE80211_RATE_SIZE)
5629220634Sbschmidt		frm = ieee80211_add_xrates(frm, rs);
5630221641Sbschmidt	if (ic->ic_htcaps & IEEE80211_HTC_HT)
5631221641Sbschmidt		frm = ieee80211_add_htcap(frm, ni);
5632198429Srpaulo
5633198429Srpaulo	/* Set length of probe request. */
5634198429Srpaulo	tx->len = htole16(frm - (uint8_t *)wh);
5635198429Srpaulo
5636198429Srpaulo	c = ic->ic_curchan;
5637198429Srpaulo	chan = (struct iwn_scan_chan *)frm;
5638201209Srpaulo	chan->chan = htole16(ieee80211_chan2ieee(ic, c));
5639198429Srpaulo	chan->flags = 0;
5640198429Srpaulo	if (ss->ss_nssid > 0)
5641198429Srpaulo		chan->flags |= htole32(IWN_CHAN_NPBREQS(1));
5642198429Srpaulo	chan->dsp_gain = 0x6e;
5643201209Srpaulo	if (IEEE80211_IS_CHAN_5GHZ(c) &&
5644201209Srpaulo	    !(c->ic_flags & IEEE80211_CHAN_PASSIVE)) {
5645198429Srpaulo		chan->rf_gain = 0x3b;
5646198429Srpaulo		chan->active  = htole16(24);
5647198429Srpaulo		chan->passive = htole16(110);
5648201209Srpaulo		chan->flags |= htole32(IWN_CHAN_ACTIVE);
5649201209Srpaulo	} else if (IEEE80211_IS_CHAN_5GHZ(c)) {
5650201209Srpaulo		chan->rf_gain = 0x3b;
5651201209Srpaulo		chan->active  = htole16(24);
5652254204Sadrian		if (sc->rxon->associd)
5653201209Srpaulo			chan->passive = htole16(78);
5654201209Srpaulo		else
5655201209Srpaulo			chan->passive = htole16(110);
5656207709Sbschmidt		hdr->crc_threshold = 0xffff;
5657201209Srpaulo	} else if (!(c->ic_flags & IEEE80211_CHAN_PASSIVE)) {
5658201209Srpaulo		chan->rf_gain = 0x28;
5659201209Srpaulo		chan->active  = htole16(36);
5660201209Srpaulo		chan->passive = htole16(120);
5661201209Srpaulo		chan->flags |= htole32(IWN_CHAN_ACTIVE);
5662198429Srpaulo	} else {
5663198429Srpaulo		chan->rf_gain = 0x28;
5664198429Srpaulo		chan->active  = htole16(36);
5665254204Sadrian		if (sc->rxon->associd)
5666201209Srpaulo			chan->passive = htole16(88);
5667201209Srpaulo		else
5668201209Srpaulo			chan->passive = htole16(120);
5669207709Sbschmidt		hdr->crc_threshold = 0xffff;
5670198429Srpaulo	}
5671198429Srpaulo
5672201209Srpaulo	DPRINTF(sc, IWN_DEBUG_STATE,
5673201209Srpaulo	    "%s: chan %u flags 0x%x rf_gain 0x%x "
5674198429Srpaulo	    "dsp_gain 0x%x active 0x%x passive 0x%x\n", __func__,
5675198429Srpaulo	    chan->chan, chan->flags, chan->rf_gain, chan->dsp_gain,
5676198429Srpaulo	    chan->active, chan->passive);
5677198429Srpaulo
5678201209Srpaulo	hdr->nchan++;
5679201209Srpaulo	chan++;
5680198429Srpaulo	buflen = (uint8_t *)chan - buf;
5681198429Srpaulo	hdr->len = htole16(buflen);
5682198429Srpaulo
5683198429Srpaulo	DPRINTF(sc, IWN_DEBUG_STATE, "sending scan command nchan=%d\n",
5684198429Srpaulo	    hdr->nchan);
5685198429Srpaulo	error = iwn_cmd(sc, IWN_CMD_SCAN, buf, buflen, 1);
5686198429Srpaulo	free(buf, M_DEVBUF);
5687253705Sadrian
5688253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
5689253705Sadrian
5690198429Srpaulo	return error;
5691198429Srpaulo}
5692198429Srpaulo
5693206477Sbschmidtstatic int
5694191746Sthompsaiwn_auth(struct iwn_softc *sc, struct ieee80211vap *vap)
5695178676Ssam{
5696220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
5697178676Ssam	struct ifnet *ifp = sc->sc_ifp;
5698178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
5699178676Ssam	struct ieee80211_node *ni = vap->iv_bss;
5700178676Ssam	int error;
5701178676Ssam
5702253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
5703253705Sadrian
5704254204Sadrian	sc->rxon = &sc->rx_on[IWN_RXON_BSS_CTX];
5705201209Srpaulo	/* Update adapter configuration. */
5706254204Sadrian	IEEE80211_ADDR_COPY(sc->rxon->bssid, ni->ni_bssid);
5707254204Sadrian	sc->rxon->chan = ieee80211_chan2ieee(ic, ni->ni_chan);
5708254204Sadrian	sc->rxon->flags = htole32(IWN_RXON_TSF | IWN_RXON_CTS_TO_SELF);
5709178676Ssam	if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
5710254204Sadrian		sc->rxon->flags |= htole32(IWN_RXON_AUTO | IWN_RXON_24GHZ);
5711198429Srpaulo	if (ic->ic_flags & IEEE80211_F_SHSLOT)
5712254204Sadrian		sc->rxon->flags |= htole32(IWN_RXON_SHSLOT);
5713198429Srpaulo	if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
5714254204Sadrian		sc->rxon->flags |= htole32(IWN_RXON_SHPREAMBLE);
5715178676Ssam	if (IEEE80211_IS_CHAN_A(ni->ni_chan)) {
5716254204Sadrian		sc->rxon->cck_mask  = 0;
5717254204Sadrian		sc->rxon->ofdm_mask = 0x15;
5718178676Ssam	} else if (IEEE80211_IS_CHAN_B(ni->ni_chan)) {
5719254204Sadrian		sc->rxon->cck_mask  = 0x03;
5720254204Sadrian		sc->rxon->ofdm_mask = 0;
5721178676Ssam	} else {
5722220725Sbschmidt		/* Assume 802.11b/g. */
5723254204Sadrian		sc->rxon->cck_mask  = 0x0f;
5724254204Sadrian		sc->rxon->ofdm_mask = 0x15;
5725178676Ssam	}
5726220724Sbschmidt	DPRINTF(sc, IWN_DEBUG_STATE, "rxon chan %d flags %x cck %x ofdm %x\n",
5727254204Sadrian	    sc->rxon->chan, sc->rxon->flags, sc->rxon->cck_mask,
5728254204Sadrian	    sc->rxon->ofdm_mask);
5729254204Sadrian	error = iwn_cmd(sc, IWN_CMD_RXON, sc->rxon, sc->rxonsz, 1);
5730178676Ssam	if (error != 0) {
5731220726Sbschmidt		device_printf(sc->sc_dev, "%s: RXON command failed, error %d\n",
5732220726Sbschmidt		    __func__, error);
5733178676Ssam		return error;
5734178676Ssam	}
5735178676Ssam
5736198429Srpaulo	/* Configuration has changed, set TX power accordingly. */
5737220728Sbschmidt	if ((error = ops->set_txpower(sc, ni->ni_chan, 1)) != 0) {
5738178676Ssam		device_printf(sc->sc_dev,
5739220724Sbschmidt		    "%s: could not set TX power, error %d\n", __func__, error);
5740178676Ssam		return error;
5741178676Ssam	}
5742178676Ssam	/*
5743201209Srpaulo	 * Reconfiguring RXON clears the firmware nodes table so we must
5744178676Ssam	 * add the broadcast node again.
5745178676Ssam	 */
5746220726Sbschmidt	if ((error = iwn_add_broadcast_node(sc, 1)) != 0) {
5747178676Ssam		device_printf(sc->sc_dev,
5748220726Sbschmidt		    "%s: could not add broadcast node, error %d\n", __func__,
5749220726Sbschmidt		    error);
5750178676Ssam		return error;
5751178676Ssam	}
5752253705Sadrian
5753253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
5754253705Sadrian
5755178676Ssam	return 0;
5756178676Ssam}
5757178676Ssam
5758206477Sbschmidtstatic int
5759191746Sthompsaiwn_run(struct iwn_softc *sc, struct ieee80211vap *vap)
5760178676Ssam{
5761220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
5762178676Ssam	struct ifnet *ifp = sc->sc_ifp;
5763178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
5764178676Ssam	struct ieee80211_node *ni = vap->iv_bss;
5765178676Ssam	struct iwn_node_info node;
5766221653Sbschmidt	uint32_t htflags = 0;
5767201209Srpaulo	int error;
5768178676Ssam
5769253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
5770253705Sadrian
5771254204Sadrian	sc->rxon = &sc->rx_on[IWN_RXON_BSS_CTX];
5772178676Ssam	if (ic->ic_opmode == IEEE80211_M_MONITOR) {
5773201209Srpaulo		/* Link LED blinks while monitoring. */
5774220674Sbschmidt		iwn_set_led(sc, IWN_LED_LINK, 5, 5);
5775178676Ssam		return 0;
5776178676Ssam	}
5777220726Sbschmidt	if ((error = iwn_set_timing(sc, ni)) != 0) {
5778198429Srpaulo		device_printf(sc->sc_dev,
5779198429Srpaulo		    "%s: could not set timing, error %d\n", __func__, error);
5780198429Srpaulo		return error;
5781198429Srpaulo	}
5782178676Ssam
5783201209Srpaulo	/* Update adapter configuration. */
5784254204Sadrian	IEEE80211_ADDR_COPY(sc->rxon->bssid, ni->ni_bssid);
5785254204Sadrian	sc->rxon->associd = htole16(IEEE80211_AID(ni->ni_associd));
5786254204Sadrian	sc->rxon->chan = ieee80211_chan2ieee(ic, ni->ni_chan);
5787254204Sadrian	sc->rxon->flags = htole32(IWN_RXON_TSF | IWN_RXON_CTS_TO_SELF);
5788201209Srpaulo	if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
5789254204Sadrian		sc->rxon->flags |= htole32(IWN_RXON_AUTO | IWN_RXON_24GHZ);
5790178676Ssam	if (ic->ic_flags & IEEE80211_F_SHSLOT)
5791254204Sadrian		sc->rxon->flags |= htole32(IWN_RXON_SHSLOT);
5792178676Ssam	if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
5793254204Sadrian		sc->rxon->flags |= htole32(IWN_RXON_SHPREAMBLE);
5794201209Srpaulo	if (IEEE80211_IS_CHAN_A(ni->ni_chan)) {
5795254204Sadrian		sc->rxon->cck_mask  = 0;
5796254204Sadrian		sc->rxon->ofdm_mask = 0x15;
5797201209Srpaulo	} else if (IEEE80211_IS_CHAN_B(ni->ni_chan)) {
5798254204Sadrian		sc->rxon->cck_mask  = 0x03;
5799254204Sadrian		sc->rxon->ofdm_mask = 0;
5800201209Srpaulo	} else {
5801220725Sbschmidt		/* Assume 802.11b/g. */
5802254204Sadrian		sc->rxon->cck_mask  = 0x0f;
5803254204Sadrian		sc->rxon->ofdm_mask = 0x15;
5804201209Srpaulo	}
5805178676Ssam	if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) {
5806221653Sbschmidt		htflags |= IWN_RXON_HT_PROTMODE(ic->ic_curhtprotmode);
5807221653Sbschmidt		if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) {
5808221653Sbschmidt			switch (ic->ic_curhtprotmode) {
5809221653Sbschmidt			case IEEE80211_HTINFO_OPMODE_HT20PR:
5810221653Sbschmidt				htflags |= IWN_RXON_HT_MODEPURE40;
5811221653Sbschmidt				break;
5812221653Sbschmidt			default:
5813221653Sbschmidt				htflags |= IWN_RXON_HT_MODEMIXED;
5814221653Sbschmidt				break;
5815221653Sbschmidt			}
5816221653Sbschmidt		}
5817221653Sbschmidt		if (IEEE80211_IS_CHAN_HT40D(ni->ni_chan))
5818221653Sbschmidt			htflags |= IWN_RXON_HT_HT40MINUS;
5819221653Sbschmidt	}
5820254204Sadrian	sc->rxon->flags |= htole32(htflags);
5821254204Sadrian	sc->rxon->filter |= htole32(IWN_FILTER_BSS);
5822220724Sbschmidt	DPRINTF(sc, IWN_DEBUG_STATE, "rxon chan %d flags %x\n",
5823254204Sadrian	    sc->rxon->chan, sc->rxon->flags);
5824254204Sadrian	error = iwn_cmd(sc, IWN_CMD_RXON, sc->rxon, sc->rxonsz, 1);
5825178676Ssam	if (error != 0) {
5826178676Ssam		device_printf(sc->sc_dev,
5827220726Sbschmidt		    "%s: could not update configuration, error %d\n", __func__,
5828220726Sbschmidt		    error);
5829178676Ssam		return error;
5830178676Ssam	}
5831178676Ssam
5832198429Srpaulo	/* Configuration has changed, set TX power accordingly. */
5833220728Sbschmidt	if ((error = ops->set_txpower(sc, ni->ni_chan, 1)) != 0) {
5834178676Ssam		device_printf(sc->sc_dev,
5835220724Sbschmidt		    "%s: could not set TX power, error %d\n", __func__, error);
5836178676Ssam		return error;
5837178676Ssam	}
5838178676Ssam
5839220715Sbschmidt	/* Fake a join to initialize the TX rate. */
5840220715Sbschmidt	((struct iwn_node *)ni)->id = IWN_ID_BSS;
5841220715Sbschmidt	iwn_newassoc(ni, 1);
5842220715Sbschmidt
5843198429Srpaulo	/* Add BSS node. */
5844178676Ssam	memset(&node, 0, sizeof node);
5845178676Ssam	IEEE80211_ADDR_COPY(node.macaddr, ni->ni_macaddr);
5846178676Ssam	node.id = IWN_ID_BSS;
5847221653Sbschmidt	if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) {
5848221653Sbschmidt		switch (ni->ni_htcap & IEEE80211_HTCAP_SMPS) {
5849221653Sbschmidt		case IEEE80211_HTCAP_SMPS_ENA:
5850221653Sbschmidt			node.htflags |= htole32(IWN_SMPS_MIMO_DIS);
5851221653Sbschmidt			break;
5852221653Sbschmidt		case IEEE80211_HTCAP_SMPS_DYNAMIC:
5853221653Sbschmidt			node.htflags |= htole32(IWN_SMPS_MIMO_PROT);
5854221653Sbschmidt			break;
5855221653Sbschmidt		}
5856221653Sbschmidt		node.htflags |= htole32(IWN_AMDPU_SIZE_FACTOR(3) |
5857221653Sbschmidt		    IWN_AMDPU_DENSITY(5));	/* 4us */
5858221653Sbschmidt		if (IEEE80211_IS_CHAN_HT40(ni->ni_chan))
5859221653Sbschmidt			node.htflags |= htole32(IWN_NODE_HT40);
5860221653Sbschmidt	}
5861220724Sbschmidt	DPRINTF(sc, IWN_DEBUG_STATE, "%s: adding BSS node\n", __func__);
5862220728Sbschmidt	error = ops->add_node(sc, &node, 1);
5863178676Ssam	if (error != 0) {
5864220724Sbschmidt		device_printf(sc->sc_dev,
5865220724Sbschmidt		    "%s: could not add BSS node, error %d\n", __func__, error);
5866178676Ssam		return error;
5867178676Ssam	}
5868220724Sbschmidt	DPRINTF(sc, IWN_DEBUG_STATE, "%s: setting link quality for node %d\n",
5869220724Sbschmidt	    __func__, node.id);
5870220726Sbschmidt	if ((error = iwn_set_link_quality(sc, ni)) != 0) {
5871178676Ssam		device_printf(sc->sc_dev,
5872220724Sbschmidt		    "%s: could not setup link quality for node %d, error %d\n",
5873178676Ssam		    __func__, node.id, error);
5874178676Ssam		return error;
5875178676Ssam	}
5876178676Ssam
5877220726Sbschmidt	if ((error = iwn_init_sensitivity(sc)) != 0) {
5878178676Ssam		device_printf(sc->sc_dev,
5879220726Sbschmidt		    "%s: could not set sensitivity, error %d\n", __func__,
5880220726Sbschmidt		    error);
5881178676Ssam		return error;
5882178676Ssam	}
5883198429Srpaulo	/* Start periodic calibration timer. */
5884178676Ssam	sc->calib.state = IWN_CALIB_STATE_ASSOC;
5885220667Sbschmidt	sc->calib_cnt = 0;
5886220667Sbschmidt	callout_reset(&sc->calib_to, msecs_to_ticks(500), iwn_calib_timeout,
5887220667Sbschmidt	    sc);
5888178676Ssam
5889198429Srpaulo	/* Link LED always on while associated. */
5890178676Ssam	iwn_set_led(sc, IWN_LED_LINK, 0, 1);
5891253705Sadrian
5892253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
5893253705Sadrian
5894178676Ssam	return 0;
5895178676Ssam}
5896178676Ssam
5897178676Ssam/*
5898201209Srpaulo * This function is called by upper layer when an ADDBA request is received
5899201209Srpaulo * from another STA and before the ADDBA response is sent.
5900201209Srpaulo */
5901206477Sbschmidtstatic int
5902221650Sbschmidtiwn_ampdu_rx_start(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap,
5903221650Sbschmidt    int baparamset, int batimeout, int baseqctl)
5904201209Srpaulo{
5905221650Sbschmidt#define MS(_v, _f)	(((_v) & _f) >> _f##_S)
5906221650Sbschmidt	struct iwn_softc *sc = ni->ni_ic->ic_ifp->if_softc;
5907220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
5908201209Srpaulo	struct iwn_node *wn = (void *)ni;
5909201209Srpaulo	struct iwn_node_info node;
5910221650Sbschmidt	uint16_t ssn;
5911221650Sbschmidt	uint8_t tid;
5912221650Sbschmidt	int error;
5913201209Srpaulo
5914253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
5915253705Sadrian
5916221650Sbschmidt	tid = MS(le16toh(baparamset), IEEE80211_BAPS_TID);
5917221650Sbschmidt	ssn = MS(le16toh(baseqctl), IEEE80211_BASEQ_START);
5918221650Sbschmidt
5919201209Srpaulo	memset(&node, 0, sizeof node);
5920201209Srpaulo	node.id = wn->id;
5921201209Srpaulo	node.control = IWN_NODE_UPDATE;
5922201209Srpaulo	node.flags = IWN_FLAG_SET_ADDBA;
5923201209Srpaulo	node.addba_tid = tid;
5924221650Sbschmidt	node.addba_ssn = htole16(ssn);
5925201209Srpaulo	DPRINTF(sc, IWN_DEBUG_RECV, "ADDBA RA=%d TID=%d SSN=%d\n",
5926221650Sbschmidt	    wn->id, tid, ssn);
5927221650Sbschmidt	error = ops->add_node(sc, &node, 1);
5928221650Sbschmidt	if (error != 0)
5929221650Sbschmidt		return error;
5930221650Sbschmidt	return sc->sc_ampdu_rx_start(ni, rap, baparamset, batimeout, baseqctl);
5931221650Sbschmidt#undef MS
5932201209Srpaulo}
5933201209Srpaulo
5934201209Srpaulo/*
5935201209Srpaulo * This function is called by upper layer on teardown of an HT-immediate
5936220725Sbschmidt * Block Ack agreement (eg. uppon receipt of a DELBA frame).
5937201209Srpaulo */
5938206477Sbschmidtstatic void
5939221650Sbschmidtiwn_ampdu_rx_stop(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap)
5940201209Srpaulo{
5941221650Sbschmidt	struct ieee80211com *ic = ni->ni_ic;
5942221650Sbschmidt	struct iwn_softc *sc = ic->ic_ifp->if_softc;
5943220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
5944201209Srpaulo	struct iwn_node *wn = (void *)ni;
5945201209Srpaulo	struct iwn_node_info node;
5946221650Sbschmidt	uint8_t tid;
5947201209Srpaulo
5948253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
5949253705Sadrian
5950221650Sbschmidt	/* XXX: tid as an argument */
5951221650Sbschmidt	for (tid = 0; tid < WME_NUM_TID; tid++) {
5952221650Sbschmidt		if (&ni->ni_rx_ampdu[tid] == rap)
5953221650Sbschmidt			break;
5954221650Sbschmidt	}
5955221650Sbschmidt
5956201209Srpaulo	memset(&node, 0, sizeof node);
5957201209Srpaulo	node.id = wn->id;
5958201209Srpaulo	node.control = IWN_NODE_UPDATE;
5959201209Srpaulo	node.flags = IWN_FLAG_SET_DELBA;
5960201209Srpaulo	node.delba_tid = tid;
5961201209Srpaulo	DPRINTF(sc, IWN_DEBUG_RECV, "DELBA RA=%d TID=%d\n", wn->id, tid);
5962220728Sbschmidt	(void)ops->add_node(sc, &node, 1);
5963221650Sbschmidt	sc->sc_ampdu_rx_stop(ni, rap);
5964201209Srpaulo}
5965201209Srpaulo
5966221651Sbschmidtstatic int
5967221651Sbschmidtiwn_addba_request(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap,
5968221651Sbschmidt    int dialogtoken, int baparamset, int batimeout)
5969221651Sbschmidt{
5970221651Sbschmidt	struct iwn_softc *sc = ni->ni_ic->ic_ifp->if_softc;
5971221651Sbschmidt	int qid;
5972221651Sbschmidt
5973253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
5974253705Sadrian
5975221651Sbschmidt	for (qid = sc->firstaggqueue; qid < sc->ntxqs; qid++) {
5976221651Sbschmidt		if (sc->qid2tap[qid] == NULL)
5977221651Sbschmidt			break;
5978221651Sbschmidt	}
5979221651Sbschmidt	if (qid == sc->ntxqs) {
5980221651Sbschmidt		DPRINTF(sc, IWN_DEBUG_XMIT, "%s: not free aggregation queue\n",
5981221651Sbschmidt		    __func__);
5982221651Sbschmidt		return 0;
5983221651Sbschmidt	}
5984221651Sbschmidt	tap->txa_private = malloc(sizeof(int), M_DEVBUF, M_NOWAIT);
5985221651Sbschmidt	if (tap->txa_private == NULL) {
5986221651Sbschmidt		device_printf(sc->sc_dev,
5987221651Sbschmidt		    "%s: failed to alloc TX aggregation structure\n", __func__);
5988221651Sbschmidt		return 0;
5989221651Sbschmidt	}
5990221651Sbschmidt	sc->qid2tap[qid] = tap;
5991221651Sbschmidt	*(int *)tap->txa_private = qid;
5992221651Sbschmidt	return sc->sc_addba_request(ni, tap, dialogtoken, baparamset,
5993221651Sbschmidt	    batimeout);
5994221651Sbschmidt}
5995221651Sbschmidt
5996221651Sbschmidtstatic int
5997221651Sbschmidtiwn_addba_response(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap,
5998221651Sbschmidt    int code, int baparamset, int batimeout)
5999221651Sbschmidt{
6000221651Sbschmidt	struct iwn_softc *sc = ni->ni_ic->ic_ifp->if_softc;
6001221651Sbschmidt	int qid = *(int *)tap->txa_private;
6002234324Sadrian	uint8_t tid = tap->txa_tid;
6003221651Sbschmidt	int ret;
6004221651Sbschmidt
6005253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
6006253705Sadrian
6007221651Sbschmidt	if (code == IEEE80211_STATUS_SUCCESS) {
6008221651Sbschmidt		ni->ni_txseqs[tid] = tap->txa_start & 0xfff;
6009221651Sbschmidt		ret = iwn_ampdu_tx_start(ni->ni_ic, ni, tid);
6010221651Sbschmidt		if (ret != 1)
6011221651Sbschmidt			return ret;
6012221651Sbschmidt	} else {
6013221651Sbschmidt		sc->qid2tap[qid] = NULL;
6014221651Sbschmidt		free(tap->txa_private, M_DEVBUF);
6015221651Sbschmidt		tap->txa_private = NULL;
6016221651Sbschmidt	}
6017221651Sbschmidt	return sc->sc_addba_response(ni, tap, code, baparamset, batimeout);
6018221651Sbschmidt}
6019221651Sbschmidt
6020201209Srpaulo/*
6021201209Srpaulo * This function is called by upper layer when an ADDBA response is received
6022201209Srpaulo * from another STA.
6023201209Srpaulo */
6024206477Sbschmidtstatic int
6025201209Srpauloiwn_ampdu_tx_start(struct ieee80211com *ic, struct ieee80211_node *ni,
6026201209Srpaulo    uint8_t tid)
6027201209Srpaulo{
6028234324Sadrian	struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[tid];
6029221651Sbschmidt	struct iwn_softc *sc = ni->ni_ic->ic_ifp->if_softc;
6030220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
6031201209Srpaulo	struct iwn_node *wn = (void *)ni;
6032201209Srpaulo	struct iwn_node_info node;
6033221651Sbschmidt	int error, qid;
6034201209Srpaulo
6035253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
6036253705Sadrian
6037201209Srpaulo	/* Enable TX for the specified RA/TID. */
6038201209Srpaulo	wn->disable_tid &= ~(1 << tid);
6039201209Srpaulo	memset(&node, 0, sizeof node);
6040201209Srpaulo	node.id = wn->id;
6041201209Srpaulo	node.control = IWN_NODE_UPDATE;
6042201209Srpaulo	node.flags = IWN_FLAG_SET_DISABLE_TID;
6043201209Srpaulo	node.disable_tid = htole16(wn->disable_tid);
6044220728Sbschmidt	error = ops->add_node(sc, &node, 1);
6045201209Srpaulo	if (error != 0)
6046221651Sbschmidt		return 0;
6047201209Srpaulo
6048201209Srpaulo	if ((error = iwn_nic_lock(sc)) != 0)
6049221651Sbschmidt		return 0;
6050221651Sbschmidt	qid = *(int *)tap->txa_private;
6051237649Sbschmidt	DPRINTF(sc, IWN_DEBUG_XMIT, "%s: ra=%d tid=%d ssn=%d qid=%d\n",
6052237649Sbschmidt	    __func__, wn->id, tid, tap->txa_start, qid);
6053221651Sbschmidt	ops->ampdu_tx_start(sc, ni, qid, tid, tap->txa_start & 0xfff);
6054201209Srpaulo	iwn_nic_unlock(sc);
6055221651Sbschmidt
6056221651Sbschmidt	iwn_set_link_quality(sc, ni);
6057221651Sbschmidt	return 1;
6058201209Srpaulo}
6059201209Srpaulo
6060206477Sbschmidtstatic void
6061221651Sbschmidtiwn_ampdu_tx_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap)
6062201209Srpaulo{
6063221651Sbschmidt	struct iwn_softc *sc = ni->ni_ic->ic_ifp->if_softc;
6064220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
6065234324Sadrian	uint8_t tid = tap->txa_tid;
6066221651Sbschmidt	int qid;
6067201209Srpaulo
6068253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
6069253705Sadrian
6070237649Sbschmidt	sc->sc_addba_stop(ni, tap);
6071237649Sbschmidt
6072221651Sbschmidt	if (tap->txa_private == NULL)
6073221651Sbschmidt		return;
6074221651Sbschmidt
6075221651Sbschmidt	qid = *(int *)tap->txa_private;
6076237649Sbschmidt	if (sc->txq[qid].queued != 0)
6077237649Sbschmidt		return;
6078220726Sbschmidt	if (iwn_nic_lock(sc) != 0)
6079201209Srpaulo		return;
6080221651Sbschmidt	ops->ampdu_tx_stop(sc, qid, tid, tap->txa_start & 0xfff);
6081201209Srpaulo	iwn_nic_unlock(sc);
6082221651Sbschmidt	sc->qid2tap[qid] = NULL;
6083221651Sbschmidt	free(tap->txa_private, M_DEVBUF);
6084221651Sbschmidt	tap->txa_private = NULL;
6085201209Srpaulo}
6086201209Srpaulo
6087206477Sbschmidtstatic void
6088201209Srpauloiwn4965_ampdu_tx_start(struct iwn_softc *sc, struct ieee80211_node *ni,
6089221651Sbschmidt    int qid, uint8_t tid, uint16_t ssn)
6090201209Srpaulo{
6091201209Srpaulo	struct iwn_node *wn = (void *)ni;
6092201209Srpaulo
6093253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
6094253705Sadrian
6095201209Srpaulo	/* Stop TX scheduler while we're changing its configuration. */
6096201209Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid),
6097201209Srpaulo	    IWN4965_TXQ_STATUS_CHGACT);
6098201209Srpaulo
6099201209Srpaulo	/* Assign RA/TID translation to the queue. */
6100201209Srpaulo	iwn_mem_write_2(sc, sc->sched_base + IWN4965_SCHED_TRANS_TBL(qid),
6101201209Srpaulo	    wn->id << 4 | tid);
6102201209Srpaulo
6103201209Srpaulo	/* Enable chain-building mode for the queue. */
6104201209Srpaulo	iwn_prph_setbits(sc, IWN4965_SCHED_QCHAIN_SEL, 1 << qid);
6105201209Srpaulo
6106201209Srpaulo	/* Set starting sequence number from the ADDBA request. */
6107221651Sbschmidt	sc->txq[qid].cur = sc->txq[qid].read = (ssn & 0xff);
6108201209Srpaulo	IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff));
6109201209Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_QUEUE_RDPTR(qid), ssn);
6110201209Srpaulo
6111201209Srpaulo	/* Set scheduler window size. */
6112201209Srpaulo	iwn_mem_write(sc, sc->sched_base + IWN4965_SCHED_QUEUE_OFFSET(qid),
6113201209Srpaulo	    IWN_SCHED_WINSZ);
6114201209Srpaulo	/* Set scheduler frame limit. */
6115201209Srpaulo	iwn_mem_write(sc, sc->sched_base + IWN4965_SCHED_QUEUE_OFFSET(qid) + 4,
6116201209Srpaulo	    IWN_SCHED_LIMIT << 16);
6117201209Srpaulo
6118201209Srpaulo	/* Enable interrupts for the queue. */
6119201209Srpaulo	iwn_prph_setbits(sc, IWN4965_SCHED_INTR_MASK, 1 << qid);
6120201209Srpaulo
6121201209Srpaulo	/* Mark the queue as active. */
6122201209Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid),
6123201209Srpaulo	    IWN4965_TXQ_STATUS_ACTIVE | IWN4965_TXQ_STATUS_AGGR_ENA |
6124201209Srpaulo	    iwn_tid2fifo[tid] << 1);
6125201209Srpaulo}
6126201209Srpaulo
6127206477Sbschmidtstatic void
6128221651Sbschmidtiwn4965_ampdu_tx_stop(struct iwn_softc *sc, int qid, uint8_t tid, uint16_t ssn)
6129201209Srpaulo{
6130253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
6131253705Sadrian
6132201209Srpaulo	/* Stop TX scheduler while we're changing its configuration. */
6133201209Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid),
6134201209Srpaulo	    IWN4965_TXQ_STATUS_CHGACT);
6135201209Srpaulo
6136201209Srpaulo	/* Set starting sequence number from the ADDBA request. */
6137201209Srpaulo	IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff));
6138201209Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_QUEUE_RDPTR(qid), ssn);
6139201209Srpaulo
6140201209Srpaulo	/* Disable interrupts for the queue. */
6141201209Srpaulo	iwn_prph_clrbits(sc, IWN4965_SCHED_INTR_MASK, 1 << qid);
6142201209Srpaulo
6143201209Srpaulo	/* Mark the queue as inactive. */
6144201209Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid),
6145201209Srpaulo	    IWN4965_TXQ_STATUS_INACTIVE | iwn_tid2fifo[tid] << 1);
6146201209Srpaulo}
6147201209Srpaulo
6148206477Sbschmidtstatic void
6149201209Srpauloiwn5000_ampdu_tx_start(struct iwn_softc *sc, struct ieee80211_node *ni,
6150221651Sbschmidt    int qid, uint8_t tid, uint16_t ssn)
6151201209Srpaulo{
6152253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
6153253705Sadrian
6154201209Srpaulo	struct iwn_node *wn = (void *)ni;
6155201209Srpaulo
6156201209Srpaulo	/* Stop TX scheduler while we're changing its configuration. */
6157201209Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid),
6158201209Srpaulo	    IWN5000_TXQ_STATUS_CHGACT);
6159201209Srpaulo
6160201209Srpaulo	/* Assign RA/TID translation to the queue. */
6161201209Srpaulo	iwn_mem_write_2(sc, sc->sched_base + IWN5000_SCHED_TRANS_TBL(qid),
6162201209Srpaulo	    wn->id << 4 | tid);
6163201209Srpaulo
6164201209Srpaulo	/* Enable chain-building mode for the queue. */
6165201209Srpaulo	iwn_prph_setbits(sc, IWN5000_SCHED_QCHAIN_SEL, 1 << qid);
6166201209Srpaulo
6167201209Srpaulo	/* Enable aggregation for the queue. */
6168201209Srpaulo	iwn_prph_setbits(sc, IWN5000_SCHED_AGGR_SEL, 1 << qid);
6169201209Srpaulo
6170201209Srpaulo	/* Set starting sequence number from the ADDBA request. */
6171221651Sbschmidt	sc->txq[qid].cur = sc->txq[qid].read = (ssn & 0xff);
6172201209Srpaulo	IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff));
6173201209Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_QUEUE_RDPTR(qid), ssn);
6174201209Srpaulo
6175201209Srpaulo	/* Set scheduler window size and frame limit. */
6176201209Srpaulo	iwn_mem_write(sc, sc->sched_base + IWN5000_SCHED_QUEUE_OFFSET(qid) + 4,
6177201209Srpaulo	    IWN_SCHED_LIMIT << 16 | IWN_SCHED_WINSZ);
6178201209Srpaulo
6179201209Srpaulo	/* Enable interrupts for the queue. */
6180201209Srpaulo	iwn_prph_setbits(sc, IWN5000_SCHED_INTR_MASK, 1 << qid);
6181201209Srpaulo
6182201209Srpaulo	/* Mark the queue as active. */
6183201209Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid),
6184201209Srpaulo	    IWN5000_TXQ_STATUS_ACTIVE | iwn_tid2fifo[tid]);
6185201209Srpaulo}
6186201209Srpaulo
6187206477Sbschmidtstatic void
6188221651Sbschmidtiwn5000_ampdu_tx_stop(struct iwn_softc *sc, int qid, uint8_t tid, uint16_t ssn)
6189201209Srpaulo{
6190253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
6191253705Sadrian
6192201209Srpaulo	/* Stop TX scheduler while we're changing its configuration. */
6193201209Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid),
6194201209Srpaulo	    IWN5000_TXQ_STATUS_CHGACT);
6195201209Srpaulo
6196201209Srpaulo	/* Disable aggregation for the queue. */
6197201209Srpaulo	iwn_prph_clrbits(sc, IWN5000_SCHED_AGGR_SEL, 1 << qid);
6198201209Srpaulo
6199201209Srpaulo	/* Set starting sequence number from the ADDBA request. */
6200201209Srpaulo	IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff));
6201201209Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_QUEUE_RDPTR(qid), ssn);
6202201209Srpaulo
6203201209Srpaulo	/* Disable interrupts for the queue. */
6204201209Srpaulo	iwn_prph_clrbits(sc, IWN5000_SCHED_INTR_MASK, 1 << qid);
6205201209Srpaulo
6206201209Srpaulo	/* Mark the queue as inactive. */
6207201209Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid),
6208201209Srpaulo	    IWN5000_TXQ_STATUS_INACTIVE | iwn_tid2fifo[tid]);
6209201209Srpaulo}
6210201209Srpaulo
6211201209Srpaulo/*
6212220674Sbschmidt * Query calibration tables from the initialization firmware.  We do this
6213220674Sbschmidt * only once at first boot.  Called from a process context.
6214212853Sbschmidt */
6215212853Sbschmidtstatic int
6216220674Sbschmidtiwn5000_query_calibration(struct iwn_softc *sc)
6217212853Sbschmidt{
6218198429Srpaulo	struct iwn5000_calib_config cmd;
6219198429Srpaulo	int error;
6220178676Ssam
6221198429Srpaulo	memset(&cmd, 0, sizeof cmd);
6222220674Sbschmidt	cmd.ucode.once.enable = 0xffffffff;
6223220674Sbschmidt	cmd.ucode.once.start  = 0xffffffff;
6224220674Sbschmidt	cmd.ucode.once.send   = 0xffffffff;
6225220674Sbschmidt	cmd.ucode.flags       = 0xffffffff;
6226220674Sbschmidt	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: sending calibration query\n",
6227220674Sbschmidt	    __func__);
6228198429Srpaulo	error = iwn_cmd(sc, IWN5000_CMD_CALIB_CONFIG, &cmd, sizeof cmd, 0);
6229198429Srpaulo	if (error != 0)
6230198429Srpaulo		return error;
6231178676Ssam
6232198429Srpaulo	/* Wait at most two seconds for calibration to complete. */
6233201209Srpaulo	if (!(sc->sc_flags & IWN_FLAG_CALIB_DONE))
6234220674Sbschmidt		error = msleep(sc, &sc->sc_mtx, PCATCH, "iwncal", 2 * hz);
6235201209Srpaulo	return error;
6236198429Srpaulo}
6237198429Srpaulo
6238198429Srpaulo/*
6239220674Sbschmidt * Send calibration results to the runtime firmware.  These results were
6240220674Sbschmidt * obtained on first boot from the initialization firmware.
6241198429Srpaulo */
6242212854Sbschmidtstatic int
6243220674Sbschmidtiwn5000_send_calibration(struct iwn_softc *sc)
6244198429Srpaulo{
6245220674Sbschmidt	int idx, error;
6246198429Srpaulo
6247220674Sbschmidt	for (idx = 0; idx < 5; idx++) {
6248220674Sbschmidt		if (sc->calibcmd[idx].buf == NULL)
6249220674Sbschmidt			continue;	/* No results available. */
6250198429Srpaulo		DPRINTF(sc, IWN_DEBUG_CALIBRATE,
6251220674Sbschmidt		    "send calibration result idx=%d len=%d\n", idx,
6252220674Sbschmidt		    sc->calibcmd[idx].len);
6253220674Sbschmidt		error = iwn_cmd(sc, IWN_CMD_PHY_CALIB, sc->calibcmd[idx].buf,
6254220674Sbschmidt		    sc->calibcmd[idx].len, 0);
6255220674Sbschmidt		if (error != 0) {
6256220674Sbschmidt			device_printf(sc->sc_dev,
6257220674Sbschmidt			    "%s: could not send calibration result, error %d\n",
6258220674Sbschmidt			    __func__, error);
6259220674Sbschmidt			return error;
6260220674Sbschmidt		}
6261178676Ssam	}
6262220674Sbschmidt	return 0;
6263198429Srpaulo}
6264178676Ssam
6265206477Sbschmidtstatic int
6266201209Srpauloiwn5000_send_wimax_coex(struct iwn_softc *sc)
6267201209Srpaulo{
6268201209Srpaulo	struct iwn5000_wimax_coex wimax;
6269201209Srpaulo
6270201209Srpaulo#ifdef notyet
6271201209Srpaulo	if (sc->hw_type == IWN_HW_REV_TYPE_6050) {
6272201209Srpaulo		/* Enable WiMAX coexistence for combo adapters. */
6273201209Srpaulo		wimax.flags =
6274201209Srpaulo		    IWN_WIMAX_COEX_ASSOC_WA_UNMASK |
6275201209Srpaulo		    IWN_WIMAX_COEX_UNASSOC_WA_UNMASK |
6276201209Srpaulo		    IWN_WIMAX_COEX_STA_TABLE_VALID |
6277201209Srpaulo		    IWN_WIMAX_COEX_ENABLE;
6278201209Srpaulo		memcpy(wimax.events, iwn6050_wimax_events,
6279201209Srpaulo		    sizeof iwn6050_wimax_events);
6280201209Srpaulo	} else
6281201209Srpaulo#endif
6282201209Srpaulo	{
6283201209Srpaulo		/* Disable WiMAX coexistence. */
6284201209Srpaulo		wimax.flags = 0;
6285201209Srpaulo		memset(wimax.events, 0, sizeof wimax.events);
6286201209Srpaulo	}
6287201209Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "%s: Configuring WiMAX coexistence\n",
6288201209Srpaulo	    __func__);
6289201209Srpaulo	return iwn_cmd(sc, IWN5000_CMD_WIMAX_COEX, &wimax, sizeof wimax, 0);
6290201209Srpaulo}
6291201209Srpaulo
6292220674Sbschmidtstatic int
6293220674Sbschmidtiwn5000_crystal_calib(struct iwn_softc *sc)
6294220674Sbschmidt{
6295220674Sbschmidt	struct iwn5000_phy_calib_crystal cmd;
6296220674Sbschmidt
6297220674Sbschmidt	memset(&cmd, 0, sizeof cmd);
6298220674Sbschmidt	cmd.code = IWN5000_PHY_CALIB_CRYSTAL;
6299220674Sbschmidt	cmd.ngroups = 1;
6300220674Sbschmidt	cmd.isvalid = 1;
6301220674Sbschmidt	cmd.cap_pin[0] = le32toh(sc->eeprom_crystal) & 0xff;
6302220674Sbschmidt	cmd.cap_pin[1] = (le32toh(sc->eeprom_crystal) >> 16) & 0xff;
6303220674Sbschmidt	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "sending crystal calibration %d, %d\n",
6304220674Sbschmidt	    cmd.cap_pin[0], cmd.cap_pin[1]);
6305220674Sbschmidt	return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 0);
6306220674Sbschmidt}
6307220674Sbschmidt
6308220676Sbschmidtstatic int
6309220676Sbschmidtiwn5000_temp_offset_calib(struct iwn_softc *sc)
6310220676Sbschmidt{
6311220676Sbschmidt	struct iwn5000_phy_calib_temp_offset cmd;
6312220676Sbschmidt
6313220676Sbschmidt	memset(&cmd, 0, sizeof cmd);
6314220676Sbschmidt	cmd.code = IWN5000_PHY_CALIB_TEMP_OFFSET;
6315220676Sbschmidt	cmd.ngroups = 1;
6316220676Sbschmidt	cmd.isvalid = 1;
6317220676Sbschmidt	if (sc->eeprom_temp != 0)
6318220676Sbschmidt		cmd.offset = htole16(sc->eeprom_temp);
6319220676Sbschmidt	else
6320220676Sbschmidt		cmd.offset = htole16(IWN_DEFAULT_TEMP_OFFSET);
6321220676Sbschmidt	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "setting radio sensor offset to %d\n",
6322220676Sbschmidt	    le16toh(cmd.offset));
6323220676Sbschmidt	return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 0);
6324220676Sbschmidt}
6325220676Sbschmidt
6326198429Srpaulo/*
6327198429Srpaulo * This function is called after the runtime firmware notifies us of its
6328220725Sbschmidt * readiness (called in a process context).
6329198429Srpaulo */
6330206477Sbschmidtstatic int
6331198429Srpauloiwn4965_post_alive(struct iwn_softc *sc)
6332198429Srpaulo{
6333198429Srpaulo	int error, qid;
6334178676Ssam
6335198429Srpaulo	if ((error = iwn_nic_lock(sc)) != 0)
6336198429Srpaulo		return error;
6337178676Ssam
6338253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
6339253705Sadrian
6340201209Srpaulo	/* Clear TX scheduler state in SRAM. */
6341198429Srpaulo	sc->sched_base = iwn_prph_read(sc, IWN_SCHED_SRAM_ADDR);
6342198429Srpaulo	iwn_mem_set_region_4(sc, sc->sched_base + IWN4965_SCHED_CTX_OFF, 0,
6343201209Srpaulo	    IWN4965_SCHED_CTX_LEN / sizeof (uint32_t));
6344178676Ssam
6345220725Sbschmidt	/* Set physical address of TX scheduler rings (1KB aligned). */
6346198429Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_DRAM_ADDR, sc->sched_dma.paddr >> 10);
6347178676Ssam
6348198429Srpaulo	IWN_SETBITS(sc, IWN_FH_TX_CHICKEN, IWN_FH_TX_CHICKEN_SCHED_RETRY);
6349178676Ssam
6350198429Srpaulo	/* Disable chain mode for all our 16 queues. */
6351198429Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_QCHAIN_SEL, 0);
6352198429Srpaulo
6353198429Srpaulo	for (qid = 0; qid < IWN4965_NTXQUEUES; qid++) {
6354198429Srpaulo		iwn_prph_write(sc, IWN4965_SCHED_QUEUE_RDPTR(qid), 0);
6355198429Srpaulo		IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | 0);
6356198429Srpaulo
6357198429Srpaulo		/* Set scheduler window size. */
6358198429Srpaulo		iwn_mem_write(sc, sc->sched_base +
6359198429Srpaulo		    IWN4965_SCHED_QUEUE_OFFSET(qid), IWN_SCHED_WINSZ);
6360198429Srpaulo		/* Set scheduler frame limit. */
6361198429Srpaulo		iwn_mem_write(sc, sc->sched_base +
6362198429Srpaulo		    IWN4965_SCHED_QUEUE_OFFSET(qid) + 4,
6363198429Srpaulo		    IWN_SCHED_LIMIT << 16);
6364178676Ssam	}
6365178676Ssam
6366198429Srpaulo	/* Enable interrupts for all our 16 queues. */
6367198429Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_INTR_MASK, 0xffff);
6368198429Srpaulo	/* Identify TX FIFO rings (0-7). */
6369198429Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_TXFACT, 0xff);
6370178676Ssam
6371198429Srpaulo	/* Mark TX rings (4 EDCA + cmd + 2 HCCA) as active. */
6372198429Srpaulo	for (qid = 0; qid < 7; qid++) {
6373198429Srpaulo		static uint8_t qid2fifo[] = { 3, 2, 1, 0, 4, 5, 6 };
6374198429Srpaulo		iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid),
6375198429Srpaulo		    IWN4965_TXQ_STATUS_ACTIVE | qid2fifo[qid] << 1);
6376198429Srpaulo	}
6377198429Srpaulo	iwn_nic_unlock(sc);
6378198429Srpaulo	return 0;
6379198429Srpaulo}
6380178676Ssam
6381198429Srpaulo/*
6382198429Srpaulo * This function is called after the initialization or runtime firmware
6383220725Sbschmidt * notifies us of its readiness (called in a process context).
6384198429Srpaulo */
6385206477Sbschmidtstatic int
6386198429Srpauloiwn5000_post_alive(struct iwn_softc *sc)
6387198429Srpaulo{
6388198429Srpaulo	int error, qid;
6389178676Ssam
6390253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
6391253705Sadrian
6392201209Srpaulo	/* Switch to using ICT interrupt mode. */
6393201209Srpaulo	iwn5000_ict_reset(sc);
6394201209Srpaulo
6395253705Sadrian	if ((error = iwn_nic_lock(sc)) != 0){
6396253705Sadrian		DPRINTF(sc, IWN_DEBUG_TRACE, "->%s end in error\n", __func__);
6397198429Srpaulo		return error;
6398253705Sadrian	}
6399178676Ssam
6400201209Srpaulo	/* Clear TX scheduler state in SRAM. */
6401198429Srpaulo	sc->sched_base = iwn_prph_read(sc, IWN_SCHED_SRAM_ADDR);
6402198429Srpaulo	iwn_mem_set_region_4(sc, sc->sched_base + IWN5000_SCHED_CTX_OFF, 0,
6403201209Srpaulo	    IWN5000_SCHED_CTX_LEN / sizeof (uint32_t));
6404178676Ssam
6405220725Sbschmidt	/* Set physical address of TX scheduler rings (1KB aligned). */
6406198429Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_DRAM_ADDR, sc->sched_dma.paddr >> 10);
6407178676Ssam
6408198429Srpaulo	IWN_SETBITS(sc, IWN_FH_TX_CHICKEN, IWN_FH_TX_CHICKEN_SCHED_RETRY);
6409178676Ssam
6410201209Srpaulo	/* Enable chain mode for all queues, except command queue. */
6411201209Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_QCHAIN_SEL, 0xfffef);
6412198429Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_AGGR_SEL, 0);
6413178676Ssam
6414198429Srpaulo	for (qid = 0; qid < IWN5000_NTXQUEUES; qid++) {
6415198429Srpaulo		iwn_prph_write(sc, IWN5000_SCHED_QUEUE_RDPTR(qid), 0);
6416198429Srpaulo		IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | 0);
6417198429Srpaulo
6418198429Srpaulo		iwn_mem_write(sc, sc->sched_base +
6419198429Srpaulo		    IWN5000_SCHED_QUEUE_OFFSET(qid), 0);
6420198429Srpaulo		/* Set scheduler window size and frame limit. */
6421198429Srpaulo		iwn_mem_write(sc, sc->sched_base +
6422198429Srpaulo		    IWN5000_SCHED_QUEUE_OFFSET(qid) + 4,
6423198429Srpaulo		    IWN_SCHED_LIMIT << 16 | IWN_SCHED_WINSZ);
6424178676Ssam	}
6425178676Ssam
6426198429Srpaulo	/* Enable interrupts for all our 20 queues. */
6427198429Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_INTR_MASK, 0xfffff);
6428198429Srpaulo	/* Identify TX FIFO rings (0-7). */
6429198429Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_TXFACT, 0xff);
6430178676Ssam
6431198429Srpaulo	/* Mark TX rings (4 EDCA + cmd + 2 HCCA) as active. */
6432198429Srpaulo	for (qid = 0; qid < 7; qid++) {
6433198429Srpaulo		static uint8_t qid2fifo[] = { 3, 2, 1, 0, 7, 5, 6 };
6434198429Srpaulo		iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid),
6435198429Srpaulo		    IWN5000_TXQ_STATUS_ACTIVE | qid2fifo[qid]);
6436198429Srpaulo	}
6437198429Srpaulo	iwn_nic_unlock(sc);
6438178676Ssam
6439201209Srpaulo	/* Configure WiMAX coexistence for combo adapters. */
6440201209Srpaulo	error = iwn5000_send_wimax_coex(sc);
6441178676Ssam	if (error != 0) {
6442178676Ssam		device_printf(sc->sc_dev,
6443198429Srpaulo		    "%s: could not configure WiMAX coexistence, error %d\n",
6444178676Ssam		    __func__, error);
6445178676Ssam		return error;
6446178676Ssam	}
6447220674Sbschmidt	if (sc->hw_type != IWN_HW_REV_TYPE_5150) {
6448220674Sbschmidt		/* Perform crystal calibration. */
6449220674Sbschmidt		error = iwn5000_crystal_calib(sc);
6450198429Srpaulo		if (error != 0) {
6451198429Srpaulo			device_printf(sc->sc_dev,
6452220674Sbschmidt			    "%s: crystal calibration failed, error %d\n",
6453220674Sbschmidt			    __func__, error);
6454198429Srpaulo			return error;
6455198429Srpaulo		}
6456220674Sbschmidt	}
6457220674Sbschmidt	if (!(sc->sc_flags & IWN_FLAG_CALIB_DONE)) {
6458220674Sbschmidt		/* Query calibration from the initialization firmware. */
6459220674Sbschmidt		if ((error = iwn5000_query_calibration(sc)) != 0) {
6460198429Srpaulo			device_printf(sc->sc_dev,
6461220674Sbschmidt			    "%s: could not query calibration, error %d\n",
6462198429Srpaulo			    __func__, error);
6463198429Srpaulo			return error;
6464198429Srpaulo		}
6465198429Srpaulo		/*
6466201209Srpaulo		 * We have the calibration results now, reboot with the
6467201209Srpaulo		 * runtime firmware (call ourselves recursively!)
6468198429Srpaulo		 */
6469198429Srpaulo		iwn_hw_stop(sc);
6470198429Srpaulo		error = iwn_hw_init(sc);
6471198429Srpaulo	} else {
6472220674Sbschmidt		/* Send calibration results to runtime firmware. */
6473220674Sbschmidt		error = iwn5000_send_calibration(sc);
6474198429Srpaulo	}
6475253705Sadrian
6476253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
6477253705Sadrian
6478198429Srpaulo	return error;
6479198429Srpaulo}
6480178676Ssam
6481198429Srpaulo/*
6482198429Srpaulo * The firmware boot code is small and is intended to be copied directly into
6483220725Sbschmidt * the NIC internal memory (no DMA transfer).
6484198429Srpaulo */
6485206477Sbschmidtstatic int
6486198429Srpauloiwn4965_load_bootcode(struct iwn_softc *sc, const uint8_t *ucode, int size)
6487198429Srpaulo{
6488198429Srpaulo	int error, ntries;
6489198429Srpaulo
6490198429Srpaulo	size /= sizeof (uint32_t);
6491198429Srpaulo
6492220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
6493198429Srpaulo		return error;
6494198429Srpaulo
6495198429Srpaulo	/* Copy microcode image into NIC memory. */
6496198429Srpaulo	iwn_prph_write_region_4(sc, IWN_BSM_SRAM_BASE,
6497198429Srpaulo	    (const uint32_t *)ucode, size);
6498198429Srpaulo
6499198429Srpaulo	iwn_prph_write(sc, IWN_BSM_WR_MEM_SRC, 0);
6500198429Srpaulo	iwn_prph_write(sc, IWN_BSM_WR_MEM_DST, IWN_FW_TEXT_BASE);
6501198429Srpaulo	iwn_prph_write(sc, IWN_BSM_WR_DWCOUNT, size);
6502198429Srpaulo
6503198429Srpaulo	/* Start boot load now. */
6504198429Srpaulo	iwn_prph_write(sc, IWN_BSM_WR_CTRL, IWN_BSM_WR_CTRL_START);
6505198429Srpaulo
6506198429Srpaulo	/* Wait for transfer to complete. */
6507198429Srpaulo	for (ntries = 0; ntries < 1000; ntries++) {
6508198429Srpaulo		if (!(iwn_prph_read(sc, IWN_BSM_WR_CTRL) &
6509198429Srpaulo		    IWN_BSM_WR_CTRL_START))
6510198429Srpaulo			break;
6511198429Srpaulo		DELAY(10);
6512198429Srpaulo	}
6513198429Srpaulo	if (ntries == 1000) {
6514198429Srpaulo		device_printf(sc->sc_dev, "%s: could not load boot firmware\n",
6515198429Srpaulo		    __func__);
6516198429Srpaulo		iwn_nic_unlock(sc);
6517198429Srpaulo		return ETIMEDOUT;
6518198429Srpaulo	}
6519198429Srpaulo
6520198429Srpaulo	/* Enable boot after power up. */
6521198429Srpaulo	iwn_prph_write(sc, IWN_BSM_WR_CTRL, IWN_BSM_WR_CTRL_START_EN);
6522198429Srpaulo
6523198429Srpaulo	iwn_nic_unlock(sc);
6524198429Srpaulo	return 0;
6525178676Ssam}
6526178676Ssam
6527206477Sbschmidtstatic int
6528198429Srpauloiwn4965_load_firmware(struct iwn_softc *sc)
6529178676Ssam{
6530198429Srpaulo	struct iwn_fw_info *fw = &sc->fw;
6531198429Srpaulo	struct iwn_dma_info *dma = &sc->fw_dma;
6532178676Ssam	int error;
6533178676Ssam
6534198429Srpaulo	/* Copy initialization sections into pre-allocated DMA-safe memory. */
6535198429Srpaulo	memcpy(dma->vaddr, fw->init.data, fw->init.datasz);
6536220661Sbschmidt	bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
6537198429Srpaulo	memcpy(dma->vaddr + IWN4965_FW_DATA_MAXSZ,
6538198429Srpaulo	    fw->init.text, fw->init.textsz);
6539220661Sbschmidt	bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
6540198429Srpaulo
6541198429Srpaulo	/* Tell adapter where to find initialization sections. */
6542220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
6543198429Srpaulo		return error;
6544198429Srpaulo	iwn_prph_write(sc, IWN_BSM_DRAM_DATA_ADDR, dma->paddr >> 4);
6545198429Srpaulo	iwn_prph_write(sc, IWN_BSM_DRAM_DATA_SIZE, fw->init.datasz);
6546198429Srpaulo	iwn_prph_write(sc, IWN_BSM_DRAM_TEXT_ADDR,
6547198429Srpaulo	    (dma->paddr + IWN4965_FW_DATA_MAXSZ) >> 4);
6548198429Srpaulo	iwn_prph_write(sc, IWN_BSM_DRAM_TEXT_SIZE, fw->init.textsz);
6549198429Srpaulo	iwn_nic_unlock(sc);
6550198429Srpaulo
6551198429Srpaulo	/* Load firmware boot code. */
6552198429Srpaulo	error = iwn4965_load_bootcode(sc, fw->boot.text, fw->boot.textsz);
6553178676Ssam	if (error != 0) {
6554198429Srpaulo		device_printf(sc->sc_dev, "%s: could not load boot firmware\n",
6555198429Srpaulo		    __func__);
6556178676Ssam		return error;
6557178676Ssam	}
6558198429Srpaulo	/* Now press "execute". */
6559198429Srpaulo	IWN_WRITE(sc, IWN_RESET, 0);
6560178676Ssam
6561198429Srpaulo	/* Wait at most one second for first alive notification. */
6562220726Sbschmidt	if ((error = msleep(sc, &sc->sc_mtx, PCATCH, "iwninit", hz)) != 0) {
6563178676Ssam		device_printf(sc->sc_dev,
6564198429Srpaulo		    "%s: timeout waiting for adapter to initialize, error %d\n",
6565178676Ssam		    __func__, error);
6566178676Ssam		return error;
6567178676Ssam	}
6568178676Ssam
6569198429Srpaulo	/* Retrieve current temperature for initial TX power calibration. */
6570198429Srpaulo	sc->rawtemp = sc->ucode_info.temp[3].chan20MHz;
6571198429Srpaulo	sc->temp = iwn4965_get_temperature(sc);
6572178676Ssam
6573198429Srpaulo	/* Copy runtime sections into pre-allocated DMA-safe memory. */
6574198429Srpaulo	memcpy(dma->vaddr, fw->main.data, fw->main.datasz);
6575220661Sbschmidt	bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
6576198429Srpaulo	memcpy(dma->vaddr + IWN4965_FW_DATA_MAXSZ,
6577198429Srpaulo	    fw->main.text, fw->main.textsz);
6578220661Sbschmidt	bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
6579198429Srpaulo
6580198429Srpaulo	/* Tell adapter where to find runtime sections. */
6581220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
6582198429Srpaulo		return error;
6583198429Srpaulo	iwn_prph_write(sc, IWN_BSM_DRAM_DATA_ADDR, dma->paddr >> 4);
6584198429Srpaulo	iwn_prph_write(sc, IWN_BSM_DRAM_DATA_SIZE, fw->main.datasz);
6585198429Srpaulo	iwn_prph_write(sc, IWN_BSM_DRAM_TEXT_ADDR,
6586198429Srpaulo	    (dma->paddr + IWN4965_FW_DATA_MAXSZ) >> 4);
6587198429Srpaulo	iwn_prph_write(sc, IWN_BSM_DRAM_TEXT_SIZE,
6588198429Srpaulo	    IWN_FW_UPDATED | fw->main.textsz);
6589198429Srpaulo	iwn_nic_unlock(sc);
6590198429Srpaulo
6591198429Srpaulo	return 0;
6592198429Srpaulo}
6593198429Srpaulo
6594206477Sbschmidtstatic int
6595198429Srpauloiwn5000_load_firmware_section(struct iwn_softc *sc, uint32_t dst,
6596198429Srpaulo    const uint8_t *section, int size)
6597198429Srpaulo{
6598198429Srpaulo	struct iwn_dma_info *dma = &sc->fw_dma;
6599198429Srpaulo	int error;
6600198429Srpaulo
6601253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
6602253705Sadrian
6603198429Srpaulo	/* Copy firmware section into pre-allocated DMA-safe memory. */
6604198429Srpaulo	memcpy(dma->vaddr, section, size);
6605220661Sbschmidt	bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
6606198429Srpaulo
6607220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
6608198429Srpaulo		return error;
6609198429Srpaulo
6610198429Srpaulo	IWN_WRITE(sc, IWN_FH_TX_CONFIG(IWN_SRVC_DMACHNL),
6611198429Srpaulo	    IWN_FH_TX_CONFIG_DMA_PAUSE);
6612198429Srpaulo
6613198429Srpaulo	IWN_WRITE(sc, IWN_FH_SRAM_ADDR(IWN_SRVC_DMACHNL), dst);
6614198429Srpaulo	IWN_WRITE(sc, IWN_FH_TFBD_CTRL0(IWN_SRVC_DMACHNL),
6615198429Srpaulo	    IWN_LOADDR(dma->paddr));
6616198429Srpaulo	IWN_WRITE(sc, IWN_FH_TFBD_CTRL1(IWN_SRVC_DMACHNL),
6617198429Srpaulo	    IWN_HIADDR(dma->paddr) << 28 | size);
6618198429Srpaulo	IWN_WRITE(sc, IWN_FH_TXBUF_STATUS(IWN_SRVC_DMACHNL),
6619198429Srpaulo	    IWN_FH_TXBUF_STATUS_TBNUM(1) |
6620198429Srpaulo	    IWN_FH_TXBUF_STATUS_TBIDX(1) |
6621198429Srpaulo	    IWN_FH_TXBUF_STATUS_TFBD_VALID);
6622198429Srpaulo
6623198429Srpaulo	/* Kick Flow Handler to start DMA transfer. */
6624198429Srpaulo	IWN_WRITE(sc, IWN_FH_TX_CONFIG(IWN_SRVC_DMACHNL),
6625198429Srpaulo	    IWN_FH_TX_CONFIG_DMA_ENA | IWN_FH_TX_CONFIG_CIRQ_HOST_ENDTFD);
6626198429Srpaulo
6627198429Srpaulo	iwn_nic_unlock(sc);
6628198429Srpaulo
6629198429Srpaulo	/* Wait at most five seconds for FH DMA transfer to complete. */
6630220661Sbschmidt	return msleep(sc, &sc->sc_mtx, PCATCH, "iwninit", 5 * hz);
6631198429Srpaulo}
6632198429Srpaulo
6633206477Sbschmidtstatic int
6634198429Srpauloiwn5000_load_firmware(struct iwn_softc *sc)
6635198429Srpaulo{
6636198429Srpaulo	struct iwn_fw_part *fw;
6637198429Srpaulo	int error;
6638198429Srpaulo
6639253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
6640253705Sadrian
6641198429Srpaulo	/* Load the initialization firmware on first boot only. */
6642201209Srpaulo	fw = (sc->sc_flags & IWN_FLAG_CALIB_DONE) ?
6643201209Srpaulo	    &sc->fw.main : &sc->fw.init;
6644198429Srpaulo
6645198429Srpaulo	error = iwn5000_load_firmware_section(sc, IWN_FW_TEXT_BASE,
6646198429Srpaulo	    fw->text, fw->textsz);
6647178676Ssam	if (error != 0) {
6648178676Ssam		device_printf(sc->sc_dev,
6649198429Srpaulo		    "%s: could not load firmware %s section, error %d\n",
6650198429Srpaulo		    __func__, ".text", error);
6651178676Ssam		return error;
6652178676Ssam	}
6653198429Srpaulo	error = iwn5000_load_firmware_section(sc, IWN_FW_DATA_BASE,
6654198429Srpaulo	    fw->data, fw->datasz);
6655178676Ssam	if (error != 0) {
6656178676Ssam		device_printf(sc->sc_dev,
6657198429Srpaulo		    "%s: could not load firmware %s section, error %d\n",
6658198429Srpaulo		    __func__, ".data", error);
6659178676Ssam		return error;
6660178676Ssam	}
6661178676Ssam
6662198429Srpaulo	/* Now press "execute". */
6663198429Srpaulo	IWN_WRITE(sc, IWN_RESET, 0);
6664198429Srpaulo	return 0;
6665198429Srpaulo}
6666198429Srpaulo
6667210111Sbschmidt/*
6668210111Sbschmidt * Extract text and data sections from a legacy firmware image.
6669210111Sbschmidt */
6670206477Sbschmidtstatic int
6671210111Sbschmidtiwn_read_firmware_leg(struct iwn_softc *sc, struct iwn_fw_info *fw)
6672198429Srpaulo{
6673201209Srpaulo	const uint32_t *ptr;
6674210111Sbschmidt	size_t hdrlen = 24;
6675201209Srpaulo	uint32_t rev;
6676198429Srpaulo
6677220661Sbschmidt	ptr = (const uint32_t *)fw->data;
6678201209Srpaulo	rev = le32toh(*ptr++);
6679210111Sbschmidt
6680201209Srpaulo	/* Check firmware API version. */
6681201209Srpaulo	if (IWN_FW_API(rev) <= 1) {
6682201209Srpaulo		device_printf(sc->sc_dev,
6683201209Srpaulo		    "%s: bad firmware, need API version >=2\n", __func__);
6684201209Srpaulo		return EINVAL;
6685201209Srpaulo	}
6686201209Srpaulo	if (IWN_FW_API(rev) >= 3) {
6687201209Srpaulo		/* Skip build number (version 2 header). */
6688210111Sbschmidt		hdrlen += 4;
6689201209Srpaulo		ptr++;
6690201209Srpaulo	}
6691210111Sbschmidt	if (fw->size < hdrlen) {
6692220724Sbschmidt		device_printf(sc->sc_dev, "%s: firmware too short: %zu bytes\n",
6693210111Sbschmidt		    __func__, fw->size);
6694210111Sbschmidt		return EINVAL;
6695210111Sbschmidt	}
6696201209Srpaulo	fw->main.textsz = le32toh(*ptr++);
6697201209Srpaulo	fw->main.datasz = le32toh(*ptr++);
6698201209Srpaulo	fw->init.textsz = le32toh(*ptr++);
6699201209Srpaulo	fw->init.datasz = le32toh(*ptr++);
6700201209Srpaulo	fw->boot.textsz = le32toh(*ptr++);
6701198429Srpaulo
6702198429Srpaulo	/* Check that all firmware sections fit. */
6703210111Sbschmidt	if (fw->size < hdrlen + fw->main.textsz + fw->main.datasz +
6704210111Sbschmidt	    fw->init.textsz + fw->init.datasz + fw->boot.textsz) {
6705220724Sbschmidt		device_printf(sc->sc_dev, "%s: firmware too short: %zu bytes\n",
6706210111Sbschmidt		    __func__, fw->size);
6707198429Srpaulo		return EINVAL;
6708178676Ssam	}
6709198429Srpaulo
6710198429Srpaulo	/* Get pointers to firmware sections. */
6711201209Srpaulo	fw->main.text = (const uint8_t *)ptr;
6712198429Srpaulo	fw->main.data = fw->main.text + fw->main.textsz;
6713198429Srpaulo	fw->init.text = fw->main.data + fw->main.datasz;
6714198429Srpaulo	fw->init.data = fw->init.text + fw->init.textsz;
6715198429Srpaulo	fw->boot.text = fw->init.data + fw->init.datasz;
6716178676Ssam	return 0;
6717178676Ssam}
6718178676Ssam
6719210111Sbschmidt/*
6720210111Sbschmidt * Extract text and data sections from a TLV firmware image.
6721210111Sbschmidt */
6722220661Sbschmidtstatic int
6723210111Sbschmidtiwn_read_firmware_tlv(struct iwn_softc *sc, struct iwn_fw_info *fw,
6724210111Sbschmidt    uint16_t alt)
6725210111Sbschmidt{
6726210111Sbschmidt	const struct iwn_fw_tlv_hdr *hdr;
6727210111Sbschmidt	const struct iwn_fw_tlv *tlv;
6728210111Sbschmidt	const uint8_t *ptr, *end;
6729210111Sbschmidt	uint64_t altmask;
6730220866Sbschmidt	uint32_t len, tmp;
6731210111Sbschmidt
6732210111Sbschmidt	if (fw->size < sizeof (*hdr)) {
6733220724Sbschmidt		device_printf(sc->sc_dev, "%s: firmware too short: %zu bytes\n",
6734210111Sbschmidt		    __func__, fw->size);
6735210111Sbschmidt		return EINVAL;
6736210111Sbschmidt	}
6737210111Sbschmidt	hdr = (const struct iwn_fw_tlv_hdr *)fw->data;
6738210111Sbschmidt	if (hdr->signature != htole32(IWN_FW_SIGNATURE)) {
6739220724Sbschmidt		device_printf(sc->sc_dev, "%s: bad firmware signature 0x%08x\n",
6740210111Sbschmidt		    __func__, le32toh(hdr->signature));
6741210111Sbschmidt		return EINVAL;
6742210111Sbschmidt	}
6743220724Sbschmidt	DPRINTF(sc, IWN_DEBUG_RESET, "FW: \"%.64s\", build 0x%x\n", hdr->descr,
6744220724Sbschmidt	    le32toh(hdr->build));
6745210111Sbschmidt
6746210111Sbschmidt	/*
6747210111Sbschmidt	 * Select the closest supported alternative that is less than
6748210111Sbschmidt	 * or equal to the specified one.
6749210111Sbschmidt	 */
6750210111Sbschmidt	altmask = le64toh(hdr->altmask);
6751210111Sbschmidt	while (alt > 0 && !(altmask & (1ULL << alt)))
6752210111Sbschmidt		alt--;	/* Downgrade. */
6753220724Sbschmidt	DPRINTF(sc, IWN_DEBUG_RESET, "using alternative %d\n", alt);
6754210111Sbschmidt
6755210111Sbschmidt	ptr = (const uint8_t *)(hdr + 1);
6756210111Sbschmidt	end = (const uint8_t *)(fw->data + fw->size);
6757210111Sbschmidt
6758210111Sbschmidt	/* Parse type-length-value fields. */
6759210111Sbschmidt	while (ptr + sizeof (*tlv) <= end) {
6760210111Sbschmidt		tlv = (const struct iwn_fw_tlv *)ptr;
6761210111Sbschmidt		len = le32toh(tlv->len);
6762210111Sbschmidt
6763210111Sbschmidt		ptr += sizeof (*tlv);
6764210111Sbschmidt		if (ptr + len > end) {
6765210111Sbschmidt			device_printf(sc->sc_dev,
6766220724Sbschmidt			    "%s: firmware too short: %zu bytes\n", __func__,
6767220724Sbschmidt			    fw->size);
6768210111Sbschmidt			return EINVAL;
6769210111Sbschmidt		}
6770210111Sbschmidt		/* Skip other alternatives. */
6771210111Sbschmidt		if (tlv->alt != 0 && tlv->alt != htole16(alt))
6772210111Sbschmidt			goto next;
6773210111Sbschmidt
6774210111Sbschmidt		switch (le16toh(tlv->type)) {
6775210111Sbschmidt		case IWN_FW_TLV_MAIN_TEXT:
6776210111Sbschmidt			fw->main.text = ptr;
6777210111Sbschmidt			fw->main.textsz = len;
6778210111Sbschmidt			break;
6779210111Sbschmidt		case IWN_FW_TLV_MAIN_DATA:
6780210111Sbschmidt			fw->main.data = ptr;
6781210111Sbschmidt			fw->main.datasz = len;
6782210111Sbschmidt			break;
6783210111Sbschmidt		case IWN_FW_TLV_INIT_TEXT:
6784210111Sbschmidt			fw->init.text = ptr;
6785210111Sbschmidt			fw->init.textsz = len;
6786210111Sbschmidt			break;
6787210111Sbschmidt		case IWN_FW_TLV_INIT_DATA:
6788210111Sbschmidt			fw->init.data = ptr;
6789210111Sbschmidt			fw->init.datasz = len;
6790210111Sbschmidt			break;
6791210111Sbschmidt		case IWN_FW_TLV_BOOT_TEXT:
6792210111Sbschmidt			fw->boot.text = ptr;
6793210111Sbschmidt			fw->boot.textsz = len;
6794210111Sbschmidt			break;
6795220866Sbschmidt		case IWN_FW_TLV_ENH_SENS:
6796220866Sbschmidt			if (!len)
6797220866Sbschmidt				sc->sc_flags |= IWN_FLAG_ENH_SENS;
6798220866Sbschmidt			break;
6799220866Sbschmidt		case IWN_FW_TLV_PHY_CALIB:
6800220866Sbschmidt			tmp = htole32(*ptr);
6801220866Sbschmidt			if (tmp < 253) {
6802220866Sbschmidt				sc->reset_noise_gain = tmp;
6803220866Sbschmidt				sc->noise_gain = tmp + 1;
6804220866Sbschmidt			}
6805220866Sbschmidt			break;
6806254204Sadrian		case IWN_FW_TLV_PAN:
6807254204Sadrian			sc->sc_flags |= IWN_FLAG_PAN_SUPPORT;
6808254204Sadrian			DPRINTF(sc, IWN_DEBUG_RESET,
6809254204Sadrian			    "PAN Support found: %d\n", 1);
6810254204Sadrian			break;
6811254204Sadrian		case IWN_FW_TLV_FLAGS :
6812254204Sadrian			sc->tlv_feature_flags = htole32(*ptr);
6813254204Sadrian			break;
6814254204Sadrian		case IWN_FW_TLV_PBREQ_MAXLEN:
6815254204Sadrian		case IWN_FW_TLV_RUNT_EVTLOG_PTR:
6816254204Sadrian		case IWN_FW_TLV_RUNT_EVTLOG_SIZE:
6817254204Sadrian		case IWN_FW_TLV_RUNT_ERRLOG_PTR:
6818254204Sadrian		case IWN_FW_TLV_INIT_EVTLOG_PTR:
6819254204Sadrian		case IWN_FW_TLV_INIT_EVTLOG_SIZE:
6820254204Sadrian		case IWN_FW_TLV_INIT_ERRLOG_PTR:
6821254204Sadrian		case IWN_FW_TLV_WOWLAN_INST:
6822254204Sadrian		case IWN_FW_TLV_WOWLAN_DATA:
6823254204Sadrian			DPRINTF(sc, IWN_DEBUG_RESET,
6824254204Sadrian			    "TLV type %d reconized but not handled\n",
6825254204Sadrian			    le16toh(tlv->type));
6826254204Sadrian			break;
6827210111Sbschmidt		default:
6828210111Sbschmidt			DPRINTF(sc, IWN_DEBUG_RESET,
6829220724Sbschmidt			    "TLV type %d not handled\n", le16toh(tlv->type));
6830210111Sbschmidt			break;
6831210111Sbschmidt		}
6832220726Sbschmidt next:		/* TLV fields are 32-bit aligned. */
6833210111Sbschmidt		ptr += (len + 3) & ~3;
6834210111Sbschmidt	}
6835210111Sbschmidt	return 0;
6836210111Sbschmidt}
6837210111Sbschmidt
6838206477Sbschmidtstatic int
6839210111Sbschmidtiwn_read_firmware(struct iwn_softc *sc)
6840210111Sbschmidt{
6841210111Sbschmidt	struct iwn_fw_info *fw = &sc->fw;
6842210111Sbschmidt	int error;
6843210111Sbschmidt
6844253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
6845253705Sadrian
6846210111Sbschmidt	IWN_UNLOCK(sc);
6847210111Sbschmidt
6848210111Sbschmidt	memset(fw, 0, sizeof (*fw));
6849210111Sbschmidt
6850210111Sbschmidt	/* Read firmware image from filesystem. */
6851210111Sbschmidt	sc->fw_fp = firmware_get(sc->fwname);
6852210111Sbschmidt	if (sc->fw_fp == NULL) {
6853220724Sbschmidt		device_printf(sc->sc_dev, "%s: could not read firmware %s\n",
6854220724Sbschmidt		    __func__, sc->fwname);
6855210111Sbschmidt		IWN_LOCK(sc);
6856210111Sbschmidt		return EINVAL;
6857210111Sbschmidt	}
6858210111Sbschmidt	IWN_LOCK(sc);
6859210111Sbschmidt
6860210111Sbschmidt	fw->size = sc->fw_fp->datasize;
6861210111Sbschmidt	fw->data = (const uint8_t *)sc->fw_fp->data;
6862210111Sbschmidt	if (fw->size < sizeof (uint32_t)) {
6863220724Sbschmidt		device_printf(sc->sc_dev, "%s: firmware too short: %zu bytes\n",
6864210111Sbschmidt		    __func__, fw->size);
6865220661Sbschmidt		firmware_put(sc->fw_fp, FIRMWARE_UNLOAD);
6866220661Sbschmidt		sc->fw_fp = NULL;
6867210111Sbschmidt		return EINVAL;
6868210111Sbschmidt	}
6869210111Sbschmidt
6870210111Sbschmidt	/* Retrieve text and data sections. */
6871210111Sbschmidt	if (*(const uint32_t *)fw->data != 0)	/* Legacy image. */
6872210111Sbschmidt		error = iwn_read_firmware_leg(sc, fw);
6873210111Sbschmidt	else
6874210111Sbschmidt		error = iwn_read_firmware_tlv(sc, fw, 1);
6875210111Sbschmidt	if (error != 0) {
6876210111Sbschmidt		device_printf(sc->sc_dev,
6877220724Sbschmidt		    "%s: could not read firmware sections, error %d\n",
6878220724Sbschmidt		    __func__, error);
6879220661Sbschmidt		firmware_put(sc->fw_fp, FIRMWARE_UNLOAD);
6880220661Sbschmidt		sc->fw_fp = NULL;
6881210111Sbschmidt		return error;
6882210111Sbschmidt	}
6883210111Sbschmidt
6884210111Sbschmidt	/* Make sure text and data sections fit in hardware memory. */
6885220728Sbschmidt	if (fw->main.textsz > sc->fw_text_maxsz ||
6886220728Sbschmidt	    fw->main.datasz > sc->fw_data_maxsz ||
6887220728Sbschmidt	    fw->init.textsz > sc->fw_text_maxsz ||
6888220728Sbschmidt	    fw->init.datasz > sc->fw_data_maxsz ||
6889210111Sbschmidt	    fw->boot.textsz > IWN_FW_BOOT_TEXT_MAXSZ ||
6890210111Sbschmidt	    (fw->boot.textsz & 3) != 0) {
6891220724Sbschmidt		device_printf(sc->sc_dev, "%s: firmware sections too large\n",
6892220724Sbschmidt		    __func__);
6893220661Sbschmidt		firmware_put(sc->fw_fp, FIRMWARE_UNLOAD);
6894220661Sbschmidt		sc->fw_fp = NULL;
6895210111Sbschmidt		return EINVAL;
6896210111Sbschmidt	}
6897210111Sbschmidt
6898210111Sbschmidt	/* We can proceed with loading the firmware. */
6899210111Sbschmidt	return 0;
6900210111Sbschmidt}
6901210111Sbschmidt
6902210111Sbschmidtstatic int
6903198429Srpauloiwn_clock_wait(struct iwn_softc *sc)
6904198429Srpaulo{
6905198429Srpaulo	int ntries;
6906178676Ssam
6907198429Srpaulo	/* Set "initialization complete" bit. */
6908198429Srpaulo	IWN_SETBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_INIT_DONE);
6909198429Srpaulo
6910198429Srpaulo	/* Wait for clock stabilization. */
6911201209Srpaulo	for (ntries = 0; ntries < 2500; ntries++) {
6912198429Srpaulo		if (IWN_READ(sc, IWN_GP_CNTRL) & IWN_GP_CNTRL_MAC_CLOCK_READY)
6913198429Srpaulo			return 0;
6914201209Srpaulo		DELAY(10);
6915178676Ssam	}
6916198429Srpaulo	device_printf(sc->sc_dev,
6917198429Srpaulo	    "%s: timeout waiting for clock stabilization\n", __func__);
6918198429Srpaulo	return ETIMEDOUT;
6919198429Srpaulo}
6920178676Ssam
6921206477Sbschmidtstatic int
6922201209Srpauloiwn_apm_init(struct iwn_softc *sc)
6923198429Srpaulo{
6924220721Sbschmidt	uint32_t reg;
6925198429Srpaulo	int error;
6926178676Ssam
6927253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
6928253705Sadrian
6929220725Sbschmidt	/* Disable L0s exit timer (NMI bug workaround). */
6930198429Srpaulo	IWN_SETBITS(sc, IWN_GIO_CHICKEN, IWN_GIO_CHICKEN_DIS_L0S_TIMER);
6931220725Sbschmidt	/* Don't wait for ICH L0s (ICH bug workaround). */
6932198429Srpaulo	IWN_SETBITS(sc, IWN_GIO_CHICKEN, IWN_GIO_CHICKEN_L1A_NO_L0S_RX);
6933178676Ssam
6934220725Sbschmidt	/* Set FH wait threshold to max (HW bug under stress workaround). */
6935198429Srpaulo	IWN_SETBITS(sc, IWN_DBG_HPET_MEM, 0xffff0000);
6936198429Srpaulo
6937201209Srpaulo	/* Enable HAP INTA to move adapter from L1a to L0s. */
6938198429Srpaulo	IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_HAP_WAKE_L1A);
6939198429Srpaulo
6940201209Srpaulo	/* Retrieve PCIe Active State Power Management (ASPM). */
6941220721Sbschmidt	reg = pci_read_config(sc->sc_dev, sc->sc_cap_off + 0x10, 1);
6942201209Srpaulo	/* Workaround for HW instability in PCIe L0->L0s->L1 transition. */
6943220721Sbschmidt	if (reg & 0x02)	/* L1 Entry enabled. */
6944201209Srpaulo		IWN_SETBITS(sc, IWN_GIO, IWN_GIO_L0S_ENA);
6945201209Srpaulo	else
6946201209Srpaulo		IWN_CLRBITS(sc, IWN_GIO, IWN_GIO_L0S_ENA);
6947201209Srpaulo
6948201209Srpaulo	if (sc->hw_type != IWN_HW_REV_TYPE_4965 &&
6949210109Sbschmidt	    sc->hw_type <= IWN_HW_REV_TYPE_1000)
6950198429Srpaulo		IWN_SETBITS(sc, IWN_ANA_PLL, IWN_ANA_PLL_INIT);
6951198429Srpaulo
6952201209Srpaulo	/* Wait for clock stabilization before accessing prph. */
6953220726Sbschmidt	if ((error = iwn_clock_wait(sc)) != 0)
6954198429Srpaulo		return error;
6955198429Srpaulo
6956220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
6957198429Srpaulo		return error;
6958201209Srpaulo	if (sc->hw_type == IWN_HW_REV_TYPE_4965) {
6959220725Sbschmidt		/* Enable DMA and BSM (Bootstrap State Machine). */
6960201209Srpaulo		iwn_prph_write(sc, IWN_APMG_CLK_EN,
6961201209Srpaulo		    IWN_APMG_CLK_CTRL_DMA_CLK_RQT |
6962201209Srpaulo		    IWN_APMG_CLK_CTRL_BSM_CLK_RQT);
6963201209Srpaulo	} else {
6964201209Srpaulo		/* Enable DMA. */
6965201209Srpaulo		iwn_prph_write(sc, IWN_APMG_CLK_EN,
6966201209Srpaulo		    IWN_APMG_CLK_CTRL_DMA_CLK_RQT);
6967201209Srpaulo	}
6968198429Srpaulo	DELAY(20);
6969201209Srpaulo	/* Disable L1-Active. */
6970198429Srpaulo	iwn_prph_setbits(sc, IWN_APMG_PCI_STT, IWN_APMG_PCI_STT_L1A_DIS);
6971198429Srpaulo	iwn_nic_unlock(sc);
6972198429Srpaulo
6973198429Srpaulo	return 0;
6974198429Srpaulo}
6975198429Srpaulo
6976206477Sbschmidtstatic void
6977198429Srpauloiwn_apm_stop_master(struct iwn_softc *sc)
6978178676Ssam{
6979178676Ssam	int ntries;
6980178676Ssam
6981201209Srpaulo	/* Stop busmaster DMA activity. */
6982198429Srpaulo	IWN_SETBITS(sc, IWN_RESET, IWN_RESET_STOP_MASTER);
6983178676Ssam	for (ntries = 0; ntries < 100; ntries++) {
6984198429Srpaulo		if (IWN_READ(sc, IWN_RESET) & IWN_RESET_MASTER_DISABLED)
6985198429Srpaulo			return;
6986178676Ssam		DELAY(10);
6987178676Ssam	}
6988220726Sbschmidt	device_printf(sc->sc_dev, "%s: timeout waiting for master\n", __func__);
6989178676Ssam}
6990178676Ssam
6991206477Sbschmidtstatic void
6992198429Srpauloiwn_apm_stop(struct iwn_softc *sc)
6993198429Srpaulo{
6994198429Srpaulo	iwn_apm_stop_master(sc);
6995198429Srpaulo
6996201209Srpaulo	/* Reset the entire device. */
6997198429Srpaulo	IWN_SETBITS(sc, IWN_RESET, IWN_RESET_SW);
6998198429Srpaulo	DELAY(10);
6999198429Srpaulo	/* Clear "initialization complete" bit. */
7000198429Srpaulo	IWN_CLRBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_INIT_DONE);
7001198429Srpaulo}
7002198429Srpaulo
7003206477Sbschmidtstatic int
7004198429Srpauloiwn4965_nic_config(struct iwn_softc *sc)
7005178676Ssam{
7006253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
7007253705Sadrian
7008198429Srpaulo	if (IWN_RFCFG_TYPE(sc->rfcfg) == 1) {
7009198429Srpaulo		/*
7010198429Srpaulo		 * I don't believe this to be correct but this is what the
7011198429Srpaulo		 * vendor driver is doing. Probably the bits should not be
7012198429Srpaulo		 * shifted in IWN_RFCFG_*.
7013198429Srpaulo		 */
7014198429Srpaulo		IWN_SETBITS(sc, IWN_HW_IF_CONFIG,
7015198429Srpaulo		    IWN_RFCFG_TYPE(sc->rfcfg) |
7016198429Srpaulo		    IWN_RFCFG_STEP(sc->rfcfg) |
7017198429Srpaulo		    IWN_RFCFG_DASH(sc->rfcfg));
7018198429Srpaulo	}
7019198429Srpaulo	IWN_SETBITS(sc, IWN_HW_IF_CONFIG,
7020198429Srpaulo	    IWN_HW_IF_CONFIG_RADIO_SI | IWN_HW_IF_CONFIG_MAC_SI);
7021198429Srpaulo	return 0;
7022198429Srpaulo}
7023178676Ssam
7024206477Sbschmidtstatic int
7025198429Srpauloiwn5000_nic_config(struct iwn_softc *sc)
7026198429Srpaulo{
7027198429Srpaulo	uint32_t tmp;
7028198429Srpaulo	int error;
7029178676Ssam
7030253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
7031253705Sadrian
7032198429Srpaulo	if (IWN_RFCFG_TYPE(sc->rfcfg) < 3) {
7033198429Srpaulo		IWN_SETBITS(sc, IWN_HW_IF_CONFIG,
7034198429Srpaulo		    IWN_RFCFG_TYPE(sc->rfcfg) |
7035198429Srpaulo		    IWN_RFCFG_STEP(sc->rfcfg) |
7036198429Srpaulo		    IWN_RFCFG_DASH(sc->rfcfg));
7037198429Srpaulo	}
7038198429Srpaulo	IWN_SETBITS(sc, IWN_HW_IF_CONFIG,
7039198429Srpaulo	    IWN_HW_IF_CONFIG_RADIO_SI | IWN_HW_IF_CONFIG_MAC_SI);
7040198429Srpaulo
7041220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
7042198429Srpaulo		return error;
7043198429Srpaulo	iwn_prph_setbits(sc, IWN_APMG_PS, IWN_APMG_PS_EARLY_PWROFF_DIS);
7044201209Srpaulo
7045201209Srpaulo	if (sc->hw_type == IWN_HW_REV_TYPE_1000) {
7046201209Srpaulo		/*
7047201209Srpaulo		 * Select first Switching Voltage Regulator (1.32V) to
7048201209Srpaulo		 * solve a stability issue related to noisy DC2DC line
7049201209Srpaulo		 * in the silicon of 1000 Series.
7050201209Srpaulo		 */
7051201209Srpaulo		tmp = iwn_prph_read(sc, IWN_APMG_DIGITAL_SVR);
7052201209Srpaulo		tmp &= ~IWN_APMG_DIGITAL_SVR_VOLTAGE_MASK;
7053201209Srpaulo		tmp |= IWN_APMG_DIGITAL_SVR_VOLTAGE_1_32;
7054201209Srpaulo		iwn_prph_write(sc, IWN_APMG_DIGITAL_SVR, tmp);
7055201209Srpaulo	}
7056198429Srpaulo	iwn_nic_unlock(sc);
7057201209Srpaulo
7058201209Srpaulo	if (sc->sc_flags & IWN_FLAG_INTERNAL_PA) {
7059201209Srpaulo		/* Use internal power amplifier only. */
7060201209Srpaulo		IWN_WRITE(sc, IWN_GP_DRIVER, IWN_GP_DRIVER_RADIO_2X2_IPA);
7061201209Srpaulo	}
7062220676Sbschmidt	if ((sc->hw_type == IWN_HW_REV_TYPE_6050 ||
7063220676Sbschmidt	     sc->hw_type == IWN_HW_REV_TYPE_6005) && sc->calib_ver >= 6) {
7064210108Sbschmidt		/* Indicate that ROM calibration version is >=6. */
7065210108Sbschmidt		IWN_SETBITS(sc, IWN_GP_DRIVER, IWN_GP_DRIVER_CALIB_VER6);
7066206444Sbschmidt	}
7067220729Sbschmidt	if (sc->hw_type == IWN_HW_REV_TYPE_6005)
7068220729Sbschmidt		IWN_SETBITS(sc, IWN_GP_DRIVER, IWN_GP_DRIVER_6050_1X2);
7069198429Srpaulo	return 0;
7070198429Srpaulo}
7071198429Srpaulo
7072198429Srpaulo/*
7073198429Srpaulo * Take NIC ownership over Intel Active Management Technology (AMT).
7074198429Srpaulo */
7075206477Sbschmidtstatic int
7076198429Srpauloiwn_hw_prepare(struct iwn_softc *sc)
7077198429Srpaulo{
7078198429Srpaulo	int ntries;
7079198429Srpaulo
7080253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
7081253705Sadrian
7082201209Srpaulo	/* Check if hardware is ready. */
7083201209Srpaulo	IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_NIC_READY);
7084201209Srpaulo	for (ntries = 0; ntries < 5; ntries++) {
7085201209Srpaulo		if (IWN_READ(sc, IWN_HW_IF_CONFIG) &
7086201209Srpaulo		    IWN_HW_IF_CONFIG_NIC_READY)
7087201209Srpaulo			return 0;
7088201209Srpaulo		DELAY(10);
7089201209Srpaulo	}
7090201209Srpaulo
7091201209Srpaulo	/* Hardware not ready, force into ready state. */
7092198429Srpaulo	IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_PREPARE);
7093198429Srpaulo	for (ntries = 0; ntries < 15000; ntries++) {
7094198429Srpaulo		if (!(IWN_READ(sc, IWN_HW_IF_CONFIG) &
7095198429Srpaulo		    IWN_HW_IF_CONFIG_PREPARE_DONE))
7096178676Ssam			break;
7097178676Ssam		DELAY(10);
7098178676Ssam	}
7099198429Srpaulo	if (ntries == 15000)
7100178676Ssam		return ETIMEDOUT;
7101198429Srpaulo
7102201209Srpaulo	/* Hardware should be ready now. */
7103198429Srpaulo	IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_NIC_READY);
7104198429Srpaulo	for (ntries = 0; ntries < 5; ntries++) {
7105198429Srpaulo		if (IWN_READ(sc, IWN_HW_IF_CONFIG) &
7106198429Srpaulo		    IWN_HW_IF_CONFIG_NIC_READY)
7107198429Srpaulo			return 0;
7108198429Srpaulo		DELAY(10);
7109178676Ssam	}
7110198429Srpaulo	return ETIMEDOUT;
7111178676Ssam}
7112178676Ssam
7113206477Sbschmidtstatic int
7114198429Srpauloiwn_hw_init(struct iwn_softc *sc)
7115178676Ssam{
7116220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
7117198429Srpaulo	int error, chnl, qid;
7118178676Ssam
7119253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
7120253705Sadrian
7121198429Srpaulo	/* Clear pending interrupts. */
7122198429Srpaulo	IWN_WRITE(sc, IWN_INT, 0xffffffff);
7123178676Ssam
7124220726Sbschmidt	if ((error = iwn_apm_init(sc)) != 0) {
7125198429Srpaulo		device_printf(sc->sc_dev,
7126220726Sbschmidt		    "%s: could not power ON adapter, error %d\n", __func__,
7127220726Sbschmidt		    error);
7128198429Srpaulo		return error;
7129178676Ssam	}
7130178676Ssam
7131198429Srpaulo	/* Select VMAIN power source. */
7132220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
7133198429Srpaulo		return error;
7134198429Srpaulo	iwn_prph_clrbits(sc, IWN_APMG_PS, IWN_APMG_PS_PWR_SRC_MASK);
7135198429Srpaulo	iwn_nic_unlock(sc);
7136178676Ssam
7137198429Srpaulo	/* Perform adapter-specific initialization. */
7138220728Sbschmidt	if ((error = ops->nic_config(sc)) != 0)
7139198429Srpaulo		return error;
7140178676Ssam
7141198429Srpaulo	/* Initialize RX ring. */
7142220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
7143198429Srpaulo		return error;
7144198429Srpaulo	IWN_WRITE(sc, IWN_FH_RX_CONFIG, 0);
7145198429Srpaulo	IWN_WRITE(sc, IWN_FH_RX_WPTR, 0);
7146220725Sbschmidt	/* Set physical address of RX ring (256-byte aligned). */
7147198429Srpaulo	IWN_WRITE(sc, IWN_FH_RX_BASE, sc->rxq.desc_dma.paddr >> 8);
7148220725Sbschmidt	/* Set physical address of RX status (16-byte aligned). */
7149198429Srpaulo	IWN_WRITE(sc, IWN_FH_STATUS_WPTR, sc->rxq.stat_dma.paddr >> 4);
7150198429Srpaulo	/* Enable RX. */
7151198429Srpaulo	IWN_WRITE(sc, IWN_FH_RX_CONFIG,
7152198429Srpaulo	    IWN_FH_RX_CONFIG_ENA           |
7153198429Srpaulo	    IWN_FH_RX_CONFIG_IGN_RXF_EMPTY |	/* HW bug workaround */
7154198429Srpaulo	    IWN_FH_RX_CONFIG_IRQ_DST_HOST  |
7155198429Srpaulo	    IWN_FH_RX_CONFIG_SINGLE_FRAME  |
7156198429Srpaulo	    IWN_FH_RX_CONFIG_RB_TIMEOUT(0) |
7157198429Srpaulo	    IWN_FH_RX_CONFIG_NRBD(IWN_RX_RING_COUNT_LOG));
7158198429Srpaulo	iwn_nic_unlock(sc);
7159198429Srpaulo	IWN_WRITE(sc, IWN_FH_RX_WPTR, (IWN_RX_RING_COUNT - 1) & ~7);
7160178676Ssam
7161220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
7162198429Srpaulo		return error;
7163178676Ssam
7164198429Srpaulo	/* Initialize TX scheduler. */
7165220728Sbschmidt	iwn_prph_write(sc, sc->sched_txfact_addr, 0);
7166178676Ssam
7167220725Sbschmidt	/* Set physical address of "keep warm" page (16-byte aligned). */
7168198429Srpaulo	IWN_WRITE(sc, IWN_FH_KW_ADDR, sc->kw_dma.paddr >> 4);
7169198429Srpaulo
7170198429Srpaulo	/* Initialize TX rings. */
7171220728Sbschmidt	for (qid = 0; qid < sc->ntxqs; qid++) {
7172198429Srpaulo		struct iwn_tx_ring *txq = &sc->txq[qid];
7173198429Srpaulo
7174220725Sbschmidt		/* Set physical address of TX ring (256-byte aligned). */
7175198429Srpaulo		IWN_WRITE(sc, IWN_FH_CBBC_QUEUE(qid),
7176198429Srpaulo		    txq->desc_dma.paddr >> 8);
7177178676Ssam	}
7178198429Srpaulo	iwn_nic_unlock(sc);
7179178676Ssam
7180198429Srpaulo	/* Enable DMA channels. */
7181220728Sbschmidt	for (chnl = 0; chnl < sc->ndmachnls; chnl++) {
7182198429Srpaulo		IWN_WRITE(sc, IWN_FH_TX_CONFIG(chnl),
7183198429Srpaulo		    IWN_FH_TX_CONFIG_DMA_ENA |
7184198429Srpaulo		    IWN_FH_TX_CONFIG_DMA_CREDIT_ENA);
7185198429Srpaulo	}
7186198429Srpaulo
7187198429Srpaulo	/* Clear "radio off" and "commands blocked" bits. */
7188198429Srpaulo	IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_RFKILL);
7189198429Srpaulo	IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_CMD_BLOCKED);
7190198429Srpaulo
7191198429Srpaulo	/* Clear pending interrupts. */
7192198429Srpaulo	IWN_WRITE(sc, IWN_INT, 0xffffffff);
7193198429Srpaulo	/* Enable interrupt coalescing. */
7194198429Srpaulo	IWN_WRITE(sc, IWN_INT_COALESCING, 512 / 8);
7195198429Srpaulo	/* Enable interrupts. */
7196201209Srpaulo	IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask);
7197198429Srpaulo
7198198429Srpaulo	/* _Really_ make sure "radio off" bit is cleared! */
7199198429Srpaulo	IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_RFKILL);
7200198429Srpaulo	IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_RFKILL);
7201198429Srpaulo
7202220729Sbschmidt	/* Enable shadow registers. */
7203220729Sbschmidt	if (sc->hw_type >= IWN_HW_REV_TYPE_6000)
7204220729Sbschmidt		IWN_SETBITS(sc, IWN_SHADOW_REG_CTRL, 0x800fffff);
7205220729Sbschmidt
7206220728Sbschmidt	if ((error = ops->load_firmware(sc)) != 0) {
7207178676Ssam		device_printf(sc->sc_dev,
7208220726Sbschmidt		    "%s: could not load firmware, error %d\n", __func__,
7209220726Sbschmidt		    error);
7210198429Srpaulo		return error;
7211178676Ssam	}
7212198429Srpaulo	/* Wait at most one second for firmware alive notification. */
7213220726Sbschmidt	if ((error = msleep(sc, &sc->sc_mtx, PCATCH, "iwninit", hz)) != 0) {
7214198429Srpaulo		device_printf(sc->sc_dev,
7215198429Srpaulo		    "%s: timeout waiting for adapter to initialize, error %d\n",
7216198429Srpaulo		    __func__, error);
7217198429Srpaulo		return error;
7218198429Srpaulo	}
7219198429Srpaulo	/* Do post-firmware initialization. */
7220253705Sadrian
7221253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
7222253705Sadrian
7223220728Sbschmidt	return ops->post_alive(sc);
7224198429Srpaulo}
7225178676Ssam
7226206477Sbschmidtstatic void
7227198429Srpauloiwn_hw_stop(struct iwn_softc *sc)
7228198429Srpaulo{
7229198429Srpaulo	int chnl, qid, ntries;
7230178676Ssam
7231253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
7232253705Sadrian
7233198429Srpaulo	IWN_WRITE(sc, IWN_RESET, IWN_RESET_NEVO);
7234178676Ssam
7235198429Srpaulo	/* Disable interrupts. */
7236201209Srpaulo	IWN_WRITE(sc, IWN_INT_MASK, 0);
7237198429Srpaulo	IWN_WRITE(sc, IWN_INT, 0xffffffff);
7238198429Srpaulo	IWN_WRITE(sc, IWN_FH_INT, 0xffffffff);
7239201209Srpaulo	sc->sc_flags &= ~IWN_FLAG_USE_ICT;
7240178676Ssam
7241198429Srpaulo	/* Make sure we no longer hold the NIC lock. */
7242198429Srpaulo	iwn_nic_unlock(sc);
7243178676Ssam
7244198429Srpaulo	/* Stop TX scheduler. */
7245220728Sbschmidt	iwn_prph_write(sc, sc->sched_txfact_addr, 0);
7246178676Ssam
7247198429Srpaulo	/* Stop all DMA channels. */
7248198429Srpaulo	if (iwn_nic_lock(sc) == 0) {
7249220728Sbschmidt		for (chnl = 0; chnl < sc->ndmachnls; chnl++) {
7250198429Srpaulo			IWN_WRITE(sc, IWN_FH_TX_CONFIG(chnl), 0);
7251198429Srpaulo			for (ntries = 0; ntries < 200; ntries++) {
7252220659Sbschmidt				if (IWN_READ(sc, IWN_FH_TX_STATUS) &
7253198429Srpaulo				    IWN_FH_TX_STATUS_IDLE(chnl))
7254198429Srpaulo					break;
7255198429Srpaulo				DELAY(10);
7256198429Srpaulo			}
7257198429Srpaulo		}
7258198429Srpaulo		iwn_nic_unlock(sc);
7259198429Srpaulo	}
7260178676Ssam
7261198429Srpaulo	/* Stop RX ring. */
7262198429Srpaulo	iwn_reset_rx_ring(sc, &sc->rxq);
7263178676Ssam
7264198429Srpaulo	/* Reset all TX rings. */
7265220728Sbschmidt	for (qid = 0; qid < sc->ntxqs; qid++)
7266198429Srpaulo		iwn_reset_tx_ring(sc, &sc->txq[qid]);
7267178676Ssam
7268198429Srpaulo	if (iwn_nic_lock(sc) == 0) {
7269201209Srpaulo		iwn_prph_write(sc, IWN_APMG_CLK_DIS,
7270201209Srpaulo		    IWN_APMG_CLK_CTRL_DMA_CLK_RQT);
7271198429Srpaulo		iwn_nic_unlock(sc);
7272178676Ssam	}
7273198429Srpaulo	DELAY(5);
7274198429Srpaulo	/* Power OFF adapter. */
7275198429Srpaulo	iwn_apm_stop(sc);
7276198429Srpaulo}
7277178676Ssam
7278206477Sbschmidtstatic void
7279220723Sbschmidtiwn_radio_on(void *arg0, int pending)
7280220723Sbschmidt{
7281220723Sbschmidt	struct iwn_softc *sc = arg0;
7282220723Sbschmidt	struct ifnet *ifp = sc->sc_ifp;
7283220723Sbschmidt	struct ieee80211com *ic = ifp->if_l2com;
7284220723Sbschmidt	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
7285220723Sbschmidt
7286253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
7287253705Sadrian
7288220723Sbschmidt	if (vap != NULL) {
7289220723Sbschmidt		iwn_init(sc);
7290220723Sbschmidt		ieee80211_init(vap);
7291220723Sbschmidt	}
7292220723Sbschmidt}
7293220723Sbschmidt
7294220723Sbschmidtstatic void
7295220723Sbschmidtiwn_radio_off(void *arg0, int pending)
7296220723Sbschmidt{
7297220723Sbschmidt	struct iwn_softc *sc = arg0;
7298220723Sbschmidt	struct ifnet *ifp = sc->sc_ifp;
7299220723Sbschmidt	struct ieee80211com *ic = ifp->if_l2com;
7300220723Sbschmidt	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
7301220723Sbschmidt
7302253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
7303253705Sadrian
7304220723Sbschmidt	iwn_stop(sc);
7305220723Sbschmidt	if (vap != NULL)
7306220723Sbschmidt		ieee80211_stop(vap);
7307220723Sbschmidt
7308220723Sbschmidt	/* Enable interrupts to get RF toggle notification. */
7309220723Sbschmidt	IWN_LOCK(sc);
7310220723Sbschmidt	IWN_WRITE(sc, IWN_INT, 0xffffffff);
7311220723Sbschmidt	IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask);
7312220723Sbschmidt	IWN_UNLOCK(sc);
7313220723Sbschmidt}
7314220723Sbschmidt
7315220723Sbschmidtstatic void
7316198429Srpauloiwn_init_locked(struct iwn_softc *sc)
7317198429Srpaulo{
7318198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
7319198429Srpaulo	int error;
7320178676Ssam
7321253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
7322253705Sadrian
7323198429Srpaulo	IWN_LOCK_ASSERT(sc);
7324178676Ssam
7325220726Sbschmidt	if ((error = iwn_hw_prepare(sc)) != 0) {
7326220724Sbschmidt		device_printf(sc->sc_dev, "%s: hardware not ready, error %d\n",
7327198429Srpaulo		    __func__, error);
7328198429Srpaulo		goto fail;
7329198429Srpaulo	}
7330198429Srpaulo
7331201209Srpaulo	/* Initialize interrupt mask to default value. */
7332201209Srpaulo	sc->int_mask = IWN_INT_MASK_DEF;
7333201209Srpaulo	sc->sc_flags &= ~IWN_FLAG_USE_ICT;
7334201209Srpaulo
7335198429Srpaulo	/* Check that the radio is not disabled by hardware switch. */
7336198429Srpaulo	if (!(IWN_READ(sc, IWN_GP_CNTRL) & IWN_GP_CNTRL_RFKILL)) {
7337178676Ssam		device_printf(sc->sc_dev,
7338201209Srpaulo		    "radio is disabled by hardware switch\n");
7339201209Srpaulo		/* Enable interrupts to get RF toggle notifications. */
7340201209Srpaulo		IWN_WRITE(sc, IWN_INT, 0xffffffff);
7341201209Srpaulo		IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask);
7342201209Srpaulo		return;
7343178676Ssam	}
7344178676Ssam
7345198429Srpaulo	/* Read firmware images from the filesystem. */
7346220726Sbschmidt	if ((error = iwn_read_firmware(sc)) != 0) {
7347178676Ssam		device_printf(sc->sc_dev,
7348220726Sbschmidt		    "%s: could not read firmware, error %d\n", __func__,
7349220726Sbschmidt		    error);
7350198429Srpaulo		goto fail;
7351178676Ssam	}
7352178676Ssam
7353198429Srpaulo	/* Initialize hardware and upload firmware. */
7354198429Srpaulo	error = iwn_hw_init(sc);
7355201209Srpaulo	firmware_put(sc->fw_fp, FIRMWARE_UNLOAD);
7356201209Srpaulo	sc->fw_fp = NULL;
7357198429Srpaulo	if (error != 0) {
7358198429Srpaulo		device_printf(sc->sc_dev,
7359220726Sbschmidt		    "%s: could not initialize hardware, error %d\n", __func__,
7360220726Sbschmidt		    error);
7361198429Srpaulo		goto fail;
7362198429Srpaulo	}
7363178676Ssam
7364198429Srpaulo	/* Configure adapter now that it is ready. */
7365220726Sbschmidt	if ((error = iwn_config(sc)) != 0) {
7366178676Ssam		device_printf(sc->sc_dev,
7367220726Sbschmidt		    "%s: could not configure device, error %d\n", __func__,
7368220726Sbschmidt		    error);
7369198429Srpaulo		goto fail;
7370178676Ssam	}
7371178676Ssam
7372178676Ssam	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
7373178676Ssam	ifp->if_drv_flags |= IFF_DRV_RUNNING;
7374198429Srpaulo
7375220667Sbschmidt	callout_reset(&sc->watchdog_to, hz, iwn_watchdog, sc);
7376253705Sadrian
7377253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
7378253705Sadrian
7379198429Srpaulo	return;
7380198429Srpaulo
7381220726Sbschmidtfail:	iwn_stop_locked(sc);
7382253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end in error\n",__func__);
7383178676Ssam}
7384178676Ssam
7385206477Sbschmidtstatic void
7386178676Ssamiwn_init(void *arg)
7387178676Ssam{
7388178676Ssam	struct iwn_softc *sc = arg;
7389178676Ssam	struct ifnet *ifp = sc->sc_ifp;
7390178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
7391178676Ssam
7392178676Ssam	IWN_LOCK(sc);
7393178676Ssam	iwn_init_locked(sc);
7394178676Ssam	IWN_UNLOCK(sc);
7395178676Ssam
7396178676Ssam	if (ifp->if_drv_flags & IFF_DRV_RUNNING)
7397178676Ssam		ieee80211_start_all(ic);
7398178676Ssam}
7399178676Ssam
7400206477Sbschmidtstatic void
7401178676Ssamiwn_stop_locked(struct iwn_softc *sc)
7402178676Ssam{
7403178676Ssam	struct ifnet *ifp = sc->sc_ifp;
7404178676Ssam
7405178676Ssam	IWN_LOCK_ASSERT(sc);
7406178676Ssam
7407178676Ssam	sc->sc_tx_timer = 0;
7408220667Sbschmidt	callout_stop(&sc->watchdog_to);
7409220667Sbschmidt	callout_stop(&sc->calib_to);
7410178676Ssam	ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
7411178676Ssam
7412198429Srpaulo	/* Power OFF hardware. */
7413198429Srpaulo	iwn_hw_stop(sc);
7414198429Srpaulo}
7415178676Ssam
7416206477Sbschmidtstatic void
7417178676Ssamiwn_stop(struct iwn_softc *sc)
7418178676Ssam{
7419178676Ssam	IWN_LOCK(sc);
7420178676Ssam	iwn_stop_locked(sc);
7421178676Ssam	IWN_UNLOCK(sc);
7422178676Ssam}
7423178676Ssam
7424178676Ssam/*
7425178676Ssam * Callback from net80211 to start a scan.
7426178676Ssam */
7427178676Ssamstatic void
7428178676Ssamiwn_scan_start(struct ieee80211com *ic)
7429178676Ssam{
7430178676Ssam	struct ifnet *ifp = ic->ic_ifp;
7431178676Ssam	struct iwn_softc *sc = ifp->if_softc;
7432178676Ssam
7433191746Sthompsa	IWN_LOCK(sc);
7434191746Sthompsa	/* make the link LED blink while we're scanning */
7435191746Sthompsa	iwn_set_led(sc, IWN_LED_LINK, 20, 2);
7436191746Sthompsa	IWN_UNLOCK(sc);
7437178676Ssam}
7438178676Ssam
7439178676Ssam/*
7440178676Ssam * Callback from net80211 to terminate a scan.
7441178676Ssam */
7442178676Ssamstatic void
7443178676Ssamiwn_scan_end(struct ieee80211com *ic)
7444178676Ssam{
7445201209Srpaulo	struct ifnet *ifp = ic->ic_ifp;
7446201209Srpaulo	struct iwn_softc *sc = ifp->if_softc;
7447201209Srpaulo	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
7448201209Srpaulo
7449201209Srpaulo	IWN_LOCK(sc);
7450201209Srpaulo	if (vap->iv_state == IEEE80211_S_RUN) {
7451201209Srpaulo		/* Set link LED to ON status if we are associated */
7452201209Srpaulo		iwn_set_led(sc, IWN_LED_LINK, 0, 1);
7453201209Srpaulo	}
7454201209Srpaulo	IWN_UNLOCK(sc);
7455178676Ssam}
7456178676Ssam
7457178676Ssam/*
7458178676Ssam * Callback from net80211 to force a channel change.
7459178676Ssam */
7460178676Ssamstatic void
7461178676Ssamiwn_set_channel(struct ieee80211com *ic)
7462178676Ssam{
7463198429Srpaulo	const struct ieee80211_channel *c = ic->ic_curchan;
7464178676Ssam	struct ifnet *ifp = ic->ic_ifp;
7465178676Ssam	struct iwn_softc *sc = ifp->if_softc;
7466225686Sadrian	int error;
7467178676Ssam
7468253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
7469253705Sadrian
7470191746Sthompsa	IWN_LOCK(sc);
7471201209Srpaulo	sc->sc_rxtap.wr_chan_freq = htole16(c->ic_freq);
7472201209Srpaulo	sc->sc_rxtap.wr_chan_flags = htole16(c->ic_flags);
7473201209Srpaulo	sc->sc_txtap.wt_chan_freq = htole16(c->ic_freq);
7474201209Srpaulo	sc->sc_txtap.wt_chan_flags = htole16(c->ic_flags);
7475225686Sadrian
7476225686Sadrian	/*
7477225686Sadrian	 * Only need to set the channel in Monitor mode. AP scanning and auth
7478225686Sadrian	 * are already taken care of by their respective firmware commands.
7479225686Sadrian	 */
7480225686Sadrian	if (ic->ic_opmode == IEEE80211_M_MONITOR) {
7481225686Sadrian		error = iwn_config(sc);
7482225686Sadrian		if (error != 0)
7483225686Sadrian		device_printf(sc->sc_dev,
7484225686Sadrian		    "%s: error %d settting channel\n", __func__, error);
7485225686Sadrian	}
7486191746Sthompsa	IWN_UNLOCK(sc);
7487178676Ssam}
7488178676Ssam
7489178676Ssam/*
7490178676Ssam * Callback from net80211 to start scanning of the current channel.
7491178676Ssam */
7492178676Ssamstatic void
7493178676Ssamiwn_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell)
7494178676Ssam{
7495178676Ssam	struct ieee80211vap *vap = ss->ss_vap;
7496178676Ssam	struct iwn_softc *sc = vap->iv_ic->ic_ifp->if_softc;
7497191746Sthompsa	int error;
7498178676Ssam
7499191746Sthompsa	IWN_LOCK(sc);
7500191746Sthompsa	error = iwn_scan(sc);
7501191746Sthompsa	IWN_UNLOCK(sc);
7502191746Sthompsa	if (error != 0)
7503191746Sthompsa		ieee80211_cancel_scan(vap);
7504178676Ssam}
7505178676Ssam
7506178676Ssam/*
7507178676Ssam * Callback from net80211 to handle the minimum dwell time being met.
7508178676Ssam * The intent is to terminate the scan but we just let the firmware
7509178676Ssam * notify us when it's finished as we have no safe way to abort it.
7510178676Ssam */
7511178676Ssamstatic void
7512178676Ssamiwn_scan_mindwell(struct ieee80211_scan_state *ss)
7513178676Ssam{
7514178676Ssam	/* NB: don't try to abort scan; wait for firmware to finish */
7515178676Ssam}
7516178676Ssam
7517178676Ssamstatic void
7518198429Srpauloiwn_hw_reset(void *arg0, int pending)
7519178676Ssam{
7520178676Ssam	struct iwn_softc *sc = arg0;
7521178676Ssam	struct ifnet *ifp = sc->sc_ifp;
7522178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
7523178676Ssam
7524253705Sadrian	DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
7525253705Sadrian
7526201209Srpaulo	iwn_stop(sc);
7527191746Sthompsa	iwn_init(sc);
7528191746Sthompsa	ieee80211_notify_radio(ic, 1);
7529191746Sthompsa}
7530253866Sadrian#ifdef	IWN_DEBUG
7531253866Sadrian#define	IWN_DESC(x) case x:	return #x
7532253866Sadrian#define	COUNTOF(array) (sizeof(array) / sizeof(array[0]))
7533253866Sadrian
7534253866Sadrian/*
7535253937Shiren * Translate CSR code to string
7536253866Sadrian */
7537253866Sadrianstatic char *iwn_get_csr_string(int csr)
7538253866Sadrian{
7539253866Sadrian	switch (csr) {
7540253866Sadrian		IWN_DESC(IWN_HW_IF_CONFIG);
7541253866Sadrian		IWN_DESC(IWN_INT_COALESCING);
7542253866Sadrian		IWN_DESC(IWN_INT);
7543253866Sadrian		IWN_DESC(IWN_INT_MASK);
7544253866Sadrian		IWN_DESC(IWN_FH_INT);
7545253866Sadrian		IWN_DESC(IWN_GPIO_IN);
7546253866Sadrian		IWN_DESC(IWN_RESET);
7547253866Sadrian		IWN_DESC(IWN_GP_CNTRL);
7548253866Sadrian		IWN_DESC(IWN_HW_REV);
7549253866Sadrian		IWN_DESC(IWN_EEPROM);
7550253866Sadrian		IWN_DESC(IWN_EEPROM_GP);
7551253866Sadrian		IWN_DESC(IWN_OTP_GP);
7552253866Sadrian		IWN_DESC(IWN_GIO);
7553253866Sadrian		IWN_DESC(IWN_GP_UCODE);
7554253866Sadrian		IWN_DESC(IWN_GP_DRIVER);
7555253866Sadrian		IWN_DESC(IWN_UCODE_GP1);
7556253866Sadrian		IWN_DESC(IWN_UCODE_GP2);
7557253866Sadrian		IWN_DESC(IWN_LED);
7558253866Sadrian		IWN_DESC(IWN_DRAM_INT_TBL);
7559253866Sadrian		IWN_DESC(IWN_GIO_CHICKEN);
7560253866Sadrian		IWN_DESC(IWN_ANA_PLL);
7561253866Sadrian		IWN_DESC(IWN_HW_REV_WA);
7562253866Sadrian		IWN_DESC(IWN_DBG_HPET_MEM);
7563253866Sadrian	default:
7564253866Sadrian		return "UNKNOWN CSR";
7565253866Sadrian	}
7566253866Sadrian}
7567253866Sadrian
7568253866Sadrian/*
7569253866Sadrian * This function print firmware register
7570253866Sadrian */
7571253866Sadrianstatic void
7572253866Sadrianiwn_debug_register(struct iwn_softc *sc)
7573253866Sadrian{
7574253866Sadrian	int i;
7575253866Sadrian	static const uint32_t csr_tbl[] = {
7576253866Sadrian		IWN_HW_IF_CONFIG,
7577253866Sadrian		IWN_INT_COALESCING,
7578253866Sadrian		IWN_INT,
7579253866Sadrian		IWN_INT_MASK,
7580253866Sadrian		IWN_FH_INT,
7581253866Sadrian		IWN_GPIO_IN,
7582253866Sadrian		IWN_RESET,
7583253866Sadrian		IWN_GP_CNTRL,
7584253866Sadrian		IWN_HW_REV,
7585253866Sadrian		IWN_EEPROM,
7586253866Sadrian		IWN_EEPROM_GP,
7587253866Sadrian		IWN_OTP_GP,
7588253866Sadrian		IWN_GIO,
7589253866Sadrian		IWN_GP_UCODE,
7590253866Sadrian		IWN_GP_DRIVER,
7591253866Sadrian		IWN_UCODE_GP1,
7592253866Sadrian		IWN_UCODE_GP2,
7593253866Sadrian		IWN_LED,
7594253866Sadrian		IWN_DRAM_INT_TBL,
7595253866Sadrian		IWN_GIO_CHICKEN,
7596253866Sadrian		IWN_ANA_PLL,
7597253866Sadrian		IWN_HW_REV_WA,
7598253866Sadrian		IWN_DBG_HPET_MEM,
7599253866Sadrian	};
7600253866Sadrian	DPRINTF(sc, IWN_DEBUG_REGISTER,
7601253866Sadrian	    "CSR values: (2nd byte of IWN_INT_COALESCING is IWN_INT_PERIODIC)%s",
7602253866Sadrian	    "\n");
7603253866Sadrian	for (i = 0; i <  COUNTOF(csr_tbl); i++){
7604253866Sadrian		DPRINTF(sc, IWN_DEBUG_REGISTER,"  %10s: 0x%08x ",
7605253866Sadrian			iwn_get_csr_string(csr_tbl[i]), IWN_READ(sc, csr_tbl[i]));
7606253866Sadrian		if ((i+1) % 3 == 0)
7607253866Sadrian			DPRINTF(sc, IWN_DEBUG_REGISTER,"%s","\n");
7608253866Sadrian	}
7609253866Sadrian	DPRINTF(sc, IWN_DEBUG_REGISTER,"%s","\n");
7610253866Sadrian}
7611253866Sadrian#endif
7612