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