1178676Ssam/*-
2198429Srpaulo * Copyright (c) 2007-2009
3178676Ssam *	Damien Bergamini <damien.bergamini@free.fr>
4178676Ssam * Copyright (c) 2008
5178676Ssam *	Benjamin Close <benjsc@FreeBSD.org>
6178676Ssam * Copyright (c) 2008 Sam Leffler, Errno Consulting
7178676Ssam *
8178676Ssam * Permission to use, copy, modify, and distribute this software for any
9178676Ssam * purpose with or without fee is hereby granted, provided that the above
10178676Ssam * copyright notice and this permission notice appear in all copies.
11178676Ssam *
12178676Ssam * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13178676Ssam * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14178676Ssam * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15178676Ssam * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16178676Ssam * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17178676Ssam * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18178676Ssam * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19178676Ssam */
20178676Ssam
21178676Ssam/*
22201209Srpaulo * Driver for Intel WiFi Link 4965 and 1000/5000/6000 Series 802.11 network
23201209Srpaulo * adapters.
24178676Ssam */
25178676Ssam
26178676Ssam#include <sys/cdefs.h>
27178676Ssam__FBSDID("$FreeBSD$");
28178676Ssam
29178676Ssam#include <sys/param.h>
30178676Ssam#include <sys/sockio.h>
31178676Ssam#include <sys/sysctl.h>
32178676Ssam#include <sys/mbuf.h>
33178676Ssam#include <sys/kernel.h>
34178676Ssam#include <sys/socket.h>
35178676Ssam#include <sys/systm.h>
36178676Ssam#include <sys/malloc.h>
37178676Ssam#include <sys/bus.h>
38178676Ssam#include <sys/rman.h>
39178676Ssam#include <sys/endian.h>
40178676Ssam#include <sys/firmware.h>
41178676Ssam#include <sys/limits.h>
42178676Ssam#include <sys/module.h>
43178676Ssam#include <sys/queue.h>
44178676Ssam#include <sys/taskqueue.h>
45178676Ssam
46178676Ssam#include <machine/bus.h>
47178676Ssam#include <machine/resource.h>
48178676Ssam#include <machine/clock.h>
49178676Ssam
50178676Ssam#include <dev/pci/pcireg.h>
51178676Ssam#include <dev/pci/pcivar.h>
52178676Ssam
53178676Ssam#include <net/bpf.h>
54178676Ssam#include <net/if.h>
55178676Ssam#include <net/if_arp.h>
56178676Ssam#include <net/ethernet.h>
57178676Ssam#include <net/if_dl.h>
58178676Ssam#include <net/if_media.h>
59178676Ssam#include <net/if_types.h>
60178676Ssam
61178676Ssam#include <netinet/in.h>
62178676Ssam#include <netinet/in_systm.h>
63178676Ssam#include <netinet/in_var.h>
64178676Ssam#include <netinet/if_ether.h>
65178676Ssam#include <netinet/ip.h>
66178676Ssam
67178676Ssam#include <net80211/ieee80211_var.h>
68178676Ssam#include <net80211/ieee80211_radiotap.h>
69178676Ssam#include <net80211/ieee80211_regdomain.h>
70206358Srpaulo#include <net80211/ieee80211_ratectl.h>
71178676Ssam
72178676Ssam#include <dev/iwn/if_iwnreg.h>
73178676Ssam#include <dev/iwn/if_iwnvar.h>
74178676Ssam
75220723Sbschmidtstruct iwn_ident {
76220723Sbschmidt	uint16_t	vendor;
77220723Sbschmidt	uint16_t	device;
78220723Sbschmidt	const char	*name;
79220723Sbschmidt};
80220723Sbschmidt
81220895Sbschmidtstatic const struct iwn_ident iwn_ident_table[] = {
82233842Sbschmidt	{ 0x8086, 0x0082, "Intel Centrino Advanced-N 6205"		},
83233842Sbschmidt	{ 0x8086, 0x0083, "Intel Centrino Wireless-N 1000"		},
84233842Sbschmidt	{ 0x8086, 0x0084, "Intel Centrino Wireless-N 1000"		},
85233842Sbschmidt	{ 0x8086, 0x0085, "Intel Centrino Advanced-N 6205"		},
86233842Sbschmidt	{ 0x8086, 0x0087, "Intel Centrino Advanced-N + WiMAX 6250"	},
87233842Sbschmidt	{ 0x8086, 0x0089, "Intel Centrino Advanced-N + WiMAX 6250"	},
88233842Sbschmidt	{ 0x8086, 0x008a, "Intel Centrino Wireless-N 1030"		},
89233842Sbschmidt	{ 0x8086, 0x008b, "Intel Centrino Wireless-N 1030"		},
90233842Sbschmidt	{ 0x8086, 0x0090, "Intel Centrino Advanced-N 6230"		},
91233842Sbschmidt	{ 0x8086, 0x0091, "Intel Centrino Advanced-N 6230"		},
92233842Sbschmidt	{ 0x8086, 0x0885, "Intel Centrino Wireless-N + WiMAX 6150"	},
93233842Sbschmidt	{ 0x8086, 0x0886, "Intel Centrino Wireless-N + WiMAX 6150"	},
94233842Sbschmidt	{ 0x8086, 0x0896, "Intel Centrino Wireless-N 130"		},
95235843Sbschmidt	{ 0x8086, 0x0887, "Intel Centrino Wireless-N 130"		},
96235843Sbschmidt	{ 0x8086, 0x08ae, "Intel Centrino Wireless-N 100"		},
97235843Sbschmidt	{ 0x8086, 0x08af, "Intel Centrino Wireless-N 100"		},
98233842Sbschmidt	{ 0x8086, 0x4229, "Intel Wireless WiFi Link 4965"		},
99233842Sbschmidt	{ 0x8086, 0x422b, "Intel Centrino Ultimate-N 6300"		},
100233842Sbschmidt	{ 0x8086, 0x422c, "Intel Centrino Advanced-N 6200"		},
101233842Sbschmidt	{ 0x8086, 0x422d, "Intel Wireless WiFi Link 4965"		},
102233842Sbschmidt	{ 0x8086, 0x4230, "Intel Wireless WiFi Link 4965"		},
103233842Sbschmidt	{ 0x8086, 0x4232, "Intel WiFi Link 5100"			},
104233842Sbschmidt	{ 0x8086, 0x4233, "Intel Wireless WiFi Link 4965"		},
105233842Sbschmidt	{ 0x8086, 0x4235, "Intel Ultimate N WiFi Link 5300"		},
106233842Sbschmidt	{ 0x8086, 0x4236, "Intel Ultimate N WiFi Link 5300"		},
107233842Sbschmidt	{ 0x8086, 0x4237, "Intel WiFi Link 5100"			},
108233842Sbschmidt	{ 0x8086, 0x4238, "Intel Centrino Ultimate-N 6300"		},
109233842Sbschmidt	{ 0x8086, 0x4239, "Intel Centrino Advanced-N 6200"		},
110233842Sbschmidt	{ 0x8086, 0x423a, "Intel WiMAX/WiFi Link 5350"			},
111233842Sbschmidt	{ 0x8086, 0x423b, "Intel WiMAX/WiFi Link 5350"			},
112233842Sbschmidt	{ 0x8086, 0x423c, "Intel WiMAX/WiFi Link 5150"			},
113233842Sbschmidt	{ 0x8086, 0x423d, "Intel WiMAX/WiFi Link 5150"			},
114220723Sbschmidt	{ 0, 0, NULL }
115220723Sbschmidt};
116220723Sbschmidt
117178676Ssamstatic int	iwn_probe(device_t);
118178676Ssamstatic int	iwn_attach(device_t);
119220728Sbschmidtstatic int	iwn4965_attach(struct iwn_softc *, uint16_t);
120220728Sbschmidtstatic int	iwn5000_attach(struct iwn_softc *, uint16_t);
121206477Sbschmidtstatic void	iwn_radiotap_attach(struct iwn_softc *);
122220723Sbschmidtstatic void	iwn_sysctlattach(struct iwn_softc *);
123178676Ssamstatic struct ieee80211vap *iwn_vap_create(struct ieee80211com *,
124234753Sdim		    const char [IFNAMSIZ], int, enum ieee80211_opmode, int,
125234753Sdim		    const uint8_t [IEEE80211_ADDR_LEN],
126234753Sdim		    const uint8_t [IEEE80211_ADDR_LEN]);
127178676Ssamstatic void	iwn_vap_delete(struct ieee80211vap *);
128206474Sbschmidtstatic int	iwn_detach(device_t);
129220723Sbschmidtstatic int	iwn_shutdown(device_t);
130220723Sbschmidtstatic int	iwn_suspend(device_t);
131220723Sbschmidtstatic int	iwn_resume(device_t);
132206477Sbschmidtstatic int	iwn_nic_lock(struct iwn_softc *);
133206477Sbschmidtstatic int	iwn_eeprom_lock(struct iwn_softc *);
134206477Sbschmidtstatic int	iwn_init_otprom(struct iwn_softc *);
135206477Sbschmidtstatic int	iwn_read_prom_data(struct iwn_softc *, uint32_t, void *, int);
136206474Sbschmidtstatic void	iwn_dma_map_addr(void *, bus_dma_segment_t *, int, int);
137178676Ssamstatic int	iwn_dma_contig_alloc(struct iwn_softc *, struct iwn_dma_info *,
138220691Sbschmidt		    void **, bus_size_t, bus_size_t);
139178676Ssamstatic void	iwn_dma_contig_free(struct iwn_dma_info *);
140206477Sbschmidtstatic int	iwn_alloc_sched(struct iwn_softc *);
141206477Sbschmidtstatic void	iwn_free_sched(struct iwn_softc *);
142206477Sbschmidtstatic int	iwn_alloc_kw(struct iwn_softc *);
143206477Sbschmidtstatic void	iwn_free_kw(struct iwn_softc *);
144206477Sbschmidtstatic int	iwn_alloc_ict(struct iwn_softc *);
145206477Sbschmidtstatic void	iwn_free_ict(struct iwn_softc *);
146206477Sbschmidtstatic int	iwn_alloc_fwmem(struct iwn_softc *);
147206477Sbschmidtstatic void	iwn_free_fwmem(struct iwn_softc *);
148206477Sbschmidtstatic int	iwn_alloc_rx_ring(struct iwn_softc *, struct iwn_rx_ring *);
149206477Sbschmidtstatic void	iwn_reset_rx_ring(struct iwn_softc *, struct iwn_rx_ring *);
150206477Sbschmidtstatic void	iwn_free_rx_ring(struct iwn_softc *, struct iwn_rx_ring *);
151206477Sbschmidtstatic int	iwn_alloc_tx_ring(struct iwn_softc *, struct iwn_tx_ring *,
152178676Ssam		    int);
153206477Sbschmidtstatic void	iwn_reset_tx_ring(struct iwn_softc *, struct iwn_tx_ring *);
154206477Sbschmidtstatic void	iwn_free_tx_ring(struct iwn_softc *, struct iwn_tx_ring *);
155206477Sbschmidtstatic void	iwn5000_ict_reset(struct iwn_softc *);
156206477Sbschmidtstatic int	iwn_read_eeprom(struct iwn_softc *,
157198429Srpaulo		    uint8_t macaddr[IEEE80211_ADDR_LEN]);
158206477Sbschmidtstatic void	iwn4965_read_eeprom(struct iwn_softc *);
159206477Sbschmidtstatic void	iwn4965_print_power_group(struct iwn_softc *, int);
160206477Sbschmidtstatic void	iwn5000_read_eeprom(struct iwn_softc *);
161206474Sbschmidtstatic uint32_t	iwn_eeprom_channel_flags(struct iwn_eeprom_chan *);
162206474Sbschmidtstatic void	iwn_read_eeprom_band(struct iwn_softc *, int);
163206474Sbschmidtstatic void	iwn_read_eeprom_ht40(struct iwn_softc *, int);
164220726Sbschmidtstatic void	iwn_read_eeprom_channels(struct iwn_softc *, int, uint32_t);
165220723Sbschmidtstatic struct iwn_eeprom_chan *iwn_find_eeprom_channel(struct iwn_softc *,
166220723Sbschmidt		    struct ieee80211_channel *);
167220723Sbschmidtstatic int	iwn_setregdomain(struct ieee80211com *,
168220723Sbschmidt		    struct ieee80211_regdomain *, int,
169220726Sbschmidt		    struct ieee80211_channel[]);
170206477Sbschmidtstatic void	iwn_read_eeprom_enhinfo(struct iwn_softc *);
171206477Sbschmidtstatic struct ieee80211_node *iwn_node_alloc(struct ieee80211vap *,
172198429Srpaulo		    const uint8_t mac[IEEE80211_ADDR_LEN]);
173220715Sbschmidtstatic void	iwn_newassoc(struct ieee80211_node *, int);
174206477Sbschmidtstatic int	iwn_media_change(struct ifnet *);
175206477Sbschmidtstatic int	iwn_newstate(struct ieee80211vap *, enum ieee80211_state, int);
176220667Sbschmidtstatic void	iwn_calib_timeout(void *);
177206477Sbschmidtstatic void	iwn_rx_phy(struct iwn_softc *, struct iwn_rx_desc *,
178198429Srpaulo		    struct iwn_rx_data *);
179206477Sbschmidtstatic void	iwn_rx_done(struct iwn_softc *, struct iwn_rx_desc *,
180178676Ssam		    struct iwn_rx_data *);
181206477Sbschmidtstatic void	iwn_rx_compressed_ba(struct iwn_softc *, struct iwn_rx_desc *,
182201209Srpaulo		    struct iwn_rx_data *);
183220674Sbschmidtstatic void	iwn5000_rx_calib_results(struct iwn_softc *,
184220674Sbschmidt		    struct iwn_rx_desc *, struct iwn_rx_data *);
185206477Sbschmidtstatic void	iwn_rx_statistics(struct iwn_softc *, struct iwn_rx_desc *,
186198429Srpaulo		    struct iwn_rx_data *);
187206477Sbschmidtstatic void	iwn4965_tx_done(struct iwn_softc *, struct iwn_rx_desc *,
188198429Srpaulo		    struct iwn_rx_data *);
189206477Sbschmidtstatic void	iwn5000_tx_done(struct iwn_softc *, struct iwn_rx_desc *,
190198429Srpaulo		    struct iwn_rx_data *);
191206477Sbschmidtstatic void	iwn_tx_done(struct iwn_softc *, struct iwn_rx_desc *, int,
192198429Srpaulo		    uint8_t);
193221651Sbschmidtstatic void	iwn_ampdu_tx_done(struct iwn_softc *, int, int, int, void *);
194206477Sbschmidtstatic void	iwn_cmd_done(struct iwn_softc *, struct iwn_rx_desc *);
195206477Sbschmidtstatic void	iwn_notif_intr(struct iwn_softc *);
196206477Sbschmidtstatic void	iwn_wakeup_intr(struct iwn_softc *);
197206477Sbschmidtstatic void	iwn_rftoggle_intr(struct iwn_softc *);
198206477Sbschmidtstatic void	iwn_fatal_intr(struct iwn_softc *);
199206477Sbschmidtstatic void	iwn_intr(void *);
200206477Sbschmidtstatic void	iwn4965_update_sched(struct iwn_softc *, int, int, uint8_t,
201198429Srpaulo		    uint16_t);
202206477Sbschmidtstatic void	iwn5000_update_sched(struct iwn_softc *, int, int, uint8_t,
203198429Srpaulo		    uint16_t);
204206475Sbschmidt#ifdef notyet
205206477Sbschmidtstatic void	iwn5000_reset_sched(struct iwn_softc *, int, int);
206206475Sbschmidt#endif
207206477Sbschmidtstatic int	iwn_tx_data(struct iwn_softc *, struct mbuf *,
208220720Sbschmidt		    struct ieee80211_node *);
209220720Sbschmidtstatic int	iwn_tx_data_raw(struct iwn_softc *, struct mbuf *,
210220720Sbschmidt		    struct ieee80211_node *,
211220720Sbschmidt		    const struct ieee80211_bpf_params *params);
212198429Srpaulostatic int	iwn_raw_xmit(struct ieee80211_node *, struct mbuf *,
213198429Srpaulo		    const struct ieee80211_bpf_params *);
214206477Sbschmidtstatic void	iwn_start(struct ifnet *);
215206477Sbschmidtstatic void	iwn_start_locked(struct ifnet *);
216220667Sbschmidtstatic void	iwn_watchdog(void *);
217206477Sbschmidtstatic int	iwn_ioctl(struct ifnet *, u_long, caddr_t);
218206477Sbschmidtstatic int	iwn_cmd(struct iwn_softc *, int, const void *, int, int);
219206477Sbschmidtstatic int	iwn4965_add_node(struct iwn_softc *, struct iwn_node_info *,
220198429Srpaulo		    int);
221206477Sbschmidtstatic int	iwn5000_add_node(struct iwn_softc *, struct iwn_node_info *,
222198429Srpaulo		    int);
223220715Sbschmidtstatic int	iwn_set_link_quality(struct iwn_softc *,
224220715Sbschmidt		    struct ieee80211_node *);
225206477Sbschmidtstatic int	iwn_add_broadcast_node(struct iwn_softc *, int);
226220721Sbschmidtstatic int	iwn_updateedca(struct ieee80211com *);
227201209Srpaulostatic void	iwn_update_mcast(struct ifnet *);
228206477Sbschmidtstatic void	iwn_set_led(struct iwn_softc *, uint8_t, uint8_t, uint8_t);
229206477Sbschmidtstatic int	iwn_set_critical_temp(struct iwn_softc *);
230206477Sbschmidtstatic int	iwn_set_timing(struct iwn_softc *, struct ieee80211_node *);
231206477Sbschmidtstatic void	iwn4965_power_calibration(struct iwn_softc *, int);
232206477Sbschmidtstatic int	iwn4965_set_txpower(struct iwn_softc *,
233201882Skeramida		    struct ieee80211_channel *, int);
234206477Sbschmidtstatic int	iwn5000_set_txpower(struct iwn_softc *,
235201882Skeramida		    struct ieee80211_channel *, int);
236206477Sbschmidtstatic int	iwn4965_get_rssi(struct iwn_softc *, struct iwn_rx_stat *);
237206477Sbschmidtstatic int	iwn5000_get_rssi(struct iwn_softc *, struct iwn_rx_stat *);
238206477Sbschmidtstatic int	iwn_get_noise(const struct iwn_rx_general_stats *);
239206477Sbschmidtstatic int	iwn4965_get_temperature(struct iwn_softc *);
240206477Sbschmidtstatic int	iwn5000_get_temperature(struct iwn_softc *);
241206477Sbschmidtstatic int	iwn_init_sensitivity(struct iwn_softc *);
242206477Sbschmidtstatic void	iwn_collect_noise(struct iwn_softc *,
243178676Ssam		    const struct iwn_rx_general_stats *);
244206477Sbschmidtstatic int	iwn4965_init_gains(struct iwn_softc *);
245206477Sbschmidtstatic int	iwn5000_init_gains(struct iwn_softc *);
246206477Sbschmidtstatic int	iwn4965_set_gains(struct iwn_softc *);
247206477Sbschmidtstatic int	iwn5000_set_gains(struct iwn_softc *);
248206477Sbschmidtstatic void	iwn_tune_sensitivity(struct iwn_softc *,
249178676Ssam		    const struct iwn_rx_stats *);
250206477Sbschmidtstatic int	iwn_send_sensitivity(struct iwn_softc *);
251206477Sbschmidtstatic int	iwn_set_pslevel(struct iwn_softc *, int, int, int);
252220662Sbschmidtstatic int	iwn_send_btcoex(struct iwn_softc *);
253220891Sbschmidtstatic int	iwn_send_advanced_btcoex(struct iwn_softc *);
254227967Sbschmidtstatic int	iwn5000_runtime_calib(struct iwn_softc *);
255206477Sbschmidtstatic int	iwn_config(struct iwn_softc *);
256220634Sbschmidtstatic uint8_t	*ieee80211_add_ssid(uint8_t *, const uint8_t *, u_int);
257206477Sbschmidtstatic int	iwn_scan(struct iwn_softc *);
258206477Sbschmidtstatic int	iwn_auth(struct iwn_softc *, struct ieee80211vap *vap);
259206477Sbschmidtstatic int	iwn_run(struct iwn_softc *, struct ieee80211vap *vap);
260221650Sbschmidtstatic int	iwn_ampdu_rx_start(struct ieee80211_node *,
261221650Sbschmidt		    struct ieee80211_rx_ampdu *, int, int, int);
262221650Sbschmidtstatic void	iwn_ampdu_rx_stop(struct ieee80211_node *,
263221650Sbschmidt		    struct ieee80211_rx_ampdu *);
264221651Sbschmidtstatic int	iwn_addba_request(struct ieee80211_node *,
265221651Sbschmidt		    struct ieee80211_tx_ampdu *, int, int, int);
266221651Sbschmidtstatic int	iwn_addba_response(struct ieee80211_node *,
267221651Sbschmidt		    struct ieee80211_tx_ampdu *, int, int, int);
268206474Sbschmidtstatic int	iwn_ampdu_tx_start(struct ieee80211com *,
269206474Sbschmidt		    struct ieee80211_node *, uint8_t);
270221651Sbschmidtstatic void	iwn_ampdu_tx_stop(struct ieee80211_node *,
271221651Sbschmidt		    struct ieee80211_tx_ampdu *);
272206474Sbschmidtstatic void	iwn4965_ampdu_tx_start(struct iwn_softc *,
273221651Sbschmidt		    struct ieee80211_node *, int, uint8_t, uint16_t);
274221651Sbschmidtstatic void	iwn4965_ampdu_tx_stop(struct iwn_softc *, int,
275220726Sbschmidt		    uint8_t, uint16_t);
276206474Sbschmidtstatic void	iwn5000_ampdu_tx_start(struct iwn_softc *,
277221651Sbschmidt		    struct ieee80211_node *, int, uint8_t, uint16_t);
278221651Sbschmidtstatic void	iwn5000_ampdu_tx_stop(struct iwn_softc *, int,
279220726Sbschmidt		    uint8_t, uint16_t);
280220674Sbschmidtstatic int	iwn5000_query_calibration(struct iwn_softc *);
281220674Sbschmidtstatic int	iwn5000_send_calibration(struct iwn_softc *);
282206477Sbschmidtstatic int	iwn5000_send_wimax_coex(struct iwn_softc *);
283220677Sbschmidtstatic int	iwn5000_crystal_calib(struct iwn_softc *);
284220676Sbschmidtstatic int	iwn5000_temp_offset_calib(struct iwn_softc *);
285206477Sbschmidtstatic int	iwn4965_post_alive(struct iwn_softc *);
286206477Sbschmidtstatic int	iwn5000_post_alive(struct iwn_softc *);
287206477Sbschmidtstatic int	iwn4965_load_bootcode(struct iwn_softc *, const uint8_t *,
288198429Srpaulo		    int);
289206477Sbschmidtstatic int	iwn4965_load_firmware(struct iwn_softc *);
290206477Sbschmidtstatic int	iwn5000_load_firmware_section(struct iwn_softc *, uint32_t,
291198429Srpaulo		    const uint8_t *, int);
292206477Sbschmidtstatic int	iwn5000_load_firmware(struct iwn_softc *);
293210111Sbschmidtstatic int	iwn_read_firmware_leg(struct iwn_softc *,
294210111Sbschmidt		    struct iwn_fw_info *);
295210111Sbschmidtstatic int	iwn_read_firmware_tlv(struct iwn_softc *,
296210111Sbschmidt		    struct iwn_fw_info *, uint16_t);
297206477Sbschmidtstatic int	iwn_read_firmware(struct iwn_softc *);
298206477Sbschmidtstatic int	iwn_clock_wait(struct iwn_softc *);
299206477Sbschmidtstatic int	iwn_apm_init(struct iwn_softc *);
300206477Sbschmidtstatic void	iwn_apm_stop_master(struct iwn_softc *);
301206477Sbschmidtstatic void	iwn_apm_stop(struct iwn_softc *);
302206477Sbschmidtstatic int	iwn4965_nic_config(struct iwn_softc *);
303206477Sbschmidtstatic int	iwn5000_nic_config(struct iwn_softc *);
304206477Sbschmidtstatic int	iwn_hw_prepare(struct iwn_softc *);
305206477Sbschmidtstatic int	iwn_hw_init(struct iwn_softc *);
306206477Sbschmidtstatic void	iwn_hw_stop(struct iwn_softc *);
307220723Sbschmidtstatic void	iwn_radio_on(void *, int);
308220723Sbschmidtstatic void	iwn_radio_off(void *, int);
309206477Sbschmidtstatic void	iwn_init_locked(struct iwn_softc *);
310206477Sbschmidtstatic void	iwn_init(void *);
311206477Sbschmidtstatic void	iwn_stop_locked(struct iwn_softc *);
312206477Sbschmidtstatic void	iwn_stop(struct iwn_softc *);
313220726Sbschmidtstatic void	iwn_scan_start(struct ieee80211com *);
314220726Sbschmidtstatic void	iwn_scan_end(struct ieee80211com *);
315220726Sbschmidtstatic void	iwn_set_channel(struct ieee80211com *);
316220726Sbschmidtstatic void	iwn_scan_curchan(struct ieee80211_scan_state *, unsigned long);
317220726Sbschmidtstatic void	iwn_scan_mindwell(struct ieee80211_scan_state *);
318198429Srpaulostatic void	iwn_hw_reset(void *, int);
319178676Ssam
320178676Ssam#define IWN_DEBUG
321178676Ssam#ifdef IWN_DEBUG
322178676Ssamenum {
323178676Ssam	IWN_DEBUG_XMIT		= 0x00000001,	/* basic xmit operation */
324178676Ssam	IWN_DEBUG_RECV		= 0x00000002,	/* basic recv operation */
325178676Ssam	IWN_DEBUG_STATE		= 0x00000004,	/* 802.11 state transitions */
326178676Ssam	IWN_DEBUG_TXPOW		= 0x00000008,	/* tx power processing */
327178676Ssam	IWN_DEBUG_RESET		= 0x00000010,	/* reset processing */
328178676Ssam	IWN_DEBUG_OPS		= 0x00000020,	/* iwn_ops processing */
329178676Ssam	IWN_DEBUG_BEACON 	= 0x00000040,	/* beacon handling */
330178676Ssam	IWN_DEBUG_WATCHDOG 	= 0x00000080,	/* watchdog timeout */
331178676Ssam	IWN_DEBUG_INTR		= 0x00000100,	/* ISR */
332178676Ssam	IWN_DEBUG_CALIBRATE	= 0x00000200,	/* periodic calibration */
333178676Ssam	IWN_DEBUG_NODE		= 0x00000400,	/* node management */
334178676Ssam	IWN_DEBUG_LED		= 0x00000800,	/* led management */
335178676Ssam	IWN_DEBUG_CMD		= 0x00001000,	/* cmd submission */
336178676Ssam	IWN_DEBUG_FATAL		= 0x80000000,	/* fatal errors */
337178676Ssam	IWN_DEBUG_ANY		= 0xffffffff
338178676Ssam};
339178676Ssam
340178676Ssam#define DPRINTF(sc, m, fmt, ...) do {			\
341178676Ssam	if (sc->sc_debug & (m))				\
342178676Ssam		printf(fmt, __VA_ARGS__);		\
343178676Ssam} while (0)
344178676Ssam
345220723Sbschmidtstatic const char *
346220723Sbschmidtiwn_intr_str(uint8_t cmd)
347220723Sbschmidt{
348220723Sbschmidt	switch (cmd) {
349220723Sbschmidt	/* Notifications */
350220723Sbschmidt	case IWN_UC_READY:		return "UC_READY";
351220723Sbschmidt	case IWN_ADD_NODE_DONE:		return "ADD_NODE_DONE";
352220723Sbschmidt	case IWN_TX_DONE:		return "TX_DONE";
353220723Sbschmidt	case IWN_START_SCAN:		return "START_SCAN";
354220723Sbschmidt	case IWN_STOP_SCAN:		return "STOP_SCAN";
355220723Sbschmidt	case IWN_RX_STATISTICS:		return "RX_STATS";
356220723Sbschmidt	case IWN_BEACON_STATISTICS:	return "BEACON_STATS";
357220723Sbschmidt	case IWN_STATE_CHANGED:		return "STATE_CHANGED";
358220723Sbschmidt	case IWN_BEACON_MISSED:		return "BEACON_MISSED";
359220723Sbschmidt	case IWN_RX_PHY:		return "RX_PHY";
360220723Sbschmidt	case IWN_MPDU_RX_DONE:		return "MPDU_RX_DONE";
361220723Sbschmidt	case IWN_RX_DONE:		return "RX_DONE";
362220723Sbschmidt
363220723Sbschmidt	/* Command Notifications */
364220723Sbschmidt	case IWN_CMD_RXON:		return "IWN_CMD_RXON";
365220723Sbschmidt	case IWN_CMD_RXON_ASSOC:	return "IWN_CMD_RXON_ASSOC";
366220723Sbschmidt	case IWN_CMD_EDCA_PARAMS:	return "IWN_CMD_EDCA_PARAMS";
367220723Sbschmidt	case IWN_CMD_TIMING:		return "IWN_CMD_TIMING";
368220723Sbschmidt	case IWN_CMD_LINK_QUALITY:	return "IWN_CMD_LINK_QUALITY";
369220723Sbschmidt	case IWN_CMD_SET_LED:		return "IWN_CMD_SET_LED";
370220723Sbschmidt	case IWN5000_CMD_WIMAX_COEX:	return "IWN5000_CMD_WIMAX_COEX";
371220723Sbschmidt	case IWN5000_CMD_CALIB_CONFIG:	return "IWN5000_CMD_CALIB_CONFIG";
372220723Sbschmidt	case IWN5000_CMD_CALIB_RESULT:	return "IWN5000_CMD_CALIB_RESULT";
373220723Sbschmidt	case IWN5000_CMD_CALIB_COMPLETE: return "IWN5000_CMD_CALIB_COMPLETE";
374220723Sbschmidt	case IWN_CMD_SET_POWER_MODE:	return "IWN_CMD_SET_POWER_MODE";
375220723Sbschmidt	case IWN_CMD_SCAN:		return "IWN_CMD_SCAN";
376220723Sbschmidt	case IWN_CMD_SCAN_RESULTS:	return "IWN_CMD_SCAN_RESULTS";
377220723Sbschmidt	case IWN_CMD_TXPOWER:		return "IWN_CMD_TXPOWER";
378220723Sbschmidt	case IWN_CMD_TXPOWER_DBM:	return "IWN_CMD_TXPOWER_DBM";
379220723Sbschmidt	case IWN5000_CMD_TX_ANT_CONFIG:	return "IWN5000_CMD_TX_ANT_CONFIG";
380220723Sbschmidt	case IWN_CMD_BT_COEX:		return "IWN_CMD_BT_COEX";
381220723Sbschmidt	case IWN_CMD_SET_CRITICAL_TEMP:	return "IWN_CMD_SET_CRITICAL_TEMP";
382220723Sbschmidt	case IWN_CMD_SET_SENSITIVITY:	return "IWN_CMD_SET_SENSITIVITY";
383220723Sbschmidt	case IWN_CMD_PHY_CALIB:		return "IWN_CMD_PHY_CALIB";
384220723Sbschmidt	}
385220723Sbschmidt	return "UNKNOWN INTR NOTIF/CMD";
386220723Sbschmidt}
387178676Ssam#else
388178676Ssam#define DPRINTF(sc, m, fmt, ...) do { (void) sc; } while (0)
389178676Ssam#endif
390178676Ssam
391220723Sbschmidtstatic device_method_t iwn_methods[] = {
392220723Sbschmidt	/* Device interface */
393220723Sbschmidt	DEVMETHOD(device_probe,		iwn_probe),
394220723Sbschmidt	DEVMETHOD(device_attach,	iwn_attach),
395220723Sbschmidt	DEVMETHOD(device_detach,	iwn_detach),
396220723Sbschmidt	DEVMETHOD(device_shutdown,	iwn_shutdown),
397220723Sbschmidt	DEVMETHOD(device_suspend,	iwn_suspend),
398220723Sbschmidt	DEVMETHOD(device_resume,	iwn_resume),
399264944Smarius
400264944Smarius	DEVMETHOD_END
401178676Ssam};
402178676Ssam
403220723Sbschmidtstatic driver_t iwn_driver = {
404220723Sbschmidt	"iwn",
405220723Sbschmidt	iwn_methods,
406220726Sbschmidt	sizeof(struct iwn_softc)
407178676Ssam};
408220723Sbschmidtstatic devclass_t iwn_devclass;
409178676Ssam
410264944SmariusDRIVER_MODULE(iwn, pci, iwn_driver, iwn_devclass, NULL, NULL);
411220726Sbschmidt
412222543SbschmidtMODULE_VERSION(iwn, 1);
413222543Sbschmidt
414220723SbschmidtMODULE_DEPEND(iwn, firmware, 1, 1, 1);
415220723SbschmidtMODULE_DEPEND(iwn, pci, 1, 1, 1);
416220723SbschmidtMODULE_DEPEND(iwn, wlan, 1, 1, 1);
417220723Sbschmidt
418178676Ssamstatic int
419178676Ssamiwn_probe(device_t dev)
420178676Ssam{
421198429Srpaulo	const struct iwn_ident *ident;
422178676Ssam
423198429Srpaulo	for (ident = iwn_ident_table; ident->name != NULL; ident++) {
424198429Srpaulo		if (pci_get_vendor(dev) == ident->vendor &&
425198429Srpaulo		    pci_get_device(dev) == ident->device) {
426198429Srpaulo			device_set_desc(dev, ident->name);
427264947Smarius			return (BUS_PROBE_DEFAULT);
428198429Srpaulo		}
429198429Srpaulo	}
430198429Srpaulo	return ENXIO;
431178676Ssam}
432178676Ssam
433178676Ssamstatic int
434178676Ssamiwn_attach(device_t dev)
435178676Ssam{
436178676Ssam	struct iwn_softc *sc = (struct iwn_softc *)device_get_softc(dev);
437178676Ssam	struct ieee80211com *ic;
438178676Ssam	struct ifnet *ifp;
439264944Smarius	int i, error, rid;
440190526Ssam	uint8_t macaddr[IEEE80211_ADDR_LEN];
441178676Ssam
442178676Ssam	sc->sc_dev = dev;
443178676Ssam
444198429Srpaulo	/*
445198429Srpaulo	 * Get the offset of the PCI Express Capability Structure in PCI
446198429Srpaulo	 * Configuration Space.
447198429Srpaulo	 */
448219902Sjhb	error = pci_find_cap(dev, PCIY_EXPRESS, &sc->sc_cap_off);
449198429Srpaulo	if (error != 0) {
450198429Srpaulo		device_printf(dev, "PCIe capability structure not found!\n");
451198429Srpaulo		return error;
452178676Ssam	}
453178676Ssam
454198429Srpaulo	/* Clear device-specific "PCI retry timeout" register (41h). */
455178676Ssam	pci_write_config(dev, 0x41, 0, 1);
456178676Ssam
457198429Srpaulo	/* Enable bus-mastering. */
458178676Ssam	pci_enable_busmaster(dev);
459178676Ssam
460264944Smarius	rid = PCIR_BAR(0);
461264944Smarius	sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
462198429Srpaulo	    RF_ACTIVE);
463220726Sbschmidt	if (sc->mem == NULL) {
464220724Sbschmidt		device_printf(dev, "can't map mem space\n");
465198429Srpaulo		error = ENOMEM;
466178676Ssam		return error;
467178676Ssam	}
468178676Ssam	sc->sc_st = rman_get_bustag(sc->mem);
469178676Ssam	sc->sc_sh = rman_get_bushandle(sc->mem);
470220726Sbschmidt
471264944Smarius	i = 1;
472264944Smarius	rid = 0;
473264944Smarius	if (pci_alloc_msi(dev, &i) == 0)
474264944Smarius		rid = 1;
475220725Sbschmidt	/* Install interrupt handler. */
476264944Smarius	sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE |
477264944Smarius	    (rid != 0 ? 0 : RF_SHAREABLE));
478178676Ssam	if (sc->irq == NULL) {
479220724Sbschmidt		device_printf(dev, "can't map interrupt\n");
480178676Ssam		error = ENOMEM;
481198429Srpaulo		goto fail;
482178676Ssam	}
483178676Ssam
484178676Ssam	IWN_LOCK_INIT(sc);
485178676Ssam
486220728Sbschmidt	/* Read hardware revision and attach. */
487220728Sbschmidt	sc->hw_type = (IWN_READ(sc, IWN_HW_REV) >> 4) & 0xf;
488220728Sbschmidt	if (sc->hw_type == IWN_HW_REV_TYPE_4965)
489220728Sbschmidt		error = iwn4965_attach(sc, pci_get_device(dev));
490220728Sbschmidt	else
491220728Sbschmidt		error = iwn5000_attach(sc, pci_get_device(dev));
492220728Sbschmidt	if (error != 0) {
493220728Sbschmidt		device_printf(dev, "could not attach device, error %d\n",
494220728Sbschmidt		    error);
495198429Srpaulo		goto fail;
496198429Srpaulo	}
497198429Srpaulo
498220726Sbschmidt	if ((error = iwn_hw_prepare(sc)) != 0) {
499198429Srpaulo		device_printf(dev, "hardware not ready, error %d\n", error);
500178676Ssam		goto fail;
501178676Ssam	}
502178676Ssam
503198429Srpaulo	/* Allocate DMA memory for firmware transfers. */
504220726Sbschmidt	if ((error = iwn_alloc_fwmem(sc)) != 0) {
505178676Ssam		device_printf(dev,
506198429Srpaulo		    "could not allocate memory for firmware, error %d\n",
507198429Srpaulo		    error);
508178676Ssam		goto fail;
509178676Ssam	}
510178676Ssam
511198429Srpaulo	/* Allocate "Keep Warm" page. */
512220726Sbschmidt	if ((error = iwn_alloc_kw(sc)) != 0) {
513178676Ssam		device_printf(dev,
514220724Sbschmidt		    "could not allocate keep warm page, error %d\n", error);
515178676Ssam		goto fail;
516178676Ssam	}
517178676Ssam
518201209Srpaulo	/* Allocate ICT table for 5000 Series. */
519201209Srpaulo	if (sc->hw_type != IWN_HW_REV_TYPE_4965 &&
520201209Srpaulo	    (error = iwn_alloc_ict(sc)) != 0) {
521220724Sbschmidt		device_printf(dev, "could not allocate ICT table, error %d\n",
522220724Sbschmidt		    error);
523201209Srpaulo		goto fail;
524201209Srpaulo	}
525201209Srpaulo
526198429Srpaulo	/* Allocate TX scheduler "rings". */
527220726Sbschmidt	if ((error = iwn_alloc_sched(sc)) != 0) {
528178676Ssam		device_printf(dev,
529220726Sbschmidt		    "could not allocate TX scheduler rings, error %d\n", error);
530178676Ssam		goto fail;
531178676Ssam	}
532178676Ssam
533220725Sbschmidt	/* Allocate TX rings (16 on 4965AGN, 20 on >=5000). */
534220728Sbschmidt	for (i = 0; i < sc->ntxqs; i++) {
535220726Sbschmidt		if ((error = iwn_alloc_tx_ring(sc, &sc->txq[i], i)) != 0) {
536178676Ssam			device_printf(dev,
537220724Sbschmidt			    "could not allocate TX ring %d, error %d\n", i,
538220724Sbschmidt			    error);
539178676Ssam			goto fail;
540178676Ssam		}
541178676Ssam	}
542178676Ssam
543198429Srpaulo	/* Allocate RX ring. */
544220726Sbschmidt	if ((error = iwn_alloc_rx_ring(sc, &sc->rxq)) != 0) {
545220724Sbschmidt		device_printf(dev, "could not allocate RX ring, error %d\n",
546220724Sbschmidt		    error);
547178676Ssam		goto fail;
548178676Ssam	}
549178676Ssam
550198429Srpaulo	/* Clear pending interrupts. */
551198429Srpaulo	IWN_WRITE(sc, IWN_INT, 0xffffffff);
552198429Srpaulo
553178676Ssam	ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211);
554178676Ssam	if (ifp == NULL) {
555178676Ssam		device_printf(dev, "can not allocate ifnet structure\n");
556178676Ssam		goto fail;
557178676Ssam	}
558220726Sbschmidt
559178676Ssam	ic = ifp->if_l2com;
560198429Srpaulo	ic->ic_ifp = ifp;
561178676Ssam	ic->ic_phytype = IEEE80211_T_OFDM;	/* not only, but not used */
562178676Ssam	ic->ic_opmode = IEEE80211_M_STA;	/* default to BSS mode */
563178676Ssam
564198429Srpaulo	/* Set device capabilities. */
565178676Ssam	ic->ic_caps =
566178957Ssam		  IEEE80211_C_STA		/* station mode supported */
567178957Ssam		| IEEE80211_C_MONITOR		/* monitor mode supported */
568222679Sbschmidt		| IEEE80211_C_BGSCAN		/* background scanning */
569178676Ssam		| IEEE80211_C_TXPMGT		/* tx power management */
570178676Ssam		| IEEE80211_C_SHSLOT		/* short slot time supported */
571178676Ssam		| IEEE80211_C_WPA
572178676Ssam		| IEEE80211_C_SHPREAMBLE	/* short preamble supported */
573178676Ssam#if 0
574178676Ssam		| IEEE80211_C_IBSS		/* ibss/adhoc mode */
575178676Ssam#endif
576178676Ssam		| IEEE80211_C_WME		/* WME */
577178676Ssam		;
578221640Sbschmidt
579221642Sbschmidt	/* Read MAC address, channels, etc from EEPROM. */
580221642Sbschmidt	if ((error = iwn_read_eeprom(sc, macaddr)) != 0) {
581221642Sbschmidt		device_printf(dev, "could not read EEPROM, error %d\n",
582221642Sbschmidt		    error);
583221642Sbschmidt		goto fail;
584221642Sbschmidt	}
585221642Sbschmidt
586221642Sbschmidt	/* Count the number of available chains. */
587221642Sbschmidt	sc->ntxchains =
588221642Sbschmidt	    ((sc->txchainmask >> 2) & 1) +
589221642Sbschmidt	    ((sc->txchainmask >> 1) & 1) +
590221642Sbschmidt	    ((sc->txchainmask >> 0) & 1);
591221642Sbschmidt	sc->nrxchains =
592221642Sbschmidt	    ((sc->rxchainmask >> 2) & 1) +
593221642Sbschmidt	    ((sc->rxchainmask >> 1) & 1) +
594221642Sbschmidt	    ((sc->rxchainmask >> 0) & 1);
595221642Sbschmidt	if (bootverbose) {
596221642Sbschmidt		device_printf(dev, "MIMO %dT%dR, %.4s, address %6D\n",
597221642Sbschmidt		    sc->ntxchains, sc->nrxchains, sc->eeprom_domain,
598221642Sbschmidt		    macaddr, ":");
599221642Sbschmidt	}
600221642Sbschmidt
601221657Sbschmidt	if (sc->sc_flags & IWN_FLAG_HAS_11N) {
602221657Sbschmidt		ic->ic_rxstream = sc->nrxchains;
603221657Sbschmidt		ic->ic_txstream = sc->ntxchains;
604221657Sbschmidt		ic->ic_htcaps =
605221657Sbschmidt			  IEEE80211_HTCAP_SMPS_OFF	/* SMPS mode disabled */
606221657Sbschmidt			| IEEE80211_HTCAP_SHORTGI20	/* short GI in 20MHz */
607221657Sbschmidt			| IEEE80211_HTCAP_CHWIDTH40	/* 40MHz channel width*/
608221657Sbschmidt			| IEEE80211_HTCAP_SHORTGI40	/* short GI in 40MHz */
609222687Sbschmidt#ifdef notyet
610221657Sbschmidt			| IEEE80211_HTCAP_GREENFIELD
611201209Srpaulo#if IWN_RBUF_SIZE == 8192
612221657Sbschmidt			| IEEE80211_HTCAP_MAXAMSDU_7935	/* max A-MSDU length */
613221657Sbschmidt#else
614221657Sbschmidt			| IEEE80211_HTCAP_MAXAMSDU_3839	/* max A-MSDU length */
615178678Ssam#endif
616201209Srpaulo#endif
617221657Sbschmidt			/* s/w capabilities */
618221657Sbschmidt			| IEEE80211_HTC_HT		/* HT operation */
619221657Sbschmidt			| IEEE80211_HTC_AMPDU		/* tx A-MPDU */
620221657Sbschmidt#ifdef notyet
621221657Sbschmidt			| IEEE80211_HTC_AMSDU		/* tx A-MSDU */
622201209Srpaulo#endif
623221657Sbschmidt			;
624221657Sbschmidt	}
625201209Srpaulo
626178676Ssam	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
627178676Ssam	ifp->if_softc = sc;
628178676Ssam	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
629178676Ssam	ifp->if_init = iwn_init;
630178676Ssam	ifp->if_ioctl = iwn_ioctl;
631178676Ssam	ifp->if_start = iwn_start;
632207554Ssobomax	IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen);
633207554Ssobomax	ifp->if_snd.ifq_drv_maxlen = ifqmaxlen;
634178676Ssam	IFQ_SET_READY(&ifp->if_snd);
635178676Ssam
636190526Ssam	ieee80211_ifattach(ic, macaddr);
637178676Ssam	ic->ic_vap_create = iwn_vap_create;
638178676Ssam	ic->ic_vap_delete = iwn_vap_delete;
639178676Ssam	ic->ic_raw_xmit = iwn_raw_xmit;
640178676Ssam	ic->ic_node_alloc = iwn_node_alloc;
641221650Sbschmidt	sc->sc_ampdu_rx_start = ic->ic_ampdu_rx_start;
642220723Sbschmidt	ic->ic_ampdu_rx_start = iwn_ampdu_rx_start;
643221650Sbschmidt	sc->sc_ampdu_rx_stop = ic->ic_ampdu_rx_stop;
644220723Sbschmidt	ic->ic_ampdu_rx_stop = iwn_ampdu_rx_stop;
645221651Sbschmidt	sc->sc_addba_request = ic->ic_addba_request;
646221651Sbschmidt	ic->ic_addba_request = iwn_addba_request;
647221651Sbschmidt	sc->sc_addba_response = ic->ic_addba_response;
648221651Sbschmidt	ic->ic_addba_response = iwn_addba_response;
649221651Sbschmidt	sc->sc_addba_stop = ic->ic_addba_stop;
650221651Sbschmidt	ic->ic_addba_stop = iwn_ampdu_tx_stop;
651220715Sbschmidt	ic->ic_newassoc = iwn_newassoc;
652220721Sbschmidt	ic->ic_wme.wme_update = iwn_updateedca;
653201209Srpaulo	ic->ic_update_mcast = iwn_update_mcast;
654198429Srpaulo	ic->ic_scan_start = iwn_scan_start;
655198429Srpaulo	ic->ic_scan_end = iwn_scan_end;
656198429Srpaulo	ic->ic_set_channel = iwn_set_channel;
657198429Srpaulo	ic->ic_scan_curchan = iwn_scan_curchan;
658198429Srpaulo	ic->ic_scan_mindwell = iwn_scan_mindwell;
659201209Srpaulo	ic->ic_setregdomain = iwn_setregdomain;
660178676Ssam
661198429Srpaulo	iwn_radiotap_attach(sc);
662220667Sbschmidt
663220667Sbschmidt	callout_init_mtx(&sc->calib_to, &sc->sc_mtx, 0);
664220667Sbschmidt	callout_init_mtx(&sc->watchdog_to, &sc->sc_mtx, 0);
665220726Sbschmidt	TASK_INIT(&sc->sc_reinit_task, 0, iwn_hw_reset, sc);
666220726Sbschmidt	TASK_INIT(&sc->sc_radioon_task, 0, iwn_radio_on, sc);
667220726Sbschmidt	TASK_INIT(&sc->sc_radiooff_task, 0, iwn_radio_off, sc);
668220667Sbschmidt
669178676Ssam	iwn_sysctlattach(sc);
670178676Ssam
671198429Srpaulo	/*
672198429Srpaulo	 * Hook our interrupt after all initialization is complete.
673198429Srpaulo	 */
674198429Srpaulo	error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET | INTR_MPSAFE,
675178676Ssam	    NULL, iwn_intr, sc, &sc->sc_ih);
676198429Srpaulo	if (error != 0) {
677220724Sbschmidt		device_printf(dev, "can't establish interrupt, error %d\n",
678198429Srpaulo		    error);
679198429Srpaulo		goto fail;
680198429Srpaulo	}
681178676Ssam
682220724Sbschmidt	if (bootverbose)
683220724Sbschmidt		ieee80211_announce(ic);
684178676Ssam	return 0;
685178676Ssamfail:
686220635Sbschmidt	iwn_detach(dev);
687178676Ssam	return error;
688178676Ssam}
689178676Ssam
690220728Sbschmidtstatic int
691220728Sbschmidtiwn4965_attach(struct iwn_softc *sc, uint16_t pid)
692178676Ssam{
693220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
694198429Srpaulo
695220728Sbschmidt	ops->load_firmware = iwn4965_load_firmware;
696220728Sbschmidt	ops->read_eeprom = iwn4965_read_eeprom;
697220728Sbschmidt	ops->post_alive = iwn4965_post_alive;
698220728Sbschmidt	ops->nic_config = iwn4965_nic_config;
699220728Sbschmidt	ops->update_sched = iwn4965_update_sched;
700220728Sbschmidt	ops->get_temperature = iwn4965_get_temperature;
701220728Sbschmidt	ops->get_rssi = iwn4965_get_rssi;
702220728Sbschmidt	ops->set_txpower = iwn4965_set_txpower;
703220728Sbschmidt	ops->init_gains = iwn4965_init_gains;
704220728Sbschmidt	ops->set_gains = iwn4965_set_gains;
705220728Sbschmidt	ops->add_node = iwn4965_add_node;
706220728Sbschmidt	ops->tx_done = iwn4965_tx_done;
707220728Sbschmidt	ops->ampdu_tx_start = iwn4965_ampdu_tx_start;
708220728Sbschmidt	ops->ampdu_tx_stop = iwn4965_ampdu_tx_stop;
709220728Sbschmidt	sc->ntxqs = IWN4965_NTXQUEUES;
710221651Sbschmidt	sc->firstaggqueue = IWN4965_FIRSTAGGQUEUE;
711220728Sbschmidt	sc->ndmachnls = IWN4965_NDMACHNLS;
712220728Sbschmidt	sc->broadcast_id = IWN4965_ID_BROADCAST;
713220728Sbschmidt	sc->rxonsz = IWN4965_RXONSZ;
714220728Sbschmidt	sc->schedsz = IWN4965_SCHEDSZ;
715220728Sbschmidt	sc->fw_text_maxsz = IWN4965_FW_TEXT_MAXSZ;
716220728Sbschmidt	sc->fw_data_maxsz = IWN4965_FW_DATA_MAXSZ;
717220728Sbschmidt	sc->fwsz = IWN4965_FWSZ;
718220728Sbschmidt	sc->sched_txfact_addr = IWN4965_SCHED_TXFACT;
719220728Sbschmidt	sc->limits = &iwn4965_sensitivity_limits;
720220728Sbschmidt	sc->fwname = "iwn4965fw";
721220728Sbschmidt	/* Override chains masks, ROM is known to be broken. */
722220728Sbschmidt	sc->txchainmask = IWN_ANT_AB;
723220728Sbschmidt	sc->rxchainmask = IWN_ANT_ABC;
724220728Sbschmidt
725220728Sbschmidt	return 0;
726220728Sbschmidt}
727220728Sbschmidt
728220728Sbschmidtstatic int
729220728Sbschmidtiwn5000_attach(struct iwn_softc *sc, uint16_t pid)
730220728Sbschmidt{
731220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
732220728Sbschmidt
733220728Sbschmidt	ops->load_firmware = iwn5000_load_firmware;
734220728Sbschmidt	ops->read_eeprom = iwn5000_read_eeprom;
735220728Sbschmidt	ops->post_alive = iwn5000_post_alive;
736220728Sbschmidt	ops->nic_config = iwn5000_nic_config;
737220728Sbschmidt	ops->update_sched = iwn5000_update_sched;
738220728Sbschmidt	ops->get_temperature = iwn5000_get_temperature;
739220728Sbschmidt	ops->get_rssi = iwn5000_get_rssi;
740220728Sbschmidt	ops->set_txpower = iwn5000_set_txpower;
741220728Sbschmidt	ops->init_gains = iwn5000_init_gains;
742220728Sbschmidt	ops->set_gains = iwn5000_set_gains;
743220728Sbschmidt	ops->add_node = iwn5000_add_node;
744220728Sbschmidt	ops->tx_done = iwn5000_tx_done;
745220728Sbschmidt	ops->ampdu_tx_start = iwn5000_ampdu_tx_start;
746220728Sbschmidt	ops->ampdu_tx_stop = iwn5000_ampdu_tx_stop;
747220728Sbschmidt	sc->ntxqs = IWN5000_NTXQUEUES;
748221651Sbschmidt	sc->firstaggqueue = IWN5000_FIRSTAGGQUEUE;
749220728Sbschmidt	sc->ndmachnls = IWN5000_NDMACHNLS;
750220728Sbschmidt	sc->broadcast_id = IWN5000_ID_BROADCAST;
751220728Sbschmidt	sc->rxonsz = IWN5000_RXONSZ;
752220728Sbschmidt	sc->schedsz = IWN5000_SCHEDSZ;
753220728Sbschmidt	sc->fw_text_maxsz = IWN5000_FW_TEXT_MAXSZ;
754220728Sbschmidt	sc->fw_data_maxsz = IWN5000_FW_DATA_MAXSZ;
755220728Sbschmidt	sc->fwsz = IWN5000_FWSZ;
756220728Sbschmidt	sc->sched_txfact_addr = IWN5000_SCHED_TXFACT;
757220866Sbschmidt	sc->reset_noise_gain = IWN5000_PHY_CALIB_RESET_NOISE_GAIN;
758220866Sbschmidt	sc->noise_gain = IWN5000_PHY_CALIB_NOISE_GAIN;
759220728Sbschmidt
760198429Srpaulo	switch (sc->hw_type) {
761198429Srpaulo	case IWN_HW_REV_TYPE_5100:
762201209Srpaulo		sc->limits = &iwn5000_sensitivity_limits;
763198439Srpaulo		sc->fwname = "iwn5000fw";
764220727Sbschmidt		/* Override chains masks, ROM is known to be broken. */
765201209Srpaulo		sc->txchainmask = IWN_ANT_B;
766201209Srpaulo		sc->rxchainmask = IWN_ANT_AB;
767198429Srpaulo		break;
768198429Srpaulo	case IWN_HW_REV_TYPE_5150:
769201209Srpaulo		sc->limits = &iwn5150_sensitivity_limits;
770198439Srpaulo		sc->fwname = "iwn5150fw";
771198429Srpaulo		break;
772198429Srpaulo	case IWN_HW_REV_TYPE_5300:
773198429Srpaulo	case IWN_HW_REV_TYPE_5350:
774201209Srpaulo		sc->limits = &iwn5000_sensitivity_limits;
775198439Srpaulo		sc->fwname = "iwn5000fw";
776198429Srpaulo		break;
777198429Srpaulo	case IWN_HW_REV_TYPE_1000:
778206444Sbschmidt		sc->limits = &iwn1000_sensitivity_limits;
779198439Srpaulo		sc->fwname = "iwn1000fw";
780198429Srpaulo		break;
781198429Srpaulo	case IWN_HW_REV_TYPE_6000:
782201209Srpaulo		sc->limits = &iwn6000_sensitivity_limits;
783198439Srpaulo		sc->fwname = "iwn6000fw";
784220728Sbschmidt		if (pid == 0x422c || pid == 0x4239) {
785201209Srpaulo			sc->sc_flags |= IWN_FLAG_INTERNAL_PA;
786220727Sbschmidt			/* Override chains masks, ROM is known to be broken. */
787201209Srpaulo			sc->txchainmask = IWN_ANT_BC;
788201209Srpaulo			sc->rxchainmask = IWN_ANT_BC;
789201209Srpaulo		}
790198429Srpaulo		break;
791198429Srpaulo	case IWN_HW_REV_TYPE_6050:
792201209Srpaulo		sc->limits = &iwn6000_sensitivity_limits;
793210109Sbschmidt		sc->fwname = "iwn6050fw";
794220867Sbschmidt		/* Override chains masks, ROM is known to be broken. */
795220867Sbschmidt		sc->txchainmask = IWN_ANT_AB;
796220867Sbschmidt		sc->rxchainmask = IWN_ANT_AB;
797198429Srpaulo		break;
798210109Sbschmidt	case IWN_HW_REV_TYPE_6005:
799210109Sbschmidt		sc->limits = &iwn6000_sensitivity_limits;
800220894Sbschmidt		if (pid != 0x0082 && pid != 0x0085) {
801220894Sbschmidt			sc->fwname = "iwn6000g2bfw";
802220891Sbschmidt			sc->sc_flags |= IWN_FLAG_ADV_BTCOEX;
803220894Sbschmidt		} else
804220894Sbschmidt			sc->fwname = "iwn6000g2afw";
805210109Sbschmidt		break;
806198429Srpaulo	default:
807198429Srpaulo		device_printf(sc->sc_dev, "adapter type %d not supported\n",
808198429Srpaulo		    sc->hw_type);
809220728Sbschmidt		return ENOTSUP;
810198429Srpaulo	}
811220728Sbschmidt	return 0;
812178676Ssam}
813178676Ssam
814178676Ssam/*
815198429Srpaulo * Attach the interface to 802.11 radiotap.
816178676Ssam */
817206477Sbschmidtstatic void
818198429Srpauloiwn_radiotap_attach(struct iwn_softc *sc)
819178676Ssam{
820178676Ssam	struct ifnet *ifp = sc->sc_ifp;
821178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
822178676Ssam
823198429Srpaulo	ieee80211_radiotap_attach(ic,
824198429Srpaulo	    &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap),
825198429Srpaulo		IWN_TX_RADIOTAP_PRESENT,
826198429Srpaulo	    &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap),
827198429Srpaulo		IWN_RX_RADIOTAP_PRESENT);
828178676Ssam}
829178676Ssam
830220723Sbschmidtstatic void
831220723Sbschmidtiwn_sysctlattach(struct iwn_softc *sc)
832220723Sbschmidt{
833220723Sbschmidt	struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev);
834220723Sbschmidt	struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev);
835220723Sbschmidt
836220723Sbschmidt#ifdef IWN_DEBUG
837220723Sbschmidt	sc->sc_debug = 0;
838220723Sbschmidt	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
839220723Sbschmidt	    "debug", CTLFLAG_RW, &sc->sc_debug, 0, "control debugging printfs");
840220723Sbschmidt#endif
841220723Sbschmidt}
842220723Sbschmidt
843178676Ssamstatic struct ieee80211vap *
844234753Sdimiwn_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit,
845234753Sdim    enum ieee80211_opmode opmode, int flags,
846220726Sbschmidt    const uint8_t bssid[IEEE80211_ADDR_LEN],
847220726Sbschmidt    const uint8_t mac[IEEE80211_ADDR_LEN])
848178676Ssam{
849178676Ssam	struct iwn_vap *ivp;
850178676Ssam	struct ieee80211vap *vap;
851178676Ssam
852178676Ssam	if (!TAILQ_EMPTY(&ic->ic_vaps))		/* only one at a time */
853178676Ssam		return NULL;
854178676Ssam	ivp = (struct iwn_vap *) malloc(sizeof(struct iwn_vap),
855178676Ssam	    M_80211_VAP, M_NOWAIT | M_ZERO);
856178676Ssam	if (ivp == NULL)
857178676Ssam		return NULL;
858178676Ssam	vap = &ivp->iv_vap;
859178676Ssam	ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac);
860178676Ssam	vap->iv_bmissthreshold = 10;		/* override default */
861198429Srpaulo	/* Override with driver methods. */
862178676Ssam	ivp->iv_newstate = vap->iv_newstate;
863178676Ssam	vap->iv_newstate = iwn_newstate;
864178676Ssam
865206358Srpaulo	ieee80211_ratectl_init(vap);
866198429Srpaulo	/* Complete setup. */
867206476Sbschmidt	ieee80211_vap_attach(vap, iwn_media_change, ieee80211_media_status);
868178676Ssam	ic->ic_opmode = opmode;
869178676Ssam	return vap;
870178676Ssam}
871178676Ssam
872178676Ssamstatic void
873178676Ssamiwn_vap_delete(struct ieee80211vap *vap)
874178676Ssam{
875178676Ssam	struct iwn_vap *ivp = IWN_VAP(vap);
876178676Ssam
877206358Srpaulo	ieee80211_ratectl_deinit(vap);
878178676Ssam	ieee80211_vap_detach(vap);
879178676Ssam	free(ivp, M_80211_VAP);
880178676Ssam}
881178676Ssam
882206477Sbschmidtstatic int
883220635Sbschmidtiwn_detach(device_t dev)
884178676Ssam{
885178676Ssam	struct iwn_softc *sc = device_get_softc(dev);
886198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
887198429Srpaulo	struct ieee80211com *ic;
888220721Sbschmidt	int qid;
889178676Ssam
890198429Srpaulo	if (ifp != NULL) {
891198429Srpaulo		ic = ifp->if_l2com;
892198429Srpaulo
893198429Srpaulo		ieee80211_draintask(ic, &sc->sc_reinit_task);
894198429Srpaulo		ieee80211_draintask(ic, &sc->sc_radioon_task);
895198429Srpaulo		ieee80211_draintask(ic, &sc->sc_radiooff_task);
896198429Srpaulo
897198429Srpaulo		iwn_stop(sc);
898220667Sbschmidt		callout_drain(&sc->watchdog_to);
899220667Sbschmidt		callout_drain(&sc->calib_to);
900198429Srpaulo		ieee80211_ifdetach(ic);
901198429Srpaulo	}
902198429Srpaulo
903220725Sbschmidt	/* Uninstall interrupt handler. */
904220723Sbschmidt	if (sc->irq != NULL) {
905220723Sbschmidt		bus_teardown_intr(dev, sc->irq, sc->sc_ih);
906264944Smarius		bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(sc->irq),
907264944Smarius		    sc->irq);
908264944Smarius		pci_release_msi(dev);
909220723Sbschmidt	}
910220723Sbschmidt
911201209Srpaulo	/* Free DMA resources. */
912198429Srpaulo	iwn_free_rx_ring(sc, &sc->rxq);
913220728Sbschmidt	for (qid = 0; qid < sc->ntxqs; qid++)
914220728Sbschmidt		iwn_free_tx_ring(sc, &sc->txq[qid]);
915198429Srpaulo	iwn_free_sched(sc);
916198429Srpaulo	iwn_free_kw(sc);
917201209Srpaulo	if (sc->ict != NULL)
918201209Srpaulo		iwn_free_ict(sc);
919198429Srpaulo	iwn_free_fwmem(sc);
920198429Srpaulo
921198429Srpaulo	if (sc->mem != NULL)
922264944Smarius		bus_release_resource(dev, SYS_RES_MEMORY,
923264944Smarius		    rman_get_rid(sc->mem), sc->mem);
924198429Srpaulo
925198429Srpaulo	if (ifp != NULL)
926198429Srpaulo		if_free(ifp);
927198429Srpaulo
928198429Srpaulo	IWN_LOCK_DESTROY(sc);
929178676Ssam	return 0;
930178676Ssam}
931178676Ssam
932178676Ssamstatic int
933220723Sbschmidtiwn_shutdown(device_t dev)
934220723Sbschmidt{
935220723Sbschmidt	struct iwn_softc *sc = device_get_softc(dev);
936220723Sbschmidt
937220723Sbschmidt	iwn_stop(sc);
938220723Sbschmidt	return 0;
939220723Sbschmidt}
940220723Sbschmidt
941220723Sbschmidtstatic int
942220723Sbschmidtiwn_suspend(device_t dev)
943220723Sbschmidt{
944220723Sbschmidt	struct iwn_softc *sc = device_get_softc(dev);
945234570Sbschmidt	struct ieee80211com *ic = sc->sc_ifp->if_l2com;
946220723Sbschmidt
947234570Sbschmidt	ieee80211_suspend_all(ic);
948220723Sbschmidt	return 0;
949220723Sbschmidt}
950220723Sbschmidt
951220723Sbschmidtstatic int
952220723Sbschmidtiwn_resume(device_t dev)
953220723Sbschmidt{
954220723Sbschmidt	struct iwn_softc *sc = device_get_softc(dev);
955234570Sbschmidt	struct ieee80211com *ic = sc->sc_ifp->if_l2com;
956220723Sbschmidt
957220723Sbschmidt	/* Clear device-specific "PCI retry timeout" register (41h). */
958220723Sbschmidt	pci_write_config(dev, 0x41, 0, 1);
959220723Sbschmidt
960234570Sbschmidt	ieee80211_resume_all(ic);
961220723Sbschmidt	return 0;
962220723Sbschmidt}
963220723Sbschmidt
964220723Sbschmidtstatic int
965198429Srpauloiwn_nic_lock(struct iwn_softc *sc)
966178676Ssam{
967198429Srpaulo	int ntries;
968178676Ssam
969198429Srpaulo	/* Request exclusive access to NIC. */
970198429Srpaulo	IWN_SETBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_MAC_ACCESS_REQ);
971178676Ssam
972198429Srpaulo	/* Spin until we actually get the lock. */
973198429Srpaulo	for (ntries = 0; ntries < 1000; ntries++) {
974198429Srpaulo		if ((IWN_READ(sc, IWN_GP_CNTRL) &
975220726Sbschmidt		     (IWN_GP_CNTRL_MAC_ACCESS_ENA | IWN_GP_CNTRL_SLEEP)) ==
976198429Srpaulo		    IWN_GP_CNTRL_MAC_ACCESS_ENA)
977198429Srpaulo			return 0;
978198429Srpaulo		DELAY(10);
979198429Srpaulo	}
980198429Srpaulo	return ETIMEDOUT;
981198429Srpaulo}
982198429Srpaulo
983198429Srpaulostatic __inline void
984198429Srpauloiwn_nic_unlock(struct iwn_softc *sc)
985198429Srpaulo{
986198429Srpaulo	IWN_CLRBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_MAC_ACCESS_REQ);
987198429Srpaulo}
988198429Srpaulo
989198429Srpaulostatic __inline uint32_t
990198429Srpauloiwn_prph_read(struct iwn_softc *sc, uint32_t addr)
991198429Srpaulo{
992198429Srpaulo	IWN_WRITE(sc, IWN_PRPH_RADDR, IWN_PRPH_DWORD | addr);
993201209Srpaulo	IWN_BARRIER_READ_WRITE(sc);
994198429Srpaulo	return IWN_READ(sc, IWN_PRPH_RDATA);
995198429Srpaulo}
996198429Srpaulo
997198429Srpaulostatic __inline void
998198429Srpauloiwn_prph_write(struct iwn_softc *sc, uint32_t addr, uint32_t data)
999198429Srpaulo{
1000198429Srpaulo	IWN_WRITE(sc, IWN_PRPH_WADDR, IWN_PRPH_DWORD | addr);
1001201209Srpaulo	IWN_BARRIER_WRITE(sc);
1002198429Srpaulo	IWN_WRITE(sc, IWN_PRPH_WDATA, data);
1003198429Srpaulo}
1004198429Srpaulo
1005198429Srpaulostatic __inline void
1006198429Srpauloiwn_prph_setbits(struct iwn_softc *sc, uint32_t addr, uint32_t mask)
1007198429Srpaulo{
1008198429Srpaulo	iwn_prph_write(sc, addr, iwn_prph_read(sc, addr) | mask);
1009198429Srpaulo}
1010198429Srpaulo
1011198429Srpaulostatic __inline void
1012198429Srpauloiwn_prph_clrbits(struct iwn_softc *sc, uint32_t addr, uint32_t mask)
1013198429Srpaulo{
1014198429Srpaulo	iwn_prph_write(sc, addr, iwn_prph_read(sc, addr) & ~mask);
1015198429Srpaulo}
1016198429Srpaulo
1017198429Srpaulostatic __inline void
1018198429Srpauloiwn_prph_write_region_4(struct iwn_softc *sc, uint32_t addr,
1019198429Srpaulo    const uint32_t *data, int count)
1020198429Srpaulo{
1021198429Srpaulo	for (; count > 0; count--, data++, addr += 4)
1022198429Srpaulo		iwn_prph_write(sc, addr, *data);
1023198429Srpaulo}
1024198429Srpaulo
1025198429Srpaulostatic __inline uint32_t
1026198429Srpauloiwn_mem_read(struct iwn_softc *sc, uint32_t addr)
1027198429Srpaulo{
1028198429Srpaulo	IWN_WRITE(sc, IWN_MEM_RADDR, addr);
1029201209Srpaulo	IWN_BARRIER_READ_WRITE(sc);
1030198429Srpaulo	return IWN_READ(sc, IWN_MEM_RDATA);
1031198429Srpaulo}
1032198429Srpaulo
1033198429Srpaulostatic __inline void
1034198429Srpauloiwn_mem_write(struct iwn_softc *sc, uint32_t addr, uint32_t data)
1035198429Srpaulo{
1036198429Srpaulo	IWN_WRITE(sc, IWN_MEM_WADDR, addr);
1037201209Srpaulo	IWN_BARRIER_WRITE(sc);
1038198429Srpaulo	IWN_WRITE(sc, IWN_MEM_WDATA, data);
1039198429Srpaulo}
1040198429Srpaulo
1041198429Srpaulostatic __inline void
1042198429Srpauloiwn_mem_write_2(struct iwn_softc *sc, uint32_t addr, uint16_t data)
1043198429Srpaulo{
1044198429Srpaulo	uint32_t tmp;
1045198429Srpaulo
1046198429Srpaulo	tmp = iwn_mem_read(sc, addr & ~3);
1047198429Srpaulo	if (addr & 3)
1048198429Srpaulo		tmp = (tmp & 0x0000ffff) | data << 16;
1049198429Srpaulo	else
1050198429Srpaulo		tmp = (tmp & 0xffff0000) | data;
1051198429Srpaulo	iwn_mem_write(sc, addr & ~3, tmp);
1052198429Srpaulo}
1053198429Srpaulo
1054198429Srpaulostatic __inline void
1055198429Srpauloiwn_mem_read_region_4(struct iwn_softc *sc, uint32_t addr, uint32_t *data,
1056198429Srpaulo    int count)
1057198429Srpaulo{
1058198429Srpaulo	for (; count > 0; count--, addr += 4)
1059198429Srpaulo		*data++ = iwn_mem_read(sc, addr);
1060198429Srpaulo}
1061198429Srpaulo
1062198429Srpaulostatic __inline void
1063198429Srpauloiwn_mem_set_region_4(struct iwn_softc *sc, uint32_t addr, uint32_t val,
1064198429Srpaulo    int count)
1065198429Srpaulo{
1066198429Srpaulo	for (; count > 0; count--, addr += 4)
1067198429Srpaulo		iwn_mem_write(sc, addr, val);
1068198429Srpaulo}
1069198429Srpaulo
1070206477Sbschmidtstatic int
1071198429Srpauloiwn_eeprom_lock(struct iwn_softc *sc)
1072198429Srpaulo{
1073198429Srpaulo	int i, ntries;
1074198429Srpaulo
1075198429Srpaulo	for (i = 0; i < 100; i++) {
1076198429Srpaulo		/* Request exclusive access to EEPROM. */
1077198429Srpaulo		IWN_SETBITS(sc, IWN_HW_IF_CONFIG,
1078198429Srpaulo		    IWN_HW_IF_CONFIG_EEPROM_LOCKED);
1079198429Srpaulo
1080198429Srpaulo		/* Spin until we actually get the lock. */
1081198429Srpaulo		for (ntries = 0; ntries < 100; ntries++) {
1082198429Srpaulo			if (IWN_READ(sc, IWN_HW_IF_CONFIG) &
1083198429Srpaulo			    IWN_HW_IF_CONFIG_EEPROM_LOCKED)
1084198429Srpaulo				return 0;
1085198429Srpaulo			DELAY(10);
1086198429Srpaulo		}
1087198429Srpaulo	}
1088198429Srpaulo	return ETIMEDOUT;
1089198429Srpaulo}
1090198429Srpaulo
1091198429Srpaulostatic __inline void
1092198429Srpauloiwn_eeprom_unlock(struct iwn_softc *sc)
1093198429Srpaulo{
1094198429Srpaulo	IWN_CLRBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_EEPROM_LOCKED);
1095198429Srpaulo}
1096198429Srpaulo
1097198429Srpaulo/*
1098198429Srpaulo * Initialize access by host to One Time Programmable ROM.
1099198429Srpaulo * NB: This kind of ROM can be found on 1000 or 6000 Series only.
1100198429Srpaulo */
1101206477Sbschmidtstatic int
1102198429Srpauloiwn_init_otprom(struct iwn_softc *sc)
1103198429Srpaulo{
1104203934Sbschmidt	uint16_t prev, base, next;
1105201209Srpaulo	int count, error;
1106198429Srpaulo
1107201209Srpaulo	/* Wait for clock stabilization before accessing prph. */
1108220726Sbschmidt	if ((error = iwn_clock_wait(sc)) != 0)
1109198429Srpaulo		return error;
1110198429Srpaulo
1111220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
1112198429Srpaulo		return error;
1113198429Srpaulo	iwn_prph_setbits(sc, IWN_APMG_PS, IWN_APMG_PS_RESET_REQ);
1114198429Srpaulo	DELAY(5);
1115198429Srpaulo	iwn_prph_clrbits(sc, IWN_APMG_PS, IWN_APMG_PS_RESET_REQ);
1116198429Srpaulo	iwn_nic_unlock(sc);
1117198429Srpaulo
1118201209Srpaulo	/* Set auto clock gate disable bit for HW with OTP shadow RAM. */
1119201209Srpaulo	if (sc->hw_type != IWN_HW_REV_TYPE_1000) {
1120201209Srpaulo		IWN_SETBITS(sc, IWN_DBG_LINK_PWR_MGMT,
1121201209Srpaulo		    IWN_RESET_LINK_PWR_MGMT_DIS);
1122201209Srpaulo	}
1123198429Srpaulo	IWN_CLRBITS(sc, IWN_EEPROM_GP, IWN_EEPROM_GP_IF_OWNER);
1124198429Srpaulo	/* Clear ECC status. */
1125198429Srpaulo	IWN_SETBITS(sc, IWN_OTP_GP,
1126198429Srpaulo	    IWN_OTP_GP_ECC_CORR_STTS | IWN_OTP_GP_ECC_UNCORR_STTS);
1127198429Srpaulo
1128201209Srpaulo	/*
1129203934Sbschmidt	 * Find the block before last block (contains the EEPROM image)
1130203934Sbschmidt	 * for HW without OTP shadow RAM.
1131201209Srpaulo	 */
1132201209Srpaulo	if (sc->hw_type == IWN_HW_REV_TYPE_1000) {
1133201209Srpaulo		/* Switch to absolute addressing mode. */
1134201209Srpaulo		IWN_CLRBITS(sc, IWN_OTP_GP, IWN_OTP_GP_RELATIVE_ACCESS);
1135203934Sbschmidt		base = prev = 0;
1136201209Srpaulo		for (count = 0; count < IWN1000_OTP_NBLOCKS; count++) {
1137201209Srpaulo			error = iwn_read_prom_data(sc, base, &next, 2);
1138201209Srpaulo			if (error != 0)
1139201209Srpaulo				return error;
1140201209Srpaulo			if (next == 0)	/* End of linked-list. */
1141201209Srpaulo				break;
1142203934Sbschmidt			prev = base;
1143201209Srpaulo			base = le16toh(next);
1144201209Srpaulo		}
1145203934Sbschmidt		if (count == 0 || count == IWN1000_OTP_NBLOCKS)
1146201209Srpaulo			return EIO;
1147201209Srpaulo		/* Skip "next" word. */
1148203934Sbschmidt		sc->prom_base = prev + 1;
1149201209Srpaulo	}
1150178676Ssam	return 0;
1151178676Ssam}
1152178676Ssam
1153206477Sbschmidtstatic int
1154198429Srpauloiwn_read_prom_data(struct iwn_softc *sc, uint32_t addr, void *data, int count)
1155198429Srpaulo{
1156220723Sbschmidt	uint8_t *out = data;
1157198429Srpaulo	uint32_t val, tmp;
1158198429Srpaulo	int ntries;
1159198429Srpaulo
1160201209Srpaulo	addr += sc->prom_base;
1161198429Srpaulo	for (; count > 0; count -= 2, addr++) {
1162198429Srpaulo		IWN_WRITE(sc, IWN_EEPROM, addr << 2);
1163201209Srpaulo		for (ntries = 0; ntries < 10; ntries++) {
1164198429Srpaulo			val = IWN_READ(sc, IWN_EEPROM);
1165198429Srpaulo			if (val & IWN_EEPROM_READ_VALID)
1166198429Srpaulo				break;
1167198429Srpaulo			DELAY(5);
1168198429Srpaulo		}
1169201209Srpaulo		if (ntries == 10) {
1170198429Srpaulo			device_printf(sc->sc_dev,
1171198429Srpaulo			    "timeout reading ROM at 0x%x\n", addr);
1172198429Srpaulo			return ETIMEDOUT;
1173198429Srpaulo		}
1174198429Srpaulo		if (sc->sc_flags & IWN_FLAG_HAS_OTPROM) {
1175198429Srpaulo			/* OTPROM, check for ECC errors. */
1176198429Srpaulo			tmp = IWN_READ(sc, IWN_OTP_GP);
1177198429Srpaulo			if (tmp & IWN_OTP_GP_ECC_UNCORR_STTS) {
1178198429Srpaulo				device_printf(sc->sc_dev,
1179198429Srpaulo				    "OTPROM ECC error at 0x%x\n", addr);
1180198429Srpaulo				return EIO;
1181198429Srpaulo			}
1182198429Srpaulo			if (tmp & IWN_OTP_GP_ECC_CORR_STTS) {
1183198429Srpaulo				/* Correctable ECC error, clear bit. */
1184198429Srpaulo				IWN_SETBITS(sc, IWN_OTP_GP,
1185198429Srpaulo				    IWN_OTP_GP_ECC_CORR_STTS);
1186198429Srpaulo			}
1187198429Srpaulo		}
1188198429Srpaulo		*out++ = val >> 16;
1189198429Srpaulo		if (count > 1)
1190198429Srpaulo			*out++ = val >> 24;
1191198429Srpaulo	}
1192198429Srpaulo	return 0;
1193198429Srpaulo}
1194198429Srpaulo
1195178676Ssamstatic void
1196178676Ssamiwn_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
1197178676Ssam{
1198198429Srpaulo	if (error != 0)
1199198429Srpaulo		return;
1200198429Srpaulo	KASSERT(nsegs == 1, ("too many DMA segments, %d should be 1", nsegs));
1201198429Srpaulo	*(bus_addr_t *)arg = segs[0].ds_addr;
1202178676Ssam}
1203178676Ssam
1204198429Srpaulostatic int
1205178676Ssamiwn_dma_contig_alloc(struct iwn_softc *sc, struct iwn_dma_info *dma,
1206220691Sbschmidt    void **kvap, bus_size_t size, bus_size_t alignment)
1207178676Ssam{
1208198429Srpaulo	int error;
1209178676Ssam
1210220723Sbschmidt	dma->tag = NULL;
1211178676Ssam	dma->size = size;
1212178676Ssam
1213198429Srpaulo	error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), alignment,
1214178676Ssam	    0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, size,
1215220691Sbschmidt	    1, size, BUS_DMA_NOWAIT, NULL, NULL, &dma->tag);
1216220711Sbschmidt	if (error != 0)
1217178676Ssam		goto fail;
1218220711Sbschmidt
1219178676Ssam	error = bus_dmamem_alloc(dma->tag, (void **)&dma->vaddr,
1220220691Sbschmidt	    BUS_DMA_NOWAIT | BUS_DMA_ZERO | BUS_DMA_COHERENT, &dma->map);
1221220711Sbschmidt	if (error != 0)
1222178676Ssam		goto fail;
1223220711Sbschmidt
1224220691Sbschmidt	error = bus_dmamap_load(dma->tag, dma->map, dma->vaddr, size,
1225220691Sbschmidt	    iwn_dma_map_addr, &dma->paddr, BUS_DMA_NOWAIT);
1226220711Sbschmidt	if (error != 0)
1227178676Ssam		goto fail;
1228178676Ssam
1229220704Sbschmidt	bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
1230220704Sbschmidt
1231178676Ssam	if (kvap != NULL)
1232178676Ssam		*kvap = dma->vaddr;
1233220726Sbschmidt
1234178676Ssam	return 0;
1235220726Sbschmidt
1236220726Sbschmidtfail:	iwn_dma_contig_free(dma);
1237178676Ssam	return error;
1238178676Ssam}
1239178676Ssam
1240206477Sbschmidtstatic void
1241178676Ssamiwn_dma_contig_free(struct iwn_dma_info *dma)
1242178676Ssam{
1243220701Sbschmidt	if (dma->map != NULL) {
1244220701Sbschmidt		if (dma->vaddr != NULL) {
1245220701Sbschmidt			bus_dmamap_sync(dma->tag, dma->map,
1246220701Sbschmidt			    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
1247220701Sbschmidt			bus_dmamap_unload(dma->tag, dma->map);
1248178676Ssam			bus_dmamem_free(dma->tag, &dma->vaddr, dma->map);
1249220701Sbschmidt			dma->vaddr = NULL;
1250178676Ssam		}
1251220701Sbschmidt		bus_dmamap_destroy(dma->tag, dma->map);
1252220701Sbschmidt		dma->map = NULL;
1253220701Sbschmidt	}
1254220701Sbschmidt	if (dma->tag != NULL) {
1255178676Ssam		bus_dma_tag_destroy(dma->tag);
1256220701Sbschmidt		dma->tag = NULL;
1257178676Ssam	}
1258178676Ssam}
1259178676Ssam
1260206477Sbschmidtstatic int
1261198429Srpauloiwn_alloc_sched(struct iwn_softc *sc)
1262178676Ssam{
1263198429Srpaulo	/* TX scheduler rings must be aligned on a 1KB boundary. */
1264220691Sbschmidt	return iwn_dma_contig_alloc(sc, &sc->sched_dma, (void **)&sc->sched,
1265220728Sbschmidt	    sc->schedsz, 1024);
1266178676Ssam}
1267178676Ssam
1268206477Sbschmidtstatic void
1269198429Srpauloiwn_free_sched(struct iwn_softc *sc)
1270178676Ssam{
1271198429Srpaulo	iwn_dma_contig_free(&sc->sched_dma);
1272178676Ssam}
1273178676Ssam
1274206477Sbschmidtstatic int
1275178676Ssamiwn_alloc_kw(struct iwn_softc *sc)
1276178676Ssam{
1277198429Srpaulo	/* "Keep Warm" page must be aligned on a 4KB boundary. */
1278220691Sbschmidt	return iwn_dma_contig_alloc(sc, &sc->kw_dma, NULL, 4096, 4096);
1279178676Ssam}
1280178676Ssam
1281206477Sbschmidtstatic void
1282178676Ssamiwn_free_kw(struct iwn_softc *sc)
1283178676Ssam{
1284178676Ssam	iwn_dma_contig_free(&sc->kw_dma);
1285178676Ssam}
1286178676Ssam
1287206477Sbschmidtstatic int
1288201209Srpauloiwn_alloc_ict(struct iwn_softc *sc)
1289201209Srpaulo{
1290201209Srpaulo	/* ICT table must be aligned on a 4KB boundary. */
1291220691Sbschmidt	return iwn_dma_contig_alloc(sc, &sc->ict_dma, (void **)&sc->ict,
1292220691Sbschmidt	    IWN_ICT_SIZE, 4096);
1293201209Srpaulo}
1294201209Srpaulo
1295206477Sbschmidtstatic void
1296201209Srpauloiwn_free_ict(struct iwn_softc *sc)
1297201209Srpaulo{
1298201209Srpaulo	iwn_dma_contig_free(&sc->ict_dma);
1299201209Srpaulo}
1300201209Srpaulo
1301206477Sbschmidtstatic int
1302178676Ssamiwn_alloc_fwmem(struct iwn_softc *sc)
1303178676Ssam{
1304198429Srpaulo	/* Must be aligned on a 16-byte boundary. */
1305220728Sbschmidt	return iwn_dma_contig_alloc(sc, &sc->fw_dma, NULL, sc->fwsz, 16);
1306178676Ssam}
1307178676Ssam
1308206477Sbschmidtstatic void
1309178676Ssamiwn_free_fwmem(struct iwn_softc *sc)
1310178676Ssam{
1311178676Ssam	iwn_dma_contig_free(&sc->fw_dma);
1312178676Ssam}
1313178676Ssam
1314206477Sbschmidtstatic int
1315178676Ssamiwn_alloc_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring)
1316178676Ssam{
1317198429Srpaulo	bus_size_t size;
1318178676Ssam	int i, error;
1319178676Ssam
1320178676Ssam	ring->cur = 0;
1321178676Ssam
1322198429Srpaulo	/* Allocate RX descriptors (256-byte aligned). */
1323198429Srpaulo	size = IWN_RX_RING_COUNT * sizeof (uint32_t);
1324220691Sbschmidt	error = iwn_dma_contig_alloc(sc, &ring->desc_dma, (void **)&ring->desc,
1325220691Sbschmidt	    size, 256);
1326178676Ssam	if (error != 0) {
1327178676Ssam		device_printf(sc->sc_dev,
1328220711Sbschmidt		    "%s: could not allocate RX ring DMA memory, error %d\n",
1329178676Ssam		    __func__, error);
1330178676Ssam		goto fail;
1331178676Ssam	}
1332178676Ssam
1333220702Sbschmidt	/* Allocate RX status area (16-byte aligned). */
1334220702Sbschmidt	error = iwn_dma_contig_alloc(sc, &ring->stat_dma, (void **)&ring->stat,
1335220702Sbschmidt	    sizeof (struct iwn_rx_status), 16);
1336198429Srpaulo	if (error != 0) {
1337198429Srpaulo		device_printf(sc->sc_dev,
1338220711Sbschmidt		    "%s: could not allocate RX status DMA memory, error %d\n",
1339178676Ssam		    __func__, error);
1340198429Srpaulo		goto fail;
1341198429Srpaulo	}
1342178676Ssam
1343220702Sbschmidt	/* Create RX buffer DMA tag. */
1344220702Sbschmidt	error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0,
1345220702Sbschmidt	    BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
1346220702Sbschmidt	    IWN_RBUF_SIZE, 1, IWN_RBUF_SIZE, BUS_DMA_NOWAIT, NULL, NULL,
1347220702Sbschmidt	    &ring->data_dmat);
1348198429Srpaulo	if (error != 0) {
1349198429Srpaulo		device_printf(sc->sc_dev,
1350220711Sbschmidt		    "%s: could not create RX buf DMA tag, error %d\n",
1351198429Srpaulo		    __func__, error);
1352198429Srpaulo		goto fail;
1353198429Srpaulo	}
1354198429Srpaulo
1355178676Ssam	/*
1356198429Srpaulo	 * Allocate and map RX buffers.
1357178676Ssam	 */
1358178676Ssam	for (i = 0; i < IWN_RX_RING_COUNT; i++) {
1359178676Ssam		struct iwn_rx_data *data = &ring->data[i];
1360178676Ssam		bus_addr_t paddr;
1361178676Ssam
1362201209Srpaulo		error = bus_dmamap_create(ring->data_dmat, 0, &data->map);
1363178676Ssam		if (error != 0) {
1364178676Ssam			device_printf(sc->sc_dev,
1365220711Sbschmidt			    "%s: could not create RX buf DMA map, error %d\n",
1366178676Ssam			    __func__, error);
1367178676Ssam			goto fail;
1368178676Ssam		}
1369198429Srpaulo
1370248078Smarius		data->m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR,
1371220692Sbschmidt		    IWN_RBUF_SIZE);
1372198439Srpaulo		if (data->m == NULL) {
1373178676Ssam			device_printf(sc->sc_dev,
1374220711Sbschmidt			    "%s: could not allocate RX mbuf\n", __func__);
1375220710Sbschmidt			error = ENOBUFS;
1376178676Ssam			goto fail;
1377178676Ssam		}
1378198429Srpaulo
1379201209Srpaulo		error = bus_dmamap_load(ring->data_dmat, data->map,
1380220692Sbschmidt		    mtod(data->m, void *), IWN_RBUF_SIZE, iwn_dma_map_addr,
1381220692Sbschmidt		    &paddr, BUS_DMA_NOWAIT);
1382178676Ssam		if (error != 0 && error != EFBIG) {
1383178676Ssam			device_printf(sc->sc_dev,
1384220711Sbschmidt			    "%s: can't not map mbuf, error %d\n", __func__,
1385220711Sbschmidt			    error);
1386178676Ssam			goto fail;
1387178676Ssam		}
1388178676Ssam
1389198429Srpaulo		/* Set physical address of RX buffer (256-byte aligned). */
1390178676Ssam		ring->desc[i] = htole32(paddr >> 8);
1391178676Ssam	}
1392220726Sbschmidt
1393178676Ssam	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
1394178676Ssam	    BUS_DMASYNC_PREWRITE);
1395220726Sbschmidt
1396178676Ssam	return 0;
1397220726Sbschmidt
1398220726Sbschmidtfail:	iwn_free_rx_ring(sc, ring);
1399178676Ssam	return error;
1400178676Ssam}
1401178676Ssam
1402206477Sbschmidtstatic void
1403178676Ssamiwn_reset_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring)
1404178676Ssam{
1405178676Ssam	int ntries;
1406178676Ssam
1407198429Srpaulo	if (iwn_nic_lock(sc) == 0) {
1408198429Srpaulo		IWN_WRITE(sc, IWN_FH_RX_CONFIG, 0);
1409198429Srpaulo		for (ntries = 0; ntries < 1000; ntries++) {
1410198429Srpaulo			if (IWN_READ(sc, IWN_FH_RX_STATUS) &
1411198429Srpaulo			    IWN_FH_RX_STATUS_IDLE)
1412198429Srpaulo				break;
1413198429Srpaulo			DELAY(10);
1414198429Srpaulo		}
1415198429Srpaulo		iwn_nic_unlock(sc);
1416198429Srpaulo	}
1417178676Ssam	ring->cur = 0;
1418198429Srpaulo	sc->last_rx_valid = 0;
1419178676Ssam}
1420178676Ssam
1421206477Sbschmidtstatic void
1422178676Ssamiwn_free_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring)
1423178676Ssam{
1424178676Ssam	int i;
1425178676Ssam
1426178676Ssam	iwn_dma_contig_free(&ring->desc_dma);
1427198429Srpaulo	iwn_dma_contig_free(&ring->stat_dma);
1428178676Ssam
1429198429Srpaulo	for (i = 0; i < IWN_RX_RING_COUNT; i++) {
1430198429Srpaulo		struct iwn_rx_data *data = &ring->data[i];
1431198429Srpaulo
1432198429Srpaulo		if (data->m != NULL) {
1433201209Srpaulo			bus_dmamap_sync(ring->data_dmat, data->map,
1434198439Srpaulo			    BUS_DMASYNC_POSTREAD);
1435201209Srpaulo			bus_dmamap_unload(ring->data_dmat, data->map);
1436198429Srpaulo			m_freem(data->m);
1437220710Sbschmidt			data->m = NULL;
1438198429Srpaulo		}
1439201209Srpaulo		if (data->map != NULL)
1440201209Srpaulo			bus_dmamap_destroy(ring->data_dmat, data->map);
1441198429Srpaulo	}
1442220701Sbschmidt	if (ring->data_dmat != NULL) {
1443220701Sbschmidt		bus_dma_tag_destroy(ring->data_dmat);
1444220701Sbschmidt		ring->data_dmat = NULL;
1445220701Sbschmidt	}
1446178676Ssam}
1447178676Ssam
1448206477Sbschmidtstatic int
1449178676Ssamiwn_alloc_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring, int qid)
1450178676Ssam{
1451220723Sbschmidt	bus_addr_t paddr;
1452178676Ssam	bus_size_t size;
1453178676Ssam	int i, error;
1454178676Ssam
1455178676Ssam	ring->qid = qid;
1456178676Ssam	ring->queued = 0;
1457178676Ssam	ring->cur = 0;
1458178676Ssam
1459220725Sbschmidt	/* Allocate TX descriptors (256-byte aligned). */
1460220726Sbschmidt	size = IWN_TX_RING_COUNT * sizeof (struct iwn_tx_desc);
1461220691Sbschmidt	error = iwn_dma_contig_alloc(sc, &ring->desc_dma, (void **)&ring->desc,
1462220691Sbschmidt	    size, 256);
1463178676Ssam	if (error != 0) {
1464178676Ssam		device_printf(sc->sc_dev,
1465198429Srpaulo		    "%s: could not allocate TX ring DMA memory, error %d\n",
1466178676Ssam		    __func__, error);
1467178676Ssam		goto fail;
1468178676Ssam	}
1469198429Srpaulo
1470220726Sbschmidt	size = IWN_TX_RING_COUNT * sizeof (struct iwn_tx_cmd);
1471220691Sbschmidt	error = iwn_dma_contig_alloc(sc, &ring->cmd_dma, (void **)&ring->cmd,
1472220691Sbschmidt	    size, 4);
1473178676Ssam	if (error != 0) {
1474178676Ssam		device_printf(sc->sc_dev,
1475198429Srpaulo		    "%s: could not allocate TX cmd DMA memory, error %d\n",
1476178676Ssam		    __func__, error);
1477178676Ssam		goto fail;
1478178676Ssam	}
1479178676Ssam
1480198429Srpaulo	error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0,
1481220726Sbschmidt	    BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES,
1482220726Sbschmidt	    IWN_MAX_SCATTER - 1, MCLBYTES, BUS_DMA_NOWAIT, NULL, NULL,
1483220726Sbschmidt	    &ring->data_dmat);
1484198429Srpaulo	if (error != 0) {
1485198429Srpaulo		device_printf(sc->sc_dev,
1486220711Sbschmidt		    "%s: could not create TX buf DMA tag, error %d\n",
1487178676Ssam		    __func__, error);
1488198429Srpaulo		goto fail;
1489198429Srpaulo	}
1490178676Ssam
1491198429Srpaulo	paddr = ring->cmd_dma.paddr;
1492178676Ssam	for (i = 0; i < IWN_TX_RING_COUNT; i++) {
1493178676Ssam		struct iwn_tx_data *data = &ring->data[i];
1494178676Ssam
1495198429Srpaulo		data->cmd_paddr = paddr;
1496198429Srpaulo		data->scratch_paddr = paddr + 12;
1497198429Srpaulo		paddr += sizeof (struct iwn_tx_cmd);
1498198429Srpaulo
1499201209Srpaulo		error = bus_dmamap_create(ring->data_dmat, 0, &data->map);
1500178676Ssam		if (error != 0) {
1501178676Ssam			device_printf(sc->sc_dev,
1502220711Sbschmidt			    "%s: could not create TX buf DMA map, error %d\n",
1503178676Ssam			    __func__, error);
1504178676Ssam			goto fail;
1505178676Ssam		}
1506178676Ssam	}
1507178676Ssam	return 0;
1508220726Sbschmidt
1509220726Sbschmidtfail:	iwn_free_tx_ring(sc, ring);
1510178676Ssam	return error;
1511178676Ssam}
1512178676Ssam
1513206477Sbschmidtstatic void
1514178676Ssamiwn_reset_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring)
1515178676Ssam{
1516198429Srpaulo	int i;
1517178676Ssam
1518178676Ssam	for (i = 0; i < IWN_TX_RING_COUNT; i++) {
1519178676Ssam		struct iwn_tx_data *data = &ring->data[i];
1520178676Ssam
1521178676Ssam		if (data->m != NULL) {
1522220704Sbschmidt			bus_dmamap_sync(ring->data_dmat, data->map,
1523220704Sbschmidt			    BUS_DMASYNC_POSTWRITE);
1524201209Srpaulo			bus_dmamap_unload(ring->data_dmat, data->map);
1525178676Ssam			m_freem(data->m);
1526178676Ssam			data->m = NULL;
1527178676Ssam		}
1528178676Ssam	}
1529198429Srpaulo	/* Clear TX descriptors. */
1530198429Srpaulo	memset(ring->desc, 0, ring->desc_dma.size);
1531198439Srpaulo	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
1532198439Srpaulo	    BUS_DMASYNC_PREWRITE);
1533198429Srpaulo	sc->qfullmsk &= ~(1 << ring->qid);
1534178676Ssam	ring->queued = 0;
1535178676Ssam	ring->cur = 0;
1536178676Ssam}
1537178676Ssam
1538206477Sbschmidtstatic void
1539178676Ssamiwn_free_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring)
1540178676Ssam{
1541178676Ssam	int i;
1542178676Ssam
1543178676Ssam	iwn_dma_contig_free(&ring->desc_dma);
1544178676Ssam	iwn_dma_contig_free(&ring->cmd_dma);
1545178676Ssam
1546201209Srpaulo	for (i = 0; i < IWN_TX_RING_COUNT; i++) {
1547201209Srpaulo		struct iwn_tx_data *data = &ring->data[i];
1548178676Ssam
1549201209Srpaulo		if (data->m != NULL) {
1550201209Srpaulo			bus_dmamap_sync(ring->data_dmat, data->map,
1551201209Srpaulo			    BUS_DMASYNC_POSTWRITE);
1552201209Srpaulo			bus_dmamap_unload(ring->data_dmat, data->map);
1553201209Srpaulo			m_freem(data->m);
1554178676Ssam		}
1555201209Srpaulo		if (data->map != NULL)
1556201209Srpaulo			bus_dmamap_destroy(ring->data_dmat, data->map);
1557178676Ssam	}
1558220701Sbschmidt	if (ring->data_dmat != NULL) {
1559220701Sbschmidt		bus_dma_tag_destroy(ring->data_dmat);
1560220701Sbschmidt		ring->data_dmat = NULL;
1561220701Sbschmidt	}
1562178676Ssam}
1563178676Ssam
1564206477Sbschmidtstatic void
1565201209Srpauloiwn5000_ict_reset(struct iwn_softc *sc)
1566201209Srpaulo{
1567201209Srpaulo	/* Disable interrupts. */
1568201209Srpaulo	IWN_WRITE(sc, IWN_INT_MASK, 0);
1569201209Srpaulo
1570201209Srpaulo	/* Reset ICT table. */
1571201209Srpaulo	memset(sc->ict, 0, IWN_ICT_SIZE);
1572201209Srpaulo	sc->ict_cur = 0;
1573201209Srpaulo
1574220725Sbschmidt	/* Set physical address of ICT table (4KB aligned). */
1575201209Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "%s: enabling ICT\n", __func__);
1576201209Srpaulo	IWN_WRITE(sc, IWN_DRAM_INT_TBL, IWN_DRAM_INT_TBL_ENABLE |
1577201209Srpaulo	    IWN_DRAM_INT_TBL_WRAP_CHECK | sc->ict_dma.paddr >> 12);
1578201209Srpaulo
1579201209Srpaulo	/* Enable periodic RX interrupt. */
1580201209Srpaulo	sc->int_mask |= IWN_INT_RX_PERIODIC;
1581201209Srpaulo	/* Switch to ICT interrupt mode in driver. */
1582201209Srpaulo	sc->sc_flags |= IWN_FLAG_USE_ICT;
1583201209Srpaulo
1584201209Srpaulo	/* Re-enable interrupts. */
1585201209Srpaulo	IWN_WRITE(sc, IWN_INT, 0xffffffff);
1586201209Srpaulo	IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask);
1587201209Srpaulo}
1588201209Srpaulo
1589206477Sbschmidtstatic int
1590198429Srpauloiwn_read_eeprom(struct iwn_softc *sc, uint8_t macaddr[IEEE80211_ADDR_LEN])
1591178676Ssam{
1592220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
1593220723Sbschmidt	uint16_t val;
1594198429Srpaulo	int error;
1595178676Ssam
1596198429Srpaulo	/* Check whether adapter has an EEPROM or an OTPROM. */
1597198429Srpaulo	if (sc->hw_type >= IWN_HW_REV_TYPE_1000 &&
1598198429Srpaulo	    (IWN_READ(sc, IWN_OTP_GP) & IWN_OTP_GP_DEV_SEL_OTP))
1599198429Srpaulo		sc->sc_flags |= IWN_FLAG_HAS_OTPROM;
1600198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "%s found\n",
1601198429Srpaulo	    (sc->sc_flags & IWN_FLAG_HAS_OTPROM) ? "OTPROM" : "EEPROM");
1602178676Ssam
1603201209Srpaulo	/* Adapter has to be powered on for EEPROM access to work. */
1604220726Sbschmidt	if ((error = iwn_apm_init(sc)) != 0) {
1605201209Srpaulo		device_printf(sc->sc_dev,
1606220726Sbschmidt		    "%s: could not power ON adapter, error %d\n", __func__,
1607220726Sbschmidt		    error);
1608201209Srpaulo		return error;
1609201209Srpaulo	}
1610201209Srpaulo
1611198429Srpaulo	if ((IWN_READ(sc, IWN_EEPROM_GP) & 0x7) == 0) {
1612198429Srpaulo		device_printf(sc->sc_dev, "%s: bad ROM signature\n", __func__);
1613198429Srpaulo		return EIO;
1614198429Srpaulo	}
1615220726Sbschmidt	if ((error = iwn_eeprom_lock(sc)) != 0) {
1616220726Sbschmidt		device_printf(sc->sc_dev, "%s: could not lock ROM, error %d\n",
1617198429Srpaulo		    __func__, error);
1618198429Srpaulo		return error;
1619198429Srpaulo	}
1620201209Srpaulo	if (sc->sc_flags & IWN_FLAG_HAS_OTPROM) {
1621220726Sbschmidt		if ((error = iwn_init_otprom(sc)) != 0) {
1622201209Srpaulo			device_printf(sc->sc_dev,
1623201209Srpaulo			    "%s: could not initialize OTPROM, error %d\n",
1624201209Srpaulo			    __func__, error);
1625201209Srpaulo			return error;
1626201209Srpaulo		}
1627198429Srpaulo	}
1628178676Ssam
1629220729Sbschmidt	iwn_read_prom_data(sc, IWN_EEPROM_SKU_CAP, &val, 2);
1630220729Sbschmidt	DPRINTF(sc, IWN_DEBUG_RESET, "SKU capabilities=0x%04x\n", le16toh(val));
1631220729Sbschmidt	/* Check if HT support is bonded out. */
1632220729Sbschmidt	if (val & htole16(IWN_EEPROM_SKU_CAP_11N))
1633220729Sbschmidt		sc->sc_flags |= IWN_FLAG_HAS_11N;
1634220729Sbschmidt
1635198429Srpaulo	iwn_read_prom_data(sc, IWN_EEPROM_RFCFG, &val, 2);
1636198429Srpaulo	sc->rfcfg = le16toh(val);
1637198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "radio config=0x%04x\n", sc->rfcfg);
1638220727Sbschmidt	/* Read Tx/Rx chains from ROM unless it's known to be broken. */
1639220727Sbschmidt	if (sc->txchainmask == 0)
1640220727Sbschmidt		sc->txchainmask = IWN_RFCFG_TXANTMSK(sc->rfcfg);
1641220727Sbschmidt	if (sc->rxchainmask == 0)
1642220727Sbschmidt		sc->rxchainmask = IWN_RFCFG_RXANTMSK(sc->rfcfg);
1643178676Ssam
1644198429Srpaulo	/* Read MAC address. */
1645198429Srpaulo	iwn_read_prom_data(sc, IWN_EEPROM_MAC, macaddr, 6);
1646178676Ssam
1647198429Srpaulo	/* Read adapter-specific information from EEPROM. */
1648220728Sbschmidt	ops->read_eeprom(sc);
1649178676Ssam
1650201209Srpaulo	iwn_apm_stop(sc);	/* Power OFF adapter. */
1651201209Srpaulo
1652198429Srpaulo	iwn_eeprom_unlock(sc);
1653198429Srpaulo	return 0;
1654178676Ssam}
1655178676Ssam
1656206477Sbschmidtstatic void
1657198429Srpauloiwn4965_read_eeprom(struct iwn_softc *sc)
1658178676Ssam{
1659201209Srpaulo	uint32_t addr;
1660220723Sbschmidt	uint16_t val;
1661198429Srpaulo	int i;
1662178676Ssam
1663220725Sbschmidt	/* Read regulatory domain (4 ASCII characters). */
1664198429Srpaulo	iwn_read_prom_data(sc, IWN4965_EEPROM_DOMAIN, sc->eeprom_domain, 4);
1665178676Ssam
1666220725Sbschmidt	/* Read the list of authorized channels (20MHz ones only). */
1667221636Sbschmidt	for (i = 0; i < 7; i++) {
1668201209Srpaulo		addr = iwn4965_regulatory_bands[i];
1669201209Srpaulo		iwn_read_eeprom_channels(sc, i, addr);
1670201209Srpaulo	}
1671198429Srpaulo
1672198429Srpaulo	/* Read maximum allowed TX power for 2GHz and 5GHz bands. */
1673198429Srpaulo	iwn_read_prom_data(sc, IWN4965_EEPROM_MAXPOW, &val, 2);
1674198429Srpaulo	sc->maxpwr2GHz = val & 0xff;
1675198429Srpaulo	sc->maxpwr5GHz = val >> 8;
1676198429Srpaulo	/* Check that EEPROM values are within valid range. */
1677198429Srpaulo	if (sc->maxpwr5GHz < 20 || sc->maxpwr5GHz > 50)
1678198429Srpaulo		sc->maxpwr5GHz = 38;
1679198429Srpaulo	if (sc->maxpwr2GHz < 20 || sc->maxpwr2GHz > 50)
1680198429Srpaulo		sc->maxpwr2GHz = 38;
1681198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "maxpwr 2GHz=%d 5GHz=%d\n",
1682198429Srpaulo	    sc->maxpwr2GHz, sc->maxpwr5GHz);
1683198429Srpaulo
1684198429Srpaulo	/* Read samples for each TX power group. */
1685198429Srpaulo	iwn_read_prom_data(sc, IWN4965_EEPROM_BANDS, sc->bands,
1686198429Srpaulo	    sizeof sc->bands);
1687198429Srpaulo
1688198429Srpaulo	/* Read voltage at which samples were taken. */
1689198429Srpaulo	iwn_read_prom_data(sc, IWN4965_EEPROM_VOLTAGE, &val, 2);
1690198429Srpaulo	sc->eeprom_voltage = (int16_t)le16toh(val);
1691198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "voltage=%d (in 0.3V)\n",
1692198429Srpaulo	    sc->eeprom_voltage);
1693198429Srpaulo
1694198429Srpaulo#ifdef IWN_DEBUG
1695198429Srpaulo	/* Print samples. */
1696201209Srpaulo	if (sc->sc_debug & IWN_DEBUG_ANY) {
1697198429Srpaulo		for (i = 0; i < IWN_NBANDS; i++)
1698198429Srpaulo			iwn4965_print_power_group(sc, i);
1699178676Ssam	}
1700198429Srpaulo#endif
1701178676Ssam}
1702178676Ssam
1703198429Srpaulo#ifdef IWN_DEBUG
1704206477Sbschmidtstatic void
1705198429Srpauloiwn4965_print_power_group(struct iwn_softc *sc, int i)
1706178676Ssam{
1707198429Srpaulo	struct iwn4965_eeprom_band *band = &sc->bands[i];
1708198429Srpaulo	struct iwn4965_eeprom_chan_samples *chans = band->chans;
1709198429Srpaulo	int j, c;
1710178676Ssam
1711198429Srpaulo	printf("===band %d===\n", i);
1712198429Srpaulo	printf("chan lo=%d, chan hi=%d\n", band->lo, band->hi);
1713198429Srpaulo	printf("chan1 num=%d\n", chans[0].num);
1714198429Srpaulo	for (c = 0; c < 2; c++) {
1715198429Srpaulo		for (j = 0; j < IWN_NSAMPLES; j++) {
1716198429Srpaulo			printf("chain %d, sample %d: temp=%d gain=%d "
1717198429Srpaulo			    "power=%d pa_det=%d\n", c, j,
1718198429Srpaulo			    chans[0].samples[c][j].temp,
1719198429Srpaulo			    chans[0].samples[c][j].gain,
1720198429Srpaulo			    chans[0].samples[c][j].power,
1721198429Srpaulo			    chans[0].samples[c][j].pa_det);
1722198429Srpaulo		}
1723198429Srpaulo	}
1724198429Srpaulo	printf("chan2 num=%d\n", chans[1].num);
1725198429Srpaulo	for (c = 0; c < 2; c++) {
1726198429Srpaulo		for (j = 0; j < IWN_NSAMPLES; j++) {
1727198429Srpaulo			printf("chain %d, sample %d: temp=%d gain=%d "
1728198429Srpaulo			    "power=%d pa_det=%d\n", c, j,
1729198429Srpaulo			    chans[1].samples[c][j].temp,
1730198429Srpaulo			    chans[1].samples[c][j].gain,
1731198429Srpaulo			    chans[1].samples[c][j].power,
1732198429Srpaulo			    chans[1].samples[c][j].pa_det);
1733198429Srpaulo		}
1734198429Srpaulo	}
1735178676Ssam}
1736198429Srpaulo#endif
1737178676Ssam
1738206477Sbschmidtstatic void
1739198429Srpauloiwn5000_read_eeprom(struct iwn_softc *sc)
1740178676Ssam{
1741206444Sbschmidt	struct iwn5000_eeprom_calib_hdr hdr;
1742220674Sbschmidt	int32_t volt;
1743220723Sbschmidt	uint32_t base, addr;
1744220723Sbschmidt	uint16_t val;
1745198429Srpaulo	int i;
1746178676Ssam
1747220725Sbschmidt	/* Read regulatory domain (4 ASCII characters). */
1748198429Srpaulo	iwn_read_prom_data(sc, IWN5000_EEPROM_REG, &val, 2);
1749198429Srpaulo	base = le16toh(val);
1750198429Srpaulo	iwn_read_prom_data(sc, base + IWN5000_EEPROM_DOMAIN,
1751198429Srpaulo	    sc->eeprom_domain, 4);
1752178676Ssam
1753220725Sbschmidt	/* Read the list of authorized channels (20MHz ones only). */
1754221636Sbschmidt	for (i = 0; i < 7; i++) {
1755221635Sbschmidt		if (sc->hw_type >= IWN_HW_REV_TYPE_6000)
1756221635Sbschmidt			addr = base + iwn6000_regulatory_bands[i];
1757221635Sbschmidt		else
1758221635Sbschmidt			addr = base + iwn5000_regulatory_bands[i];
1759201209Srpaulo		iwn_read_eeprom_channels(sc, i, addr);
1760198429Srpaulo	}
1761178676Ssam
1762201209Srpaulo	/* Read enhanced TX power information for 6000 Series. */
1763201209Srpaulo	if (sc->hw_type >= IWN_HW_REV_TYPE_6000)
1764201209Srpaulo		iwn_read_eeprom_enhinfo(sc);
1765201209Srpaulo
1766198429Srpaulo	iwn_read_prom_data(sc, IWN5000_EEPROM_CAL, &val, 2);
1767198429Srpaulo	base = le16toh(val);
1768206444Sbschmidt	iwn_read_prom_data(sc, base, &hdr, sizeof hdr);
1769206444Sbschmidt	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
1770220726Sbschmidt	    "%s: calib version=%u pa type=%u voltage=%u\n", __func__,
1771220726Sbschmidt	    hdr.version, hdr.pa_type, le16toh(hdr.volt));
1772210108Sbschmidt	sc->calib_ver = hdr.version;
1773206444Sbschmidt
1774198429Srpaulo	if (sc->hw_type == IWN_HW_REV_TYPE_5150) {
1775201209Srpaulo		/* Compute temperature offset. */
1776198429Srpaulo		iwn_read_prom_data(sc, base + IWN5000_EEPROM_TEMP, &val, 2);
1777220674Sbschmidt		sc->eeprom_temp = le16toh(val);
1778198429Srpaulo		iwn_read_prom_data(sc, base + IWN5000_EEPROM_VOLT, &val, 2);
1779198429Srpaulo		volt = le16toh(val);
1780220674Sbschmidt		sc->temp_off = sc->eeprom_temp - (volt / -5);
1781201209Srpaulo		DPRINTF(sc, IWN_DEBUG_CALIBRATE, "temp=%d volt=%d offset=%dK\n",
1782220674Sbschmidt		    sc->eeprom_temp, volt, sc->temp_off);
1783220674Sbschmidt	} else {
1784220674Sbschmidt		/* Read crystal calibration. */
1785220674Sbschmidt		iwn_read_prom_data(sc, base + IWN5000_EEPROM_CRYSTAL,
1786220674Sbschmidt		    &sc->eeprom_crystal, sizeof (uint32_t));
1787220674Sbschmidt		DPRINTF(sc, IWN_DEBUG_CALIBRATE, "crystal calibration 0x%08x\n",
1788220674Sbschmidt		    le32toh(sc->eeprom_crystal));
1789178676Ssam	}
1790178676Ssam}
1791178676Ssam
1792201209Srpaulo/*
1793201209Srpaulo * Translate EEPROM flags to net80211.
1794201209Srpaulo */
1795201209Srpaulostatic uint32_t
1796201209Srpauloiwn_eeprom_channel_flags(struct iwn_eeprom_chan *channel)
1797201209Srpaulo{
1798201209Srpaulo	uint32_t nflags;
1799201209Srpaulo
1800201209Srpaulo	nflags = 0;
1801201209Srpaulo	if ((channel->flags & IWN_EEPROM_CHAN_ACTIVE) == 0)
1802201209Srpaulo		nflags |= IEEE80211_CHAN_PASSIVE;
1803201209Srpaulo	if ((channel->flags & IWN_EEPROM_CHAN_IBSS) == 0)
1804201209Srpaulo		nflags |= IEEE80211_CHAN_NOADHOC;
1805201209Srpaulo	if (channel->flags & IWN_EEPROM_CHAN_RADAR) {
1806201209Srpaulo		nflags |= IEEE80211_CHAN_DFS;
1807201209Srpaulo		/* XXX apparently IBSS may still be marked */
1808201209Srpaulo		nflags |= IEEE80211_CHAN_NOADHOC;
1809201209Srpaulo	}
1810201209Srpaulo
1811201209Srpaulo	return nflags;
1812201209Srpaulo}
1813201209Srpaulo
1814198429Srpaulostatic void
1815201209Srpauloiwn_read_eeprom_band(struct iwn_softc *sc, int n)
1816178676Ssam{
1817198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
1818198429Srpaulo	struct ieee80211com *ic = ifp->if_l2com;
1819201209Srpaulo	struct iwn_eeprom_chan *channels = sc->eeprom_channels[n];
1820201209Srpaulo	const struct iwn_chan_band *band = &iwn_bands[n];
1821198429Srpaulo	struct ieee80211_channel *c;
1822220687Sbschmidt	uint8_t chan;
1823220687Sbschmidt	int i, nflags;
1824178676Ssam
1825198429Srpaulo	for (i = 0; i < band->nchan; i++) {
1826198429Srpaulo		if (!(channels[i].flags & IWN_EEPROM_CHAN_VALID)) {
1827198429Srpaulo			DPRINTF(sc, IWN_DEBUG_RESET,
1828198429Srpaulo			    "skip chan %d flags 0x%x maxpwr %d\n",
1829198429Srpaulo			    band->chan[i], channels[i].flags,
1830198429Srpaulo			    channels[i].maxpwr);
1831198429Srpaulo			continue;
1832198429Srpaulo		}
1833198429Srpaulo		chan = band->chan[i];
1834201209Srpaulo		nflags = iwn_eeprom_channel_flags(&channels[i]);
1835178676Ssam
1836198429Srpaulo		c = &ic->ic_channels[ic->ic_nchans++];
1837198429Srpaulo		c->ic_ieee = chan;
1838198429Srpaulo		c->ic_maxregpower = channels[i].maxpwr;
1839198429Srpaulo		c->ic_maxpower = 2*c->ic_maxregpower;
1840206445Sbschmidt
1841201209Srpaulo		if (n == 0) {	/* 2GHz band */
1842220726Sbschmidt			c->ic_freq = ieee80211_ieee2mhz(chan, IEEE80211_CHAN_G);
1843198429Srpaulo			/* G =>'s B is supported */
1844198429Srpaulo			c->ic_flags = IEEE80211_CHAN_B | nflags;
1845198429Srpaulo			c = &ic->ic_channels[ic->ic_nchans++];
1846198429Srpaulo			c[0] = c[-1];
1847198429Srpaulo			c->ic_flags = IEEE80211_CHAN_G | nflags;
1848198429Srpaulo		} else {	/* 5GHz band */
1849220726Sbschmidt			c->ic_freq = ieee80211_ieee2mhz(chan, IEEE80211_CHAN_A);
1850198429Srpaulo			c->ic_flags = IEEE80211_CHAN_A | nflags;
1851178676Ssam		}
1852220723Sbschmidt
1853220723Sbschmidt		/* Save maximum allowed TX power for this channel. */
1854220723Sbschmidt		sc->maxpwr[chan] = channels[i].maxpwr;
1855220723Sbschmidt
1856220723Sbschmidt		DPRINTF(sc, IWN_DEBUG_RESET,
1857220726Sbschmidt		    "add chan %d flags 0x%x maxpwr %d\n", chan,
1858220726Sbschmidt		    channels[i].flags, channels[i].maxpwr);
1859220723Sbschmidt
1860221636Sbschmidt		if (sc->sc_flags & IWN_FLAG_HAS_11N) {
1861221636Sbschmidt			/* add HT20, HT40 added separately */
1862221636Sbschmidt			c = &ic->ic_channels[ic->ic_nchans++];
1863221636Sbschmidt			c[0] = c[-1];
1864221636Sbschmidt			c->ic_flags |= IEEE80211_CHAN_HT20;
1865221636Sbschmidt		}
1866178676Ssam	}
1867178676Ssam}
1868178676Ssam
1869198429Srpaulostatic void
1870201209Srpauloiwn_read_eeprom_ht40(struct iwn_softc *sc, int n)
1871178676Ssam{
1872198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
1873198429Srpaulo	struct ieee80211com *ic = ifp->if_l2com;
1874201209Srpaulo	struct iwn_eeprom_chan *channels = sc->eeprom_channels[n];
1875201209Srpaulo	const struct iwn_chan_band *band = &iwn_bands[n];
1876198429Srpaulo	struct ieee80211_channel *c, *cent, *extc;
1877221636Sbschmidt	uint8_t chan;
1878221636Sbschmidt	int i, nflags;
1879178676Ssam
1880221636Sbschmidt	if (!(sc->sc_flags & IWN_FLAG_HAS_11N))
1881221636Sbschmidt		return;
1882221636Sbschmidt
1883198429Srpaulo	for (i = 0; i < band->nchan; i++) {
1884221636Sbschmidt		if (!(channels[i].flags & IWN_EEPROM_CHAN_VALID)) {
1885198429Srpaulo			DPRINTF(sc, IWN_DEBUG_RESET,
1886198429Srpaulo			    "skip chan %d flags 0x%x maxpwr %d\n",
1887198429Srpaulo			    band->chan[i], channels[i].flags,
1888198429Srpaulo			    channels[i].maxpwr);
1889198429Srpaulo			continue;
1890198429Srpaulo		}
1891221636Sbschmidt		chan = band->chan[i];
1892221636Sbschmidt		nflags = iwn_eeprom_channel_flags(&channels[i]);
1893221636Sbschmidt
1894198429Srpaulo		/*
1895198429Srpaulo		 * Each entry defines an HT40 channel pair; find the
1896198429Srpaulo		 * center channel, then the extension channel above.
1897198429Srpaulo		 */
1898221636Sbschmidt		cent = ieee80211_find_channel_byieee(ic, chan,
1899221636Sbschmidt		    (n == 5 ? IEEE80211_CHAN_G : IEEE80211_CHAN_A));
1900198429Srpaulo		if (cent == NULL) {	/* XXX shouldn't happen */
1901198429Srpaulo			device_printf(sc->sc_dev,
1902221636Sbschmidt			    "%s: no entry for channel %d\n", __func__, chan);
1903198429Srpaulo			continue;
1904198429Srpaulo		}
1905198429Srpaulo		extc = ieee80211_find_channel(ic, cent->ic_freq+20,
1906221636Sbschmidt		    (n == 5 ? IEEE80211_CHAN_G : IEEE80211_CHAN_A));
1907198429Srpaulo		if (extc == NULL) {
1908198429Srpaulo			DPRINTF(sc, IWN_DEBUG_RESET,
1909221636Sbschmidt			    "%s: skip chan %d, extension channel not found\n",
1910221636Sbschmidt			    __func__, chan);
1911198429Srpaulo			continue;
1912198429Srpaulo		}
1913178676Ssam
1914198429Srpaulo		DPRINTF(sc, IWN_DEBUG_RESET,
1915198429Srpaulo		    "add ht40 chan %d flags 0x%x maxpwr %d\n",
1916221636Sbschmidt		    chan, channels[i].flags, channels[i].maxpwr);
1917178676Ssam
1918198429Srpaulo		c = &ic->ic_channels[ic->ic_nchans++];
1919198429Srpaulo		c[0] = cent[0];
1920198429Srpaulo		c->ic_extieee = extc->ic_ieee;
1921198429Srpaulo		c->ic_flags &= ~IEEE80211_CHAN_HT;
1922221636Sbschmidt		c->ic_flags |= IEEE80211_CHAN_HT40U | nflags;
1923198429Srpaulo		c = &ic->ic_channels[ic->ic_nchans++];
1924198429Srpaulo		c[0] = extc[0];
1925198429Srpaulo		c->ic_extieee = cent->ic_ieee;
1926198429Srpaulo		c->ic_flags &= ~IEEE80211_CHAN_HT;
1927221636Sbschmidt		c->ic_flags |= IEEE80211_CHAN_HT40D | nflags;
1928178676Ssam	}
1929198429Srpaulo}
1930178676Ssam
1931198429Srpaulostatic void
1932201209Srpauloiwn_read_eeprom_channels(struct iwn_softc *sc, int n, uint32_t addr)
1933198429Srpaulo{
1934198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
1935198429Srpaulo	struct ieee80211com *ic = ifp->if_l2com;
1936178676Ssam
1937201209Srpaulo	iwn_read_prom_data(sc, addr, &sc->eeprom_channels[n],
1938201209Srpaulo	    iwn_bands[n].nchan * sizeof (struct iwn_eeprom_chan));
1939201209Srpaulo
1940198429Srpaulo	if (n < 5)
1941201209Srpaulo		iwn_read_eeprom_band(sc, n);
1942198429Srpaulo	else
1943201209Srpaulo		iwn_read_eeprom_ht40(sc, n);
1944198429Srpaulo	ieee80211_sort_channels(ic->ic_channels, ic->ic_nchans);
1945178676Ssam}
1946178676Ssam
1947220723Sbschmidtstatic struct iwn_eeprom_chan *
1948220723Sbschmidtiwn_find_eeprom_channel(struct iwn_softc *sc, struct ieee80211_channel *c)
1949220723Sbschmidt{
1950221636Sbschmidt	int band, chan, i, j;
1951220723Sbschmidt
1952221636Sbschmidt	if (IEEE80211_IS_CHAN_HT40(c)) {
1953221636Sbschmidt		band = IEEE80211_IS_CHAN_5GHZ(c) ? 6 : 5;
1954221636Sbschmidt		if (IEEE80211_IS_CHAN_HT40D(c))
1955221636Sbschmidt			chan = c->ic_extieee;
1956221636Sbschmidt		else
1957221636Sbschmidt			chan = c->ic_ieee;
1958221636Sbschmidt		for (i = 0; i < iwn_bands[band].nchan; i++) {
1959221636Sbschmidt			if (iwn_bands[band].chan[i] == chan)
1960221636Sbschmidt				return &sc->eeprom_channels[band][i];
1961220723Sbschmidt		}
1962221636Sbschmidt	} else {
1963221636Sbschmidt		for (j = 0; j < 5; j++) {
1964221636Sbschmidt			for (i = 0; i < iwn_bands[j].nchan; i++) {
1965221636Sbschmidt				if (iwn_bands[j].chan[i] == c->ic_ieee)
1966221636Sbschmidt					return &sc->eeprom_channels[j][i];
1967221636Sbschmidt			}
1968221636Sbschmidt		}
1969220723Sbschmidt	}
1970220723Sbschmidt	return NULL;
1971220723Sbschmidt}
1972220723Sbschmidt
1973220723Sbschmidt/*
1974220723Sbschmidt * Enforce flags read from EEPROM.
1975220723Sbschmidt */
1976220723Sbschmidtstatic int
1977220723Sbschmidtiwn_setregdomain(struct ieee80211com *ic, struct ieee80211_regdomain *rd,
1978220723Sbschmidt    int nchan, struct ieee80211_channel chans[])
1979220723Sbschmidt{
1980220723Sbschmidt	struct iwn_softc *sc = ic->ic_ifp->if_softc;
1981220723Sbschmidt	int i;
1982220723Sbschmidt
1983220723Sbschmidt	for (i = 0; i < nchan; i++) {
1984220723Sbschmidt		struct ieee80211_channel *c = &chans[i];
1985220723Sbschmidt		struct iwn_eeprom_chan *channel;
1986220723Sbschmidt
1987220723Sbschmidt		channel = iwn_find_eeprom_channel(sc, c);
1988220723Sbschmidt		if (channel == NULL) {
1989220723Sbschmidt			if_printf(ic->ic_ifp,
1990220723Sbschmidt			    "%s: invalid channel %u freq %u/0x%x\n",
1991220723Sbschmidt			    __func__, c->ic_ieee, c->ic_freq, c->ic_flags);
1992220723Sbschmidt			return EINVAL;
1993220723Sbschmidt		}
1994220723Sbschmidt		c->ic_flags |= iwn_eeprom_channel_flags(channel);
1995220723Sbschmidt	}
1996220723Sbschmidt
1997220723Sbschmidt	return 0;
1998220723Sbschmidt}
1999220723Sbschmidt
2000206477Sbschmidtstatic void
2001201209Srpauloiwn_read_eeprom_enhinfo(struct iwn_softc *sc)
2002201209Srpaulo{
2003201209Srpaulo	struct iwn_eeprom_enhinfo enhinfo[35];
2004221637Sbschmidt	struct ifnet *ifp = sc->sc_ifp;
2005221637Sbschmidt	struct ieee80211com *ic = ifp->if_l2com;
2006221637Sbschmidt	struct ieee80211_channel *c;
2007201209Srpaulo	uint16_t val, base;
2008201209Srpaulo	int8_t maxpwr;
2009221637Sbschmidt	uint8_t flags;
2010221637Sbschmidt	int i, j;
2011201209Srpaulo
2012201209Srpaulo	iwn_read_prom_data(sc, IWN5000_EEPROM_REG, &val, 2);
2013201209Srpaulo	base = le16toh(val);
2014201209Srpaulo	iwn_read_prom_data(sc, base + IWN6000_EEPROM_ENHINFO,
2015201209Srpaulo	    enhinfo, sizeof enhinfo);
2016201209Srpaulo
2017201209Srpaulo	for (i = 0; i < nitems(enhinfo); i++) {
2018221637Sbschmidt		flags = enhinfo[i].flags;
2019221637Sbschmidt		if (!(flags & IWN_ENHINFO_VALID))
2020201209Srpaulo			continue;	/* Skip invalid entries. */
2021201209Srpaulo
2022201209Srpaulo		maxpwr = 0;
2023201209Srpaulo		if (sc->txchainmask & IWN_ANT_A)
2024201209Srpaulo			maxpwr = MAX(maxpwr, enhinfo[i].chain[0]);
2025201209Srpaulo		if (sc->txchainmask & IWN_ANT_B)
2026201209Srpaulo			maxpwr = MAX(maxpwr, enhinfo[i].chain[1]);
2027201209Srpaulo		if (sc->txchainmask & IWN_ANT_C)
2028201209Srpaulo			maxpwr = MAX(maxpwr, enhinfo[i].chain[2]);
2029201209Srpaulo		if (sc->ntxchains == 2)
2030201209Srpaulo			maxpwr = MAX(maxpwr, enhinfo[i].mimo2);
2031201209Srpaulo		else if (sc->ntxchains == 3)
2032201209Srpaulo			maxpwr = MAX(maxpwr, enhinfo[i].mimo3);
2033201209Srpaulo
2034221637Sbschmidt		for (j = 0; j < ic->ic_nchans; j++) {
2035221637Sbschmidt			c = &ic->ic_channels[j];
2036221637Sbschmidt			if ((flags & IWN_ENHINFO_5GHZ)) {
2037221637Sbschmidt				if (!IEEE80211_IS_CHAN_A(c))
2038221637Sbschmidt					continue;
2039221637Sbschmidt			} else if ((flags & IWN_ENHINFO_OFDM)) {
2040221637Sbschmidt				if (!IEEE80211_IS_CHAN_G(c))
2041221637Sbschmidt					continue;
2042221637Sbschmidt			} else if (!IEEE80211_IS_CHAN_B(c))
2043221637Sbschmidt				continue;
2044221637Sbschmidt			if ((flags & IWN_ENHINFO_HT40)) {
2045221637Sbschmidt				if (!IEEE80211_IS_CHAN_HT40(c))
2046221637Sbschmidt					continue;
2047221637Sbschmidt			} else {
2048221637Sbschmidt				if (IEEE80211_IS_CHAN_HT40(c))
2049221637Sbschmidt					continue;
2050221637Sbschmidt			}
2051221637Sbschmidt			if (enhinfo[i].chan != 0 &&
2052221637Sbschmidt			    enhinfo[i].chan != c->ic_ieee)
2053221637Sbschmidt				continue;
2054221637Sbschmidt
2055221637Sbschmidt			DPRINTF(sc, IWN_DEBUG_RESET,
2056221637Sbschmidt			    "channel %d(%x), maxpwr %d\n", c->ic_ieee,
2057221637Sbschmidt			    c->ic_flags, maxpwr / 2);
2058221637Sbschmidt			c->ic_maxregpower = maxpwr / 2;
2059221637Sbschmidt			c->ic_maxpower = maxpwr;
2060221637Sbschmidt		}
2061201209Srpaulo	}
2062201209Srpaulo}
2063201209Srpaulo
2064206477Sbschmidtstatic struct ieee80211_node *
2065198429Srpauloiwn_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN])
2066178676Ssam{
2067198429Srpaulo	return malloc(sizeof (struct iwn_node), M_80211_NODE,M_NOWAIT | M_ZERO);
2068198429Srpaulo}
2069178676Ssam
2070221648Sbschmidtstatic __inline int
2071221648Sbschmidtrate2plcp(int rate)
2072221648Sbschmidt{
2073221648Sbschmidt	switch (rate & 0xff) {
2074221648Sbschmidt	case 12:	return 0xd;
2075221648Sbschmidt	case 18:	return 0xf;
2076221648Sbschmidt	case 24:	return 0x5;
2077221648Sbschmidt	case 36:	return 0x7;
2078221648Sbschmidt	case 48:	return 0x9;
2079221648Sbschmidt	case 72:	return 0xb;
2080221648Sbschmidt	case 96:	return 0x1;
2081221648Sbschmidt	case 108:	return 0x3;
2082221648Sbschmidt	case 2:		return 10;
2083221648Sbschmidt	case 4:		return 20;
2084221648Sbschmidt	case 11:	return 55;
2085221648Sbschmidt	case 22:	return 110;
2086221648Sbschmidt	}
2087221648Sbschmidt	return 0;
2088221648Sbschmidt}
2089221648Sbschmidt
2090220715Sbschmidtstatic void
2091220715Sbschmidtiwn_newassoc(struct ieee80211_node *ni, int isnew)
2092220715Sbschmidt{
2093222933Sbschmidt#define	RV(v)	((v) & IEEE80211_RATE_VAL)
2094221648Sbschmidt	struct ieee80211com *ic = ni->ni_ic;
2095221648Sbschmidt	struct iwn_softc *sc = ic->ic_ifp->if_softc;
2096220715Sbschmidt	struct iwn_node *wn = (void *)ni;
2097221649Sbschmidt	uint8_t txant1, txant2;
2098221648Sbschmidt	int i, plcp, rate, ridx;
2099220715Sbschmidt
2100221648Sbschmidt	/* Use the first valid TX antenna. */
2101221649Sbschmidt	txant1 = IWN_LSB(sc->txchainmask);
2102221649Sbschmidt	txant2 = IWN_LSB(sc->txchainmask & ~txant1);
2103221648Sbschmidt
2104221649Sbschmidt	if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) {
2105221649Sbschmidt		ridx = ni->ni_rates.rs_nrates - 1;
2106221649Sbschmidt		for (i = ni->ni_htrates.rs_nrates - 1; i >= 0; i--) {
2107222933Sbschmidt			plcp = RV(ni->ni_htrates.rs_rates[i]) | IWN_RFLAG_MCS;
2108221649Sbschmidt			if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) {
2109221649Sbschmidt				plcp |= IWN_RFLAG_HT40;
2110221649Sbschmidt				if (ni->ni_htcap & IEEE80211_HTCAP_SHORTGI40)
2111221649Sbschmidt					plcp |= IWN_RFLAG_SGI;
2112221649Sbschmidt			} else if (ni->ni_htcap & IEEE80211_HTCAP_SHORTGI20)
2113221649Sbschmidt				plcp |= IWN_RFLAG_SGI;
2114230618Sbschmidt			if (RV(ni->ni_htrates.rs_rates[i]) > 7)
2115221649Sbschmidt				plcp |= IWN_RFLAG_ANT(txant1 | txant2);
2116221649Sbschmidt			else
2117221649Sbschmidt				plcp |= IWN_RFLAG_ANT(txant1);
2118221649Sbschmidt			if (ridx >= 0) {
2119222933Sbschmidt				rate = RV(ni->ni_rates.rs_rates[ridx]);
2120221649Sbschmidt				wn->ridx[rate] = plcp;
2121221649Sbschmidt			}
2122221649Sbschmidt			wn->ridx[IEEE80211_RATE_MCS | i] = plcp;
2123221649Sbschmidt			ridx--;
2124221649Sbschmidt		}
2125221649Sbschmidt	} else {
2126221649Sbschmidt		for (i = 0; i < ni->ni_rates.rs_nrates; i++) {
2127222933Sbschmidt			rate = RV(ni->ni_rates.rs_rates[i]);
2128221649Sbschmidt			plcp = rate2plcp(rate);
2129221649Sbschmidt			ridx = ic->ic_rt->rateCodeToIndex[rate];
2130221649Sbschmidt			if (ridx < IWN_RIDX_OFDM6 &&
2131221649Sbschmidt			    IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
2132221649Sbschmidt				plcp |= IWN_RFLAG_CCK;
2133221649Sbschmidt			plcp |= IWN_RFLAG_ANT(txant1);
2134221649Sbschmidt			wn->ridx[rate] = htole32(plcp);
2135221649Sbschmidt		}
2136220715Sbschmidt	}
2137222933Sbschmidt#undef	RV
2138220715Sbschmidt}
2139220715Sbschmidt
2140206477Sbschmidtstatic int
2141198429Srpauloiwn_media_change(struct ifnet *ifp)
2142178676Ssam{
2143220726Sbschmidt	int error;
2144220726Sbschmidt
2145220726Sbschmidt	error = ieee80211_media_change(ifp);
2146198429Srpaulo	/* NB: only the fixed rate can change and that doesn't need a reset */
2147198429Srpaulo	return (error == ENETRESET ? 0 : error);
2148198429Srpaulo}
2149178676Ssam
2150206477Sbschmidtstatic int
2151198429Srpauloiwn_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
2152198429Srpaulo{
2153198429Srpaulo	struct iwn_vap *ivp = IWN_VAP(vap);
2154198429Srpaulo	struct ieee80211com *ic = vap->iv_ic;
2155198429Srpaulo	struct iwn_softc *sc = ic->ic_ifp->if_softc;
2156220688Sbschmidt	int error = 0;
2157178676Ssam
2158198429Srpaulo	DPRINTF(sc, IWN_DEBUG_STATE, "%s: %s -> %s\n", __func__,
2159220726Sbschmidt	    ieee80211_state_name[vap->iv_state], ieee80211_state_name[nstate]);
2160178676Ssam
2161198429Srpaulo	IEEE80211_UNLOCK(ic);
2162198429Srpaulo	IWN_LOCK(sc);
2163220667Sbschmidt	callout_stop(&sc->calib_to);
2164178676Ssam
2165210114Sbschmidt	switch (nstate) {
2166210114Sbschmidt	case IEEE80211_S_ASSOC:
2167210114Sbschmidt		if (vap->iv_state != IEEE80211_S_RUN)
2168210114Sbschmidt			break;
2169210114Sbschmidt		/* FALLTHROUGH */
2170210114Sbschmidt	case IEEE80211_S_AUTH:
2171210114Sbschmidt		if (vap->iv_state == IEEE80211_S_AUTH)
2172210114Sbschmidt			break;
2173210114Sbschmidt
2174210114Sbschmidt		/*
2175210114Sbschmidt		 * !AUTH -> AUTH transition requires state reset to handle
2176210114Sbschmidt		 * reassociations correctly.
2177210114Sbschmidt		 */
2178198439Srpaulo		sc->rxon.associd = 0;
2179198439Srpaulo		sc->rxon.filter &= ~htole32(IWN_FILTER_BSS);
2180220667Sbschmidt		sc->calib.state = IWN_CALIB_STATE_INIT;
2181220667Sbschmidt
2182220688Sbschmidt		if ((error = iwn_auth(sc, vap)) != 0) {
2183220688Sbschmidt			device_printf(sc->sc_dev,
2184220688Sbschmidt			    "%s: could not move to auth state\n", __func__);
2185220688Sbschmidt		}
2186210114Sbschmidt		break;
2187210114Sbschmidt
2188210114Sbschmidt	case IEEE80211_S_RUN:
2189198429Srpaulo		/*
2190210114Sbschmidt		 * RUN -> RUN transition; Just restart the timers.
2191210114Sbschmidt		 */
2192220667Sbschmidt		if (vap->iv_state == IEEE80211_S_RUN) {
2193220667Sbschmidt			sc->calib_cnt = 0;
2194210114Sbschmidt			break;
2195210114Sbschmidt		}
2196210114Sbschmidt
2197210114Sbschmidt		/*
2198198429Srpaulo		 * !RUN -> RUN requires setting the association id
2199198429Srpaulo		 * which is done with a firmware cmd.  We also defer
2200198429Srpaulo		 * starting the timers until that work is done.
2201198429Srpaulo		 */
2202220688Sbschmidt		if ((error = iwn_run(sc, vap)) != 0) {
2203220688Sbschmidt			device_printf(sc->sc_dev,
2204220688Sbschmidt			    "%s: could not move to run state\n", __func__);
2205220688Sbschmidt		}
2206210114Sbschmidt		break;
2207210114Sbschmidt
2208220667Sbschmidt	case IEEE80211_S_INIT:
2209220667Sbschmidt		sc->calib.state = IWN_CALIB_STATE_INIT;
2210220667Sbschmidt		break;
2211220667Sbschmidt
2212210114Sbschmidt	default:
2213210114Sbschmidt		break;
2214178676Ssam	}
2215198429Srpaulo	IWN_UNLOCK(sc);
2216198429Srpaulo	IEEE80211_LOCK(ic);
2217220688Sbschmidt	if (error != 0)
2218220688Sbschmidt		return error;
2219198429Srpaulo	return ivp->iv_newstate(vap, nstate, arg);
2220178676Ssam}
2221178676Ssam
2222220667Sbschmidtstatic void
2223220667Sbschmidtiwn_calib_timeout(void *arg)
2224220667Sbschmidt{
2225220667Sbschmidt	struct iwn_softc *sc = arg;
2226220667Sbschmidt
2227220667Sbschmidt	IWN_LOCK_ASSERT(sc);
2228220667Sbschmidt
2229220667Sbschmidt	/* Force automatic TX power calibration every 60 secs. */
2230220667Sbschmidt	if (++sc->calib_cnt >= 120) {
2231220667Sbschmidt		uint32_t flags = 0;
2232220667Sbschmidt
2233220667Sbschmidt		DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s\n",
2234220667Sbschmidt		    "sending request for statistics");
2235220667Sbschmidt		(void)iwn_cmd(sc, IWN_CMD_GET_STATISTICS, &flags,
2236220667Sbschmidt		    sizeof flags, 1);
2237220667Sbschmidt		sc->calib_cnt = 0;
2238220667Sbschmidt	}
2239220667Sbschmidt	callout_reset(&sc->calib_to, msecs_to_ticks(500), iwn_calib_timeout,
2240220667Sbschmidt	    sc);
2241220667Sbschmidt}
2242220667Sbschmidt
2243198429Srpaulo/*
2244198429Srpaulo * Process an RX_PHY firmware notification.  This is usually immediately
2245198429Srpaulo * followed by an MPDU_RX_DONE notification.
2246198429Srpaulo */
2247206477Sbschmidtstatic void
2248198429Srpauloiwn_rx_phy(struct iwn_softc *sc, struct iwn_rx_desc *desc,
2249198429Srpaulo    struct iwn_rx_data *data)
2250178676Ssam{
2251198429Srpaulo	struct iwn_rx_stat *stat = (struct iwn_rx_stat *)(desc + 1);
2252198429Srpaulo
2253198429Srpaulo	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: received PHY stats\n", __func__);
2254202986Srpaulo	bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD);
2255198429Srpaulo
2256198429Srpaulo	/* Save RX statistics, they will be used on MPDU_RX_DONE. */
2257198429Srpaulo	memcpy(&sc->last_rx_stat, stat, sizeof (*stat));
2258198429Srpaulo	sc->last_rx_valid = 1;
2259178676Ssam}
2260178676Ssam
2261198429Srpaulo/*
2262198429Srpaulo * Process an RX_DONE (4965AGN only) or MPDU_RX_DONE firmware notification.
2263198429Srpaulo * Each MPDU_RX_DONE notification must be preceded by an RX_PHY one.
2264198429Srpaulo */
2265206477Sbschmidtstatic void
2266198429Srpauloiwn_rx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc,
2267178676Ssam    struct iwn_rx_data *data)
2268178676Ssam{
2269220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
2270178676Ssam	struct ifnet *ifp = sc->sc_ifp;
2271178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
2272178676Ssam	struct iwn_rx_ring *ring = &sc->rxq;
2273178676Ssam	struct ieee80211_frame *wh;
2274178676Ssam	struct ieee80211_node *ni;
2275198429Srpaulo	struct mbuf *m, *m1;
2276178676Ssam	struct iwn_rx_stat *stat;
2277178676Ssam	caddr_t head;
2278178676Ssam	bus_addr_t paddr;
2279198429Srpaulo	uint32_t flags;
2280198429Srpaulo	int error, len, rssi, nf;
2281178676Ssam
2282198429Srpaulo	if (desc->type == IWN_MPDU_RX_DONE) {
2283198429Srpaulo		/* Check for prior RX_PHY notification. */
2284178676Ssam		if (!sc->last_rx_valid) {
2285178676Ssam			DPRINTF(sc, IWN_DEBUG_ANY,
2286201209Srpaulo			    "%s: missing RX_PHY\n", __func__);
2287178676Ssam			return;
2288178676Ssam		}
2289178676Ssam		stat = &sc->last_rx_stat;
2290178676Ssam	} else
2291178676Ssam		stat = (struct iwn_rx_stat *)(desc + 1);
2292178676Ssam
2293201209Srpaulo	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD);
2294198439Srpaulo
2295178676Ssam	if (stat->cfg_phy_len > IWN_STAT_MAXLEN) {
2296178676Ssam		device_printf(sc->sc_dev,
2297220724Sbschmidt		    "%s: invalid RX statistic header, len %d\n", __func__,
2298220724Sbschmidt		    stat->cfg_phy_len);
2299178676Ssam		return;
2300178676Ssam	}
2301198429Srpaulo	if (desc->type == IWN_MPDU_RX_DONE) {
2302198429Srpaulo		struct iwn_rx_mpdu *mpdu = (struct iwn_rx_mpdu *)(desc + 1);
2303198429Srpaulo		head = (caddr_t)(mpdu + 1);
2304198429Srpaulo		len = le16toh(mpdu->len);
2305178676Ssam	} else {
2306178676Ssam		head = (caddr_t)(stat + 1) + stat->cfg_phy_len;
2307178676Ssam		len = le16toh(stat->len);
2308178676Ssam	}
2309178676Ssam
2310198429Srpaulo	flags = le32toh(*(uint32_t *)(head + len));
2311198429Srpaulo
2312198429Srpaulo	/* Discard frames with a bad FCS early. */
2313198429Srpaulo	if ((flags & IWN_RX_NOERROR) != IWN_RX_NOERROR) {
2314220724Sbschmidt		DPRINTF(sc, IWN_DEBUG_RECV, "%s: RX flags error %x\n",
2315198429Srpaulo		    __func__, flags);
2316178676Ssam		ifp->if_ierrors++;
2317178676Ssam		return;
2318178676Ssam	}
2319198429Srpaulo	/* Discard frames that are too short. */
2320198429Srpaulo	if (len < sizeof (*wh)) {
2321178676Ssam		DPRINTF(sc, IWN_DEBUG_RECV, "%s: frame too short: %d\n",
2322178676Ssam		    __func__, len);
2323178676Ssam		ifp->if_ierrors++;
2324178676Ssam		return;
2325178676Ssam	}
2326178676Ssam
2327248078Smarius	m1 = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, IWN_RBUF_SIZE);
2328198429Srpaulo	if (m1 == NULL) {
2329178676Ssam		DPRINTF(sc, IWN_DEBUG_ANY, "%s: no mbuf to restock ring\n",
2330178676Ssam		    __func__);
2331178676Ssam		ifp->if_ierrors++;
2332178676Ssam		return;
2333178676Ssam	}
2334201209Srpaulo	bus_dmamap_unload(ring->data_dmat, data->map);
2335201209Srpaulo
2336220692Sbschmidt	error = bus_dmamap_load(ring->data_dmat, data->map, mtod(m1, void *),
2337220692Sbschmidt	    IWN_RBUF_SIZE, iwn_dma_map_addr, &paddr, BUS_DMA_NOWAIT);
2338178676Ssam	if (error != 0 && error != EFBIG) {
2339178676Ssam		device_printf(sc->sc_dev,
2340178676Ssam		    "%s: bus_dmamap_load failed, error %d\n", __func__, error);
2341198429Srpaulo		m_freem(m1);
2342220693Sbschmidt
2343220693Sbschmidt		/* Try to reload the old mbuf. */
2344220693Sbschmidt		error = bus_dmamap_load(ring->data_dmat, data->map,
2345220693Sbschmidt		    mtod(data->m, void *), IWN_RBUF_SIZE, iwn_dma_map_addr,
2346220693Sbschmidt		    &paddr, BUS_DMA_NOWAIT);
2347220693Sbschmidt		if (error != 0 && error != EFBIG) {
2348220693Sbschmidt			panic("%s: could not load old RX mbuf", __func__);
2349220693Sbschmidt		}
2350220693Sbschmidt		/* Physical address may have changed. */
2351220693Sbschmidt		ring->desc[ring->cur] = htole32(paddr >> 8);
2352220693Sbschmidt		bus_dmamap_sync(ring->data_dmat, ring->desc_dma.map,
2353220693Sbschmidt		    BUS_DMASYNC_PREWRITE);
2354178676Ssam		ifp->if_ierrors++;
2355178676Ssam		return;
2356178676Ssam	}
2357178676Ssam
2358178676Ssam	m = data->m;
2359198429Srpaulo	data->m = m1;
2360198429Srpaulo	/* Update RX descriptor. */
2361198429Srpaulo	ring->desc[ring->cur] = htole32(paddr >> 8);
2362201209Srpaulo	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
2363201209Srpaulo	    BUS_DMASYNC_PREWRITE);
2364198429Srpaulo
2365198429Srpaulo	/* Finalize mbuf. */
2366178676Ssam	m->m_pkthdr.rcvif = ifp;
2367178676Ssam	m->m_data = head;
2368178676Ssam	m->m_pkthdr.len = m->m_len = len;
2369178676Ssam
2370198429Srpaulo	/* Grab a reference to the source node. */
2371178676Ssam	wh = mtod(m, struct ieee80211_frame *);
2372178676Ssam	ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh);
2373178676Ssam	nf = (ni != NULL && ni->ni_vap->iv_state == IEEE80211_S_RUN &&
2374178676Ssam	    (ic->ic_flags & IEEE80211_F_SCAN) == 0) ? sc->noise : -95;
2375178676Ssam
2376220728Sbschmidt	rssi = ops->get_rssi(sc, stat);
2377220689Sbschmidt
2378192468Ssam	if (ieee80211_radiotap_active(ic)) {
2379178676Ssam		struct iwn_rx_radiotap_header *tap = &sc->sc_rxtap;
2380178676Ssam
2381178676Ssam		tap->wr_flags = 0;
2382201209Srpaulo		if (stat->flags & htole16(IWN_STAT_FLAG_SHPREAMBLE))
2383192468Ssam			tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
2384220723Sbschmidt		tap->wr_dbm_antsignal = (int8_t)rssi;
2385220723Sbschmidt		tap->wr_dbm_antnoise = (int8_t)nf;
2386220723Sbschmidt		tap->wr_tsft = stat->tstamp;
2387201209Srpaulo		switch (stat->rate) {
2388201209Srpaulo		/* CCK rates. */
2389201209Srpaulo		case  10: tap->wr_rate =   2; break;
2390201209Srpaulo		case  20: tap->wr_rate =   4; break;
2391201209Srpaulo		case  55: tap->wr_rate =  11; break;
2392201209Srpaulo		case 110: tap->wr_rate =  22; break;
2393201209Srpaulo		/* OFDM rates. */
2394201209Srpaulo		case 0xd: tap->wr_rate =  12; break;
2395201209Srpaulo		case 0xf: tap->wr_rate =  18; break;
2396201209Srpaulo		case 0x5: tap->wr_rate =  24; break;
2397201209Srpaulo		case 0x7: tap->wr_rate =  36; break;
2398201209Srpaulo		case 0x9: tap->wr_rate =  48; break;
2399201209Srpaulo		case 0xb: tap->wr_rate =  72; break;
2400201209Srpaulo		case 0x1: tap->wr_rate =  96; break;
2401201209Srpaulo		case 0x3: tap->wr_rate = 108; break;
2402201209Srpaulo		/* Unknown rate: should not happen. */
2403201209Srpaulo		default:  tap->wr_rate =   0;
2404201209Srpaulo		}
2405178676Ssam	}
2406178676Ssam
2407178676Ssam	IWN_UNLOCK(sc);
2408178676Ssam
2409198429Srpaulo	/* Send the frame to the 802.11 layer. */
2410178676Ssam	if (ni != NULL) {
2411221650Sbschmidt		if (ni->ni_flags & IEEE80211_NODE_HT)
2412221650Sbschmidt			m->m_flags |= M_AMPDU;
2413220726Sbschmidt		(void)ieee80211_input(ni, m, rssi - nf, nf);
2414198429Srpaulo		/* Node is no longer needed. */
2415178676Ssam		ieee80211_free_node(ni);
2416178676Ssam	} else
2417220726Sbschmidt		(void)ieee80211_input_all(ic, m, rssi - nf, nf);
2418178676Ssam
2419178676Ssam	IWN_LOCK(sc);
2420178676Ssam}
2421178676Ssam
2422201209Srpaulo/* Process an incoming Compressed BlockAck. */
2423206477Sbschmidtstatic void
2424201209Srpauloiwn_rx_compressed_ba(struct iwn_softc *sc, struct iwn_rx_desc *desc,
2425201209Srpaulo    struct iwn_rx_data *data)
2426201209Srpaulo{
2427237917Sbschmidt	struct iwn_ops *ops = &sc->ops;
2428221651Sbschmidt	struct ifnet *ifp = sc->sc_ifp;
2429221651Sbschmidt	struct iwn_node *wn;
2430221651Sbschmidt	struct ieee80211_node *ni;
2431201209Srpaulo	struct iwn_compressed_ba *ba = (struct iwn_compressed_ba *)(desc + 1);
2432201209Srpaulo	struct iwn_tx_ring *txq;
2433237917Sbschmidt	struct iwn_tx_data *txdata;
2434221651Sbschmidt	struct ieee80211_tx_ampdu *tap;
2435237917Sbschmidt	struct mbuf *m;
2436221651Sbschmidt	uint64_t bitmap;
2437237917Sbschmidt	uint16_t ssn;
2438221651Sbschmidt	uint8_t tid;
2439237917Sbschmidt	int ackfailcnt = 0, i, lastidx, qid, *res, shift;
2440201209Srpaulo
2441220704Sbschmidt	bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD);
2442220704Sbschmidt
2443237917Sbschmidt	qid = le16toh(ba->qid);
2444237917Sbschmidt	txq = &sc->txq[ba->qid];
2445237917Sbschmidt	tap = sc->qid2tap[ba->qid];
2446221651Sbschmidt	tid = WME_AC_TO_TID(tap->txa_ac);
2447237917Sbschmidt	wn = (void *)tap->txa_ni;
2448221651Sbschmidt
2449237917Sbschmidt	res = NULL;
2450237917Sbschmidt	ssn = 0;
2451237917Sbschmidt	if (!IEEE80211_AMPDU_RUNNING(tap)) {
2452237917Sbschmidt		res = tap->txa_private;
2453237917Sbschmidt		ssn = tap->txa_start & 0xfff;
2454237917Sbschmidt	}
2455237917Sbschmidt
2456237917Sbschmidt	for (lastidx = le16toh(ba->ssn) & 0xff; txq->read != lastidx;) {
2457237917Sbschmidt		txdata = &txq->data[txq->read];
2458237917Sbschmidt
2459237917Sbschmidt		/* Unmap and free mbuf. */
2460237917Sbschmidt		bus_dmamap_sync(txq->data_dmat, txdata->map,
2461237917Sbschmidt		    BUS_DMASYNC_POSTWRITE);
2462237917Sbschmidt		bus_dmamap_unload(txq->data_dmat, txdata->map);
2463237917Sbschmidt		m = txdata->m, txdata->m = NULL;
2464237917Sbschmidt		ni = txdata->ni, txdata->ni = NULL;
2465237917Sbschmidt
2466237917Sbschmidt		KASSERT(ni != NULL, ("no node"));
2467237917Sbschmidt		KASSERT(m != NULL, ("no mbuf"));
2468237917Sbschmidt
2469237917Sbschmidt		if (m->m_flags & M_TXCB)
2470237917Sbschmidt			ieee80211_process_callback(ni, m, 1);
2471237917Sbschmidt
2472237917Sbschmidt		m_freem(m);
2473237917Sbschmidt		ieee80211_free_node(ni);
2474237917Sbschmidt
2475237917Sbschmidt		txq->queued--;
2476237917Sbschmidt		txq->read = (txq->read + 1) % IWN_TX_RING_COUNT;
2477237917Sbschmidt	}
2478237917Sbschmidt
2479237917Sbschmidt	if (txq->queued == 0 && res != NULL) {
2480237917Sbschmidt		iwn_nic_lock(sc);
2481237917Sbschmidt		ops->ampdu_tx_stop(sc, qid, tid, ssn);
2482237917Sbschmidt		iwn_nic_unlock(sc);
2483237917Sbschmidt		sc->qid2tap[qid] = NULL;
2484237917Sbschmidt		free(res, M_DEVBUF);
2485237917Sbschmidt		return;
2486237917Sbschmidt	}
2487237917Sbschmidt
2488221651Sbschmidt	if (wn->agg[tid].bitmap == 0)
2489221651Sbschmidt		return;
2490221651Sbschmidt
2491221651Sbschmidt	shift = wn->agg[tid].startidx - ((le16toh(ba->seq) >> 4) & 0xff);
2492221651Sbschmidt	if (shift < 0)
2493221651Sbschmidt		shift += 0x100;
2494221651Sbschmidt
2495221651Sbschmidt	if (wn->agg[tid].nframes > (64 - shift))
2496221651Sbschmidt		return;
2497221651Sbschmidt
2498237917Sbschmidt	ni = tap->txa_ni;
2499221651Sbschmidt	bitmap = (le64toh(ba->bitmap) >> shift) & wn->agg[tid].bitmap;
2500221651Sbschmidt	for (i = 0; bitmap; i++) {
2501221651Sbschmidt		if ((bitmap & 1) == 0) {
2502221651Sbschmidt			ifp->if_oerrors++;
2503221651Sbschmidt			ieee80211_ratectl_tx_complete(ni->ni_vap, ni,
2504221651Sbschmidt			    IEEE80211_RATECTL_TX_FAILURE, &ackfailcnt, NULL);
2505221651Sbschmidt		} else {
2506221651Sbschmidt			ifp->if_opackets++;
2507221651Sbschmidt			ieee80211_ratectl_tx_complete(ni->ni_vap, ni,
2508221651Sbschmidt			    IEEE80211_RATECTL_TX_SUCCESS, &ackfailcnt, NULL);
2509221651Sbschmidt		}
2510221651Sbschmidt		bitmap >>= 1;
2511221651Sbschmidt	}
2512201209Srpaulo}
2513201209Srpaulo
2514198429Srpaulo/*
2515220674Sbschmidt * Process a CALIBRATION_RESULT notification sent by the initialization
2516220674Sbschmidt * firmware on response to a CMD_CALIB_CONFIG command (5000 only).
2517220674Sbschmidt */
2518220674Sbschmidtstatic void
2519220674Sbschmidtiwn5000_rx_calib_results(struct iwn_softc *sc, struct iwn_rx_desc *desc,
2520220674Sbschmidt    struct iwn_rx_data *data)
2521220674Sbschmidt{
2522220674Sbschmidt	struct iwn_phy_calib *calib = (struct iwn_phy_calib *)(desc + 1);
2523220674Sbschmidt	int len, idx = -1;
2524220674Sbschmidt
2525220674Sbschmidt	/* Runtime firmware should not send such a notification. */
2526220674Sbschmidt	if (sc->sc_flags & IWN_FLAG_CALIB_DONE)
2527220674Sbschmidt		return;
2528220674Sbschmidt
2529220674Sbschmidt	len = (le32toh(desc->len) & 0x3fff) - 4;
2530220674Sbschmidt	bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD);
2531220674Sbschmidt
2532220674Sbschmidt	switch (calib->code) {
2533220674Sbschmidt	case IWN5000_PHY_CALIB_DC:
2534220867Sbschmidt		if ((sc->sc_flags & IWN_FLAG_INTERNAL_PA) == 0 &&
2535220867Sbschmidt		    (sc->hw_type == IWN_HW_REV_TYPE_5150 ||
2536227967Sbschmidt		     sc->hw_type >= IWN_HW_REV_TYPE_6000) &&
2537227967Sbschmidt		     sc->hw_type != IWN_HW_REV_TYPE_6050)
2538220674Sbschmidt			idx = 0;
2539220674Sbschmidt		break;
2540220674Sbschmidt	case IWN5000_PHY_CALIB_LO:
2541220674Sbschmidt		idx = 1;
2542220674Sbschmidt		break;
2543220674Sbschmidt	case IWN5000_PHY_CALIB_TX_IQ:
2544220674Sbschmidt		idx = 2;
2545220674Sbschmidt		break;
2546220674Sbschmidt	case IWN5000_PHY_CALIB_TX_IQ_PERIODIC:
2547220674Sbschmidt		if (sc->hw_type < IWN_HW_REV_TYPE_6000 &&
2548220674Sbschmidt		    sc->hw_type != IWN_HW_REV_TYPE_5150)
2549220674Sbschmidt			idx = 3;
2550220674Sbschmidt		break;
2551220674Sbschmidt	case IWN5000_PHY_CALIB_BASE_BAND:
2552220674Sbschmidt		idx = 4;
2553220674Sbschmidt		break;
2554220674Sbschmidt	}
2555220674Sbschmidt	if (idx == -1)	/* Ignore other results. */
2556220674Sbschmidt		return;
2557220674Sbschmidt
2558220674Sbschmidt	/* Save calibration result. */
2559220674Sbschmidt	if (sc->calibcmd[idx].buf != NULL)
2560220674Sbschmidt		free(sc->calibcmd[idx].buf, M_DEVBUF);
2561220674Sbschmidt	sc->calibcmd[idx].buf = malloc(len, M_DEVBUF, M_NOWAIT);
2562220674Sbschmidt	if (sc->calibcmd[idx].buf == NULL) {
2563220674Sbschmidt		DPRINTF(sc, IWN_DEBUG_CALIBRATE,
2564220674Sbschmidt		    "not enough memory for calibration result %d\n",
2565220674Sbschmidt		    calib->code);
2566220674Sbschmidt		return;
2567220674Sbschmidt	}
2568220674Sbschmidt	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
2569220674Sbschmidt	    "saving calibration result code=%d len=%d\n", calib->code, len);
2570220674Sbschmidt	sc->calibcmd[idx].len = len;
2571220674Sbschmidt	memcpy(sc->calibcmd[idx].buf, calib, len);
2572220674Sbschmidt}
2573220674Sbschmidt
2574220674Sbschmidt/*
2575198429Srpaulo * Process an RX_STATISTICS or BEACON_STATISTICS firmware notification.
2576198429Srpaulo * The latter is sent by the firmware after each received beacon.
2577198429Srpaulo */
2578206477Sbschmidtstatic void
2579198429Srpauloiwn_rx_statistics(struct iwn_softc *sc, struct iwn_rx_desc *desc,
2580198429Srpaulo    struct iwn_rx_data *data)
2581198429Srpaulo{
2582220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
2583178676Ssam	struct ifnet *ifp = sc->sc_ifp;
2584178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
2585178676Ssam	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
2586178676Ssam	struct iwn_calib_state *calib = &sc->calib;
2587178676Ssam	struct iwn_stats *stats = (struct iwn_stats *)(desc + 1);
2588198429Srpaulo	int temp;
2589178676Ssam
2590220725Sbschmidt	/* Ignore statistics received during a scan. */
2591178676Ssam	if (vap->iv_state != IEEE80211_S_RUN ||
2592178676Ssam	    (ic->ic_flags & IEEE80211_F_SCAN))
2593178676Ssam		return;
2594178676Ssam
2595202986Srpaulo	bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD);
2596220724Sbschmidt
2597220724Sbschmidt	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: received statistics, cmd %d\n",
2598220724Sbschmidt	    __func__, desc->type);
2599220667Sbschmidt	sc->calib_cnt = 0;	/* Reset TX power calibration timeout. */
2600178676Ssam
2601198429Srpaulo	/* Test if temperature has changed. */
2602178676Ssam	if (stats->general.temp != sc->rawtemp) {
2603198429Srpaulo		/* Convert "raw" temperature to degC. */
2604178676Ssam		sc->rawtemp = stats->general.temp;
2605220728Sbschmidt		temp = ops->get_temperature(sc);
2606178676Ssam		DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: temperature %d\n",
2607178676Ssam		    __func__, temp);
2608178676Ssam
2609220725Sbschmidt		/* Update TX power if need be (4965AGN only). */
2610198429Srpaulo		if (sc->hw_type == IWN_HW_REV_TYPE_4965)
2611198429Srpaulo			iwn4965_power_calibration(sc, temp);
2612178676Ssam	}
2613178676Ssam
2614178676Ssam	if (desc->type != IWN_BEACON_STATISTICS)
2615198429Srpaulo		return;	/* Reply to a statistics request. */
2616178676Ssam
2617178676Ssam	sc->noise = iwn_get_noise(&stats->rx.general);
2618178676Ssam	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: noise %d\n", __func__, sc->noise);
2619178676Ssam
2620198429Srpaulo	/* Test that RSSI and noise are present in stats report. */
2621198429Srpaulo	if (le32toh(stats->rx.general.flags) != 1) {
2622178676Ssam		DPRINTF(sc, IWN_DEBUG_ANY, "%s\n",
2623178676Ssam		    "received statistics without RSSI");
2624178676Ssam		return;
2625178676Ssam	}
2626178676Ssam
2627178676Ssam	if (calib->state == IWN_CALIB_STATE_ASSOC)
2628198429Srpaulo		iwn_collect_noise(sc, &stats->rx.general);
2629178676Ssam	else if (calib->state == IWN_CALIB_STATE_RUN)
2630178676Ssam		iwn_tune_sensitivity(sc, &stats->rx);
2631178676Ssam}
2632178676Ssam
2633198429Srpaulo/*
2634198429Srpaulo * Process a TX_DONE firmware notification.  Unfortunately, the 4965AGN
2635198429Srpaulo * and 5000 adapters have different incompatible TX status formats.
2636198429Srpaulo */
2637206477Sbschmidtstatic void
2638198429Srpauloiwn4965_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc,
2639198429Srpaulo    struct iwn_rx_data *data)
2640178676Ssam{
2641198429Srpaulo	struct iwn4965_tx_stat *stat = (struct iwn4965_tx_stat *)(desc + 1);
2642221651Sbschmidt	struct iwn_tx_ring *ring;
2643221651Sbschmidt	int qid;
2644198429Srpaulo
2645221651Sbschmidt	qid = desc->qid & 0xf;
2646221651Sbschmidt	ring = &sc->txq[qid];
2647221651Sbschmidt
2648198429Srpaulo	DPRINTF(sc, IWN_DEBUG_XMIT, "%s: "
2649198429Srpaulo	    "qid %d idx %d retries %d nkill %d rate %x duration %d status %x\n",
2650201209Srpaulo	    __func__, desc->qid, desc->idx, stat->ackfailcnt,
2651201209Srpaulo	    stat->btkillcnt, stat->rate, le16toh(stat->duration),
2652198429Srpaulo	    le32toh(stat->status));
2653201209Srpaulo
2654207001Sbschmidt	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD);
2655221651Sbschmidt	if (qid >= sc->firstaggqueue) {
2656221651Sbschmidt		iwn_ampdu_tx_done(sc, qid, desc->idx, stat->nframes,
2657221651Sbschmidt		    &stat->status);
2658221651Sbschmidt	} else {
2659221651Sbschmidt		iwn_tx_done(sc, desc, stat->ackfailcnt,
2660221651Sbschmidt		    le32toh(stat->status) & 0xff);
2661221651Sbschmidt	}
2662198429Srpaulo}
2663198429Srpaulo
2664206477Sbschmidtstatic void
2665198429Srpauloiwn5000_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc,
2666198429Srpaulo    struct iwn_rx_data *data)
2667198429Srpaulo{
2668198429Srpaulo	struct iwn5000_tx_stat *stat = (struct iwn5000_tx_stat *)(desc + 1);
2669221651Sbschmidt	struct iwn_tx_ring *ring;
2670221651Sbschmidt	int qid;
2671198429Srpaulo
2672221651Sbschmidt	qid = desc->qid & 0xf;
2673221651Sbschmidt	ring = &sc->txq[qid];
2674221651Sbschmidt
2675198429Srpaulo	DPRINTF(sc, IWN_DEBUG_XMIT, "%s: "
2676198429Srpaulo	    "qid %d idx %d retries %d nkill %d rate %x duration %d status %x\n",
2677201209Srpaulo	    __func__, desc->qid, desc->idx, stat->ackfailcnt,
2678201209Srpaulo	    stat->btkillcnt, stat->rate, le16toh(stat->duration),
2679198429Srpaulo	    le32toh(stat->status));
2680198429Srpaulo
2681201209Srpaulo#ifdef notyet
2682198429Srpaulo	/* Reset TX scheduler slot. */
2683198429Srpaulo	iwn5000_reset_sched(sc, desc->qid & 0xf, desc->idx);
2684201209Srpaulo#endif
2685202986Srpaulo
2686207001Sbschmidt	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD);
2687221651Sbschmidt	if (qid >= sc->firstaggqueue) {
2688221651Sbschmidt		iwn_ampdu_tx_done(sc, qid, desc->idx, stat->nframes,
2689221651Sbschmidt		    &stat->status);
2690221651Sbschmidt	} else {
2691221651Sbschmidt		iwn_tx_done(sc, desc, stat->ackfailcnt,
2692221651Sbschmidt		    le16toh(stat->status) & 0xff);
2693221651Sbschmidt	}
2694198429Srpaulo}
2695198429Srpaulo
2696198429Srpaulo/*
2697198429Srpaulo * Adapter-independent backend for TX_DONE firmware notifications.
2698198429Srpaulo */
2699206477Sbschmidtstatic void
2700201209Srpauloiwn_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, int ackfailcnt,
2701198429Srpaulo    uint8_t status)
2702198429Srpaulo{
2703178676Ssam	struct ifnet *ifp = sc->sc_ifp;
2704178676Ssam	struct iwn_tx_ring *ring = &sc->txq[desc->qid & 0xf];
2705178676Ssam	struct iwn_tx_data *data = &ring->data[desc->idx];
2706178676Ssam	struct mbuf *m;
2707178676Ssam	struct ieee80211_node *ni;
2708206358Srpaulo	struct ieee80211vap *vap;
2709178676Ssam
2710178676Ssam	KASSERT(data->ni != NULL, ("no node"));
2711178676Ssam
2712198429Srpaulo	/* Unmap and free mbuf. */
2713201209Srpaulo	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTWRITE);
2714201209Srpaulo	bus_dmamap_unload(ring->data_dmat, data->map);
2715178676Ssam	m = data->m, data->m = NULL;
2716178676Ssam	ni = data->ni, data->ni = NULL;
2717206358Srpaulo	vap = ni->ni_vap;
2718178676Ssam
2719178676Ssam	if (m->m_flags & M_TXCB) {
2720178676Ssam		/*
2721178676Ssam		 * Channels marked for "radar" require traffic to be received
2722178676Ssam		 * to unlock before we can transmit.  Until traffic is seen
2723178676Ssam		 * any attempt to transmit is returned immediately with status
2724178676Ssam		 * set to IWN_TX_FAIL_TX_LOCKED.  Unfortunately this can easily
2725178676Ssam		 * happen on first authenticate after scanning.  To workaround
2726178676Ssam		 * this we ignore a failure of this sort in AUTH state so the
2727178676Ssam		 * 802.11 layer will fall back to using a timeout to wait for
2728178676Ssam		 * the AUTH reply.  This allows the firmware time to see
2729178676Ssam		 * traffic so a subsequent retry of AUTH succeeds.  It's
2730178676Ssam		 * unclear why the firmware does not maintain state for
2731178676Ssam		 * channels recently visited as this would allow immediate
2732178676Ssam		 * use of the channel after a scan (where we see traffic).
2733178676Ssam		 */
2734178676Ssam		if (status == IWN_TX_FAIL_TX_LOCKED &&
2735178676Ssam		    ni->ni_vap->iv_state == IEEE80211_S_AUTH)
2736178676Ssam			ieee80211_process_callback(ni, m, 0);
2737178676Ssam		else
2738178676Ssam			ieee80211_process_callback(ni, m,
2739178676Ssam			    (status & IWN_TX_FAIL) != 0);
2740178676Ssam	}
2741201209Srpaulo
2742201209Srpaulo	/*
2743201209Srpaulo	 * Update rate control statistics for the node.
2744201209Srpaulo	 */
2745220719Sbschmidt	if (status & IWN_TX_FAIL) {
2746201209Srpaulo		ifp->if_oerrors++;
2747206358Srpaulo		ieee80211_ratectl_tx_complete(vap, ni,
2748206358Srpaulo		    IEEE80211_RATECTL_TX_FAILURE, &ackfailcnt, NULL);
2749201209Srpaulo	} else {
2750220719Sbschmidt		ifp->if_opackets++;
2751206358Srpaulo		ieee80211_ratectl_tx_complete(vap, ni,
2752206358Srpaulo		    IEEE80211_RATECTL_TX_SUCCESS, &ackfailcnt, NULL);
2753201209Srpaulo	}
2754178676Ssam	m_freem(m);
2755178676Ssam	ieee80211_free_node(ni);
2756178676Ssam
2757178676Ssam	sc->sc_tx_timer = 0;
2758198429Srpaulo	if (--ring->queued < IWN_TX_RING_LOMARK) {
2759198429Srpaulo		sc->qfullmsk &= ~(1 << ring->qid);
2760198429Srpaulo		if (sc->qfullmsk == 0 &&
2761198429Srpaulo		    (ifp->if_drv_flags & IFF_DRV_OACTIVE)) {
2762198429Srpaulo			ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
2763198429Srpaulo			iwn_start_locked(ifp);
2764198429Srpaulo		}
2765198429Srpaulo	}
2766178676Ssam}
2767178676Ssam
2768198429Srpaulo/*
2769198429Srpaulo * Process a "command done" firmware notification.  This is where we wakeup
2770198429Srpaulo * processes waiting for a synchronous command completion.
2771198429Srpaulo */
2772206477Sbschmidtstatic void
2773198429Srpauloiwn_cmd_done(struct iwn_softc *sc, struct iwn_rx_desc *desc)
2774178676Ssam{
2775178676Ssam	struct iwn_tx_ring *ring = &sc->txq[4];
2776178676Ssam	struct iwn_tx_data *data;
2777178676Ssam
2778178676Ssam	if ((desc->qid & 0xf) != 4)
2779198429Srpaulo		return;	/* Not a command ack. */
2780178676Ssam
2781178676Ssam	data = &ring->data[desc->idx];
2782178676Ssam
2783198429Srpaulo	/* If the command was mapped in an mbuf, free it. */
2784178676Ssam	if (data->m != NULL) {
2785220704Sbschmidt		bus_dmamap_sync(ring->data_dmat, data->map,
2786220704Sbschmidt		    BUS_DMASYNC_POSTWRITE);
2787201209Srpaulo		bus_dmamap_unload(ring->data_dmat, data->map);
2788178676Ssam		m_freem(data->m);
2789178676Ssam		data->m = NULL;
2790178676Ssam	}
2791198439Srpaulo	wakeup(&ring->desc[desc->idx]);
2792178676Ssam}
2793178676Ssam
2794221651Sbschmidtstatic void
2795221651Sbschmidtiwn_ampdu_tx_done(struct iwn_softc *sc, int qid, int idx, int nframes,
2796221651Sbschmidt    void *stat)
2797221651Sbschmidt{
2798237917Sbschmidt	struct iwn_ops *ops = &sc->ops;
2799221651Sbschmidt	struct ifnet *ifp = sc->sc_ifp;
2800221651Sbschmidt	struct iwn_tx_ring *ring = &sc->txq[qid];
2801221651Sbschmidt	struct iwn_tx_data *data;
2802221651Sbschmidt	struct mbuf *m;
2803221651Sbschmidt	struct iwn_node *wn;
2804221651Sbschmidt	struct ieee80211_node *ni;
2805221651Sbschmidt	struct ieee80211_tx_ampdu *tap;
2806221651Sbschmidt	uint64_t bitmap;
2807221651Sbschmidt	uint32_t *status = stat;
2808221651Sbschmidt	uint16_t *aggstatus = stat;
2809237917Sbschmidt	uint16_t ssn;
2810221651Sbschmidt	uint8_t tid;
2811237917Sbschmidt	int bit, i, lastidx, *res, seqno, shift, start;
2812221651Sbschmidt
2813221651Sbschmidt#ifdef NOT_YET
2814221651Sbschmidt	if (nframes == 1) {
2815221651Sbschmidt		if ((*status & 0xff) != 1 && (*status & 0xff) != 2)
2816221651Sbschmidt			printf("ieee80211_send_bar()\n");
2817221651Sbschmidt	}
2818221651Sbschmidt#endif
2819221651Sbschmidt
2820221651Sbschmidt	bitmap = 0;
2821221651Sbschmidt	start = idx;
2822221651Sbschmidt	for (i = 0; i < nframes; i++) {
2823221651Sbschmidt		if (le16toh(aggstatus[i * 2]) & 0xc)
2824221651Sbschmidt			continue;
2825221651Sbschmidt
2826221651Sbschmidt		idx = le16toh(aggstatus[2*i + 1]) & 0xff;
2827221651Sbschmidt		bit = idx - start;
2828221651Sbschmidt		shift = 0;
2829221651Sbschmidt		if (bit >= 64) {
2830221651Sbschmidt			shift = 0x100 - idx + start;
2831221651Sbschmidt			bit = 0;
2832221651Sbschmidt			start = idx;
2833221651Sbschmidt		} else if (bit <= -64)
2834221651Sbschmidt			bit = 0x100 - start + idx;
2835221651Sbschmidt		else if (bit < 0) {
2836221651Sbschmidt			shift = start - idx;
2837221651Sbschmidt			start = idx;
2838221651Sbschmidt			bit = 0;
2839221651Sbschmidt		}
2840221651Sbschmidt		bitmap = bitmap << shift;
2841221651Sbschmidt		bitmap |= 1ULL << bit;
2842221651Sbschmidt	}
2843221651Sbschmidt	tap = sc->qid2tap[qid];
2844237917Sbschmidt	tid = WME_AC_TO_TID(tap->txa_ac);
2845237917Sbschmidt	wn = (void *)tap->txa_ni;
2846237917Sbschmidt	wn->agg[tid].bitmap = bitmap;
2847237917Sbschmidt	wn->agg[tid].startidx = start;
2848237917Sbschmidt	wn->agg[tid].nframes = nframes;
2849237917Sbschmidt
2850237917Sbschmidt	res = NULL;
2851237917Sbschmidt	ssn = 0;
2852237917Sbschmidt	if (!IEEE80211_AMPDU_RUNNING(tap)) {
2853237917Sbschmidt		res = tap->txa_private;
2854237917Sbschmidt		ssn = tap->txa_start & 0xfff;
2855231143Sbschmidt	}
2856221651Sbschmidt
2857221651Sbschmidt	seqno = le32toh(*(status + nframes)) & 0xfff;
2858221651Sbschmidt	for (lastidx = (seqno & 0xff); ring->read != lastidx;) {
2859221651Sbschmidt		data = &ring->data[ring->read];
2860221651Sbschmidt
2861221651Sbschmidt		/* Unmap and free mbuf. */
2862221651Sbschmidt		bus_dmamap_sync(ring->data_dmat, data->map,
2863221651Sbschmidt		    BUS_DMASYNC_POSTWRITE);
2864221651Sbschmidt		bus_dmamap_unload(ring->data_dmat, data->map);
2865221651Sbschmidt		m = data->m, data->m = NULL;
2866221651Sbschmidt		ni = data->ni, data->ni = NULL;
2867221651Sbschmidt
2868237917Sbschmidt		KASSERT(ni != NULL, ("no node"));
2869237917Sbschmidt		KASSERT(m != NULL, ("no mbuf"));
2870237917Sbschmidt
2871221651Sbschmidt		if (m->m_flags & M_TXCB)
2872221651Sbschmidt			ieee80211_process_callback(ni, m, 1);
2873221651Sbschmidt
2874221651Sbschmidt		m_freem(m);
2875221651Sbschmidt		ieee80211_free_node(ni);
2876221651Sbschmidt
2877221651Sbschmidt		ring->queued--;
2878221651Sbschmidt		ring->read = (ring->read + 1) % IWN_TX_RING_COUNT;
2879221651Sbschmidt	}
2880221651Sbschmidt
2881237917Sbschmidt	if (ring->queued == 0 && res != NULL) {
2882237917Sbschmidt		iwn_nic_lock(sc);
2883237917Sbschmidt		ops->ampdu_tx_stop(sc, qid, tid, ssn);
2884237917Sbschmidt		iwn_nic_unlock(sc);
2885237917Sbschmidt		sc->qid2tap[qid] = NULL;
2886237917Sbschmidt		free(res, M_DEVBUF);
2887237917Sbschmidt		return;
2888237917Sbschmidt	}
2889237917Sbschmidt
2890221651Sbschmidt	sc->sc_tx_timer = 0;
2891221651Sbschmidt	if (ring->queued < IWN_TX_RING_LOMARK) {
2892221651Sbschmidt		sc->qfullmsk &= ~(1 << ring->qid);
2893221651Sbschmidt		if (sc->qfullmsk == 0 &&
2894221651Sbschmidt		    (ifp->if_drv_flags & IFF_DRV_OACTIVE)) {
2895221651Sbschmidt			ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
2896221651Sbschmidt			iwn_start_locked(ifp);
2897221651Sbschmidt		}
2898221651Sbschmidt	}
2899221651Sbschmidt}
2900221651Sbschmidt
2901198429Srpaulo/*
2902198429Srpaulo * Process an INT_FH_RX or INT_SW_RX interrupt.
2903198429Srpaulo */
2904206477Sbschmidtstatic void
2905178676Ssamiwn_notif_intr(struct iwn_softc *sc)
2906178676Ssam{
2907220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
2908178676Ssam	struct ifnet *ifp = sc->sc_ifp;
2909178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
2910178676Ssam	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
2911178676Ssam	uint16_t hw;
2912178676Ssam
2913198429Srpaulo	bus_dmamap_sync(sc->rxq.stat_dma.tag, sc->rxq.stat_dma.map,
2914198429Srpaulo	    BUS_DMASYNC_POSTREAD);
2915198429Srpaulo
2916198429Srpaulo	hw = le16toh(sc->rxq.stat->closed_count) & 0xfff;
2917178676Ssam	while (sc->rxq.cur != hw) {
2918178676Ssam		struct iwn_rx_data *data = &sc->rxq.data[sc->rxq.cur];
2919198429Srpaulo		struct iwn_rx_desc *desc;
2920178676Ssam
2921201209Srpaulo		bus_dmamap_sync(sc->rxq.data_dmat, data->map,
2922201209Srpaulo		    BUS_DMASYNC_POSTREAD);
2923198429Srpaulo		desc = mtod(data->m, struct iwn_rx_desc *);
2924198429Srpaulo
2925178676Ssam		DPRINTF(sc, IWN_DEBUG_RECV,
2926178676Ssam		    "%s: qid %x idx %d flags %x type %d(%s) len %d\n",
2927198439Srpaulo		    __func__, desc->qid & 0xf, desc->idx, desc->flags,
2928178676Ssam		    desc->type, iwn_intr_str(desc->type),
2929178676Ssam		    le16toh(desc->len));
2930178676Ssam
2931198429Srpaulo		if (!(desc->qid & 0x80))	/* Reply to a command. */
2932198429Srpaulo			iwn_cmd_done(sc, desc);
2933178676Ssam
2934178676Ssam		switch (desc->type) {
2935198429Srpaulo		case IWN_RX_PHY:
2936198429Srpaulo			iwn_rx_phy(sc, desc, data);
2937178676Ssam			break;
2938178676Ssam
2939198429Srpaulo		case IWN_RX_DONE:		/* 4965AGN only. */
2940198429Srpaulo		case IWN_MPDU_RX_DONE:
2941198429Srpaulo			/* An 802.11 frame has been received. */
2942198429Srpaulo			iwn_rx_done(sc, desc, data);
2943178676Ssam			break;
2944178676Ssam
2945201209Srpaulo		case IWN_RX_COMPRESSED_BA:
2946201209Srpaulo			/* A Compressed BlockAck has been received. */
2947201209Srpaulo			iwn_rx_compressed_ba(sc, desc, data);
2948201209Srpaulo			break;
2949201209Srpaulo
2950178676Ssam		case IWN_TX_DONE:
2951198429Srpaulo			/* An 802.11 frame has been transmitted. */
2952220728Sbschmidt			ops->tx_done(sc, desc, data);
2953178676Ssam			break;
2954178676Ssam
2955178676Ssam		case IWN_RX_STATISTICS:
2956178676Ssam		case IWN_BEACON_STATISTICS:
2957198429Srpaulo			iwn_rx_statistics(sc, desc, data);
2958178676Ssam			break;
2959178676Ssam
2960198429Srpaulo		case IWN_BEACON_MISSED:
2961198429Srpaulo		{
2962178676Ssam			struct iwn_beacon_missed *miss =
2963178676Ssam			    (struct iwn_beacon_missed *)(desc + 1);
2964202986Srpaulo			int misses;
2965178676Ssam
2966201209Srpaulo			bus_dmamap_sync(sc->rxq.data_dmat, data->map,
2967201209Srpaulo			    BUS_DMASYNC_POSTREAD);
2968202986Srpaulo			misses = le32toh(miss->consecutive);
2969201209Srpaulo
2970178676Ssam			DPRINTF(sc, IWN_DEBUG_STATE,
2971178676Ssam			    "%s: beacons missed %d/%d\n", __func__,
2972178676Ssam			    misses, le32toh(miss->total));
2973178676Ssam			/*
2974178676Ssam			 * If more than 5 consecutive beacons are missed,
2975178676Ssam			 * reinitialize the sensitivity state machine.
2976178676Ssam			 */
2977220660Sbschmidt			if (vap->iv_state == IEEE80211_S_RUN &&
2978226626Sbschmidt			    (ic->ic_flags & IEEE80211_F_SCAN) == 0) {
2979220660Sbschmidt				if (misses > 5)
2980220660Sbschmidt					(void)iwn_init_sensitivity(sc);
2981220660Sbschmidt				if (misses >= vap->iv_bmissthreshold) {
2982220660Sbschmidt					IWN_UNLOCK(sc);
2983220660Sbschmidt					ieee80211_beacon_miss(ic);
2984220660Sbschmidt					IWN_LOCK(sc);
2985220660Sbschmidt				}
2986201209Srpaulo			}
2987178676Ssam			break;
2988178676Ssam		}
2989198429Srpaulo		case IWN_UC_READY:
2990198429Srpaulo		{
2991178676Ssam			struct iwn_ucode_info *uc =
2992178676Ssam			    (struct iwn_ucode_info *)(desc + 1);
2993178676Ssam
2994198429Srpaulo			/* The microcontroller is ready. */
2995201209Srpaulo			bus_dmamap_sync(sc->rxq.data_dmat, data->map,
2996201209Srpaulo			    BUS_DMASYNC_POSTREAD);
2997178676Ssam			DPRINTF(sc, IWN_DEBUG_RESET,
2998178676Ssam			    "microcode alive notification version=%d.%d "
2999178676Ssam			    "subtype=%x alive=%x\n", uc->major, uc->minor,
3000178676Ssam			    uc->subtype, le32toh(uc->valid));
3001178676Ssam
3002178676Ssam			if (le32toh(uc->valid) != 1) {
3003178676Ssam				device_printf(sc->sc_dev,
3004198429Srpaulo				    "microcontroller initialization failed");
3005178676Ssam				break;
3006178676Ssam			}
3007178676Ssam			if (uc->subtype == IWN_UCODE_INIT) {
3008201209Srpaulo				/* Save microcontroller report. */
3009178676Ssam				memcpy(&sc->ucode_info, uc, sizeof (*uc));
3010178676Ssam			}
3011198429Srpaulo			/* Save the address of the error log in SRAM. */
3012198429Srpaulo			sc->errptr = le32toh(uc->errptr);
3013178676Ssam			break;
3014178676Ssam		}
3015198429Srpaulo		case IWN_STATE_CHANGED:
3016198429Srpaulo		{
3017178676Ssam			uint32_t *status = (uint32_t *)(desc + 1);
3018178676Ssam
3019178676Ssam			/*
3020178676Ssam			 * State change allows hardware switch change to be
3021178676Ssam			 * noted. However, we handle this in iwn_intr as we
3022178676Ssam			 * get both the enable/disble intr.
3023178676Ssam			 */
3024201209Srpaulo			bus_dmamap_sync(sc->rxq.data_dmat, data->map,
3025201209Srpaulo			    BUS_DMASYNC_POSTREAD);
3026178676Ssam			DPRINTF(sc, IWN_DEBUG_INTR, "state changed to %x\n",
3027178676Ssam			    le32toh(*status));
3028178676Ssam			break;
3029178676Ssam		}
3030198429Srpaulo		case IWN_START_SCAN:
3031198429Srpaulo		{
3032178676Ssam			struct iwn_start_scan *scan =
3033178676Ssam			    (struct iwn_start_scan *)(desc + 1);
3034178676Ssam
3035201209Srpaulo			bus_dmamap_sync(sc->rxq.data_dmat, data->map,
3036201209Srpaulo			    BUS_DMASYNC_POSTREAD);
3037178676Ssam			DPRINTF(sc, IWN_DEBUG_ANY,
3038178676Ssam			    "%s: scanning channel %d status %x\n",
3039178676Ssam			    __func__, scan->chan, le32toh(scan->status));
3040178676Ssam			break;
3041178676Ssam		}
3042198429Srpaulo		case IWN_STOP_SCAN:
3043198429Srpaulo		{
3044178676Ssam			struct iwn_stop_scan *scan =
3045178676Ssam			    (struct iwn_stop_scan *)(desc + 1);
3046178676Ssam
3047201209Srpaulo			bus_dmamap_sync(sc->rxq.data_dmat, data->map,
3048201209Srpaulo			    BUS_DMASYNC_POSTREAD);
3049178676Ssam			DPRINTF(sc, IWN_DEBUG_STATE,
3050178676Ssam			    "scan finished nchan=%d status=%d chan=%d\n",
3051178676Ssam			    scan->nchan, scan->status, scan->chan);
3052178676Ssam
3053201209Srpaulo			IWN_UNLOCK(sc);
3054191746Sthompsa			ieee80211_scan_next(vap);
3055201209Srpaulo			IWN_LOCK(sc);
3056178676Ssam			break;
3057178676Ssam		}
3058198429Srpaulo		case IWN5000_CALIBRATION_RESULT:
3059220674Sbschmidt			iwn5000_rx_calib_results(sc, desc, data);
3060198429Srpaulo			break;
3061198429Srpaulo
3062198429Srpaulo		case IWN5000_CALIBRATION_DONE:
3063201209Srpaulo			sc->sc_flags |= IWN_FLAG_CALIB_DONE;
3064198429Srpaulo			wakeup(sc);
3065198429Srpaulo			break;
3066178676Ssam		}
3067198429Srpaulo
3068178676Ssam		sc->rxq.cur = (sc->rxq.cur + 1) % IWN_RX_RING_COUNT;
3069178676Ssam	}
3070178676Ssam
3071198429Srpaulo	/* Tell the firmware what we have processed. */
3072178676Ssam	hw = (hw == 0) ? IWN_RX_RING_COUNT - 1 : hw - 1;
3073198429Srpaulo	IWN_WRITE(sc, IWN_FH_RX_WPTR, hw & ~7);
3074178676Ssam}
3075178676Ssam
3076198429Srpaulo/*
3077198429Srpaulo * Process an INT_WAKEUP interrupt raised when the microcontroller wakes up
3078198429Srpaulo * from power-down sleep mode.
3079198429Srpaulo */
3080206477Sbschmidtstatic void
3081198429Srpauloiwn_wakeup_intr(struct iwn_softc *sc)
3082198429Srpaulo{
3083198429Srpaulo	int qid;
3084198429Srpaulo
3085198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "%s: ucode wakeup from power-down sleep\n",
3086198429Srpaulo	    __func__);
3087198429Srpaulo
3088198429Srpaulo	/* Wakeup RX and TX rings. */
3089198429Srpaulo	IWN_WRITE(sc, IWN_FH_RX_WPTR, sc->rxq.cur & ~7);
3090220728Sbschmidt	for (qid = 0; qid < sc->ntxqs; qid++) {
3091198429Srpaulo		struct iwn_tx_ring *ring = &sc->txq[qid];
3092198429Srpaulo		IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | ring->cur);
3093198429Srpaulo	}
3094198429Srpaulo}
3095198429Srpaulo
3096206477Sbschmidtstatic void
3097191746Sthompsaiwn_rftoggle_intr(struct iwn_softc *sc)
3098191746Sthompsa{
3099191746Sthompsa	struct ifnet *ifp = sc->sc_ifp;
3100191746Sthompsa	struct ieee80211com *ic = ifp->if_l2com;
3101198429Srpaulo	uint32_t tmp = IWN_READ(sc, IWN_GP_CNTRL);
3102191746Sthompsa
3103191746Sthompsa	IWN_LOCK_ASSERT(sc);
3104191746Sthompsa
3105191746Sthompsa	device_printf(sc->sc_dev, "RF switch: radio %s\n",
3106198429Srpaulo	    (tmp & IWN_GP_CNTRL_RFKILL) ? "enabled" : "disabled");
3107198429Srpaulo	if (tmp & IWN_GP_CNTRL_RFKILL)
3108191746Sthompsa		ieee80211_runtask(ic, &sc->sc_radioon_task);
3109191746Sthompsa	else
3110191746Sthompsa		ieee80211_runtask(ic, &sc->sc_radiooff_task);
3111191746Sthompsa}
3112191746Sthompsa
3113198429Srpaulo/*
3114198429Srpaulo * Dump the error log of the firmware when a firmware panic occurs.  Although
3115198429Srpaulo * we can't debug the firmware because it is neither open source nor free, it
3116198429Srpaulo * can help us to identify certain classes of problems.
3117198429Srpaulo */
3118206477Sbschmidtstatic void
3119201209Srpauloiwn_fatal_intr(struct iwn_softc *sc)
3120191746Sthompsa{
3121198429Srpaulo	struct iwn_fw_dump dump;
3122198429Srpaulo	int i;
3123191746Sthompsa
3124191746Sthompsa	IWN_LOCK_ASSERT(sc);
3125191746Sthompsa
3126201209Srpaulo	/* Force a complete recalibration on next init. */
3127201209Srpaulo	sc->sc_flags &= ~IWN_FLAG_CALIB_DONE;
3128201209Srpaulo
3129198429Srpaulo	/* Check that the error log address is valid. */
3130198429Srpaulo	if (sc->errptr < IWN_FW_DATA_BASE ||
3131198429Srpaulo	    sc->errptr + sizeof (dump) >
3132220728Sbschmidt	    IWN_FW_DATA_BASE + sc->fw_data_maxsz) {
3133220726Sbschmidt		printf("%s: bad firmware error log address 0x%08x\n", __func__,
3134220726Sbschmidt		    sc->errptr);
3135198429Srpaulo		return;
3136198429Srpaulo	}
3137198429Srpaulo	if (iwn_nic_lock(sc) != 0) {
3138220726Sbschmidt		printf("%s: could not read firmware error log\n", __func__);
3139198429Srpaulo		return;
3140198429Srpaulo	}
3141198429Srpaulo	/* Read firmware error log from SRAM. */
3142198429Srpaulo	iwn_mem_read_region_4(sc, sc->errptr, (uint32_t *)&dump,
3143198429Srpaulo	    sizeof (dump) / sizeof (uint32_t));
3144198429Srpaulo	iwn_nic_unlock(sc);
3145198429Srpaulo
3146198429Srpaulo	if (dump.valid == 0) {
3147220726Sbschmidt		printf("%s: firmware error log is empty\n", __func__);
3148198429Srpaulo		return;
3149198429Srpaulo	}
3150198429Srpaulo	printf("firmware error log:\n");
3151198429Srpaulo	printf("  error type      = \"%s\" (0x%08X)\n",
3152198429Srpaulo	    (dump.id < nitems(iwn_fw_errmsg)) ?
3153198429Srpaulo		iwn_fw_errmsg[dump.id] : "UNKNOWN",
3154198429Srpaulo	    dump.id);
3155198429Srpaulo	printf("  program counter = 0x%08X\n", dump.pc);
3156198429Srpaulo	printf("  source line     = 0x%08X\n", dump.src_line);
3157198429Srpaulo	printf("  error data      = 0x%08X%08X\n",
3158198429Srpaulo	    dump.error_data[0], dump.error_data[1]);
3159198429Srpaulo	printf("  branch link     = 0x%08X%08X\n",
3160198429Srpaulo	    dump.branch_link[0], dump.branch_link[1]);
3161198429Srpaulo	printf("  interrupt link  = 0x%08X%08X\n",
3162198429Srpaulo	    dump.interrupt_link[0], dump.interrupt_link[1]);
3163198429Srpaulo	printf("  time            = %u\n", dump.time[0]);
3164198429Srpaulo
3165198429Srpaulo	/* Dump driver status (TX and RX rings) while we're here. */
3166198429Srpaulo	printf("driver status:\n");
3167220728Sbschmidt	for (i = 0; i < sc->ntxqs; i++) {
3168198429Srpaulo		struct iwn_tx_ring *ring = &sc->txq[i];
3169198429Srpaulo		printf("  tx ring %2d: qid=%-2d cur=%-3d queued=%-3d\n",
3170198429Srpaulo		    i, ring->qid, ring->cur, ring->queued);
3171198429Srpaulo	}
3172198429Srpaulo	printf("  rx ring: cur=%d\n", sc->rxq.cur);
3173191746Sthompsa}
3174191746Sthompsa
3175206477Sbschmidtstatic void
3176178676Ssamiwn_intr(void *arg)
3177178676Ssam{
3178178676Ssam	struct iwn_softc *sc = arg;
3179198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
3180201209Srpaulo	uint32_t r1, r2, tmp;
3181178676Ssam
3182178676Ssam	IWN_LOCK(sc);
3183178676Ssam
3184198429Srpaulo	/* Disable interrupts. */
3185201209Srpaulo	IWN_WRITE(sc, IWN_INT_MASK, 0);
3186178676Ssam
3187201209Srpaulo	/* Read interrupts from ICT (fast) or from registers (slow). */
3188201209Srpaulo	if (sc->sc_flags & IWN_FLAG_USE_ICT) {
3189201209Srpaulo		tmp = 0;
3190201209Srpaulo		while (sc->ict[sc->ict_cur] != 0) {
3191201209Srpaulo			tmp |= sc->ict[sc->ict_cur];
3192201209Srpaulo			sc->ict[sc->ict_cur] = 0;	/* Acknowledge. */
3193201209Srpaulo			sc->ict_cur = (sc->ict_cur + 1) % IWN_ICT_COUNT;
3194201209Srpaulo		}
3195201209Srpaulo		tmp = le32toh(tmp);
3196206444Sbschmidt		if (tmp == 0xffffffff)	/* Shouldn't happen. */
3197206444Sbschmidt			tmp = 0;
3198206444Sbschmidt		else if (tmp & 0xc0000)	/* Workaround a HW bug. */
3199206444Sbschmidt			tmp |= 0x8000;
3200201209Srpaulo		r1 = (tmp & 0xff00) << 16 | (tmp & 0xff);
3201201209Srpaulo		r2 = 0;	/* Unused. */
3202201209Srpaulo	} else {
3203201209Srpaulo		r1 = IWN_READ(sc, IWN_INT);
3204201209Srpaulo		if (r1 == 0xffffffff || (r1 & 0xfffffff0) == 0xa5a5a5a0)
3205201209Srpaulo			return;	/* Hardware gone! */
3206201209Srpaulo		r2 = IWN_READ(sc, IWN_FH_INT);
3207201209Srpaulo	}
3208178676Ssam
3209198429Srpaulo	DPRINTF(sc, IWN_DEBUG_INTR, "interrupt reg1=%x reg2=%x\n", r1, r2);
3210198429Srpaulo
3211201209Srpaulo	if (r1 == 0 && r2 == 0)
3212198429Srpaulo		goto done;	/* Interrupt not for us. */
3213178676Ssam
3214198429Srpaulo	/* Acknowledge interrupts. */
3215198429Srpaulo	IWN_WRITE(sc, IWN_INT, r1);
3216201209Srpaulo	if (!(sc->sc_flags & IWN_FLAG_USE_ICT))
3217201209Srpaulo		IWN_WRITE(sc, IWN_FH_INT, r2);
3218178676Ssam
3219198429Srpaulo	if (r1 & IWN_INT_RF_TOGGLED) {
3220191746Sthompsa		iwn_rftoggle_intr(sc);
3221201209Srpaulo		goto done;
3222198429Srpaulo	}
3223198429Srpaulo	if (r1 & IWN_INT_CT_REACHED) {
3224198429Srpaulo		device_printf(sc->sc_dev, "%s: critical temperature reached!\n",
3225198429Srpaulo		    __func__);
3226198429Srpaulo	}
3227198429Srpaulo	if (r1 & (IWN_INT_SW_ERR | IWN_INT_HW_ERR)) {
3228220724Sbschmidt		device_printf(sc->sc_dev, "%s: fatal firmware error\n",
3229220724Sbschmidt		    __func__);
3230220725Sbschmidt		/* Dump firmware error log and stop. */
3231201209Srpaulo		iwn_fatal_intr(sc);
3232201209Srpaulo		ifp->if_flags &= ~IFF_UP;
3233201209Srpaulo		iwn_stop_locked(sc);
3234178676Ssam		goto done;
3235178676Ssam	}
3236201209Srpaulo	if ((r1 & (IWN_INT_FH_RX | IWN_INT_SW_RX | IWN_INT_RX_PERIODIC)) ||
3237201209Srpaulo	    (r2 & IWN_FH_INT_RX)) {
3238201209Srpaulo		if (sc->sc_flags & IWN_FLAG_USE_ICT) {
3239201209Srpaulo			if (r1 & (IWN_INT_FH_RX | IWN_INT_SW_RX))
3240201209Srpaulo				IWN_WRITE(sc, IWN_FH_INT, IWN_FH_INT_RX);
3241201209Srpaulo			IWN_WRITE_1(sc, IWN_INT_PERIODIC,
3242201209Srpaulo			    IWN_INT_PERIODIC_DIS);
3243201209Srpaulo			iwn_notif_intr(sc);
3244201209Srpaulo			if (r1 & (IWN_INT_FH_RX | IWN_INT_SW_RX)) {
3245201209Srpaulo				IWN_WRITE_1(sc, IWN_INT_PERIODIC,
3246201209Srpaulo				    IWN_INT_PERIODIC_ENA);
3247201209Srpaulo			}
3248201209Srpaulo		} else
3249201209Srpaulo			iwn_notif_intr(sc);
3250201209Srpaulo	}
3251178676Ssam
3252201209Srpaulo	if ((r1 & IWN_INT_FH_TX) || (r2 & IWN_FH_INT_TX)) {
3253201209Srpaulo		if (sc->sc_flags & IWN_FLAG_USE_ICT)
3254201209Srpaulo			IWN_WRITE(sc, IWN_FH_INT, IWN_FH_INT_TX);
3255198429Srpaulo		wakeup(sc);	/* FH DMA transfer completed. */
3256201209Srpaulo	}
3257198429Srpaulo
3258198429Srpaulo	if (r1 & IWN_INT_ALIVE)
3259198429Srpaulo		wakeup(sc);	/* Firmware is alive. */
3260198429Srpaulo
3261198429Srpaulo	if (r1 & IWN_INT_WAKEUP)
3262198429Srpaulo		iwn_wakeup_intr(sc);
3263198429Srpaulo
3264201209Srpaulodone:
3265198429Srpaulo	/* Re-enable interrupts. */
3266201209Srpaulo	if (ifp->if_flags & IFF_UP)
3267201209Srpaulo		IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask);
3268198429Srpaulo
3269178676Ssam	IWN_UNLOCK(sc);
3270178676Ssam}
3271178676Ssam
3272198429Srpaulo/*
3273198429Srpaulo * Update TX scheduler ring when transmitting an 802.11 frame (4965AGN and
3274220725Sbschmidt * 5000 adapters use a slightly different format).
3275198429Srpaulo */
3276206477Sbschmidtstatic void
3277198429Srpauloiwn4965_update_sched(struct iwn_softc *sc, int qid, int idx, uint8_t id,
3278198429Srpaulo    uint16_t len)
3279178676Ssam{
3280198429Srpaulo	uint16_t *w = &sc->sched[qid * IWN4965_SCHED_COUNT + idx];
3281178676Ssam
3282198429Srpaulo	*w = htole16(len + 8);
3283198439Srpaulo	bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map,
3284198439Srpaulo	    BUS_DMASYNC_PREWRITE);
3285201209Srpaulo	if (idx < IWN_SCHED_WINSZ) {
3286198429Srpaulo		*(w + IWN_TX_RING_COUNT) = *w;
3287198439Srpaulo		bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map,
3288198439Srpaulo		    BUS_DMASYNC_PREWRITE);
3289198439Srpaulo	}
3290198429Srpaulo}
3291198429Srpaulo
3292206477Sbschmidtstatic void
3293198429Srpauloiwn5000_update_sched(struct iwn_softc *sc, int qid, int idx, uint8_t id,
3294198429Srpaulo    uint16_t len)
3295198429Srpaulo{
3296198429Srpaulo	uint16_t *w = &sc->sched[qid * IWN5000_SCHED_COUNT + idx];
3297198429Srpaulo
3298198429Srpaulo	*w = htole16(id << 12 | (len + 8));
3299198439Srpaulo	bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map,
3300198439Srpaulo	    BUS_DMASYNC_PREWRITE);
3301198439Srpaulo	if (idx < IWN_SCHED_WINSZ) {
3302198429Srpaulo		*(w + IWN_TX_RING_COUNT) = *w;
3303198439Srpaulo		bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map,
3304198439Srpaulo		    BUS_DMASYNC_PREWRITE);
3305198439Srpaulo	}
3306198429Srpaulo}
3307198429Srpaulo
3308206475Sbschmidt#ifdef notyet
3309206477Sbschmidtstatic void
3310198429Srpauloiwn5000_reset_sched(struct iwn_softc *sc, int qid, int idx)
3311198429Srpaulo{
3312198429Srpaulo	uint16_t *w = &sc->sched[qid * IWN5000_SCHED_COUNT + idx];
3313198429Srpaulo
3314198429Srpaulo	*w = (*w & htole16(0xf000)) | htole16(1);
3315198439Srpaulo	bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map,
3316198439Srpaulo	    BUS_DMASYNC_PREWRITE);
3317198439Srpaulo	if (idx < IWN_SCHED_WINSZ) {
3318198429Srpaulo		*(w + IWN_TX_RING_COUNT) = *w;
3319198439Srpaulo		bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map,
3320206443Sbschmidt		    BUS_DMASYNC_PREWRITE);
3321198439Srpaulo	}
3322198429Srpaulo}
3323206475Sbschmidt#endif
3324198429Srpaulo
3325206477Sbschmidtstatic int
3326220720Sbschmidtiwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
3327178676Ssam{
3328221651Sbschmidt	struct iwn_ops *ops = &sc->ops;
3329198429Srpaulo	const struct ieee80211_txparam *tp;
3330178676Ssam	struct ieee80211vap *vap = ni->ni_vap;
3331178676Ssam	struct ieee80211com *ic = ni->ni_ic;
3332198429Srpaulo	struct iwn_node *wn = (void *)ni;
3333220720Sbschmidt	struct iwn_tx_ring *ring;
3334178676Ssam	struct iwn_tx_desc *desc;
3335178676Ssam	struct iwn_tx_data *data;
3336178676Ssam	struct iwn_tx_cmd *cmd;
3337178676Ssam	struct iwn_cmd_data *tx;
3338178676Ssam	struct ieee80211_frame *wh;
3339198429Srpaulo	struct ieee80211_key *k = NULL;
3340220700Sbschmidt	struct mbuf *m1;
3341178676Ssam	uint32_t flags;
3342220720Sbschmidt	uint16_t qos;
3343178676Ssam	u_int hdrlen;
3344220723Sbschmidt	bus_dma_segment_t *seg, segs[IWN_MAX_SCATTER];
3345220723Sbschmidt	uint8_t tid, ridx, txant, type;
3346220720Sbschmidt	int ac, i, totlen, error, pad, nsegs = 0, rate;
3347178676Ssam
3348178676Ssam	IWN_LOCK_ASSERT(sc);
3349178676Ssam
3350198429Srpaulo	wh = mtod(m, struct ieee80211_frame *);
3351198429Srpaulo	hdrlen = ieee80211_anyhdrsize(wh);
3352178676Ssam	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
3353178676Ssam
3354220720Sbschmidt	/* Select EDCA Access Category and TX ring for this frame. */
3355220720Sbschmidt	if (IEEE80211_QOS_HAS_SEQ(wh)) {
3356220720Sbschmidt		qos = ((const struct ieee80211_qosframe *)wh)->i_qos[0];
3357220720Sbschmidt		tid = qos & IEEE80211_QOS_TID;
3358220720Sbschmidt	} else {
3359220720Sbschmidt		qos = 0;
3360220720Sbschmidt		tid = 0;
3361220720Sbschmidt	}
3362220720Sbschmidt	ac = M_WME_GETAC(m);
3363237917Sbschmidt	if (m->m_flags & M_AMPDU_MPDU) {
3364221651Sbschmidt		struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[ac];
3365221651Sbschmidt
3366237917Sbschmidt		if (!IEEE80211_AMPDU_RUNNING(tap)) {
3367237917Sbschmidt			m_freem(m);
3368237917Sbschmidt			return EINVAL;
3369237917Sbschmidt		}
3370237917Sbschmidt
3371237917Sbschmidt		ac = *(int *)tap->txa_private;
3372221651Sbschmidt		*(uint16_t *)wh->i_seq =
3373221651Sbschmidt		    htole16(ni->ni_txseqs[tid] << IEEE80211_SEQ_SEQ_SHIFT);
3374221651Sbschmidt		ni->ni_txseqs[tid]++;
3375221651Sbschmidt	}
3376237917Sbschmidt	ring = &sc->txq[ac];
3377198429Srpaulo	desc = &ring->desc[ring->cur];
3378198429Srpaulo	data = &ring->data[ring->cur];
3379198429Srpaulo
3380198429Srpaulo	/* Choose a TX rate index. */
3381201209Srpaulo	tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)];
3382178676Ssam	if (type == IEEE80211_FC0_TYPE_MGT)
3383178676Ssam		rate = tp->mgmtrate;
3384198429Srpaulo	else if (IEEE80211_IS_MULTICAST(wh->i_addr1))
3385178676Ssam		rate = tp->mcastrate;
3386178676Ssam	else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE)
3387178676Ssam		rate = tp->ucastrate;
3388178676Ssam	else {
3389206358Srpaulo		/* XXX pass pktlen */
3390206358Srpaulo		(void) ieee80211_ratectl_rate(ni, NULL, 0);
3391178676Ssam		rate = ni->ni_txrate;
3392178676Ssam	}
3393221648Sbschmidt	ridx = ic->ic_rt->rateCodeToIndex[rate];
3394178676Ssam
3395198429Srpaulo	/* Encrypt the frame if need be. */
3396178676Ssam	if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
3397220725Sbschmidt		/* Retrieve key for TX. */
3398198429Srpaulo		k = ieee80211_crypto_encap(ni, m);
3399178676Ssam		if (k == NULL) {
3400198429Srpaulo			m_freem(m);
3401178676Ssam			return ENOBUFS;
3402178676Ssam		}
3403220725Sbschmidt		/* 802.11 header may have moved. */
3404198429Srpaulo		wh = mtod(m, struct ieee80211_frame *);
3405198429Srpaulo	}
3406198429Srpaulo	totlen = m->m_pkthdr.len;
3407178676Ssam
3408192468Ssam	if (ieee80211_radiotap_active_vap(vap)) {
3409178676Ssam		struct iwn_tx_radiotap_header *tap = &sc->sc_txtap;
3410178676Ssam
3411178676Ssam		tap->wt_flags = 0;
3412221648Sbschmidt		tap->wt_rate = rate;
3413178676Ssam		if (k != NULL)
3414178676Ssam			tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP;
3415178676Ssam
3416198429Srpaulo		ieee80211_radiotap_tx(vap, m);
3417178676Ssam	}
3418178676Ssam
3419198429Srpaulo	/* Prepare TX firmware command. */
3420198429Srpaulo	cmd = &ring->cmd[ring->cur];
3421198429Srpaulo	cmd->code = IWN_CMD_TX_DATA;
3422198429Srpaulo	cmd->flags = 0;
3423198429Srpaulo	cmd->qid = ring->qid;
3424198429Srpaulo	cmd->idx = ring->cur;
3425198429Srpaulo
3426198429Srpaulo	tx = (struct iwn_cmd_data *)cmd->data;
3427198429Srpaulo	/* NB: No need to clear tx, all fields are reinitialized here. */
3428198429Srpaulo	tx->scratch = 0;	/* clear "scratch" area */
3429198429Srpaulo
3430198429Srpaulo	flags = 0;
3431220720Sbschmidt	if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
3432220720Sbschmidt		/* Unicast frame, check if an ACK is expected. */
3433220720Sbschmidt		if (!qos || (qos & IEEE80211_QOS_ACKPOLICY) !=
3434220720Sbschmidt		    IEEE80211_QOS_ACKPOLICY_NOACK)
3435220720Sbschmidt			flags |= IWN_TX_NEED_ACK;
3436220720Sbschmidt	}
3437198429Srpaulo	if ((wh->i_fc[0] &
3438198429Srpaulo	    (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) ==
3439198429Srpaulo	    (IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_BAR))
3440198429Srpaulo		flags |= IWN_TX_IMM_BA;		/* Cannot happen yet. */
3441178676Ssam
3442198429Srpaulo	if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG)
3443198429Srpaulo		flags |= IWN_TX_MORE_FRAG;	/* Cannot happen yet. */
3444178676Ssam
3445198429Srpaulo	/* Check if frame must be protected using RTS/CTS or CTS-to-self. */
3446198429Srpaulo	if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
3447198429Srpaulo		/* NB: Group frames are sent using CCK in 802.11b/g. */
3448198429Srpaulo		if (totlen + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) {
3449198429Srpaulo			flags |= IWN_TX_NEED_RTS;
3450178676Ssam		} else if ((ic->ic_flags & IEEE80211_F_USEPROT) &&
3451201209Srpaulo		    ridx >= IWN_RIDX_OFDM6) {
3452178676Ssam			if (ic->ic_protmode == IEEE80211_PROT_CTSONLY)
3453198429Srpaulo				flags |= IWN_TX_NEED_CTS;
3454178676Ssam			else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS)
3455198429Srpaulo				flags |= IWN_TX_NEED_RTS;
3456178676Ssam		}
3457198429Srpaulo		if (flags & (IWN_TX_NEED_RTS | IWN_TX_NEED_CTS)) {
3458198429Srpaulo			if (sc->hw_type != IWN_HW_REV_TYPE_4965) {
3459198429Srpaulo				/* 5000 autoselects RTS/CTS or CTS-to-self. */
3460198429Srpaulo				flags &= ~(IWN_TX_NEED_RTS | IWN_TX_NEED_CTS);
3461198429Srpaulo				flags |= IWN_TX_NEED_PROTECTION;
3462198429Srpaulo			} else
3463198429Srpaulo				flags |= IWN_TX_FULL_TXOP;
3464198429Srpaulo		}
3465201209Srpaulo	}
3466178676Ssam
3467198429Srpaulo	if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
3468198429Srpaulo	    type != IEEE80211_FC0_TYPE_DATA)
3469220728Sbschmidt		tx->id = sc->broadcast_id;
3470198429Srpaulo	else
3471198429Srpaulo		tx->id = wn->id;
3472198429Srpaulo
3473178676Ssam	if (type == IEEE80211_FC0_TYPE_MGT) {
3474178676Ssam		uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
3475178676Ssam
3476198429Srpaulo		/* Tell HW to set timestamp in probe responses. */
3477178676Ssam		if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
3478178676Ssam			flags |= IWN_TX_INSERT_TSTAMP;
3479178676Ssam		if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ ||
3480178676Ssam		    subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ)
3481198429Srpaulo			tx->timeout = htole16(3);
3482178676Ssam		else
3483198429Srpaulo			tx->timeout = htole16(2);
3484178676Ssam	} else
3485198429Srpaulo		tx->timeout = htole16(0);
3486178676Ssam
3487178676Ssam	if (hdrlen & 3) {
3488201209Srpaulo		/* First segment length must be a multiple of 4. */
3489178676Ssam		flags |= IWN_TX_NEED_PADDING;
3490178676Ssam		pad = 4 - (hdrlen & 3);
3491178676Ssam	} else
3492178676Ssam		pad = 0;
3493178676Ssam
3494198429Srpaulo	tx->len = htole16(totlen);
3495220720Sbschmidt	tx->tid = tid;
3496201209Srpaulo	tx->rts_ntries = 60;
3497201209Srpaulo	tx->data_ntries = 15;
3498178676Ssam	tx->lifetime = htole32(IWN_LIFETIME_INFINITE);
3499221648Sbschmidt	tx->rate = wn->ridx[rate];
3500220728Sbschmidt	if (tx->id == sc->broadcast_id) {
3501201209Srpaulo		/* Group or management frame. */
3502201209Srpaulo		tx->linkq = 0;
3503198429Srpaulo		/* XXX Alternate between antenna A and B? */
3504201209Srpaulo		txant = IWN_LSB(sc->txchainmask);
3505221648Sbschmidt		tx->rate |= htole32(IWN_RFLAG_ANT(txant));
3506201209Srpaulo	} else {
3507220715Sbschmidt		tx->linkq = ni->ni_rates.rs_nrates - ridx - 1;
3508201209Srpaulo		flags |= IWN_TX_LINKQ;	/* enable MRR */
3509201209Srpaulo	}
3510198429Srpaulo	/* Set physical address of "scratch area". */
3511201209Srpaulo	tx->loaddr = htole32(IWN_LOADDR(data->scratch_paddr));
3512201209Srpaulo	tx->hiaddr = IWN_HIADDR(data->scratch_paddr);
3513178676Ssam
3514198429Srpaulo	/* Copy 802.11 header in TX command. */
3515178676Ssam	memcpy((uint8_t *)(tx + 1), wh, hdrlen);
3516178676Ssam
3517198429Srpaulo	/* Trim 802.11 header. */
3518198429Srpaulo	m_adj(m, hdrlen);
3519198429Srpaulo	tx->security = 0;
3520198429Srpaulo	tx->flags = htole32(flags);
3521198429Srpaulo
3522220700Sbschmidt	error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m, segs,
3523220700Sbschmidt	    &nsegs, BUS_DMA_NOWAIT);
3524220700Sbschmidt	if (error != 0) {
3525220700Sbschmidt		if (error != EFBIG) {
3526220700Sbschmidt			device_printf(sc->sc_dev,
3527220700Sbschmidt			    "%s: can't map mbuf (error %d)\n", __func__, error);
3528220700Sbschmidt			m_freem(m);
3529220700Sbschmidt			return error;
3530178676Ssam		}
3531220700Sbschmidt		/* Too many DMA segments, linearize mbuf. */
3532248078Smarius		m1 = m_collapse(m, M_NOWAIT, IWN_MAX_SCATTER);
3533220700Sbschmidt		if (m1 == NULL) {
3534220700Sbschmidt			device_printf(sc->sc_dev,
3535220700Sbschmidt			    "%s: could not defrag mbuf\n", __func__);
3536220700Sbschmidt			m_freem(m);
3537220700Sbschmidt			return ENOBUFS;
3538220700Sbschmidt		}
3539220700Sbschmidt		m = m1;
3540220700Sbschmidt
3541220700Sbschmidt		error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m,
3542220700Sbschmidt		    segs, &nsegs, BUS_DMA_NOWAIT);
3543178676Ssam		if (error != 0) {
3544178676Ssam			device_printf(sc->sc_dev,
3545220700Sbschmidt			    "%s: can't map mbuf (error %d)\n", __func__, error);
3546198429Srpaulo			m_freem(m);
3547178676Ssam			return error;
3548178676Ssam		}
3549178676Ssam	}
3550178676Ssam
3551198429Srpaulo	data->m = m;
3552178676Ssam	data->ni = ni;
3553178676Ssam
3554178676Ssam	DPRINTF(sc, IWN_DEBUG_XMIT, "%s: qid %d idx %d len %d nsegs %d\n",
3555198429Srpaulo	    __func__, ring->qid, ring->cur, m->m_pkthdr.len, nsegs);
3556178676Ssam
3557198429Srpaulo	/* Fill TX descriptor. */
3558220700Sbschmidt	desc->nsegs = 1;
3559220700Sbschmidt	if (m->m_len != 0)
3560220700Sbschmidt		desc->nsegs += nsegs;
3561198429Srpaulo	/* First DMA segment is used by the TX command. */
3562201209Srpaulo	desc->segs[0].addr = htole32(IWN_LOADDR(data->cmd_paddr));
3563201209Srpaulo	desc->segs[0].len  = htole16(IWN_HIADDR(data->cmd_paddr) |
3564198429Srpaulo	    (4 + sizeof (*tx) + hdrlen + pad) << 4);
3565198429Srpaulo	/* Other DMA segments are for data payload. */
3566220700Sbschmidt	seg = &segs[0];
3567178676Ssam	for (i = 1; i <= nsegs; i++) {
3568220700Sbschmidt		desc->segs[i].addr = htole32(IWN_LOADDR(seg->ds_addr));
3569220700Sbschmidt		desc->segs[i].len  = htole16(IWN_HIADDR(seg->ds_addr) |
3570220700Sbschmidt		    seg->ds_len << 4);
3571220700Sbschmidt		seg++;
3572178676Ssam	}
3573178676Ssam
3574201209Srpaulo	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE);
3575201209Srpaulo	bus_dmamap_sync(ring->data_dmat, ring->cmd_dma.map,
3576198429Srpaulo	    BUS_DMASYNC_PREWRITE);
3577198429Srpaulo	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
3578198429Srpaulo	    BUS_DMASYNC_PREWRITE);
3579178676Ssam
3580198429Srpaulo	/* Update TX scheduler. */
3581221945Sbschmidt	if (ring->qid >= sc->firstaggqueue)
3582221945Sbschmidt		ops->update_sched(sc, ring->qid, ring->cur, tx->id, totlen);
3583178676Ssam
3584198429Srpaulo	/* Kick TX ring. */
3585178676Ssam	ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT;
3586198429Srpaulo	IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur);
3587178676Ssam
3588198429Srpaulo	/* Mark TX ring as full if we reach a certain threshold. */
3589198429Srpaulo	if (++ring->queued > IWN_TX_RING_HIMARK)
3590198429Srpaulo		sc->qfullmsk |= 1 << ring->qid;
3591178676Ssam
3592178676Ssam	return 0;
3593178676Ssam}
3594178676Ssam
3595178676Ssamstatic int
3596201209Srpauloiwn_tx_data_raw(struct iwn_softc *sc, struct mbuf *m,
3597220720Sbschmidt    struct ieee80211_node *ni, const struct ieee80211_bpf_params *params)
3598178676Ssam{
3599221651Sbschmidt	struct iwn_ops *ops = &sc->ops;
3600178676Ssam	struct ifnet *ifp = sc->sc_ifp;
3601198429Srpaulo	struct ieee80211vap *vap = ni->ni_vap;
3602198429Srpaulo	struct ieee80211com *ic = ifp->if_l2com;
3603198429Srpaulo	struct iwn_tx_cmd *cmd;
3604198429Srpaulo	struct iwn_cmd_data *tx;
3605198429Srpaulo	struct ieee80211_frame *wh;
3606220720Sbschmidt	struct iwn_tx_ring *ring;
3607178676Ssam	struct iwn_tx_desc *desc;
3608178676Ssam	struct iwn_tx_data *data;
3609220700Sbschmidt	struct mbuf *m1;
3610220700Sbschmidt	bus_dma_segment_t *seg, segs[IWN_MAX_SCATTER];
3611198429Srpaulo	uint32_t flags;
3612198429Srpaulo	u_int hdrlen;
3613220720Sbschmidt	int ac, totlen, error, pad, nsegs = 0, i, rate;
3614201209Srpaulo	uint8_t ridx, type, txant;
3615178676Ssam
3616198429Srpaulo	IWN_LOCK_ASSERT(sc);
3617178676Ssam
3618201209Srpaulo	wh = mtod(m, struct ieee80211_frame *);
3619198429Srpaulo	hdrlen = ieee80211_anyhdrsize(wh);
3620198429Srpaulo	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
3621198429Srpaulo
3622220720Sbschmidt	ac = params->ibp_pri & 3;
3623220720Sbschmidt
3624220720Sbschmidt	ring = &sc->txq[ac];
3625178676Ssam	desc = &ring->desc[ring->cur];
3626178676Ssam	data = &ring->data[ring->cur];
3627178676Ssam
3628198429Srpaulo	/* Choose a TX rate index. */
3629198429Srpaulo	rate = params->ibp_rate0;
3630221648Sbschmidt	ridx = ic->ic_rt->rateCodeToIndex[rate];
3631221648Sbschmidt	if (ridx == (uint8_t)-1) {
3632198429Srpaulo		/* XXX fall back to mcast/mgmt rate? */
3633201209Srpaulo		m_freem(m);
3634198429Srpaulo		return EINVAL;
3635198429Srpaulo	}
3636198429Srpaulo
3637201209Srpaulo	totlen = m->m_pkthdr.len;
3638198429Srpaulo
3639201209Srpaulo	/* Prepare TX firmware command. */
3640198429Srpaulo	cmd = &ring->cmd[ring->cur];
3641198429Srpaulo	cmd->code = IWN_CMD_TX_DATA;
3642198429Srpaulo	cmd->flags = 0;
3643198429Srpaulo	cmd->qid = ring->qid;
3644198429Srpaulo	cmd->idx = ring->cur;
3645198429Srpaulo
3646198429Srpaulo	tx = (struct iwn_cmd_data *)cmd->data;
3647201209Srpaulo	/* NB: No need to clear tx, all fields are reinitialized here. */
3648198429Srpaulo	tx->scratch = 0;	/* clear "scratch" area */
3649198429Srpaulo
3650198429Srpaulo	flags = 0;
3651198429Srpaulo	if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0)
3652198429Srpaulo		flags |= IWN_TX_NEED_ACK;
3653198429Srpaulo	if (params->ibp_flags & IEEE80211_BPF_RTS) {
3654198429Srpaulo		if (sc->hw_type != IWN_HW_REV_TYPE_4965) {
3655198429Srpaulo			/* 5000 autoselects RTS/CTS or CTS-to-self. */
3656198429Srpaulo			flags &= ~IWN_TX_NEED_RTS;
3657198429Srpaulo			flags |= IWN_TX_NEED_PROTECTION;
3658198429Srpaulo		} else
3659198429Srpaulo			flags |= IWN_TX_NEED_RTS | IWN_TX_FULL_TXOP;
3660198429Srpaulo	}
3661198429Srpaulo	if (params->ibp_flags & IEEE80211_BPF_CTS) {
3662198429Srpaulo		if (sc->hw_type != IWN_HW_REV_TYPE_4965) {
3663198429Srpaulo			/* 5000 autoselects RTS/CTS or CTS-to-self. */
3664198429Srpaulo			flags &= ~IWN_TX_NEED_CTS;
3665198429Srpaulo			flags |= IWN_TX_NEED_PROTECTION;
3666198429Srpaulo		} else
3667198429Srpaulo			flags |= IWN_TX_NEED_CTS | IWN_TX_FULL_TXOP;
3668198429Srpaulo	}
3669198429Srpaulo	if (type == IEEE80211_FC0_TYPE_MGT) {
3670198429Srpaulo		uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
3671198429Srpaulo
3672220725Sbschmidt		/* Tell HW to set timestamp in probe responses. */
3673198429Srpaulo		if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
3674198429Srpaulo			flags |= IWN_TX_INSERT_TSTAMP;
3675198429Srpaulo
3676198429Srpaulo		if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ ||
3677198429Srpaulo		    subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ)
3678198429Srpaulo			tx->timeout = htole16(3);
3679198429Srpaulo		else
3680198429Srpaulo			tx->timeout = htole16(2);
3681198429Srpaulo	} else
3682198429Srpaulo		tx->timeout = htole16(0);
3683198429Srpaulo
3684198429Srpaulo	if (hdrlen & 3) {
3685201209Srpaulo		/* First segment length must be a multiple of 4. */
3686198429Srpaulo		flags |= IWN_TX_NEED_PADDING;
3687198429Srpaulo		pad = 4 - (hdrlen & 3);
3688198429Srpaulo	} else
3689198429Srpaulo		pad = 0;
3690198429Srpaulo
3691198429Srpaulo	if (ieee80211_radiotap_active_vap(vap)) {
3692198429Srpaulo		struct iwn_tx_radiotap_header *tap = &sc->sc_txtap;
3693198429Srpaulo
3694198429Srpaulo		tap->wt_flags = 0;
3695198429Srpaulo		tap->wt_rate = rate;
3696198429Srpaulo
3697201209Srpaulo		ieee80211_radiotap_tx(vap, m);
3698198429Srpaulo	}
3699198429Srpaulo
3700198429Srpaulo	tx->len = htole16(totlen);
3701198429Srpaulo	tx->tid = 0;
3702220728Sbschmidt	tx->id = sc->broadcast_id;
3703198429Srpaulo	tx->rts_ntries = params->ibp_try1;
3704198429Srpaulo	tx->data_ntries = params->ibp_try0;
3705198429Srpaulo	tx->lifetime = htole32(IWN_LIFETIME_INFINITE);
3706221648Sbschmidt	tx->rate = htole32(rate2plcp(rate));
3707221648Sbschmidt	if (ridx < IWN_RIDX_OFDM6 &&
3708221648Sbschmidt	    IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
3709221648Sbschmidt		tx->rate |= htole32(IWN_RFLAG_CCK);
3710201209Srpaulo	/* Group or management frame. */
3711201209Srpaulo	tx->linkq = 0;
3712201209Srpaulo	txant = IWN_LSB(sc->txchainmask);
3713221648Sbschmidt	tx->rate |= htole32(IWN_RFLAG_ANT(txant));
3714198429Srpaulo	/* Set physical address of "scratch area". */
3715220694Sbschmidt	tx->loaddr = htole32(IWN_LOADDR(data->scratch_paddr));
3716220694Sbschmidt	tx->hiaddr = IWN_HIADDR(data->scratch_paddr);
3717198429Srpaulo
3718198429Srpaulo	/* Copy 802.11 header in TX command. */
3719198429Srpaulo	memcpy((uint8_t *)(tx + 1), wh, hdrlen);
3720198429Srpaulo
3721198429Srpaulo	/* Trim 802.11 header. */
3722201209Srpaulo	m_adj(m, hdrlen);
3723198429Srpaulo	tx->security = 0;
3724198429Srpaulo	tx->flags = htole32(flags);
3725198429Srpaulo
3726220700Sbschmidt	error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m, segs,
3727220700Sbschmidt	    &nsegs, BUS_DMA_NOWAIT);
3728220700Sbschmidt	if (error != 0) {
3729220700Sbschmidt		if (error != EFBIG) {
3730220700Sbschmidt			device_printf(sc->sc_dev,
3731220700Sbschmidt			    "%s: can't map mbuf (error %d)\n", __func__, error);
3732220700Sbschmidt			m_freem(m);
3733220700Sbschmidt			return error;
3734178676Ssam		}
3735220700Sbschmidt		/* Too many DMA segments, linearize mbuf. */
3736248078Smarius		m1 = m_collapse(m, M_NOWAIT, IWN_MAX_SCATTER);
3737220700Sbschmidt		if (m1 == NULL) {
3738220700Sbschmidt			device_printf(sc->sc_dev,
3739220700Sbschmidt			    "%s: could not defrag mbuf\n", __func__);
3740220700Sbschmidt			m_freem(m);
3741220700Sbschmidt			return ENOBUFS;
3742220700Sbschmidt		}
3743220700Sbschmidt		m = m1;
3744220700Sbschmidt
3745220700Sbschmidt		error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m,
3746220700Sbschmidt		    segs, &nsegs, BUS_DMA_NOWAIT);
3747178676Ssam		if (error != 0) {
3748178676Ssam			device_printf(sc->sc_dev,
3749220700Sbschmidt			    "%s: can't map mbuf (error %d)\n", __func__, error);
3750201209Srpaulo			m_freem(m);
3751178676Ssam			return error;
3752178676Ssam		}
3753178676Ssam	}
3754178676Ssam
3755201209Srpaulo	data->m = m;
3756178676Ssam	data->ni = ni;
3757178676Ssam
3758178676Ssam	DPRINTF(sc, IWN_DEBUG_XMIT, "%s: qid %d idx %d len %d nsegs %d\n",
3759201209Srpaulo	    __func__, ring->qid, ring->cur, m->m_pkthdr.len, nsegs);
3760178676Ssam
3761198429Srpaulo	/* Fill TX descriptor. */
3762220700Sbschmidt	desc->nsegs = 1;
3763220700Sbschmidt	if (m->m_len != 0)
3764220700Sbschmidt		desc->nsegs += nsegs;
3765198429Srpaulo	/* First DMA segment is used by the TX command. */
3766201209Srpaulo	desc->segs[0].addr = htole32(IWN_LOADDR(data->cmd_paddr));
3767201209Srpaulo	desc->segs[0].len  = htole16(IWN_HIADDR(data->cmd_paddr) |
3768198429Srpaulo	    (4 + sizeof (*tx) + hdrlen + pad) << 4);
3769198429Srpaulo	/* Other DMA segments are for data payload. */
3770220700Sbschmidt	seg = &segs[0];
3771178676Ssam	for (i = 1; i <= nsegs; i++) {
3772220700Sbschmidt		desc->segs[i].addr = htole32(IWN_LOADDR(seg->ds_addr));
3773220700Sbschmidt		desc->segs[i].len  = htole16(IWN_HIADDR(seg->ds_addr) |
3774220700Sbschmidt		    seg->ds_len << 4);
3775220700Sbschmidt		seg++;
3776178676Ssam	}
3777178676Ssam
3778201209Srpaulo	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE);
3779201209Srpaulo	bus_dmamap_sync(ring->data_dmat, ring->cmd_dma.map,
3780201209Srpaulo	    BUS_DMASYNC_PREWRITE);
3781201209Srpaulo	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
3782201209Srpaulo	    BUS_DMASYNC_PREWRITE);
3783201209Srpaulo
3784198429Srpaulo	/* Update TX scheduler. */
3785221945Sbschmidt	if (ring->qid >= sc->firstaggqueue)
3786221945Sbschmidt		ops->update_sched(sc, ring->qid, ring->cur, tx->id, totlen);
3787178676Ssam
3788198429Srpaulo	/* Kick TX ring. */
3789178676Ssam	ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT;
3790198429Srpaulo	IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur);
3791178676Ssam
3792198429Srpaulo	/* Mark TX ring as full if we reach a certain threshold. */
3793198429Srpaulo	if (++ring->queued > IWN_TX_RING_HIMARK)
3794198429Srpaulo		sc->qfullmsk |= 1 << ring->qid;
3795178676Ssam
3796178676Ssam	return 0;
3797178676Ssam}
3798178676Ssam
3799178676Ssamstatic int
3800178676Ssamiwn_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
3801220720Sbschmidt    const struct ieee80211_bpf_params *params)
3802178676Ssam{
3803178676Ssam	struct ieee80211com *ic = ni->ni_ic;
3804178676Ssam	struct ifnet *ifp = ic->ic_ifp;
3805178676Ssam	struct iwn_softc *sc = ifp->if_softc;
3806198429Srpaulo	int error = 0;
3807178676Ssam
3808178676Ssam	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
3809178676Ssam		ieee80211_free_node(ni);
3810178676Ssam		m_freem(m);
3811178676Ssam		return ENETDOWN;
3812178676Ssam	}
3813178676Ssam
3814178676Ssam	IWN_LOCK(sc);
3815178676Ssam	if (params == NULL) {
3816178676Ssam		/*
3817178676Ssam		 * Legacy path; interpret frame contents to decide
3818178676Ssam		 * precisely how to send the frame.
3819178676Ssam		 */
3820220720Sbschmidt		error = iwn_tx_data(sc, m, ni);
3821178676Ssam	} else {
3822178676Ssam		/*
3823178676Ssam		 * Caller supplied explicit parameters to use in
3824178676Ssam		 * sending the frame.
3825178676Ssam		 */
3826220720Sbschmidt		error = iwn_tx_data_raw(sc, m, ni, params);
3827178676Ssam	}
3828178676Ssam	if (error != 0) {
3829178676Ssam		/* NB: m is reclaimed on tx failure */
3830178676Ssam		ieee80211_free_node(ni);
3831178676Ssam		ifp->if_oerrors++;
3832178676Ssam	}
3833220667Sbschmidt	sc->sc_tx_timer = 5;
3834220667Sbschmidt
3835178676Ssam	IWN_UNLOCK(sc);
3836178676Ssam	return error;
3837178676Ssam}
3838178676Ssam
3839206477Sbschmidtstatic void
3840198429Srpauloiwn_start(struct ifnet *ifp)
3841198429Srpaulo{
3842198429Srpaulo	struct iwn_softc *sc = ifp->if_softc;
3843198429Srpaulo
3844198429Srpaulo	IWN_LOCK(sc);
3845198429Srpaulo	iwn_start_locked(ifp);
3846198429Srpaulo	IWN_UNLOCK(sc);
3847198429Srpaulo}
3848198429Srpaulo
3849206477Sbschmidtstatic void
3850198429Srpauloiwn_start_locked(struct ifnet *ifp)
3851198429Srpaulo{
3852198429Srpaulo	struct iwn_softc *sc = ifp->if_softc;
3853198429Srpaulo	struct ieee80211_node *ni;
3854198429Srpaulo	struct mbuf *m;
3855198429Srpaulo
3856198429Srpaulo	IWN_LOCK_ASSERT(sc);
3857198429Srpaulo
3858220720Sbschmidt	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 ||
3859220720Sbschmidt	    (ifp->if_drv_flags & IFF_DRV_OACTIVE))
3860220720Sbschmidt		return;
3861220720Sbschmidt
3862198429Srpaulo	for (;;) {
3863198429Srpaulo		if (sc->qfullmsk != 0) {
3864198429Srpaulo			ifp->if_drv_flags |= IFF_DRV_OACTIVE;
3865198429Srpaulo			break;
3866198429Srpaulo		}
3867198429Srpaulo		IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
3868198429Srpaulo		if (m == NULL)
3869198429Srpaulo			break;
3870198429Srpaulo		ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
3871220720Sbschmidt		if (iwn_tx_data(sc, m, ni) != 0) {
3872220720Sbschmidt			ieee80211_free_node(ni);
3873198429Srpaulo			ifp->if_oerrors++;
3874220720Sbschmidt			continue;
3875198429Srpaulo		}
3876198429Srpaulo		sc->sc_tx_timer = 5;
3877198429Srpaulo	}
3878198429Srpaulo}
3879198429Srpaulo
3880178676Ssamstatic void
3881220667Sbschmidtiwn_watchdog(void *arg)
3882178676Ssam{
3883220667Sbschmidt	struct iwn_softc *sc = arg;
3884220667Sbschmidt	struct ifnet *ifp = sc->sc_ifp;
3885220667Sbschmidt	struct ieee80211com *ic = ifp->if_l2com;
3886178676Ssam
3887220667Sbschmidt	IWN_LOCK_ASSERT(sc);
3888220667Sbschmidt
3889220667Sbschmidt	KASSERT(ifp->if_drv_flags & IFF_DRV_RUNNING, ("not running"));
3890220667Sbschmidt
3891220668Sbschmidt	if (sc->sc_tx_timer > 0) {
3892220668Sbschmidt		if (--sc->sc_tx_timer == 0) {
3893220667Sbschmidt			if_printf(ifp, "device timeout\n");
3894220667Sbschmidt			ieee80211_runtask(ic, &sc->sc_reinit_task);
3895220667Sbschmidt			return;
3896220667Sbschmidt		}
3897178676Ssam	}
3898220667Sbschmidt	callout_reset(&sc->watchdog_to, hz, iwn_watchdog, sc);
3899178676Ssam}
3900178676Ssam
3901206477Sbschmidtstatic int
3902178676Ssamiwn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
3903178676Ssam{
3904178676Ssam	struct iwn_softc *sc = ifp->if_softc;
3905178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
3906201209Srpaulo	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
3907178676Ssam	struct ifreq *ifr = (struct ifreq *) data;
3908201209Srpaulo	int error = 0, startall = 0, stop = 0;
3909178676Ssam
3910178676Ssam	switch (cmd) {
3911220723Sbschmidt	case SIOCGIFADDR:
3912220723Sbschmidt		error = ether_ioctl(ifp, cmd, data);
3913220723Sbschmidt		break;
3914178676Ssam	case SIOCSIFFLAGS:
3915178704Sthompsa		IWN_LOCK(sc);
3916178676Ssam		if (ifp->if_flags & IFF_UP) {
3917178676Ssam			if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
3918178676Ssam				iwn_init_locked(sc);
3919201209Srpaulo				if (IWN_READ(sc, IWN_GP_CNTRL) & IWN_GP_CNTRL_RFKILL)
3920201209Srpaulo					startall = 1;
3921201209Srpaulo				else
3922201209Srpaulo					stop = 1;
3923178676Ssam			}
3924178676Ssam		} else {
3925178676Ssam			if (ifp->if_drv_flags & IFF_DRV_RUNNING)
3926178676Ssam				iwn_stop_locked(sc);
3927178676Ssam		}
3928178704Sthompsa		IWN_UNLOCK(sc);
3929178704Sthompsa		if (startall)
3930178704Sthompsa			ieee80211_start_all(ic);
3931201209Srpaulo		else if (vap != NULL && stop)
3932201209Srpaulo			ieee80211_stop(vap);
3933178676Ssam		break;
3934178676Ssam	case SIOCGIFMEDIA:
3935178676Ssam		error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd);
3936178676Ssam		break;
3937178704Sthompsa	default:
3938178704Sthompsa		error = EINVAL;
3939178704Sthompsa		break;
3940178676Ssam	}
3941178676Ssam	return error;
3942178676Ssam}
3943178676Ssam
3944178676Ssam/*
3945178676Ssam * Send a command to the firmware.
3946178676Ssam */
3947206477Sbschmidtstatic int
3948178676Ssamiwn_cmd(struct iwn_softc *sc, int code, const void *buf, int size, int async)
3949178676Ssam{
3950178676Ssam	struct iwn_tx_ring *ring = &sc->txq[4];
3951178676Ssam	struct iwn_tx_desc *desc;
3952198429Srpaulo	struct iwn_tx_data *data;
3953178676Ssam	struct iwn_tx_cmd *cmd;
3954198439Srpaulo	struct mbuf *m;
3955178676Ssam	bus_addr_t paddr;
3956198439Srpaulo	int totlen, error;
3957178676Ssam
3958221650Sbschmidt	if (async == 0)
3959221650Sbschmidt		IWN_LOCK_ASSERT(sc);
3960178676Ssam
3961178676Ssam	desc = &ring->desc[ring->cur];
3962198429Srpaulo	data = &ring->data[ring->cur];
3963198429Srpaulo	totlen = 4 + size;
3964178676Ssam
3965198439Srpaulo	if (size > sizeof cmd->data) {
3966198439Srpaulo		/* Command is too large to fit in a descriptor. */
3967198439Srpaulo		if (totlen > MCLBYTES)
3968198439Srpaulo			return EINVAL;
3969248078Smarius		m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE);
3970198439Srpaulo		if (m == NULL)
3971198439Srpaulo			return ENOMEM;
3972198439Srpaulo		cmd = mtod(m, struct iwn_tx_cmd *);
3973201209Srpaulo		error = bus_dmamap_load(ring->data_dmat, data->map, cmd,
3974198439Srpaulo		    totlen, iwn_dma_map_addr, &paddr, BUS_DMA_NOWAIT);
3975198439Srpaulo		if (error != 0) {
3976198439Srpaulo			m_freem(m);
3977198439Srpaulo			return error;
3978198439Srpaulo		}
3979198439Srpaulo		data->m = m;
3980198439Srpaulo	} else {
3981198439Srpaulo		cmd = &ring->cmd[ring->cur];
3982198439Srpaulo		paddr = data->cmd_paddr;
3983198439Srpaulo	}
3984198439Srpaulo
3985178676Ssam	cmd->code = code;
3986178676Ssam	cmd->flags = 0;
3987178676Ssam	cmd->qid = ring->qid;
3988178676Ssam	cmd->idx = ring->cur;
3989178676Ssam	memcpy(cmd->data, buf, size);
3990178676Ssam
3991198429Srpaulo	desc->nsegs = 1;
3992198429Srpaulo	desc->segs[0].addr = htole32(IWN_LOADDR(paddr));
3993198429Srpaulo	desc->segs[0].len  = htole16(IWN_HIADDR(paddr) | totlen << 4);
3994178676Ssam
3995178676Ssam	DPRINTF(sc, IWN_DEBUG_CMD, "%s: %s (0x%x) flags %d qid %d idx %d\n",
3996178676Ssam	    __func__, iwn_intr_str(cmd->code), cmd->code,
3997178676Ssam	    cmd->flags, cmd->qid, cmd->idx);
3998178676Ssam
3999198439Srpaulo	if (size > sizeof cmd->data) {
4000201209Srpaulo		bus_dmamap_sync(ring->data_dmat, data->map,
4001198439Srpaulo		    BUS_DMASYNC_PREWRITE);
4002198439Srpaulo	} else {
4003201209Srpaulo		bus_dmamap_sync(ring->data_dmat, ring->cmd_dma.map,
4004198439Srpaulo		    BUS_DMASYNC_PREWRITE);
4005198439Srpaulo	}
4006198439Srpaulo	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
4007198439Srpaulo	    BUS_DMASYNC_PREWRITE);
4008198439Srpaulo
4009198429Srpaulo	/* Kick command ring. */
4010178676Ssam	ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT;
4011198429Srpaulo	IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur);
4012178676Ssam
4013198439Srpaulo	return async ? 0 : msleep(desc, &sc->sc_mtx, PCATCH, "iwncmd", hz);
4014178676Ssam}
4015178676Ssam
4016206477Sbschmidtstatic int
4017198429Srpauloiwn4965_add_node(struct iwn_softc *sc, struct iwn_node_info *node, int async)
4018198429Srpaulo{
4019198429Srpaulo	struct iwn4965_node_info hnode;
4020198429Srpaulo	caddr_t src, dst;
4021198429Srpaulo
4022198429Srpaulo	/*
4023198429Srpaulo	 * We use the node structure for 5000 Series internally (it is
4024198429Srpaulo	 * a superset of the one for 4965AGN). We thus copy the common
4025198429Srpaulo	 * fields before sending the command.
4026198429Srpaulo	 */
4027198429Srpaulo	src = (caddr_t)node;
4028198429Srpaulo	dst = (caddr_t)&hnode;
4029198429Srpaulo	memcpy(dst, src, 48);
4030198429Srpaulo	/* Skip TSC, RX MIC and TX MIC fields from ``src''. */
4031198429Srpaulo	memcpy(dst + 48, src + 72, 20);
4032198429Srpaulo	return iwn_cmd(sc, IWN_CMD_ADD_NODE, &hnode, sizeof hnode, async);
4033198429Srpaulo}
4034198429Srpaulo
4035206477Sbschmidtstatic int
4036198429Srpauloiwn5000_add_node(struct iwn_softc *sc, struct iwn_node_info *node, int async)
4037198429Srpaulo{
4038198429Srpaulo	/* Direct mapping. */
4039198429Srpaulo	return iwn_cmd(sc, IWN_CMD_ADD_NODE, node, sizeof (*node), async);
4040198429Srpaulo}
4041198429Srpaulo
4042206477Sbschmidtstatic int
4043220715Sbschmidtiwn_set_link_quality(struct iwn_softc *sc, struct ieee80211_node *ni)
4044178676Ssam{
4045222933Sbschmidt#define	RV(v)	((v) & IEEE80211_RATE_VAL)
4046220715Sbschmidt	struct iwn_node *wn = (void *)ni;
4047220715Sbschmidt	struct ieee80211_rateset *rs = &ni->ni_rates;
4048198429Srpaulo	struct iwn_cmd_link_quality linkq;
4049220715Sbschmidt	uint8_t txant;
4050221648Sbschmidt	int i, rate, txrate;
4051178676Ssam
4052198429Srpaulo	/* Use the first valid TX antenna. */
4053201209Srpaulo	txant = IWN_LSB(sc->txchainmask);
4054178676Ssam
4055198429Srpaulo	memset(&linkq, 0, sizeof linkq);
4056220715Sbschmidt	linkq.id = wn->id;
4057198429Srpaulo	linkq.antmsk_1stream = txant;
4058201209Srpaulo	linkq.antmsk_2stream = IWN_ANT_AB;
4059221651Sbschmidt	linkq.ampdu_max = 64;
4060198429Srpaulo	linkq.ampdu_threshold = 3;
4061198429Srpaulo	linkq.ampdu_limit = htole16(4000);	/* 4ms */
4062198429Srpaulo
4063220715Sbschmidt	/* Start at highest available bit-rate. */
4064221649Sbschmidt	if (IEEE80211_IS_CHAN_HT(ni->ni_chan))
4065221649Sbschmidt		txrate = ni->ni_htrates.rs_nrates - 1;
4066221649Sbschmidt	else
4067221649Sbschmidt		txrate = rs->rs_nrates - 1;
4068178676Ssam	for (i = 0; i < IWN_MAX_TX_RETRIES; i++) {
4069221649Sbschmidt		if (IEEE80211_IS_CHAN_HT(ni->ni_chan))
4070221649Sbschmidt			rate = IEEE80211_RATE_MCS | txrate;
4071221649Sbschmidt		else
4072222933Sbschmidt			rate = RV(rs->rs_rates[txrate]);
4073221648Sbschmidt		linkq.retry[i] = wn->ridx[rate];
4074221648Sbschmidt
4075221649Sbschmidt		if ((le32toh(wn->ridx[rate]) & IWN_RFLAG_MCS) &&
4076222933Sbschmidt		    RV(le32toh(wn->ridx[rate])) > 7)
4077221649Sbschmidt			linkq.mimo = i + 1;
4078221649Sbschmidt
4079220715Sbschmidt		/* Next retry at immediate lower bit-rate. */
4080220715Sbschmidt		if (txrate > 0)
4081220715Sbschmidt			txrate--;
4082178676Ssam	}
4083220715Sbschmidt	return iwn_cmd(sc, IWN_CMD_LINK_QUALITY, &linkq, sizeof linkq, 1);
4084222933Sbschmidt#undef	RV
4085178676Ssam}
4086178676Ssam
4087178676Ssam/*
4088198429Srpaulo * Broadcast node is used to send group-addressed and management frames.
4089178676Ssam */
4090206477Sbschmidtstatic int
4091201209Srpauloiwn_add_broadcast_node(struct iwn_softc *sc, int async)
4092178676Ssam{
4093220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
4094198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
4095220715Sbschmidt	struct ieee80211com *ic = ifp->if_l2com;
4096178676Ssam	struct iwn_node_info node;
4097220715Sbschmidt	struct iwn_cmd_link_quality linkq;
4098220715Sbschmidt	uint8_t txant;
4099220715Sbschmidt	int i, error;
4100178676Ssam
4101178676Ssam	memset(&node, 0, sizeof node);
4102198429Srpaulo	IEEE80211_ADDR_COPY(node.macaddr, ifp->if_broadcastaddr);
4103220728Sbschmidt	node.id = sc->broadcast_id;
4104198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "%s: adding broadcast node\n", __func__);
4105220728Sbschmidt	if ((error = ops->add_node(sc, &node, async)) != 0)
4106198429Srpaulo		return error;
4107178676Ssam
4108220715Sbschmidt	/* Use the first valid TX antenna. */
4109220715Sbschmidt	txant = IWN_LSB(sc->txchainmask);
4110220715Sbschmidt
4111220715Sbschmidt	memset(&linkq, 0, sizeof linkq);
4112220728Sbschmidt	linkq.id = sc->broadcast_id;
4113220715Sbschmidt	linkq.antmsk_1stream = txant;
4114220715Sbschmidt	linkq.antmsk_2stream = IWN_ANT_AB;
4115220715Sbschmidt	linkq.ampdu_max = 64;
4116220715Sbschmidt	linkq.ampdu_threshold = 3;
4117220715Sbschmidt	linkq.ampdu_limit = htole16(4000);	/* 4ms */
4118220715Sbschmidt
4119220715Sbschmidt	/* Use lowest mandatory bit-rate. */
4120220715Sbschmidt	if (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan))
4121221648Sbschmidt		linkq.retry[0] = htole32(0xd);
4122220715Sbschmidt	else
4123221648Sbschmidt		linkq.retry[0] = htole32(10 | IWN_RFLAG_CCK);
4124221648Sbschmidt	linkq.retry[0] |= htole32(IWN_RFLAG_ANT(txant));
4125220715Sbschmidt	/* Use same bit-rate for all TX retries. */
4126220715Sbschmidt	for (i = 1; i < IWN_MAX_TX_RETRIES; i++) {
4127221648Sbschmidt		linkq.retry[i] = linkq.retry[0];
4128220715Sbschmidt	}
4129220715Sbschmidt	return iwn_cmd(sc, IWN_CMD_LINK_QUALITY, &linkq, sizeof linkq, async);
4130178676Ssam}
4131178676Ssam
4132206477Sbschmidtstatic int
4133220721Sbschmidtiwn_updateedca(struct ieee80211com *ic)
4134178676Ssam{
4135178676Ssam#define IWN_EXP2(x)	((1 << (x)) - 1)	/* CWmin = 2^ECWmin - 1 */
4136178676Ssam	struct iwn_softc *sc = ic->ic_ifp->if_softc;
4137178676Ssam	struct iwn_edca_params cmd;
4138220721Sbschmidt	int aci;
4139178676Ssam
4140178676Ssam	memset(&cmd, 0, sizeof cmd);
4141178676Ssam	cmd.flags = htole32(IWN_EDCA_UPDATE);
4142220721Sbschmidt	for (aci = 0; aci < WME_NUM_AC; aci++) {
4143220721Sbschmidt		const struct wmeParams *ac =
4144220721Sbschmidt		    &ic->ic_wme.wme_chanParams.cap_wmeParams[aci];
4145220721Sbschmidt		cmd.ac[aci].aifsn = ac->wmep_aifsn;
4146220721Sbschmidt		cmd.ac[aci].cwmin = htole16(IWN_EXP2(ac->wmep_logcwmin));
4147220721Sbschmidt		cmd.ac[aci].cwmax = htole16(IWN_EXP2(ac->wmep_logcwmax));
4148220721Sbschmidt		cmd.ac[aci].txoplimit =
4149220721Sbschmidt		    htole16(IEEE80211_TXOP_TO_US(ac->wmep_txopLimit));
4150178676Ssam	}
4151201209Srpaulo	IEEE80211_UNLOCK(ic);
4152178676Ssam	IWN_LOCK(sc);
4153220725Sbschmidt	(void)iwn_cmd(sc, IWN_CMD_EDCA_PARAMS, &cmd, sizeof cmd, 1);
4154178676Ssam	IWN_UNLOCK(sc);
4155201209Srpaulo	IEEE80211_LOCK(ic);
4156178676Ssam	return 0;
4157178676Ssam#undef IWN_EXP2
4158178676Ssam}
4159178676Ssam
4160201209Srpaulostatic void
4161201209Srpauloiwn_update_mcast(struct ifnet *ifp)
4162201209Srpaulo{
4163201209Srpaulo	/* Ignore */
4164201209Srpaulo}
4165201209Srpaulo
4166206477Sbschmidtstatic void
4167178676Ssamiwn_set_led(struct iwn_softc *sc, uint8_t which, uint8_t off, uint8_t on)
4168178676Ssam{
4169178676Ssam	struct iwn_cmd_led led;
4170178676Ssam
4171198429Srpaulo	/* Clear microcode LED ownership. */
4172198429Srpaulo	IWN_CLRBITS(sc, IWN_LED, IWN_LED_BSM_CTRL);
4173198429Srpaulo
4174178676Ssam	led.which = which;
4175198429Srpaulo	led.unit = htole32(10000);	/* on/off in unit of 100ms */
4176178676Ssam	led.off = off;
4177178676Ssam	led.on = on;
4178198429Srpaulo	(void)iwn_cmd(sc, IWN_CMD_SET_LED, &led, sizeof led, 1);
4179178676Ssam}
4180178676Ssam
4181178676Ssam/*
4182201209Srpaulo * Set the critical temperature at which the firmware will stop the radio
4183201209Srpaulo * and notify us.
4184178676Ssam */
4185206477Sbschmidtstatic int
4186178676Ssamiwn_set_critical_temp(struct iwn_softc *sc)
4187178676Ssam{
4188178676Ssam	struct iwn_critical_temp crit;
4189201209Srpaulo	int32_t temp;
4190178676Ssam
4191198429Srpaulo	IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_CTEMP_STOP_RF);
4192178676Ssam
4193201209Srpaulo	if (sc->hw_type == IWN_HW_REV_TYPE_5150)
4194201209Srpaulo		temp = (IWN_CTOK(110) - sc->temp_off) * -5;
4195201209Srpaulo	else if (sc->hw_type == IWN_HW_REV_TYPE_4965)
4196201209Srpaulo		temp = IWN_CTOK(110);
4197201209Srpaulo	else
4198201209Srpaulo		temp = 110;
4199178676Ssam	memset(&crit, 0, sizeof crit);
4200201209Srpaulo	crit.tempR = htole32(temp);
4201220726Sbschmidt	DPRINTF(sc, IWN_DEBUG_RESET, "setting critical temp to %d\n", temp);
4202178676Ssam	return iwn_cmd(sc, IWN_CMD_SET_CRITICAL_TEMP, &crit, sizeof crit, 0);
4203178676Ssam}
4204178676Ssam
4205206477Sbschmidtstatic int
4206198429Srpauloiwn_set_timing(struct iwn_softc *sc, struct ieee80211_node *ni)
4207178676Ssam{
4208198429Srpaulo	struct iwn_cmd_timing cmd;
4209178676Ssam	uint64_t val, mod;
4210178676Ssam
4211198429Srpaulo	memset(&cmd, 0, sizeof cmd);
4212198429Srpaulo	memcpy(&cmd.tstamp, ni->ni_tstamp.data, sizeof (uint64_t));
4213198429Srpaulo	cmd.bintval = htole16(ni->ni_intval);
4214198429Srpaulo	cmd.lintval = htole16(10);
4215178676Ssam
4216198429Srpaulo	/* Compute remaining time until next beacon. */
4217220634Sbschmidt	val = (uint64_t)ni->ni_intval * IEEE80211_DUR_TU;
4218198429Srpaulo	mod = le64toh(cmd.tstamp) % val;
4219198429Srpaulo	cmd.binitval = htole32((uint32_t)(val - mod));
4220178676Ssam
4221198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "timing bintval=%u tstamp=%ju, init=%u\n",
4222198429Srpaulo	    ni->ni_intval, le64toh(cmd.tstamp), (uint32_t)(val - mod));
4223178676Ssam
4224198429Srpaulo	return iwn_cmd(sc, IWN_CMD_TIMING, &cmd, sizeof cmd, 1);
4225178676Ssam}
4226178676Ssam
4227206477Sbschmidtstatic void
4228198429Srpauloiwn4965_power_calibration(struct iwn_softc *sc, int temp)
4229178676Ssam{
4230201882Skeramida	struct ifnet *ifp = sc->sc_ifp;
4231201882Skeramida	struct ieee80211com *ic = ifp->if_l2com;
4232201882Skeramida
4233220725Sbschmidt	/* Adjust TX power if need be (delta >= 3 degC). */
4234178676Ssam	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: temperature %d->%d\n",
4235178676Ssam	    __func__, sc->temp, temp);
4236198429Srpaulo	if (abs(temp - sc->temp) >= 3) {
4237198429Srpaulo		/* Record temperature of last calibration. */
4238198429Srpaulo		sc->temp = temp;
4239201882Skeramida		(void)iwn4965_set_txpower(sc, ic->ic_bsschan, 1);
4240178676Ssam	}
4241178676Ssam}
4242178676Ssam
4243178676Ssam/*
4244198429Srpaulo * Set TX power for current channel (each rate has its own power settings).
4245178676Ssam * This function takes into account the regulatory information from EEPROM,
4246178676Ssam * the current temperature and the current voltage.
4247178676Ssam */
4248206477Sbschmidtstatic int
4249201882Skeramidaiwn4965_set_txpower(struct iwn_softc *sc, struct ieee80211_channel *ch,
4250201882Skeramida    int async)
4251178676Ssam{
4252198429Srpaulo/* Fixed-point arithmetic division using a n-bit fractional part. */
4253178676Ssam#define fdivround(a, b, n)	\
4254178676Ssam	((((1 << n) * (a)) / (b) + (1 << n) / 2) / (1 << n))
4255198429Srpaulo/* Linear interpolation. */
4256178676Ssam#define interpolate(x, x1, y1, x2, y2, n)	\
4257178676Ssam	((y1) + fdivround(((int)(x) - (x1)) * ((y2) - (y1)), (x2) - (x1), n))
4258178676Ssam
4259178676Ssam	static const int tdiv[IWN_NATTEN_GROUPS] = { 9, 8, 8, 8, 6 };
4260178676Ssam	struct iwn_ucode_info *uc = &sc->ucode_info;
4261198429Srpaulo	struct iwn4965_cmd_txpower cmd;
4262198429Srpaulo	struct iwn4965_eeprom_chan_samples *chans;
4263220723Sbschmidt	const uint8_t *rf_gain, *dsp_gain;
4264178676Ssam	int32_t vdiff, tdiff;
4265178676Ssam	int i, c, grp, maxpwr;
4266198429Srpaulo	uint8_t chan;
4267178676Ssam
4268220687Sbschmidt	/* Retrieve current channel from last RXON. */
4269220687Sbschmidt	chan = sc->rxon.chan;
4270201209Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "setting TX power for channel %d\n",
4271201209Srpaulo	    chan);
4272178676Ssam
4273178676Ssam	memset(&cmd, 0, sizeof cmd);
4274178676Ssam	cmd.band = IEEE80211_IS_CHAN_5GHZ(ch) ? 0 : 1;
4275178676Ssam	cmd.chan = chan;
4276178676Ssam
4277178676Ssam	if (IEEE80211_IS_CHAN_5GHZ(ch)) {
4278178676Ssam		maxpwr   = sc->maxpwr5GHz;
4279198429Srpaulo		rf_gain  = iwn4965_rf_gain_5ghz;
4280198429Srpaulo		dsp_gain = iwn4965_dsp_gain_5ghz;
4281178676Ssam	} else {
4282178676Ssam		maxpwr   = sc->maxpwr2GHz;
4283198429Srpaulo		rf_gain  = iwn4965_rf_gain_2ghz;
4284198429Srpaulo		dsp_gain = iwn4965_dsp_gain_2ghz;
4285178676Ssam	}
4286178676Ssam
4287198429Srpaulo	/* Compute voltage compensation. */
4288178676Ssam	vdiff = ((int32_t)le32toh(uc->volt) - sc->eeprom_voltage) / 7;
4289178676Ssam	if (vdiff > 0)
4290178676Ssam		vdiff *= 2;
4291178676Ssam	if (abs(vdiff) > 2)
4292178676Ssam		vdiff = 0;
4293178676Ssam	DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
4294178676Ssam	    "%s: voltage compensation=%d (UCODE=%d, EEPROM=%d)\n",
4295178676Ssam	    __func__, vdiff, le32toh(uc->volt), sc->eeprom_voltage);
4296178676Ssam
4297201209Srpaulo	/* Get channel attenuation group. */
4298178676Ssam	if (chan <= 20)		/* 1-20 */
4299178676Ssam		grp = 4;
4300178676Ssam	else if (chan <= 43)	/* 34-43 */
4301178676Ssam		grp = 0;
4302178676Ssam	else if (chan <= 70)	/* 44-70 */
4303178676Ssam		grp = 1;
4304178676Ssam	else if (chan <= 124)	/* 71-124 */
4305178676Ssam		grp = 2;
4306178676Ssam	else			/* 125-200 */
4307178676Ssam		grp = 3;
4308178676Ssam	DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
4309178676Ssam	    "%s: chan %d, attenuation group=%d\n", __func__, chan, grp);
4310178676Ssam
4311201209Srpaulo	/* Get channel sub-band. */
4312178676Ssam	for (i = 0; i < IWN_NBANDS; i++)
4313178676Ssam		if (sc->bands[i].lo != 0 &&
4314178676Ssam		    sc->bands[i].lo <= chan && chan <= sc->bands[i].hi)
4315178676Ssam			break;
4316198429Srpaulo	if (i == IWN_NBANDS)	/* Can't happen in real-life. */
4317198429Srpaulo		return EINVAL;
4318178676Ssam	chans = sc->bands[i].chans;
4319178676Ssam	DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
4320178676Ssam	    "%s: chan %d sub-band=%d\n", __func__, chan, i);
4321178676Ssam
4322198429Srpaulo	for (c = 0; c < 2; c++) {
4323178676Ssam		uint8_t power, gain, temp;
4324178676Ssam		int maxchpwr, pwr, ridx, idx;
4325178676Ssam
4326178676Ssam		power = interpolate(chan,
4327178676Ssam		    chans[0].num, chans[0].samples[c][1].power,
4328178676Ssam		    chans[1].num, chans[1].samples[c][1].power, 1);
4329178676Ssam		gain  = interpolate(chan,
4330178676Ssam		    chans[0].num, chans[0].samples[c][1].gain,
4331178676Ssam		    chans[1].num, chans[1].samples[c][1].gain, 1);
4332178676Ssam		temp  = interpolate(chan,
4333178676Ssam		    chans[0].num, chans[0].samples[c][1].temp,
4334178676Ssam		    chans[1].num, chans[1].samples[c][1].temp, 1);
4335178676Ssam		DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
4336178676Ssam		    "%s: Tx chain %d: power=%d gain=%d temp=%d\n",
4337178676Ssam		    __func__, c, power, gain, temp);
4338178676Ssam
4339198429Srpaulo		/* Compute temperature compensation. */
4340178676Ssam		tdiff = ((sc->temp - temp) * 2) / tdiv[grp];
4341178676Ssam		DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
4342178676Ssam		    "%s: temperature compensation=%d (current=%d, EEPROM=%d)\n",
4343178676Ssam		    __func__, tdiff, sc->temp, temp);
4344178676Ssam
4345178676Ssam		for (ridx = 0; ridx <= IWN_RIDX_MAX; ridx++) {
4346201209Srpaulo			/* Convert dBm to half-dBm. */
4347198429Srpaulo			maxchpwr = sc->maxpwr[chan] * 2;
4348198429Srpaulo			if ((ridx / 8) & 1)
4349198429Srpaulo				maxchpwr -= 6;	/* MIMO 2T: -3dB */
4350178676Ssam
4351198429Srpaulo			pwr = maxpwr;
4352178676Ssam
4353198429Srpaulo			/* Adjust TX power based on rate. */
4354198429Srpaulo			if ((ridx % 8) == 5)
4355198429Srpaulo				pwr -= 15;	/* OFDM48: -7.5dB */
4356198429Srpaulo			else if ((ridx % 8) == 6)
4357198429Srpaulo				pwr -= 17;	/* OFDM54: -8.5dB */
4358198429Srpaulo			else if ((ridx % 8) == 7)
4359198429Srpaulo				pwr -= 20;	/* OFDM60: -10dB */
4360198429Srpaulo			else
4361198429Srpaulo				pwr -= 10;	/* Others: -5dB */
4362178676Ssam
4363201209Srpaulo			/* Do not exceed channel max TX power. */
4364178676Ssam			if (pwr > maxchpwr)
4365178676Ssam				pwr = maxchpwr;
4366178676Ssam
4367178676Ssam			idx = gain - (pwr - power) - tdiff - vdiff;
4368178676Ssam			if ((ridx / 8) & 1)	/* MIMO */
4369178676Ssam				idx += (int32_t)le32toh(uc->atten[grp][c]);
4370178676Ssam
4371178676Ssam			if (cmd.band == 0)
4372178676Ssam				idx += 9;	/* 5GHz */
4373178676Ssam			if (ridx == IWN_RIDX_MAX)
4374178676Ssam				idx += 5;	/* CCK */
4375178676Ssam
4376198429Srpaulo			/* Make sure idx stays in a valid range. */
4377178676Ssam			if (idx < 0)
4378178676Ssam				idx = 0;
4379198429Srpaulo			else if (idx > IWN4965_MAX_PWR_INDEX)
4380198429Srpaulo				idx = IWN4965_MAX_PWR_INDEX;
4381178676Ssam
4382178676Ssam			DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
4383178676Ssam			    "%s: Tx chain %d, rate idx %d: power=%d\n",
4384178676Ssam			    __func__, c, ridx, idx);
4385178676Ssam			cmd.power[ridx].rf_gain[c] = rf_gain[idx];
4386178676Ssam			cmd.power[ridx].dsp_gain[c] = dsp_gain[idx];
4387178676Ssam		}
4388178676Ssam	}
4389178676Ssam
4390178676Ssam	DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
4391178676Ssam	    "%s: set tx power for chan %d\n", __func__, chan);
4392178676Ssam	return iwn_cmd(sc, IWN_CMD_TXPOWER, &cmd, sizeof cmd, async);
4393178676Ssam
4394178676Ssam#undef interpolate
4395178676Ssam#undef fdivround
4396178676Ssam}
4397178676Ssam
4398206477Sbschmidtstatic int
4399201882Skeramidaiwn5000_set_txpower(struct iwn_softc *sc, struct ieee80211_channel *ch,
4400201882Skeramida    int async)
4401198429Srpaulo{
4402198429Srpaulo	struct iwn5000_cmd_txpower cmd;
4403198429Srpaulo
4404198429Srpaulo	/*
4405198429Srpaulo	 * TX power calibration is handled automatically by the firmware
4406198429Srpaulo	 * for 5000 Series.
4407198429Srpaulo	 */
4408198429Srpaulo	memset(&cmd, 0, sizeof cmd);
4409198429Srpaulo	cmd.global_limit = 2 * IWN5000_TXPOWER_MAX_DBM;	/* 16 dBm */
4410198429Srpaulo	cmd.flags = IWN5000_TXPOWER_NO_CLOSED;
4411198429Srpaulo	cmd.srv_limit = IWN5000_TXPOWER_AUTO;
4412198429Srpaulo	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: setting TX power\n", __func__);
4413198429Srpaulo	return iwn_cmd(sc, IWN_CMD_TXPOWER_DBM, &cmd, sizeof cmd, async);
4414198429Srpaulo}
4415198429Srpaulo
4416178676Ssam/*
4417198429Srpaulo * Retrieve the maximum RSSI (in dBm) among receivers.
4418178676Ssam */
4419206477Sbschmidtstatic int
4420198429Srpauloiwn4965_get_rssi(struct iwn_softc *sc, struct iwn_rx_stat *stat)
4421178676Ssam{
4422198429Srpaulo	struct iwn4965_rx_phystat *phy = (void *)stat->phybuf;
4423198429Srpaulo	uint8_t mask, agc;
4424198429Srpaulo	int rssi;
4425178676Ssam
4426201209Srpaulo	mask = (le16toh(phy->antenna) >> 4) & IWN_ANT_ABC;
4427198429Srpaulo	agc  = (le16toh(phy->agc) >> 7) & 0x7f;
4428178676Ssam
4429178676Ssam	rssi = 0;
4430220689Sbschmidt	if (mask & IWN_ANT_A)
4431220689Sbschmidt		rssi = MAX(rssi, phy->rssi[0]);
4432220689Sbschmidt	if (mask & IWN_ANT_B)
4433220689Sbschmidt		rssi = MAX(rssi, phy->rssi[2]);
4434220689Sbschmidt	if (mask & IWN_ANT_C)
4435220689Sbschmidt		rssi = MAX(rssi, phy->rssi[4]);
4436198429Srpaulo
4437220724Sbschmidt	DPRINTF(sc, IWN_DEBUG_RECV,
4438220724Sbschmidt	    "%s: agc %d mask 0x%x rssi %d %d %d result %d\n", __func__, agc,
4439220724Sbschmidt	    mask, phy->rssi[0], phy->rssi[2], phy->rssi[4],
4440178676Ssam	    rssi - agc - IWN_RSSI_TO_DBM);
4441178676Ssam	return rssi - agc - IWN_RSSI_TO_DBM;
4442178676Ssam}
4443178676Ssam
4444206477Sbschmidtstatic int
4445198429Srpauloiwn5000_get_rssi(struct iwn_softc *sc, struct iwn_rx_stat *stat)
4446198429Srpaulo{
4447198429Srpaulo	struct iwn5000_rx_phystat *phy = (void *)stat->phybuf;
4448220723Sbschmidt	uint8_t agc;
4449198429Srpaulo	int rssi;
4450198429Srpaulo
4451198429Srpaulo	agc = (le32toh(phy->agc) >> 9) & 0x7f;
4452198429Srpaulo
4453198429Srpaulo	rssi = MAX(le16toh(phy->rssi[0]) & 0xff,
4454198429Srpaulo		   le16toh(phy->rssi[1]) & 0xff);
4455198429Srpaulo	rssi = MAX(le16toh(phy->rssi[2]) & 0xff, rssi);
4456198429Srpaulo
4457220724Sbschmidt	DPRINTF(sc, IWN_DEBUG_RECV,
4458220724Sbschmidt	    "%s: agc %d rssi %d %d %d result %d\n", __func__, agc,
4459201822Strasz	    phy->rssi[0], phy->rssi[1], phy->rssi[2],
4460198429Srpaulo	    rssi - agc - IWN_RSSI_TO_DBM);
4461198429Srpaulo	return rssi - agc - IWN_RSSI_TO_DBM;
4462198429Srpaulo}
4463198429Srpaulo
4464178676Ssam/*
4465198429Srpaulo * Retrieve the average noise (in dBm) among receivers.
4466178676Ssam */
4467206477Sbschmidtstatic int
4468178676Ssamiwn_get_noise(const struct iwn_rx_general_stats *stats)
4469178676Ssam{
4470178676Ssam	int i, total, nbant, noise;
4471178676Ssam
4472178676Ssam	total = nbant = 0;
4473178676Ssam	for (i = 0; i < 3; i++) {
4474198429Srpaulo		if ((noise = le32toh(stats->noise[i]) & 0xff) == 0)
4475198429Srpaulo			continue;
4476198429Srpaulo		total += noise;
4477198429Srpaulo		nbant++;
4478178676Ssam	}
4479198429Srpaulo	/* There should be at least one antenna but check anyway. */
4480178676Ssam	return (nbant == 0) ? -127 : (total / nbant) - 107;
4481178676Ssam}
4482178676Ssam
4483178676Ssam/*
4484198429Srpaulo * Compute temperature (in degC) from last received statistics.
4485178676Ssam */
4486206477Sbschmidtstatic int
4487198429Srpauloiwn4965_get_temperature(struct iwn_softc *sc)
4488178676Ssam{
4489178676Ssam	struct iwn_ucode_info *uc = &sc->ucode_info;
4490178676Ssam	int32_t r1, r2, r3, r4, temp;
4491178676Ssam
4492178676Ssam	r1 = le32toh(uc->temp[0].chan20MHz);
4493178676Ssam	r2 = le32toh(uc->temp[1].chan20MHz);
4494178676Ssam	r3 = le32toh(uc->temp[2].chan20MHz);
4495178676Ssam	r4 = le32toh(sc->rawtemp);
4496178676Ssam
4497220725Sbschmidt	if (r1 == r3)	/* Prevents division by 0 (should not happen). */
4498178676Ssam		return 0;
4499178676Ssam
4500198429Srpaulo	/* Sign-extend 23-bit R4 value to 32-bit. */
4501220659Sbschmidt	r4 = ((r4 & 0xffffff) ^ 0x800000) - 0x800000;
4502198429Srpaulo	/* Compute temperature in Kelvin. */
4503178676Ssam	temp = (259 * (r4 - r2)) / (r3 - r1);
4504178676Ssam	temp = (temp * 97) / 100 + 8;
4505178676Ssam
4506201209Srpaulo	DPRINTF(sc, IWN_DEBUG_ANY, "temperature %dK/%dC\n", temp,
4507201209Srpaulo	    IWN_KTOC(temp));
4508178676Ssam	return IWN_KTOC(temp);
4509178676Ssam}
4510178676Ssam
4511206477Sbschmidtstatic int
4512198429Srpauloiwn5000_get_temperature(struct iwn_softc *sc)
4513198429Srpaulo{
4514201209Srpaulo	int32_t temp;
4515201209Srpaulo
4516198429Srpaulo	/*
4517198429Srpaulo	 * Temperature is not used by the driver for 5000 Series because
4518220725Sbschmidt	 * TX power calibration is handled by firmware.
4519198429Srpaulo	 */
4520201209Srpaulo	temp = le32toh(sc->rawtemp);
4521201209Srpaulo	if (sc->hw_type == IWN_HW_REV_TYPE_5150) {
4522201209Srpaulo		temp = (temp / -5) + sc->temp_off;
4523201209Srpaulo		temp = IWN_KTOC(temp);
4524201209Srpaulo	}
4525201209Srpaulo	return temp;
4526198429Srpaulo}
4527198429Srpaulo
4528178676Ssam/*
4529178676Ssam * Initialize sensitivity calibration state machine.
4530178676Ssam */
4531206477Sbschmidtstatic int
4532178676Ssamiwn_init_sensitivity(struct iwn_softc *sc)
4533178676Ssam{
4534220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
4535178676Ssam	struct iwn_calib_state *calib = &sc->calib;
4536198429Srpaulo	uint32_t flags;
4537178676Ssam	int error;
4538178676Ssam
4539198429Srpaulo	/* Reset calibration state machine. */
4540178676Ssam	memset(calib, 0, sizeof (*calib));
4541178676Ssam	calib->state = IWN_CALIB_STATE_INIT;
4542178676Ssam	calib->cck_state = IWN_CCK_STATE_HIFA;
4543198429Srpaulo	/* Set initial correlation values. */
4544201209Srpaulo	calib->ofdm_x1     = sc->limits->min_ofdm_x1;
4545201209Srpaulo	calib->ofdm_mrc_x1 = sc->limits->min_ofdm_mrc_x1;
4546206444Sbschmidt	calib->ofdm_x4     = sc->limits->min_ofdm_x4;
4547201209Srpaulo	calib->ofdm_mrc_x4 = sc->limits->min_ofdm_mrc_x4;
4548198429Srpaulo	calib->cck_x4      = 125;
4549201209Srpaulo	calib->cck_mrc_x4  = sc->limits->min_cck_mrc_x4;
4550201209Srpaulo	calib->energy_cck  = sc->limits->energy_cck;
4551178676Ssam
4552198429Srpaulo	/* Write initial sensitivity. */
4553220726Sbschmidt	if ((error = iwn_send_sensitivity(sc)) != 0)
4554178676Ssam		return error;
4555178676Ssam
4556198429Srpaulo	/* Write initial gains. */
4557220728Sbschmidt	if ((error = ops->init_gains(sc)) != 0)
4558198429Srpaulo		return error;
4559198429Srpaulo
4560198429Srpaulo	/* Request statistics at each beacon interval. */
4561198429Srpaulo	flags = 0;
4562220724Sbschmidt	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: sending request for statistics\n",
4563220724Sbschmidt	    __func__);
4564198429Srpaulo	return iwn_cmd(sc, IWN_CMD_GET_STATISTICS, &flags, sizeof flags, 1);
4565178676Ssam}
4566178676Ssam
4567178676Ssam/*
4568178676Ssam * Collect noise and RSSI statistics for the first 20 beacons received
4569178676Ssam * after association and use them to determine connected antennas and
4570198429Srpaulo * to set differential gains.
4571178676Ssam */
4572206477Sbschmidtstatic void
4573198429Srpauloiwn_collect_noise(struct iwn_softc *sc,
4574178676Ssam    const struct iwn_rx_general_stats *stats)
4575178676Ssam{
4576220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
4577178676Ssam	struct iwn_calib_state *calib = &sc->calib;
4578198429Srpaulo	uint32_t val;
4579198429Srpaulo	int i;
4580178676Ssam
4581198429Srpaulo	/* Accumulate RSSI and noise for all 3 antennas. */
4582178676Ssam	for (i = 0; i < 3; i++) {
4583178676Ssam		calib->rssi[i] += le32toh(stats->rssi[i]) & 0xff;
4584178676Ssam		calib->noise[i] += le32toh(stats->noise[i]) & 0xff;
4585178676Ssam	}
4586198429Srpaulo	/* NB: We update differential gains only once after 20 beacons. */
4587178676Ssam	if (++calib->nbeacons < 20)
4588178676Ssam		return;
4589178676Ssam
4590198429Srpaulo	/* Determine highest average RSSI. */
4591198429Srpaulo	val = MAX(calib->rssi[0], calib->rssi[1]);
4592198429Srpaulo	val = MAX(calib->rssi[2], val);
4593178676Ssam
4594198429Srpaulo	/* Determine which antennas are connected. */
4595210110Sbschmidt	sc->chainmask = sc->rxchainmask;
4596178676Ssam	for (i = 0; i < 3; i++)
4597210110Sbschmidt		if (val - calib->rssi[i] > 15 * 20)
4598210110Sbschmidt			sc->chainmask &= ~(1 << i);
4599210110Sbschmidt	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4600210110Sbschmidt	    "%s: RX chains mask: theoretical=0x%x, actual=0x%x\n",
4601210110Sbschmidt	    __func__, sc->rxchainmask, sc->chainmask);
4602210110Sbschmidt
4603198429Srpaulo	/* If none of the TX antennas are connected, keep at least one. */
4604201209Srpaulo	if ((sc->chainmask & sc->txchainmask) == 0)
4605201209Srpaulo		sc->chainmask |= IWN_LSB(sc->txchainmask);
4606178676Ssam
4607220728Sbschmidt	(void)ops->set_gains(sc);
4608198429Srpaulo	calib->state = IWN_CALIB_STATE_RUN;
4609198429Srpaulo
4610198429Srpaulo#ifdef notyet
4611198429Srpaulo	/* XXX Disable RX chains with no antennas connected. */
4612201209Srpaulo	sc->rxon.rxchain = htole16(IWN_RXCHAIN_SEL(sc->chainmask));
4613220728Sbschmidt	(void)iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, sc->rxonsz, 1);
4614198429Srpaulo#endif
4615198429Srpaulo
4616198429Srpaulo#if 0
4617198429Srpaulo	/* XXX: not yet */
4618198429Srpaulo	/* Enable power-saving mode if requested by user. */
4619198429Srpaulo	if (sc->sc_ic.ic_flags & IEEE80211_F_PMGTON)
4620198429Srpaulo		(void)iwn_set_pslevel(sc, 0, 3, 1);
4621198429Srpaulo#endif
4622198429Srpaulo}
4623198429Srpaulo
4624206477Sbschmidtstatic int
4625198429Srpauloiwn4965_init_gains(struct iwn_softc *sc)
4626198429Srpaulo{
4627198429Srpaulo	struct iwn_phy_calib_gain cmd;
4628198429Srpaulo
4629198429Srpaulo	memset(&cmd, 0, sizeof cmd);
4630198429Srpaulo	cmd.code = IWN4965_PHY_CALIB_DIFF_GAIN;
4631198429Srpaulo	/* Differential gains initially set to 0 for all 3 antennas. */
4632198429Srpaulo	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4633198429Srpaulo	    "%s: setting initial differential gains\n", __func__);
4634198429Srpaulo	return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1);
4635198429Srpaulo}
4636198429Srpaulo
4637206477Sbschmidtstatic int
4638198429Srpauloiwn5000_init_gains(struct iwn_softc *sc)
4639198429Srpaulo{
4640198429Srpaulo	struct iwn_phy_calib cmd;
4641198429Srpaulo
4642198429Srpaulo	memset(&cmd, 0, sizeof cmd);
4643220866Sbschmidt	cmd.code = sc->reset_noise_gain;
4644198429Srpaulo	cmd.ngroups = 1;
4645198429Srpaulo	cmd.isvalid = 1;
4646198429Srpaulo	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4647198429Srpaulo	    "%s: setting initial differential gains\n", __func__);
4648198429Srpaulo	return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1);
4649198429Srpaulo}
4650198429Srpaulo
4651206477Sbschmidtstatic int
4652198429Srpauloiwn4965_set_gains(struct iwn_softc *sc)
4653198429Srpaulo{
4654198429Srpaulo	struct iwn_calib_state *calib = &sc->calib;
4655198429Srpaulo	struct iwn_phy_calib_gain cmd;
4656198429Srpaulo	int i, delta, noise;
4657198429Srpaulo
4658198429Srpaulo	/* Get minimal noise among connected antennas. */
4659201209Srpaulo	noise = INT_MAX;	/* NB: There's at least one antenna. */
4660178676Ssam	for (i = 0; i < 3; i++)
4661201209Srpaulo		if (sc->chainmask & (1 << i))
4662198429Srpaulo			noise = MIN(calib->noise[i], noise);
4663178676Ssam
4664178676Ssam	memset(&cmd, 0, sizeof cmd);
4665198429Srpaulo	cmd.code = IWN4965_PHY_CALIB_DIFF_GAIN;
4666198429Srpaulo	/* Set differential gains for connected antennas. */
4667178676Ssam	for (i = 0; i < 3; i++) {
4668201209Srpaulo		if (sc->chainmask & (1 << i)) {
4669198429Srpaulo			/* Compute attenuation (in unit of 1.5dB). */
4670198429Srpaulo			delta = (noise - (int32_t)calib->noise[i]) / 30;
4671198429Srpaulo			/* NB: delta <= 0 */
4672198429Srpaulo			/* Limit to [-4.5dB,0]. */
4673198429Srpaulo			cmd.gain[i] = MIN(abs(delta), 3);
4674198429Srpaulo			if (delta < 0)
4675198429Srpaulo				cmd.gain[i] |= 1 << 2;	/* sign bit */
4676178676Ssam		}
4677178676Ssam	}
4678178676Ssam	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4679198429Srpaulo	    "setting differential gains Ant A/B/C: %x/%x/%x (%x)\n",
4680201209Srpaulo	    cmd.gain[0], cmd.gain[1], cmd.gain[2], sc->chainmask);
4681198429Srpaulo	return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1);
4682178676Ssam}
4683178676Ssam
4684206477Sbschmidtstatic int
4685198429Srpauloiwn5000_set_gains(struct iwn_softc *sc)
4686198429Srpaulo{
4687198429Srpaulo	struct iwn_calib_state *calib = &sc->calib;
4688198429Srpaulo	struct iwn_phy_calib_gain cmd;
4689220723Sbschmidt	int i, ant, div, delta;
4690198429Srpaulo
4691206444Sbschmidt	/* We collected 20 beacons and !=6050 need a 1.5 factor. */
4692206444Sbschmidt	div = (sc->hw_type == IWN_HW_REV_TYPE_6050) ? 20 : 30;
4693198429Srpaulo
4694198429Srpaulo	memset(&cmd, 0, sizeof cmd);
4695220866Sbschmidt	cmd.code = sc->noise_gain;
4696198429Srpaulo	cmd.ngroups = 1;
4697198429Srpaulo	cmd.isvalid = 1;
4698201209Srpaulo	/* Get first available RX antenna as referential. */
4699201209Srpaulo	ant = IWN_LSB(sc->rxchainmask);
4700201209Srpaulo	/* Set differential gains for other antennas. */
4701201209Srpaulo	for (i = ant + 1; i < 3; i++) {
4702201209Srpaulo		if (sc->chainmask & (1 << i)) {
4703201209Srpaulo			/* The delta is relative to antenna "ant". */
4704201209Srpaulo			delta = ((int32_t)calib->noise[ant] -
4705206444Sbschmidt			    (int32_t)calib->noise[i]) / div;
4706198429Srpaulo			/* Limit to [-4.5dB,+4.5dB]. */
4707198429Srpaulo			cmd.gain[i - 1] = MIN(abs(delta), 3);
4708198429Srpaulo			if (delta < 0)
4709198429Srpaulo				cmd.gain[i - 1] |= 1 << 2;	/* sign bit */
4710198429Srpaulo		}
4711198429Srpaulo	}
4712198429Srpaulo	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4713198429Srpaulo	    "setting differential gains Ant B/C: %x/%x (%x)\n",
4714201209Srpaulo	    cmd.gain[0], cmd.gain[1], sc->chainmask);
4715198429Srpaulo	return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1);
4716198429Srpaulo}
4717198429Srpaulo
4718178676Ssam/*
4719198429Srpaulo * Tune RF RX sensitivity based on the number of false alarms detected
4720178676Ssam * during the last beacon period.
4721178676Ssam */
4722206477Sbschmidtstatic void
4723178676Ssamiwn_tune_sensitivity(struct iwn_softc *sc, const struct iwn_rx_stats *stats)
4724178676Ssam{
4725198429Srpaulo#define inc(val, inc, max)			\
4726178676Ssam	if ((val) < (max)) {			\
4727178676Ssam		if ((val) < (max) - (inc))	\
4728178676Ssam			(val) += (inc);		\
4729178676Ssam		else				\
4730178676Ssam			(val) = (max);		\
4731178676Ssam		needs_update = 1;		\
4732178676Ssam	}
4733198429Srpaulo#define dec(val, dec, min)			\
4734178676Ssam	if ((val) > (min)) {			\
4735178676Ssam		if ((val) > (min) + (dec))	\
4736178676Ssam			(val) -= (dec);		\
4737178676Ssam		else				\
4738178676Ssam			(val) = (min);		\
4739178676Ssam		needs_update = 1;		\
4740178676Ssam	}
4741178676Ssam
4742201209Srpaulo	const struct iwn_sensitivity_limits *limits = sc->limits;
4743178676Ssam	struct iwn_calib_state *calib = &sc->calib;
4744178676Ssam	uint32_t val, rxena, fa;
4745178676Ssam	uint32_t energy[3], energy_min;
4746198439Srpaulo	uint8_t noise[3], noise_ref;
4747198429Srpaulo	int i, needs_update = 0;
4748178676Ssam
4749198429Srpaulo	/* Check that we've been enabled long enough. */
4750220726Sbschmidt	if ((rxena = le32toh(stats->general.load)) == 0)
4751178676Ssam		return;
4752178676Ssam
4753198429Srpaulo	/* Compute number of false alarms since last call for OFDM. */
4754178676Ssam	fa  = le32toh(stats->ofdm.bad_plcp) - calib->bad_plcp_ofdm;
4755178676Ssam	fa += le32toh(stats->ofdm.fa) - calib->fa_ofdm;
4756220634Sbschmidt	fa *= 200 * IEEE80211_DUR_TU;	/* 200TU */
4757178676Ssam
4758198429Srpaulo	/* Save counters values for next call. */
4759178676Ssam	calib->bad_plcp_ofdm = le32toh(stats->ofdm.bad_plcp);
4760178676Ssam	calib->fa_ofdm = le32toh(stats->ofdm.fa);
4761178676Ssam
4762178676Ssam	if (fa > 50 * rxena) {
4763198429Srpaulo		/* High false alarm count, decrease sensitivity. */
4764178676Ssam		DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4765178676Ssam		    "%s: OFDM high false alarm count: %u\n", __func__, fa);
4766198429Srpaulo		inc(calib->ofdm_x1,     1, limits->max_ofdm_x1);
4767198429Srpaulo		inc(calib->ofdm_mrc_x1, 1, limits->max_ofdm_mrc_x1);
4768198429Srpaulo		inc(calib->ofdm_x4,     1, limits->max_ofdm_x4);
4769198429Srpaulo		inc(calib->ofdm_mrc_x4, 1, limits->max_ofdm_mrc_x4);
4770178676Ssam
4771178676Ssam	} else if (fa < 5 * rxena) {
4772198429Srpaulo		/* Low false alarm count, increase sensitivity. */
4773178676Ssam		DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4774178676Ssam		    "%s: OFDM low false alarm count: %u\n", __func__, fa);
4775198429Srpaulo		dec(calib->ofdm_x1,     1, limits->min_ofdm_x1);
4776198429Srpaulo		dec(calib->ofdm_mrc_x1, 1, limits->min_ofdm_mrc_x1);
4777198429Srpaulo		dec(calib->ofdm_x4,     1, limits->min_ofdm_x4);
4778198429Srpaulo		dec(calib->ofdm_mrc_x4, 1, limits->min_ofdm_mrc_x4);
4779178676Ssam	}
4780178676Ssam
4781198429Srpaulo	/* Compute maximum noise among 3 receivers. */
4782178676Ssam	for (i = 0; i < 3; i++)
4783178676Ssam		noise[i] = (le32toh(stats->general.noise[i]) >> 8) & 0xff;
4784198429Srpaulo	val = MAX(noise[0], noise[1]);
4785198429Srpaulo	val = MAX(noise[2], val);
4786198429Srpaulo	/* Insert it into our samples table. */
4787178676Ssam	calib->noise_samples[calib->cur_noise_sample] = val;
4788178676Ssam	calib->cur_noise_sample = (calib->cur_noise_sample + 1) % 20;
4789178676Ssam
4790198429Srpaulo	/* Compute maximum noise among last 20 samples. */
4791178676Ssam	noise_ref = calib->noise_samples[0];
4792178676Ssam	for (i = 1; i < 20; i++)
4793198429Srpaulo		noise_ref = MAX(noise_ref, calib->noise_samples[i]);
4794178676Ssam
4795198429Srpaulo	/* Compute maximum energy among 3 receivers. */
4796178676Ssam	for (i = 0; i < 3; i++)
4797178676Ssam		energy[i] = le32toh(stats->general.energy[i]);
4798198429Srpaulo	val = MIN(energy[0], energy[1]);
4799198429Srpaulo	val = MIN(energy[2], val);
4800198429Srpaulo	/* Insert it into our samples table. */
4801178676Ssam	calib->energy_samples[calib->cur_energy_sample] = val;
4802178676Ssam	calib->cur_energy_sample = (calib->cur_energy_sample + 1) % 10;
4803178676Ssam
4804198429Srpaulo	/* Compute minimum energy among last 10 samples. */
4805178676Ssam	energy_min = calib->energy_samples[0];
4806178676Ssam	for (i = 1; i < 10; i++)
4807198429Srpaulo		energy_min = MAX(energy_min, calib->energy_samples[i]);
4808178676Ssam	energy_min += 6;
4809178676Ssam
4810198429Srpaulo	/* Compute number of false alarms since last call for CCK. */
4811178676Ssam	fa  = le32toh(stats->cck.bad_plcp) - calib->bad_plcp_cck;
4812178676Ssam	fa += le32toh(stats->cck.fa) - calib->fa_cck;
4813220634Sbschmidt	fa *= 200 * IEEE80211_DUR_TU;	/* 200TU */
4814178676Ssam
4815198429Srpaulo	/* Save counters values for next call. */
4816178676Ssam	calib->bad_plcp_cck = le32toh(stats->cck.bad_plcp);
4817178676Ssam	calib->fa_cck = le32toh(stats->cck.fa);
4818178676Ssam
4819178676Ssam	if (fa > 50 * rxena) {
4820198429Srpaulo		/* High false alarm count, decrease sensitivity. */
4821178676Ssam		DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4822178676Ssam		    "%s: CCK high false alarm count: %u\n", __func__, fa);
4823178676Ssam		calib->cck_state = IWN_CCK_STATE_HIFA;
4824178676Ssam		calib->low_fa = 0;
4825178676Ssam
4826198429Srpaulo		if (calib->cck_x4 > 160) {
4827178676Ssam			calib->noise_ref = noise_ref;
4828178676Ssam			if (calib->energy_cck > 2)
4829198429Srpaulo				dec(calib->energy_cck, 2, energy_min);
4830178676Ssam		}
4831198429Srpaulo		if (calib->cck_x4 < 160) {
4832198429Srpaulo			calib->cck_x4 = 161;
4833178676Ssam			needs_update = 1;
4834178676Ssam		} else
4835198429Srpaulo			inc(calib->cck_x4, 3, limits->max_cck_x4);
4836178676Ssam
4837198429Srpaulo		inc(calib->cck_mrc_x4, 3, limits->max_cck_mrc_x4);
4838178676Ssam
4839178676Ssam	} else if (fa < 5 * rxena) {
4840198429Srpaulo		/* Low false alarm count, increase sensitivity. */
4841178676Ssam		DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4842178676Ssam		    "%s: CCK low false alarm count: %u\n", __func__, fa);
4843178676Ssam		calib->cck_state = IWN_CCK_STATE_LOFA;
4844178676Ssam		calib->low_fa++;
4845178676Ssam
4846198429Srpaulo		if (calib->cck_state != IWN_CCK_STATE_INIT &&
4847198429Srpaulo		    (((int32_t)calib->noise_ref - (int32_t)noise_ref) > 2 ||
4848220726Sbschmidt		     calib->low_fa > 100)) {
4849198429Srpaulo			inc(calib->energy_cck, 2, limits->min_energy_cck);
4850198429Srpaulo			dec(calib->cck_x4,     3, limits->min_cck_x4);
4851198429Srpaulo			dec(calib->cck_mrc_x4, 3, limits->min_cck_mrc_x4);
4852178676Ssam		}
4853178676Ssam	} else {
4854198429Srpaulo		/* Not worth to increase or decrease sensitivity. */
4855178676Ssam		DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4856178676Ssam		    "%s: CCK normal false alarm count: %u\n", __func__, fa);
4857178676Ssam		calib->low_fa = 0;
4858178676Ssam		calib->noise_ref = noise_ref;
4859178676Ssam
4860178676Ssam		if (calib->cck_state == IWN_CCK_STATE_HIFA) {
4861198429Srpaulo			/* Previous interval had many false alarms. */
4862198429Srpaulo			dec(calib->energy_cck, 8, energy_min);
4863178676Ssam		}
4864178676Ssam		calib->cck_state = IWN_CCK_STATE_INIT;
4865178676Ssam	}
4866178676Ssam
4867178676Ssam	if (needs_update)
4868178676Ssam		(void)iwn_send_sensitivity(sc);
4869198429Srpaulo#undef dec
4870198429Srpaulo#undef inc
4871178676Ssam}
4872178676Ssam
4873206477Sbschmidtstatic int
4874178676Ssamiwn_send_sensitivity(struct iwn_softc *sc)
4875178676Ssam{
4876178676Ssam	struct iwn_calib_state *calib = &sc->calib;
4877220729Sbschmidt	struct iwn_enhanced_sensitivity_cmd cmd;
4878220729Sbschmidt	int len;
4879178676Ssam
4880178676Ssam	memset(&cmd, 0, sizeof cmd);
4881220729Sbschmidt	len = sizeof (struct iwn_sensitivity_cmd);
4882178676Ssam	cmd.which = IWN_SENSITIVITY_WORKTBL;
4883198429Srpaulo	/* OFDM modulation. */
4884220726Sbschmidt	cmd.corr_ofdm_x1       = htole16(calib->ofdm_x1);
4885220726Sbschmidt	cmd.corr_ofdm_mrc_x1   = htole16(calib->ofdm_mrc_x1);
4886220726Sbschmidt	cmd.corr_ofdm_x4       = htole16(calib->ofdm_x4);
4887220726Sbschmidt	cmd.corr_ofdm_mrc_x4   = htole16(calib->ofdm_mrc_x4);
4888220726Sbschmidt	cmd.energy_ofdm        = htole16(sc->limits->energy_ofdm);
4889220726Sbschmidt	cmd.energy_ofdm_th     = htole16(62);
4890198429Srpaulo	/* CCK modulation. */
4891220726Sbschmidt	cmd.corr_cck_x4        = htole16(calib->cck_x4);
4892220726Sbschmidt	cmd.corr_cck_mrc_x4    = htole16(calib->cck_mrc_x4);
4893220726Sbschmidt	cmd.energy_cck         = htole16(calib->energy_cck);
4894198429Srpaulo	/* Barker modulation: use default values. */
4895220726Sbschmidt	cmd.corr_barker        = htole16(190);
4896220726Sbschmidt	cmd.corr_barker_mrc    = htole16(390);
4897178676Ssam
4898202986Srpaulo	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
4899178676Ssam	    "%s: set sensitivity %d/%d/%d/%d/%d/%d/%d\n", __func__,
4900198429Srpaulo	    calib->ofdm_x1, calib->ofdm_mrc_x1, calib->ofdm_x4,
4901198429Srpaulo	    calib->ofdm_mrc_x4, calib->cck_x4,
4902198429Srpaulo	    calib->cck_mrc_x4, calib->energy_cck);
4903220729Sbschmidt
4904220729Sbschmidt	if (!(sc->sc_flags & IWN_FLAG_ENH_SENS))
4905220729Sbschmidt		goto send;
4906220729Sbschmidt	/* Enhanced sensitivity settings. */
4907220729Sbschmidt	len = sizeof (struct iwn_enhanced_sensitivity_cmd);
4908220729Sbschmidt	cmd.ofdm_det_slope_mrc = htole16(668);
4909220729Sbschmidt	cmd.ofdm_det_icept_mrc = htole16(4);
4910220729Sbschmidt	cmd.ofdm_det_slope     = htole16(486);
4911220729Sbschmidt	cmd.ofdm_det_icept     = htole16(37);
4912220729Sbschmidt	cmd.cck_det_slope_mrc  = htole16(853);
4913220729Sbschmidt	cmd.cck_det_icept_mrc  = htole16(4);
4914220729Sbschmidt	cmd.cck_det_slope      = htole16(476);
4915220729Sbschmidt	cmd.cck_det_icept      = htole16(99);
4916220729Sbschmidtsend:
4917220729Sbschmidt	return iwn_cmd(sc, IWN_CMD_SET_SENSITIVITY, &cmd, len, 1);
4918178676Ssam}
4919178676Ssam
4920198429Srpaulo/*
4921198429Srpaulo * Set STA mode power saving level (between 0 and 5).
4922198429Srpaulo * Level 0 is CAM (Continuously Aware Mode), 5 is for maximum power saving.
4923198429Srpaulo */
4924206477Sbschmidtstatic int
4925198429Srpauloiwn_set_pslevel(struct iwn_softc *sc, int dtim, int level, int async)
4926198429Srpaulo{
4927220723Sbschmidt	struct iwn_pmgt_cmd cmd;
4928198429Srpaulo	const struct iwn_pmgt *pmgt;
4929198429Srpaulo	uint32_t max, skip_dtim;
4930220721Sbschmidt	uint32_t reg;
4931198429Srpaulo	int i;
4932198429Srpaulo
4933198429Srpaulo	/* Select which PS parameters to use. */
4934198429Srpaulo	if (dtim <= 2)
4935198429Srpaulo		pmgt = &iwn_pmgt[0][level];
4936198429Srpaulo	else if (dtim <= 10)
4937198429Srpaulo		pmgt = &iwn_pmgt[1][level];
4938198429Srpaulo	else
4939198429Srpaulo		pmgt = &iwn_pmgt[2][level];
4940198429Srpaulo
4941198429Srpaulo	memset(&cmd, 0, sizeof cmd);
4942198429Srpaulo	if (level != 0)	/* not CAM */
4943198429Srpaulo		cmd.flags |= htole16(IWN_PS_ALLOW_SLEEP);
4944198429Srpaulo	if (level == 5)
4945198429Srpaulo		cmd.flags |= htole16(IWN_PS_FAST_PD);
4946201209Srpaulo	/* Retrieve PCIe Active State Power Management (ASPM). */
4947220721Sbschmidt	reg = pci_read_config(sc->sc_dev, sc->sc_cap_off + 0x10, 1);
4948220721Sbschmidt	if (!(reg & 0x1))	/* L0s Entry disabled. */
4949198429Srpaulo		cmd.flags |= htole16(IWN_PS_PCI_PMGT);
4950198429Srpaulo	cmd.rxtimeout = htole32(pmgt->rxtimeout * 1024);
4951198429Srpaulo	cmd.txtimeout = htole32(pmgt->txtimeout * 1024);
4952198429Srpaulo
4953198429Srpaulo	if (dtim == 0) {
4954198429Srpaulo		dtim = 1;
4955198429Srpaulo		skip_dtim = 0;
4956198429Srpaulo	} else
4957198429Srpaulo		skip_dtim = pmgt->skip_dtim;
4958198429Srpaulo	if (skip_dtim != 0) {
4959198429Srpaulo		cmd.flags |= htole16(IWN_PS_SLEEP_OVER_DTIM);
4960198429Srpaulo		max = pmgt->intval[4];
4961198429Srpaulo		if (max == (uint32_t)-1)
4962198429Srpaulo			max = dtim * (skip_dtim + 1);
4963198429Srpaulo		else if (max > dtim)
4964198429Srpaulo			max = (max / dtim) * dtim;
4965198429Srpaulo	} else
4966198429Srpaulo		max = dtim;
4967198429Srpaulo	for (i = 0; i < 5; i++)
4968198429Srpaulo		cmd.intval[i] = htole32(MIN(max, pmgt->intval[i]));
4969198429Srpaulo
4970198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "setting power saving level to %d\n",
4971198429Srpaulo	    level);
4972198429Srpaulo	return iwn_cmd(sc, IWN_CMD_SET_POWER_MODE, &cmd, sizeof cmd, async);
4973198429Srpaulo}
4974198429Srpaulo
4975206477Sbschmidtstatic int
4976220662Sbschmidtiwn_send_btcoex(struct iwn_softc *sc)
4977220662Sbschmidt{
4978220662Sbschmidt	struct iwn_bluetooth cmd;
4979220662Sbschmidt
4980220662Sbschmidt	memset(&cmd, 0, sizeof cmd);
4981220662Sbschmidt	cmd.flags = IWN_BT_COEX_CHAN_ANN | IWN_BT_COEX_BT_PRIO;
4982220662Sbschmidt	cmd.lead_time = IWN_BT_LEAD_TIME_DEF;
4983220662Sbschmidt	cmd.max_kill = IWN_BT_MAX_KILL_DEF;
4984220662Sbschmidt	DPRINTF(sc, IWN_DEBUG_RESET, "%s: configuring bluetooth coexistence\n",
4985220662Sbschmidt	    __func__);
4986220662Sbschmidt	return iwn_cmd(sc, IWN_CMD_BT_COEX, &cmd, sizeof(cmd), 0);
4987220662Sbschmidt}
4988220662Sbschmidt
4989220662Sbschmidtstatic int
4990220891Sbschmidtiwn_send_advanced_btcoex(struct iwn_softc *sc)
4991220891Sbschmidt{
4992220891Sbschmidt	static const uint32_t btcoex_3wire[12] = {
4993220891Sbschmidt		0xaaaaaaaa, 0xaaaaaaaa, 0xaeaaaaaa, 0xaaaaaaaa,
4994220891Sbschmidt		0xcc00ff28, 0x0000aaaa, 0xcc00aaaa, 0x0000aaaa,
4995220891Sbschmidt		0xc0004000, 0x00004000, 0xf0005000, 0xf0005000,
4996220891Sbschmidt	};
4997220891Sbschmidt	struct iwn6000_btcoex_config btconfig;
4998220891Sbschmidt	struct iwn_btcoex_priotable btprio;
4999220891Sbschmidt	struct iwn_btcoex_prot btprot;
5000220891Sbschmidt	int error, i;
5001220891Sbschmidt
5002220891Sbschmidt	memset(&btconfig, 0, sizeof btconfig);
5003220891Sbschmidt	btconfig.flags = 145;
5004220891Sbschmidt	btconfig.max_kill = 5;
5005220891Sbschmidt	btconfig.bt3_t7_timer = 1;
5006220891Sbschmidt	btconfig.kill_ack = htole32(0xffff0000);
5007220891Sbschmidt	btconfig.kill_cts = htole32(0xffff0000);
5008220891Sbschmidt	btconfig.sample_time = 2;
5009220891Sbschmidt	btconfig.bt3_t2_timer = 0xc;
5010220891Sbschmidt	for (i = 0; i < 12; i++)
5011220891Sbschmidt		btconfig.lookup_table[i] = htole32(btcoex_3wire[i]);
5012220891Sbschmidt	btconfig.valid = htole16(0xff);
5013220891Sbschmidt	btconfig.prio_boost = 0xf0;
5014220891Sbschmidt	DPRINTF(sc, IWN_DEBUG_RESET,
5015220891Sbschmidt	    "%s: configuring advanced bluetooth coexistence\n", __func__);
5016220891Sbschmidt	error = iwn_cmd(sc, IWN_CMD_BT_COEX, &btconfig, sizeof(btconfig), 1);
5017220891Sbschmidt	if (error != 0)
5018220891Sbschmidt		return error;
5019220891Sbschmidt
5020220891Sbschmidt	memset(&btprio, 0, sizeof btprio);
5021220891Sbschmidt	btprio.calib_init1 = 0x6;
5022220891Sbschmidt	btprio.calib_init2 = 0x7;
5023220891Sbschmidt	btprio.calib_periodic_low1 = 0x2;
5024220891Sbschmidt	btprio.calib_periodic_low2 = 0x3;
5025220891Sbschmidt	btprio.calib_periodic_high1 = 0x4;
5026220891Sbschmidt	btprio.calib_periodic_high2 = 0x5;
5027220891Sbschmidt	btprio.dtim = 0x6;
5028220891Sbschmidt	btprio.scan52 = 0x8;
5029220891Sbschmidt	btprio.scan24 = 0xa;
5030220891Sbschmidt	error = iwn_cmd(sc, IWN_CMD_BT_COEX_PRIOTABLE, &btprio, sizeof(btprio),
5031220891Sbschmidt	    1);
5032220891Sbschmidt	if (error != 0)
5033220891Sbschmidt		return error;
5034220891Sbschmidt
5035220891Sbschmidt	/* Force BT state machine change. */
5036220891Sbschmidt	memset(&btprot, 0, sizeof btprio);
5037220891Sbschmidt	btprot.open = 1;
5038220891Sbschmidt	btprot.type = 1;
5039220891Sbschmidt	error = iwn_cmd(sc, IWN_CMD_BT_COEX_PROT, &btprot, sizeof(btprot), 1);
5040220891Sbschmidt	if (error != 0)
5041220891Sbschmidt		return error;
5042220891Sbschmidt	btprot.open = 0;
5043220891Sbschmidt	return iwn_cmd(sc, IWN_CMD_BT_COEX_PROT, &btprot, sizeof(btprot), 1);
5044220891Sbschmidt}
5045220891Sbschmidt
5046220891Sbschmidtstatic int
5047227967Sbschmidtiwn5000_runtime_calib(struct iwn_softc *sc)
5048227967Sbschmidt{
5049227967Sbschmidt	struct iwn5000_calib_config cmd;
5050227967Sbschmidt
5051227967Sbschmidt	memset(&cmd, 0, sizeof cmd);
5052227967Sbschmidt	cmd.ucode.once.enable = 0xffffffff;
5053227967Sbschmidt	cmd.ucode.once.start = IWN5000_CALIB_DC;
5054227967Sbschmidt	DPRINTF(sc, IWN_DEBUG_CALIBRATE,
5055227967Sbschmidt	    "%s: configuring runtime calibration\n", __func__);
5056227967Sbschmidt	return iwn_cmd(sc, IWN5000_CMD_CALIB_CONFIG, &cmd, sizeof(cmd), 0);
5057227967Sbschmidt}
5058227967Sbschmidt
5059227967Sbschmidtstatic int
5060198429Srpauloiwn_config(struct iwn_softc *sc)
5061198429Srpaulo{
5062220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
5063198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
5064198429Srpaulo	struct ieee80211com *ic = ifp->if_l2com;
5065201209Srpaulo	uint32_t txmask;
5066220723Sbschmidt	uint16_t rxchain;
5067198429Srpaulo	int error;
5068198429Srpaulo
5069220676Sbschmidt	if (sc->hw_type == IWN_HW_REV_TYPE_6005) {
5070220676Sbschmidt		/* Set radio temperature sensor offset. */
5071220676Sbschmidt		error = iwn5000_temp_offset_calib(sc);
5072220676Sbschmidt		if (error != 0) {
5073220676Sbschmidt			device_printf(sc->sc_dev,
5074220676Sbschmidt			    "%s: could not set temperature offset\n", __func__);
5075220676Sbschmidt			return error;
5076220676Sbschmidt		}
5077220676Sbschmidt	}
5078220676Sbschmidt
5079227967Sbschmidt	if (sc->hw_type == IWN_HW_REV_TYPE_6050) {
5080227967Sbschmidt		/* Configure runtime DC calibration. */
5081227967Sbschmidt		error = iwn5000_runtime_calib(sc);
5082227967Sbschmidt		if (error != 0) {
5083227967Sbschmidt			device_printf(sc->sc_dev,
5084227967Sbschmidt			    "%s: could not configure runtime calibration\n",
5085227967Sbschmidt			    __func__);
5086227967Sbschmidt			return error;
5087227967Sbschmidt		}
5088227967Sbschmidt	}
5089227967Sbschmidt
5090220725Sbschmidt	/* Configure valid TX chains for >=5000 Series. */
5091201209Srpaulo	if (sc->hw_type != IWN_HW_REV_TYPE_4965) {
5092201209Srpaulo		txmask = htole32(sc->txchainmask);
5093201209Srpaulo		DPRINTF(sc, IWN_DEBUG_RESET,
5094201209Srpaulo		    "%s: configuring valid TX chains 0x%x\n", __func__, txmask);
5095201209Srpaulo		error = iwn_cmd(sc, IWN5000_CMD_TX_ANT_CONFIG, &txmask,
5096201209Srpaulo		    sizeof txmask, 0);
5097201209Srpaulo		if (error != 0) {
5098201209Srpaulo			device_printf(sc->sc_dev,
5099201209Srpaulo			    "%s: could not configure valid TX chains, "
5100201209Srpaulo			    "error %d\n", __func__, error);
5101201209Srpaulo			return error;
5102201209Srpaulo		}
5103198429Srpaulo	}
5104198429Srpaulo
5105198429Srpaulo	/* Configure bluetooth coexistence. */
5106220891Sbschmidt	if (sc->sc_flags & IWN_FLAG_ADV_BTCOEX)
5107220891Sbschmidt		error = iwn_send_advanced_btcoex(sc);
5108220891Sbschmidt	else
5109220891Sbschmidt		error = iwn_send_btcoex(sc);
5110198429Srpaulo	if (error != 0) {
5111198429Srpaulo		device_printf(sc->sc_dev,
5112198429Srpaulo		    "%s: could not configure bluetooth coexistence, error %d\n",
5113198429Srpaulo		    __func__, error);
5114198429Srpaulo		return error;
5115198429Srpaulo	}
5116198429Srpaulo
5117201209Srpaulo	/* Set mode, channel, RX filter and enable RX. */
5118198429Srpaulo	memset(&sc->rxon, 0, sizeof (struct iwn_rxon));
5119198429Srpaulo	IEEE80211_ADDR_COPY(sc->rxon.myaddr, IF_LLADDR(ifp));
5120198429Srpaulo	IEEE80211_ADDR_COPY(sc->rxon.wlap, IF_LLADDR(ifp));
5121198429Srpaulo	sc->rxon.chan = ieee80211_chan2ieee(ic, ic->ic_curchan);
5122198429Srpaulo	sc->rxon.flags = htole32(IWN_RXON_TSF | IWN_RXON_CTS_TO_SELF);
5123198429Srpaulo	if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan))
5124198429Srpaulo		sc->rxon.flags |= htole32(IWN_RXON_AUTO | IWN_RXON_24GHZ);
5125198429Srpaulo	switch (ic->ic_opmode) {
5126198429Srpaulo	case IEEE80211_M_STA:
5127198429Srpaulo		sc->rxon.mode = IWN_MODE_STA;
5128198429Srpaulo		sc->rxon.filter = htole32(IWN_FILTER_MULTICAST);
5129198429Srpaulo		break;
5130198429Srpaulo	case IEEE80211_M_MONITOR:
5131198429Srpaulo		sc->rxon.mode = IWN_MODE_MONITOR;
5132198429Srpaulo		sc->rxon.filter = htole32(IWN_FILTER_MULTICAST |
5133198429Srpaulo		    IWN_FILTER_CTL | IWN_FILTER_PROMISC);
5134198429Srpaulo		break;
5135198429Srpaulo	default:
5136198429Srpaulo		/* Should not get there. */
5137198429Srpaulo		break;
5138198429Srpaulo	}
5139198429Srpaulo	sc->rxon.cck_mask  = 0x0f;	/* not yet negotiated */
5140198429Srpaulo	sc->rxon.ofdm_mask = 0xff;	/* not yet negotiated */
5141198429Srpaulo	sc->rxon.ht_single_mask = 0xff;
5142198429Srpaulo	sc->rxon.ht_dual_mask = 0xff;
5143201209Srpaulo	sc->rxon.ht_triple_mask = 0xff;
5144201209Srpaulo	rxchain =
5145201209Srpaulo	    IWN_RXCHAIN_VALID(sc->rxchainmask) |
5146201209Srpaulo	    IWN_RXCHAIN_MIMO_COUNT(2) |
5147201209Srpaulo	    IWN_RXCHAIN_IDLE_COUNT(2);
5148198429Srpaulo	sc->rxon.rxchain = htole16(rxchain);
5149198429Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "%s: setting configuration\n", __func__);
5150220728Sbschmidt	error = iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, sc->rxonsz, 0);
5151198429Srpaulo	if (error != 0) {
5152220726Sbschmidt		device_printf(sc->sc_dev, "%s: RXON command failed\n",
5153220726Sbschmidt		    __func__);
5154198429Srpaulo		return error;
5155198429Srpaulo	}
5156198429Srpaulo
5157220726Sbschmidt	if ((error = iwn_add_broadcast_node(sc, 0)) != 0) {
5158220726Sbschmidt		device_printf(sc->sc_dev, "%s: could not add broadcast node\n",
5159220726Sbschmidt		    __func__);
5160201209Srpaulo		return error;
5161201209Srpaulo	}
5162201209Srpaulo
5163198429Srpaulo	/* Configuration has changed, set TX power accordingly. */
5164220728Sbschmidt	if ((error = ops->set_txpower(sc, ic->ic_curchan, 0)) != 0) {
5165220726Sbschmidt		device_printf(sc->sc_dev, "%s: could not set TX power\n",
5166220726Sbschmidt		    __func__);
5167198429Srpaulo		return error;
5168198429Srpaulo	}
5169198429Srpaulo
5170220726Sbschmidt	if ((error = iwn_set_critical_temp(sc)) != 0) {
5171198429Srpaulo		device_printf(sc->sc_dev,
5172220724Sbschmidt		    "%s: could not set critical temperature\n", __func__);
5173198429Srpaulo		return error;
5174198429Srpaulo	}
5175198429Srpaulo
5176201209Srpaulo	/* Set power saving level to CAM during initialization. */
5177220726Sbschmidt	if ((error = iwn_set_pslevel(sc, 0, 0, 0)) != 0) {
5178198429Srpaulo		device_printf(sc->sc_dev,
5179201209Srpaulo		    "%s: could not set power saving level\n", __func__);
5180198429Srpaulo		return error;
5181198429Srpaulo	}
5182198429Srpaulo	return 0;
5183198429Srpaulo}
5184198429Srpaulo
5185220634Sbschmidt/*
5186220634Sbschmidt * Add an ssid element to a frame.
5187220634Sbschmidt */
5188220634Sbschmidtstatic uint8_t *
5189220634Sbschmidtieee80211_add_ssid(uint8_t *frm, const uint8_t *ssid, u_int len)
5190220634Sbschmidt{
5191220634Sbschmidt	*frm++ = IEEE80211_ELEMID_SSID;
5192220634Sbschmidt	*frm++ = len;
5193220634Sbschmidt	memcpy(frm, ssid, len);
5194220634Sbschmidt	return frm + len;
5195220634Sbschmidt}
5196220634Sbschmidt
5197206477Sbschmidtstatic int
5198198429Srpauloiwn_scan(struct iwn_softc *sc)
5199198429Srpaulo{
5200198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
5201198429Srpaulo	struct ieee80211com *ic = ifp->if_l2com;
5202198429Srpaulo	struct ieee80211_scan_state *ss = ic->ic_scan;	/*XXX*/
5203221641Sbschmidt	struct ieee80211_node *ni = ss->ss_vap->iv_bss;
5204198429Srpaulo	struct iwn_scan_hdr *hdr;
5205198429Srpaulo	struct iwn_cmd_data *tx;
5206198429Srpaulo	struct iwn_scan_essid *essid;
5207198429Srpaulo	struct iwn_scan_chan *chan;
5208198429Srpaulo	struct ieee80211_frame *wh;
5209198429Srpaulo	struct ieee80211_rateset *rs;
5210198429Srpaulo	struct ieee80211_channel *c;
5211220726Sbschmidt	uint8_t *buf, *frm;
5212220723Sbschmidt	uint16_t rxchain;
5213220726Sbschmidt	uint8_t txant;
5214220634Sbschmidt	int buflen, error;
5215198429Srpaulo
5216198429Srpaulo	buf = malloc(IWN_SCAN_MAXSZ, M_DEVBUF, M_NOWAIT | M_ZERO);
5217198429Srpaulo	if (buf == NULL) {
5218198429Srpaulo		device_printf(sc->sc_dev,
5219198429Srpaulo		    "%s: could not allocate buffer for scan command\n",
5220198429Srpaulo		    __func__);
5221198429Srpaulo		return ENOMEM;
5222198429Srpaulo	}
5223198429Srpaulo	hdr = (struct iwn_scan_hdr *)buf;
5224198429Srpaulo	/*
5225198429Srpaulo	 * Move to the next channel if no frames are received within 10ms
5226198429Srpaulo	 * after sending the probe request.
5227198429Srpaulo	 */
5228198429Srpaulo	hdr->quiet_time = htole16(10);		/* timeout in milliseconds */
5229198429Srpaulo	hdr->quiet_threshold = htole16(1);	/* min # of packets */
5230198429Srpaulo
5231198429Srpaulo	/* Select antennas for scanning. */
5232201209Srpaulo	rxchain =
5233201209Srpaulo	    IWN_RXCHAIN_VALID(sc->rxchainmask) |
5234201209Srpaulo	    IWN_RXCHAIN_FORCE_MIMO_SEL(sc->rxchainmask) |
5235201209Srpaulo	    IWN_RXCHAIN_DRIVER_FORCE;
5236198429Srpaulo	if (IEEE80211_IS_CHAN_A(ic->ic_curchan) &&
5237198429Srpaulo	    sc->hw_type == IWN_HW_REV_TYPE_4965) {
5238198429Srpaulo		/* Ant A must be avoided in 5GHz because of an HW bug. */
5239222679Sbschmidt		rxchain |= IWN_RXCHAIN_FORCE_SEL(IWN_ANT_B);
5240198429Srpaulo	} else	/* Use all available RX antennas. */
5241201209Srpaulo		rxchain |= IWN_RXCHAIN_FORCE_SEL(sc->rxchainmask);
5242198429Srpaulo	hdr->rxchain = htole16(rxchain);
5243198429Srpaulo	hdr->filter = htole32(IWN_FILTER_MULTICAST | IWN_FILTER_BEACON);
5244198429Srpaulo
5245198429Srpaulo	tx = (struct iwn_cmd_data *)(hdr + 1);
5246198429Srpaulo	tx->flags = htole32(IWN_TX_AUTO_SEQ);
5247220728Sbschmidt	tx->id = sc->broadcast_id;
5248198429Srpaulo	tx->lifetime = htole32(IWN_LIFETIME_INFINITE);
5249198429Srpaulo
5250222679Sbschmidt	if (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan)) {
5251198429Srpaulo		/* Send probe requests at 6Mbps. */
5252221648Sbschmidt		tx->rate = htole32(0xd);
5253201209Srpaulo		rs = &ic->ic_sup_rates[IEEE80211_MODE_11A];
5254198429Srpaulo	} else {
5255198429Srpaulo		hdr->flags = htole32(IWN_RXON_24GHZ | IWN_RXON_AUTO);
5256222679Sbschmidt		if (sc->hw_type == IWN_HW_REV_TYPE_4965 &&
5257222679Sbschmidt		    sc->rxon.associd && sc->rxon.chan > 14)
5258222679Sbschmidt			tx->rate = htole32(0xd);
5259222679Sbschmidt		else {
5260222679Sbschmidt			/* Send probe requests at 1Mbps. */
5261222679Sbschmidt			tx->rate = htole32(10 | IWN_RFLAG_CCK);
5262222679Sbschmidt		}
5263201209Srpaulo		rs = &ic->ic_sup_rates[IEEE80211_MODE_11G];
5264198429Srpaulo	}
5265198429Srpaulo	/* Use the first valid TX antenna. */
5266201209Srpaulo	txant = IWN_LSB(sc->txchainmask);
5267221648Sbschmidt	tx->rate |= htole32(IWN_RFLAG_ANT(txant));
5268198429Srpaulo
5269198429Srpaulo	essid = (struct iwn_scan_essid *)(tx + 1);
5270198429Srpaulo	if (ss->ss_ssid[0].len != 0) {
5271198429Srpaulo		essid[0].id = IEEE80211_ELEMID_SSID;
5272198429Srpaulo		essid[0].len = ss->ss_ssid[0].len;
5273198429Srpaulo		memcpy(essid[0].data, ss->ss_ssid[0].ssid, ss->ss_ssid[0].len);
5274198429Srpaulo	}
5275198429Srpaulo	/*
5276198429Srpaulo	 * Build a probe request frame.  Most of the following code is a
5277198429Srpaulo	 * copy & paste of what is done in net80211.
5278198429Srpaulo	 */
5279198429Srpaulo	wh = (struct ieee80211_frame *)(essid + 20);
5280198429Srpaulo	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT |
5281198429Srpaulo	    IEEE80211_FC0_SUBTYPE_PROBE_REQ;
5282198429Srpaulo	wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
5283198429Srpaulo	IEEE80211_ADDR_COPY(wh->i_addr1, ifp->if_broadcastaddr);
5284198429Srpaulo	IEEE80211_ADDR_COPY(wh->i_addr2, IF_LLADDR(ifp));
5285198429Srpaulo	IEEE80211_ADDR_COPY(wh->i_addr3, ifp->if_broadcastaddr);
5286198429Srpaulo	*(uint16_t *)&wh->i_dur[0] = 0;	/* filled by HW */
5287198429Srpaulo	*(uint16_t *)&wh->i_seq[0] = 0;	/* filled by HW */
5288198429Srpaulo
5289198429Srpaulo	frm = (uint8_t *)(wh + 1);
5290220634Sbschmidt	frm = ieee80211_add_ssid(frm, NULL, 0);
5291220634Sbschmidt	frm = ieee80211_add_rates(frm, rs);
5292220634Sbschmidt	if (rs->rs_nrates > IEEE80211_RATE_SIZE)
5293220634Sbschmidt		frm = ieee80211_add_xrates(frm, rs);
5294221641Sbschmidt	if (ic->ic_htcaps & IEEE80211_HTC_HT)
5295221641Sbschmidt		frm = ieee80211_add_htcap(frm, ni);
5296198429Srpaulo
5297198429Srpaulo	/* Set length of probe request. */
5298198429Srpaulo	tx->len = htole16(frm - (uint8_t *)wh);
5299198429Srpaulo
5300198429Srpaulo	c = ic->ic_curchan;
5301198429Srpaulo	chan = (struct iwn_scan_chan *)frm;
5302201209Srpaulo	chan->chan = htole16(ieee80211_chan2ieee(ic, c));
5303198429Srpaulo	chan->flags = 0;
5304198429Srpaulo	if (ss->ss_nssid > 0)
5305198429Srpaulo		chan->flags |= htole32(IWN_CHAN_NPBREQS(1));
5306198429Srpaulo	chan->dsp_gain = 0x6e;
5307201209Srpaulo	if (IEEE80211_IS_CHAN_5GHZ(c) &&
5308201209Srpaulo	    !(c->ic_flags & IEEE80211_CHAN_PASSIVE)) {
5309198429Srpaulo		chan->rf_gain = 0x3b;
5310198429Srpaulo		chan->active  = htole16(24);
5311198429Srpaulo		chan->passive = htole16(110);
5312201209Srpaulo		chan->flags |= htole32(IWN_CHAN_ACTIVE);
5313201209Srpaulo	} else if (IEEE80211_IS_CHAN_5GHZ(c)) {
5314201209Srpaulo		chan->rf_gain = 0x3b;
5315201209Srpaulo		chan->active  = htole16(24);
5316201209Srpaulo		if (sc->rxon.associd)
5317201209Srpaulo			chan->passive = htole16(78);
5318201209Srpaulo		else
5319201209Srpaulo			chan->passive = htole16(110);
5320207709Sbschmidt		hdr->crc_threshold = 0xffff;
5321201209Srpaulo	} else if (!(c->ic_flags & IEEE80211_CHAN_PASSIVE)) {
5322201209Srpaulo		chan->rf_gain = 0x28;
5323201209Srpaulo		chan->active  = htole16(36);
5324201209Srpaulo		chan->passive = htole16(120);
5325201209Srpaulo		chan->flags |= htole32(IWN_CHAN_ACTIVE);
5326198429Srpaulo	} else {
5327198429Srpaulo		chan->rf_gain = 0x28;
5328198429Srpaulo		chan->active  = htole16(36);
5329201209Srpaulo		if (sc->rxon.associd)
5330201209Srpaulo			chan->passive = htole16(88);
5331201209Srpaulo		else
5332201209Srpaulo			chan->passive = htole16(120);
5333207709Sbschmidt		hdr->crc_threshold = 0xffff;
5334198429Srpaulo	}
5335198429Srpaulo
5336201209Srpaulo	DPRINTF(sc, IWN_DEBUG_STATE,
5337201209Srpaulo	    "%s: chan %u flags 0x%x rf_gain 0x%x "
5338198429Srpaulo	    "dsp_gain 0x%x active 0x%x passive 0x%x\n", __func__,
5339198429Srpaulo	    chan->chan, chan->flags, chan->rf_gain, chan->dsp_gain,
5340198429Srpaulo	    chan->active, chan->passive);
5341198429Srpaulo
5342201209Srpaulo	hdr->nchan++;
5343201209Srpaulo	chan++;
5344198429Srpaulo	buflen = (uint8_t *)chan - buf;
5345198429Srpaulo	hdr->len = htole16(buflen);
5346198429Srpaulo
5347198429Srpaulo	DPRINTF(sc, IWN_DEBUG_STATE, "sending scan command nchan=%d\n",
5348198429Srpaulo	    hdr->nchan);
5349198429Srpaulo	error = iwn_cmd(sc, IWN_CMD_SCAN, buf, buflen, 1);
5350198429Srpaulo	free(buf, M_DEVBUF);
5351198429Srpaulo	return error;
5352198429Srpaulo}
5353198429Srpaulo
5354206477Sbschmidtstatic int
5355191746Sthompsaiwn_auth(struct iwn_softc *sc, struct ieee80211vap *vap)
5356178676Ssam{
5357220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
5358178676Ssam	struct ifnet *ifp = sc->sc_ifp;
5359178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
5360178676Ssam	struct ieee80211_node *ni = vap->iv_bss;
5361178676Ssam	int error;
5362178676Ssam
5363201209Srpaulo	/* Update adapter configuration. */
5364198429Srpaulo	IEEE80211_ADDR_COPY(sc->rxon.bssid, ni->ni_bssid);
5365220636Sbschmidt	sc->rxon.chan = ieee80211_chan2ieee(ic, ni->ni_chan);
5366198429Srpaulo	sc->rxon.flags = htole32(IWN_RXON_TSF | IWN_RXON_CTS_TO_SELF);
5367178676Ssam	if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
5368198429Srpaulo		sc->rxon.flags |= htole32(IWN_RXON_AUTO | IWN_RXON_24GHZ);
5369198429Srpaulo	if (ic->ic_flags & IEEE80211_F_SHSLOT)
5370198429Srpaulo		sc->rxon.flags |= htole32(IWN_RXON_SHSLOT);
5371198429Srpaulo	if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
5372198429Srpaulo		sc->rxon.flags |= htole32(IWN_RXON_SHPREAMBLE);
5373178676Ssam	if (IEEE80211_IS_CHAN_A(ni->ni_chan)) {
5374198429Srpaulo		sc->rxon.cck_mask  = 0;
5375198429Srpaulo		sc->rxon.ofdm_mask = 0x15;
5376178676Ssam	} else if (IEEE80211_IS_CHAN_B(ni->ni_chan)) {
5377198429Srpaulo		sc->rxon.cck_mask  = 0x03;
5378198429Srpaulo		sc->rxon.ofdm_mask = 0;
5379178676Ssam	} else {
5380220725Sbschmidt		/* Assume 802.11b/g. */
5381198429Srpaulo		sc->rxon.cck_mask  = 0x0f;
5382198429Srpaulo		sc->rxon.ofdm_mask = 0x15;
5383178676Ssam	}
5384220724Sbschmidt	DPRINTF(sc, IWN_DEBUG_STATE, "rxon chan %d flags %x cck %x ofdm %x\n",
5385220724Sbschmidt	    sc->rxon.chan, sc->rxon.flags, sc->rxon.cck_mask,
5386220724Sbschmidt	    sc->rxon.ofdm_mask);
5387220728Sbschmidt	error = iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, sc->rxonsz, 1);
5388178676Ssam	if (error != 0) {
5389220726Sbschmidt		device_printf(sc->sc_dev, "%s: RXON command failed, error %d\n",
5390220726Sbschmidt		    __func__, error);
5391178676Ssam		return error;
5392178676Ssam	}
5393178676Ssam
5394198429Srpaulo	/* Configuration has changed, set TX power accordingly. */
5395220728Sbschmidt	if ((error = ops->set_txpower(sc, ni->ni_chan, 1)) != 0) {
5396178676Ssam		device_printf(sc->sc_dev,
5397220724Sbschmidt		    "%s: could not set TX power, error %d\n", __func__, error);
5398178676Ssam		return error;
5399178676Ssam	}
5400178676Ssam	/*
5401201209Srpaulo	 * Reconfiguring RXON clears the firmware nodes table so we must
5402178676Ssam	 * add the broadcast node again.
5403178676Ssam	 */
5404220726Sbschmidt	if ((error = iwn_add_broadcast_node(sc, 1)) != 0) {
5405178676Ssam		device_printf(sc->sc_dev,
5406220726Sbschmidt		    "%s: could not add broadcast node, error %d\n", __func__,
5407220726Sbschmidt		    error);
5408178676Ssam		return error;
5409178676Ssam	}
5410178676Ssam	return 0;
5411178676Ssam}
5412178676Ssam
5413206477Sbschmidtstatic int
5414191746Sthompsaiwn_run(struct iwn_softc *sc, struct ieee80211vap *vap)
5415178676Ssam{
5416220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
5417178676Ssam	struct ifnet *ifp = sc->sc_ifp;
5418178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
5419178676Ssam	struct ieee80211_node *ni = vap->iv_bss;
5420178676Ssam	struct iwn_node_info node;
5421221653Sbschmidt	uint32_t htflags = 0;
5422201209Srpaulo	int error;
5423178676Ssam
5424178676Ssam	if (ic->ic_opmode == IEEE80211_M_MONITOR) {
5425201209Srpaulo		/* Link LED blinks while monitoring. */
5426220674Sbschmidt		iwn_set_led(sc, IWN_LED_LINK, 5, 5);
5427178676Ssam		return 0;
5428178676Ssam	}
5429220726Sbschmidt	if ((error = iwn_set_timing(sc, ni)) != 0) {
5430198429Srpaulo		device_printf(sc->sc_dev,
5431198429Srpaulo		    "%s: could not set timing, error %d\n", __func__, error);
5432198429Srpaulo		return error;
5433198429Srpaulo	}
5434178676Ssam
5435201209Srpaulo	/* Update adapter configuration. */
5436201209Srpaulo	IEEE80211_ADDR_COPY(sc->rxon.bssid, ni->ni_bssid);
5437198429Srpaulo	sc->rxon.associd = htole16(IEEE80211_AID(ni->ni_associd));
5438220636Sbschmidt	sc->rxon.chan = ieee80211_chan2ieee(ic, ni->ni_chan);
5439220636Sbschmidt	sc->rxon.flags = htole32(IWN_RXON_TSF | IWN_RXON_CTS_TO_SELF);
5440201209Srpaulo	if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
5441201209Srpaulo		sc->rxon.flags |= htole32(IWN_RXON_AUTO | IWN_RXON_24GHZ);
5442178676Ssam	if (ic->ic_flags & IEEE80211_F_SHSLOT)
5443198429Srpaulo		sc->rxon.flags |= htole32(IWN_RXON_SHSLOT);
5444178676Ssam	if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
5445198429Srpaulo		sc->rxon.flags |= htole32(IWN_RXON_SHPREAMBLE);
5446201209Srpaulo	if (IEEE80211_IS_CHAN_A(ni->ni_chan)) {
5447201209Srpaulo		sc->rxon.cck_mask  = 0;
5448201209Srpaulo		sc->rxon.ofdm_mask = 0x15;
5449201209Srpaulo	} else if (IEEE80211_IS_CHAN_B(ni->ni_chan)) {
5450201209Srpaulo		sc->rxon.cck_mask  = 0x03;
5451201209Srpaulo		sc->rxon.ofdm_mask = 0;
5452201209Srpaulo	} else {
5453220725Sbschmidt		/* Assume 802.11b/g. */
5454201209Srpaulo		sc->rxon.cck_mask  = 0x0f;
5455201209Srpaulo		sc->rxon.ofdm_mask = 0x15;
5456201209Srpaulo	}
5457178676Ssam	if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) {
5458221653Sbschmidt		htflags |= IWN_RXON_HT_PROTMODE(ic->ic_curhtprotmode);
5459221653Sbschmidt		if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) {
5460221653Sbschmidt			switch (ic->ic_curhtprotmode) {
5461221653Sbschmidt			case IEEE80211_HTINFO_OPMODE_HT20PR:
5462221653Sbschmidt				htflags |= IWN_RXON_HT_MODEPURE40;
5463221653Sbschmidt				break;
5464221653Sbschmidt			default:
5465221653Sbschmidt				htflags |= IWN_RXON_HT_MODEMIXED;
5466221653Sbschmidt				break;
5467221653Sbschmidt			}
5468221653Sbschmidt		}
5469221653Sbschmidt		if (IEEE80211_IS_CHAN_HT40D(ni->ni_chan))
5470221653Sbschmidt			htflags |= IWN_RXON_HT_HT40MINUS;
5471221653Sbschmidt	}
5472221653Sbschmidt	sc->rxon.flags |= htole32(htflags);
5473198429Srpaulo	sc->rxon.filter |= htole32(IWN_FILTER_BSS);
5474220724Sbschmidt	DPRINTF(sc, IWN_DEBUG_STATE, "rxon chan %d flags %x\n",
5475220724Sbschmidt	    sc->rxon.chan, sc->rxon.flags);
5476220728Sbschmidt	error = iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, sc->rxonsz, 1);
5477178676Ssam	if (error != 0) {
5478178676Ssam		device_printf(sc->sc_dev,
5479220726Sbschmidt		    "%s: could not update configuration, error %d\n", __func__,
5480220726Sbschmidt		    error);
5481178676Ssam		return error;
5482178676Ssam	}
5483178676Ssam
5484198429Srpaulo	/* Configuration has changed, set TX power accordingly. */
5485220728Sbschmidt	if ((error = ops->set_txpower(sc, ni->ni_chan, 1)) != 0) {
5486178676Ssam		device_printf(sc->sc_dev,
5487220724Sbschmidt		    "%s: could not set TX power, error %d\n", __func__, error);
5488178676Ssam		return error;
5489178676Ssam	}
5490178676Ssam
5491220715Sbschmidt	/* Fake a join to initialize the TX rate. */
5492220715Sbschmidt	((struct iwn_node *)ni)->id = IWN_ID_BSS;
5493220715Sbschmidt	iwn_newassoc(ni, 1);
5494220715Sbschmidt
5495198429Srpaulo	/* Add BSS node. */
5496178676Ssam	memset(&node, 0, sizeof node);
5497178676Ssam	IEEE80211_ADDR_COPY(node.macaddr, ni->ni_macaddr);
5498178676Ssam	node.id = IWN_ID_BSS;
5499221653Sbschmidt	if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) {
5500221653Sbschmidt		switch (ni->ni_htcap & IEEE80211_HTCAP_SMPS) {
5501221653Sbschmidt		case IEEE80211_HTCAP_SMPS_ENA:
5502221653Sbschmidt			node.htflags |= htole32(IWN_SMPS_MIMO_DIS);
5503221653Sbschmidt			break;
5504221653Sbschmidt		case IEEE80211_HTCAP_SMPS_DYNAMIC:
5505221653Sbschmidt			node.htflags |= htole32(IWN_SMPS_MIMO_PROT);
5506221653Sbschmidt			break;
5507221653Sbschmidt		}
5508221653Sbschmidt		node.htflags |= htole32(IWN_AMDPU_SIZE_FACTOR(3) |
5509221653Sbschmidt		    IWN_AMDPU_DENSITY(5));	/* 4us */
5510221653Sbschmidt		if (IEEE80211_IS_CHAN_HT40(ni->ni_chan))
5511221653Sbschmidt			node.htflags |= htole32(IWN_NODE_HT40);
5512221653Sbschmidt	}
5513220724Sbschmidt	DPRINTF(sc, IWN_DEBUG_STATE, "%s: adding BSS node\n", __func__);
5514220728Sbschmidt	error = ops->add_node(sc, &node, 1);
5515178676Ssam	if (error != 0) {
5516220724Sbschmidt		device_printf(sc->sc_dev,
5517220724Sbschmidt		    "%s: could not add BSS node, error %d\n", __func__, error);
5518178676Ssam		return error;
5519178676Ssam	}
5520220724Sbschmidt	DPRINTF(sc, IWN_DEBUG_STATE, "%s: setting link quality for node %d\n",
5521220724Sbschmidt	    __func__, node.id);
5522220726Sbschmidt	if ((error = iwn_set_link_quality(sc, ni)) != 0) {
5523178676Ssam		device_printf(sc->sc_dev,
5524220724Sbschmidt		    "%s: could not setup link quality for node %d, error %d\n",
5525178676Ssam		    __func__, node.id, error);
5526178676Ssam		return error;
5527178676Ssam	}
5528178676Ssam
5529220726Sbschmidt	if ((error = iwn_init_sensitivity(sc)) != 0) {
5530178676Ssam		device_printf(sc->sc_dev,
5531220726Sbschmidt		    "%s: could not set sensitivity, error %d\n", __func__,
5532220726Sbschmidt		    error);
5533178676Ssam		return error;
5534178676Ssam	}
5535198429Srpaulo	/* Start periodic calibration timer. */
5536178676Ssam	sc->calib.state = IWN_CALIB_STATE_ASSOC;
5537220667Sbschmidt	sc->calib_cnt = 0;
5538220667Sbschmidt	callout_reset(&sc->calib_to, msecs_to_ticks(500), iwn_calib_timeout,
5539220667Sbschmidt	    sc);
5540178676Ssam
5541198429Srpaulo	/* Link LED always on while associated. */
5542178676Ssam	iwn_set_led(sc, IWN_LED_LINK, 0, 1);
5543178676Ssam	return 0;
5544178676Ssam}
5545178676Ssam
5546178676Ssam/*
5547201209Srpaulo * This function is called by upper layer when an ADDBA request is received
5548201209Srpaulo * from another STA and before the ADDBA response is sent.
5549201209Srpaulo */
5550206477Sbschmidtstatic int
5551221650Sbschmidtiwn_ampdu_rx_start(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap,
5552221650Sbschmidt    int baparamset, int batimeout, int baseqctl)
5553201209Srpaulo{
5554221650Sbschmidt#define MS(_v, _f)	(((_v) & _f) >> _f##_S)
5555221650Sbschmidt	struct iwn_softc *sc = ni->ni_ic->ic_ifp->if_softc;
5556220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
5557201209Srpaulo	struct iwn_node *wn = (void *)ni;
5558201209Srpaulo	struct iwn_node_info node;
5559221650Sbschmidt	uint16_t ssn;
5560221650Sbschmidt	uint8_t tid;
5561221650Sbschmidt	int error;
5562201209Srpaulo
5563221650Sbschmidt	tid = MS(le16toh(baparamset), IEEE80211_BAPS_TID);
5564221650Sbschmidt	ssn = MS(le16toh(baseqctl), IEEE80211_BASEQ_START);
5565221650Sbschmidt
5566201209Srpaulo	memset(&node, 0, sizeof node);
5567201209Srpaulo	node.id = wn->id;
5568201209Srpaulo	node.control = IWN_NODE_UPDATE;
5569201209Srpaulo	node.flags = IWN_FLAG_SET_ADDBA;
5570201209Srpaulo	node.addba_tid = tid;
5571221650Sbschmidt	node.addba_ssn = htole16(ssn);
5572201209Srpaulo	DPRINTF(sc, IWN_DEBUG_RECV, "ADDBA RA=%d TID=%d SSN=%d\n",
5573221650Sbschmidt	    wn->id, tid, ssn);
5574221650Sbschmidt	error = ops->add_node(sc, &node, 1);
5575221650Sbschmidt	if (error != 0)
5576221650Sbschmidt		return error;
5577221650Sbschmidt	return sc->sc_ampdu_rx_start(ni, rap, baparamset, batimeout, baseqctl);
5578221650Sbschmidt#undef MS
5579201209Srpaulo}
5580201209Srpaulo
5581201209Srpaulo/*
5582201209Srpaulo * This function is called by upper layer on teardown of an HT-immediate
5583220725Sbschmidt * Block Ack agreement (eg. uppon receipt of a DELBA frame).
5584201209Srpaulo */
5585206477Sbschmidtstatic void
5586221650Sbschmidtiwn_ampdu_rx_stop(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap)
5587201209Srpaulo{
5588221650Sbschmidt	struct ieee80211com *ic = ni->ni_ic;
5589221650Sbschmidt	struct iwn_softc *sc = ic->ic_ifp->if_softc;
5590220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
5591201209Srpaulo	struct iwn_node *wn = (void *)ni;
5592201209Srpaulo	struct iwn_node_info node;
5593221650Sbschmidt	uint8_t tid;
5594201209Srpaulo
5595221650Sbschmidt	/* XXX: tid as an argument */
5596221650Sbschmidt	for (tid = 0; tid < WME_NUM_TID; tid++) {
5597221650Sbschmidt		if (&ni->ni_rx_ampdu[tid] == rap)
5598221650Sbschmidt			break;
5599221650Sbschmidt	}
5600221650Sbschmidt
5601201209Srpaulo	memset(&node, 0, sizeof node);
5602201209Srpaulo	node.id = wn->id;
5603201209Srpaulo	node.control = IWN_NODE_UPDATE;
5604201209Srpaulo	node.flags = IWN_FLAG_SET_DELBA;
5605201209Srpaulo	node.delba_tid = tid;
5606201209Srpaulo	DPRINTF(sc, IWN_DEBUG_RECV, "DELBA RA=%d TID=%d\n", wn->id, tid);
5607220728Sbschmidt	(void)ops->add_node(sc, &node, 1);
5608221650Sbschmidt	sc->sc_ampdu_rx_stop(ni, rap);
5609201209Srpaulo}
5610201209Srpaulo
5611221651Sbschmidtstatic int
5612221651Sbschmidtiwn_addba_request(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap,
5613221651Sbschmidt    int dialogtoken, int baparamset, int batimeout)
5614221651Sbschmidt{
5615221651Sbschmidt	struct iwn_softc *sc = ni->ni_ic->ic_ifp->if_softc;
5616221651Sbschmidt	int qid;
5617221651Sbschmidt
5618221651Sbschmidt	for (qid = sc->firstaggqueue; qid < sc->ntxqs; qid++) {
5619221651Sbschmidt		if (sc->qid2tap[qid] == NULL)
5620221651Sbschmidt			break;
5621221651Sbschmidt	}
5622221651Sbschmidt	if (qid == sc->ntxqs) {
5623221651Sbschmidt		DPRINTF(sc, IWN_DEBUG_XMIT, "%s: not free aggregation queue\n",
5624221651Sbschmidt		    __func__);
5625221651Sbschmidt		return 0;
5626221651Sbschmidt	}
5627221651Sbschmidt	tap->txa_private = malloc(sizeof(int), M_DEVBUF, M_NOWAIT);
5628221651Sbschmidt	if (tap->txa_private == NULL) {
5629221651Sbschmidt		device_printf(sc->sc_dev,
5630221651Sbschmidt		    "%s: failed to alloc TX aggregation structure\n", __func__);
5631221651Sbschmidt		return 0;
5632221651Sbschmidt	}
5633221651Sbschmidt	sc->qid2tap[qid] = tap;
5634221651Sbschmidt	*(int *)tap->txa_private = qid;
5635221651Sbschmidt	return sc->sc_addba_request(ni, tap, dialogtoken, baparamset,
5636221651Sbschmidt	    batimeout);
5637221651Sbschmidt}
5638221651Sbschmidt
5639221651Sbschmidtstatic int
5640221651Sbschmidtiwn_addba_response(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap,
5641221651Sbschmidt    int code, int baparamset, int batimeout)
5642221651Sbschmidt{
5643221651Sbschmidt	struct iwn_softc *sc = ni->ni_ic->ic_ifp->if_softc;
5644221651Sbschmidt	int qid = *(int *)tap->txa_private;
5645221651Sbschmidt	uint8_t tid = WME_AC_TO_TID(tap->txa_ac);
5646221651Sbschmidt	int ret;
5647221651Sbschmidt
5648221651Sbschmidt	if (code == IEEE80211_STATUS_SUCCESS) {
5649221651Sbschmidt		ni->ni_txseqs[tid] = tap->txa_start & 0xfff;
5650221651Sbschmidt		ret = iwn_ampdu_tx_start(ni->ni_ic, ni, tid);
5651221651Sbschmidt		if (ret != 1)
5652221651Sbschmidt			return ret;
5653221651Sbschmidt	} else {
5654221651Sbschmidt		sc->qid2tap[qid] = NULL;
5655221651Sbschmidt		free(tap->txa_private, M_DEVBUF);
5656221651Sbschmidt		tap->txa_private = NULL;
5657221651Sbschmidt	}
5658221651Sbschmidt	return sc->sc_addba_response(ni, tap, code, baparamset, batimeout);
5659221651Sbschmidt}
5660221651Sbschmidt
5661201209Srpaulo/*
5662201209Srpaulo * This function is called by upper layer when an ADDBA response is received
5663201209Srpaulo * from another STA.
5664201209Srpaulo */
5665206477Sbschmidtstatic int
5666201209Srpauloiwn_ampdu_tx_start(struct ieee80211com *ic, struct ieee80211_node *ni,
5667201209Srpaulo    uint8_t tid)
5668201209Srpaulo{
5669221651Sbschmidt	struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[TID_TO_WME_AC(tid)];
5670221651Sbschmidt	struct iwn_softc *sc = ni->ni_ic->ic_ifp->if_softc;
5671220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
5672201209Srpaulo	struct iwn_node *wn = (void *)ni;
5673201209Srpaulo	struct iwn_node_info node;
5674221651Sbschmidt	int error, qid;
5675201209Srpaulo
5676201209Srpaulo	/* Enable TX for the specified RA/TID. */
5677201209Srpaulo	wn->disable_tid &= ~(1 << tid);
5678201209Srpaulo	memset(&node, 0, sizeof node);
5679201209Srpaulo	node.id = wn->id;
5680201209Srpaulo	node.control = IWN_NODE_UPDATE;
5681201209Srpaulo	node.flags = IWN_FLAG_SET_DISABLE_TID;
5682201209Srpaulo	node.disable_tid = htole16(wn->disable_tid);
5683220728Sbschmidt	error = ops->add_node(sc, &node, 1);
5684201209Srpaulo	if (error != 0)
5685221651Sbschmidt		return 0;
5686201209Srpaulo
5687201209Srpaulo	if ((error = iwn_nic_lock(sc)) != 0)
5688221651Sbschmidt		return 0;
5689221651Sbschmidt	qid = *(int *)tap->txa_private;
5690237917Sbschmidt	DPRINTF(sc, IWN_DEBUG_XMIT, "%s: ra=%d tid=%d ssn=%d qid=%d\n",
5691237917Sbschmidt	    __func__, wn->id, tid, tap->txa_start, qid);
5692221651Sbschmidt	ops->ampdu_tx_start(sc, ni, qid, tid, tap->txa_start & 0xfff);
5693201209Srpaulo	iwn_nic_unlock(sc);
5694221651Sbschmidt
5695221651Sbschmidt	iwn_set_link_quality(sc, ni);
5696221651Sbschmidt	return 1;
5697201209Srpaulo}
5698201209Srpaulo
5699206477Sbschmidtstatic void
5700221651Sbschmidtiwn_ampdu_tx_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap)
5701201209Srpaulo{
5702221651Sbschmidt	struct iwn_softc *sc = ni->ni_ic->ic_ifp->if_softc;
5703220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
5704221651Sbschmidt	uint8_t tid = WME_AC_TO_TID(tap->txa_ac);
5705221651Sbschmidt	int qid;
5706201209Srpaulo
5707237917Sbschmidt	sc->sc_addba_stop(ni, tap);
5708237917Sbschmidt
5709221651Sbschmidt	if (tap->txa_private == NULL)
5710221651Sbschmidt		return;
5711221651Sbschmidt
5712221651Sbschmidt	qid = *(int *)tap->txa_private;
5713237917Sbschmidt	if (sc->txq[qid].queued != 0)
5714237917Sbschmidt		return;
5715220726Sbschmidt	if (iwn_nic_lock(sc) != 0)
5716201209Srpaulo		return;
5717221651Sbschmidt	ops->ampdu_tx_stop(sc, qid, tid, tap->txa_start & 0xfff);
5718201209Srpaulo	iwn_nic_unlock(sc);
5719221651Sbschmidt	sc->qid2tap[qid] = NULL;
5720221651Sbschmidt	free(tap->txa_private, M_DEVBUF);
5721221651Sbschmidt	tap->txa_private = NULL;
5722201209Srpaulo}
5723201209Srpaulo
5724206477Sbschmidtstatic void
5725201209Srpauloiwn4965_ampdu_tx_start(struct iwn_softc *sc, struct ieee80211_node *ni,
5726221651Sbschmidt    int qid, uint8_t tid, uint16_t ssn)
5727201209Srpaulo{
5728201209Srpaulo	struct iwn_node *wn = (void *)ni;
5729201209Srpaulo
5730201209Srpaulo	/* Stop TX scheduler while we're changing its configuration. */
5731201209Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid),
5732201209Srpaulo	    IWN4965_TXQ_STATUS_CHGACT);
5733201209Srpaulo
5734201209Srpaulo	/* Assign RA/TID translation to the queue. */
5735201209Srpaulo	iwn_mem_write_2(sc, sc->sched_base + IWN4965_SCHED_TRANS_TBL(qid),
5736201209Srpaulo	    wn->id << 4 | tid);
5737201209Srpaulo
5738201209Srpaulo	/* Enable chain-building mode for the queue. */
5739201209Srpaulo	iwn_prph_setbits(sc, IWN4965_SCHED_QCHAIN_SEL, 1 << qid);
5740201209Srpaulo
5741201209Srpaulo	/* Set starting sequence number from the ADDBA request. */
5742221651Sbschmidt	sc->txq[qid].cur = sc->txq[qid].read = (ssn & 0xff);
5743201209Srpaulo	IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff));
5744201209Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_QUEUE_RDPTR(qid), ssn);
5745201209Srpaulo
5746201209Srpaulo	/* Set scheduler window size. */
5747201209Srpaulo	iwn_mem_write(sc, sc->sched_base + IWN4965_SCHED_QUEUE_OFFSET(qid),
5748201209Srpaulo	    IWN_SCHED_WINSZ);
5749201209Srpaulo	/* Set scheduler frame limit. */
5750201209Srpaulo	iwn_mem_write(sc, sc->sched_base + IWN4965_SCHED_QUEUE_OFFSET(qid) + 4,
5751201209Srpaulo	    IWN_SCHED_LIMIT << 16);
5752201209Srpaulo
5753201209Srpaulo	/* Enable interrupts for the queue. */
5754201209Srpaulo	iwn_prph_setbits(sc, IWN4965_SCHED_INTR_MASK, 1 << qid);
5755201209Srpaulo
5756201209Srpaulo	/* Mark the queue as active. */
5757201209Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid),
5758201209Srpaulo	    IWN4965_TXQ_STATUS_ACTIVE | IWN4965_TXQ_STATUS_AGGR_ENA |
5759201209Srpaulo	    iwn_tid2fifo[tid] << 1);
5760201209Srpaulo}
5761201209Srpaulo
5762206477Sbschmidtstatic void
5763221651Sbschmidtiwn4965_ampdu_tx_stop(struct iwn_softc *sc, int qid, uint8_t tid, uint16_t ssn)
5764201209Srpaulo{
5765201209Srpaulo	/* Stop TX scheduler while we're changing its configuration. */
5766201209Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid),
5767201209Srpaulo	    IWN4965_TXQ_STATUS_CHGACT);
5768201209Srpaulo
5769201209Srpaulo	/* Set starting sequence number from the ADDBA request. */
5770201209Srpaulo	IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff));
5771201209Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_QUEUE_RDPTR(qid), ssn);
5772201209Srpaulo
5773201209Srpaulo	/* Disable interrupts for the queue. */
5774201209Srpaulo	iwn_prph_clrbits(sc, IWN4965_SCHED_INTR_MASK, 1 << qid);
5775201209Srpaulo
5776201209Srpaulo	/* Mark the queue as inactive. */
5777201209Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid),
5778201209Srpaulo	    IWN4965_TXQ_STATUS_INACTIVE | iwn_tid2fifo[tid] << 1);
5779201209Srpaulo}
5780201209Srpaulo
5781206477Sbschmidtstatic void
5782201209Srpauloiwn5000_ampdu_tx_start(struct iwn_softc *sc, struct ieee80211_node *ni,
5783221651Sbschmidt    int qid, uint8_t tid, uint16_t ssn)
5784201209Srpaulo{
5785201209Srpaulo	struct iwn_node *wn = (void *)ni;
5786201209Srpaulo
5787201209Srpaulo	/* Stop TX scheduler while we're changing its configuration. */
5788201209Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid),
5789201209Srpaulo	    IWN5000_TXQ_STATUS_CHGACT);
5790201209Srpaulo
5791201209Srpaulo	/* Assign RA/TID translation to the queue. */
5792201209Srpaulo	iwn_mem_write_2(sc, sc->sched_base + IWN5000_SCHED_TRANS_TBL(qid),
5793201209Srpaulo	    wn->id << 4 | tid);
5794201209Srpaulo
5795201209Srpaulo	/* Enable chain-building mode for the queue. */
5796201209Srpaulo	iwn_prph_setbits(sc, IWN5000_SCHED_QCHAIN_SEL, 1 << qid);
5797201209Srpaulo
5798201209Srpaulo	/* Enable aggregation for the queue. */
5799201209Srpaulo	iwn_prph_setbits(sc, IWN5000_SCHED_AGGR_SEL, 1 << qid);
5800201209Srpaulo
5801201209Srpaulo	/* Set starting sequence number from the ADDBA request. */
5802221651Sbschmidt	sc->txq[qid].cur = sc->txq[qid].read = (ssn & 0xff);
5803201209Srpaulo	IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff));
5804201209Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_QUEUE_RDPTR(qid), ssn);
5805201209Srpaulo
5806201209Srpaulo	/* Set scheduler window size and frame limit. */
5807201209Srpaulo	iwn_mem_write(sc, sc->sched_base + IWN5000_SCHED_QUEUE_OFFSET(qid) + 4,
5808201209Srpaulo	    IWN_SCHED_LIMIT << 16 | IWN_SCHED_WINSZ);
5809201209Srpaulo
5810201209Srpaulo	/* Enable interrupts for the queue. */
5811201209Srpaulo	iwn_prph_setbits(sc, IWN5000_SCHED_INTR_MASK, 1 << qid);
5812201209Srpaulo
5813201209Srpaulo	/* Mark the queue as active. */
5814201209Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid),
5815201209Srpaulo	    IWN5000_TXQ_STATUS_ACTIVE | iwn_tid2fifo[tid]);
5816201209Srpaulo}
5817201209Srpaulo
5818206477Sbschmidtstatic void
5819221651Sbschmidtiwn5000_ampdu_tx_stop(struct iwn_softc *sc, int qid, uint8_t tid, uint16_t ssn)
5820201209Srpaulo{
5821201209Srpaulo	/* Stop TX scheduler while we're changing its configuration. */
5822201209Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid),
5823201209Srpaulo	    IWN5000_TXQ_STATUS_CHGACT);
5824201209Srpaulo
5825201209Srpaulo	/* Disable aggregation for the queue. */
5826201209Srpaulo	iwn_prph_clrbits(sc, IWN5000_SCHED_AGGR_SEL, 1 << qid);
5827201209Srpaulo
5828201209Srpaulo	/* Set starting sequence number from the ADDBA request. */
5829201209Srpaulo	IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff));
5830201209Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_QUEUE_RDPTR(qid), ssn);
5831201209Srpaulo
5832201209Srpaulo	/* Disable interrupts for the queue. */
5833201209Srpaulo	iwn_prph_clrbits(sc, IWN5000_SCHED_INTR_MASK, 1 << qid);
5834201209Srpaulo
5835201209Srpaulo	/* Mark the queue as inactive. */
5836201209Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid),
5837201209Srpaulo	    IWN5000_TXQ_STATUS_INACTIVE | iwn_tid2fifo[tid]);
5838201209Srpaulo}
5839201209Srpaulo
5840201209Srpaulo/*
5841220674Sbschmidt * Query calibration tables from the initialization firmware.  We do this
5842220674Sbschmidt * only once at first boot.  Called from a process context.
5843212853Sbschmidt */
5844212853Sbschmidtstatic int
5845220674Sbschmidtiwn5000_query_calibration(struct iwn_softc *sc)
5846212853Sbschmidt{
5847198429Srpaulo	struct iwn5000_calib_config cmd;
5848198429Srpaulo	int error;
5849178676Ssam
5850198429Srpaulo	memset(&cmd, 0, sizeof cmd);
5851220674Sbschmidt	cmd.ucode.once.enable = 0xffffffff;
5852220674Sbschmidt	cmd.ucode.once.start  = 0xffffffff;
5853220674Sbschmidt	cmd.ucode.once.send   = 0xffffffff;
5854220674Sbschmidt	cmd.ucode.flags       = 0xffffffff;
5855220674Sbschmidt	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: sending calibration query\n",
5856220674Sbschmidt	    __func__);
5857198429Srpaulo	error = iwn_cmd(sc, IWN5000_CMD_CALIB_CONFIG, &cmd, sizeof cmd, 0);
5858198429Srpaulo	if (error != 0)
5859198429Srpaulo		return error;
5860178676Ssam
5861198429Srpaulo	/* Wait at most two seconds for calibration to complete. */
5862201209Srpaulo	if (!(sc->sc_flags & IWN_FLAG_CALIB_DONE))
5863220674Sbschmidt		error = msleep(sc, &sc->sc_mtx, PCATCH, "iwncal", 2 * hz);
5864201209Srpaulo	return error;
5865198429Srpaulo}
5866198429Srpaulo
5867198429Srpaulo/*
5868220674Sbschmidt * Send calibration results to the runtime firmware.  These results were
5869220674Sbschmidt * obtained on first boot from the initialization firmware.
5870198429Srpaulo */
5871212854Sbschmidtstatic int
5872220674Sbschmidtiwn5000_send_calibration(struct iwn_softc *sc)
5873198429Srpaulo{
5874220674Sbschmidt	int idx, error;
5875198429Srpaulo
5876220674Sbschmidt	for (idx = 0; idx < 5; idx++) {
5877220674Sbschmidt		if (sc->calibcmd[idx].buf == NULL)
5878220674Sbschmidt			continue;	/* No results available. */
5879198429Srpaulo		DPRINTF(sc, IWN_DEBUG_CALIBRATE,
5880220674Sbschmidt		    "send calibration result idx=%d len=%d\n", idx,
5881220674Sbschmidt		    sc->calibcmd[idx].len);
5882220674Sbschmidt		error = iwn_cmd(sc, IWN_CMD_PHY_CALIB, sc->calibcmd[idx].buf,
5883220674Sbschmidt		    sc->calibcmd[idx].len, 0);
5884220674Sbschmidt		if (error != 0) {
5885220674Sbschmidt			device_printf(sc->sc_dev,
5886220674Sbschmidt			    "%s: could not send calibration result, error %d\n",
5887220674Sbschmidt			    __func__, error);
5888220674Sbschmidt			return error;
5889220674Sbschmidt		}
5890178676Ssam	}
5891220674Sbschmidt	return 0;
5892198429Srpaulo}
5893178676Ssam
5894206477Sbschmidtstatic int
5895201209Srpauloiwn5000_send_wimax_coex(struct iwn_softc *sc)
5896201209Srpaulo{
5897201209Srpaulo	struct iwn5000_wimax_coex wimax;
5898201209Srpaulo
5899201209Srpaulo#ifdef notyet
5900201209Srpaulo	if (sc->hw_type == IWN_HW_REV_TYPE_6050) {
5901201209Srpaulo		/* Enable WiMAX coexistence for combo adapters. */
5902201209Srpaulo		wimax.flags =
5903201209Srpaulo		    IWN_WIMAX_COEX_ASSOC_WA_UNMASK |
5904201209Srpaulo		    IWN_WIMAX_COEX_UNASSOC_WA_UNMASK |
5905201209Srpaulo		    IWN_WIMAX_COEX_STA_TABLE_VALID |
5906201209Srpaulo		    IWN_WIMAX_COEX_ENABLE;
5907201209Srpaulo		memcpy(wimax.events, iwn6050_wimax_events,
5908201209Srpaulo		    sizeof iwn6050_wimax_events);
5909201209Srpaulo	} else
5910201209Srpaulo#endif
5911201209Srpaulo	{
5912201209Srpaulo		/* Disable WiMAX coexistence. */
5913201209Srpaulo		wimax.flags = 0;
5914201209Srpaulo		memset(wimax.events, 0, sizeof wimax.events);
5915201209Srpaulo	}
5916201209Srpaulo	DPRINTF(sc, IWN_DEBUG_RESET, "%s: Configuring WiMAX coexistence\n",
5917201209Srpaulo	    __func__);
5918201209Srpaulo	return iwn_cmd(sc, IWN5000_CMD_WIMAX_COEX, &wimax, sizeof wimax, 0);
5919201209Srpaulo}
5920201209Srpaulo
5921220674Sbschmidtstatic int
5922220674Sbschmidtiwn5000_crystal_calib(struct iwn_softc *sc)
5923220674Sbschmidt{
5924220674Sbschmidt	struct iwn5000_phy_calib_crystal cmd;
5925220674Sbschmidt
5926220674Sbschmidt	memset(&cmd, 0, sizeof cmd);
5927220674Sbschmidt	cmd.code = IWN5000_PHY_CALIB_CRYSTAL;
5928220674Sbschmidt	cmd.ngroups = 1;
5929220674Sbschmidt	cmd.isvalid = 1;
5930220674Sbschmidt	cmd.cap_pin[0] = le32toh(sc->eeprom_crystal) & 0xff;
5931220674Sbschmidt	cmd.cap_pin[1] = (le32toh(sc->eeprom_crystal) >> 16) & 0xff;
5932220674Sbschmidt	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "sending crystal calibration %d, %d\n",
5933220674Sbschmidt	    cmd.cap_pin[0], cmd.cap_pin[1]);
5934220674Sbschmidt	return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 0);
5935220674Sbschmidt}
5936220674Sbschmidt
5937220676Sbschmidtstatic int
5938220676Sbschmidtiwn5000_temp_offset_calib(struct iwn_softc *sc)
5939220676Sbschmidt{
5940220676Sbschmidt	struct iwn5000_phy_calib_temp_offset cmd;
5941220676Sbschmidt
5942220676Sbschmidt	memset(&cmd, 0, sizeof cmd);
5943220676Sbschmidt	cmd.code = IWN5000_PHY_CALIB_TEMP_OFFSET;
5944220676Sbschmidt	cmd.ngroups = 1;
5945220676Sbschmidt	cmd.isvalid = 1;
5946220676Sbschmidt	if (sc->eeprom_temp != 0)
5947220676Sbschmidt		cmd.offset = htole16(sc->eeprom_temp);
5948220676Sbschmidt	else
5949220676Sbschmidt		cmd.offset = htole16(IWN_DEFAULT_TEMP_OFFSET);
5950220676Sbschmidt	DPRINTF(sc, IWN_DEBUG_CALIBRATE, "setting radio sensor offset to %d\n",
5951220676Sbschmidt	    le16toh(cmd.offset));
5952220676Sbschmidt	return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 0);
5953220676Sbschmidt}
5954220676Sbschmidt
5955198429Srpaulo/*
5956198429Srpaulo * This function is called after the runtime firmware notifies us of its
5957220725Sbschmidt * readiness (called in a process context).
5958198429Srpaulo */
5959206477Sbschmidtstatic int
5960198429Srpauloiwn4965_post_alive(struct iwn_softc *sc)
5961198429Srpaulo{
5962198429Srpaulo	int error, qid;
5963178676Ssam
5964198429Srpaulo	if ((error = iwn_nic_lock(sc)) != 0)
5965198429Srpaulo		return error;
5966178676Ssam
5967201209Srpaulo	/* Clear TX scheduler state in SRAM. */
5968198429Srpaulo	sc->sched_base = iwn_prph_read(sc, IWN_SCHED_SRAM_ADDR);
5969198429Srpaulo	iwn_mem_set_region_4(sc, sc->sched_base + IWN4965_SCHED_CTX_OFF, 0,
5970201209Srpaulo	    IWN4965_SCHED_CTX_LEN / sizeof (uint32_t));
5971178676Ssam
5972220725Sbschmidt	/* Set physical address of TX scheduler rings (1KB aligned). */
5973198429Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_DRAM_ADDR, sc->sched_dma.paddr >> 10);
5974178676Ssam
5975198429Srpaulo	IWN_SETBITS(sc, IWN_FH_TX_CHICKEN, IWN_FH_TX_CHICKEN_SCHED_RETRY);
5976178676Ssam
5977198429Srpaulo	/* Disable chain mode for all our 16 queues. */
5978198429Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_QCHAIN_SEL, 0);
5979198429Srpaulo
5980198429Srpaulo	for (qid = 0; qid < IWN4965_NTXQUEUES; qid++) {
5981198429Srpaulo		iwn_prph_write(sc, IWN4965_SCHED_QUEUE_RDPTR(qid), 0);
5982198429Srpaulo		IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | 0);
5983198429Srpaulo
5984198429Srpaulo		/* Set scheduler window size. */
5985198429Srpaulo		iwn_mem_write(sc, sc->sched_base +
5986198429Srpaulo		    IWN4965_SCHED_QUEUE_OFFSET(qid), IWN_SCHED_WINSZ);
5987198429Srpaulo		/* Set scheduler frame limit. */
5988198429Srpaulo		iwn_mem_write(sc, sc->sched_base +
5989198429Srpaulo		    IWN4965_SCHED_QUEUE_OFFSET(qid) + 4,
5990198429Srpaulo		    IWN_SCHED_LIMIT << 16);
5991178676Ssam	}
5992178676Ssam
5993198429Srpaulo	/* Enable interrupts for all our 16 queues. */
5994198429Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_INTR_MASK, 0xffff);
5995198429Srpaulo	/* Identify TX FIFO rings (0-7). */
5996198429Srpaulo	iwn_prph_write(sc, IWN4965_SCHED_TXFACT, 0xff);
5997178676Ssam
5998198429Srpaulo	/* Mark TX rings (4 EDCA + cmd + 2 HCCA) as active. */
5999198429Srpaulo	for (qid = 0; qid < 7; qid++) {
6000198429Srpaulo		static uint8_t qid2fifo[] = { 3, 2, 1, 0, 4, 5, 6 };
6001198429Srpaulo		iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid),
6002198429Srpaulo		    IWN4965_TXQ_STATUS_ACTIVE | qid2fifo[qid] << 1);
6003198429Srpaulo	}
6004198429Srpaulo	iwn_nic_unlock(sc);
6005198429Srpaulo	return 0;
6006198429Srpaulo}
6007178676Ssam
6008198429Srpaulo/*
6009198429Srpaulo * This function is called after the initialization or runtime firmware
6010220725Sbschmidt * notifies us of its readiness (called in a process context).
6011198429Srpaulo */
6012206477Sbschmidtstatic int
6013198429Srpauloiwn5000_post_alive(struct iwn_softc *sc)
6014198429Srpaulo{
6015198429Srpaulo	int error, qid;
6016178676Ssam
6017201209Srpaulo	/* Switch to using ICT interrupt mode. */
6018201209Srpaulo	iwn5000_ict_reset(sc);
6019201209Srpaulo
6020220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
6021198429Srpaulo		return error;
6022178676Ssam
6023201209Srpaulo	/* Clear TX scheduler state in SRAM. */
6024198429Srpaulo	sc->sched_base = iwn_prph_read(sc, IWN_SCHED_SRAM_ADDR);
6025198429Srpaulo	iwn_mem_set_region_4(sc, sc->sched_base + IWN5000_SCHED_CTX_OFF, 0,
6026201209Srpaulo	    IWN5000_SCHED_CTX_LEN / sizeof (uint32_t));
6027178676Ssam
6028220725Sbschmidt	/* Set physical address of TX scheduler rings (1KB aligned). */
6029198429Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_DRAM_ADDR, sc->sched_dma.paddr >> 10);
6030178676Ssam
6031198429Srpaulo	IWN_SETBITS(sc, IWN_FH_TX_CHICKEN, IWN_FH_TX_CHICKEN_SCHED_RETRY);
6032178676Ssam
6033201209Srpaulo	/* Enable chain mode for all queues, except command queue. */
6034201209Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_QCHAIN_SEL, 0xfffef);
6035198429Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_AGGR_SEL, 0);
6036178676Ssam
6037198429Srpaulo	for (qid = 0; qid < IWN5000_NTXQUEUES; qid++) {
6038198429Srpaulo		iwn_prph_write(sc, IWN5000_SCHED_QUEUE_RDPTR(qid), 0);
6039198429Srpaulo		IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | 0);
6040198429Srpaulo
6041198429Srpaulo		iwn_mem_write(sc, sc->sched_base +
6042198429Srpaulo		    IWN5000_SCHED_QUEUE_OFFSET(qid), 0);
6043198429Srpaulo		/* Set scheduler window size and frame limit. */
6044198429Srpaulo		iwn_mem_write(sc, sc->sched_base +
6045198429Srpaulo		    IWN5000_SCHED_QUEUE_OFFSET(qid) + 4,
6046198429Srpaulo		    IWN_SCHED_LIMIT << 16 | IWN_SCHED_WINSZ);
6047178676Ssam	}
6048178676Ssam
6049198429Srpaulo	/* Enable interrupts for all our 20 queues. */
6050198429Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_INTR_MASK, 0xfffff);
6051198429Srpaulo	/* Identify TX FIFO rings (0-7). */
6052198429Srpaulo	iwn_prph_write(sc, IWN5000_SCHED_TXFACT, 0xff);
6053178676Ssam
6054198429Srpaulo	/* Mark TX rings (4 EDCA + cmd + 2 HCCA) as active. */
6055198429Srpaulo	for (qid = 0; qid < 7; qid++) {
6056198429Srpaulo		static uint8_t qid2fifo[] = { 3, 2, 1, 0, 7, 5, 6 };
6057198429Srpaulo		iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid),
6058198429Srpaulo		    IWN5000_TXQ_STATUS_ACTIVE | qid2fifo[qid]);
6059198429Srpaulo	}
6060198429Srpaulo	iwn_nic_unlock(sc);
6061178676Ssam
6062201209Srpaulo	/* Configure WiMAX coexistence for combo adapters. */
6063201209Srpaulo	error = iwn5000_send_wimax_coex(sc);
6064178676Ssam	if (error != 0) {
6065178676Ssam		device_printf(sc->sc_dev,
6066198429Srpaulo		    "%s: could not configure WiMAX coexistence, error %d\n",
6067178676Ssam		    __func__, error);
6068178676Ssam		return error;
6069178676Ssam	}
6070220674Sbschmidt	if (sc->hw_type != IWN_HW_REV_TYPE_5150) {
6071220674Sbschmidt		/* Perform crystal calibration. */
6072220674Sbschmidt		error = iwn5000_crystal_calib(sc);
6073198429Srpaulo		if (error != 0) {
6074198429Srpaulo			device_printf(sc->sc_dev,
6075220674Sbschmidt			    "%s: crystal calibration failed, error %d\n",
6076220674Sbschmidt			    __func__, error);
6077198429Srpaulo			return error;
6078198429Srpaulo		}
6079220674Sbschmidt	}
6080220674Sbschmidt	if (!(sc->sc_flags & IWN_FLAG_CALIB_DONE)) {
6081220674Sbschmidt		/* Query calibration from the initialization firmware. */
6082220674Sbschmidt		if ((error = iwn5000_query_calibration(sc)) != 0) {
6083198429Srpaulo			device_printf(sc->sc_dev,
6084220674Sbschmidt			    "%s: could not query calibration, error %d\n",
6085198429Srpaulo			    __func__, error);
6086198429Srpaulo			return error;
6087198429Srpaulo		}
6088198429Srpaulo		/*
6089201209Srpaulo		 * We have the calibration results now, reboot with the
6090201209Srpaulo		 * runtime firmware (call ourselves recursively!)
6091198429Srpaulo		 */
6092198429Srpaulo		iwn_hw_stop(sc);
6093198429Srpaulo		error = iwn_hw_init(sc);
6094198429Srpaulo	} else {
6095220674Sbschmidt		/* Send calibration results to runtime firmware. */
6096220674Sbschmidt		error = iwn5000_send_calibration(sc);
6097198429Srpaulo	}
6098198429Srpaulo	return error;
6099198429Srpaulo}
6100178676Ssam
6101198429Srpaulo/*
6102198429Srpaulo * The firmware boot code is small and is intended to be copied directly into
6103220725Sbschmidt * the NIC internal memory (no DMA transfer).
6104198429Srpaulo */
6105206477Sbschmidtstatic int
6106198429Srpauloiwn4965_load_bootcode(struct iwn_softc *sc, const uint8_t *ucode, int size)
6107198429Srpaulo{
6108198429Srpaulo	int error, ntries;
6109198429Srpaulo
6110198429Srpaulo	size /= sizeof (uint32_t);
6111198429Srpaulo
6112220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
6113198429Srpaulo		return error;
6114198429Srpaulo
6115198429Srpaulo	/* Copy microcode image into NIC memory. */
6116198429Srpaulo	iwn_prph_write_region_4(sc, IWN_BSM_SRAM_BASE,
6117198429Srpaulo	    (const uint32_t *)ucode, size);
6118198429Srpaulo
6119198429Srpaulo	iwn_prph_write(sc, IWN_BSM_WR_MEM_SRC, 0);
6120198429Srpaulo	iwn_prph_write(sc, IWN_BSM_WR_MEM_DST, IWN_FW_TEXT_BASE);
6121198429Srpaulo	iwn_prph_write(sc, IWN_BSM_WR_DWCOUNT, size);
6122198429Srpaulo
6123198429Srpaulo	/* Start boot load now. */
6124198429Srpaulo	iwn_prph_write(sc, IWN_BSM_WR_CTRL, IWN_BSM_WR_CTRL_START);
6125198429Srpaulo
6126198429Srpaulo	/* Wait for transfer to complete. */
6127198429Srpaulo	for (ntries = 0; ntries < 1000; ntries++) {
6128198429Srpaulo		if (!(iwn_prph_read(sc, IWN_BSM_WR_CTRL) &
6129198429Srpaulo		    IWN_BSM_WR_CTRL_START))
6130198429Srpaulo			break;
6131198429Srpaulo		DELAY(10);
6132198429Srpaulo	}
6133198429Srpaulo	if (ntries == 1000) {
6134198429Srpaulo		device_printf(sc->sc_dev, "%s: could not load boot firmware\n",
6135198429Srpaulo		    __func__);
6136198429Srpaulo		iwn_nic_unlock(sc);
6137198429Srpaulo		return ETIMEDOUT;
6138198429Srpaulo	}
6139198429Srpaulo
6140198429Srpaulo	/* Enable boot after power up. */
6141198429Srpaulo	iwn_prph_write(sc, IWN_BSM_WR_CTRL, IWN_BSM_WR_CTRL_START_EN);
6142198429Srpaulo
6143198429Srpaulo	iwn_nic_unlock(sc);
6144198429Srpaulo	return 0;
6145178676Ssam}
6146178676Ssam
6147206477Sbschmidtstatic int
6148198429Srpauloiwn4965_load_firmware(struct iwn_softc *sc)
6149178676Ssam{
6150198429Srpaulo	struct iwn_fw_info *fw = &sc->fw;
6151198429Srpaulo	struct iwn_dma_info *dma = &sc->fw_dma;
6152178676Ssam	int error;
6153178676Ssam
6154198429Srpaulo	/* Copy initialization sections into pre-allocated DMA-safe memory. */
6155198429Srpaulo	memcpy(dma->vaddr, fw->init.data, fw->init.datasz);
6156220661Sbschmidt	bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
6157198429Srpaulo	memcpy(dma->vaddr + IWN4965_FW_DATA_MAXSZ,
6158198429Srpaulo	    fw->init.text, fw->init.textsz);
6159220661Sbschmidt	bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
6160198429Srpaulo
6161198429Srpaulo	/* Tell adapter where to find initialization sections. */
6162220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
6163198429Srpaulo		return error;
6164198429Srpaulo	iwn_prph_write(sc, IWN_BSM_DRAM_DATA_ADDR, dma->paddr >> 4);
6165198429Srpaulo	iwn_prph_write(sc, IWN_BSM_DRAM_DATA_SIZE, fw->init.datasz);
6166198429Srpaulo	iwn_prph_write(sc, IWN_BSM_DRAM_TEXT_ADDR,
6167198429Srpaulo	    (dma->paddr + IWN4965_FW_DATA_MAXSZ) >> 4);
6168198429Srpaulo	iwn_prph_write(sc, IWN_BSM_DRAM_TEXT_SIZE, fw->init.textsz);
6169198429Srpaulo	iwn_nic_unlock(sc);
6170198429Srpaulo
6171198429Srpaulo	/* Load firmware boot code. */
6172198429Srpaulo	error = iwn4965_load_bootcode(sc, fw->boot.text, fw->boot.textsz);
6173178676Ssam	if (error != 0) {
6174198429Srpaulo		device_printf(sc->sc_dev, "%s: could not load boot firmware\n",
6175198429Srpaulo		    __func__);
6176178676Ssam		return error;
6177178676Ssam	}
6178198429Srpaulo	/* Now press "execute". */
6179198429Srpaulo	IWN_WRITE(sc, IWN_RESET, 0);
6180178676Ssam
6181198429Srpaulo	/* Wait at most one second for first alive notification. */
6182220726Sbschmidt	if ((error = msleep(sc, &sc->sc_mtx, PCATCH, "iwninit", hz)) != 0) {
6183178676Ssam		device_printf(sc->sc_dev,
6184198429Srpaulo		    "%s: timeout waiting for adapter to initialize, error %d\n",
6185178676Ssam		    __func__, error);
6186178676Ssam		return error;
6187178676Ssam	}
6188178676Ssam
6189198429Srpaulo	/* Retrieve current temperature for initial TX power calibration. */
6190198429Srpaulo	sc->rawtemp = sc->ucode_info.temp[3].chan20MHz;
6191198429Srpaulo	sc->temp = iwn4965_get_temperature(sc);
6192178676Ssam
6193198429Srpaulo	/* Copy runtime sections into pre-allocated DMA-safe memory. */
6194198429Srpaulo	memcpy(dma->vaddr, fw->main.data, fw->main.datasz);
6195220661Sbschmidt	bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
6196198429Srpaulo	memcpy(dma->vaddr + IWN4965_FW_DATA_MAXSZ,
6197198429Srpaulo	    fw->main.text, fw->main.textsz);
6198220661Sbschmidt	bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
6199198429Srpaulo
6200198429Srpaulo	/* Tell adapter where to find runtime sections. */
6201220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
6202198429Srpaulo		return error;
6203198429Srpaulo	iwn_prph_write(sc, IWN_BSM_DRAM_DATA_ADDR, dma->paddr >> 4);
6204198429Srpaulo	iwn_prph_write(sc, IWN_BSM_DRAM_DATA_SIZE, fw->main.datasz);
6205198429Srpaulo	iwn_prph_write(sc, IWN_BSM_DRAM_TEXT_ADDR,
6206198429Srpaulo	    (dma->paddr + IWN4965_FW_DATA_MAXSZ) >> 4);
6207198429Srpaulo	iwn_prph_write(sc, IWN_BSM_DRAM_TEXT_SIZE,
6208198429Srpaulo	    IWN_FW_UPDATED | fw->main.textsz);
6209198429Srpaulo	iwn_nic_unlock(sc);
6210198429Srpaulo
6211198429Srpaulo	return 0;
6212198429Srpaulo}
6213198429Srpaulo
6214206477Sbschmidtstatic int
6215198429Srpauloiwn5000_load_firmware_section(struct iwn_softc *sc, uint32_t dst,
6216198429Srpaulo    const uint8_t *section, int size)
6217198429Srpaulo{
6218198429Srpaulo	struct iwn_dma_info *dma = &sc->fw_dma;
6219198429Srpaulo	int error;
6220198429Srpaulo
6221198429Srpaulo	/* Copy firmware section into pre-allocated DMA-safe memory. */
6222198429Srpaulo	memcpy(dma->vaddr, section, size);
6223220661Sbschmidt	bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
6224198429Srpaulo
6225220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
6226198429Srpaulo		return error;
6227198429Srpaulo
6228198429Srpaulo	IWN_WRITE(sc, IWN_FH_TX_CONFIG(IWN_SRVC_DMACHNL),
6229198429Srpaulo	    IWN_FH_TX_CONFIG_DMA_PAUSE);
6230198429Srpaulo
6231198429Srpaulo	IWN_WRITE(sc, IWN_FH_SRAM_ADDR(IWN_SRVC_DMACHNL), dst);
6232198429Srpaulo	IWN_WRITE(sc, IWN_FH_TFBD_CTRL0(IWN_SRVC_DMACHNL),
6233198429Srpaulo	    IWN_LOADDR(dma->paddr));
6234198429Srpaulo	IWN_WRITE(sc, IWN_FH_TFBD_CTRL1(IWN_SRVC_DMACHNL),
6235198429Srpaulo	    IWN_HIADDR(dma->paddr) << 28 | size);
6236198429Srpaulo	IWN_WRITE(sc, IWN_FH_TXBUF_STATUS(IWN_SRVC_DMACHNL),
6237198429Srpaulo	    IWN_FH_TXBUF_STATUS_TBNUM(1) |
6238198429Srpaulo	    IWN_FH_TXBUF_STATUS_TBIDX(1) |
6239198429Srpaulo	    IWN_FH_TXBUF_STATUS_TFBD_VALID);
6240198429Srpaulo
6241198429Srpaulo	/* Kick Flow Handler to start DMA transfer. */
6242198429Srpaulo	IWN_WRITE(sc, IWN_FH_TX_CONFIG(IWN_SRVC_DMACHNL),
6243198429Srpaulo	    IWN_FH_TX_CONFIG_DMA_ENA | IWN_FH_TX_CONFIG_CIRQ_HOST_ENDTFD);
6244198429Srpaulo
6245198429Srpaulo	iwn_nic_unlock(sc);
6246198429Srpaulo
6247198429Srpaulo	/* Wait at most five seconds for FH DMA transfer to complete. */
6248220661Sbschmidt	return msleep(sc, &sc->sc_mtx, PCATCH, "iwninit", 5 * hz);
6249198429Srpaulo}
6250198429Srpaulo
6251206477Sbschmidtstatic int
6252198429Srpauloiwn5000_load_firmware(struct iwn_softc *sc)
6253198429Srpaulo{
6254198429Srpaulo	struct iwn_fw_part *fw;
6255198429Srpaulo	int error;
6256198429Srpaulo
6257198429Srpaulo	/* Load the initialization firmware on first boot only. */
6258201209Srpaulo	fw = (sc->sc_flags & IWN_FLAG_CALIB_DONE) ?
6259201209Srpaulo	    &sc->fw.main : &sc->fw.init;
6260198429Srpaulo
6261198429Srpaulo	error = iwn5000_load_firmware_section(sc, IWN_FW_TEXT_BASE,
6262198429Srpaulo	    fw->text, fw->textsz);
6263178676Ssam	if (error != 0) {
6264178676Ssam		device_printf(sc->sc_dev,
6265198429Srpaulo		    "%s: could not load firmware %s section, error %d\n",
6266198429Srpaulo		    __func__, ".text", error);
6267178676Ssam		return error;
6268178676Ssam	}
6269198429Srpaulo	error = iwn5000_load_firmware_section(sc, IWN_FW_DATA_BASE,
6270198429Srpaulo	    fw->data, fw->datasz);
6271178676Ssam	if (error != 0) {
6272178676Ssam		device_printf(sc->sc_dev,
6273198429Srpaulo		    "%s: could not load firmware %s section, error %d\n",
6274198429Srpaulo		    __func__, ".data", error);
6275178676Ssam		return error;
6276178676Ssam	}
6277178676Ssam
6278198429Srpaulo	/* Now press "execute". */
6279198429Srpaulo	IWN_WRITE(sc, IWN_RESET, 0);
6280198429Srpaulo	return 0;
6281198429Srpaulo}
6282198429Srpaulo
6283210111Sbschmidt/*
6284210111Sbschmidt * Extract text and data sections from a legacy firmware image.
6285210111Sbschmidt */
6286206477Sbschmidtstatic int
6287210111Sbschmidtiwn_read_firmware_leg(struct iwn_softc *sc, struct iwn_fw_info *fw)
6288198429Srpaulo{
6289201209Srpaulo	const uint32_t *ptr;
6290210111Sbschmidt	size_t hdrlen = 24;
6291201209Srpaulo	uint32_t rev;
6292198429Srpaulo
6293220661Sbschmidt	ptr = (const uint32_t *)fw->data;
6294201209Srpaulo	rev = le32toh(*ptr++);
6295210111Sbschmidt
6296201209Srpaulo	/* Check firmware API version. */
6297201209Srpaulo	if (IWN_FW_API(rev) <= 1) {
6298201209Srpaulo		device_printf(sc->sc_dev,
6299201209Srpaulo		    "%s: bad firmware, need API version >=2\n", __func__);
6300201209Srpaulo		return EINVAL;
6301201209Srpaulo	}
6302201209Srpaulo	if (IWN_FW_API(rev) >= 3) {
6303201209Srpaulo		/* Skip build number (version 2 header). */
6304210111Sbschmidt		hdrlen += 4;
6305201209Srpaulo		ptr++;
6306201209Srpaulo	}
6307210111Sbschmidt	if (fw->size < hdrlen) {
6308220724Sbschmidt		device_printf(sc->sc_dev, "%s: firmware too short: %zu bytes\n",
6309210111Sbschmidt		    __func__, fw->size);
6310210111Sbschmidt		return EINVAL;
6311210111Sbschmidt	}
6312201209Srpaulo	fw->main.textsz = le32toh(*ptr++);
6313201209Srpaulo	fw->main.datasz = le32toh(*ptr++);
6314201209Srpaulo	fw->init.textsz = le32toh(*ptr++);
6315201209Srpaulo	fw->init.datasz = le32toh(*ptr++);
6316201209Srpaulo	fw->boot.textsz = le32toh(*ptr++);
6317198429Srpaulo
6318198429Srpaulo	/* Check that all firmware sections fit. */
6319210111Sbschmidt	if (fw->size < hdrlen + fw->main.textsz + fw->main.datasz +
6320210111Sbschmidt	    fw->init.textsz + fw->init.datasz + fw->boot.textsz) {
6321220724Sbschmidt		device_printf(sc->sc_dev, "%s: firmware too short: %zu bytes\n",
6322210111Sbschmidt		    __func__, fw->size);
6323198429Srpaulo		return EINVAL;
6324178676Ssam	}
6325198429Srpaulo
6326198429Srpaulo	/* Get pointers to firmware sections. */
6327201209Srpaulo	fw->main.text = (const uint8_t *)ptr;
6328198429Srpaulo	fw->main.data = fw->main.text + fw->main.textsz;
6329198429Srpaulo	fw->init.text = fw->main.data + fw->main.datasz;
6330198429Srpaulo	fw->init.data = fw->init.text + fw->init.textsz;
6331198429Srpaulo	fw->boot.text = fw->init.data + fw->init.datasz;
6332178676Ssam	return 0;
6333178676Ssam}
6334178676Ssam
6335210111Sbschmidt/*
6336210111Sbschmidt * Extract text and data sections from a TLV firmware image.
6337210111Sbschmidt */
6338220661Sbschmidtstatic int
6339210111Sbschmidtiwn_read_firmware_tlv(struct iwn_softc *sc, struct iwn_fw_info *fw,
6340210111Sbschmidt    uint16_t alt)
6341210111Sbschmidt{
6342210111Sbschmidt	const struct iwn_fw_tlv_hdr *hdr;
6343210111Sbschmidt	const struct iwn_fw_tlv *tlv;
6344210111Sbschmidt	const uint8_t *ptr, *end;
6345210111Sbschmidt	uint64_t altmask;
6346220866Sbschmidt	uint32_t len, tmp;
6347210111Sbschmidt
6348210111Sbschmidt	if (fw->size < sizeof (*hdr)) {
6349220724Sbschmidt		device_printf(sc->sc_dev, "%s: firmware too short: %zu bytes\n",
6350210111Sbschmidt		    __func__, fw->size);
6351210111Sbschmidt		return EINVAL;
6352210111Sbschmidt	}
6353210111Sbschmidt	hdr = (const struct iwn_fw_tlv_hdr *)fw->data;
6354210111Sbschmidt	if (hdr->signature != htole32(IWN_FW_SIGNATURE)) {
6355220724Sbschmidt		device_printf(sc->sc_dev, "%s: bad firmware signature 0x%08x\n",
6356210111Sbschmidt		    __func__, le32toh(hdr->signature));
6357210111Sbschmidt		return EINVAL;
6358210111Sbschmidt	}
6359220724Sbschmidt	DPRINTF(sc, IWN_DEBUG_RESET, "FW: \"%.64s\", build 0x%x\n", hdr->descr,
6360220724Sbschmidt	    le32toh(hdr->build));
6361210111Sbschmidt
6362210111Sbschmidt	/*
6363210111Sbschmidt	 * Select the closest supported alternative that is less than
6364210111Sbschmidt	 * or equal to the specified one.
6365210111Sbschmidt	 */
6366210111Sbschmidt	altmask = le64toh(hdr->altmask);
6367210111Sbschmidt	while (alt > 0 && !(altmask & (1ULL << alt)))
6368210111Sbschmidt		alt--;	/* Downgrade. */
6369220724Sbschmidt	DPRINTF(sc, IWN_DEBUG_RESET, "using alternative %d\n", alt);
6370210111Sbschmidt
6371210111Sbschmidt	ptr = (const uint8_t *)(hdr + 1);
6372210111Sbschmidt	end = (const uint8_t *)(fw->data + fw->size);
6373210111Sbschmidt
6374210111Sbschmidt	/* Parse type-length-value fields. */
6375210111Sbschmidt	while (ptr + sizeof (*tlv) <= end) {
6376210111Sbschmidt		tlv = (const struct iwn_fw_tlv *)ptr;
6377210111Sbschmidt		len = le32toh(tlv->len);
6378210111Sbschmidt
6379210111Sbschmidt		ptr += sizeof (*tlv);
6380210111Sbschmidt		if (ptr + len > end) {
6381210111Sbschmidt			device_printf(sc->sc_dev,
6382220724Sbschmidt			    "%s: firmware too short: %zu bytes\n", __func__,
6383220724Sbschmidt			    fw->size);
6384210111Sbschmidt			return EINVAL;
6385210111Sbschmidt		}
6386210111Sbschmidt		/* Skip other alternatives. */
6387210111Sbschmidt		if (tlv->alt != 0 && tlv->alt != htole16(alt))
6388210111Sbschmidt			goto next;
6389210111Sbschmidt
6390210111Sbschmidt		switch (le16toh(tlv->type)) {
6391210111Sbschmidt		case IWN_FW_TLV_MAIN_TEXT:
6392210111Sbschmidt			fw->main.text = ptr;
6393210111Sbschmidt			fw->main.textsz = len;
6394210111Sbschmidt			break;
6395210111Sbschmidt		case IWN_FW_TLV_MAIN_DATA:
6396210111Sbschmidt			fw->main.data = ptr;
6397210111Sbschmidt			fw->main.datasz = len;
6398210111Sbschmidt			break;
6399210111Sbschmidt		case IWN_FW_TLV_INIT_TEXT:
6400210111Sbschmidt			fw->init.text = ptr;
6401210111Sbschmidt			fw->init.textsz = len;
6402210111Sbschmidt			break;
6403210111Sbschmidt		case IWN_FW_TLV_INIT_DATA:
6404210111Sbschmidt			fw->init.data = ptr;
6405210111Sbschmidt			fw->init.datasz = len;
6406210111Sbschmidt			break;
6407210111Sbschmidt		case IWN_FW_TLV_BOOT_TEXT:
6408210111Sbschmidt			fw->boot.text = ptr;
6409210111Sbschmidt			fw->boot.textsz = len;
6410210111Sbschmidt			break;
6411220866Sbschmidt		case IWN_FW_TLV_ENH_SENS:
6412220866Sbschmidt			if (!len)
6413220866Sbschmidt				sc->sc_flags |= IWN_FLAG_ENH_SENS;
6414220866Sbschmidt			break;
6415220866Sbschmidt		case IWN_FW_TLV_PHY_CALIB:
6416220866Sbschmidt			tmp = htole32(*ptr);
6417220866Sbschmidt			if (tmp < 253) {
6418220866Sbschmidt				sc->reset_noise_gain = tmp;
6419220866Sbschmidt				sc->noise_gain = tmp + 1;
6420220866Sbschmidt			}
6421220866Sbschmidt			break;
6422210111Sbschmidt		default:
6423210111Sbschmidt			DPRINTF(sc, IWN_DEBUG_RESET,
6424220724Sbschmidt			    "TLV type %d not handled\n", le16toh(tlv->type));
6425210111Sbschmidt			break;
6426210111Sbschmidt		}
6427220726Sbschmidt next:		/* TLV fields are 32-bit aligned. */
6428210111Sbschmidt		ptr += (len + 3) & ~3;
6429210111Sbschmidt	}
6430210111Sbschmidt	return 0;
6431210111Sbschmidt}
6432210111Sbschmidt
6433206477Sbschmidtstatic int
6434210111Sbschmidtiwn_read_firmware(struct iwn_softc *sc)
6435210111Sbschmidt{
6436210111Sbschmidt	struct iwn_fw_info *fw = &sc->fw;
6437210111Sbschmidt	int error;
6438210111Sbschmidt
6439210111Sbschmidt	IWN_UNLOCK(sc);
6440210111Sbschmidt
6441210111Sbschmidt	memset(fw, 0, sizeof (*fw));
6442210111Sbschmidt
6443210111Sbschmidt	/* Read firmware image from filesystem. */
6444210111Sbschmidt	sc->fw_fp = firmware_get(sc->fwname);
6445210111Sbschmidt	if (sc->fw_fp == NULL) {
6446220724Sbschmidt		device_printf(sc->sc_dev, "%s: could not read firmware %s\n",
6447220724Sbschmidt		    __func__, sc->fwname);
6448210111Sbschmidt		IWN_LOCK(sc);
6449210111Sbschmidt		return EINVAL;
6450210111Sbschmidt	}
6451210111Sbschmidt	IWN_LOCK(sc);
6452210111Sbschmidt
6453210111Sbschmidt	fw->size = sc->fw_fp->datasize;
6454210111Sbschmidt	fw->data = (const uint8_t *)sc->fw_fp->data;
6455210111Sbschmidt	if (fw->size < sizeof (uint32_t)) {
6456220724Sbschmidt		device_printf(sc->sc_dev, "%s: firmware too short: %zu bytes\n",
6457210111Sbschmidt		    __func__, fw->size);
6458220661Sbschmidt		firmware_put(sc->fw_fp, FIRMWARE_UNLOAD);
6459220661Sbschmidt		sc->fw_fp = NULL;
6460210111Sbschmidt		return EINVAL;
6461210111Sbschmidt	}
6462210111Sbschmidt
6463210111Sbschmidt	/* Retrieve text and data sections. */
6464210111Sbschmidt	if (*(const uint32_t *)fw->data != 0)	/* Legacy image. */
6465210111Sbschmidt		error = iwn_read_firmware_leg(sc, fw);
6466210111Sbschmidt	else
6467210111Sbschmidt		error = iwn_read_firmware_tlv(sc, fw, 1);
6468210111Sbschmidt	if (error != 0) {
6469210111Sbschmidt		device_printf(sc->sc_dev,
6470220724Sbschmidt		    "%s: could not read firmware sections, error %d\n",
6471220724Sbschmidt		    __func__, error);
6472220661Sbschmidt		firmware_put(sc->fw_fp, FIRMWARE_UNLOAD);
6473220661Sbschmidt		sc->fw_fp = NULL;
6474210111Sbschmidt		return error;
6475210111Sbschmidt	}
6476210111Sbschmidt
6477210111Sbschmidt	/* Make sure text and data sections fit in hardware memory. */
6478220728Sbschmidt	if (fw->main.textsz > sc->fw_text_maxsz ||
6479220728Sbschmidt	    fw->main.datasz > sc->fw_data_maxsz ||
6480220728Sbschmidt	    fw->init.textsz > sc->fw_text_maxsz ||
6481220728Sbschmidt	    fw->init.datasz > sc->fw_data_maxsz ||
6482210111Sbschmidt	    fw->boot.textsz > IWN_FW_BOOT_TEXT_MAXSZ ||
6483210111Sbschmidt	    (fw->boot.textsz & 3) != 0) {
6484220724Sbschmidt		device_printf(sc->sc_dev, "%s: firmware sections too large\n",
6485220724Sbschmidt		    __func__);
6486220661Sbschmidt		firmware_put(sc->fw_fp, FIRMWARE_UNLOAD);
6487220661Sbschmidt		sc->fw_fp = NULL;
6488210111Sbschmidt		return EINVAL;
6489210111Sbschmidt	}
6490210111Sbschmidt
6491210111Sbschmidt	/* We can proceed with loading the firmware. */
6492210111Sbschmidt	return 0;
6493210111Sbschmidt}
6494210111Sbschmidt
6495210111Sbschmidtstatic int
6496198429Srpauloiwn_clock_wait(struct iwn_softc *sc)
6497198429Srpaulo{
6498198429Srpaulo	int ntries;
6499178676Ssam
6500198429Srpaulo	/* Set "initialization complete" bit. */
6501198429Srpaulo	IWN_SETBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_INIT_DONE);
6502198429Srpaulo
6503198429Srpaulo	/* Wait for clock stabilization. */
6504201209Srpaulo	for (ntries = 0; ntries < 2500; ntries++) {
6505198429Srpaulo		if (IWN_READ(sc, IWN_GP_CNTRL) & IWN_GP_CNTRL_MAC_CLOCK_READY)
6506198429Srpaulo			return 0;
6507201209Srpaulo		DELAY(10);
6508178676Ssam	}
6509198429Srpaulo	device_printf(sc->sc_dev,
6510198429Srpaulo	    "%s: timeout waiting for clock stabilization\n", __func__);
6511198429Srpaulo	return ETIMEDOUT;
6512198429Srpaulo}
6513178676Ssam
6514206477Sbschmidtstatic int
6515201209Srpauloiwn_apm_init(struct iwn_softc *sc)
6516198429Srpaulo{
6517220721Sbschmidt	uint32_t reg;
6518198429Srpaulo	int error;
6519178676Ssam
6520220725Sbschmidt	/* Disable L0s exit timer (NMI bug workaround). */
6521198429Srpaulo	IWN_SETBITS(sc, IWN_GIO_CHICKEN, IWN_GIO_CHICKEN_DIS_L0S_TIMER);
6522220725Sbschmidt	/* Don't wait for ICH L0s (ICH bug workaround). */
6523198429Srpaulo	IWN_SETBITS(sc, IWN_GIO_CHICKEN, IWN_GIO_CHICKEN_L1A_NO_L0S_RX);
6524178676Ssam
6525220725Sbschmidt	/* Set FH wait threshold to max (HW bug under stress workaround). */
6526198429Srpaulo	IWN_SETBITS(sc, IWN_DBG_HPET_MEM, 0xffff0000);
6527198429Srpaulo
6528201209Srpaulo	/* Enable HAP INTA to move adapter from L1a to L0s. */
6529198429Srpaulo	IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_HAP_WAKE_L1A);
6530198429Srpaulo
6531201209Srpaulo	/* Retrieve PCIe Active State Power Management (ASPM). */
6532220721Sbschmidt	reg = pci_read_config(sc->sc_dev, sc->sc_cap_off + 0x10, 1);
6533201209Srpaulo	/* Workaround for HW instability in PCIe L0->L0s->L1 transition. */
6534220721Sbschmidt	if (reg & 0x02)	/* L1 Entry enabled. */
6535201209Srpaulo		IWN_SETBITS(sc, IWN_GIO, IWN_GIO_L0S_ENA);
6536201209Srpaulo	else
6537201209Srpaulo		IWN_CLRBITS(sc, IWN_GIO, IWN_GIO_L0S_ENA);
6538201209Srpaulo
6539201209Srpaulo	if (sc->hw_type != IWN_HW_REV_TYPE_4965 &&
6540210109Sbschmidt	    sc->hw_type <= IWN_HW_REV_TYPE_1000)
6541198429Srpaulo		IWN_SETBITS(sc, IWN_ANA_PLL, IWN_ANA_PLL_INIT);
6542198429Srpaulo
6543201209Srpaulo	/* Wait for clock stabilization before accessing prph. */
6544220726Sbschmidt	if ((error = iwn_clock_wait(sc)) != 0)
6545198429Srpaulo		return error;
6546198429Srpaulo
6547220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
6548198429Srpaulo		return error;
6549201209Srpaulo	if (sc->hw_type == IWN_HW_REV_TYPE_4965) {
6550220725Sbschmidt		/* Enable DMA and BSM (Bootstrap State Machine). */
6551201209Srpaulo		iwn_prph_write(sc, IWN_APMG_CLK_EN,
6552201209Srpaulo		    IWN_APMG_CLK_CTRL_DMA_CLK_RQT |
6553201209Srpaulo		    IWN_APMG_CLK_CTRL_BSM_CLK_RQT);
6554201209Srpaulo	} else {
6555201209Srpaulo		/* Enable DMA. */
6556201209Srpaulo		iwn_prph_write(sc, IWN_APMG_CLK_EN,
6557201209Srpaulo		    IWN_APMG_CLK_CTRL_DMA_CLK_RQT);
6558201209Srpaulo	}
6559198429Srpaulo	DELAY(20);
6560201209Srpaulo	/* Disable L1-Active. */
6561198429Srpaulo	iwn_prph_setbits(sc, IWN_APMG_PCI_STT, IWN_APMG_PCI_STT_L1A_DIS);
6562198429Srpaulo	iwn_nic_unlock(sc);
6563198429Srpaulo
6564198429Srpaulo	return 0;
6565198429Srpaulo}
6566198429Srpaulo
6567206477Sbschmidtstatic void
6568198429Srpauloiwn_apm_stop_master(struct iwn_softc *sc)
6569178676Ssam{
6570178676Ssam	int ntries;
6571178676Ssam
6572201209Srpaulo	/* Stop busmaster DMA activity. */
6573198429Srpaulo	IWN_SETBITS(sc, IWN_RESET, IWN_RESET_STOP_MASTER);
6574178676Ssam	for (ntries = 0; ntries < 100; ntries++) {
6575198429Srpaulo		if (IWN_READ(sc, IWN_RESET) & IWN_RESET_MASTER_DISABLED)
6576198429Srpaulo			return;
6577178676Ssam		DELAY(10);
6578178676Ssam	}
6579220726Sbschmidt	device_printf(sc->sc_dev, "%s: timeout waiting for master\n", __func__);
6580178676Ssam}
6581178676Ssam
6582206477Sbschmidtstatic void
6583198429Srpauloiwn_apm_stop(struct iwn_softc *sc)
6584198429Srpaulo{
6585198429Srpaulo	iwn_apm_stop_master(sc);
6586198429Srpaulo
6587201209Srpaulo	/* Reset the entire device. */
6588198429Srpaulo	IWN_SETBITS(sc, IWN_RESET, IWN_RESET_SW);
6589198429Srpaulo	DELAY(10);
6590198429Srpaulo	/* Clear "initialization complete" bit. */
6591198429Srpaulo	IWN_CLRBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_INIT_DONE);
6592198429Srpaulo}
6593198429Srpaulo
6594206477Sbschmidtstatic int
6595198429Srpauloiwn4965_nic_config(struct iwn_softc *sc)
6596178676Ssam{
6597198429Srpaulo	if (IWN_RFCFG_TYPE(sc->rfcfg) == 1) {
6598198429Srpaulo		/*
6599198429Srpaulo		 * I don't believe this to be correct but this is what the
6600198429Srpaulo		 * vendor driver is doing. Probably the bits should not be
6601198429Srpaulo		 * shifted in IWN_RFCFG_*.
6602198429Srpaulo		 */
6603198429Srpaulo		IWN_SETBITS(sc, IWN_HW_IF_CONFIG,
6604198429Srpaulo		    IWN_RFCFG_TYPE(sc->rfcfg) |
6605198429Srpaulo		    IWN_RFCFG_STEP(sc->rfcfg) |
6606198429Srpaulo		    IWN_RFCFG_DASH(sc->rfcfg));
6607198429Srpaulo	}
6608198429Srpaulo	IWN_SETBITS(sc, IWN_HW_IF_CONFIG,
6609198429Srpaulo	    IWN_HW_IF_CONFIG_RADIO_SI | IWN_HW_IF_CONFIG_MAC_SI);
6610198429Srpaulo	return 0;
6611198429Srpaulo}
6612178676Ssam
6613206477Sbschmidtstatic int
6614198429Srpauloiwn5000_nic_config(struct iwn_softc *sc)
6615198429Srpaulo{
6616198429Srpaulo	uint32_t tmp;
6617198429Srpaulo	int error;
6618178676Ssam
6619198429Srpaulo	if (IWN_RFCFG_TYPE(sc->rfcfg) < 3) {
6620198429Srpaulo		IWN_SETBITS(sc, IWN_HW_IF_CONFIG,
6621198429Srpaulo		    IWN_RFCFG_TYPE(sc->rfcfg) |
6622198429Srpaulo		    IWN_RFCFG_STEP(sc->rfcfg) |
6623198429Srpaulo		    IWN_RFCFG_DASH(sc->rfcfg));
6624198429Srpaulo	}
6625198429Srpaulo	IWN_SETBITS(sc, IWN_HW_IF_CONFIG,
6626198429Srpaulo	    IWN_HW_IF_CONFIG_RADIO_SI | IWN_HW_IF_CONFIG_MAC_SI);
6627198429Srpaulo
6628220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
6629198429Srpaulo		return error;
6630198429Srpaulo	iwn_prph_setbits(sc, IWN_APMG_PS, IWN_APMG_PS_EARLY_PWROFF_DIS);
6631201209Srpaulo
6632201209Srpaulo	if (sc->hw_type == IWN_HW_REV_TYPE_1000) {
6633201209Srpaulo		/*
6634201209Srpaulo		 * Select first Switching Voltage Regulator (1.32V) to
6635201209Srpaulo		 * solve a stability issue related to noisy DC2DC line
6636201209Srpaulo		 * in the silicon of 1000 Series.
6637201209Srpaulo		 */
6638201209Srpaulo		tmp = iwn_prph_read(sc, IWN_APMG_DIGITAL_SVR);
6639201209Srpaulo		tmp &= ~IWN_APMG_DIGITAL_SVR_VOLTAGE_MASK;
6640201209Srpaulo		tmp |= IWN_APMG_DIGITAL_SVR_VOLTAGE_1_32;
6641201209Srpaulo		iwn_prph_write(sc, IWN_APMG_DIGITAL_SVR, tmp);
6642201209Srpaulo	}
6643198429Srpaulo	iwn_nic_unlock(sc);
6644201209Srpaulo
6645201209Srpaulo	if (sc->sc_flags & IWN_FLAG_INTERNAL_PA) {
6646201209Srpaulo		/* Use internal power amplifier only. */
6647201209Srpaulo		IWN_WRITE(sc, IWN_GP_DRIVER, IWN_GP_DRIVER_RADIO_2X2_IPA);
6648201209Srpaulo	}
6649220676Sbschmidt	if ((sc->hw_type == IWN_HW_REV_TYPE_6050 ||
6650220676Sbschmidt	     sc->hw_type == IWN_HW_REV_TYPE_6005) && sc->calib_ver >= 6) {
6651210108Sbschmidt		/* Indicate that ROM calibration version is >=6. */
6652210108Sbschmidt		IWN_SETBITS(sc, IWN_GP_DRIVER, IWN_GP_DRIVER_CALIB_VER6);
6653206444Sbschmidt	}
6654220729Sbschmidt	if (sc->hw_type == IWN_HW_REV_TYPE_6005)
6655220729Sbschmidt		IWN_SETBITS(sc, IWN_GP_DRIVER, IWN_GP_DRIVER_6050_1X2);
6656198429Srpaulo	return 0;
6657198429Srpaulo}
6658198429Srpaulo
6659198429Srpaulo/*
6660198429Srpaulo * Take NIC ownership over Intel Active Management Technology (AMT).
6661198429Srpaulo */
6662206477Sbschmidtstatic int
6663198429Srpauloiwn_hw_prepare(struct iwn_softc *sc)
6664198429Srpaulo{
6665198429Srpaulo	int ntries;
6666198429Srpaulo
6667201209Srpaulo	/* Check if hardware is ready. */
6668201209Srpaulo	IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_NIC_READY);
6669201209Srpaulo	for (ntries = 0; ntries < 5; ntries++) {
6670201209Srpaulo		if (IWN_READ(sc, IWN_HW_IF_CONFIG) &
6671201209Srpaulo		    IWN_HW_IF_CONFIG_NIC_READY)
6672201209Srpaulo			return 0;
6673201209Srpaulo		DELAY(10);
6674201209Srpaulo	}
6675201209Srpaulo
6676201209Srpaulo	/* Hardware not ready, force into ready state. */
6677198429Srpaulo	IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_PREPARE);
6678198429Srpaulo	for (ntries = 0; ntries < 15000; ntries++) {
6679198429Srpaulo		if (!(IWN_READ(sc, IWN_HW_IF_CONFIG) &
6680198429Srpaulo		    IWN_HW_IF_CONFIG_PREPARE_DONE))
6681178676Ssam			break;
6682178676Ssam		DELAY(10);
6683178676Ssam	}
6684198429Srpaulo	if (ntries == 15000)
6685178676Ssam		return ETIMEDOUT;
6686198429Srpaulo
6687201209Srpaulo	/* Hardware should be ready now. */
6688198429Srpaulo	IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_NIC_READY);
6689198429Srpaulo	for (ntries = 0; ntries < 5; ntries++) {
6690198429Srpaulo		if (IWN_READ(sc, IWN_HW_IF_CONFIG) &
6691198429Srpaulo		    IWN_HW_IF_CONFIG_NIC_READY)
6692198429Srpaulo			return 0;
6693198429Srpaulo		DELAY(10);
6694178676Ssam	}
6695198429Srpaulo	return ETIMEDOUT;
6696178676Ssam}
6697178676Ssam
6698206477Sbschmidtstatic int
6699198429Srpauloiwn_hw_init(struct iwn_softc *sc)
6700178676Ssam{
6701220728Sbschmidt	struct iwn_ops *ops = &sc->ops;
6702198429Srpaulo	int error, chnl, qid;
6703178676Ssam
6704198429Srpaulo	/* Clear pending interrupts. */
6705198429Srpaulo	IWN_WRITE(sc, IWN_INT, 0xffffffff);
6706178676Ssam
6707220726Sbschmidt	if ((error = iwn_apm_init(sc)) != 0) {
6708198429Srpaulo		device_printf(sc->sc_dev,
6709220726Sbschmidt		    "%s: could not power ON adapter, error %d\n", __func__,
6710220726Sbschmidt		    error);
6711198429Srpaulo		return error;
6712178676Ssam	}
6713178676Ssam
6714198429Srpaulo	/* Select VMAIN power source. */
6715220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
6716198429Srpaulo		return error;
6717198429Srpaulo	iwn_prph_clrbits(sc, IWN_APMG_PS, IWN_APMG_PS_PWR_SRC_MASK);
6718198429Srpaulo	iwn_nic_unlock(sc);
6719178676Ssam
6720198429Srpaulo	/* Perform adapter-specific initialization. */
6721220728Sbschmidt	if ((error = ops->nic_config(sc)) != 0)
6722198429Srpaulo		return error;
6723178676Ssam
6724198429Srpaulo	/* Initialize RX ring. */
6725220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
6726198429Srpaulo		return error;
6727198429Srpaulo	IWN_WRITE(sc, IWN_FH_RX_CONFIG, 0);
6728198429Srpaulo	IWN_WRITE(sc, IWN_FH_RX_WPTR, 0);
6729220725Sbschmidt	/* Set physical address of RX ring (256-byte aligned). */
6730198429Srpaulo	IWN_WRITE(sc, IWN_FH_RX_BASE, sc->rxq.desc_dma.paddr >> 8);
6731220725Sbschmidt	/* Set physical address of RX status (16-byte aligned). */
6732198429Srpaulo	IWN_WRITE(sc, IWN_FH_STATUS_WPTR, sc->rxq.stat_dma.paddr >> 4);
6733198429Srpaulo	/* Enable RX. */
6734198429Srpaulo	IWN_WRITE(sc, IWN_FH_RX_CONFIG,
6735198429Srpaulo	    IWN_FH_RX_CONFIG_ENA           |
6736198429Srpaulo	    IWN_FH_RX_CONFIG_IGN_RXF_EMPTY |	/* HW bug workaround */
6737198429Srpaulo	    IWN_FH_RX_CONFIG_IRQ_DST_HOST  |
6738198429Srpaulo	    IWN_FH_RX_CONFIG_SINGLE_FRAME  |
6739198429Srpaulo	    IWN_FH_RX_CONFIG_RB_TIMEOUT(0) |
6740198429Srpaulo	    IWN_FH_RX_CONFIG_NRBD(IWN_RX_RING_COUNT_LOG));
6741198429Srpaulo	iwn_nic_unlock(sc);
6742198429Srpaulo	IWN_WRITE(sc, IWN_FH_RX_WPTR, (IWN_RX_RING_COUNT - 1) & ~7);
6743178676Ssam
6744220726Sbschmidt	if ((error = iwn_nic_lock(sc)) != 0)
6745198429Srpaulo		return error;
6746178676Ssam
6747198429Srpaulo	/* Initialize TX scheduler. */
6748220728Sbschmidt	iwn_prph_write(sc, sc->sched_txfact_addr, 0);
6749178676Ssam
6750220725Sbschmidt	/* Set physical address of "keep warm" page (16-byte aligned). */
6751198429Srpaulo	IWN_WRITE(sc, IWN_FH_KW_ADDR, sc->kw_dma.paddr >> 4);
6752198429Srpaulo
6753198429Srpaulo	/* Initialize TX rings. */
6754220728Sbschmidt	for (qid = 0; qid < sc->ntxqs; qid++) {
6755198429Srpaulo		struct iwn_tx_ring *txq = &sc->txq[qid];
6756198429Srpaulo
6757220725Sbschmidt		/* Set physical address of TX ring (256-byte aligned). */
6758198429Srpaulo		IWN_WRITE(sc, IWN_FH_CBBC_QUEUE(qid),
6759198429Srpaulo		    txq->desc_dma.paddr >> 8);
6760178676Ssam	}
6761198429Srpaulo	iwn_nic_unlock(sc);
6762178676Ssam
6763198429Srpaulo	/* Enable DMA channels. */
6764220728Sbschmidt	for (chnl = 0; chnl < sc->ndmachnls; chnl++) {
6765198429Srpaulo		IWN_WRITE(sc, IWN_FH_TX_CONFIG(chnl),
6766198429Srpaulo		    IWN_FH_TX_CONFIG_DMA_ENA |
6767198429Srpaulo		    IWN_FH_TX_CONFIG_DMA_CREDIT_ENA);
6768198429Srpaulo	}
6769198429Srpaulo
6770198429Srpaulo	/* Clear "radio off" and "commands blocked" bits. */
6771198429Srpaulo	IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_RFKILL);
6772198429Srpaulo	IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_CMD_BLOCKED);
6773198429Srpaulo
6774198429Srpaulo	/* Clear pending interrupts. */
6775198429Srpaulo	IWN_WRITE(sc, IWN_INT, 0xffffffff);
6776198429Srpaulo	/* Enable interrupt coalescing. */
6777198429Srpaulo	IWN_WRITE(sc, IWN_INT_COALESCING, 512 / 8);
6778198429Srpaulo	/* Enable interrupts. */
6779201209Srpaulo	IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask);
6780198429Srpaulo
6781198429Srpaulo	/* _Really_ make sure "radio off" bit is cleared! */
6782198429Srpaulo	IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_RFKILL);
6783198429Srpaulo	IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_RFKILL);
6784198429Srpaulo
6785220729Sbschmidt	/* Enable shadow registers. */
6786220729Sbschmidt	if (sc->hw_type >= IWN_HW_REV_TYPE_6000)
6787220729Sbschmidt		IWN_SETBITS(sc, IWN_SHADOW_REG_CTRL, 0x800fffff);
6788220729Sbschmidt
6789220728Sbschmidt	if ((error = ops->load_firmware(sc)) != 0) {
6790178676Ssam		device_printf(sc->sc_dev,
6791220726Sbschmidt		    "%s: could not load firmware, error %d\n", __func__,
6792220726Sbschmidt		    error);
6793198429Srpaulo		return error;
6794178676Ssam	}
6795198429Srpaulo	/* Wait at most one second for firmware alive notification. */
6796220726Sbschmidt	if ((error = msleep(sc, &sc->sc_mtx, PCATCH, "iwninit", hz)) != 0) {
6797198429Srpaulo		device_printf(sc->sc_dev,
6798198429Srpaulo		    "%s: timeout waiting for adapter to initialize, error %d\n",
6799198429Srpaulo		    __func__, error);
6800198429Srpaulo		return error;
6801198429Srpaulo	}
6802198429Srpaulo	/* Do post-firmware initialization. */
6803220728Sbschmidt	return ops->post_alive(sc);
6804198429Srpaulo}
6805178676Ssam
6806206477Sbschmidtstatic void
6807198429Srpauloiwn_hw_stop(struct iwn_softc *sc)
6808198429Srpaulo{
6809198429Srpaulo	int chnl, qid, ntries;
6810178676Ssam
6811198429Srpaulo	IWN_WRITE(sc, IWN_RESET, IWN_RESET_NEVO);
6812178676Ssam
6813198429Srpaulo	/* Disable interrupts. */
6814201209Srpaulo	IWN_WRITE(sc, IWN_INT_MASK, 0);
6815198429Srpaulo	IWN_WRITE(sc, IWN_INT, 0xffffffff);
6816198429Srpaulo	IWN_WRITE(sc, IWN_FH_INT, 0xffffffff);
6817201209Srpaulo	sc->sc_flags &= ~IWN_FLAG_USE_ICT;
6818178676Ssam
6819198429Srpaulo	/* Make sure we no longer hold the NIC lock. */
6820198429Srpaulo	iwn_nic_unlock(sc);
6821178676Ssam
6822198429Srpaulo	/* Stop TX scheduler. */
6823220728Sbschmidt	iwn_prph_write(sc, sc->sched_txfact_addr, 0);
6824178676Ssam
6825198429Srpaulo	/* Stop all DMA channels. */
6826198429Srpaulo	if (iwn_nic_lock(sc) == 0) {
6827220728Sbschmidt		for (chnl = 0; chnl < sc->ndmachnls; chnl++) {
6828198429Srpaulo			IWN_WRITE(sc, IWN_FH_TX_CONFIG(chnl), 0);
6829198429Srpaulo			for (ntries = 0; ntries < 200; ntries++) {
6830220659Sbschmidt				if (IWN_READ(sc, IWN_FH_TX_STATUS) &
6831198429Srpaulo				    IWN_FH_TX_STATUS_IDLE(chnl))
6832198429Srpaulo					break;
6833198429Srpaulo				DELAY(10);
6834198429Srpaulo			}
6835198429Srpaulo		}
6836198429Srpaulo		iwn_nic_unlock(sc);
6837198429Srpaulo	}
6838178676Ssam
6839198429Srpaulo	/* Stop RX ring. */
6840198429Srpaulo	iwn_reset_rx_ring(sc, &sc->rxq);
6841178676Ssam
6842198429Srpaulo	/* Reset all TX rings. */
6843220728Sbschmidt	for (qid = 0; qid < sc->ntxqs; qid++)
6844198429Srpaulo		iwn_reset_tx_ring(sc, &sc->txq[qid]);
6845178676Ssam
6846198429Srpaulo	if (iwn_nic_lock(sc) == 0) {
6847201209Srpaulo		iwn_prph_write(sc, IWN_APMG_CLK_DIS,
6848201209Srpaulo		    IWN_APMG_CLK_CTRL_DMA_CLK_RQT);
6849198429Srpaulo		iwn_nic_unlock(sc);
6850178676Ssam	}
6851198429Srpaulo	DELAY(5);
6852198429Srpaulo	/* Power OFF adapter. */
6853198429Srpaulo	iwn_apm_stop(sc);
6854198429Srpaulo}
6855178676Ssam
6856206477Sbschmidtstatic void
6857220723Sbschmidtiwn_radio_on(void *arg0, int pending)
6858220723Sbschmidt{
6859220723Sbschmidt	struct iwn_softc *sc = arg0;
6860220723Sbschmidt	struct ifnet *ifp = sc->sc_ifp;
6861220723Sbschmidt	struct ieee80211com *ic = ifp->if_l2com;
6862220723Sbschmidt	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
6863220723Sbschmidt
6864220723Sbschmidt	if (vap != NULL) {
6865220723Sbschmidt		iwn_init(sc);
6866220723Sbschmidt		ieee80211_init(vap);
6867220723Sbschmidt	}
6868220723Sbschmidt}
6869220723Sbschmidt
6870220723Sbschmidtstatic void
6871220723Sbschmidtiwn_radio_off(void *arg0, int pending)
6872220723Sbschmidt{
6873220723Sbschmidt	struct iwn_softc *sc = arg0;
6874220723Sbschmidt	struct ifnet *ifp = sc->sc_ifp;
6875220723Sbschmidt	struct ieee80211com *ic = ifp->if_l2com;
6876220723Sbschmidt	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
6877220723Sbschmidt
6878220723Sbschmidt	iwn_stop(sc);
6879220723Sbschmidt	if (vap != NULL)
6880220723Sbschmidt		ieee80211_stop(vap);
6881220723Sbschmidt
6882220723Sbschmidt	/* Enable interrupts to get RF toggle notification. */
6883220723Sbschmidt	IWN_LOCK(sc);
6884220723Sbschmidt	IWN_WRITE(sc, IWN_INT, 0xffffffff);
6885220723Sbschmidt	IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask);
6886220723Sbschmidt	IWN_UNLOCK(sc);
6887220723Sbschmidt}
6888220723Sbschmidt
6889220723Sbschmidtstatic void
6890198429Srpauloiwn_init_locked(struct iwn_softc *sc)
6891198429Srpaulo{
6892198429Srpaulo	struct ifnet *ifp = sc->sc_ifp;
6893198429Srpaulo	int error;
6894178676Ssam
6895198429Srpaulo	IWN_LOCK_ASSERT(sc);
6896178676Ssam
6897220726Sbschmidt	if ((error = iwn_hw_prepare(sc)) != 0) {
6898220724Sbschmidt		device_printf(sc->sc_dev, "%s: hardware not ready, error %d\n",
6899198429Srpaulo		    __func__, error);
6900198429Srpaulo		goto fail;
6901198429Srpaulo	}
6902198429Srpaulo
6903201209Srpaulo	/* Initialize interrupt mask to default value. */
6904201209Srpaulo	sc->int_mask = IWN_INT_MASK_DEF;
6905201209Srpaulo	sc->sc_flags &= ~IWN_FLAG_USE_ICT;
6906201209Srpaulo
6907198429Srpaulo	/* Check that the radio is not disabled by hardware switch. */
6908198429Srpaulo	if (!(IWN_READ(sc, IWN_GP_CNTRL) & IWN_GP_CNTRL_RFKILL)) {
6909178676Ssam		device_printf(sc->sc_dev,
6910201209Srpaulo		    "radio is disabled by hardware switch\n");
6911201209Srpaulo		/* Enable interrupts to get RF toggle notifications. */
6912201209Srpaulo		IWN_WRITE(sc, IWN_INT, 0xffffffff);
6913201209Srpaulo		IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask);
6914201209Srpaulo		return;
6915178676Ssam	}
6916178676Ssam
6917198429Srpaulo	/* Read firmware images from the filesystem. */
6918220726Sbschmidt	if ((error = iwn_read_firmware(sc)) != 0) {
6919178676Ssam		device_printf(sc->sc_dev,
6920220726Sbschmidt		    "%s: could not read firmware, error %d\n", __func__,
6921220726Sbschmidt		    error);
6922198429Srpaulo		goto fail;
6923178676Ssam	}
6924178676Ssam
6925198429Srpaulo	/* Initialize hardware and upload firmware. */
6926198429Srpaulo	error = iwn_hw_init(sc);
6927201209Srpaulo	firmware_put(sc->fw_fp, FIRMWARE_UNLOAD);
6928201209Srpaulo	sc->fw_fp = NULL;
6929198429Srpaulo	if (error != 0) {
6930198429Srpaulo		device_printf(sc->sc_dev,
6931220726Sbschmidt		    "%s: could not initialize hardware, error %d\n", __func__,
6932220726Sbschmidt		    error);
6933198429Srpaulo		goto fail;
6934198429Srpaulo	}
6935178676Ssam
6936198429Srpaulo	/* Configure adapter now that it is ready. */
6937220726Sbschmidt	if ((error = iwn_config(sc)) != 0) {
6938178676Ssam		device_printf(sc->sc_dev,
6939220726Sbschmidt		    "%s: could not configure device, error %d\n", __func__,
6940220726Sbschmidt		    error);
6941198429Srpaulo		goto fail;
6942178676Ssam	}
6943178676Ssam
6944178676Ssam	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
6945178676Ssam	ifp->if_drv_flags |= IFF_DRV_RUNNING;
6946198429Srpaulo
6947220667Sbschmidt	callout_reset(&sc->watchdog_to, hz, iwn_watchdog, sc);
6948198429Srpaulo	return;
6949198429Srpaulo
6950220726Sbschmidtfail:	iwn_stop_locked(sc);
6951178676Ssam}
6952178676Ssam
6953206477Sbschmidtstatic void
6954178676Ssamiwn_init(void *arg)
6955178676Ssam{
6956178676Ssam	struct iwn_softc *sc = arg;
6957178676Ssam	struct ifnet *ifp = sc->sc_ifp;
6958178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
6959178676Ssam
6960178676Ssam	IWN_LOCK(sc);
6961178676Ssam	iwn_init_locked(sc);
6962178676Ssam	IWN_UNLOCK(sc);
6963178676Ssam
6964178676Ssam	if (ifp->if_drv_flags & IFF_DRV_RUNNING)
6965178676Ssam		ieee80211_start_all(ic);
6966178676Ssam}
6967178676Ssam
6968206477Sbschmidtstatic void
6969178676Ssamiwn_stop_locked(struct iwn_softc *sc)
6970178676Ssam{
6971178676Ssam	struct ifnet *ifp = sc->sc_ifp;
6972178676Ssam
6973178676Ssam	IWN_LOCK_ASSERT(sc);
6974178676Ssam
6975178676Ssam	sc->sc_tx_timer = 0;
6976220667Sbschmidt	callout_stop(&sc->watchdog_to);
6977220667Sbschmidt	callout_stop(&sc->calib_to);
6978178676Ssam	ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
6979178676Ssam
6980198429Srpaulo	/* Power OFF hardware. */
6981198429Srpaulo	iwn_hw_stop(sc);
6982198429Srpaulo}
6983178676Ssam
6984206477Sbschmidtstatic void
6985178676Ssamiwn_stop(struct iwn_softc *sc)
6986178676Ssam{
6987178676Ssam	IWN_LOCK(sc);
6988178676Ssam	iwn_stop_locked(sc);
6989178676Ssam	IWN_UNLOCK(sc);
6990178676Ssam}
6991178676Ssam
6992178676Ssam/*
6993178676Ssam * Callback from net80211 to start a scan.
6994178676Ssam */
6995178676Ssamstatic void
6996178676Ssamiwn_scan_start(struct ieee80211com *ic)
6997178676Ssam{
6998178676Ssam	struct ifnet *ifp = ic->ic_ifp;
6999178676Ssam	struct iwn_softc *sc = ifp->if_softc;
7000178676Ssam
7001191746Sthompsa	IWN_LOCK(sc);
7002191746Sthompsa	/* make the link LED blink while we're scanning */
7003191746Sthompsa	iwn_set_led(sc, IWN_LED_LINK, 20, 2);
7004191746Sthompsa	IWN_UNLOCK(sc);
7005178676Ssam}
7006178676Ssam
7007178676Ssam/*
7008178676Ssam * Callback from net80211 to terminate a scan.
7009178676Ssam */
7010178676Ssamstatic void
7011178676Ssamiwn_scan_end(struct ieee80211com *ic)
7012178676Ssam{
7013201209Srpaulo	struct ifnet *ifp = ic->ic_ifp;
7014201209Srpaulo	struct iwn_softc *sc = ifp->if_softc;
7015201209Srpaulo	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
7016201209Srpaulo
7017201209Srpaulo	IWN_LOCK(sc);
7018201209Srpaulo	if (vap->iv_state == IEEE80211_S_RUN) {
7019201209Srpaulo		/* Set link LED to ON status if we are associated */
7020201209Srpaulo		iwn_set_led(sc, IWN_LED_LINK, 0, 1);
7021201209Srpaulo	}
7022201209Srpaulo	IWN_UNLOCK(sc);
7023178676Ssam}
7024178676Ssam
7025178676Ssam/*
7026178676Ssam * Callback from net80211 to force a channel change.
7027178676Ssam */
7028178676Ssamstatic void
7029178676Ssamiwn_set_channel(struct ieee80211com *ic)
7030178676Ssam{
7031198429Srpaulo	const struct ieee80211_channel *c = ic->ic_curchan;
7032178676Ssam	struct ifnet *ifp = ic->ic_ifp;
7033178676Ssam	struct iwn_softc *sc = ifp->if_softc;
7034225686Sadrian	int error;
7035178676Ssam
7036191746Sthompsa	IWN_LOCK(sc);
7037201209Srpaulo	sc->sc_rxtap.wr_chan_freq = htole16(c->ic_freq);
7038201209Srpaulo	sc->sc_rxtap.wr_chan_flags = htole16(c->ic_flags);
7039201209Srpaulo	sc->sc_txtap.wt_chan_freq = htole16(c->ic_freq);
7040201209Srpaulo	sc->sc_txtap.wt_chan_flags = htole16(c->ic_flags);
7041225686Sadrian
7042225686Sadrian	/*
7043225686Sadrian	 * Only need to set the channel in Monitor mode. AP scanning and auth
7044225686Sadrian	 * are already taken care of by their respective firmware commands.
7045225686Sadrian	 */
7046225686Sadrian	if (ic->ic_opmode == IEEE80211_M_MONITOR) {
7047225686Sadrian		error = iwn_config(sc);
7048225686Sadrian		if (error != 0)
7049225686Sadrian		device_printf(sc->sc_dev,
7050225686Sadrian		    "%s: error %d settting channel\n", __func__, error);
7051225686Sadrian	}
7052191746Sthompsa	IWN_UNLOCK(sc);
7053178676Ssam}
7054178676Ssam
7055178676Ssam/*
7056178676Ssam * Callback from net80211 to start scanning of the current channel.
7057178676Ssam */
7058178676Ssamstatic void
7059178676Ssamiwn_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell)
7060178676Ssam{
7061178676Ssam	struct ieee80211vap *vap = ss->ss_vap;
7062178676Ssam	struct iwn_softc *sc = vap->iv_ic->ic_ifp->if_softc;
7063191746Sthompsa	int error;
7064178676Ssam
7065191746Sthompsa	IWN_LOCK(sc);
7066191746Sthompsa	error = iwn_scan(sc);
7067191746Sthompsa	IWN_UNLOCK(sc);
7068191746Sthompsa	if (error != 0)
7069191746Sthompsa		ieee80211_cancel_scan(vap);
7070178676Ssam}
7071178676Ssam
7072178676Ssam/*
7073178676Ssam * Callback from net80211 to handle the minimum dwell time being met.
7074178676Ssam * The intent is to terminate the scan but we just let the firmware
7075178676Ssam * notify us when it's finished as we have no safe way to abort it.
7076178676Ssam */
7077178676Ssamstatic void
7078178676Ssamiwn_scan_mindwell(struct ieee80211_scan_state *ss)
7079178676Ssam{
7080178676Ssam	/* NB: don't try to abort scan; wait for firmware to finish */
7081178676Ssam}
7082178676Ssam
7083178676Ssamstatic void
7084198429Srpauloiwn_hw_reset(void *arg0, int pending)
7085178676Ssam{
7086178676Ssam	struct iwn_softc *sc = arg0;
7087178676Ssam	struct ifnet *ifp = sc->sc_ifp;
7088178676Ssam	struct ieee80211com *ic = ifp->if_l2com;
7089178676Ssam
7090201209Srpaulo	iwn_stop(sc);
7091191746Sthompsa	iwn_init(sc);
7092191746Sthompsa	ieee80211_notify_radio(ic, 1);
7093191746Sthompsa}
7094