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