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