1ffd7c74aSJoe Talbott /*-
24b420e05SMatthew Dillon * Copyright (c) 2007-2009 Damien Bergamini <damien.bergamini@free.fr>
34b420e05SMatthew Dillon * Copyright (c) 2008 Benjamin Close <benjsc@FreeBSD.org>
4ffd7c74aSJoe Talbott * Copyright (c) 2008 Sam Leffler, Errno Consulting
54b420e05SMatthew Dillon * Copyright (c) 2011 Intel Corporation
64b420e05SMatthew Dillon * Copyright (c) 2013 Cedric GROSS <c.gross@kreiz-it.fr>
74b420e05SMatthew Dillon * Copyright (c) 2013 Adrian Chadd <adrian@FreeBSD.org>
8ffd7c74aSJoe Talbott *
9ffd7c74aSJoe Talbott * Permission to use, copy, modify, and distribute this software for any
10ffd7c74aSJoe Talbott * purpose with or without fee is hereby granted, provided that the above
11ffd7c74aSJoe Talbott * copyright notice and this permission notice appear in all copies.
12ffd7c74aSJoe Talbott *
13ffd7c74aSJoe Talbott * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14ffd7c74aSJoe Talbott * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15ffd7c74aSJoe Talbott * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16ffd7c74aSJoe Talbott * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17ffd7c74aSJoe Talbott * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18ffd7c74aSJoe Talbott * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19ffd7c74aSJoe Talbott * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20ffd7c74aSJoe Talbott */
21ffd7c74aSJoe Talbott
22ffd7c74aSJoe Talbott /*
23ffd7c74aSJoe Talbott * Driver for Intel WiFi Link 4965 and 1000/5000/6000 Series 802.11 network
24ffd7c74aSJoe Talbott * adapters.
25ffd7c74aSJoe Talbott */
26ffd7c74aSJoe Talbott
2705538f72SMatthew Dillon #include <sys/cdefs.h>
2805538f72SMatthew Dillon __FBSDID("$FreeBSD$");
2905538f72SMatthew Dillon
30da10ea93SMatthew Dillon #include "opt_wlan.h"
31da10ea93SMatthew Dillon #include "opt_iwn.h"
32da10ea93SMatthew Dillon
33ffd7c74aSJoe Talbott #include <sys/param.h>
34ffd7c74aSJoe Talbott #include <sys/sockio.h>
35ffd7c74aSJoe Talbott #include <sys/sysctl.h>
36ffd7c74aSJoe Talbott #include <sys/mbuf.h>
37ffd7c74aSJoe Talbott #include <sys/kernel.h>
38ffd7c74aSJoe Talbott #include <sys/socket.h>
39ffd7c74aSJoe Talbott #include <sys/systm.h>
40ffd7c74aSJoe Talbott #include <sys/malloc.h>
41ffd7c74aSJoe Talbott #include <sys/bus.h>
42977fc0dbSMatthew Dillon #include <sys/conf.h>
43ffd7c74aSJoe Talbott #include <sys/rman.h>
44ffd7c74aSJoe Talbott #include <sys/endian.h>
45ffd7c74aSJoe Talbott #include <sys/firmware.h>
46ffd7c74aSJoe Talbott #include <sys/limits.h>
47ffd7c74aSJoe Talbott #include <sys/module.h>
482b3f93eaSMatthew Dillon #include <sys/caps.h>
49ffd7c74aSJoe Talbott #include <sys/queue.h>
50ffd7c74aSJoe Talbott #include <sys/taskqueue.h>
51977fc0dbSMatthew Dillon #if defined(__DragonFly__)
52977fc0dbSMatthew Dillon #include <sys/device.h>
53977fc0dbSMatthew Dillon #endif
54ffd7c74aSJoe Talbott
55977fc0dbSMatthew Dillon #if defined(__DragonFly__)
56977fc0dbSMatthew Dillon /* empty */
57977fc0dbSMatthew Dillon #else
58977fc0dbSMatthew Dillon #include <machine/bus.h>
59977fc0dbSMatthew Dillon #include <machine/resource.h>
60ffd7c74aSJoe Talbott #include <machine/clock.h>
61977fc0dbSMatthew Dillon #endif
62ffd7c74aSJoe Talbott
63977fc0dbSMatthew Dillon #if defined(__DragonFly__)
64101554a6SMatthew Dillon #include <bus/pci/pcireg.h>
65101554a6SMatthew Dillon #include <bus/pci/pcivar.h>
66977fc0dbSMatthew Dillon #else
67977fc0dbSMatthew Dillon #include <dev/pci/pcireg.h>
68977fc0dbSMatthew Dillon #include <dev/pci/pcivar.h>
69977fc0dbSMatthew Dillon #endif
70ffd7c74aSJoe Talbott
71ffd7c74aSJoe Talbott #include <net/if.h>
72da10ea93SMatthew Dillon #include <net/if_var.h>
73ffd7c74aSJoe Talbott #include <net/if_dl.h>
74ffd7c74aSJoe Talbott #include <net/if_media.h>
75ffd7c74aSJoe Talbott
76ffd7c74aSJoe Talbott #include <netinet/in.h>
77ffd7c74aSJoe Talbott #include <netinet/if_ether.h>
78ffd7c74aSJoe Talbott
79101554a6SMatthew Dillon #include <netproto/802_11/ieee80211_var.h>
80101554a6SMatthew Dillon #include <netproto/802_11/ieee80211_radiotap.h>
81101554a6SMatthew Dillon #include <netproto/802_11/ieee80211_regdomain.h>
82101554a6SMatthew Dillon #include <netproto/802_11/ieee80211_ratectl.h>
83ffd7c74aSJoe Talbott
8405538f72SMatthew Dillon #include <dev/netif/iwn/if_iwnreg.h>
8505538f72SMatthew Dillon #include <dev/netif/iwn/if_iwnvar.h>
8605538f72SMatthew Dillon #include <dev/netif/iwn/if_iwn_devid.h>
8705538f72SMatthew Dillon #include <dev/netif/iwn/if_iwn_chip_cfg.h>
8805538f72SMatthew Dillon #include <dev/netif/iwn/if_iwn_debug.h>
8905538f72SMatthew Dillon #include <dev/netif/iwn/if_iwn_ioctl.h>
90fd49669cSMichael Neumann
91da10ea93SMatthew Dillon struct iwn_ident {
92da10ea93SMatthew Dillon uint16_t vendor;
93da10ea93SMatthew Dillon uint16_t device;
94da10ea93SMatthew Dillon const char *name;
95da10ea93SMatthew Dillon };
96da10ea93SMatthew Dillon
97da10ea93SMatthew Dillon static const struct iwn_ident iwn_ident_table[] = {
98da10ea93SMatthew Dillon { 0x8086, IWN_DID_6x05_1, "Intel Centrino Advanced-N 6205" },
99da10ea93SMatthew Dillon { 0x8086, IWN_DID_1000_1, "Intel Centrino Wireless-N 1000" },
100da10ea93SMatthew Dillon { 0x8086, IWN_DID_1000_2, "Intel Centrino Wireless-N 1000" },
101da10ea93SMatthew Dillon { 0x8086, IWN_DID_6x05_2, "Intel Centrino Advanced-N 6205" },
102da10ea93SMatthew Dillon { 0x8086, IWN_DID_6050_1, "Intel Centrino Advanced-N + WiMAX 6250" },
103da10ea93SMatthew Dillon { 0x8086, IWN_DID_6050_2, "Intel Centrino Advanced-N + WiMAX 6250" },
104da10ea93SMatthew Dillon { 0x8086, IWN_DID_x030_1, "Intel Centrino Wireless-N 1030" },
105da10ea93SMatthew Dillon { 0x8086, IWN_DID_x030_2, "Intel Centrino Wireless-N 1030" },
106da10ea93SMatthew Dillon { 0x8086, IWN_DID_x030_3, "Intel Centrino Advanced-N 6230" },
107da10ea93SMatthew Dillon { 0x8086, IWN_DID_x030_4, "Intel Centrino Advanced-N 6230" },
108da10ea93SMatthew Dillon { 0x8086, IWN_DID_6150_1, "Intel Centrino Wireless-N + WiMAX 6150" },
109da10ea93SMatthew Dillon { 0x8086, IWN_DID_6150_2, "Intel Centrino Wireless-N + WiMAX 6150" },
110da10ea93SMatthew Dillon { 0x8086, IWN_DID_2x00_1, "Intel(R) Centrino(R) Wireless-N 2200 BGN" },
111da10ea93SMatthew Dillon { 0x8086, IWN_DID_2x00_2, "Intel(R) Centrino(R) Wireless-N 2200 BGN" },
112da10ea93SMatthew Dillon /* XXX 2200D is IWN_SDID_2x00_4; there's no way to express this here! */
113da10ea93SMatthew Dillon { 0x8086, IWN_DID_2x30_1, "Intel Centrino Wireless-N 2230" },
114da10ea93SMatthew Dillon { 0x8086, IWN_DID_2x30_2, "Intel Centrino Wireless-N 2230" },
115da10ea93SMatthew Dillon { 0x8086, IWN_DID_130_1, "Intel Centrino Wireless-N 130" },
116da10ea93SMatthew Dillon { 0x8086, IWN_DID_130_2, "Intel Centrino Wireless-N 130" },
117da10ea93SMatthew Dillon { 0x8086, IWN_DID_100_1, "Intel Centrino Wireless-N 100" },
118da10ea93SMatthew Dillon { 0x8086, IWN_DID_100_2, "Intel Centrino Wireless-N 100" },
119fd49669cSMichael Neumann { 0x8086, IWN_DID_105_1, "Intel Centrino Wireless-N 105" },
120fd49669cSMichael Neumann { 0x8086, IWN_DID_105_2, "Intel Centrino Wireless-N 105" },
121fd49669cSMichael Neumann { 0x8086, IWN_DID_135_1, "Intel Centrino Wireless-N 135" },
122fd49669cSMichael Neumann { 0x8086, IWN_DID_135_2, "Intel Centrino Wireless-N 135" },
123da10ea93SMatthew Dillon { 0x8086, IWN_DID_4965_1, "Intel Wireless WiFi Link 4965" },
124da10ea93SMatthew Dillon { 0x8086, IWN_DID_6x00_1, "Intel Centrino Ultimate-N 6300" },
125da10ea93SMatthew Dillon { 0x8086, IWN_DID_6x00_2, "Intel Centrino Advanced-N 6200" },
126da10ea93SMatthew Dillon { 0x8086, IWN_DID_4965_2, "Intel Wireless WiFi Link 4965" },
127da10ea93SMatthew Dillon { 0x8086, IWN_DID_4965_3, "Intel Wireless WiFi Link 4965" },
128da10ea93SMatthew Dillon { 0x8086, IWN_DID_5x00_1, "Intel WiFi Link 5100" },
129da10ea93SMatthew Dillon { 0x8086, IWN_DID_4965_4, "Intel Wireless WiFi Link 4965" },
130da10ea93SMatthew Dillon { 0x8086, IWN_DID_5x00_3, "Intel Ultimate N WiFi Link 5300" },
131da10ea93SMatthew Dillon { 0x8086, IWN_DID_5x00_4, "Intel Ultimate N WiFi Link 5300" },
132da10ea93SMatthew Dillon { 0x8086, IWN_DID_5x00_2, "Intel WiFi Link 5100" },
133da10ea93SMatthew Dillon { 0x8086, IWN_DID_6x00_3, "Intel Centrino Ultimate-N 6300" },
134da10ea93SMatthew Dillon { 0x8086, IWN_DID_6x00_4, "Intel Centrino Advanced-N 6200" },
135da10ea93SMatthew Dillon { 0x8086, IWN_DID_5x50_1, "Intel WiMAX/WiFi Link 5350" },
136da10ea93SMatthew Dillon { 0x8086, IWN_DID_5x50_2, "Intel WiMAX/WiFi Link 5350" },
137da10ea93SMatthew Dillon { 0x8086, IWN_DID_5x50_3, "Intel WiMAX/WiFi Link 5150" },
138da10ea93SMatthew Dillon { 0x8086, IWN_DID_5x50_4, "Intel WiMAX/WiFi Link 5150" },
139da10ea93SMatthew Dillon { 0x8086, IWN_DID_6035_1, "Intel Centrino Advanced 6235" },
140da10ea93SMatthew Dillon { 0x8086, IWN_DID_6035_2, "Intel Centrino Advanced 6235" },
141da10ea93SMatthew Dillon { 0, 0, NULL }
142da10ea93SMatthew Dillon };
143da10ea93SMatthew Dillon
14405538f72SMatthew Dillon static int iwn_probe(device_t);
14505538f72SMatthew Dillon static int iwn_attach(device_t);
146da10ea93SMatthew Dillon static int iwn4965_attach(struct iwn_softc *, uint16_t);
147da10ea93SMatthew Dillon static int iwn5000_attach(struct iwn_softc *, uint16_t);
148da10ea93SMatthew Dillon static int iwn_config_specific(struct iwn_softc *, uint16_t);
149ffd7c74aSJoe Talbott static void iwn_radiotap_attach(struct iwn_softc *);
150da10ea93SMatthew Dillon static void iwn_sysctlattach(struct iwn_softc *);
151ffd7c74aSJoe Talbott static struct ieee80211vap *iwn_vap_create(struct ieee80211com *,
152da10ea93SMatthew Dillon const char [IFNAMSIZ], int, enum ieee80211_opmode, int,
153da10ea93SMatthew Dillon const uint8_t [IEEE80211_ADDR_LEN],
154da10ea93SMatthew Dillon const uint8_t [IEEE80211_ADDR_LEN]);
155ffd7c74aSJoe Talbott static void iwn_vap_delete(struct ieee80211vap *);
15605538f72SMatthew Dillon static int iwn_detach(device_t);
15705538f72SMatthew Dillon static int iwn_shutdown(device_t);
15805538f72SMatthew Dillon static int iwn_suspend(device_t);
15905538f72SMatthew Dillon static int iwn_resume(device_t);
160ffd7c74aSJoe Talbott static int iwn_nic_lock(struct iwn_softc *);
161ffd7c74aSJoe Talbott static int iwn_eeprom_lock(struct iwn_softc *);
162ffd7c74aSJoe Talbott static int iwn_init_otprom(struct iwn_softc *);
163ffd7c74aSJoe Talbott static int iwn_read_prom_data(struct iwn_softc *, uint32_t, void *, int);
164ffd7c74aSJoe Talbott static void iwn_dma_map_addr(void *, bus_dma_segment_t *, int, int);
165ffd7c74aSJoe Talbott static int iwn_dma_contig_alloc(struct iwn_softc *, struct iwn_dma_info *,
166da10ea93SMatthew Dillon void **, bus_size_t, bus_size_t);
167ffd7c74aSJoe Talbott static void iwn_dma_contig_free(struct iwn_dma_info *);
168ffd7c74aSJoe Talbott static int iwn_alloc_sched(struct iwn_softc *);
169ffd7c74aSJoe Talbott static void iwn_free_sched(struct iwn_softc *);
170ffd7c74aSJoe Talbott static int iwn_alloc_kw(struct iwn_softc *);
171ffd7c74aSJoe Talbott static void iwn_free_kw(struct iwn_softc *);
172ffd7c74aSJoe Talbott static int iwn_alloc_ict(struct iwn_softc *);
173ffd7c74aSJoe Talbott static void iwn_free_ict(struct iwn_softc *);
174ffd7c74aSJoe Talbott static int iwn_alloc_fwmem(struct iwn_softc *);
175ffd7c74aSJoe Talbott static void iwn_free_fwmem(struct iwn_softc *);
176ffd7c74aSJoe Talbott static int iwn_alloc_rx_ring(struct iwn_softc *, struct iwn_rx_ring *);
177ffd7c74aSJoe Talbott static void iwn_reset_rx_ring(struct iwn_softc *, struct iwn_rx_ring *);
178ffd7c74aSJoe Talbott static void iwn_free_rx_ring(struct iwn_softc *, struct iwn_rx_ring *);
179ffd7c74aSJoe Talbott static int iwn_alloc_tx_ring(struct iwn_softc *, struct iwn_tx_ring *,
180ffd7c74aSJoe Talbott int);
181ffd7c74aSJoe Talbott static void iwn_reset_tx_ring(struct iwn_softc *, struct iwn_tx_ring *);
182ffd7c74aSJoe Talbott static void iwn_free_tx_ring(struct iwn_softc *, struct iwn_tx_ring *);
183ffd7c74aSJoe Talbott static void iwn5000_ict_reset(struct iwn_softc *);
184ffd7c74aSJoe Talbott static int iwn_read_eeprom(struct iwn_softc *,
185ffd7c74aSJoe Talbott uint8_t macaddr[IEEE80211_ADDR_LEN]);
186ffd7c74aSJoe Talbott static void iwn4965_read_eeprom(struct iwn_softc *);
187da10ea93SMatthew Dillon #ifdef IWN_DEBUG
188ffd7c74aSJoe Talbott static void iwn4965_print_power_group(struct iwn_softc *, int);
189da10ea93SMatthew Dillon #endif
190ffd7c74aSJoe Talbott static void iwn5000_read_eeprom(struct iwn_softc *);
191ffd7c74aSJoe Talbott static uint32_t iwn_eeprom_channel_flags(struct iwn_eeprom_chan *);
192977fc0dbSMatthew Dillon static void iwn_read_eeprom_band(struct iwn_softc *, int, int, int *,
193977fc0dbSMatthew Dillon struct ieee80211_channel[]);
194977fc0dbSMatthew Dillon static void iwn_read_eeprom_ht40(struct iwn_softc *, int, int, int *,
195977fc0dbSMatthew Dillon struct ieee80211_channel[]);
196da10ea93SMatthew Dillon static void iwn_read_eeprom_channels(struct iwn_softc *, int, uint32_t);
197da10ea93SMatthew Dillon static struct iwn_eeprom_chan *iwn_find_eeprom_channel(struct iwn_softc *,
198da10ea93SMatthew Dillon struct ieee80211_channel *);
199977fc0dbSMatthew Dillon static void iwn_getradiocaps(struct ieee80211com *, int, int *,
200977fc0dbSMatthew Dillon struct ieee80211_channel[]);
201da10ea93SMatthew Dillon static int iwn_setregdomain(struct ieee80211com *,
202da10ea93SMatthew Dillon struct ieee80211_regdomain *, int,
203da10ea93SMatthew Dillon struct ieee80211_channel[]);
204ffd7c74aSJoe Talbott static void iwn_read_eeprom_enhinfo(struct iwn_softc *);
205ffd7c74aSJoe Talbott static struct ieee80211_node *iwn_node_alloc(struct ieee80211vap *,
206ffd7c74aSJoe Talbott const uint8_t mac[IEEE80211_ADDR_LEN]);
207ffd7c74aSJoe Talbott static void iwn_newassoc(struct ieee80211_node *, int);
208ffd7c74aSJoe Talbott static int iwn_media_change(struct ifnet *);
209ffd7c74aSJoe Talbott static int iwn_newstate(struct ieee80211vap *, enum ieee80211_state, int);
210da10ea93SMatthew Dillon static void iwn_calib_timeout(void *);
211ffd7c74aSJoe Talbott static void iwn_rx_phy(struct iwn_softc *, struct iwn_rx_desc *,
212ffd7c74aSJoe Talbott struct iwn_rx_data *);
213ffd7c74aSJoe Talbott static void iwn_rx_done(struct iwn_softc *, struct iwn_rx_desc *,
214ffd7c74aSJoe Talbott struct iwn_rx_data *);
215ffd7c74aSJoe Talbott static void iwn_rx_compressed_ba(struct iwn_softc *, struct iwn_rx_desc *,
216ffd7c74aSJoe Talbott struct iwn_rx_data *);
217ffd7c74aSJoe Talbott static void iwn5000_rx_calib_results(struct iwn_softc *,
218ffd7c74aSJoe Talbott struct iwn_rx_desc *, struct iwn_rx_data *);
219ffd7c74aSJoe Talbott static void iwn_rx_statistics(struct iwn_softc *, struct iwn_rx_desc *,
220ffd7c74aSJoe Talbott struct iwn_rx_data *);
221ffd7c74aSJoe Talbott static void iwn4965_tx_done(struct iwn_softc *, struct iwn_rx_desc *,
222ffd7c74aSJoe Talbott struct iwn_rx_data *);
223ffd7c74aSJoe Talbott static void iwn5000_tx_done(struct iwn_softc *, struct iwn_rx_desc *,
224ffd7c74aSJoe Talbott struct iwn_rx_data *);
225ffd7c74aSJoe Talbott static void iwn_tx_done(struct iwn_softc *, struct iwn_rx_desc *, int,
226ffd7c74aSJoe Talbott uint8_t);
22705538f72SMatthew Dillon static void iwn_ampdu_tx_done(struct iwn_softc *, int, int, int, int, void *);
228ffd7c74aSJoe Talbott static void iwn_cmd_done(struct iwn_softc *, struct iwn_rx_desc *);
229ffd7c74aSJoe Talbott static void iwn_notif_intr(struct iwn_softc *);
230ffd7c74aSJoe Talbott static void iwn_wakeup_intr(struct iwn_softc *);
231ffd7c74aSJoe Talbott static void iwn_rftoggle_intr(struct iwn_softc *);
232ffd7c74aSJoe Talbott static void iwn_fatal_intr(struct iwn_softc *);
233ffd7c74aSJoe Talbott static void iwn_intr(void *);
234ffd7c74aSJoe Talbott static void iwn4965_update_sched(struct iwn_softc *, int, int, uint8_t,
235ffd7c74aSJoe Talbott uint16_t);
236ffd7c74aSJoe Talbott static void iwn5000_update_sched(struct iwn_softc *, int, int, uint8_t,
237ffd7c74aSJoe Talbott uint16_t);
238ffd7c74aSJoe Talbott #ifdef notyet
239ffd7c74aSJoe Talbott static void iwn5000_reset_sched(struct iwn_softc *, int, int);
240ffd7c74aSJoe Talbott #endif
241ffd7c74aSJoe Talbott static int iwn_tx_data(struct iwn_softc *, struct mbuf *,
242da10ea93SMatthew Dillon struct ieee80211_node *);
243da10ea93SMatthew Dillon static int iwn_tx_data_raw(struct iwn_softc *, struct mbuf *,
244da10ea93SMatthew Dillon struct ieee80211_node *,
245da10ea93SMatthew Dillon const struct ieee80211_bpf_params *params);
246977fc0dbSMatthew Dillon static void iwn_xmit_task(void *arg0, int pending);
247ffd7c74aSJoe Talbott static int iwn_raw_xmit(struct ieee80211_node *, struct mbuf *,
248ffd7c74aSJoe Talbott const struct ieee80211_bpf_params *);
249977fc0dbSMatthew Dillon static int iwn_transmit(struct ieee80211com *, struct mbuf *);
25005538f72SMatthew Dillon static void iwn_watchdog(void *);
251977fc0dbSMatthew Dillon static int iwn_ioctl(struct ieee80211com *, u_long , void *);
252977fc0dbSMatthew Dillon static void iwn_parent(struct ieee80211com *);
253ffd7c74aSJoe Talbott static int iwn_cmd(struct iwn_softc *, int, const void *, int, int);
254ffd7c74aSJoe Talbott static int iwn4965_add_node(struct iwn_softc *, struct iwn_node_info *,
255ffd7c74aSJoe Talbott int);
256ffd7c74aSJoe Talbott static int iwn5000_add_node(struct iwn_softc *, struct iwn_node_info *,
257ffd7c74aSJoe Talbott int);
258da10ea93SMatthew Dillon static int iwn_set_link_quality(struct iwn_softc *,
259da10ea93SMatthew Dillon struct ieee80211_node *);
260ffd7c74aSJoe Talbott static int iwn_add_broadcast_node(struct iwn_softc *, int);
261da10ea93SMatthew Dillon static int iwn_updateedca(struct ieee80211com *);
2624f898719SImre Vadász static void iwn_update_mcast(struct ieee80211com *);
263ffd7c74aSJoe Talbott static void iwn_set_led(struct iwn_softc *, uint8_t, uint8_t, uint8_t);
264ffd7c74aSJoe Talbott static int iwn_set_critical_temp(struct iwn_softc *);
265ffd7c74aSJoe Talbott static int iwn_set_timing(struct iwn_softc *, struct ieee80211_node *);
266ffd7c74aSJoe Talbott static void iwn4965_power_calibration(struct iwn_softc *, int);
267ffd7c74aSJoe Talbott static int iwn4965_set_txpower(struct iwn_softc *,
268ffd7c74aSJoe Talbott struct ieee80211_channel *, int);
269ffd7c74aSJoe Talbott static int iwn5000_set_txpower(struct iwn_softc *,
270ffd7c74aSJoe Talbott struct ieee80211_channel *, int);
271ffd7c74aSJoe Talbott static int iwn4965_get_rssi(struct iwn_softc *, struct iwn_rx_stat *);
272ffd7c74aSJoe Talbott static int iwn5000_get_rssi(struct iwn_softc *, struct iwn_rx_stat *);
273ffd7c74aSJoe Talbott static int iwn_get_noise(const struct iwn_rx_general_stats *);
274ffd7c74aSJoe Talbott static int iwn4965_get_temperature(struct iwn_softc *);
275ffd7c74aSJoe Talbott static int iwn5000_get_temperature(struct iwn_softc *);
276ffd7c74aSJoe Talbott static int iwn_init_sensitivity(struct iwn_softc *);
277ffd7c74aSJoe Talbott static void iwn_collect_noise(struct iwn_softc *,
278ffd7c74aSJoe Talbott const struct iwn_rx_general_stats *);
279ffd7c74aSJoe Talbott static int iwn4965_init_gains(struct iwn_softc *);
280ffd7c74aSJoe Talbott static int iwn5000_init_gains(struct iwn_softc *);
281ffd7c74aSJoe Talbott static int iwn4965_set_gains(struct iwn_softc *);
282ffd7c74aSJoe Talbott static int iwn5000_set_gains(struct iwn_softc *);
283ffd7c74aSJoe Talbott static void iwn_tune_sensitivity(struct iwn_softc *,
284ffd7c74aSJoe Talbott const struct iwn_rx_stats *);
285fd49669cSMichael Neumann static void iwn_save_stats_counters(struct iwn_softc *,
286fd49669cSMichael Neumann const struct iwn_stats *);
287ffd7c74aSJoe Talbott static int iwn_send_sensitivity(struct iwn_softc *);
288fd49669cSMichael Neumann static void iwn_check_rx_recovery(struct iwn_softc *, struct iwn_stats *);
289ffd7c74aSJoe Talbott static int iwn_set_pslevel(struct iwn_softc *, int, int, int);
290da10ea93SMatthew Dillon static int iwn_send_btcoex(struct iwn_softc *);
291da10ea93SMatthew Dillon static int iwn_send_advanced_btcoex(struct iwn_softc *);
292da10ea93SMatthew Dillon static int iwn5000_runtime_calib(struct iwn_softc *);
293ffd7c74aSJoe Talbott static int iwn_config(struct iwn_softc *);
294fd49669cSMichael Neumann static int iwn_scan(struct iwn_softc *, struct ieee80211vap *,
295fd49669cSMichael Neumann struct ieee80211_scan_state *, struct ieee80211_channel *);
296ffd7c74aSJoe Talbott static int iwn_auth(struct iwn_softc *, struct ieee80211vap *vap);
297ffd7c74aSJoe Talbott static int iwn_run(struct iwn_softc *, struct ieee80211vap *vap);
298da10ea93SMatthew Dillon static int iwn_ampdu_rx_start(struct ieee80211_node *,
299da10ea93SMatthew Dillon struct ieee80211_rx_ampdu *, int, int, int);
300da10ea93SMatthew Dillon static void iwn_ampdu_rx_stop(struct ieee80211_node *,
301da10ea93SMatthew Dillon struct ieee80211_rx_ampdu *);
302da10ea93SMatthew Dillon static int iwn_addba_request(struct ieee80211_node *,
303da10ea93SMatthew Dillon struct ieee80211_tx_ampdu *, int, int, int);
304da10ea93SMatthew Dillon static int iwn_addba_response(struct ieee80211_node *,
305da10ea93SMatthew Dillon struct ieee80211_tx_ampdu *, int, int, int);
306ffd7c74aSJoe Talbott static int iwn_ampdu_tx_start(struct ieee80211com *,
307ffd7c74aSJoe Talbott struct ieee80211_node *, uint8_t);
308da10ea93SMatthew Dillon static void iwn_ampdu_tx_stop(struct ieee80211_node *,
309da10ea93SMatthew Dillon struct ieee80211_tx_ampdu *);
310ffd7c74aSJoe Talbott static void iwn4965_ampdu_tx_start(struct iwn_softc *,
311da10ea93SMatthew Dillon struct ieee80211_node *, int, uint8_t, uint16_t);
312da10ea93SMatthew Dillon static void iwn4965_ampdu_tx_stop(struct iwn_softc *, int,
313da10ea93SMatthew Dillon uint8_t, uint16_t);
314ffd7c74aSJoe Talbott static void iwn5000_ampdu_tx_start(struct iwn_softc *,
315da10ea93SMatthew Dillon struct ieee80211_node *, int, uint8_t, uint16_t);
316da10ea93SMatthew Dillon static void iwn5000_ampdu_tx_stop(struct iwn_softc *, int,
317da10ea93SMatthew Dillon uint8_t, uint16_t);
318ffd7c74aSJoe Talbott static int iwn5000_query_calibration(struct iwn_softc *);
319ffd7c74aSJoe Talbott static int iwn5000_send_calibration(struct iwn_softc *);
320ffd7c74aSJoe Talbott static int iwn5000_send_wimax_coex(struct iwn_softc *);
321da10ea93SMatthew Dillon static int iwn5000_crystal_calib(struct iwn_softc *);
322da10ea93SMatthew Dillon static int iwn5000_temp_offset_calib(struct iwn_softc *);
323da10ea93SMatthew Dillon static int iwn5000_temp_offset_calibv2(struct iwn_softc *);
324ffd7c74aSJoe Talbott static int iwn4965_post_alive(struct iwn_softc *);
325ffd7c74aSJoe Talbott static int iwn5000_post_alive(struct iwn_softc *);
326ffd7c74aSJoe Talbott static int iwn4965_load_bootcode(struct iwn_softc *, const uint8_t *,
327ffd7c74aSJoe Talbott int);
328ffd7c74aSJoe Talbott static int iwn4965_load_firmware(struct iwn_softc *);
329ffd7c74aSJoe Talbott static int iwn5000_load_firmware_section(struct iwn_softc *, uint32_t,
330ffd7c74aSJoe Talbott const uint8_t *, int);
331ffd7c74aSJoe Talbott static int iwn5000_load_firmware(struct iwn_softc *);
332da10ea93SMatthew Dillon static int iwn_read_firmware_leg(struct iwn_softc *,
333da10ea93SMatthew Dillon struct iwn_fw_info *);
334da10ea93SMatthew Dillon static int iwn_read_firmware_tlv(struct iwn_softc *,
335da10ea93SMatthew Dillon struct iwn_fw_info *, uint16_t);
336ffd7c74aSJoe Talbott static int iwn_read_firmware(struct iwn_softc *);
337977fc0dbSMatthew Dillon static void iwn_unload_firmware(struct iwn_softc *);
338ffd7c74aSJoe Talbott static int iwn_clock_wait(struct iwn_softc *);
339ffd7c74aSJoe Talbott static int iwn_apm_init(struct iwn_softc *);
340ffd7c74aSJoe Talbott static void iwn_apm_stop_master(struct iwn_softc *);
341ffd7c74aSJoe Talbott static void iwn_apm_stop(struct iwn_softc *);
342ffd7c74aSJoe Talbott static int iwn4965_nic_config(struct iwn_softc *);
343ffd7c74aSJoe Talbott static int iwn5000_nic_config(struct iwn_softc *);
344ffd7c74aSJoe Talbott static int iwn_hw_prepare(struct iwn_softc *);
345ffd7c74aSJoe Talbott static int iwn_hw_init(struct iwn_softc *);
346ffd7c74aSJoe Talbott static void iwn_hw_stop(struct iwn_softc *);
34705538f72SMatthew Dillon static void iwn_radio_on(void *, int);
34805538f72SMatthew Dillon static void iwn_radio_off(void *, int);
34905538f72SMatthew Dillon static void iwn_panicked(void *, int);
350ffd7c74aSJoe Talbott static void iwn_init_locked(struct iwn_softc *);
351977fc0dbSMatthew Dillon static void iwn_init(struct iwn_softc *);
352ffd7c74aSJoe Talbott static void iwn_stop_locked(struct iwn_softc *);
35305538f72SMatthew Dillon static void iwn_stop(struct iwn_softc *);
354ffd7c74aSJoe Talbott static void iwn_scan_start(struct ieee80211com *);
355ffd7c74aSJoe Talbott static void iwn_scan_end(struct ieee80211com *);
356ffd7c74aSJoe Talbott static void iwn_set_channel(struct ieee80211com *);
357ffd7c74aSJoe Talbott static void iwn_scan_curchan(struct ieee80211_scan_state *, unsigned long);
358ffd7c74aSJoe Talbott static void iwn_scan_mindwell(struct ieee80211_scan_state *);
359ffd7c74aSJoe Talbott #ifdef IWN_DEBUG
360da10ea93SMatthew Dillon static char *iwn_get_csr_string(int);
361da10ea93SMatthew Dillon static void iwn_debug_register(struct iwn_softc *);
362ffd7c74aSJoe Talbott #endif
363ffd7c74aSJoe Talbott
364da10ea93SMatthew Dillon static device_method_t iwn_methods[] = {
365da10ea93SMatthew Dillon /* Device interface */
36605538f72SMatthew Dillon DEVMETHOD(device_probe, iwn_probe),
36705538f72SMatthew Dillon DEVMETHOD(device_attach, iwn_attach),
36805538f72SMatthew Dillon DEVMETHOD(device_detach, iwn_detach),
36905538f72SMatthew Dillon DEVMETHOD(device_shutdown, iwn_shutdown),
37005538f72SMatthew Dillon DEVMETHOD(device_suspend, iwn_suspend),
37105538f72SMatthew Dillon DEVMETHOD(device_resume, iwn_resume),
372fd49669cSMichael Neumann
373fd49669cSMichael Neumann DEVMETHOD_END
374ffd7c74aSJoe Talbott };
375ffd7c74aSJoe Talbott
376da10ea93SMatthew Dillon static driver_t iwn_driver = {
377da10ea93SMatthew Dillon "iwn",
378da10ea93SMatthew Dillon iwn_methods,
379da10ea93SMatthew Dillon sizeof(struct iwn_softc)
380ffd7c74aSJoe Talbott };
381da10ea93SMatthew Dillon static devclass_t iwn_devclass;
382ffd7c74aSJoe Talbott
383101554a6SMatthew Dillon DRIVER_MODULE(iwn, pci, iwn_driver, iwn_devclass, NULL, NULL);
384ffd7c74aSJoe Talbott
385da10ea93SMatthew Dillon MODULE_VERSION(iwn, 1);
386da10ea93SMatthew Dillon
387da10ea93SMatthew Dillon MODULE_DEPEND(iwn, firmware, 1, 1, 1);
388da10ea93SMatthew Dillon MODULE_DEPEND(iwn, pci, 1, 1, 1);
389da10ea93SMatthew Dillon MODULE_DEPEND(iwn, wlan, 1, 1, 1);
390ffd7c74aSJoe Talbott
391977fc0dbSMatthew Dillon static d_ioctl_t iwn_cdev_ioctl;
392977fc0dbSMatthew Dillon static d_open_t iwn_cdev_open;
393977fc0dbSMatthew Dillon static d_close_t iwn_cdev_close;
394977fc0dbSMatthew Dillon
395977fc0dbSMatthew Dillon static struct dev_ops iwn_cdevsw = {
396977fc0dbSMatthew Dillon #if defined(__DragonFly__)
397977fc0dbSMatthew Dillon /* none */
398977fc0dbSMatthew Dillon { "iwn", 0, 0 },
399977fc0dbSMatthew Dillon #else
400977fc0dbSMatthew Dillon .d_version = D_VERSION,
401977fc0dbSMatthew Dillon .d_flags = 0,
402977fc0dbSMatthew Dillon #endif
403977fc0dbSMatthew Dillon .d_open = iwn_cdev_open,
404977fc0dbSMatthew Dillon .d_close = iwn_cdev_close,
405977fc0dbSMatthew Dillon .d_ioctl = iwn_cdev_ioctl,
406977fc0dbSMatthew Dillon #if defined(__DragonFly__)
407977fc0dbSMatthew Dillon /* none */
408977fc0dbSMatthew Dillon #else
409977fc0dbSMatthew Dillon .d_name = "iwn",
410977fc0dbSMatthew Dillon #endif
411977fc0dbSMatthew Dillon };
412977fc0dbSMatthew Dillon
413ffd7c74aSJoe Talbott static int
iwn_probe(device_t dev)41405538f72SMatthew Dillon iwn_probe(device_t dev)
415ffd7c74aSJoe Talbott {
416ffd7c74aSJoe Talbott const struct iwn_ident *ident;
417ffd7c74aSJoe Talbott
418ffd7c74aSJoe Talbott for (ident = iwn_ident_table; ident->name != NULL; ident++) {
419ffd7c74aSJoe Talbott if (pci_get_vendor(dev) == ident->vendor &&
420ffd7c74aSJoe Talbott pci_get_device(dev) == ident->device) {
421ffd7c74aSJoe Talbott device_set_desc(dev, ident->name);
422fd49669cSMichael Neumann return (BUS_PROBE_DEFAULT);
423ffd7c74aSJoe Talbott }
424ffd7c74aSJoe Talbott }
425ffd7c74aSJoe Talbott return ENXIO;
426ffd7c74aSJoe Talbott }
427ffd7c74aSJoe Talbott
428ffd7c74aSJoe Talbott static int
iwn_is_3stream_device(struct iwn_softc * sc)42905538f72SMatthew Dillon iwn_is_3stream_device(struct iwn_softc *sc)
43005538f72SMatthew Dillon {
43105538f72SMatthew Dillon /* XXX for now only 5300, until the 5350 can be tested */
43205538f72SMatthew Dillon if (sc->hw_type == IWN_HW_REV_TYPE_5300)
43305538f72SMatthew Dillon return (1);
43405538f72SMatthew Dillon return (0);
43505538f72SMatthew Dillon }
43605538f72SMatthew Dillon
43705538f72SMatthew Dillon static int
iwn_attach(device_t dev)43805538f72SMatthew Dillon iwn_attach(device_t dev)
439ffd7c74aSJoe Talbott {
440977fc0dbSMatthew Dillon struct iwn_softc *sc = device_get_softc(dev);
441ffd7c74aSJoe Talbott struct ieee80211com *ic;
442fd49669cSMichael Neumann int i, error, rid;
443b1ee9c70SMatthew Dillon #if defined(__DragonFly__)
444b1ee9c70SMatthew Dillon int irq_flags;
445b1ee9c70SMatthew Dillon #endif
446458fc9cfSMatthew Dillon
447ffd7c74aSJoe Talbott sc->sc_dev = dev;
448101554a6SMatthew Dillon
449da10ea93SMatthew Dillon #ifdef IWN_DEBUG
450da10ea93SMatthew Dillon error = resource_int_value(device_get_name(sc->sc_dev),
451da10ea93SMatthew Dillon device_get_unit(sc->sc_dev), "debug", &(sc->sc_debug));
452da10ea93SMatthew Dillon if (error != 0)
453da10ea93SMatthew Dillon sc->sc_debug = 0;
454da10ea93SMatthew Dillon #else
455da10ea93SMatthew Dillon sc->sc_debug = 0;
456da10ea93SMatthew Dillon #endif
4573db796acSJoe Talbott
458da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: begin\n",__func__);
459ffd7c74aSJoe Talbott
460ffd7c74aSJoe Talbott /*
461ffd7c74aSJoe Talbott * Get the offset of the PCI Express Capability Structure in PCI
462ffd7c74aSJoe Talbott * Configuration Space.
463ffd7c74aSJoe Talbott */
46405538f72SMatthew Dillon #if defined(__DragonFly__)
465101554a6SMatthew Dillon error = pci_find_extcap(dev, PCIY_EXPRESS, &sc->sc_cap_off);
46605538f72SMatthew Dillon #else
46705538f72SMatthew Dillon error = pci_find_cap(dev, PCIY_EXPRESS, &sc->sc_cap_off);
46805538f72SMatthew Dillon #endif
469ffd7c74aSJoe Talbott if (error != 0) {
470ffd7c74aSJoe Talbott device_printf(dev, "PCIe capability structure not found!\n");
47105538f72SMatthew Dillon return error;
472ffd7c74aSJoe Talbott }
473ffd7c74aSJoe Talbott
474ffd7c74aSJoe Talbott /* Clear device-specific "PCI retry timeout" register (41h). */
475ffd7c74aSJoe Talbott pci_write_config(dev, 0x41, 0, 1);
476ffd7c74aSJoe Talbott
477ffd7c74aSJoe Talbott /* Enable bus-mastering. */
478ffd7c74aSJoe Talbott pci_enable_busmaster(dev);
479ffd7c74aSJoe Talbott
480fd49669cSMichael Neumann rid = PCIR_BAR(0);
481fd49669cSMichael Neumann sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
482ffd7c74aSJoe Talbott RF_ACTIVE);
483ffd7c74aSJoe Talbott if (sc->mem == NULL) {
484da10ea93SMatthew Dillon device_printf(dev, "can't map mem space\n");
485ffd7c74aSJoe Talbott error = ENOMEM;
48605538f72SMatthew Dillon return error;
487ffd7c74aSJoe Talbott }
488ffd7c74aSJoe Talbott sc->sc_st = rman_get_bustag(sc->mem);
489ffd7c74aSJoe Talbott sc->sc_sh = rman_get_bushandle(sc->mem);
490da10ea93SMatthew Dillon
491977fc0dbSMatthew Dillon #if defined(__DragonFly__)
492b1ee9c70SMatthew Dillon pci_alloc_1intr(dev, 1, &rid, &irq_flags);
493b1ee9c70SMatthew Dillon /* Install interrupt handler. */
494b1ee9c70SMatthew Dillon sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, irq_flags);
495977fc0dbSMatthew Dillon #else
496fd49669cSMichael Neumann i = 1;
497977fc0dbSMatthew Dillon rid = 0;
498fd49669cSMichael Neumann if (pci_alloc_msi(dev, &i) == 0)
499fd49669cSMichael Neumann rid = 1;
500da10ea93SMatthew Dillon /* Install interrupt handler. */
501fd49669cSMichael Neumann sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE |
502fd49669cSMichael Neumann (rid != 0 ? 0 : RF_SHAREABLE));
503b1ee9c70SMatthew Dillon #endif
504ffd7c74aSJoe Talbott if (sc->irq == NULL) {
505da10ea93SMatthew Dillon device_printf(dev, "can't map interrupt\n");
506ffd7c74aSJoe Talbott error = ENOMEM;
507ffd7c74aSJoe Talbott goto fail;
508ffd7c74aSJoe Talbott }
509ffd7c74aSJoe Talbott
51005538f72SMatthew Dillon IWN_LOCK_INIT(sc);
51105538f72SMatthew Dillon
512da10ea93SMatthew Dillon /* Read hardware revision and attach. */
513da10ea93SMatthew Dillon sc->hw_type = (IWN_READ(sc, IWN_HW_REV) >> IWN_HW_REV_TYPE_SHIFT)
514da10ea93SMatthew Dillon & IWN_HW_REV_TYPE_MASK;
515da10ea93SMatthew Dillon sc->subdevice_id = pci_get_subdevice(dev);
51605538f72SMatthew Dillon
517da10ea93SMatthew Dillon /*
518da10ea93SMatthew Dillon * 4965 versus 5000 and later have different methods.
519da10ea93SMatthew Dillon * Let's set those up first.
520da10ea93SMatthew Dillon */
521da10ea93SMatthew Dillon if (sc->hw_type == IWN_HW_REV_TYPE_4965)
522da10ea93SMatthew Dillon error = iwn4965_attach(sc, pci_get_device(dev));
523da10ea93SMatthew Dillon else
524da10ea93SMatthew Dillon error = iwn5000_attach(sc, pci_get_device(dev));
525da10ea93SMatthew Dillon if (error != 0) {
526da10ea93SMatthew Dillon device_printf(dev, "could not attach device, error %d\n",
527da10ea93SMatthew Dillon error);
528ffd7c74aSJoe Talbott goto fail;
529ffd7c74aSJoe Talbott }
530ffd7c74aSJoe Talbott
531da10ea93SMatthew Dillon /*
532da10ea93SMatthew Dillon * Next, let's setup the various parameters of each NIC.
533da10ea93SMatthew Dillon */
534da10ea93SMatthew Dillon error = iwn_config_specific(sc, pci_get_device(dev));
535ffd7c74aSJoe Talbott if (error != 0) {
536da10ea93SMatthew Dillon device_printf(dev, "could not attach device, error %d\n",
537da10ea93SMatthew Dillon error);
538da10ea93SMatthew Dillon goto fail;
539da10ea93SMatthew Dillon }
540da10ea93SMatthew Dillon
541da10ea93SMatthew Dillon if ((error = iwn_hw_prepare(sc)) != 0) {
542ffd7c74aSJoe Talbott device_printf(dev, "hardware not ready, error %d\n", error);
543ffd7c74aSJoe Talbott goto fail;
544ffd7c74aSJoe Talbott }
545ffd7c74aSJoe Talbott
546ffd7c74aSJoe Talbott /* Allocate DMA memory for firmware transfers. */
547da10ea93SMatthew Dillon if ((error = iwn_alloc_fwmem(sc)) != 0) {
548ffd7c74aSJoe Talbott device_printf(dev,
549ffd7c74aSJoe Talbott "could not allocate memory for firmware, error %d\n",
550ffd7c74aSJoe Talbott error);
551ffd7c74aSJoe Talbott goto fail;
552ffd7c74aSJoe Talbott }
553ffd7c74aSJoe Talbott
554ffd7c74aSJoe Talbott /* Allocate "Keep Warm" page. */
555da10ea93SMatthew Dillon if ((error = iwn_alloc_kw(sc)) != 0) {
556ffd7c74aSJoe Talbott device_printf(dev,
557da10ea93SMatthew Dillon "could not allocate keep warm page, error %d\n", error);
558ffd7c74aSJoe Talbott goto fail;
559ffd7c74aSJoe Talbott }
560ffd7c74aSJoe Talbott
561ffd7c74aSJoe Talbott /* Allocate ICT table for 5000 Series. */
562ffd7c74aSJoe Talbott if (sc->hw_type != IWN_HW_REV_TYPE_4965 &&
563ffd7c74aSJoe Talbott (error = iwn_alloc_ict(sc)) != 0) {
564da10ea93SMatthew Dillon device_printf(dev, "could not allocate ICT table, error %d\n",
565ffd7c74aSJoe Talbott error);
566ffd7c74aSJoe Talbott goto fail;
567ffd7c74aSJoe Talbott }
568ffd7c74aSJoe Talbott
569da10ea93SMatthew Dillon /* Allocate TX scheduler "rings". */
570da10ea93SMatthew Dillon if ((error = iwn_alloc_sched(sc)) != 0) {
571ffd7c74aSJoe Talbott device_printf(dev,
572da10ea93SMatthew Dillon "could not allocate TX scheduler rings, error %d\n", error);
573da10ea93SMatthew Dillon goto fail;
574da10ea93SMatthew Dillon }
575da10ea93SMatthew Dillon
576da10ea93SMatthew Dillon /* Allocate TX rings (16 on 4965AGN, 20 on >=5000). */
577da10ea93SMatthew Dillon for (i = 0; i < sc->ntxqs; i++) {
578da10ea93SMatthew Dillon if ((error = iwn_alloc_tx_ring(sc, &sc->txq[i], i)) != 0) {
579da10ea93SMatthew Dillon device_printf(dev,
580da10ea93SMatthew Dillon "could not allocate TX ring %d, error %d\n", i,
581da10ea93SMatthew Dillon error);
582ffd7c74aSJoe Talbott goto fail;
583ffd7c74aSJoe Talbott }
584ffd7c74aSJoe Talbott }
585ffd7c74aSJoe Talbott
586ffd7c74aSJoe Talbott /* Allocate RX ring. */
587da10ea93SMatthew Dillon if ((error = iwn_alloc_rx_ring(sc, &sc->rxq)) != 0) {
588da10ea93SMatthew Dillon device_printf(dev, "could not allocate RX ring, error %d\n",
589da10ea93SMatthew Dillon error);
590ffd7c74aSJoe Talbott goto fail;
591ffd7c74aSJoe Talbott }
592ffd7c74aSJoe Talbott
593ffd7c74aSJoe Talbott /* Clear pending interrupts. */
594ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_INT, 0xffffffff);
595ffd7c74aSJoe Talbott
596977fc0dbSMatthew Dillon ic = &sc->sc_ic;
59713f98998SMatthew Dillon ic->ic_softc = sc;
5984f898719SImre Vadász ic->ic_name = device_get_nameunit(dev);
599da10ea93SMatthew Dillon ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
600da10ea93SMatthew Dillon ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */
601da10ea93SMatthew Dillon
602da10ea93SMatthew Dillon /* Set device capabilities. */
603da10ea93SMatthew Dillon ic->ic_caps =
604da10ea93SMatthew Dillon IEEE80211_C_STA /* station mode supported */
605da10ea93SMatthew Dillon | IEEE80211_C_MONITOR /* monitor mode supported */
606977fc0dbSMatthew Dillon #if 0
607da10ea93SMatthew Dillon | IEEE80211_C_BGSCAN /* background scanning */
608977fc0dbSMatthew Dillon #endif
609da10ea93SMatthew Dillon | IEEE80211_C_TXPMGT /* tx power management */
610da10ea93SMatthew Dillon | IEEE80211_C_SHSLOT /* short slot time supported */
611da10ea93SMatthew Dillon | IEEE80211_C_WPA
612da10ea93SMatthew Dillon | IEEE80211_C_SHPREAMBLE /* short preamble supported */
613da10ea93SMatthew Dillon #if 0
614da10ea93SMatthew Dillon | IEEE80211_C_IBSS /* ibss/adhoc mode */
615da10ea93SMatthew Dillon #endif
616da10ea93SMatthew Dillon | IEEE80211_C_WME /* WME */
617da10ea93SMatthew Dillon | IEEE80211_C_PMGT /* Station-side power mgmt */
618da10ea93SMatthew Dillon ;
619da10ea93SMatthew Dillon
620da10ea93SMatthew Dillon /* Read MAC address, channels, etc from EEPROM. */
621977fc0dbSMatthew Dillon if ((error = iwn_read_eeprom(sc, ic->ic_macaddr)) != 0) {
622da10ea93SMatthew Dillon device_printf(dev, "could not read EEPROM, error %d\n",
623da10ea93SMatthew Dillon error);
624da10ea93SMatthew Dillon goto fail;
625da10ea93SMatthew Dillon }
626da10ea93SMatthew Dillon
627ffd7c74aSJoe Talbott /* Count the number of available chains. */
628ffd7c74aSJoe Talbott sc->ntxchains =
629ffd7c74aSJoe Talbott ((sc->txchainmask >> 2) & 1) +
630ffd7c74aSJoe Talbott ((sc->txchainmask >> 1) & 1) +
631ffd7c74aSJoe Talbott ((sc->txchainmask >> 0) & 1);
632ffd7c74aSJoe Talbott sc->nrxchains =
633ffd7c74aSJoe Talbott ((sc->rxchainmask >> 2) & 1) +
634ffd7c74aSJoe Talbott ((sc->rxchainmask >> 1) & 1) +
635ffd7c74aSJoe Talbott ((sc->rxchainmask >> 0) & 1);
636da10ea93SMatthew Dillon if (bootverbose) {
637977fc0dbSMatthew Dillon #if defined(__DragonFly__)
638977fc0dbSMatthew Dillon char ethstr[ETHER_ADDRSTRLEN+1];
639f92fae3fSSascha Wildner device_printf(dev, "MIMO %dT%dR, %.4s, address %s\n",
640da10ea93SMatthew Dillon sc->ntxchains, sc->nrxchains, sc->eeprom_domain,
641977fc0dbSMatthew Dillon kether_ntoa(ic->ic_macaddr, ethstr));
642977fc0dbSMatthew Dillon #else
643977fc0dbSMatthew Dillon device_printf(dev, "MIMO %dT%dR, %.4s, address %6D\n",
644977fc0dbSMatthew Dillon sc->ntxchains, sc->nrxchains, sc->eeprom_domain,
645977fc0dbSMatthew Dillon ic->ic_macaddr, ":");
646977fc0dbSMatthew Dillon #endif
647ffd7c74aSJoe Talbott }
648ffd7c74aSJoe Talbott
649da10ea93SMatthew Dillon if (sc->sc_flags & IWN_FLAG_HAS_11N) {
650da10ea93SMatthew Dillon ic->ic_rxstream = sc->nrxchains;
651da10ea93SMatthew Dillon ic->ic_txstream = sc->ntxchains;
652ffd7c74aSJoe Talbott
653da10ea93SMatthew Dillon /*
65405538f72SMatthew Dillon * Some of the 3 antenna devices (ie, the 4965) only supports
65505538f72SMatthew Dillon * 2x2 operation. So correct the number of streams if
65605538f72SMatthew Dillon * it's not a 3-stream device.
657da10ea93SMatthew Dillon */
65805538f72SMatthew Dillon if (! iwn_is_3stream_device(sc)) {
659da10ea93SMatthew Dillon if (ic->ic_rxstream > 2)
660da10ea93SMatthew Dillon ic->ic_rxstream = 2;
661da10ea93SMatthew Dillon if (ic->ic_txstream > 2)
662da10ea93SMatthew Dillon ic->ic_txstream = 2;
66305538f72SMatthew Dillon }
664da10ea93SMatthew Dillon
665ffd7c74aSJoe Talbott ic->ic_htcaps =
666da10ea93SMatthew Dillon IEEE80211_HTCAP_SMPS_OFF /* SMPS mode disabled */
667ffd7c74aSJoe Talbott | IEEE80211_HTCAP_SHORTGI20 /* short GI in 20MHz */
668da10ea93SMatthew Dillon | IEEE80211_HTCAP_CHWIDTH40 /* 40MHz channel width*/
669ffd7c74aSJoe Talbott | IEEE80211_HTCAP_SHORTGI40 /* short GI in 40MHz */
670da10ea93SMatthew Dillon #ifdef notyet
671da10ea93SMatthew Dillon | IEEE80211_HTCAP_GREENFIELD
672da10ea93SMatthew Dillon #if IWN_RBUF_SIZE == 8192
673da10ea93SMatthew Dillon | IEEE80211_HTCAP_MAXAMSDU_7935 /* max A-MSDU length */
674da10ea93SMatthew Dillon #else
675ffd7c74aSJoe Talbott | IEEE80211_HTCAP_MAXAMSDU_3839 /* max A-MSDU length */
676da10ea93SMatthew Dillon #endif
677da10ea93SMatthew Dillon #endif
678ffd7c74aSJoe Talbott /* s/w capabilities */
679ffd7c74aSJoe Talbott | IEEE80211_HTC_HT /* HT operation */
680ffd7c74aSJoe Talbott | IEEE80211_HTC_AMPDU /* tx A-MPDU */
681da10ea93SMatthew Dillon #ifdef notyet
682ffd7c74aSJoe Talbott | IEEE80211_HTC_AMSDU /* tx A-MSDU */
683da10ea93SMatthew Dillon #endif
684ffd7c74aSJoe Talbott ;
685ffd7c74aSJoe Talbott }
686ffd7c74aSJoe Talbott
687977fc0dbSMatthew Dillon ieee80211_ifattach(ic);
688ffd7c74aSJoe Talbott ic->ic_vap_create = iwn_vap_create;
689977fc0dbSMatthew Dillon ic->ic_ioctl = iwn_ioctl;
690977fc0dbSMatthew Dillon ic->ic_parent = iwn_parent;
691ffd7c74aSJoe Talbott ic->ic_vap_delete = iwn_vap_delete;
692977fc0dbSMatthew Dillon ic->ic_transmit = iwn_transmit;
693ffd7c74aSJoe Talbott ic->ic_raw_xmit = iwn_raw_xmit;
694ffd7c74aSJoe Talbott ic->ic_node_alloc = iwn_node_alloc;
695da10ea93SMatthew Dillon sc->sc_ampdu_rx_start = ic->ic_ampdu_rx_start;
696da10ea93SMatthew Dillon ic->ic_ampdu_rx_start = iwn_ampdu_rx_start;
697da10ea93SMatthew Dillon sc->sc_ampdu_rx_stop = ic->ic_ampdu_rx_stop;
698da10ea93SMatthew Dillon ic->ic_ampdu_rx_stop = iwn_ampdu_rx_stop;
699da10ea93SMatthew Dillon sc->sc_addba_request = ic->ic_addba_request;
700da10ea93SMatthew Dillon ic->ic_addba_request = iwn_addba_request;
701da10ea93SMatthew Dillon sc->sc_addba_response = ic->ic_addba_response;
702da10ea93SMatthew Dillon ic->ic_addba_response = iwn_addba_response;
703da10ea93SMatthew Dillon sc->sc_addba_stop = ic->ic_addba_stop;
704da10ea93SMatthew Dillon ic->ic_addba_stop = iwn_ampdu_tx_stop;
705ffd7c74aSJoe Talbott ic->ic_newassoc = iwn_newassoc;
706da10ea93SMatthew Dillon ic->ic_wme.wme_update = iwn_updateedca;
707ffd7c74aSJoe Talbott ic->ic_update_mcast = iwn_update_mcast;
708ffd7c74aSJoe Talbott ic->ic_scan_start = iwn_scan_start;
709ffd7c74aSJoe Talbott ic->ic_scan_end = iwn_scan_end;
710ffd7c74aSJoe Talbott ic->ic_set_channel = iwn_set_channel;
711ffd7c74aSJoe Talbott ic->ic_scan_curchan = iwn_scan_curchan;
712ffd7c74aSJoe Talbott ic->ic_scan_mindwell = iwn_scan_mindwell;
713977fc0dbSMatthew Dillon ic->ic_getradiocaps = iwn_getradiocaps;
714ffd7c74aSJoe Talbott ic->ic_setregdomain = iwn_setregdomain;
715ffd7c74aSJoe Talbott
716ffd7c74aSJoe Talbott iwn_radiotap_attach(sc);
717da10ea93SMatthew Dillon
718977fc0dbSMatthew Dillon #if defined(__DragonFly__)
719977fc0dbSMatthew Dillon callout_init_lk(&sc->calib_to, &sc->sc_lk);
720977fc0dbSMatthew Dillon callout_init_lk(&sc->watchdog_to, &sc->sc_lk);
721977fc0dbSMatthew Dillon #else
72205538f72SMatthew Dillon callout_init_mtx(&sc->calib_to, &sc->sc_mtx, 0);
72305538f72SMatthew Dillon callout_init_mtx(&sc->watchdog_to, &sc->sc_mtx, 0);
724977fc0dbSMatthew Dillon #endif
72505538f72SMatthew Dillon TASK_INIT(&sc->sc_radioon_task, 0, iwn_radio_on, sc);
72605538f72SMatthew Dillon TASK_INIT(&sc->sc_radiooff_task, 0, iwn_radio_off, sc);
72705538f72SMatthew Dillon TASK_INIT(&sc->sc_panic_task, 0, iwn_panicked, sc);
728977fc0dbSMatthew Dillon TASK_INIT(&sc->sc_xmit_task, 0, iwn_xmit_task, sc);
729977fc0dbSMatthew Dillon
730977fc0dbSMatthew Dillon mbufq_init(&sc->sc_xmit_queue, 1024);
731fd49669cSMichael Neumann
732fd49669cSMichael Neumann sc->sc_tq = taskqueue_create("iwn_taskq", M_WAITOK,
733fd49669cSMichael Neumann taskqueue_thread_enqueue, &sc->sc_tq);
73405538f72SMatthew Dillon #if defined(__DragonFly__)
73505538f72SMatthew Dillon error = taskqueue_start_threads(&sc->sc_tq, 1, TDPRI_KERN_DAEMON,
73605538f72SMatthew Dillon -1, "iwn_taskq");
73705538f72SMatthew Dillon #else
73805538f72SMatthew Dillon error = taskqueue_start_threads(&sc->sc_tq, 1, 0, "iwn_taskq");
73905538f72SMatthew Dillon #endif
740fd49669cSMichael Neumann if (error != 0) {
741fd49669cSMichael Neumann device_printf(dev, "can't start threads, error %d\n", error);
742fd49669cSMichael Neumann goto fail;
743fd49669cSMichael Neumann }
744da10ea93SMatthew Dillon
745ffd7c74aSJoe Talbott iwn_sysctlattach(sc);
746ffd7c74aSJoe Talbott
747ffd7c74aSJoe Talbott /*
748ffd7c74aSJoe Talbott * Hook our interrupt after all initialization is complete.
749ffd7c74aSJoe Talbott */
75005538f72SMatthew Dillon #if defined(__DragonFly__)
751101554a6SMatthew Dillon error = bus_setup_intr(dev, sc->irq, INTR_MPSAFE,
752101554a6SMatthew Dillon iwn_intr, sc, &sc->sc_ih,
753101554a6SMatthew Dillon &wlan_global_serializer);
75405538f72SMatthew Dillon #else
75505538f72SMatthew Dillon error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET | INTR_MPSAFE,
75605538f72SMatthew Dillon NULL, iwn_intr, sc, &sc->sc_ih);
75705538f72SMatthew Dillon #endif
758ffd7c74aSJoe Talbott if (error != 0) {
759da10ea93SMatthew Dillon device_printf(dev, "can't establish interrupt, error %d\n",
760ffd7c74aSJoe Talbott error);
761ffd7c74aSJoe Talbott goto fail;
762ffd7c74aSJoe Talbott }
763ffd7c74aSJoe Talbott
764fd49669cSMichael Neumann #if 0
765fd49669cSMichael Neumann device_printf(sc->sc_dev, "%s: rx_stats=%d, rx_stats_bt=%d\n",
766fd49669cSMichael Neumann __func__,
767fd49669cSMichael Neumann sizeof(struct iwn_stats),
768fd49669cSMichael Neumann sizeof(struct iwn_stats_bt));
769fd49669cSMichael Neumann #endif
770fd49669cSMichael Neumann
771da10ea93SMatthew Dillon if (bootverbose)
772ffd7c74aSJoe Talbott ieee80211_announce(ic);
773da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
774977fc0dbSMatthew Dillon
775977fc0dbSMatthew Dillon /* Add debug ioctl right at the end */
776977fc0dbSMatthew Dillon sc->sc_cdev = make_dev(&iwn_cdevsw, device_get_unit(dev),
777977fc0dbSMatthew Dillon UID_ROOT, GID_WHEEL, 0600, "%s", device_get_nameunit(dev));
778977fc0dbSMatthew Dillon if (sc->sc_cdev == NULL) {
779977fc0dbSMatthew Dillon device_printf(dev, "failed to create debug character device\n");
780977fc0dbSMatthew Dillon } else {
781977fc0dbSMatthew Dillon sc->sc_cdev->si_drv1 = sc;
782977fc0dbSMatthew Dillon }
783ffd7c74aSJoe Talbott return 0;
784ffd7c74aSJoe Talbott fail:
78505538f72SMatthew Dillon iwn_detach(dev);
786da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end in error\n",__func__);
787ffd7c74aSJoe Talbott return error;
788ffd7c74aSJoe Talbott }
789ffd7c74aSJoe Talbott
790da10ea93SMatthew Dillon /*
791da10ea93SMatthew Dillon * Define specific configuration based on device id and subdevice id
792da10ea93SMatthew Dillon * pid : PCI device id
793da10ea93SMatthew Dillon */
794da10ea93SMatthew Dillon static int
iwn_config_specific(struct iwn_softc * sc,uint16_t pid)795da10ea93SMatthew Dillon iwn_config_specific(struct iwn_softc *sc, uint16_t pid)
796ffd7c74aSJoe Talbott {
797ffd7c74aSJoe Talbott
798da10ea93SMatthew Dillon switch (pid) {
799da10ea93SMatthew Dillon /* 4965 series */
800da10ea93SMatthew Dillon case IWN_DID_4965_1:
801da10ea93SMatthew Dillon case IWN_DID_4965_2:
802da10ea93SMatthew Dillon case IWN_DID_4965_3:
803da10ea93SMatthew Dillon case IWN_DID_4965_4:
804da10ea93SMatthew Dillon sc->base_params = &iwn4965_base_params;
805ffd7c74aSJoe Talbott sc->limits = &iwn4965_sensitivity_limits;
806ffd7c74aSJoe Talbott sc->fwname = "iwn4965fw";
807da10ea93SMatthew Dillon /* Override chains masks, ROM is known to be broken. */
808ffd7c74aSJoe Talbott sc->txchainmask = IWN_ANT_AB;
809ffd7c74aSJoe Talbott sc->rxchainmask = IWN_ANT_ABC;
810da10ea93SMatthew Dillon /* Enable normal btcoex */
811da10ea93SMatthew Dillon sc->sc_flags |= IWN_FLAG_BTCOEX;
812ffd7c74aSJoe Talbott break;
813da10ea93SMatthew Dillon /* 1000 Series */
814da10ea93SMatthew Dillon case IWN_DID_1000_1:
815da10ea93SMatthew Dillon case IWN_DID_1000_2:
816da10ea93SMatthew Dillon switch(sc->subdevice_id) {
817da10ea93SMatthew Dillon case IWN_SDID_1000_1:
818da10ea93SMatthew Dillon case IWN_SDID_1000_2:
819da10ea93SMatthew Dillon case IWN_SDID_1000_3:
820da10ea93SMatthew Dillon case IWN_SDID_1000_4:
821da10ea93SMatthew Dillon case IWN_SDID_1000_5:
822da10ea93SMatthew Dillon case IWN_SDID_1000_6:
823da10ea93SMatthew Dillon case IWN_SDID_1000_7:
824da10ea93SMatthew Dillon case IWN_SDID_1000_8:
825da10ea93SMatthew Dillon case IWN_SDID_1000_9:
826da10ea93SMatthew Dillon case IWN_SDID_1000_10:
827da10ea93SMatthew Dillon case IWN_SDID_1000_11:
828da10ea93SMatthew Dillon case IWN_SDID_1000_12:
829ffd7c74aSJoe Talbott sc->limits = &iwn1000_sensitivity_limits;
830da10ea93SMatthew Dillon sc->base_params = &iwn1000_base_params;
831ffd7c74aSJoe Talbott sc->fwname = "iwn1000fw";
832ffd7c74aSJoe Talbott break;
833da10ea93SMatthew Dillon default:
834da10ea93SMatthew Dillon device_printf(sc->sc_dev, "adapter type id : 0x%04x sub id :"
835da10ea93SMatthew Dillon "0x%04x rev %d not supported (subdevice)\n", pid,
836da10ea93SMatthew Dillon sc->subdevice_id,sc->hw_type);
837da10ea93SMatthew Dillon return ENOTSUP;
838da10ea93SMatthew Dillon }
839da10ea93SMatthew Dillon break;
840da10ea93SMatthew Dillon /* 6x00 Series */
841da10ea93SMatthew Dillon case IWN_DID_6x00_2:
842da10ea93SMatthew Dillon case IWN_DID_6x00_4:
843da10ea93SMatthew Dillon case IWN_DID_6x00_1:
844da10ea93SMatthew Dillon case IWN_DID_6x00_3:
845ffd7c74aSJoe Talbott sc->fwname = "iwn6000fw";
846da10ea93SMatthew Dillon sc->limits = &iwn6000_sensitivity_limits;
847da10ea93SMatthew Dillon switch(sc->subdevice_id) {
848da10ea93SMatthew Dillon case IWN_SDID_6x00_1:
849da10ea93SMatthew Dillon case IWN_SDID_6x00_2:
850da10ea93SMatthew Dillon case IWN_SDID_6x00_8:
851da10ea93SMatthew Dillon //iwl6000_3agn_cfg
852da10ea93SMatthew Dillon sc->base_params = &iwn_6000_base_params;
853da10ea93SMatthew Dillon break;
854da10ea93SMatthew Dillon case IWN_SDID_6x00_3:
855da10ea93SMatthew Dillon case IWN_SDID_6x00_6:
856da10ea93SMatthew Dillon case IWN_SDID_6x00_9:
857da10ea93SMatthew Dillon ////iwl6000i_2agn
858da10ea93SMatthew Dillon case IWN_SDID_6x00_4:
859da10ea93SMatthew Dillon case IWN_SDID_6x00_7:
860da10ea93SMatthew Dillon case IWN_SDID_6x00_10:
861da10ea93SMatthew Dillon //iwl6000i_2abg_cfg
862da10ea93SMatthew Dillon case IWN_SDID_6x00_5:
863da10ea93SMatthew Dillon //iwl6000i_2bg_cfg
864da10ea93SMatthew Dillon sc->base_params = &iwn_6000i_base_params;
865ffd7c74aSJoe Talbott sc->sc_flags |= IWN_FLAG_INTERNAL_PA;
866ffd7c74aSJoe Talbott sc->txchainmask = IWN_ANT_BC;
867ffd7c74aSJoe Talbott sc->rxchainmask = IWN_ANT_BC;
868ffd7c74aSJoe Talbott break;
869ffd7c74aSJoe Talbott default:
870da10ea93SMatthew Dillon device_printf(sc->sc_dev, "adapter type id : 0x%04x sub id :"
871da10ea93SMatthew Dillon "0x%04x rev %d not supported (subdevice)\n", pid,
872da10ea93SMatthew Dillon sc->subdevice_id,sc->hw_type);
873da10ea93SMatthew Dillon return ENOTSUP;
874da10ea93SMatthew Dillon }
875da10ea93SMatthew Dillon break;
876da10ea93SMatthew Dillon /* 6x05 Series */
877da10ea93SMatthew Dillon case IWN_DID_6x05_1:
878da10ea93SMatthew Dillon case IWN_DID_6x05_2:
879da10ea93SMatthew Dillon switch(sc->subdevice_id) {
880da10ea93SMatthew Dillon case IWN_SDID_6x05_1:
881da10ea93SMatthew Dillon case IWN_SDID_6x05_4:
882da10ea93SMatthew Dillon case IWN_SDID_6x05_6:
883da10ea93SMatthew Dillon //iwl6005_2agn_cfg
884da10ea93SMatthew Dillon case IWN_SDID_6x05_2:
885da10ea93SMatthew Dillon case IWN_SDID_6x05_5:
886da10ea93SMatthew Dillon case IWN_SDID_6x05_7:
887da10ea93SMatthew Dillon //iwl6005_2abg_cfg
888da10ea93SMatthew Dillon case IWN_SDID_6x05_3:
889da10ea93SMatthew Dillon //iwl6005_2bg_cfg
890da10ea93SMatthew Dillon case IWN_SDID_6x05_8:
891da10ea93SMatthew Dillon case IWN_SDID_6x05_9:
892da10ea93SMatthew Dillon //iwl6005_2agn_sff_cfg
893da10ea93SMatthew Dillon case IWN_SDID_6x05_10:
894da10ea93SMatthew Dillon //iwl6005_2agn_d_cfg
895da10ea93SMatthew Dillon case IWN_SDID_6x05_11:
896da10ea93SMatthew Dillon //iwl6005_2agn_mow1_cfg
897da10ea93SMatthew Dillon case IWN_SDID_6x05_12:
898da10ea93SMatthew Dillon //iwl6005_2agn_mow2_cfg
899da10ea93SMatthew Dillon sc->fwname = "iwn6000g2afw";
900da10ea93SMatthew Dillon sc->limits = &iwn6000_sensitivity_limits;
901da10ea93SMatthew Dillon sc->base_params = &iwn_6000g2_base_params;
902da10ea93SMatthew Dillon break;
903da10ea93SMatthew Dillon default:
904da10ea93SMatthew Dillon device_printf(sc->sc_dev, "adapter type id : 0x%04x sub id :"
905da10ea93SMatthew Dillon "0x%04x rev %d not supported (subdevice)\n", pid,
906da10ea93SMatthew Dillon sc->subdevice_id,sc->hw_type);
907da10ea93SMatthew Dillon return ENOTSUP;
908da10ea93SMatthew Dillon }
909da10ea93SMatthew Dillon break;
910da10ea93SMatthew Dillon /* 6x35 Series */
911da10ea93SMatthew Dillon case IWN_DID_6035_1:
912da10ea93SMatthew Dillon case IWN_DID_6035_2:
913da10ea93SMatthew Dillon switch(sc->subdevice_id) {
914da10ea93SMatthew Dillon case IWN_SDID_6035_1:
915da10ea93SMatthew Dillon case IWN_SDID_6035_2:
916da10ea93SMatthew Dillon case IWN_SDID_6035_3:
917da10ea93SMatthew Dillon case IWN_SDID_6035_4:
91836c2fb3eSTobias Heilig case IWN_SDID_6035_5:
919da10ea93SMatthew Dillon sc->fwname = "iwn6000g2bfw";
920fd49669cSMichael Neumann sc->limits = &iwn6235_sensitivity_limits;
921fd49669cSMichael Neumann sc->base_params = &iwn_6235_base_params;
922da10ea93SMatthew Dillon break;
923da10ea93SMatthew Dillon default:
924da10ea93SMatthew Dillon device_printf(sc->sc_dev, "adapter type id : 0x%04x sub id :"
925da10ea93SMatthew Dillon "0x%04x rev %d not supported (subdevice)\n", pid,
926da10ea93SMatthew Dillon sc->subdevice_id,sc->hw_type);
927da10ea93SMatthew Dillon return ENOTSUP;
928da10ea93SMatthew Dillon }
929da10ea93SMatthew Dillon break;
930da10ea93SMatthew Dillon /* 6x50 WiFi/WiMax Series */
931da10ea93SMatthew Dillon case IWN_DID_6050_1:
932da10ea93SMatthew Dillon case IWN_DID_6050_2:
933da10ea93SMatthew Dillon switch(sc->subdevice_id) {
934da10ea93SMatthew Dillon case IWN_SDID_6050_1:
935da10ea93SMatthew Dillon case IWN_SDID_6050_3:
936da10ea93SMatthew Dillon case IWN_SDID_6050_5:
937da10ea93SMatthew Dillon //iwl6050_2agn_cfg
938da10ea93SMatthew Dillon case IWN_SDID_6050_2:
939da10ea93SMatthew Dillon case IWN_SDID_6050_4:
940da10ea93SMatthew Dillon case IWN_SDID_6050_6:
941da10ea93SMatthew Dillon //iwl6050_2abg_cfg
942da10ea93SMatthew Dillon sc->fwname = "iwn6050fw";
943da10ea93SMatthew Dillon sc->txchainmask = IWN_ANT_AB;
944da10ea93SMatthew Dillon sc->rxchainmask = IWN_ANT_AB;
945da10ea93SMatthew Dillon sc->limits = &iwn6000_sensitivity_limits;
946da10ea93SMatthew Dillon sc->base_params = &iwn_6050_base_params;
947da10ea93SMatthew Dillon break;
948da10ea93SMatthew Dillon default:
949da10ea93SMatthew Dillon device_printf(sc->sc_dev, "adapter type id : 0x%04x sub id :"
950da10ea93SMatthew Dillon "0x%04x rev %d not supported (subdevice)\n", pid,
951da10ea93SMatthew Dillon sc->subdevice_id,sc->hw_type);
952da10ea93SMatthew Dillon return ENOTSUP;
953da10ea93SMatthew Dillon }
954da10ea93SMatthew Dillon break;
955da10ea93SMatthew Dillon /* 6150 WiFi/WiMax Series */
956da10ea93SMatthew Dillon case IWN_DID_6150_1:
957da10ea93SMatthew Dillon case IWN_DID_6150_2:
958da10ea93SMatthew Dillon switch(sc->subdevice_id) {
959da10ea93SMatthew Dillon case IWN_SDID_6150_1:
960da10ea93SMatthew Dillon case IWN_SDID_6150_3:
961da10ea93SMatthew Dillon case IWN_SDID_6150_5:
962da10ea93SMatthew Dillon // iwl6150_bgn_cfg
963da10ea93SMatthew Dillon case IWN_SDID_6150_2:
964da10ea93SMatthew Dillon case IWN_SDID_6150_4:
965da10ea93SMatthew Dillon case IWN_SDID_6150_6:
966da10ea93SMatthew Dillon //iwl6150_bg_cfg
967da10ea93SMatthew Dillon sc->fwname = "iwn6050fw";
968da10ea93SMatthew Dillon sc->limits = &iwn6000_sensitivity_limits;
969da10ea93SMatthew Dillon sc->base_params = &iwn_6150_base_params;
970da10ea93SMatthew Dillon break;
971da10ea93SMatthew Dillon default:
972da10ea93SMatthew Dillon device_printf(sc->sc_dev, "adapter type id : 0x%04x sub id :"
973da10ea93SMatthew Dillon "0x%04x rev %d not supported (subdevice)\n", pid,
974da10ea93SMatthew Dillon sc->subdevice_id,sc->hw_type);
975da10ea93SMatthew Dillon return ENOTSUP;
976da10ea93SMatthew Dillon }
977da10ea93SMatthew Dillon break;
978da10ea93SMatthew Dillon /* 6030 Series and 1030 Series */
979da10ea93SMatthew Dillon case IWN_DID_x030_1:
980da10ea93SMatthew Dillon case IWN_DID_x030_2:
981da10ea93SMatthew Dillon case IWN_DID_x030_3:
982da10ea93SMatthew Dillon case IWN_DID_x030_4:
983da10ea93SMatthew Dillon switch(sc->subdevice_id) {
984da10ea93SMatthew Dillon case IWN_SDID_x030_1:
985da10ea93SMatthew Dillon case IWN_SDID_x030_3:
986da10ea93SMatthew Dillon case IWN_SDID_x030_5:
987da10ea93SMatthew Dillon // iwl1030_bgn_cfg
988da10ea93SMatthew Dillon case IWN_SDID_x030_2:
989da10ea93SMatthew Dillon case IWN_SDID_x030_4:
990da10ea93SMatthew Dillon case IWN_SDID_x030_6:
991da10ea93SMatthew Dillon //iwl1030_bg_cfg
992da10ea93SMatthew Dillon case IWN_SDID_x030_7:
993da10ea93SMatthew Dillon case IWN_SDID_x030_10:
994da10ea93SMatthew Dillon case IWN_SDID_x030_14:
995da10ea93SMatthew Dillon //iwl6030_2agn_cfg
996da10ea93SMatthew Dillon case IWN_SDID_x030_8:
997da10ea93SMatthew Dillon case IWN_SDID_x030_11:
998da10ea93SMatthew Dillon case IWN_SDID_x030_15:
999da10ea93SMatthew Dillon // iwl6030_2bgn_cfg
1000da10ea93SMatthew Dillon case IWN_SDID_x030_9:
1001da10ea93SMatthew Dillon case IWN_SDID_x030_12:
1002da10ea93SMatthew Dillon case IWN_SDID_x030_16:
1003da10ea93SMatthew Dillon // iwl6030_2abg_cfg
1004da10ea93SMatthew Dillon case IWN_SDID_x030_13:
1005da10ea93SMatthew Dillon //iwl6030_2bg_cfg
1006da10ea93SMatthew Dillon sc->fwname = "iwn6000g2bfw";
1007da10ea93SMatthew Dillon sc->limits = &iwn6000_sensitivity_limits;
1008da10ea93SMatthew Dillon sc->base_params = &iwn_6000g2b_base_params;
1009da10ea93SMatthew Dillon break;
1010da10ea93SMatthew Dillon default:
1011da10ea93SMatthew Dillon device_printf(sc->sc_dev, "adapter type id : 0x%04x sub id :"
1012da10ea93SMatthew Dillon "0x%04x rev %d not supported (subdevice)\n", pid,
1013da10ea93SMatthew Dillon sc->subdevice_id,sc->hw_type);
1014da10ea93SMatthew Dillon return ENOTSUP;
1015da10ea93SMatthew Dillon }
1016da10ea93SMatthew Dillon break;
1017da10ea93SMatthew Dillon /* 130 Series WiFi */
1018da10ea93SMatthew Dillon /* XXX: This series will need adjustment for rate.
1019da10ea93SMatthew Dillon * see rx_with_siso_diversity in linux kernel
1020da10ea93SMatthew Dillon */
1021da10ea93SMatthew Dillon case IWN_DID_130_1:
1022da10ea93SMatthew Dillon case IWN_DID_130_2:
1023da10ea93SMatthew Dillon switch(sc->subdevice_id) {
1024da10ea93SMatthew Dillon case IWN_SDID_130_1:
1025da10ea93SMatthew Dillon case IWN_SDID_130_3:
1026da10ea93SMatthew Dillon case IWN_SDID_130_5:
1027da10ea93SMatthew Dillon //iwl130_bgn_cfg
1028da10ea93SMatthew Dillon case IWN_SDID_130_2:
1029da10ea93SMatthew Dillon case IWN_SDID_130_4:
1030da10ea93SMatthew Dillon case IWN_SDID_130_6:
1031da10ea93SMatthew Dillon //iwl130_bg_cfg
1032da10ea93SMatthew Dillon sc->fwname = "iwn6000g2bfw";
1033da10ea93SMatthew Dillon sc->limits = &iwn6000_sensitivity_limits;
1034da10ea93SMatthew Dillon sc->base_params = &iwn_6000g2b_base_params;
1035da10ea93SMatthew Dillon break;
1036da10ea93SMatthew Dillon default:
1037da10ea93SMatthew Dillon device_printf(sc->sc_dev, "adapter type id : 0x%04x sub id :"
1038da10ea93SMatthew Dillon "0x%04x rev %d not supported (subdevice)\n", pid,
1039da10ea93SMatthew Dillon sc->subdevice_id,sc->hw_type);
1040da10ea93SMatthew Dillon return ENOTSUP;
1041da10ea93SMatthew Dillon }
1042da10ea93SMatthew Dillon break;
1043da10ea93SMatthew Dillon /* 100 Series WiFi */
1044da10ea93SMatthew Dillon case IWN_DID_100_1:
1045da10ea93SMatthew Dillon case IWN_DID_100_2:
1046da10ea93SMatthew Dillon switch(sc->subdevice_id) {
1047da10ea93SMatthew Dillon case IWN_SDID_100_1:
1048da10ea93SMatthew Dillon case IWN_SDID_100_2:
1049da10ea93SMatthew Dillon case IWN_SDID_100_3:
1050da10ea93SMatthew Dillon case IWN_SDID_100_4:
1051da10ea93SMatthew Dillon case IWN_SDID_100_5:
1052da10ea93SMatthew Dillon case IWN_SDID_100_6:
1053da10ea93SMatthew Dillon sc->limits = &iwn1000_sensitivity_limits;
1054da10ea93SMatthew Dillon sc->base_params = &iwn1000_base_params;
1055da10ea93SMatthew Dillon sc->fwname = "iwn100fw";
1056da10ea93SMatthew Dillon break;
1057da10ea93SMatthew Dillon default:
1058da10ea93SMatthew Dillon device_printf(sc->sc_dev, "adapter type id : 0x%04x sub id :"
1059da10ea93SMatthew Dillon "0x%04x rev %d not supported (subdevice)\n", pid,
1060da10ea93SMatthew Dillon sc->subdevice_id,sc->hw_type);
1061da10ea93SMatthew Dillon return ENOTSUP;
1062da10ea93SMatthew Dillon }
1063da10ea93SMatthew Dillon break;
1064da10ea93SMatthew Dillon
1065fd49669cSMichael Neumann /* 105 Series */
1066fd49669cSMichael Neumann /* XXX: This series will need adjustment for rate.
1067fd49669cSMichael Neumann * see rx_with_siso_diversity in linux kernel
1068fd49669cSMichael Neumann */
1069fd49669cSMichael Neumann case IWN_DID_105_1:
1070fd49669cSMichael Neumann case IWN_DID_105_2:
1071fd49669cSMichael Neumann switch(sc->subdevice_id) {
1072fd49669cSMichael Neumann case IWN_SDID_105_1:
1073fd49669cSMichael Neumann case IWN_SDID_105_2:
1074fd49669cSMichael Neumann case IWN_SDID_105_3:
1075fd49669cSMichael Neumann //iwl105_bgn_cfg
1076fd49669cSMichael Neumann case IWN_SDID_105_4:
1077fd49669cSMichael Neumann //iwl105_bgn_d_cfg
1078fd49669cSMichael Neumann sc->limits = &iwn2030_sensitivity_limits;
1079fd49669cSMichael Neumann sc->base_params = &iwn2000_base_params;
1080fd49669cSMichael Neumann sc->fwname = "iwn105fw";
1081fd49669cSMichael Neumann break;
1082fd49669cSMichael Neumann default:
1083fd49669cSMichael Neumann device_printf(sc->sc_dev, "adapter type id : 0x%04x sub id :"
1084fd49669cSMichael Neumann "0x%04x rev %d not supported (subdevice)\n", pid,
1085fd49669cSMichael Neumann sc->subdevice_id,sc->hw_type);
1086fd49669cSMichael Neumann return ENOTSUP;
1087fd49669cSMichael Neumann }
1088fd49669cSMichael Neumann break;
1089fd49669cSMichael Neumann
1090fd49669cSMichael Neumann /* 135 Series */
1091fd49669cSMichael Neumann /* XXX: This series will need adjustment for rate.
1092fd49669cSMichael Neumann * see rx_with_siso_diversity in linux kernel
1093fd49669cSMichael Neumann */
1094fd49669cSMichael Neumann case IWN_DID_135_1:
1095fd49669cSMichael Neumann case IWN_DID_135_2:
1096fd49669cSMichael Neumann switch(sc->subdevice_id) {
1097fd49669cSMichael Neumann case IWN_SDID_135_1:
1098fd49669cSMichael Neumann case IWN_SDID_135_2:
1099fd49669cSMichael Neumann case IWN_SDID_135_3:
1100fd49669cSMichael Neumann sc->limits = &iwn2030_sensitivity_limits;
1101fd49669cSMichael Neumann sc->base_params = &iwn2030_base_params;
1102fd49669cSMichael Neumann sc->fwname = "iwn135fw";
1103fd49669cSMichael Neumann break;
1104fd49669cSMichael Neumann default:
1105fd49669cSMichael Neumann device_printf(sc->sc_dev, "adapter type id : 0x%04x sub id :"
1106fd49669cSMichael Neumann "0x%04x rev %d not supported (subdevice)\n", pid,
1107fd49669cSMichael Neumann sc->subdevice_id,sc->hw_type);
1108fd49669cSMichael Neumann return ENOTSUP;
1109fd49669cSMichael Neumann }
1110fd49669cSMichael Neumann break;
1111fd49669cSMichael Neumann
1112da10ea93SMatthew Dillon /* 2x00 Series */
1113da10ea93SMatthew Dillon case IWN_DID_2x00_1:
1114da10ea93SMatthew Dillon case IWN_DID_2x00_2:
1115da10ea93SMatthew Dillon switch(sc->subdevice_id) {
1116da10ea93SMatthew Dillon case IWN_SDID_2x00_1:
1117da10ea93SMatthew Dillon case IWN_SDID_2x00_2:
1118da10ea93SMatthew Dillon case IWN_SDID_2x00_3:
1119da10ea93SMatthew Dillon //iwl2000_2bgn_cfg
1120da10ea93SMatthew Dillon case IWN_SDID_2x00_4:
1121da10ea93SMatthew Dillon //iwl2000_2bgn_d_cfg
1122da10ea93SMatthew Dillon sc->limits = &iwn2030_sensitivity_limits;
1123da10ea93SMatthew Dillon sc->base_params = &iwn2000_base_params;
1124da10ea93SMatthew Dillon sc->fwname = "iwn2000fw";
1125da10ea93SMatthew Dillon break;
1126da10ea93SMatthew Dillon default:
1127da10ea93SMatthew Dillon device_printf(sc->sc_dev, "adapter type id : 0x%04x sub id :"
1128da10ea93SMatthew Dillon "0x%04x rev %d not supported (subdevice) \n",
1129da10ea93SMatthew Dillon pid, sc->subdevice_id, sc->hw_type);
1130da10ea93SMatthew Dillon return ENOTSUP;
1131da10ea93SMatthew Dillon }
1132da10ea93SMatthew Dillon break;
1133da10ea93SMatthew Dillon /* 2x30 Series */
1134da10ea93SMatthew Dillon case IWN_DID_2x30_1:
1135da10ea93SMatthew Dillon case IWN_DID_2x30_2:
1136da10ea93SMatthew Dillon switch(sc->subdevice_id) {
1137da10ea93SMatthew Dillon case IWN_SDID_2x30_1:
1138da10ea93SMatthew Dillon case IWN_SDID_2x30_3:
1139da10ea93SMatthew Dillon case IWN_SDID_2x30_5:
1140da10ea93SMatthew Dillon //iwl100_bgn_cfg
1141da10ea93SMatthew Dillon case IWN_SDID_2x30_2:
1142da10ea93SMatthew Dillon case IWN_SDID_2x30_4:
1143da10ea93SMatthew Dillon case IWN_SDID_2x30_6:
1144da10ea93SMatthew Dillon //iwl100_bg_cfg
1145da10ea93SMatthew Dillon sc->limits = &iwn2030_sensitivity_limits;
1146da10ea93SMatthew Dillon sc->base_params = &iwn2030_base_params;
1147da10ea93SMatthew Dillon sc->fwname = "iwn2030fw";
1148da10ea93SMatthew Dillon break;
1149da10ea93SMatthew Dillon default:
1150da10ea93SMatthew Dillon device_printf(sc->sc_dev, "adapter type id : 0x%04x sub id :"
1151da10ea93SMatthew Dillon "0x%04x rev %d not supported (subdevice)\n", pid,
1152da10ea93SMatthew Dillon sc->subdevice_id,sc->hw_type);
1153da10ea93SMatthew Dillon return ENOTSUP;
1154da10ea93SMatthew Dillon }
1155da10ea93SMatthew Dillon break;
1156da10ea93SMatthew Dillon /* 5x00 Series */
1157da10ea93SMatthew Dillon case IWN_DID_5x00_1:
1158da10ea93SMatthew Dillon case IWN_DID_5x00_2:
1159da10ea93SMatthew Dillon case IWN_DID_5x00_3:
1160da10ea93SMatthew Dillon case IWN_DID_5x00_4:
1161da10ea93SMatthew Dillon sc->limits = &iwn5000_sensitivity_limits;
1162da10ea93SMatthew Dillon sc->base_params = &iwn5000_base_params;
1163da10ea93SMatthew Dillon sc->fwname = "iwn5000fw";
1164da10ea93SMatthew Dillon switch(sc->subdevice_id) {
1165da10ea93SMatthew Dillon case IWN_SDID_5x00_1:
1166da10ea93SMatthew Dillon case IWN_SDID_5x00_2:
1167da10ea93SMatthew Dillon case IWN_SDID_5x00_3:
1168da10ea93SMatthew Dillon case IWN_SDID_5x00_4:
1169da10ea93SMatthew Dillon case IWN_SDID_5x00_9:
1170da10ea93SMatthew Dillon case IWN_SDID_5x00_10:
1171da10ea93SMatthew Dillon case IWN_SDID_5x00_11:
1172da10ea93SMatthew Dillon case IWN_SDID_5x00_12:
1173da10ea93SMatthew Dillon case IWN_SDID_5x00_17:
1174da10ea93SMatthew Dillon case IWN_SDID_5x00_18:
1175da10ea93SMatthew Dillon case IWN_SDID_5x00_19:
1176da10ea93SMatthew Dillon case IWN_SDID_5x00_20:
1177da10ea93SMatthew Dillon //iwl5100_agn_cfg
1178da10ea93SMatthew Dillon sc->txchainmask = IWN_ANT_B;
1179da10ea93SMatthew Dillon sc->rxchainmask = IWN_ANT_AB;
1180da10ea93SMatthew Dillon break;
1181da10ea93SMatthew Dillon case IWN_SDID_5x00_5:
1182da10ea93SMatthew Dillon case IWN_SDID_5x00_6:
1183da10ea93SMatthew Dillon case IWN_SDID_5x00_13:
1184da10ea93SMatthew Dillon case IWN_SDID_5x00_14:
1185da10ea93SMatthew Dillon case IWN_SDID_5x00_21:
1186da10ea93SMatthew Dillon case IWN_SDID_5x00_22:
1187da10ea93SMatthew Dillon //iwl5100_bgn_cfg
1188da10ea93SMatthew Dillon sc->txchainmask = IWN_ANT_B;
1189da10ea93SMatthew Dillon sc->rxchainmask = IWN_ANT_AB;
1190da10ea93SMatthew Dillon break;
1191da10ea93SMatthew Dillon case IWN_SDID_5x00_7:
1192da10ea93SMatthew Dillon case IWN_SDID_5x00_8:
1193da10ea93SMatthew Dillon case IWN_SDID_5x00_15:
1194da10ea93SMatthew Dillon case IWN_SDID_5x00_16:
1195da10ea93SMatthew Dillon case IWN_SDID_5x00_23:
1196da10ea93SMatthew Dillon case IWN_SDID_5x00_24:
1197da10ea93SMatthew Dillon //iwl5100_abg_cfg
1198da10ea93SMatthew Dillon sc->txchainmask = IWN_ANT_B;
1199da10ea93SMatthew Dillon sc->rxchainmask = IWN_ANT_AB;
1200da10ea93SMatthew Dillon break;
1201da10ea93SMatthew Dillon case IWN_SDID_5x00_25:
1202da10ea93SMatthew Dillon case IWN_SDID_5x00_26:
1203da10ea93SMatthew Dillon case IWN_SDID_5x00_27:
1204da10ea93SMatthew Dillon case IWN_SDID_5x00_28:
1205da10ea93SMatthew Dillon case IWN_SDID_5x00_29:
1206da10ea93SMatthew Dillon case IWN_SDID_5x00_30:
1207da10ea93SMatthew Dillon case IWN_SDID_5x00_31:
1208da10ea93SMatthew Dillon case IWN_SDID_5x00_32:
1209da10ea93SMatthew Dillon case IWN_SDID_5x00_33:
1210da10ea93SMatthew Dillon case IWN_SDID_5x00_34:
1211da10ea93SMatthew Dillon case IWN_SDID_5x00_35:
1212da10ea93SMatthew Dillon case IWN_SDID_5x00_36:
1213da10ea93SMatthew Dillon //iwl5300_agn_cfg
1214ffd7c74aSJoe Talbott sc->txchainmask = IWN_ANT_ABC;
1215ffd7c74aSJoe Talbott sc->rxchainmask = IWN_ANT_ABC;
1216ffd7c74aSJoe Talbott break;
1217da10ea93SMatthew Dillon default:
1218da10ea93SMatthew Dillon device_printf(sc->sc_dev, "adapter type id : 0x%04x sub id :"
1219da10ea93SMatthew Dillon "0x%04x rev %d not supported (subdevice)\n", pid,
1220da10ea93SMatthew Dillon sc->subdevice_id,sc->hw_type);
1221da10ea93SMatthew Dillon return ENOTSUP;
1222ffd7c74aSJoe Talbott }
1223ffd7c74aSJoe Talbott break;
1224da10ea93SMatthew Dillon /* 5x50 Series */
1225da10ea93SMatthew Dillon case IWN_DID_5x50_1:
1226da10ea93SMatthew Dillon case IWN_DID_5x50_2:
1227da10ea93SMatthew Dillon case IWN_DID_5x50_3:
1228da10ea93SMatthew Dillon case IWN_DID_5x50_4:
1229da10ea93SMatthew Dillon sc->limits = &iwn5000_sensitivity_limits;
1230da10ea93SMatthew Dillon sc->base_params = &iwn5000_base_params;
1231da10ea93SMatthew Dillon sc->fwname = "iwn5000fw";
1232da10ea93SMatthew Dillon switch(sc->subdevice_id) {
1233da10ea93SMatthew Dillon case IWN_SDID_5x50_1:
1234da10ea93SMatthew Dillon case IWN_SDID_5x50_2:
1235da10ea93SMatthew Dillon case IWN_SDID_5x50_3:
1236da10ea93SMatthew Dillon //iwl5350_agn_cfg
1237da10ea93SMatthew Dillon sc->limits = &iwn5000_sensitivity_limits;
1238da10ea93SMatthew Dillon sc->base_params = &iwn5000_base_params;
1239da10ea93SMatthew Dillon sc->fwname = "iwn5000fw";
1240da10ea93SMatthew Dillon break;
1241da10ea93SMatthew Dillon case IWN_SDID_5x50_4:
1242da10ea93SMatthew Dillon case IWN_SDID_5x50_5:
1243da10ea93SMatthew Dillon case IWN_SDID_5x50_8:
1244da10ea93SMatthew Dillon case IWN_SDID_5x50_9:
1245da10ea93SMatthew Dillon case IWN_SDID_5x50_10:
1246da10ea93SMatthew Dillon case IWN_SDID_5x50_11:
1247da10ea93SMatthew Dillon //iwl5150_agn_cfg
1248da10ea93SMatthew Dillon case IWN_SDID_5x50_6:
1249da10ea93SMatthew Dillon case IWN_SDID_5x50_7:
1250da10ea93SMatthew Dillon case IWN_SDID_5x50_12:
1251da10ea93SMatthew Dillon case IWN_SDID_5x50_13:
1252da10ea93SMatthew Dillon //iwl5150_abg_cfg
1253da10ea93SMatthew Dillon sc->limits = &iwn5000_sensitivity_limits;
1254da10ea93SMatthew Dillon sc->fwname = "iwn5150fw";
1255da10ea93SMatthew Dillon sc->base_params = &iwn_5x50_base_params;
1256ffd7c74aSJoe Talbott break;
1257ffd7c74aSJoe Talbott default:
1258da10ea93SMatthew Dillon device_printf(sc->sc_dev, "adapter type id : 0x%04x sub id :"
1259da10ea93SMatthew Dillon "0x%04x rev %d not supported (subdevice)\n", pid,
1260da10ea93SMatthew Dillon sc->subdevice_id,sc->hw_type);
1261da10ea93SMatthew Dillon return ENOTSUP;
1262ffd7c74aSJoe Talbott }
1263da10ea93SMatthew Dillon break;
1264da10ea93SMatthew Dillon default:
1265da10ea93SMatthew Dillon device_printf(sc->sc_dev, "adapter type id : 0x%04x sub id : 0x%04x"
1266da10ea93SMatthew Dillon "rev 0x%08x not supported (device)\n", pid, sc->subdevice_id,
1267da10ea93SMatthew Dillon sc->hw_type);
1268da10ea93SMatthew Dillon return ENOTSUP;
1269da10ea93SMatthew Dillon }
1270da10ea93SMatthew Dillon return 0;
1271da10ea93SMatthew Dillon }
1272da10ea93SMatthew Dillon
1273da10ea93SMatthew Dillon static int
iwn4965_attach(struct iwn_softc * sc,uint16_t pid)1274da10ea93SMatthew Dillon iwn4965_attach(struct iwn_softc *sc, uint16_t pid)
1275da10ea93SMatthew Dillon {
1276da10ea93SMatthew Dillon struct iwn_ops *ops = &sc->ops;
1277da10ea93SMatthew Dillon
1278da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
1279da10ea93SMatthew Dillon ops->load_firmware = iwn4965_load_firmware;
1280da10ea93SMatthew Dillon ops->read_eeprom = iwn4965_read_eeprom;
1281da10ea93SMatthew Dillon ops->post_alive = iwn4965_post_alive;
1282da10ea93SMatthew Dillon ops->nic_config = iwn4965_nic_config;
1283da10ea93SMatthew Dillon ops->update_sched = iwn4965_update_sched;
1284da10ea93SMatthew Dillon ops->get_temperature = iwn4965_get_temperature;
1285da10ea93SMatthew Dillon ops->get_rssi = iwn4965_get_rssi;
1286da10ea93SMatthew Dillon ops->set_txpower = iwn4965_set_txpower;
1287da10ea93SMatthew Dillon ops->init_gains = iwn4965_init_gains;
1288da10ea93SMatthew Dillon ops->set_gains = iwn4965_set_gains;
1289da10ea93SMatthew Dillon ops->add_node = iwn4965_add_node;
1290da10ea93SMatthew Dillon ops->tx_done = iwn4965_tx_done;
1291da10ea93SMatthew Dillon ops->ampdu_tx_start = iwn4965_ampdu_tx_start;
1292da10ea93SMatthew Dillon ops->ampdu_tx_stop = iwn4965_ampdu_tx_stop;
1293da10ea93SMatthew Dillon sc->ntxqs = IWN4965_NTXQUEUES;
1294da10ea93SMatthew Dillon sc->firstaggqueue = IWN4965_FIRSTAGGQUEUE;
1295da10ea93SMatthew Dillon sc->ndmachnls = IWN4965_NDMACHNLS;
1296da10ea93SMatthew Dillon sc->broadcast_id = IWN4965_ID_BROADCAST;
1297da10ea93SMatthew Dillon sc->rxonsz = IWN4965_RXONSZ;
1298da10ea93SMatthew Dillon sc->schedsz = IWN4965_SCHEDSZ;
1299da10ea93SMatthew Dillon sc->fw_text_maxsz = IWN4965_FW_TEXT_MAXSZ;
1300da10ea93SMatthew Dillon sc->fw_data_maxsz = IWN4965_FW_DATA_MAXSZ;
1301da10ea93SMatthew Dillon sc->fwsz = IWN4965_FWSZ;
1302da10ea93SMatthew Dillon sc->sched_txfact_addr = IWN4965_SCHED_TXFACT;
1303da10ea93SMatthew Dillon sc->limits = &iwn4965_sensitivity_limits;
1304da10ea93SMatthew Dillon sc->fwname = "iwn4965fw";
1305da10ea93SMatthew Dillon /* Override chains masks, ROM is known to be broken. */
1306da10ea93SMatthew Dillon sc->txchainmask = IWN_ANT_AB;
1307da10ea93SMatthew Dillon sc->rxchainmask = IWN_ANT_ABC;
1308da10ea93SMatthew Dillon /* Enable normal btcoex */
1309da10ea93SMatthew Dillon sc->sc_flags |= IWN_FLAG_BTCOEX;
1310da10ea93SMatthew Dillon
1311da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "%s: end\n",__func__);
1312da10ea93SMatthew Dillon
1313da10ea93SMatthew Dillon return 0;
1314da10ea93SMatthew Dillon }
1315da10ea93SMatthew Dillon
1316da10ea93SMatthew Dillon static int
iwn5000_attach(struct iwn_softc * sc,uint16_t pid)1317da10ea93SMatthew Dillon iwn5000_attach(struct iwn_softc *sc, uint16_t pid)
1318da10ea93SMatthew Dillon {
1319da10ea93SMatthew Dillon struct iwn_ops *ops = &sc->ops;
1320da10ea93SMatthew Dillon
1321da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
1322da10ea93SMatthew Dillon
1323da10ea93SMatthew Dillon ops->load_firmware = iwn5000_load_firmware;
1324da10ea93SMatthew Dillon ops->read_eeprom = iwn5000_read_eeprom;
1325da10ea93SMatthew Dillon ops->post_alive = iwn5000_post_alive;
1326da10ea93SMatthew Dillon ops->nic_config = iwn5000_nic_config;
1327da10ea93SMatthew Dillon ops->update_sched = iwn5000_update_sched;
1328da10ea93SMatthew Dillon ops->get_temperature = iwn5000_get_temperature;
1329da10ea93SMatthew Dillon ops->get_rssi = iwn5000_get_rssi;
1330da10ea93SMatthew Dillon ops->set_txpower = iwn5000_set_txpower;
1331da10ea93SMatthew Dillon ops->init_gains = iwn5000_init_gains;
1332da10ea93SMatthew Dillon ops->set_gains = iwn5000_set_gains;
1333da10ea93SMatthew Dillon ops->add_node = iwn5000_add_node;
1334da10ea93SMatthew Dillon ops->tx_done = iwn5000_tx_done;
1335da10ea93SMatthew Dillon ops->ampdu_tx_start = iwn5000_ampdu_tx_start;
1336da10ea93SMatthew Dillon ops->ampdu_tx_stop = iwn5000_ampdu_tx_stop;
1337da10ea93SMatthew Dillon sc->ntxqs = IWN5000_NTXQUEUES;
1338da10ea93SMatthew Dillon sc->firstaggqueue = IWN5000_FIRSTAGGQUEUE;
1339da10ea93SMatthew Dillon sc->ndmachnls = IWN5000_NDMACHNLS;
1340da10ea93SMatthew Dillon sc->broadcast_id = IWN5000_ID_BROADCAST;
1341da10ea93SMatthew Dillon sc->rxonsz = IWN5000_RXONSZ;
1342da10ea93SMatthew Dillon sc->schedsz = IWN5000_SCHEDSZ;
1343da10ea93SMatthew Dillon sc->fw_text_maxsz = IWN5000_FW_TEXT_MAXSZ;
1344da10ea93SMatthew Dillon sc->fw_data_maxsz = IWN5000_FW_DATA_MAXSZ;
1345da10ea93SMatthew Dillon sc->fwsz = IWN5000_FWSZ;
1346da10ea93SMatthew Dillon sc->sched_txfact_addr = IWN5000_SCHED_TXFACT;
1347da10ea93SMatthew Dillon sc->reset_noise_gain = IWN5000_PHY_CALIB_RESET_NOISE_GAIN;
1348da10ea93SMatthew Dillon sc->noise_gain = IWN5000_PHY_CALIB_NOISE_GAIN;
1349da10ea93SMatthew Dillon
1350da10ea93SMatthew Dillon return 0;
1351ffd7c74aSJoe Talbott }
1352ffd7c74aSJoe Talbott
1353ffd7c74aSJoe Talbott /*
1354ffd7c74aSJoe Talbott * Attach the interface to 802.11 radiotap.
1355ffd7c74aSJoe Talbott */
1356ffd7c74aSJoe Talbott static void
iwn_radiotap_attach(struct iwn_softc * sc)1357ffd7c74aSJoe Talbott iwn_radiotap_attach(struct iwn_softc *sc)
1358ffd7c74aSJoe Talbott {
1359977fc0dbSMatthew Dillon
1360da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
1361977fc0dbSMatthew Dillon ieee80211_radiotap_attach(&sc->sc_ic,
1362ffd7c74aSJoe Talbott &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap),
1363ffd7c74aSJoe Talbott IWN_TX_RADIOTAP_PRESENT,
1364ffd7c74aSJoe Talbott &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap),
1365ffd7c74aSJoe Talbott IWN_RX_RADIOTAP_PRESENT);
1366da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s end\n", __func__);
1367da10ea93SMatthew Dillon }
1368da10ea93SMatthew Dillon
1369da10ea93SMatthew Dillon static void
iwn_sysctlattach(struct iwn_softc * sc)1370da10ea93SMatthew Dillon iwn_sysctlattach(struct iwn_softc *sc)
1371da10ea93SMatthew Dillon {
1372da10ea93SMatthew Dillon #ifdef IWN_DEBUG
137305538f72SMatthew Dillon struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev);
137405538f72SMatthew Dillon struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev);
1375da10ea93SMatthew Dillon
1376da10ea93SMatthew Dillon SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
1377da10ea93SMatthew Dillon "debug", CTLFLAG_RW, &sc->sc_debug, sc->sc_debug,
1378da10ea93SMatthew Dillon "control debugging printfs");
1379da10ea93SMatthew Dillon #endif
1380ffd7c74aSJoe Talbott }
1381ffd7c74aSJoe Talbott
1382ffd7c74aSJoe Talbott static struct ieee80211vap *
iwn_vap_create(struct ieee80211com * ic,const char name[IFNAMSIZ],int unit,enum ieee80211_opmode opmode,int flags,const uint8_t bssid[IEEE80211_ADDR_LEN],const uint8_t mac[IEEE80211_ADDR_LEN])1383da10ea93SMatthew Dillon iwn_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit,
138418ef6e46SSascha Wildner enum ieee80211_opmode opmode, int flags,
1385ffd7c74aSJoe Talbott const uint8_t bssid[IEEE80211_ADDR_LEN],
1386ffd7c74aSJoe Talbott const uint8_t mac[IEEE80211_ADDR_LEN])
1387ffd7c74aSJoe Talbott {
1388977fc0dbSMatthew Dillon struct iwn_softc *sc = ic->ic_softc;
1389ffd7c74aSJoe Talbott struct iwn_vap *ivp;
1390ffd7c74aSJoe Talbott struct ieee80211vap *vap;
1391ffd7c74aSJoe Talbott
1392ffd7c74aSJoe Talbott if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */
1393ffd7c74aSJoe Talbott return NULL;
1394da10ea93SMatthew Dillon
1395977fc0dbSMatthew Dillon ivp = kmalloc(sizeof(struct iwn_vap), M_80211_VAP, M_WAITOK | M_ZERO);
1396ffd7c74aSJoe Talbott vap = &ivp->iv_vap;
1397977fc0dbSMatthew Dillon ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid);
1398da10ea93SMatthew Dillon ivp->ctx = IWN_RXON_BSS_CTX;
1399ffd7c74aSJoe Talbott vap->iv_bmissthreshold = 10; /* override default */
1400ffd7c74aSJoe Talbott /* Override with driver methods. */
1401ffd7c74aSJoe Talbott ivp->iv_newstate = vap->iv_newstate;
1402ffd7c74aSJoe Talbott vap->iv_newstate = iwn_newstate;
1403da10ea93SMatthew Dillon sc->ivap[IWN_RXON_BSS_CTX] = vap;
1404ffd7c74aSJoe Talbott
1405ffd7c74aSJoe Talbott ieee80211_ratectl_init(vap);
1406ffd7c74aSJoe Talbott /* Complete setup. */
1407977fc0dbSMatthew Dillon ieee80211_vap_attach(vap, iwn_media_change, ieee80211_media_status,
1408977fc0dbSMatthew Dillon mac);
1409ffd7c74aSJoe Talbott ic->ic_opmode = opmode;
1410ffd7c74aSJoe Talbott return vap;
1411ffd7c74aSJoe Talbott }
1412ffd7c74aSJoe Talbott
1413ffd7c74aSJoe Talbott static void
iwn_vap_delete(struct ieee80211vap * vap)1414ffd7c74aSJoe Talbott iwn_vap_delete(struct ieee80211vap *vap)
1415ffd7c74aSJoe Talbott {
1416ffd7c74aSJoe Talbott struct iwn_vap *ivp = IWN_VAP(vap);
1417ffd7c74aSJoe Talbott
1418ffd7c74aSJoe Talbott ieee80211_ratectl_deinit(vap);
1419ffd7c74aSJoe Talbott ieee80211_vap_detach(vap);
1420101554a6SMatthew Dillon kfree(ivp, M_80211_VAP);
1421ffd7c74aSJoe Talbott }
1422ffd7c74aSJoe Talbott
1423977fc0dbSMatthew Dillon static void
iwn_xmit_queue_drain(struct iwn_softc * sc)1424977fc0dbSMatthew Dillon iwn_xmit_queue_drain(struct iwn_softc *sc)
1425977fc0dbSMatthew Dillon {
1426977fc0dbSMatthew Dillon struct mbuf *m;
1427977fc0dbSMatthew Dillon struct ieee80211_node *ni;
1428977fc0dbSMatthew Dillon
1429977fc0dbSMatthew Dillon IWN_LOCK_ASSERT(sc);
1430977fc0dbSMatthew Dillon while ((m = mbufq_dequeue(&sc->sc_xmit_queue)) != NULL) {
1431977fc0dbSMatthew Dillon ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
1432977fc0dbSMatthew Dillon ieee80211_free_node(ni);
1433977fc0dbSMatthew Dillon m_freem(m);
1434977fc0dbSMatthew Dillon }
1435977fc0dbSMatthew Dillon }
1436977fc0dbSMatthew Dillon
1437977fc0dbSMatthew Dillon static int
iwn_xmit_queue_enqueue(struct iwn_softc * sc,struct mbuf * m)1438977fc0dbSMatthew Dillon iwn_xmit_queue_enqueue(struct iwn_softc *sc, struct mbuf *m)
1439977fc0dbSMatthew Dillon {
1440977fc0dbSMatthew Dillon
1441977fc0dbSMatthew Dillon IWN_LOCK_ASSERT(sc);
1442977fc0dbSMatthew Dillon return (mbufq_enqueue(&sc->sc_xmit_queue, m));
1443977fc0dbSMatthew Dillon }
1444977fc0dbSMatthew Dillon
1445ffd7c74aSJoe Talbott static int
iwn_detach(device_t dev)144605538f72SMatthew Dillon iwn_detach(device_t dev)
1447ffd7c74aSJoe Talbott {
1448ffd7c74aSJoe Talbott struct iwn_softc *sc = device_get_softc(dev);
1449da10ea93SMatthew Dillon int qid;
1450da10ea93SMatthew Dillon
1451da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
1452ffd7c74aSJoe Talbott
1453977fc0dbSMatthew Dillon if (sc->sc_ic.ic_softc != NULL) {
1454977fc0dbSMatthew Dillon /* Free the mbuf queue and node references */
1455977fc0dbSMatthew Dillon IWN_LOCK(sc);
1456977fc0dbSMatthew Dillon iwn_xmit_queue_drain(sc);
1457977fc0dbSMatthew Dillon IWN_UNLOCK(sc);
1458ffd7c74aSJoe Talbott
1459977fc0dbSMatthew Dillon ieee80211_draintask(&sc->sc_ic, &sc->sc_radioon_task);
1460977fc0dbSMatthew Dillon ieee80211_draintask(&sc->sc_ic, &sc->sc_radiooff_task);
146105538f72SMatthew Dillon iwn_stop(sc);
1462fd49669cSMichael Neumann
146305538f72SMatthew Dillon #if defined(__DragonFly__)
146405538f72SMatthew Dillon /* doesn't exist for DFly, DFly drains tasks on free */
146505538f72SMatthew Dillon #else
1466fd49669cSMichael Neumann taskqueue_drain_all(sc->sc_tq);
1467fd49669cSMichael Neumann #endif
1468fd49669cSMichael Neumann taskqueue_free(sc->sc_tq);
1469fd49669cSMichael Neumann
147005538f72SMatthew Dillon callout_drain(&sc->watchdog_to);
147105538f72SMatthew Dillon callout_drain(&sc->calib_to);
1472977fc0dbSMatthew Dillon ieee80211_ifdetach(&sc->sc_ic);
1473ffd7c74aSJoe Talbott }
1474ffd7c74aSJoe Talbott
1475da10ea93SMatthew Dillon /* Uninstall interrupt handler. */
1476ffd7c74aSJoe Talbott if (sc->irq != NULL) {
1477ffd7c74aSJoe Talbott bus_teardown_intr(dev, sc->irq, sc->sc_ih);
1478fd49669cSMichael Neumann bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(sc->irq),
1479fd49669cSMichael Neumann sc->irq);
1480ffd7c74aSJoe Talbott pci_release_msi(dev);
1481ffd7c74aSJoe Talbott }
1482ffd7c74aSJoe Talbott
1483da10ea93SMatthew Dillon /* Free DMA resources. */
1484da10ea93SMatthew Dillon iwn_free_rx_ring(sc, &sc->rxq);
1485da10ea93SMatthew Dillon for (qid = 0; qid < sc->ntxqs; qid++)
1486da10ea93SMatthew Dillon iwn_free_tx_ring(sc, &sc->txq[qid]);
1487da10ea93SMatthew Dillon iwn_free_sched(sc);
1488da10ea93SMatthew Dillon iwn_free_kw(sc);
148905538f72SMatthew Dillon if (sc->ict != NULL)
1490da10ea93SMatthew Dillon iwn_free_ict(sc);
1491da10ea93SMatthew Dillon iwn_free_fwmem(sc);
1492da10ea93SMatthew Dillon
149305538f72SMatthew Dillon if (sc->mem != NULL)
1494fd49669cSMichael Neumann bus_release_resource(dev, SYS_RES_MEMORY,
1495fd49669cSMichael Neumann rman_get_rid(sc->mem), sc->mem);
1496ffd7c74aSJoe Talbott
1497977fc0dbSMatthew Dillon if (sc->sc_cdev) {
1498977fc0dbSMatthew Dillon destroy_dev(sc->sc_cdev);
1499977fc0dbSMatthew Dillon sc->sc_cdev = NULL;
1500977fc0dbSMatthew Dillon }
1501ffd7c74aSJoe Talbott
1502da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n", __func__);
150305538f72SMatthew Dillon IWN_LOCK_DESTROY(sc);
1504ffd7c74aSJoe Talbott return 0;
1505ffd7c74aSJoe Talbott }
1506ffd7c74aSJoe Talbott
1507ffd7c74aSJoe Talbott static int
iwn_shutdown(device_t dev)150805538f72SMatthew Dillon iwn_shutdown(device_t dev)
1509ffd7c74aSJoe Talbott {
1510da10ea93SMatthew Dillon struct iwn_softc *sc = device_get_softc(dev);
15113db796acSJoe Talbott
151205538f72SMatthew Dillon iwn_stop(sc);
1513da10ea93SMatthew Dillon return 0;
1514da10ea93SMatthew Dillon }
1515458fc9cfSMatthew Dillon
1516da10ea93SMatthew Dillon static int
iwn_suspend(device_t dev)151705538f72SMatthew Dillon iwn_suspend(device_t dev)
1518da10ea93SMatthew Dillon {
1519da10ea93SMatthew Dillon struct iwn_softc *sc = device_get_softc(dev);
1520da10ea93SMatthew Dillon
1521977fc0dbSMatthew Dillon ieee80211_suspend_all(&sc->sc_ic);
1522da10ea93SMatthew Dillon return 0;
1523da10ea93SMatthew Dillon }
1524da10ea93SMatthew Dillon
1525da10ea93SMatthew Dillon static int
iwn_resume(device_t dev)152605538f72SMatthew Dillon iwn_resume(device_t dev)
1527da10ea93SMatthew Dillon {
1528da10ea93SMatthew Dillon struct iwn_softc *sc = device_get_softc(dev);
1529da10ea93SMatthew Dillon
1530da10ea93SMatthew Dillon /* Clear device-specific "PCI retry timeout" register (41h). */
1531da10ea93SMatthew Dillon pci_write_config(dev, 0x41, 0, 1);
1532da10ea93SMatthew Dillon
1533977fc0dbSMatthew Dillon ieee80211_resume_all(&sc->sc_ic);
1534ffd7c74aSJoe Talbott return 0;
1535ffd7c74aSJoe Talbott }
1536ffd7c74aSJoe Talbott
1537ffd7c74aSJoe Talbott static int
iwn_nic_lock(struct iwn_softc * sc)1538ffd7c74aSJoe Talbott iwn_nic_lock(struct iwn_softc *sc)
1539ffd7c74aSJoe Talbott {
1540ffd7c74aSJoe Talbott int ntries;
1541ffd7c74aSJoe Talbott
1542ffd7c74aSJoe Talbott /* Request exclusive access to NIC. */
1543ffd7c74aSJoe Talbott IWN_SETBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_MAC_ACCESS_REQ);
1544ffd7c74aSJoe Talbott
1545ffd7c74aSJoe Talbott /* Spin until we actually get the lock. */
1546ffd7c74aSJoe Talbott for (ntries = 0; ntries < 1000; ntries++) {
1547ffd7c74aSJoe Talbott if ((IWN_READ(sc, IWN_GP_CNTRL) &
1548ffd7c74aSJoe Talbott (IWN_GP_CNTRL_MAC_ACCESS_ENA | IWN_GP_CNTRL_SLEEP)) ==
1549ffd7c74aSJoe Talbott IWN_GP_CNTRL_MAC_ACCESS_ENA)
1550ffd7c74aSJoe Talbott return 0;
1551ffd7c74aSJoe Talbott DELAY(10);
1552ffd7c74aSJoe Talbott }
1553ffd7c74aSJoe Talbott return ETIMEDOUT;
1554ffd7c74aSJoe Talbott }
1555ffd7c74aSJoe Talbott
1556ffd7c74aSJoe Talbott static __inline void
iwn_nic_unlock(struct iwn_softc * sc)1557ffd7c74aSJoe Talbott iwn_nic_unlock(struct iwn_softc *sc)
1558ffd7c74aSJoe Talbott {
1559ffd7c74aSJoe Talbott IWN_CLRBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_MAC_ACCESS_REQ);
1560ffd7c74aSJoe Talbott }
1561ffd7c74aSJoe Talbott
1562ffd7c74aSJoe Talbott static __inline uint32_t
iwn_prph_read(struct iwn_softc * sc,uint32_t addr)1563ffd7c74aSJoe Talbott iwn_prph_read(struct iwn_softc *sc, uint32_t addr)
1564ffd7c74aSJoe Talbott {
1565ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_PRPH_RADDR, IWN_PRPH_DWORD | addr);
1566ffd7c74aSJoe Talbott IWN_BARRIER_READ_WRITE(sc);
1567ffd7c74aSJoe Talbott return IWN_READ(sc, IWN_PRPH_RDATA);
1568ffd7c74aSJoe Talbott }
1569ffd7c74aSJoe Talbott
1570ffd7c74aSJoe Talbott static __inline void
iwn_prph_write(struct iwn_softc * sc,uint32_t addr,uint32_t data)1571ffd7c74aSJoe Talbott iwn_prph_write(struct iwn_softc *sc, uint32_t addr, uint32_t data)
1572ffd7c74aSJoe Talbott {
1573ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_PRPH_WADDR, IWN_PRPH_DWORD | addr);
1574ffd7c74aSJoe Talbott IWN_BARRIER_WRITE(sc);
1575ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_PRPH_WDATA, data);
1576ffd7c74aSJoe Talbott }
1577ffd7c74aSJoe Talbott
1578ffd7c74aSJoe Talbott static __inline void
iwn_prph_setbits(struct iwn_softc * sc,uint32_t addr,uint32_t mask)1579ffd7c74aSJoe Talbott iwn_prph_setbits(struct iwn_softc *sc, uint32_t addr, uint32_t mask)
1580ffd7c74aSJoe Talbott {
1581ffd7c74aSJoe Talbott iwn_prph_write(sc, addr, iwn_prph_read(sc, addr) | mask);
1582ffd7c74aSJoe Talbott }
1583ffd7c74aSJoe Talbott
1584ffd7c74aSJoe Talbott static __inline void
iwn_prph_clrbits(struct iwn_softc * sc,uint32_t addr,uint32_t mask)1585ffd7c74aSJoe Talbott iwn_prph_clrbits(struct iwn_softc *sc, uint32_t addr, uint32_t mask)
1586ffd7c74aSJoe Talbott {
1587ffd7c74aSJoe Talbott iwn_prph_write(sc, addr, iwn_prph_read(sc, addr) & ~mask);
1588ffd7c74aSJoe Talbott }
1589ffd7c74aSJoe Talbott
1590ffd7c74aSJoe Talbott static __inline void
iwn_prph_write_region_4(struct iwn_softc * sc,uint32_t addr,const uint32_t * data,int count)1591ffd7c74aSJoe Talbott iwn_prph_write_region_4(struct iwn_softc *sc, uint32_t addr,
1592ffd7c74aSJoe Talbott const uint32_t *data, int count)
1593ffd7c74aSJoe Talbott {
1594ffd7c74aSJoe Talbott for (; count > 0; count--, data++, addr += 4)
1595ffd7c74aSJoe Talbott iwn_prph_write(sc, addr, *data);
1596ffd7c74aSJoe Talbott }
1597ffd7c74aSJoe Talbott
1598ffd7c74aSJoe Talbott static __inline uint32_t
iwn_mem_read(struct iwn_softc * sc,uint32_t addr)1599ffd7c74aSJoe Talbott iwn_mem_read(struct iwn_softc *sc, uint32_t addr)
1600ffd7c74aSJoe Talbott {
1601ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_MEM_RADDR, addr);
1602ffd7c74aSJoe Talbott IWN_BARRIER_READ_WRITE(sc);
1603ffd7c74aSJoe Talbott return IWN_READ(sc, IWN_MEM_RDATA);
1604ffd7c74aSJoe Talbott }
1605ffd7c74aSJoe Talbott
1606ffd7c74aSJoe Talbott static __inline void
iwn_mem_write(struct iwn_softc * sc,uint32_t addr,uint32_t data)1607ffd7c74aSJoe Talbott iwn_mem_write(struct iwn_softc *sc, uint32_t addr, uint32_t data)
1608ffd7c74aSJoe Talbott {
1609ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_MEM_WADDR, addr);
1610ffd7c74aSJoe Talbott IWN_BARRIER_WRITE(sc);
1611ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_MEM_WDATA, data);
1612ffd7c74aSJoe Talbott }
1613ffd7c74aSJoe Talbott
1614ffd7c74aSJoe Talbott static __inline void
iwn_mem_write_2(struct iwn_softc * sc,uint32_t addr,uint16_t data)1615ffd7c74aSJoe Talbott iwn_mem_write_2(struct iwn_softc *sc, uint32_t addr, uint16_t data)
1616ffd7c74aSJoe Talbott {
1617ffd7c74aSJoe Talbott uint32_t tmp;
1618ffd7c74aSJoe Talbott
1619ffd7c74aSJoe Talbott tmp = iwn_mem_read(sc, addr & ~3);
1620ffd7c74aSJoe Talbott if (addr & 3)
1621ffd7c74aSJoe Talbott tmp = (tmp & 0x0000ffff) | data << 16;
1622ffd7c74aSJoe Talbott else
1623ffd7c74aSJoe Talbott tmp = (tmp & 0xffff0000) | data;
1624ffd7c74aSJoe Talbott iwn_mem_write(sc, addr & ~3, tmp);
1625ffd7c74aSJoe Talbott }
1626ffd7c74aSJoe Talbott
1627ffd7c74aSJoe Talbott static __inline void
iwn_mem_read_region_4(struct iwn_softc * sc,uint32_t addr,uint32_t * data,int count)1628ffd7c74aSJoe Talbott iwn_mem_read_region_4(struct iwn_softc *sc, uint32_t addr, uint32_t *data,
1629ffd7c74aSJoe Talbott int count)
1630ffd7c74aSJoe Talbott {
1631ffd7c74aSJoe Talbott for (; count > 0; count--, addr += 4)
1632ffd7c74aSJoe Talbott *data++ = iwn_mem_read(sc, addr);
1633ffd7c74aSJoe Talbott }
1634ffd7c74aSJoe Talbott
1635ffd7c74aSJoe Talbott static __inline void
iwn_mem_set_region_4(struct iwn_softc * sc,uint32_t addr,uint32_t val,int count)1636ffd7c74aSJoe Talbott iwn_mem_set_region_4(struct iwn_softc *sc, uint32_t addr, uint32_t val,
1637ffd7c74aSJoe Talbott int count)
1638ffd7c74aSJoe Talbott {
1639ffd7c74aSJoe Talbott for (; count > 0; count--, addr += 4)
1640ffd7c74aSJoe Talbott iwn_mem_write(sc, addr, val);
1641ffd7c74aSJoe Talbott }
1642ffd7c74aSJoe Talbott
1643ffd7c74aSJoe Talbott static int
iwn_eeprom_lock(struct iwn_softc * sc)1644ffd7c74aSJoe Talbott iwn_eeprom_lock(struct iwn_softc *sc)
1645ffd7c74aSJoe Talbott {
1646ffd7c74aSJoe Talbott int i, ntries;
1647ffd7c74aSJoe Talbott
1648ffd7c74aSJoe Talbott for (i = 0; i < 100; i++) {
1649ffd7c74aSJoe Talbott /* Request exclusive access to EEPROM. */
1650ffd7c74aSJoe Talbott IWN_SETBITS(sc, IWN_HW_IF_CONFIG,
1651ffd7c74aSJoe Talbott IWN_HW_IF_CONFIG_EEPROM_LOCKED);
1652ffd7c74aSJoe Talbott
1653ffd7c74aSJoe Talbott /* Spin until we actually get the lock. */
1654ffd7c74aSJoe Talbott for (ntries = 0; ntries < 100; ntries++) {
1655ffd7c74aSJoe Talbott if (IWN_READ(sc, IWN_HW_IF_CONFIG) &
1656ffd7c74aSJoe Talbott IWN_HW_IF_CONFIG_EEPROM_LOCKED)
1657ffd7c74aSJoe Talbott return 0;
1658ffd7c74aSJoe Talbott DELAY(10);
1659ffd7c74aSJoe Talbott }
1660ffd7c74aSJoe Talbott }
1661da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s end timeout\n", __func__);
1662ffd7c74aSJoe Talbott return ETIMEDOUT;
1663ffd7c74aSJoe Talbott }
1664ffd7c74aSJoe Talbott
1665ffd7c74aSJoe Talbott static __inline void
iwn_eeprom_unlock(struct iwn_softc * sc)1666ffd7c74aSJoe Talbott iwn_eeprom_unlock(struct iwn_softc *sc)
1667ffd7c74aSJoe Talbott {
1668ffd7c74aSJoe Talbott IWN_CLRBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_EEPROM_LOCKED);
1669ffd7c74aSJoe Talbott }
1670ffd7c74aSJoe Talbott
1671ffd7c74aSJoe Talbott /*
1672ffd7c74aSJoe Talbott * Initialize access by host to One Time Programmable ROM.
1673ffd7c74aSJoe Talbott * NB: This kind of ROM can be found on 1000 or 6000 Series only.
1674ffd7c74aSJoe Talbott */
1675ffd7c74aSJoe Talbott static int
iwn_init_otprom(struct iwn_softc * sc)1676ffd7c74aSJoe Talbott iwn_init_otprom(struct iwn_softc *sc)
1677ffd7c74aSJoe Talbott {
1678ffd7c74aSJoe Talbott uint16_t prev, base, next;
1679ffd7c74aSJoe Talbott int count, error;
1680ffd7c74aSJoe Talbott
1681da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
1682da10ea93SMatthew Dillon
1683ffd7c74aSJoe Talbott /* Wait for clock stabilization before accessing prph. */
1684da10ea93SMatthew Dillon if ((error = iwn_clock_wait(sc)) != 0)
1685ffd7c74aSJoe Talbott return error;
1686ffd7c74aSJoe Talbott
1687da10ea93SMatthew Dillon if ((error = iwn_nic_lock(sc)) != 0)
1688ffd7c74aSJoe Talbott return error;
1689ffd7c74aSJoe Talbott iwn_prph_setbits(sc, IWN_APMG_PS, IWN_APMG_PS_RESET_REQ);
1690ffd7c74aSJoe Talbott DELAY(5);
1691ffd7c74aSJoe Talbott iwn_prph_clrbits(sc, IWN_APMG_PS, IWN_APMG_PS_RESET_REQ);
1692ffd7c74aSJoe Talbott iwn_nic_unlock(sc);
1693ffd7c74aSJoe Talbott
1694ffd7c74aSJoe Talbott /* Set auto clock gate disable bit for HW with OTP shadow RAM. */
1695da10ea93SMatthew Dillon if (sc->base_params->shadow_ram_support) {
1696ffd7c74aSJoe Talbott IWN_SETBITS(sc, IWN_DBG_LINK_PWR_MGMT,
1697ffd7c74aSJoe Talbott IWN_RESET_LINK_PWR_MGMT_DIS);
1698ffd7c74aSJoe Talbott }
1699ffd7c74aSJoe Talbott IWN_CLRBITS(sc, IWN_EEPROM_GP, IWN_EEPROM_GP_IF_OWNER);
1700ffd7c74aSJoe Talbott /* Clear ECC status. */
1701ffd7c74aSJoe Talbott IWN_SETBITS(sc, IWN_OTP_GP,
1702ffd7c74aSJoe Talbott IWN_OTP_GP_ECC_CORR_STTS | IWN_OTP_GP_ECC_UNCORR_STTS);
1703ffd7c74aSJoe Talbott
1704ffd7c74aSJoe Talbott /*
1705ffd7c74aSJoe Talbott * Find the block before last block (contains the EEPROM image)
1706ffd7c74aSJoe Talbott * for HW without OTP shadow RAM.
1707ffd7c74aSJoe Talbott */
1708da10ea93SMatthew Dillon if (! sc->base_params->shadow_ram_support) {
1709ffd7c74aSJoe Talbott /* Switch to absolute addressing mode. */
1710ffd7c74aSJoe Talbott IWN_CLRBITS(sc, IWN_OTP_GP, IWN_OTP_GP_RELATIVE_ACCESS);
1711ffd7c74aSJoe Talbott base = prev = 0;
1712da10ea93SMatthew Dillon for (count = 0; count < sc->base_params->max_ll_items;
1713da10ea93SMatthew Dillon count++) {
1714ffd7c74aSJoe Talbott error = iwn_read_prom_data(sc, base, &next, 2);
1715ffd7c74aSJoe Talbott if (error != 0)
1716ffd7c74aSJoe Talbott return error;
1717ffd7c74aSJoe Talbott if (next == 0) /* End of linked-list. */
1718ffd7c74aSJoe Talbott break;
1719ffd7c74aSJoe Talbott prev = base;
1720ffd7c74aSJoe Talbott base = le16toh(next);
1721ffd7c74aSJoe Talbott }
1722da10ea93SMatthew Dillon if (count == 0 || count == sc->base_params->max_ll_items)
1723ffd7c74aSJoe Talbott return EIO;
1724ffd7c74aSJoe Talbott /* Skip "next" word. */
1725ffd7c74aSJoe Talbott sc->prom_base = prev + 1;
1726ffd7c74aSJoe Talbott }
1727da10ea93SMatthew Dillon
1728da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s end\n", __func__);
1729da10ea93SMatthew Dillon
1730ffd7c74aSJoe Talbott return 0;
1731ffd7c74aSJoe Talbott }
1732ffd7c74aSJoe Talbott
1733ffd7c74aSJoe Talbott static int
iwn_read_prom_data(struct iwn_softc * sc,uint32_t addr,void * data,int count)1734ffd7c74aSJoe Talbott iwn_read_prom_data(struct iwn_softc *sc, uint32_t addr, void *data, int count)
1735ffd7c74aSJoe Talbott {
1736da10ea93SMatthew Dillon uint8_t *out = data;
1737ffd7c74aSJoe Talbott uint32_t val, tmp;
1738ffd7c74aSJoe Talbott int ntries;
1739da10ea93SMatthew Dillon
1740da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
1741ffd7c74aSJoe Talbott
1742ffd7c74aSJoe Talbott addr += sc->prom_base;
1743ffd7c74aSJoe Talbott for (; count > 0; count -= 2, addr++) {
1744ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_EEPROM, addr << 2);
1745ffd7c74aSJoe Talbott for (ntries = 0; ntries < 10; ntries++) {
1746ffd7c74aSJoe Talbott val = IWN_READ(sc, IWN_EEPROM);
1747ffd7c74aSJoe Talbott if (val & IWN_EEPROM_READ_VALID)
1748ffd7c74aSJoe Talbott break;
1749ffd7c74aSJoe Talbott DELAY(5);
1750ffd7c74aSJoe Talbott }
1751ffd7c74aSJoe Talbott if (ntries == 10) {
1752ffd7c74aSJoe Talbott device_printf(sc->sc_dev,
1753ffd7c74aSJoe Talbott "timeout reading ROM at 0x%x\n", addr);
1754ffd7c74aSJoe Talbott return ETIMEDOUT;
1755ffd7c74aSJoe Talbott }
1756ffd7c74aSJoe Talbott if (sc->sc_flags & IWN_FLAG_HAS_OTPROM) {
1757ffd7c74aSJoe Talbott /* OTPROM, check for ECC errors. */
1758ffd7c74aSJoe Talbott tmp = IWN_READ(sc, IWN_OTP_GP);
1759ffd7c74aSJoe Talbott if (tmp & IWN_OTP_GP_ECC_UNCORR_STTS) {
1760ffd7c74aSJoe Talbott device_printf(sc->sc_dev,
1761ffd7c74aSJoe Talbott "OTPROM ECC error at 0x%x\n", addr);
1762ffd7c74aSJoe Talbott return EIO;
1763ffd7c74aSJoe Talbott }
1764ffd7c74aSJoe Talbott if (tmp & IWN_OTP_GP_ECC_CORR_STTS) {
1765ffd7c74aSJoe Talbott /* Correctable ECC error, clear bit. */
1766ffd7c74aSJoe Talbott IWN_SETBITS(sc, IWN_OTP_GP,
1767ffd7c74aSJoe Talbott IWN_OTP_GP_ECC_CORR_STTS);
1768ffd7c74aSJoe Talbott }
1769ffd7c74aSJoe Talbott }
1770ffd7c74aSJoe Talbott *out++ = val >> 16;
1771ffd7c74aSJoe Talbott if (count > 1)
1772ffd7c74aSJoe Talbott *out++ = val >> 24;
1773ffd7c74aSJoe Talbott }
1774da10ea93SMatthew Dillon
1775da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s end\n", __func__);
1776da10ea93SMatthew Dillon
1777ffd7c74aSJoe Talbott return 0;
1778ffd7c74aSJoe Talbott }
1779ffd7c74aSJoe Talbott
1780ffd7c74aSJoe Talbott static void
iwn_dma_map_addr(void * arg,bus_dma_segment_t * segs,int nsegs,int error)1781ffd7c74aSJoe Talbott iwn_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
1782ffd7c74aSJoe Talbott {
1783ffd7c74aSJoe Talbott if (error != 0)
1784ffd7c74aSJoe Talbott return;
1785ffd7c74aSJoe Talbott KASSERT(nsegs == 1, ("too many DMA segments, %d should be 1", nsegs));
1786ffd7c74aSJoe Talbott *(bus_addr_t *)arg = segs[0].ds_addr;
1787ffd7c74aSJoe Talbott }
1788ffd7c74aSJoe Talbott
1789ffd7c74aSJoe Talbott static int
iwn_dma_contig_alloc(struct iwn_softc * sc,struct iwn_dma_info * dma,void ** kvap,bus_size_t size,bus_size_t alignment)1790ffd7c74aSJoe Talbott iwn_dma_contig_alloc(struct iwn_softc *sc, struct iwn_dma_info *dma,
1791da10ea93SMatthew Dillon void **kvap, bus_size_t size, bus_size_t alignment)
1792ffd7c74aSJoe Talbott {
1793ffd7c74aSJoe Talbott int error;
1794ffd7c74aSJoe Talbott
1795ffd7c74aSJoe Talbott dma->tag = NULL;
1796da10ea93SMatthew Dillon dma->size = size;
1797ffd7c74aSJoe Talbott
179805538f72SMatthew Dillon #if defined(__DragonFly__)
179905538f72SMatthew Dillon error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), alignment,
1800*030b0c8cSMichael Neumann 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, size,
1801977fc0dbSMatthew Dillon 1, size, 0, &dma->tag);
180205538f72SMatthew Dillon #else
180305538f72SMatthew Dillon error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), alignment,
1804ffd7c74aSJoe Talbott 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, size,
1805977fc0dbSMatthew Dillon 1, size, 0, NULL, NULL, &dma->tag);
180605538f72SMatthew Dillon #endif
1807da10ea93SMatthew Dillon if (error != 0)
1808ffd7c74aSJoe Talbott goto fail;
1809da10ea93SMatthew Dillon
1810ffd7c74aSJoe Talbott error = bus_dmamem_alloc(dma->tag, (void **)&dma->vaddr,
1811da10ea93SMatthew Dillon BUS_DMA_NOWAIT | BUS_DMA_ZERO | BUS_DMA_COHERENT, &dma->map);
1812da10ea93SMatthew Dillon if (error != 0)
1813ffd7c74aSJoe Talbott goto fail;
1814da10ea93SMatthew Dillon
1815da10ea93SMatthew Dillon error = bus_dmamap_load(dma->tag, dma->map, dma->vaddr, size,
1816da10ea93SMatthew Dillon iwn_dma_map_addr, &dma->paddr, BUS_DMA_NOWAIT);
1817da10ea93SMatthew Dillon if (error != 0)
1818ffd7c74aSJoe Talbott goto fail;
1819da10ea93SMatthew Dillon
1820da10ea93SMatthew Dillon bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
1821ffd7c74aSJoe Talbott
1822ffd7c74aSJoe Talbott if (kvap != NULL)
1823ffd7c74aSJoe Talbott *kvap = dma->vaddr;
1824da10ea93SMatthew Dillon
1825ffd7c74aSJoe Talbott return 0;
1826da10ea93SMatthew Dillon
1827da10ea93SMatthew Dillon fail: iwn_dma_contig_free(dma);
1828ffd7c74aSJoe Talbott return error;
1829ffd7c74aSJoe Talbott }
1830ffd7c74aSJoe Talbott
1831ffd7c74aSJoe Talbott static void
iwn_dma_contig_free(struct iwn_dma_info * dma)1832ffd7c74aSJoe Talbott iwn_dma_contig_free(struct iwn_dma_info *dma)
1833ffd7c74aSJoe Talbott {
1834da10ea93SMatthew Dillon if (dma->vaddr != NULL) {
1835ffd7c74aSJoe Talbott bus_dmamap_sync(dma->tag, dma->map,
1836ffd7c74aSJoe Talbott BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
1837ffd7c74aSJoe Talbott bus_dmamap_unload(dma->tag, dma->map);
1838da10ea93SMatthew Dillon bus_dmamem_free(dma->tag, dma->vaddr, dma->map);
1839da10ea93SMatthew Dillon dma->vaddr = NULL;
1840ffd7c74aSJoe Talbott }
1841da10ea93SMatthew Dillon if (dma->tag != NULL) {
1842ffd7c74aSJoe Talbott bus_dma_tag_destroy(dma->tag);
1843da10ea93SMatthew Dillon dma->tag = NULL;
1844ffd7c74aSJoe Talbott }
1845ffd7c74aSJoe Talbott }
1846ffd7c74aSJoe Talbott
1847ffd7c74aSJoe Talbott static int
iwn_alloc_sched(struct iwn_softc * sc)1848ffd7c74aSJoe Talbott iwn_alloc_sched(struct iwn_softc *sc)
1849ffd7c74aSJoe Talbott {
1850ffd7c74aSJoe Talbott /* TX scheduler rings must be aligned on a 1KB boundary. */
1851da10ea93SMatthew Dillon return iwn_dma_contig_alloc(sc, &sc->sched_dma, (void **)&sc->sched,
1852da10ea93SMatthew Dillon sc->schedsz, 1024);
1853ffd7c74aSJoe Talbott }
1854ffd7c74aSJoe Talbott
1855ffd7c74aSJoe Talbott static void
iwn_free_sched(struct iwn_softc * sc)1856ffd7c74aSJoe Talbott iwn_free_sched(struct iwn_softc *sc)
1857ffd7c74aSJoe Talbott {
1858ffd7c74aSJoe Talbott iwn_dma_contig_free(&sc->sched_dma);
1859ffd7c74aSJoe Talbott }
1860ffd7c74aSJoe Talbott
1861ffd7c74aSJoe Talbott static int
iwn_alloc_kw(struct iwn_softc * sc)1862ffd7c74aSJoe Talbott iwn_alloc_kw(struct iwn_softc *sc)
1863ffd7c74aSJoe Talbott {
1864ffd7c74aSJoe Talbott /* "Keep Warm" page must be aligned on a 4KB boundary. */
1865da10ea93SMatthew Dillon return iwn_dma_contig_alloc(sc, &sc->kw_dma, NULL, 4096, 4096);
1866ffd7c74aSJoe Talbott }
1867ffd7c74aSJoe Talbott
1868ffd7c74aSJoe Talbott static void
iwn_free_kw(struct iwn_softc * sc)1869ffd7c74aSJoe Talbott iwn_free_kw(struct iwn_softc *sc)
1870ffd7c74aSJoe Talbott {
1871ffd7c74aSJoe Talbott iwn_dma_contig_free(&sc->kw_dma);
1872ffd7c74aSJoe Talbott }
1873ffd7c74aSJoe Talbott
1874ffd7c74aSJoe Talbott static int
iwn_alloc_ict(struct iwn_softc * sc)1875ffd7c74aSJoe Talbott iwn_alloc_ict(struct iwn_softc *sc)
1876ffd7c74aSJoe Talbott {
1877ffd7c74aSJoe Talbott /* ICT table must be aligned on a 4KB boundary. */
1878da10ea93SMatthew Dillon return iwn_dma_contig_alloc(sc, &sc->ict_dma, (void **)&sc->ict,
1879da10ea93SMatthew Dillon IWN_ICT_SIZE, 4096);
1880ffd7c74aSJoe Talbott }
1881ffd7c74aSJoe Talbott
1882ffd7c74aSJoe Talbott static void
iwn_free_ict(struct iwn_softc * sc)1883ffd7c74aSJoe Talbott iwn_free_ict(struct iwn_softc *sc)
1884ffd7c74aSJoe Talbott {
1885ffd7c74aSJoe Talbott iwn_dma_contig_free(&sc->ict_dma);
1886ffd7c74aSJoe Talbott }
1887ffd7c74aSJoe Talbott
1888ffd7c74aSJoe Talbott static int
iwn_alloc_fwmem(struct iwn_softc * sc)1889ffd7c74aSJoe Talbott iwn_alloc_fwmem(struct iwn_softc *sc)
1890ffd7c74aSJoe Talbott {
1891ffd7c74aSJoe Talbott /* Must be aligned on a 16-byte boundary. */
1892da10ea93SMatthew Dillon return iwn_dma_contig_alloc(sc, &sc->fw_dma, NULL, sc->fwsz, 16);
1893ffd7c74aSJoe Talbott }
1894ffd7c74aSJoe Talbott
1895ffd7c74aSJoe Talbott static void
iwn_free_fwmem(struct iwn_softc * sc)1896ffd7c74aSJoe Talbott iwn_free_fwmem(struct iwn_softc *sc)
1897ffd7c74aSJoe Talbott {
1898ffd7c74aSJoe Talbott iwn_dma_contig_free(&sc->fw_dma);
1899ffd7c74aSJoe Talbott }
1900ffd7c74aSJoe Talbott
1901ffd7c74aSJoe Talbott static int
iwn_alloc_rx_ring(struct iwn_softc * sc,struct iwn_rx_ring * ring)1902ffd7c74aSJoe Talbott iwn_alloc_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring)
1903ffd7c74aSJoe Talbott {
1904ffd7c74aSJoe Talbott bus_size_t size;
1905ffd7c74aSJoe Talbott int i, error;
1906ffd7c74aSJoe Talbott
1907ffd7c74aSJoe Talbott ring->cur = 0;
1908ffd7c74aSJoe Talbott
1909da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
1910da10ea93SMatthew Dillon
1911ffd7c74aSJoe Talbott /* Allocate RX descriptors (256-byte aligned). */
1912ffd7c74aSJoe Talbott size = IWN_RX_RING_COUNT * sizeof (uint32_t);
1913da10ea93SMatthew Dillon error = iwn_dma_contig_alloc(sc, &ring->desc_dma, (void **)&ring->desc,
1914da10ea93SMatthew Dillon size, 256);
1915ffd7c74aSJoe Talbott if (error != 0) {
1916ffd7c74aSJoe Talbott device_printf(sc->sc_dev,
1917da10ea93SMatthew Dillon "%s: could not allocate RX ring DMA memory, error %d\n",
1918ffd7c74aSJoe Talbott __func__, error);
1919ffd7c74aSJoe Talbott goto fail;
1920ffd7c74aSJoe Talbott }
1921ffd7c74aSJoe Talbott
1922ffd7c74aSJoe Talbott /* Allocate RX status area (16-byte aligned). */
1923da10ea93SMatthew Dillon error = iwn_dma_contig_alloc(sc, &ring->stat_dma, (void **)&ring->stat,
1924da10ea93SMatthew Dillon sizeof (struct iwn_rx_status), 16);
1925ffd7c74aSJoe Talbott if (error != 0) {
1926ffd7c74aSJoe Talbott device_printf(sc->sc_dev,
1927da10ea93SMatthew Dillon "%s: could not allocate RX status DMA memory, error %d\n",
1928da10ea93SMatthew Dillon __func__, error);
1929da10ea93SMatthew Dillon goto fail;
1930da10ea93SMatthew Dillon }
1931da10ea93SMatthew Dillon
1932da10ea93SMatthew Dillon /* Create RX buffer DMA tag. */
193305538f72SMatthew Dillon #if defined(__DragonFly__)
1934977fc0dbSMatthew Dillon error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0,
1935*030b0c8cSMichael Neumann BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR,
1936977fc0dbSMatthew Dillon IWN_RBUF_SIZE, 1, IWN_RBUF_SIZE, 0, &ring->data_dmat);
193705538f72SMatthew Dillon #else
193805538f72SMatthew Dillon error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0,
1939da10ea93SMatthew Dillon BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
1940977fc0dbSMatthew Dillon IWN_RBUF_SIZE, 1, IWN_RBUF_SIZE, 0, NULL, NULL, &ring->data_dmat);
194105538f72SMatthew Dillon #endif
1942da10ea93SMatthew Dillon if (error != 0) {
1943da10ea93SMatthew Dillon device_printf(sc->sc_dev,
1944da10ea93SMatthew Dillon "%s: could not create RX buf DMA tag, error %d\n",
1945ffd7c74aSJoe Talbott __func__, error);
1946ffd7c74aSJoe Talbott goto fail;
1947ffd7c74aSJoe Talbott }
1948ffd7c74aSJoe Talbott
1949ffd7c74aSJoe Talbott /*
1950ffd7c74aSJoe Talbott * Allocate and map RX buffers.
1951ffd7c74aSJoe Talbott */
1952ffd7c74aSJoe Talbott for (i = 0; i < IWN_RX_RING_COUNT; i++) {
1953ffd7c74aSJoe Talbott struct iwn_rx_data *data = &ring->data[i];
1954ffd7c74aSJoe Talbott bus_addr_t paddr;
1955ffd7c74aSJoe Talbott
1956ffd7c74aSJoe Talbott error = bus_dmamap_create(ring->data_dmat, 0, &data->map);
1957ffd7c74aSJoe Talbott if (error != 0) {
1958ffd7c74aSJoe Talbott device_printf(sc->sc_dev,
1959da10ea93SMatthew Dillon "%s: could not create RX buf DMA map, error %d\n",
1960ffd7c74aSJoe Talbott __func__, error);
1961ffd7c74aSJoe Talbott goto fail;
1962ffd7c74aSJoe Talbott }
1963ffd7c74aSJoe Talbott
1964b5523eacSSascha Wildner data->m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR,
196505538f72SMatthew Dillon IWN_RBUF_SIZE);
1966ffd7c74aSJoe Talbott if (data->m == NULL) {
1967ffd7c74aSJoe Talbott device_printf(sc->sc_dev,
1968da10ea93SMatthew Dillon "%s: could not allocate RX mbuf\n", __func__);
1969da10ea93SMatthew Dillon error = ENOBUFS;
1970ffd7c74aSJoe Talbott goto fail;
1971ffd7c74aSJoe Talbott }
1972ffd7c74aSJoe Talbott
1973ffd7c74aSJoe Talbott error = bus_dmamap_load(ring->data_dmat, data->map,
1974da10ea93SMatthew Dillon mtod(data->m, void *), IWN_RBUF_SIZE, iwn_dma_map_addr,
1975da10ea93SMatthew Dillon &paddr, BUS_DMA_NOWAIT);
1976ffd7c74aSJoe Talbott if (error != 0 && error != EFBIG) {
1977ffd7c74aSJoe Talbott device_printf(sc->sc_dev,
1978977fc0dbSMatthew Dillon "%s: can't map mbuf, error %d\n", __func__,
1979da10ea93SMatthew Dillon error);
1980ffd7c74aSJoe Talbott goto fail;
1981ffd7c74aSJoe Talbott }
1982ffd7c74aSJoe Talbott
1983ffd7c74aSJoe Talbott /* Set physical address of RX buffer (256-byte aligned). */
1984ffd7c74aSJoe Talbott ring->desc[i] = htole32(paddr >> 8);
1985ffd7c74aSJoe Talbott }
1986da10ea93SMatthew Dillon
1987ffd7c74aSJoe Talbott bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
1988ffd7c74aSJoe Talbott BUS_DMASYNC_PREWRITE);
1989da10ea93SMatthew Dillon
1990da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
1991da10ea93SMatthew Dillon
1992ffd7c74aSJoe Talbott return 0;
1993da10ea93SMatthew Dillon
1994da10ea93SMatthew Dillon fail: iwn_free_rx_ring(sc, ring);
1995da10ea93SMatthew Dillon
1996da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end in error\n",__func__);
1997da10ea93SMatthew Dillon
1998ffd7c74aSJoe Talbott return error;
1999ffd7c74aSJoe Talbott }
2000ffd7c74aSJoe Talbott
2001ffd7c74aSJoe Talbott static void
iwn_reset_rx_ring(struct iwn_softc * sc,struct iwn_rx_ring * ring)2002ffd7c74aSJoe Talbott iwn_reset_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring)
2003ffd7c74aSJoe Talbott {
2004ffd7c74aSJoe Talbott int ntries;
2005ffd7c74aSJoe Talbott
2006da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
2007da10ea93SMatthew Dillon
2008ffd7c74aSJoe Talbott if (iwn_nic_lock(sc) == 0) {
2009ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_FH_RX_CONFIG, 0);
2010ffd7c74aSJoe Talbott for (ntries = 0; ntries < 1000; ntries++) {
2011ffd7c74aSJoe Talbott if (IWN_READ(sc, IWN_FH_RX_STATUS) &
2012ffd7c74aSJoe Talbott IWN_FH_RX_STATUS_IDLE)
2013ffd7c74aSJoe Talbott break;
2014ffd7c74aSJoe Talbott DELAY(10);
2015ffd7c74aSJoe Talbott }
2016ffd7c74aSJoe Talbott iwn_nic_unlock(sc);
2017ffd7c74aSJoe Talbott }
2018ffd7c74aSJoe Talbott ring->cur = 0;
2019ffd7c74aSJoe Talbott sc->last_rx_valid = 0;
2020ffd7c74aSJoe Talbott }
2021ffd7c74aSJoe Talbott
2022ffd7c74aSJoe Talbott static void
iwn_free_rx_ring(struct iwn_softc * sc,struct iwn_rx_ring * ring)2023ffd7c74aSJoe Talbott iwn_free_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring)
2024ffd7c74aSJoe Talbott {
2025ffd7c74aSJoe Talbott int i;
2026ffd7c74aSJoe Talbott
2027da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s \n", __func__);
2028da10ea93SMatthew Dillon
2029ffd7c74aSJoe Talbott iwn_dma_contig_free(&ring->desc_dma);
2030ffd7c74aSJoe Talbott iwn_dma_contig_free(&ring->stat_dma);
2031ffd7c74aSJoe Talbott
2032ffd7c74aSJoe Talbott for (i = 0; i < IWN_RX_RING_COUNT; i++) {
2033ffd7c74aSJoe Talbott struct iwn_rx_data *data = &ring->data[i];
2034ffd7c74aSJoe Talbott
2035ffd7c74aSJoe Talbott if (data->m != NULL) {
2036ffd7c74aSJoe Talbott bus_dmamap_sync(ring->data_dmat, data->map,
2037ffd7c74aSJoe Talbott BUS_DMASYNC_POSTREAD);
2038ffd7c74aSJoe Talbott bus_dmamap_unload(ring->data_dmat, data->map);
2039ffd7c74aSJoe Talbott m_freem(data->m);
2040da10ea93SMatthew Dillon data->m = NULL;
2041ffd7c74aSJoe Talbott }
2042ffd7c74aSJoe Talbott if (data->map != NULL)
2043ffd7c74aSJoe Talbott bus_dmamap_destroy(ring->data_dmat, data->map);
2044ffd7c74aSJoe Talbott }
2045da10ea93SMatthew Dillon if (ring->data_dmat != NULL) {
2046da10ea93SMatthew Dillon bus_dma_tag_destroy(ring->data_dmat);
2047da10ea93SMatthew Dillon ring->data_dmat = NULL;
2048da10ea93SMatthew Dillon }
2049ffd7c74aSJoe Talbott }
2050ffd7c74aSJoe Talbott
2051ffd7c74aSJoe Talbott static int
iwn_alloc_tx_ring(struct iwn_softc * sc,struct iwn_tx_ring * ring,int qid)2052ffd7c74aSJoe Talbott iwn_alloc_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring, int qid)
2053ffd7c74aSJoe Talbott {
2054ffd7c74aSJoe Talbott bus_addr_t paddr;
2055da10ea93SMatthew Dillon bus_size_t size;
2056ffd7c74aSJoe Talbott int i, error;
2057ffd7c74aSJoe Talbott
2058ffd7c74aSJoe Talbott ring->qid = qid;
2059ffd7c74aSJoe Talbott ring->queued = 0;
2060ffd7c74aSJoe Talbott ring->cur = 0;
2061ffd7c74aSJoe Talbott
2062da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
2063da10ea93SMatthew Dillon
2064da10ea93SMatthew Dillon /* Allocate TX descriptors (256-byte aligned). */
2065ffd7c74aSJoe Talbott size = IWN_TX_RING_COUNT * sizeof (struct iwn_tx_desc);
2066da10ea93SMatthew Dillon error = iwn_dma_contig_alloc(sc, &ring->desc_dma, (void **)&ring->desc,
2067da10ea93SMatthew Dillon size, 256);
2068ffd7c74aSJoe Talbott if (error != 0) {
2069ffd7c74aSJoe Talbott device_printf(sc->sc_dev,
2070ffd7c74aSJoe Talbott "%s: could not allocate TX ring DMA memory, error %d\n",
2071ffd7c74aSJoe Talbott __func__, error);
2072ffd7c74aSJoe Talbott goto fail;
2073ffd7c74aSJoe Talbott }
2074ffd7c74aSJoe Talbott
2075ffd7c74aSJoe Talbott size = IWN_TX_RING_COUNT * sizeof (struct iwn_tx_cmd);
2076da10ea93SMatthew Dillon error = iwn_dma_contig_alloc(sc, &ring->cmd_dma, (void **)&ring->cmd,
2077da10ea93SMatthew Dillon size, 4);
2078ffd7c74aSJoe Talbott if (error != 0) {
2079ffd7c74aSJoe Talbott device_printf(sc->sc_dev,
2080ffd7c74aSJoe Talbott "%s: could not allocate TX cmd DMA memory, error %d\n",
2081ffd7c74aSJoe Talbott __func__, error);
2082ffd7c74aSJoe Talbott goto fail;
2083ffd7c74aSJoe Talbott }
2084ffd7c74aSJoe Talbott
208505538f72SMatthew Dillon #if defined(__DragonFly__)
2086977fc0dbSMatthew Dillon error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0,
2087*030b0c8cSMichael Neumann BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, MCLBYTES,
2088977fc0dbSMatthew Dillon IWN_MAX_SCATTER - 1, MCLBYTES, 0, &ring->data_dmat);
208905538f72SMatthew Dillon #else
209005538f72SMatthew Dillon error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0,
2091da10ea93SMatthew Dillon BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES,
2092977fc0dbSMatthew Dillon IWN_MAX_SCATTER - 1, MCLBYTES, 0, NULL, NULL, &ring->data_dmat);
209305538f72SMatthew Dillon #endif
2094ffd7c74aSJoe Talbott if (error != 0) {
2095ffd7c74aSJoe Talbott device_printf(sc->sc_dev,
2096da10ea93SMatthew Dillon "%s: could not create TX buf DMA tag, error %d\n",
2097ffd7c74aSJoe Talbott __func__, error);
2098ffd7c74aSJoe Talbott goto fail;
2099ffd7c74aSJoe Talbott }
2100ffd7c74aSJoe Talbott
2101ffd7c74aSJoe Talbott paddr = ring->cmd_dma.paddr;
2102ffd7c74aSJoe Talbott for (i = 0; i < IWN_TX_RING_COUNT; i++) {
2103ffd7c74aSJoe Talbott struct iwn_tx_data *data = &ring->data[i];
2104ffd7c74aSJoe Talbott
2105ffd7c74aSJoe Talbott data->cmd_paddr = paddr;
2106ffd7c74aSJoe Talbott data->scratch_paddr = paddr + 12;
2107ffd7c74aSJoe Talbott paddr += sizeof (struct iwn_tx_cmd);
2108ffd7c74aSJoe Talbott
2109ffd7c74aSJoe Talbott error = bus_dmamap_create(ring->data_dmat, 0, &data->map);
2110ffd7c74aSJoe Talbott if (error != 0) {
2111ffd7c74aSJoe Talbott device_printf(sc->sc_dev,
2112da10ea93SMatthew Dillon "%s: could not create TX buf DMA map, error %d\n",
2113ffd7c74aSJoe Talbott __func__, error);
2114ffd7c74aSJoe Talbott goto fail;
2115ffd7c74aSJoe Talbott }
2116ffd7c74aSJoe Talbott }
2117da10ea93SMatthew Dillon
2118da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s end\n", __func__);
2119da10ea93SMatthew Dillon
2120ffd7c74aSJoe Talbott return 0;
2121da10ea93SMatthew Dillon
2122da10ea93SMatthew Dillon fail: iwn_free_tx_ring(sc, ring);
2123da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s end in error\n", __func__);
2124ffd7c74aSJoe Talbott return error;
2125ffd7c74aSJoe Talbott }
2126ffd7c74aSJoe Talbott
2127ffd7c74aSJoe Talbott static void
iwn_reset_tx_ring(struct iwn_softc * sc,struct iwn_tx_ring * ring)2128ffd7c74aSJoe Talbott iwn_reset_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring)
2129ffd7c74aSJoe Talbott {
2130ffd7c74aSJoe Talbott int i;
2131ffd7c74aSJoe Talbott
2132da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->doing %s \n", __func__);
2133da10ea93SMatthew Dillon
2134ffd7c74aSJoe Talbott for (i = 0; i < IWN_TX_RING_COUNT; i++) {
2135ffd7c74aSJoe Talbott struct iwn_tx_data *data = &ring->data[i];
2136ffd7c74aSJoe Talbott
2137ffd7c74aSJoe Talbott if (data->m != NULL) {
2138da10ea93SMatthew Dillon bus_dmamap_sync(ring->data_dmat, data->map,
2139da10ea93SMatthew Dillon BUS_DMASYNC_POSTWRITE);
2140ffd7c74aSJoe Talbott bus_dmamap_unload(ring->data_dmat, data->map);
2141ffd7c74aSJoe Talbott m_freem(data->m);
2142ffd7c74aSJoe Talbott data->m = NULL;
2143ffd7c74aSJoe Talbott }
2144977fc0dbSMatthew Dillon if (data->ni != NULL) {
2145977fc0dbSMatthew Dillon ieee80211_free_node(data->ni);
2146977fc0dbSMatthew Dillon data->ni = NULL;
2147977fc0dbSMatthew Dillon }
2148ffd7c74aSJoe Talbott }
2149ffd7c74aSJoe Talbott /* Clear TX descriptors. */
2150ffd7c74aSJoe Talbott memset(ring->desc, 0, ring->desc_dma.size);
2151ffd7c74aSJoe Talbott bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
2152ffd7c74aSJoe Talbott BUS_DMASYNC_PREWRITE);
2153ffd7c74aSJoe Talbott sc->qfullmsk &= ~(1 << ring->qid);
2154ffd7c74aSJoe Talbott ring->queued = 0;
2155ffd7c74aSJoe Talbott ring->cur = 0;
2156ffd7c74aSJoe Talbott }
2157ffd7c74aSJoe Talbott
2158ffd7c74aSJoe Talbott static void
iwn_free_tx_ring(struct iwn_softc * sc,struct iwn_tx_ring * ring)2159ffd7c74aSJoe Talbott iwn_free_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring)
2160ffd7c74aSJoe Talbott {
2161ffd7c74aSJoe Talbott int i;
2162ffd7c74aSJoe Talbott
2163da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s \n", __func__);
2164da10ea93SMatthew Dillon
2165ffd7c74aSJoe Talbott iwn_dma_contig_free(&ring->desc_dma);
2166ffd7c74aSJoe Talbott iwn_dma_contig_free(&ring->cmd_dma);
2167ffd7c74aSJoe Talbott
2168ffd7c74aSJoe Talbott for (i = 0; i < IWN_TX_RING_COUNT; i++) {
2169ffd7c74aSJoe Talbott struct iwn_tx_data *data = &ring->data[i];
2170ffd7c74aSJoe Talbott
2171ffd7c74aSJoe Talbott if (data->m != NULL) {
2172ffd7c74aSJoe Talbott bus_dmamap_sync(ring->data_dmat, data->map,
2173ffd7c74aSJoe Talbott BUS_DMASYNC_POSTWRITE);
2174ffd7c74aSJoe Talbott bus_dmamap_unload(ring->data_dmat, data->map);
2175ffd7c74aSJoe Talbott m_freem(data->m);
2176ffd7c74aSJoe Talbott }
2177ffd7c74aSJoe Talbott if (data->map != NULL)
2178ffd7c74aSJoe Talbott bus_dmamap_destroy(ring->data_dmat, data->map);
2179ffd7c74aSJoe Talbott }
2180da10ea93SMatthew Dillon if (ring->data_dmat != NULL) {
2181da10ea93SMatthew Dillon bus_dma_tag_destroy(ring->data_dmat);
2182da10ea93SMatthew Dillon ring->data_dmat = NULL;
2183da10ea93SMatthew Dillon }
2184ffd7c74aSJoe Talbott }
2185ffd7c74aSJoe Talbott
2186ffd7c74aSJoe Talbott static void
iwn5000_ict_reset(struct iwn_softc * sc)2187ffd7c74aSJoe Talbott iwn5000_ict_reset(struct iwn_softc *sc)
2188ffd7c74aSJoe Talbott {
2189ffd7c74aSJoe Talbott /* Disable interrupts. */
2190ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_INT_MASK, 0);
2191ffd7c74aSJoe Talbott
2192ffd7c74aSJoe Talbott /* Reset ICT table. */
2193ffd7c74aSJoe Talbott memset(sc->ict, 0, IWN_ICT_SIZE);
2194ffd7c74aSJoe Talbott sc->ict_cur = 0;
2195ffd7c74aSJoe Talbott
2196da10ea93SMatthew Dillon /* Set physical address of ICT table (4KB aligned). */
2197ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_RESET, "%s: enabling ICT\n", __func__);
2198ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_DRAM_INT_TBL, IWN_DRAM_INT_TBL_ENABLE |
2199ffd7c74aSJoe Talbott IWN_DRAM_INT_TBL_WRAP_CHECK | sc->ict_dma.paddr >> 12);
2200ffd7c74aSJoe Talbott
2201ffd7c74aSJoe Talbott /* Enable periodic RX interrupt. */
2202ffd7c74aSJoe Talbott sc->int_mask |= IWN_INT_RX_PERIODIC;
2203ffd7c74aSJoe Talbott /* Switch to ICT interrupt mode in driver. */
2204ffd7c74aSJoe Talbott sc->sc_flags |= IWN_FLAG_USE_ICT;
2205ffd7c74aSJoe Talbott
2206ffd7c74aSJoe Talbott /* Re-enable interrupts. */
2207ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_INT, 0xffffffff);
2208ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask);
2209ffd7c74aSJoe Talbott }
2210ffd7c74aSJoe Talbott
2211ffd7c74aSJoe Talbott static int
iwn_read_eeprom(struct iwn_softc * sc,uint8_t macaddr[IEEE80211_ADDR_LEN])2212ffd7c74aSJoe Talbott iwn_read_eeprom(struct iwn_softc *sc, uint8_t macaddr[IEEE80211_ADDR_LEN])
2213ffd7c74aSJoe Talbott {
2214da10ea93SMatthew Dillon struct iwn_ops *ops = &sc->ops;
2215ffd7c74aSJoe Talbott uint16_t val;
2216da10ea93SMatthew Dillon int error;
2217da10ea93SMatthew Dillon
2218da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
2219ffd7c74aSJoe Talbott
2220ffd7c74aSJoe Talbott /* Check whether adapter has an EEPROM or an OTPROM. */
2221ffd7c74aSJoe Talbott if (sc->hw_type >= IWN_HW_REV_TYPE_1000 &&
2222ffd7c74aSJoe Talbott (IWN_READ(sc, IWN_OTP_GP) & IWN_OTP_GP_DEV_SEL_OTP))
2223ffd7c74aSJoe Talbott sc->sc_flags |= IWN_FLAG_HAS_OTPROM;
2224ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_RESET, "%s found\n",
2225ffd7c74aSJoe Talbott (sc->sc_flags & IWN_FLAG_HAS_OTPROM) ? "OTPROM" : "EEPROM");
2226ffd7c74aSJoe Talbott
2227ffd7c74aSJoe Talbott /* Adapter has to be powered on for EEPROM access to work. */
2228da10ea93SMatthew Dillon if ((error = iwn_apm_init(sc)) != 0) {
2229ffd7c74aSJoe Talbott device_printf(sc->sc_dev,
2230da10ea93SMatthew Dillon "%s: could not power ON adapter, error %d\n", __func__,
2231da10ea93SMatthew Dillon error);
2232ffd7c74aSJoe Talbott return error;
2233ffd7c74aSJoe Talbott }
2234ffd7c74aSJoe Talbott
2235ffd7c74aSJoe Talbott if ((IWN_READ(sc, IWN_EEPROM_GP) & 0x7) == 0) {
2236ffd7c74aSJoe Talbott device_printf(sc->sc_dev, "%s: bad ROM signature\n", __func__);
2237ffd7c74aSJoe Talbott return EIO;
2238ffd7c74aSJoe Talbott }
2239da10ea93SMatthew Dillon if ((error = iwn_eeprom_lock(sc)) != 0) {
2240da10ea93SMatthew Dillon device_printf(sc->sc_dev, "%s: could not lock ROM, error %d\n",
2241ffd7c74aSJoe Talbott __func__, error);
2242ffd7c74aSJoe Talbott return error;
2243ffd7c74aSJoe Talbott }
2244ffd7c74aSJoe Talbott if (sc->sc_flags & IWN_FLAG_HAS_OTPROM) {
2245da10ea93SMatthew Dillon if ((error = iwn_init_otprom(sc)) != 0) {
2246ffd7c74aSJoe Talbott device_printf(sc->sc_dev,
2247ffd7c74aSJoe Talbott "%s: could not initialize OTPROM, error %d\n",
2248ffd7c74aSJoe Talbott __func__, error);
2249ffd7c74aSJoe Talbott return error;
2250ffd7c74aSJoe Talbott }
2251ffd7c74aSJoe Talbott }
2252ffd7c74aSJoe Talbott
2253da10ea93SMatthew Dillon iwn_read_prom_data(sc, IWN_EEPROM_SKU_CAP, &val, 2);
2254da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_RESET, "SKU capabilities=0x%04x\n", le16toh(val));
2255da10ea93SMatthew Dillon /* Check if HT support is bonded out. */
2256da10ea93SMatthew Dillon if (val & htole16(IWN_EEPROM_SKU_CAP_11N))
2257da10ea93SMatthew Dillon sc->sc_flags |= IWN_FLAG_HAS_11N;
2258da10ea93SMatthew Dillon
2259ffd7c74aSJoe Talbott iwn_read_prom_data(sc, IWN_EEPROM_RFCFG, &val, 2);
2260ffd7c74aSJoe Talbott sc->rfcfg = le16toh(val);
2261ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_RESET, "radio config=0x%04x\n", sc->rfcfg);
2262da10ea93SMatthew Dillon /* Read Tx/Rx chains from ROM unless it's known to be broken. */
2263da10ea93SMatthew Dillon if (sc->txchainmask == 0)
2264da10ea93SMatthew Dillon sc->txchainmask = IWN_RFCFG_TXANTMSK(sc->rfcfg);
2265da10ea93SMatthew Dillon if (sc->rxchainmask == 0)
2266da10ea93SMatthew Dillon sc->rxchainmask = IWN_RFCFG_RXANTMSK(sc->rfcfg);
2267ffd7c74aSJoe Talbott
2268ffd7c74aSJoe Talbott /* Read MAC address. */
2269ffd7c74aSJoe Talbott iwn_read_prom_data(sc, IWN_EEPROM_MAC, macaddr, 6);
2270ffd7c74aSJoe Talbott
2271ffd7c74aSJoe Talbott /* Read adapter-specific information from EEPROM. */
2272da10ea93SMatthew Dillon ops->read_eeprom(sc);
2273ffd7c74aSJoe Talbott
2274ffd7c74aSJoe Talbott iwn_apm_stop(sc); /* Power OFF adapter. */
2275ffd7c74aSJoe Talbott
2276ffd7c74aSJoe Talbott iwn_eeprom_unlock(sc);
2277da10ea93SMatthew Dillon
2278da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s end\n", __func__);
2279da10ea93SMatthew Dillon
2280ffd7c74aSJoe Talbott return 0;
2281ffd7c74aSJoe Talbott }
2282ffd7c74aSJoe Talbott
2283ffd7c74aSJoe Talbott static void
iwn4965_read_eeprom(struct iwn_softc * sc)2284ffd7c74aSJoe Talbott iwn4965_read_eeprom(struct iwn_softc *sc)
2285ffd7c74aSJoe Talbott {
2286ffd7c74aSJoe Talbott uint32_t addr;
2287ffd7c74aSJoe Talbott uint16_t val;
2288da10ea93SMatthew Dillon int i;
2289ffd7c74aSJoe Talbott
2290da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
2291da10ea93SMatthew Dillon
2292da10ea93SMatthew Dillon /* Read regulatory domain (4 ASCII characters). */
2293ffd7c74aSJoe Talbott iwn_read_prom_data(sc, IWN4965_EEPROM_DOMAIN, sc->eeprom_domain, 4);
2294ffd7c74aSJoe Talbott
2295977fc0dbSMatthew Dillon /* Read the list of authorized channels (20MHz & 40MHz). */
2296da10ea93SMatthew Dillon for (i = 0; i < IWN_NBANDS - 1; i++) {
2297ffd7c74aSJoe Talbott addr = iwn4965_regulatory_bands[i];
2298ffd7c74aSJoe Talbott iwn_read_eeprom_channels(sc, i, addr);
2299ffd7c74aSJoe Talbott }
2300ffd7c74aSJoe Talbott
2301ffd7c74aSJoe Talbott /* Read maximum allowed TX power for 2GHz and 5GHz bands. */
2302ffd7c74aSJoe Talbott iwn_read_prom_data(sc, IWN4965_EEPROM_MAXPOW, &val, 2);
2303ffd7c74aSJoe Talbott sc->maxpwr2GHz = val & 0xff;
2304ffd7c74aSJoe Talbott sc->maxpwr5GHz = val >> 8;
2305ffd7c74aSJoe Talbott /* Check that EEPROM values are within valid range. */
2306ffd7c74aSJoe Talbott if (sc->maxpwr5GHz < 20 || sc->maxpwr5GHz > 50)
2307ffd7c74aSJoe Talbott sc->maxpwr5GHz = 38;
2308ffd7c74aSJoe Talbott if (sc->maxpwr2GHz < 20 || sc->maxpwr2GHz > 50)
2309ffd7c74aSJoe Talbott sc->maxpwr2GHz = 38;
2310ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_RESET, "maxpwr 2GHz=%d 5GHz=%d\n",
2311ffd7c74aSJoe Talbott sc->maxpwr2GHz, sc->maxpwr5GHz);
2312ffd7c74aSJoe Talbott
2313ffd7c74aSJoe Talbott /* Read samples for each TX power group. */
2314ffd7c74aSJoe Talbott iwn_read_prom_data(sc, IWN4965_EEPROM_BANDS, sc->bands,
2315ffd7c74aSJoe Talbott sizeof sc->bands);
2316ffd7c74aSJoe Talbott
2317ffd7c74aSJoe Talbott /* Read voltage at which samples were taken. */
2318ffd7c74aSJoe Talbott iwn_read_prom_data(sc, IWN4965_EEPROM_VOLTAGE, &val, 2);
2319ffd7c74aSJoe Talbott sc->eeprom_voltage = (int16_t)le16toh(val);
2320ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_RESET, "voltage=%d (in 0.3V)\n",
2321ffd7c74aSJoe Talbott sc->eeprom_voltage);
2322ffd7c74aSJoe Talbott
2323ffd7c74aSJoe Talbott #ifdef IWN_DEBUG
2324ffd7c74aSJoe Talbott /* Print samples. */
2325ffd7c74aSJoe Talbott if (sc->sc_debug & IWN_DEBUG_ANY) {
2326da10ea93SMatthew Dillon for (i = 0; i < IWN_NBANDS - 1; i++)
2327ffd7c74aSJoe Talbott iwn4965_print_power_group(sc, i);
2328ffd7c74aSJoe Talbott }
2329ffd7c74aSJoe Talbott #endif
2330da10ea93SMatthew Dillon
2331da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s end\n", __func__);
2332ffd7c74aSJoe Talbott }
2333ffd7c74aSJoe Talbott
2334ffd7c74aSJoe Talbott #ifdef IWN_DEBUG
2335ffd7c74aSJoe Talbott static void
iwn4965_print_power_group(struct iwn_softc * sc,int i)2336ffd7c74aSJoe Talbott iwn4965_print_power_group(struct iwn_softc *sc, int i)
2337ffd7c74aSJoe Talbott {
2338ffd7c74aSJoe Talbott struct iwn4965_eeprom_band *band = &sc->bands[i];
2339ffd7c74aSJoe Talbott struct iwn4965_eeprom_chan_samples *chans = band->chans;
2340ffd7c74aSJoe Talbott int j, c;
2341ffd7c74aSJoe Talbott
2342101554a6SMatthew Dillon kprintf("===band %d===\n", i);
2343101554a6SMatthew Dillon kprintf("chan lo=%d, chan hi=%d\n", band->lo, band->hi);
2344101554a6SMatthew Dillon kprintf("chan1 num=%d\n", chans[0].num);
2345ffd7c74aSJoe Talbott for (c = 0; c < 2; c++) {
2346ffd7c74aSJoe Talbott for (j = 0; j < IWN_NSAMPLES; j++) {
2347101554a6SMatthew Dillon kprintf("chain %d, sample %d: temp=%d gain=%d "
2348ffd7c74aSJoe Talbott "power=%d pa_det=%d\n", c, j,
2349ffd7c74aSJoe Talbott chans[0].samples[c][j].temp,
2350ffd7c74aSJoe Talbott chans[0].samples[c][j].gain,
2351ffd7c74aSJoe Talbott chans[0].samples[c][j].power,
2352ffd7c74aSJoe Talbott chans[0].samples[c][j].pa_det);
2353ffd7c74aSJoe Talbott }
2354ffd7c74aSJoe Talbott }
2355101554a6SMatthew Dillon kprintf("chan2 num=%d\n", chans[1].num);
2356ffd7c74aSJoe Talbott for (c = 0; c < 2; c++) {
2357ffd7c74aSJoe Talbott for (j = 0; j < IWN_NSAMPLES; j++) {
2358101554a6SMatthew Dillon kprintf("chain %d, sample %d: temp=%d gain=%d "
2359ffd7c74aSJoe Talbott "power=%d pa_det=%d\n", c, j,
2360ffd7c74aSJoe Talbott chans[1].samples[c][j].temp,
2361ffd7c74aSJoe Talbott chans[1].samples[c][j].gain,
2362ffd7c74aSJoe Talbott chans[1].samples[c][j].power,
2363ffd7c74aSJoe Talbott chans[1].samples[c][j].pa_det);
2364ffd7c74aSJoe Talbott }
2365ffd7c74aSJoe Talbott }
2366ffd7c74aSJoe Talbott }
2367ffd7c74aSJoe Talbott #endif
2368ffd7c74aSJoe Talbott
2369ffd7c74aSJoe Talbott static void
iwn5000_read_eeprom(struct iwn_softc * sc)2370ffd7c74aSJoe Talbott iwn5000_read_eeprom(struct iwn_softc *sc)
2371ffd7c74aSJoe Talbott {
2372ffd7c74aSJoe Talbott struct iwn5000_eeprom_calib_hdr hdr;
2373da10ea93SMatthew Dillon int32_t volt;
2374da10ea93SMatthew Dillon uint32_t base, addr;
2375ffd7c74aSJoe Talbott uint16_t val;
2376da10ea93SMatthew Dillon int i;
2377ffd7c74aSJoe Talbott
2378da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
2379da10ea93SMatthew Dillon
2380da10ea93SMatthew Dillon /* Read regulatory domain (4 ASCII characters). */
2381ffd7c74aSJoe Talbott iwn_read_prom_data(sc, IWN5000_EEPROM_REG, &val, 2);
2382ffd7c74aSJoe Talbott base = le16toh(val);
2383ffd7c74aSJoe Talbott iwn_read_prom_data(sc, base + IWN5000_EEPROM_DOMAIN,
2384ffd7c74aSJoe Talbott sc->eeprom_domain, 4);
2385ffd7c74aSJoe Talbott
2386977fc0dbSMatthew Dillon /* Read the list of authorized channels (20MHz & 40MHz). */
2387da10ea93SMatthew Dillon for (i = 0; i < IWN_NBANDS - 1; i++) {
2388da10ea93SMatthew Dillon addr = base + sc->base_params->regulatory_bands[i];
2389ffd7c74aSJoe Talbott iwn_read_eeprom_channels(sc, i, addr);
2390ffd7c74aSJoe Talbott }
2391ffd7c74aSJoe Talbott
2392ffd7c74aSJoe Talbott /* Read enhanced TX power information for 6000 Series. */
2393da10ea93SMatthew Dillon if (sc->base_params->enhanced_TX_power)
2394ffd7c74aSJoe Talbott iwn_read_eeprom_enhinfo(sc);
2395ffd7c74aSJoe Talbott
2396ffd7c74aSJoe Talbott iwn_read_prom_data(sc, IWN5000_EEPROM_CAL, &val, 2);
2397ffd7c74aSJoe Talbott base = le16toh(val);
2398ffd7c74aSJoe Talbott iwn_read_prom_data(sc, base, &hdr, sizeof hdr);
2399ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_CALIBRATE,
2400da10ea93SMatthew Dillon "%s: calib version=%u pa type=%u voltage=%u\n", __func__,
2401da10ea93SMatthew Dillon hdr.version, hdr.pa_type, le16toh(hdr.volt));
2402ffd7c74aSJoe Talbott sc->calib_ver = hdr.version;
2403ffd7c74aSJoe Talbott
2404da10ea93SMatthew Dillon if (sc->base_params->calib_need & IWN_FLG_NEED_PHY_CALIB_TEMP_OFFSETv2) {
2405da10ea93SMatthew Dillon sc->eeprom_voltage = le16toh(hdr.volt);
2406da10ea93SMatthew Dillon iwn_read_prom_data(sc, base + IWN5000_EEPROM_TEMP, &val, 2);
2407da10ea93SMatthew Dillon sc->eeprom_temp_high=le16toh(val);
2408da10ea93SMatthew Dillon iwn_read_prom_data(sc, base + IWN5000_EEPROM_VOLT, &val, 2);
2409da10ea93SMatthew Dillon sc->eeprom_temp = le16toh(val);
2410da10ea93SMatthew Dillon }
2411da10ea93SMatthew Dillon
2412ffd7c74aSJoe Talbott if (sc->hw_type == IWN_HW_REV_TYPE_5150) {
2413ffd7c74aSJoe Talbott /* Compute temperature offset. */
2414ffd7c74aSJoe Talbott iwn_read_prom_data(sc, base + IWN5000_EEPROM_TEMP, &val, 2);
2415da10ea93SMatthew Dillon sc->eeprom_temp = le16toh(val);
2416ffd7c74aSJoe Talbott iwn_read_prom_data(sc, base + IWN5000_EEPROM_VOLT, &val, 2);
2417ffd7c74aSJoe Talbott volt = le16toh(val);
2418da10ea93SMatthew Dillon sc->temp_off = sc->eeprom_temp - (volt / -5);
2419ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_CALIBRATE, "temp=%d volt=%d offset=%dK\n",
2420da10ea93SMatthew Dillon sc->eeprom_temp, volt, sc->temp_off);
2421ffd7c74aSJoe Talbott } else {
2422ffd7c74aSJoe Talbott /* Read crystal calibration. */
2423ffd7c74aSJoe Talbott iwn_read_prom_data(sc, base + IWN5000_EEPROM_CRYSTAL,
2424ffd7c74aSJoe Talbott &sc->eeprom_crystal, sizeof (uint32_t));
2425ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_CALIBRATE, "crystal calibration 0x%08x\n",
2426ffd7c74aSJoe Talbott le32toh(sc->eeprom_crystal));
2427ffd7c74aSJoe Talbott }
2428da10ea93SMatthew Dillon
2429da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s end\n", __func__);
2430da10ea93SMatthew Dillon
2431ffd7c74aSJoe Talbott }
2432ffd7c74aSJoe Talbott
2433ffd7c74aSJoe Talbott /*
2434ffd7c74aSJoe Talbott * Translate EEPROM flags to net80211.
2435ffd7c74aSJoe Talbott */
2436ffd7c74aSJoe Talbott static uint32_t
iwn_eeprom_channel_flags(struct iwn_eeprom_chan * channel)2437ffd7c74aSJoe Talbott iwn_eeprom_channel_flags(struct iwn_eeprom_chan *channel)
2438ffd7c74aSJoe Talbott {
2439ffd7c74aSJoe Talbott uint32_t nflags;
2440ffd7c74aSJoe Talbott
2441ffd7c74aSJoe Talbott nflags = 0;
2442ffd7c74aSJoe Talbott if ((channel->flags & IWN_EEPROM_CHAN_ACTIVE) == 0)
2443ffd7c74aSJoe Talbott nflags |= IEEE80211_CHAN_PASSIVE;
2444ffd7c74aSJoe Talbott if ((channel->flags & IWN_EEPROM_CHAN_IBSS) == 0)
2445ffd7c74aSJoe Talbott nflags |= IEEE80211_CHAN_NOADHOC;
2446ffd7c74aSJoe Talbott if (channel->flags & IWN_EEPROM_CHAN_RADAR) {
2447ffd7c74aSJoe Talbott nflags |= IEEE80211_CHAN_DFS;
2448ffd7c74aSJoe Talbott /* XXX apparently IBSS may still be marked */
2449ffd7c74aSJoe Talbott nflags |= IEEE80211_CHAN_NOADHOC;
2450ffd7c74aSJoe Talbott }
2451ffd7c74aSJoe Talbott
2452ffd7c74aSJoe Talbott return nflags;
2453ffd7c74aSJoe Talbott }
2454ffd7c74aSJoe Talbott
2455ffd7c74aSJoe Talbott static void
iwn_read_eeprom_band(struct iwn_softc * sc,int n,int maxchans,int * nchans,struct ieee80211_channel chans[])2456977fc0dbSMatthew Dillon iwn_read_eeprom_band(struct iwn_softc *sc, int n, int maxchans, int *nchans,
2457977fc0dbSMatthew Dillon struct ieee80211_channel chans[])
2458ffd7c74aSJoe Talbott {
2459ffd7c74aSJoe Talbott struct iwn_eeprom_chan *channels = sc->eeprom_channels[n];
2460ffd7c74aSJoe Talbott const struct iwn_chan_band *band = &iwn_bands[n];
2461977fc0dbSMatthew Dillon uint8_t bands[IEEE80211_MODE_BYTES];
2462da10ea93SMatthew Dillon uint8_t chan;
2463977fc0dbSMatthew Dillon int i, error, nflags;
2464da10ea93SMatthew Dillon
2465da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
2466ffd7c74aSJoe Talbott
2467977fc0dbSMatthew Dillon memset(bands, 0, sizeof(bands));
2468977fc0dbSMatthew Dillon if (n == 0) {
2469977fc0dbSMatthew Dillon setbit(bands, IEEE80211_MODE_11B);
2470977fc0dbSMatthew Dillon setbit(bands, IEEE80211_MODE_11G);
2471977fc0dbSMatthew Dillon if (sc->sc_flags & IWN_FLAG_HAS_11N)
2472977fc0dbSMatthew Dillon setbit(bands, IEEE80211_MODE_11NG);
2473977fc0dbSMatthew Dillon } else {
2474977fc0dbSMatthew Dillon setbit(bands, IEEE80211_MODE_11A);
2475977fc0dbSMatthew Dillon if (sc->sc_flags & IWN_FLAG_HAS_11N)
2476977fc0dbSMatthew Dillon setbit(bands, IEEE80211_MODE_11NA);
2477977fc0dbSMatthew Dillon }
2478977fc0dbSMatthew Dillon
2479ffd7c74aSJoe Talbott for (i = 0; i < band->nchan; i++) {
2480ffd7c74aSJoe Talbott if (!(channels[i].flags & IWN_EEPROM_CHAN_VALID)) {
2481ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_RESET,
2482ffd7c74aSJoe Talbott "skip chan %d flags 0x%x maxpwr %d\n",
2483ffd7c74aSJoe Talbott band->chan[i], channels[i].flags,
2484ffd7c74aSJoe Talbott channels[i].maxpwr);
2485ffd7c74aSJoe Talbott continue;
2486ffd7c74aSJoe Talbott }
2487977fc0dbSMatthew Dillon
2488ffd7c74aSJoe Talbott chan = band->chan[i];
2489ffd7c74aSJoe Talbott nflags = iwn_eeprom_channel_flags(&channels[i]);
2490977fc0dbSMatthew Dillon error = ieee80211_add_channel(chans, maxchans, nchans,
2491977fc0dbSMatthew Dillon chan, 0, channels[i].maxpwr, nflags, bands);
2492977fc0dbSMatthew Dillon if (error != 0)
2493977fc0dbSMatthew Dillon break;
2494da10ea93SMatthew Dillon
2495da10ea93SMatthew Dillon /* Save maximum allowed TX power for this channel. */
2496977fc0dbSMatthew Dillon /* XXX wrong */
2497da10ea93SMatthew Dillon sc->maxpwr[chan] = channels[i].maxpwr;
2498da10ea93SMatthew Dillon
2499da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_RESET,
2500da10ea93SMatthew Dillon "add chan %d flags 0x%x maxpwr %d\n", chan,
2501da10ea93SMatthew Dillon channels[i].flags, channels[i].maxpwr);
2502ffd7c74aSJoe Talbott }
2503ffd7c74aSJoe Talbott
2504da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s end\n", __func__);
2505da10ea93SMatthew Dillon
2506da10ea93SMatthew Dillon }
2507da10ea93SMatthew Dillon
2508ffd7c74aSJoe Talbott static void
iwn_read_eeprom_ht40(struct iwn_softc * sc,int n,int maxchans,int * nchans,struct ieee80211_channel chans[])2509977fc0dbSMatthew Dillon iwn_read_eeprom_ht40(struct iwn_softc *sc, int n, int maxchans, int *nchans,
2510977fc0dbSMatthew Dillon struct ieee80211_channel chans[])
2511ffd7c74aSJoe Talbott {
2512ffd7c74aSJoe Talbott struct iwn_eeprom_chan *channels = sc->eeprom_channels[n];
2513ffd7c74aSJoe Talbott const struct iwn_chan_band *band = &iwn_bands[n];
2514da10ea93SMatthew Dillon uint8_t chan;
2515977fc0dbSMatthew Dillon int i, error, nflags;
2516da10ea93SMatthew Dillon
2517da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s start\n", __func__);
2518da10ea93SMatthew Dillon
2519da10ea93SMatthew Dillon if (!(sc->sc_flags & IWN_FLAG_HAS_11N)) {
2520da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s end no 11n\n", __func__);
2521da10ea93SMatthew Dillon return;
2522da10ea93SMatthew Dillon }
2523ffd7c74aSJoe Talbott
2524ffd7c74aSJoe Talbott for (i = 0; i < band->nchan; i++) {
2525da10ea93SMatthew Dillon if (!(channels[i].flags & IWN_EEPROM_CHAN_VALID)) {
2526ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_RESET,
2527ffd7c74aSJoe Talbott "skip chan %d flags 0x%x maxpwr %d\n",
2528ffd7c74aSJoe Talbott band->chan[i], channels[i].flags,
2529ffd7c74aSJoe Talbott channels[i].maxpwr);
2530ffd7c74aSJoe Talbott continue;
2531ffd7c74aSJoe Talbott }
2532977fc0dbSMatthew Dillon
2533da10ea93SMatthew Dillon chan = band->chan[i];
2534da10ea93SMatthew Dillon nflags = iwn_eeprom_channel_flags(&channels[i]);
2535977fc0dbSMatthew Dillon nflags |= (n == 5 ? IEEE80211_CHAN_G : IEEE80211_CHAN_A);
2536977fc0dbSMatthew Dillon error = ieee80211_add_channel_ht40(chans, maxchans, nchans,
2537977fc0dbSMatthew Dillon chan, channels[i].maxpwr, nflags);
2538977fc0dbSMatthew Dillon switch (error) {
2539977fc0dbSMatthew Dillon case EINVAL:
2540ffd7c74aSJoe Talbott device_printf(sc->sc_dev,
2541da10ea93SMatthew Dillon "%s: no entry for channel %d\n", __func__, chan);
2542ffd7c74aSJoe Talbott continue;
2543977fc0dbSMatthew Dillon case ENOENT:
2544ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_RESET,
2545da10ea93SMatthew Dillon "%s: skip chan %d, extension channel not found\n",
2546da10ea93SMatthew Dillon __func__, chan);
2547ffd7c74aSJoe Talbott continue;
2548977fc0dbSMatthew Dillon case ENOBUFS:
2549977fc0dbSMatthew Dillon device_printf(sc->sc_dev,
2550977fc0dbSMatthew Dillon "%s: channel table is full!\n", __func__);
2551977fc0dbSMatthew Dillon break;
2552977fc0dbSMatthew Dillon case 0:
2553ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_RESET,
2554ffd7c74aSJoe Talbott "add ht40 chan %d flags 0x%x maxpwr %d\n",
2555da10ea93SMatthew Dillon chan, channels[i].flags, channels[i].maxpwr);
2556977fc0dbSMatthew Dillon /* FALLTHROUGH */
2557977fc0dbSMatthew Dillon default:
2558977fc0dbSMatthew Dillon break;
2559977fc0dbSMatthew Dillon }
2560ffd7c74aSJoe Talbott }
2561da10ea93SMatthew Dillon
2562da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s end\n", __func__);
2563da10ea93SMatthew Dillon
2564ffd7c74aSJoe Talbott }
2565ffd7c74aSJoe Talbott
2566ffd7c74aSJoe Talbott static void
iwn_read_eeprom_channels(struct iwn_softc * sc,int n,uint32_t addr)2567ffd7c74aSJoe Talbott iwn_read_eeprom_channels(struct iwn_softc *sc, int n, uint32_t addr)
2568ffd7c74aSJoe Talbott {
2569977fc0dbSMatthew Dillon struct ieee80211com *ic = &sc->sc_ic;
2570ffd7c74aSJoe Talbott
2571ffd7c74aSJoe Talbott iwn_read_prom_data(sc, addr, &sc->eeprom_channels[n],
2572ffd7c74aSJoe Talbott iwn_bands[n].nchan * sizeof (struct iwn_eeprom_chan));
2573ffd7c74aSJoe Talbott
2574977fc0dbSMatthew Dillon if (n < 5) {
2575977fc0dbSMatthew Dillon iwn_read_eeprom_band(sc, n, IEEE80211_CHAN_MAX, &ic->ic_nchans,
2576977fc0dbSMatthew Dillon ic->ic_channels);
2577977fc0dbSMatthew Dillon } else {
2578977fc0dbSMatthew Dillon iwn_read_eeprom_ht40(sc, n, IEEE80211_CHAN_MAX, &ic->ic_nchans,
2579977fc0dbSMatthew Dillon ic->ic_channels);
2580977fc0dbSMatthew Dillon }
2581ffd7c74aSJoe Talbott ieee80211_sort_channels(ic->ic_channels, ic->ic_nchans);
2582ffd7c74aSJoe Talbott }
2583ffd7c74aSJoe Talbott
2584da10ea93SMatthew Dillon static struct iwn_eeprom_chan *
iwn_find_eeprom_channel(struct iwn_softc * sc,struct ieee80211_channel * c)2585da10ea93SMatthew Dillon iwn_find_eeprom_channel(struct iwn_softc *sc, struct ieee80211_channel *c)
2586da10ea93SMatthew Dillon {
2587da10ea93SMatthew Dillon int band, chan, i, j;
2588da10ea93SMatthew Dillon
2589da10ea93SMatthew Dillon if (IEEE80211_IS_CHAN_HT40(c)) {
2590da10ea93SMatthew Dillon band = IEEE80211_IS_CHAN_5GHZ(c) ? 6 : 5;
2591da10ea93SMatthew Dillon if (IEEE80211_IS_CHAN_HT40D(c))
2592da10ea93SMatthew Dillon chan = c->ic_extieee;
2593da10ea93SMatthew Dillon else
2594da10ea93SMatthew Dillon chan = c->ic_ieee;
2595da10ea93SMatthew Dillon for (i = 0; i < iwn_bands[band].nchan; i++) {
2596da10ea93SMatthew Dillon if (iwn_bands[band].chan[i] == chan)
2597da10ea93SMatthew Dillon return &sc->eeprom_channels[band][i];
2598da10ea93SMatthew Dillon }
2599da10ea93SMatthew Dillon } else {
2600da10ea93SMatthew Dillon for (j = 0; j < 5; j++) {
2601da10ea93SMatthew Dillon for (i = 0; i < iwn_bands[j].nchan; i++) {
2602977fc0dbSMatthew Dillon if (iwn_bands[j].chan[i] == c->ic_ieee &&
2603977fc0dbSMatthew Dillon ((j == 0) ^ IEEE80211_IS_CHAN_A(c)) == 1)
2604da10ea93SMatthew Dillon return &sc->eeprom_channels[j][i];
2605da10ea93SMatthew Dillon }
2606da10ea93SMatthew Dillon }
2607da10ea93SMatthew Dillon }
2608da10ea93SMatthew Dillon return NULL;
2609da10ea93SMatthew Dillon }
2610da10ea93SMatthew Dillon
2611977fc0dbSMatthew Dillon static void
iwn_getradiocaps(struct ieee80211com * ic,int maxchans,int * nchans,struct ieee80211_channel chans[])2612977fc0dbSMatthew Dillon iwn_getradiocaps(struct ieee80211com *ic,
2613977fc0dbSMatthew Dillon int maxchans, int *nchans, struct ieee80211_channel chans[])
2614977fc0dbSMatthew Dillon {
2615977fc0dbSMatthew Dillon struct iwn_softc *sc = ic->ic_softc;
2616977fc0dbSMatthew Dillon int i;
2617977fc0dbSMatthew Dillon
2618977fc0dbSMatthew Dillon /* Parse the list of authorized channels. */
2619977fc0dbSMatthew Dillon for (i = 0; i < 5 && *nchans < maxchans; i++)
2620977fc0dbSMatthew Dillon iwn_read_eeprom_band(sc, i, maxchans, nchans, chans);
2621977fc0dbSMatthew Dillon for (i = 5; i < IWN_NBANDS - 1 && *nchans < maxchans; i++)
2622977fc0dbSMatthew Dillon iwn_read_eeprom_ht40(sc, i, maxchans, nchans, chans);
2623977fc0dbSMatthew Dillon }
2624977fc0dbSMatthew Dillon
2625da10ea93SMatthew Dillon /*
2626da10ea93SMatthew Dillon * Enforce flags read from EEPROM.
2627da10ea93SMatthew Dillon */
2628da10ea93SMatthew Dillon static int
iwn_setregdomain(struct ieee80211com * ic,struct ieee80211_regdomain * rd,int nchan,struct ieee80211_channel chans[])2629da10ea93SMatthew Dillon iwn_setregdomain(struct ieee80211com *ic, struct ieee80211_regdomain *rd,
2630da10ea93SMatthew Dillon int nchan, struct ieee80211_channel chans[])
2631da10ea93SMatthew Dillon {
26324f1aaf2fSImre Vadász struct iwn_softc *sc = ic->ic_softc;
2633da10ea93SMatthew Dillon int i;
2634da10ea93SMatthew Dillon
2635da10ea93SMatthew Dillon for (i = 0; i < nchan; i++) {
2636da10ea93SMatthew Dillon struct ieee80211_channel *c = &chans[i];
2637da10ea93SMatthew Dillon struct iwn_eeprom_chan *channel;
2638da10ea93SMatthew Dillon
2639da10ea93SMatthew Dillon channel = iwn_find_eeprom_channel(sc, c);
2640da10ea93SMatthew Dillon if (channel == NULL) {
2641977fc0dbSMatthew Dillon ic_printf(ic, "%s: invalid channel %u freq %u/0x%x\n",
2642da10ea93SMatthew Dillon __func__, c->ic_ieee, c->ic_freq, c->ic_flags);
2643da10ea93SMatthew Dillon return EINVAL;
2644da10ea93SMatthew Dillon }
2645da10ea93SMatthew Dillon c->ic_flags |= iwn_eeprom_channel_flags(channel);
2646da10ea93SMatthew Dillon }
2647da10ea93SMatthew Dillon
2648da10ea93SMatthew Dillon return 0;
2649da10ea93SMatthew Dillon }
2650da10ea93SMatthew Dillon
2651ffd7c74aSJoe Talbott static void
iwn_read_eeprom_enhinfo(struct iwn_softc * sc)2652ffd7c74aSJoe Talbott iwn_read_eeprom_enhinfo(struct iwn_softc *sc)
2653ffd7c74aSJoe Talbott {
2654ffd7c74aSJoe Talbott struct iwn_eeprom_enhinfo enhinfo[35];
2655977fc0dbSMatthew Dillon struct ieee80211com *ic = &sc->sc_ic;
2656da10ea93SMatthew Dillon struct ieee80211_channel *c;
2657ffd7c74aSJoe Talbott uint16_t val, base;
2658ffd7c74aSJoe Talbott int8_t maxpwr;
2659da10ea93SMatthew Dillon uint8_t flags;
2660da10ea93SMatthew Dillon int i, j;
2661da10ea93SMatthew Dillon
2662da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
2663ffd7c74aSJoe Talbott
2664ffd7c74aSJoe Talbott iwn_read_prom_data(sc, IWN5000_EEPROM_REG, &val, 2);
2665ffd7c74aSJoe Talbott base = le16toh(val);
2666ffd7c74aSJoe Talbott iwn_read_prom_data(sc, base + IWN6000_EEPROM_ENHINFO,
2667ffd7c74aSJoe Talbott enhinfo, sizeof enhinfo);
2668ffd7c74aSJoe Talbott
2669da10ea93SMatthew Dillon for (i = 0; i < nitems(enhinfo); i++) {
2670da10ea93SMatthew Dillon flags = enhinfo[i].flags;
2671da10ea93SMatthew Dillon if (!(flags & IWN_ENHINFO_VALID))
2672ffd7c74aSJoe Talbott continue; /* Skip invalid entries. */
2673ffd7c74aSJoe Talbott
2674ffd7c74aSJoe Talbott maxpwr = 0;
2675ffd7c74aSJoe Talbott if (sc->txchainmask & IWN_ANT_A)
2676ffd7c74aSJoe Talbott maxpwr = MAX(maxpwr, enhinfo[i].chain[0]);
2677ffd7c74aSJoe Talbott if (sc->txchainmask & IWN_ANT_B)
2678ffd7c74aSJoe Talbott maxpwr = MAX(maxpwr, enhinfo[i].chain[1]);
2679ffd7c74aSJoe Talbott if (sc->txchainmask & IWN_ANT_C)
2680ffd7c74aSJoe Talbott maxpwr = MAX(maxpwr, enhinfo[i].chain[2]);
2681ffd7c74aSJoe Talbott if (sc->ntxchains == 2)
2682ffd7c74aSJoe Talbott maxpwr = MAX(maxpwr, enhinfo[i].mimo2);
2683ffd7c74aSJoe Talbott else if (sc->ntxchains == 3)
2684ffd7c74aSJoe Talbott maxpwr = MAX(maxpwr, enhinfo[i].mimo3);
2685ffd7c74aSJoe Talbott
2686da10ea93SMatthew Dillon for (j = 0; j < ic->ic_nchans; j++) {
2687da10ea93SMatthew Dillon c = &ic->ic_channels[j];
2688da10ea93SMatthew Dillon if ((flags & IWN_ENHINFO_5GHZ)) {
2689da10ea93SMatthew Dillon if (!IEEE80211_IS_CHAN_A(c))
2690da10ea93SMatthew Dillon continue;
2691da10ea93SMatthew Dillon } else if ((flags & IWN_ENHINFO_OFDM)) {
2692da10ea93SMatthew Dillon if (!IEEE80211_IS_CHAN_G(c))
2693da10ea93SMatthew Dillon continue;
2694da10ea93SMatthew Dillon } else if (!IEEE80211_IS_CHAN_B(c))
2695da10ea93SMatthew Dillon continue;
2696da10ea93SMatthew Dillon if ((flags & IWN_ENHINFO_HT40)) {
2697da10ea93SMatthew Dillon if (!IEEE80211_IS_CHAN_HT40(c))
2698da10ea93SMatthew Dillon continue;
2699da10ea93SMatthew Dillon } else {
2700da10ea93SMatthew Dillon if (IEEE80211_IS_CHAN_HT40(c))
2701da10ea93SMatthew Dillon continue;
2702ffd7c74aSJoe Talbott }
2703da10ea93SMatthew Dillon if (enhinfo[i].chan != 0 &&
2704da10ea93SMatthew Dillon enhinfo[i].chan != c->ic_ieee)
2705da10ea93SMatthew Dillon continue;
2706da10ea93SMatthew Dillon
2707da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_RESET,
2708da10ea93SMatthew Dillon "channel %d(%x), maxpwr %d\n", c->ic_ieee,
2709da10ea93SMatthew Dillon c->ic_flags, maxpwr / 2);
2710da10ea93SMatthew Dillon c->ic_maxregpower = maxpwr / 2;
2711da10ea93SMatthew Dillon c->ic_maxpower = maxpwr;
2712da10ea93SMatthew Dillon }
2713da10ea93SMatthew Dillon }
2714da10ea93SMatthew Dillon
2715da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s end\n", __func__);
2716da10ea93SMatthew Dillon
2717ffd7c74aSJoe Talbott }
2718ffd7c74aSJoe Talbott
2719ffd7c74aSJoe Talbott static struct ieee80211_node *
iwn_node_alloc(struct ieee80211vap * vap,const uint8_t mac[IEEE80211_ADDR_LEN])2720ffd7c74aSJoe Talbott iwn_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN])
2721ffd7c74aSJoe Talbott {
2722977fc0dbSMatthew Dillon return kmalloc(sizeof (struct iwn_node), M_80211_NODE,
2723977fc0dbSMatthew Dillon M_INTWAIT | M_ZERO);
2724da10ea93SMatthew Dillon }
2725da10ea93SMatthew Dillon
2726da10ea93SMatthew Dillon static __inline int
rate2plcp(int rate)2727da10ea93SMatthew Dillon rate2plcp(int rate)
2728da10ea93SMatthew Dillon {
2729da10ea93SMatthew Dillon switch (rate & 0xff) {
2730da10ea93SMatthew Dillon case 12: return 0xd;
2731da10ea93SMatthew Dillon case 18: return 0xf;
2732da10ea93SMatthew Dillon case 24: return 0x5;
2733da10ea93SMatthew Dillon case 36: return 0x7;
2734da10ea93SMatthew Dillon case 48: return 0x9;
2735da10ea93SMatthew Dillon case 72: return 0xb;
2736da10ea93SMatthew Dillon case 96: return 0x1;
2737da10ea93SMatthew Dillon case 108: return 0x3;
2738da10ea93SMatthew Dillon case 2: return 10;
2739da10ea93SMatthew Dillon case 4: return 20;
2740da10ea93SMatthew Dillon case 11: return 55;
2741da10ea93SMatthew Dillon case 22: return 110;
2742da10ea93SMatthew Dillon }
2743da10ea93SMatthew Dillon return 0;
2744da10ea93SMatthew Dillon }
2745da10ea93SMatthew Dillon
274605538f72SMatthew Dillon static int
iwn_get_1stream_tx_antmask(struct iwn_softc * sc)274705538f72SMatthew Dillon iwn_get_1stream_tx_antmask(struct iwn_softc *sc)
274805538f72SMatthew Dillon {
274905538f72SMatthew Dillon
275005538f72SMatthew Dillon return IWN_LSB(sc->txchainmask);
275105538f72SMatthew Dillon }
275205538f72SMatthew Dillon
275305538f72SMatthew Dillon static int
iwn_get_2stream_tx_antmask(struct iwn_softc * sc)275405538f72SMatthew Dillon iwn_get_2stream_tx_antmask(struct iwn_softc *sc)
275505538f72SMatthew Dillon {
275605538f72SMatthew Dillon int tx;
275705538f72SMatthew Dillon
275805538f72SMatthew Dillon /*
275905538f72SMatthew Dillon * The '2 stream' setup is a bit .. odd.
276005538f72SMatthew Dillon *
276105538f72SMatthew Dillon * For NICs that support only 1 antenna, default to IWN_ANT_AB or
276205538f72SMatthew Dillon * the firmware panics (eg Intel 5100.)
276305538f72SMatthew Dillon *
276405538f72SMatthew Dillon * For NICs that support two antennas, we use ANT_AB.
276505538f72SMatthew Dillon *
276605538f72SMatthew Dillon * For NICs that support three antennas, we use the two that
276705538f72SMatthew Dillon * wasn't the default one.
276805538f72SMatthew Dillon *
276905538f72SMatthew Dillon * XXX TODO: if bluetooth (full concurrent) is enabled, restrict
277005538f72SMatthew Dillon * this to only one antenna.
277105538f72SMatthew Dillon */
277205538f72SMatthew Dillon
277305538f72SMatthew Dillon /* Default - transmit on the other antennas */
277405538f72SMatthew Dillon tx = (sc->txchainmask & ~IWN_LSB(sc->txchainmask));
277505538f72SMatthew Dillon
277605538f72SMatthew Dillon /* Now, if it's zero, set it to IWN_ANT_AB, so to not panic firmware */
277705538f72SMatthew Dillon if (tx == 0)
277805538f72SMatthew Dillon tx = IWN_ANT_AB;
277905538f72SMatthew Dillon
278005538f72SMatthew Dillon /*
278105538f72SMatthew Dillon * If the NIC is a two-stream TX NIC, configure the TX mask to
278205538f72SMatthew Dillon * the default chainmask
278305538f72SMatthew Dillon */
278405538f72SMatthew Dillon else if (sc->ntxchains == 2)
278505538f72SMatthew Dillon tx = sc->txchainmask;
278605538f72SMatthew Dillon
278705538f72SMatthew Dillon return (tx);
278805538f72SMatthew Dillon }
278905538f72SMatthew Dillon
279005538f72SMatthew Dillon
279105538f72SMatthew Dillon
2792da10ea93SMatthew Dillon /*
2793da10ea93SMatthew Dillon * Calculate the required PLCP value from the given rate,
2794da10ea93SMatthew Dillon * to the given node.
2795da10ea93SMatthew Dillon *
2796da10ea93SMatthew Dillon * This will take the node configuration (eg 11n, rate table
2797da10ea93SMatthew Dillon * setup, etc) into consideration.
2798da10ea93SMatthew Dillon */
2799da10ea93SMatthew Dillon static uint32_t
iwn_rate_to_plcp(struct iwn_softc * sc,struct ieee80211_node * ni,uint8_t rate)2800da10ea93SMatthew Dillon iwn_rate_to_plcp(struct iwn_softc *sc, struct ieee80211_node *ni,
2801da10ea93SMatthew Dillon uint8_t rate)
2802da10ea93SMatthew Dillon {
2803da10ea93SMatthew Dillon struct ieee80211com *ic = ni->ni_ic;
2804da10ea93SMatthew Dillon uint32_t plcp = 0;
2805da10ea93SMatthew Dillon int ridx;
2806da10ea93SMatthew Dillon
2807da10ea93SMatthew Dillon /*
2808da10ea93SMatthew Dillon * If it's an MCS rate, let's set the plcp correctly
2809da10ea93SMatthew Dillon * and set the relevant flags based on the node config.
2810da10ea93SMatthew Dillon */
2811da10ea93SMatthew Dillon if (rate & IEEE80211_RATE_MCS) {
2812da10ea93SMatthew Dillon /*
2813da10ea93SMatthew Dillon * Set the initial PLCP value to be between 0->31 for
2814da10ea93SMatthew Dillon * MCS 0 -> MCS 31, then set the "I'm an MCS rate!"
2815da10ea93SMatthew Dillon * flag.
2816da10ea93SMatthew Dillon */
2817977fc0dbSMatthew Dillon plcp = IEEE80211_RV(rate) | IWN_RFLAG_MCS;
2818da10ea93SMatthew Dillon
2819da10ea93SMatthew Dillon /*
2820da10ea93SMatthew Dillon * XXX the following should only occur if both
2821da10ea93SMatthew Dillon * the local configuration _and_ the remote node
2822da10ea93SMatthew Dillon * advertise these capabilities. Thus this code
2823da10ea93SMatthew Dillon * may need fixing!
2824da10ea93SMatthew Dillon */
2825da10ea93SMatthew Dillon
2826da10ea93SMatthew Dillon /*
2827da10ea93SMatthew Dillon * Set the channel width and guard interval.
2828da10ea93SMatthew Dillon */
2829da10ea93SMatthew Dillon if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) {
2830da10ea93SMatthew Dillon plcp |= IWN_RFLAG_HT40;
2831da10ea93SMatthew Dillon if (ni->ni_htcap & IEEE80211_HTCAP_SHORTGI40)
2832da10ea93SMatthew Dillon plcp |= IWN_RFLAG_SGI;
2833da10ea93SMatthew Dillon } else if (ni->ni_htcap & IEEE80211_HTCAP_SHORTGI20) {
2834da10ea93SMatthew Dillon plcp |= IWN_RFLAG_SGI;
2835da10ea93SMatthew Dillon }
2836da10ea93SMatthew Dillon
2837da10ea93SMatthew Dillon /*
283805538f72SMatthew Dillon * Ensure the selected rate matches the link quality
283905538f72SMatthew Dillon * table entries being used.
2840da10ea93SMatthew Dillon */
284105538f72SMatthew Dillon if (rate > 0x8f)
284205538f72SMatthew Dillon plcp |= IWN_RFLAG_ANT(sc->txchainmask);
284305538f72SMatthew Dillon else if (rate > 0x87)
284405538f72SMatthew Dillon plcp |= IWN_RFLAG_ANT(iwn_get_2stream_tx_antmask(sc));
2845da10ea93SMatthew Dillon else
284605538f72SMatthew Dillon plcp |= IWN_RFLAG_ANT(iwn_get_1stream_tx_antmask(sc));
2847da10ea93SMatthew Dillon } else {
2848da10ea93SMatthew Dillon /*
2849da10ea93SMatthew Dillon * Set the initial PLCP - fine for both
2850da10ea93SMatthew Dillon * OFDM and CCK rates.
2851da10ea93SMatthew Dillon */
2852da10ea93SMatthew Dillon plcp = rate2plcp(rate);
2853da10ea93SMatthew Dillon
2854da10ea93SMatthew Dillon /* Set CCK flag if it's CCK */
2855da10ea93SMatthew Dillon
2856da10ea93SMatthew Dillon /* XXX It would be nice to have a method
2857da10ea93SMatthew Dillon * to map the ridx -> phy table entry
2858da10ea93SMatthew Dillon * so we could just query that, rather than
2859da10ea93SMatthew Dillon * this hack to check against IWN_RIDX_OFDM6.
2860da10ea93SMatthew Dillon */
2861da10ea93SMatthew Dillon ridx = ieee80211_legacy_rate_lookup(ic->ic_rt,
2862da10ea93SMatthew Dillon rate & IEEE80211_RATE_VAL);
2863da10ea93SMatthew Dillon if (ridx < IWN_RIDX_OFDM6 &&
2864da10ea93SMatthew Dillon IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
2865da10ea93SMatthew Dillon plcp |= IWN_RFLAG_CCK;
2866da10ea93SMatthew Dillon
2867da10ea93SMatthew Dillon /* Set antenna configuration */
286805538f72SMatthew Dillon /* XXX TODO: is this the right antenna to use for legacy? */
286905538f72SMatthew Dillon plcp |= IWN_RFLAG_ANT(iwn_get_1stream_tx_antmask(sc));
2870da10ea93SMatthew Dillon }
2871da10ea93SMatthew Dillon
2872da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TXRATE, "%s: rate=0x%02x, plcp=0x%08x\n",
2873da10ea93SMatthew Dillon __func__,
2874da10ea93SMatthew Dillon rate,
2875da10ea93SMatthew Dillon plcp);
2876da10ea93SMatthew Dillon
2877da10ea93SMatthew Dillon return (htole32(plcp));
2878ffd7c74aSJoe Talbott }
2879ffd7c74aSJoe Talbott
2880ffd7c74aSJoe Talbott static void
iwn_newassoc(struct ieee80211_node * ni,int isnew)2881ffd7c74aSJoe Talbott iwn_newassoc(struct ieee80211_node *ni, int isnew)
2882ffd7c74aSJoe Talbott {
2883da10ea93SMatthew Dillon /* Doesn't do anything at the moment */
2884ffd7c74aSJoe Talbott }
2885ffd7c74aSJoe Talbott
2886ffd7c74aSJoe Talbott static int
iwn_media_change(struct ifnet * ifp)2887ffd7c74aSJoe Talbott iwn_media_change(struct ifnet *ifp)
2888ffd7c74aSJoe Talbott {
2889da10ea93SMatthew Dillon int error;
2890da10ea93SMatthew Dillon
2891da10ea93SMatthew Dillon error = ieee80211_media_change(ifp);
2892ffd7c74aSJoe Talbott /* NB: only the fixed rate can change and that doesn't need a reset */
2893ffd7c74aSJoe Talbott return (error == ENETRESET ? 0 : error);
2894ffd7c74aSJoe Talbott }
2895ffd7c74aSJoe Talbott
2896ffd7c74aSJoe Talbott static int
iwn_newstate(struct ieee80211vap * vap,enum ieee80211_state nstate,int arg)2897ffd7c74aSJoe Talbott iwn_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
2898ffd7c74aSJoe Talbott {
2899ffd7c74aSJoe Talbott struct iwn_vap *ivp = IWN_VAP(vap);
2900ffd7c74aSJoe Talbott struct ieee80211com *ic = vap->iv_ic;
29014f1aaf2fSImre Vadász struct iwn_softc *sc = ic->ic_softc;
2902da10ea93SMatthew Dillon int error = 0;
2903da10ea93SMatthew Dillon
2904da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
2905ffd7c74aSJoe Talbott
2906ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_STATE, "%s: %s -> %s\n", __func__,
2907da10ea93SMatthew Dillon ieee80211_state_name[vap->iv_state], ieee80211_state_name[nstate]);
2908ffd7c74aSJoe Talbott
290905538f72SMatthew Dillon IEEE80211_UNLOCK(ic);
291005538f72SMatthew Dillon IWN_LOCK(sc);
291105538f72SMatthew Dillon #if defined(__DragonFly__)
2912eb67213aSMatthew Dillon callout_cancel(&sc->calib_to);
291305538f72SMatthew Dillon #else
2914da10ea93SMatthew Dillon callout_stop(&sc->calib_to);
291505538f72SMatthew Dillon #endif
2916ffd7c74aSJoe Talbott
2917da10ea93SMatthew Dillon sc->rxon = &sc->rx_on[IWN_RXON_BSS_CTX];
2918da10ea93SMatthew Dillon
2919da10ea93SMatthew Dillon switch (nstate) {
2920da10ea93SMatthew Dillon case IEEE80211_S_ASSOC:
2921da10ea93SMatthew Dillon if (vap->iv_state != IEEE80211_S_RUN)
2922da10ea93SMatthew Dillon break;
2923da10ea93SMatthew Dillon /* FALLTHROUGH */
2924da10ea93SMatthew Dillon case IEEE80211_S_AUTH:
2925da10ea93SMatthew Dillon if (vap->iv_state == IEEE80211_S_AUTH)
2926da10ea93SMatthew Dillon break;
2927da10ea93SMatthew Dillon
2928da10ea93SMatthew Dillon /*
2929da10ea93SMatthew Dillon * !AUTH -> AUTH transition requires state reset to handle
2930da10ea93SMatthew Dillon * reassociations correctly.
2931da10ea93SMatthew Dillon */
2932da10ea93SMatthew Dillon sc->rxon->associd = 0;
2933da10ea93SMatthew Dillon sc->rxon->filter &= ~htole32(IWN_FILTER_BSS);
2934da10ea93SMatthew Dillon sc->calib.state = IWN_CALIB_STATE_INIT;
2935da10ea93SMatthew Dillon
2936977fc0dbSMatthew Dillon /* Wait until we hear a beacon before we transmit */
2937977fc0dbSMatthew Dillon if (IEEE80211_IS_CHAN_PASSIVE(ic->ic_curchan))
2938977fc0dbSMatthew Dillon sc->sc_beacon_wait = 1;
2939977fc0dbSMatthew Dillon
2940da10ea93SMatthew Dillon if ((error = iwn_auth(sc, vap)) != 0) {
2941da10ea93SMatthew Dillon device_printf(sc->sc_dev,
2942da10ea93SMatthew Dillon "%s: could not move to auth state\n", __func__);
2943ffd7c74aSJoe Talbott }
2944da10ea93SMatthew Dillon break;
2945da10ea93SMatthew Dillon
2946da10ea93SMatthew Dillon case IEEE80211_S_RUN:
2947da10ea93SMatthew Dillon /*
2948da10ea93SMatthew Dillon * RUN -> RUN transition; Just restart the timers.
2949da10ea93SMatthew Dillon */
2950da10ea93SMatthew Dillon if (vap->iv_state == IEEE80211_S_RUN) {
2951da10ea93SMatthew Dillon sc->calib_cnt = 0;
2952da10ea93SMatthew Dillon break;
2953da10ea93SMatthew Dillon }
2954da10ea93SMatthew Dillon
2955977fc0dbSMatthew Dillon /* Wait until we hear a beacon before we transmit */
2956977fc0dbSMatthew Dillon if (IEEE80211_IS_CHAN_PASSIVE(ic->ic_curchan))
2957977fc0dbSMatthew Dillon sc->sc_beacon_wait = 1;
2958977fc0dbSMatthew Dillon
2959ffd7c74aSJoe Talbott /*
2960ffd7c74aSJoe Talbott * !RUN -> RUN requires setting the association id
2961ffd7c74aSJoe Talbott * which is done with a firmware cmd. We also defer
2962ffd7c74aSJoe Talbott * starting the timers until that work is done.
2963ffd7c74aSJoe Talbott */
2964da10ea93SMatthew Dillon if ((error = iwn_run(sc, vap)) != 0) {
2965da10ea93SMatthew Dillon device_printf(sc->sc_dev,
2966da10ea93SMatthew Dillon "%s: could not move to run state\n", __func__);
2967ffd7c74aSJoe Talbott }
2968da10ea93SMatthew Dillon break;
2969da10ea93SMatthew Dillon
2970da10ea93SMatthew Dillon case IEEE80211_S_INIT:
2971da10ea93SMatthew Dillon sc->calib.state = IWN_CALIB_STATE_INIT;
2972977fc0dbSMatthew Dillon /*
2973977fc0dbSMatthew Dillon * Purge the xmit queue so we don't have old frames
2974977fc0dbSMatthew Dillon * during a new association attempt.
2975977fc0dbSMatthew Dillon */
2976977fc0dbSMatthew Dillon sc->sc_beacon_wait = 0;
2977977fc0dbSMatthew Dillon iwn_xmit_queue_drain(sc);
2978da10ea93SMatthew Dillon break;
2979da10ea93SMatthew Dillon
2980da10ea93SMatthew Dillon default:
2981da10ea93SMatthew Dillon break;
2982ffd7c74aSJoe Talbott }
298305538f72SMatthew Dillon IWN_UNLOCK(sc);
298405538f72SMatthew Dillon IEEE80211_LOCK(ic);
2985da10ea93SMatthew Dillon if (error != 0){
2986da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s end in error\n", __func__);
2987da10ea93SMatthew Dillon return error;
2988da10ea93SMatthew Dillon }
2989da10ea93SMatthew Dillon
2990da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
2991da10ea93SMatthew Dillon
2992ffd7c74aSJoe Talbott return ivp->iv_newstate(vap, nstate, arg);
2993ffd7c74aSJoe Talbott }
2994ffd7c74aSJoe Talbott
2995da10ea93SMatthew Dillon static void
iwn_calib_timeout(void * arg)2996da10ea93SMatthew Dillon iwn_calib_timeout(void *arg)
2997da10ea93SMatthew Dillon {
2998da10ea93SMatthew Dillon struct iwn_softc *sc = arg;
2999da10ea93SMatthew Dillon
300005538f72SMatthew Dillon IWN_LOCK_ASSERT(sc);
3001da10ea93SMatthew Dillon
3002da10ea93SMatthew Dillon /* Force automatic TX power calibration every 60 secs. */
3003da10ea93SMatthew Dillon if (++sc->calib_cnt >= 120) {
3004da10ea93SMatthew Dillon uint32_t flags = 0;
3005da10ea93SMatthew Dillon
3006da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s\n",
3007da10ea93SMatthew Dillon "sending request for statistics");
3008da10ea93SMatthew Dillon (void)iwn_cmd(sc, IWN_CMD_GET_STATISTICS, &flags,
3009da10ea93SMatthew Dillon sizeof flags, 1);
3010da10ea93SMatthew Dillon sc->calib_cnt = 0;
3011da10ea93SMatthew Dillon }
3012da10ea93SMatthew Dillon callout_reset(&sc->calib_to, msecs_to_ticks(500), iwn_calib_timeout,
3013da10ea93SMatthew Dillon sc);
3014da10ea93SMatthew Dillon }
3015da10ea93SMatthew Dillon
3016ffd7c74aSJoe Talbott /*
3017ffd7c74aSJoe Talbott * Process an RX_PHY firmware notification. This is usually immediately
3018ffd7c74aSJoe Talbott * followed by an MPDU_RX_DONE notification.
3019ffd7c74aSJoe Talbott */
3020ffd7c74aSJoe Talbott static void
iwn_rx_phy(struct iwn_softc * sc,struct iwn_rx_desc * desc,struct iwn_rx_data * data)3021ffd7c74aSJoe Talbott iwn_rx_phy(struct iwn_softc *sc, struct iwn_rx_desc *desc,
3022ffd7c74aSJoe Talbott struct iwn_rx_data *data)
3023ffd7c74aSJoe Talbott {
3024ffd7c74aSJoe Talbott struct iwn_rx_stat *stat = (struct iwn_rx_stat *)(desc + 1);
3025ffd7c74aSJoe Talbott
3026ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: received PHY stats\n", __func__);
3027ffd7c74aSJoe Talbott bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD);
3028ffd7c74aSJoe Talbott
3029ffd7c74aSJoe Talbott /* Save RX statistics, they will be used on MPDU_RX_DONE. */
3030ffd7c74aSJoe Talbott memcpy(&sc->last_rx_stat, stat, sizeof (*stat));
3031ffd7c74aSJoe Talbott sc->last_rx_valid = 1;
3032ffd7c74aSJoe Talbott }
3033ffd7c74aSJoe Talbott
3034ffd7c74aSJoe Talbott /*
3035ffd7c74aSJoe Talbott * Process an RX_DONE (4965AGN only) or MPDU_RX_DONE firmware notification.
3036ffd7c74aSJoe Talbott * Each MPDU_RX_DONE notification must be preceded by an RX_PHY one.
3037ffd7c74aSJoe Talbott */
3038ffd7c74aSJoe Talbott static void
iwn_rx_done(struct iwn_softc * sc,struct iwn_rx_desc * desc,struct iwn_rx_data * data)3039ffd7c74aSJoe Talbott iwn_rx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc,
3040ffd7c74aSJoe Talbott struct iwn_rx_data *data)
3041ffd7c74aSJoe Talbott {
3042da10ea93SMatthew Dillon struct iwn_ops *ops = &sc->ops;
3043977fc0dbSMatthew Dillon struct ieee80211com *ic = &sc->sc_ic;
3044ffd7c74aSJoe Talbott struct iwn_rx_ring *ring = &sc->rxq;
3045ffd7c74aSJoe Talbott struct ieee80211_frame *wh;
3046ffd7c74aSJoe Talbott struct ieee80211_node *ni;
3047ffd7c74aSJoe Talbott struct mbuf *m, *m1;
3048ffd7c74aSJoe Talbott struct iwn_rx_stat *stat;
3049ffd7c74aSJoe Talbott caddr_t head;
3050ffd7c74aSJoe Talbott bus_addr_t paddr;
3051ffd7c74aSJoe Talbott uint32_t flags;
3052ffd7c74aSJoe Talbott int error, len, rssi, nf;
3053ffd7c74aSJoe Talbott
3054da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
3055da10ea93SMatthew Dillon
3056ffd7c74aSJoe Talbott if (desc->type == IWN_MPDU_RX_DONE) {
3057ffd7c74aSJoe Talbott /* Check for prior RX_PHY notification. */
3058ffd7c74aSJoe Talbott if (!sc->last_rx_valid) {
3059ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_ANY,
3060ffd7c74aSJoe Talbott "%s: missing RX_PHY\n", __func__);
3061ffd7c74aSJoe Talbott return;
3062ffd7c74aSJoe Talbott }
3063ffd7c74aSJoe Talbott stat = &sc->last_rx_stat;
3064ffd7c74aSJoe Talbott } else
3065ffd7c74aSJoe Talbott stat = (struct iwn_rx_stat *)(desc + 1);
3066ffd7c74aSJoe Talbott
3067ffd7c74aSJoe Talbott bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD);
3068ffd7c74aSJoe Talbott
3069ffd7c74aSJoe Talbott if (stat->cfg_phy_len > IWN_STAT_MAXLEN) {
3070ffd7c74aSJoe Talbott device_printf(sc->sc_dev,
3071da10ea93SMatthew Dillon "%s: invalid RX statistic header, len %d\n", __func__,
3072da10ea93SMatthew Dillon stat->cfg_phy_len);
3073ffd7c74aSJoe Talbott return;
3074ffd7c74aSJoe Talbott }
3075ffd7c74aSJoe Talbott if (desc->type == IWN_MPDU_RX_DONE) {
3076ffd7c74aSJoe Talbott struct iwn_rx_mpdu *mpdu = (struct iwn_rx_mpdu *)(desc + 1);
3077ffd7c74aSJoe Talbott head = (caddr_t)(mpdu + 1);
3078ffd7c74aSJoe Talbott len = le16toh(mpdu->len);
3079ffd7c74aSJoe Talbott } else {
3080ffd7c74aSJoe Talbott head = (caddr_t)(stat + 1) + stat->cfg_phy_len;
3081ffd7c74aSJoe Talbott len = le16toh(stat->len);
3082ffd7c74aSJoe Talbott }
3083ffd7c74aSJoe Talbott
3084ffd7c74aSJoe Talbott flags = le32toh(*(uint32_t *)(head + len));
3085ffd7c74aSJoe Talbott
3086ffd7c74aSJoe Talbott /* Discard frames with a bad FCS early. */
3087ffd7c74aSJoe Talbott if ((flags & IWN_RX_NOERROR) != IWN_RX_NOERROR) {
3088da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_RECV, "%s: RX flags error %x\n",
3089ffd7c74aSJoe Talbott __func__, flags);
309005538f72SMatthew Dillon #if defined(__DragonFly__)
3091977fc0dbSMatthew Dillon ++ic->ic_ierrors;
309205538f72SMatthew Dillon #else
3093977fc0dbSMatthew Dillon counter_u64_add(ic->ic_ierrors, 1);
309405538f72SMatthew Dillon #endif
3095ffd7c74aSJoe Talbott return;
3096ffd7c74aSJoe Talbott }
3097ffd7c74aSJoe Talbott /* Discard frames that are too short. */
3098977fc0dbSMatthew Dillon if (len < sizeof (struct ieee80211_frame_ack)) {
3099ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_RECV, "%s: frame too short: %d\n",
3100ffd7c74aSJoe Talbott __func__, len);
310105538f72SMatthew Dillon #if defined(__DragonFly__)
3102977fc0dbSMatthew Dillon ++ic->ic_ierrors;
310305538f72SMatthew Dillon #else
3104977fc0dbSMatthew Dillon counter_u64_add(ic->ic_ierrors, 1);
310505538f72SMatthew Dillon #endif
3106ffd7c74aSJoe Talbott return;
3107ffd7c74aSJoe Talbott }
3108ffd7c74aSJoe Talbott
3109b5523eacSSascha Wildner m1 = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, IWN_RBUF_SIZE);
3110ffd7c74aSJoe Talbott if (m1 == NULL) {
3111ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_ANY, "%s: no mbuf to restock ring\n",
3112ffd7c74aSJoe Talbott __func__);
311305538f72SMatthew Dillon #if defined(__DragonFly__)
3114977fc0dbSMatthew Dillon ++ic->ic_ierrors;
311505538f72SMatthew Dillon #else
3116977fc0dbSMatthew Dillon counter_u64_add(ic->ic_ierrors, 1);
311705538f72SMatthew Dillon #endif
3118ffd7c74aSJoe Talbott return;
3119ffd7c74aSJoe Talbott }
3120ffd7c74aSJoe Talbott bus_dmamap_unload(ring->data_dmat, data->map);
3121ffd7c74aSJoe Talbott
3122da10ea93SMatthew Dillon error = bus_dmamap_load(ring->data_dmat, data->map, mtod(m1, void *),
3123da10ea93SMatthew Dillon IWN_RBUF_SIZE, iwn_dma_map_addr, &paddr, BUS_DMA_NOWAIT);
3124ffd7c74aSJoe Talbott if (error != 0 && error != EFBIG) {
3125ffd7c74aSJoe Talbott device_printf(sc->sc_dev,
3126ffd7c74aSJoe Talbott "%s: bus_dmamap_load failed, error %d\n", __func__, error);
3127ffd7c74aSJoe Talbott m_freem(m1);
3128da10ea93SMatthew Dillon
3129da10ea93SMatthew Dillon /* Try to reload the old mbuf. */
3130da10ea93SMatthew Dillon error = bus_dmamap_load(ring->data_dmat, data->map,
3131da10ea93SMatthew Dillon mtod(data->m, void *), IWN_RBUF_SIZE, iwn_dma_map_addr,
3132da10ea93SMatthew Dillon &paddr, BUS_DMA_NOWAIT);
3133da10ea93SMatthew Dillon if (error != 0 && error != EFBIG) {
3134da10ea93SMatthew Dillon panic("%s: could not load old RX mbuf", __func__);
3135da10ea93SMatthew Dillon }
3136da10ea93SMatthew Dillon /* Physical address may have changed. */
3137da10ea93SMatthew Dillon ring->desc[ring->cur] = htole32(paddr >> 8);
3138da10ea93SMatthew Dillon bus_dmamap_sync(ring->data_dmat, ring->desc_dma.map,
3139da10ea93SMatthew Dillon BUS_DMASYNC_PREWRITE);
314005538f72SMatthew Dillon #if defined(__DragonFly__)
3141977fc0dbSMatthew Dillon ++ic->ic_ierrors;
314205538f72SMatthew Dillon #else
3143977fc0dbSMatthew Dillon counter_u64_add(ic->ic_ierrors, 1);
314405538f72SMatthew Dillon #endif
3145ffd7c74aSJoe Talbott return;
3146ffd7c74aSJoe Talbott }
3147ffd7c74aSJoe Talbott
3148ffd7c74aSJoe Talbott m = data->m;
3149ffd7c74aSJoe Talbott data->m = m1;
3150ffd7c74aSJoe Talbott /* Update RX descriptor. */
3151ffd7c74aSJoe Talbott ring->desc[ring->cur] = htole32(paddr >> 8);
3152ffd7c74aSJoe Talbott bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
3153ffd7c74aSJoe Talbott BUS_DMASYNC_PREWRITE);
3154ffd7c74aSJoe Talbott
3155ffd7c74aSJoe Talbott /* Finalize mbuf. */
3156ffd7c74aSJoe Talbott m->m_data = head;
3157ffd7c74aSJoe Talbott m->m_pkthdr.len = m->m_len = len;
3158ffd7c74aSJoe Talbott
3159ffd7c74aSJoe Talbott /* Grab a reference to the source node. */
3160ffd7c74aSJoe Talbott wh = mtod(m, struct ieee80211_frame *);
3161977fc0dbSMatthew Dillon if (len >= sizeof(struct ieee80211_frame_min))
3162ffd7c74aSJoe Talbott ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh);
3163977fc0dbSMatthew Dillon else
3164977fc0dbSMatthew Dillon ni = NULL;
3165ffd7c74aSJoe Talbott nf = (ni != NULL && ni->ni_vap->iv_state == IEEE80211_S_RUN &&
3166ffd7c74aSJoe Talbott (ic->ic_flags & IEEE80211_F_SCAN) == 0) ? sc->noise : -95;
3167ffd7c74aSJoe Talbott
3168da10ea93SMatthew Dillon rssi = ops->get_rssi(sc, stat);
3169da10ea93SMatthew Dillon
3170ffd7c74aSJoe Talbott if (ieee80211_radiotap_active(ic)) {
3171ffd7c74aSJoe Talbott struct iwn_rx_radiotap_header *tap = &sc->sc_rxtap;
3172ffd7c74aSJoe Talbott
3173ffd7c74aSJoe Talbott tap->wr_flags = 0;
3174ffd7c74aSJoe Talbott if (stat->flags & htole16(IWN_STAT_FLAG_SHPREAMBLE))
3175ffd7c74aSJoe Talbott tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
3176da10ea93SMatthew Dillon tap->wr_dbm_antsignal = (int8_t)rssi;
3177da10ea93SMatthew Dillon tap->wr_dbm_antnoise = (int8_t)nf;
3178da10ea93SMatthew Dillon tap->wr_tsft = stat->tstamp;
3179ffd7c74aSJoe Talbott switch (stat->rate) {
3180ffd7c74aSJoe Talbott /* CCK rates. */
3181ffd7c74aSJoe Talbott case 10: tap->wr_rate = 2; break;
3182ffd7c74aSJoe Talbott case 20: tap->wr_rate = 4; break;
3183ffd7c74aSJoe Talbott case 55: tap->wr_rate = 11; break;
3184ffd7c74aSJoe Talbott case 110: tap->wr_rate = 22; break;
3185ffd7c74aSJoe Talbott /* OFDM rates. */
3186ffd7c74aSJoe Talbott case 0xd: tap->wr_rate = 12; break;
3187ffd7c74aSJoe Talbott case 0xf: tap->wr_rate = 18; break;
3188ffd7c74aSJoe Talbott case 0x5: tap->wr_rate = 24; break;
3189ffd7c74aSJoe Talbott case 0x7: tap->wr_rate = 36; break;
3190ffd7c74aSJoe Talbott case 0x9: tap->wr_rate = 48; break;
3191ffd7c74aSJoe Talbott case 0xb: tap->wr_rate = 72; break;
3192ffd7c74aSJoe Talbott case 0x1: tap->wr_rate = 96; break;
3193ffd7c74aSJoe Talbott case 0x3: tap->wr_rate = 108; break;
3194ffd7c74aSJoe Talbott /* Unknown rate: should not happen. */
3195ffd7c74aSJoe Talbott default: tap->wr_rate = 0;
3196ffd7c74aSJoe Talbott }
3197ffd7c74aSJoe Talbott }
3198ffd7c74aSJoe Talbott
3199977fc0dbSMatthew Dillon /*
3200977fc0dbSMatthew Dillon * If it's a beacon and we're waiting, then do the
3201977fc0dbSMatthew Dillon * wakeup. This should unblock raw_xmit/start.
3202977fc0dbSMatthew Dillon */
3203977fc0dbSMatthew Dillon if (sc->sc_beacon_wait) {
3204977fc0dbSMatthew Dillon uint8_t type, subtype;
3205977fc0dbSMatthew Dillon /* NB: Re-assign wh */
3206977fc0dbSMatthew Dillon wh = mtod(m, struct ieee80211_frame *);
3207977fc0dbSMatthew Dillon type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
3208977fc0dbSMatthew Dillon subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
3209977fc0dbSMatthew Dillon /*
3210977fc0dbSMatthew Dillon * This assumes at this point we've received our own
3211977fc0dbSMatthew Dillon * beacon.
3212977fc0dbSMatthew Dillon */
3213977fc0dbSMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE,
3214977fc0dbSMatthew Dillon "%s: beacon_wait, type=%d, subtype=%d\n",
3215977fc0dbSMatthew Dillon __func__, type, subtype);
3216977fc0dbSMatthew Dillon if (type == IEEE80211_FC0_TYPE_MGT &&
3217977fc0dbSMatthew Dillon subtype == IEEE80211_FC0_SUBTYPE_BEACON) {
3218977fc0dbSMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE | IWN_DEBUG_XMIT,
3219977fc0dbSMatthew Dillon "%s: waking things up\n", __func__);
3220977fc0dbSMatthew Dillon /* queue taskqueue to transmit! */
3221977fc0dbSMatthew Dillon taskqueue_enqueue(sc->sc_tq, &sc->sc_xmit_task);
3222977fc0dbSMatthew Dillon }
3223977fc0dbSMatthew Dillon }
3224977fc0dbSMatthew Dillon
322505538f72SMatthew Dillon IWN_UNLOCK(sc);
322605538f72SMatthew Dillon
3227ffd7c74aSJoe Talbott /* Send the frame to the 802.11 layer. */
3228ffd7c74aSJoe Talbott if (ni != NULL) {
3229da10ea93SMatthew Dillon if (ni->ni_flags & IEEE80211_NODE_HT)
3230da10ea93SMatthew Dillon m->m_flags |= M_AMPDU;
3231ffd7c74aSJoe Talbott (void)ieee80211_input(ni, m, rssi - nf, nf);
3232ffd7c74aSJoe Talbott /* Node is no longer needed. */
3233ffd7c74aSJoe Talbott ieee80211_free_node(ni);
323405538f72SMatthew Dillon } else
3235ffd7c74aSJoe Talbott (void)ieee80211_input_all(ic, m, rssi - nf, nf);
323605538f72SMatthew Dillon
323705538f72SMatthew Dillon IWN_LOCK(sc);
3238da10ea93SMatthew Dillon
3239da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
3240da10ea93SMatthew Dillon
3241ffd7c74aSJoe Talbott }
3242ffd7c74aSJoe Talbott
3243ffd7c74aSJoe Talbott /* Process an incoming Compressed BlockAck. */
3244ffd7c74aSJoe Talbott static void
iwn_rx_compressed_ba(struct iwn_softc * sc,struct iwn_rx_desc * desc,struct iwn_rx_data * data)3245ffd7c74aSJoe Talbott iwn_rx_compressed_ba(struct iwn_softc *sc, struct iwn_rx_desc *desc,
3246ffd7c74aSJoe Talbott struct iwn_rx_data *data)
3247ffd7c74aSJoe Talbott {
3248da10ea93SMatthew Dillon struct iwn_ops *ops = &sc->ops;
3249da10ea93SMatthew Dillon struct iwn_node *wn;
3250da10ea93SMatthew Dillon struct ieee80211_node *ni;
3251ffd7c74aSJoe Talbott struct iwn_compressed_ba *ba = (struct iwn_compressed_ba *)(desc + 1);
3252ffd7c74aSJoe Talbott struct iwn_tx_ring *txq;
3253da10ea93SMatthew Dillon struct iwn_tx_data *txdata;
3254da10ea93SMatthew Dillon struct ieee80211_tx_ampdu *tap;
3255da10ea93SMatthew Dillon struct mbuf *m;
3256da10ea93SMatthew Dillon uint64_t bitmap;
3257da10ea93SMatthew Dillon uint16_t ssn;
3258da10ea93SMatthew Dillon uint8_t tid;
3259da10ea93SMatthew Dillon int ackfailcnt = 0, i, lastidx, qid, *res, shift;
326005538f72SMatthew Dillon int tx_ok = 0, tx_err = 0;
3261ffd7c74aSJoe Talbott
326205538f72SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE | IWN_DEBUG_XMIT, "->%s begin\n", __func__);
3263da10ea93SMatthew Dillon
3264da10ea93SMatthew Dillon bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD);
3265da10ea93SMatthew Dillon
3266da10ea93SMatthew Dillon qid = le16toh(ba->qid);
3267da10ea93SMatthew Dillon txq = &sc->txq[ba->qid];
3268da10ea93SMatthew Dillon tap = sc->qid2tap[ba->qid];
3269085ff963SMatthew Dillon tid = tap->txa_tid;
3270da10ea93SMatthew Dillon wn = (void *)tap->txa_ni;
3271da10ea93SMatthew Dillon
3272da10ea93SMatthew Dillon res = NULL;
3273da10ea93SMatthew Dillon ssn = 0;
3274da10ea93SMatthew Dillon if (!IEEE80211_AMPDU_RUNNING(tap)) {
3275da10ea93SMatthew Dillon res = tap->txa_private;
3276da10ea93SMatthew Dillon ssn = tap->txa_start & 0xfff;
3277ffd7c74aSJoe Talbott }
3278da10ea93SMatthew Dillon
3279da10ea93SMatthew Dillon for (lastidx = le16toh(ba->ssn) & 0xff; txq->read != lastidx;) {
3280da10ea93SMatthew Dillon txdata = &txq->data[txq->read];
3281da10ea93SMatthew Dillon
3282da10ea93SMatthew Dillon /* Unmap and free mbuf. */
3283da10ea93SMatthew Dillon bus_dmamap_sync(txq->data_dmat, txdata->map,
3284da10ea93SMatthew Dillon BUS_DMASYNC_POSTWRITE);
3285da10ea93SMatthew Dillon bus_dmamap_unload(txq->data_dmat, txdata->map);
3286da10ea93SMatthew Dillon m = txdata->m, txdata->m = NULL;
3287da10ea93SMatthew Dillon ni = txdata->ni, txdata->ni = NULL;
3288da10ea93SMatthew Dillon
3289da10ea93SMatthew Dillon KASSERT(ni != NULL, ("no node"));
3290da10ea93SMatthew Dillon KASSERT(m != NULL, ("no mbuf"));
3291da10ea93SMatthew Dillon
329205538f72SMatthew Dillon DPRINTF(sc, IWN_DEBUG_XMIT, "%s: freeing m=%p\n", __func__, m);
3293da10ea93SMatthew Dillon ieee80211_tx_complete(ni, m, 1);
3294da10ea93SMatthew Dillon
3295da10ea93SMatthew Dillon txq->queued--;
3296da10ea93SMatthew Dillon txq->read = (txq->read + 1) % IWN_TX_RING_COUNT;
3297da10ea93SMatthew Dillon }
3298da10ea93SMatthew Dillon
3299da10ea93SMatthew Dillon if (txq->queued == 0 && res != NULL) {
3300da10ea93SMatthew Dillon iwn_nic_lock(sc);
3301da10ea93SMatthew Dillon ops->ampdu_tx_stop(sc, qid, tid, ssn);
3302da10ea93SMatthew Dillon iwn_nic_unlock(sc);
3303da10ea93SMatthew Dillon sc->qid2tap[qid] = NULL;
3304101554a6SMatthew Dillon kfree(res, M_DEVBUF);
3305da10ea93SMatthew Dillon return;
3306da10ea93SMatthew Dillon }
3307da10ea93SMatthew Dillon
3308da10ea93SMatthew Dillon if (wn->agg[tid].bitmap == 0)
3309da10ea93SMatthew Dillon return;
3310da10ea93SMatthew Dillon
3311da10ea93SMatthew Dillon shift = wn->agg[tid].startidx - ((le16toh(ba->seq) >> 4) & 0xff);
3312da10ea93SMatthew Dillon if (shift < 0)
3313da10ea93SMatthew Dillon shift += 0x100;
3314da10ea93SMatthew Dillon
3315da10ea93SMatthew Dillon if (wn->agg[tid].nframes > (64 - shift))
3316da10ea93SMatthew Dillon return;
3317da10ea93SMatthew Dillon
331805538f72SMatthew Dillon /*
331905538f72SMatthew Dillon * Walk the bitmap and calculate how many successful and failed
332005538f72SMatthew Dillon * attempts are made.
332105538f72SMatthew Dillon *
332205538f72SMatthew Dillon * Yes, the rate control code doesn't know these are A-MPDU
332305538f72SMatthew Dillon * subframes and that it's okay to fail some of these.
332405538f72SMatthew Dillon */
3325da10ea93SMatthew Dillon ni = tap->txa_ni;
3326da10ea93SMatthew Dillon bitmap = (le64toh(ba->bitmap) >> shift) & wn->agg[tid].bitmap;
3327da10ea93SMatthew Dillon for (i = 0; bitmap; i++) {
3328da10ea93SMatthew Dillon if ((bitmap & 1) == 0) {
332905538f72SMatthew Dillon tx_err ++;
3330da10ea93SMatthew Dillon ieee80211_ratectl_tx_complete(ni->ni_vap, ni,
3331da10ea93SMatthew Dillon IEEE80211_RATECTL_TX_FAILURE, &ackfailcnt, NULL);
3332da10ea93SMatthew Dillon } else {
333305538f72SMatthew Dillon tx_ok ++;
3334da10ea93SMatthew Dillon ieee80211_ratectl_tx_complete(ni->ni_vap, ni,
3335da10ea93SMatthew Dillon IEEE80211_RATECTL_TX_SUCCESS, &ackfailcnt, NULL);
3336da10ea93SMatthew Dillon }
3337da10ea93SMatthew Dillon bitmap >>= 1;
3338da10ea93SMatthew Dillon }
3339da10ea93SMatthew Dillon
334005538f72SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE | IWN_DEBUG_XMIT,
334105538f72SMatthew Dillon "->%s: end; %d ok; %d err\n",__func__, tx_ok, tx_err);
3342da10ea93SMatthew Dillon
3343da10ea93SMatthew Dillon }
3344ffd7c74aSJoe Talbott
3345ffd7c74aSJoe Talbott /*
3346ffd7c74aSJoe Talbott * Process a CALIBRATION_RESULT notification sent by the initialization
3347da10ea93SMatthew Dillon * firmware on response to a CMD_CALIB_CONFIG command (5000 only).
3348ffd7c74aSJoe Talbott */
3349ffd7c74aSJoe Talbott static void
iwn5000_rx_calib_results(struct iwn_softc * sc,struct iwn_rx_desc * desc,struct iwn_rx_data * data)3350ffd7c74aSJoe Talbott iwn5000_rx_calib_results(struct iwn_softc *sc, struct iwn_rx_desc *desc,
3351ffd7c74aSJoe Talbott struct iwn_rx_data *data)
3352ffd7c74aSJoe Talbott {
3353ffd7c74aSJoe Talbott struct iwn_phy_calib *calib = (struct iwn_phy_calib *)(desc + 1);
3354ffd7c74aSJoe Talbott int len, idx = -1;
3355ffd7c74aSJoe Talbott
3356da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
3357ffd7c74aSJoe Talbott
3358da10ea93SMatthew Dillon /* Runtime firmware should not send such a notification. */
3359da10ea93SMatthew Dillon if (sc->sc_flags & IWN_FLAG_CALIB_DONE){
3360da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s received after clib done\n",
3361da10ea93SMatthew Dillon __func__);
3362da10ea93SMatthew Dillon return;
3363da10ea93SMatthew Dillon }
3364ffd7c74aSJoe Talbott len = (le32toh(desc->len) & 0x3fff) - 4;
3365da10ea93SMatthew Dillon bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD);
3366ffd7c74aSJoe Talbott
3367ffd7c74aSJoe Talbott switch (calib->code) {
3368ffd7c74aSJoe Talbott case IWN5000_PHY_CALIB_DC:
3369da10ea93SMatthew Dillon if (sc->base_params->calib_need & IWN_FLG_NEED_PHY_CALIB_DC)
3370ffd7c74aSJoe Talbott idx = 0;
3371ffd7c74aSJoe Talbott break;
3372ffd7c74aSJoe Talbott case IWN5000_PHY_CALIB_LO:
3373da10ea93SMatthew Dillon if (sc->base_params->calib_need & IWN_FLG_NEED_PHY_CALIB_LO)
3374ffd7c74aSJoe Talbott idx = 1;
3375ffd7c74aSJoe Talbott break;
3376ffd7c74aSJoe Talbott case IWN5000_PHY_CALIB_TX_IQ:
3377da10ea93SMatthew Dillon if (sc->base_params->calib_need & IWN_FLG_NEED_PHY_CALIB_TX_IQ)
3378ffd7c74aSJoe Talbott idx = 2;
3379ffd7c74aSJoe Talbott break;
3380ffd7c74aSJoe Talbott case IWN5000_PHY_CALIB_TX_IQ_PERIODIC:
3381da10ea93SMatthew Dillon if (sc->base_params->calib_need & IWN_FLG_NEED_PHY_CALIB_TX_IQ_PERIODIC)
3382ffd7c74aSJoe Talbott idx = 3;
3383ffd7c74aSJoe Talbott break;
3384ffd7c74aSJoe Talbott case IWN5000_PHY_CALIB_BASE_BAND:
3385da10ea93SMatthew Dillon if (sc->base_params->calib_need & IWN_FLG_NEED_PHY_CALIB_BASE_BAND)
3386ffd7c74aSJoe Talbott idx = 4;
3387ffd7c74aSJoe Talbott break;
3388ffd7c74aSJoe Talbott }
3389ffd7c74aSJoe Talbott if (idx == -1) /* Ignore other results. */
3390ffd7c74aSJoe Talbott return;
3391ffd7c74aSJoe Talbott
3392ffd7c74aSJoe Talbott /* Save calibration result. */
3393ffd7c74aSJoe Talbott if (sc->calibcmd[idx].buf != NULL)
3394101554a6SMatthew Dillon kfree(sc->calibcmd[idx].buf, M_DEVBUF);
3395101554a6SMatthew Dillon sc->calibcmd[idx].buf = kmalloc(len, M_DEVBUF, M_INTWAIT);
339605538f72SMatthew Dillon if (sc->calibcmd[idx].buf == NULL) {
339705538f72SMatthew Dillon DPRINTF(sc, IWN_DEBUG_CALIBRATE,
339805538f72SMatthew Dillon "not enough memory for calibration result %d\n",
339905538f72SMatthew Dillon calib->code);
340005538f72SMatthew Dillon return;
340105538f72SMatthew Dillon }
3402ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_CALIBRATE,
3403da10ea93SMatthew Dillon "saving calibration result idx=%d, code=%d len=%d\n", idx, calib->code, len);
3404ffd7c74aSJoe Talbott sc->calibcmd[idx].len = len;
3405ffd7c74aSJoe Talbott memcpy(sc->calibcmd[idx].buf, calib, len);
3406ffd7c74aSJoe Talbott }
3407ffd7c74aSJoe Talbott
3408fd49669cSMichael Neumann static void
iwn_stats_update(struct iwn_softc * sc,struct iwn_calib_state * calib,struct iwn_stats * stats,int len)3409fd49669cSMichael Neumann iwn_stats_update(struct iwn_softc *sc, struct iwn_calib_state *calib,
3410fd49669cSMichael Neumann struct iwn_stats *stats, int len)
3411fd49669cSMichael Neumann {
3412fd49669cSMichael Neumann struct iwn_stats_bt *stats_bt;
3413fd49669cSMichael Neumann struct iwn_stats *lstats;
3414fd49669cSMichael Neumann
3415fd49669cSMichael Neumann /*
3416fd49669cSMichael Neumann * First - check whether the length is the bluetooth or normal.
3417fd49669cSMichael Neumann *
3418fd49669cSMichael Neumann * If it's normal - just copy it and bump out.
3419fd49669cSMichael Neumann * Otherwise we have to convert things.
3420fd49669cSMichael Neumann */
3421fd49669cSMichael Neumann
3422fd49669cSMichael Neumann if (len == sizeof(struct iwn_stats) + 4) {
3423fd49669cSMichael Neumann memcpy(&sc->last_stat, stats, sizeof(struct iwn_stats));
3424fd49669cSMichael Neumann sc->last_stat_valid = 1;
3425fd49669cSMichael Neumann return;
3426fd49669cSMichael Neumann }
3427fd49669cSMichael Neumann
3428fd49669cSMichael Neumann /*
3429fd49669cSMichael Neumann * If it's not the bluetooth size - log, then just copy.
3430fd49669cSMichael Neumann */
3431fd49669cSMichael Neumann if (len != sizeof(struct iwn_stats_bt) + 4) {
3432fd49669cSMichael Neumann DPRINTF(sc, IWN_DEBUG_STATS,
3433fd49669cSMichael Neumann "%s: size of rx statistics (%d) not an expected size!\n",
3434fd49669cSMichael Neumann __func__,
3435fd49669cSMichael Neumann len);
3436fd49669cSMichael Neumann memcpy(&sc->last_stat, stats, sizeof(struct iwn_stats));
3437fd49669cSMichael Neumann sc->last_stat_valid = 1;
3438fd49669cSMichael Neumann return;
3439fd49669cSMichael Neumann }
3440fd49669cSMichael Neumann
3441fd49669cSMichael Neumann /*
3442fd49669cSMichael Neumann * Ok. Time to copy.
3443fd49669cSMichael Neumann */
3444fd49669cSMichael Neumann stats_bt = (struct iwn_stats_bt *) stats;
3445fd49669cSMichael Neumann lstats = &sc->last_stat;
3446fd49669cSMichael Neumann
3447fd49669cSMichael Neumann /* flags */
3448fd49669cSMichael Neumann lstats->flags = stats_bt->flags;
3449fd49669cSMichael Neumann /* rx_bt */
3450fd49669cSMichael Neumann memcpy(&lstats->rx.ofdm, &stats_bt->rx_bt.ofdm,
3451fd49669cSMichael Neumann sizeof(struct iwn_rx_phy_stats));
3452fd49669cSMichael Neumann memcpy(&lstats->rx.cck, &stats_bt->rx_bt.cck,
3453fd49669cSMichael Neumann sizeof(struct iwn_rx_phy_stats));
3454fd49669cSMichael Neumann memcpy(&lstats->rx.general, &stats_bt->rx_bt.general_bt.common,
3455fd49669cSMichael Neumann sizeof(struct iwn_rx_general_stats));
3456fd49669cSMichael Neumann memcpy(&lstats->rx.ht, &stats_bt->rx_bt.ht,
3457fd49669cSMichael Neumann sizeof(struct iwn_rx_ht_phy_stats));
3458fd49669cSMichael Neumann /* tx */
3459fd49669cSMichael Neumann memcpy(&lstats->tx, &stats_bt->tx,
3460fd49669cSMichael Neumann sizeof(struct iwn_tx_stats));
3461fd49669cSMichael Neumann /* general */
3462fd49669cSMichael Neumann memcpy(&lstats->general, &stats_bt->general,
3463fd49669cSMichael Neumann sizeof(struct iwn_general_stats));
3464fd49669cSMichael Neumann
3465fd49669cSMichael Neumann /* XXX TODO: Squirrel away the extra bluetooth stats somewhere */
3466fd49669cSMichael Neumann sc->last_stat_valid = 1;
3467fd49669cSMichael Neumann }
3468fd49669cSMichael Neumann
3469ffd7c74aSJoe Talbott /*
3470ffd7c74aSJoe Talbott * Process an RX_STATISTICS or BEACON_STATISTICS firmware notification.
3471ffd7c74aSJoe Talbott * The latter is sent by the firmware after each received beacon.
3472ffd7c74aSJoe Talbott */
3473ffd7c74aSJoe Talbott static void
iwn_rx_statistics(struct iwn_softc * sc,struct iwn_rx_desc * desc,struct iwn_rx_data * data)3474ffd7c74aSJoe Talbott iwn_rx_statistics(struct iwn_softc *sc, struct iwn_rx_desc *desc,
3475ffd7c74aSJoe Talbott struct iwn_rx_data *data)
3476ffd7c74aSJoe Talbott {
3477da10ea93SMatthew Dillon struct iwn_ops *ops = &sc->ops;
3478977fc0dbSMatthew Dillon struct ieee80211com *ic = &sc->sc_ic;
3479ffd7c74aSJoe Talbott struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
3480ffd7c74aSJoe Talbott struct iwn_calib_state *calib = &sc->calib;
3481ffd7c74aSJoe Talbott struct iwn_stats *stats = (struct iwn_stats *)(desc + 1);
3482fd49669cSMichael Neumann struct iwn_stats *lstats;
3483ffd7c74aSJoe Talbott int temp;
3484ffd7c74aSJoe Talbott
3485da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
3486da10ea93SMatthew Dillon
3487da10ea93SMatthew Dillon /* Ignore statistics received during a scan. */
3488ffd7c74aSJoe Talbott if (vap->iv_state != IEEE80211_S_RUN ||
3489da10ea93SMatthew Dillon (ic->ic_flags & IEEE80211_F_SCAN)){
3490da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s received during calib\n",
3491da10ea93SMatthew Dillon __func__);
3492ffd7c74aSJoe Talbott return;
3493da10ea93SMatthew Dillon }
3494ffd7c74aSJoe Talbott
3495ffd7c74aSJoe Talbott bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD);
3496da10ea93SMatthew Dillon
3497fd49669cSMichael Neumann DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_STATS,
3498fd49669cSMichael Neumann "%s: received statistics, cmd %d, len %d\n",
3499fd49669cSMichael Neumann __func__, desc->type, le16toh(desc->len));
3500da10ea93SMatthew Dillon sc->calib_cnt = 0; /* Reset TX power calibration timeout. */
3501ffd7c74aSJoe Talbott
3502fd49669cSMichael Neumann /*
3503fd49669cSMichael Neumann * Collect/track general statistics for reporting.
3504fd49669cSMichael Neumann *
3505fd49669cSMichael Neumann * This takes care of ensuring that the bluetooth sized message
3506fd49669cSMichael Neumann * will be correctly converted to the legacy sized message.
3507fd49669cSMichael Neumann */
3508fd49669cSMichael Neumann iwn_stats_update(sc, calib, stats, le16toh(desc->len));
3509fd49669cSMichael Neumann
3510fd49669cSMichael Neumann /*
3511fd49669cSMichael Neumann * And now, let's take a reference of it to use!
3512fd49669cSMichael Neumann */
3513fd49669cSMichael Neumann lstats = &sc->last_stat;
3514fd49669cSMichael Neumann
3515ffd7c74aSJoe Talbott /* Test if temperature has changed. */
3516fd49669cSMichael Neumann if (lstats->general.temp != sc->rawtemp) {
3517ffd7c74aSJoe Talbott /* Convert "raw" temperature to degC. */
3518ffd7c74aSJoe Talbott sc->rawtemp = stats->general.temp;
3519da10ea93SMatthew Dillon temp = ops->get_temperature(sc);
3520ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: temperature %d\n",
3521ffd7c74aSJoe Talbott __func__, temp);
3522ffd7c74aSJoe Talbott
3523da10ea93SMatthew Dillon /* Update TX power if need be (4965AGN only). */
3524ffd7c74aSJoe Talbott if (sc->hw_type == IWN_HW_REV_TYPE_4965)
3525ffd7c74aSJoe Talbott iwn4965_power_calibration(sc, temp);
3526ffd7c74aSJoe Talbott }
3527ffd7c74aSJoe Talbott
3528ffd7c74aSJoe Talbott if (desc->type != IWN_BEACON_STATISTICS)
3529ffd7c74aSJoe Talbott return; /* Reply to a statistics request. */
3530ffd7c74aSJoe Talbott
3531fd49669cSMichael Neumann sc->noise = iwn_get_noise(&lstats->rx.general);
3532ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: noise %d\n", __func__, sc->noise);
3533ffd7c74aSJoe Talbott
3534ffd7c74aSJoe Talbott /* Test that RSSI and noise are present in stats report. */
3535fd49669cSMichael Neumann if (le32toh(lstats->rx.general.flags) != 1) {
3536ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_ANY, "%s\n",
3537ffd7c74aSJoe Talbott "received statistics without RSSI");
3538ffd7c74aSJoe Talbott return;
3539ffd7c74aSJoe Talbott }
3540ffd7c74aSJoe Talbott
3541ffd7c74aSJoe Talbott if (calib->state == IWN_CALIB_STATE_ASSOC)
3542fd49669cSMichael Neumann iwn_collect_noise(sc, &lstats->rx.general);
3543fd49669cSMichael Neumann else if (calib->state == IWN_CALIB_STATE_RUN) {
3544fd49669cSMichael Neumann iwn_tune_sensitivity(sc, &lstats->rx);
3545fd49669cSMichael Neumann /*
3546fd49669cSMichael Neumann * XXX TODO: Only run the RX recovery if we're associated!
3547fd49669cSMichael Neumann */
3548fd49669cSMichael Neumann iwn_check_rx_recovery(sc, lstats);
3549fd49669cSMichael Neumann iwn_save_stats_counters(sc, lstats);
3550fd49669cSMichael Neumann }
3551da10ea93SMatthew Dillon
3552da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
3553ffd7c74aSJoe Talbott }
3554ffd7c74aSJoe Talbott
3555ffd7c74aSJoe Talbott /*
3556fd49669cSMichael Neumann * Save the relevant statistic counters for the next calibration
3557fd49669cSMichael Neumann * pass.
3558fd49669cSMichael Neumann */
3559fd49669cSMichael Neumann static void
iwn_save_stats_counters(struct iwn_softc * sc,const struct iwn_stats * rs)3560fd49669cSMichael Neumann iwn_save_stats_counters(struct iwn_softc *sc, const struct iwn_stats *rs)
3561fd49669cSMichael Neumann {
3562fd49669cSMichael Neumann struct iwn_calib_state *calib = &sc->calib;
3563fd49669cSMichael Neumann
3564fd49669cSMichael Neumann /* Save counters values for next call. */
3565fd49669cSMichael Neumann calib->bad_plcp_cck = le32toh(rs->rx.cck.bad_plcp);
3566fd49669cSMichael Neumann calib->fa_cck = le32toh(rs->rx.cck.fa);
3567fd49669cSMichael Neumann calib->bad_plcp_ht = le32toh(rs->rx.ht.bad_plcp);
3568fd49669cSMichael Neumann calib->bad_plcp_ofdm = le32toh(rs->rx.ofdm.bad_plcp);
3569fd49669cSMichael Neumann calib->fa_ofdm = le32toh(rs->rx.ofdm.fa);
3570fd49669cSMichael Neumann
3571fd49669cSMichael Neumann /* Last time we received these tick values */
3572fd49669cSMichael Neumann sc->last_calib_ticks = ticks;
3573fd49669cSMichael Neumann }
3574fd49669cSMichael Neumann
3575fd49669cSMichael Neumann /*
3576ffd7c74aSJoe Talbott * Process a TX_DONE firmware notification. Unfortunately, the 4965AGN
3577ffd7c74aSJoe Talbott * and 5000 adapters have different incompatible TX status formats.
3578ffd7c74aSJoe Talbott */
3579ffd7c74aSJoe Talbott static void
iwn4965_tx_done(struct iwn_softc * sc,struct iwn_rx_desc * desc,struct iwn_rx_data * data)3580ffd7c74aSJoe Talbott iwn4965_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc,
3581ffd7c74aSJoe Talbott struct iwn_rx_data *data)
3582ffd7c74aSJoe Talbott {
3583ffd7c74aSJoe Talbott struct iwn4965_tx_stat *stat = (struct iwn4965_tx_stat *)(desc + 1);
3584da10ea93SMatthew Dillon struct iwn_tx_ring *ring;
3585da10ea93SMatthew Dillon int qid;
3586da10ea93SMatthew Dillon
3587da10ea93SMatthew Dillon qid = desc->qid & 0xf;
3588da10ea93SMatthew Dillon ring = &sc->txq[qid];
3589ffd7c74aSJoe Talbott
3590ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_XMIT, "%s: "
359105538f72SMatthew Dillon "qid %d idx %d RTS retries %d ACK retries %d nkill %d rate %x duration %d status %x\n",
359205538f72SMatthew Dillon __func__, desc->qid, desc->idx,
359305538f72SMatthew Dillon stat->rtsfailcnt,
359405538f72SMatthew Dillon stat->ackfailcnt,
359505538f72SMatthew Dillon stat->btkillcnt,
359605538f72SMatthew Dillon stat->rate, le16toh(stat->duration),
3597ffd7c74aSJoe Talbott le32toh(stat->status));
3598ffd7c74aSJoe Talbott
3599ffd7c74aSJoe Talbott bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD);
3600da10ea93SMatthew Dillon if (qid >= sc->firstaggqueue) {
3601da10ea93SMatthew Dillon iwn_ampdu_tx_done(sc, qid, desc->idx, stat->nframes,
360205538f72SMatthew Dillon stat->ackfailcnt, &stat->status);
3603da10ea93SMatthew Dillon } else {
3604da10ea93SMatthew Dillon iwn_tx_done(sc, desc, stat->ackfailcnt,
3605da10ea93SMatthew Dillon le32toh(stat->status) & 0xff);
3606da10ea93SMatthew Dillon }
3607ffd7c74aSJoe Talbott }
3608ffd7c74aSJoe Talbott
3609ffd7c74aSJoe Talbott static void
iwn5000_tx_done(struct iwn_softc * sc,struct iwn_rx_desc * desc,struct iwn_rx_data * data)3610ffd7c74aSJoe Talbott iwn5000_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc,
3611ffd7c74aSJoe Talbott struct iwn_rx_data *data)
3612ffd7c74aSJoe Talbott {
3613ffd7c74aSJoe Talbott struct iwn5000_tx_stat *stat = (struct iwn5000_tx_stat *)(desc + 1);
3614da10ea93SMatthew Dillon struct iwn_tx_ring *ring;
3615da10ea93SMatthew Dillon int qid;
3616da10ea93SMatthew Dillon
3617da10ea93SMatthew Dillon qid = desc->qid & 0xf;
3618da10ea93SMatthew Dillon ring = &sc->txq[qid];
3619ffd7c74aSJoe Talbott
3620ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_XMIT, "%s: "
362105538f72SMatthew Dillon "qid %d idx %d RTS retries %d ACK retries %d nkill %d rate %x duration %d status %x\n",
362205538f72SMatthew Dillon __func__, desc->qid, desc->idx,
362305538f72SMatthew Dillon stat->rtsfailcnt,
362405538f72SMatthew Dillon stat->ackfailcnt,
362505538f72SMatthew Dillon stat->btkillcnt,
362605538f72SMatthew Dillon stat->rate, le16toh(stat->duration),
3627ffd7c74aSJoe Talbott le32toh(stat->status));
3628ffd7c74aSJoe Talbott
3629ffd7c74aSJoe Talbott #ifdef notyet
3630ffd7c74aSJoe Talbott /* Reset TX scheduler slot. */
3631ffd7c74aSJoe Talbott iwn5000_reset_sched(sc, desc->qid & 0xf, desc->idx);
3632ffd7c74aSJoe Talbott #endif
3633ffd7c74aSJoe Talbott
3634ffd7c74aSJoe Talbott bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD);
3635da10ea93SMatthew Dillon if (qid >= sc->firstaggqueue) {
3636da10ea93SMatthew Dillon iwn_ampdu_tx_done(sc, qid, desc->idx, stat->nframes,
363705538f72SMatthew Dillon stat->ackfailcnt, &stat->status);
3638da10ea93SMatthew Dillon } else {
3639da10ea93SMatthew Dillon iwn_tx_done(sc, desc, stat->ackfailcnt,
3640da10ea93SMatthew Dillon le16toh(stat->status) & 0xff);
3641da10ea93SMatthew Dillon }
3642ffd7c74aSJoe Talbott }
3643ffd7c74aSJoe Talbott
3644ffd7c74aSJoe Talbott /*
3645ffd7c74aSJoe Talbott * Adapter-independent backend for TX_DONE firmware notifications.
3646ffd7c74aSJoe Talbott */
3647ffd7c74aSJoe Talbott static void
iwn_tx_done(struct iwn_softc * sc,struct iwn_rx_desc * desc,int ackfailcnt,uint8_t status)3648ffd7c74aSJoe Talbott iwn_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, int ackfailcnt,
3649ffd7c74aSJoe Talbott uint8_t status)
3650ffd7c74aSJoe Talbott {
3651ffd7c74aSJoe Talbott struct iwn_tx_ring *ring = &sc->txq[desc->qid & 0xf];
3652ffd7c74aSJoe Talbott struct iwn_tx_data *data = &ring->data[desc->idx];
3653ffd7c74aSJoe Talbott struct mbuf *m;
3654ffd7c74aSJoe Talbott struct ieee80211_node *ni;
3655ffd7c74aSJoe Talbott struct ieee80211vap *vap;
3656ffd7c74aSJoe Talbott
3657ffd7c74aSJoe Talbott KASSERT(data->ni != NULL, ("no node"));
3658ffd7c74aSJoe Talbott
3659da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
3660da10ea93SMatthew Dillon
3661ffd7c74aSJoe Talbott /* Unmap and free mbuf. */
3662ffd7c74aSJoe Talbott bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTWRITE);
3663ffd7c74aSJoe Talbott bus_dmamap_unload(ring->data_dmat, data->map);
3664ffd7c74aSJoe Talbott m = data->m, data->m = NULL;
3665ffd7c74aSJoe Talbott ni = data->ni, data->ni = NULL;
3666ffd7c74aSJoe Talbott vap = ni->ni_vap;
3667ffd7c74aSJoe Talbott
3668da10ea93SMatthew Dillon /*
3669da10ea93SMatthew Dillon * Update rate control statistics for the node.
3670da10ea93SMatthew Dillon */
3671977fc0dbSMatthew Dillon if (status & IWN_TX_FAIL)
3672da10ea93SMatthew Dillon ieee80211_ratectl_tx_complete(vap, ni,
3673da10ea93SMatthew Dillon IEEE80211_RATECTL_TX_FAILURE, &ackfailcnt, NULL);
3674977fc0dbSMatthew Dillon else
3675da10ea93SMatthew Dillon ieee80211_ratectl_tx_complete(vap, ni,
3676da10ea93SMatthew Dillon IEEE80211_RATECTL_TX_SUCCESS, &ackfailcnt, NULL);
3677da10ea93SMatthew Dillon
3678ffd7c74aSJoe Talbott /*
3679ffd7c74aSJoe Talbott * Channels marked for "radar" require traffic to be received
3680ffd7c74aSJoe Talbott * to unlock before we can transmit. Until traffic is seen
3681ffd7c74aSJoe Talbott * any attempt to transmit is returned immediately with status
3682ffd7c74aSJoe Talbott * set to IWN_TX_FAIL_TX_LOCKED. Unfortunately this can easily
3683ffd7c74aSJoe Talbott * happen on first authenticate after scanning. To workaround
3684ffd7c74aSJoe Talbott * this we ignore a failure of this sort in AUTH state so the
3685ffd7c74aSJoe Talbott * 802.11 layer will fall back to using a timeout to wait for
3686ffd7c74aSJoe Talbott * the AUTH reply. This allows the firmware time to see
3687ffd7c74aSJoe Talbott * traffic so a subsequent retry of AUTH succeeds. It's
3688ffd7c74aSJoe Talbott * unclear why the firmware does not maintain state for
3689ffd7c74aSJoe Talbott * channels recently visited as this would allow immediate
3690ffd7c74aSJoe Talbott * use of the channel after a scan (where we see traffic).
3691ffd7c74aSJoe Talbott */
3692ffd7c74aSJoe Talbott if (status == IWN_TX_FAIL_TX_LOCKED &&
3693ffd7c74aSJoe Talbott ni->ni_vap->iv_state == IEEE80211_S_AUTH)
3694da10ea93SMatthew Dillon ieee80211_tx_complete(ni, m, 0);
3695ffd7c74aSJoe Talbott else
3696da10ea93SMatthew Dillon ieee80211_tx_complete(ni, m,
3697ffd7c74aSJoe Talbott (status & IWN_TX_FAIL) != 0);
3698ffd7c74aSJoe Talbott
3699ffd7c74aSJoe Talbott sc->sc_tx_timer = 0;
3700977fc0dbSMatthew Dillon if (--ring->queued < IWN_TX_RING_LOMARK)
3701ffd7c74aSJoe Talbott sc->qfullmsk &= ~(1 << ring->qid);
3702da10ea93SMatthew Dillon
3703da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
3704ffd7c74aSJoe Talbott }
3705ffd7c74aSJoe Talbott
3706ffd7c74aSJoe Talbott /*
3707ffd7c74aSJoe Talbott * Process a "command done" firmware notification. This is where we wakeup
3708ffd7c74aSJoe Talbott * processes waiting for a synchronous command completion.
3709ffd7c74aSJoe Talbott */
3710ffd7c74aSJoe Talbott static void
iwn_cmd_done(struct iwn_softc * sc,struct iwn_rx_desc * desc)3711ffd7c74aSJoe Talbott iwn_cmd_done(struct iwn_softc *sc, struct iwn_rx_desc *desc)
3712ffd7c74aSJoe Talbott {
3713da10ea93SMatthew Dillon struct iwn_tx_ring *ring;
3714ffd7c74aSJoe Talbott struct iwn_tx_data *data;
3715da10ea93SMatthew Dillon int cmd_queue_num;
3716ffd7c74aSJoe Talbott
3717da10ea93SMatthew Dillon if (sc->sc_flags & IWN_FLAG_PAN_SUPPORT)
3718da10ea93SMatthew Dillon cmd_queue_num = IWN_PAN_CMD_QUEUE;
3719da10ea93SMatthew Dillon else
3720da10ea93SMatthew Dillon cmd_queue_num = IWN_CMD_QUEUE_NUM;
3721da10ea93SMatthew Dillon
3722da10ea93SMatthew Dillon if ((desc->qid & IWN_RX_DESC_QID_MSK) != cmd_queue_num)
3723ffd7c74aSJoe Talbott return; /* Not a command ack. */
3724ffd7c74aSJoe Talbott
3725da10ea93SMatthew Dillon ring = &sc->txq[cmd_queue_num];
3726ffd7c74aSJoe Talbott data = &ring->data[desc->idx];
3727ffd7c74aSJoe Talbott
3728ffd7c74aSJoe Talbott /* If the command was mapped in an mbuf, free it. */
3729ffd7c74aSJoe Talbott if (data->m != NULL) {
3730da10ea93SMatthew Dillon bus_dmamap_sync(ring->data_dmat, data->map,
3731da10ea93SMatthew Dillon BUS_DMASYNC_POSTWRITE);
3732ffd7c74aSJoe Talbott bus_dmamap_unload(ring->data_dmat, data->map);
3733ffd7c74aSJoe Talbott m_freem(data->m);
3734ffd7c74aSJoe Talbott data->m = NULL;
3735ffd7c74aSJoe Talbott }
3736ffd7c74aSJoe Talbott wakeup(&ring->desc[desc->idx]);
3737ffd7c74aSJoe Talbott }
3738ffd7c74aSJoe Talbott
3739da10ea93SMatthew Dillon static void
iwn_ampdu_tx_done(struct iwn_softc * sc,int qid,int idx,int nframes,int ackfailcnt,void * stat)3740da10ea93SMatthew Dillon iwn_ampdu_tx_done(struct iwn_softc *sc, int qid, int idx, int nframes,
374105538f72SMatthew Dillon int ackfailcnt, void *stat)
3742da10ea93SMatthew Dillon {
3743da10ea93SMatthew Dillon struct iwn_ops *ops = &sc->ops;
3744da10ea93SMatthew Dillon struct iwn_tx_ring *ring = &sc->txq[qid];
3745da10ea93SMatthew Dillon struct iwn_tx_data *data;
3746da10ea93SMatthew Dillon struct mbuf *m;
3747da10ea93SMatthew Dillon struct iwn_node *wn;
3748da10ea93SMatthew Dillon struct ieee80211_node *ni;
3749da10ea93SMatthew Dillon struct ieee80211_tx_ampdu *tap;
3750da10ea93SMatthew Dillon uint64_t bitmap;
3751da10ea93SMatthew Dillon uint32_t *status = stat;
3752da10ea93SMatthew Dillon uint16_t *aggstatus = stat;
3753da10ea93SMatthew Dillon uint16_t ssn;
3754da10ea93SMatthew Dillon uint8_t tid;
3755da10ea93SMatthew Dillon int bit, i, lastidx, *res, seqno, shift, start;
3756da10ea93SMatthew Dillon
375705538f72SMatthew Dillon /* XXX TODO: status is le16 field! Grr */
3758da10ea93SMatthew Dillon
375905538f72SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
376005538f72SMatthew Dillon DPRINTF(sc, IWN_DEBUG_XMIT, "%s: nframes=%d, status=0x%08x\n",
376105538f72SMatthew Dillon __func__,
376205538f72SMatthew Dillon nframes,
376305538f72SMatthew Dillon *status);
376405538f72SMatthew Dillon
376505538f72SMatthew Dillon tap = sc->qid2tap[qid];
376605538f72SMatthew Dillon tid = tap->txa_tid;
376705538f72SMatthew Dillon wn = (void *)tap->txa_ni;
376805538f72SMatthew Dillon ni = tap->txa_ni;
376905538f72SMatthew Dillon
377005538f72SMatthew Dillon /*
377105538f72SMatthew Dillon * XXX TODO: ACK and RTS failures would be nice here!
377205538f72SMatthew Dillon */
377305538f72SMatthew Dillon
377405538f72SMatthew Dillon /*
377505538f72SMatthew Dillon * A-MPDU single frame status - if we failed to transmit it
377605538f72SMatthew Dillon * in A-MPDU, then it may be a permanent failure.
377705538f72SMatthew Dillon *
377805538f72SMatthew Dillon * XXX TODO: check what the Linux iwlwifi driver does here;
377905538f72SMatthew Dillon * there's some permanent and temporary failures that may be
378005538f72SMatthew Dillon * handled differently.
378105538f72SMatthew Dillon */
3782da10ea93SMatthew Dillon if (nframes == 1) {
3783da10ea93SMatthew Dillon if ((*status & 0xff) != 1 && (*status & 0xff) != 2) {
3784da10ea93SMatthew Dillon #ifdef NOT_YET
3785101554a6SMatthew Dillon kprintf("ieee80211_send_bar()\n");
3786da10ea93SMatthew Dillon #endif
3787da10ea93SMatthew Dillon /*
3788da10ea93SMatthew Dillon * If we completely fail a transmit, make sure a
3789da10ea93SMatthew Dillon * notification is pushed up to the rate control
3790da10ea93SMatthew Dillon * layer.
3791da10ea93SMatthew Dillon */
379205538f72SMatthew Dillon ieee80211_ratectl_tx_complete(ni->ni_vap,
379305538f72SMatthew Dillon ni,
379405538f72SMatthew Dillon IEEE80211_RATECTL_TX_FAILURE,
379505538f72SMatthew Dillon &ackfailcnt,
379605538f72SMatthew Dillon NULL);
379705538f72SMatthew Dillon } else {
379805538f72SMatthew Dillon /*
379905538f72SMatthew Dillon * If nframes=1, then we won't be getting a BA for
380005538f72SMatthew Dillon * this frame. Ensure that we correctly update the
380105538f72SMatthew Dillon * rate control code with how many retries were
380205538f72SMatthew Dillon * needed to send it.
380305538f72SMatthew Dillon */
380405538f72SMatthew Dillon ieee80211_ratectl_tx_complete(ni->ni_vap,
380505538f72SMatthew Dillon ni,
380605538f72SMatthew Dillon IEEE80211_RATECTL_TX_SUCCESS,
380705538f72SMatthew Dillon &ackfailcnt,
380805538f72SMatthew Dillon NULL);
3809da10ea93SMatthew Dillon }
3810da10ea93SMatthew Dillon }
3811da10ea93SMatthew Dillon
3812da10ea93SMatthew Dillon bitmap = 0;
3813da10ea93SMatthew Dillon start = idx;
3814da10ea93SMatthew Dillon for (i = 0; i < nframes; i++) {
3815da10ea93SMatthew Dillon if (le16toh(aggstatus[i * 2]) & 0xc)
3816da10ea93SMatthew Dillon continue;
3817da10ea93SMatthew Dillon
3818da10ea93SMatthew Dillon idx = le16toh(aggstatus[2*i + 1]) & 0xff;
3819da10ea93SMatthew Dillon bit = idx - start;
3820da10ea93SMatthew Dillon shift = 0;
3821da10ea93SMatthew Dillon if (bit >= 64) {
3822da10ea93SMatthew Dillon shift = 0x100 - idx + start;
3823da10ea93SMatthew Dillon bit = 0;
3824da10ea93SMatthew Dillon start = idx;
3825da10ea93SMatthew Dillon } else if (bit <= -64)
3826da10ea93SMatthew Dillon bit = 0x100 - start + idx;
3827da10ea93SMatthew Dillon else if (bit < 0) {
3828da10ea93SMatthew Dillon shift = start - idx;
3829da10ea93SMatthew Dillon start = idx;
3830da10ea93SMatthew Dillon bit = 0;
3831da10ea93SMatthew Dillon }
3832da10ea93SMatthew Dillon bitmap = bitmap << shift;
3833da10ea93SMatthew Dillon bitmap |= 1ULL << bit;
3834da10ea93SMatthew Dillon }
3835da10ea93SMatthew Dillon tap = sc->qid2tap[qid];
3836085ff963SMatthew Dillon tid = tap->txa_tid;
3837da10ea93SMatthew Dillon wn = (void *)tap->txa_ni;
3838da10ea93SMatthew Dillon wn->agg[tid].bitmap = bitmap;
3839da10ea93SMatthew Dillon wn->agg[tid].startidx = start;
3840da10ea93SMatthew Dillon wn->agg[tid].nframes = nframes;
3841da10ea93SMatthew Dillon
3842da10ea93SMatthew Dillon res = NULL;
3843da10ea93SMatthew Dillon ssn = 0;
3844da10ea93SMatthew Dillon if (!IEEE80211_AMPDU_RUNNING(tap)) {
3845da10ea93SMatthew Dillon res = tap->txa_private;
3846da10ea93SMatthew Dillon ssn = tap->txa_start & 0xfff;
3847da10ea93SMatthew Dillon }
3848da10ea93SMatthew Dillon
384905538f72SMatthew Dillon /* This is going nframes DWORDS into the descriptor? */
3850da10ea93SMatthew Dillon seqno = le32toh(*(status + nframes)) & 0xfff;
3851da10ea93SMatthew Dillon for (lastidx = (seqno & 0xff); ring->read != lastidx;) {
3852da10ea93SMatthew Dillon data = &ring->data[ring->read];
3853da10ea93SMatthew Dillon
3854da10ea93SMatthew Dillon /* Unmap and free mbuf. */
3855da10ea93SMatthew Dillon bus_dmamap_sync(ring->data_dmat, data->map,
3856da10ea93SMatthew Dillon BUS_DMASYNC_POSTWRITE);
3857da10ea93SMatthew Dillon bus_dmamap_unload(ring->data_dmat, data->map);
3858da10ea93SMatthew Dillon m = data->m, data->m = NULL;
3859da10ea93SMatthew Dillon ni = data->ni, data->ni = NULL;
3860da10ea93SMatthew Dillon
3861da10ea93SMatthew Dillon KASSERT(ni != NULL, ("no node"));
3862da10ea93SMatthew Dillon KASSERT(m != NULL, ("no mbuf"));
386305538f72SMatthew Dillon DPRINTF(sc, IWN_DEBUG_XMIT, "%s: freeing m=%p\n", __func__, m);
3864da10ea93SMatthew Dillon ieee80211_tx_complete(ni, m, 1);
3865da10ea93SMatthew Dillon
3866da10ea93SMatthew Dillon ring->queued--;
3867da10ea93SMatthew Dillon ring->read = (ring->read + 1) % IWN_TX_RING_COUNT;
3868da10ea93SMatthew Dillon }
3869da10ea93SMatthew Dillon
3870da10ea93SMatthew Dillon if (ring->queued == 0 && res != NULL) {
3871da10ea93SMatthew Dillon iwn_nic_lock(sc);
3872da10ea93SMatthew Dillon ops->ampdu_tx_stop(sc, qid, tid, ssn);
3873da10ea93SMatthew Dillon iwn_nic_unlock(sc);
3874da10ea93SMatthew Dillon sc->qid2tap[qid] = NULL;
3875101554a6SMatthew Dillon kfree(res, M_DEVBUF);
3876da10ea93SMatthew Dillon return;
3877da10ea93SMatthew Dillon }
3878da10ea93SMatthew Dillon
3879da10ea93SMatthew Dillon sc->sc_tx_timer = 0;
3880977fc0dbSMatthew Dillon if (ring->queued < IWN_TX_RING_LOMARK)
3881da10ea93SMatthew Dillon sc->qfullmsk &= ~(1 << ring->qid);
3882da10ea93SMatthew Dillon
3883da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
3884da10ea93SMatthew Dillon }
3885da10ea93SMatthew Dillon
3886ffd7c74aSJoe Talbott /*
3887ffd7c74aSJoe Talbott * Process an INT_FH_RX or INT_SW_RX interrupt.
3888ffd7c74aSJoe Talbott */
3889ffd7c74aSJoe Talbott static void
iwn_notif_intr(struct iwn_softc * sc)3890ffd7c74aSJoe Talbott iwn_notif_intr(struct iwn_softc *sc)
3891ffd7c74aSJoe Talbott {
3892da10ea93SMatthew Dillon struct iwn_ops *ops = &sc->ops;
3893977fc0dbSMatthew Dillon struct ieee80211com *ic = &sc->sc_ic;
3894ffd7c74aSJoe Talbott struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
3895ffd7c74aSJoe Talbott uint16_t hw;
3896ffd7c74aSJoe Talbott
3897ffd7c74aSJoe Talbott bus_dmamap_sync(sc->rxq.stat_dma.tag, sc->rxq.stat_dma.map,
3898ffd7c74aSJoe Talbott BUS_DMASYNC_POSTREAD);
3899ffd7c74aSJoe Talbott
3900ffd7c74aSJoe Talbott hw = le16toh(sc->rxq.stat->closed_count) & 0xfff;
3901ffd7c74aSJoe Talbott while (sc->rxq.cur != hw) {
3902ffd7c74aSJoe Talbott struct iwn_rx_data *data = &sc->rxq.data[sc->rxq.cur];
3903ffd7c74aSJoe Talbott struct iwn_rx_desc *desc;
3904ffd7c74aSJoe Talbott
3905ffd7c74aSJoe Talbott bus_dmamap_sync(sc->rxq.data_dmat, data->map,
3906ffd7c74aSJoe Talbott BUS_DMASYNC_POSTREAD);
3907ffd7c74aSJoe Talbott desc = mtod(data->m, struct iwn_rx_desc *);
3908ffd7c74aSJoe Talbott
3909ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_RECV,
39104b420e05SMatthew Dillon "%s: cur=%d; qid %x idx %d flags %x type %d(%s) len %d\n",
39114b420e05SMatthew Dillon __func__, sc->rxq.cur, desc->qid & 0xf, desc->idx, desc->flags,
3912ffd7c74aSJoe Talbott desc->type, iwn_intr_str(desc->type),
3913ffd7c74aSJoe Talbott le16toh(desc->len));
3914ffd7c74aSJoe Talbott
3915da10ea93SMatthew Dillon if (!(desc->qid & IWN_UNSOLICITED_RX_NOTIF)) /* Reply to a command. */
3916ffd7c74aSJoe Talbott iwn_cmd_done(sc, desc);
3917ffd7c74aSJoe Talbott
3918ffd7c74aSJoe Talbott switch (desc->type) {
3919ffd7c74aSJoe Talbott case IWN_RX_PHY:
3920ffd7c74aSJoe Talbott iwn_rx_phy(sc, desc, data);
3921ffd7c74aSJoe Talbott break;
3922ffd7c74aSJoe Talbott
3923ffd7c74aSJoe Talbott case IWN_RX_DONE: /* 4965AGN only. */
3924ffd7c74aSJoe Talbott case IWN_MPDU_RX_DONE:
3925ffd7c74aSJoe Talbott /* An 802.11 frame has been received. */
3926ffd7c74aSJoe Talbott iwn_rx_done(sc, desc, data);
3927ffd7c74aSJoe Talbott break;
3928ffd7c74aSJoe Talbott
3929ffd7c74aSJoe Talbott case IWN_RX_COMPRESSED_BA:
3930ffd7c74aSJoe Talbott /* A Compressed BlockAck has been received. */
3931ffd7c74aSJoe Talbott iwn_rx_compressed_ba(sc, desc, data);
3932ffd7c74aSJoe Talbott break;
3933ffd7c74aSJoe Talbott
3934ffd7c74aSJoe Talbott case IWN_TX_DONE:
3935ffd7c74aSJoe Talbott /* An 802.11 frame has been transmitted. */
3936da10ea93SMatthew Dillon ops->tx_done(sc, desc, data);
3937ffd7c74aSJoe Talbott break;
3938ffd7c74aSJoe Talbott
3939ffd7c74aSJoe Talbott case IWN_RX_STATISTICS:
3940ffd7c74aSJoe Talbott case IWN_BEACON_STATISTICS:
3941ffd7c74aSJoe Talbott iwn_rx_statistics(sc, desc, data);
3942ffd7c74aSJoe Talbott break;
3943ffd7c74aSJoe Talbott
3944ffd7c74aSJoe Talbott case IWN_BEACON_MISSED:
3945ffd7c74aSJoe Talbott {
3946ffd7c74aSJoe Talbott struct iwn_beacon_missed *miss =
3947ffd7c74aSJoe Talbott (struct iwn_beacon_missed *)(desc + 1);
3948ffd7c74aSJoe Talbott int misses;
3949ffd7c74aSJoe Talbott
3950ffd7c74aSJoe Talbott bus_dmamap_sync(sc->rxq.data_dmat, data->map,
3951ffd7c74aSJoe Talbott BUS_DMASYNC_POSTREAD);
3952ffd7c74aSJoe Talbott misses = le32toh(miss->consecutive);
3953ffd7c74aSJoe Talbott
3954ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_STATE,
3955ffd7c74aSJoe Talbott "%s: beacons missed %d/%d\n", __func__,
3956ffd7c74aSJoe Talbott misses, le32toh(miss->total));
3957ffd7c74aSJoe Talbott /*
3958ffd7c74aSJoe Talbott * If more than 5 consecutive beacons are missed,
3959ffd7c74aSJoe Talbott * reinitialize the sensitivity state machine.
3960ffd7c74aSJoe Talbott */
3961da10ea93SMatthew Dillon if (vap->iv_state == IEEE80211_S_RUN &&
3962da10ea93SMatthew Dillon (ic->ic_flags & IEEE80211_F_SCAN) == 0) {
3963da10ea93SMatthew Dillon if (misses > 5)
3964ffd7c74aSJoe Talbott (void)iwn_init_sensitivity(sc);
3965da10ea93SMatthew Dillon if (misses >= vap->iv_bmissthreshold) {
396605538f72SMatthew Dillon IWN_UNLOCK(sc);
3967ffd7c74aSJoe Talbott ieee80211_beacon_miss(ic);
396805538f72SMatthew Dillon IWN_LOCK(sc);
3969da10ea93SMatthew Dillon }
3970da10ea93SMatthew Dillon }
3971ffd7c74aSJoe Talbott break;
3972ffd7c74aSJoe Talbott }
3973ffd7c74aSJoe Talbott case IWN_UC_READY:
3974ffd7c74aSJoe Talbott {
3975ffd7c74aSJoe Talbott struct iwn_ucode_info *uc =
3976ffd7c74aSJoe Talbott (struct iwn_ucode_info *)(desc + 1);
3977ffd7c74aSJoe Talbott
3978ffd7c74aSJoe Talbott /* The microcontroller is ready. */
3979ffd7c74aSJoe Talbott bus_dmamap_sync(sc->rxq.data_dmat, data->map,
3980ffd7c74aSJoe Talbott BUS_DMASYNC_POSTREAD);
3981ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_RESET,
3982ffd7c74aSJoe Talbott "microcode alive notification version=%d.%d "
3983ffd7c74aSJoe Talbott "subtype=%x alive=%x\n", uc->major, uc->minor,
3984ffd7c74aSJoe Talbott uc->subtype, le32toh(uc->valid));
3985ffd7c74aSJoe Talbott
3986ffd7c74aSJoe Talbott if (le32toh(uc->valid) != 1) {
3987ffd7c74aSJoe Talbott device_printf(sc->sc_dev,
3988ffd7c74aSJoe Talbott "microcontroller initialization failed");
3989ffd7c74aSJoe Talbott break;
3990ffd7c74aSJoe Talbott }
3991ffd7c74aSJoe Talbott if (uc->subtype == IWN_UCODE_INIT) {
3992ffd7c74aSJoe Talbott /* Save microcontroller report. */
3993ffd7c74aSJoe Talbott memcpy(&sc->ucode_info, uc, sizeof (*uc));
3994ffd7c74aSJoe Talbott }
3995ffd7c74aSJoe Talbott /* Save the address of the error log in SRAM. */
3996ffd7c74aSJoe Talbott sc->errptr = le32toh(uc->errptr);
3997ffd7c74aSJoe Talbott break;
3998ffd7c74aSJoe Talbott }
3999ffd7c74aSJoe Talbott case IWN_STATE_CHANGED:
4000ffd7c74aSJoe Talbott {
4001ffd7c74aSJoe Talbott /*
4002ffd7c74aSJoe Talbott * State change allows hardware switch change to be
4003ffd7c74aSJoe Talbott * noted. However, we handle this in iwn_intr as we
4004ffd7c74aSJoe Talbott * get both the enable/disble intr.
4005ffd7c74aSJoe Talbott */
4006ffd7c74aSJoe Talbott bus_dmamap_sync(sc->rxq.data_dmat, data->map,
4007ffd7c74aSJoe Talbott BUS_DMASYNC_POSTREAD);
4008da10ea93SMatthew Dillon #ifdef IWN_DEBUG
4009da10ea93SMatthew Dillon uint32_t *status = (uint32_t *)(desc + 1);
40104b420e05SMatthew Dillon DPRINTF(sc, IWN_DEBUG_INTR | IWN_DEBUG_STATE,
40114b420e05SMatthew Dillon "state changed to %x\n",
4012ffd7c74aSJoe Talbott le32toh(*status));
4013da10ea93SMatthew Dillon #endif
4014ffd7c74aSJoe Talbott break;
4015ffd7c74aSJoe Talbott }
4016ffd7c74aSJoe Talbott case IWN_START_SCAN:
4017ffd7c74aSJoe Talbott {
4018ffd7c74aSJoe Talbott bus_dmamap_sync(sc->rxq.data_dmat, data->map,
4019ffd7c74aSJoe Talbott BUS_DMASYNC_POSTREAD);
4020da10ea93SMatthew Dillon #ifdef IWN_DEBUG
4021da10ea93SMatthew Dillon struct iwn_start_scan *scan =
4022da10ea93SMatthew Dillon (struct iwn_start_scan *)(desc + 1);
4023ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_ANY,
4024ffd7c74aSJoe Talbott "%s: scanning channel %d status %x\n",
4025ffd7c74aSJoe Talbott __func__, scan->chan, le32toh(scan->status));
4026da10ea93SMatthew Dillon #endif
4027ffd7c74aSJoe Talbott break;
4028ffd7c74aSJoe Talbott }
4029ffd7c74aSJoe Talbott case IWN_STOP_SCAN:
4030ffd7c74aSJoe Talbott {
4031ffd7c74aSJoe Talbott bus_dmamap_sync(sc->rxq.data_dmat, data->map,
4032ffd7c74aSJoe Talbott BUS_DMASYNC_POSTREAD);
4033da10ea93SMatthew Dillon #ifdef IWN_DEBUG
4034da10ea93SMatthew Dillon struct iwn_stop_scan *scan =
4035da10ea93SMatthew Dillon (struct iwn_stop_scan *)(desc + 1);
40364b420e05SMatthew Dillon DPRINTF(sc, IWN_DEBUG_STATE | IWN_DEBUG_SCAN,
4037ffd7c74aSJoe Talbott "scan finished nchan=%d status=%d chan=%d\n",
4038ffd7c74aSJoe Talbott scan->nchan, scan->status, scan->chan);
4039da10ea93SMatthew Dillon #endif
40404b420e05SMatthew Dillon sc->sc_is_scanning = 0;
404105538f72SMatthew Dillon IWN_UNLOCK(sc);
4042ffd7c74aSJoe Talbott ieee80211_scan_next(vap);
404305538f72SMatthew Dillon IWN_LOCK(sc);
4044ffd7c74aSJoe Talbott break;
4045ffd7c74aSJoe Talbott }
4046ffd7c74aSJoe Talbott case IWN5000_CALIBRATION_RESULT:
4047ffd7c74aSJoe Talbott iwn5000_rx_calib_results(sc, desc, data);
4048ffd7c74aSJoe Talbott break;
4049ffd7c74aSJoe Talbott
4050ffd7c74aSJoe Talbott case IWN5000_CALIBRATION_DONE:
4051ffd7c74aSJoe Talbott sc->sc_flags |= IWN_FLAG_CALIB_DONE;
4052ffd7c74aSJoe Talbott wakeup(sc);
4053ffd7c74aSJoe Talbott break;
4054ffd7c74aSJoe Talbott }
4055ffd7c74aSJoe Talbott
4056ffd7c74aSJoe Talbott sc->rxq.cur = (sc->rxq.cur + 1) % IWN_RX_RING_COUNT;
4057ffd7c74aSJoe Talbott }
4058ffd7c74aSJoe Talbott
4059ffd7c74aSJoe Talbott /* Tell the firmware what we have processed. */
4060ffd7c74aSJoe Talbott hw = (hw == 0) ? IWN_RX_RING_COUNT - 1 : hw - 1;
4061ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_FH_RX_WPTR, hw & ~7);
4062ffd7c74aSJoe Talbott }
4063ffd7c74aSJoe Talbott
4064ffd7c74aSJoe Talbott /*
4065ffd7c74aSJoe Talbott * Process an INT_WAKEUP interrupt raised when the microcontroller wakes up
4066ffd7c74aSJoe Talbott * from power-down sleep mode.
4067ffd7c74aSJoe Talbott */
4068ffd7c74aSJoe Talbott static void
iwn_wakeup_intr(struct iwn_softc * sc)4069ffd7c74aSJoe Talbott iwn_wakeup_intr(struct iwn_softc *sc)
4070ffd7c74aSJoe Talbott {
4071ffd7c74aSJoe Talbott int qid;
4072ffd7c74aSJoe Talbott
4073ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_RESET, "%s: ucode wakeup from power-down sleep\n",
4074ffd7c74aSJoe Talbott __func__);
4075ffd7c74aSJoe Talbott
4076ffd7c74aSJoe Talbott /* Wakeup RX and TX rings. */
4077ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_FH_RX_WPTR, sc->rxq.cur & ~7);
4078da10ea93SMatthew Dillon for (qid = 0; qid < sc->ntxqs; qid++) {
4079ffd7c74aSJoe Talbott struct iwn_tx_ring *ring = &sc->txq[qid];
4080ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | ring->cur);
4081ffd7c74aSJoe Talbott }
4082ffd7c74aSJoe Talbott }
4083ffd7c74aSJoe Talbott
4084ffd7c74aSJoe Talbott static void
iwn_rftoggle_intr(struct iwn_softc * sc)4085ffd7c74aSJoe Talbott iwn_rftoggle_intr(struct iwn_softc *sc)
4086ffd7c74aSJoe Talbott {
4087977fc0dbSMatthew Dillon struct ieee80211com *ic = &sc->sc_ic;
4088ffd7c74aSJoe Talbott uint32_t tmp = IWN_READ(sc, IWN_GP_CNTRL);
4089ffd7c74aSJoe Talbott
409005538f72SMatthew Dillon IWN_LOCK_ASSERT(sc);
409105538f72SMatthew Dillon
4092ffd7c74aSJoe Talbott device_printf(sc->sc_dev, "RF switch: radio %s\n",
4093ffd7c74aSJoe Talbott (tmp & IWN_GP_CNTRL_RFKILL) ? "enabled" : "disabled");
4094ffd7c74aSJoe Talbott if (tmp & IWN_GP_CNTRL_RFKILL)
4095ffd7c74aSJoe Talbott ieee80211_runtask(ic, &sc->sc_radioon_task);
4096ffd7c74aSJoe Talbott else
4097ffd7c74aSJoe Talbott ieee80211_runtask(ic, &sc->sc_radiooff_task);
4098ffd7c74aSJoe Talbott }
4099ffd7c74aSJoe Talbott
4100ffd7c74aSJoe Talbott /*
4101ffd7c74aSJoe Talbott * Dump the error log of the firmware when a firmware panic occurs. Although
4102ffd7c74aSJoe Talbott * we can't debug the firmware because it is neither open source nor free, it
4103ffd7c74aSJoe Talbott * can help us to identify certain classes of problems.
4104ffd7c74aSJoe Talbott */
4105ffd7c74aSJoe Talbott static void
iwn_fatal_intr(struct iwn_softc * sc)4106ffd7c74aSJoe Talbott iwn_fatal_intr(struct iwn_softc *sc)
4107ffd7c74aSJoe Talbott {
4108ffd7c74aSJoe Talbott struct iwn_fw_dump dump;
4109ffd7c74aSJoe Talbott int i;
4110ffd7c74aSJoe Talbott
411105538f72SMatthew Dillon IWN_LOCK_ASSERT(sc);
411205538f72SMatthew Dillon
4113ffd7c74aSJoe Talbott /* Force a complete recalibration on next init. */
4114ffd7c74aSJoe Talbott sc->sc_flags &= ~IWN_FLAG_CALIB_DONE;
4115ffd7c74aSJoe Talbott
4116ffd7c74aSJoe Talbott /* Check that the error log address is valid. */
4117ffd7c74aSJoe Talbott if (sc->errptr < IWN_FW_DATA_BASE ||
4118ffd7c74aSJoe Talbott sc->errptr + sizeof (dump) >
4119da10ea93SMatthew Dillon IWN_FW_DATA_BASE + sc->fw_data_maxsz) {
4120101554a6SMatthew Dillon kprintf("%s: bad firmware error log address 0x%08x\n", __func__,
4121da10ea93SMatthew Dillon sc->errptr);
4122ffd7c74aSJoe Talbott return;
4123ffd7c74aSJoe Talbott }
4124ffd7c74aSJoe Talbott if (iwn_nic_lock(sc) != 0) {
4125101554a6SMatthew Dillon kprintf("%s: could not read firmware error log\n", __func__);
4126ffd7c74aSJoe Talbott return;
4127ffd7c74aSJoe Talbott }
4128ffd7c74aSJoe Talbott /* Read firmware error log from SRAM. */
4129ffd7c74aSJoe Talbott iwn_mem_read_region_4(sc, sc->errptr, (uint32_t *)&dump,
4130ffd7c74aSJoe Talbott sizeof (dump) / sizeof (uint32_t));
4131ffd7c74aSJoe Talbott iwn_nic_unlock(sc);
4132ffd7c74aSJoe Talbott
4133ffd7c74aSJoe Talbott if (dump.valid == 0) {
4134101554a6SMatthew Dillon kprintf("%s: firmware error log is empty\n", __func__);
4135ffd7c74aSJoe Talbott return;
4136ffd7c74aSJoe Talbott }
4137101554a6SMatthew Dillon kprintf("firmware error log:\n");
4138101554a6SMatthew Dillon kprintf(" error type = \"%s\" (0x%08X)\n",
4139da10ea93SMatthew Dillon (dump.id < nitems(iwn_fw_errmsg)) ?
4140ffd7c74aSJoe Talbott iwn_fw_errmsg[dump.id] : "UNKNOWN",
4141ffd7c74aSJoe Talbott dump.id);
4142101554a6SMatthew Dillon kprintf(" program counter = 0x%08X\n", dump.pc);
4143101554a6SMatthew Dillon kprintf(" source line = 0x%08X\n", dump.src_line);
4144101554a6SMatthew Dillon kprintf(" error data = 0x%08X%08X\n",
4145ffd7c74aSJoe Talbott dump.error_data[0], dump.error_data[1]);
4146101554a6SMatthew Dillon kprintf(" branch link = 0x%08X%08X\n",
4147ffd7c74aSJoe Talbott dump.branch_link[0], dump.branch_link[1]);
4148101554a6SMatthew Dillon kprintf(" interrupt link = 0x%08X%08X\n",
4149ffd7c74aSJoe Talbott dump.interrupt_link[0], dump.interrupt_link[1]);
4150101554a6SMatthew Dillon kprintf(" time = %u\n", dump.time[0]);
4151ffd7c74aSJoe Talbott
4152ffd7c74aSJoe Talbott /* Dump driver status (TX and RX rings) while we're here. */
4153101554a6SMatthew Dillon kprintf("driver status:\n");
4154da10ea93SMatthew Dillon for (i = 0; i < sc->ntxqs; i++) {
4155ffd7c74aSJoe Talbott struct iwn_tx_ring *ring = &sc->txq[i];
4156101554a6SMatthew Dillon kprintf(" tx ring %2d: qid=%-2d cur=%-3d queued=%-3d\n",
4157ffd7c74aSJoe Talbott i, ring->qid, ring->cur, ring->queued);
4158ffd7c74aSJoe Talbott }
4159101554a6SMatthew Dillon kprintf(" rx ring: cur=%d\n", sc->rxq.cur);
4160ffd7c74aSJoe Talbott }
4161ffd7c74aSJoe Talbott
4162ffd7c74aSJoe Talbott static void
iwn_intr(void * arg)4163ffd7c74aSJoe Talbott iwn_intr(void *arg)
4164ffd7c74aSJoe Talbott {
4165ffd7c74aSJoe Talbott struct iwn_softc *sc = arg;
4166ffd7c74aSJoe Talbott uint32_t r1, r2, tmp;
4167ffd7c74aSJoe Talbott
416805538f72SMatthew Dillon IWN_LOCK(sc);
416905538f72SMatthew Dillon
4170ffd7c74aSJoe Talbott /* Disable interrupts. */
4171ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_INT_MASK, 0);
4172ffd7c74aSJoe Talbott
4173ffd7c74aSJoe Talbott /* Read interrupts from ICT (fast) or from registers (slow). */
4174ffd7c74aSJoe Talbott if (sc->sc_flags & IWN_FLAG_USE_ICT) {
4175ffd7c74aSJoe Talbott tmp = 0;
4176ffd7c74aSJoe Talbott while (sc->ict[sc->ict_cur] != 0) {
4177ffd7c74aSJoe Talbott tmp |= sc->ict[sc->ict_cur];
4178ffd7c74aSJoe Talbott sc->ict[sc->ict_cur] = 0; /* Acknowledge. */
4179ffd7c74aSJoe Talbott sc->ict_cur = (sc->ict_cur + 1) % IWN_ICT_COUNT;
4180ffd7c74aSJoe Talbott }
4181ffd7c74aSJoe Talbott tmp = le32toh(tmp);
4182ffd7c74aSJoe Talbott if (tmp == 0xffffffff) /* Shouldn't happen. */
4183ffd7c74aSJoe Talbott tmp = 0;
4184ffd7c74aSJoe Talbott else if (tmp & 0xc0000) /* Workaround a HW bug. */
4185ffd7c74aSJoe Talbott tmp |= 0x8000;
4186ffd7c74aSJoe Talbott r1 = (tmp & 0xff00) << 16 | (tmp & 0xff);
4187ffd7c74aSJoe Talbott r2 = 0; /* Unused. */
4188ffd7c74aSJoe Talbott } else {
4189ffd7c74aSJoe Talbott r1 = IWN_READ(sc, IWN_INT);
4190977fc0dbSMatthew Dillon if (r1 == 0xffffffff || (r1 & 0xfffffff0) == 0xa5a5a5a0) {
4191977fc0dbSMatthew Dillon IWN_UNLOCK(sc);
4192ffd7c74aSJoe Talbott return; /* Hardware gone! */
4193977fc0dbSMatthew Dillon }
4194ffd7c74aSJoe Talbott r2 = IWN_READ(sc, IWN_FH_INT);
4195ffd7c74aSJoe Talbott }
4196ffd7c74aSJoe Talbott
4197da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_INTR, "interrupt reg1=0x%08x reg2=0x%08x\n"
4198da10ea93SMatthew Dillon , r1, r2);
4199ffd7c74aSJoe Talbott
4200ffd7c74aSJoe Talbott if (r1 == 0 && r2 == 0)
4201ffd7c74aSJoe Talbott goto done; /* Interrupt not for us. */
4202ffd7c74aSJoe Talbott
4203ffd7c74aSJoe Talbott /* Acknowledge interrupts. */
4204ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_INT, r1);
4205ffd7c74aSJoe Talbott if (!(sc->sc_flags & IWN_FLAG_USE_ICT))
4206ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_FH_INT, r2);
4207ffd7c74aSJoe Talbott
4208ffd7c74aSJoe Talbott if (r1 & IWN_INT_RF_TOGGLED) {
4209ffd7c74aSJoe Talbott iwn_rftoggle_intr(sc);
4210ffd7c74aSJoe Talbott goto done;
4211ffd7c74aSJoe Talbott }
4212ffd7c74aSJoe Talbott if (r1 & IWN_INT_CT_REACHED) {
4213ffd7c74aSJoe Talbott device_printf(sc->sc_dev, "%s: critical temperature reached!\n",
4214ffd7c74aSJoe Talbott __func__);
4215ffd7c74aSJoe Talbott }
4216ffd7c74aSJoe Talbott if (r1 & (IWN_INT_SW_ERR | IWN_INT_HW_ERR)) {
4217da10ea93SMatthew Dillon device_printf(sc->sc_dev, "%s: fatal firmware error\n",
4218da10ea93SMatthew Dillon __func__);
4219da10ea93SMatthew Dillon #ifdef IWN_DEBUG
4220da10ea93SMatthew Dillon iwn_debug_register(sc);
4221da10ea93SMatthew Dillon #endif
4222da10ea93SMatthew Dillon /* Dump firmware error log and stop. */
4223ffd7c74aSJoe Talbott iwn_fatal_intr(sc);
4224fd49669cSMichael Neumann
4225fd49669cSMichael Neumann taskqueue_enqueue(sc->sc_tq, &sc->sc_panic_task);
4226ffd7c74aSJoe Talbott goto done;
4227ffd7c74aSJoe Talbott }
4228ffd7c74aSJoe Talbott if ((r1 & (IWN_INT_FH_RX | IWN_INT_SW_RX | IWN_INT_RX_PERIODIC)) ||
4229ffd7c74aSJoe Talbott (r2 & IWN_FH_INT_RX)) {
4230ffd7c74aSJoe Talbott if (sc->sc_flags & IWN_FLAG_USE_ICT) {
4231ffd7c74aSJoe Talbott if (r1 & (IWN_INT_FH_RX | IWN_INT_SW_RX))
4232ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_FH_INT, IWN_FH_INT_RX);
4233ffd7c74aSJoe Talbott IWN_WRITE_1(sc, IWN_INT_PERIODIC,
4234ffd7c74aSJoe Talbott IWN_INT_PERIODIC_DIS);
4235ffd7c74aSJoe Talbott iwn_notif_intr(sc);
4236ffd7c74aSJoe Talbott if (r1 & (IWN_INT_FH_RX | IWN_INT_SW_RX)) {
4237ffd7c74aSJoe Talbott IWN_WRITE_1(sc, IWN_INT_PERIODIC,
4238ffd7c74aSJoe Talbott IWN_INT_PERIODIC_ENA);
4239ffd7c74aSJoe Talbott }
4240ffd7c74aSJoe Talbott } else
4241ffd7c74aSJoe Talbott iwn_notif_intr(sc);
4242ffd7c74aSJoe Talbott }
4243ffd7c74aSJoe Talbott
4244ffd7c74aSJoe Talbott if ((r1 & IWN_INT_FH_TX) || (r2 & IWN_FH_INT_TX)) {
4245ffd7c74aSJoe Talbott if (sc->sc_flags & IWN_FLAG_USE_ICT)
4246ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_FH_INT, IWN_FH_INT_TX);
4247ffd7c74aSJoe Talbott wakeup(sc); /* FH DMA transfer completed. */
4248ffd7c74aSJoe Talbott }
4249ffd7c74aSJoe Talbott
4250ffd7c74aSJoe Talbott if (r1 & IWN_INT_ALIVE)
4251ffd7c74aSJoe Talbott wakeup(sc); /* Firmware is alive. */
4252ffd7c74aSJoe Talbott
4253ffd7c74aSJoe Talbott if (r1 & IWN_INT_WAKEUP)
4254ffd7c74aSJoe Talbott iwn_wakeup_intr(sc);
4255ffd7c74aSJoe Talbott
4256ffd7c74aSJoe Talbott done:
4257ffd7c74aSJoe Talbott /* Re-enable interrupts. */
4258977fc0dbSMatthew Dillon if (sc->sc_flags & IWN_FLAG_RUNNING)
4259ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask);
426005538f72SMatthew Dillon
426105538f72SMatthew Dillon IWN_UNLOCK(sc);
4262ffd7c74aSJoe Talbott }
4263ffd7c74aSJoe Talbott
4264ffd7c74aSJoe Talbott /*
4265ffd7c74aSJoe Talbott * Update TX scheduler ring when transmitting an 802.11 frame (4965AGN and
4266da10ea93SMatthew Dillon * 5000 adapters use a slightly different format).
4267ffd7c74aSJoe Talbott */
4268ffd7c74aSJoe Talbott static void
iwn4965_update_sched(struct iwn_softc * sc,int qid,int idx,uint8_t id,uint16_t len)4269ffd7c74aSJoe Talbott iwn4965_update_sched(struct iwn_softc *sc, int qid, int idx, uint8_t id,
4270ffd7c74aSJoe Talbott uint16_t len)
4271ffd7c74aSJoe Talbott {
4272ffd7c74aSJoe Talbott uint16_t *w = &sc->sched[qid * IWN4965_SCHED_COUNT + idx];
4273ffd7c74aSJoe Talbott
4274da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
4275da10ea93SMatthew Dillon
4276ffd7c74aSJoe Talbott *w = htole16(len + 8);
4277ffd7c74aSJoe Talbott bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map,
4278ffd7c74aSJoe Talbott BUS_DMASYNC_PREWRITE);
4279ffd7c74aSJoe Talbott if (idx < IWN_SCHED_WINSZ) {
4280ffd7c74aSJoe Talbott *(w + IWN_TX_RING_COUNT) = *w;
4281ffd7c74aSJoe Talbott bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map,
4282ffd7c74aSJoe Talbott BUS_DMASYNC_PREWRITE);
4283ffd7c74aSJoe Talbott }
4284ffd7c74aSJoe Talbott }
4285ffd7c74aSJoe Talbott
4286ffd7c74aSJoe Talbott static void
iwn5000_update_sched(struct iwn_softc * sc,int qid,int idx,uint8_t id,uint16_t len)4287ffd7c74aSJoe Talbott iwn5000_update_sched(struct iwn_softc *sc, int qid, int idx, uint8_t id,
4288ffd7c74aSJoe Talbott uint16_t len)
4289ffd7c74aSJoe Talbott {
4290ffd7c74aSJoe Talbott uint16_t *w = &sc->sched[qid * IWN5000_SCHED_COUNT + idx];
4291ffd7c74aSJoe Talbott
4292da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
4293ffd7c74aSJoe Talbott
4294da10ea93SMatthew Dillon *w = htole16(id << 12 | (len + 8));
4295ffd7c74aSJoe Talbott bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map,
4296ffd7c74aSJoe Talbott BUS_DMASYNC_PREWRITE);
4297ffd7c74aSJoe Talbott if (idx < IWN_SCHED_WINSZ) {
4298ffd7c74aSJoe Talbott *(w + IWN_TX_RING_COUNT) = *w;
4299ffd7c74aSJoe Talbott bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map,
4300ffd7c74aSJoe Talbott BUS_DMASYNC_PREWRITE);
4301ffd7c74aSJoe Talbott }
4302ffd7c74aSJoe Talbott }
4303ffd7c74aSJoe Talbott
4304ffd7c74aSJoe Talbott #ifdef notyet
4305ffd7c74aSJoe Talbott static void
iwn5000_reset_sched(struct iwn_softc * sc,int qid,int idx)4306ffd7c74aSJoe Talbott iwn5000_reset_sched(struct iwn_softc *sc, int qid, int idx)
4307ffd7c74aSJoe Talbott {
4308ffd7c74aSJoe Talbott uint16_t *w = &sc->sched[qid * IWN5000_SCHED_COUNT + idx];
4309ffd7c74aSJoe Talbott
4310da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
4311da10ea93SMatthew Dillon
4312ffd7c74aSJoe Talbott *w = (*w & htole16(0xf000)) | htole16(1);
4313ffd7c74aSJoe Talbott bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map,
4314ffd7c74aSJoe Talbott BUS_DMASYNC_PREWRITE);
4315ffd7c74aSJoe Talbott if (idx < IWN_SCHED_WINSZ) {
4316ffd7c74aSJoe Talbott *(w + IWN_TX_RING_COUNT) = *w;
4317ffd7c74aSJoe Talbott bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map,
4318ffd7c74aSJoe Talbott BUS_DMASYNC_PREWRITE);
4319ffd7c74aSJoe Talbott }
4320ffd7c74aSJoe Talbott }
4321ffd7c74aSJoe Talbott #endif
4322ffd7c74aSJoe Talbott
4323da10ea93SMatthew Dillon /*
4324da10ea93SMatthew Dillon * Check whether OFDM 11g protection will be enabled for the given rate.
4325da10ea93SMatthew Dillon *
4326da10ea93SMatthew Dillon * The original driver code only enabled protection for OFDM rates.
4327da10ea93SMatthew Dillon * It didn't check to see whether it was operating in 11a or 11bg mode.
4328da10ea93SMatthew Dillon */
4329da10ea93SMatthew Dillon static int
iwn_check_rate_needs_protection(struct iwn_softc * sc,struct ieee80211vap * vap,uint8_t rate)4330da10ea93SMatthew Dillon iwn_check_rate_needs_protection(struct iwn_softc *sc,
4331da10ea93SMatthew Dillon struct ieee80211vap *vap, uint8_t rate)
4332da10ea93SMatthew Dillon {
4333da10ea93SMatthew Dillon struct ieee80211com *ic = vap->iv_ic;
4334ffd7c74aSJoe Talbott
4335da10ea93SMatthew Dillon /*
4336da10ea93SMatthew Dillon * Not in 2GHz mode? Then there's no need to enable OFDM
4337da10ea93SMatthew Dillon * 11bg protection.
4338da10ea93SMatthew Dillon */
4339da10ea93SMatthew Dillon if (! IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) {
4340da10ea93SMatthew Dillon return (0);
4341ffd7c74aSJoe Talbott }
4342ffd7c74aSJoe Talbott
4343da10ea93SMatthew Dillon /*
4344da10ea93SMatthew Dillon * 11bg protection not enabled? Then don't use it.
4345da10ea93SMatthew Dillon */
4346da10ea93SMatthew Dillon if ((ic->ic_flags & IEEE80211_F_USEPROT) == 0)
4347da10ea93SMatthew Dillon return (0);
4348da10ea93SMatthew Dillon
4349da10ea93SMatthew Dillon /*
435005538f72SMatthew Dillon * If it's an 11n rate - no protection.
435105538f72SMatthew Dillon * We'll do it via a specific 11n check.
4352da10ea93SMatthew Dillon */
4353da10ea93SMatthew Dillon if (rate & IEEE80211_RATE_MCS) {
435405538f72SMatthew Dillon return (0);
4355da10ea93SMatthew Dillon }
4356da10ea93SMatthew Dillon
4357da10ea93SMatthew Dillon /*
4358da10ea93SMatthew Dillon * Do a rate table lookup. If the PHY is CCK,
4359da10ea93SMatthew Dillon * don't do protection.
4360da10ea93SMatthew Dillon */
4361da10ea93SMatthew Dillon if (ieee80211_rate2phytype(ic->ic_rt, rate) == IEEE80211_T_CCK)
4362da10ea93SMatthew Dillon return (0);
4363da10ea93SMatthew Dillon
4364da10ea93SMatthew Dillon /*
4365da10ea93SMatthew Dillon * Yup, enable protection.
4366da10ea93SMatthew Dillon */
4367da10ea93SMatthew Dillon return (1);
4368da10ea93SMatthew Dillon }
4369da10ea93SMatthew Dillon
4370da10ea93SMatthew Dillon /*
4371da10ea93SMatthew Dillon * return a value between 0 and IWN_MAX_TX_RETRIES-1 as an index into
4372da10ea93SMatthew Dillon * the link quality table that reflects this particular entry.
4373da10ea93SMatthew Dillon */
4374da10ea93SMatthew Dillon static int
iwn_tx_rate_to_linkq_offset(struct iwn_softc * sc,struct ieee80211_node * ni,uint8_t rate)4375da10ea93SMatthew Dillon iwn_tx_rate_to_linkq_offset(struct iwn_softc *sc, struct ieee80211_node *ni,
4376da10ea93SMatthew Dillon uint8_t rate)
4377da10ea93SMatthew Dillon {
4378da10ea93SMatthew Dillon struct ieee80211_rateset *rs;
4379da10ea93SMatthew Dillon int is_11n;
4380da10ea93SMatthew Dillon int nr;
4381da10ea93SMatthew Dillon int i;
4382da10ea93SMatthew Dillon uint8_t cmp_rate;
4383da10ea93SMatthew Dillon
4384da10ea93SMatthew Dillon /*
4385da10ea93SMatthew Dillon * Figure out if we're using 11n or not here.
4386da10ea93SMatthew Dillon */
4387da10ea93SMatthew Dillon if (IEEE80211_IS_CHAN_HT(ni->ni_chan) && ni->ni_htrates.rs_nrates > 0)
4388da10ea93SMatthew Dillon is_11n = 1;
4389da10ea93SMatthew Dillon else
4390da10ea93SMatthew Dillon is_11n = 0;
4391da10ea93SMatthew Dillon
4392da10ea93SMatthew Dillon /*
4393da10ea93SMatthew Dillon * Use the correct rate table.
4394da10ea93SMatthew Dillon */
4395da10ea93SMatthew Dillon if (is_11n) {
4396da10ea93SMatthew Dillon rs = (struct ieee80211_rateset *) &ni->ni_htrates;
4397da10ea93SMatthew Dillon nr = ni->ni_htrates.rs_nrates;
4398da10ea93SMatthew Dillon } else {
4399da10ea93SMatthew Dillon rs = &ni->ni_rates;
4400da10ea93SMatthew Dillon nr = rs->rs_nrates;
4401da10ea93SMatthew Dillon }
4402da10ea93SMatthew Dillon
4403da10ea93SMatthew Dillon /*
4404da10ea93SMatthew Dillon * Find the relevant link quality entry in the table.
4405da10ea93SMatthew Dillon */
4406da10ea93SMatthew Dillon for (i = 0; i < nr && i < IWN_MAX_TX_RETRIES - 1 ; i++) {
4407da10ea93SMatthew Dillon /*
4408da10ea93SMatthew Dillon * The link quality table index starts at 0 == highest
4409da10ea93SMatthew Dillon * rate, so we walk the rate table backwards.
4410da10ea93SMatthew Dillon */
4411da10ea93SMatthew Dillon cmp_rate = rs->rs_rates[(nr - 1) - i];
4412da10ea93SMatthew Dillon if (rate & IEEE80211_RATE_MCS)
4413da10ea93SMatthew Dillon cmp_rate |= IEEE80211_RATE_MCS;
4414da10ea93SMatthew Dillon
44154b420e05SMatthew Dillon #if 0
4416da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_XMIT, "%s: idx %d: nr=%d, rate=0x%02x, rateentry=0x%02x\n",
4417da10ea93SMatthew Dillon __func__,
4418da10ea93SMatthew Dillon i,
4419da10ea93SMatthew Dillon nr,
4420da10ea93SMatthew Dillon rate,
4421da10ea93SMatthew Dillon cmp_rate);
44224b420e05SMatthew Dillon #endif
4423da10ea93SMatthew Dillon
4424da10ea93SMatthew Dillon if (cmp_rate == rate)
4425da10ea93SMatthew Dillon return (i);
4426da10ea93SMatthew Dillon }
4427da10ea93SMatthew Dillon
4428da10ea93SMatthew Dillon /* Failed? Start at the end */
4429da10ea93SMatthew Dillon return (IWN_MAX_TX_RETRIES - 1);
4430ffd7c74aSJoe Talbott }
4431ffd7c74aSJoe Talbott
4432ffd7c74aSJoe Talbott static int
iwn_tx_data(struct iwn_softc * sc,struct mbuf * m,struct ieee80211_node * ni)4433da10ea93SMatthew Dillon iwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
4434ffd7c74aSJoe Talbott {
4435da10ea93SMatthew Dillon struct iwn_ops *ops = &sc->ops;
4436ffd7c74aSJoe Talbott const struct ieee80211_txparam *tp;
4437ffd7c74aSJoe Talbott struct ieee80211vap *vap = ni->ni_vap;
4438ffd7c74aSJoe Talbott struct ieee80211com *ic = ni->ni_ic;
4439ffd7c74aSJoe Talbott struct iwn_node *wn = (void *)ni;
4440da10ea93SMatthew Dillon struct iwn_tx_ring *ring;
4441ffd7c74aSJoe Talbott struct iwn_tx_desc *desc;
4442ffd7c74aSJoe Talbott struct iwn_tx_data *data;
4443ffd7c74aSJoe Talbott struct iwn_tx_cmd *cmd;
4444ffd7c74aSJoe Talbott struct iwn_cmd_data *tx;
4445ffd7c74aSJoe Talbott struct ieee80211_frame *wh;
4446ffd7c74aSJoe Talbott struct ieee80211_key *k = NULL;
4447da10ea93SMatthew Dillon struct mbuf *m1;
4448ffd7c74aSJoe Talbott uint32_t flags;
4449da10ea93SMatthew Dillon uint16_t qos;
4450ffd7c74aSJoe Talbott u_int hdrlen;
4451da10ea93SMatthew Dillon bus_dma_segment_t *seg, segs[IWN_MAX_SCATTER];
4452da10ea93SMatthew Dillon uint8_t tid, type;
4453da10ea93SMatthew Dillon int ac, i, totlen, error, pad, nsegs = 0, rate;
4454da10ea93SMatthew Dillon
4455da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
4456da10ea93SMatthew Dillon
445705538f72SMatthew Dillon IWN_LOCK_ASSERT(sc);
445805538f72SMatthew Dillon
4459ffd7c74aSJoe Talbott wh = mtod(m, struct ieee80211_frame *);
4460ffd7c74aSJoe Talbott hdrlen = ieee80211_anyhdrsize(wh);
4461ffd7c74aSJoe Talbott type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
4462ffd7c74aSJoe Talbott
4463da10ea93SMatthew Dillon /* Select EDCA Access Category and TX ring for this frame. */
4464da10ea93SMatthew Dillon if (IEEE80211_QOS_HAS_SEQ(wh)) {
4465da10ea93SMatthew Dillon qos = ((const struct ieee80211_qosframe *)wh)->i_qos[0];
4466da10ea93SMatthew Dillon tid = qos & IEEE80211_QOS_TID;
4467da10ea93SMatthew Dillon } else {
4468da10ea93SMatthew Dillon qos = 0;
4469da10ea93SMatthew Dillon tid = 0;
4470da10ea93SMatthew Dillon }
4471da10ea93SMatthew Dillon ac = M_WME_GETAC(m);
4472da10ea93SMatthew Dillon if (m->m_flags & M_AMPDU_MPDU) {
44734b420e05SMatthew Dillon uint16_t seqno;
4474da10ea93SMatthew Dillon struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[ac];
4475da10ea93SMatthew Dillon
4476da10ea93SMatthew Dillon if (!IEEE80211_AMPDU_RUNNING(tap)) {
4477da10ea93SMatthew Dillon return EINVAL;
4478da10ea93SMatthew Dillon }
4479da10ea93SMatthew Dillon
44804b420e05SMatthew Dillon /*
44814b420e05SMatthew Dillon * Queue this frame to the hardware ring that we've
44824b420e05SMatthew Dillon * negotiated AMPDU TX on.
44834b420e05SMatthew Dillon *
44844b420e05SMatthew Dillon * Note that the sequence number must match the TX slot
44854b420e05SMatthew Dillon * being used!
44864b420e05SMatthew Dillon */
4487da10ea93SMatthew Dillon ac = *(int *)tap->txa_private;
44884b420e05SMatthew Dillon seqno = ni->ni_txseqs[tid];
4489da10ea93SMatthew Dillon *(uint16_t *)wh->i_seq =
44904b420e05SMatthew Dillon htole16(seqno << IEEE80211_SEQ_SEQ_SHIFT);
44914b420e05SMatthew Dillon ring = &sc->txq[ac];
44924b420e05SMatthew Dillon if ((seqno % 256) != ring->cur) {
44934b420e05SMatthew Dillon device_printf(sc->sc_dev,
44944b420e05SMatthew Dillon "%s: m=%p: seqno (%d) (%d) != ring index (%d) !\n",
44954b420e05SMatthew Dillon __func__,
44964b420e05SMatthew Dillon m,
44974b420e05SMatthew Dillon seqno,
44984b420e05SMatthew Dillon seqno % 256,
44994b420e05SMatthew Dillon ring->cur);
45004b420e05SMatthew Dillon }
4501da10ea93SMatthew Dillon ni->ni_txseqs[tid]++;
4502da10ea93SMatthew Dillon }
4503da10ea93SMatthew Dillon ring = &sc->txq[ac];
4504ffd7c74aSJoe Talbott desc = &ring->desc[ring->cur];
4505ffd7c74aSJoe Talbott data = &ring->data[ring->cur];
4506ffd7c74aSJoe Talbott
4507ffd7c74aSJoe Talbott /* Choose a TX rate index. */
4508ffd7c74aSJoe Talbott tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)];
4509ffd7c74aSJoe Talbott if (type == IEEE80211_FC0_TYPE_MGT)
4510ffd7c74aSJoe Talbott rate = tp->mgmtrate;
4511ffd7c74aSJoe Talbott else if (IEEE80211_IS_MULTICAST(wh->i_addr1))
4512ffd7c74aSJoe Talbott rate = tp->mcastrate;
4513ffd7c74aSJoe Talbott else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE)
4514ffd7c74aSJoe Talbott rate = tp->ucastrate;
4515da10ea93SMatthew Dillon else if (m->m_flags & M_EAPOL)
4516da10ea93SMatthew Dillon rate = tp->mgmtrate;
4517ffd7c74aSJoe Talbott else {
4518ffd7c74aSJoe Talbott /* XXX pass pktlen */
4519da10ea93SMatthew Dillon (void) ieee80211_ratectl_rate(ni, NULL, 0);
4520ffd7c74aSJoe Talbott rate = ni->ni_txrate;
4521ffd7c74aSJoe Talbott }
4522ffd7c74aSJoe Talbott
4523ffd7c74aSJoe Talbott /* Encrypt the frame if need be. */
4524085ff963SMatthew Dillon if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
4525da10ea93SMatthew Dillon /* Retrieve key for TX. */
4526ffd7c74aSJoe Talbott k = ieee80211_crypto_encap(ni, m);
4527ffd7c74aSJoe Talbott if (k == NULL) {
4528ffd7c74aSJoe Talbott return ENOBUFS;
4529ffd7c74aSJoe Talbott }
4530da10ea93SMatthew Dillon /* 802.11 header may have moved. */
4531ffd7c74aSJoe Talbott wh = mtod(m, struct ieee80211_frame *);
4532ffd7c74aSJoe Talbott }
4533ffd7c74aSJoe Talbott totlen = m->m_pkthdr.len;
4534ffd7c74aSJoe Talbott
4535ffd7c74aSJoe Talbott if (ieee80211_radiotap_active_vap(vap)) {
4536ffd7c74aSJoe Talbott struct iwn_tx_radiotap_header *tap = &sc->sc_txtap;
4537ffd7c74aSJoe Talbott
4538ffd7c74aSJoe Talbott tap->wt_flags = 0;
4539da10ea93SMatthew Dillon tap->wt_rate = rate;
4540ffd7c74aSJoe Talbott if (k != NULL)
4541ffd7c74aSJoe Talbott tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP;
4542ffd7c74aSJoe Talbott
4543ffd7c74aSJoe Talbott ieee80211_radiotap_tx(vap, m);
4544ffd7c74aSJoe Talbott }
4545ffd7c74aSJoe Talbott
4546ffd7c74aSJoe Talbott /* Prepare TX firmware command. */
4547ffd7c74aSJoe Talbott cmd = &ring->cmd[ring->cur];
4548ffd7c74aSJoe Talbott cmd->code = IWN_CMD_TX_DATA;
4549ffd7c74aSJoe Talbott cmd->flags = 0;
4550ffd7c74aSJoe Talbott cmd->qid = ring->qid;
4551ffd7c74aSJoe Talbott cmd->idx = ring->cur;
4552ffd7c74aSJoe Talbott
4553ffd7c74aSJoe Talbott tx = (struct iwn_cmd_data *)cmd->data;
4554ffd7c74aSJoe Talbott /* NB: No need to clear tx, all fields are reinitialized here. */
4555ffd7c74aSJoe Talbott tx->scratch = 0; /* clear "scratch" area */
4556ffd7c74aSJoe Talbott
4557ffd7c74aSJoe Talbott flags = 0;
4558da10ea93SMatthew Dillon if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
4559da10ea93SMatthew Dillon /* Unicast frame, check if an ACK is expected. */
4560da10ea93SMatthew Dillon if (!qos || (qos & IEEE80211_QOS_ACKPOLICY) !=
4561da10ea93SMatthew Dillon IEEE80211_QOS_ACKPOLICY_NOACK)
4562ffd7c74aSJoe Talbott flags |= IWN_TX_NEED_ACK;
4563da10ea93SMatthew Dillon }
4564ffd7c74aSJoe Talbott if ((wh->i_fc[0] &
4565ffd7c74aSJoe Talbott (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) ==
4566ffd7c74aSJoe Talbott (IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_BAR))
4567ffd7c74aSJoe Talbott flags |= IWN_TX_IMM_BA; /* Cannot happen yet. */
4568ffd7c74aSJoe Talbott
4569ffd7c74aSJoe Talbott if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG)
4570ffd7c74aSJoe Talbott flags |= IWN_TX_MORE_FRAG; /* Cannot happen yet. */
4571ffd7c74aSJoe Talbott
4572ffd7c74aSJoe Talbott /* Check if frame must be protected using RTS/CTS or CTS-to-self. */
4573ffd7c74aSJoe Talbott if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
4574ffd7c74aSJoe Talbott /* NB: Group frames are sent using CCK in 802.11b/g. */
4575ffd7c74aSJoe Talbott if (totlen + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) {
4576ffd7c74aSJoe Talbott flags |= IWN_TX_NEED_RTS;
4577da10ea93SMatthew Dillon } else if (iwn_check_rate_needs_protection(sc, vap, rate)) {
4578ffd7c74aSJoe Talbott if (ic->ic_protmode == IEEE80211_PROT_CTSONLY)
4579ffd7c74aSJoe Talbott flags |= IWN_TX_NEED_CTS;
4580ffd7c74aSJoe Talbott else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS)
4581ffd7c74aSJoe Talbott flags |= IWN_TX_NEED_RTS;
458205538f72SMatthew Dillon } else if ((rate & IEEE80211_RATE_MCS) &&
458305538f72SMatthew Dillon (ic->ic_htprotmode == IEEE80211_PROT_RTSCTS)) {
458405538f72SMatthew Dillon flags |= IWN_TX_NEED_RTS;
4585ffd7c74aSJoe Talbott }
4586da10ea93SMatthew Dillon
4587da10ea93SMatthew Dillon /* XXX HT protection? */
4588da10ea93SMatthew Dillon
4589ffd7c74aSJoe Talbott if (flags & (IWN_TX_NEED_RTS | IWN_TX_NEED_CTS)) {
4590ffd7c74aSJoe Talbott if (sc->hw_type != IWN_HW_REV_TYPE_4965) {
4591ffd7c74aSJoe Talbott /* 5000 autoselects RTS/CTS or CTS-to-self. */
4592ffd7c74aSJoe Talbott flags &= ~(IWN_TX_NEED_RTS | IWN_TX_NEED_CTS);
4593ffd7c74aSJoe Talbott flags |= IWN_TX_NEED_PROTECTION;
4594ffd7c74aSJoe Talbott } else
4595ffd7c74aSJoe Talbott flags |= IWN_TX_FULL_TXOP;
4596ffd7c74aSJoe Talbott }
4597ffd7c74aSJoe Talbott }
4598ffd7c74aSJoe Talbott
4599ffd7c74aSJoe Talbott if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
4600ffd7c74aSJoe Talbott type != IEEE80211_FC0_TYPE_DATA)
4601da10ea93SMatthew Dillon tx->id = sc->broadcast_id;
4602ffd7c74aSJoe Talbott else
4603ffd7c74aSJoe Talbott tx->id = wn->id;
4604ffd7c74aSJoe Talbott
4605ffd7c74aSJoe Talbott if (type == IEEE80211_FC0_TYPE_MGT) {
4606ffd7c74aSJoe Talbott uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
4607ffd7c74aSJoe Talbott
4608ffd7c74aSJoe Talbott /* Tell HW to set timestamp in probe responses. */
4609ffd7c74aSJoe Talbott if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
4610ffd7c74aSJoe Talbott flags |= IWN_TX_INSERT_TSTAMP;
4611ffd7c74aSJoe Talbott if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ ||
4612ffd7c74aSJoe Talbott subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ)
4613ffd7c74aSJoe Talbott tx->timeout = htole16(3);
4614ffd7c74aSJoe Talbott else
4615ffd7c74aSJoe Talbott tx->timeout = htole16(2);
4616ffd7c74aSJoe Talbott } else
4617ffd7c74aSJoe Talbott tx->timeout = htole16(0);
4618ffd7c74aSJoe Talbott
4619ffd7c74aSJoe Talbott if (hdrlen & 3) {
4620ffd7c74aSJoe Talbott /* First segment length must be a multiple of 4. */
4621ffd7c74aSJoe Talbott flags |= IWN_TX_NEED_PADDING;
4622ffd7c74aSJoe Talbott pad = 4 - (hdrlen & 3);
4623ffd7c74aSJoe Talbott } else
4624ffd7c74aSJoe Talbott pad = 0;
4625ffd7c74aSJoe Talbott
4626ffd7c74aSJoe Talbott tx->len = htole16(totlen);
4627da10ea93SMatthew Dillon tx->tid = tid;
4628ffd7c74aSJoe Talbott tx->rts_ntries = 60;
4629ffd7c74aSJoe Talbott tx->data_ntries = 15;
4630ffd7c74aSJoe Talbott tx->lifetime = htole32(IWN_LIFETIME_INFINITE);
4631da10ea93SMatthew Dillon tx->rate = iwn_rate_to_plcp(sc, ni, rate);
4632da10ea93SMatthew Dillon if (tx->id == sc->broadcast_id) {
4633ffd7c74aSJoe Talbott /* Group or management frame. */
4634ffd7c74aSJoe Talbott tx->linkq = 0;
4635ffd7c74aSJoe Talbott } else {
4636da10ea93SMatthew Dillon tx->linkq = iwn_tx_rate_to_linkq_offset(sc, ni, rate);
4637ffd7c74aSJoe Talbott flags |= IWN_TX_LINKQ; /* enable MRR */
4638ffd7c74aSJoe Talbott }
4639ffd7c74aSJoe Talbott
4640ffd7c74aSJoe Talbott /* Set physical address of "scratch area". */
4641ffd7c74aSJoe Talbott tx->loaddr = htole32(IWN_LOADDR(data->scratch_paddr));
4642ffd7c74aSJoe Talbott tx->hiaddr = IWN_HIADDR(data->scratch_paddr);
4643ffd7c74aSJoe Talbott
4644ffd7c74aSJoe Talbott /* Copy 802.11 header in TX command. */
4645ffd7c74aSJoe Talbott memcpy((uint8_t *)(tx + 1), wh, hdrlen);
4646ffd7c74aSJoe Talbott
4647ffd7c74aSJoe Talbott /* Trim 802.11 header. */
4648ffd7c74aSJoe Talbott m_adj(m, hdrlen);
4649ffd7c74aSJoe Talbott tx->security = 0;
4650ffd7c74aSJoe Talbott tx->flags = htole32(flags);
4651ffd7c74aSJoe Talbott
465205538f72SMatthew Dillon #if defined(__DragonFly__)
465305538f72SMatthew Dillon error = bus_dmamap_load_mbuf_segment(ring->data_dmat,
465405538f72SMatthew Dillon data->map, m,
465505538f72SMatthew Dillon segs, IWN_MAX_SCATTER - 1,
4656da10ea93SMatthew Dillon &nsegs, BUS_DMA_NOWAIT);
465705538f72SMatthew Dillon #else
465805538f72SMatthew Dillon error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m, segs,
465905538f72SMatthew Dillon &nsegs, BUS_DMA_NOWAIT);
466005538f72SMatthew Dillon #endif
4661da10ea93SMatthew Dillon if (error != 0) {
4662da10ea93SMatthew Dillon if (error != EFBIG) {
4663da10ea93SMatthew Dillon device_printf(sc->sc_dev,
4664da10ea93SMatthew Dillon "%s: can't map mbuf (error %d)\n", __func__, error);
4665da10ea93SMatthew Dillon return error;
4666da10ea93SMatthew Dillon }
4667da10ea93SMatthew Dillon /* Too many DMA segments, linearize mbuf. */
466805538f72SMatthew Dillon #if defined(__DragonFly__)
4669b5523eacSSascha Wildner m1 = m_defrag(m, M_NOWAIT);
467005538f72SMatthew Dillon #else
4671977fc0dbSMatthew Dillon m1 = m_collapse(m, M_NOWAIT, IWN_MAX_SCATTER - 1);
467205538f72SMatthew Dillon #endif
4673da10ea93SMatthew Dillon if (m1 == NULL) {
4674ffd7c74aSJoe Talbott device_printf(sc->sc_dev,
4675ffd7c74aSJoe Talbott "%s: could not defrag mbuf\n", __func__);
4676ffd7c74aSJoe Talbott return ENOBUFS;
4677ffd7c74aSJoe Talbott }
4678da10ea93SMatthew Dillon m = m1;
4679da10ea93SMatthew Dillon
468005538f72SMatthew Dillon #if defined(__DragonFly__)
4681101554a6SMatthew Dillon error = bus_dmamap_load_mbuf_segment(ring->data_dmat,
468205538f72SMatthew Dillon data->map, m,
468305538f72SMatthew Dillon segs, IWN_MAX_SCATTER - 1,
4684101554a6SMatthew Dillon &nsegs, BUS_DMA_NOWAIT);
468505538f72SMatthew Dillon #else
468605538f72SMatthew Dillon error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m,
468705538f72SMatthew Dillon segs, &nsegs, BUS_DMA_NOWAIT);
468805538f72SMatthew Dillon #endif
4689ffd7c74aSJoe Talbott if (error != 0) {
4690ffd7c74aSJoe Talbott device_printf(sc->sc_dev,
4691da10ea93SMatthew Dillon "%s: can't map mbuf (error %d)\n", __func__, error);
4692ffd7c74aSJoe Talbott return error;
4693ffd7c74aSJoe Talbott }
4694ffd7c74aSJoe Talbott }
4695ffd7c74aSJoe Talbott
4696ffd7c74aSJoe Talbott data->m = m;
4697ffd7c74aSJoe Talbott data->ni = ni;
4698ffd7c74aSJoe Talbott
4699da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_XMIT,
470005538f72SMatthew Dillon "%s: qid %d idx %d len %d nsegs %d flags 0x%08x rate 0x%04x plcp 0x%08x\n",
4701da10ea93SMatthew Dillon __func__,
4702da10ea93SMatthew Dillon ring->qid,
4703da10ea93SMatthew Dillon ring->cur,
4704da10ea93SMatthew Dillon m->m_pkthdr.len,
4705da10ea93SMatthew Dillon nsegs,
470605538f72SMatthew Dillon flags,
4707da10ea93SMatthew Dillon rate,
4708da10ea93SMatthew Dillon tx->rate);
4709ffd7c74aSJoe Talbott
4710ffd7c74aSJoe Talbott /* Fill TX descriptor. */
4711da10ea93SMatthew Dillon desc->nsegs = 1;
4712da10ea93SMatthew Dillon if (m->m_len != 0)
4713da10ea93SMatthew Dillon desc->nsegs += nsegs;
4714ffd7c74aSJoe Talbott /* First DMA segment is used by the TX command. */
4715ffd7c74aSJoe Talbott desc->segs[0].addr = htole32(IWN_LOADDR(data->cmd_paddr));
4716ffd7c74aSJoe Talbott desc->segs[0].len = htole16(IWN_HIADDR(data->cmd_paddr) |
4717ffd7c74aSJoe Talbott (4 + sizeof (*tx) + hdrlen + pad) << 4);
4718ffd7c74aSJoe Talbott /* Other DMA segments are for data payload. */
4719da10ea93SMatthew Dillon seg = &segs[0];
4720ffd7c74aSJoe Talbott for (i = 1; i <= nsegs; i++) {
4721da10ea93SMatthew Dillon desc->segs[i].addr = htole32(IWN_LOADDR(seg->ds_addr));
4722da10ea93SMatthew Dillon desc->segs[i].len = htole16(IWN_HIADDR(seg->ds_addr) |
4723da10ea93SMatthew Dillon seg->ds_len << 4);
4724da10ea93SMatthew Dillon seg++;
4725ffd7c74aSJoe Talbott }
4726ffd7c74aSJoe Talbott
4727ffd7c74aSJoe Talbott bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE);
4728ffd7c74aSJoe Talbott bus_dmamap_sync(ring->data_dmat, ring->cmd_dma.map,
4729ffd7c74aSJoe Talbott BUS_DMASYNC_PREWRITE);
4730ffd7c74aSJoe Talbott bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
4731ffd7c74aSJoe Talbott BUS_DMASYNC_PREWRITE);
4732ffd7c74aSJoe Talbott
4733ffd7c74aSJoe Talbott /* Update TX scheduler. */
4734da10ea93SMatthew Dillon if (ring->qid >= sc->firstaggqueue)
4735da10ea93SMatthew Dillon ops->update_sched(sc, ring->qid, ring->cur, tx->id, totlen);
4736ffd7c74aSJoe Talbott
4737ffd7c74aSJoe Talbott /* Kick TX ring. */
4738ffd7c74aSJoe Talbott ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT;
4739ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur);
4740ffd7c74aSJoe Talbott
4741ffd7c74aSJoe Talbott /* Mark TX ring as full if we reach a certain threshold. */
4742ffd7c74aSJoe Talbott if (++ring->queued > IWN_TX_RING_HIMARK)
4743ffd7c74aSJoe Talbott sc->qfullmsk |= 1 << ring->qid;
4744ffd7c74aSJoe Talbott
4745da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
4746da10ea93SMatthew Dillon
4747ffd7c74aSJoe Talbott return 0;
4748ffd7c74aSJoe Talbott }
4749ffd7c74aSJoe Talbott
4750ffd7c74aSJoe Talbott static int
iwn_tx_data_raw(struct iwn_softc * sc,struct mbuf * m,struct ieee80211_node * ni,const struct ieee80211_bpf_params * params)4751ffd7c74aSJoe Talbott iwn_tx_data_raw(struct iwn_softc *sc, struct mbuf *m,
4752da10ea93SMatthew Dillon struct ieee80211_node *ni, const struct ieee80211_bpf_params *params)
4753ffd7c74aSJoe Talbott {
4754da10ea93SMatthew Dillon struct iwn_ops *ops = &sc->ops;
4755ffd7c74aSJoe Talbott struct ieee80211vap *vap = ni->ni_vap;
4756ffd7c74aSJoe Talbott struct iwn_tx_cmd *cmd;
4757ffd7c74aSJoe Talbott struct iwn_cmd_data *tx;
4758ffd7c74aSJoe Talbott struct ieee80211_frame *wh;
4759da10ea93SMatthew Dillon struct iwn_tx_ring *ring;
4760ffd7c74aSJoe Talbott struct iwn_tx_desc *desc;
4761ffd7c74aSJoe Talbott struct iwn_tx_data *data;
4762da10ea93SMatthew Dillon struct mbuf *m1;
4763da10ea93SMatthew Dillon bus_dma_segment_t *seg, segs[IWN_MAX_SCATTER];
4764ffd7c74aSJoe Talbott uint32_t flags;
4765ffd7c74aSJoe Talbott u_int hdrlen;
4766da10ea93SMatthew Dillon int ac, totlen, error, pad, nsegs = 0, i, rate;
4767da10ea93SMatthew Dillon uint8_t type;
4768da10ea93SMatthew Dillon
4769da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
4770da10ea93SMatthew Dillon
477105538f72SMatthew Dillon IWN_LOCK_ASSERT(sc);
477205538f72SMatthew Dillon
4773ffd7c74aSJoe Talbott wh = mtod(m, struct ieee80211_frame *);
4774ffd7c74aSJoe Talbott hdrlen = ieee80211_anyhdrsize(wh);
4775ffd7c74aSJoe Talbott type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
4776ffd7c74aSJoe Talbott
4777da10ea93SMatthew Dillon ac = params->ibp_pri & 3;
4778da10ea93SMatthew Dillon
4779da10ea93SMatthew Dillon ring = &sc->txq[ac];
4780ffd7c74aSJoe Talbott desc = &ring->desc[ring->cur];
4781ffd7c74aSJoe Talbott data = &ring->data[ring->cur];
4782ffd7c74aSJoe Talbott
4783da10ea93SMatthew Dillon /* Choose a TX rate. */
4784ffd7c74aSJoe Talbott rate = params->ibp_rate0;
4785ffd7c74aSJoe Talbott totlen = m->m_pkthdr.len;
4786ffd7c74aSJoe Talbott
4787ffd7c74aSJoe Talbott /* Prepare TX firmware command. */
4788ffd7c74aSJoe Talbott cmd = &ring->cmd[ring->cur];
4789ffd7c74aSJoe Talbott cmd->code = IWN_CMD_TX_DATA;
4790ffd7c74aSJoe Talbott cmd->flags = 0;
4791ffd7c74aSJoe Talbott cmd->qid = ring->qid;
4792ffd7c74aSJoe Talbott cmd->idx = ring->cur;
4793ffd7c74aSJoe Talbott
4794ffd7c74aSJoe Talbott tx = (struct iwn_cmd_data *)cmd->data;
4795ffd7c74aSJoe Talbott /* NB: No need to clear tx, all fields are reinitialized here. */
4796ffd7c74aSJoe Talbott tx->scratch = 0; /* clear "scratch" area */
4797ffd7c74aSJoe Talbott
4798ffd7c74aSJoe Talbott flags = 0;
4799ffd7c74aSJoe Talbott if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0)
4800ffd7c74aSJoe Talbott flags |= IWN_TX_NEED_ACK;
4801ffd7c74aSJoe Talbott if (params->ibp_flags & IEEE80211_BPF_RTS) {
4802ffd7c74aSJoe Talbott if (sc->hw_type != IWN_HW_REV_TYPE_4965) {
4803ffd7c74aSJoe Talbott /* 5000 autoselects RTS/CTS or CTS-to-self. */
4804ffd7c74aSJoe Talbott flags &= ~IWN_TX_NEED_RTS;
4805ffd7c74aSJoe Talbott flags |= IWN_TX_NEED_PROTECTION;
4806ffd7c74aSJoe Talbott } else
4807ffd7c74aSJoe Talbott flags |= IWN_TX_NEED_RTS | IWN_TX_FULL_TXOP;
4808ffd7c74aSJoe Talbott }
4809ffd7c74aSJoe Talbott if (params->ibp_flags & IEEE80211_BPF_CTS) {
4810ffd7c74aSJoe Talbott if (sc->hw_type != IWN_HW_REV_TYPE_4965) {
4811ffd7c74aSJoe Talbott /* 5000 autoselects RTS/CTS or CTS-to-self. */
4812ffd7c74aSJoe Talbott flags &= ~IWN_TX_NEED_CTS;
4813ffd7c74aSJoe Talbott flags |= IWN_TX_NEED_PROTECTION;
4814ffd7c74aSJoe Talbott } else
4815ffd7c74aSJoe Talbott flags |= IWN_TX_NEED_CTS | IWN_TX_FULL_TXOP;
4816ffd7c74aSJoe Talbott }
4817ffd7c74aSJoe Talbott if (type == IEEE80211_FC0_TYPE_MGT) {
4818ffd7c74aSJoe Talbott uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
4819ffd7c74aSJoe Talbott
4820da10ea93SMatthew Dillon /* Tell HW to set timestamp in probe responses. */
4821ffd7c74aSJoe Talbott if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
4822ffd7c74aSJoe Talbott flags |= IWN_TX_INSERT_TSTAMP;
4823ffd7c74aSJoe Talbott
4824ffd7c74aSJoe Talbott if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ ||
4825ffd7c74aSJoe Talbott subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ)
4826ffd7c74aSJoe Talbott tx->timeout = htole16(3);
4827ffd7c74aSJoe Talbott else
4828ffd7c74aSJoe Talbott tx->timeout = htole16(2);
4829ffd7c74aSJoe Talbott } else
4830ffd7c74aSJoe Talbott tx->timeout = htole16(0);
4831ffd7c74aSJoe Talbott
4832ffd7c74aSJoe Talbott if (hdrlen & 3) {
4833ffd7c74aSJoe Talbott /* First segment length must be a multiple of 4. */
4834ffd7c74aSJoe Talbott flags |= IWN_TX_NEED_PADDING;
4835ffd7c74aSJoe Talbott pad = 4 - (hdrlen & 3);
4836ffd7c74aSJoe Talbott } else
4837ffd7c74aSJoe Talbott pad = 0;
4838ffd7c74aSJoe Talbott
4839ffd7c74aSJoe Talbott if (ieee80211_radiotap_active_vap(vap)) {
4840ffd7c74aSJoe Talbott struct iwn_tx_radiotap_header *tap = &sc->sc_txtap;
4841ffd7c74aSJoe Talbott
4842ffd7c74aSJoe Talbott tap->wt_flags = 0;
4843ffd7c74aSJoe Talbott tap->wt_rate = rate;
4844ffd7c74aSJoe Talbott
4845ffd7c74aSJoe Talbott ieee80211_radiotap_tx(vap, m);
4846ffd7c74aSJoe Talbott }
4847ffd7c74aSJoe Talbott
4848ffd7c74aSJoe Talbott tx->len = htole16(totlen);
4849ffd7c74aSJoe Talbott tx->tid = 0;
4850da10ea93SMatthew Dillon tx->id = sc->broadcast_id;
4851ffd7c74aSJoe Talbott tx->rts_ntries = params->ibp_try1;
4852ffd7c74aSJoe Talbott tx->data_ntries = params->ibp_try0;
4853ffd7c74aSJoe Talbott tx->lifetime = htole32(IWN_LIFETIME_INFINITE);
4854da10ea93SMatthew Dillon tx->rate = iwn_rate_to_plcp(sc, ni, rate);
4855da10ea93SMatthew Dillon
4856ffd7c74aSJoe Talbott /* Group or management frame. */
4857ffd7c74aSJoe Talbott tx->linkq = 0;
4858da10ea93SMatthew Dillon
4859ffd7c74aSJoe Talbott /* Set physical address of "scratch area". */
4860da10ea93SMatthew Dillon tx->loaddr = htole32(IWN_LOADDR(data->scratch_paddr));
4861da10ea93SMatthew Dillon tx->hiaddr = IWN_HIADDR(data->scratch_paddr);
4862ffd7c74aSJoe Talbott
4863ffd7c74aSJoe Talbott /* Copy 802.11 header in TX command. */
4864ffd7c74aSJoe Talbott memcpy((uint8_t *)(tx + 1), wh, hdrlen);
4865ffd7c74aSJoe Talbott
4866ffd7c74aSJoe Talbott /* Trim 802.11 header. */
4867ffd7c74aSJoe Talbott m_adj(m, hdrlen);
4868ffd7c74aSJoe Talbott tx->security = 0;
4869ffd7c74aSJoe Talbott tx->flags = htole32(flags);
4870ffd7c74aSJoe Talbott
487105538f72SMatthew Dillon #if defined(__DragonFly__)
4872101554a6SMatthew Dillon error = bus_dmamap_load_mbuf_segment(ring->data_dmat, data->map,
487305538f72SMatthew Dillon m, segs, IWN_MAX_SCATTER - 1,
4874da10ea93SMatthew Dillon &nsegs, BUS_DMA_NOWAIT);
487505538f72SMatthew Dillon #else
487605538f72SMatthew Dillon error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m, segs,
487705538f72SMatthew Dillon &nsegs, BUS_DMA_NOWAIT);
487805538f72SMatthew Dillon #endif
4879da10ea93SMatthew Dillon if (error != 0) {
4880da10ea93SMatthew Dillon if (error != EFBIG) {
4881da10ea93SMatthew Dillon device_printf(sc->sc_dev,
4882da10ea93SMatthew Dillon "%s: can't map mbuf (error %d)\n", __func__, error);
4883da10ea93SMatthew Dillon return error;
4884da10ea93SMatthew Dillon }
4885da10ea93SMatthew Dillon /* Too many DMA segments, linearize mbuf. */
488605538f72SMatthew Dillon #if defined(__DragonFly__)
4887b5523eacSSascha Wildner m1 = m_defrag(m, M_NOWAIT);
488805538f72SMatthew Dillon #else
4889977fc0dbSMatthew Dillon m1 = m_collapse(m, M_NOWAIT, IWN_MAX_SCATTER - 1);
489005538f72SMatthew Dillon #endif
4891da10ea93SMatthew Dillon if (m1 == NULL) {
4892ffd7c74aSJoe Talbott device_printf(sc->sc_dev,
4893ffd7c74aSJoe Talbott "%s: could not defrag mbuf\n", __func__);
4894ffd7c74aSJoe Talbott return ENOBUFS;
4895ffd7c74aSJoe Talbott }
4896da10ea93SMatthew Dillon m = m1;
4897da10ea93SMatthew Dillon
489805538f72SMatthew Dillon #if defined(__DragonFly__)
4899101554a6SMatthew Dillon error = bus_dmamap_load_mbuf_segment(ring->data_dmat,
490005538f72SMatthew Dillon data->map, m,
490105538f72SMatthew Dillon segs, IWN_MAX_SCATTER - 1,
4902101554a6SMatthew Dillon &nsegs, BUS_DMA_NOWAIT);
490305538f72SMatthew Dillon #else
490405538f72SMatthew Dillon error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m,
490505538f72SMatthew Dillon segs, &nsegs, BUS_DMA_NOWAIT);
490605538f72SMatthew Dillon #endif
4907ffd7c74aSJoe Talbott if (error != 0) {
4908ffd7c74aSJoe Talbott device_printf(sc->sc_dev,
4909da10ea93SMatthew Dillon "%s: can't map mbuf (error %d)\n", __func__, error);
4910ffd7c74aSJoe Talbott return error;
4911ffd7c74aSJoe Talbott }
4912ffd7c74aSJoe Talbott }
4913ffd7c74aSJoe Talbott
4914ffd7c74aSJoe Talbott data->m = m;
4915ffd7c74aSJoe Talbott data->ni = ni;
4916ffd7c74aSJoe Talbott
4917ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_XMIT, "%s: qid %d idx %d len %d nsegs %d\n",
4918ffd7c74aSJoe Talbott __func__, ring->qid, ring->cur, m->m_pkthdr.len, nsegs);
4919ffd7c74aSJoe Talbott
4920ffd7c74aSJoe Talbott /* Fill TX descriptor. */
4921da10ea93SMatthew Dillon desc->nsegs = 1;
4922da10ea93SMatthew Dillon if (m->m_len != 0)
4923da10ea93SMatthew Dillon desc->nsegs += nsegs;
4924ffd7c74aSJoe Talbott /* First DMA segment is used by the TX command. */
4925ffd7c74aSJoe Talbott desc->segs[0].addr = htole32(IWN_LOADDR(data->cmd_paddr));
4926ffd7c74aSJoe Talbott desc->segs[0].len = htole16(IWN_HIADDR(data->cmd_paddr) |
4927ffd7c74aSJoe Talbott (4 + sizeof (*tx) + hdrlen + pad) << 4);
4928ffd7c74aSJoe Talbott /* Other DMA segments are for data payload. */
4929da10ea93SMatthew Dillon seg = &segs[0];
4930ffd7c74aSJoe Talbott for (i = 1; i <= nsegs; i++) {
4931da10ea93SMatthew Dillon desc->segs[i].addr = htole32(IWN_LOADDR(seg->ds_addr));
4932da10ea93SMatthew Dillon desc->segs[i].len = htole16(IWN_HIADDR(seg->ds_addr) |
4933da10ea93SMatthew Dillon seg->ds_len << 4);
4934da10ea93SMatthew Dillon seg++;
4935ffd7c74aSJoe Talbott }
4936ffd7c74aSJoe Talbott
4937ffd7c74aSJoe Talbott bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE);
4938ffd7c74aSJoe Talbott bus_dmamap_sync(ring->data_dmat, ring->cmd_dma.map,
4939ffd7c74aSJoe Talbott BUS_DMASYNC_PREWRITE);
4940ffd7c74aSJoe Talbott bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
4941ffd7c74aSJoe Talbott BUS_DMASYNC_PREWRITE);
4942ffd7c74aSJoe Talbott
4943ffd7c74aSJoe Talbott /* Update TX scheduler. */
4944da10ea93SMatthew Dillon if (ring->qid >= sc->firstaggqueue)
4945da10ea93SMatthew Dillon ops->update_sched(sc, ring->qid, ring->cur, tx->id, totlen);
4946ffd7c74aSJoe Talbott
4947ffd7c74aSJoe Talbott /* Kick TX ring. */
4948ffd7c74aSJoe Talbott ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT;
4949ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur);
4950ffd7c74aSJoe Talbott
4951ffd7c74aSJoe Talbott /* Mark TX ring as full if we reach a certain threshold. */
4952ffd7c74aSJoe Talbott if (++ring->queued > IWN_TX_RING_HIMARK)
4953ffd7c74aSJoe Talbott sc->qfullmsk |= 1 << ring->qid;
4954ffd7c74aSJoe Talbott
4955da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
4956da10ea93SMatthew Dillon
4957ffd7c74aSJoe Talbott return 0;
4958ffd7c74aSJoe Talbott }
4959ffd7c74aSJoe Talbott
4960977fc0dbSMatthew Dillon static void
iwn_xmit_task(void * arg0,int pending)4961977fc0dbSMatthew Dillon iwn_xmit_task(void *arg0, int pending)
4962977fc0dbSMatthew Dillon {
4963977fc0dbSMatthew Dillon struct iwn_softc *sc = arg0;
4964977fc0dbSMatthew Dillon struct ieee80211_node *ni;
4965977fc0dbSMatthew Dillon struct mbuf *m;
4966977fc0dbSMatthew Dillon int error;
4967977fc0dbSMatthew Dillon struct ieee80211_bpf_params p;
4968977fc0dbSMatthew Dillon int have_p;
4969977fc0dbSMatthew Dillon
4970977fc0dbSMatthew Dillon DPRINTF(sc, IWN_DEBUG_XMIT, "%s: called\n", __func__);
4971977fc0dbSMatthew Dillon
4972977fc0dbSMatthew Dillon IWN_LOCK(sc);
4973977fc0dbSMatthew Dillon /*
4974977fc0dbSMatthew Dillon * Dequeue frames, attempt to transmit,
4975977fc0dbSMatthew Dillon * then disable beaconwait when we're done.
4976977fc0dbSMatthew Dillon */
4977977fc0dbSMatthew Dillon while ((m = mbufq_dequeue(&sc->sc_xmit_queue)) != NULL) {
4978977fc0dbSMatthew Dillon have_p = 0;
4979977fc0dbSMatthew Dillon ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
4980977fc0dbSMatthew Dillon
4981977fc0dbSMatthew Dillon /* Get xmit params if appropriate */
4982977fc0dbSMatthew Dillon if (ieee80211_get_xmit_params(m, &p) == 0)
4983977fc0dbSMatthew Dillon have_p = 1;
4984977fc0dbSMatthew Dillon
4985977fc0dbSMatthew Dillon DPRINTF(sc, IWN_DEBUG_XMIT, "%s: m=%p, have_p=%d\n",
4986977fc0dbSMatthew Dillon __func__, m, have_p);
4987977fc0dbSMatthew Dillon
4988977fc0dbSMatthew Dillon /* If we have xmit params, use them */
4989977fc0dbSMatthew Dillon if (have_p)
4990977fc0dbSMatthew Dillon error = iwn_tx_data_raw(sc, m, ni, &p);
4991977fc0dbSMatthew Dillon else
4992977fc0dbSMatthew Dillon error = iwn_tx_data(sc, m, ni);
4993977fc0dbSMatthew Dillon
4994977fc0dbSMatthew Dillon if (error != 0) {
4995977fc0dbSMatthew Dillon if_inc_counter(ni->ni_vap->iv_ifp,
4996977fc0dbSMatthew Dillon IFCOUNTER_OERRORS, 1);
4997977fc0dbSMatthew Dillon ieee80211_free_node(ni);
4998977fc0dbSMatthew Dillon m_freem(m);
4999977fc0dbSMatthew Dillon }
5000977fc0dbSMatthew Dillon }
5001977fc0dbSMatthew Dillon
5002977fc0dbSMatthew Dillon sc->sc_beacon_wait = 0;
5003977fc0dbSMatthew Dillon IWN_UNLOCK(sc);
5004977fc0dbSMatthew Dillon }
5005977fc0dbSMatthew Dillon
5006977fc0dbSMatthew Dillon /*
5007977fc0dbSMatthew Dillon * raw frame xmit - free node/reference if failed.
5008977fc0dbSMatthew Dillon */
5009ffd7c74aSJoe Talbott static int
iwn_raw_xmit(struct ieee80211_node * ni,struct mbuf * m,const struct ieee80211_bpf_params * params)5010ffd7c74aSJoe Talbott iwn_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
5011ffd7c74aSJoe Talbott const struct ieee80211_bpf_params *params)
5012ffd7c74aSJoe Talbott {
5013ffd7c74aSJoe Talbott struct ieee80211com *ic = ni->ni_ic;
50144f1aaf2fSImre Vadász struct iwn_softc *sc = ic->ic_softc;
5015ffd7c74aSJoe Talbott int error = 0;
5016ffd7c74aSJoe Talbott
501705538f72SMatthew Dillon DPRINTF(sc, IWN_DEBUG_XMIT | IWN_DEBUG_TRACE, "->%s begin\n", __func__);
5018da10ea93SMatthew Dillon
5019977fc0dbSMatthew Dillon IWN_LOCK(sc);
5020977fc0dbSMatthew Dillon if ((sc->sc_flags & IWN_FLAG_RUNNING) == 0) {
5021ffd7c74aSJoe Talbott m_freem(m);
5022977fc0dbSMatthew Dillon IWN_UNLOCK(sc);
5023977fc0dbSMatthew Dillon return (ENETDOWN);
5024ffd7c74aSJoe Talbott }
5025ffd7c74aSJoe Talbott
5026977fc0dbSMatthew Dillon /* queue frame if we have to */
5027977fc0dbSMatthew Dillon if (sc->sc_beacon_wait) {
5028977fc0dbSMatthew Dillon if (iwn_xmit_queue_enqueue(sc, m) != 0) {
5029977fc0dbSMatthew Dillon m_freem(m);
5030977fc0dbSMatthew Dillon IWN_UNLOCK(sc);
5031977fc0dbSMatthew Dillon return (ENOBUFS);
5032977fc0dbSMatthew Dillon }
5033977fc0dbSMatthew Dillon /* Queued, so just return OK */
5034977fc0dbSMatthew Dillon IWN_UNLOCK(sc);
5035977fc0dbSMatthew Dillon return (0);
5036977fc0dbSMatthew Dillon }
5037977fc0dbSMatthew Dillon
5038ffd7c74aSJoe Talbott if (params == NULL) {
5039ffd7c74aSJoe Talbott /*
5040ffd7c74aSJoe Talbott * Legacy path; interpret frame contents to decide
5041ffd7c74aSJoe Talbott * precisely how to send the frame.
5042ffd7c74aSJoe Talbott */
5043da10ea93SMatthew Dillon error = iwn_tx_data(sc, m, ni);
5044ffd7c74aSJoe Talbott } else {
5045ffd7c74aSJoe Talbott /*
5046ffd7c74aSJoe Talbott * Caller supplied explicit parameters to use in
5047ffd7c74aSJoe Talbott * sending the frame.
5048ffd7c74aSJoe Talbott */
5049da10ea93SMatthew Dillon error = iwn_tx_data_raw(sc, m, ni, params);
5050ffd7c74aSJoe Talbott }
5051977fc0dbSMatthew Dillon if (error == 0)
5052da10ea93SMatthew Dillon sc->sc_tx_timer = 5;
5053977fc0dbSMatthew Dillon else
5054977fc0dbSMatthew Dillon m_freem(m);
5055da10ea93SMatthew Dillon
505605538f72SMatthew Dillon IWN_UNLOCK(sc);
505705538f72SMatthew Dillon
505805538f72SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE | IWN_DEBUG_XMIT, "->%s: end\n",__func__);
5059da10ea93SMatthew Dillon
5060977fc0dbSMatthew Dillon return (error);
5061ffd7c74aSJoe Talbott }
5062ffd7c74aSJoe Talbott
5063977fc0dbSMatthew Dillon /*
5064977fc0dbSMatthew Dillon * transmit - don't free mbuf if failed; don't free node ref if failed.
5065977fc0dbSMatthew Dillon */
5066977fc0dbSMatthew Dillon static int
iwn_transmit(struct ieee80211com * ic,struct mbuf * m)5067977fc0dbSMatthew Dillon iwn_transmit(struct ieee80211com *ic, struct mbuf *m)
5068ffd7c74aSJoe Talbott {
5069977fc0dbSMatthew Dillon struct iwn_softc *sc = ic->ic_softc;
5070977fc0dbSMatthew Dillon struct ieee80211_node *ni;
5071977fc0dbSMatthew Dillon int error;
5072977fc0dbSMatthew Dillon
5073977fc0dbSMatthew Dillon ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
507405538f72SMatthew Dillon
507505538f72SMatthew Dillon IWN_LOCK(sc);
5076977fc0dbSMatthew Dillon if ((sc->sc_flags & IWN_FLAG_RUNNING) == 0 || sc->sc_beacon_wait) {
507705538f72SMatthew Dillon IWN_UNLOCK(sc);
5078977fc0dbSMatthew Dillon return (ENXIO);
5079ffd7c74aSJoe Talbott }
5080ffd7c74aSJoe Talbott
5081977fc0dbSMatthew Dillon if (sc->qfullmsk) {
5082977fc0dbSMatthew Dillon IWN_UNLOCK(sc);
5083977fc0dbSMatthew Dillon return (ENOBUFS);
5084ffd7c74aSJoe Talbott }
5085977fc0dbSMatthew Dillon
5086977fc0dbSMatthew Dillon error = iwn_tx_data(sc, m, ni);
5087977fc0dbSMatthew Dillon if (!error)
5088ffd7c74aSJoe Talbott sc->sc_tx_timer = 5;
5089977fc0dbSMatthew Dillon IWN_UNLOCK(sc);
5090977fc0dbSMatthew Dillon return (error);
5091ffd7c74aSJoe Talbott }
509205538f72SMatthew Dillon
509305538f72SMatthew Dillon static void
iwn_watchdog(void * arg)509405538f72SMatthew Dillon iwn_watchdog(void *arg)
5095ffd7c74aSJoe Talbott {
5096da10ea93SMatthew Dillon struct iwn_softc *sc = arg;
5097977fc0dbSMatthew Dillon struct ieee80211com *ic = &sc->sc_ic;
5098ffd7c74aSJoe Talbott
509905538f72SMatthew Dillon IWN_LOCK_ASSERT(sc);
5100da10ea93SMatthew Dillon
5101977fc0dbSMatthew Dillon KASSERT(sc->sc_flags & IWN_FLAG_RUNNING, ("not running"));
5102da10ea93SMatthew Dillon
5103da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
5104da10ea93SMatthew Dillon
5105da10ea93SMatthew Dillon if (sc->sc_tx_timer > 0) {
5106da10ea93SMatthew Dillon if (--sc->sc_tx_timer == 0) {
5107977fc0dbSMatthew Dillon ic_printf(ic, "device timeout\n");
5108977fc0dbSMatthew Dillon ieee80211_restart_all(ic);
5109da10ea93SMatthew Dillon return;
5110ffd7c74aSJoe Talbott }
5111ffd7c74aSJoe Talbott }
511205538f72SMatthew Dillon callout_reset(&sc->watchdog_to, hz, iwn_watchdog, sc);
5113da10ea93SMatthew Dillon }
5114ffd7c74aSJoe Talbott
511505538f72SMatthew Dillon #if defined(__DragonFly__)
5116ffd7c74aSJoe Talbott static int
iwn_cdev_open(struct dev_open_args * ap)5117977fc0dbSMatthew Dillon iwn_cdev_open(struct dev_open_args *ap)
511805538f72SMatthew Dillon #else
511905538f72SMatthew Dillon static int
5120977fc0dbSMatthew Dillon iwn_cdev_open(struct cdev *dev, int flags, int type, struct thread *td)
512105538f72SMatthew Dillon #endif
5122ffd7c74aSJoe Talbott {
5123977fc0dbSMatthew Dillon
5124977fc0dbSMatthew Dillon return (0);
5125977fc0dbSMatthew Dillon }
5126977fc0dbSMatthew Dillon
5127977fc0dbSMatthew Dillon #if defined(__DragonFly__)
5128977fc0dbSMatthew Dillon static int
iwn_cdev_close(struct dev_close_args * ap)5129977fc0dbSMatthew Dillon iwn_cdev_close(struct dev_close_args *ap)
5130977fc0dbSMatthew Dillon #else
5131977fc0dbSMatthew Dillon static int
5132977fc0dbSMatthew Dillon iwn_cdev_close(struct cdev *dev, int flags, int type, struct thread *td)
5133977fc0dbSMatthew Dillon #endif
5134977fc0dbSMatthew Dillon {
5135977fc0dbSMatthew Dillon
5136977fc0dbSMatthew Dillon return (0);
5137977fc0dbSMatthew Dillon }
5138977fc0dbSMatthew Dillon
5139977fc0dbSMatthew Dillon #if defined(__DragonFly__)
5140977fc0dbSMatthew Dillon static int
iwn_cdev_ioctl(struct dev_ioctl_args * ap)5141977fc0dbSMatthew Dillon iwn_cdev_ioctl(struct dev_ioctl_args *ap)
5142977fc0dbSMatthew Dillon {
5143977fc0dbSMatthew Dillon cdev_t dev = ap->a_head.a_dev;
5144977fc0dbSMatthew Dillon unsigned long cmd = ap->a_cmd;
5145977fc0dbSMatthew Dillon caddr_t data = ap->a_data;
5146977fc0dbSMatthew Dillon struct thread *td = curthread;
5147977fc0dbSMatthew Dillon #else
5148977fc0dbSMatthew Dillon static int
5149977fc0dbSMatthew Dillon iwn_cdev_ioctl(struct cdev *dev, unsigned long cmd, caddr_t data, int fflag,
5150977fc0dbSMatthew Dillon struct thread *td)
5151977fc0dbSMatthew Dillon {
5152977fc0dbSMatthew Dillon #endif
5153977fc0dbSMatthew Dillon int rc;
5154977fc0dbSMatthew Dillon struct iwn_softc *sc = dev->si_drv1;
5155977fc0dbSMatthew Dillon struct iwn_ioctl_data *d;
5156977fc0dbSMatthew Dillon
51572b3f93eaSMatthew Dillon rc = caps_priv_check((td ? td->td_ucred : NULL),
51582b3f93eaSMatthew Dillon SYSCAP_NODRIVER | __SYSCAP_NULLCRED);
5159977fc0dbSMatthew Dillon if (rc != 0)
5160977fc0dbSMatthew Dillon return (0);
5161ffd7c74aSJoe Talbott
5162ffd7c74aSJoe Talbott switch (cmd) {
5163fd49669cSMichael Neumann case SIOCGIWNSTATS:
5164977fc0dbSMatthew Dillon d = (struct iwn_ioctl_data *) data;
5165fd49669cSMichael Neumann IWN_LOCK(sc);
5166fd49669cSMichael Neumann /* XXX validate permissions/memory/etc? */
5167977fc0dbSMatthew Dillon rc = copyout(&sc->last_stat, d->dst_addr, sizeof(struct iwn_stats));
5168fd49669cSMichael Neumann IWN_UNLOCK(sc);
5169fd49669cSMichael Neumann break;
5170fd49669cSMichael Neumann case SIOCZIWNSTATS:
5171fd49669cSMichael Neumann IWN_LOCK(sc);
5172fd49669cSMichael Neumann memset(&sc->last_stat, 0, sizeof(struct iwn_stats));
5173fd49669cSMichael Neumann IWN_UNLOCK(sc);
5174fd49669cSMichael Neumann break;
5175ffd7c74aSJoe Talbott default:
5176977fc0dbSMatthew Dillon rc = EINVAL;
5177ffd7c74aSJoe Talbott break;
5178ffd7c74aSJoe Talbott }
5179977fc0dbSMatthew Dillon return (rc);
5180977fc0dbSMatthew Dillon }
5181977fc0dbSMatthew Dillon
5182977fc0dbSMatthew Dillon static int
5183977fc0dbSMatthew Dillon iwn_ioctl(struct ieee80211com *ic, u_long cmd, void *data)
5184977fc0dbSMatthew Dillon {
5185977fc0dbSMatthew Dillon
5186977fc0dbSMatthew Dillon return (ENOTTY);
5187977fc0dbSMatthew Dillon }
5188977fc0dbSMatthew Dillon
5189977fc0dbSMatthew Dillon static void
5190977fc0dbSMatthew Dillon iwn_parent(struct ieee80211com *ic)
5191977fc0dbSMatthew Dillon {
5192977fc0dbSMatthew Dillon struct iwn_softc *sc = ic->ic_softc;
5193977fc0dbSMatthew Dillon struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
5194977fc0dbSMatthew Dillon int startall = 0, stop = 0;
5195977fc0dbSMatthew Dillon
5196977fc0dbSMatthew Dillon IWN_LOCK(sc);
5197977fc0dbSMatthew Dillon if (ic->ic_nrunning > 0) {
5198977fc0dbSMatthew Dillon if (!(sc->sc_flags & IWN_FLAG_RUNNING)) {
5199977fc0dbSMatthew Dillon iwn_init_locked(sc);
5200977fc0dbSMatthew Dillon if (IWN_READ(sc, IWN_GP_CNTRL) & IWN_GP_CNTRL_RFKILL)
5201977fc0dbSMatthew Dillon startall = 1;
5202977fc0dbSMatthew Dillon else
5203977fc0dbSMatthew Dillon stop = 1;
5204977fc0dbSMatthew Dillon }
5205977fc0dbSMatthew Dillon } else if (sc->sc_flags & IWN_FLAG_RUNNING)
5206977fc0dbSMatthew Dillon iwn_stop_locked(sc);
5207977fc0dbSMatthew Dillon IWN_UNLOCK(sc);
5208977fc0dbSMatthew Dillon if (startall)
5209977fc0dbSMatthew Dillon ieee80211_start_all(ic);
5210977fc0dbSMatthew Dillon else if (vap != NULL && stop)
5211977fc0dbSMatthew Dillon ieee80211_stop(vap);
5212ffd7c74aSJoe Talbott }
5213ffd7c74aSJoe Talbott
5214ffd7c74aSJoe Talbott /*
5215ffd7c74aSJoe Talbott * Send a command to the firmware.
5216ffd7c74aSJoe Talbott */
5217ffd7c74aSJoe Talbott static int
5218ffd7c74aSJoe Talbott iwn_cmd(struct iwn_softc *sc, int code, const void *buf, int size, int async)
5219ffd7c74aSJoe Talbott {
5220da10ea93SMatthew Dillon struct iwn_tx_ring *ring;
5221ffd7c74aSJoe Talbott struct iwn_tx_desc *desc;
5222ffd7c74aSJoe Talbott struct iwn_tx_data *data;
5223ffd7c74aSJoe Talbott struct iwn_tx_cmd *cmd;
5224ffd7c74aSJoe Talbott struct mbuf *m;
5225ffd7c74aSJoe Talbott bus_addr_t paddr;
5226ffd7c74aSJoe Talbott int totlen, error;
5227da10ea93SMatthew Dillon int cmd_queue_num;
5228ffd7c74aSJoe Talbott
5229da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
5230da10ea93SMatthew Dillon
523105538f72SMatthew Dillon if (async == 0)
523205538f72SMatthew Dillon IWN_LOCK_ASSERT(sc);
523305538f72SMatthew Dillon
5234da10ea93SMatthew Dillon if (sc->sc_flags & IWN_FLAG_PAN_SUPPORT)
5235da10ea93SMatthew Dillon cmd_queue_num = IWN_PAN_CMD_QUEUE;
5236da10ea93SMatthew Dillon else
5237da10ea93SMatthew Dillon cmd_queue_num = IWN_CMD_QUEUE_NUM;
5238da10ea93SMatthew Dillon
5239da10ea93SMatthew Dillon ring = &sc->txq[cmd_queue_num];
5240ffd7c74aSJoe Talbott desc = &ring->desc[ring->cur];
5241ffd7c74aSJoe Talbott data = &ring->data[ring->cur];
5242ffd7c74aSJoe Talbott totlen = 4 + size;
5243ffd7c74aSJoe Talbott
5244ffd7c74aSJoe Talbott if (size > sizeof cmd->data) {
5245ffd7c74aSJoe Talbott /* Command is too large to fit in a descriptor. */
524605538f72SMatthew Dillon if (totlen > MCLBYTES)
5247ffd7c74aSJoe Talbott return EINVAL;
5248b5523eacSSascha Wildner m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE);
5249ffd7c74aSJoe Talbott if (m == NULL)
5250ffd7c74aSJoe Talbott return ENOMEM;
5251ffd7c74aSJoe Talbott cmd = mtod(m, struct iwn_tx_cmd *);
5252ffd7c74aSJoe Talbott error = bus_dmamap_load(ring->data_dmat, data->map, cmd,
5253ffd7c74aSJoe Talbott totlen, iwn_dma_map_addr, &paddr, BUS_DMA_NOWAIT);
5254ffd7c74aSJoe Talbott if (error != 0) {
5255ffd7c74aSJoe Talbott m_freem(m);
5256ffd7c74aSJoe Talbott return error;
5257ffd7c74aSJoe Talbott }
5258ffd7c74aSJoe Talbott data->m = m;
5259ffd7c74aSJoe Talbott } else {
5260ffd7c74aSJoe Talbott cmd = &ring->cmd[ring->cur];
5261ffd7c74aSJoe Talbott paddr = data->cmd_paddr;
5262ffd7c74aSJoe Talbott }
5263ffd7c74aSJoe Talbott
5264ffd7c74aSJoe Talbott cmd->code = code;
5265ffd7c74aSJoe Talbott cmd->flags = 0;
5266ffd7c74aSJoe Talbott cmd->qid = ring->qid;
5267ffd7c74aSJoe Talbott cmd->idx = ring->cur;
5268ffd7c74aSJoe Talbott memcpy(cmd->data, buf, size);
5269ffd7c74aSJoe Talbott
5270ffd7c74aSJoe Talbott desc->nsegs = 1;
5271ffd7c74aSJoe Talbott desc->segs[0].addr = htole32(IWN_LOADDR(paddr));
5272ffd7c74aSJoe Talbott desc->segs[0].len = htole16(IWN_HIADDR(paddr) | totlen << 4);
5273ffd7c74aSJoe Talbott
5274ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_CMD, "%s: %s (0x%x) flags %d qid %d idx %d\n",
5275ffd7c74aSJoe Talbott __func__, iwn_intr_str(cmd->code), cmd->code,
5276ffd7c74aSJoe Talbott cmd->flags, cmd->qid, cmd->idx);
5277ffd7c74aSJoe Talbott
5278ffd7c74aSJoe Talbott if (size > sizeof cmd->data) {
5279ffd7c74aSJoe Talbott bus_dmamap_sync(ring->data_dmat, data->map,
5280ffd7c74aSJoe Talbott BUS_DMASYNC_PREWRITE);
5281ffd7c74aSJoe Talbott } else {
5282ffd7c74aSJoe Talbott bus_dmamap_sync(ring->data_dmat, ring->cmd_dma.map,
5283ffd7c74aSJoe Talbott BUS_DMASYNC_PREWRITE);
5284ffd7c74aSJoe Talbott }
5285ffd7c74aSJoe Talbott bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
5286ffd7c74aSJoe Talbott BUS_DMASYNC_PREWRITE);
5287ffd7c74aSJoe Talbott
5288ffd7c74aSJoe Talbott /* Kick command ring. */
5289ffd7c74aSJoe Talbott ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT;
5290ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur);
5291ffd7c74aSJoe Talbott
5292da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
5293da10ea93SMatthew Dillon
5294977fc0dbSMatthew Dillon #if defined(__DragonFly__)
5295977fc0dbSMatthew Dillon return async ? 0 : lksleep(desc, &sc->sc_lk, PCATCH, "iwncmd", hz);
5296977fc0dbSMatthew Dillon #else
5297977fc0dbSMatthew Dillon return async ? 0 : msleep(desc, &sc->sc_mtx, PCATCH, "iwncmd", hz);
5298977fc0dbSMatthew Dillon #endif
5299ffd7c74aSJoe Talbott }
5300ffd7c74aSJoe Talbott
5301ffd7c74aSJoe Talbott static int
5302ffd7c74aSJoe Talbott iwn4965_add_node(struct iwn_softc *sc, struct iwn_node_info *node, int async)
5303ffd7c74aSJoe Talbott {
5304ffd7c74aSJoe Talbott struct iwn4965_node_info hnode;
5305ffd7c74aSJoe Talbott caddr_t src, dst;
5306ffd7c74aSJoe Talbott
5307da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
5308da10ea93SMatthew Dillon
5309ffd7c74aSJoe Talbott /*
5310ffd7c74aSJoe Talbott * We use the node structure for 5000 Series internally (it is
5311ffd7c74aSJoe Talbott * a superset of the one for 4965AGN). We thus copy the common
5312ffd7c74aSJoe Talbott * fields before sending the command.
5313ffd7c74aSJoe Talbott */
5314ffd7c74aSJoe Talbott src = (caddr_t)node;
5315ffd7c74aSJoe Talbott dst = (caddr_t)&hnode;
5316ffd7c74aSJoe Talbott memcpy(dst, src, 48);
5317ffd7c74aSJoe Talbott /* Skip TSC, RX MIC and TX MIC fields from ``src''. */
5318ffd7c74aSJoe Talbott memcpy(dst + 48, src + 72, 20);
5319ffd7c74aSJoe Talbott return iwn_cmd(sc, IWN_CMD_ADD_NODE, &hnode, sizeof hnode, async);
5320ffd7c74aSJoe Talbott }
5321ffd7c74aSJoe Talbott
5322ffd7c74aSJoe Talbott static int
5323ffd7c74aSJoe Talbott iwn5000_add_node(struct iwn_softc *sc, struct iwn_node_info *node, int async)
5324ffd7c74aSJoe Talbott {
5325da10ea93SMatthew Dillon
5326da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
5327da10ea93SMatthew Dillon
5328ffd7c74aSJoe Talbott /* Direct mapping. */
5329ffd7c74aSJoe Talbott return iwn_cmd(sc, IWN_CMD_ADD_NODE, node, sizeof (*node), async);
5330ffd7c74aSJoe Talbott }
5331ffd7c74aSJoe Talbott
5332ffd7c74aSJoe Talbott static int
5333da10ea93SMatthew Dillon iwn_set_link_quality(struct iwn_softc *sc, struct ieee80211_node *ni)
5334ffd7c74aSJoe Talbott {
5335da10ea93SMatthew Dillon struct iwn_node *wn = (void *)ni;
5336da10ea93SMatthew Dillon struct ieee80211_rateset *rs;
5337ffd7c74aSJoe Talbott struct iwn_cmd_link_quality linkq;
5338da10ea93SMatthew Dillon int i, rate, txrate;
5339da10ea93SMatthew Dillon int is_11n;
5340da10ea93SMatthew Dillon
5341da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
5342ffd7c74aSJoe Talbott
5343ffd7c74aSJoe Talbott memset(&linkq, 0, sizeof linkq);
5344da10ea93SMatthew Dillon linkq.id = wn->id;
534505538f72SMatthew Dillon linkq.antmsk_1stream = iwn_get_1stream_tx_antmask(sc);
534605538f72SMatthew Dillon linkq.antmsk_2stream = iwn_get_2stream_tx_antmask(sc);
5347da10ea93SMatthew Dillon
5348da10ea93SMatthew Dillon linkq.ampdu_max = 32; /* XXX negotiated? */
5349ffd7c74aSJoe Talbott linkq.ampdu_threshold = 3;
5350ffd7c74aSJoe Talbott linkq.ampdu_limit = htole16(4000); /* 4ms */
5351ffd7c74aSJoe Talbott
5352da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_XMIT,
5353da10ea93SMatthew Dillon "%s: 1stream antenna=0x%02x, 2stream antenna=0x%02x, ntxstreams=%d\n",
5354da10ea93SMatthew Dillon __func__,
5355da10ea93SMatthew Dillon linkq.antmsk_1stream,
5356da10ea93SMatthew Dillon linkq.antmsk_2stream,
5357da10ea93SMatthew Dillon sc->ntxchains);
5358ffd7c74aSJoe Talbott
5359da10ea93SMatthew Dillon /*
5360da10ea93SMatthew Dillon * Are we using 11n rates? Ensure the channel is
5361da10ea93SMatthew Dillon * 11n _and_ we have some 11n rates, or don't
5362da10ea93SMatthew Dillon * try.
5363da10ea93SMatthew Dillon */
5364da10ea93SMatthew Dillon if (IEEE80211_IS_CHAN_HT(ni->ni_chan) && ni->ni_htrates.rs_nrates > 0) {
5365da10ea93SMatthew Dillon rs = (struct ieee80211_rateset *) &ni->ni_htrates;
5366da10ea93SMatthew Dillon is_11n = 1;
5367da10ea93SMatthew Dillon } else {
5368da10ea93SMatthew Dillon rs = &ni->ni_rates;
5369da10ea93SMatthew Dillon is_11n = 0;
5370da10ea93SMatthew Dillon }
5371da10ea93SMatthew Dillon
5372da10ea93SMatthew Dillon /* Start at highest available bit-rate. */
5373da10ea93SMatthew Dillon /*
5374da10ea93SMatthew Dillon * XXX this is all very dirty!
5375da10ea93SMatthew Dillon */
5376da10ea93SMatthew Dillon if (is_11n)
5377da10ea93SMatthew Dillon txrate = ni->ni_htrates.rs_nrates - 1;
5378ffd7c74aSJoe Talbott else
5379da10ea93SMatthew Dillon txrate = rs->rs_nrates - 1;
5380ffd7c74aSJoe Talbott for (i = 0; i < IWN_MAX_TX_RETRIES; i++) {
5381da10ea93SMatthew Dillon uint32_t plcp;
5382da10ea93SMatthew Dillon
538305538f72SMatthew Dillon /*
538405538f72SMatthew Dillon * XXX TODO: ensure the last two slots are the two lowest
538505538f72SMatthew Dillon * rate entries, just for now.
538605538f72SMatthew Dillon */
538705538f72SMatthew Dillon if (i == 14 || i == 15)
538805538f72SMatthew Dillon txrate = 0;
538905538f72SMatthew Dillon
5390da10ea93SMatthew Dillon if (is_11n)
5391da10ea93SMatthew Dillon rate = IEEE80211_RATE_MCS | rs->rs_rates[txrate];
5392da10ea93SMatthew Dillon else
5393977fc0dbSMatthew Dillon rate = IEEE80211_RV(rs->rs_rates[txrate]);
5394da10ea93SMatthew Dillon
5395da10ea93SMatthew Dillon /* Do rate -> PLCP config mapping */
5396da10ea93SMatthew Dillon plcp = iwn_rate_to_plcp(sc, ni, rate);
5397da10ea93SMatthew Dillon linkq.retry[i] = plcp;
539805538f72SMatthew Dillon DPRINTF(sc, IWN_DEBUG_XMIT,
539905538f72SMatthew Dillon "%s: i=%d, txrate=%d, rate=0x%02x, plcp=0x%08x\n",
540005538f72SMatthew Dillon __func__,
540105538f72SMatthew Dillon i,
540205538f72SMatthew Dillon txrate,
540305538f72SMatthew Dillon rate,
540405538f72SMatthew Dillon le32toh(plcp));
5405da10ea93SMatthew Dillon
5406da10ea93SMatthew Dillon /*
5407da10ea93SMatthew Dillon * The mimo field is an index into the table which
5408da10ea93SMatthew Dillon * indicates the first index where it and subsequent entries
5409da10ea93SMatthew Dillon * will not be using MIMO.
5410da10ea93SMatthew Dillon *
5411da10ea93SMatthew Dillon * Since we're filling linkq from 0..15 and we're filling
5412977fc0dbSMatthew Dillon * from the highest MCS rates to the lowest rates, if we
5413da10ea93SMatthew Dillon * _are_ doing a dual-stream rate, set mimo to idx+1 (ie,
5414da10ea93SMatthew Dillon * the next entry.) That way if the next entry is a non-MIMO
5415da10ea93SMatthew Dillon * entry, we're already pointing at it.
5416da10ea93SMatthew Dillon */
5417da10ea93SMatthew Dillon if ((le32toh(plcp) & IWN_RFLAG_MCS) &&
5418977fc0dbSMatthew Dillon IEEE80211_RV(le32toh(plcp)) > 7)
5419da10ea93SMatthew Dillon linkq.mimo = i + 1;
5420da10ea93SMatthew Dillon
5421da10ea93SMatthew Dillon /* Next retry at immediate lower bit-rate. */
5422da10ea93SMatthew Dillon if (txrate > 0)
5423da10ea93SMatthew Dillon txrate--;
5424ffd7c74aSJoe Talbott }
542505538f72SMatthew Dillon /*
542605538f72SMatthew Dillon * If we reached the end of the list and indeed we hit
542705538f72SMatthew Dillon * all MIMO rates (eg 5300 doing MCS23-15) then yes,
542805538f72SMatthew Dillon * set mimo to 15. Setting it to 16 panics the firmware.
542905538f72SMatthew Dillon */
543005538f72SMatthew Dillon if (linkq.mimo > 15)
543105538f72SMatthew Dillon linkq.mimo = 15;
543205538f72SMatthew Dillon
543305538f72SMatthew Dillon DPRINTF(sc, IWN_DEBUG_XMIT, "%s: mimo = %d\n", __func__, linkq.mimo);
5434da10ea93SMatthew Dillon
5435da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
5436da10ea93SMatthew Dillon
5437da10ea93SMatthew Dillon return iwn_cmd(sc, IWN_CMD_LINK_QUALITY, &linkq, sizeof linkq, 1);
5438ffd7c74aSJoe Talbott }
5439ffd7c74aSJoe Talbott
5440ffd7c74aSJoe Talbott /*
5441ffd7c74aSJoe Talbott * Broadcast node is used to send group-addressed and management frames.
5442ffd7c74aSJoe Talbott */
5443ffd7c74aSJoe Talbott static int
5444ffd7c74aSJoe Talbott iwn_add_broadcast_node(struct iwn_softc *sc, int async)
5445ffd7c74aSJoe Talbott {
5446da10ea93SMatthew Dillon struct iwn_ops *ops = &sc->ops;
5447977fc0dbSMatthew Dillon struct ieee80211com *ic = &sc->sc_ic;
5448ffd7c74aSJoe Talbott struct iwn_node_info node;
5449da10ea93SMatthew Dillon struct iwn_cmd_link_quality linkq;
5450da10ea93SMatthew Dillon uint8_t txant;
5451da10ea93SMatthew Dillon int i, error;
5452da10ea93SMatthew Dillon
5453da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
5454da10ea93SMatthew Dillon
5455da10ea93SMatthew Dillon sc->rxon = &sc->rx_on[IWN_RXON_BSS_CTX];
5456ffd7c74aSJoe Talbott
5457ffd7c74aSJoe Talbott memset(&node, 0, sizeof node);
5458977fc0dbSMatthew Dillon IEEE80211_ADDR_COPY(node.macaddr, ieee80211broadcastaddr);
5459da10ea93SMatthew Dillon node.id = sc->broadcast_id;
5460ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_RESET, "%s: adding broadcast node\n", __func__);
5461da10ea93SMatthew Dillon if ((error = ops->add_node(sc, &node, async)) != 0)
5462ffd7c74aSJoe Talbott return error;
5463ffd7c74aSJoe Talbott
5464da10ea93SMatthew Dillon /* Use the first valid TX antenna. */
5465da10ea93SMatthew Dillon txant = IWN_LSB(sc->txchainmask);
5466da10ea93SMatthew Dillon
5467da10ea93SMatthew Dillon memset(&linkq, 0, sizeof linkq);
5468da10ea93SMatthew Dillon linkq.id = sc->broadcast_id;
546905538f72SMatthew Dillon linkq.antmsk_1stream = iwn_get_1stream_tx_antmask(sc);
547005538f72SMatthew Dillon linkq.antmsk_2stream = iwn_get_2stream_tx_antmask(sc);
5471da10ea93SMatthew Dillon linkq.ampdu_max = 64;
5472da10ea93SMatthew Dillon linkq.ampdu_threshold = 3;
5473da10ea93SMatthew Dillon linkq.ampdu_limit = htole16(4000); /* 4ms */
5474da10ea93SMatthew Dillon
5475da10ea93SMatthew Dillon /* Use lowest mandatory bit-rate. */
547605538f72SMatthew Dillon /* XXX rate table lookup? */
5477da10ea93SMatthew Dillon if (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan))
5478da10ea93SMatthew Dillon linkq.retry[0] = htole32(0xd);
5479da10ea93SMatthew Dillon else
5480da10ea93SMatthew Dillon linkq.retry[0] = htole32(10 | IWN_RFLAG_CCK);
5481da10ea93SMatthew Dillon linkq.retry[0] |= htole32(IWN_RFLAG_ANT(txant));
5482da10ea93SMatthew Dillon /* Use same bit-rate for all TX retries. */
5483da10ea93SMatthew Dillon for (i = 1; i < IWN_MAX_TX_RETRIES; i++) {
5484da10ea93SMatthew Dillon linkq.retry[i] = linkq.retry[0];
5485da10ea93SMatthew Dillon }
5486da10ea93SMatthew Dillon
5487da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
5488da10ea93SMatthew Dillon
5489da10ea93SMatthew Dillon return iwn_cmd(sc, IWN_CMD_LINK_QUALITY, &linkq, sizeof linkq, async);
5490ffd7c74aSJoe Talbott }
5491ffd7c74aSJoe Talbott
5492ffd7c74aSJoe Talbott static int
5493da10ea93SMatthew Dillon iwn_updateedca(struct ieee80211com *ic)
5494ffd7c74aSJoe Talbott {
5495ffd7c74aSJoe Talbott #define IWN_EXP2(x) ((1 << (x)) - 1) /* CWmin = 2^ECWmin - 1 */
54964f1aaf2fSImre Vadász struct iwn_softc *sc = ic->ic_softc;
5497ffd7c74aSJoe Talbott struct iwn_edca_params cmd;
5498da10ea93SMatthew Dillon int aci;
5499da10ea93SMatthew Dillon
5500da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
5501ffd7c74aSJoe Talbott
5502ffd7c74aSJoe Talbott memset(&cmd, 0, sizeof cmd);
5503ffd7c74aSJoe Talbott cmd.flags = htole32(IWN_EDCA_UPDATE);
5504977fc0dbSMatthew Dillon
5505977fc0dbSMatthew Dillon IEEE80211_LOCK(ic);
5506da10ea93SMatthew Dillon for (aci = 0; aci < WME_NUM_AC; aci++) {
5507da10ea93SMatthew Dillon const struct wmeParams *ac =
5508da10ea93SMatthew Dillon &ic->ic_wme.wme_chanParams.cap_wmeParams[aci];
5509da10ea93SMatthew Dillon cmd.ac[aci].aifsn = ac->wmep_aifsn;
5510da10ea93SMatthew Dillon cmd.ac[aci].cwmin = htole16(IWN_EXP2(ac->wmep_logcwmin));
5511da10ea93SMatthew Dillon cmd.ac[aci].cwmax = htole16(IWN_EXP2(ac->wmep_logcwmax));
5512da10ea93SMatthew Dillon cmd.ac[aci].txoplimit =
5513da10ea93SMatthew Dillon htole16(IEEE80211_TXOP_TO_US(ac->wmep_txopLimit));
5514ffd7c74aSJoe Talbott }
551505538f72SMatthew Dillon IEEE80211_UNLOCK(ic);
5516977fc0dbSMatthew Dillon
551705538f72SMatthew Dillon IWN_LOCK(sc);
5518da10ea93SMatthew Dillon (void)iwn_cmd(sc, IWN_CMD_EDCA_PARAMS, &cmd, sizeof cmd, 1);
551905538f72SMatthew Dillon IWN_UNLOCK(sc);
5520da10ea93SMatthew Dillon
5521da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
5522da10ea93SMatthew Dillon
5523ffd7c74aSJoe Talbott return 0;
5524ffd7c74aSJoe Talbott #undef IWN_EXP2
5525ffd7c74aSJoe Talbott }
5526ffd7c74aSJoe Talbott
5527ffd7c74aSJoe Talbott static void
55284f898719SImre Vadász iwn_update_mcast(struct ieee80211com *ic)
5529ffd7c74aSJoe Talbott {
5530ffd7c74aSJoe Talbott /* Ignore */
5531ffd7c74aSJoe Talbott }
5532ffd7c74aSJoe Talbott
5533ffd7c74aSJoe Talbott static void
5534ffd7c74aSJoe Talbott iwn_set_led(struct iwn_softc *sc, uint8_t which, uint8_t off, uint8_t on)
5535ffd7c74aSJoe Talbott {
5536ffd7c74aSJoe Talbott struct iwn_cmd_led led;
5537ffd7c74aSJoe Talbott
5538da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
5539da10ea93SMatthew Dillon
55404b420e05SMatthew Dillon #if 0
55414b420e05SMatthew Dillon /* XXX don't set LEDs during scan? */
55424b420e05SMatthew Dillon if (sc->sc_is_scanning)
55434b420e05SMatthew Dillon return;
55444b420e05SMatthew Dillon #endif
55454b420e05SMatthew Dillon
5546ffd7c74aSJoe Talbott /* Clear microcode LED ownership. */
5547ffd7c74aSJoe Talbott IWN_CLRBITS(sc, IWN_LED, IWN_LED_BSM_CTRL);
5548ffd7c74aSJoe Talbott
5549ffd7c74aSJoe Talbott led.which = which;
5550ffd7c74aSJoe Talbott led.unit = htole32(10000); /* on/off in unit of 100ms */
5551ffd7c74aSJoe Talbott led.off = off;
5552ffd7c74aSJoe Talbott led.on = on;
5553ffd7c74aSJoe Talbott (void)iwn_cmd(sc, IWN_CMD_SET_LED, &led, sizeof led, 1);
5554ffd7c74aSJoe Talbott }
5555ffd7c74aSJoe Talbott
5556ffd7c74aSJoe Talbott /*
5557ffd7c74aSJoe Talbott * Set the critical temperature at which the firmware will stop the radio
5558ffd7c74aSJoe Talbott * and notify us.
5559ffd7c74aSJoe Talbott */
5560ffd7c74aSJoe Talbott static int
5561ffd7c74aSJoe Talbott iwn_set_critical_temp(struct iwn_softc *sc)
5562ffd7c74aSJoe Talbott {
5563ffd7c74aSJoe Talbott struct iwn_critical_temp crit;
5564ffd7c74aSJoe Talbott int32_t temp;
5565ffd7c74aSJoe Talbott
5566da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
5567da10ea93SMatthew Dillon
5568ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_CTEMP_STOP_RF);
5569ffd7c74aSJoe Talbott
5570ffd7c74aSJoe Talbott if (sc->hw_type == IWN_HW_REV_TYPE_5150)
5571ffd7c74aSJoe Talbott temp = (IWN_CTOK(110) - sc->temp_off) * -5;
5572ffd7c74aSJoe Talbott else if (sc->hw_type == IWN_HW_REV_TYPE_4965)
5573ffd7c74aSJoe Talbott temp = IWN_CTOK(110);
5574ffd7c74aSJoe Talbott else
5575ffd7c74aSJoe Talbott temp = 110;
5576ffd7c74aSJoe Talbott memset(&crit, 0, sizeof crit);
5577ffd7c74aSJoe Talbott crit.tempR = htole32(temp);
5578da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_RESET, "setting critical temp to %d\n", temp);
5579ffd7c74aSJoe Talbott return iwn_cmd(sc, IWN_CMD_SET_CRITICAL_TEMP, &crit, sizeof crit, 0);
5580ffd7c74aSJoe Talbott }
5581ffd7c74aSJoe Talbott
5582ffd7c74aSJoe Talbott static int
5583ffd7c74aSJoe Talbott iwn_set_timing(struct iwn_softc *sc, struct ieee80211_node *ni)
5584ffd7c74aSJoe Talbott {
5585ffd7c74aSJoe Talbott struct iwn_cmd_timing cmd;
5586ffd7c74aSJoe Talbott uint64_t val, mod;
5587ffd7c74aSJoe Talbott
5588da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
5589da10ea93SMatthew Dillon
5590ffd7c74aSJoe Talbott memset(&cmd, 0, sizeof cmd);
5591ffd7c74aSJoe Talbott memcpy(&cmd.tstamp, ni->ni_tstamp.data, sizeof (uint64_t));
5592ffd7c74aSJoe Talbott cmd.bintval = htole16(ni->ni_intval);
5593ffd7c74aSJoe Talbott cmd.lintval = htole16(10);
5594ffd7c74aSJoe Talbott
5595ffd7c74aSJoe Talbott /* Compute remaining time until next beacon. */
5596da10ea93SMatthew Dillon val = (uint64_t)ni->ni_intval * IEEE80211_DUR_TU;
5597ffd7c74aSJoe Talbott mod = le64toh(cmd.tstamp) % val;
5598ffd7c74aSJoe Talbott cmd.binitval = htole32((uint32_t)(val - mod));
5599ffd7c74aSJoe Talbott
5600ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_RESET, "timing bintval=%u tstamp=%ju, init=%u\n",
5601ffd7c74aSJoe Talbott ni->ni_intval, le64toh(cmd.tstamp), (uint32_t)(val - mod));
5602ffd7c74aSJoe Talbott
5603ffd7c74aSJoe Talbott return iwn_cmd(sc, IWN_CMD_TIMING, &cmd, sizeof cmd, 1);
5604ffd7c74aSJoe Talbott }
5605ffd7c74aSJoe Talbott
5606ffd7c74aSJoe Talbott static void
5607ffd7c74aSJoe Talbott iwn4965_power_calibration(struct iwn_softc *sc, int temp)
5608ffd7c74aSJoe Talbott {
5609977fc0dbSMatthew Dillon struct ieee80211com *ic = &sc->sc_ic;
5610ffd7c74aSJoe Talbott
5611da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
5612da10ea93SMatthew Dillon
5613da10ea93SMatthew Dillon /* Adjust TX power if need be (delta >= 3 degC). */
5614ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: temperature %d->%d\n",
5615ffd7c74aSJoe Talbott __func__, sc->temp, temp);
5616ffd7c74aSJoe Talbott if (abs(temp - sc->temp) >= 3) {
5617ffd7c74aSJoe Talbott /* Record temperature of last calibration. */
5618ffd7c74aSJoe Talbott sc->temp = temp;
5619ffd7c74aSJoe Talbott (void)iwn4965_set_txpower(sc, ic->ic_bsschan, 1);
5620ffd7c74aSJoe Talbott }
5621ffd7c74aSJoe Talbott }
5622ffd7c74aSJoe Talbott
5623ffd7c74aSJoe Talbott /*
5624ffd7c74aSJoe Talbott * Set TX power for current channel (each rate has its own power settings).
5625ffd7c74aSJoe Talbott * This function takes into account the regulatory information from EEPROM,
5626ffd7c74aSJoe Talbott * the current temperature and the current voltage.
5627ffd7c74aSJoe Talbott */
5628ffd7c74aSJoe Talbott static int
5629ffd7c74aSJoe Talbott iwn4965_set_txpower(struct iwn_softc *sc, struct ieee80211_channel *ch,
5630ffd7c74aSJoe Talbott int async)
5631ffd7c74aSJoe Talbott {
5632ffd7c74aSJoe Talbott /* Fixed-point arithmetic division using a n-bit fractional part. */
5633ffd7c74aSJoe Talbott #define fdivround(a, b, n) \
5634ffd7c74aSJoe Talbott ((((1 << n) * (a)) / (b) + (1 << n) / 2) / (1 << n))
5635ffd7c74aSJoe Talbott /* Linear interpolation. */
5636ffd7c74aSJoe Talbott #define interpolate(x, x1, y1, x2, y2, n) \
5637ffd7c74aSJoe Talbott ((y1) + fdivround(((int)(x) - (x1)) * ((y2) - (y1)), (x2) - (x1), n))
5638ffd7c74aSJoe Talbott
5639ffd7c74aSJoe Talbott static const int tdiv[IWN_NATTEN_GROUPS] = { 9, 8, 8, 8, 6 };
5640ffd7c74aSJoe Talbott struct iwn_ucode_info *uc = &sc->ucode_info;
5641ffd7c74aSJoe Talbott struct iwn4965_cmd_txpower cmd;
5642ffd7c74aSJoe Talbott struct iwn4965_eeprom_chan_samples *chans;
5643da10ea93SMatthew Dillon const uint8_t *rf_gain, *dsp_gain;
5644ffd7c74aSJoe Talbott int32_t vdiff, tdiff;
5645ffd7c74aSJoe Talbott int i, c, grp, maxpwr;
5646ffd7c74aSJoe Talbott uint8_t chan;
5647ffd7c74aSJoe Talbott
5648da10ea93SMatthew Dillon sc->rxon = &sc->rx_on[IWN_RXON_BSS_CTX];
5649da10ea93SMatthew Dillon /* Retrieve current channel from last RXON. */
5650da10ea93SMatthew Dillon chan = sc->rxon->chan;
5651ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_RESET, "setting TX power for channel %d\n",
5652ffd7c74aSJoe Talbott chan);
5653ffd7c74aSJoe Talbott
5654ffd7c74aSJoe Talbott memset(&cmd, 0, sizeof cmd);
5655ffd7c74aSJoe Talbott cmd.band = IEEE80211_IS_CHAN_5GHZ(ch) ? 0 : 1;
5656ffd7c74aSJoe Talbott cmd.chan = chan;
5657ffd7c74aSJoe Talbott
5658ffd7c74aSJoe Talbott if (IEEE80211_IS_CHAN_5GHZ(ch)) {
5659ffd7c74aSJoe Talbott maxpwr = sc->maxpwr5GHz;
5660ffd7c74aSJoe Talbott rf_gain = iwn4965_rf_gain_5ghz;
5661ffd7c74aSJoe Talbott dsp_gain = iwn4965_dsp_gain_5ghz;
5662ffd7c74aSJoe Talbott } else {
5663ffd7c74aSJoe Talbott maxpwr = sc->maxpwr2GHz;
5664ffd7c74aSJoe Talbott rf_gain = iwn4965_rf_gain_2ghz;
5665ffd7c74aSJoe Talbott dsp_gain = iwn4965_dsp_gain_2ghz;
5666ffd7c74aSJoe Talbott }
5667ffd7c74aSJoe Talbott
5668ffd7c74aSJoe Talbott /* Compute voltage compensation. */
5669ffd7c74aSJoe Talbott vdiff = ((int32_t)le32toh(uc->volt) - sc->eeprom_voltage) / 7;
5670ffd7c74aSJoe Talbott if (vdiff > 0)
5671ffd7c74aSJoe Talbott vdiff *= 2;
5672ffd7c74aSJoe Talbott if (abs(vdiff) > 2)
5673ffd7c74aSJoe Talbott vdiff = 0;
5674ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
5675ffd7c74aSJoe Talbott "%s: voltage compensation=%d (UCODE=%d, EEPROM=%d)\n",
5676ffd7c74aSJoe Talbott __func__, vdiff, le32toh(uc->volt), sc->eeprom_voltage);
5677ffd7c74aSJoe Talbott
5678ffd7c74aSJoe Talbott /* Get channel attenuation group. */
5679ffd7c74aSJoe Talbott if (chan <= 20) /* 1-20 */
5680ffd7c74aSJoe Talbott grp = 4;
5681ffd7c74aSJoe Talbott else if (chan <= 43) /* 34-43 */
5682ffd7c74aSJoe Talbott grp = 0;
5683ffd7c74aSJoe Talbott else if (chan <= 70) /* 44-70 */
5684ffd7c74aSJoe Talbott grp = 1;
5685ffd7c74aSJoe Talbott else if (chan <= 124) /* 71-124 */
5686ffd7c74aSJoe Talbott grp = 2;
5687ffd7c74aSJoe Talbott else /* 125-200 */
5688ffd7c74aSJoe Talbott grp = 3;
5689ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
5690ffd7c74aSJoe Talbott "%s: chan %d, attenuation group=%d\n", __func__, chan, grp);
5691ffd7c74aSJoe Talbott
5692ffd7c74aSJoe Talbott /* Get channel sub-band. */
5693ffd7c74aSJoe Talbott for (i = 0; i < IWN_NBANDS; i++)
5694ffd7c74aSJoe Talbott if (sc->bands[i].lo != 0 &&
5695ffd7c74aSJoe Talbott sc->bands[i].lo <= chan && chan <= sc->bands[i].hi)
5696ffd7c74aSJoe Talbott break;
5697ffd7c74aSJoe Talbott if (i == IWN_NBANDS) /* Can't happen in real-life. */
5698ffd7c74aSJoe Talbott return EINVAL;
5699ffd7c74aSJoe Talbott chans = sc->bands[i].chans;
5700ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
5701ffd7c74aSJoe Talbott "%s: chan %d sub-band=%d\n", __func__, chan, i);
5702ffd7c74aSJoe Talbott
5703ffd7c74aSJoe Talbott for (c = 0; c < 2; c++) {
5704ffd7c74aSJoe Talbott uint8_t power, gain, temp;
5705ffd7c74aSJoe Talbott int maxchpwr, pwr, ridx, idx;
5706ffd7c74aSJoe Talbott
5707ffd7c74aSJoe Talbott power = interpolate(chan,
5708ffd7c74aSJoe Talbott chans[0].num, chans[0].samples[c][1].power,
5709ffd7c74aSJoe Talbott chans[1].num, chans[1].samples[c][1].power, 1);
5710ffd7c74aSJoe Talbott gain = interpolate(chan,
5711ffd7c74aSJoe Talbott chans[0].num, chans[0].samples[c][1].gain,
5712ffd7c74aSJoe Talbott chans[1].num, chans[1].samples[c][1].gain, 1);
5713ffd7c74aSJoe Talbott temp = interpolate(chan,
5714ffd7c74aSJoe Talbott chans[0].num, chans[0].samples[c][1].temp,
5715ffd7c74aSJoe Talbott chans[1].num, chans[1].samples[c][1].temp, 1);
5716ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
5717ffd7c74aSJoe Talbott "%s: Tx chain %d: power=%d gain=%d temp=%d\n",
5718ffd7c74aSJoe Talbott __func__, c, power, gain, temp);
5719ffd7c74aSJoe Talbott
5720ffd7c74aSJoe Talbott /* Compute temperature compensation. */
5721ffd7c74aSJoe Talbott tdiff = ((sc->temp - temp) * 2) / tdiv[grp];
5722ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
5723ffd7c74aSJoe Talbott "%s: temperature compensation=%d (current=%d, EEPROM=%d)\n",
5724ffd7c74aSJoe Talbott __func__, tdiff, sc->temp, temp);
5725ffd7c74aSJoe Talbott
5726ffd7c74aSJoe Talbott for (ridx = 0; ridx <= IWN_RIDX_MAX; ridx++) {
5727ffd7c74aSJoe Talbott /* Convert dBm to half-dBm. */
5728ffd7c74aSJoe Talbott maxchpwr = sc->maxpwr[chan] * 2;
5729ffd7c74aSJoe Talbott if ((ridx / 8) & 1)
5730ffd7c74aSJoe Talbott maxchpwr -= 6; /* MIMO 2T: -3dB */
5731ffd7c74aSJoe Talbott
5732ffd7c74aSJoe Talbott pwr = maxpwr;
5733ffd7c74aSJoe Talbott
5734ffd7c74aSJoe Talbott /* Adjust TX power based on rate. */
5735ffd7c74aSJoe Talbott if ((ridx % 8) == 5)
5736ffd7c74aSJoe Talbott pwr -= 15; /* OFDM48: -7.5dB */
5737ffd7c74aSJoe Talbott else if ((ridx % 8) == 6)
5738ffd7c74aSJoe Talbott pwr -= 17; /* OFDM54: -8.5dB */
5739ffd7c74aSJoe Talbott else if ((ridx % 8) == 7)
5740ffd7c74aSJoe Talbott pwr -= 20; /* OFDM60: -10dB */
5741ffd7c74aSJoe Talbott else
5742ffd7c74aSJoe Talbott pwr -= 10; /* Others: -5dB */
5743ffd7c74aSJoe Talbott
5744ffd7c74aSJoe Talbott /* Do not exceed channel max TX power. */
5745ffd7c74aSJoe Talbott if (pwr > maxchpwr)
5746ffd7c74aSJoe Talbott pwr = maxchpwr;
5747ffd7c74aSJoe Talbott
5748ffd7c74aSJoe Talbott idx = gain - (pwr - power) - tdiff - vdiff;
5749ffd7c74aSJoe Talbott if ((ridx / 8) & 1) /* MIMO */
5750ffd7c74aSJoe Talbott idx += (int32_t)le32toh(uc->atten[grp][c]);
5751ffd7c74aSJoe Talbott
5752ffd7c74aSJoe Talbott if (cmd.band == 0)
5753ffd7c74aSJoe Talbott idx += 9; /* 5GHz */
5754ffd7c74aSJoe Talbott if (ridx == IWN_RIDX_MAX)
5755ffd7c74aSJoe Talbott idx += 5; /* CCK */
5756ffd7c74aSJoe Talbott
5757ffd7c74aSJoe Talbott /* Make sure idx stays in a valid range. */
5758ffd7c74aSJoe Talbott if (idx < 0)
5759ffd7c74aSJoe Talbott idx = 0;
5760ffd7c74aSJoe Talbott else if (idx > IWN4965_MAX_PWR_INDEX)
5761ffd7c74aSJoe Talbott idx = IWN4965_MAX_PWR_INDEX;
5762ffd7c74aSJoe Talbott
5763ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
5764ffd7c74aSJoe Talbott "%s: Tx chain %d, rate idx %d: power=%d\n",
5765ffd7c74aSJoe Talbott __func__, c, ridx, idx);
5766ffd7c74aSJoe Talbott cmd.power[ridx].rf_gain[c] = rf_gain[idx];
5767ffd7c74aSJoe Talbott cmd.power[ridx].dsp_gain[c] = dsp_gain[idx];
5768ffd7c74aSJoe Talbott }
5769ffd7c74aSJoe Talbott }
5770ffd7c74aSJoe Talbott
5771ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
5772ffd7c74aSJoe Talbott "%s: set tx power for chan %d\n", __func__, chan);
5773ffd7c74aSJoe Talbott return iwn_cmd(sc, IWN_CMD_TXPOWER, &cmd, sizeof cmd, async);
5774ffd7c74aSJoe Talbott
5775ffd7c74aSJoe Talbott #undef interpolate
5776ffd7c74aSJoe Talbott #undef fdivround
5777ffd7c74aSJoe Talbott }
5778ffd7c74aSJoe Talbott
5779ffd7c74aSJoe Talbott static int
5780ffd7c74aSJoe Talbott iwn5000_set_txpower(struct iwn_softc *sc, struct ieee80211_channel *ch,
5781ffd7c74aSJoe Talbott int async)
5782ffd7c74aSJoe Talbott {
5783ffd7c74aSJoe Talbott struct iwn5000_cmd_txpower cmd;
578405538f72SMatthew Dillon int cmdid;
5785ffd7c74aSJoe Talbott
5786da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
5787da10ea93SMatthew Dillon
5788ffd7c74aSJoe Talbott /*
5789ffd7c74aSJoe Talbott * TX power calibration is handled automatically by the firmware
5790ffd7c74aSJoe Talbott * for 5000 Series.
5791ffd7c74aSJoe Talbott */
5792ffd7c74aSJoe Talbott memset(&cmd, 0, sizeof cmd);
5793ffd7c74aSJoe Talbott cmd.global_limit = 2 * IWN5000_TXPOWER_MAX_DBM; /* 16 dBm */
5794ffd7c74aSJoe Talbott cmd.flags = IWN5000_TXPOWER_NO_CLOSED;
5795ffd7c74aSJoe Talbott cmd.srv_limit = IWN5000_TXPOWER_AUTO;
579605538f72SMatthew Dillon DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_XMIT,
579705538f72SMatthew Dillon "%s: setting TX power; rev=%d\n",
579805538f72SMatthew Dillon __func__,
579905538f72SMatthew Dillon IWN_UCODE_API(sc->ucode_rev));
580005538f72SMatthew Dillon if (IWN_UCODE_API(sc->ucode_rev) == 1)
580105538f72SMatthew Dillon cmdid = IWN_CMD_TXPOWER_DBM_V1;
580205538f72SMatthew Dillon else
580305538f72SMatthew Dillon cmdid = IWN_CMD_TXPOWER_DBM;
580405538f72SMatthew Dillon return iwn_cmd(sc, cmdid, &cmd, sizeof cmd, async);
5805ffd7c74aSJoe Talbott }
5806ffd7c74aSJoe Talbott
5807ffd7c74aSJoe Talbott /*
5808ffd7c74aSJoe Talbott * Retrieve the maximum RSSI (in dBm) among receivers.
5809ffd7c74aSJoe Talbott */
5810ffd7c74aSJoe Talbott static int
5811ffd7c74aSJoe Talbott iwn4965_get_rssi(struct iwn_softc *sc, struct iwn_rx_stat *stat)
5812ffd7c74aSJoe Talbott {
5813ffd7c74aSJoe Talbott struct iwn4965_rx_phystat *phy = (void *)stat->phybuf;
5814ffd7c74aSJoe Talbott uint8_t mask, agc;
5815ffd7c74aSJoe Talbott int rssi;
5816ffd7c74aSJoe Talbott
5817da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
5818da10ea93SMatthew Dillon
5819ffd7c74aSJoe Talbott mask = (le16toh(phy->antenna) >> 4) & IWN_ANT_ABC;
5820ffd7c74aSJoe Talbott agc = (le16toh(phy->agc) >> 7) & 0x7f;
5821ffd7c74aSJoe Talbott
5822ffd7c74aSJoe Talbott rssi = 0;
5823da10ea93SMatthew Dillon if (mask & IWN_ANT_A)
5824da10ea93SMatthew Dillon rssi = MAX(rssi, phy->rssi[0]);
5825da10ea93SMatthew Dillon if (mask & IWN_ANT_B)
5826da10ea93SMatthew Dillon rssi = MAX(rssi, phy->rssi[2]);
5827da10ea93SMatthew Dillon if (mask & IWN_ANT_C)
5828da10ea93SMatthew Dillon rssi = MAX(rssi, phy->rssi[4]);
5829ffd7c74aSJoe Talbott
5830da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_RECV,
5831da10ea93SMatthew Dillon "%s: agc %d mask 0x%x rssi %d %d %d result %d\n", __func__, agc,
5832da10ea93SMatthew Dillon mask, phy->rssi[0], phy->rssi[2], phy->rssi[4],
5833ffd7c74aSJoe Talbott rssi - agc - IWN_RSSI_TO_DBM);
5834ffd7c74aSJoe Talbott return rssi - agc - IWN_RSSI_TO_DBM;
5835ffd7c74aSJoe Talbott }
5836ffd7c74aSJoe Talbott
5837ffd7c74aSJoe Talbott static int
5838ffd7c74aSJoe Talbott iwn5000_get_rssi(struct iwn_softc *sc, struct iwn_rx_stat *stat)
5839ffd7c74aSJoe Talbott {
5840ffd7c74aSJoe Talbott struct iwn5000_rx_phystat *phy = (void *)stat->phybuf;
5841ffd7c74aSJoe Talbott uint8_t agc;
5842da10ea93SMatthew Dillon int rssi;
5843da10ea93SMatthew Dillon
5844da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
5845ffd7c74aSJoe Talbott
5846ffd7c74aSJoe Talbott agc = (le32toh(phy->agc) >> 9) & 0x7f;
5847ffd7c74aSJoe Talbott
5848ffd7c74aSJoe Talbott rssi = MAX(le16toh(phy->rssi[0]) & 0xff,
5849ffd7c74aSJoe Talbott le16toh(phy->rssi[1]) & 0xff);
5850ffd7c74aSJoe Talbott rssi = MAX(le16toh(phy->rssi[2]) & 0xff, rssi);
5851ffd7c74aSJoe Talbott
5852da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_RECV,
5853da10ea93SMatthew Dillon "%s: agc %d rssi %d %d %d result %d\n", __func__, agc,
5854ffd7c74aSJoe Talbott phy->rssi[0], phy->rssi[1], phy->rssi[2],
5855ffd7c74aSJoe Talbott rssi - agc - IWN_RSSI_TO_DBM);
5856ffd7c74aSJoe Talbott return rssi - agc - IWN_RSSI_TO_DBM;
5857ffd7c74aSJoe Talbott }
5858ffd7c74aSJoe Talbott
5859ffd7c74aSJoe Talbott /*
5860ffd7c74aSJoe Talbott * Retrieve the average noise (in dBm) among receivers.
5861ffd7c74aSJoe Talbott */
5862ffd7c74aSJoe Talbott static int
5863ffd7c74aSJoe Talbott iwn_get_noise(const struct iwn_rx_general_stats *stats)
5864ffd7c74aSJoe Talbott {
5865ffd7c74aSJoe Talbott int i, total, nbant, noise;
5866ffd7c74aSJoe Talbott
5867ffd7c74aSJoe Talbott total = nbant = 0;
5868ffd7c74aSJoe Talbott for (i = 0; i < 3; i++) {
5869ffd7c74aSJoe Talbott if ((noise = le32toh(stats->noise[i]) & 0xff) == 0)
5870ffd7c74aSJoe Talbott continue;
5871ffd7c74aSJoe Talbott total += noise;
5872ffd7c74aSJoe Talbott nbant++;
5873ffd7c74aSJoe Talbott }
5874ffd7c74aSJoe Talbott /* There should be at least one antenna but check anyway. */
5875ffd7c74aSJoe Talbott return (nbant == 0) ? -127 : (total / nbant) - 107;
5876ffd7c74aSJoe Talbott }
5877ffd7c74aSJoe Talbott
5878ffd7c74aSJoe Talbott /*
5879ffd7c74aSJoe Talbott * Compute temperature (in degC) from last received statistics.
5880ffd7c74aSJoe Talbott */
5881ffd7c74aSJoe Talbott static int
5882ffd7c74aSJoe Talbott iwn4965_get_temperature(struct iwn_softc *sc)
5883ffd7c74aSJoe Talbott {
5884ffd7c74aSJoe Talbott struct iwn_ucode_info *uc = &sc->ucode_info;
5885ffd7c74aSJoe Talbott int32_t r1, r2, r3, r4, temp;
5886ffd7c74aSJoe Talbott
5887da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
5888da10ea93SMatthew Dillon
5889ffd7c74aSJoe Talbott r1 = le32toh(uc->temp[0].chan20MHz);
5890ffd7c74aSJoe Talbott r2 = le32toh(uc->temp[1].chan20MHz);
5891ffd7c74aSJoe Talbott r3 = le32toh(uc->temp[2].chan20MHz);
5892ffd7c74aSJoe Talbott r4 = le32toh(sc->rawtemp);
5893ffd7c74aSJoe Talbott
5894da10ea93SMatthew Dillon if (r1 == r3) /* Prevents division by 0 (should not happen). */
5895ffd7c74aSJoe Talbott return 0;
5896ffd7c74aSJoe Talbott
5897ffd7c74aSJoe Talbott /* Sign-extend 23-bit R4 value to 32-bit. */
5898da10ea93SMatthew Dillon r4 = ((r4 & 0xffffff) ^ 0x800000) - 0x800000;
5899ffd7c74aSJoe Talbott /* Compute temperature in Kelvin. */
5900ffd7c74aSJoe Talbott temp = (259 * (r4 - r2)) / (r3 - r1);
5901ffd7c74aSJoe Talbott temp = (temp * 97) / 100 + 8;
5902ffd7c74aSJoe Talbott
5903ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_ANY, "temperature %dK/%dC\n", temp,
5904ffd7c74aSJoe Talbott IWN_KTOC(temp));
5905ffd7c74aSJoe Talbott return IWN_KTOC(temp);
5906ffd7c74aSJoe Talbott }
5907ffd7c74aSJoe Talbott
5908ffd7c74aSJoe Talbott static int
5909ffd7c74aSJoe Talbott iwn5000_get_temperature(struct iwn_softc *sc)
5910ffd7c74aSJoe Talbott {
5911ffd7c74aSJoe Talbott int32_t temp;
5912ffd7c74aSJoe Talbott
5913da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
5914da10ea93SMatthew Dillon
5915ffd7c74aSJoe Talbott /*
5916ffd7c74aSJoe Talbott * Temperature is not used by the driver for 5000 Series because
5917da10ea93SMatthew Dillon * TX power calibration is handled by firmware.
5918ffd7c74aSJoe Talbott */
5919ffd7c74aSJoe Talbott temp = le32toh(sc->rawtemp);
5920ffd7c74aSJoe Talbott if (sc->hw_type == IWN_HW_REV_TYPE_5150) {
5921ffd7c74aSJoe Talbott temp = (temp / -5) + sc->temp_off;
5922ffd7c74aSJoe Talbott temp = IWN_KTOC(temp);
5923ffd7c74aSJoe Talbott }
5924ffd7c74aSJoe Talbott return temp;
5925ffd7c74aSJoe Talbott }
5926ffd7c74aSJoe Talbott
5927ffd7c74aSJoe Talbott /*
5928ffd7c74aSJoe Talbott * Initialize sensitivity calibration state machine.
5929ffd7c74aSJoe Talbott */
5930ffd7c74aSJoe Talbott static int
5931ffd7c74aSJoe Talbott iwn_init_sensitivity(struct iwn_softc *sc)
5932ffd7c74aSJoe Talbott {
5933da10ea93SMatthew Dillon struct iwn_ops *ops = &sc->ops;
5934ffd7c74aSJoe Talbott struct iwn_calib_state *calib = &sc->calib;
5935ffd7c74aSJoe Talbott uint32_t flags;
5936ffd7c74aSJoe Talbott int error;
5937ffd7c74aSJoe Talbott
5938da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
5939da10ea93SMatthew Dillon
5940ffd7c74aSJoe Talbott /* Reset calibration state machine. */
5941ffd7c74aSJoe Talbott memset(calib, 0, sizeof (*calib));
5942ffd7c74aSJoe Talbott calib->state = IWN_CALIB_STATE_INIT;
5943ffd7c74aSJoe Talbott calib->cck_state = IWN_CCK_STATE_HIFA;
5944ffd7c74aSJoe Talbott /* Set initial correlation values. */
5945ffd7c74aSJoe Talbott calib->ofdm_x1 = sc->limits->min_ofdm_x1;
5946ffd7c74aSJoe Talbott calib->ofdm_mrc_x1 = sc->limits->min_ofdm_mrc_x1;
5947ffd7c74aSJoe Talbott calib->ofdm_x4 = sc->limits->min_ofdm_x4;
5948ffd7c74aSJoe Talbott calib->ofdm_mrc_x4 = sc->limits->min_ofdm_mrc_x4;
5949ffd7c74aSJoe Talbott calib->cck_x4 = 125;
5950ffd7c74aSJoe Talbott calib->cck_mrc_x4 = sc->limits->min_cck_mrc_x4;
5951ffd7c74aSJoe Talbott calib->energy_cck = sc->limits->energy_cck;
5952ffd7c74aSJoe Talbott
5953ffd7c74aSJoe Talbott /* Write initial sensitivity. */
5954da10ea93SMatthew Dillon if ((error = iwn_send_sensitivity(sc)) != 0)
5955ffd7c74aSJoe Talbott return error;
5956ffd7c74aSJoe Talbott
5957ffd7c74aSJoe Talbott /* Write initial gains. */
5958da10ea93SMatthew Dillon if ((error = ops->init_gains(sc)) != 0)
5959ffd7c74aSJoe Talbott return error;
5960ffd7c74aSJoe Talbott
5961ffd7c74aSJoe Talbott /* Request statistics at each beacon interval. */
5962ffd7c74aSJoe Talbott flags = 0;
5963da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: sending request for statistics\n",
5964da10ea93SMatthew Dillon __func__);
5965ffd7c74aSJoe Talbott return iwn_cmd(sc, IWN_CMD_GET_STATISTICS, &flags, sizeof flags, 1);
5966ffd7c74aSJoe Talbott }
5967ffd7c74aSJoe Talbott
5968ffd7c74aSJoe Talbott /*
5969ffd7c74aSJoe Talbott * Collect noise and RSSI statistics for the first 20 beacons received
5970ffd7c74aSJoe Talbott * after association and use them to determine connected antennas and
5971ffd7c74aSJoe Talbott * to set differential gains.
5972ffd7c74aSJoe Talbott */
5973ffd7c74aSJoe Talbott static void
5974ffd7c74aSJoe Talbott iwn_collect_noise(struct iwn_softc *sc,
5975ffd7c74aSJoe Talbott const struct iwn_rx_general_stats *stats)
5976ffd7c74aSJoe Talbott {
5977da10ea93SMatthew Dillon struct iwn_ops *ops = &sc->ops;
5978ffd7c74aSJoe Talbott struct iwn_calib_state *calib = &sc->calib;
5979977fc0dbSMatthew Dillon struct ieee80211com *ic = &sc->sc_ic;
5980ffd7c74aSJoe Talbott uint32_t val;
5981ffd7c74aSJoe Talbott int i;
5982ffd7c74aSJoe Talbott
5983da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
5984da10ea93SMatthew Dillon
5985ffd7c74aSJoe Talbott /* Accumulate RSSI and noise for all 3 antennas. */
5986ffd7c74aSJoe Talbott for (i = 0; i < 3; i++) {
5987ffd7c74aSJoe Talbott calib->rssi[i] += le32toh(stats->rssi[i]) & 0xff;
5988ffd7c74aSJoe Talbott calib->noise[i] += le32toh(stats->noise[i]) & 0xff;
5989ffd7c74aSJoe Talbott }
5990ffd7c74aSJoe Talbott /* NB: We update differential gains only once after 20 beacons. */
5991ffd7c74aSJoe Talbott if (++calib->nbeacons < 20)
5992ffd7c74aSJoe Talbott return;
5993ffd7c74aSJoe Talbott
5994ffd7c74aSJoe Talbott /* Determine highest average RSSI. */
5995ffd7c74aSJoe Talbott val = MAX(calib->rssi[0], calib->rssi[1]);
5996ffd7c74aSJoe Talbott val = MAX(calib->rssi[2], val);
5997ffd7c74aSJoe Talbott
5998ffd7c74aSJoe Talbott /* Determine which antennas are connected. */
5999d50981caSJoe Talbott sc->chainmask = sc->rxchainmask;
6000ffd7c74aSJoe Talbott for (i = 0; i < 3; i++)
6001d50981caSJoe Talbott if (val - calib->rssi[i] > 15 * 20)
6002d50981caSJoe Talbott sc->chainmask &= ~(1 << i);
600305538f72SMatthew Dillon DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_XMIT,
6004da10ea93SMatthew Dillon "%s: RX chains mask: theoretical=0x%x, actual=0x%x\n",
6005da10ea93SMatthew Dillon __func__, sc->rxchainmask, sc->chainmask);
6006d50981caSJoe Talbott
6007ffd7c74aSJoe Talbott /* If none of the TX antennas are connected, keep at least one. */
6008ffd7c74aSJoe Talbott if ((sc->chainmask & sc->txchainmask) == 0)
6009ffd7c74aSJoe Talbott sc->chainmask |= IWN_LSB(sc->txchainmask);
6010ffd7c74aSJoe Talbott
6011da10ea93SMatthew Dillon (void)ops->set_gains(sc);
6012ffd7c74aSJoe Talbott calib->state = IWN_CALIB_STATE_RUN;
6013ffd7c74aSJoe Talbott
6014ffd7c74aSJoe Talbott #ifdef notyet
6015ffd7c74aSJoe Talbott /* XXX Disable RX chains with no antennas connected. */
6016da10ea93SMatthew Dillon sc->rxon->rxchain = htole16(IWN_RXCHAIN_SEL(sc->chainmask));
60174b420e05SMatthew Dillon if (sc->sc_is_scanning)
60184b420e05SMatthew Dillon device_printf(sc->sc_dev,
60194b420e05SMatthew Dillon "%s: is_scanning set, before RXON\n",
60204b420e05SMatthew Dillon __func__);
6021da10ea93SMatthew Dillon (void)iwn_cmd(sc, IWN_CMD_RXON, sc->rxon, sc->rxonsz, 1);
6022ffd7c74aSJoe Talbott #endif
6023ffd7c74aSJoe Talbott
6024ffd7c74aSJoe Talbott /* Enable power-saving mode if requested by user. */
6025da10ea93SMatthew Dillon if (ic->ic_flags & IEEE80211_F_PMGTON)
6026ffd7c74aSJoe Talbott (void)iwn_set_pslevel(sc, 0, 3, 1);
6027da10ea93SMatthew Dillon
6028da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
6029da10ea93SMatthew Dillon
6030ffd7c74aSJoe Talbott }
6031ffd7c74aSJoe Talbott
6032ffd7c74aSJoe Talbott static int
6033ffd7c74aSJoe Talbott iwn4965_init_gains(struct iwn_softc *sc)
6034ffd7c74aSJoe Talbott {
6035ffd7c74aSJoe Talbott struct iwn_phy_calib_gain cmd;
6036ffd7c74aSJoe Talbott
6037da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
6038da10ea93SMatthew Dillon
6039ffd7c74aSJoe Talbott memset(&cmd, 0, sizeof cmd);
6040ffd7c74aSJoe Talbott cmd.code = IWN4965_PHY_CALIB_DIFF_GAIN;
6041ffd7c74aSJoe Talbott /* Differential gains initially set to 0 for all 3 antennas. */
6042ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_CALIBRATE,
6043ffd7c74aSJoe Talbott "%s: setting initial differential gains\n", __func__);
6044ffd7c74aSJoe Talbott return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1);
6045ffd7c74aSJoe Talbott }
6046ffd7c74aSJoe Talbott
6047ffd7c74aSJoe Talbott static int
6048ffd7c74aSJoe Talbott iwn5000_init_gains(struct iwn_softc *sc)
6049ffd7c74aSJoe Talbott {
6050ffd7c74aSJoe Talbott struct iwn_phy_calib cmd;
6051ffd7c74aSJoe Talbott
6052da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
6053da10ea93SMatthew Dillon
6054ffd7c74aSJoe Talbott memset(&cmd, 0, sizeof cmd);
6055da10ea93SMatthew Dillon cmd.code = sc->reset_noise_gain;
6056ffd7c74aSJoe Talbott cmd.ngroups = 1;
6057ffd7c74aSJoe Talbott cmd.isvalid = 1;
6058ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_CALIBRATE,
6059ffd7c74aSJoe Talbott "%s: setting initial differential gains\n", __func__);
6060ffd7c74aSJoe Talbott return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1);
6061ffd7c74aSJoe Talbott }
6062ffd7c74aSJoe Talbott
6063ffd7c74aSJoe Talbott static int
6064ffd7c74aSJoe Talbott iwn4965_set_gains(struct iwn_softc *sc)
6065ffd7c74aSJoe Talbott {
6066ffd7c74aSJoe Talbott struct iwn_calib_state *calib = &sc->calib;
6067ffd7c74aSJoe Talbott struct iwn_phy_calib_gain cmd;
6068ffd7c74aSJoe Talbott int i, delta, noise;
6069ffd7c74aSJoe Talbott
6070da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
6071da10ea93SMatthew Dillon
6072ffd7c74aSJoe Talbott /* Get minimal noise among connected antennas. */
6073ffd7c74aSJoe Talbott noise = INT_MAX; /* NB: There's at least one antenna. */
6074ffd7c74aSJoe Talbott for (i = 0; i < 3; i++)
6075ffd7c74aSJoe Talbott if (sc->chainmask & (1 << i))
6076ffd7c74aSJoe Talbott noise = MIN(calib->noise[i], noise);
6077ffd7c74aSJoe Talbott
6078ffd7c74aSJoe Talbott memset(&cmd, 0, sizeof cmd);
6079ffd7c74aSJoe Talbott cmd.code = IWN4965_PHY_CALIB_DIFF_GAIN;
6080ffd7c74aSJoe Talbott /* Set differential gains for connected antennas. */
6081ffd7c74aSJoe Talbott for (i = 0; i < 3; i++) {
6082ffd7c74aSJoe Talbott if (sc->chainmask & (1 << i)) {
6083ffd7c74aSJoe Talbott /* Compute attenuation (in unit of 1.5dB). */
6084ffd7c74aSJoe Talbott delta = (noise - (int32_t)calib->noise[i]) / 30;
6085ffd7c74aSJoe Talbott /* NB: delta <= 0 */
6086ffd7c74aSJoe Talbott /* Limit to [-4.5dB,0]. */
6087ffd7c74aSJoe Talbott cmd.gain[i] = MIN(abs(delta), 3);
6088ffd7c74aSJoe Talbott if (delta < 0)
6089ffd7c74aSJoe Talbott cmd.gain[i] |= 1 << 2; /* sign bit */
6090ffd7c74aSJoe Talbott }
6091ffd7c74aSJoe Talbott }
6092ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_CALIBRATE,
6093ffd7c74aSJoe Talbott "setting differential gains Ant A/B/C: %x/%x/%x (%x)\n",
6094ffd7c74aSJoe Talbott cmd.gain[0], cmd.gain[1], cmd.gain[2], sc->chainmask);
6095ffd7c74aSJoe Talbott return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1);
6096ffd7c74aSJoe Talbott }
6097ffd7c74aSJoe Talbott
6098ffd7c74aSJoe Talbott static int
6099ffd7c74aSJoe Talbott iwn5000_set_gains(struct iwn_softc *sc)
6100ffd7c74aSJoe Talbott {
6101ffd7c74aSJoe Talbott struct iwn_calib_state *calib = &sc->calib;
6102ffd7c74aSJoe Talbott struct iwn_phy_calib_gain cmd;
6103da10ea93SMatthew Dillon int i, ant, div, delta;
6104da10ea93SMatthew Dillon
6105da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
6106ffd7c74aSJoe Talbott
6107ffd7c74aSJoe Talbott /* We collected 20 beacons and !=6050 need a 1.5 factor. */
6108ffd7c74aSJoe Talbott div = (sc->hw_type == IWN_HW_REV_TYPE_6050) ? 20 : 30;
6109ffd7c74aSJoe Talbott
6110ffd7c74aSJoe Talbott memset(&cmd, 0, sizeof cmd);
6111da10ea93SMatthew Dillon cmd.code = sc->noise_gain;
6112ffd7c74aSJoe Talbott cmd.ngroups = 1;
6113ffd7c74aSJoe Talbott cmd.isvalid = 1;
6114ffd7c74aSJoe Talbott /* Get first available RX antenna as referential. */
6115ffd7c74aSJoe Talbott ant = IWN_LSB(sc->rxchainmask);
6116ffd7c74aSJoe Talbott /* Set differential gains for other antennas. */
6117ffd7c74aSJoe Talbott for (i = ant + 1; i < 3; i++) {
6118ffd7c74aSJoe Talbott if (sc->chainmask & (1 << i)) {
6119ffd7c74aSJoe Talbott /* The delta is relative to antenna "ant". */
6120ffd7c74aSJoe Talbott delta = ((int32_t)calib->noise[ant] -
6121ffd7c74aSJoe Talbott (int32_t)calib->noise[i]) / div;
6122ffd7c74aSJoe Talbott /* Limit to [-4.5dB,+4.5dB]. */
6123ffd7c74aSJoe Talbott cmd.gain[i - 1] = MIN(abs(delta), 3);
6124ffd7c74aSJoe Talbott if (delta < 0)
6125ffd7c74aSJoe Talbott cmd.gain[i - 1] |= 1 << 2; /* sign bit */
6126ffd7c74aSJoe Talbott }
6127ffd7c74aSJoe Talbott }
612805538f72SMatthew Dillon DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_XMIT,
6129ffd7c74aSJoe Talbott "setting differential gains Ant B/C: %x/%x (%x)\n",
6130ffd7c74aSJoe Talbott cmd.gain[0], cmd.gain[1], sc->chainmask);
6131ffd7c74aSJoe Talbott return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1);
6132ffd7c74aSJoe Talbott }
6133ffd7c74aSJoe Talbott
6134ffd7c74aSJoe Talbott /*
6135ffd7c74aSJoe Talbott * Tune RF RX sensitivity based on the number of false alarms detected
6136ffd7c74aSJoe Talbott * during the last beacon period.
6137ffd7c74aSJoe Talbott */
6138ffd7c74aSJoe Talbott static void
6139ffd7c74aSJoe Talbott iwn_tune_sensitivity(struct iwn_softc *sc, const struct iwn_rx_stats *stats)
6140ffd7c74aSJoe Talbott {
6141ffd7c74aSJoe Talbott #define inc(val, inc, max) \
6142ffd7c74aSJoe Talbott if ((val) < (max)) { \
6143ffd7c74aSJoe Talbott if ((val) < (max) - (inc)) \
6144ffd7c74aSJoe Talbott (val) += (inc); \
6145ffd7c74aSJoe Talbott else \
6146ffd7c74aSJoe Talbott (val) = (max); \
6147ffd7c74aSJoe Talbott needs_update = 1; \
6148ffd7c74aSJoe Talbott }
6149ffd7c74aSJoe Talbott #define dec(val, dec, min) \
6150ffd7c74aSJoe Talbott if ((val) > (min)) { \
6151ffd7c74aSJoe Talbott if ((val) > (min) + (dec)) \
6152ffd7c74aSJoe Talbott (val) -= (dec); \
6153ffd7c74aSJoe Talbott else \
6154ffd7c74aSJoe Talbott (val) = (min); \
6155ffd7c74aSJoe Talbott needs_update = 1; \
6156ffd7c74aSJoe Talbott }
6157ffd7c74aSJoe Talbott
6158ffd7c74aSJoe Talbott const struct iwn_sensitivity_limits *limits = sc->limits;
6159ffd7c74aSJoe Talbott struct iwn_calib_state *calib = &sc->calib;
6160ffd7c74aSJoe Talbott uint32_t val, rxena, fa;
6161ffd7c74aSJoe Talbott uint32_t energy[3], energy_min;
6162ffd7c74aSJoe Talbott uint8_t noise[3], noise_ref;
6163ffd7c74aSJoe Talbott int i, needs_update = 0;
6164ffd7c74aSJoe Talbott
6165da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
6166da10ea93SMatthew Dillon
6167ffd7c74aSJoe Talbott /* Check that we've been enabled long enough. */
6168da10ea93SMatthew Dillon if ((rxena = le32toh(stats->general.load)) == 0){
6169da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s end not so long\n", __func__);
6170ffd7c74aSJoe Talbott return;
6171da10ea93SMatthew Dillon }
6172ffd7c74aSJoe Talbott
6173ffd7c74aSJoe Talbott /* Compute number of false alarms since last call for OFDM. */
6174ffd7c74aSJoe Talbott fa = le32toh(stats->ofdm.bad_plcp) - calib->bad_plcp_ofdm;
6175ffd7c74aSJoe Talbott fa += le32toh(stats->ofdm.fa) - calib->fa_ofdm;
6176da10ea93SMatthew Dillon fa *= 200 * IEEE80211_DUR_TU; /* 200TU */
6177ffd7c74aSJoe Talbott
6178ffd7c74aSJoe Talbott if (fa > 50 * rxena) {
6179ffd7c74aSJoe Talbott /* High false alarm count, decrease sensitivity. */
6180ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_CALIBRATE,
6181ffd7c74aSJoe Talbott "%s: OFDM high false alarm count: %u\n", __func__, fa);
6182ffd7c74aSJoe Talbott inc(calib->ofdm_x1, 1, limits->max_ofdm_x1);
6183ffd7c74aSJoe Talbott inc(calib->ofdm_mrc_x1, 1, limits->max_ofdm_mrc_x1);
6184ffd7c74aSJoe Talbott inc(calib->ofdm_x4, 1, limits->max_ofdm_x4);
6185ffd7c74aSJoe Talbott inc(calib->ofdm_mrc_x4, 1, limits->max_ofdm_mrc_x4);
6186ffd7c74aSJoe Talbott
6187ffd7c74aSJoe Talbott } else if (fa < 5 * rxena) {
6188ffd7c74aSJoe Talbott /* Low false alarm count, increase sensitivity. */
6189ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_CALIBRATE,
6190ffd7c74aSJoe Talbott "%s: OFDM low false alarm count: %u\n", __func__, fa);
6191ffd7c74aSJoe Talbott dec(calib->ofdm_x1, 1, limits->min_ofdm_x1);
6192ffd7c74aSJoe Talbott dec(calib->ofdm_mrc_x1, 1, limits->min_ofdm_mrc_x1);
6193ffd7c74aSJoe Talbott dec(calib->ofdm_x4, 1, limits->min_ofdm_x4);
6194ffd7c74aSJoe Talbott dec(calib->ofdm_mrc_x4, 1, limits->min_ofdm_mrc_x4);
6195ffd7c74aSJoe Talbott }
6196ffd7c74aSJoe Talbott
6197ffd7c74aSJoe Talbott /* Compute maximum noise among 3 receivers. */
6198ffd7c74aSJoe Talbott for (i = 0; i < 3; i++)
6199ffd7c74aSJoe Talbott noise[i] = (le32toh(stats->general.noise[i]) >> 8) & 0xff;
6200ffd7c74aSJoe Talbott val = MAX(noise[0], noise[1]);
6201ffd7c74aSJoe Talbott val = MAX(noise[2], val);
6202ffd7c74aSJoe Talbott /* Insert it into our samples table. */
6203ffd7c74aSJoe Talbott calib->noise_samples[calib->cur_noise_sample] = val;
6204ffd7c74aSJoe Talbott calib->cur_noise_sample = (calib->cur_noise_sample + 1) % 20;
6205ffd7c74aSJoe Talbott
6206ffd7c74aSJoe Talbott /* Compute maximum noise among last 20 samples. */
6207ffd7c74aSJoe Talbott noise_ref = calib->noise_samples[0];
6208ffd7c74aSJoe Talbott for (i = 1; i < 20; i++)
6209ffd7c74aSJoe Talbott noise_ref = MAX(noise_ref, calib->noise_samples[i]);
6210ffd7c74aSJoe Talbott
6211ffd7c74aSJoe Talbott /* Compute maximum energy among 3 receivers. */
6212ffd7c74aSJoe Talbott for (i = 0; i < 3; i++)
6213ffd7c74aSJoe Talbott energy[i] = le32toh(stats->general.energy[i]);
6214ffd7c74aSJoe Talbott val = MIN(energy[0], energy[1]);
6215ffd7c74aSJoe Talbott val = MIN(energy[2], val);
6216ffd7c74aSJoe Talbott /* Insert it into our samples table. */
6217ffd7c74aSJoe Talbott calib->energy_samples[calib->cur_energy_sample] = val;
6218ffd7c74aSJoe Talbott calib->cur_energy_sample = (calib->cur_energy_sample + 1) % 10;
6219ffd7c74aSJoe Talbott
6220ffd7c74aSJoe Talbott /* Compute minimum energy among last 10 samples. */
6221ffd7c74aSJoe Talbott energy_min = calib->energy_samples[0];
6222ffd7c74aSJoe Talbott for (i = 1; i < 10; i++)
6223ffd7c74aSJoe Talbott energy_min = MAX(energy_min, calib->energy_samples[i]);
6224ffd7c74aSJoe Talbott energy_min += 6;
6225ffd7c74aSJoe Talbott
6226ffd7c74aSJoe Talbott /* Compute number of false alarms since last call for CCK. */
6227ffd7c74aSJoe Talbott fa = le32toh(stats->cck.bad_plcp) - calib->bad_plcp_cck;
6228ffd7c74aSJoe Talbott fa += le32toh(stats->cck.fa) - calib->fa_cck;
6229da10ea93SMatthew Dillon fa *= 200 * IEEE80211_DUR_TU; /* 200TU */
6230ffd7c74aSJoe Talbott
6231ffd7c74aSJoe Talbott if (fa > 50 * rxena) {
6232ffd7c74aSJoe Talbott /* High false alarm count, decrease sensitivity. */
6233ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_CALIBRATE,
6234ffd7c74aSJoe Talbott "%s: CCK high false alarm count: %u\n", __func__, fa);
6235ffd7c74aSJoe Talbott calib->cck_state = IWN_CCK_STATE_HIFA;
6236ffd7c74aSJoe Talbott calib->low_fa = 0;
6237ffd7c74aSJoe Talbott
6238ffd7c74aSJoe Talbott if (calib->cck_x4 > 160) {
6239ffd7c74aSJoe Talbott calib->noise_ref = noise_ref;
6240ffd7c74aSJoe Talbott if (calib->energy_cck > 2)
6241ffd7c74aSJoe Talbott dec(calib->energy_cck, 2, energy_min);
6242ffd7c74aSJoe Talbott }
6243ffd7c74aSJoe Talbott if (calib->cck_x4 < 160) {
6244ffd7c74aSJoe Talbott calib->cck_x4 = 161;
6245ffd7c74aSJoe Talbott needs_update = 1;
6246ffd7c74aSJoe Talbott } else
6247ffd7c74aSJoe Talbott inc(calib->cck_x4, 3, limits->max_cck_x4);
6248ffd7c74aSJoe Talbott
6249ffd7c74aSJoe Talbott inc(calib->cck_mrc_x4, 3, limits->max_cck_mrc_x4);
6250ffd7c74aSJoe Talbott
6251ffd7c74aSJoe Talbott } else if (fa < 5 * rxena) {
6252ffd7c74aSJoe Talbott /* Low false alarm count, increase sensitivity. */
6253ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_CALIBRATE,
6254ffd7c74aSJoe Talbott "%s: CCK low false alarm count: %u\n", __func__, fa);
6255ffd7c74aSJoe Talbott calib->cck_state = IWN_CCK_STATE_LOFA;
6256ffd7c74aSJoe Talbott calib->low_fa++;
6257ffd7c74aSJoe Talbott
6258ffd7c74aSJoe Talbott if (calib->cck_state != IWN_CCK_STATE_INIT &&
6259ffd7c74aSJoe Talbott (((int32_t)calib->noise_ref - (int32_t)noise_ref) > 2 ||
6260ffd7c74aSJoe Talbott calib->low_fa > 100)) {
6261ffd7c74aSJoe Talbott inc(calib->energy_cck, 2, limits->min_energy_cck);
6262ffd7c74aSJoe Talbott dec(calib->cck_x4, 3, limits->min_cck_x4);
6263ffd7c74aSJoe Talbott dec(calib->cck_mrc_x4, 3, limits->min_cck_mrc_x4);
6264ffd7c74aSJoe Talbott }
6265ffd7c74aSJoe Talbott } else {
6266ffd7c74aSJoe Talbott /* Not worth to increase or decrease sensitivity. */
6267ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_CALIBRATE,
6268ffd7c74aSJoe Talbott "%s: CCK normal false alarm count: %u\n", __func__, fa);
6269ffd7c74aSJoe Talbott calib->low_fa = 0;
6270ffd7c74aSJoe Talbott calib->noise_ref = noise_ref;
6271ffd7c74aSJoe Talbott
6272ffd7c74aSJoe Talbott if (calib->cck_state == IWN_CCK_STATE_HIFA) {
6273ffd7c74aSJoe Talbott /* Previous interval had many false alarms. */
6274ffd7c74aSJoe Talbott dec(calib->energy_cck, 8, energy_min);
6275ffd7c74aSJoe Talbott }
6276ffd7c74aSJoe Talbott calib->cck_state = IWN_CCK_STATE_INIT;
6277ffd7c74aSJoe Talbott }
6278ffd7c74aSJoe Talbott
6279ffd7c74aSJoe Talbott if (needs_update)
6280ffd7c74aSJoe Talbott (void)iwn_send_sensitivity(sc);
6281da10ea93SMatthew Dillon
6282da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
6283da10ea93SMatthew Dillon
6284ffd7c74aSJoe Talbott #undef dec
6285ffd7c74aSJoe Talbott #undef inc
6286ffd7c74aSJoe Talbott }
6287ffd7c74aSJoe Talbott
6288ffd7c74aSJoe Talbott static int
6289ffd7c74aSJoe Talbott iwn_send_sensitivity(struct iwn_softc *sc)
6290ffd7c74aSJoe Talbott {
6291ffd7c74aSJoe Talbott struct iwn_calib_state *calib = &sc->calib;
6292da10ea93SMatthew Dillon struct iwn_enhanced_sensitivity_cmd cmd;
6293da10ea93SMatthew Dillon int len;
6294ffd7c74aSJoe Talbott
6295ffd7c74aSJoe Talbott memset(&cmd, 0, sizeof cmd);
6296da10ea93SMatthew Dillon len = sizeof (struct iwn_sensitivity_cmd);
6297ffd7c74aSJoe Talbott cmd.which = IWN_SENSITIVITY_WORKTBL;
6298ffd7c74aSJoe Talbott /* OFDM modulation. */
6299ffd7c74aSJoe Talbott cmd.corr_ofdm_x1 = htole16(calib->ofdm_x1);
6300ffd7c74aSJoe Talbott cmd.corr_ofdm_mrc_x1 = htole16(calib->ofdm_mrc_x1);
6301ffd7c74aSJoe Talbott cmd.corr_ofdm_x4 = htole16(calib->ofdm_x4);
6302ffd7c74aSJoe Talbott cmd.corr_ofdm_mrc_x4 = htole16(calib->ofdm_mrc_x4);
6303ffd7c74aSJoe Talbott cmd.energy_ofdm = htole16(sc->limits->energy_ofdm);
6304ffd7c74aSJoe Talbott cmd.energy_ofdm_th = htole16(62);
6305ffd7c74aSJoe Talbott /* CCK modulation. */
6306ffd7c74aSJoe Talbott cmd.corr_cck_x4 = htole16(calib->cck_x4);
6307ffd7c74aSJoe Talbott cmd.corr_cck_mrc_x4 = htole16(calib->cck_mrc_x4);
6308ffd7c74aSJoe Talbott cmd.energy_cck = htole16(calib->energy_cck);
6309ffd7c74aSJoe Talbott /* Barker modulation: use default values. */
6310ffd7c74aSJoe Talbott cmd.corr_barker = htole16(190);
6311fd49669cSMichael Neumann cmd.corr_barker_mrc = htole16(sc->limits->barker_mrc);
6312ffd7c74aSJoe Talbott
6313ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_CALIBRATE,
6314ffd7c74aSJoe Talbott "%s: set sensitivity %d/%d/%d/%d/%d/%d/%d\n", __func__,
6315ffd7c74aSJoe Talbott calib->ofdm_x1, calib->ofdm_mrc_x1, calib->ofdm_x4,
6316ffd7c74aSJoe Talbott calib->ofdm_mrc_x4, calib->cck_x4,
6317ffd7c74aSJoe Talbott calib->cck_mrc_x4, calib->energy_cck);
6318da10ea93SMatthew Dillon
6319da10ea93SMatthew Dillon if (!(sc->sc_flags & IWN_FLAG_ENH_SENS))
6320da10ea93SMatthew Dillon goto send;
6321da10ea93SMatthew Dillon /* Enhanced sensitivity settings. */
6322da10ea93SMatthew Dillon len = sizeof (struct iwn_enhanced_sensitivity_cmd);
6323da10ea93SMatthew Dillon cmd.ofdm_det_slope_mrc = htole16(668);
6324da10ea93SMatthew Dillon cmd.ofdm_det_icept_mrc = htole16(4);
6325da10ea93SMatthew Dillon cmd.ofdm_det_slope = htole16(486);
6326da10ea93SMatthew Dillon cmd.ofdm_det_icept = htole16(37);
6327da10ea93SMatthew Dillon cmd.cck_det_slope_mrc = htole16(853);
6328da10ea93SMatthew Dillon cmd.cck_det_icept_mrc = htole16(4);
6329da10ea93SMatthew Dillon cmd.cck_det_slope = htole16(476);
6330da10ea93SMatthew Dillon cmd.cck_det_icept = htole16(99);
6331da10ea93SMatthew Dillon send:
6332da10ea93SMatthew Dillon return iwn_cmd(sc, IWN_CMD_SET_SENSITIVITY, &cmd, len, 1);
6333ffd7c74aSJoe Talbott }
6334ffd7c74aSJoe Talbott
6335ffd7c74aSJoe Talbott /*
6336fd49669cSMichael Neumann * Look at the increase of PLCP errors over time; if it exceeds
6337fd49669cSMichael Neumann * a programmed threshold then trigger an RF retune.
6338fd49669cSMichael Neumann */
6339fd49669cSMichael Neumann static void
6340fd49669cSMichael Neumann iwn_check_rx_recovery(struct iwn_softc *sc, struct iwn_stats *rs)
6341fd49669cSMichael Neumann {
6342fd49669cSMichael Neumann int32_t delta_ofdm, delta_ht, delta_cck;
6343fd49669cSMichael Neumann struct iwn_calib_state *calib = &sc->calib;
6344fd49669cSMichael Neumann int delta_ticks, cur_ticks;
6345fd49669cSMichael Neumann int delta_msec;
6346fd49669cSMichael Neumann int thresh;
6347fd49669cSMichael Neumann
6348fd49669cSMichael Neumann /*
6349fd49669cSMichael Neumann * Calculate the difference between the current and
6350fd49669cSMichael Neumann * previous statistics.
6351fd49669cSMichael Neumann */
6352fd49669cSMichael Neumann delta_cck = le32toh(rs->rx.cck.bad_plcp) - calib->bad_plcp_cck;
6353fd49669cSMichael Neumann delta_ofdm = le32toh(rs->rx.ofdm.bad_plcp) - calib->bad_plcp_ofdm;
6354fd49669cSMichael Neumann delta_ht = le32toh(rs->rx.ht.bad_plcp) - calib->bad_plcp_ht;
6355fd49669cSMichael Neumann
6356fd49669cSMichael Neumann /*
6357fd49669cSMichael Neumann * Calculate the delta in time between successive statistics
6358fd49669cSMichael Neumann * messages. Yes, it can roll over; so we make sure that
6359fd49669cSMichael Neumann * this doesn't happen.
6360fd49669cSMichael Neumann *
6361fd49669cSMichael Neumann * XXX go figure out what to do about rollover
6362fd49669cSMichael Neumann * XXX go figure out what to do if ticks rolls over to -ve instead!
6363fd49669cSMichael Neumann * XXX go stab signed integer overflow undefined-ness in the face.
6364fd49669cSMichael Neumann */
6365fd49669cSMichael Neumann cur_ticks = ticks;
6366fd49669cSMichael Neumann delta_ticks = cur_ticks - sc->last_calib_ticks;
6367fd49669cSMichael Neumann
6368fd49669cSMichael Neumann /*
6369fd49669cSMichael Neumann * If any are negative, then the firmware likely reset; so just
6370fd49669cSMichael Neumann * bail. We'll pick this up next time.
6371fd49669cSMichael Neumann */
6372fd49669cSMichael Neumann if (delta_cck < 0 || delta_ofdm < 0 || delta_ht < 0 || delta_ticks < 0)
6373fd49669cSMichael Neumann return;
6374fd49669cSMichael Neumann
6375fd49669cSMichael Neumann /*
6376fd49669cSMichael Neumann * delta_ticks is in ticks; we need to convert it up to milliseconds
6377fd49669cSMichael Neumann * so we can do some useful math with it.
6378fd49669cSMichael Neumann */
6379fd49669cSMichael Neumann delta_msec = ticks_to_msecs(delta_ticks);
6380fd49669cSMichael Neumann
6381fd49669cSMichael Neumann /*
6382fd49669cSMichael Neumann * Calculate what our threshold is given the current delta_msec.
6383fd49669cSMichael Neumann */
6384fd49669cSMichael Neumann thresh = sc->base_params->plcp_err_threshold * delta_msec;
6385fd49669cSMichael Neumann
6386fd49669cSMichael Neumann DPRINTF(sc, IWN_DEBUG_STATE,
6387fd49669cSMichael Neumann "%s: time delta: %d; cck=%d, ofdm=%d, ht=%d, total=%d, thresh=%d\n",
6388fd49669cSMichael Neumann __func__,
6389fd49669cSMichael Neumann delta_msec,
6390fd49669cSMichael Neumann delta_cck,
6391fd49669cSMichael Neumann delta_ofdm,
6392fd49669cSMichael Neumann delta_ht,
6393fd49669cSMichael Neumann (delta_msec + delta_cck + delta_ofdm + delta_ht),
6394fd49669cSMichael Neumann thresh);
6395fd49669cSMichael Neumann
6396fd49669cSMichael Neumann /*
6397fd49669cSMichael Neumann * If we need a retune, then schedule a single channel scan
6398fd49669cSMichael Neumann * to a channel that isn't the currently active one!
6399fd49669cSMichael Neumann *
6400fd49669cSMichael Neumann * The math from linux iwlwifi:
6401fd49669cSMichael Neumann *
6402fd49669cSMichael Neumann * if ((delta * 100 / msecs) > threshold)
6403fd49669cSMichael Neumann */
6404fd49669cSMichael Neumann if (thresh > 0 && (delta_cck + delta_ofdm + delta_ht) * 100 > thresh) {
6405fd49669cSMichael Neumann DPRINTF(sc, IWN_DEBUG_ANY,
6406fd49669cSMichael Neumann "%s: PLCP error threshold raw (%d) comparison (%d) "
6407fd49669cSMichael Neumann "over limit (%d); retune!\n",
6408fd49669cSMichael Neumann __func__,
6409fd49669cSMichael Neumann (delta_cck + delta_ofdm + delta_ht),
6410fd49669cSMichael Neumann (delta_cck + delta_ofdm + delta_ht) * 100,
6411fd49669cSMichael Neumann thresh);
6412fd49669cSMichael Neumann }
6413fd49669cSMichael Neumann }
6414fd49669cSMichael Neumann
6415fd49669cSMichael Neumann /*
6416ffd7c74aSJoe Talbott * Set STA mode power saving level (between 0 and 5).
6417ffd7c74aSJoe Talbott * Level 0 is CAM (Continuously Aware Mode), 5 is for maximum power saving.
6418ffd7c74aSJoe Talbott */
6419ffd7c74aSJoe Talbott static int
6420ffd7c74aSJoe Talbott iwn_set_pslevel(struct iwn_softc *sc, int dtim, int level, int async)
6421ffd7c74aSJoe Talbott {
6422ffd7c74aSJoe Talbott struct iwn_pmgt_cmd cmd;
6423da10ea93SMatthew Dillon const struct iwn_pmgt *pmgt;
6424ffd7c74aSJoe Talbott uint32_t max, skip_dtim;
6425da10ea93SMatthew Dillon uint32_t reg;
6426ffd7c74aSJoe Talbott int i;
6427ffd7c74aSJoe Talbott
6428da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_PWRSAVE,
6429da10ea93SMatthew Dillon "%s: dtim=%d, level=%d, async=%d\n",
6430da10ea93SMatthew Dillon __func__,
6431da10ea93SMatthew Dillon dtim,
6432da10ea93SMatthew Dillon level,
6433da10ea93SMatthew Dillon async);
6434da10ea93SMatthew Dillon
6435ffd7c74aSJoe Talbott /* Select which PS parameters to use. */
6436ffd7c74aSJoe Talbott if (dtim <= 2)
6437ffd7c74aSJoe Talbott pmgt = &iwn_pmgt[0][level];
6438ffd7c74aSJoe Talbott else if (dtim <= 10)
6439ffd7c74aSJoe Talbott pmgt = &iwn_pmgt[1][level];
6440ffd7c74aSJoe Talbott else
6441ffd7c74aSJoe Talbott pmgt = &iwn_pmgt[2][level];
6442ffd7c74aSJoe Talbott
6443ffd7c74aSJoe Talbott memset(&cmd, 0, sizeof cmd);
6444ffd7c74aSJoe Talbott if (level != 0) /* not CAM */
6445ffd7c74aSJoe Talbott cmd.flags |= htole16(IWN_PS_ALLOW_SLEEP);
6446ffd7c74aSJoe Talbott if (level == 5)
6447ffd7c74aSJoe Talbott cmd.flags |= htole16(IWN_PS_FAST_PD);
6448ffd7c74aSJoe Talbott /* Retrieve PCIe Active State Power Management (ASPM). */
6449977fc0dbSMatthew Dillon #if defined(__DragonFly__)
6450977fc0dbSMatthew Dillon reg = pci_read_config(sc->sc_dev, sc->sc_cap_off + PCIER_LINKCTRL, 4);
6451977fc0dbSMatthew Dillon if (!(reg & PCIEM_LNKCTL_ASPM_L0S)) /* L0s Entry disabled. */
6452ffd7c74aSJoe Talbott cmd.flags |= htole16(IWN_PS_PCI_PMGT);
6453977fc0dbSMatthew Dillon #else
6454977fc0dbSMatthew Dillon reg = pci_read_config(sc->sc_dev, sc->sc_cap_off + PCIER_LINK_CTL, 4);
6455977fc0dbSMatthew Dillon if (!(reg & PCIEM_LINK_CTL_ASPMC_L0S)) /* L0s Entry disabled. */
6456977fc0dbSMatthew Dillon cmd.flags |= htole16(IWN_PS_PCI_PMGT);
6457977fc0dbSMatthew Dillon #endif
6458ffd7c74aSJoe Talbott cmd.rxtimeout = htole32(pmgt->rxtimeout * 1024);
6459ffd7c74aSJoe Talbott cmd.txtimeout = htole32(pmgt->txtimeout * 1024);
6460ffd7c74aSJoe Talbott
6461ffd7c74aSJoe Talbott if (dtim == 0) {
6462ffd7c74aSJoe Talbott dtim = 1;
6463ffd7c74aSJoe Talbott skip_dtim = 0;
6464ffd7c74aSJoe Talbott } else
6465ffd7c74aSJoe Talbott skip_dtim = pmgt->skip_dtim;
6466ffd7c74aSJoe Talbott if (skip_dtim != 0) {
6467ffd7c74aSJoe Talbott cmd.flags |= htole16(IWN_PS_SLEEP_OVER_DTIM);
6468ffd7c74aSJoe Talbott max = pmgt->intval[4];
6469ffd7c74aSJoe Talbott if (max == (uint32_t)-1)
6470ffd7c74aSJoe Talbott max = dtim * (skip_dtim + 1);
6471ffd7c74aSJoe Talbott else if (max > dtim)
6472977fc0dbSMatthew Dillon max = rounddown(max, dtim);
6473ffd7c74aSJoe Talbott } else
6474ffd7c74aSJoe Talbott max = dtim;
6475ffd7c74aSJoe Talbott for (i = 0; i < 5; i++)
6476ffd7c74aSJoe Talbott cmd.intval[i] = htole32(MIN(max, pmgt->intval[i]));
6477ffd7c74aSJoe Talbott
6478ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_RESET, "setting power saving level to %d\n",
6479ffd7c74aSJoe Talbott level);
6480ffd7c74aSJoe Talbott return iwn_cmd(sc, IWN_CMD_SET_POWER_MODE, &cmd, sizeof cmd, async);
6481ffd7c74aSJoe Talbott }
6482ffd7c74aSJoe Talbott
6483ffd7c74aSJoe Talbott static int
6484da10ea93SMatthew Dillon iwn_send_btcoex(struct iwn_softc *sc)
6485da10ea93SMatthew Dillon {
6486da10ea93SMatthew Dillon struct iwn_bluetooth cmd;
6487da10ea93SMatthew Dillon
6488da10ea93SMatthew Dillon memset(&cmd, 0, sizeof cmd);
6489da10ea93SMatthew Dillon cmd.flags = IWN_BT_COEX_CHAN_ANN | IWN_BT_COEX_BT_PRIO;
6490da10ea93SMatthew Dillon cmd.lead_time = IWN_BT_LEAD_TIME_DEF;
6491da10ea93SMatthew Dillon cmd.max_kill = IWN_BT_MAX_KILL_DEF;
6492da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_RESET, "%s: configuring bluetooth coexistence\n",
6493da10ea93SMatthew Dillon __func__);
6494da10ea93SMatthew Dillon return iwn_cmd(sc, IWN_CMD_BT_COEX, &cmd, sizeof(cmd), 0);
6495da10ea93SMatthew Dillon }
6496da10ea93SMatthew Dillon
6497da10ea93SMatthew Dillon static int
6498da10ea93SMatthew Dillon iwn_send_advanced_btcoex(struct iwn_softc *sc)
6499da10ea93SMatthew Dillon {
6500da10ea93SMatthew Dillon static const uint32_t btcoex_3wire[12] = {
6501da10ea93SMatthew Dillon 0xaaaaaaaa, 0xaaaaaaaa, 0xaeaaaaaa, 0xaaaaaaaa,
6502da10ea93SMatthew Dillon 0xcc00ff28, 0x0000aaaa, 0xcc00aaaa, 0x0000aaaa,
6503da10ea93SMatthew Dillon 0xc0004000, 0x00004000, 0xf0005000, 0xf0005000,
6504da10ea93SMatthew Dillon };
6505da10ea93SMatthew Dillon struct iwn6000_btcoex_config btconfig;
6506da10ea93SMatthew Dillon struct iwn2000_btcoex_config btconfig2k;
6507da10ea93SMatthew Dillon struct iwn_btcoex_priotable btprio;
6508da10ea93SMatthew Dillon struct iwn_btcoex_prot btprot;
6509da10ea93SMatthew Dillon int error, i;
6510da10ea93SMatthew Dillon uint8_t flags;
6511da10ea93SMatthew Dillon
6512da10ea93SMatthew Dillon memset(&btconfig, 0, sizeof btconfig);
6513da10ea93SMatthew Dillon memset(&btconfig2k, 0, sizeof btconfig2k);
6514da10ea93SMatthew Dillon
6515da10ea93SMatthew Dillon flags = IWN_BT_FLAG_COEX6000_MODE_3W <<
6516da10ea93SMatthew Dillon IWN_BT_FLAG_COEX6000_MODE_SHIFT; // Done as is in linux kernel 3.2
6517da10ea93SMatthew Dillon
6518da10ea93SMatthew Dillon if (sc->base_params->bt_sco_disable)
6519da10ea93SMatthew Dillon flags &= ~IWN_BT_FLAG_SYNC_2_BT_DISABLE;
6520da10ea93SMatthew Dillon else
6521da10ea93SMatthew Dillon flags |= IWN_BT_FLAG_SYNC_2_BT_DISABLE;
6522da10ea93SMatthew Dillon
6523da10ea93SMatthew Dillon flags |= IWN_BT_FLAG_COEX6000_CHAN_INHIBITION;
6524da10ea93SMatthew Dillon
6525da10ea93SMatthew Dillon /* Default flags result is 145 as old value */
6526da10ea93SMatthew Dillon
6527da10ea93SMatthew Dillon /*
6528da10ea93SMatthew Dillon * Flags value has to be review. Values must change if we
6529da10ea93SMatthew Dillon * which to disable it
6530da10ea93SMatthew Dillon */
6531da10ea93SMatthew Dillon if (sc->base_params->bt_session_2) {
6532da10ea93SMatthew Dillon btconfig2k.flags = flags;
6533da10ea93SMatthew Dillon btconfig2k.max_kill = 5;
6534da10ea93SMatthew Dillon btconfig2k.bt3_t7_timer = 1;
6535da10ea93SMatthew Dillon btconfig2k.kill_ack = htole32(0xffff0000);
6536da10ea93SMatthew Dillon btconfig2k.kill_cts = htole32(0xffff0000);
6537da10ea93SMatthew Dillon btconfig2k.sample_time = 2;
6538da10ea93SMatthew Dillon btconfig2k.bt3_t2_timer = 0xc;
6539da10ea93SMatthew Dillon
6540da10ea93SMatthew Dillon for (i = 0; i < 12; i++)
6541da10ea93SMatthew Dillon btconfig2k.lookup_table[i] = htole32(btcoex_3wire[i]);
6542da10ea93SMatthew Dillon btconfig2k.valid = htole16(0xff);
6543da10ea93SMatthew Dillon btconfig2k.prio_boost = htole32(0xf0);
6544da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_RESET,
6545da10ea93SMatthew Dillon "%s: configuring advanced bluetooth coexistence"
6546da10ea93SMatthew Dillon " session 2, flags : 0x%x\n",
6547da10ea93SMatthew Dillon __func__,
6548da10ea93SMatthew Dillon flags);
6549da10ea93SMatthew Dillon error = iwn_cmd(sc, IWN_CMD_BT_COEX, &btconfig2k,
6550da10ea93SMatthew Dillon sizeof(btconfig2k), 1);
6551da10ea93SMatthew Dillon } else {
6552da10ea93SMatthew Dillon btconfig.flags = flags;
6553da10ea93SMatthew Dillon btconfig.max_kill = 5;
6554da10ea93SMatthew Dillon btconfig.bt3_t7_timer = 1;
6555da10ea93SMatthew Dillon btconfig.kill_ack = htole32(0xffff0000);
6556da10ea93SMatthew Dillon btconfig.kill_cts = htole32(0xffff0000);
6557da10ea93SMatthew Dillon btconfig.sample_time = 2;
6558da10ea93SMatthew Dillon btconfig.bt3_t2_timer = 0xc;
6559da10ea93SMatthew Dillon
6560da10ea93SMatthew Dillon for (i = 0; i < 12; i++)
6561da10ea93SMatthew Dillon btconfig.lookup_table[i] = htole32(btcoex_3wire[i]);
6562da10ea93SMatthew Dillon btconfig.valid = htole16(0xff);
6563da10ea93SMatthew Dillon btconfig.prio_boost = 0xf0;
6564da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_RESET,
6565da10ea93SMatthew Dillon "%s: configuring advanced bluetooth coexistence,"
6566da10ea93SMatthew Dillon " flags : 0x%x\n",
6567da10ea93SMatthew Dillon __func__,
6568da10ea93SMatthew Dillon flags);
6569da10ea93SMatthew Dillon error = iwn_cmd(sc, IWN_CMD_BT_COEX, &btconfig,
6570da10ea93SMatthew Dillon sizeof(btconfig), 1);
6571da10ea93SMatthew Dillon }
6572da10ea93SMatthew Dillon
6573da10ea93SMatthew Dillon if (error != 0)
6574da10ea93SMatthew Dillon return error;
6575da10ea93SMatthew Dillon
6576da10ea93SMatthew Dillon memset(&btprio, 0, sizeof btprio);
6577da10ea93SMatthew Dillon btprio.calib_init1 = 0x6;
6578da10ea93SMatthew Dillon btprio.calib_init2 = 0x7;
6579da10ea93SMatthew Dillon btprio.calib_periodic_low1 = 0x2;
6580da10ea93SMatthew Dillon btprio.calib_periodic_low2 = 0x3;
6581da10ea93SMatthew Dillon btprio.calib_periodic_high1 = 0x4;
6582da10ea93SMatthew Dillon btprio.calib_periodic_high2 = 0x5;
6583da10ea93SMatthew Dillon btprio.dtim = 0x6;
6584da10ea93SMatthew Dillon btprio.scan52 = 0x8;
6585da10ea93SMatthew Dillon btprio.scan24 = 0xa;
6586da10ea93SMatthew Dillon error = iwn_cmd(sc, IWN_CMD_BT_COEX_PRIOTABLE, &btprio, sizeof(btprio),
6587da10ea93SMatthew Dillon 1);
6588da10ea93SMatthew Dillon if (error != 0)
6589da10ea93SMatthew Dillon return error;
6590da10ea93SMatthew Dillon
6591da10ea93SMatthew Dillon /* Force BT state machine change. */
6592da10ea93SMatthew Dillon memset(&btprot, 0, sizeof btprot);
6593da10ea93SMatthew Dillon btprot.open = 1;
6594da10ea93SMatthew Dillon btprot.type = 1;
6595da10ea93SMatthew Dillon error = iwn_cmd(sc, IWN_CMD_BT_COEX_PROT, &btprot, sizeof(btprot), 1);
6596da10ea93SMatthew Dillon if (error != 0)
6597da10ea93SMatthew Dillon return error;
6598da10ea93SMatthew Dillon btprot.open = 0;
6599da10ea93SMatthew Dillon return iwn_cmd(sc, IWN_CMD_BT_COEX_PROT, &btprot, sizeof(btprot), 1);
6600da10ea93SMatthew Dillon }
6601da10ea93SMatthew Dillon
6602da10ea93SMatthew Dillon static int
6603da10ea93SMatthew Dillon iwn5000_runtime_calib(struct iwn_softc *sc)
6604da10ea93SMatthew Dillon {
6605da10ea93SMatthew Dillon struct iwn5000_calib_config cmd;
6606da10ea93SMatthew Dillon
6607da10ea93SMatthew Dillon memset(&cmd, 0, sizeof cmd);
6608da10ea93SMatthew Dillon cmd.ucode.once.enable = 0xffffffff;
6609da10ea93SMatthew Dillon cmd.ucode.once.start = IWN5000_CALIB_DC;
6610da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_CALIBRATE,
6611da10ea93SMatthew Dillon "%s: configuring runtime calibration\n", __func__);
6612da10ea93SMatthew Dillon return iwn_cmd(sc, IWN5000_CMD_CALIB_CONFIG, &cmd, sizeof(cmd), 0);
6613da10ea93SMatthew Dillon }
6614da10ea93SMatthew Dillon
6615977fc0dbSMatthew Dillon static uint32_t
6616977fc0dbSMatthew Dillon iwn_get_rxon_ht_flags(struct iwn_softc *sc, struct ieee80211_channel *c)
6617977fc0dbSMatthew Dillon {
6618977fc0dbSMatthew Dillon struct ieee80211com *ic = &sc->sc_ic;
6619977fc0dbSMatthew Dillon uint32_t htflags = 0;
6620977fc0dbSMatthew Dillon
6621977fc0dbSMatthew Dillon if (! IEEE80211_IS_CHAN_HT(c))
6622977fc0dbSMatthew Dillon return (0);
6623977fc0dbSMatthew Dillon
6624977fc0dbSMatthew Dillon htflags |= IWN_RXON_HT_PROTMODE(ic->ic_curhtprotmode);
6625977fc0dbSMatthew Dillon
6626977fc0dbSMatthew Dillon if (IEEE80211_IS_CHAN_HT40(c)) {
6627977fc0dbSMatthew Dillon switch (ic->ic_curhtprotmode) {
6628977fc0dbSMatthew Dillon case IEEE80211_HTINFO_OPMODE_HT20PR:
6629977fc0dbSMatthew Dillon htflags |= IWN_RXON_HT_MODEPURE40;
6630977fc0dbSMatthew Dillon break;
6631977fc0dbSMatthew Dillon default:
6632977fc0dbSMatthew Dillon htflags |= IWN_RXON_HT_MODEMIXED;
6633977fc0dbSMatthew Dillon break;
6634977fc0dbSMatthew Dillon }
6635977fc0dbSMatthew Dillon }
6636977fc0dbSMatthew Dillon if (IEEE80211_IS_CHAN_HT40D(c))
6637977fc0dbSMatthew Dillon htflags |= IWN_RXON_HT_HT40MINUS;
6638977fc0dbSMatthew Dillon
6639977fc0dbSMatthew Dillon return (htflags);
6640977fc0dbSMatthew Dillon }
6641977fc0dbSMatthew Dillon
6642da10ea93SMatthew Dillon static int
6643ffd7c74aSJoe Talbott iwn_config(struct iwn_softc *sc)
6644ffd7c74aSJoe Talbott {
6645da10ea93SMatthew Dillon struct iwn_ops *ops = &sc->ops;
6646977fc0dbSMatthew Dillon struct ieee80211com *ic = &sc->sc_ic;
6647977fc0dbSMatthew Dillon struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
6648977fc0dbSMatthew Dillon const uint8_t *macaddr;
6649ffd7c74aSJoe Talbott uint32_t txmask;
6650ffd7c74aSJoe Talbott uint16_t rxchain;
6651da10ea93SMatthew Dillon int error;
6652ffd7c74aSJoe Talbott
6653da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
6654da10ea93SMatthew Dillon
6655da10ea93SMatthew Dillon if ((sc->base_params->calib_need & IWN_FLG_NEED_PHY_CALIB_TEMP_OFFSET)
6656da10ea93SMatthew Dillon && (sc->base_params->calib_need & IWN_FLG_NEED_PHY_CALIB_TEMP_OFFSETv2)) {
6657da10ea93SMatthew Dillon device_printf(sc->sc_dev,"%s: temp_offset and temp_offsetv2 are"
6658da10ea93SMatthew Dillon " exclusive each together. Review NIC config file. Conf"
6659da10ea93SMatthew Dillon " : 0x%08x Flags : 0x%08x \n", __func__,
6660da10ea93SMatthew Dillon sc->base_params->calib_need,
6661da10ea93SMatthew Dillon (IWN_FLG_NEED_PHY_CALIB_TEMP_OFFSET |
6662da10ea93SMatthew Dillon IWN_FLG_NEED_PHY_CALIB_TEMP_OFFSETv2));
6663da10ea93SMatthew Dillon return (EINVAL);
6664da10ea93SMatthew Dillon }
6665da10ea93SMatthew Dillon
6666da10ea93SMatthew Dillon /* Compute temperature calib if needed. Will be send by send calib */
6667da10ea93SMatthew Dillon if (sc->base_params->calib_need & IWN_FLG_NEED_PHY_CALIB_TEMP_OFFSET) {
6668da10ea93SMatthew Dillon error = iwn5000_temp_offset_calib(sc);
6669da10ea93SMatthew Dillon if (error != 0) {
6670da10ea93SMatthew Dillon device_printf(sc->sc_dev,
6671da10ea93SMatthew Dillon "%s: could not set temperature offset\n", __func__);
6672da10ea93SMatthew Dillon return (error);
6673da10ea93SMatthew Dillon }
6674da10ea93SMatthew Dillon } else if (sc->base_params->calib_need & IWN_FLG_NEED_PHY_CALIB_TEMP_OFFSETv2) {
6675da10ea93SMatthew Dillon error = iwn5000_temp_offset_calibv2(sc);
6676da10ea93SMatthew Dillon if (error != 0) {
6677da10ea93SMatthew Dillon device_printf(sc->sc_dev,
6678da10ea93SMatthew Dillon "%s: could not compute temperature offset v2\n",
6679da10ea93SMatthew Dillon __func__);
6680da10ea93SMatthew Dillon return (error);
6681da10ea93SMatthew Dillon }
6682da10ea93SMatthew Dillon }
6683da10ea93SMatthew Dillon
6684da10ea93SMatthew Dillon if (sc->hw_type == IWN_HW_REV_TYPE_6050) {
6685da10ea93SMatthew Dillon /* Configure runtime DC calibration. */
6686da10ea93SMatthew Dillon error = iwn5000_runtime_calib(sc);
6687da10ea93SMatthew Dillon if (error != 0) {
6688da10ea93SMatthew Dillon device_printf(sc->sc_dev,
6689da10ea93SMatthew Dillon "%s: could not configure runtime calibration\n",
6690da10ea93SMatthew Dillon __func__);
6691da10ea93SMatthew Dillon return error;
6692da10ea93SMatthew Dillon }
6693da10ea93SMatthew Dillon }
6694da10ea93SMatthew Dillon
6695da10ea93SMatthew Dillon /* Configure valid TX chains for >=5000 Series. */
669605538f72SMatthew Dillon if (sc->hw_type != IWN_HW_REV_TYPE_4965 &&
669705538f72SMatthew Dillon IWN_UCODE_API(sc->ucode_rev) > 1) {
6698ffd7c74aSJoe Talbott txmask = htole32(sc->txchainmask);
669905538f72SMatthew Dillon DPRINTF(sc, IWN_DEBUG_RESET | IWN_DEBUG_XMIT,
6700ffd7c74aSJoe Talbott "%s: configuring valid TX chains 0x%x\n", __func__, txmask);
6701ffd7c74aSJoe Talbott error = iwn_cmd(sc, IWN5000_CMD_TX_ANT_CONFIG, &txmask,
6702ffd7c74aSJoe Talbott sizeof txmask, 0);
6703ffd7c74aSJoe Talbott if (error != 0) {
6704ffd7c74aSJoe Talbott device_printf(sc->sc_dev,
6705ffd7c74aSJoe Talbott "%s: could not configure valid TX chains, "
6706ffd7c74aSJoe Talbott "error %d\n", __func__, error);
6707ffd7c74aSJoe Talbott return error;
6708ffd7c74aSJoe Talbott }
6709ffd7c74aSJoe Talbott }
6710ffd7c74aSJoe Talbott
6711ffd7c74aSJoe Talbott /* Configure bluetooth coexistence. */
6712da10ea93SMatthew Dillon error = 0;
6713da10ea93SMatthew Dillon
6714da10ea93SMatthew Dillon /* Configure bluetooth coexistence if needed. */
6715da10ea93SMatthew Dillon if (sc->base_params->bt_mode == IWN_BT_ADVANCED)
6716da10ea93SMatthew Dillon error = iwn_send_advanced_btcoex(sc);
6717da10ea93SMatthew Dillon if (sc->base_params->bt_mode == IWN_BT_SIMPLE)
6718da10ea93SMatthew Dillon error = iwn_send_btcoex(sc);
6719da10ea93SMatthew Dillon
6720ffd7c74aSJoe Talbott if (error != 0) {
6721ffd7c74aSJoe Talbott device_printf(sc->sc_dev,
6722ffd7c74aSJoe Talbott "%s: could not configure bluetooth coexistence, error %d\n",
6723ffd7c74aSJoe Talbott __func__, error);
6724ffd7c74aSJoe Talbott return error;
6725ffd7c74aSJoe Talbott }
6726ffd7c74aSJoe Talbott
6727ffd7c74aSJoe Talbott /* Set mode, channel, RX filter and enable RX. */
6728da10ea93SMatthew Dillon sc->rxon = &sc->rx_on[IWN_RXON_BSS_CTX];
6729da10ea93SMatthew Dillon memset(sc->rxon, 0, sizeof (struct iwn_rxon));
6730977fc0dbSMatthew Dillon macaddr = vap ? vap->iv_myaddr : ic->ic_macaddr;
6731977fc0dbSMatthew Dillon IEEE80211_ADDR_COPY(sc->rxon->myaddr, macaddr);
6732977fc0dbSMatthew Dillon IEEE80211_ADDR_COPY(sc->rxon->wlap, macaddr);
6733da10ea93SMatthew Dillon sc->rxon->chan = ieee80211_chan2ieee(ic, ic->ic_curchan);
6734da10ea93SMatthew Dillon sc->rxon->flags = htole32(IWN_RXON_TSF | IWN_RXON_CTS_TO_SELF);
6735ffd7c74aSJoe Talbott if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan))
6736da10ea93SMatthew Dillon sc->rxon->flags |= htole32(IWN_RXON_AUTO | IWN_RXON_24GHZ);
6737ffd7c74aSJoe Talbott switch (ic->ic_opmode) {
6738ffd7c74aSJoe Talbott case IEEE80211_M_STA:
6739da10ea93SMatthew Dillon sc->rxon->mode = IWN_MODE_STA;
6740da10ea93SMatthew Dillon sc->rxon->filter = htole32(IWN_FILTER_MULTICAST);
6741ffd7c74aSJoe Talbott break;
6742ffd7c74aSJoe Talbott case IEEE80211_M_MONITOR:
6743da10ea93SMatthew Dillon sc->rxon->mode = IWN_MODE_MONITOR;
6744da10ea93SMatthew Dillon sc->rxon->filter = htole32(IWN_FILTER_MULTICAST |
6745ffd7c74aSJoe Talbott IWN_FILTER_CTL | IWN_FILTER_PROMISC);
6746ffd7c74aSJoe Talbott break;
6747ffd7c74aSJoe Talbott default:
6748ffd7c74aSJoe Talbott /* Should not get there. */
6749ffd7c74aSJoe Talbott break;
6750ffd7c74aSJoe Talbott }
6751da10ea93SMatthew Dillon sc->rxon->cck_mask = 0x0f; /* not yet negotiated */
6752da10ea93SMatthew Dillon sc->rxon->ofdm_mask = 0xff; /* not yet negotiated */
6753da10ea93SMatthew Dillon sc->rxon->ht_single_mask = 0xff;
6754da10ea93SMatthew Dillon sc->rxon->ht_dual_mask = 0xff;
6755da10ea93SMatthew Dillon sc->rxon->ht_triple_mask = 0xff;
675605538f72SMatthew Dillon /*
675705538f72SMatthew Dillon * In active association mode, ensure that
675805538f72SMatthew Dillon * all the receive chains are enabled.
675905538f72SMatthew Dillon *
676005538f72SMatthew Dillon * Since we're not yet doing SMPS, don't allow the
676105538f72SMatthew Dillon * number of idle RX chains to be less than the active
676205538f72SMatthew Dillon * number.
676305538f72SMatthew Dillon */
6764ffd7c74aSJoe Talbott rxchain =
6765ffd7c74aSJoe Talbott IWN_RXCHAIN_VALID(sc->rxchainmask) |
676605538f72SMatthew Dillon IWN_RXCHAIN_MIMO_COUNT(sc->nrxchains) |
676705538f72SMatthew Dillon IWN_RXCHAIN_IDLE_COUNT(sc->nrxchains);
6768da10ea93SMatthew Dillon sc->rxon->rxchain = htole16(rxchain);
676905538f72SMatthew Dillon DPRINTF(sc, IWN_DEBUG_RESET | IWN_DEBUG_XMIT,
677005538f72SMatthew Dillon "%s: rxchainmask=0x%x, nrxchains=%d\n",
677105538f72SMatthew Dillon __func__,
677205538f72SMatthew Dillon sc->rxchainmask,
677305538f72SMatthew Dillon sc->nrxchains);
6774977fc0dbSMatthew Dillon
6775977fc0dbSMatthew Dillon sc->rxon->flags |= htole32(iwn_get_rxon_ht_flags(sc, ic->ic_curchan));
6776977fc0dbSMatthew Dillon
6777977fc0dbSMatthew Dillon DPRINTF(sc, IWN_DEBUG_RESET,
6778977fc0dbSMatthew Dillon "%s: setting configuration; flags=0x%08x\n",
6779977fc0dbSMatthew Dillon __func__, le32toh(sc->rxon->flags));
67804b420e05SMatthew Dillon if (sc->sc_is_scanning)
67814b420e05SMatthew Dillon device_printf(sc->sc_dev,
67824b420e05SMatthew Dillon "%s: is_scanning set, before RXON\n",
67834b420e05SMatthew Dillon __func__);
6784da10ea93SMatthew Dillon error = iwn_cmd(sc, IWN_CMD_RXON, sc->rxon, sc->rxonsz, 0);
6785ffd7c74aSJoe Talbott if (error != 0) {
6786da10ea93SMatthew Dillon device_printf(sc->sc_dev, "%s: RXON command failed\n",
6787da10ea93SMatthew Dillon __func__);
6788ffd7c74aSJoe Talbott return error;
6789ffd7c74aSJoe Talbott }
6790ffd7c74aSJoe Talbott
6791da10ea93SMatthew Dillon if ((error = iwn_add_broadcast_node(sc, 0)) != 0) {
6792da10ea93SMatthew Dillon device_printf(sc->sc_dev, "%s: could not add broadcast node\n",
6793da10ea93SMatthew Dillon __func__);
6794ffd7c74aSJoe Talbott return error;
6795ffd7c74aSJoe Talbott }
6796ffd7c74aSJoe Talbott
6797ffd7c74aSJoe Talbott /* Configuration has changed, set TX power accordingly. */
6798da10ea93SMatthew Dillon if ((error = ops->set_txpower(sc, ic->ic_curchan, 0)) != 0) {
6799da10ea93SMatthew Dillon device_printf(sc->sc_dev, "%s: could not set TX power\n",
6800da10ea93SMatthew Dillon __func__);
6801ffd7c74aSJoe Talbott return error;
6802ffd7c74aSJoe Talbott }
6803ffd7c74aSJoe Talbott
6804da10ea93SMatthew Dillon if ((error = iwn_set_critical_temp(sc)) != 0) {
6805ffd7c74aSJoe Talbott device_printf(sc->sc_dev,
6806da10ea93SMatthew Dillon "%s: could not set critical temperature\n", __func__);
6807ffd7c74aSJoe Talbott return error;
6808ffd7c74aSJoe Talbott }
6809ffd7c74aSJoe Talbott
6810ffd7c74aSJoe Talbott /* Set power saving level to CAM during initialization. */
6811da10ea93SMatthew Dillon if ((error = iwn_set_pslevel(sc, 0, 0, 0)) != 0) {
6812ffd7c74aSJoe Talbott device_printf(sc->sc_dev,
6813ffd7c74aSJoe Talbott "%s: could not set power saving level\n", __func__);
6814ffd7c74aSJoe Talbott return error;
6815ffd7c74aSJoe Talbott }
6816da10ea93SMatthew Dillon
6817da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
6818da10ea93SMatthew Dillon
6819ffd7c74aSJoe Talbott return 0;
6820ffd7c74aSJoe Talbott }
6821ffd7c74aSJoe Talbott
68224b420e05SMatthew Dillon static uint16_t
68234b420e05SMatthew Dillon iwn_get_active_dwell_time(struct iwn_softc *sc,
68244b420e05SMatthew Dillon struct ieee80211_channel *c, uint8_t n_probes)
68254b420e05SMatthew Dillon {
68264b420e05SMatthew Dillon /* No channel? Default to 2GHz settings */
68274b420e05SMatthew Dillon if (c == NULL || IEEE80211_IS_CHAN_2GHZ(c)) {
68284b420e05SMatthew Dillon return (IWN_ACTIVE_DWELL_TIME_2GHZ +
68294b420e05SMatthew Dillon IWN_ACTIVE_DWELL_FACTOR_2GHZ * (n_probes + 1));
68304b420e05SMatthew Dillon }
68314b420e05SMatthew Dillon
68324b420e05SMatthew Dillon /* 5GHz dwell time */
68334b420e05SMatthew Dillon return (IWN_ACTIVE_DWELL_TIME_5GHZ +
68344b420e05SMatthew Dillon IWN_ACTIVE_DWELL_FACTOR_5GHZ * (n_probes + 1));
68354b420e05SMatthew Dillon }
68364b420e05SMatthew Dillon
68374b420e05SMatthew Dillon /*
68384b420e05SMatthew Dillon * Limit the total dwell time to 85% of the beacon interval.
68394b420e05SMatthew Dillon *
68404b420e05SMatthew Dillon * Returns the dwell time in milliseconds.
68414b420e05SMatthew Dillon */
68424b420e05SMatthew Dillon static uint16_t
68434b420e05SMatthew Dillon iwn_limit_dwell(struct iwn_softc *sc, uint16_t dwell_time)
68444b420e05SMatthew Dillon {
6845977fc0dbSMatthew Dillon struct ieee80211com *ic = &sc->sc_ic;
68464b420e05SMatthew Dillon struct ieee80211vap *vap = NULL;
68474b420e05SMatthew Dillon int bintval = 0;
68484b420e05SMatthew Dillon
68494b420e05SMatthew Dillon /* bintval is in TU (1.024mS) */
68504b420e05SMatthew Dillon if (! TAILQ_EMPTY(&ic->ic_vaps)) {
68514b420e05SMatthew Dillon vap = TAILQ_FIRST(&ic->ic_vaps);
68524b420e05SMatthew Dillon bintval = vap->iv_bss->ni_intval;
68534b420e05SMatthew Dillon }
68544b420e05SMatthew Dillon
68554b420e05SMatthew Dillon /*
68564b420e05SMatthew Dillon * If it's non-zero, we should calculate the minimum of
68574b420e05SMatthew Dillon * it and the DWELL_BASE.
68584b420e05SMatthew Dillon *
68594b420e05SMatthew Dillon * XXX Yes, the math should take into account that bintval
68604b420e05SMatthew Dillon * is 1.024mS, not 1mS..
68614b420e05SMatthew Dillon */
68624b420e05SMatthew Dillon if (bintval > 0) {
68634b420e05SMatthew Dillon DPRINTF(sc, IWN_DEBUG_SCAN,
68644b420e05SMatthew Dillon "%s: bintval=%d\n",
68654b420e05SMatthew Dillon __func__,
68664b420e05SMatthew Dillon bintval);
68674b420e05SMatthew Dillon return (MIN(IWN_PASSIVE_DWELL_BASE, ((bintval * 85) / 100)));
68684b420e05SMatthew Dillon }
68694b420e05SMatthew Dillon
68704b420e05SMatthew Dillon /* No association context? Default */
68714b420e05SMatthew Dillon return (IWN_PASSIVE_DWELL_BASE);
68724b420e05SMatthew Dillon }
68734b420e05SMatthew Dillon
68744b420e05SMatthew Dillon static uint16_t
68754b420e05SMatthew Dillon iwn_get_passive_dwell_time(struct iwn_softc *sc, struct ieee80211_channel *c)
68764b420e05SMatthew Dillon {
68774b420e05SMatthew Dillon uint16_t passive;
68784b420e05SMatthew Dillon
68794b420e05SMatthew Dillon if (c == NULL || IEEE80211_IS_CHAN_2GHZ(c)) {
68804b420e05SMatthew Dillon passive = IWN_PASSIVE_DWELL_BASE + IWN_PASSIVE_DWELL_TIME_2GHZ;
68814b420e05SMatthew Dillon } else {
68824b420e05SMatthew Dillon passive = IWN_PASSIVE_DWELL_BASE + IWN_PASSIVE_DWELL_TIME_5GHZ;
68834b420e05SMatthew Dillon }
68844b420e05SMatthew Dillon
68854b420e05SMatthew Dillon /* Clamp to the beacon interval if we're associated */
68864b420e05SMatthew Dillon return (iwn_limit_dwell(sc, passive));
68874b420e05SMatthew Dillon }
68884b420e05SMatthew Dillon
6889ffd7c74aSJoe Talbott static int
6890fd49669cSMichael Neumann iwn_scan(struct iwn_softc *sc, struct ieee80211vap *vap,
6891fd49669cSMichael Neumann struct ieee80211_scan_state *ss, struct ieee80211_channel *c)
6892ffd7c74aSJoe Talbott {
6893977fc0dbSMatthew Dillon struct ieee80211com *ic = &sc->sc_ic;
6894fd49669cSMichael Neumann struct ieee80211_node *ni = vap->iv_bss;
6895ffd7c74aSJoe Talbott struct iwn_scan_hdr *hdr;
6896ffd7c74aSJoe Talbott struct iwn_cmd_data *tx;
6897ffd7c74aSJoe Talbott struct iwn_scan_essid *essid;
6898ffd7c74aSJoe Talbott struct iwn_scan_chan *chan;
6899ffd7c74aSJoe Talbott struct ieee80211_frame *wh;
6900ffd7c74aSJoe Talbott struct ieee80211_rateset *rs;
6901da10ea93SMatthew Dillon uint8_t *buf, *frm;
6902ffd7c74aSJoe Talbott uint16_t rxchain;
6903da10ea93SMatthew Dillon uint8_t txant;
6904da10ea93SMatthew Dillon int buflen, error;
69054b420e05SMatthew Dillon int is_active;
69064b420e05SMatthew Dillon uint16_t dwell_active, dwell_passive;
69074b420e05SMatthew Dillon uint32_t extra, scan_service_time;
6908ffd7c74aSJoe Talbott
6909da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
6910da10ea93SMatthew Dillon
69114b420e05SMatthew Dillon /*
69124b420e05SMatthew Dillon * We are absolutely not allowed to send a scan command when another
69134b420e05SMatthew Dillon * scan command is pending.
69144b420e05SMatthew Dillon */
69154b420e05SMatthew Dillon if (sc->sc_is_scanning) {
69164b420e05SMatthew Dillon device_printf(sc->sc_dev, "%s: called whilst scanning!\n",
69174b420e05SMatthew Dillon __func__);
69184b420e05SMatthew Dillon return (EAGAIN);
69194b420e05SMatthew Dillon }
69204b420e05SMatthew Dillon
6921fd49669cSMichael Neumann /* Assign the scan channel */
6922fd49669cSMichael Neumann c = ic->ic_curchan;
6923fd49669cSMichael Neumann
6924da10ea93SMatthew Dillon sc->rxon = &sc->rx_on[IWN_RXON_BSS_CTX];
6925101554a6SMatthew Dillon buf = kmalloc(IWN_SCAN_MAXSZ, M_DEVBUF, M_INTWAIT | M_ZERO);
692605538f72SMatthew Dillon if (buf == NULL) {
692705538f72SMatthew Dillon device_printf(sc->sc_dev,
692805538f72SMatthew Dillon "%s: could not allocate buffer for scan command\n",
692905538f72SMatthew Dillon __func__);
693005538f72SMatthew Dillon return ENOMEM;
693105538f72SMatthew Dillon }
6932ffd7c74aSJoe Talbott hdr = (struct iwn_scan_hdr *)buf;
6933ffd7c74aSJoe Talbott /*
6934ffd7c74aSJoe Talbott * Move to the next channel if no frames are received within 10ms
6935ffd7c74aSJoe Talbott * after sending the probe request.
6936ffd7c74aSJoe Talbott */
6937ffd7c74aSJoe Talbott hdr->quiet_time = htole16(10); /* timeout in milliseconds */
6938ffd7c74aSJoe Talbott hdr->quiet_threshold = htole16(1); /* min # of packets */
6939da10ea93SMatthew Dillon /*
6940da10ea93SMatthew Dillon * Max needs to be greater than active and passive and quiet!
6941da10ea93SMatthew Dillon * It's also in microseconds!
6942da10ea93SMatthew Dillon */
69434b420e05SMatthew Dillon hdr->max_svc = htole32(250 * 1024);
69444b420e05SMatthew Dillon
69454b420e05SMatthew Dillon /*
69464b420e05SMatthew Dillon * Reset scan: interval=100
69474b420e05SMatthew Dillon * Normal scan: interval=becaon interval
69484b420e05SMatthew Dillon * suspend_time: 100 (TU)
69494b420e05SMatthew Dillon *
69504b420e05SMatthew Dillon */
69514b420e05SMatthew Dillon extra = (100 /* suspend_time */ / 100 /* beacon interval */) << 22;
69524b420e05SMatthew Dillon //scan_service_time = extra | ((100 /* susp */ % 100 /* int */) * 1024);
69534b420e05SMatthew Dillon scan_service_time = (4 << 22) | (100 * 1024); /* Hardcode for now! */
69544b420e05SMatthew Dillon hdr->pause_svc = htole32(scan_service_time);
6955ffd7c74aSJoe Talbott
6956ffd7c74aSJoe Talbott /* Select antennas for scanning. */
6957ffd7c74aSJoe Talbott rxchain =
6958ffd7c74aSJoe Talbott IWN_RXCHAIN_VALID(sc->rxchainmask) |
6959ffd7c74aSJoe Talbott IWN_RXCHAIN_FORCE_MIMO_SEL(sc->rxchainmask) |
6960ffd7c74aSJoe Talbott IWN_RXCHAIN_DRIVER_FORCE;
6961fd49669cSMichael Neumann if (IEEE80211_IS_CHAN_A(c) &&
6962ffd7c74aSJoe Talbott sc->hw_type == IWN_HW_REV_TYPE_4965) {
6963ffd7c74aSJoe Talbott /* Ant A must be avoided in 5GHz because of an HW bug. */
6964da10ea93SMatthew Dillon rxchain |= IWN_RXCHAIN_FORCE_SEL(IWN_ANT_B);
6965ffd7c74aSJoe Talbott } else /* Use all available RX antennas. */
6966ffd7c74aSJoe Talbott rxchain |= IWN_RXCHAIN_FORCE_SEL(sc->rxchainmask);
6967ffd7c74aSJoe Talbott hdr->rxchain = htole16(rxchain);
6968ffd7c74aSJoe Talbott hdr->filter = htole32(IWN_FILTER_MULTICAST | IWN_FILTER_BEACON);
6969ffd7c74aSJoe Talbott
6970ffd7c74aSJoe Talbott tx = (struct iwn_cmd_data *)(hdr + 1);
6971ffd7c74aSJoe Talbott tx->flags = htole32(IWN_TX_AUTO_SEQ);
6972da10ea93SMatthew Dillon tx->id = sc->broadcast_id;
6973ffd7c74aSJoe Talbott tx->lifetime = htole32(IWN_LIFETIME_INFINITE);
6974ffd7c74aSJoe Talbott
6975fd49669cSMichael Neumann if (IEEE80211_IS_CHAN_5GHZ(c)) {
6976ffd7c74aSJoe Talbott /* Send probe requests at 6Mbps. */
6977da10ea93SMatthew Dillon tx->rate = htole32(0xd);
6978ffd7c74aSJoe Talbott rs = &ic->ic_sup_rates[IEEE80211_MODE_11A];
6979ffd7c74aSJoe Talbott } else {
6980ffd7c74aSJoe Talbott hdr->flags = htole32(IWN_RXON_24GHZ | IWN_RXON_AUTO);
6981da10ea93SMatthew Dillon if (sc->hw_type == IWN_HW_REV_TYPE_4965 &&
6982da10ea93SMatthew Dillon sc->rxon->associd && sc->rxon->chan > 14)
6983da10ea93SMatthew Dillon tx->rate = htole32(0xd);
6984da10ea93SMatthew Dillon else {
6985ffd7c74aSJoe Talbott /* Send probe requests at 1Mbps. */
6986da10ea93SMatthew Dillon tx->rate = htole32(10 | IWN_RFLAG_CCK);
6987da10ea93SMatthew Dillon }
6988ffd7c74aSJoe Talbott rs = &ic->ic_sup_rates[IEEE80211_MODE_11G];
6989ffd7c74aSJoe Talbott }
6990ffd7c74aSJoe Talbott /* Use the first valid TX antenna. */
6991ffd7c74aSJoe Talbott txant = IWN_LSB(sc->txchainmask);
6992da10ea93SMatthew Dillon tx->rate |= htole32(IWN_RFLAG_ANT(txant));
6993ffd7c74aSJoe Talbott
69944b420e05SMatthew Dillon /*
69954b420e05SMatthew Dillon * Only do active scanning if we're announcing a probe request
69964b420e05SMatthew Dillon * for a given SSID (or more, if we ever add it to the driver.)
69974b420e05SMatthew Dillon */
69984b420e05SMatthew Dillon is_active = 0;
69994b420e05SMatthew Dillon
70004b420e05SMatthew Dillon /*
70014b420e05SMatthew Dillon * If we're scanning for a specific SSID, add it to the command.
7002fd49669cSMichael Neumann *
7003fd49669cSMichael Neumann * XXX maybe look at adding support for scanning multiple SSIDs?
70044b420e05SMatthew Dillon */
7005ffd7c74aSJoe Talbott essid = (struct iwn_scan_essid *)(tx + 1);
7006fd49669cSMichael Neumann if (ss != NULL) {
7007ffd7c74aSJoe Talbott if (ss->ss_ssid[0].len != 0) {
7008ffd7c74aSJoe Talbott essid[0].id = IEEE80211_ELEMID_SSID;
7009ffd7c74aSJoe Talbott essid[0].len = ss->ss_ssid[0].len;
7010ffd7c74aSJoe Talbott memcpy(essid[0].data, ss->ss_ssid[0].ssid, ss->ss_ssid[0].len);
7011ffd7c74aSJoe Talbott }
70124b420e05SMatthew Dillon
70134b420e05SMatthew Dillon DPRINTF(sc, IWN_DEBUG_SCAN, "%s: ssid_len=%d, ssid=%*s\n",
70144b420e05SMatthew Dillon __func__,
70154b420e05SMatthew Dillon ss->ss_ssid[0].len,
70164b420e05SMatthew Dillon ss->ss_ssid[0].len,
70174b420e05SMatthew Dillon ss->ss_ssid[0].ssid);
70184b420e05SMatthew Dillon
70194b420e05SMatthew Dillon if (ss->ss_nssid > 0)
70204b420e05SMatthew Dillon is_active = 1;
7021fd49669cSMichael Neumann }
70224b420e05SMatthew Dillon
7023ffd7c74aSJoe Talbott /*
7024ffd7c74aSJoe Talbott * Build a probe request frame. Most of the following code is a
7025ffd7c74aSJoe Talbott * copy & paste of what is done in net80211.
7026ffd7c74aSJoe Talbott */
7027ffd7c74aSJoe Talbott wh = (struct ieee80211_frame *)(essid + 20);
7028ffd7c74aSJoe Talbott wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT |
7029ffd7c74aSJoe Talbott IEEE80211_FC0_SUBTYPE_PROBE_REQ;
7030ffd7c74aSJoe Talbott wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
7031977fc0dbSMatthew Dillon IEEE80211_ADDR_COPY(wh->i_addr1, vap->iv_ifp->if_broadcastaddr);
7032977fc0dbSMatthew Dillon IEEE80211_ADDR_COPY(wh->i_addr2, IF_LLADDR(vap->iv_ifp));
7033977fc0dbSMatthew Dillon IEEE80211_ADDR_COPY(wh->i_addr3, vap->iv_ifp->if_broadcastaddr);
7034ffd7c74aSJoe Talbott *(uint16_t *)&wh->i_dur[0] = 0; /* filled by HW */
7035ffd7c74aSJoe Talbott *(uint16_t *)&wh->i_seq[0] = 0; /* filled by HW */
7036ffd7c74aSJoe Talbott
7037ffd7c74aSJoe Talbott frm = (uint8_t *)(wh + 1);
7038da10ea93SMatthew Dillon frm = ieee80211_add_ssid(frm, NULL, 0);
7039da10ea93SMatthew Dillon frm = ieee80211_add_rates(frm, rs);
7040da10ea93SMatthew Dillon if (rs->rs_nrates > IEEE80211_RATE_SIZE)
7041da10ea93SMatthew Dillon frm = ieee80211_add_xrates(frm, rs);
7042da10ea93SMatthew Dillon if (ic->ic_htcaps & IEEE80211_HTC_HT)
7043da10ea93SMatthew Dillon frm = ieee80211_add_htcap(frm, ni);
7044ffd7c74aSJoe Talbott
7045ffd7c74aSJoe Talbott /* Set length of probe request. */
7046ffd7c74aSJoe Talbott tx->len = htole16(frm - (uint8_t *)wh);
7047ffd7c74aSJoe Talbott
70484b420e05SMatthew Dillon /*
70494b420e05SMatthew Dillon * If active scanning is requested but a certain channel is
70504b420e05SMatthew Dillon * marked passive, we can do active scanning if we detect
70514b420e05SMatthew Dillon * transmissions.
70524b420e05SMatthew Dillon *
70534b420e05SMatthew Dillon * There is an issue with some firmware versions that triggers
70544b420e05SMatthew Dillon * a sysassert on a "good CRC threshold" of zero (== disabled),
70554b420e05SMatthew Dillon * on a radar channel even though this means that we should NOT
70564b420e05SMatthew Dillon * send probes.
70574b420e05SMatthew Dillon *
70584b420e05SMatthew Dillon * The "good CRC threshold" is the number of frames that we
70594b420e05SMatthew Dillon * need to receive during our dwell time on a channel before
70604b420e05SMatthew Dillon * sending out probes -- setting this to a huge value will
70614b420e05SMatthew Dillon * mean we never reach it, but at the same time work around
70624b420e05SMatthew Dillon * the aforementioned issue. Thus use IWL_GOOD_CRC_TH_NEVER
70634b420e05SMatthew Dillon * here instead of IWL_GOOD_CRC_TH_DISABLED.
70644b420e05SMatthew Dillon *
70654b420e05SMatthew Dillon * This was fixed in later versions along with some other
70664b420e05SMatthew Dillon * scan changes, and the threshold behaves as a flag in those
70674b420e05SMatthew Dillon * versions.
70684b420e05SMatthew Dillon */
70694b420e05SMatthew Dillon
70704b420e05SMatthew Dillon /*
70714b420e05SMatthew Dillon * If we're doing active scanning, set the crc_threshold
70724b420e05SMatthew Dillon * to a suitable value. This is different to active veruss
70734b420e05SMatthew Dillon * passive scanning depending upon the channel flags; the
70744b420e05SMatthew Dillon * firmware will obey that particular check for us.
70754b420e05SMatthew Dillon */
70764b420e05SMatthew Dillon if (sc->tlv_feature_flags & IWN_UCODE_TLV_FLAGS_NEWSCAN)
70774b420e05SMatthew Dillon hdr->crc_threshold = is_active ?
70784b420e05SMatthew Dillon IWN_GOOD_CRC_TH_DEFAULT : IWN_GOOD_CRC_TH_DISABLED;
70794b420e05SMatthew Dillon else
70804b420e05SMatthew Dillon hdr->crc_threshold = is_active ?
70814b420e05SMatthew Dillon IWN_GOOD_CRC_TH_DEFAULT : IWN_GOOD_CRC_TH_NEVER;
70824b420e05SMatthew Dillon
7083ffd7c74aSJoe Talbott chan = (struct iwn_scan_chan *)frm;
7084ffd7c74aSJoe Talbott chan->chan = htole16(ieee80211_chan2ieee(ic, c));
7085ffd7c74aSJoe Talbott chan->flags = 0;
7086ffd7c74aSJoe Talbott if (ss->ss_nssid > 0)
7087ffd7c74aSJoe Talbott chan->flags |= htole32(IWN_CHAN_NPBREQS(1));
7088ffd7c74aSJoe Talbott chan->dsp_gain = 0x6e;
70894b420e05SMatthew Dillon
70904b420e05SMatthew Dillon /*
70914b420e05SMatthew Dillon * Set the passive/active flag depending upon the channel mode.
70924b420e05SMatthew Dillon * XXX TODO: take the is_active flag into account as well?
70934b420e05SMatthew Dillon */
70944b420e05SMatthew Dillon if (c->ic_flags & IEEE80211_CHAN_PASSIVE)
70954b420e05SMatthew Dillon chan->flags |= htole32(IWN_CHAN_PASSIVE);
70964b420e05SMatthew Dillon else
70974b420e05SMatthew Dillon chan->flags |= htole32(IWN_CHAN_ACTIVE);
70984b420e05SMatthew Dillon
70994b420e05SMatthew Dillon /*
71004b420e05SMatthew Dillon * Calculate the active/passive dwell times.
71014b420e05SMatthew Dillon */
71024b420e05SMatthew Dillon
71034b420e05SMatthew Dillon dwell_active = iwn_get_active_dwell_time(sc, c, ss->ss_nssid);
71044b420e05SMatthew Dillon dwell_passive = iwn_get_passive_dwell_time(sc, c);
71054b420e05SMatthew Dillon
71064b420e05SMatthew Dillon /* Make sure they're valid */
71074b420e05SMatthew Dillon if (dwell_passive <= dwell_active)
71084b420e05SMatthew Dillon dwell_passive = dwell_active + 1;
71094b420e05SMatthew Dillon
71104b420e05SMatthew Dillon chan->active = htole16(dwell_active);
71114b420e05SMatthew Dillon chan->passive = htole16(dwell_passive);
71124b420e05SMatthew Dillon
7113977fc0dbSMatthew Dillon if (IEEE80211_IS_CHAN_5GHZ(c))
7114ffd7c74aSJoe Talbott chan->rf_gain = 0x3b;
7115977fc0dbSMatthew Dillon else
7116ffd7c74aSJoe Talbott chan->rf_gain = 0x28;
7117ffd7c74aSJoe Talbott
7118ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_STATE,
7119ffd7c74aSJoe Talbott "%s: chan %u flags 0x%x rf_gain 0x%x "
71204b420e05SMatthew Dillon "dsp_gain 0x%x active %d passive %d scan_svc_time %d crc 0x%x "
71214b420e05SMatthew Dillon "isactive=%d numssid=%d\n", __func__,
7122ffd7c74aSJoe Talbott chan->chan, chan->flags, chan->rf_gain, chan->dsp_gain,
71234b420e05SMatthew Dillon dwell_active, dwell_passive, scan_service_time,
71244b420e05SMatthew Dillon hdr->crc_threshold, is_active, ss->ss_nssid);
7125ffd7c74aSJoe Talbott
7126ffd7c74aSJoe Talbott hdr->nchan++;
7127ffd7c74aSJoe Talbott chan++;
7128ffd7c74aSJoe Talbott buflen = (uint8_t *)chan - buf;
7129ffd7c74aSJoe Talbott hdr->len = htole16(buflen);
7130ffd7c74aSJoe Talbott
71314b420e05SMatthew Dillon if (sc->sc_is_scanning) {
71324b420e05SMatthew Dillon device_printf(sc->sc_dev,
71334b420e05SMatthew Dillon "%s: called with is_scanning set!\n",
71344b420e05SMatthew Dillon __func__);
71354b420e05SMatthew Dillon }
71364b420e05SMatthew Dillon sc->sc_is_scanning = 1;
71374b420e05SMatthew Dillon
7138ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_STATE, "sending scan command nchan=%d\n",
7139ffd7c74aSJoe Talbott hdr->nchan);
7140ffd7c74aSJoe Talbott error = iwn_cmd(sc, IWN_CMD_SCAN, buf, buflen, 1);
7141101554a6SMatthew Dillon kfree(buf, M_DEVBUF);
7142da10ea93SMatthew Dillon
7143da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
7144da10ea93SMatthew Dillon
7145ffd7c74aSJoe Talbott return error;
7146ffd7c74aSJoe Talbott }
7147ffd7c74aSJoe Talbott
7148ffd7c74aSJoe Talbott static int
7149ffd7c74aSJoe Talbott iwn_auth(struct iwn_softc *sc, struct ieee80211vap *vap)
7150ffd7c74aSJoe Talbott {
7151da10ea93SMatthew Dillon struct iwn_ops *ops = &sc->ops;
7152977fc0dbSMatthew Dillon struct ieee80211com *ic = &sc->sc_ic;
7153ffd7c74aSJoe Talbott struct ieee80211_node *ni = vap->iv_bss;
7154ffd7c74aSJoe Talbott int error;
7155ffd7c74aSJoe Talbott
7156da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
7157ffd7c74aSJoe Talbott
7158da10ea93SMatthew Dillon sc->rxon = &sc->rx_on[IWN_RXON_BSS_CTX];
7159ffd7c74aSJoe Talbott /* Update adapter configuration. */
7160da10ea93SMatthew Dillon IEEE80211_ADDR_COPY(sc->rxon->bssid, ni->ni_bssid);
7161da10ea93SMatthew Dillon sc->rxon->chan = ieee80211_chan2ieee(ic, ni->ni_chan);
7162da10ea93SMatthew Dillon sc->rxon->flags = htole32(IWN_RXON_TSF | IWN_RXON_CTS_TO_SELF);
7163ffd7c74aSJoe Talbott if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
7164da10ea93SMatthew Dillon sc->rxon->flags |= htole32(IWN_RXON_AUTO | IWN_RXON_24GHZ);
7165ffd7c74aSJoe Talbott if (ic->ic_flags & IEEE80211_F_SHSLOT)
7166da10ea93SMatthew Dillon sc->rxon->flags |= htole32(IWN_RXON_SHSLOT);
7167ffd7c74aSJoe Talbott if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
7168da10ea93SMatthew Dillon sc->rxon->flags |= htole32(IWN_RXON_SHPREAMBLE);
7169ffd7c74aSJoe Talbott if (IEEE80211_IS_CHAN_A(ni->ni_chan)) {
7170da10ea93SMatthew Dillon sc->rxon->cck_mask = 0;
7171da10ea93SMatthew Dillon sc->rxon->ofdm_mask = 0x15;
7172ffd7c74aSJoe Talbott } else if (IEEE80211_IS_CHAN_B(ni->ni_chan)) {
7173da10ea93SMatthew Dillon sc->rxon->cck_mask = 0x03;
7174da10ea93SMatthew Dillon sc->rxon->ofdm_mask = 0;
7175ffd7c74aSJoe Talbott } else {
7176da10ea93SMatthew Dillon /* Assume 802.11b/g. */
7177fd49669cSMichael Neumann sc->rxon->cck_mask = 0x03;
7178da10ea93SMatthew Dillon sc->rxon->ofdm_mask = 0x15;
7179ffd7c74aSJoe Talbott }
7180977fc0dbSMatthew Dillon
7181977fc0dbSMatthew Dillon /* try HT */
7182977fc0dbSMatthew Dillon sc->rxon->flags |= htole32(iwn_get_rxon_ht_flags(sc, ic->ic_curchan));
7183977fc0dbSMatthew Dillon
7184da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_STATE, "rxon chan %d flags %x cck %x ofdm %x\n",
7185da10ea93SMatthew Dillon sc->rxon->chan, sc->rxon->flags, sc->rxon->cck_mask,
7186da10ea93SMatthew Dillon sc->rxon->ofdm_mask);
71874b420e05SMatthew Dillon if (sc->sc_is_scanning)
71884b420e05SMatthew Dillon device_printf(sc->sc_dev,
71894b420e05SMatthew Dillon "%s: is_scanning set, before RXON\n",
71904b420e05SMatthew Dillon __func__);
7191da10ea93SMatthew Dillon error = iwn_cmd(sc, IWN_CMD_RXON, sc->rxon, sc->rxonsz, 1);
7192ffd7c74aSJoe Talbott if (error != 0) {
7193da10ea93SMatthew Dillon device_printf(sc->sc_dev, "%s: RXON command failed, error %d\n",
7194da10ea93SMatthew Dillon __func__, error);
7195ffd7c74aSJoe Talbott return error;
7196ffd7c74aSJoe Talbott }
7197ffd7c74aSJoe Talbott
7198ffd7c74aSJoe Talbott /* Configuration has changed, set TX power accordingly. */
7199da10ea93SMatthew Dillon if ((error = ops->set_txpower(sc, ni->ni_chan, 1)) != 0) {
7200ffd7c74aSJoe Talbott device_printf(sc->sc_dev,
7201da10ea93SMatthew Dillon "%s: could not set TX power, error %d\n", __func__, error);
7202ffd7c74aSJoe Talbott return error;
7203ffd7c74aSJoe Talbott }
7204ffd7c74aSJoe Talbott /*
7205ffd7c74aSJoe Talbott * Reconfiguring RXON clears the firmware nodes table so we must
7206ffd7c74aSJoe Talbott * add the broadcast node again.
7207ffd7c74aSJoe Talbott */
7208da10ea93SMatthew Dillon if ((error = iwn_add_broadcast_node(sc, 1)) != 0) {
7209ffd7c74aSJoe Talbott device_printf(sc->sc_dev,
7210da10ea93SMatthew Dillon "%s: could not add broadcast node, error %d\n", __func__,
7211da10ea93SMatthew Dillon error);
7212ffd7c74aSJoe Talbott return error;
7213ffd7c74aSJoe Talbott }
7214da10ea93SMatthew Dillon
7215da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
7216da10ea93SMatthew Dillon
7217ffd7c74aSJoe Talbott return 0;
7218ffd7c74aSJoe Talbott }
7219ffd7c74aSJoe Talbott
7220ffd7c74aSJoe Talbott static int
7221ffd7c74aSJoe Talbott iwn_run(struct iwn_softc *sc, struct ieee80211vap *vap)
7222ffd7c74aSJoe Talbott {
7223da10ea93SMatthew Dillon struct iwn_ops *ops = &sc->ops;
7224977fc0dbSMatthew Dillon struct ieee80211com *ic = &sc->sc_ic;
7225ffd7c74aSJoe Talbott struct ieee80211_node *ni = vap->iv_bss;
7226ffd7c74aSJoe Talbott struct iwn_node_info node;
7227ffd7c74aSJoe Talbott int error;
7228ffd7c74aSJoe Talbott
7229da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
7230ffd7c74aSJoe Talbott
7231da10ea93SMatthew Dillon sc->rxon = &sc->rx_on[IWN_RXON_BSS_CTX];
7232ffd7c74aSJoe Talbott if (ic->ic_opmode == IEEE80211_M_MONITOR) {
7233ffd7c74aSJoe Talbott /* Link LED blinks while monitoring. */
7234ffd7c74aSJoe Talbott iwn_set_led(sc, IWN_LED_LINK, 5, 5);
7235ffd7c74aSJoe Talbott return 0;
7236ffd7c74aSJoe Talbott }
7237da10ea93SMatthew Dillon if ((error = iwn_set_timing(sc, ni)) != 0) {
7238ffd7c74aSJoe Talbott device_printf(sc->sc_dev,
7239ffd7c74aSJoe Talbott "%s: could not set timing, error %d\n", __func__, error);
7240ffd7c74aSJoe Talbott return error;
7241ffd7c74aSJoe Talbott }
7242ffd7c74aSJoe Talbott
7243ffd7c74aSJoe Talbott /* Update adapter configuration. */
7244da10ea93SMatthew Dillon IEEE80211_ADDR_COPY(sc->rxon->bssid, ni->ni_bssid);
7245da10ea93SMatthew Dillon sc->rxon->associd = htole16(IEEE80211_AID(ni->ni_associd));
7246da10ea93SMatthew Dillon sc->rxon->chan = ieee80211_chan2ieee(ic, ni->ni_chan);
7247da10ea93SMatthew Dillon sc->rxon->flags = htole32(IWN_RXON_TSF | IWN_RXON_CTS_TO_SELF);
7248ffd7c74aSJoe Talbott if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
7249da10ea93SMatthew Dillon sc->rxon->flags |= htole32(IWN_RXON_AUTO | IWN_RXON_24GHZ);
7250ffd7c74aSJoe Talbott if (ic->ic_flags & IEEE80211_F_SHSLOT)
7251da10ea93SMatthew Dillon sc->rxon->flags |= htole32(IWN_RXON_SHSLOT);
7252ffd7c74aSJoe Talbott if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
7253da10ea93SMatthew Dillon sc->rxon->flags |= htole32(IWN_RXON_SHPREAMBLE);
7254ffd7c74aSJoe Talbott if (IEEE80211_IS_CHAN_A(ni->ni_chan)) {
7255da10ea93SMatthew Dillon sc->rxon->cck_mask = 0;
7256da10ea93SMatthew Dillon sc->rxon->ofdm_mask = 0x15;
7257ffd7c74aSJoe Talbott } else if (IEEE80211_IS_CHAN_B(ni->ni_chan)) {
7258da10ea93SMatthew Dillon sc->rxon->cck_mask = 0x03;
7259da10ea93SMatthew Dillon sc->rxon->ofdm_mask = 0;
7260ffd7c74aSJoe Talbott } else {
7261da10ea93SMatthew Dillon /* Assume 802.11b/g. */
7262da10ea93SMatthew Dillon sc->rxon->cck_mask = 0x0f;
7263da10ea93SMatthew Dillon sc->rxon->ofdm_mask = 0x15;
7264ffd7c74aSJoe Talbott }
7265977fc0dbSMatthew Dillon /* try HT */
7266977fc0dbSMatthew Dillon sc->rxon->flags |= htole32(iwn_get_rxon_ht_flags(sc, ni->ni_chan));
7267da10ea93SMatthew Dillon sc->rxon->filter |= htole32(IWN_FILTER_BSS);
7268977fc0dbSMatthew Dillon DPRINTF(sc, IWN_DEBUG_STATE, "rxon chan %d flags %x, curhtprotmode=%d\n",
7269977fc0dbSMatthew Dillon sc->rxon->chan, le32toh(sc->rxon->flags), ic->ic_curhtprotmode);
72704b420e05SMatthew Dillon if (sc->sc_is_scanning)
72714b420e05SMatthew Dillon device_printf(sc->sc_dev,
72724b420e05SMatthew Dillon "%s: is_scanning set, before RXON\n",
72734b420e05SMatthew Dillon __func__);
7274da10ea93SMatthew Dillon error = iwn_cmd(sc, IWN_CMD_RXON, sc->rxon, sc->rxonsz, 1);
7275ffd7c74aSJoe Talbott if (error != 0) {
7276ffd7c74aSJoe Talbott device_printf(sc->sc_dev,
7277da10ea93SMatthew Dillon "%s: could not update configuration, error %d\n", __func__,
7278da10ea93SMatthew Dillon error);
7279ffd7c74aSJoe Talbott return error;
7280ffd7c74aSJoe Talbott }
7281ffd7c74aSJoe Talbott
7282ffd7c74aSJoe Talbott /* Configuration has changed, set TX power accordingly. */
7283da10ea93SMatthew Dillon if ((error = ops->set_txpower(sc, ni->ni_chan, 1)) != 0) {
7284ffd7c74aSJoe Talbott device_printf(sc->sc_dev,
7285da10ea93SMatthew Dillon "%s: could not set TX power, error %d\n", __func__, error);
7286ffd7c74aSJoe Talbott return error;
7287ffd7c74aSJoe Talbott }
7288ffd7c74aSJoe Talbott
7289da10ea93SMatthew Dillon /* Fake a join to initialize the TX rate. */
7290da10ea93SMatthew Dillon ((struct iwn_node *)ni)->id = IWN_ID_BSS;
7291da10ea93SMatthew Dillon iwn_newassoc(ni, 1);
7292da10ea93SMatthew Dillon
7293ffd7c74aSJoe Talbott /* Add BSS node. */
7294ffd7c74aSJoe Talbott memset(&node, 0, sizeof node);
7295ffd7c74aSJoe Talbott IEEE80211_ADDR_COPY(node.macaddr, ni->ni_macaddr);
7296ffd7c74aSJoe Talbott node.id = IWN_ID_BSS;
7297da10ea93SMatthew Dillon if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) {
7298da10ea93SMatthew Dillon switch (ni->ni_htcap & IEEE80211_HTCAP_SMPS) {
7299da10ea93SMatthew Dillon case IEEE80211_HTCAP_SMPS_ENA:
7300da10ea93SMatthew Dillon node.htflags |= htole32(IWN_SMPS_MIMO_DIS);
7301da10ea93SMatthew Dillon break;
7302da10ea93SMatthew Dillon case IEEE80211_HTCAP_SMPS_DYNAMIC:
7303da10ea93SMatthew Dillon node.htflags |= htole32(IWN_SMPS_MIMO_PROT);
7304da10ea93SMatthew Dillon break;
7305ffd7c74aSJoe Talbott }
7306da10ea93SMatthew Dillon node.htflags |= htole32(IWN_AMDPU_SIZE_FACTOR(3) |
7307da10ea93SMatthew Dillon IWN_AMDPU_DENSITY(5)); /* 4us */
7308da10ea93SMatthew Dillon if (IEEE80211_IS_CHAN_HT40(ni->ni_chan))
7309da10ea93SMatthew Dillon node.htflags |= htole32(IWN_NODE_HT40);
7310da10ea93SMatthew Dillon }
7311da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_STATE, "%s: adding BSS node\n", __func__);
7312da10ea93SMatthew Dillon error = ops->add_node(sc, &node, 1);
7313ffd7c74aSJoe Talbott if (error != 0) {
7314ffd7c74aSJoe Talbott device_printf(sc->sc_dev,
7315da10ea93SMatthew Dillon "%s: could not add BSS node, error %d\n", __func__, error);
7316da10ea93SMatthew Dillon return error;
7317da10ea93SMatthew Dillon }
7318da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_STATE, "%s: setting link quality for node %d\n",
7319da10ea93SMatthew Dillon __func__, node.id);
7320da10ea93SMatthew Dillon if ((error = iwn_set_link_quality(sc, ni)) != 0) {
7321da10ea93SMatthew Dillon device_printf(sc->sc_dev,
7322da10ea93SMatthew Dillon "%s: could not setup link quality for node %d, error %d\n",
7323ffd7c74aSJoe Talbott __func__, node.id, error);
7324ffd7c74aSJoe Talbott return error;
7325ffd7c74aSJoe Talbott }
7326ffd7c74aSJoe Talbott
7327da10ea93SMatthew Dillon if ((error = iwn_init_sensitivity(sc)) != 0) {
7328ffd7c74aSJoe Talbott device_printf(sc->sc_dev,
7329da10ea93SMatthew Dillon "%s: could not set sensitivity, error %d\n", __func__,
7330da10ea93SMatthew Dillon error);
7331ffd7c74aSJoe Talbott return error;
7332ffd7c74aSJoe Talbott }
7333ffd7c74aSJoe Talbott /* Start periodic calibration timer. */
7334ffd7c74aSJoe Talbott sc->calib.state = IWN_CALIB_STATE_ASSOC;
7335da10ea93SMatthew Dillon sc->calib_cnt = 0;
7336da10ea93SMatthew Dillon callout_reset(&sc->calib_to, msecs_to_ticks(500), iwn_calib_timeout,
7337da10ea93SMatthew Dillon sc);
7338ffd7c74aSJoe Talbott
7339ffd7c74aSJoe Talbott /* Link LED always on while associated. */
7340ffd7c74aSJoe Talbott iwn_set_led(sc, IWN_LED_LINK, 0, 1);
7341ffd7c74aSJoe Talbott
7342da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
7343da10ea93SMatthew Dillon
7344ffd7c74aSJoe Talbott return 0;
7345ffd7c74aSJoe Talbott }
7346ffd7c74aSJoe Talbott
7347ffd7c74aSJoe Talbott /*
7348ffd7c74aSJoe Talbott * This function is called by upper layer when an ADDBA request is received
7349ffd7c74aSJoe Talbott * from another STA and before the ADDBA response is sent.
7350ffd7c74aSJoe Talbott */
7351ffd7c74aSJoe Talbott static int
7352da10ea93SMatthew Dillon iwn_ampdu_rx_start(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap,
7353da10ea93SMatthew Dillon int baparamset, int batimeout, int baseqctl)
7354ffd7c74aSJoe Talbott {
7355da10ea93SMatthew Dillon #define MS(_v, _f) (((_v) & _f) >> _f##_S)
73564f1aaf2fSImre Vadász struct iwn_softc *sc = ni->ni_ic->ic_softc;
7357da10ea93SMatthew Dillon struct iwn_ops *ops = &sc->ops;
7358ffd7c74aSJoe Talbott struct iwn_node *wn = (void *)ni;
7359ffd7c74aSJoe Talbott struct iwn_node_info node;
7360da10ea93SMatthew Dillon uint16_t ssn;
7361da10ea93SMatthew Dillon uint8_t tid;
7362da10ea93SMatthew Dillon int error;
7363da10ea93SMatthew Dillon
7364da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
7365da10ea93SMatthew Dillon
7366da10ea93SMatthew Dillon tid = MS(le16toh(baparamset), IEEE80211_BAPS_TID);
7367da10ea93SMatthew Dillon ssn = MS(le16toh(baseqctl), IEEE80211_BASEQ_START);
7368ffd7c74aSJoe Talbott
7369ffd7c74aSJoe Talbott memset(&node, 0, sizeof node);
7370ffd7c74aSJoe Talbott node.id = wn->id;
7371ffd7c74aSJoe Talbott node.control = IWN_NODE_UPDATE;
7372ffd7c74aSJoe Talbott node.flags = IWN_FLAG_SET_ADDBA;
7373ffd7c74aSJoe Talbott node.addba_tid = tid;
7374da10ea93SMatthew Dillon node.addba_ssn = htole16(ssn);
7375ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_RECV, "ADDBA RA=%d TID=%d SSN=%d\n",
7376da10ea93SMatthew Dillon wn->id, tid, ssn);
7377da10ea93SMatthew Dillon error = ops->add_node(sc, &node, 1);
7378da10ea93SMatthew Dillon if (error != 0)
7379da10ea93SMatthew Dillon return error;
7380da10ea93SMatthew Dillon return sc->sc_ampdu_rx_start(ni, rap, baparamset, batimeout, baseqctl);
7381da10ea93SMatthew Dillon #undef MS
7382ffd7c74aSJoe Talbott }
7383ffd7c74aSJoe Talbott
7384ffd7c74aSJoe Talbott /*
7385ffd7c74aSJoe Talbott * This function is called by upper layer on teardown of an HT-immediate
7386da10ea93SMatthew Dillon * Block Ack agreement (eg. uppon receipt of a DELBA frame).
7387ffd7c74aSJoe Talbott */
7388ffd7c74aSJoe Talbott static void
7389da10ea93SMatthew Dillon iwn_ampdu_rx_stop(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap)
7390ffd7c74aSJoe Talbott {
7391da10ea93SMatthew Dillon struct ieee80211com *ic = ni->ni_ic;
73924f1aaf2fSImre Vadász struct iwn_softc *sc = ic->ic_softc;
7393da10ea93SMatthew Dillon struct iwn_ops *ops = &sc->ops;
7394ffd7c74aSJoe Talbott struct iwn_node *wn = (void *)ni;
7395ffd7c74aSJoe Talbott struct iwn_node_info node;
7396da10ea93SMatthew Dillon uint8_t tid;
7397da10ea93SMatthew Dillon
7398da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
7399da10ea93SMatthew Dillon
7400da10ea93SMatthew Dillon /* XXX: tid as an argument */
7401da10ea93SMatthew Dillon for (tid = 0; tid < WME_NUM_TID; tid++) {
7402da10ea93SMatthew Dillon if (&ni->ni_rx_ampdu[tid] == rap)
7403da10ea93SMatthew Dillon break;
7404da10ea93SMatthew Dillon }
7405ffd7c74aSJoe Talbott
7406ffd7c74aSJoe Talbott memset(&node, 0, sizeof node);
7407ffd7c74aSJoe Talbott node.id = wn->id;
7408ffd7c74aSJoe Talbott node.control = IWN_NODE_UPDATE;
7409ffd7c74aSJoe Talbott node.flags = IWN_FLAG_SET_DELBA;
7410ffd7c74aSJoe Talbott node.delba_tid = tid;
7411ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_RECV, "DELBA RA=%d TID=%d\n", wn->id, tid);
7412da10ea93SMatthew Dillon (void)ops->add_node(sc, &node, 1);
7413da10ea93SMatthew Dillon sc->sc_ampdu_rx_stop(ni, rap);
7414da10ea93SMatthew Dillon }
7415da10ea93SMatthew Dillon
7416da10ea93SMatthew Dillon static int
7417da10ea93SMatthew Dillon iwn_addba_request(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap,
7418da10ea93SMatthew Dillon int dialogtoken, int baparamset, int batimeout)
7419da10ea93SMatthew Dillon {
74204f1aaf2fSImre Vadász struct iwn_softc *sc = ni->ni_ic->ic_softc;
7421da10ea93SMatthew Dillon int qid;
7422da10ea93SMatthew Dillon
7423da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
7424da10ea93SMatthew Dillon
7425da10ea93SMatthew Dillon for (qid = sc->firstaggqueue; qid < sc->ntxqs; qid++) {
7426da10ea93SMatthew Dillon if (sc->qid2tap[qid] == NULL)
7427da10ea93SMatthew Dillon break;
7428da10ea93SMatthew Dillon }
7429da10ea93SMatthew Dillon if (qid == sc->ntxqs) {
7430da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_XMIT, "%s: not free aggregation queue\n",
7431da10ea93SMatthew Dillon __func__);
7432da10ea93SMatthew Dillon return 0;
7433da10ea93SMatthew Dillon }
7434101554a6SMatthew Dillon tap->txa_private = kmalloc(sizeof(int), M_DEVBUF, M_INTWAIT);
743505538f72SMatthew Dillon if (tap->txa_private == NULL) {
743605538f72SMatthew Dillon device_printf(sc->sc_dev,
743705538f72SMatthew Dillon "%s: failed to alloc TX aggregation structure\n", __func__);
743805538f72SMatthew Dillon return 0;
743905538f72SMatthew Dillon }
7440da10ea93SMatthew Dillon sc->qid2tap[qid] = tap;
7441da10ea93SMatthew Dillon *(int *)tap->txa_private = qid;
7442da10ea93SMatthew Dillon return sc->sc_addba_request(ni, tap, dialogtoken, baparamset,
7443da10ea93SMatthew Dillon batimeout);
7444da10ea93SMatthew Dillon }
7445da10ea93SMatthew Dillon
7446da10ea93SMatthew Dillon static int
7447da10ea93SMatthew Dillon iwn_addba_response(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap,
7448da10ea93SMatthew Dillon int code, int baparamset, int batimeout)
7449da10ea93SMatthew Dillon {
74504f1aaf2fSImre Vadász struct iwn_softc *sc = ni->ni_ic->ic_softc;
7451da10ea93SMatthew Dillon int qid = *(int *)tap->txa_private;
7452085ff963SMatthew Dillon uint8_t tid = tap->txa_tid;
7453da10ea93SMatthew Dillon int ret;
7454da10ea93SMatthew Dillon
7455da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
7456da10ea93SMatthew Dillon
7457da10ea93SMatthew Dillon if (code == IEEE80211_STATUS_SUCCESS) {
7458da10ea93SMatthew Dillon ni->ni_txseqs[tid] = tap->txa_start & 0xfff;
7459da10ea93SMatthew Dillon ret = iwn_ampdu_tx_start(ni->ni_ic, ni, tid);
7460da10ea93SMatthew Dillon if (ret != 1)
7461da10ea93SMatthew Dillon return ret;
7462da10ea93SMatthew Dillon } else {
7463da10ea93SMatthew Dillon sc->qid2tap[qid] = NULL;
7464101554a6SMatthew Dillon kfree(tap->txa_private, M_DEVBUF);
7465da10ea93SMatthew Dillon tap->txa_private = NULL;
7466da10ea93SMatthew Dillon }
7467da10ea93SMatthew Dillon return sc->sc_addba_response(ni, tap, code, baparamset, batimeout);
7468ffd7c74aSJoe Talbott }
7469ffd7c74aSJoe Talbott
7470ffd7c74aSJoe Talbott /*
7471ffd7c74aSJoe Talbott * This function is called by upper layer when an ADDBA response is received
7472ffd7c74aSJoe Talbott * from another STA.
7473ffd7c74aSJoe Talbott */
7474ffd7c74aSJoe Talbott static int
7475ffd7c74aSJoe Talbott iwn_ampdu_tx_start(struct ieee80211com *ic, struct ieee80211_node *ni,
7476ffd7c74aSJoe Talbott uint8_t tid)
7477ffd7c74aSJoe Talbott {
7478da10ea93SMatthew Dillon struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[tid];
74794f1aaf2fSImre Vadász struct iwn_softc *sc = ni->ni_ic->ic_softc;
7480da10ea93SMatthew Dillon struct iwn_ops *ops = &sc->ops;
7481ffd7c74aSJoe Talbott struct iwn_node *wn = (void *)ni;
7482ffd7c74aSJoe Talbott struct iwn_node_info node;
7483da10ea93SMatthew Dillon int error, qid;
7484da10ea93SMatthew Dillon
7485da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
7486ffd7c74aSJoe Talbott
7487ffd7c74aSJoe Talbott /* Enable TX for the specified RA/TID. */
7488ffd7c74aSJoe Talbott wn->disable_tid &= ~(1 << tid);
7489ffd7c74aSJoe Talbott memset(&node, 0, sizeof node);
7490ffd7c74aSJoe Talbott node.id = wn->id;
7491ffd7c74aSJoe Talbott node.control = IWN_NODE_UPDATE;
7492ffd7c74aSJoe Talbott node.flags = IWN_FLAG_SET_DISABLE_TID;
7493ffd7c74aSJoe Talbott node.disable_tid = htole16(wn->disable_tid);
7494da10ea93SMatthew Dillon error = ops->add_node(sc, &node, 1);
7495ffd7c74aSJoe Talbott if (error != 0)
7496da10ea93SMatthew Dillon return 0;
7497ffd7c74aSJoe Talbott
7498ffd7c74aSJoe Talbott if ((error = iwn_nic_lock(sc)) != 0)
7499ffd7c74aSJoe Talbott return 0;
7500da10ea93SMatthew Dillon qid = *(int *)tap->txa_private;
7501da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_XMIT, "%s: ra=%d tid=%d ssn=%d qid=%d\n",
7502da10ea93SMatthew Dillon __func__, wn->id, tid, tap->txa_start, qid);
7503da10ea93SMatthew Dillon ops->ampdu_tx_start(sc, ni, qid, tid, tap->txa_start & 0xfff);
7504da10ea93SMatthew Dillon iwn_nic_unlock(sc);
7505da10ea93SMatthew Dillon
7506da10ea93SMatthew Dillon iwn_set_link_quality(sc, ni);
7507da10ea93SMatthew Dillon return 1;
7508ffd7c74aSJoe Talbott }
7509ffd7c74aSJoe Talbott
7510ffd7c74aSJoe Talbott static void
7511da10ea93SMatthew Dillon iwn_ampdu_tx_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap)
7512ffd7c74aSJoe Talbott {
75134f1aaf2fSImre Vadász struct iwn_softc *sc = ni->ni_ic->ic_softc;
7514da10ea93SMatthew Dillon struct iwn_ops *ops = &sc->ops;
7515085ff963SMatthew Dillon uint8_t tid = tap->txa_tid;
7516da10ea93SMatthew Dillon int qid;
7517ffd7c74aSJoe Talbott
7518da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
7519da10ea93SMatthew Dillon
7520da10ea93SMatthew Dillon sc->sc_addba_stop(ni, tap);
7521da10ea93SMatthew Dillon
7522da10ea93SMatthew Dillon if (tap->txa_private == NULL)
7523ffd7c74aSJoe Talbott return;
7524da10ea93SMatthew Dillon
7525da10ea93SMatthew Dillon qid = *(int *)tap->txa_private;
7526da10ea93SMatthew Dillon if (sc->txq[qid].queued != 0)
7527da10ea93SMatthew Dillon return;
7528da10ea93SMatthew Dillon if (iwn_nic_lock(sc) != 0)
7529da10ea93SMatthew Dillon return;
7530da10ea93SMatthew Dillon ops->ampdu_tx_stop(sc, qid, tid, tap->txa_start & 0xfff);
7531ffd7c74aSJoe Talbott iwn_nic_unlock(sc);
7532da10ea93SMatthew Dillon sc->qid2tap[qid] = NULL;
7533101554a6SMatthew Dillon kfree(tap->txa_private, M_DEVBUF);
7534da10ea93SMatthew Dillon tap->txa_private = NULL;
7535ffd7c74aSJoe Talbott }
7536ffd7c74aSJoe Talbott
7537ffd7c74aSJoe Talbott static void
7538ffd7c74aSJoe Talbott iwn4965_ampdu_tx_start(struct iwn_softc *sc, struct ieee80211_node *ni,
7539da10ea93SMatthew Dillon int qid, uint8_t tid, uint16_t ssn)
7540ffd7c74aSJoe Talbott {
7541ffd7c74aSJoe Talbott struct iwn_node *wn = (void *)ni;
7542da10ea93SMatthew Dillon
7543da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
7544ffd7c74aSJoe Talbott
7545ffd7c74aSJoe Talbott /* Stop TX scheduler while we're changing its configuration. */
7546ffd7c74aSJoe Talbott iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid),
7547ffd7c74aSJoe Talbott IWN4965_TXQ_STATUS_CHGACT);
7548ffd7c74aSJoe Talbott
7549ffd7c74aSJoe Talbott /* Assign RA/TID translation to the queue. */
7550ffd7c74aSJoe Talbott iwn_mem_write_2(sc, sc->sched_base + IWN4965_SCHED_TRANS_TBL(qid),
7551ffd7c74aSJoe Talbott wn->id << 4 | tid);
7552ffd7c74aSJoe Talbott
7553ffd7c74aSJoe Talbott /* Enable chain-building mode for the queue. */
7554ffd7c74aSJoe Talbott iwn_prph_setbits(sc, IWN4965_SCHED_QCHAIN_SEL, 1 << qid);
7555ffd7c74aSJoe Talbott
7556ffd7c74aSJoe Talbott /* Set starting sequence number from the ADDBA request. */
7557da10ea93SMatthew Dillon sc->txq[qid].cur = sc->txq[qid].read = (ssn & 0xff);
7558ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff));
7559ffd7c74aSJoe Talbott iwn_prph_write(sc, IWN4965_SCHED_QUEUE_RDPTR(qid), ssn);
7560ffd7c74aSJoe Talbott
7561ffd7c74aSJoe Talbott /* Set scheduler window size. */
7562ffd7c74aSJoe Talbott iwn_mem_write(sc, sc->sched_base + IWN4965_SCHED_QUEUE_OFFSET(qid),
7563ffd7c74aSJoe Talbott IWN_SCHED_WINSZ);
7564ffd7c74aSJoe Talbott /* Set scheduler frame limit. */
7565ffd7c74aSJoe Talbott iwn_mem_write(sc, sc->sched_base + IWN4965_SCHED_QUEUE_OFFSET(qid) + 4,
7566ffd7c74aSJoe Talbott IWN_SCHED_LIMIT << 16);
7567ffd7c74aSJoe Talbott
7568ffd7c74aSJoe Talbott /* Enable interrupts for the queue. */
7569ffd7c74aSJoe Talbott iwn_prph_setbits(sc, IWN4965_SCHED_INTR_MASK, 1 << qid);
7570ffd7c74aSJoe Talbott
7571ffd7c74aSJoe Talbott /* Mark the queue as active. */
7572ffd7c74aSJoe Talbott iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid),
7573ffd7c74aSJoe Talbott IWN4965_TXQ_STATUS_ACTIVE | IWN4965_TXQ_STATUS_AGGR_ENA |
7574ffd7c74aSJoe Talbott iwn_tid2fifo[tid] << 1);
7575ffd7c74aSJoe Talbott }
7576ffd7c74aSJoe Talbott
7577ffd7c74aSJoe Talbott static void
7578da10ea93SMatthew Dillon iwn4965_ampdu_tx_stop(struct iwn_softc *sc, int qid, uint8_t tid, uint16_t ssn)
7579ffd7c74aSJoe Talbott {
7580da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
7581ffd7c74aSJoe Talbott
7582ffd7c74aSJoe Talbott /* Stop TX scheduler while we're changing its configuration. */
7583ffd7c74aSJoe Talbott iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid),
7584ffd7c74aSJoe Talbott IWN4965_TXQ_STATUS_CHGACT);
7585ffd7c74aSJoe Talbott
7586ffd7c74aSJoe Talbott /* Set starting sequence number from the ADDBA request. */
7587ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff));
7588ffd7c74aSJoe Talbott iwn_prph_write(sc, IWN4965_SCHED_QUEUE_RDPTR(qid), ssn);
7589ffd7c74aSJoe Talbott
7590ffd7c74aSJoe Talbott /* Disable interrupts for the queue. */
7591ffd7c74aSJoe Talbott iwn_prph_clrbits(sc, IWN4965_SCHED_INTR_MASK, 1 << qid);
7592ffd7c74aSJoe Talbott
7593ffd7c74aSJoe Talbott /* Mark the queue as inactive. */
7594ffd7c74aSJoe Talbott iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid),
7595ffd7c74aSJoe Talbott IWN4965_TXQ_STATUS_INACTIVE | iwn_tid2fifo[tid] << 1);
7596ffd7c74aSJoe Talbott }
7597ffd7c74aSJoe Talbott
7598ffd7c74aSJoe Talbott static void
7599ffd7c74aSJoe Talbott iwn5000_ampdu_tx_start(struct iwn_softc *sc, struct ieee80211_node *ni,
7600da10ea93SMatthew Dillon int qid, uint8_t tid, uint16_t ssn)
7601ffd7c74aSJoe Talbott {
7602da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
7603da10ea93SMatthew Dillon
7604ffd7c74aSJoe Talbott struct iwn_node *wn = (void *)ni;
7605ffd7c74aSJoe Talbott
7606ffd7c74aSJoe Talbott /* Stop TX scheduler while we're changing its configuration. */
7607ffd7c74aSJoe Talbott iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid),
7608ffd7c74aSJoe Talbott IWN5000_TXQ_STATUS_CHGACT);
7609ffd7c74aSJoe Talbott
7610ffd7c74aSJoe Talbott /* Assign RA/TID translation to the queue. */
7611ffd7c74aSJoe Talbott iwn_mem_write_2(sc, sc->sched_base + IWN5000_SCHED_TRANS_TBL(qid),
7612ffd7c74aSJoe Talbott wn->id << 4 | tid);
7613ffd7c74aSJoe Talbott
7614ffd7c74aSJoe Talbott /* Enable chain-building mode for the queue. */
7615ffd7c74aSJoe Talbott iwn_prph_setbits(sc, IWN5000_SCHED_QCHAIN_SEL, 1 << qid);
7616ffd7c74aSJoe Talbott
7617ffd7c74aSJoe Talbott /* Enable aggregation for the queue. */
7618ffd7c74aSJoe Talbott iwn_prph_setbits(sc, IWN5000_SCHED_AGGR_SEL, 1 << qid);
7619ffd7c74aSJoe Talbott
7620ffd7c74aSJoe Talbott /* Set starting sequence number from the ADDBA request. */
7621da10ea93SMatthew Dillon sc->txq[qid].cur = sc->txq[qid].read = (ssn & 0xff);
7622ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff));
7623ffd7c74aSJoe Talbott iwn_prph_write(sc, IWN5000_SCHED_QUEUE_RDPTR(qid), ssn);
7624ffd7c74aSJoe Talbott
7625ffd7c74aSJoe Talbott /* Set scheduler window size and frame limit. */
7626ffd7c74aSJoe Talbott iwn_mem_write(sc, sc->sched_base + IWN5000_SCHED_QUEUE_OFFSET(qid) + 4,
7627ffd7c74aSJoe Talbott IWN_SCHED_LIMIT << 16 | IWN_SCHED_WINSZ);
7628ffd7c74aSJoe Talbott
7629ffd7c74aSJoe Talbott /* Enable interrupts for the queue. */
7630ffd7c74aSJoe Talbott iwn_prph_setbits(sc, IWN5000_SCHED_INTR_MASK, 1 << qid);
7631ffd7c74aSJoe Talbott
7632ffd7c74aSJoe Talbott /* Mark the queue as active. */
7633ffd7c74aSJoe Talbott iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid),
7634ffd7c74aSJoe Talbott IWN5000_TXQ_STATUS_ACTIVE | iwn_tid2fifo[tid]);
7635ffd7c74aSJoe Talbott }
7636ffd7c74aSJoe Talbott
7637ffd7c74aSJoe Talbott static void
7638da10ea93SMatthew Dillon iwn5000_ampdu_tx_stop(struct iwn_softc *sc, int qid, uint8_t tid, uint16_t ssn)
7639ffd7c74aSJoe Talbott {
7640da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
7641ffd7c74aSJoe Talbott
7642ffd7c74aSJoe Talbott /* Stop TX scheduler while we're changing its configuration. */
7643ffd7c74aSJoe Talbott iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid),
7644ffd7c74aSJoe Talbott IWN5000_TXQ_STATUS_CHGACT);
7645ffd7c74aSJoe Talbott
7646ffd7c74aSJoe Talbott /* Disable aggregation for the queue. */
7647ffd7c74aSJoe Talbott iwn_prph_clrbits(sc, IWN5000_SCHED_AGGR_SEL, 1 << qid);
7648ffd7c74aSJoe Talbott
7649ffd7c74aSJoe Talbott /* Set starting sequence number from the ADDBA request. */
7650ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff));
7651ffd7c74aSJoe Talbott iwn_prph_write(sc, IWN5000_SCHED_QUEUE_RDPTR(qid), ssn);
7652ffd7c74aSJoe Talbott
7653ffd7c74aSJoe Talbott /* Disable interrupts for the queue. */
7654ffd7c74aSJoe Talbott iwn_prph_clrbits(sc, IWN5000_SCHED_INTR_MASK, 1 << qid);
7655ffd7c74aSJoe Talbott
7656ffd7c74aSJoe Talbott /* Mark the queue as inactive. */
7657ffd7c74aSJoe Talbott iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid),
7658ffd7c74aSJoe Talbott IWN5000_TXQ_STATUS_INACTIVE | iwn_tid2fifo[tid]);
7659ffd7c74aSJoe Talbott }
7660ffd7c74aSJoe Talbott
7661ffd7c74aSJoe Talbott /*
7662ffd7c74aSJoe Talbott * Query calibration tables from the initialization firmware. We do this
7663ffd7c74aSJoe Talbott * only once at first boot. Called from a process context.
7664ffd7c74aSJoe Talbott */
7665ffd7c74aSJoe Talbott static int
7666ffd7c74aSJoe Talbott iwn5000_query_calibration(struct iwn_softc *sc)
7667ffd7c74aSJoe Talbott {
7668ffd7c74aSJoe Talbott struct iwn5000_calib_config cmd;
7669ffd7c74aSJoe Talbott int error;
7670ffd7c74aSJoe Talbott
7671ffd7c74aSJoe Talbott memset(&cmd, 0, sizeof cmd);
7672fd49669cSMichael Neumann cmd.ucode.once.enable = htole32(0xffffffff);
7673fd49669cSMichael Neumann cmd.ucode.once.start = htole32(0xffffffff);
7674fd49669cSMichael Neumann cmd.ucode.once.send = htole32(0xffffffff);
7675fd49669cSMichael Neumann cmd.ucode.flags = htole32(0xffffffff);
7676ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: sending calibration query\n",
7677ffd7c74aSJoe Talbott __func__);
7678ffd7c74aSJoe Talbott error = iwn_cmd(sc, IWN5000_CMD_CALIB_CONFIG, &cmd, sizeof cmd, 0);
7679ffd7c74aSJoe Talbott if (error != 0)
7680ffd7c74aSJoe Talbott return error;
7681ffd7c74aSJoe Talbott
7682ffd7c74aSJoe Talbott /* Wait at most two seconds for calibration to complete. */
7683da10ea93SMatthew Dillon if (!(sc->sc_flags & IWN_FLAG_CALIB_DONE))
7684977fc0dbSMatthew Dillon #if defined(__DragonFly__)
7685977fc0dbSMatthew Dillon error = lksleep(sc, &sc->sc_lk, PCATCH, "iwncal", 2 * hz);
7686977fc0dbSMatthew Dillon #else
7687977fc0dbSMatthew Dillon error = msleep(sc, &sc->sc_mtx, PCATCH, "iwncal", 2 * hz);
7688977fc0dbSMatthew Dillon #endif
7689ffd7c74aSJoe Talbott return error;
7690ffd7c74aSJoe Talbott }
7691ffd7c74aSJoe Talbott
7692ffd7c74aSJoe Talbott /*
7693ffd7c74aSJoe Talbott * Send calibration results to the runtime firmware. These results were
7694ffd7c74aSJoe Talbott * obtained on first boot from the initialization firmware.
7695ffd7c74aSJoe Talbott */
7696ffd7c74aSJoe Talbott static int
7697ffd7c74aSJoe Talbott iwn5000_send_calibration(struct iwn_softc *sc)
7698ffd7c74aSJoe Talbott {
7699ffd7c74aSJoe Talbott int idx, error;
7700ffd7c74aSJoe Talbott
7701da10ea93SMatthew Dillon for (idx = 0; idx < IWN5000_PHY_CALIB_MAX_RESULT; idx++) {
7702da10ea93SMatthew Dillon if (!(sc->base_params->calib_need & (1<<idx))) {
7703ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_CALIBRATE,
7704da10ea93SMatthew Dillon "No need of calib %d\n",
7705da10ea93SMatthew Dillon idx);
7706da10ea93SMatthew Dillon continue; /* no need for this calib */
7707da10ea93SMatthew Dillon }
7708da10ea93SMatthew Dillon if (sc->calibcmd[idx].buf == NULL) {
7709da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_CALIBRATE,
7710da10ea93SMatthew Dillon "Need calib idx : %d but no available data\n",
7711da10ea93SMatthew Dillon idx);
7712da10ea93SMatthew Dillon continue;
7713da10ea93SMatthew Dillon }
7714da10ea93SMatthew Dillon
7715da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_CALIBRATE,
7716da10ea93SMatthew Dillon "send calibration result idx=%d len=%d\n", idx,
7717da10ea93SMatthew Dillon sc->calibcmd[idx].len);
7718ffd7c74aSJoe Talbott error = iwn_cmd(sc, IWN_CMD_PHY_CALIB, sc->calibcmd[idx].buf,
7719ffd7c74aSJoe Talbott sc->calibcmd[idx].len, 0);
7720ffd7c74aSJoe Talbott if (error != 0) {
7721ffd7c74aSJoe Talbott device_printf(sc->sc_dev,
7722ffd7c74aSJoe Talbott "%s: could not send calibration result, error %d\n",
7723ffd7c74aSJoe Talbott __func__, error);
7724ffd7c74aSJoe Talbott return error;
7725ffd7c74aSJoe Talbott }
7726ffd7c74aSJoe Talbott }
7727ffd7c74aSJoe Talbott return 0;
7728ffd7c74aSJoe Talbott }
7729ffd7c74aSJoe Talbott
7730ffd7c74aSJoe Talbott static int
7731ffd7c74aSJoe Talbott iwn5000_send_wimax_coex(struct iwn_softc *sc)
7732ffd7c74aSJoe Talbott {
7733ffd7c74aSJoe Talbott struct iwn5000_wimax_coex wimax;
7734ffd7c74aSJoe Talbott
7735fd49669cSMichael Neumann #if 0
7736ffd7c74aSJoe Talbott if (sc->hw_type == IWN_HW_REV_TYPE_6050) {
7737ffd7c74aSJoe Talbott /* Enable WiMAX coexistence for combo adapters. */
7738ffd7c74aSJoe Talbott wimax.flags =
7739ffd7c74aSJoe Talbott IWN_WIMAX_COEX_ASSOC_WA_UNMASK |
7740ffd7c74aSJoe Talbott IWN_WIMAX_COEX_UNASSOC_WA_UNMASK |
7741ffd7c74aSJoe Talbott IWN_WIMAX_COEX_STA_TABLE_VALID |
7742ffd7c74aSJoe Talbott IWN_WIMAX_COEX_ENABLE;
7743ffd7c74aSJoe Talbott memcpy(wimax.events, iwn6050_wimax_events,
7744ffd7c74aSJoe Talbott sizeof iwn6050_wimax_events);
7745ffd7c74aSJoe Talbott } else
7746ffd7c74aSJoe Talbott #endif
7747ffd7c74aSJoe Talbott {
7748ffd7c74aSJoe Talbott /* Disable WiMAX coexistence. */
7749ffd7c74aSJoe Talbott wimax.flags = 0;
7750ffd7c74aSJoe Talbott memset(wimax.events, 0, sizeof wimax.events);
7751ffd7c74aSJoe Talbott }
7752ffd7c74aSJoe Talbott DPRINTF(sc, IWN_DEBUG_RESET, "%s: Configuring WiMAX coexistence\n",
7753ffd7c74aSJoe Talbott __func__);
7754ffd7c74aSJoe Talbott return iwn_cmd(sc, IWN5000_CMD_WIMAX_COEX, &wimax, sizeof wimax, 0);
7755ffd7c74aSJoe Talbott }
7756ffd7c74aSJoe Talbott
7757da10ea93SMatthew Dillon static int
7758da10ea93SMatthew Dillon iwn5000_crystal_calib(struct iwn_softc *sc)
7759da10ea93SMatthew Dillon {
7760da10ea93SMatthew Dillon struct iwn5000_phy_calib_crystal cmd;
7761da10ea93SMatthew Dillon
7762da10ea93SMatthew Dillon memset(&cmd, 0, sizeof cmd);
7763da10ea93SMatthew Dillon cmd.code = IWN5000_PHY_CALIB_CRYSTAL;
7764da10ea93SMatthew Dillon cmd.ngroups = 1;
7765da10ea93SMatthew Dillon cmd.isvalid = 1;
7766da10ea93SMatthew Dillon cmd.cap_pin[0] = le32toh(sc->eeprom_crystal) & 0xff;
7767da10ea93SMatthew Dillon cmd.cap_pin[1] = (le32toh(sc->eeprom_crystal) >> 16) & 0xff;
7768da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_CALIBRATE, "sending crystal calibration %d, %d\n",
7769da10ea93SMatthew Dillon cmd.cap_pin[0], cmd.cap_pin[1]);
7770da10ea93SMatthew Dillon return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 0);
7771da10ea93SMatthew Dillon }
7772da10ea93SMatthew Dillon
7773da10ea93SMatthew Dillon static int
7774da10ea93SMatthew Dillon iwn5000_temp_offset_calib(struct iwn_softc *sc)
7775da10ea93SMatthew Dillon {
7776da10ea93SMatthew Dillon struct iwn5000_phy_calib_temp_offset cmd;
7777da10ea93SMatthew Dillon
7778da10ea93SMatthew Dillon memset(&cmd, 0, sizeof cmd);
7779da10ea93SMatthew Dillon cmd.code = IWN5000_PHY_CALIB_TEMP_OFFSET;
7780da10ea93SMatthew Dillon cmd.ngroups = 1;
7781da10ea93SMatthew Dillon cmd.isvalid = 1;
7782da10ea93SMatthew Dillon if (sc->eeprom_temp != 0)
7783da10ea93SMatthew Dillon cmd.offset = htole16(sc->eeprom_temp);
7784da10ea93SMatthew Dillon else
7785da10ea93SMatthew Dillon cmd.offset = htole16(IWN_DEFAULT_TEMP_OFFSET);
7786da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_CALIBRATE, "setting radio sensor offset to %d\n",
7787da10ea93SMatthew Dillon le16toh(cmd.offset));
7788da10ea93SMatthew Dillon return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 0);
7789da10ea93SMatthew Dillon }
7790da10ea93SMatthew Dillon
7791da10ea93SMatthew Dillon static int
7792da10ea93SMatthew Dillon iwn5000_temp_offset_calibv2(struct iwn_softc *sc)
7793da10ea93SMatthew Dillon {
7794da10ea93SMatthew Dillon struct iwn5000_phy_calib_temp_offsetv2 cmd;
7795da10ea93SMatthew Dillon
7796da10ea93SMatthew Dillon memset(&cmd, 0, sizeof cmd);
7797da10ea93SMatthew Dillon cmd.code = IWN5000_PHY_CALIB_TEMP_OFFSET;
7798da10ea93SMatthew Dillon cmd.ngroups = 1;
7799da10ea93SMatthew Dillon cmd.isvalid = 1;
7800da10ea93SMatthew Dillon if (sc->eeprom_temp != 0) {
7801da10ea93SMatthew Dillon cmd.offset_low = htole16(sc->eeprom_temp);
7802da10ea93SMatthew Dillon cmd.offset_high = htole16(sc->eeprom_temp_high);
7803da10ea93SMatthew Dillon } else {
7804da10ea93SMatthew Dillon cmd.offset_low = htole16(IWN_DEFAULT_TEMP_OFFSET);
7805da10ea93SMatthew Dillon cmd.offset_high = htole16(IWN_DEFAULT_TEMP_OFFSET);
7806da10ea93SMatthew Dillon }
7807da10ea93SMatthew Dillon cmd.burnt_voltage_ref = htole16(sc->eeprom_voltage);
7808da10ea93SMatthew Dillon
7809da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_CALIBRATE,
7810da10ea93SMatthew Dillon "setting radio sensor low offset to %d, high offset to %d, voltage to %d\n",
7811da10ea93SMatthew Dillon le16toh(cmd.offset_low),
7812da10ea93SMatthew Dillon le16toh(cmd.offset_high),
7813da10ea93SMatthew Dillon le16toh(cmd.burnt_voltage_ref));
7814da10ea93SMatthew Dillon
7815da10ea93SMatthew Dillon return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 0);
7816da10ea93SMatthew Dillon }
7817da10ea93SMatthew Dillon
7818ffd7c74aSJoe Talbott /*
7819ffd7c74aSJoe Talbott * This function is called after the runtime firmware notifies us of its
7820da10ea93SMatthew Dillon * readiness (called in a process context).
7821ffd7c74aSJoe Talbott */
7822ffd7c74aSJoe Talbott static int
7823ffd7c74aSJoe Talbott iwn4965_post_alive(struct iwn_softc *sc)
7824ffd7c74aSJoe Talbott {
7825ffd7c74aSJoe Talbott int error, qid;
7826ffd7c74aSJoe Talbott
7827ffd7c74aSJoe Talbott if ((error = iwn_nic_lock(sc)) != 0)
7828ffd7c74aSJoe Talbott return error;
7829ffd7c74aSJoe Talbott
7830da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
7831da10ea93SMatthew Dillon
7832ffd7c74aSJoe Talbott /* Clear TX scheduler state in SRAM. */
7833ffd7c74aSJoe Talbott sc->sched_base = iwn_prph_read(sc, IWN_SCHED_SRAM_ADDR);
7834ffd7c74aSJoe Talbott iwn_mem_set_region_4(sc, sc->sched_base + IWN4965_SCHED_CTX_OFF, 0,
7835ffd7c74aSJoe Talbott IWN4965_SCHED_CTX_LEN / sizeof (uint32_t));
7836ffd7c74aSJoe Talbott
7837da10ea93SMatthew Dillon /* Set physical address of TX scheduler rings (1KB aligned). */
7838ffd7c74aSJoe Talbott iwn_prph_write(sc, IWN4965_SCHED_DRAM_ADDR, sc->sched_dma.paddr >> 10);
7839ffd7c74aSJoe Talbott
7840ffd7c74aSJoe Talbott IWN_SETBITS(sc, IWN_FH_TX_CHICKEN, IWN_FH_TX_CHICKEN_SCHED_RETRY);
7841ffd7c74aSJoe Talbott
7842ffd7c74aSJoe Talbott /* Disable chain mode for all our 16 queues. */
7843ffd7c74aSJoe Talbott iwn_prph_write(sc, IWN4965_SCHED_QCHAIN_SEL, 0);
7844ffd7c74aSJoe Talbott
7845ffd7c74aSJoe Talbott for (qid = 0; qid < IWN4965_NTXQUEUES; qid++) {
7846ffd7c74aSJoe Talbott iwn_prph_write(sc, IWN4965_SCHED_QUEUE_RDPTR(qid), 0);
7847ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | 0);
7848ffd7c74aSJoe Talbott
7849ffd7c74aSJoe Talbott /* Set scheduler window size. */
7850ffd7c74aSJoe Talbott iwn_mem_write(sc, sc->sched_base +
7851ffd7c74aSJoe Talbott IWN4965_SCHED_QUEUE_OFFSET(qid), IWN_SCHED_WINSZ);
7852ffd7c74aSJoe Talbott /* Set scheduler frame limit. */
7853ffd7c74aSJoe Talbott iwn_mem_write(sc, sc->sched_base +
7854ffd7c74aSJoe Talbott IWN4965_SCHED_QUEUE_OFFSET(qid) + 4,
7855ffd7c74aSJoe Talbott IWN_SCHED_LIMIT << 16);
7856ffd7c74aSJoe Talbott }
7857ffd7c74aSJoe Talbott
7858ffd7c74aSJoe Talbott /* Enable interrupts for all our 16 queues. */
7859ffd7c74aSJoe Talbott iwn_prph_write(sc, IWN4965_SCHED_INTR_MASK, 0xffff);
7860ffd7c74aSJoe Talbott /* Identify TX FIFO rings (0-7). */
7861ffd7c74aSJoe Talbott iwn_prph_write(sc, IWN4965_SCHED_TXFACT, 0xff);
7862ffd7c74aSJoe Talbott
7863ffd7c74aSJoe Talbott /* Mark TX rings (4 EDCA + cmd + 2 HCCA) as active. */
7864ffd7c74aSJoe Talbott for (qid = 0; qid < 7; qid++) {
7865ffd7c74aSJoe Talbott static uint8_t qid2fifo[] = { 3, 2, 1, 0, 4, 5, 6 };
7866ffd7c74aSJoe Talbott iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid),
7867ffd7c74aSJoe Talbott IWN4965_TXQ_STATUS_ACTIVE | qid2fifo[qid] << 1);
7868ffd7c74aSJoe Talbott }
7869ffd7c74aSJoe Talbott iwn_nic_unlock(sc);
7870ffd7c74aSJoe Talbott return 0;
7871ffd7c74aSJoe Talbott }
7872ffd7c74aSJoe Talbott
7873ffd7c74aSJoe Talbott /*
7874ffd7c74aSJoe Talbott * This function is called after the initialization or runtime firmware
7875da10ea93SMatthew Dillon * notifies us of its readiness (called in a process context).
7876ffd7c74aSJoe Talbott */
7877ffd7c74aSJoe Talbott static int
7878ffd7c74aSJoe Talbott iwn5000_post_alive(struct iwn_softc *sc)
7879ffd7c74aSJoe Talbott {
7880ffd7c74aSJoe Talbott int error, qid;
7881ffd7c74aSJoe Talbott
7882da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
7883da10ea93SMatthew Dillon
7884ffd7c74aSJoe Talbott /* Switch to using ICT interrupt mode. */
7885ffd7c74aSJoe Talbott iwn5000_ict_reset(sc);
7886ffd7c74aSJoe Talbott
7887da10ea93SMatthew Dillon if ((error = iwn_nic_lock(sc)) != 0){
7888da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s end in error\n", __func__);
7889ffd7c74aSJoe Talbott return error;
7890da10ea93SMatthew Dillon }
7891ffd7c74aSJoe Talbott
7892ffd7c74aSJoe Talbott /* Clear TX scheduler state in SRAM. */
7893ffd7c74aSJoe Talbott sc->sched_base = iwn_prph_read(sc, IWN_SCHED_SRAM_ADDR);
7894ffd7c74aSJoe Talbott iwn_mem_set_region_4(sc, sc->sched_base + IWN5000_SCHED_CTX_OFF, 0,
7895ffd7c74aSJoe Talbott IWN5000_SCHED_CTX_LEN / sizeof (uint32_t));
7896ffd7c74aSJoe Talbott
7897da10ea93SMatthew Dillon /* Set physical address of TX scheduler rings (1KB aligned). */
7898ffd7c74aSJoe Talbott iwn_prph_write(sc, IWN5000_SCHED_DRAM_ADDR, sc->sched_dma.paddr >> 10);
7899ffd7c74aSJoe Talbott
7900ffd7c74aSJoe Talbott IWN_SETBITS(sc, IWN_FH_TX_CHICKEN, IWN_FH_TX_CHICKEN_SCHED_RETRY);
7901ffd7c74aSJoe Talbott
7902ffd7c74aSJoe Talbott /* Enable chain mode for all queues, except command queue. */
7903da10ea93SMatthew Dillon if (sc->sc_flags & IWN_FLAG_PAN_SUPPORT)
7904da10ea93SMatthew Dillon iwn_prph_write(sc, IWN5000_SCHED_QCHAIN_SEL, 0xfffdf);
7905da10ea93SMatthew Dillon else
7906ffd7c74aSJoe Talbott iwn_prph_write(sc, IWN5000_SCHED_QCHAIN_SEL, 0xfffef);
7907ffd7c74aSJoe Talbott iwn_prph_write(sc, IWN5000_SCHED_AGGR_SEL, 0);
7908ffd7c74aSJoe Talbott
7909ffd7c74aSJoe Talbott for (qid = 0; qid < IWN5000_NTXQUEUES; qid++) {
7910ffd7c74aSJoe Talbott iwn_prph_write(sc, IWN5000_SCHED_QUEUE_RDPTR(qid), 0);
7911ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | 0);
7912ffd7c74aSJoe Talbott
7913ffd7c74aSJoe Talbott iwn_mem_write(sc, sc->sched_base +
7914ffd7c74aSJoe Talbott IWN5000_SCHED_QUEUE_OFFSET(qid), 0);
7915ffd7c74aSJoe Talbott /* Set scheduler window size and frame limit. */
7916ffd7c74aSJoe Talbott iwn_mem_write(sc, sc->sched_base +
7917ffd7c74aSJoe Talbott IWN5000_SCHED_QUEUE_OFFSET(qid) + 4,
7918ffd7c74aSJoe Talbott IWN_SCHED_LIMIT << 16 | IWN_SCHED_WINSZ);
7919ffd7c74aSJoe Talbott }
7920ffd7c74aSJoe Talbott
7921ffd7c74aSJoe Talbott /* Enable interrupts for all our 20 queues. */
7922ffd7c74aSJoe Talbott iwn_prph_write(sc, IWN5000_SCHED_INTR_MASK, 0xfffff);
7923ffd7c74aSJoe Talbott /* Identify TX FIFO rings (0-7). */
7924ffd7c74aSJoe Talbott iwn_prph_write(sc, IWN5000_SCHED_TXFACT, 0xff);
7925ffd7c74aSJoe Talbott
7926ffd7c74aSJoe Talbott /* Mark TX rings (4 EDCA + cmd + 2 HCCA) as active. */
7927da10ea93SMatthew Dillon if (sc->sc_flags & IWN_FLAG_PAN_SUPPORT) {
7928da10ea93SMatthew Dillon /* Mark TX rings as active. */
7929da10ea93SMatthew Dillon for (qid = 0; qid < 11; qid++) {
7930da10ea93SMatthew Dillon static uint8_t qid2fifo[] = { 3, 2, 1, 0, 0, 4, 2, 5, 4, 7, 5 };
7931da10ea93SMatthew Dillon iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid),
7932da10ea93SMatthew Dillon IWN5000_TXQ_STATUS_ACTIVE | qid2fifo[qid]);
7933da10ea93SMatthew Dillon }
7934da10ea93SMatthew Dillon } else {
7935da10ea93SMatthew Dillon /* Mark TX rings (4 EDCA + cmd + 2 HCCA) as active. */
7936ffd7c74aSJoe Talbott for (qid = 0; qid < 7; qid++) {
7937ffd7c74aSJoe Talbott static uint8_t qid2fifo[] = { 3, 2, 1, 0, 7, 5, 6 };
7938ffd7c74aSJoe Talbott iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid),
7939ffd7c74aSJoe Talbott IWN5000_TXQ_STATUS_ACTIVE | qid2fifo[qid]);
7940ffd7c74aSJoe Talbott }
7941da10ea93SMatthew Dillon }
7942ffd7c74aSJoe Talbott iwn_nic_unlock(sc);
7943ffd7c74aSJoe Talbott
7944ffd7c74aSJoe Talbott /* Configure WiMAX coexistence for combo adapters. */
7945ffd7c74aSJoe Talbott error = iwn5000_send_wimax_coex(sc);
7946ffd7c74aSJoe Talbott if (error != 0) {
7947ffd7c74aSJoe Talbott device_printf(sc->sc_dev,
7948ffd7c74aSJoe Talbott "%s: could not configure WiMAX coexistence, error %d\n",
7949ffd7c74aSJoe Talbott __func__, error);
7950ffd7c74aSJoe Talbott return error;
7951ffd7c74aSJoe Talbott }
7952ffd7c74aSJoe Talbott if (sc->hw_type != IWN_HW_REV_TYPE_5150) {
7953ffd7c74aSJoe Talbott /* Perform crystal calibration. */
7954da10ea93SMatthew Dillon error = iwn5000_crystal_calib(sc);
7955ffd7c74aSJoe Talbott if (error != 0) {
7956ffd7c74aSJoe Talbott device_printf(sc->sc_dev,
7957ffd7c74aSJoe Talbott "%s: crystal calibration failed, error %d\n",
7958ffd7c74aSJoe Talbott __func__, error);
7959ffd7c74aSJoe Talbott return error;
7960ffd7c74aSJoe Talbott }
7961ffd7c74aSJoe Talbott }
7962ffd7c74aSJoe Talbott if (!(sc->sc_flags & IWN_FLAG_CALIB_DONE)) {
7963ffd7c74aSJoe Talbott /* Query calibration from the initialization firmware. */
7964da10ea93SMatthew Dillon if ((error = iwn5000_query_calibration(sc)) != 0) {
7965ffd7c74aSJoe Talbott device_printf(sc->sc_dev,
7966ffd7c74aSJoe Talbott "%s: could not query calibration, error %d\n",
7967ffd7c74aSJoe Talbott __func__, error);
7968ffd7c74aSJoe Talbott return error;
7969ffd7c74aSJoe Talbott }
7970ffd7c74aSJoe Talbott /*
7971ffd7c74aSJoe Talbott * We have the calibration results now, reboot with the
7972ffd7c74aSJoe Talbott * runtime firmware (call ourselves recursively!)
7973ffd7c74aSJoe Talbott */
7974ffd7c74aSJoe Talbott iwn_hw_stop(sc);
7975ffd7c74aSJoe Talbott error = iwn_hw_init(sc);
7976ffd7c74aSJoe Talbott } else {
7977ffd7c74aSJoe Talbott /* Send calibration results to runtime firmware. */
7978ffd7c74aSJoe Talbott error = iwn5000_send_calibration(sc);
7979ffd7c74aSJoe Talbott }
7980da10ea93SMatthew Dillon
7981da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
7982da10ea93SMatthew Dillon
7983ffd7c74aSJoe Talbott return error;
7984ffd7c74aSJoe Talbott }
7985ffd7c74aSJoe Talbott
7986ffd7c74aSJoe Talbott /*
7987ffd7c74aSJoe Talbott * The firmware boot code is small and is intended to be copied directly into
7988da10ea93SMatthew Dillon * the NIC internal memory (no DMA transfer).
7989ffd7c74aSJoe Talbott */
7990ffd7c74aSJoe Talbott static int
7991ffd7c74aSJoe Talbott iwn4965_load_bootcode(struct iwn_softc *sc, const uint8_t *ucode, int size)
7992ffd7c74aSJoe Talbott {
7993ffd7c74aSJoe Talbott int error, ntries;
7994ffd7c74aSJoe Talbott
7995ffd7c74aSJoe Talbott size /= sizeof (uint32_t);
7996ffd7c74aSJoe Talbott
7997da10ea93SMatthew Dillon if ((error = iwn_nic_lock(sc)) != 0)
7998ffd7c74aSJoe Talbott return error;
7999ffd7c74aSJoe Talbott
8000ffd7c74aSJoe Talbott /* Copy microcode image into NIC memory. */
8001ffd7c74aSJoe Talbott iwn_prph_write_region_4(sc, IWN_BSM_SRAM_BASE,
8002ffd7c74aSJoe Talbott (const uint32_t *)ucode, size);
8003ffd7c74aSJoe Talbott
8004ffd7c74aSJoe Talbott iwn_prph_write(sc, IWN_BSM_WR_MEM_SRC, 0);
8005ffd7c74aSJoe Talbott iwn_prph_write(sc, IWN_BSM_WR_MEM_DST, IWN_FW_TEXT_BASE);
8006ffd7c74aSJoe Talbott iwn_prph_write(sc, IWN_BSM_WR_DWCOUNT, size);
8007ffd7c74aSJoe Talbott
8008ffd7c74aSJoe Talbott /* Start boot load now. */
8009ffd7c74aSJoe Talbott iwn_prph_write(sc, IWN_BSM_WR_CTRL, IWN_BSM_WR_CTRL_START);
8010ffd7c74aSJoe Talbott
8011ffd7c74aSJoe Talbott /* Wait for transfer to complete. */
8012ffd7c74aSJoe Talbott for (ntries = 0; ntries < 1000; ntries++) {
8013ffd7c74aSJoe Talbott if (!(iwn_prph_read(sc, IWN_BSM_WR_CTRL) &
8014ffd7c74aSJoe Talbott IWN_BSM_WR_CTRL_START))
8015ffd7c74aSJoe Talbott break;
8016ffd7c74aSJoe Talbott DELAY(10);
8017ffd7c74aSJoe Talbott }
8018ffd7c74aSJoe Talbott if (ntries == 1000) {
8019ffd7c74aSJoe Talbott device_printf(sc->sc_dev, "%s: could not load boot firmware\n",
8020ffd7c74aSJoe Talbott __func__);
8021ffd7c74aSJoe Talbott iwn_nic_unlock(sc);
8022ffd7c74aSJoe Talbott return ETIMEDOUT;
8023ffd7c74aSJoe Talbott }
8024ffd7c74aSJoe Talbott
8025ffd7c74aSJoe Talbott /* Enable boot after power up. */
8026ffd7c74aSJoe Talbott iwn_prph_write(sc, IWN_BSM_WR_CTRL, IWN_BSM_WR_CTRL_START_EN);
8027ffd7c74aSJoe Talbott
8028ffd7c74aSJoe Talbott iwn_nic_unlock(sc);
8029ffd7c74aSJoe Talbott return 0;
8030ffd7c74aSJoe Talbott }
8031ffd7c74aSJoe Talbott
8032ffd7c74aSJoe Talbott static int
8033ffd7c74aSJoe Talbott iwn4965_load_firmware(struct iwn_softc *sc)
8034ffd7c74aSJoe Talbott {
8035ffd7c74aSJoe Talbott struct iwn_fw_info *fw = &sc->fw;
8036ffd7c74aSJoe Talbott struct iwn_dma_info *dma = &sc->fw_dma;
8037ffd7c74aSJoe Talbott int error;
8038ffd7c74aSJoe Talbott
8039ffd7c74aSJoe Talbott /* Copy initialization sections into pre-allocated DMA-safe memory. */
8040ffd7c74aSJoe Talbott memcpy(dma->vaddr, fw->init.data, fw->init.datasz);
8041da10ea93SMatthew Dillon bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
8042ffd7c74aSJoe Talbott memcpy(dma->vaddr + IWN4965_FW_DATA_MAXSZ,
8043ffd7c74aSJoe Talbott fw->init.text, fw->init.textsz);
8044da10ea93SMatthew Dillon bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
8045ffd7c74aSJoe Talbott
8046ffd7c74aSJoe Talbott /* Tell adapter where to find initialization sections. */
8047da10ea93SMatthew Dillon if ((error = iwn_nic_lock(sc)) != 0)
8048ffd7c74aSJoe Talbott return error;
8049ffd7c74aSJoe Talbott iwn_prph_write(sc, IWN_BSM_DRAM_DATA_ADDR, dma->paddr >> 4);
8050ffd7c74aSJoe Talbott iwn_prph_write(sc, IWN_BSM_DRAM_DATA_SIZE, fw->init.datasz);
8051ffd7c74aSJoe Talbott iwn_prph_write(sc, IWN_BSM_DRAM_TEXT_ADDR,
8052ffd7c74aSJoe Talbott (dma->paddr + IWN4965_FW_DATA_MAXSZ) >> 4);
8053ffd7c74aSJoe Talbott iwn_prph_write(sc, IWN_BSM_DRAM_TEXT_SIZE, fw->init.textsz);
8054ffd7c74aSJoe Talbott iwn_nic_unlock(sc);
8055ffd7c74aSJoe Talbott
8056ffd7c74aSJoe Talbott /* Load firmware boot code. */
8057ffd7c74aSJoe Talbott error = iwn4965_load_bootcode(sc, fw->boot.text, fw->boot.textsz);
8058ffd7c74aSJoe Talbott if (error != 0) {
8059ffd7c74aSJoe Talbott device_printf(sc->sc_dev, "%s: could not load boot firmware\n",
8060ffd7c74aSJoe Talbott __func__);
8061ffd7c74aSJoe Talbott return error;
8062ffd7c74aSJoe Talbott }
8063ffd7c74aSJoe Talbott /* Now press "execute". */
8064ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_RESET, 0);
8065ffd7c74aSJoe Talbott
8066ffd7c74aSJoe Talbott /* Wait at most one second for first alive notification. */
8067977fc0dbSMatthew Dillon #if defined(__DragonFly__)
8068977fc0dbSMatthew Dillon if ((error = lksleep(sc, &sc->sc_lk, PCATCH, "iwninit", hz)) != 0) {
8069977fc0dbSMatthew Dillon #else
8070977fc0dbSMatthew Dillon if ((error = msleep(sc, &sc->sc_mtx, PCATCH, "iwninit", hz)) != 0) {
8071977fc0dbSMatthew Dillon #endif
8072ffd7c74aSJoe Talbott device_printf(sc->sc_dev,
8073ffd7c74aSJoe Talbott "%s: timeout waiting for adapter to initialize, error %d\n",
8074ffd7c74aSJoe Talbott __func__, error);
8075ffd7c74aSJoe Talbott return error;
8076ffd7c74aSJoe Talbott }
8077ffd7c74aSJoe Talbott
8078ffd7c74aSJoe Talbott /* Retrieve current temperature for initial TX power calibration. */
8079ffd7c74aSJoe Talbott sc->rawtemp = sc->ucode_info.temp[3].chan20MHz;
8080ffd7c74aSJoe Talbott sc->temp = iwn4965_get_temperature(sc);
8081ffd7c74aSJoe Talbott
8082ffd7c74aSJoe Talbott /* Copy runtime sections into pre-allocated DMA-safe memory. */
8083ffd7c74aSJoe Talbott memcpy(dma->vaddr, fw->main.data, fw->main.datasz);
8084da10ea93SMatthew Dillon bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
8085ffd7c74aSJoe Talbott memcpy(dma->vaddr + IWN4965_FW_DATA_MAXSZ,
8086ffd7c74aSJoe Talbott fw->main.text, fw->main.textsz);
8087da10ea93SMatthew Dillon bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
8088ffd7c74aSJoe Talbott
8089ffd7c74aSJoe Talbott /* Tell adapter where to find runtime sections. */
8090da10ea93SMatthew Dillon if ((error = iwn_nic_lock(sc)) != 0)
8091ffd7c74aSJoe Talbott return error;
8092ffd7c74aSJoe Talbott iwn_prph_write(sc, IWN_BSM_DRAM_DATA_ADDR, dma->paddr >> 4);
8093ffd7c74aSJoe Talbott iwn_prph_write(sc, IWN_BSM_DRAM_DATA_SIZE, fw->main.datasz);
8094ffd7c74aSJoe Talbott iwn_prph_write(sc, IWN_BSM_DRAM_TEXT_ADDR,
8095ffd7c74aSJoe Talbott (dma->paddr + IWN4965_FW_DATA_MAXSZ) >> 4);
8096ffd7c74aSJoe Talbott iwn_prph_write(sc, IWN_BSM_DRAM_TEXT_SIZE,
8097ffd7c74aSJoe Talbott IWN_FW_UPDATED | fw->main.textsz);
8098ffd7c74aSJoe Talbott iwn_nic_unlock(sc);
8099ffd7c74aSJoe Talbott
8100ffd7c74aSJoe Talbott return 0;
8101ffd7c74aSJoe Talbott }
8102ffd7c74aSJoe Talbott
8103ffd7c74aSJoe Talbott static int
8104ffd7c74aSJoe Talbott iwn5000_load_firmware_section(struct iwn_softc *sc, uint32_t dst,
8105ffd7c74aSJoe Talbott const uint8_t *section, int size)
8106ffd7c74aSJoe Talbott {
8107ffd7c74aSJoe Talbott struct iwn_dma_info *dma = &sc->fw_dma;
8108ffd7c74aSJoe Talbott int error;
8109ffd7c74aSJoe Talbott
8110da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
8111da10ea93SMatthew Dillon
8112ffd7c74aSJoe Talbott /* Copy firmware section into pre-allocated DMA-safe memory. */
8113ffd7c74aSJoe Talbott memcpy(dma->vaddr, section, size);
8114da10ea93SMatthew Dillon bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
8115ffd7c74aSJoe Talbott
8116da10ea93SMatthew Dillon if ((error = iwn_nic_lock(sc)) != 0)
8117ffd7c74aSJoe Talbott return error;
8118ffd7c74aSJoe Talbott
8119ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_FH_TX_CONFIG(IWN_SRVC_DMACHNL),
8120ffd7c74aSJoe Talbott IWN_FH_TX_CONFIG_DMA_PAUSE);
8121ffd7c74aSJoe Talbott
8122ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_FH_SRAM_ADDR(IWN_SRVC_DMACHNL), dst);
8123ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_FH_TFBD_CTRL0(IWN_SRVC_DMACHNL),
8124ffd7c74aSJoe Talbott IWN_LOADDR(dma->paddr));
8125ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_FH_TFBD_CTRL1(IWN_SRVC_DMACHNL),
8126ffd7c74aSJoe Talbott IWN_HIADDR(dma->paddr) << 28 | size);
8127ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_FH_TXBUF_STATUS(IWN_SRVC_DMACHNL),
8128ffd7c74aSJoe Talbott IWN_FH_TXBUF_STATUS_TBNUM(1) |
8129ffd7c74aSJoe Talbott IWN_FH_TXBUF_STATUS_TBIDX(1) |
8130ffd7c74aSJoe Talbott IWN_FH_TXBUF_STATUS_TFBD_VALID);
8131ffd7c74aSJoe Talbott
8132ffd7c74aSJoe Talbott /* Kick Flow Handler to start DMA transfer. */
8133ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_FH_TX_CONFIG(IWN_SRVC_DMACHNL),
8134ffd7c74aSJoe Talbott IWN_FH_TX_CONFIG_DMA_ENA | IWN_FH_TX_CONFIG_CIRQ_HOST_ENDTFD);
8135ffd7c74aSJoe Talbott
8136ffd7c74aSJoe Talbott iwn_nic_unlock(sc);
8137ffd7c74aSJoe Talbott
8138da10ea93SMatthew Dillon /* Wait at most five seconds for FH DMA transfer to complete. */
8139977fc0dbSMatthew Dillon #if defined(__DragonFly__)
8140977fc0dbSMatthew Dillon return lksleep(sc, &sc->sc_lk, PCATCH, "iwninit", 5 * hz);
8141977fc0dbSMatthew Dillon #else
8142977fc0dbSMatthew Dillon return msleep(sc, &sc->sc_mtx, PCATCH, "iwninit", 5 * hz);
8143977fc0dbSMatthew Dillon #endif
8144ffd7c74aSJoe Talbott }
8145ffd7c74aSJoe Talbott
8146ffd7c74aSJoe Talbott static int
8147ffd7c74aSJoe Talbott iwn5000_load_firmware(struct iwn_softc *sc)
8148ffd7c74aSJoe Talbott {
8149ffd7c74aSJoe Talbott struct iwn_fw_part *fw;
8150ffd7c74aSJoe Talbott int error;
8151ffd7c74aSJoe Talbott
8152da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
8153da10ea93SMatthew Dillon
8154ffd7c74aSJoe Talbott /* Load the initialization firmware on first boot only. */
8155ffd7c74aSJoe Talbott fw = (sc->sc_flags & IWN_FLAG_CALIB_DONE) ?
8156ffd7c74aSJoe Talbott &sc->fw.main : &sc->fw.init;
8157ffd7c74aSJoe Talbott
8158ffd7c74aSJoe Talbott error = iwn5000_load_firmware_section(sc, IWN_FW_TEXT_BASE,
8159ffd7c74aSJoe Talbott fw->text, fw->textsz);
8160ffd7c74aSJoe Talbott if (error != 0) {
8161ffd7c74aSJoe Talbott device_printf(sc->sc_dev,
8162ffd7c74aSJoe Talbott "%s: could not load firmware %s section, error %d\n",
8163ffd7c74aSJoe Talbott __func__, ".text", error);
8164ffd7c74aSJoe Talbott return error;
8165ffd7c74aSJoe Talbott }
8166ffd7c74aSJoe Talbott error = iwn5000_load_firmware_section(sc, IWN_FW_DATA_BASE,
8167ffd7c74aSJoe Talbott fw->data, fw->datasz);
8168ffd7c74aSJoe Talbott if (error != 0) {
8169ffd7c74aSJoe Talbott device_printf(sc->sc_dev,
8170ffd7c74aSJoe Talbott "%s: could not load firmware %s section, error %d\n",
8171ffd7c74aSJoe Talbott __func__, ".data", error);
8172ffd7c74aSJoe Talbott return error;
8173ffd7c74aSJoe Talbott }
8174ffd7c74aSJoe Talbott
8175ffd7c74aSJoe Talbott /* Now press "execute". */
8176ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_RESET, 0);
8177ffd7c74aSJoe Talbott return 0;
8178ffd7c74aSJoe Talbott }
8179ffd7c74aSJoe Talbott
8180458fc9cfSMatthew Dillon /*
8181da10ea93SMatthew Dillon * Extract text and data sections from a legacy firmware image.
8182458fc9cfSMatthew Dillon */
8183da10ea93SMatthew Dillon static int
8184da10ea93SMatthew Dillon iwn_read_firmware_leg(struct iwn_softc *sc, struct iwn_fw_info *fw)
8185da10ea93SMatthew Dillon {
8186da10ea93SMatthew Dillon const uint32_t *ptr;
8187da10ea93SMatthew Dillon size_t hdrlen = 24;
8188da10ea93SMatthew Dillon uint32_t rev;
8189ffd7c74aSJoe Talbott
8190da10ea93SMatthew Dillon ptr = (const uint32_t *)fw->data;
8191ffd7c74aSJoe Talbott rev = le32toh(*ptr++);
8192da10ea93SMatthew Dillon
819305538f72SMatthew Dillon sc->ucode_rev = rev;
819405538f72SMatthew Dillon
8195ffd7c74aSJoe Talbott /* Check firmware API version. */
8196ffd7c74aSJoe Talbott if (IWN_FW_API(rev) <= 1) {
8197ffd7c74aSJoe Talbott device_printf(sc->sc_dev,
8198ffd7c74aSJoe Talbott "%s: bad firmware, need API version >=2\n", __func__);
8199ffd7c74aSJoe Talbott return EINVAL;
8200ffd7c74aSJoe Talbott }
8201ffd7c74aSJoe Talbott if (IWN_FW_API(rev) >= 3) {
8202ffd7c74aSJoe Talbott /* Skip build number (version 2 header). */
8203da10ea93SMatthew Dillon hdrlen += 4;
8204ffd7c74aSJoe Talbott ptr++;
8205ffd7c74aSJoe Talbott }
8206da10ea93SMatthew Dillon if (fw->size < hdrlen) {
8207da10ea93SMatthew Dillon device_printf(sc->sc_dev, "%s: firmware too short: %zu bytes\n",
8208da10ea93SMatthew Dillon __func__, fw->size);
8209da10ea93SMatthew Dillon return EINVAL;
8210da10ea93SMatthew Dillon }
8211ffd7c74aSJoe Talbott fw->main.textsz = le32toh(*ptr++);
8212ffd7c74aSJoe Talbott fw->main.datasz = le32toh(*ptr++);
8213ffd7c74aSJoe Talbott fw->init.textsz = le32toh(*ptr++);
8214ffd7c74aSJoe Talbott fw->init.datasz = le32toh(*ptr++);
8215ffd7c74aSJoe Talbott fw->boot.textsz = le32toh(*ptr++);
8216ffd7c74aSJoe Talbott
8217ffd7c74aSJoe Talbott /* Check that all firmware sections fit. */
8218da10ea93SMatthew Dillon if (fw->size < hdrlen + fw->main.textsz + fw->main.datasz +
8219da10ea93SMatthew Dillon fw->init.textsz + fw->init.datasz + fw->boot.textsz) {
8220da10ea93SMatthew Dillon device_printf(sc->sc_dev, "%s: firmware too short: %zu bytes\n",
8221da10ea93SMatthew Dillon __func__, fw->size);
8222ffd7c74aSJoe Talbott return EINVAL;
8223ffd7c74aSJoe Talbott }
8224ffd7c74aSJoe Talbott
8225ffd7c74aSJoe Talbott /* Get pointers to firmware sections. */
8226ffd7c74aSJoe Talbott fw->main.text = (const uint8_t *)ptr;
8227ffd7c74aSJoe Talbott fw->main.data = fw->main.text + fw->main.textsz;
8228ffd7c74aSJoe Talbott fw->init.text = fw->main.data + fw->main.datasz;
8229ffd7c74aSJoe Talbott fw->init.data = fw->init.text + fw->init.textsz;
8230ffd7c74aSJoe Talbott fw->boot.text = fw->init.data + fw->init.datasz;
8231da10ea93SMatthew Dillon return 0;
8232da10ea93SMatthew Dillon }
8233ffd7c74aSJoe Talbott
8234da10ea93SMatthew Dillon /*
8235da10ea93SMatthew Dillon * Extract text and data sections from a TLV firmware image.
8236da10ea93SMatthew Dillon */
8237da10ea93SMatthew Dillon static int
8238da10ea93SMatthew Dillon iwn_read_firmware_tlv(struct iwn_softc *sc, struct iwn_fw_info *fw,
8239da10ea93SMatthew Dillon uint16_t alt)
8240da10ea93SMatthew Dillon {
8241da10ea93SMatthew Dillon const struct iwn_fw_tlv_hdr *hdr;
8242da10ea93SMatthew Dillon const struct iwn_fw_tlv *tlv;
8243da10ea93SMatthew Dillon const uint8_t *ptr, *end;
8244da10ea93SMatthew Dillon uint64_t altmask;
8245da10ea93SMatthew Dillon uint32_t len, tmp;
8246da10ea93SMatthew Dillon
8247da10ea93SMatthew Dillon if (fw->size < sizeof (*hdr)) {
8248da10ea93SMatthew Dillon device_printf(sc->sc_dev, "%s: firmware too short: %zu bytes\n",
8249da10ea93SMatthew Dillon __func__, fw->size);
8250da10ea93SMatthew Dillon return EINVAL;
8251da10ea93SMatthew Dillon }
8252da10ea93SMatthew Dillon hdr = (const struct iwn_fw_tlv_hdr *)fw->data;
8253da10ea93SMatthew Dillon if (hdr->signature != htole32(IWN_FW_SIGNATURE)) {
8254da10ea93SMatthew Dillon device_printf(sc->sc_dev, "%s: bad firmware signature 0x%08x\n",
8255da10ea93SMatthew Dillon __func__, le32toh(hdr->signature));
8256da10ea93SMatthew Dillon return EINVAL;
8257da10ea93SMatthew Dillon }
8258da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_RESET, "FW: \"%.64s\", build 0x%x\n", hdr->descr,
8259da10ea93SMatthew Dillon le32toh(hdr->build));
826005538f72SMatthew Dillon sc->ucode_rev = le32toh(hdr->rev);
8261da10ea93SMatthew Dillon
8262da10ea93SMatthew Dillon /*
8263da10ea93SMatthew Dillon * Select the closest supported alternative that is less than
8264da10ea93SMatthew Dillon * or equal to the specified one.
8265da10ea93SMatthew Dillon */
8266da10ea93SMatthew Dillon altmask = le64toh(hdr->altmask);
8267da10ea93SMatthew Dillon while (alt > 0 && !(altmask & (1ULL << alt)))
8268da10ea93SMatthew Dillon alt--; /* Downgrade. */
8269da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_RESET, "using alternative %d\n", alt);
8270da10ea93SMatthew Dillon
8271da10ea93SMatthew Dillon ptr = (const uint8_t *)(hdr + 1);
8272da10ea93SMatthew Dillon end = (const uint8_t *)(fw->data + fw->size);
8273da10ea93SMatthew Dillon
8274da10ea93SMatthew Dillon /* Parse type-length-value fields. */
8275da10ea93SMatthew Dillon while (ptr + sizeof (*tlv) <= end) {
8276da10ea93SMatthew Dillon tlv = (const struct iwn_fw_tlv *)ptr;
8277da10ea93SMatthew Dillon len = le32toh(tlv->len);
8278da10ea93SMatthew Dillon
8279da10ea93SMatthew Dillon ptr += sizeof (*tlv);
8280da10ea93SMatthew Dillon if (ptr + len > end) {
8281da10ea93SMatthew Dillon device_printf(sc->sc_dev,
8282da10ea93SMatthew Dillon "%s: firmware too short: %zu bytes\n", __func__,
8283da10ea93SMatthew Dillon fw->size);
8284da10ea93SMatthew Dillon return EINVAL;
8285da10ea93SMatthew Dillon }
8286da10ea93SMatthew Dillon /* Skip other alternatives. */
8287da10ea93SMatthew Dillon if (tlv->alt != 0 && tlv->alt != htole16(alt))
8288da10ea93SMatthew Dillon goto next;
8289da10ea93SMatthew Dillon
8290da10ea93SMatthew Dillon switch (le16toh(tlv->type)) {
8291da10ea93SMatthew Dillon case IWN_FW_TLV_MAIN_TEXT:
8292da10ea93SMatthew Dillon fw->main.text = ptr;
8293da10ea93SMatthew Dillon fw->main.textsz = len;
8294da10ea93SMatthew Dillon break;
8295da10ea93SMatthew Dillon case IWN_FW_TLV_MAIN_DATA:
8296da10ea93SMatthew Dillon fw->main.data = ptr;
8297da10ea93SMatthew Dillon fw->main.datasz = len;
8298da10ea93SMatthew Dillon break;
8299da10ea93SMatthew Dillon case IWN_FW_TLV_INIT_TEXT:
8300da10ea93SMatthew Dillon fw->init.text = ptr;
8301da10ea93SMatthew Dillon fw->init.textsz = len;
8302da10ea93SMatthew Dillon break;
8303da10ea93SMatthew Dillon case IWN_FW_TLV_INIT_DATA:
8304da10ea93SMatthew Dillon fw->init.data = ptr;
8305da10ea93SMatthew Dillon fw->init.datasz = len;
8306da10ea93SMatthew Dillon break;
8307da10ea93SMatthew Dillon case IWN_FW_TLV_BOOT_TEXT:
8308da10ea93SMatthew Dillon fw->boot.text = ptr;
8309da10ea93SMatthew Dillon fw->boot.textsz = len;
8310da10ea93SMatthew Dillon break;
8311da10ea93SMatthew Dillon case IWN_FW_TLV_ENH_SENS:
8312da10ea93SMatthew Dillon if (!len)
8313da10ea93SMatthew Dillon sc->sc_flags |= IWN_FLAG_ENH_SENS;
8314da10ea93SMatthew Dillon break;
8315da10ea93SMatthew Dillon case IWN_FW_TLV_PHY_CALIB:
83164b420e05SMatthew Dillon tmp = le32toh(*ptr);
8317da10ea93SMatthew Dillon if (tmp < 253) {
8318da10ea93SMatthew Dillon sc->reset_noise_gain = tmp;
8319da10ea93SMatthew Dillon sc->noise_gain = tmp + 1;
8320da10ea93SMatthew Dillon }
8321da10ea93SMatthew Dillon break;
8322da10ea93SMatthew Dillon case IWN_FW_TLV_PAN:
8323da10ea93SMatthew Dillon sc->sc_flags |= IWN_FLAG_PAN_SUPPORT;
8324da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_RESET,
8325da10ea93SMatthew Dillon "PAN Support found: %d\n", 1);
8326da10ea93SMatthew Dillon break;
8327da10ea93SMatthew Dillon case IWN_FW_TLV_FLAGS:
83284b420e05SMatthew Dillon if (len < sizeof(uint32_t))
83294b420e05SMatthew Dillon break;
83304b420e05SMatthew Dillon if (len % sizeof(uint32_t))
83314b420e05SMatthew Dillon break;
83324b420e05SMatthew Dillon sc->tlv_feature_flags = le32toh(*ptr);
83334b420e05SMatthew Dillon DPRINTF(sc, IWN_DEBUG_RESET,
83344b420e05SMatthew Dillon "%s: feature: 0x%08x\n",
83354b420e05SMatthew Dillon __func__,
83364b420e05SMatthew Dillon sc->tlv_feature_flags);
8337da10ea93SMatthew Dillon break;
8338da10ea93SMatthew Dillon case IWN_FW_TLV_PBREQ_MAXLEN:
8339da10ea93SMatthew Dillon case IWN_FW_TLV_RUNT_EVTLOG_PTR:
8340da10ea93SMatthew Dillon case IWN_FW_TLV_RUNT_EVTLOG_SIZE:
8341da10ea93SMatthew Dillon case IWN_FW_TLV_RUNT_ERRLOG_PTR:
8342da10ea93SMatthew Dillon case IWN_FW_TLV_INIT_EVTLOG_PTR:
8343da10ea93SMatthew Dillon case IWN_FW_TLV_INIT_EVTLOG_SIZE:
8344da10ea93SMatthew Dillon case IWN_FW_TLV_INIT_ERRLOG_PTR:
8345da10ea93SMatthew Dillon case IWN_FW_TLV_WOWLAN_INST:
8346da10ea93SMatthew Dillon case IWN_FW_TLV_WOWLAN_DATA:
8347da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_RESET,
8348d3e40d4dSSascha Wildner "TLV type %d recognized but not handled\n",
8349da10ea93SMatthew Dillon le16toh(tlv->type));
8350da10ea93SMatthew Dillon break;
8351da10ea93SMatthew Dillon default:
8352da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_RESET,
8353da10ea93SMatthew Dillon "TLV type %d not handled\n", le16toh(tlv->type));
8354da10ea93SMatthew Dillon break;
8355da10ea93SMatthew Dillon }
8356da10ea93SMatthew Dillon next: /* TLV fields are 32-bit aligned. */
8357da10ea93SMatthew Dillon ptr += (len + 3) & ~3;
8358da10ea93SMatthew Dillon }
8359da10ea93SMatthew Dillon return 0;
8360da10ea93SMatthew Dillon }
8361da10ea93SMatthew Dillon
8362da10ea93SMatthew Dillon static int
8363da10ea93SMatthew Dillon iwn_read_firmware(struct iwn_softc *sc)
8364da10ea93SMatthew Dillon {
8365da10ea93SMatthew Dillon struct iwn_fw_info *fw = &sc->fw;
8366da10ea93SMatthew Dillon int error;
8367da10ea93SMatthew Dillon
8368da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
8369da10ea93SMatthew Dillon
837005538f72SMatthew Dillon IWN_UNLOCK(sc);
837105538f72SMatthew Dillon
8372da10ea93SMatthew Dillon memset(fw, 0, sizeof (*fw));
8373da10ea93SMatthew Dillon
837405538f72SMatthew Dillon /* Read firmware image from filesystem. */
8375da10ea93SMatthew Dillon sc->fw_fp = firmware_get(sc->fwname);
8376da10ea93SMatthew Dillon if (sc->fw_fp == NULL) {
8377da10ea93SMatthew Dillon device_printf(sc->sc_dev, "%s: could not read firmware %s\n",
8378da10ea93SMatthew Dillon __func__, sc->fwname);
837905538f72SMatthew Dillon IWN_LOCK(sc);
8380977fc0dbSMatthew Dillon return EINVAL;
8381da10ea93SMatthew Dillon }
838205538f72SMatthew Dillon IWN_LOCK(sc);
8383da10ea93SMatthew Dillon
8384da10ea93SMatthew Dillon fw->size = sc->fw_fp->datasize;
8385da10ea93SMatthew Dillon fw->data = (const uint8_t *)sc->fw_fp->data;
8386da10ea93SMatthew Dillon if (fw->size < sizeof (uint32_t)) {
8387da10ea93SMatthew Dillon device_printf(sc->sc_dev, "%s: firmware too short: %zu bytes\n",
8388da10ea93SMatthew Dillon __func__, fw->size);
838905538f72SMatthew Dillon error = EINVAL;
8390977fc0dbSMatthew Dillon goto fail;
8391da10ea93SMatthew Dillon }
8392da10ea93SMatthew Dillon
8393da10ea93SMatthew Dillon /* Retrieve text and data sections. */
8394da10ea93SMatthew Dillon if (*(const uint32_t *)fw->data != 0) /* Legacy image. */
8395da10ea93SMatthew Dillon error = iwn_read_firmware_leg(sc, fw);
8396da10ea93SMatthew Dillon else
8397da10ea93SMatthew Dillon error = iwn_read_firmware_tlv(sc, fw, 1);
8398da10ea93SMatthew Dillon if (error != 0) {
8399da10ea93SMatthew Dillon device_printf(sc->sc_dev,
8400da10ea93SMatthew Dillon "%s: could not read firmware sections, error %d\n",
8401da10ea93SMatthew Dillon __func__, error);
8402977fc0dbSMatthew Dillon goto fail;
8403da10ea93SMatthew Dillon }
8404da10ea93SMatthew Dillon
840505538f72SMatthew Dillon device_printf(sc->sc_dev, "%s: ucode rev=0x%08x\n", __func__, sc->ucode_rev);
840605538f72SMatthew Dillon
8407da10ea93SMatthew Dillon /* Make sure text and data sections fit in hardware memory. */
8408da10ea93SMatthew Dillon if (fw->main.textsz > sc->fw_text_maxsz ||
8409da10ea93SMatthew Dillon fw->main.datasz > sc->fw_data_maxsz ||
8410da10ea93SMatthew Dillon fw->init.textsz > sc->fw_text_maxsz ||
8411da10ea93SMatthew Dillon fw->init.datasz > sc->fw_data_maxsz ||
8412da10ea93SMatthew Dillon fw->boot.textsz > IWN_FW_BOOT_TEXT_MAXSZ ||
8413da10ea93SMatthew Dillon (fw->boot.textsz & 3) != 0) {
8414da10ea93SMatthew Dillon device_printf(sc->sc_dev, "%s: firmware sections too large\n",
8415da10ea93SMatthew Dillon __func__);
8416977fc0dbSMatthew Dillon error = EINVAL;
8417977fc0dbSMatthew Dillon goto fail;
8418977fc0dbSMatthew Dillon }
8419977fc0dbSMatthew Dillon
8420977fc0dbSMatthew Dillon /* We can proceed with loading the firmware. */
8421977fc0dbSMatthew Dillon return 0;
8422977fc0dbSMatthew Dillon
8423977fc0dbSMatthew Dillon fail: iwn_unload_firmware(sc);
8424977fc0dbSMatthew Dillon return error;
8425977fc0dbSMatthew Dillon }
8426977fc0dbSMatthew Dillon
8427977fc0dbSMatthew Dillon static void
8428977fc0dbSMatthew Dillon iwn_unload_firmware(struct iwn_softc *sc)
8429977fc0dbSMatthew Dillon {
8430da10ea93SMatthew Dillon firmware_put(sc->fw_fp, FIRMWARE_UNLOAD);
8431da10ea93SMatthew Dillon sc->fw_fp = NULL;
8432ffd7c74aSJoe Talbott }
8433ffd7c74aSJoe Talbott
8434ffd7c74aSJoe Talbott static int
8435ffd7c74aSJoe Talbott iwn_clock_wait(struct iwn_softc *sc)
8436ffd7c74aSJoe Talbott {
8437ffd7c74aSJoe Talbott int ntries;
8438ffd7c74aSJoe Talbott
8439ffd7c74aSJoe Talbott /* Set "initialization complete" bit. */
8440ffd7c74aSJoe Talbott IWN_SETBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_INIT_DONE);
8441ffd7c74aSJoe Talbott
8442ffd7c74aSJoe Talbott /* Wait for clock stabilization. */
8443ffd7c74aSJoe Talbott for (ntries = 0; ntries < 2500; ntries++) {
8444ffd7c74aSJoe Talbott if (IWN_READ(sc, IWN_GP_CNTRL) & IWN_GP_CNTRL_MAC_CLOCK_READY)
8445ffd7c74aSJoe Talbott return 0;
8446ffd7c74aSJoe Talbott DELAY(10);
8447ffd7c74aSJoe Talbott }
8448ffd7c74aSJoe Talbott device_printf(sc->sc_dev,
8449ffd7c74aSJoe Talbott "%s: timeout waiting for clock stabilization\n", __func__);
8450ffd7c74aSJoe Talbott return ETIMEDOUT;
8451ffd7c74aSJoe Talbott }
8452ffd7c74aSJoe Talbott
8453ffd7c74aSJoe Talbott static int
8454ffd7c74aSJoe Talbott iwn_apm_init(struct iwn_softc *sc)
8455ffd7c74aSJoe Talbott {
8456da10ea93SMatthew Dillon uint32_t reg;
8457ffd7c74aSJoe Talbott int error;
8458ffd7c74aSJoe Talbott
8459da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
8460da10ea93SMatthew Dillon
8461da10ea93SMatthew Dillon /* Disable L0s exit timer (NMI bug workaround). */
8462ffd7c74aSJoe Talbott IWN_SETBITS(sc, IWN_GIO_CHICKEN, IWN_GIO_CHICKEN_DIS_L0S_TIMER);
8463da10ea93SMatthew Dillon /* Don't wait for ICH L0s (ICH bug workaround). */
8464ffd7c74aSJoe Talbott IWN_SETBITS(sc, IWN_GIO_CHICKEN, IWN_GIO_CHICKEN_L1A_NO_L0S_RX);
8465ffd7c74aSJoe Talbott
8466da10ea93SMatthew Dillon /* Set FH wait threshold to max (HW bug under stress workaround). */
8467ffd7c74aSJoe Talbott IWN_SETBITS(sc, IWN_DBG_HPET_MEM, 0xffff0000);
8468ffd7c74aSJoe Talbott
8469ffd7c74aSJoe Talbott /* Enable HAP INTA to move adapter from L1a to L0s. */
8470ffd7c74aSJoe Talbott IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_HAP_WAKE_L1A);
8471ffd7c74aSJoe Talbott
8472ffd7c74aSJoe Talbott /* Retrieve PCIe Active State Power Management (ASPM). */
8473977fc0dbSMatthew Dillon #if defined(__DragonFly__)
8474977fc0dbSMatthew Dillon reg = pci_read_config(sc->sc_dev, sc->sc_cap_off + PCIER_LINKCTRL, 4);
8475ffd7c74aSJoe Talbott /* Workaround for HW instability in PCIe L0->L0s->L1 transition. */
8476977fc0dbSMatthew Dillon if (reg & PCIEM_LNKCTL_ASPM_L1) /* L1 Entry enabled. */
8477977fc0dbSMatthew Dillon #else
8478977fc0dbSMatthew Dillon reg = pci_read_config(sc->sc_dev, sc->sc_cap_off + PCIER_LINK_CTL, 4);
8479977fc0dbSMatthew Dillon /* Workaround for HW instability in PCIe L0->L0s->L1 transition. */
8480977fc0dbSMatthew Dillon if (reg & PCIEM_LINK_CTL_ASPMC_L1) /* L1 Entry enabled. */
8481977fc0dbSMatthew Dillon #endif
8482ffd7c74aSJoe Talbott IWN_SETBITS(sc, IWN_GIO, IWN_GIO_L0S_ENA);
8483ffd7c74aSJoe Talbott else
8484ffd7c74aSJoe Talbott IWN_CLRBITS(sc, IWN_GIO, IWN_GIO_L0S_ENA);
8485ffd7c74aSJoe Talbott
8486da10ea93SMatthew Dillon if (sc->base_params->pll_cfg_val)
8487da10ea93SMatthew Dillon IWN_SETBITS(sc, IWN_ANA_PLL, sc->base_params->pll_cfg_val);
8488ffd7c74aSJoe Talbott
8489ffd7c74aSJoe Talbott /* Wait for clock stabilization before accessing prph. */
8490da10ea93SMatthew Dillon if ((error = iwn_clock_wait(sc)) != 0)
8491ffd7c74aSJoe Talbott return error;
8492ffd7c74aSJoe Talbott
8493da10ea93SMatthew Dillon if ((error = iwn_nic_lock(sc)) != 0)
8494ffd7c74aSJoe Talbott return error;
8495ffd7c74aSJoe Talbott if (sc->hw_type == IWN_HW_REV_TYPE_4965) {
8496da10ea93SMatthew Dillon /* Enable DMA and BSM (Bootstrap State Machine). */
8497ffd7c74aSJoe Talbott iwn_prph_write(sc, IWN_APMG_CLK_EN,
8498ffd7c74aSJoe Talbott IWN_APMG_CLK_CTRL_DMA_CLK_RQT |
8499ffd7c74aSJoe Talbott IWN_APMG_CLK_CTRL_BSM_CLK_RQT);
8500ffd7c74aSJoe Talbott } else {
8501ffd7c74aSJoe Talbott /* Enable DMA. */
8502ffd7c74aSJoe Talbott iwn_prph_write(sc, IWN_APMG_CLK_EN,
8503ffd7c74aSJoe Talbott IWN_APMG_CLK_CTRL_DMA_CLK_RQT);
8504ffd7c74aSJoe Talbott }
8505ffd7c74aSJoe Talbott DELAY(20);
8506ffd7c74aSJoe Talbott /* Disable L1-Active. */
8507ffd7c74aSJoe Talbott iwn_prph_setbits(sc, IWN_APMG_PCI_STT, IWN_APMG_PCI_STT_L1A_DIS);
8508ffd7c74aSJoe Talbott iwn_nic_unlock(sc);
8509ffd7c74aSJoe Talbott
8510ffd7c74aSJoe Talbott return 0;
8511ffd7c74aSJoe Talbott }
8512ffd7c74aSJoe Talbott
8513ffd7c74aSJoe Talbott static void
8514ffd7c74aSJoe Talbott iwn_apm_stop_master(struct iwn_softc *sc)
8515ffd7c74aSJoe Talbott {
8516ffd7c74aSJoe Talbott int ntries;
8517ffd7c74aSJoe Talbott
8518ffd7c74aSJoe Talbott /* Stop busmaster DMA activity. */
8519ffd7c74aSJoe Talbott IWN_SETBITS(sc, IWN_RESET, IWN_RESET_STOP_MASTER);
8520ffd7c74aSJoe Talbott for (ntries = 0; ntries < 100; ntries++) {
8521ffd7c74aSJoe Talbott if (IWN_READ(sc, IWN_RESET) & IWN_RESET_MASTER_DISABLED)
8522ffd7c74aSJoe Talbott return;
8523ffd7c74aSJoe Talbott DELAY(10);
8524ffd7c74aSJoe Talbott }
8525da10ea93SMatthew Dillon device_printf(sc->sc_dev, "%s: timeout waiting for master\n", __func__);
8526ffd7c74aSJoe Talbott }
8527ffd7c74aSJoe Talbott
8528ffd7c74aSJoe Talbott static void
8529ffd7c74aSJoe Talbott iwn_apm_stop(struct iwn_softc *sc)
8530ffd7c74aSJoe Talbott {
8531ffd7c74aSJoe Talbott iwn_apm_stop_master(sc);
8532ffd7c74aSJoe Talbott
8533ffd7c74aSJoe Talbott /* Reset the entire device. */
8534ffd7c74aSJoe Talbott IWN_SETBITS(sc, IWN_RESET, IWN_RESET_SW);
8535ffd7c74aSJoe Talbott DELAY(10);
8536ffd7c74aSJoe Talbott /* Clear "initialization complete" bit. */
8537ffd7c74aSJoe Talbott IWN_CLRBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_INIT_DONE);
8538ffd7c74aSJoe Talbott }
8539ffd7c74aSJoe Talbott
8540ffd7c74aSJoe Talbott static int
8541ffd7c74aSJoe Talbott iwn4965_nic_config(struct iwn_softc *sc)
8542ffd7c74aSJoe Talbott {
8543da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
8544da10ea93SMatthew Dillon
8545ffd7c74aSJoe Talbott if (IWN_RFCFG_TYPE(sc->rfcfg) == 1) {
8546ffd7c74aSJoe Talbott /*
8547ffd7c74aSJoe Talbott * I don't believe this to be correct but this is what the
8548ffd7c74aSJoe Talbott * vendor driver is doing. Probably the bits should not be
8549ffd7c74aSJoe Talbott * shifted in IWN_RFCFG_*.
8550ffd7c74aSJoe Talbott */
8551ffd7c74aSJoe Talbott IWN_SETBITS(sc, IWN_HW_IF_CONFIG,
8552ffd7c74aSJoe Talbott IWN_RFCFG_TYPE(sc->rfcfg) |
8553ffd7c74aSJoe Talbott IWN_RFCFG_STEP(sc->rfcfg) |
8554ffd7c74aSJoe Talbott IWN_RFCFG_DASH(sc->rfcfg));
8555ffd7c74aSJoe Talbott }
8556ffd7c74aSJoe Talbott IWN_SETBITS(sc, IWN_HW_IF_CONFIG,
8557ffd7c74aSJoe Talbott IWN_HW_IF_CONFIG_RADIO_SI | IWN_HW_IF_CONFIG_MAC_SI);
8558ffd7c74aSJoe Talbott return 0;
8559ffd7c74aSJoe Talbott }
8560ffd7c74aSJoe Talbott
8561ffd7c74aSJoe Talbott static int
8562ffd7c74aSJoe Talbott iwn5000_nic_config(struct iwn_softc *sc)
8563ffd7c74aSJoe Talbott {
8564ffd7c74aSJoe Talbott uint32_t tmp;
8565ffd7c74aSJoe Talbott int error;
8566ffd7c74aSJoe Talbott
8567da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
8568da10ea93SMatthew Dillon
8569ffd7c74aSJoe Talbott if (IWN_RFCFG_TYPE(sc->rfcfg) < 3) {
8570ffd7c74aSJoe Talbott IWN_SETBITS(sc, IWN_HW_IF_CONFIG,
8571ffd7c74aSJoe Talbott IWN_RFCFG_TYPE(sc->rfcfg) |
8572ffd7c74aSJoe Talbott IWN_RFCFG_STEP(sc->rfcfg) |
8573ffd7c74aSJoe Talbott IWN_RFCFG_DASH(sc->rfcfg));
8574ffd7c74aSJoe Talbott }
8575ffd7c74aSJoe Talbott IWN_SETBITS(sc, IWN_HW_IF_CONFIG,
8576ffd7c74aSJoe Talbott IWN_HW_IF_CONFIG_RADIO_SI | IWN_HW_IF_CONFIG_MAC_SI);
8577ffd7c74aSJoe Talbott
8578da10ea93SMatthew Dillon if ((error = iwn_nic_lock(sc)) != 0)
8579ffd7c74aSJoe Talbott return error;
8580ffd7c74aSJoe Talbott iwn_prph_setbits(sc, IWN_APMG_PS, IWN_APMG_PS_EARLY_PWROFF_DIS);
8581ffd7c74aSJoe Talbott
8582ffd7c74aSJoe Talbott if (sc->hw_type == IWN_HW_REV_TYPE_1000) {
8583ffd7c74aSJoe Talbott /*
8584ffd7c74aSJoe Talbott * Select first Switching Voltage Regulator (1.32V) to
8585ffd7c74aSJoe Talbott * solve a stability issue related to noisy DC2DC line
8586ffd7c74aSJoe Talbott * in the silicon of 1000 Series.
8587ffd7c74aSJoe Talbott */
8588ffd7c74aSJoe Talbott tmp = iwn_prph_read(sc, IWN_APMG_DIGITAL_SVR);
8589ffd7c74aSJoe Talbott tmp &= ~IWN_APMG_DIGITAL_SVR_VOLTAGE_MASK;
8590ffd7c74aSJoe Talbott tmp |= IWN_APMG_DIGITAL_SVR_VOLTAGE_1_32;
8591ffd7c74aSJoe Talbott iwn_prph_write(sc, IWN_APMG_DIGITAL_SVR, tmp);
8592ffd7c74aSJoe Talbott }
8593ffd7c74aSJoe Talbott iwn_nic_unlock(sc);
8594ffd7c74aSJoe Talbott
8595ffd7c74aSJoe Talbott if (sc->sc_flags & IWN_FLAG_INTERNAL_PA) {
8596ffd7c74aSJoe Talbott /* Use internal power amplifier only. */
8597ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_GP_DRIVER, IWN_GP_DRIVER_RADIO_2X2_IPA);
8598ffd7c74aSJoe Talbott }
8599da10ea93SMatthew Dillon if (sc->base_params->additional_nic_config && sc->calib_ver >= 6) {
8600ffd7c74aSJoe Talbott /* Indicate that ROM calibration version is >=6. */
8601ffd7c74aSJoe Talbott IWN_SETBITS(sc, IWN_GP_DRIVER, IWN_GP_DRIVER_CALIB_VER6);
8602ffd7c74aSJoe Talbott }
8603da10ea93SMatthew Dillon if (sc->base_params->additional_gp_drv_bit)
8604da10ea93SMatthew Dillon IWN_SETBITS(sc, IWN_GP_DRIVER,
8605da10ea93SMatthew Dillon sc->base_params->additional_gp_drv_bit);
8606ffd7c74aSJoe Talbott return 0;
8607ffd7c74aSJoe Talbott }
8608ffd7c74aSJoe Talbott
8609ffd7c74aSJoe Talbott /*
8610ffd7c74aSJoe Talbott * Take NIC ownership over Intel Active Management Technology (AMT).
8611ffd7c74aSJoe Talbott */
8612ffd7c74aSJoe Talbott static int
8613ffd7c74aSJoe Talbott iwn_hw_prepare(struct iwn_softc *sc)
8614ffd7c74aSJoe Talbott {
8615ffd7c74aSJoe Talbott int ntries;
8616ffd7c74aSJoe Talbott
8617da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
8618da10ea93SMatthew Dillon
8619ffd7c74aSJoe Talbott /* Check if hardware is ready. */
8620ffd7c74aSJoe Talbott IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_NIC_READY);
8621ffd7c74aSJoe Talbott for (ntries = 0; ntries < 5; ntries++) {
8622ffd7c74aSJoe Talbott if (IWN_READ(sc, IWN_HW_IF_CONFIG) &
8623ffd7c74aSJoe Talbott IWN_HW_IF_CONFIG_NIC_READY)
8624ffd7c74aSJoe Talbott return 0;
8625ffd7c74aSJoe Talbott DELAY(10);
8626ffd7c74aSJoe Talbott }
8627ffd7c74aSJoe Talbott
8628ffd7c74aSJoe Talbott /* Hardware not ready, force into ready state. */
8629ffd7c74aSJoe Talbott IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_PREPARE);
8630ffd7c74aSJoe Talbott for (ntries = 0; ntries < 15000; ntries++) {
8631ffd7c74aSJoe Talbott if (!(IWN_READ(sc, IWN_HW_IF_CONFIG) &
8632ffd7c74aSJoe Talbott IWN_HW_IF_CONFIG_PREPARE_DONE))
8633ffd7c74aSJoe Talbott break;
8634ffd7c74aSJoe Talbott DELAY(10);
8635ffd7c74aSJoe Talbott }
8636ffd7c74aSJoe Talbott if (ntries == 15000)
8637ffd7c74aSJoe Talbott return ETIMEDOUT;
8638ffd7c74aSJoe Talbott
8639ffd7c74aSJoe Talbott /* Hardware should be ready now. */
8640ffd7c74aSJoe Talbott IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_NIC_READY);
8641ffd7c74aSJoe Talbott for (ntries = 0; ntries < 5; ntries++) {
8642ffd7c74aSJoe Talbott if (IWN_READ(sc, IWN_HW_IF_CONFIG) &
8643ffd7c74aSJoe Talbott IWN_HW_IF_CONFIG_NIC_READY)
8644ffd7c74aSJoe Talbott return 0;
8645ffd7c74aSJoe Talbott DELAY(10);
8646ffd7c74aSJoe Talbott }
8647ffd7c74aSJoe Talbott return ETIMEDOUT;
8648ffd7c74aSJoe Talbott }
8649ffd7c74aSJoe Talbott
8650ffd7c74aSJoe Talbott static int
8651ffd7c74aSJoe Talbott iwn_hw_init(struct iwn_softc *sc)
8652ffd7c74aSJoe Talbott {
8653da10ea93SMatthew Dillon struct iwn_ops *ops = &sc->ops;
8654ffd7c74aSJoe Talbott int error, chnl, qid;
8655ffd7c74aSJoe Talbott
8656da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
8657da10ea93SMatthew Dillon
8658ffd7c74aSJoe Talbott /* Clear pending interrupts. */
8659ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_INT, 0xffffffff);
8660ffd7c74aSJoe Talbott
8661da10ea93SMatthew Dillon if ((error = iwn_apm_init(sc)) != 0) {
8662ffd7c74aSJoe Talbott device_printf(sc->sc_dev,
8663da10ea93SMatthew Dillon "%s: could not power ON adapter, error %d\n", __func__,
8664da10ea93SMatthew Dillon error);
8665da10ea93SMatthew Dillon return error;
8666ffd7c74aSJoe Talbott }
8667ffd7c74aSJoe Talbott
8668ffd7c74aSJoe Talbott /* Select VMAIN power source. */
8669da10ea93SMatthew Dillon if ((error = iwn_nic_lock(sc)) != 0)
8670da10ea93SMatthew Dillon return error;
8671ffd7c74aSJoe Talbott iwn_prph_clrbits(sc, IWN_APMG_PS, IWN_APMG_PS_PWR_SRC_MASK);
8672ffd7c74aSJoe Talbott iwn_nic_unlock(sc);
8673ffd7c74aSJoe Talbott
8674ffd7c74aSJoe Talbott /* Perform adapter-specific initialization. */
8675da10ea93SMatthew Dillon if ((error = ops->nic_config(sc)) != 0)
8676da10ea93SMatthew Dillon return error;
8677ffd7c74aSJoe Talbott
8678ffd7c74aSJoe Talbott /* Initialize RX ring. */
8679da10ea93SMatthew Dillon if ((error = iwn_nic_lock(sc)) != 0)
8680da10ea93SMatthew Dillon return error;
8681ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_FH_RX_CONFIG, 0);
8682ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_FH_RX_WPTR, 0);
8683da10ea93SMatthew Dillon /* Set physical address of RX ring (256-byte aligned). */
8684ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_FH_RX_BASE, sc->rxq.desc_dma.paddr >> 8);
8685da10ea93SMatthew Dillon /* Set physical address of RX status (16-byte aligned). */
8686ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_FH_STATUS_WPTR, sc->rxq.stat_dma.paddr >> 4);
8687ffd7c74aSJoe Talbott /* Enable RX. */
8688ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_FH_RX_CONFIG,
8689ffd7c74aSJoe Talbott IWN_FH_RX_CONFIG_ENA |
8690ffd7c74aSJoe Talbott IWN_FH_RX_CONFIG_IGN_RXF_EMPTY | /* HW bug workaround */
8691ffd7c74aSJoe Talbott IWN_FH_RX_CONFIG_IRQ_DST_HOST |
8692ffd7c74aSJoe Talbott IWN_FH_RX_CONFIG_SINGLE_FRAME |
8693ffd7c74aSJoe Talbott IWN_FH_RX_CONFIG_RB_TIMEOUT(0) |
8694ffd7c74aSJoe Talbott IWN_FH_RX_CONFIG_NRBD(IWN_RX_RING_COUNT_LOG));
8695ffd7c74aSJoe Talbott iwn_nic_unlock(sc);
8696ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_FH_RX_WPTR, (IWN_RX_RING_COUNT - 1) & ~7);
8697ffd7c74aSJoe Talbott
8698da10ea93SMatthew Dillon if ((error = iwn_nic_lock(sc)) != 0)
8699da10ea93SMatthew Dillon return error;
8700ffd7c74aSJoe Talbott
8701ffd7c74aSJoe Talbott /* Initialize TX scheduler. */
8702da10ea93SMatthew Dillon iwn_prph_write(sc, sc->sched_txfact_addr, 0);
8703ffd7c74aSJoe Talbott
8704da10ea93SMatthew Dillon /* Set physical address of "keep warm" page (16-byte aligned). */
8705ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_FH_KW_ADDR, sc->kw_dma.paddr >> 4);
8706ffd7c74aSJoe Talbott
8707ffd7c74aSJoe Talbott /* Initialize TX rings. */
8708da10ea93SMatthew Dillon for (qid = 0; qid < sc->ntxqs; qid++) {
8709ffd7c74aSJoe Talbott struct iwn_tx_ring *txq = &sc->txq[qid];
8710ffd7c74aSJoe Talbott
8711da10ea93SMatthew Dillon /* Set physical address of TX ring (256-byte aligned). */
8712ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_FH_CBBC_QUEUE(qid),
8713ffd7c74aSJoe Talbott txq->desc_dma.paddr >> 8);
8714ffd7c74aSJoe Talbott }
8715ffd7c74aSJoe Talbott iwn_nic_unlock(sc);
8716ffd7c74aSJoe Talbott
8717ffd7c74aSJoe Talbott /* Enable DMA channels. */
8718da10ea93SMatthew Dillon for (chnl = 0; chnl < sc->ndmachnls; chnl++) {
8719ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_FH_TX_CONFIG(chnl),
8720ffd7c74aSJoe Talbott IWN_FH_TX_CONFIG_DMA_ENA |
8721ffd7c74aSJoe Talbott IWN_FH_TX_CONFIG_DMA_CREDIT_ENA);
8722ffd7c74aSJoe Talbott }
8723ffd7c74aSJoe Talbott
8724ffd7c74aSJoe Talbott /* Clear "radio off" and "commands blocked" bits. */
8725ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_RFKILL);
8726ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_CMD_BLOCKED);
8727ffd7c74aSJoe Talbott
8728ffd7c74aSJoe Talbott /* Clear pending interrupts. */
8729ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_INT, 0xffffffff);
8730ffd7c74aSJoe Talbott /* Enable interrupt coalescing. */
8731ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_INT_COALESCING, 512 / 8);
8732ffd7c74aSJoe Talbott /* Enable interrupts. */
8733ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask);
8734ffd7c74aSJoe Talbott
8735ffd7c74aSJoe Talbott /* _Really_ make sure "radio off" bit is cleared! */
8736ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_RFKILL);
8737ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_RFKILL);
8738ffd7c74aSJoe Talbott
8739da10ea93SMatthew Dillon /* Enable shadow registers. */
8740da10ea93SMatthew Dillon if (sc->base_params->shadow_reg_enable)
8741da10ea93SMatthew Dillon IWN_SETBITS(sc, IWN_SHADOW_REG_CTRL, 0x800fffff);
8742da10ea93SMatthew Dillon
8743da10ea93SMatthew Dillon if ((error = ops->load_firmware(sc)) != 0) {
8744ffd7c74aSJoe Talbott device_printf(sc->sc_dev,
8745da10ea93SMatthew Dillon "%s: could not load firmware, error %d\n", __func__,
8746da10ea93SMatthew Dillon error);
8747da10ea93SMatthew Dillon return error;
8748ffd7c74aSJoe Talbott }
8749ffd7c74aSJoe Talbott /* Wait at most one second for firmware alive notification. */
8750977fc0dbSMatthew Dillon #if defined(__DragonFly__)
8751977fc0dbSMatthew Dillon if ((error = lksleep(sc, &sc->sc_lk, PCATCH, "iwninit", hz)) != 0) {
8752977fc0dbSMatthew Dillon #else
8753977fc0dbSMatthew Dillon if ((error = msleep(sc, &sc->sc_mtx, PCATCH, "iwninit", hz)) != 0) {
8754977fc0dbSMatthew Dillon #endif
8755ffd7c74aSJoe Talbott device_printf(sc->sc_dev,
8756ffd7c74aSJoe Talbott "%s: timeout waiting for adapter to initialize, error %d\n",
8757ffd7c74aSJoe Talbott __func__, error);
8758da10ea93SMatthew Dillon return error;
8759ffd7c74aSJoe Talbott }
8760ffd7c74aSJoe Talbott /* Do post-firmware initialization. */
8761da10ea93SMatthew Dillon
8762da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
8763da10ea93SMatthew Dillon
8764da10ea93SMatthew Dillon return ops->post_alive(sc);
8765ffd7c74aSJoe Talbott }
8766ffd7c74aSJoe Talbott
8767ffd7c74aSJoe Talbott static void
8768ffd7c74aSJoe Talbott iwn_hw_stop(struct iwn_softc *sc)
8769ffd7c74aSJoe Talbott {
8770ffd7c74aSJoe Talbott int chnl, qid, ntries;
8771ffd7c74aSJoe Talbott
8772da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
8773da10ea93SMatthew Dillon
8774ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_RESET, IWN_RESET_NEVO);
8775ffd7c74aSJoe Talbott
8776ffd7c74aSJoe Talbott /* Disable interrupts. */
8777ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_INT_MASK, 0);
8778ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_INT, 0xffffffff);
8779ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_FH_INT, 0xffffffff);
8780ffd7c74aSJoe Talbott sc->sc_flags &= ~IWN_FLAG_USE_ICT;
8781ffd7c74aSJoe Talbott
8782ffd7c74aSJoe Talbott /* Make sure we no longer hold the NIC lock. */
8783ffd7c74aSJoe Talbott iwn_nic_unlock(sc);
8784ffd7c74aSJoe Talbott
8785ffd7c74aSJoe Talbott /* Stop TX scheduler. */
8786da10ea93SMatthew Dillon iwn_prph_write(sc, sc->sched_txfact_addr, 0);
8787ffd7c74aSJoe Talbott
8788ffd7c74aSJoe Talbott /* Stop all DMA channels. */
8789ffd7c74aSJoe Talbott if (iwn_nic_lock(sc) == 0) {
8790da10ea93SMatthew Dillon for (chnl = 0; chnl < sc->ndmachnls; chnl++) {
8791ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_FH_TX_CONFIG(chnl), 0);
8792ffd7c74aSJoe Talbott for (ntries = 0; ntries < 200; ntries++) {
8793da10ea93SMatthew Dillon if (IWN_READ(sc, IWN_FH_TX_STATUS) &
8794ffd7c74aSJoe Talbott IWN_FH_TX_STATUS_IDLE(chnl))
8795ffd7c74aSJoe Talbott break;
8796ffd7c74aSJoe Talbott DELAY(10);
8797ffd7c74aSJoe Talbott }
8798ffd7c74aSJoe Talbott }
8799ffd7c74aSJoe Talbott iwn_nic_unlock(sc);
8800ffd7c74aSJoe Talbott }
8801ffd7c74aSJoe Talbott
8802ffd7c74aSJoe Talbott /* Stop RX ring. */
8803ffd7c74aSJoe Talbott iwn_reset_rx_ring(sc, &sc->rxq);
8804ffd7c74aSJoe Talbott
8805ffd7c74aSJoe Talbott /* Reset all TX rings. */
8806da10ea93SMatthew Dillon for (qid = 0; qid < sc->ntxqs; qid++)
8807ffd7c74aSJoe Talbott iwn_reset_tx_ring(sc, &sc->txq[qid]);
8808ffd7c74aSJoe Talbott
8809ffd7c74aSJoe Talbott if (iwn_nic_lock(sc) == 0) {
8810ffd7c74aSJoe Talbott iwn_prph_write(sc, IWN_APMG_CLK_DIS,
8811ffd7c74aSJoe Talbott IWN_APMG_CLK_CTRL_DMA_CLK_RQT);
8812ffd7c74aSJoe Talbott iwn_nic_unlock(sc);
8813ffd7c74aSJoe Talbott }
8814ffd7c74aSJoe Talbott DELAY(5);
8815ffd7c74aSJoe Talbott /* Power OFF adapter. */
8816ffd7c74aSJoe Talbott iwn_apm_stop(sc);
8817ffd7c74aSJoe Talbott }
8818ffd7c74aSJoe Talbott
8819ffd7c74aSJoe Talbott static void
882005538f72SMatthew Dillon iwn_radio_on(void *arg0, int pending)
8821da10ea93SMatthew Dillon {
8822da10ea93SMatthew Dillon struct iwn_softc *sc = arg0;
8823977fc0dbSMatthew Dillon struct ieee80211com *ic = &sc->sc_ic;
882405538f72SMatthew Dillon struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
8825da10ea93SMatthew Dillon
8826da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
8827da10ea93SMatthew Dillon
8828da10ea93SMatthew Dillon if (vap != NULL) {
882905538f72SMatthew Dillon iwn_init(sc);
8830da10ea93SMatthew Dillon ieee80211_init(vap);
8831da10ea93SMatthew Dillon }
8832da10ea93SMatthew Dillon }
8833da10ea93SMatthew Dillon
8834da10ea93SMatthew Dillon static void
883505538f72SMatthew Dillon iwn_radio_off(void *arg0, int pending)
8836da10ea93SMatthew Dillon {
8837da10ea93SMatthew Dillon struct iwn_softc *sc = arg0;
8838977fc0dbSMatthew Dillon struct ieee80211com *ic = &sc->sc_ic;
883905538f72SMatthew Dillon struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
8840da10ea93SMatthew Dillon
8841da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
8842da10ea93SMatthew Dillon
884305538f72SMatthew Dillon iwn_stop(sc);
8844da10ea93SMatthew Dillon if (vap != NULL)
8845da10ea93SMatthew Dillon ieee80211_stop(vap);
8846da10ea93SMatthew Dillon
8847da10ea93SMatthew Dillon /* Enable interrupts to get RF toggle notification. */
884805538f72SMatthew Dillon IWN_LOCK(sc);
8849da10ea93SMatthew Dillon IWN_WRITE(sc, IWN_INT, 0xffffffff);
8850da10ea93SMatthew Dillon IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask);
885105538f72SMatthew Dillon IWN_UNLOCK(sc);
8852da10ea93SMatthew Dillon }
8853da10ea93SMatthew Dillon
8854da10ea93SMatthew Dillon static void
885505538f72SMatthew Dillon iwn_panicked(void *arg0, int pending)
8856fd49669cSMichael Neumann {
8857fd49669cSMichael Neumann struct iwn_softc *sc = arg0;
8858977fc0dbSMatthew Dillon struct ieee80211com *ic = &sc->sc_ic;
8859fd49669cSMichael Neumann struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
8860977fc0dbSMatthew Dillon #if 0
8861fd49669cSMichael Neumann int error;
8862977fc0dbSMatthew Dillon #endif
8863fd49669cSMichael Neumann
8864fd49669cSMichael Neumann if (vap == NULL) {
8865fd49669cSMichael Neumann kprintf("%s: null vap\n", __func__);
8866fd49669cSMichael Neumann return;
8867fd49669cSMichael Neumann }
8868fd49669cSMichael Neumann
8869fd49669cSMichael Neumann device_printf(sc->sc_dev, "%s: controller panicked, iv_state = %d; "
8870977fc0dbSMatthew Dillon "restarting\n", __func__, vap->iv_state);
8871fd49669cSMichael Neumann
8872977fc0dbSMatthew Dillon /*
8873977fc0dbSMatthew Dillon * This is not enough work. We need to also reinitialise
8874977fc0dbSMatthew Dillon * the correct transmit state for aggregation enabled queues,
8875977fc0dbSMatthew Dillon * which has a very specific requirement of
8876977fc0dbSMatthew Dillon * ring index = 802.11 seqno % 256. If we don't do this (which
8877977fc0dbSMatthew Dillon * we definitely don't!) then the firmware will just panic again.
8878977fc0dbSMatthew Dillon */
8879977fc0dbSMatthew Dillon #if 1
8880977fc0dbSMatthew Dillon ieee80211_restart_all(ic);
8881977fc0dbSMatthew Dillon #else
888205538f72SMatthew Dillon IWN_LOCK(sc);
8883fd49669cSMichael Neumann
8884fd49669cSMichael Neumann iwn_stop_locked(sc);
8885fd49669cSMichael Neumann iwn_init_locked(sc);
8886fd49669cSMichael Neumann if (vap->iv_state >= IEEE80211_S_AUTH &&
8887fd49669cSMichael Neumann (error = iwn_auth(sc, vap)) != 0) {
8888fd49669cSMichael Neumann device_printf(sc->sc_dev,
8889fd49669cSMichael Neumann "%s: could not move to auth state\n", __func__);
8890fd49669cSMichael Neumann }
8891fd49669cSMichael Neumann if (vap->iv_state >= IEEE80211_S_RUN &&
8892fd49669cSMichael Neumann (error = iwn_run(sc, vap)) != 0) {
8893fd49669cSMichael Neumann device_printf(sc->sc_dev,
8894fd49669cSMichael Neumann "%s: could not move to run state\n", __func__);
8895fd49669cSMichael Neumann }
8896fd49669cSMichael Neumann
889705538f72SMatthew Dillon IWN_UNLOCK(sc);
8898977fc0dbSMatthew Dillon #endif
8899fd49669cSMichael Neumann }
8900fd49669cSMichael Neumann
8901fd49669cSMichael Neumann static void
8902ffd7c74aSJoe Talbott iwn_init_locked(struct iwn_softc *sc)
8903ffd7c74aSJoe Talbott {
8904ffd7c74aSJoe Talbott int error;
8905b8b498edSMatthew Dillon
8906da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__);
8907ffd7c74aSJoe Talbott
890805538f72SMatthew Dillon IWN_LOCK_ASSERT(sc);
890905538f72SMatthew Dillon
8910977fc0dbSMatthew Dillon sc->sc_flags |= IWN_FLAG_RUNNING;
8911977fc0dbSMatthew Dillon
8912da10ea93SMatthew Dillon if ((error = iwn_hw_prepare(sc)) != 0) {
8913da10ea93SMatthew Dillon device_printf(sc->sc_dev, "%s: hardware not ready, error %d\n",
8914ffd7c74aSJoe Talbott __func__, error);
8915ffd7c74aSJoe Talbott goto fail;
8916ffd7c74aSJoe Talbott }
8917ffd7c74aSJoe Talbott
8918ffd7c74aSJoe Talbott /* Initialize interrupt mask to default value. */
8919ffd7c74aSJoe Talbott sc->int_mask = IWN_INT_MASK_DEF;
8920ffd7c74aSJoe Talbott sc->sc_flags &= ~IWN_FLAG_USE_ICT;
8921ffd7c74aSJoe Talbott
8922ffd7c74aSJoe Talbott /* Check that the radio is not disabled by hardware switch. */
8923ffd7c74aSJoe Talbott if (!(IWN_READ(sc, IWN_GP_CNTRL) & IWN_GP_CNTRL_RFKILL)) {
8924ffd7c74aSJoe Talbott device_printf(sc->sc_dev,
8925ffd7c74aSJoe Talbott "radio is disabled by hardware switch\n");
8926ffd7c74aSJoe Talbott /* Enable interrupts to get RF toggle notifications. */
8927ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_INT, 0xffffffff);
8928ffd7c74aSJoe Talbott IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask);
8929ffd7c74aSJoe Talbott return;
8930ffd7c74aSJoe Talbott }
8931ffd7c74aSJoe Talbott
8932ffd7c74aSJoe Talbott /* Read firmware images from the filesystem. */
8933da10ea93SMatthew Dillon if ((error = iwn_read_firmware(sc)) != 0) {
8934ffd7c74aSJoe Talbott device_printf(sc->sc_dev,
8935da10ea93SMatthew Dillon "%s: could not read firmware, error %d\n", __func__,
8936da10ea93SMatthew Dillon error);
8937ffd7c74aSJoe Talbott goto fail;
8938ffd7c74aSJoe Talbott }
8939ffd7c74aSJoe Talbott
8940ffd7c74aSJoe Talbott /* Initialize hardware and upload firmware. */
8941ffd7c74aSJoe Talbott error = iwn_hw_init(sc);
8942977fc0dbSMatthew Dillon iwn_unload_firmware(sc);
8943ffd7c74aSJoe Talbott if (error != 0) {
8944ffd7c74aSJoe Talbott device_printf(sc->sc_dev,
8945da10ea93SMatthew Dillon "%s: could not initialize hardware, error %d\n", __func__,
8946da10ea93SMatthew Dillon error);
8947ffd7c74aSJoe Talbott goto fail;
8948ffd7c74aSJoe Talbott }
8949ffd7c74aSJoe Talbott
8950ffd7c74aSJoe Talbott /* Configure adapter now that it is ready. */
8951da10ea93SMatthew Dillon if ((error = iwn_config(sc)) != 0) {
8952ffd7c74aSJoe Talbott device_printf(sc->sc_dev,
8953da10ea93SMatthew Dillon "%s: could not configure device, error %d\n", __func__,
8954da10ea93SMatthew Dillon error);
8955ffd7c74aSJoe Talbott goto fail;
8956ffd7c74aSJoe Talbott }
8957ffd7c74aSJoe Talbott
895805538f72SMatthew Dillon callout_reset(&sc->watchdog_to, hz, iwn_watchdog, sc);
8959da10ea93SMatthew Dillon
8960da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
8961da10ea93SMatthew Dillon
8962ffd7c74aSJoe Talbott return;
8963ffd7c74aSJoe Talbott
8964977fc0dbSMatthew Dillon fail:
8965977fc0dbSMatthew Dillon sc->sc_flags &= ~IWN_FLAG_RUNNING;
8966977fc0dbSMatthew Dillon iwn_stop_locked(sc);
8967da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end in error\n",__func__);
8968ffd7c74aSJoe Talbott }
8969ffd7c74aSJoe Talbott
8970ffd7c74aSJoe Talbott static void
8971977fc0dbSMatthew Dillon iwn_init(struct iwn_softc *sc)
8972ffd7c74aSJoe Talbott {
8973ffd7c74aSJoe Talbott
897405538f72SMatthew Dillon IWN_LOCK(sc);
8975ffd7c74aSJoe Talbott iwn_init_locked(sc);
897605538f72SMatthew Dillon IWN_UNLOCK(sc);
8977ffd7c74aSJoe Talbott
8978977fc0dbSMatthew Dillon if (sc->sc_flags & IWN_FLAG_RUNNING)
8979977fc0dbSMatthew Dillon ieee80211_start_all(&sc->sc_ic);
8980ffd7c74aSJoe Talbott }
8981ffd7c74aSJoe Talbott
8982ffd7c74aSJoe Talbott static void
8983ffd7c74aSJoe Talbott iwn_stop_locked(struct iwn_softc *sc)
8984ffd7c74aSJoe Talbott {
8985ffd7c74aSJoe Talbott
898605538f72SMatthew Dillon IWN_LOCK_ASSERT(sc);
898705538f72SMatthew Dillon
89884b420e05SMatthew Dillon sc->sc_is_scanning = 0;
8989ffd7c74aSJoe Talbott sc->sc_tx_timer = 0;
899005538f72SMatthew Dillon #if defined(__DragonFly__)
8991eb67213aSMatthew Dillon callout_cancel(&sc->watchdog_to);
8992eb67213aSMatthew Dillon callout_cancel(&sc->calib_to);
899305538f72SMatthew Dillon #else
8994da10ea93SMatthew Dillon callout_stop(&sc->watchdog_to);
8995da10ea93SMatthew Dillon callout_stop(&sc->calib_to);
899605538f72SMatthew Dillon #endif
8997977fc0dbSMatthew Dillon sc->sc_flags &= ~IWN_FLAG_RUNNING;
8998ffd7c74aSJoe Talbott
8999ffd7c74aSJoe Talbott /* Power OFF hardware. */
9000ffd7c74aSJoe Talbott iwn_hw_stop(sc);
9001ffd7c74aSJoe Talbott }
9002ffd7c74aSJoe Talbott
900305538f72SMatthew Dillon static void
900405538f72SMatthew Dillon iwn_stop(struct iwn_softc *sc)
900505538f72SMatthew Dillon {
900605538f72SMatthew Dillon IWN_LOCK(sc);
900705538f72SMatthew Dillon iwn_stop_locked(sc);
900805538f72SMatthew Dillon IWN_UNLOCK(sc);
900905538f72SMatthew Dillon }
901005538f72SMatthew Dillon
9011ffd7c74aSJoe Talbott /*
9012ffd7c74aSJoe Talbott * Callback from net80211 to start a scan.
9013ffd7c74aSJoe Talbott */
9014ffd7c74aSJoe Talbott static void
9015ffd7c74aSJoe Talbott iwn_scan_start(struct ieee80211com *ic)
9016ffd7c74aSJoe Talbott {
90174f1aaf2fSImre Vadász struct iwn_softc *sc = ic->ic_softc;
9018ffd7c74aSJoe Talbott
901905538f72SMatthew Dillon IWN_LOCK(sc);
9020ffd7c74aSJoe Talbott /* make the link LED blink while we're scanning */
9021ffd7c74aSJoe Talbott iwn_set_led(sc, IWN_LED_LINK, 20, 2);
902205538f72SMatthew Dillon IWN_UNLOCK(sc);
9023ffd7c74aSJoe Talbott }
9024ffd7c74aSJoe Talbott
9025ffd7c74aSJoe Talbott /*
9026ffd7c74aSJoe Talbott * Callback from net80211 to terminate a scan.
9027ffd7c74aSJoe Talbott */
9028ffd7c74aSJoe Talbott static void
9029ffd7c74aSJoe Talbott iwn_scan_end(struct ieee80211com *ic)
9030ffd7c74aSJoe Talbott {
90314f1aaf2fSImre Vadász struct iwn_softc *sc = ic->ic_softc;
9032ffd7c74aSJoe Talbott struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
9033ffd7c74aSJoe Talbott
903405538f72SMatthew Dillon IWN_LOCK(sc);
9035ffd7c74aSJoe Talbott if (vap->iv_state == IEEE80211_S_RUN) {
9036ffd7c74aSJoe Talbott /* Set link LED to ON status if we are associated */
9037ffd7c74aSJoe Talbott iwn_set_led(sc, IWN_LED_LINK, 0, 1);
9038ffd7c74aSJoe Talbott }
903905538f72SMatthew Dillon IWN_UNLOCK(sc);
9040ffd7c74aSJoe Talbott }
9041ffd7c74aSJoe Talbott
9042ffd7c74aSJoe Talbott /*
9043ffd7c74aSJoe Talbott * Callback from net80211 to force a channel change.
9044ffd7c74aSJoe Talbott */
9045ffd7c74aSJoe Talbott static void
9046ffd7c74aSJoe Talbott iwn_set_channel(struct ieee80211com *ic)
9047ffd7c74aSJoe Talbott {
9048ffd7c74aSJoe Talbott const struct ieee80211_channel *c = ic->ic_curchan;
90494f1aaf2fSImre Vadász struct iwn_softc *sc = ic->ic_softc;
9050da10ea93SMatthew Dillon int error;
9051ffd7c74aSJoe Talbott
9052da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__);
9053da10ea93SMatthew Dillon
905405538f72SMatthew Dillon IWN_LOCK(sc);
9055ffd7c74aSJoe Talbott sc->sc_rxtap.wr_chan_freq = htole16(c->ic_freq);
9056ffd7c74aSJoe Talbott sc->sc_rxtap.wr_chan_flags = htole16(c->ic_flags);
9057ffd7c74aSJoe Talbott sc->sc_txtap.wt_chan_freq = htole16(c->ic_freq);
9058ffd7c74aSJoe Talbott sc->sc_txtap.wt_chan_flags = htole16(c->ic_flags);
9059da10ea93SMatthew Dillon
9060da10ea93SMatthew Dillon /*
9061da10ea93SMatthew Dillon * Only need to set the channel in Monitor mode. AP scanning and auth
9062da10ea93SMatthew Dillon * are already taken care of by their respective firmware commands.
9063da10ea93SMatthew Dillon */
9064da10ea93SMatthew Dillon if (ic->ic_opmode == IEEE80211_M_MONITOR) {
9065da10ea93SMatthew Dillon error = iwn_config(sc);
9066da10ea93SMatthew Dillon if (error != 0)
9067da10ea93SMatthew Dillon device_printf(sc->sc_dev,
9068da10ea93SMatthew Dillon "%s: error %d settting channel\n", __func__, error);
9069da10ea93SMatthew Dillon }
907005538f72SMatthew Dillon IWN_UNLOCK(sc);
9071ffd7c74aSJoe Talbott }
9072ffd7c74aSJoe Talbott
9073ffd7c74aSJoe Talbott /*
9074ffd7c74aSJoe Talbott * Callback from net80211 to start scanning of the current channel.
9075ffd7c74aSJoe Talbott */
9076ffd7c74aSJoe Talbott static void
9077ffd7c74aSJoe Talbott iwn_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell)
9078ffd7c74aSJoe Talbott {
9079ffd7c74aSJoe Talbott struct ieee80211vap *vap = ss->ss_vap;
9080fd49669cSMichael Neumann struct ieee80211com *ic = vap->iv_ic;
9081977fc0dbSMatthew Dillon struct iwn_softc *sc = ic->ic_softc;
9082ffd7c74aSJoe Talbott int error;
9083ffd7c74aSJoe Talbott
908405538f72SMatthew Dillon IWN_LOCK(sc);
9085fd49669cSMichael Neumann error = iwn_scan(sc, vap, ss, ic->ic_curchan);
908605538f72SMatthew Dillon IWN_UNLOCK(sc);
9087ffd7c74aSJoe Talbott if (error != 0)
9088ffd7c74aSJoe Talbott ieee80211_cancel_scan(vap);
9089ffd7c74aSJoe Talbott }
9090ffd7c74aSJoe Talbott
9091ffd7c74aSJoe Talbott /*
9092ffd7c74aSJoe Talbott * Callback from net80211 to handle the minimum dwell time being met.
9093ffd7c74aSJoe Talbott * The intent is to terminate the scan but we just let the firmware
9094ffd7c74aSJoe Talbott * notify us when it's finished as we have no safe way to abort it.
9095ffd7c74aSJoe Talbott */
9096ffd7c74aSJoe Talbott static void
9097ffd7c74aSJoe Talbott iwn_scan_mindwell(struct ieee80211_scan_state *ss)
9098ffd7c74aSJoe Talbott {
9099ffd7c74aSJoe Talbott /* NB: don't try to abort scan; wait for firmware to finish */
9100ffd7c74aSJoe Talbott }
9101da10ea93SMatthew Dillon #ifdef IWN_DEBUG
9102da10ea93SMatthew Dillon #define IWN_DESC(x) case x: return #x
9103da10ea93SMatthew Dillon
9104da10ea93SMatthew Dillon /*
9105da10ea93SMatthew Dillon * Translate CSR code to string
9106da10ea93SMatthew Dillon */
9107da10ea93SMatthew Dillon static char *iwn_get_csr_string(int csr)
9108da10ea93SMatthew Dillon {
9109da10ea93SMatthew Dillon switch (csr) {
9110da10ea93SMatthew Dillon IWN_DESC(IWN_HW_IF_CONFIG);
9111da10ea93SMatthew Dillon IWN_DESC(IWN_INT_COALESCING);
9112da10ea93SMatthew Dillon IWN_DESC(IWN_INT);
9113da10ea93SMatthew Dillon IWN_DESC(IWN_INT_MASK);
9114da10ea93SMatthew Dillon IWN_DESC(IWN_FH_INT);
9115da10ea93SMatthew Dillon IWN_DESC(IWN_GPIO_IN);
9116da10ea93SMatthew Dillon IWN_DESC(IWN_RESET);
9117da10ea93SMatthew Dillon IWN_DESC(IWN_GP_CNTRL);
9118da10ea93SMatthew Dillon IWN_DESC(IWN_HW_REV);
9119da10ea93SMatthew Dillon IWN_DESC(IWN_EEPROM);
9120da10ea93SMatthew Dillon IWN_DESC(IWN_EEPROM_GP);
9121da10ea93SMatthew Dillon IWN_DESC(IWN_OTP_GP);
9122da10ea93SMatthew Dillon IWN_DESC(IWN_GIO);
9123da10ea93SMatthew Dillon IWN_DESC(IWN_GP_UCODE);
9124da10ea93SMatthew Dillon IWN_DESC(IWN_GP_DRIVER);
9125da10ea93SMatthew Dillon IWN_DESC(IWN_UCODE_GP1);
9126da10ea93SMatthew Dillon IWN_DESC(IWN_UCODE_GP2);
9127da10ea93SMatthew Dillon IWN_DESC(IWN_LED);
9128da10ea93SMatthew Dillon IWN_DESC(IWN_DRAM_INT_TBL);
9129da10ea93SMatthew Dillon IWN_DESC(IWN_GIO_CHICKEN);
9130da10ea93SMatthew Dillon IWN_DESC(IWN_ANA_PLL);
9131da10ea93SMatthew Dillon IWN_DESC(IWN_HW_REV_WA);
9132da10ea93SMatthew Dillon IWN_DESC(IWN_DBG_HPET_MEM);
9133da10ea93SMatthew Dillon default:
9134da10ea93SMatthew Dillon return "UNKNOWN CSR";
9135da10ea93SMatthew Dillon }
9136ffd7c74aSJoe Talbott }
9137ffd7c74aSJoe Talbott
9138ffd7c74aSJoe Talbott /*
9139da10ea93SMatthew Dillon * This function print firmware register
9140ffd7c74aSJoe Talbott */
9141da10ea93SMatthew Dillon static void
9142da10ea93SMatthew Dillon iwn_debug_register(struct iwn_softc *sc)
9143ffd7c74aSJoe Talbott {
9144ffd7c74aSJoe Talbott int i;
9145da10ea93SMatthew Dillon static const uint32_t csr_tbl[] = {
9146da10ea93SMatthew Dillon IWN_HW_IF_CONFIG,
9147da10ea93SMatthew Dillon IWN_INT_COALESCING,
9148da10ea93SMatthew Dillon IWN_INT,
9149da10ea93SMatthew Dillon IWN_INT_MASK,
9150da10ea93SMatthew Dillon IWN_FH_INT,
9151da10ea93SMatthew Dillon IWN_GPIO_IN,
9152da10ea93SMatthew Dillon IWN_RESET,
9153da10ea93SMatthew Dillon IWN_GP_CNTRL,
9154da10ea93SMatthew Dillon IWN_HW_REV,
9155da10ea93SMatthew Dillon IWN_EEPROM,
9156da10ea93SMatthew Dillon IWN_EEPROM_GP,
9157da10ea93SMatthew Dillon IWN_OTP_GP,
9158da10ea93SMatthew Dillon IWN_GIO,
9159da10ea93SMatthew Dillon IWN_GP_UCODE,
9160da10ea93SMatthew Dillon IWN_GP_DRIVER,
9161da10ea93SMatthew Dillon IWN_UCODE_GP1,
9162da10ea93SMatthew Dillon IWN_UCODE_GP2,
9163da10ea93SMatthew Dillon IWN_LED,
9164da10ea93SMatthew Dillon IWN_DRAM_INT_TBL,
9165da10ea93SMatthew Dillon IWN_GIO_CHICKEN,
9166da10ea93SMatthew Dillon IWN_ANA_PLL,
9167da10ea93SMatthew Dillon IWN_HW_REV_WA,
9168da10ea93SMatthew Dillon IWN_DBG_HPET_MEM,
9169da10ea93SMatthew Dillon };
9170da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_REGISTER,
9171da10ea93SMatthew Dillon "CSR values: (2nd byte of IWN_INT_COALESCING is IWN_INT_PERIODIC)%s",
9172da10ea93SMatthew Dillon "\n");
9173977fc0dbSMatthew Dillon for (i = 0; i < nitems(csr_tbl); i++){
9174da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_REGISTER," %10s: 0x%08x ",
9175da10ea93SMatthew Dillon iwn_get_csr_string(csr_tbl[i]), IWN_READ(sc, csr_tbl[i]));
9176da10ea93SMatthew Dillon if ((i+1) % 3 == 0)
9177da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_REGISTER,"%s","\n");
9178ffd7c74aSJoe Talbott }
9179da10ea93SMatthew Dillon DPRINTF(sc, IWN_DEBUG_REGISTER,"%s","\n");
9180ffd7c74aSJoe Talbott }
9181ffd7c74aSJoe Talbott #endif
918205538f72SMatthew Dillon
918305538f72SMatthew Dillon
9184