if_iwn.c revision 221634
1178676Ssam/*- 2198429Srpaulo * Copyright (c) 2007-2009 3178676Ssam * Damien Bergamini <damien.bergamini@free.fr> 4178676Ssam * Copyright (c) 2008 5178676Ssam * Benjamin Close <benjsc@FreeBSD.org> 6178676Ssam * Copyright (c) 2008 Sam Leffler, Errno Consulting 7178676Ssam * 8178676Ssam * Permission to use, copy, modify, and distribute this software for any 9178676Ssam * purpose with or without fee is hereby granted, provided that the above 10178676Ssam * copyright notice and this permission notice appear in all copies. 11178676Ssam * 12178676Ssam * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 13178676Ssam * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 14178676Ssam * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 15178676Ssam * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 16178676Ssam * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 17178676Ssam * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 18178676Ssam * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 19178676Ssam */ 20178676Ssam 21178676Ssam/* 22201209Srpaulo * Driver for Intel WiFi Link 4965 and 1000/5000/6000 Series 802.11 network 23201209Srpaulo * adapters. 24178676Ssam */ 25178676Ssam 26178676Ssam#include <sys/cdefs.h> 27178676Ssam__FBSDID("$FreeBSD: head/sys/dev/iwn/if_iwn.c 221634 2011-05-08 10:19:29Z bschmidt $"); 28178676Ssam 29178676Ssam#include <sys/param.h> 30178676Ssam#include <sys/sockio.h> 31178676Ssam#include <sys/sysctl.h> 32178676Ssam#include <sys/mbuf.h> 33178676Ssam#include <sys/kernel.h> 34178676Ssam#include <sys/socket.h> 35178676Ssam#include <sys/systm.h> 36178676Ssam#include <sys/malloc.h> 37178676Ssam#include <sys/bus.h> 38178676Ssam#include <sys/rman.h> 39178676Ssam#include <sys/endian.h> 40178676Ssam#include <sys/firmware.h> 41178676Ssam#include <sys/limits.h> 42178676Ssam#include <sys/module.h> 43178676Ssam#include <sys/queue.h> 44178676Ssam#include <sys/taskqueue.h> 45178676Ssam 46178676Ssam#include <machine/bus.h> 47178676Ssam#include <machine/resource.h> 48178676Ssam#include <machine/clock.h> 49178676Ssam 50178676Ssam#include <dev/pci/pcireg.h> 51178676Ssam#include <dev/pci/pcivar.h> 52178676Ssam 53178676Ssam#include <net/bpf.h> 54178676Ssam#include <net/if.h> 55178676Ssam#include <net/if_arp.h> 56178676Ssam#include <net/ethernet.h> 57178676Ssam#include <net/if_dl.h> 58178676Ssam#include <net/if_media.h> 59178676Ssam#include <net/if_types.h> 60178676Ssam 61178676Ssam#include <netinet/in.h> 62178676Ssam#include <netinet/in_systm.h> 63178676Ssam#include <netinet/in_var.h> 64178676Ssam#include <netinet/if_ether.h> 65178676Ssam#include <netinet/ip.h> 66178676Ssam 67178676Ssam#include <net80211/ieee80211_var.h> 68178676Ssam#include <net80211/ieee80211_radiotap.h> 69178676Ssam#include <net80211/ieee80211_regdomain.h> 70206358Srpaulo#include <net80211/ieee80211_ratectl.h> 71178676Ssam 72178676Ssam#include <dev/iwn/if_iwnreg.h> 73178676Ssam#include <dev/iwn/if_iwnvar.h> 74178676Ssam 75220723Sbschmidtstruct iwn_ident { 76220723Sbschmidt uint16_t vendor; 77220723Sbschmidt uint16_t device; 78220723Sbschmidt const char *name; 79220723Sbschmidt}; 80220723Sbschmidt 81220895Sbschmidtstatic const struct iwn_ident iwn_ident_table[] = { 82220895Sbschmidt { 0x8086, 0x0082, "Intel(R) Centrino(R) Advanced-N 6205" }, 83220895Sbschmidt { 0x8086, 0x0083, "Intel(R) Centrino(R) Wireless-N 1000" }, 84220895Sbschmidt { 0x8086, 0x0084, "Intel(R) Centrino(R) Wireless-N 1000" }, 85220895Sbschmidt { 0x8086, 0x0085, "Intel(R) Centrino(R) Advanced-N 6205" }, 86220895Sbschmidt { 0x8086, 0x0087, "Intel(R) Centrino(R) Advanced-N + WiMAX 6250" }, 87220895Sbschmidt { 0x8086, 0x0089, "Intel(R) Centrino(R) Advanced-N + WiMAX 6250" }, 88220895Sbschmidt { 0x8086, 0x008a, "Intel(R) Centrino(R) Wireless-N 1030" }, 89220895Sbschmidt { 0x8086, 0x008b, "Intel(R) Centrino(R) Wireless-N 1030" }, 90220895Sbschmidt { 0x8086, 0x0090, "Intel(R) Centrino(R) Advanced-N 6230" }, 91220895Sbschmidt { 0x8086, 0x0091, "Intel(R) Centrino(R) Advanced-N 6230" }, 92220895Sbschmidt { 0x8086, 0x4229, "Intel(R) Wireless WiFi Link 4965" }, 93220895Sbschmidt { 0x8086, 0x422b, "Intel(R) Centrino(R) Ultimate-N 6300" }, 94220895Sbschmidt { 0x8086, 0x422c, "Intel(R) Centrino(R) Advanced-N 6200" }, 95221634Sbschmidt { 0x8086, 0x422d, "Intel(R) Wireless WiFi Link 4965" }, 96220895Sbschmidt { 0x8086, 0x4230, "Intel(R) Wireless WiFi Link 4965" }, 97220895Sbschmidt { 0x8086, 0x4232, "Intel(R) WiFi Link 5100" }, 98221634Sbschmidt { 0x8086, 0x4233, "Intel(R) Wireless WiFi Link 4965" }, 99220895Sbschmidt { 0x8086, 0x4235, "Intel(R) Ultimate N WiFi Link 5300" }, 100220895Sbschmidt { 0x8086, 0x4236, "Intel(R) Ultimate N WiFi Link 5300" }, 101220895Sbschmidt { 0x8086, 0x4237, "Intel(R) WiFi Link 5100" }, 102220895Sbschmidt { 0x8086, 0x4238, "Intel(R) Centrino(R) Ultimate-N 6300" }, 103220895Sbschmidt { 0x8086, 0x4239, "Intel(R) Centrino(R) Advanced-N 6200" }, 104220895Sbschmidt { 0x8086, 0x423a, "Intel(R) WiMAX/WiFi Link 5350" }, 105220895Sbschmidt { 0x8086, 0x423b, "Intel(R) WiMAX/WiFi Link 5350" }, 106220895Sbschmidt { 0x8086, 0x423c, "Intel(R) WiMAX/WiFi Link 5150" }, 107220895Sbschmidt { 0x8086, 0x423d, "Intel(R) WiMAX/WiFi Link 5150" }, 108220723Sbschmidt { 0, 0, NULL } 109220723Sbschmidt}; 110220723Sbschmidt 111178676Ssamstatic int iwn_probe(device_t); 112178676Ssamstatic int iwn_attach(device_t); 113220728Sbschmidtstatic int iwn4965_attach(struct iwn_softc *, uint16_t); 114220728Sbschmidtstatic int iwn5000_attach(struct iwn_softc *, uint16_t); 115206477Sbschmidtstatic void iwn_radiotap_attach(struct iwn_softc *); 116220723Sbschmidtstatic void iwn_sysctlattach(struct iwn_softc *); 117178676Ssamstatic struct ieee80211vap *iwn_vap_create(struct ieee80211com *, 118178676Ssam const char name[IFNAMSIZ], int unit, int opmode, 119178676Ssam int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], 120178676Ssam const uint8_t mac[IEEE80211_ADDR_LEN]); 121178676Ssamstatic void iwn_vap_delete(struct ieee80211vap *); 122206474Sbschmidtstatic int iwn_detach(device_t); 123220723Sbschmidtstatic int iwn_shutdown(device_t); 124220723Sbschmidtstatic int iwn_suspend(device_t); 125220723Sbschmidtstatic int iwn_resume(device_t); 126206477Sbschmidtstatic int iwn_nic_lock(struct iwn_softc *); 127206477Sbschmidtstatic int iwn_eeprom_lock(struct iwn_softc *); 128206477Sbschmidtstatic int iwn_init_otprom(struct iwn_softc *); 129206477Sbschmidtstatic int iwn_read_prom_data(struct iwn_softc *, uint32_t, void *, int); 130206474Sbschmidtstatic void iwn_dma_map_addr(void *, bus_dma_segment_t *, int, int); 131178676Ssamstatic int iwn_dma_contig_alloc(struct iwn_softc *, struct iwn_dma_info *, 132220691Sbschmidt void **, bus_size_t, bus_size_t); 133178676Ssamstatic void iwn_dma_contig_free(struct iwn_dma_info *); 134206477Sbschmidtstatic int iwn_alloc_sched(struct iwn_softc *); 135206477Sbschmidtstatic void iwn_free_sched(struct iwn_softc *); 136206477Sbschmidtstatic int iwn_alloc_kw(struct iwn_softc *); 137206477Sbschmidtstatic void iwn_free_kw(struct iwn_softc *); 138206477Sbschmidtstatic int iwn_alloc_ict(struct iwn_softc *); 139206477Sbschmidtstatic void iwn_free_ict(struct iwn_softc *); 140206477Sbschmidtstatic int iwn_alloc_fwmem(struct iwn_softc *); 141206477Sbschmidtstatic void iwn_free_fwmem(struct iwn_softc *); 142206477Sbschmidtstatic int iwn_alloc_rx_ring(struct iwn_softc *, struct iwn_rx_ring *); 143206477Sbschmidtstatic void iwn_reset_rx_ring(struct iwn_softc *, struct iwn_rx_ring *); 144206477Sbschmidtstatic void iwn_free_rx_ring(struct iwn_softc *, struct iwn_rx_ring *); 145206477Sbschmidtstatic int iwn_alloc_tx_ring(struct iwn_softc *, struct iwn_tx_ring *, 146178676Ssam int); 147206477Sbschmidtstatic void iwn_reset_tx_ring(struct iwn_softc *, struct iwn_tx_ring *); 148206477Sbschmidtstatic void iwn_free_tx_ring(struct iwn_softc *, struct iwn_tx_ring *); 149206477Sbschmidtstatic void iwn5000_ict_reset(struct iwn_softc *); 150206477Sbschmidtstatic int iwn_read_eeprom(struct iwn_softc *, 151198429Srpaulo uint8_t macaddr[IEEE80211_ADDR_LEN]); 152206477Sbschmidtstatic void iwn4965_read_eeprom(struct iwn_softc *); 153206477Sbschmidtstatic void iwn4965_print_power_group(struct iwn_softc *, int); 154206477Sbschmidtstatic void iwn5000_read_eeprom(struct iwn_softc *); 155206474Sbschmidtstatic uint32_t iwn_eeprom_channel_flags(struct iwn_eeprom_chan *); 156206474Sbschmidtstatic void iwn_read_eeprom_band(struct iwn_softc *, int); 157206474Sbschmidt#if 0 /* HT */ 158206474Sbschmidtstatic void iwn_read_eeprom_ht40(struct iwn_softc *, int); 159206474Sbschmidt#endif 160220726Sbschmidtstatic void iwn_read_eeprom_channels(struct iwn_softc *, int, uint32_t); 161220723Sbschmidtstatic struct iwn_eeprom_chan *iwn_find_eeprom_channel(struct iwn_softc *, 162220723Sbschmidt struct ieee80211_channel *); 163220723Sbschmidtstatic int iwn_setregdomain(struct ieee80211com *, 164220723Sbschmidt struct ieee80211_regdomain *, int, 165220726Sbschmidt struct ieee80211_channel[]); 166206477Sbschmidtstatic void iwn_read_eeprom_enhinfo(struct iwn_softc *); 167206477Sbschmidtstatic struct ieee80211_node *iwn_node_alloc(struct ieee80211vap *, 168198429Srpaulo const uint8_t mac[IEEE80211_ADDR_LEN]); 169220715Sbschmidtstatic void iwn_newassoc(struct ieee80211_node *, int); 170206477Sbschmidtstatic int iwn_media_change(struct ifnet *); 171206477Sbschmidtstatic int iwn_newstate(struct ieee80211vap *, enum ieee80211_state, int); 172220667Sbschmidtstatic void iwn_calib_timeout(void *); 173206477Sbschmidtstatic void iwn_rx_phy(struct iwn_softc *, struct iwn_rx_desc *, 174198429Srpaulo struct iwn_rx_data *); 175206477Sbschmidtstatic void iwn_rx_done(struct iwn_softc *, struct iwn_rx_desc *, 176178676Ssam struct iwn_rx_data *); 177201209Srpaulo#if 0 /* HT */ 178206477Sbschmidtstatic void iwn_rx_compressed_ba(struct iwn_softc *, struct iwn_rx_desc *, 179201209Srpaulo struct iwn_rx_data *); 180201209Srpaulo#endif 181220674Sbschmidtstatic void iwn5000_rx_calib_results(struct iwn_softc *, 182220674Sbschmidt struct iwn_rx_desc *, struct iwn_rx_data *); 183206477Sbschmidtstatic void iwn_rx_statistics(struct iwn_softc *, struct iwn_rx_desc *, 184198429Srpaulo struct iwn_rx_data *); 185206477Sbschmidtstatic void iwn4965_tx_done(struct iwn_softc *, struct iwn_rx_desc *, 186198429Srpaulo struct iwn_rx_data *); 187206477Sbschmidtstatic void iwn5000_tx_done(struct iwn_softc *, struct iwn_rx_desc *, 188198429Srpaulo struct iwn_rx_data *); 189206477Sbschmidtstatic void iwn_tx_done(struct iwn_softc *, struct iwn_rx_desc *, int, 190198429Srpaulo uint8_t); 191206477Sbschmidtstatic void iwn_cmd_done(struct iwn_softc *, struct iwn_rx_desc *); 192206477Sbschmidtstatic void iwn_notif_intr(struct iwn_softc *); 193206477Sbschmidtstatic void iwn_wakeup_intr(struct iwn_softc *); 194206477Sbschmidtstatic void iwn_rftoggle_intr(struct iwn_softc *); 195206477Sbschmidtstatic void iwn_fatal_intr(struct iwn_softc *); 196206477Sbschmidtstatic void iwn_intr(void *); 197206477Sbschmidtstatic void iwn4965_update_sched(struct iwn_softc *, int, int, uint8_t, 198198429Srpaulo uint16_t); 199206477Sbschmidtstatic void iwn5000_update_sched(struct iwn_softc *, int, int, uint8_t, 200198429Srpaulo uint16_t); 201206475Sbschmidt#ifdef notyet 202206477Sbschmidtstatic void iwn5000_reset_sched(struct iwn_softc *, int, int); 203206475Sbschmidt#endif 204206474Sbschmidtstatic uint8_t iwn_plcp_signal(int); 205206477Sbschmidtstatic int iwn_tx_data(struct iwn_softc *, struct mbuf *, 206220720Sbschmidt struct ieee80211_node *); 207220720Sbschmidtstatic int iwn_tx_data_raw(struct iwn_softc *, struct mbuf *, 208220720Sbschmidt struct ieee80211_node *, 209220720Sbschmidt const struct ieee80211_bpf_params *params); 210198429Srpaulostatic int iwn_raw_xmit(struct ieee80211_node *, struct mbuf *, 211198429Srpaulo const struct ieee80211_bpf_params *); 212206477Sbschmidtstatic void iwn_start(struct ifnet *); 213206477Sbschmidtstatic void iwn_start_locked(struct ifnet *); 214220667Sbschmidtstatic void iwn_watchdog(void *); 215206477Sbschmidtstatic int iwn_ioctl(struct ifnet *, u_long, caddr_t); 216206477Sbschmidtstatic int iwn_cmd(struct iwn_softc *, int, const void *, int, int); 217206477Sbschmidtstatic int iwn4965_add_node(struct iwn_softc *, struct iwn_node_info *, 218198429Srpaulo int); 219206477Sbschmidtstatic int iwn5000_add_node(struct iwn_softc *, struct iwn_node_info *, 220198429Srpaulo int); 221220715Sbschmidtstatic int iwn_set_link_quality(struct iwn_softc *, 222220715Sbschmidt struct ieee80211_node *); 223206477Sbschmidtstatic int iwn_add_broadcast_node(struct iwn_softc *, int); 224220721Sbschmidtstatic int iwn_updateedca(struct ieee80211com *); 225201209Srpaulostatic void iwn_update_mcast(struct ifnet *); 226206477Sbschmidtstatic void iwn_set_led(struct iwn_softc *, uint8_t, uint8_t, uint8_t); 227206477Sbschmidtstatic int iwn_set_critical_temp(struct iwn_softc *); 228206477Sbschmidtstatic int iwn_set_timing(struct iwn_softc *, struct ieee80211_node *); 229206477Sbschmidtstatic void iwn4965_power_calibration(struct iwn_softc *, int); 230206477Sbschmidtstatic int iwn4965_set_txpower(struct iwn_softc *, 231201882Skeramida struct ieee80211_channel *, int); 232206477Sbschmidtstatic int iwn5000_set_txpower(struct iwn_softc *, 233201882Skeramida struct ieee80211_channel *, int); 234206477Sbschmidtstatic int iwn4965_get_rssi(struct iwn_softc *, struct iwn_rx_stat *); 235206477Sbschmidtstatic int iwn5000_get_rssi(struct iwn_softc *, struct iwn_rx_stat *); 236206477Sbschmidtstatic int iwn_get_noise(const struct iwn_rx_general_stats *); 237206477Sbschmidtstatic int iwn4965_get_temperature(struct iwn_softc *); 238206477Sbschmidtstatic int iwn5000_get_temperature(struct iwn_softc *); 239206477Sbschmidtstatic int iwn_init_sensitivity(struct iwn_softc *); 240206477Sbschmidtstatic void iwn_collect_noise(struct iwn_softc *, 241178676Ssam const struct iwn_rx_general_stats *); 242206477Sbschmidtstatic int iwn4965_init_gains(struct iwn_softc *); 243206477Sbschmidtstatic int iwn5000_init_gains(struct iwn_softc *); 244206477Sbschmidtstatic int iwn4965_set_gains(struct iwn_softc *); 245206477Sbschmidtstatic int iwn5000_set_gains(struct iwn_softc *); 246206477Sbschmidtstatic void iwn_tune_sensitivity(struct iwn_softc *, 247178676Ssam const struct iwn_rx_stats *); 248206477Sbschmidtstatic int iwn_send_sensitivity(struct iwn_softc *); 249206477Sbschmidtstatic int iwn_set_pslevel(struct iwn_softc *, int, int, int); 250220662Sbschmidtstatic int iwn_send_btcoex(struct iwn_softc *); 251220891Sbschmidtstatic int iwn_send_advanced_btcoex(struct iwn_softc *); 252206477Sbschmidtstatic int iwn_config(struct iwn_softc *); 253220634Sbschmidtstatic uint8_t *ieee80211_add_ssid(uint8_t *, const uint8_t *, u_int); 254206477Sbschmidtstatic int iwn_scan(struct iwn_softc *); 255206477Sbschmidtstatic int iwn_auth(struct iwn_softc *, struct ieee80211vap *vap); 256206477Sbschmidtstatic int iwn_run(struct iwn_softc *, struct ieee80211vap *vap); 257206474Sbschmidt#if 0 /* HT */ 258206474Sbschmidtstatic int iwn_ampdu_rx_start(struct ieee80211com *, 259206474Sbschmidt struct ieee80211_node *, uint8_t); 260206474Sbschmidtstatic void iwn_ampdu_rx_stop(struct ieee80211com *, 261206474Sbschmidt struct ieee80211_node *, uint8_t); 262206474Sbschmidtstatic int iwn_ampdu_tx_start(struct ieee80211com *, 263206474Sbschmidt struct ieee80211_node *, uint8_t); 264206474Sbschmidtstatic void iwn_ampdu_tx_stop(struct ieee80211com *, 265206474Sbschmidt struct ieee80211_node *, uint8_t); 266206474Sbschmidtstatic void iwn4965_ampdu_tx_start(struct iwn_softc *, 267206474Sbschmidt struct ieee80211_node *, uint8_t, uint16_t); 268220726Sbschmidtstatic void iwn4965_ampdu_tx_stop(struct iwn_softc *, 269220726Sbschmidt uint8_t, uint16_t); 270206474Sbschmidtstatic void iwn5000_ampdu_tx_start(struct iwn_softc *, 271206474Sbschmidt struct ieee80211_node *, uint8_t, uint16_t); 272220726Sbschmidtstatic void iwn5000_ampdu_tx_stop(struct iwn_softc *, 273220726Sbschmidt uint8_t, uint16_t); 274206474Sbschmidt#endif 275220674Sbschmidtstatic int iwn5000_query_calibration(struct iwn_softc *); 276220674Sbschmidtstatic int iwn5000_send_calibration(struct iwn_softc *); 277206477Sbschmidtstatic int iwn5000_send_wimax_coex(struct iwn_softc *); 278220677Sbschmidtstatic int iwn5000_crystal_calib(struct iwn_softc *); 279220676Sbschmidtstatic int iwn5000_temp_offset_calib(struct iwn_softc *); 280206477Sbschmidtstatic int iwn4965_post_alive(struct iwn_softc *); 281206477Sbschmidtstatic int iwn5000_post_alive(struct iwn_softc *); 282206477Sbschmidtstatic int iwn4965_load_bootcode(struct iwn_softc *, const uint8_t *, 283198429Srpaulo int); 284206477Sbschmidtstatic int iwn4965_load_firmware(struct iwn_softc *); 285206477Sbschmidtstatic int iwn5000_load_firmware_section(struct iwn_softc *, uint32_t, 286198429Srpaulo const uint8_t *, int); 287206477Sbschmidtstatic int iwn5000_load_firmware(struct iwn_softc *); 288210111Sbschmidtstatic int iwn_read_firmware_leg(struct iwn_softc *, 289210111Sbschmidt struct iwn_fw_info *); 290210111Sbschmidtstatic int iwn_read_firmware_tlv(struct iwn_softc *, 291210111Sbschmidt struct iwn_fw_info *, uint16_t); 292206477Sbschmidtstatic int iwn_read_firmware(struct iwn_softc *); 293206477Sbschmidtstatic int iwn_clock_wait(struct iwn_softc *); 294206477Sbschmidtstatic int iwn_apm_init(struct iwn_softc *); 295206477Sbschmidtstatic void iwn_apm_stop_master(struct iwn_softc *); 296206477Sbschmidtstatic void iwn_apm_stop(struct iwn_softc *); 297206477Sbschmidtstatic int iwn4965_nic_config(struct iwn_softc *); 298206477Sbschmidtstatic int iwn5000_nic_config(struct iwn_softc *); 299206477Sbschmidtstatic int iwn_hw_prepare(struct iwn_softc *); 300206477Sbschmidtstatic int iwn_hw_init(struct iwn_softc *); 301206477Sbschmidtstatic void iwn_hw_stop(struct iwn_softc *); 302220723Sbschmidtstatic void iwn_radio_on(void *, int); 303220723Sbschmidtstatic void iwn_radio_off(void *, int); 304206477Sbschmidtstatic void iwn_init_locked(struct iwn_softc *); 305206477Sbschmidtstatic void iwn_init(void *); 306206477Sbschmidtstatic void iwn_stop_locked(struct iwn_softc *); 307206477Sbschmidtstatic void iwn_stop(struct iwn_softc *); 308220726Sbschmidtstatic void iwn_scan_start(struct ieee80211com *); 309220726Sbschmidtstatic void iwn_scan_end(struct ieee80211com *); 310220726Sbschmidtstatic void iwn_set_channel(struct ieee80211com *); 311220726Sbschmidtstatic void iwn_scan_curchan(struct ieee80211_scan_state *, unsigned long); 312220726Sbschmidtstatic void iwn_scan_mindwell(struct ieee80211_scan_state *); 313198429Srpaulostatic void iwn_hw_reset(void *, int); 314178676Ssam 315178676Ssam#define IWN_DEBUG 316178676Ssam#ifdef IWN_DEBUG 317178676Ssamenum { 318178676Ssam IWN_DEBUG_XMIT = 0x00000001, /* basic xmit operation */ 319178676Ssam IWN_DEBUG_RECV = 0x00000002, /* basic recv operation */ 320178676Ssam IWN_DEBUG_STATE = 0x00000004, /* 802.11 state transitions */ 321178676Ssam IWN_DEBUG_TXPOW = 0x00000008, /* tx power processing */ 322178676Ssam IWN_DEBUG_RESET = 0x00000010, /* reset processing */ 323178676Ssam IWN_DEBUG_OPS = 0x00000020, /* iwn_ops processing */ 324178676Ssam IWN_DEBUG_BEACON = 0x00000040, /* beacon handling */ 325178676Ssam IWN_DEBUG_WATCHDOG = 0x00000080, /* watchdog timeout */ 326178676Ssam IWN_DEBUG_INTR = 0x00000100, /* ISR */ 327178676Ssam IWN_DEBUG_CALIBRATE = 0x00000200, /* periodic calibration */ 328178676Ssam IWN_DEBUG_NODE = 0x00000400, /* node management */ 329178676Ssam IWN_DEBUG_LED = 0x00000800, /* led management */ 330178676Ssam IWN_DEBUG_CMD = 0x00001000, /* cmd submission */ 331178676Ssam IWN_DEBUG_FATAL = 0x80000000, /* fatal errors */ 332178676Ssam IWN_DEBUG_ANY = 0xffffffff 333178676Ssam}; 334178676Ssam 335178676Ssam#define DPRINTF(sc, m, fmt, ...) do { \ 336178676Ssam if (sc->sc_debug & (m)) \ 337178676Ssam printf(fmt, __VA_ARGS__); \ 338178676Ssam} while (0) 339178676Ssam 340220723Sbschmidtstatic const char * 341220723Sbschmidtiwn_intr_str(uint8_t cmd) 342220723Sbschmidt{ 343220723Sbschmidt switch (cmd) { 344220723Sbschmidt /* Notifications */ 345220723Sbschmidt case IWN_UC_READY: return "UC_READY"; 346220723Sbschmidt case IWN_ADD_NODE_DONE: return "ADD_NODE_DONE"; 347220723Sbschmidt case IWN_TX_DONE: return "TX_DONE"; 348220723Sbschmidt case IWN_START_SCAN: return "START_SCAN"; 349220723Sbschmidt case IWN_STOP_SCAN: return "STOP_SCAN"; 350220723Sbschmidt case IWN_RX_STATISTICS: return "RX_STATS"; 351220723Sbschmidt case IWN_BEACON_STATISTICS: return "BEACON_STATS"; 352220723Sbschmidt case IWN_STATE_CHANGED: return "STATE_CHANGED"; 353220723Sbschmidt case IWN_BEACON_MISSED: return "BEACON_MISSED"; 354220723Sbschmidt case IWN_RX_PHY: return "RX_PHY"; 355220723Sbschmidt case IWN_MPDU_RX_DONE: return "MPDU_RX_DONE"; 356220723Sbschmidt case IWN_RX_DONE: return "RX_DONE"; 357220723Sbschmidt 358220723Sbschmidt /* Command Notifications */ 359220723Sbschmidt case IWN_CMD_RXON: return "IWN_CMD_RXON"; 360220723Sbschmidt case IWN_CMD_RXON_ASSOC: return "IWN_CMD_RXON_ASSOC"; 361220723Sbschmidt case IWN_CMD_EDCA_PARAMS: return "IWN_CMD_EDCA_PARAMS"; 362220723Sbschmidt case IWN_CMD_TIMING: return "IWN_CMD_TIMING"; 363220723Sbschmidt case IWN_CMD_LINK_QUALITY: return "IWN_CMD_LINK_QUALITY"; 364220723Sbschmidt case IWN_CMD_SET_LED: return "IWN_CMD_SET_LED"; 365220723Sbschmidt case IWN5000_CMD_WIMAX_COEX: return "IWN5000_CMD_WIMAX_COEX"; 366220723Sbschmidt case IWN5000_CMD_CALIB_CONFIG: return "IWN5000_CMD_CALIB_CONFIG"; 367220723Sbschmidt case IWN5000_CMD_CALIB_RESULT: return "IWN5000_CMD_CALIB_RESULT"; 368220723Sbschmidt case IWN5000_CMD_CALIB_COMPLETE: return "IWN5000_CMD_CALIB_COMPLETE"; 369220723Sbschmidt case IWN_CMD_SET_POWER_MODE: return "IWN_CMD_SET_POWER_MODE"; 370220723Sbschmidt case IWN_CMD_SCAN: return "IWN_CMD_SCAN"; 371220723Sbschmidt case IWN_CMD_SCAN_RESULTS: return "IWN_CMD_SCAN_RESULTS"; 372220723Sbschmidt case IWN_CMD_TXPOWER: return "IWN_CMD_TXPOWER"; 373220723Sbschmidt case IWN_CMD_TXPOWER_DBM: return "IWN_CMD_TXPOWER_DBM"; 374220723Sbschmidt case IWN5000_CMD_TX_ANT_CONFIG: return "IWN5000_CMD_TX_ANT_CONFIG"; 375220723Sbschmidt case IWN_CMD_BT_COEX: return "IWN_CMD_BT_COEX"; 376220723Sbschmidt case IWN_CMD_SET_CRITICAL_TEMP: return "IWN_CMD_SET_CRITICAL_TEMP"; 377220723Sbschmidt case IWN_CMD_SET_SENSITIVITY: return "IWN_CMD_SET_SENSITIVITY"; 378220723Sbschmidt case IWN_CMD_PHY_CALIB: return "IWN_CMD_PHY_CALIB"; 379220723Sbschmidt } 380220723Sbschmidt return "UNKNOWN INTR NOTIF/CMD"; 381220723Sbschmidt} 382178676Ssam#else 383178676Ssam#define DPRINTF(sc, m, fmt, ...) do { (void) sc; } while (0) 384178676Ssam#endif 385178676Ssam 386220723Sbschmidtstatic device_method_t iwn_methods[] = { 387220723Sbschmidt /* Device interface */ 388220723Sbschmidt DEVMETHOD(device_probe, iwn_probe), 389220723Sbschmidt DEVMETHOD(device_attach, iwn_attach), 390220723Sbschmidt DEVMETHOD(device_detach, iwn_detach), 391220723Sbschmidt DEVMETHOD(device_shutdown, iwn_shutdown), 392220723Sbschmidt DEVMETHOD(device_suspend, iwn_suspend), 393220723Sbschmidt DEVMETHOD(device_resume, iwn_resume), 394220723Sbschmidt { 0, 0 } 395178676Ssam}; 396178676Ssam 397220723Sbschmidtstatic driver_t iwn_driver = { 398220723Sbschmidt "iwn", 399220723Sbschmidt iwn_methods, 400220726Sbschmidt sizeof(struct iwn_softc) 401178676Ssam}; 402220723Sbschmidtstatic devclass_t iwn_devclass; 403178676Ssam 404220723SbschmidtDRIVER_MODULE(iwn, pci, iwn_driver, iwn_devclass, 0, 0); 405220726Sbschmidt 406220723SbschmidtMODULE_DEPEND(iwn, firmware, 1, 1, 1); 407220723SbschmidtMODULE_DEPEND(iwn, pci, 1, 1, 1); 408220723SbschmidtMODULE_DEPEND(iwn, wlan, 1, 1, 1); 409220723Sbschmidt 410178676Ssamstatic int 411178676Ssamiwn_probe(device_t dev) 412178676Ssam{ 413198429Srpaulo const struct iwn_ident *ident; 414178676Ssam 415198429Srpaulo for (ident = iwn_ident_table; ident->name != NULL; ident++) { 416198429Srpaulo if (pci_get_vendor(dev) == ident->vendor && 417198429Srpaulo pci_get_device(dev) == ident->device) { 418198429Srpaulo device_set_desc(dev, ident->name); 419198429Srpaulo return 0; 420198429Srpaulo } 421198429Srpaulo } 422198429Srpaulo return ENXIO; 423178676Ssam} 424178676Ssam 425178676Ssamstatic int 426178676Ssamiwn_attach(device_t dev) 427178676Ssam{ 428178676Ssam struct iwn_softc *sc = (struct iwn_softc *)device_get_softc(dev); 429178676Ssam struct ieee80211com *ic; 430178676Ssam struct ifnet *ifp; 431220721Sbschmidt uint32_t reg; 432184233Smav int i, error, result; 433190526Ssam uint8_t macaddr[IEEE80211_ADDR_LEN]; 434178676Ssam 435178676Ssam sc->sc_dev = dev; 436178676Ssam 437198429Srpaulo /* 438198429Srpaulo * Get the offset of the PCI Express Capability Structure in PCI 439198429Srpaulo * Configuration Space. 440198429Srpaulo */ 441219902Sjhb error = pci_find_cap(dev, PCIY_EXPRESS, &sc->sc_cap_off); 442198429Srpaulo if (error != 0) { 443198429Srpaulo device_printf(dev, "PCIe capability structure not found!\n"); 444198429Srpaulo return error; 445178676Ssam } 446178676Ssam 447198429Srpaulo /* Clear device-specific "PCI retry timeout" register (41h). */ 448178676Ssam pci_write_config(dev, 0x41, 0, 1); 449178676Ssam 450198429Srpaulo /* Hardware bug workaround. */ 451220721Sbschmidt reg = pci_read_config(dev, PCIR_COMMAND, 1); 452220721Sbschmidt if (reg & PCIM_CMD_INTxDIS) { 453198429Srpaulo DPRINTF(sc, IWN_DEBUG_RESET, "%s: PCIe INTx Disable set\n", 454198429Srpaulo __func__); 455220721Sbschmidt reg &= ~PCIM_CMD_INTxDIS; 456220721Sbschmidt pci_write_config(dev, PCIR_COMMAND, reg, 1); 457198429Srpaulo } 458198429Srpaulo 459198429Srpaulo /* Enable bus-mastering. */ 460178676Ssam pci_enable_busmaster(dev); 461178676Ssam 462198429Srpaulo sc->mem_rid = PCIR_BAR(0); 463178676Ssam sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid, 464198429Srpaulo RF_ACTIVE); 465220726Sbschmidt if (sc->mem == NULL) { 466220724Sbschmidt device_printf(dev, "can't map mem space\n"); 467198429Srpaulo error = ENOMEM; 468178676Ssam return error; 469178676Ssam } 470178676Ssam sc->sc_st = rman_get_bustag(sc->mem); 471178676Ssam sc->sc_sh = rman_get_bushandle(sc->mem); 472220726Sbschmidt 473178676Ssam sc->irq_rid = 0; 474184233Smav if ((result = pci_msi_count(dev)) == 1 && 475184233Smav pci_alloc_msi(dev, &result) == 0) 476184233Smav sc->irq_rid = 1; 477220725Sbschmidt /* Install interrupt handler. */ 478178676Ssam sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irq_rid, 479198429Srpaulo RF_ACTIVE | RF_SHAREABLE); 480178676Ssam if (sc->irq == NULL) { 481220724Sbschmidt device_printf(dev, "can't map interrupt\n"); 482178676Ssam error = ENOMEM; 483198429Srpaulo goto fail; 484178676Ssam } 485178676Ssam 486178676Ssam IWN_LOCK_INIT(sc); 487178676Ssam 488220728Sbschmidt /* Read hardware revision and attach. */ 489220728Sbschmidt sc->hw_type = (IWN_READ(sc, IWN_HW_REV) >> 4) & 0xf; 490220728Sbschmidt if (sc->hw_type == IWN_HW_REV_TYPE_4965) 491220728Sbschmidt error = iwn4965_attach(sc, pci_get_device(dev)); 492220728Sbschmidt else 493220728Sbschmidt error = iwn5000_attach(sc, pci_get_device(dev)); 494220728Sbschmidt if (error != 0) { 495220728Sbschmidt device_printf(dev, "could not attach device, error %d\n", 496220728Sbschmidt error); 497198429Srpaulo goto fail; 498198429Srpaulo } 499198429Srpaulo 500220726Sbschmidt if ((error = iwn_hw_prepare(sc)) != 0) { 501198429Srpaulo device_printf(dev, "hardware not ready, error %d\n", error); 502178676Ssam goto fail; 503178676Ssam } 504178676Ssam 505198429Srpaulo /* Allocate DMA memory for firmware transfers. */ 506220726Sbschmidt if ((error = iwn_alloc_fwmem(sc)) != 0) { 507178676Ssam device_printf(dev, 508198429Srpaulo "could not allocate memory for firmware, error %d\n", 509198429Srpaulo error); 510178676Ssam goto fail; 511178676Ssam } 512178676Ssam 513198429Srpaulo /* Allocate "Keep Warm" page. */ 514220726Sbschmidt if ((error = iwn_alloc_kw(sc)) != 0) { 515178676Ssam device_printf(dev, 516220724Sbschmidt "could not allocate keep warm page, error %d\n", error); 517178676Ssam goto fail; 518178676Ssam } 519178676Ssam 520201209Srpaulo /* Allocate ICT table for 5000 Series. */ 521201209Srpaulo if (sc->hw_type != IWN_HW_REV_TYPE_4965 && 522201209Srpaulo (error = iwn_alloc_ict(sc)) != 0) { 523220724Sbschmidt device_printf(dev, "could not allocate ICT table, error %d\n", 524220724Sbschmidt error); 525201209Srpaulo goto fail; 526201209Srpaulo } 527201209Srpaulo 528198429Srpaulo /* Allocate TX scheduler "rings". */ 529220726Sbschmidt if ((error = iwn_alloc_sched(sc)) != 0) { 530178676Ssam device_printf(dev, 531220726Sbschmidt "could not allocate TX scheduler rings, error %d\n", error); 532178676Ssam goto fail; 533178676Ssam } 534178676Ssam 535220725Sbschmidt /* Allocate TX rings (16 on 4965AGN, 20 on >=5000). */ 536220728Sbschmidt for (i = 0; i < sc->ntxqs; i++) { 537220726Sbschmidt if ((error = iwn_alloc_tx_ring(sc, &sc->txq[i], i)) != 0) { 538178676Ssam device_printf(dev, 539220724Sbschmidt "could not allocate TX ring %d, error %d\n", i, 540220724Sbschmidt error); 541178676Ssam goto fail; 542178676Ssam } 543178676Ssam } 544178676Ssam 545198429Srpaulo /* Allocate RX ring. */ 546220726Sbschmidt if ((error = iwn_alloc_rx_ring(sc, &sc->rxq)) != 0) { 547220724Sbschmidt device_printf(dev, "could not allocate RX ring, error %d\n", 548220724Sbschmidt error); 549178676Ssam goto fail; 550178676Ssam } 551178676Ssam 552198429Srpaulo /* Clear pending interrupts. */ 553198429Srpaulo IWN_WRITE(sc, IWN_INT, 0xffffffff); 554198429Srpaulo 555201209Srpaulo /* Count the number of available chains. */ 556201209Srpaulo sc->ntxchains = 557201209Srpaulo ((sc->txchainmask >> 2) & 1) + 558201209Srpaulo ((sc->txchainmask >> 1) & 1) + 559201209Srpaulo ((sc->txchainmask >> 0) & 1); 560201209Srpaulo sc->nrxchains = 561201209Srpaulo ((sc->rxchainmask >> 2) & 1) + 562201209Srpaulo ((sc->rxchainmask >> 1) & 1) + 563201209Srpaulo ((sc->rxchainmask >> 0) & 1); 564220724Sbschmidt if (bootverbose) { 565220724Sbschmidt device_printf(dev, "MIMO %dT%dR, %.4s, address %6D\n", 566220724Sbschmidt sc->ntxchains, sc->nrxchains, sc->eeprom_domain, 567220724Sbschmidt macaddr, ":"); 568220724Sbschmidt } 569198429Srpaulo 570178676Ssam ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211); 571178676Ssam if (ifp == NULL) { 572178676Ssam device_printf(dev, "can not allocate ifnet structure\n"); 573178676Ssam goto fail; 574178676Ssam } 575220726Sbschmidt 576178676Ssam ic = ifp->if_l2com; 577198429Srpaulo ic->ic_ifp = ifp; 578178676Ssam ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ 579178676Ssam ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ 580178676Ssam 581198429Srpaulo /* Set device capabilities. */ 582178676Ssam ic->ic_caps = 583178957Ssam IEEE80211_C_STA /* station mode supported */ 584178957Ssam | IEEE80211_C_MONITOR /* monitor mode supported */ 585178676Ssam | IEEE80211_C_TXPMGT /* tx power management */ 586178676Ssam | IEEE80211_C_SHSLOT /* short slot time supported */ 587178676Ssam | IEEE80211_C_WPA 588178676Ssam | IEEE80211_C_SHPREAMBLE /* short preamble supported */ 589201209Srpaulo | IEEE80211_C_BGSCAN /* background scanning */ 590178676Ssam#if 0 591178676Ssam | IEEE80211_C_IBSS /* ibss/adhoc mode */ 592178676Ssam#endif 593178676Ssam | IEEE80211_C_WME /* WME */ 594178676Ssam ; 595201209Srpaulo#if 0 /* HT */ 596178678Ssam /* XXX disable until HT channel setup works */ 597178676Ssam ic->ic_htcaps = 598178676Ssam IEEE80211_HTCAP_SMPS_ENA /* SM PS mode enabled */ 599178676Ssam | IEEE80211_HTCAP_CHWIDTH40 /* 40MHz channel width */ 600178676Ssam | IEEE80211_HTCAP_SHORTGI20 /* short GI in 20MHz */ 601178676Ssam | IEEE80211_HTCAP_SHORTGI40 /* short GI in 40MHz */ 602178676Ssam | IEEE80211_HTCAP_RXSTBC_2STREAM/* 1-2 spatial streams */ 603178676Ssam | IEEE80211_HTCAP_MAXAMSDU_3839 /* max A-MSDU length */ 604178676Ssam /* s/w capabilities */ 605178676Ssam | IEEE80211_HTC_HT /* HT operation */ 606178676Ssam | IEEE80211_HTC_AMPDU /* tx A-MPDU */ 607178676Ssam | IEEE80211_HTC_AMSDU /* tx A-MSDU */ 608178676Ssam ; 609201209Srpaulo 610201209Srpaulo /* Set HT capabilities. */ 611201209Srpaulo ic->ic_htcaps = 612201209Srpaulo#if IWN_RBUF_SIZE == 8192 613201209Srpaulo IEEE80211_HTCAP_AMSDU7935 | 614178678Ssam#endif 615201209Srpaulo IEEE80211_HTCAP_CBW20_40 | 616201209Srpaulo IEEE80211_HTCAP_SGI20 | 617201209Srpaulo IEEE80211_HTCAP_SGI40; 618201209Srpaulo if (sc->hw_type != IWN_HW_REV_TYPE_4965) 619201209Srpaulo ic->ic_htcaps |= IEEE80211_HTCAP_GF; 620206444Sbschmidt if (sc->hw_type == IWN_HW_REV_TYPE_6050) 621206444Sbschmidt ic->ic_htcaps |= IEEE80211_HTCAP_SMPS_DYN; 622206444Sbschmidt else 623206444Sbschmidt ic->ic_htcaps |= IEEE80211_HTCAP_SMPS_DIS; 624201209Srpaulo#endif 625178676Ssam 626198429Srpaulo /* Read MAC address, channels, etc from EEPROM. */ 627220726Sbschmidt if ((error = iwn_read_eeprom(sc, macaddr)) != 0) { 628198429Srpaulo device_printf(dev, "could not read EEPROM, error %d\n", 629198429Srpaulo error); 630198429Srpaulo goto fail; 631198429Srpaulo } 632198429Srpaulo 633201209Srpaulo#if 0 /* HT */ 634201209Srpaulo /* Set supported HT rates. */ 635201209Srpaulo ic->ic_sup_mcs[0] = 0xff; 636201209Srpaulo if (sc->nrxchains > 1) 637201209Srpaulo ic->ic_sup_mcs[1] = 0xff; 638201209Srpaulo if (sc->nrxchains > 2) 639201209Srpaulo ic->ic_sup_mcs[2] = 0xff; 640201209Srpaulo#endif 641201209Srpaulo 642178676Ssam if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 643178676Ssam ifp->if_softc = sc; 644178676Ssam ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 645178676Ssam ifp->if_init = iwn_init; 646178676Ssam ifp->if_ioctl = iwn_ioctl; 647178676Ssam ifp->if_start = iwn_start; 648207554Ssobomax IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); 649207554Ssobomax ifp->if_snd.ifq_drv_maxlen = ifqmaxlen; 650178676Ssam IFQ_SET_READY(&ifp->if_snd); 651178676Ssam 652190526Ssam ieee80211_ifattach(ic, macaddr); 653178676Ssam ic->ic_vap_create = iwn_vap_create; 654178676Ssam ic->ic_vap_delete = iwn_vap_delete; 655178676Ssam ic->ic_raw_xmit = iwn_raw_xmit; 656178676Ssam ic->ic_node_alloc = iwn_node_alloc; 657220723Sbschmidt#if 0 /* HT */ 658220723Sbschmidt ic->ic_ampdu_rx_start = iwn_ampdu_rx_start; 659220723Sbschmidt ic->ic_ampdu_rx_stop = iwn_ampdu_rx_stop; 660220723Sbschmidt ic->ic_ampdu_tx_start = iwn_ampdu_tx_start; 661220723Sbschmidt ic->ic_ampdu_tx_stop = iwn_ampdu_tx_stop; 662220723Sbschmidt#endif 663220715Sbschmidt ic->ic_newassoc = iwn_newassoc; 664220721Sbschmidt ic->ic_wme.wme_update = iwn_updateedca; 665201209Srpaulo ic->ic_update_mcast = iwn_update_mcast; 666198429Srpaulo ic->ic_scan_start = iwn_scan_start; 667198429Srpaulo ic->ic_scan_end = iwn_scan_end; 668198429Srpaulo ic->ic_set_channel = iwn_set_channel; 669198429Srpaulo ic->ic_scan_curchan = iwn_scan_curchan; 670198429Srpaulo ic->ic_scan_mindwell = iwn_scan_mindwell; 671201209Srpaulo ic->ic_setregdomain = iwn_setregdomain; 672178676Ssam 673198429Srpaulo iwn_radiotap_attach(sc); 674220667Sbschmidt 675220667Sbschmidt callout_init_mtx(&sc->calib_to, &sc->sc_mtx, 0); 676220667Sbschmidt callout_init_mtx(&sc->watchdog_to, &sc->sc_mtx, 0); 677220726Sbschmidt TASK_INIT(&sc->sc_reinit_task, 0, iwn_hw_reset, sc); 678220726Sbschmidt TASK_INIT(&sc->sc_radioon_task, 0, iwn_radio_on, sc); 679220726Sbschmidt TASK_INIT(&sc->sc_radiooff_task, 0, iwn_radio_off, sc); 680220667Sbschmidt 681178676Ssam iwn_sysctlattach(sc); 682178676Ssam 683198429Srpaulo /* 684198429Srpaulo * Hook our interrupt after all initialization is complete. 685198429Srpaulo */ 686198429Srpaulo error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET | INTR_MPSAFE, 687178676Ssam NULL, iwn_intr, sc, &sc->sc_ih); 688198429Srpaulo if (error != 0) { 689220724Sbschmidt device_printf(dev, "can't establish interrupt, error %d\n", 690198429Srpaulo error); 691198429Srpaulo goto fail; 692198429Srpaulo } 693178676Ssam 694220724Sbschmidt if (bootverbose) 695220724Sbschmidt ieee80211_announce(ic); 696178676Ssam return 0; 697178676Ssamfail: 698220635Sbschmidt iwn_detach(dev); 699178676Ssam return error; 700178676Ssam} 701178676Ssam 702220728Sbschmidtstatic int 703220728Sbschmidtiwn4965_attach(struct iwn_softc *sc, uint16_t pid) 704178676Ssam{ 705220728Sbschmidt struct iwn_ops *ops = &sc->ops; 706198429Srpaulo 707220728Sbschmidt ops->load_firmware = iwn4965_load_firmware; 708220728Sbschmidt ops->read_eeprom = iwn4965_read_eeprom; 709220728Sbschmidt ops->post_alive = iwn4965_post_alive; 710220728Sbschmidt ops->nic_config = iwn4965_nic_config; 711220728Sbschmidt ops->update_sched = iwn4965_update_sched; 712220728Sbschmidt ops->get_temperature = iwn4965_get_temperature; 713220728Sbschmidt ops->get_rssi = iwn4965_get_rssi; 714220728Sbschmidt ops->set_txpower = iwn4965_set_txpower; 715220728Sbschmidt ops->init_gains = iwn4965_init_gains; 716220728Sbschmidt ops->set_gains = iwn4965_set_gains; 717220728Sbschmidt ops->add_node = iwn4965_add_node; 718220728Sbschmidt ops->tx_done = iwn4965_tx_done; 719220728Sbschmidt#if 0 /* HT */ 720220728Sbschmidt ops->ampdu_tx_start = iwn4965_ampdu_tx_start; 721220728Sbschmidt ops->ampdu_tx_stop = iwn4965_ampdu_tx_stop; 722220728Sbschmidt#endif 723220728Sbschmidt sc->ntxqs = IWN4965_NTXQUEUES; 724220728Sbschmidt sc->ndmachnls = IWN4965_NDMACHNLS; 725220728Sbschmidt sc->broadcast_id = IWN4965_ID_BROADCAST; 726220728Sbschmidt sc->rxonsz = IWN4965_RXONSZ; 727220728Sbschmidt sc->schedsz = IWN4965_SCHEDSZ; 728220728Sbschmidt sc->fw_text_maxsz = IWN4965_FW_TEXT_MAXSZ; 729220728Sbschmidt sc->fw_data_maxsz = IWN4965_FW_DATA_MAXSZ; 730220728Sbschmidt sc->fwsz = IWN4965_FWSZ; 731220728Sbschmidt sc->sched_txfact_addr = IWN4965_SCHED_TXFACT; 732220728Sbschmidt sc->limits = &iwn4965_sensitivity_limits; 733220728Sbschmidt sc->fwname = "iwn4965fw"; 734220728Sbschmidt /* Override chains masks, ROM is known to be broken. */ 735220728Sbschmidt sc->txchainmask = IWN_ANT_AB; 736220728Sbschmidt sc->rxchainmask = IWN_ANT_ABC; 737220728Sbschmidt 738220728Sbschmidt return 0; 739220728Sbschmidt} 740220728Sbschmidt 741220728Sbschmidtstatic int 742220728Sbschmidtiwn5000_attach(struct iwn_softc *sc, uint16_t pid) 743220728Sbschmidt{ 744220728Sbschmidt struct iwn_ops *ops = &sc->ops; 745220728Sbschmidt 746220728Sbschmidt ops->load_firmware = iwn5000_load_firmware; 747220728Sbschmidt ops->read_eeprom = iwn5000_read_eeprom; 748220728Sbschmidt ops->post_alive = iwn5000_post_alive; 749220728Sbschmidt ops->nic_config = iwn5000_nic_config; 750220728Sbschmidt ops->update_sched = iwn5000_update_sched; 751220728Sbschmidt ops->get_temperature = iwn5000_get_temperature; 752220728Sbschmidt ops->get_rssi = iwn5000_get_rssi; 753220728Sbschmidt ops->set_txpower = iwn5000_set_txpower; 754220728Sbschmidt ops->init_gains = iwn5000_init_gains; 755220728Sbschmidt ops->set_gains = iwn5000_set_gains; 756220728Sbschmidt ops->add_node = iwn5000_add_node; 757220728Sbschmidt ops->tx_done = iwn5000_tx_done; 758220728Sbschmidt#if 0 /* HT */ 759220728Sbschmidt ops->ampdu_tx_start = iwn5000_ampdu_tx_start; 760220728Sbschmidt ops->ampdu_tx_stop = iwn5000_ampdu_tx_stop; 761220728Sbschmidt#endif 762220728Sbschmidt sc->ntxqs = IWN5000_NTXQUEUES; 763220728Sbschmidt sc->ndmachnls = IWN5000_NDMACHNLS; 764220728Sbschmidt sc->broadcast_id = IWN5000_ID_BROADCAST; 765220728Sbschmidt sc->rxonsz = IWN5000_RXONSZ; 766220728Sbschmidt sc->schedsz = IWN5000_SCHEDSZ; 767220728Sbschmidt sc->fw_text_maxsz = IWN5000_FW_TEXT_MAXSZ; 768220728Sbschmidt sc->fw_data_maxsz = IWN5000_FW_DATA_MAXSZ; 769220728Sbschmidt sc->fwsz = IWN5000_FWSZ; 770220728Sbschmidt sc->sched_txfact_addr = IWN5000_SCHED_TXFACT; 771220866Sbschmidt sc->reset_noise_gain = IWN5000_PHY_CALIB_RESET_NOISE_GAIN; 772220866Sbschmidt sc->noise_gain = IWN5000_PHY_CALIB_NOISE_GAIN; 773220728Sbschmidt 774198429Srpaulo switch (sc->hw_type) { 775198429Srpaulo case IWN_HW_REV_TYPE_5100: 776201209Srpaulo sc->limits = &iwn5000_sensitivity_limits; 777198439Srpaulo sc->fwname = "iwn5000fw"; 778220727Sbschmidt /* Override chains masks, ROM is known to be broken. */ 779201209Srpaulo sc->txchainmask = IWN_ANT_B; 780201209Srpaulo sc->rxchainmask = IWN_ANT_AB; 781198429Srpaulo break; 782198429Srpaulo case IWN_HW_REV_TYPE_5150: 783201209Srpaulo sc->limits = &iwn5150_sensitivity_limits; 784198439Srpaulo sc->fwname = "iwn5150fw"; 785198429Srpaulo break; 786198429Srpaulo case IWN_HW_REV_TYPE_5300: 787198429Srpaulo case IWN_HW_REV_TYPE_5350: 788201209Srpaulo sc->limits = &iwn5000_sensitivity_limits; 789198439Srpaulo sc->fwname = "iwn5000fw"; 790198429Srpaulo break; 791198429Srpaulo case IWN_HW_REV_TYPE_1000: 792206444Sbschmidt sc->limits = &iwn1000_sensitivity_limits; 793198439Srpaulo sc->fwname = "iwn1000fw"; 794198429Srpaulo break; 795198429Srpaulo case IWN_HW_REV_TYPE_6000: 796201209Srpaulo sc->limits = &iwn6000_sensitivity_limits; 797198439Srpaulo sc->fwname = "iwn6000fw"; 798220728Sbschmidt if (pid == 0x422c || pid == 0x4239) { 799201209Srpaulo sc->sc_flags |= IWN_FLAG_INTERNAL_PA; 800220727Sbschmidt /* Override chains masks, ROM is known to be broken. */ 801201209Srpaulo sc->txchainmask = IWN_ANT_BC; 802201209Srpaulo sc->rxchainmask = IWN_ANT_BC; 803201209Srpaulo } 804198429Srpaulo break; 805198429Srpaulo case IWN_HW_REV_TYPE_6050: 806201209Srpaulo sc->limits = &iwn6000_sensitivity_limits; 807210109Sbschmidt sc->fwname = "iwn6050fw"; 808220867Sbschmidt /* Override chains masks, ROM is known to be broken. */ 809220867Sbschmidt sc->txchainmask = IWN_ANT_AB; 810220867Sbschmidt sc->rxchainmask = IWN_ANT_AB; 811198429Srpaulo break; 812210109Sbschmidt case IWN_HW_REV_TYPE_6005: 813210109Sbschmidt sc->limits = &iwn6000_sensitivity_limits; 814220894Sbschmidt if (pid != 0x0082 && pid != 0x0085) { 815220894Sbschmidt sc->fwname = "iwn6000g2bfw"; 816220891Sbschmidt sc->sc_flags |= IWN_FLAG_ADV_BTCOEX; 817220894Sbschmidt } else 818220894Sbschmidt sc->fwname = "iwn6000g2afw"; 819210109Sbschmidt break; 820198429Srpaulo default: 821198429Srpaulo device_printf(sc->sc_dev, "adapter type %d not supported\n", 822198429Srpaulo sc->hw_type); 823220728Sbschmidt return ENOTSUP; 824198429Srpaulo } 825220728Sbschmidt return 0; 826178676Ssam} 827178676Ssam 828178676Ssam/* 829198429Srpaulo * Attach the interface to 802.11 radiotap. 830178676Ssam */ 831206477Sbschmidtstatic void 832198429Srpauloiwn_radiotap_attach(struct iwn_softc *sc) 833178676Ssam{ 834178676Ssam struct ifnet *ifp = sc->sc_ifp; 835178676Ssam struct ieee80211com *ic = ifp->if_l2com; 836178676Ssam 837198429Srpaulo ieee80211_radiotap_attach(ic, 838198429Srpaulo &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), 839198429Srpaulo IWN_TX_RADIOTAP_PRESENT, 840198429Srpaulo &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), 841198429Srpaulo IWN_RX_RADIOTAP_PRESENT); 842178676Ssam} 843178676Ssam 844220723Sbschmidtstatic void 845220723Sbschmidtiwn_sysctlattach(struct iwn_softc *sc) 846220723Sbschmidt{ 847220723Sbschmidt struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); 848220723Sbschmidt struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); 849220723Sbschmidt 850220723Sbschmidt#ifdef IWN_DEBUG 851220723Sbschmidt sc->sc_debug = 0; 852220723Sbschmidt SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 853220723Sbschmidt "debug", CTLFLAG_RW, &sc->sc_debug, 0, "control debugging printfs"); 854220723Sbschmidt#endif 855220723Sbschmidt} 856220723Sbschmidt 857178676Ssamstatic struct ieee80211vap * 858178676Ssamiwn_vap_create(struct ieee80211com *ic, 859220726Sbschmidt const char name[IFNAMSIZ], int unit, int opmode, int flags, 860220726Sbschmidt const uint8_t bssid[IEEE80211_ADDR_LEN], 861220726Sbschmidt const uint8_t mac[IEEE80211_ADDR_LEN]) 862178676Ssam{ 863178676Ssam struct iwn_vap *ivp; 864178676Ssam struct ieee80211vap *vap; 865178676Ssam 866178676Ssam if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ 867178676Ssam return NULL; 868178676Ssam ivp = (struct iwn_vap *) malloc(sizeof(struct iwn_vap), 869178676Ssam M_80211_VAP, M_NOWAIT | M_ZERO); 870178676Ssam if (ivp == NULL) 871178676Ssam return NULL; 872178676Ssam vap = &ivp->iv_vap; 873178676Ssam ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac); 874178676Ssam vap->iv_bmissthreshold = 10; /* override default */ 875198429Srpaulo /* Override with driver methods. */ 876178676Ssam ivp->iv_newstate = vap->iv_newstate; 877178676Ssam vap->iv_newstate = iwn_newstate; 878178676Ssam 879206358Srpaulo ieee80211_ratectl_init(vap); 880198429Srpaulo /* Complete setup. */ 881206476Sbschmidt ieee80211_vap_attach(vap, iwn_media_change, ieee80211_media_status); 882178676Ssam ic->ic_opmode = opmode; 883178676Ssam return vap; 884178676Ssam} 885178676Ssam 886178676Ssamstatic void 887178676Ssamiwn_vap_delete(struct ieee80211vap *vap) 888178676Ssam{ 889178676Ssam struct iwn_vap *ivp = IWN_VAP(vap); 890178676Ssam 891206358Srpaulo ieee80211_ratectl_deinit(vap); 892178676Ssam ieee80211_vap_detach(vap); 893178676Ssam free(ivp, M_80211_VAP); 894178676Ssam} 895178676Ssam 896206477Sbschmidtstatic int 897220635Sbschmidtiwn_detach(device_t dev) 898178676Ssam{ 899178676Ssam struct iwn_softc *sc = device_get_softc(dev); 900198429Srpaulo struct ifnet *ifp = sc->sc_ifp; 901198429Srpaulo struct ieee80211com *ic; 902220721Sbschmidt int qid; 903178676Ssam 904198429Srpaulo if (ifp != NULL) { 905198429Srpaulo ic = ifp->if_l2com; 906198429Srpaulo 907198429Srpaulo ieee80211_draintask(ic, &sc->sc_reinit_task); 908198429Srpaulo ieee80211_draintask(ic, &sc->sc_radioon_task); 909198429Srpaulo ieee80211_draintask(ic, &sc->sc_radiooff_task); 910198429Srpaulo 911198429Srpaulo iwn_stop(sc); 912220667Sbschmidt callout_drain(&sc->watchdog_to); 913220667Sbschmidt callout_drain(&sc->calib_to); 914198429Srpaulo ieee80211_ifdetach(ic); 915198429Srpaulo } 916198429Srpaulo 917220725Sbschmidt /* Uninstall interrupt handler. */ 918220723Sbschmidt if (sc->irq != NULL) { 919220723Sbschmidt bus_teardown_intr(dev, sc->irq, sc->sc_ih); 920220723Sbschmidt bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq); 921220723Sbschmidt if (sc->irq_rid == 1) 922220723Sbschmidt pci_release_msi(dev); 923220723Sbschmidt } 924220723Sbschmidt 925201209Srpaulo /* Free DMA resources. */ 926198429Srpaulo iwn_free_rx_ring(sc, &sc->rxq); 927220728Sbschmidt for (qid = 0; qid < sc->ntxqs; qid++) 928220728Sbschmidt iwn_free_tx_ring(sc, &sc->txq[qid]); 929198429Srpaulo iwn_free_sched(sc); 930198429Srpaulo iwn_free_kw(sc); 931201209Srpaulo if (sc->ict != NULL) 932201209Srpaulo iwn_free_ict(sc); 933198429Srpaulo iwn_free_fwmem(sc); 934198429Srpaulo 935198429Srpaulo if (sc->mem != NULL) 936198429Srpaulo bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem); 937198429Srpaulo 938198429Srpaulo if (ifp != NULL) 939198429Srpaulo if_free(ifp); 940198429Srpaulo 941198429Srpaulo IWN_LOCK_DESTROY(sc); 942178676Ssam return 0; 943178676Ssam} 944178676Ssam 945178676Ssamstatic int 946220723Sbschmidtiwn_shutdown(device_t dev) 947220723Sbschmidt{ 948220723Sbschmidt struct iwn_softc *sc = device_get_softc(dev); 949220723Sbschmidt 950220723Sbschmidt iwn_stop(sc); 951220723Sbschmidt return 0; 952220723Sbschmidt} 953220723Sbschmidt 954220723Sbschmidtstatic int 955220723Sbschmidtiwn_suspend(device_t dev) 956220723Sbschmidt{ 957220723Sbschmidt struct iwn_softc *sc = device_get_softc(dev); 958220723Sbschmidt struct ifnet *ifp = sc->sc_ifp; 959220723Sbschmidt struct ieee80211com *ic = ifp->if_l2com; 960220723Sbschmidt struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 961220723Sbschmidt 962220723Sbschmidt iwn_stop(sc); 963220723Sbschmidt if (vap != NULL) 964220723Sbschmidt ieee80211_stop(vap); 965220723Sbschmidt return 0; 966220723Sbschmidt} 967220723Sbschmidt 968220723Sbschmidtstatic int 969220723Sbschmidtiwn_resume(device_t dev) 970220723Sbschmidt{ 971220723Sbschmidt struct iwn_softc *sc = device_get_softc(dev); 972220723Sbschmidt struct ifnet *ifp = sc->sc_ifp; 973220723Sbschmidt struct ieee80211com *ic = ifp->if_l2com; 974220723Sbschmidt struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 975220723Sbschmidt 976220723Sbschmidt /* Clear device-specific "PCI retry timeout" register (41h). */ 977220723Sbschmidt pci_write_config(dev, 0x41, 0, 1); 978220723Sbschmidt 979220723Sbschmidt if (ifp->if_flags & IFF_UP) { 980220723Sbschmidt iwn_init(sc); 981220723Sbschmidt if (vap != NULL) 982220723Sbschmidt ieee80211_init(vap); 983220723Sbschmidt if (ifp->if_drv_flags & IFF_DRV_RUNNING) 984220723Sbschmidt iwn_start(ifp); 985220723Sbschmidt } 986220723Sbschmidt return 0; 987220723Sbschmidt} 988220723Sbschmidt 989220723Sbschmidtstatic int 990198429Srpauloiwn_nic_lock(struct iwn_softc *sc) 991178676Ssam{ 992198429Srpaulo int ntries; 993178676Ssam 994198429Srpaulo /* Request exclusive access to NIC. */ 995198429Srpaulo IWN_SETBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_MAC_ACCESS_REQ); 996178676Ssam 997198429Srpaulo /* Spin until we actually get the lock. */ 998198429Srpaulo for (ntries = 0; ntries < 1000; ntries++) { 999198429Srpaulo if ((IWN_READ(sc, IWN_GP_CNTRL) & 1000220726Sbschmidt (IWN_GP_CNTRL_MAC_ACCESS_ENA | IWN_GP_CNTRL_SLEEP)) == 1001198429Srpaulo IWN_GP_CNTRL_MAC_ACCESS_ENA) 1002198429Srpaulo return 0; 1003198429Srpaulo DELAY(10); 1004198429Srpaulo } 1005198429Srpaulo return ETIMEDOUT; 1006198429Srpaulo} 1007198429Srpaulo 1008198429Srpaulostatic __inline void 1009198429Srpauloiwn_nic_unlock(struct iwn_softc *sc) 1010198429Srpaulo{ 1011198429Srpaulo IWN_CLRBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_MAC_ACCESS_REQ); 1012198429Srpaulo} 1013198429Srpaulo 1014198429Srpaulostatic __inline uint32_t 1015198429Srpauloiwn_prph_read(struct iwn_softc *sc, uint32_t addr) 1016198429Srpaulo{ 1017198429Srpaulo IWN_WRITE(sc, IWN_PRPH_RADDR, IWN_PRPH_DWORD | addr); 1018201209Srpaulo IWN_BARRIER_READ_WRITE(sc); 1019198429Srpaulo return IWN_READ(sc, IWN_PRPH_RDATA); 1020198429Srpaulo} 1021198429Srpaulo 1022198429Srpaulostatic __inline void 1023198429Srpauloiwn_prph_write(struct iwn_softc *sc, uint32_t addr, uint32_t data) 1024198429Srpaulo{ 1025198429Srpaulo IWN_WRITE(sc, IWN_PRPH_WADDR, IWN_PRPH_DWORD | addr); 1026201209Srpaulo IWN_BARRIER_WRITE(sc); 1027198429Srpaulo IWN_WRITE(sc, IWN_PRPH_WDATA, data); 1028198429Srpaulo} 1029198429Srpaulo 1030198429Srpaulostatic __inline void 1031198429Srpauloiwn_prph_setbits(struct iwn_softc *sc, uint32_t addr, uint32_t mask) 1032198429Srpaulo{ 1033198429Srpaulo iwn_prph_write(sc, addr, iwn_prph_read(sc, addr) | mask); 1034198429Srpaulo} 1035198429Srpaulo 1036198429Srpaulostatic __inline void 1037198429Srpauloiwn_prph_clrbits(struct iwn_softc *sc, uint32_t addr, uint32_t mask) 1038198429Srpaulo{ 1039198429Srpaulo iwn_prph_write(sc, addr, iwn_prph_read(sc, addr) & ~mask); 1040198429Srpaulo} 1041198429Srpaulo 1042198429Srpaulostatic __inline void 1043198429Srpauloiwn_prph_write_region_4(struct iwn_softc *sc, uint32_t addr, 1044198429Srpaulo const uint32_t *data, int count) 1045198429Srpaulo{ 1046198429Srpaulo for (; count > 0; count--, data++, addr += 4) 1047198429Srpaulo iwn_prph_write(sc, addr, *data); 1048198429Srpaulo} 1049198429Srpaulo 1050198429Srpaulostatic __inline uint32_t 1051198429Srpauloiwn_mem_read(struct iwn_softc *sc, uint32_t addr) 1052198429Srpaulo{ 1053198429Srpaulo IWN_WRITE(sc, IWN_MEM_RADDR, addr); 1054201209Srpaulo IWN_BARRIER_READ_WRITE(sc); 1055198429Srpaulo return IWN_READ(sc, IWN_MEM_RDATA); 1056198429Srpaulo} 1057198429Srpaulo 1058198429Srpaulostatic __inline void 1059198429Srpauloiwn_mem_write(struct iwn_softc *sc, uint32_t addr, uint32_t data) 1060198429Srpaulo{ 1061198429Srpaulo IWN_WRITE(sc, IWN_MEM_WADDR, addr); 1062201209Srpaulo IWN_BARRIER_WRITE(sc); 1063198429Srpaulo IWN_WRITE(sc, IWN_MEM_WDATA, data); 1064198429Srpaulo} 1065198429Srpaulo 1066198429Srpaulostatic __inline void 1067198429Srpauloiwn_mem_write_2(struct iwn_softc *sc, uint32_t addr, uint16_t data) 1068198429Srpaulo{ 1069198429Srpaulo uint32_t tmp; 1070198429Srpaulo 1071198429Srpaulo tmp = iwn_mem_read(sc, addr & ~3); 1072198429Srpaulo if (addr & 3) 1073198429Srpaulo tmp = (tmp & 0x0000ffff) | data << 16; 1074198429Srpaulo else 1075198429Srpaulo tmp = (tmp & 0xffff0000) | data; 1076198429Srpaulo iwn_mem_write(sc, addr & ~3, tmp); 1077198429Srpaulo} 1078198429Srpaulo 1079198429Srpaulostatic __inline void 1080198429Srpauloiwn_mem_read_region_4(struct iwn_softc *sc, uint32_t addr, uint32_t *data, 1081198429Srpaulo int count) 1082198429Srpaulo{ 1083198429Srpaulo for (; count > 0; count--, addr += 4) 1084198429Srpaulo *data++ = iwn_mem_read(sc, addr); 1085198429Srpaulo} 1086198429Srpaulo 1087198429Srpaulostatic __inline void 1088198429Srpauloiwn_mem_set_region_4(struct iwn_softc *sc, uint32_t addr, uint32_t val, 1089198429Srpaulo int count) 1090198429Srpaulo{ 1091198429Srpaulo for (; count > 0; count--, addr += 4) 1092198429Srpaulo iwn_mem_write(sc, addr, val); 1093198429Srpaulo} 1094198429Srpaulo 1095206477Sbschmidtstatic int 1096198429Srpauloiwn_eeprom_lock(struct iwn_softc *sc) 1097198429Srpaulo{ 1098198429Srpaulo int i, ntries; 1099198429Srpaulo 1100198429Srpaulo for (i = 0; i < 100; i++) { 1101198429Srpaulo /* Request exclusive access to EEPROM. */ 1102198429Srpaulo IWN_SETBITS(sc, IWN_HW_IF_CONFIG, 1103198429Srpaulo IWN_HW_IF_CONFIG_EEPROM_LOCKED); 1104198429Srpaulo 1105198429Srpaulo /* Spin until we actually get the lock. */ 1106198429Srpaulo for (ntries = 0; ntries < 100; ntries++) { 1107198429Srpaulo if (IWN_READ(sc, IWN_HW_IF_CONFIG) & 1108198429Srpaulo IWN_HW_IF_CONFIG_EEPROM_LOCKED) 1109198429Srpaulo return 0; 1110198429Srpaulo DELAY(10); 1111198429Srpaulo } 1112198429Srpaulo } 1113198429Srpaulo return ETIMEDOUT; 1114198429Srpaulo} 1115198429Srpaulo 1116198429Srpaulostatic __inline void 1117198429Srpauloiwn_eeprom_unlock(struct iwn_softc *sc) 1118198429Srpaulo{ 1119198429Srpaulo IWN_CLRBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_EEPROM_LOCKED); 1120198429Srpaulo} 1121198429Srpaulo 1122198429Srpaulo/* 1123198429Srpaulo * Initialize access by host to One Time Programmable ROM. 1124198429Srpaulo * NB: This kind of ROM can be found on 1000 or 6000 Series only. 1125198429Srpaulo */ 1126206477Sbschmidtstatic int 1127198429Srpauloiwn_init_otprom(struct iwn_softc *sc) 1128198429Srpaulo{ 1129203934Sbschmidt uint16_t prev, base, next; 1130201209Srpaulo int count, error; 1131198429Srpaulo 1132201209Srpaulo /* Wait for clock stabilization before accessing prph. */ 1133220726Sbschmidt if ((error = iwn_clock_wait(sc)) != 0) 1134198429Srpaulo return error; 1135198429Srpaulo 1136220726Sbschmidt if ((error = iwn_nic_lock(sc)) != 0) 1137198429Srpaulo return error; 1138198429Srpaulo iwn_prph_setbits(sc, IWN_APMG_PS, IWN_APMG_PS_RESET_REQ); 1139198429Srpaulo DELAY(5); 1140198429Srpaulo iwn_prph_clrbits(sc, IWN_APMG_PS, IWN_APMG_PS_RESET_REQ); 1141198429Srpaulo iwn_nic_unlock(sc); 1142198429Srpaulo 1143201209Srpaulo /* Set auto clock gate disable bit for HW with OTP shadow RAM. */ 1144201209Srpaulo if (sc->hw_type != IWN_HW_REV_TYPE_1000) { 1145201209Srpaulo IWN_SETBITS(sc, IWN_DBG_LINK_PWR_MGMT, 1146201209Srpaulo IWN_RESET_LINK_PWR_MGMT_DIS); 1147201209Srpaulo } 1148198429Srpaulo IWN_CLRBITS(sc, IWN_EEPROM_GP, IWN_EEPROM_GP_IF_OWNER); 1149198429Srpaulo /* Clear ECC status. */ 1150198429Srpaulo IWN_SETBITS(sc, IWN_OTP_GP, 1151198429Srpaulo IWN_OTP_GP_ECC_CORR_STTS | IWN_OTP_GP_ECC_UNCORR_STTS); 1152198429Srpaulo 1153201209Srpaulo /* 1154203934Sbschmidt * Find the block before last block (contains the EEPROM image) 1155203934Sbschmidt * for HW without OTP shadow RAM. 1156201209Srpaulo */ 1157201209Srpaulo if (sc->hw_type == IWN_HW_REV_TYPE_1000) { 1158201209Srpaulo /* Switch to absolute addressing mode. */ 1159201209Srpaulo IWN_CLRBITS(sc, IWN_OTP_GP, IWN_OTP_GP_RELATIVE_ACCESS); 1160203934Sbschmidt base = prev = 0; 1161201209Srpaulo for (count = 0; count < IWN1000_OTP_NBLOCKS; count++) { 1162201209Srpaulo error = iwn_read_prom_data(sc, base, &next, 2); 1163201209Srpaulo if (error != 0) 1164201209Srpaulo return error; 1165201209Srpaulo if (next == 0) /* End of linked-list. */ 1166201209Srpaulo break; 1167203934Sbschmidt prev = base; 1168201209Srpaulo base = le16toh(next); 1169201209Srpaulo } 1170203934Sbschmidt if (count == 0 || count == IWN1000_OTP_NBLOCKS) 1171201209Srpaulo return EIO; 1172201209Srpaulo /* Skip "next" word. */ 1173203934Sbschmidt sc->prom_base = prev + 1; 1174201209Srpaulo } 1175178676Ssam return 0; 1176178676Ssam} 1177178676Ssam 1178206477Sbschmidtstatic int 1179198429Srpauloiwn_read_prom_data(struct iwn_softc *sc, uint32_t addr, void *data, int count) 1180198429Srpaulo{ 1181220723Sbschmidt uint8_t *out = data; 1182198429Srpaulo uint32_t val, tmp; 1183198429Srpaulo int ntries; 1184198429Srpaulo 1185201209Srpaulo addr += sc->prom_base; 1186198429Srpaulo for (; count > 0; count -= 2, addr++) { 1187198429Srpaulo IWN_WRITE(sc, IWN_EEPROM, addr << 2); 1188201209Srpaulo for (ntries = 0; ntries < 10; ntries++) { 1189198429Srpaulo val = IWN_READ(sc, IWN_EEPROM); 1190198429Srpaulo if (val & IWN_EEPROM_READ_VALID) 1191198429Srpaulo break; 1192198429Srpaulo DELAY(5); 1193198429Srpaulo } 1194201209Srpaulo if (ntries == 10) { 1195198429Srpaulo device_printf(sc->sc_dev, 1196198429Srpaulo "timeout reading ROM at 0x%x\n", addr); 1197198429Srpaulo return ETIMEDOUT; 1198198429Srpaulo } 1199198429Srpaulo if (sc->sc_flags & IWN_FLAG_HAS_OTPROM) { 1200198429Srpaulo /* OTPROM, check for ECC errors. */ 1201198429Srpaulo tmp = IWN_READ(sc, IWN_OTP_GP); 1202198429Srpaulo if (tmp & IWN_OTP_GP_ECC_UNCORR_STTS) { 1203198429Srpaulo device_printf(sc->sc_dev, 1204198429Srpaulo "OTPROM ECC error at 0x%x\n", addr); 1205198429Srpaulo return EIO; 1206198429Srpaulo } 1207198429Srpaulo if (tmp & IWN_OTP_GP_ECC_CORR_STTS) { 1208198429Srpaulo /* Correctable ECC error, clear bit. */ 1209198429Srpaulo IWN_SETBITS(sc, IWN_OTP_GP, 1210198429Srpaulo IWN_OTP_GP_ECC_CORR_STTS); 1211198429Srpaulo } 1212198429Srpaulo } 1213198429Srpaulo *out++ = val >> 16; 1214198429Srpaulo if (count > 1) 1215198429Srpaulo *out++ = val >> 24; 1216198429Srpaulo } 1217198429Srpaulo return 0; 1218198429Srpaulo} 1219198429Srpaulo 1220178676Ssamstatic void 1221178676Ssamiwn_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nsegs, int error) 1222178676Ssam{ 1223198429Srpaulo if (error != 0) 1224198429Srpaulo return; 1225198429Srpaulo KASSERT(nsegs == 1, ("too many DMA segments, %d should be 1", nsegs)); 1226198429Srpaulo *(bus_addr_t *)arg = segs[0].ds_addr; 1227178676Ssam} 1228178676Ssam 1229198429Srpaulostatic int 1230178676Ssamiwn_dma_contig_alloc(struct iwn_softc *sc, struct iwn_dma_info *dma, 1231220691Sbschmidt void **kvap, bus_size_t size, bus_size_t alignment) 1232178676Ssam{ 1233198429Srpaulo int error; 1234178676Ssam 1235220723Sbschmidt dma->tag = NULL; 1236178676Ssam dma->size = size; 1237178676Ssam 1238198429Srpaulo error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), alignment, 1239178676Ssam 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, size, 1240220691Sbschmidt 1, size, BUS_DMA_NOWAIT, NULL, NULL, &dma->tag); 1241220711Sbschmidt if (error != 0) 1242178676Ssam goto fail; 1243220711Sbschmidt 1244178676Ssam error = bus_dmamem_alloc(dma->tag, (void **)&dma->vaddr, 1245220691Sbschmidt BUS_DMA_NOWAIT | BUS_DMA_ZERO | BUS_DMA_COHERENT, &dma->map); 1246220711Sbschmidt if (error != 0) 1247178676Ssam goto fail; 1248220711Sbschmidt 1249220691Sbschmidt error = bus_dmamap_load(dma->tag, dma->map, dma->vaddr, size, 1250220691Sbschmidt iwn_dma_map_addr, &dma->paddr, BUS_DMA_NOWAIT); 1251220711Sbschmidt if (error != 0) 1252178676Ssam goto fail; 1253178676Ssam 1254220704Sbschmidt bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); 1255220704Sbschmidt 1256178676Ssam if (kvap != NULL) 1257178676Ssam *kvap = dma->vaddr; 1258220726Sbschmidt 1259178676Ssam return 0; 1260220726Sbschmidt 1261220726Sbschmidtfail: iwn_dma_contig_free(dma); 1262178676Ssam return error; 1263178676Ssam} 1264178676Ssam 1265206477Sbschmidtstatic void 1266178676Ssamiwn_dma_contig_free(struct iwn_dma_info *dma) 1267178676Ssam{ 1268220701Sbschmidt if (dma->map != NULL) { 1269220701Sbschmidt if (dma->vaddr != NULL) { 1270220701Sbschmidt bus_dmamap_sync(dma->tag, dma->map, 1271220701Sbschmidt BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 1272220701Sbschmidt bus_dmamap_unload(dma->tag, dma->map); 1273178676Ssam bus_dmamem_free(dma->tag, &dma->vaddr, dma->map); 1274220701Sbschmidt dma->vaddr = NULL; 1275178676Ssam } 1276220701Sbschmidt bus_dmamap_destroy(dma->tag, dma->map); 1277220701Sbschmidt dma->map = NULL; 1278220701Sbschmidt } 1279220701Sbschmidt if (dma->tag != NULL) { 1280178676Ssam bus_dma_tag_destroy(dma->tag); 1281220701Sbschmidt dma->tag = NULL; 1282178676Ssam } 1283178676Ssam} 1284178676Ssam 1285206477Sbschmidtstatic int 1286198429Srpauloiwn_alloc_sched(struct iwn_softc *sc) 1287178676Ssam{ 1288198429Srpaulo /* TX scheduler rings must be aligned on a 1KB boundary. */ 1289220691Sbschmidt return iwn_dma_contig_alloc(sc, &sc->sched_dma, (void **)&sc->sched, 1290220728Sbschmidt sc->schedsz, 1024); 1291178676Ssam} 1292178676Ssam 1293206477Sbschmidtstatic void 1294198429Srpauloiwn_free_sched(struct iwn_softc *sc) 1295178676Ssam{ 1296198429Srpaulo iwn_dma_contig_free(&sc->sched_dma); 1297178676Ssam} 1298178676Ssam 1299206477Sbschmidtstatic int 1300178676Ssamiwn_alloc_kw(struct iwn_softc *sc) 1301178676Ssam{ 1302198429Srpaulo /* "Keep Warm" page must be aligned on a 4KB boundary. */ 1303220691Sbschmidt return iwn_dma_contig_alloc(sc, &sc->kw_dma, NULL, 4096, 4096); 1304178676Ssam} 1305178676Ssam 1306206477Sbschmidtstatic void 1307178676Ssamiwn_free_kw(struct iwn_softc *sc) 1308178676Ssam{ 1309178676Ssam iwn_dma_contig_free(&sc->kw_dma); 1310178676Ssam} 1311178676Ssam 1312206477Sbschmidtstatic int 1313201209Srpauloiwn_alloc_ict(struct iwn_softc *sc) 1314201209Srpaulo{ 1315201209Srpaulo /* ICT table must be aligned on a 4KB boundary. */ 1316220691Sbschmidt return iwn_dma_contig_alloc(sc, &sc->ict_dma, (void **)&sc->ict, 1317220691Sbschmidt IWN_ICT_SIZE, 4096); 1318201209Srpaulo} 1319201209Srpaulo 1320206477Sbschmidtstatic void 1321201209Srpauloiwn_free_ict(struct iwn_softc *sc) 1322201209Srpaulo{ 1323201209Srpaulo iwn_dma_contig_free(&sc->ict_dma); 1324201209Srpaulo} 1325201209Srpaulo 1326206477Sbschmidtstatic int 1327178676Ssamiwn_alloc_fwmem(struct iwn_softc *sc) 1328178676Ssam{ 1329198429Srpaulo /* Must be aligned on a 16-byte boundary. */ 1330220728Sbschmidt return iwn_dma_contig_alloc(sc, &sc->fw_dma, NULL, sc->fwsz, 16); 1331178676Ssam} 1332178676Ssam 1333206477Sbschmidtstatic void 1334178676Ssamiwn_free_fwmem(struct iwn_softc *sc) 1335178676Ssam{ 1336178676Ssam iwn_dma_contig_free(&sc->fw_dma); 1337178676Ssam} 1338178676Ssam 1339206477Sbschmidtstatic int 1340178676Ssamiwn_alloc_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring) 1341178676Ssam{ 1342198429Srpaulo bus_size_t size; 1343178676Ssam int i, error; 1344178676Ssam 1345178676Ssam ring->cur = 0; 1346178676Ssam 1347198429Srpaulo /* Allocate RX descriptors (256-byte aligned). */ 1348198429Srpaulo size = IWN_RX_RING_COUNT * sizeof (uint32_t); 1349220691Sbschmidt error = iwn_dma_contig_alloc(sc, &ring->desc_dma, (void **)&ring->desc, 1350220691Sbschmidt size, 256); 1351178676Ssam if (error != 0) { 1352178676Ssam device_printf(sc->sc_dev, 1353220711Sbschmidt "%s: could not allocate RX ring DMA memory, error %d\n", 1354178676Ssam __func__, error); 1355178676Ssam goto fail; 1356178676Ssam } 1357178676Ssam 1358220702Sbschmidt /* Allocate RX status area (16-byte aligned). */ 1359220702Sbschmidt error = iwn_dma_contig_alloc(sc, &ring->stat_dma, (void **)&ring->stat, 1360220702Sbschmidt sizeof (struct iwn_rx_status), 16); 1361198429Srpaulo if (error != 0) { 1362198429Srpaulo device_printf(sc->sc_dev, 1363220711Sbschmidt "%s: could not allocate RX status DMA memory, error %d\n", 1364178676Ssam __func__, error); 1365198429Srpaulo goto fail; 1366198429Srpaulo } 1367178676Ssam 1368220702Sbschmidt /* Create RX buffer DMA tag. */ 1369220702Sbschmidt error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, 1370220702Sbschmidt BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, 1371220702Sbschmidt IWN_RBUF_SIZE, 1, IWN_RBUF_SIZE, BUS_DMA_NOWAIT, NULL, NULL, 1372220702Sbschmidt &ring->data_dmat); 1373198429Srpaulo if (error != 0) { 1374198429Srpaulo device_printf(sc->sc_dev, 1375220711Sbschmidt "%s: could not create RX buf DMA tag, error %d\n", 1376198429Srpaulo __func__, error); 1377198429Srpaulo goto fail; 1378198429Srpaulo } 1379198429Srpaulo 1380178676Ssam /* 1381198429Srpaulo * Allocate and map RX buffers. 1382178676Ssam */ 1383178676Ssam for (i = 0; i < IWN_RX_RING_COUNT; i++) { 1384178676Ssam struct iwn_rx_data *data = &ring->data[i]; 1385178676Ssam bus_addr_t paddr; 1386178676Ssam 1387201209Srpaulo error = bus_dmamap_create(ring->data_dmat, 0, &data->map); 1388178676Ssam if (error != 0) { 1389178676Ssam device_printf(sc->sc_dev, 1390220711Sbschmidt "%s: could not create RX buf DMA map, error %d\n", 1391178676Ssam __func__, error); 1392178676Ssam goto fail; 1393178676Ssam } 1394198429Srpaulo 1395220692Sbschmidt data->m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, 1396220692Sbschmidt IWN_RBUF_SIZE); 1397198439Srpaulo if (data->m == NULL) { 1398178676Ssam device_printf(sc->sc_dev, 1399220711Sbschmidt "%s: could not allocate RX mbuf\n", __func__); 1400220710Sbschmidt error = ENOBUFS; 1401178676Ssam goto fail; 1402178676Ssam } 1403198429Srpaulo 1404201209Srpaulo error = bus_dmamap_load(ring->data_dmat, data->map, 1405220692Sbschmidt mtod(data->m, void *), IWN_RBUF_SIZE, iwn_dma_map_addr, 1406220692Sbschmidt &paddr, BUS_DMA_NOWAIT); 1407178676Ssam if (error != 0 && error != EFBIG) { 1408178676Ssam device_printf(sc->sc_dev, 1409220711Sbschmidt "%s: can't not map mbuf, error %d\n", __func__, 1410220711Sbschmidt error); 1411178676Ssam goto fail; 1412178676Ssam } 1413178676Ssam 1414198429Srpaulo /* Set physical address of RX buffer (256-byte aligned). */ 1415178676Ssam ring->desc[i] = htole32(paddr >> 8); 1416178676Ssam } 1417220726Sbschmidt 1418178676Ssam bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, 1419178676Ssam BUS_DMASYNC_PREWRITE); 1420220726Sbschmidt 1421178676Ssam return 0; 1422220726Sbschmidt 1423220726Sbschmidtfail: iwn_free_rx_ring(sc, ring); 1424178676Ssam return error; 1425178676Ssam} 1426178676Ssam 1427206477Sbschmidtstatic void 1428178676Ssamiwn_reset_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring) 1429178676Ssam{ 1430178676Ssam int ntries; 1431178676Ssam 1432198429Srpaulo if (iwn_nic_lock(sc) == 0) { 1433198429Srpaulo IWN_WRITE(sc, IWN_FH_RX_CONFIG, 0); 1434198429Srpaulo for (ntries = 0; ntries < 1000; ntries++) { 1435198429Srpaulo if (IWN_READ(sc, IWN_FH_RX_STATUS) & 1436198429Srpaulo IWN_FH_RX_STATUS_IDLE) 1437198429Srpaulo break; 1438198429Srpaulo DELAY(10); 1439198429Srpaulo } 1440198429Srpaulo iwn_nic_unlock(sc); 1441198429Srpaulo } 1442178676Ssam ring->cur = 0; 1443198429Srpaulo sc->last_rx_valid = 0; 1444178676Ssam} 1445178676Ssam 1446206477Sbschmidtstatic void 1447178676Ssamiwn_free_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring) 1448178676Ssam{ 1449178676Ssam int i; 1450178676Ssam 1451178676Ssam iwn_dma_contig_free(&ring->desc_dma); 1452198429Srpaulo iwn_dma_contig_free(&ring->stat_dma); 1453178676Ssam 1454198429Srpaulo for (i = 0; i < IWN_RX_RING_COUNT; i++) { 1455198429Srpaulo struct iwn_rx_data *data = &ring->data[i]; 1456198429Srpaulo 1457198429Srpaulo if (data->m != NULL) { 1458201209Srpaulo bus_dmamap_sync(ring->data_dmat, data->map, 1459198439Srpaulo BUS_DMASYNC_POSTREAD); 1460201209Srpaulo bus_dmamap_unload(ring->data_dmat, data->map); 1461198429Srpaulo m_freem(data->m); 1462220710Sbschmidt data->m = NULL; 1463198429Srpaulo } 1464201209Srpaulo if (data->map != NULL) 1465201209Srpaulo bus_dmamap_destroy(ring->data_dmat, data->map); 1466198429Srpaulo } 1467220701Sbschmidt if (ring->data_dmat != NULL) { 1468220701Sbschmidt bus_dma_tag_destroy(ring->data_dmat); 1469220701Sbschmidt ring->data_dmat = NULL; 1470220701Sbschmidt } 1471178676Ssam} 1472178676Ssam 1473206477Sbschmidtstatic int 1474178676Ssamiwn_alloc_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring, int qid) 1475178676Ssam{ 1476220723Sbschmidt bus_addr_t paddr; 1477178676Ssam bus_size_t size; 1478178676Ssam int i, error; 1479178676Ssam 1480178676Ssam ring->qid = qid; 1481178676Ssam ring->queued = 0; 1482178676Ssam ring->cur = 0; 1483178676Ssam 1484220725Sbschmidt /* Allocate TX descriptors (256-byte aligned). */ 1485220726Sbschmidt size = IWN_TX_RING_COUNT * sizeof (struct iwn_tx_desc); 1486220691Sbschmidt error = iwn_dma_contig_alloc(sc, &ring->desc_dma, (void **)&ring->desc, 1487220691Sbschmidt size, 256); 1488178676Ssam if (error != 0) { 1489178676Ssam device_printf(sc->sc_dev, 1490198429Srpaulo "%s: could not allocate TX ring DMA memory, error %d\n", 1491178676Ssam __func__, error); 1492178676Ssam goto fail; 1493178676Ssam } 1494198429Srpaulo /* 1495198429Srpaulo * We only use rings 0 through 4 (4 EDCA + cmd) so there is no need 1496198429Srpaulo * to allocate commands space for other rings. 1497220725Sbschmidt * XXX Do we really need to allocate descriptors for other rings? 1498198429Srpaulo */ 1499198429Srpaulo if (qid > 4) 1500198429Srpaulo return 0; 1501198429Srpaulo 1502220726Sbschmidt size = IWN_TX_RING_COUNT * sizeof (struct iwn_tx_cmd); 1503220691Sbschmidt error = iwn_dma_contig_alloc(sc, &ring->cmd_dma, (void **)&ring->cmd, 1504220691Sbschmidt size, 4); 1505178676Ssam if (error != 0) { 1506178676Ssam device_printf(sc->sc_dev, 1507198429Srpaulo "%s: could not allocate TX cmd DMA memory, error %d\n", 1508178676Ssam __func__, error); 1509178676Ssam goto fail; 1510178676Ssam } 1511178676Ssam 1512198429Srpaulo error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, 1513220726Sbschmidt BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, 1514220726Sbschmidt IWN_MAX_SCATTER - 1, MCLBYTES, BUS_DMA_NOWAIT, NULL, NULL, 1515220726Sbschmidt &ring->data_dmat); 1516198429Srpaulo if (error != 0) { 1517198429Srpaulo device_printf(sc->sc_dev, 1518220711Sbschmidt "%s: could not create TX buf DMA tag, error %d\n", 1519178676Ssam __func__, error); 1520198429Srpaulo goto fail; 1521198429Srpaulo } 1522178676Ssam 1523198429Srpaulo paddr = ring->cmd_dma.paddr; 1524178676Ssam for (i = 0; i < IWN_TX_RING_COUNT; i++) { 1525178676Ssam struct iwn_tx_data *data = &ring->data[i]; 1526178676Ssam 1527198429Srpaulo data->cmd_paddr = paddr; 1528198429Srpaulo data->scratch_paddr = paddr + 12; 1529198429Srpaulo paddr += sizeof (struct iwn_tx_cmd); 1530198429Srpaulo 1531201209Srpaulo error = bus_dmamap_create(ring->data_dmat, 0, &data->map); 1532178676Ssam if (error != 0) { 1533178676Ssam device_printf(sc->sc_dev, 1534220711Sbschmidt "%s: could not create TX buf DMA map, error %d\n", 1535178676Ssam __func__, error); 1536178676Ssam goto fail; 1537178676Ssam } 1538178676Ssam } 1539178676Ssam return 0; 1540220726Sbschmidt 1541220726Sbschmidtfail: iwn_free_tx_ring(sc, ring); 1542178676Ssam return error; 1543178676Ssam} 1544178676Ssam 1545206477Sbschmidtstatic void 1546178676Ssamiwn_reset_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring) 1547178676Ssam{ 1548198429Srpaulo int i; 1549178676Ssam 1550178676Ssam for (i = 0; i < IWN_TX_RING_COUNT; i++) { 1551178676Ssam struct iwn_tx_data *data = &ring->data[i]; 1552178676Ssam 1553178676Ssam if (data->m != NULL) { 1554220704Sbschmidt bus_dmamap_sync(ring->data_dmat, data->map, 1555220704Sbschmidt BUS_DMASYNC_POSTWRITE); 1556201209Srpaulo bus_dmamap_unload(ring->data_dmat, data->map); 1557178676Ssam m_freem(data->m); 1558178676Ssam data->m = NULL; 1559178676Ssam } 1560178676Ssam } 1561198429Srpaulo /* Clear TX descriptors. */ 1562198429Srpaulo memset(ring->desc, 0, ring->desc_dma.size); 1563198439Srpaulo bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, 1564198439Srpaulo BUS_DMASYNC_PREWRITE); 1565198429Srpaulo sc->qfullmsk &= ~(1 << ring->qid); 1566178676Ssam ring->queued = 0; 1567178676Ssam ring->cur = 0; 1568178676Ssam} 1569178676Ssam 1570206477Sbschmidtstatic void 1571178676Ssamiwn_free_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring) 1572178676Ssam{ 1573178676Ssam int i; 1574178676Ssam 1575178676Ssam iwn_dma_contig_free(&ring->desc_dma); 1576178676Ssam iwn_dma_contig_free(&ring->cmd_dma); 1577178676Ssam 1578201209Srpaulo for (i = 0; i < IWN_TX_RING_COUNT; i++) { 1579201209Srpaulo struct iwn_tx_data *data = &ring->data[i]; 1580178676Ssam 1581201209Srpaulo if (data->m != NULL) { 1582201209Srpaulo bus_dmamap_sync(ring->data_dmat, data->map, 1583201209Srpaulo BUS_DMASYNC_POSTWRITE); 1584201209Srpaulo bus_dmamap_unload(ring->data_dmat, data->map); 1585201209Srpaulo m_freem(data->m); 1586178676Ssam } 1587201209Srpaulo if (data->map != NULL) 1588201209Srpaulo bus_dmamap_destroy(ring->data_dmat, data->map); 1589178676Ssam } 1590220701Sbschmidt if (ring->data_dmat != NULL) { 1591220701Sbschmidt bus_dma_tag_destroy(ring->data_dmat); 1592220701Sbschmidt ring->data_dmat = NULL; 1593220701Sbschmidt } 1594178676Ssam} 1595178676Ssam 1596206477Sbschmidtstatic void 1597201209Srpauloiwn5000_ict_reset(struct iwn_softc *sc) 1598201209Srpaulo{ 1599201209Srpaulo /* Disable interrupts. */ 1600201209Srpaulo IWN_WRITE(sc, IWN_INT_MASK, 0); 1601201209Srpaulo 1602201209Srpaulo /* Reset ICT table. */ 1603201209Srpaulo memset(sc->ict, 0, IWN_ICT_SIZE); 1604201209Srpaulo sc->ict_cur = 0; 1605201209Srpaulo 1606220725Sbschmidt /* Set physical address of ICT table (4KB aligned). */ 1607201209Srpaulo DPRINTF(sc, IWN_DEBUG_RESET, "%s: enabling ICT\n", __func__); 1608201209Srpaulo IWN_WRITE(sc, IWN_DRAM_INT_TBL, IWN_DRAM_INT_TBL_ENABLE | 1609201209Srpaulo IWN_DRAM_INT_TBL_WRAP_CHECK | sc->ict_dma.paddr >> 12); 1610201209Srpaulo 1611201209Srpaulo /* Enable periodic RX interrupt. */ 1612201209Srpaulo sc->int_mask |= IWN_INT_RX_PERIODIC; 1613201209Srpaulo /* Switch to ICT interrupt mode in driver. */ 1614201209Srpaulo sc->sc_flags |= IWN_FLAG_USE_ICT; 1615201209Srpaulo 1616201209Srpaulo /* Re-enable interrupts. */ 1617201209Srpaulo IWN_WRITE(sc, IWN_INT, 0xffffffff); 1618201209Srpaulo IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask); 1619201209Srpaulo} 1620201209Srpaulo 1621206477Sbschmidtstatic int 1622198429Srpauloiwn_read_eeprom(struct iwn_softc *sc, uint8_t macaddr[IEEE80211_ADDR_LEN]) 1623178676Ssam{ 1624220728Sbschmidt struct iwn_ops *ops = &sc->ops; 1625220723Sbschmidt uint16_t val; 1626198429Srpaulo int error; 1627178676Ssam 1628198429Srpaulo /* Check whether adapter has an EEPROM or an OTPROM. */ 1629198429Srpaulo if (sc->hw_type >= IWN_HW_REV_TYPE_1000 && 1630198429Srpaulo (IWN_READ(sc, IWN_OTP_GP) & IWN_OTP_GP_DEV_SEL_OTP)) 1631198429Srpaulo sc->sc_flags |= IWN_FLAG_HAS_OTPROM; 1632198429Srpaulo DPRINTF(sc, IWN_DEBUG_RESET, "%s found\n", 1633198429Srpaulo (sc->sc_flags & IWN_FLAG_HAS_OTPROM) ? "OTPROM" : "EEPROM"); 1634178676Ssam 1635201209Srpaulo /* Adapter has to be powered on for EEPROM access to work. */ 1636220726Sbschmidt if ((error = iwn_apm_init(sc)) != 0) { 1637201209Srpaulo device_printf(sc->sc_dev, 1638220726Sbschmidt "%s: could not power ON adapter, error %d\n", __func__, 1639220726Sbschmidt error); 1640201209Srpaulo return error; 1641201209Srpaulo } 1642201209Srpaulo 1643198429Srpaulo if ((IWN_READ(sc, IWN_EEPROM_GP) & 0x7) == 0) { 1644198429Srpaulo device_printf(sc->sc_dev, "%s: bad ROM signature\n", __func__); 1645198429Srpaulo return EIO; 1646198429Srpaulo } 1647220726Sbschmidt if ((error = iwn_eeprom_lock(sc)) != 0) { 1648220726Sbschmidt device_printf(sc->sc_dev, "%s: could not lock ROM, error %d\n", 1649198429Srpaulo __func__, error); 1650198429Srpaulo return error; 1651198429Srpaulo } 1652201209Srpaulo if (sc->sc_flags & IWN_FLAG_HAS_OTPROM) { 1653220726Sbschmidt if ((error = iwn_init_otprom(sc)) != 0) { 1654201209Srpaulo device_printf(sc->sc_dev, 1655201209Srpaulo "%s: could not initialize OTPROM, error %d\n", 1656201209Srpaulo __func__, error); 1657201209Srpaulo return error; 1658201209Srpaulo } 1659198429Srpaulo } 1660178676Ssam 1661220729Sbschmidt iwn_read_prom_data(sc, IWN_EEPROM_SKU_CAP, &val, 2); 1662220729Sbschmidt DPRINTF(sc, IWN_DEBUG_RESET, "SKU capabilities=0x%04x\n", le16toh(val)); 1663220729Sbschmidt /* Check if HT support is bonded out. */ 1664220729Sbschmidt if (val & htole16(IWN_EEPROM_SKU_CAP_11N)) 1665220729Sbschmidt sc->sc_flags |= IWN_FLAG_HAS_11N; 1666220729Sbschmidt 1667198429Srpaulo iwn_read_prom_data(sc, IWN_EEPROM_RFCFG, &val, 2); 1668198429Srpaulo sc->rfcfg = le16toh(val); 1669198429Srpaulo DPRINTF(sc, IWN_DEBUG_RESET, "radio config=0x%04x\n", sc->rfcfg); 1670220727Sbschmidt /* Read Tx/Rx chains from ROM unless it's known to be broken. */ 1671220727Sbschmidt if (sc->txchainmask == 0) 1672220727Sbschmidt sc->txchainmask = IWN_RFCFG_TXANTMSK(sc->rfcfg); 1673220727Sbschmidt if (sc->rxchainmask == 0) 1674220727Sbschmidt sc->rxchainmask = IWN_RFCFG_RXANTMSK(sc->rfcfg); 1675178676Ssam 1676198429Srpaulo /* Read MAC address. */ 1677198429Srpaulo iwn_read_prom_data(sc, IWN_EEPROM_MAC, macaddr, 6); 1678178676Ssam 1679198429Srpaulo /* Read adapter-specific information from EEPROM. */ 1680220728Sbschmidt ops->read_eeprom(sc); 1681178676Ssam 1682201209Srpaulo iwn_apm_stop(sc); /* Power OFF adapter. */ 1683201209Srpaulo 1684198429Srpaulo iwn_eeprom_unlock(sc); 1685198429Srpaulo return 0; 1686178676Ssam} 1687178676Ssam 1688206477Sbschmidtstatic void 1689198429Srpauloiwn4965_read_eeprom(struct iwn_softc *sc) 1690178676Ssam{ 1691201209Srpaulo uint32_t addr; 1692220723Sbschmidt uint16_t val; 1693198429Srpaulo int i; 1694178676Ssam 1695220725Sbschmidt /* Read regulatory domain (4 ASCII characters). */ 1696198429Srpaulo iwn_read_prom_data(sc, IWN4965_EEPROM_DOMAIN, sc->eeprom_domain, 4); 1697178676Ssam 1698220725Sbschmidt /* Read the list of authorized channels (20MHz ones only). */ 1699201209Srpaulo for (i = 0; i < 5; i++) { 1700201209Srpaulo addr = iwn4965_regulatory_bands[i]; 1701201209Srpaulo iwn_read_eeprom_channels(sc, i, addr); 1702201209Srpaulo } 1703198429Srpaulo 1704198429Srpaulo /* Read maximum allowed TX power for 2GHz and 5GHz bands. */ 1705198429Srpaulo iwn_read_prom_data(sc, IWN4965_EEPROM_MAXPOW, &val, 2); 1706198429Srpaulo sc->maxpwr2GHz = val & 0xff; 1707198429Srpaulo sc->maxpwr5GHz = val >> 8; 1708198429Srpaulo /* Check that EEPROM values are within valid range. */ 1709198429Srpaulo if (sc->maxpwr5GHz < 20 || sc->maxpwr5GHz > 50) 1710198429Srpaulo sc->maxpwr5GHz = 38; 1711198429Srpaulo if (sc->maxpwr2GHz < 20 || sc->maxpwr2GHz > 50) 1712198429Srpaulo sc->maxpwr2GHz = 38; 1713198429Srpaulo DPRINTF(sc, IWN_DEBUG_RESET, "maxpwr 2GHz=%d 5GHz=%d\n", 1714198429Srpaulo sc->maxpwr2GHz, sc->maxpwr5GHz); 1715198429Srpaulo 1716198429Srpaulo /* Read samples for each TX power group. */ 1717198429Srpaulo iwn_read_prom_data(sc, IWN4965_EEPROM_BANDS, sc->bands, 1718198429Srpaulo sizeof sc->bands); 1719198429Srpaulo 1720198429Srpaulo /* Read voltage at which samples were taken. */ 1721198429Srpaulo iwn_read_prom_data(sc, IWN4965_EEPROM_VOLTAGE, &val, 2); 1722198429Srpaulo sc->eeprom_voltage = (int16_t)le16toh(val); 1723198429Srpaulo DPRINTF(sc, IWN_DEBUG_RESET, "voltage=%d (in 0.3V)\n", 1724198429Srpaulo sc->eeprom_voltage); 1725198429Srpaulo 1726198429Srpaulo#ifdef IWN_DEBUG 1727198429Srpaulo /* Print samples. */ 1728201209Srpaulo if (sc->sc_debug & IWN_DEBUG_ANY) { 1729198429Srpaulo for (i = 0; i < IWN_NBANDS; i++) 1730198429Srpaulo iwn4965_print_power_group(sc, i); 1731178676Ssam } 1732198429Srpaulo#endif 1733178676Ssam} 1734178676Ssam 1735198429Srpaulo#ifdef IWN_DEBUG 1736206477Sbschmidtstatic void 1737198429Srpauloiwn4965_print_power_group(struct iwn_softc *sc, int i) 1738178676Ssam{ 1739198429Srpaulo struct iwn4965_eeprom_band *band = &sc->bands[i]; 1740198429Srpaulo struct iwn4965_eeprom_chan_samples *chans = band->chans; 1741198429Srpaulo int j, c; 1742178676Ssam 1743198429Srpaulo printf("===band %d===\n", i); 1744198429Srpaulo printf("chan lo=%d, chan hi=%d\n", band->lo, band->hi); 1745198429Srpaulo printf("chan1 num=%d\n", chans[0].num); 1746198429Srpaulo for (c = 0; c < 2; c++) { 1747198429Srpaulo for (j = 0; j < IWN_NSAMPLES; j++) { 1748198429Srpaulo printf("chain %d, sample %d: temp=%d gain=%d " 1749198429Srpaulo "power=%d pa_det=%d\n", c, j, 1750198429Srpaulo chans[0].samples[c][j].temp, 1751198429Srpaulo chans[0].samples[c][j].gain, 1752198429Srpaulo chans[0].samples[c][j].power, 1753198429Srpaulo chans[0].samples[c][j].pa_det); 1754198429Srpaulo } 1755198429Srpaulo } 1756198429Srpaulo printf("chan2 num=%d\n", chans[1].num); 1757198429Srpaulo for (c = 0; c < 2; c++) { 1758198429Srpaulo for (j = 0; j < IWN_NSAMPLES; j++) { 1759198429Srpaulo printf("chain %d, sample %d: temp=%d gain=%d " 1760198429Srpaulo "power=%d pa_det=%d\n", c, j, 1761198429Srpaulo chans[1].samples[c][j].temp, 1762198429Srpaulo chans[1].samples[c][j].gain, 1763198429Srpaulo chans[1].samples[c][j].power, 1764198429Srpaulo chans[1].samples[c][j].pa_det); 1765198429Srpaulo } 1766198429Srpaulo } 1767178676Ssam} 1768198429Srpaulo#endif 1769178676Ssam 1770206477Sbschmidtstatic void 1771198429Srpauloiwn5000_read_eeprom(struct iwn_softc *sc) 1772178676Ssam{ 1773206444Sbschmidt struct iwn5000_eeprom_calib_hdr hdr; 1774220674Sbschmidt int32_t volt; 1775220723Sbschmidt uint32_t base, addr; 1776220723Sbschmidt uint16_t val; 1777198429Srpaulo int i; 1778178676Ssam 1779220725Sbschmidt /* Read regulatory domain (4 ASCII characters). */ 1780198429Srpaulo iwn_read_prom_data(sc, IWN5000_EEPROM_REG, &val, 2); 1781198429Srpaulo base = le16toh(val); 1782198429Srpaulo iwn_read_prom_data(sc, base + IWN5000_EEPROM_DOMAIN, 1783198429Srpaulo sc->eeprom_domain, 4); 1784178676Ssam 1785220725Sbschmidt /* Read the list of authorized channels (20MHz ones only). */ 1786201209Srpaulo for (i = 0; i < 5; i++) { 1787198429Srpaulo addr = base + iwn5000_regulatory_bands[i]; 1788201209Srpaulo iwn_read_eeprom_channels(sc, i, addr); 1789198429Srpaulo } 1790178676Ssam 1791201209Srpaulo /* Read enhanced TX power information for 6000 Series. */ 1792201209Srpaulo if (sc->hw_type >= IWN_HW_REV_TYPE_6000) 1793201209Srpaulo iwn_read_eeprom_enhinfo(sc); 1794201209Srpaulo 1795198429Srpaulo iwn_read_prom_data(sc, IWN5000_EEPROM_CAL, &val, 2); 1796198429Srpaulo base = le16toh(val); 1797206444Sbschmidt iwn_read_prom_data(sc, base, &hdr, sizeof hdr); 1798206444Sbschmidt DPRINTF(sc, IWN_DEBUG_CALIBRATE, 1799220726Sbschmidt "%s: calib version=%u pa type=%u voltage=%u\n", __func__, 1800220726Sbschmidt hdr.version, hdr.pa_type, le16toh(hdr.volt)); 1801210108Sbschmidt sc->calib_ver = hdr.version; 1802206444Sbschmidt 1803198429Srpaulo if (sc->hw_type == IWN_HW_REV_TYPE_5150) { 1804201209Srpaulo /* Compute temperature offset. */ 1805198429Srpaulo iwn_read_prom_data(sc, base + IWN5000_EEPROM_TEMP, &val, 2); 1806220674Sbschmidt sc->eeprom_temp = le16toh(val); 1807198429Srpaulo iwn_read_prom_data(sc, base + IWN5000_EEPROM_VOLT, &val, 2); 1808198429Srpaulo volt = le16toh(val); 1809220674Sbschmidt sc->temp_off = sc->eeprom_temp - (volt / -5); 1810201209Srpaulo DPRINTF(sc, IWN_DEBUG_CALIBRATE, "temp=%d volt=%d offset=%dK\n", 1811220674Sbschmidt sc->eeprom_temp, volt, sc->temp_off); 1812220674Sbschmidt } else { 1813220674Sbschmidt /* Read crystal calibration. */ 1814220674Sbschmidt iwn_read_prom_data(sc, base + IWN5000_EEPROM_CRYSTAL, 1815220674Sbschmidt &sc->eeprom_crystal, sizeof (uint32_t)); 1816220674Sbschmidt DPRINTF(sc, IWN_DEBUG_CALIBRATE, "crystal calibration 0x%08x\n", 1817220674Sbschmidt le32toh(sc->eeprom_crystal)); 1818178676Ssam } 1819178676Ssam} 1820178676Ssam 1821201209Srpaulo/* 1822201209Srpaulo * Translate EEPROM flags to net80211. 1823201209Srpaulo */ 1824201209Srpaulostatic uint32_t 1825201209Srpauloiwn_eeprom_channel_flags(struct iwn_eeprom_chan *channel) 1826201209Srpaulo{ 1827201209Srpaulo uint32_t nflags; 1828201209Srpaulo 1829201209Srpaulo nflags = 0; 1830201209Srpaulo if ((channel->flags & IWN_EEPROM_CHAN_ACTIVE) == 0) 1831201209Srpaulo nflags |= IEEE80211_CHAN_PASSIVE; 1832201209Srpaulo if ((channel->flags & IWN_EEPROM_CHAN_IBSS) == 0) 1833201209Srpaulo nflags |= IEEE80211_CHAN_NOADHOC; 1834201209Srpaulo if (channel->flags & IWN_EEPROM_CHAN_RADAR) { 1835201209Srpaulo nflags |= IEEE80211_CHAN_DFS; 1836201209Srpaulo /* XXX apparently IBSS may still be marked */ 1837201209Srpaulo nflags |= IEEE80211_CHAN_NOADHOC; 1838201209Srpaulo } 1839201209Srpaulo 1840201209Srpaulo return nflags; 1841201209Srpaulo} 1842201209Srpaulo 1843198429Srpaulostatic void 1844201209Srpauloiwn_read_eeprom_band(struct iwn_softc *sc, int n) 1845178676Ssam{ 1846198429Srpaulo struct ifnet *ifp = sc->sc_ifp; 1847198429Srpaulo struct ieee80211com *ic = ifp->if_l2com; 1848201209Srpaulo struct iwn_eeprom_chan *channels = sc->eeprom_channels[n]; 1849201209Srpaulo const struct iwn_chan_band *band = &iwn_bands[n]; 1850198429Srpaulo struct ieee80211_channel *c; 1851220687Sbschmidt uint8_t chan; 1852220687Sbschmidt int i, nflags; 1853178676Ssam 1854198429Srpaulo for (i = 0; i < band->nchan; i++) { 1855198429Srpaulo if (!(channels[i].flags & IWN_EEPROM_CHAN_VALID)) { 1856198429Srpaulo DPRINTF(sc, IWN_DEBUG_RESET, 1857198429Srpaulo "skip chan %d flags 0x%x maxpwr %d\n", 1858198429Srpaulo band->chan[i], channels[i].flags, 1859198429Srpaulo channels[i].maxpwr); 1860198429Srpaulo continue; 1861198429Srpaulo } 1862198429Srpaulo chan = band->chan[i]; 1863201209Srpaulo nflags = iwn_eeprom_channel_flags(&channels[i]); 1864178676Ssam 1865198429Srpaulo c = &ic->ic_channels[ic->ic_nchans++]; 1866198429Srpaulo c->ic_ieee = chan; 1867198429Srpaulo c->ic_maxregpower = channels[i].maxpwr; 1868198429Srpaulo c->ic_maxpower = 2*c->ic_maxregpower; 1869206445Sbschmidt 1870201209Srpaulo if (n == 0) { /* 2GHz band */ 1871220726Sbschmidt c->ic_freq = ieee80211_ieee2mhz(chan, IEEE80211_CHAN_G); 1872198429Srpaulo /* G =>'s B is supported */ 1873198429Srpaulo c->ic_flags = IEEE80211_CHAN_B | nflags; 1874198429Srpaulo c = &ic->ic_channels[ic->ic_nchans++]; 1875198429Srpaulo c[0] = c[-1]; 1876198429Srpaulo c->ic_flags = IEEE80211_CHAN_G | nflags; 1877198429Srpaulo } else { /* 5GHz band */ 1878220726Sbschmidt c->ic_freq = ieee80211_ieee2mhz(chan, IEEE80211_CHAN_A); 1879198429Srpaulo c->ic_flags = IEEE80211_CHAN_A | nflags; 1880178676Ssam } 1881220723Sbschmidt 1882220723Sbschmidt /* Save maximum allowed TX power for this channel. */ 1883220723Sbschmidt sc->maxpwr[chan] = channels[i].maxpwr; 1884220723Sbschmidt 1885220723Sbschmidt DPRINTF(sc, IWN_DEBUG_RESET, 1886220726Sbschmidt "add chan %d flags 0x%x maxpwr %d\n", chan, 1887220726Sbschmidt channels[i].flags, channels[i].maxpwr); 1888220723Sbschmidt 1889201209Srpaulo#if 0 /* HT */ 1890198429Srpaulo /* XXX no constraints on using HT20 */ 1891198429Srpaulo /* add HT20, HT40 added separately */ 1892198429Srpaulo c = &ic->ic_channels[ic->ic_nchans++]; 1893198429Srpaulo c[0] = c[-1]; 1894198429Srpaulo c->ic_flags |= IEEE80211_CHAN_HT20; 1895198429Srpaulo /* XXX NARROW =>'s 1/2 and 1/4 width? */ 1896201209Srpaulo#endif 1897178676Ssam } 1898178676Ssam} 1899178676Ssam 1900201209Srpaulo#if 0 /* HT */ 1901198429Srpaulostatic void 1902201209Srpauloiwn_read_eeprom_ht40(struct iwn_softc *sc, int n) 1903178676Ssam{ 1904198429Srpaulo struct ifnet *ifp = sc->sc_ifp; 1905198429Srpaulo struct ieee80211com *ic = ifp->if_l2com; 1906201209Srpaulo struct iwn_eeprom_chan *channels = sc->eeprom_channels[n]; 1907201209Srpaulo const struct iwn_chan_band *band = &iwn_bands[n]; 1908198429Srpaulo struct ieee80211_channel *c, *cent, *extc; 1909198429Srpaulo int i; 1910178676Ssam 1911198429Srpaulo for (i = 0; i < band->nchan; i++) { 1912198429Srpaulo if (!(channels[i].flags & IWN_EEPROM_CHAN_VALID) || 1913198429Srpaulo !(channels[i].flags & IWN_EEPROM_CHAN_WIDE)) { 1914198429Srpaulo DPRINTF(sc, IWN_DEBUG_RESET, 1915198429Srpaulo "skip chan %d flags 0x%x maxpwr %d\n", 1916198429Srpaulo band->chan[i], channels[i].flags, 1917198429Srpaulo channels[i].maxpwr); 1918198429Srpaulo continue; 1919198429Srpaulo } 1920198429Srpaulo /* 1921198429Srpaulo * Each entry defines an HT40 channel pair; find the 1922198429Srpaulo * center channel, then the extension channel above. 1923198429Srpaulo */ 1924198429Srpaulo cent = ieee80211_find_channel_byieee(ic, band->chan[i], 1925201209Srpaulo band->flags & ~IEEE80211_CHAN_HT); 1926198429Srpaulo if (cent == NULL) { /* XXX shouldn't happen */ 1927198429Srpaulo device_printf(sc->sc_dev, 1928198429Srpaulo "%s: no entry for channel %d\n", 1929198429Srpaulo __func__, band->chan[i]); 1930198429Srpaulo continue; 1931198429Srpaulo } 1932198429Srpaulo extc = ieee80211_find_channel(ic, cent->ic_freq+20, 1933201209Srpaulo band->flags & ~IEEE80211_CHAN_HT); 1934198429Srpaulo if (extc == NULL) { 1935198429Srpaulo DPRINTF(sc, IWN_DEBUG_RESET, 1936198429Srpaulo "skip chan %d, extension channel not found\n", 1937198429Srpaulo band->chan[i]); 1938198429Srpaulo continue; 1939198429Srpaulo } 1940178676Ssam 1941198429Srpaulo DPRINTF(sc, IWN_DEBUG_RESET, 1942198429Srpaulo "add ht40 chan %d flags 0x%x maxpwr %d\n", 1943198429Srpaulo band->chan[i], channels[i].flags, channels[i].maxpwr); 1944178676Ssam 1945198429Srpaulo c = &ic->ic_channels[ic->ic_nchans++]; 1946198429Srpaulo c[0] = cent[0]; 1947198429Srpaulo c->ic_extieee = extc->ic_ieee; 1948198429Srpaulo c->ic_flags &= ~IEEE80211_CHAN_HT; 1949198429Srpaulo c->ic_flags |= IEEE80211_CHAN_HT40U; 1950198429Srpaulo c = &ic->ic_channels[ic->ic_nchans++]; 1951198429Srpaulo c[0] = extc[0]; 1952198429Srpaulo c->ic_extieee = cent->ic_ieee; 1953198429Srpaulo c->ic_flags &= ~IEEE80211_CHAN_HT; 1954198429Srpaulo c->ic_flags |= IEEE80211_CHAN_HT40D; 1955178676Ssam } 1956198429Srpaulo} 1957201209Srpaulo#endif 1958178676Ssam 1959198429Srpaulostatic void 1960201209Srpauloiwn_read_eeprom_channels(struct iwn_softc *sc, int n, uint32_t addr) 1961198429Srpaulo{ 1962198429Srpaulo struct ifnet *ifp = sc->sc_ifp; 1963198429Srpaulo struct ieee80211com *ic = ifp->if_l2com; 1964178676Ssam 1965201209Srpaulo iwn_read_prom_data(sc, addr, &sc->eeprom_channels[n], 1966201209Srpaulo iwn_bands[n].nchan * sizeof (struct iwn_eeprom_chan)); 1967201209Srpaulo 1968198429Srpaulo if (n < 5) 1969201209Srpaulo iwn_read_eeprom_band(sc, n); 1970201209Srpaulo#if 0 /* HT */ 1971198429Srpaulo else 1972201209Srpaulo iwn_read_eeprom_ht40(sc, n); 1973201209Srpaulo#endif 1974198429Srpaulo ieee80211_sort_channels(ic->ic_channels, ic->ic_nchans); 1975178676Ssam} 1976178676Ssam 1977220723Sbschmidtstatic struct iwn_eeprom_chan * 1978220723Sbschmidtiwn_find_eeprom_channel(struct iwn_softc *sc, struct ieee80211_channel *c) 1979220723Sbschmidt{ 1980220723Sbschmidt int i, j; 1981220723Sbschmidt 1982220723Sbschmidt for (j = 0; j < 7; j++) { 1983220723Sbschmidt for (i = 0; i < iwn_bands[j].nchan; i++) { 1984220723Sbschmidt if (iwn_bands[j].chan[i] == c->ic_ieee) 1985220723Sbschmidt return &sc->eeprom_channels[j][i]; 1986220723Sbschmidt } 1987220723Sbschmidt } 1988220723Sbschmidt 1989220723Sbschmidt return NULL; 1990220723Sbschmidt} 1991220723Sbschmidt 1992220723Sbschmidt/* 1993220723Sbschmidt * Enforce flags read from EEPROM. 1994220723Sbschmidt */ 1995220723Sbschmidtstatic int 1996220723Sbschmidtiwn_setregdomain(struct ieee80211com *ic, struct ieee80211_regdomain *rd, 1997220723Sbschmidt int nchan, struct ieee80211_channel chans[]) 1998220723Sbschmidt{ 1999220723Sbschmidt struct iwn_softc *sc = ic->ic_ifp->if_softc; 2000220723Sbschmidt int i; 2001220723Sbschmidt 2002220723Sbschmidt for (i = 0; i < nchan; i++) { 2003220723Sbschmidt struct ieee80211_channel *c = &chans[i]; 2004220723Sbschmidt struct iwn_eeprom_chan *channel; 2005220723Sbschmidt 2006220723Sbschmidt channel = iwn_find_eeprom_channel(sc, c); 2007220723Sbschmidt if (channel == NULL) { 2008220723Sbschmidt if_printf(ic->ic_ifp, 2009220723Sbschmidt "%s: invalid channel %u freq %u/0x%x\n", 2010220723Sbschmidt __func__, c->ic_ieee, c->ic_freq, c->ic_flags); 2011220723Sbschmidt return EINVAL; 2012220723Sbschmidt } 2013220723Sbschmidt c->ic_flags |= iwn_eeprom_channel_flags(channel); 2014220723Sbschmidt } 2015220723Sbschmidt 2016220723Sbschmidt return 0; 2017220723Sbschmidt} 2018220723Sbschmidt 2019201209Srpaulo#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) 2020201209Srpaulo 2021206477Sbschmidtstatic void 2022201209Srpauloiwn_read_eeprom_enhinfo(struct iwn_softc *sc) 2023201209Srpaulo{ 2024201209Srpaulo struct iwn_eeprom_enhinfo enhinfo[35]; 2025201209Srpaulo uint16_t val, base; 2026201209Srpaulo int8_t maxpwr; 2027201209Srpaulo int i; 2028201209Srpaulo 2029201209Srpaulo iwn_read_prom_data(sc, IWN5000_EEPROM_REG, &val, 2); 2030201209Srpaulo base = le16toh(val); 2031201209Srpaulo iwn_read_prom_data(sc, base + IWN6000_EEPROM_ENHINFO, 2032201209Srpaulo enhinfo, sizeof enhinfo); 2033201209Srpaulo 2034201209Srpaulo memset(sc->enh_maxpwr, 0, sizeof sc->enh_maxpwr); 2035201209Srpaulo for (i = 0; i < nitems(enhinfo); i++) { 2036201209Srpaulo if (enhinfo[i].chan == 0 || enhinfo[i].reserved != 0) 2037201209Srpaulo continue; /* Skip invalid entries. */ 2038201209Srpaulo 2039201209Srpaulo maxpwr = 0; 2040201209Srpaulo if (sc->txchainmask & IWN_ANT_A) 2041201209Srpaulo maxpwr = MAX(maxpwr, enhinfo[i].chain[0]); 2042201209Srpaulo if (sc->txchainmask & IWN_ANT_B) 2043201209Srpaulo maxpwr = MAX(maxpwr, enhinfo[i].chain[1]); 2044201209Srpaulo if (sc->txchainmask & IWN_ANT_C) 2045201209Srpaulo maxpwr = MAX(maxpwr, enhinfo[i].chain[2]); 2046201209Srpaulo if (sc->ntxchains == 2) 2047201209Srpaulo maxpwr = MAX(maxpwr, enhinfo[i].mimo2); 2048201209Srpaulo else if (sc->ntxchains == 3) 2049201209Srpaulo maxpwr = MAX(maxpwr, enhinfo[i].mimo3); 2050201209Srpaulo maxpwr /= 2; /* Convert half-dBm to dBm. */ 2051201209Srpaulo 2052201209Srpaulo DPRINTF(sc, IWN_DEBUG_RESET, "enhinfo %d, maxpwr=%d\n", i, 2053201209Srpaulo maxpwr); 2054201209Srpaulo sc->enh_maxpwr[i] = maxpwr; 2055201209Srpaulo } 2056201209Srpaulo} 2057201209Srpaulo 2058206477Sbschmidtstatic struct ieee80211_node * 2059198429Srpauloiwn_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) 2060178676Ssam{ 2061198429Srpaulo return malloc(sizeof (struct iwn_node), M_80211_NODE,M_NOWAIT | M_ZERO); 2062198429Srpaulo} 2063178676Ssam 2064220715Sbschmidtstatic void 2065220715Sbschmidtiwn_newassoc(struct ieee80211_node *ni, int isnew) 2066220715Sbschmidt{ 2067220715Sbschmidt struct iwn_node *wn = (void *)ni; 2068220715Sbschmidt int ridx, i; 2069220715Sbschmidt 2070220715Sbschmidt for (i = 0; i < ni->ni_rates.rs_nrates; i++) { 2071220715Sbschmidt ridx = iwn_plcp_signal(ni->ni_rates.rs_rates[i]); 2072220715Sbschmidt wn->ridx[i] = ridx; 2073220715Sbschmidt } 2074220715Sbschmidt} 2075220715Sbschmidt 2076206477Sbschmidtstatic int 2077198429Srpauloiwn_media_change(struct ifnet *ifp) 2078178676Ssam{ 2079220726Sbschmidt int error; 2080220726Sbschmidt 2081220726Sbschmidt error = ieee80211_media_change(ifp); 2082198429Srpaulo /* NB: only the fixed rate can change and that doesn't need a reset */ 2083198429Srpaulo return (error == ENETRESET ? 0 : error); 2084198429Srpaulo} 2085178676Ssam 2086206477Sbschmidtstatic int 2087198429Srpauloiwn_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) 2088198429Srpaulo{ 2089198429Srpaulo struct iwn_vap *ivp = IWN_VAP(vap); 2090198429Srpaulo struct ieee80211com *ic = vap->iv_ic; 2091198429Srpaulo struct iwn_softc *sc = ic->ic_ifp->if_softc; 2092220688Sbschmidt int error = 0; 2093178676Ssam 2094198429Srpaulo DPRINTF(sc, IWN_DEBUG_STATE, "%s: %s -> %s\n", __func__, 2095220726Sbschmidt ieee80211_state_name[vap->iv_state], ieee80211_state_name[nstate]); 2096178676Ssam 2097198429Srpaulo IEEE80211_UNLOCK(ic); 2098198429Srpaulo IWN_LOCK(sc); 2099220667Sbschmidt callout_stop(&sc->calib_to); 2100178676Ssam 2101210114Sbschmidt switch (nstate) { 2102210114Sbschmidt case IEEE80211_S_ASSOC: 2103210114Sbschmidt if (vap->iv_state != IEEE80211_S_RUN) 2104210114Sbschmidt break; 2105210114Sbschmidt /* FALLTHROUGH */ 2106210114Sbschmidt case IEEE80211_S_AUTH: 2107210114Sbschmidt if (vap->iv_state == IEEE80211_S_AUTH) 2108210114Sbschmidt break; 2109210114Sbschmidt 2110210114Sbschmidt /* 2111210114Sbschmidt * !AUTH -> AUTH transition requires state reset to handle 2112210114Sbschmidt * reassociations correctly. 2113210114Sbschmidt */ 2114198439Srpaulo sc->rxon.associd = 0; 2115198439Srpaulo sc->rxon.filter &= ~htole32(IWN_FILTER_BSS); 2116220667Sbschmidt sc->calib.state = IWN_CALIB_STATE_INIT; 2117220667Sbschmidt 2118220688Sbschmidt if ((error = iwn_auth(sc, vap)) != 0) { 2119220688Sbschmidt device_printf(sc->sc_dev, 2120220688Sbschmidt "%s: could not move to auth state\n", __func__); 2121220688Sbschmidt } 2122210114Sbschmidt break; 2123210114Sbschmidt 2124210114Sbschmidt case IEEE80211_S_RUN: 2125198429Srpaulo /* 2126210114Sbschmidt * RUN -> RUN transition; Just restart the timers. 2127210114Sbschmidt */ 2128220667Sbschmidt if (vap->iv_state == IEEE80211_S_RUN) { 2129220667Sbschmidt sc->calib_cnt = 0; 2130210114Sbschmidt break; 2131210114Sbschmidt } 2132210114Sbschmidt 2133210114Sbschmidt /* 2134198429Srpaulo * !RUN -> RUN requires setting the association id 2135198429Srpaulo * which is done with a firmware cmd. We also defer 2136198429Srpaulo * starting the timers until that work is done. 2137198429Srpaulo */ 2138220688Sbschmidt if ((error = iwn_run(sc, vap)) != 0) { 2139220688Sbschmidt device_printf(sc->sc_dev, 2140220688Sbschmidt "%s: could not move to run state\n", __func__); 2141220688Sbschmidt } 2142210114Sbschmidt break; 2143210114Sbschmidt 2144220667Sbschmidt case IEEE80211_S_INIT: 2145220667Sbschmidt sc->calib.state = IWN_CALIB_STATE_INIT; 2146220667Sbschmidt break; 2147220667Sbschmidt 2148210114Sbschmidt default: 2149210114Sbschmidt break; 2150178676Ssam } 2151198429Srpaulo IWN_UNLOCK(sc); 2152198429Srpaulo IEEE80211_LOCK(ic); 2153220688Sbschmidt if (error != 0) 2154220688Sbschmidt return error; 2155198429Srpaulo return ivp->iv_newstate(vap, nstate, arg); 2156178676Ssam} 2157178676Ssam 2158220667Sbschmidtstatic void 2159220667Sbschmidtiwn_calib_timeout(void *arg) 2160220667Sbschmidt{ 2161220667Sbschmidt struct iwn_softc *sc = arg; 2162220667Sbschmidt 2163220667Sbschmidt IWN_LOCK_ASSERT(sc); 2164220667Sbschmidt 2165220667Sbschmidt /* Force automatic TX power calibration every 60 secs. */ 2166220667Sbschmidt if (++sc->calib_cnt >= 120) { 2167220667Sbschmidt uint32_t flags = 0; 2168220667Sbschmidt 2169220667Sbschmidt DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s\n", 2170220667Sbschmidt "sending request for statistics"); 2171220667Sbschmidt (void)iwn_cmd(sc, IWN_CMD_GET_STATISTICS, &flags, 2172220667Sbschmidt sizeof flags, 1); 2173220667Sbschmidt sc->calib_cnt = 0; 2174220667Sbschmidt } 2175220667Sbschmidt callout_reset(&sc->calib_to, msecs_to_ticks(500), iwn_calib_timeout, 2176220667Sbschmidt sc); 2177220667Sbschmidt} 2178220667Sbschmidt 2179198429Srpaulo/* 2180198429Srpaulo * Process an RX_PHY firmware notification. This is usually immediately 2181198429Srpaulo * followed by an MPDU_RX_DONE notification. 2182198429Srpaulo */ 2183206477Sbschmidtstatic void 2184198429Srpauloiwn_rx_phy(struct iwn_softc *sc, struct iwn_rx_desc *desc, 2185198429Srpaulo struct iwn_rx_data *data) 2186178676Ssam{ 2187198429Srpaulo struct iwn_rx_stat *stat = (struct iwn_rx_stat *)(desc + 1); 2188198429Srpaulo 2189198429Srpaulo DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: received PHY stats\n", __func__); 2190202986Srpaulo bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD); 2191198429Srpaulo 2192198429Srpaulo /* Save RX statistics, they will be used on MPDU_RX_DONE. */ 2193198429Srpaulo memcpy(&sc->last_rx_stat, stat, sizeof (*stat)); 2194198429Srpaulo sc->last_rx_valid = 1; 2195178676Ssam} 2196178676Ssam 2197198429Srpaulo/* 2198198429Srpaulo * Process an RX_DONE (4965AGN only) or MPDU_RX_DONE firmware notification. 2199198429Srpaulo * Each MPDU_RX_DONE notification must be preceded by an RX_PHY one. 2200198429Srpaulo */ 2201206477Sbschmidtstatic void 2202198429Srpauloiwn_rx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, 2203178676Ssam struct iwn_rx_data *data) 2204178676Ssam{ 2205220728Sbschmidt struct iwn_ops *ops = &sc->ops; 2206178676Ssam struct ifnet *ifp = sc->sc_ifp; 2207178676Ssam struct ieee80211com *ic = ifp->if_l2com; 2208178676Ssam struct iwn_rx_ring *ring = &sc->rxq; 2209178676Ssam struct ieee80211_frame *wh; 2210178676Ssam struct ieee80211_node *ni; 2211198429Srpaulo struct mbuf *m, *m1; 2212178676Ssam struct iwn_rx_stat *stat; 2213178676Ssam caddr_t head; 2214178676Ssam bus_addr_t paddr; 2215198429Srpaulo uint32_t flags; 2216198429Srpaulo int error, len, rssi, nf; 2217178676Ssam 2218198429Srpaulo if (desc->type == IWN_MPDU_RX_DONE) { 2219198429Srpaulo /* Check for prior RX_PHY notification. */ 2220178676Ssam if (!sc->last_rx_valid) { 2221178676Ssam DPRINTF(sc, IWN_DEBUG_ANY, 2222201209Srpaulo "%s: missing RX_PHY\n", __func__); 2223178676Ssam return; 2224178676Ssam } 2225178676Ssam sc->last_rx_valid = 0; 2226178676Ssam stat = &sc->last_rx_stat; 2227178676Ssam } else 2228178676Ssam stat = (struct iwn_rx_stat *)(desc + 1); 2229178676Ssam 2230201209Srpaulo bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD); 2231198439Srpaulo 2232178676Ssam if (stat->cfg_phy_len > IWN_STAT_MAXLEN) { 2233178676Ssam device_printf(sc->sc_dev, 2234220724Sbschmidt "%s: invalid RX statistic header, len %d\n", __func__, 2235220724Sbschmidt stat->cfg_phy_len); 2236178676Ssam return; 2237178676Ssam } 2238198429Srpaulo if (desc->type == IWN_MPDU_RX_DONE) { 2239198429Srpaulo struct iwn_rx_mpdu *mpdu = (struct iwn_rx_mpdu *)(desc + 1); 2240198429Srpaulo head = (caddr_t)(mpdu + 1); 2241198429Srpaulo len = le16toh(mpdu->len); 2242178676Ssam } else { 2243178676Ssam head = (caddr_t)(stat + 1) + stat->cfg_phy_len; 2244178676Ssam len = le16toh(stat->len); 2245178676Ssam } 2246178676Ssam 2247198429Srpaulo flags = le32toh(*(uint32_t *)(head + len)); 2248198429Srpaulo 2249198429Srpaulo /* Discard frames with a bad FCS early. */ 2250198429Srpaulo if ((flags & IWN_RX_NOERROR) != IWN_RX_NOERROR) { 2251220724Sbschmidt DPRINTF(sc, IWN_DEBUG_RECV, "%s: RX flags error %x\n", 2252198429Srpaulo __func__, flags); 2253178676Ssam ifp->if_ierrors++; 2254178676Ssam return; 2255178676Ssam } 2256198429Srpaulo /* Discard frames that are too short. */ 2257198429Srpaulo if (len < sizeof (*wh)) { 2258178676Ssam DPRINTF(sc, IWN_DEBUG_RECV, "%s: frame too short: %d\n", 2259178676Ssam __func__, len); 2260178676Ssam ifp->if_ierrors++; 2261178676Ssam return; 2262178676Ssam } 2263178676Ssam 2264220692Sbschmidt m1 = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, IWN_RBUF_SIZE); 2265198429Srpaulo if (m1 == NULL) { 2266178676Ssam DPRINTF(sc, IWN_DEBUG_ANY, "%s: no mbuf to restock ring\n", 2267178676Ssam __func__); 2268178676Ssam ifp->if_ierrors++; 2269178676Ssam return; 2270178676Ssam } 2271201209Srpaulo bus_dmamap_unload(ring->data_dmat, data->map); 2272201209Srpaulo 2273220692Sbschmidt error = bus_dmamap_load(ring->data_dmat, data->map, mtod(m1, void *), 2274220692Sbschmidt IWN_RBUF_SIZE, iwn_dma_map_addr, &paddr, BUS_DMA_NOWAIT); 2275178676Ssam if (error != 0 && error != EFBIG) { 2276178676Ssam device_printf(sc->sc_dev, 2277178676Ssam "%s: bus_dmamap_load failed, error %d\n", __func__, error); 2278198429Srpaulo m_freem(m1); 2279220693Sbschmidt 2280220693Sbschmidt /* Try to reload the old mbuf. */ 2281220693Sbschmidt error = bus_dmamap_load(ring->data_dmat, data->map, 2282220693Sbschmidt mtod(data->m, void *), IWN_RBUF_SIZE, iwn_dma_map_addr, 2283220693Sbschmidt &paddr, BUS_DMA_NOWAIT); 2284220693Sbschmidt if (error != 0 && error != EFBIG) { 2285220693Sbschmidt panic("%s: could not load old RX mbuf", __func__); 2286220693Sbschmidt } 2287220693Sbschmidt /* Physical address may have changed. */ 2288220693Sbschmidt ring->desc[ring->cur] = htole32(paddr >> 8); 2289220693Sbschmidt bus_dmamap_sync(ring->data_dmat, ring->desc_dma.map, 2290220693Sbschmidt BUS_DMASYNC_PREWRITE); 2291178676Ssam ifp->if_ierrors++; 2292178676Ssam return; 2293178676Ssam } 2294178676Ssam 2295178676Ssam m = data->m; 2296198429Srpaulo data->m = m1; 2297198429Srpaulo /* Update RX descriptor. */ 2298198429Srpaulo ring->desc[ring->cur] = htole32(paddr >> 8); 2299201209Srpaulo bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, 2300201209Srpaulo BUS_DMASYNC_PREWRITE); 2301198429Srpaulo 2302198429Srpaulo /* Finalize mbuf. */ 2303178676Ssam m->m_pkthdr.rcvif = ifp; 2304178676Ssam m->m_data = head; 2305178676Ssam m->m_pkthdr.len = m->m_len = len; 2306178676Ssam 2307198429Srpaulo /* Grab a reference to the source node. */ 2308178676Ssam wh = mtod(m, struct ieee80211_frame *); 2309178676Ssam ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh); 2310178676Ssam nf = (ni != NULL && ni->ni_vap->iv_state == IEEE80211_S_RUN && 2311178676Ssam (ic->ic_flags & IEEE80211_F_SCAN) == 0) ? sc->noise : -95; 2312178676Ssam 2313220728Sbschmidt rssi = ops->get_rssi(sc, stat); 2314220689Sbschmidt 2315192468Ssam if (ieee80211_radiotap_active(ic)) { 2316178676Ssam struct iwn_rx_radiotap_header *tap = &sc->sc_rxtap; 2317178676Ssam 2318178676Ssam tap->wr_flags = 0; 2319201209Srpaulo if (stat->flags & htole16(IWN_STAT_FLAG_SHPREAMBLE)) 2320192468Ssam tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; 2321220723Sbschmidt tap->wr_dbm_antsignal = (int8_t)rssi; 2322220723Sbschmidt tap->wr_dbm_antnoise = (int8_t)nf; 2323220723Sbschmidt tap->wr_tsft = stat->tstamp; 2324201209Srpaulo switch (stat->rate) { 2325201209Srpaulo /* CCK rates. */ 2326201209Srpaulo case 10: tap->wr_rate = 2; break; 2327201209Srpaulo case 20: tap->wr_rate = 4; break; 2328201209Srpaulo case 55: tap->wr_rate = 11; break; 2329201209Srpaulo case 110: tap->wr_rate = 22; break; 2330201209Srpaulo /* OFDM rates. */ 2331201209Srpaulo case 0xd: tap->wr_rate = 12; break; 2332201209Srpaulo case 0xf: tap->wr_rate = 18; break; 2333201209Srpaulo case 0x5: tap->wr_rate = 24; break; 2334201209Srpaulo case 0x7: tap->wr_rate = 36; break; 2335201209Srpaulo case 0x9: tap->wr_rate = 48; break; 2336201209Srpaulo case 0xb: tap->wr_rate = 72; break; 2337201209Srpaulo case 0x1: tap->wr_rate = 96; break; 2338201209Srpaulo case 0x3: tap->wr_rate = 108; break; 2339201209Srpaulo /* Unknown rate: should not happen. */ 2340201209Srpaulo default: tap->wr_rate = 0; 2341201209Srpaulo } 2342178676Ssam } 2343178676Ssam 2344178676Ssam IWN_UNLOCK(sc); 2345178676Ssam 2346198429Srpaulo /* Send the frame to the 802.11 layer. */ 2347178676Ssam if (ni != NULL) { 2348220726Sbschmidt (void)ieee80211_input(ni, m, rssi - nf, nf); 2349198429Srpaulo /* Node is no longer needed. */ 2350178676Ssam ieee80211_free_node(ni); 2351178676Ssam } else 2352220726Sbschmidt (void)ieee80211_input_all(ic, m, rssi - nf, nf); 2353178676Ssam 2354178676Ssam IWN_LOCK(sc); 2355178676Ssam} 2356178676Ssam 2357201209Srpaulo#if 0 /* HT */ 2358201209Srpaulo/* Process an incoming Compressed BlockAck. */ 2359206477Sbschmidtstatic void 2360201209Srpauloiwn_rx_compressed_ba(struct iwn_softc *sc, struct iwn_rx_desc *desc, 2361201209Srpaulo struct iwn_rx_data *data) 2362201209Srpaulo{ 2363201209Srpaulo struct iwn_compressed_ba *ba = (struct iwn_compressed_ba *)(desc + 1); 2364201209Srpaulo struct iwn_tx_ring *txq; 2365201209Srpaulo 2366220704Sbschmidt bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD); 2367220704Sbschmidt 2368201209Srpaulo txq = &sc->txq[letoh16(ba->qid)]; 2369201209Srpaulo /* XXX TBD */ 2370201209Srpaulo} 2371201209Srpaulo#endif 2372201209Srpaulo 2373198429Srpaulo/* 2374220674Sbschmidt * Process a CALIBRATION_RESULT notification sent by the initialization 2375220674Sbschmidt * firmware on response to a CMD_CALIB_CONFIG command (5000 only). 2376220674Sbschmidt */ 2377220674Sbschmidtstatic void 2378220674Sbschmidtiwn5000_rx_calib_results(struct iwn_softc *sc, struct iwn_rx_desc *desc, 2379220674Sbschmidt struct iwn_rx_data *data) 2380220674Sbschmidt{ 2381220674Sbschmidt struct iwn_phy_calib *calib = (struct iwn_phy_calib *)(desc + 1); 2382220674Sbschmidt int len, idx = -1; 2383220674Sbschmidt 2384220674Sbschmidt /* Runtime firmware should not send such a notification. */ 2385220674Sbschmidt if (sc->sc_flags & IWN_FLAG_CALIB_DONE) 2386220674Sbschmidt return; 2387220674Sbschmidt 2388220674Sbschmidt len = (le32toh(desc->len) & 0x3fff) - 4; 2389220674Sbschmidt bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD); 2390220674Sbschmidt 2391220674Sbschmidt switch (calib->code) { 2392220674Sbschmidt case IWN5000_PHY_CALIB_DC: 2393220867Sbschmidt if ((sc->sc_flags & IWN_FLAG_INTERNAL_PA) == 0 && 2394220867Sbschmidt (sc->hw_type == IWN_HW_REV_TYPE_5150 || 2395220867Sbschmidt sc->hw_type >= IWN_HW_REV_TYPE_6000)) 2396220674Sbschmidt idx = 0; 2397220674Sbschmidt break; 2398220674Sbschmidt case IWN5000_PHY_CALIB_LO: 2399220674Sbschmidt idx = 1; 2400220674Sbschmidt break; 2401220674Sbschmidt case IWN5000_PHY_CALIB_TX_IQ: 2402220674Sbschmidt idx = 2; 2403220674Sbschmidt break; 2404220674Sbschmidt case IWN5000_PHY_CALIB_TX_IQ_PERIODIC: 2405220674Sbschmidt if (sc->hw_type < IWN_HW_REV_TYPE_6000 && 2406220674Sbschmidt sc->hw_type != IWN_HW_REV_TYPE_5150) 2407220674Sbschmidt idx = 3; 2408220674Sbschmidt break; 2409220674Sbschmidt case IWN5000_PHY_CALIB_BASE_BAND: 2410220674Sbschmidt idx = 4; 2411220674Sbschmidt break; 2412220674Sbschmidt } 2413220674Sbschmidt if (idx == -1) /* Ignore other results. */ 2414220674Sbschmidt return; 2415220674Sbschmidt 2416220674Sbschmidt /* Save calibration result. */ 2417220674Sbschmidt if (sc->calibcmd[idx].buf != NULL) 2418220674Sbschmidt free(sc->calibcmd[idx].buf, M_DEVBUF); 2419220674Sbschmidt sc->calibcmd[idx].buf = malloc(len, M_DEVBUF, M_NOWAIT); 2420220674Sbschmidt if (sc->calibcmd[idx].buf == NULL) { 2421220674Sbschmidt DPRINTF(sc, IWN_DEBUG_CALIBRATE, 2422220674Sbschmidt "not enough memory for calibration result %d\n", 2423220674Sbschmidt calib->code); 2424220674Sbschmidt return; 2425220674Sbschmidt } 2426220674Sbschmidt DPRINTF(sc, IWN_DEBUG_CALIBRATE, 2427220674Sbschmidt "saving calibration result code=%d len=%d\n", calib->code, len); 2428220674Sbschmidt sc->calibcmd[idx].len = len; 2429220674Sbschmidt memcpy(sc->calibcmd[idx].buf, calib, len); 2430220674Sbschmidt} 2431220674Sbschmidt 2432220674Sbschmidt/* 2433198429Srpaulo * Process an RX_STATISTICS or BEACON_STATISTICS firmware notification. 2434198429Srpaulo * The latter is sent by the firmware after each received beacon. 2435198429Srpaulo */ 2436206477Sbschmidtstatic void 2437198429Srpauloiwn_rx_statistics(struct iwn_softc *sc, struct iwn_rx_desc *desc, 2438198429Srpaulo struct iwn_rx_data *data) 2439198429Srpaulo{ 2440220728Sbschmidt struct iwn_ops *ops = &sc->ops; 2441178676Ssam struct ifnet *ifp = sc->sc_ifp; 2442178676Ssam struct ieee80211com *ic = ifp->if_l2com; 2443178676Ssam struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 2444178676Ssam struct iwn_calib_state *calib = &sc->calib; 2445178676Ssam struct iwn_stats *stats = (struct iwn_stats *)(desc + 1); 2446198429Srpaulo int temp; 2447178676Ssam 2448220725Sbschmidt /* Ignore statistics received during a scan. */ 2449178676Ssam if (vap->iv_state != IEEE80211_S_RUN || 2450178676Ssam (ic->ic_flags & IEEE80211_F_SCAN)) 2451178676Ssam return; 2452178676Ssam 2453202986Srpaulo bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD); 2454220724Sbschmidt 2455220724Sbschmidt DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: received statistics, cmd %d\n", 2456220724Sbschmidt __func__, desc->type); 2457220667Sbschmidt sc->calib_cnt = 0; /* Reset TX power calibration timeout. */ 2458178676Ssam 2459198429Srpaulo /* Test if temperature has changed. */ 2460178676Ssam if (stats->general.temp != sc->rawtemp) { 2461198429Srpaulo /* Convert "raw" temperature to degC. */ 2462178676Ssam sc->rawtemp = stats->general.temp; 2463220728Sbschmidt temp = ops->get_temperature(sc); 2464178676Ssam DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: temperature %d\n", 2465178676Ssam __func__, temp); 2466178676Ssam 2467220725Sbschmidt /* Update TX power if need be (4965AGN only). */ 2468198429Srpaulo if (sc->hw_type == IWN_HW_REV_TYPE_4965) 2469198429Srpaulo iwn4965_power_calibration(sc, temp); 2470178676Ssam } 2471178676Ssam 2472178676Ssam if (desc->type != IWN_BEACON_STATISTICS) 2473198429Srpaulo return; /* Reply to a statistics request. */ 2474178676Ssam 2475178676Ssam sc->noise = iwn_get_noise(&stats->rx.general); 2476178676Ssam DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: noise %d\n", __func__, sc->noise); 2477178676Ssam 2478198429Srpaulo /* Test that RSSI and noise are present in stats report. */ 2479198429Srpaulo if (le32toh(stats->rx.general.flags) != 1) { 2480178676Ssam DPRINTF(sc, IWN_DEBUG_ANY, "%s\n", 2481178676Ssam "received statistics without RSSI"); 2482178676Ssam return; 2483178676Ssam } 2484178676Ssam 2485178676Ssam if (calib->state == IWN_CALIB_STATE_ASSOC) 2486198429Srpaulo iwn_collect_noise(sc, &stats->rx.general); 2487178676Ssam else if (calib->state == IWN_CALIB_STATE_RUN) 2488178676Ssam iwn_tune_sensitivity(sc, &stats->rx); 2489178676Ssam} 2490178676Ssam 2491198429Srpaulo/* 2492198429Srpaulo * Process a TX_DONE firmware notification. Unfortunately, the 4965AGN 2493198429Srpaulo * and 5000 adapters have different incompatible TX status formats. 2494198429Srpaulo */ 2495206477Sbschmidtstatic void 2496198429Srpauloiwn4965_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, 2497198429Srpaulo struct iwn_rx_data *data) 2498178676Ssam{ 2499198429Srpaulo struct iwn4965_tx_stat *stat = (struct iwn4965_tx_stat *)(desc + 1); 2500207001Sbschmidt struct iwn_tx_ring *ring = &sc->txq[desc->qid & 0xf]; 2501198429Srpaulo 2502198429Srpaulo DPRINTF(sc, IWN_DEBUG_XMIT, "%s: " 2503198429Srpaulo "qid %d idx %d retries %d nkill %d rate %x duration %d status %x\n", 2504201209Srpaulo __func__, desc->qid, desc->idx, stat->ackfailcnt, 2505201209Srpaulo stat->btkillcnt, stat->rate, le16toh(stat->duration), 2506198429Srpaulo le32toh(stat->status)); 2507201209Srpaulo 2508207001Sbschmidt bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD); 2509201209Srpaulo iwn_tx_done(sc, desc, stat->ackfailcnt, le32toh(stat->status) & 0xff); 2510198429Srpaulo} 2511198429Srpaulo 2512206477Sbschmidtstatic void 2513198429Srpauloiwn5000_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, 2514198429Srpaulo struct iwn_rx_data *data) 2515198429Srpaulo{ 2516198429Srpaulo struct iwn5000_tx_stat *stat = (struct iwn5000_tx_stat *)(desc + 1); 2517207001Sbschmidt struct iwn_tx_ring *ring = &sc->txq[desc->qid & 0xf]; 2518198429Srpaulo 2519198429Srpaulo DPRINTF(sc, IWN_DEBUG_XMIT, "%s: " 2520198429Srpaulo "qid %d idx %d retries %d nkill %d rate %x duration %d status %x\n", 2521201209Srpaulo __func__, desc->qid, desc->idx, stat->ackfailcnt, 2522201209Srpaulo stat->btkillcnt, stat->rate, le16toh(stat->duration), 2523198429Srpaulo le32toh(stat->status)); 2524198429Srpaulo 2525201209Srpaulo#ifdef notyet 2526198429Srpaulo /* Reset TX scheduler slot. */ 2527198429Srpaulo iwn5000_reset_sched(sc, desc->qid & 0xf, desc->idx); 2528201209Srpaulo#endif 2529202986Srpaulo 2530207001Sbschmidt bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD); 2531201209Srpaulo iwn_tx_done(sc, desc, stat->ackfailcnt, le16toh(stat->status) & 0xff); 2532198429Srpaulo} 2533198429Srpaulo 2534198429Srpaulo/* 2535198429Srpaulo * Adapter-independent backend for TX_DONE firmware notifications. 2536198429Srpaulo */ 2537206477Sbschmidtstatic void 2538201209Srpauloiwn_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, int ackfailcnt, 2539198429Srpaulo uint8_t status) 2540198429Srpaulo{ 2541178676Ssam struct ifnet *ifp = sc->sc_ifp; 2542178676Ssam struct iwn_tx_ring *ring = &sc->txq[desc->qid & 0xf]; 2543178676Ssam struct iwn_tx_data *data = &ring->data[desc->idx]; 2544178676Ssam struct mbuf *m; 2545178676Ssam struct ieee80211_node *ni; 2546206358Srpaulo struct ieee80211vap *vap; 2547178676Ssam 2548178676Ssam KASSERT(data->ni != NULL, ("no node")); 2549178676Ssam 2550198429Srpaulo /* Unmap and free mbuf. */ 2551201209Srpaulo bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTWRITE); 2552201209Srpaulo bus_dmamap_unload(ring->data_dmat, data->map); 2553178676Ssam m = data->m, data->m = NULL; 2554178676Ssam ni = data->ni, data->ni = NULL; 2555206358Srpaulo vap = ni->ni_vap; 2556178676Ssam 2557178676Ssam if (m->m_flags & M_TXCB) { 2558178676Ssam /* 2559178676Ssam * Channels marked for "radar" require traffic to be received 2560178676Ssam * to unlock before we can transmit. Until traffic is seen 2561178676Ssam * any attempt to transmit is returned immediately with status 2562178676Ssam * set to IWN_TX_FAIL_TX_LOCKED. Unfortunately this can easily 2563178676Ssam * happen on first authenticate after scanning. To workaround 2564178676Ssam * this we ignore a failure of this sort in AUTH state so the 2565178676Ssam * 802.11 layer will fall back to using a timeout to wait for 2566178676Ssam * the AUTH reply. This allows the firmware time to see 2567178676Ssam * traffic so a subsequent retry of AUTH succeeds. It's 2568178676Ssam * unclear why the firmware does not maintain state for 2569178676Ssam * channels recently visited as this would allow immediate 2570178676Ssam * use of the channel after a scan (where we see traffic). 2571178676Ssam */ 2572178676Ssam if (status == IWN_TX_FAIL_TX_LOCKED && 2573178676Ssam ni->ni_vap->iv_state == IEEE80211_S_AUTH) 2574178676Ssam ieee80211_process_callback(ni, m, 0); 2575178676Ssam else 2576178676Ssam ieee80211_process_callback(ni, m, 2577178676Ssam (status & IWN_TX_FAIL) != 0); 2578178676Ssam } 2579201209Srpaulo 2580201209Srpaulo /* 2581201209Srpaulo * Update rate control statistics for the node. 2582201209Srpaulo */ 2583220719Sbschmidt if (status & IWN_TX_FAIL) { 2584201209Srpaulo ifp->if_oerrors++; 2585206358Srpaulo ieee80211_ratectl_tx_complete(vap, ni, 2586206358Srpaulo IEEE80211_RATECTL_TX_FAILURE, &ackfailcnt, NULL); 2587201209Srpaulo } else { 2588220719Sbschmidt ifp->if_opackets++; 2589206358Srpaulo ieee80211_ratectl_tx_complete(vap, ni, 2590206358Srpaulo IEEE80211_RATECTL_TX_SUCCESS, &ackfailcnt, NULL); 2591201209Srpaulo } 2592178676Ssam m_freem(m); 2593178676Ssam ieee80211_free_node(ni); 2594178676Ssam 2595178676Ssam sc->sc_tx_timer = 0; 2596198429Srpaulo if (--ring->queued < IWN_TX_RING_LOMARK) { 2597198429Srpaulo sc->qfullmsk &= ~(1 << ring->qid); 2598198429Srpaulo if (sc->qfullmsk == 0 && 2599198429Srpaulo (ifp->if_drv_flags & IFF_DRV_OACTIVE)) { 2600198429Srpaulo ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 2601198429Srpaulo iwn_start_locked(ifp); 2602198429Srpaulo } 2603198429Srpaulo } 2604178676Ssam} 2605178676Ssam 2606198429Srpaulo/* 2607198429Srpaulo * Process a "command done" firmware notification. This is where we wakeup 2608198429Srpaulo * processes waiting for a synchronous command completion. 2609198429Srpaulo */ 2610206477Sbschmidtstatic void 2611198429Srpauloiwn_cmd_done(struct iwn_softc *sc, struct iwn_rx_desc *desc) 2612178676Ssam{ 2613178676Ssam struct iwn_tx_ring *ring = &sc->txq[4]; 2614178676Ssam struct iwn_tx_data *data; 2615178676Ssam 2616178676Ssam if ((desc->qid & 0xf) != 4) 2617198429Srpaulo return; /* Not a command ack. */ 2618178676Ssam 2619178676Ssam data = &ring->data[desc->idx]; 2620178676Ssam 2621198429Srpaulo /* If the command was mapped in an mbuf, free it. */ 2622178676Ssam if (data->m != NULL) { 2623220704Sbschmidt bus_dmamap_sync(ring->data_dmat, data->map, 2624220704Sbschmidt BUS_DMASYNC_POSTWRITE); 2625201209Srpaulo bus_dmamap_unload(ring->data_dmat, data->map); 2626178676Ssam m_freem(data->m); 2627178676Ssam data->m = NULL; 2628178676Ssam } 2629198439Srpaulo wakeup(&ring->desc[desc->idx]); 2630178676Ssam} 2631178676Ssam 2632198429Srpaulo/* 2633198429Srpaulo * Process an INT_FH_RX or INT_SW_RX interrupt. 2634198429Srpaulo */ 2635206477Sbschmidtstatic void 2636178676Ssamiwn_notif_intr(struct iwn_softc *sc) 2637178676Ssam{ 2638220728Sbschmidt struct iwn_ops *ops = &sc->ops; 2639178676Ssam struct ifnet *ifp = sc->sc_ifp; 2640178676Ssam struct ieee80211com *ic = ifp->if_l2com; 2641178676Ssam struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 2642178676Ssam uint16_t hw; 2643178676Ssam 2644198429Srpaulo bus_dmamap_sync(sc->rxq.stat_dma.tag, sc->rxq.stat_dma.map, 2645198429Srpaulo BUS_DMASYNC_POSTREAD); 2646198429Srpaulo 2647198429Srpaulo hw = le16toh(sc->rxq.stat->closed_count) & 0xfff; 2648178676Ssam while (sc->rxq.cur != hw) { 2649178676Ssam struct iwn_rx_data *data = &sc->rxq.data[sc->rxq.cur]; 2650198429Srpaulo struct iwn_rx_desc *desc; 2651178676Ssam 2652201209Srpaulo bus_dmamap_sync(sc->rxq.data_dmat, data->map, 2653201209Srpaulo BUS_DMASYNC_POSTREAD); 2654198429Srpaulo desc = mtod(data->m, struct iwn_rx_desc *); 2655198429Srpaulo 2656178676Ssam DPRINTF(sc, IWN_DEBUG_RECV, 2657178676Ssam "%s: qid %x idx %d flags %x type %d(%s) len %d\n", 2658198439Srpaulo __func__, desc->qid & 0xf, desc->idx, desc->flags, 2659178676Ssam desc->type, iwn_intr_str(desc->type), 2660178676Ssam le16toh(desc->len)); 2661178676Ssam 2662198429Srpaulo if (!(desc->qid & 0x80)) /* Reply to a command. */ 2663198429Srpaulo iwn_cmd_done(sc, desc); 2664178676Ssam 2665178676Ssam switch (desc->type) { 2666198429Srpaulo case IWN_RX_PHY: 2667198429Srpaulo iwn_rx_phy(sc, desc, data); 2668178676Ssam break; 2669178676Ssam 2670198429Srpaulo case IWN_RX_DONE: /* 4965AGN only. */ 2671198429Srpaulo case IWN_MPDU_RX_DONE: 2672198429Srpaulo /* An 802.11 frame has been received. */ 2673198429Srpaulo iwn_rx_done(sc, desc, data); 2674178676Ssam break; 2675178676Ssam 2676201209Srpaulo#if 0 /* HT */ 2677201209Srpaulo case IWN_RX_COMPRESSED_BA: 2678201209Srpaulo /* A Compressed BlockAck has been received. */ 2679201209Srpaulo iwn_rx_compressed_ba(sc, desc, data); 2680201209Srpaulo break; 2681201209Srpaulo#endif 2682201209Srpaulo 2683178676Ssam case IWN_TX_DONE: 2684198429Srpaulo /* An 802.11 frame has been transmitted. */ 2685220728Sbschmidt ops->tx_done(sc, desc, data); 2686178676Ssam break; 2687178676Ssam 2688178676Ssam case IWN_RX_STATISTICS: 2689178676Ssam case IWN_BEACON_STATISTICS: 2690198429Srpaulo iwn_rx_statistics(sc, desc, data); 2691178676Ssam break; 2692178676Ssam 2693198429Srpaulo case IWN_BEACON_MISSED: 2694198429Srpaulo { 2695178676Ssam struct iwn_beacon_missed *miss = 2696178676Ssam (struct iwn_beacon_missed *)(desc + 1); 2697202986Srpaulo int misses; 2698178676Ssam 2699201209Srpaulo bus_dmamap_sync(sc->rxq.data_dmat, data->map, 2700201209Srpaulo BUS_DMASYNC_POSTREAD); 2701202986Srpaulo misses = le32toh(miss->consecutive); 2702201209Srpaulo 2703178676Ssam DPRINTF(sc, IWN_DEBUG_STATE, 2704178676Ssam "%s: beacons missed %d/%d\n", __func__, 2705178676Ssam misses, le32toh(miss->total)); 2706178676Ssam /* 2707178676Ssam * If more than 5 consecutive beacons are missed, 2708178676Ssam * reinitialize the sensitivity state machine. 2709178676Ssam */ 2710220660Sbschmidt if (vap->iv_state == IEEE80211_S_RUN && 2711220660Sbschmidt (ic->ic_flags & IEEE80211_F_SCAN) != 0) { 2712220660Sbschmidt if (misses > 5) 2713220660Sbschmidt (void)iwn_init_sensitivity(sc); 2714220660Sbschmidt if (misses >= vap->iv_bmissthreshold) { 2715220660Sbschmidt IWN_UNLOCK(sc); 2716220660Sbschmidt ieee80211_beacon_miss(ic); 2717220660Sbschmidt IWN_LOCK(sc); 2718220660Sbschmidt } 2719201209Srpaulo } 2720178676Ssam break; 2721178676Ssam } 2722198429Srpaulo case IWN_UC_READY: 2723198429Srpaulo { 2724178676Ssam struct iwn_ucode_info *uc = 2725178676Ssam (struct iwn_ucode_info *)(desc + 1); 2726178676Ssam 2727198429Srpaulo /* The microcontroller is ready. */ 2728201209Srpaulo bus_dmamap_sync(sc->rxq.data_dmat, data->map, 2729201209Srpaulo BUS_DMASYNC_POSTREAD); 2730178676Ssam DPRINTF(sc, IWN_DEBUG_RESET, 2731178676Ssam "microcode alive notification version=%d.%d " 2732178676Ssam "subtype=%x alive=%x\n", uc->major, uc->minor, 2733178676Ssam uc->subtype, le32toh(uc->valid)); 2734178676Ssam 2735178676Ssam if (le32toh(uc->valid) != 1) { 2736178676Ssam device_printf(sc->sc_dev, 2737198429Srpaulo "microcontroller initialization failed"); 2738178676Ssam break; 2739178676Ssam } 2740178676Ssam if (uc->subtype == IWN_UCODE_INIT) { 2741201209Srpaulo /* Save microcontroller report. */ 2742178676Ssam memcpy(&sc->ucode_info, uc, sizeof (*uc)); 2743178676Ssam } 2744198429Srpaulo /* Save the address of the error log in SRAM. */ 2745198429Srpaulo sc->errptr = le32toh(uc->errptr); 2746178676Ssam break; 2747178676Ssam } 2748198429Srpaulo case IWN_STATE_CHANGED: 2749198429Srpaulo { 2750178676Ssam uint32_t *status = (uint32_t *)(desc + 1); 2751178676Ssam 2752178676Ssam /* 2753178676Ssam * State change allows hardware switch change to be 2754178676Ssam * noted. However, we handle this in iwn_intr as we 2755178676Ssam * get both the enable/disble intr. 2756178676Ssam */ 2757201209Srpaulo bus_dmamap_sync(sc->rxq.data_dmat, data->map, 2758201209Srpaulo BUS_DMASYNC_POSTREAD); 2759178676Ssam DPRINTF(sc, IWN_DEBUG_INTR, "state changed to %x\n", 2760178676Ssam le32toh(*status)); 2761178676Ssam break; 2762178676Ssam } 2763198429Srpaulo case IWN_START_SCAN: 2764198429Srpaulo { 2765178676Ssam struct iwn_start_scan *scan = 2766178676Ssam (struct iwn_start_scan *)(desc + 1); 2767178676Ssam 2768201209Srpaulo bus_dmamap_sync(sc->rxq.data_dmat, data->map, 2769201209Srpaulo BUS_DMASYNC_POSTREAD); 2770178676Ssam DPRINTF(sc, IWN_DEBUG_ANY, 2771178676Ssam "%s: scanning channel %d status %x\n", 2772178676Ssam __func__, scan->chan, le32toh(scan->status)); 2773178676Ssam break; 2774178676Ssam } 2775198429Srpaulo case IWN_STOP_SCAN: 2776198429Srpaulo { 2777178676Ssam struct iwn_stop_scan *scan = 2778178676Ssam (struct iwn_stop_scan *)(desc + 1); 2779178676Ssam 2780201209Srpaulo bus_dmamap_sync(sc->rxq.data_dmat, data->map, 2781201209Srpaulo BUS_DMASYNC_POSTREAD); 2782178676Ssam DPRINTF(sc, IWN_DEBUG_STATE, 2783178676Ssam "scan finished nchan=%d status=%d chan=%d\n", 2784178676Ssam scan->nchan, scan->status, scan->chan); 2785178676Ssam 2786201209Srpaulo IWN_UNLOCK(sc); 2787191746Sthompsa ieee80211_scan_next(vap); 2788201209Srpaulo IWN_LOCK(sc); 2789178676Ssam break; 2790178676Ssam } 2791198429Srpaulo case IWN5000_CALIBRATION_RESULT: 2792220674Sbschmidt iwn5000_rx_calib_results(sc, desc, data); 2793198429Srpaulo break; 2794198429Srpaulo 2795198429Srpaulo case IWN5000_CALIBRATION_DONE: 2796201209Srpaulo sc->sc_flags |= IWN_FLAG_CALIB_DONE; 2797198429Srpaulo wakeup(sc); 2798198429Srpaulo break; 2799178676Ssam } 2800198429Srpaulo 2801178676Ssam sc->rxq.cur = (sc->rxq.cur + 1) % IWN_RX_RING_COUNT; 2802178676Ssam } 2803178676Ssam 2804198429Srpaulo /* Tell the firmware what we have processed. */ 2805178676Ssam hw = (hw == 0) ? IWN_RX_RING_COUNT - 1 : hw - 1; 2806198429Srpaulo IWN_WRITE(sc, IWN_FH_RX_WPTR, hw & ~7); 2807178676Ssam} 2808178676Ssam 2809198429Srpaulo/* 2810198429Srpaulo * Process an INT_WAKEUP interrupt raised when the microcontroller wakes up 2811198429Srpaulo * from power-down sleep mode. 2812198429Srpaulo */ 2813206477Sbschmidtstatic void 2814198429Srpauloiwn_wakeup_intr(struct iwn_softc *sc) 2815198429Srpaulo{ 2816198429Srpaulo int qid; 2817198429Srpaulo 2818198429Srpaulo DPRINTF(sc, IWN_DEBUG_RESET, "%s: ucode wakeup from power-down sleep\n", 2819198429Srpaulo __func__); 2820198429Srpaulo 2821198429Srpaulo /* Wakeup RX and TX rings. */ 2822198429Srpaulo IWN_WRITE(sc, IWN_FH_RX_WPTR, sc->rxq.cur & ~7); 2823220728Sbschmidt for (qid = 0; qid < sc->ntxqs; qid++) { 2824198429Srpaulo struct iwn_tx_ring *ring = &sc->txq[qid]; 2825198429Srpaulo IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | ring->cur); 2826198429Srpaulo } 2827198429Srpaulo} 2828198429Srpaulo 2829206477Sbschmidtstatic void 2830191746Sthompsaiwn_rftoggle_intr(struct iwn_softc *sc) 2831191746Sthompsa{ 2832191746Sthompsa struct ifnet *ifp = sc->sc_ifp; 2833191746Sthompsa struct ieee80211com *ic = ifp->if_l2com; 2834198429Srpaulo uint32_t tmp = IWN_READ(sc, IWN_GP_CNTRL); 2835191746Sthompsa 2836191746Sthompsa IWN_LOCK_ASSERT(sc); 2837191746Sthompsa 2838191746Sthompsa device_printf(sc->sc_dev, "RF switch: radio %s\n", 2839198429Srpaulo (tmp & IWN_GP_CNTRL_RFKILL) ? "enabled" : "disabled"); 2840198429Srpaulo if (tmp & IWN_GP_CNTRL_RFKILL) 2841191746Sthompsa ieee80211_runtask(ic, &sc->sc_radioon_task); 2842191746Sthompsa else 2843191746Sthompsa ieee80211_runtask(ic, &sc->sc_radiooff_task); 2844191746Sthompsa} 2845191746Sthompsa 2846198429Srpaulo/* 2847198429Srpaulo * Dump the error log of the firmware when a firmware panic occurs. Although 2848198429Srpaulo * we can't debug the firmware because it is neither open source nor free, it 2849198429Srpaulo * can help us to identify certain classes of problems. 2850198429Srpaulo */ 2851206477Sbschmidtstatic void 2852201209Srpauloiwn_fatal_intr(struct iwn_softc *sc) 2853191746Sthompsa{ 2854198429Srpaulo struct iwn_fw_dump dump; 2855198429Srpaulo int i; 2856191746Sthompsa 2857191746Sthompsa IWN_LOCK_ASSERT(sc); 2858191746Sthompsa 2859201209Srpaulo /* Force a complete recalibration on next init. */ 2860201209Srpaulo sc->sc_flags &= ~IWN_FLAG_CALIB_DONE; 2861201209Srpaulo 2862198429Srpaulo /* Check that the error log address is valid. */ 2863198429Srpaulo if (sc->errptr < IWN_FW_DATA_BASE || 2864198429Srpaulo sc->errptr + sizeof (dump) > 2865220728Sbschmidt IWN_FW_DATA_BASE + sc->fw_data_maxsz) { 2866220726Sbschmidt printf("%s: bad firmware error log address 0x%08x\n", __func__, 2867220726Sbschmidt sc->errptr); 2868198429Srpaulo return; 2869198429Srpaulo } 2870198429Srpaulo if (iwn_nic_lock(sc) != 0) { 2871220726Sbschmidt printf("%s: could not read firmware error log\n", __func__); 2872198429Srpaulo return; 2873198429Srpaulo } 2874198429Srpaulo /* Read firmware error log from SRAM. */ 2875198429Srpaulo iwn_mem_read_region_4(sc, sc->errptr, (uint32_t *)&dump, 2876198429Srpaulo sizeof (dump) / sizeof (uint32_t)); 2877198429Srpaulo iwn_nic_unlock(sc); 2878198429Srpaulo 2879198429Srpaulo if (dump.valid == 0) { 2880220726Sbschmidt printf("%s: firmware error log is empty\n", __func__); 2881198429Srpaulo return; 2882198429Srpaulo } 2883198429Srpaulo printf("firmware error log:\n"); 2884198429Srpaulo printf(" error type = \"%s\" (0x%08X)\n", 2885198429Srpaulo (dump.id < nitems(iwn_fw_errmsg)) ? 2886198429Srpaulo iwn_fw_errmsg[dump.id] : "UNKNOWN", 2887198429Srpaulo dump.id); 2888198429Srpaulo printf(" program counter = 0x%08X\n", dump.pc); 2889198429Srpaulo printf(" source line = 0x%08X\n", dump.src_line); 2890198429Srpaulo printf(" error data = 0x%08X%08X\n", 2891198429Srpaulo dump.error_data[0], dump.error_data[1]); 2892198429Srpaulo printf(" branch link = 0x%08X%08X\n", 2893198429Srpaulo dump.branch_link[0], dump.branch_link[1]); 2894198429Srpaulo printf(" interrupt link = 0x%08X%08X\n", 2895198429Srpaulo dump.interrupt_link[0], dump.interrupt_link[1]); 2896198429Srpaulo printf(" time = %u\n", dump.time[0]); 2897198429Srpaulo 2898198429Srpaulo /* Dump driver status (TX and RX rings) while we're here. */ 2899198429Srpaulo printf("driver status:\n"); 2900220728Sbschmidt for (i = 0; i < sc->ntxqs; i++) { 2901198429Srpaulo struct iwn_tx_ring *ring = &sc->txq[i]; 2902198429Srpaulo printf(" tx ring %2d: qid=%-2d cur=%-3d queued=%-3d\n", 2903198429Srpaulo i, ring->qid, ring->cur, ring->queued); 2904198429Srpaulo } 2905198429Srpaulo printf(" rx ring: cur=%d\n", sc->rxq.cur); 2906191746Sthompsa} 2907191746Sthompsa 2908206477Sbschmidtstatic void 2909178676Ssamiwn_intr(void *arg) 2910178676Ssam{ 2911178676Ssam struct iwn_softc *sc = arg; 2912198429Srpaulo struct ifnet *ifp = sc->sc_ifp; 2913201209Srpaulo uint32_t r1, r2, tmp; 2914178676Ssam 2915178676Ssam IWN_LOCK(sc); 2916178676Ssam 2917198429Srpaulo /* Disable interrupts. */ 2918201209Srpaulo IWN_WRITE(sc, IWN_INT_MASK, 0); 2919178676Ssam 2920201209Srpaulo /* Read interrupts from ICT (fast) or from registers (slow). */ 2921201209Srpaulo if (sc->sc_flags & IWN_FLAG_USE_ICT) { 2922201209Srpaulo tmp = 0; 2923201209Srpaulo while (sc->ict[sc->ict_cur] != 0) { 2924201209Srpaulo tmp |= sc->ict[sc->ict_cur]; 2925201209Srpaulo sc->ict[sc->ict_cur] = 0; /* Acknowledge. */ 2926201209Srpaulo sc->ict_cur = (sc->ict_cur + 1) % IWN_ICT_COUNT; 2927201209Srpaulo } 2928201209Srpaulo tmp = le32toh(tmp); 2929206444Sbschmidt if (tmp == 0xffffffff) /* Shouldn't happen. */ 2930206444Sbschmidt tmp = 0; 2931206444Sbschmidt else if (tmp & 0xc0000) /* Workaround a HW bug. */ 2932206444Sbschmidt tmp |= 0x8000; 2933201209Srpaulo r1 = (tmp & 0xff00) << 16 | (tmp & 0xff); 2934201209Srpaulo r2 = 0; /* Unused. */ 2935201209Srpaulo } else { 2936201209Srpaulo r1 = IWN_READ(sc, IWN_INT); 2937201209Srpaulo if (r1 == 0xffffffff || (r1 & 0xfffffff0) == 0xa5a5a5a0) 2938201209Srpaulo return; /* Hardware gone! */ 2939201209Srpaulo r2 = IWN_READ(sc, IWN_FH_INT); 2940201209Srpaulo } 2941178676Ssam 2942198429Srpaulo DPRINTF(sc, IWN_DEBUG_INTR, "interrupt reg1=%x reg2=%x\n", r1, r2); 2943198429Srpaulo 2944201209Srpaulo if (r1 == 0 && r2 == 0) 2945198429Srpaulo goto done; /* Interrupt not for us. */ 2946178676Ssam 2947198429Srpaulo /* Acknowledge interrupts. */ 2948198429Srpaulo IWN_WRITE(sc, IWN_INT, r1); 2949201209Srpaulo if (!(sc->sc_flags & IWN_FLAG_USE_ICT)) 2950201209Srpaulo IWN_WRITE(sc, IWN_FH_INT, r2); 2951178676Ssam 2952198429Srpaulo if (r1 & IWN_INT_RF_TOGGLED) { 2953191746Sthompsa iwn_rftoggle_intr(sc); 2954201209Srpaulo goto done; 2955198429Srpaulo } 2956198429Srpaulo if (r1 & IWN_INT_CT_REACHED) { 2957198429Srpaulo device_printf(sc->sc_dev, "%s: critical temperature reached!\n", 2958198429Srpaulo __func__); 2959198429Srpaulo } 2960198429Srpaulo if (r1 & (IWN_INT_SW_ERR | IWN_INT_HW_ERR)) { 2961220724Sbschmidt device_printf(sc->sc_dev, "%s: fatal firmware error\n", 2962220724Sbschmidt __func__); 2963220725Sbschmidt /* Dump firmware error log and stop. */ 2964201209Srpaulo iwn_fatal_intr(sc); 2965201209Srpaulo ifp->if_flags &= ~IFF_UP; 2966201209Srpaulo iwn_stop_locked(sc); 2967178676Ssam goto done; 2968178676Ssam } 2969201209Srpaulo if ((r1 & (IWN_INT_FH_RX | IWN_INT_SW_RX | IWN_INT_RX_PERIODIC)) || 2970201209Srpaulo (r2 & IWN_FH_INT_RX)) { 2971201209Srpaulo if (sc->sc_flags & IWN_FLAG_USE_ICT) { 2972201209Srpaulo if (r1 & (IWN_INT_FH_RX | IWN_INT_SW_RX)) 2973201209Srpaulo IWN_WRITE(sc, IWN_FH_INT, IWN_FH_INT_RX); 2974201209Srpaulo IWN_WRITE_1(sc, IWN_INT_PERIODIC, 2975201209Srpaulo IWN_INT_PERIODIC_DIS); 2976201209Srpaulo iwn_notif_intr(sc); 2977201209Srpaulo if (r1 & (IWN_INT_FH_RX | IWN_INT_SW_RX)) { 2978201209Srpaulo IWN_WRITE_1(sc, IWN_INT_PERIODIC, 2979201209Srpaulo IWN_INT_PERIODIC_ENA); 2980201209Srpaulo } 2981201209Srpaulo } else 2982201209Srpaulo iwn_notif_intr(sc); 2983201209Srpaulo } 2984178676Ssam 2985201209Srpaulo if ((r1 & IWN_INT_FH_TX) || (r2 & IWN_FH_INT_TX)) { 2986201209Srpaulo if (sc->sc_flags & IWN_FLAG_USE_ICT) 2987201209Srpaulo IWN_WRITE(sc, IWN_FH_INT, IWN_FH_INT_TX); 2988198429Srpaulo wakeup(sc); /* FH DMA transfer completed. */ 2989201209Srpaulo } 2990198429Srpaulo 2991198429Srpaulo if (r1 & IWN_INT_ALIVE) 2992198429Srpaulo wakeup(sc); /* Firmware is alive. */ 2993198429Srpaulo 2994198429Srpaulo if (r1 & IWN_INT_WAKEUP) 2995198429Srpaulo iwn_wakeup_intr(sc); 2996198429Srpaulo 2997201209Srpaulodone: 2998198429Srpaulo /* Re-enable interrupts. */ 2999201209Srpaulo if (ifp->if_flags & IFF_UP) 3000201209Srpaulo IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask); 3001198429Srpaulo 3002178676Ssam IWN_UNLOCK(sc); 3003178676Ssam} 3004178676Ssam 3005198429Srpaulo/* 3006198429Srpaulo * Update TX scheduler ring when transmitting an 802.11 frame (4965AGN and 3007220725Sbschmidt * 5000 adapters use a slightly different format). 3008198429Srpaulo */ 3009206477Sbschmidtstatic void 3010198429Srpauloiwn4965_update_sched(struct iwn_softc *sc, int qid, int idx, uint8_t id, 3011198429Srpaulo uint16_t len) 3012178676Ssam{ 3013198429Srpaulo uint16_t *w = &sc->sched[qid * IWN4965_SCHED_COUNT + idx]; 3014178676Ssam 3015198429Srpaulo *w = htole16(len + 8); 3016198439Srpaulo bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map, 3017198439Srpaulo BUS_DMASYNC_PREWRITE); 3018201209Srpaulo if (idx < IWN_SCHED_WINSZ) { 3019198429Srpaulo *(w + IWN_TX_RING_COUNT) = *w; 3020198439Srpaulo bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map, 3021198439Srpaulo BUS_DMASYNC_PREWRITE); 3022198439Srpaulo } 3023198429Srpaulo} 3024198429Srpaulo 3025206477Sbschmidtstatic void 3026198429Srpauloiwn5000_update_sched(struct iwn_softc *sc, int qid, int idx, uint8_t id, 3027198429Srpaulo uint16_t len) 3028198429Srpaulo{ 3029198429Srpaulo uint16_t *w = &sc->sched[qid * IWN5000_SCHED_COUNT + idx]; 3030198429Srpaulo 3031198429Srpaulo *w = htole16(id << 12 | (len + 8)); 3032198439Srpaulo bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map, 3033198439Srpaulo BUS_DMASYNC_PREWRITE); 3034198439Srpaulo if (idx < IWN_SCHED_WINSZ) { 3035198429Srpaulo *(w + IWN_TX_RING_COUNT) = *w; 3036198439Srpaulo bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map, 3037198439Srpaulo BUS_DMASYNC_PREWRITE); 3038198439Srpaulo } 3039198429Srpaulo} 3040198429Srpaulo 3041206475Sbschmidt#ifdef notyet 3042206477Sbschmidtstatic void 3043198429Srpauloiwn5000_reset_sched(struct iwn_softc *sc, int qid, int idx) 3044198429Srpaulo{ 3045198429Srpaulo uint16_t *w = &sc->sched[qid * IWN5000_SCHED_COUNT + idx]; 3046198429Srpaulo 3047198429Srpaulo *w = (*w & htole16(0xf000)) | htole16(1); 3048198439Srpaulo bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map, 3049198439Srpaulo BUS_DMASYNC_PREWRITE); 3050198439Srpaulo if (idx < IWN_SCHED_WINSZ) { 3051198429Srpaulo *(w + IWN_TX_RING_COUNT) = *w; 3052198439Srpaulo bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map, 3053206443Sbschmidt BUS_DMASYNC_PREWRITE); 3054198439Srpaulo } 3055198429Srpaulo} 3056206475Sbschmidt#endif 3057198429Srpaulo 3058201209Srpaulostatic uint8_t 3059198429Srpauloiwn_plcp_signal(int rate) { 3060198429Srpaulo int i; 3061198429Srpaulo 3062198429Srpaulo for (i = 0; i < IWN_RIDX_MAX + 1; i++) { 3063220715Sbschmidt if ((rate & IEEE80211_RATE_VAL) == iwn_rates[i].rate) 3064201209Srpaulo return i; 3065178676Ssam } 3066198429Srpaulo 3067201209Srpaulo return 0; 3068178676Ssam} 3069178676Ssam 3070206477Sbschmidtstatic int 3071220720Sbschmidtiwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni) 3072178676Ssam{ 3073198429Srpaulo const struct ieee80211_txparam *tp; 3074178676Ssam struct ieee80211vap *vap = ni->ni_vap; 3075178676Ssam struct ieee80211com *ic = ni->ni_ic; 3076198429Srpaulo struct iwn_node *wn = (void *)ni; 3077220720Sbschmidt struct iwn_tx_ring *ring; 3078178676Ssam struct iwn_tx_desc *desc; 3079178676Ssam struct iwn_tx_data *data; 3080178676Ssam struct iwn_tx_cmd *cmd; 3081178676Ssam struct iwn_cmd_data *tx; 3082220723Sbschmidt const struct iwn_rate *rinfo; 3083178676Ssam struct ieee80211_frame *wh; 3084198429Srpaulo struct ieee80211_key *k = NULL; 3085220700Sbschmidt struct mbuf *m1; 3086178676Ssam uint32_t flags; 3087220720Sbschmidt uint16_t qos; 3088178676Ssam u_int hdrlen; 3089220723Sbschmidt bus_dma_segment_t *seg, segs[IWN_MAX_SCATTER]; 3090220723Sbschmidt uint8_t tid, ridx, txant, type; 3091220720Sbschmidt int ac, i, totlen, error, pad, nsegs = 0, rate; 3092178676Ssam 3093178676Ssam IWN_LOCK_ASSERT(sc); 3094178676Ssam 3095198429Srpaulo wh = mtod(m, struct ieee80211_frame *); 3096198429Srpaulo hdrlen = ieee80211_anyhdrsize(wh); 3097178676Ssam type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; 3098178676Ssam 3099220720Sbschmidt /* Select EDCA Access Category and TX ring for this frame. */ 3100220720Sbschmidt if (IEEE80211_QOS_HAS_SEQ(wh)) { 3101220720Sbschmidt qos = ((const struct ieee80211_qosframe *)wh)->i_qos[0]; 3102220720Sbschmidt tid = qos & IEEE80211_QOS_TID; 3103220720Sbschmidt } else { 3104220720Sbschmidt qos = 0; 3105220720Sbschmidt tid = 0; 3106220720Sbschmidt } 3107220720Sbschmidt ac = M_WME_GETAC(m); 3108220720Sbschmidt 3109220720Sbschmidt ring = &sc->txq[ac]; 3110198429Srpaulo desc = &ring->desc[ring->cur]; 3111198429Srpaulo data = &ring->data[ring->cur]; 3112198429Srpaulo 3113198429Srpaulo /* Choose a TX rate index. */ 3114201209Srpaulo tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)]; 3115178676Ssam if (type == IEEE80211_FC0_TYPE_MGT) 3116178676Ssam rate = tp->mgmtrate; 3117198429Srpaulo else if (IEEE80211_IS_MULTICAST(wh->i_addr1)) 3118178676Ssam rate = tp->mcastrate; 3119178676Ssam else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) 3120178676Ssam rate = tp->ucastrate; 3121178676Ssam else { 3122206358Srpaulo /* XXX pass pktlen */ 3123206358Srpaulo (void) ieee80211_ratectl_rate(ni, NULL, 0); 3124178676Ssam rate = ni->ni_txrate; 3125178676Ssam } 3126201209Srpaulo ridx = iwn_plcp_signal(rate); 3127201209Srpaulo rinfo = &iwn_rates[ridx]; 3128178676Ssam 3129198429Srpaulo /* Encrypt the frame if need be. */ 3130178676Ssam if (wh->i_fc[1] & IEEE80211_FC1_WEP) { 3131220725Sbschmidt /* Retrieve key for TX. */ 3132198429Srpaulo k = ieee80211_crypto_encap(ni, m); 3133178676Ssam if (k == NULL) { 3134198429Srpaulo m_freem(m); 3135178676Ssam return ENOBUFS; 3136178676Ssam } 3137220725Sbschmidt /* 802.11 header may have moved. */ 3138198429Srpaulo wh = mtod(m, struct ieee80211_frame *); 3139198429Srpaulo } 3140198429Srpaulo totlen = m->m_pkthdr.len; 3141178676Ssam 3142192468Ssam if (ieee80211_radiotap_active_vap(vap)) { 3143178676Ssam struct iwn_tx_radiotap_header *tap = &sc->sc_txtap; 3144178676Ssam 3145178676Ssam tap->wt_flags = 0; 3146201209Srpaulo tap->wt_rate = rinfo->rate; 3147178676Ssam if (k != NULL) 3148178676Ssam tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP; 3149178676Ssam 3150198429Srpaulo ieee80211_radiotap_tx(vap, m); 3151178676Ssam } 3152178676Ssam 3153198429Srpaulo /* Prepare TX firmware command. */ 3154198429Srpaulo cmd = &ring->cmd[ring->cur]; 3155198429Srpaulo cmd->code = IWN_CMD_TX_DATA; 3156198429Srpaulo cmd->flags = 0; 3157198429Srpaulo cmd->qid = ring->qid; 3158198429Srpaulo cmd->idx = ring->cur; 3159198429Srpaulo 3160198429Srpaulo tx = (struct iwn_cmd_data *)cmd->data; 3161198429Srpaulo /* NB: No need to clear tx, all fields are reinitialized here. */ 3162198429Srpaulo tx->scratch = 0; /* clear "scratch" area */ 3163198429Srpaulo 3164198429Srpaulo flags = 0; 3165220720Sbschmidt if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { 3166220720Sbschmidt /* Unicast frame, check if an ACK is expected. */ 3167220720Sbschmidt if (!qos || (qos & IEEE80211_QOS_ACKPOLICY) != 3168220720Sbschmidt IEEE80211_QOS_ACKPOLICY_NOACK) 3169220720Sbschmidt flags |= IWN_TX_NEED_ACK; 3170220720Sbschmidt } 3171198429Srpaulo if ((wh->i_fc[0] & 3172198429Srpaulo (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) == 3173198429Srpaulo (IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_BAR)) 3174198429Srpaulo flags |= IWN_TX_IMM_BA; /* Cannot happen yet. */ 3175178676Ssam 3176198429Srpaulo if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG) 3177198429Srpaulo flags |= IWN_TX_MORE_FRAG; /* Cannot happen yet. */ 3178178676Ssam 3179198429Srpaulo /* Check if frame must be protected using RTS/CTS or CTS-to-self. */ 3180198429Srpaulo if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { 3181198429Srpaulo /* NB: Group frames are sent using CCK in 802.11b/g. */ 3182198429Srpaulo if (totlen + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) { 3183198429Srpaulo flags |= IWN_TX_NEED_RTS; 3184178676Ssam } else if ((ic->ic_flags & IEEE80211_F_USEPROT) && 3185201209Srpaulo ridx >= IWN_RIDX_OFDM6) { 3186178676Ssam if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) 3187198429Srpaulo flags |= IWN_TX_NEED_CTS; 3188178676Ssam else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) 3189198429Srpaulo flags |= IWN_TX_NEED_RTS; 3190178676Ssam } 3191198429Srpaulo if (flags & (IWN_TX_NEED_RTS | IWN_TX_NEED_CTS)) { 3192198429Srpaulo if (sc->hw_type != IWN_HW_REV_TYPE_4965) { 3193198429Srpaulo /* 5000 autoselects RTS/CTS or CTS-to-self. */ 3194198429Srpaulo flags &= ~(IWN_TX_NEED_RTS | IWN_TX_NEED_CTS); 3195198429Srpaulo flags |= IWN_TX_NEED_PROTECTION; 3196198429Srpaulo } else 3197198429Srpaulo flags |= IWN_TX_FULL_TXOP; 3198198429Srpaulo } 3199201209Srpaulo } 3200178676Ssam 3201198429Srpaulo if (IEEE80211_IS_MULTICAST(wh->i_addr1) || 3202198429Srpaulo type != IEEE80211_FC0_TYPE_DATA) 3203220728Sbschmidt tx->id = sc->broadcast_id; 3204198429Srpaulo else 3205198429Srpaulo tx->id = wn->id; 3206198429Srpaulo 3207178676Ssam if (type == IEEE80211_FC0_TYPE_MGT) { 3208178676Ssam uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; 3209178676Ssam 3210198429Srpaulo /* Tell HW to set timestamp in probe responses. */ 3211178676Ssam if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP) 3212178676Ssam flags |= IWN_TX_INSERT_TSTAMP; 3213178676Ssam if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ || 3214178676Ssam subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) 3215198429Srpaulo tx->timeout = htole16(3); 3216178676Ssam else 3217198429Srpaulo tx->timeout = htole16(2); 3218178676Ssam } else 3219198429Srpaulo tx->timeout = htole16(0); 3220178676Ssam 3221178676Ssam if (hdrlen & 3) { 3222201209Srpaulo /* First segment length must be a multiple of 4. */ 3223178676Ssam flags |= IWN_TX_NEED_PADDING; 3224178676Ssam pad = 4 - (hdrlen & 3); 3225178676Ssam } else 3226178676Ssam pad = 0; 3227178676Ssam 3228198429Srpaulo tx->len = htole16(totlen); 3229220720Sbschmidt tx->tid = tid; 3230201209Srpaulo tx->rts_ntries = 60; 3231201209Srpaulo tx->data_ntries = 15; 3232178676Ssam tx->lifetime = htole32(IWN_LIFETIME_INFINITE); 3233198429Srpaulo tx->plcp = rinfo->plcp; 3234198429Srpaulo tx->rflags = rinfo->flags; 3235220728Sbschmidt if (tx->id == sc->broadcast_id) { 3236201209Srpaulo /* Group or management frame. */ 3237201209Srpaulo tx->linkq = 0; 3238198429Srpaulo /* XXX Alternate between antenna A and B? */ 3239201209Srpaulo txant = IWN_LSB(sc->txchainmask); 3240198429Srpaulo tx->rflags |= IWN_RFLAG_ANT(txant); 3241201209Srpaulo } else { 3242220715Sbschmidt tx->linkq = ni->ni_rates.rs_nrates - ridx - 1; 3243201209Srpaulo flags |= IWN_TX_LINKQ; /* enable MRR */ 3244201209Srpaulo } 3245198429Srpaulo /* Set physical address of "scratch area". */ 3246201209Srpaulo tx->loaddr = htole32(IWN_LOADDR(data->scratch_paddr)); 3247201209Srpaulo tx->hiaddr = IWN_HIADDR(data->scratch_paddr); 3248178676Ssam 3249198429Srpaulo /* Copy 802.11 header in TX command. */ 3250178676Ssam memcpy((uint8_t *)(tx + 1), wh, hdrlen); 3251178676Ssam 3252198429Srpaulo /* Trim 802.11 header. */ 3253198429Srpaulo m_adj(m, hdrlen); 3254198429Srpaulo tx->security = 0; 3255198429Srpaulo tx->flags = htole32(flags); 3256198429Srpaulo 3257220700Sbschmidt error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m, segs, 3258220700Sbschmidt &nsegs, BUS_DMA_NOWAIT); 3259220700Sbschmidt if (error != 0) { 3260220700Sbschmidt if (error != EFBIG) { 3261220700Sbschmidt device_printf(sc->sc_dev, 3262220700Sbschmidt "%s: can't map mbuf (error %d)\n", __func__, error); 3263220700Sbschmidt m_freem(m); 3264220700Sbschmidt return error; 3265178676Ssam } 3266220700Sbschmidt /* Too many DMA segments, linearize mbuf. */ 3267220700Sbschmidt m1 = m_collapse(m, M_DONTWAIT, IWN_MAX_SCATTER); 3268220700Sbschmidt if (m1 == NULL) { 3269220700Sbschmidt device_printf(sc->sc_dev, 3270220700Sbschmidt "%s: could not defrag mbuf\n", __func__); 3271220700Sbschmidt m_freem(m); 3272220700Sbschmidt return ENOBUFS; 3273220700Sbschmidt } 3274220700Sbschmidt m = m1; 3275220700Sbschmidt 3276220700Sbschmidt error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m, 3277220700Sbschmidt segs, &nsegs, BUS_DMA_NOWAIT); 3278178676Ssam if (error != 0) { 3279178676Ssam device_printf(sc->sc_dev, 3280220700Sbschmidt "%s: can't map mbuf (error %d)\n", __func__, error); 3281198429Srpaulo m_freem(m); 3282178676Ssam return error; 3283178676Ssam } 3284178676Ssam } 3285178676Ssam 3286198429Srpaulo data->m = m; 3287178676Ssam data->ni = ni; 3288178676Ssam 3289178676Ssam DPRINTF(sc, IWN_DEBUG_XMIT, "%s: qid %d idx %d len %d nsegs %d\n", 3290198429Srpaulo __func__, ring->qid, ring->cur, m->m_pkthdr.len, nsegs); 3291178676Ssam 3292198429Srpaulo /* Fill TX descriptor. */ 3293220700Sbschmidt desc->nsegs = 1; 3294220700Sbschmidt if (m->m_len != 0) 3295220700Sbschmidt desc->nsegs += nsegs; 3296198429Srpaulo /* First DMA segment is used by the TX command. */ 3297201209Srpaulo desc->segs[0].addr = htole32(IWN_LOADDR(data->cmd_paddr)); 3298201209Srpaulo desc->segs[0].len = htole16(IWN_HIADDR(data->cmd_paddr) | 3299198429Srpaulo (4 + sizeof (*tx) + hdrlen + pad) << 4); 3300198429Srpaulo /* Other DMA segments are for data payload. */ 3301220700Sbschmidt seg = &segs[0]; 3302178676Ssam for (i = 1; i <= nsegs; i++) { 3303220700Sbschmidt desc->segs[i].addr = htole32(IWN_LOADDR(seg->ds_addr)); 3304220700Sbschmidt desc->segs[i].len = htole16(IWN_HIADDR(seg->ds_addr) | 3305220700Sbschmidt seg->ds_len << 4); 3306220700Sbschmidt seg++; 3307178676Ssam } 3308178676Ssam 3309201209Srpaulo bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE); 3310201209Srpaulo bus_dmamap_sync(ring->data_dmat, ring->cmd_dma.map, 3311198429Srpaulo BUS_DMASYNC_PREWRITE); 3312198429Srpaulo bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, 3313198429Srpaulo BUS_DMASYNC_PREWRITE); 3314178676Ssam 3315201209Srpaulo#ifdef notyet 3316198429Srpaulo /* Update TX scheduler. */ 3317220728Sbschmidt ops->update_sched(sc, ring->qid, ring->cur, tx->id, totlen); 3318201209Srpaulo#endif 3319178676Ssam 3320198429Srpaulo /* Kick TX ring. */ 3321178676Ssam ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT; 3322198429Srpaulo IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur); 3323178676Ssam 3324198429Srpaulo /* Mark TX ring as full if we reach a certain threshold. */ 3325198429Srpaulo if (++ring->queued > IWN_TX_RING_HIMARK) 3326198429Srpaulo sc->qfullmsk |= 1 << ring->qid; 3327178676Ssam 3328178676Ssam return 0; 3329178676Ssam} 3330178676Ssam 3331178676Ssamstatic int 3332201209Srpauloiwn_tx_data_raw(struct iwn_softc *sc, struct mbuf *m, 3333220720Sbschmidt struct ieee80211_node *ni, const struct ieee80211_bpf_params *params) 3334178676Ssam{ 3335198429Srpaulo const struct iwn_rate *rinfo; 3336178676Ssam struct ifnet *ifp = sc->sc_ifp; 3337198429Srpaulo struct ieee80211vap *vap = ni->ni_vap; 3338198429Srpaulo struct ieee80211com *ic = ifp->if_l2com; 3339198429Srpaulo struct iwn_tx_cmd *cmd; 3340198429Srpaulo struct iwn_cmd_data *tx; 3341198429Srpaulo struct ieee80211_frame *wh; 3342220720Sbschmidt struct iwn_tx_ring *ring; 3343178676Ssam struct iwn_tx_desc *desc; 3344178676Ssam struct iwn_tx_data *data; 3345220700Sbschmidt struct mbuf *m1; 3346220700Sbschmidt bus_dma_segment_t *seg, segs[IWN_MAX_SCATTER]; 3347198429Srpaulo uint32_t flags; 3348198429Srpaulo u_int hdrlen; 3349220720Sbschmidt int ac, totlen, error, pad, nsegs = 0, i, rate; 3350201209Srpaulo uint8_t ridx, type, txant; 3351178676Ssam 3352198429Srpaulo IWN_LOCK_ASSERT(sc); 3353178676Ssam 3354201209Srpaulo wh = mtod(m, struct ieee80211_frame *); 3355198429Srpaulo hdrlen = ieee80211_anyhdrsize(wh); 3356198429Srpaulo type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; 3357198429Srpaulo 3358220720Sbschmidt ac = params->ibp_pri & 3; 3359220720Sbschmidt 3360220720Sbschmidt ring = &sc->txq[ac]; 3361178676Ssam desc = &ring->desc[ring->cur]; 3362178676Ssam data = &ring->data[ring->cur]; 3363178676Ssam 3364198429Srpaulo /* Choose a TX rate index. */ 3365198429Srpaulo rate = params->ibp_rate0; 3366198429Srpaulo if (!ieee80211_isratevalid(ic->ic_rt, rate)) { 3367198429Srpaulo /* XXX fall back to mcast/mgmt rate? */ 3368201209Srpaulo m_freem(m); 3369198429Srpaulo return EINVAL; 3370198429Srpaulo } 3371201209Srpaulo ridx = iwn_plcp_signal(rate); 3372201209Srpaulo rinfo = &iwn_rates[ridx]; 3373198429Srpaulo 3374201209Srpaulo totlen = m->m_pkthdr.len; 3375198429Srpaulo 3376201209Srpaulo /* Prepare TX firmware command. */ 3377198429Srpaulo cmd = &ring->cmd[ring->cur]; 3378198429Srpaulo cmd->code = IWN_CMD_TX_DATA; 3379198429Srpaulo cmd->flags = 0; 3380198429Srpaulo cmd->qid = ring->qid; 3381198429Srpaulo cmd->idx = ring->cur; 3382198429Srpaulo 3383198429Srpaulo tx = (struct iwn_cmd_data *)cmd->data; 3384201209Srpaulo /* NB: No need to clear tx, all fields are reinitialized here. */ 3385198429Srpaulo tx->scratch = 0; /* clear "scratch" area */ 3386198429Srpaulo 3387198429Srpaulo flags = 0; 3388198429Srpaulo if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0) 3389198429Srpaulo flags |= IWN_TX_NEED_ACK; 3390198429Srpaulo if (params->ibp_flags & IEEE80211_BPF_RTS) { 3391198429Srpaulo if (sc->hw_type != IWN_HW_REV_TYPE_4965) { 3392198429Srpaulo /* 5000 autoselects RTS/CTS or CTS-to-self. */ 3393198429Srpaulo flags &= ~IWN_TX_NEED_RTS; 3394198429Srpaulo flags |= IWN_TX_NEED_PROTECTION; 3395198429Srpaulo } else 3396198429Srpaulo flags |= IWN_TX_NEED_RTS | IWN_TX_FULL_TXOP; 3397198429Srpaulo } 3398198429Srpaulo if (params->ibp_flags & IEEE80211_BPF_CTS) { 3399198429Srpaulo if (sc->hw_type != IWN_HW_REV_TYPE_4965) { 3400198429Srpaulo /* 5000 autoselects RTS/CTS or CTS-to-self. */ 3401198429Srpaulo flags &= ~IWN_TX_NEED_CTS; 3402198429Srpaulo flags |= IWN_TX_NEED_PROTECTION; 3403198429Srpaulo } else 3404198429Srpaulo flags |= IWN_TX_NEED_CTS | IWN_TX_FULL_TXOP; 3405198429Srpaulo } 3406198429Srpaulo if (type == IEEE80211_FC0_TYPE_MGT) { 3407198429Srpaulo uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; 3408198429Srpaulo 3409220725Sbschmidt /* Tell HW to set timestamp in probe responses. */ 3410198429Srpaulo if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP) 3411198429Srpaulo flags |= IWN_TX_INSERT_TSTAMP; 3412198429Srpaulo 3413198429Srpaulo if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ || 3414198429Srpaulo subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) 3415198429Srpaulo tx->timeout = htole16(3); 3416198429Srpaulo else 3417198429Srpaulo tx->timeout = htole16(2); 3418198429Srpaulo } else 3419198429Srpaulo tx->timeout = htole16(0); 3420198429Srpaulo 3421198429Srpaulo if (hdrlen & 3) { 3422201209Srpaulo /* First segment length must be a multiple of 4. */ 3423198429Srpaulo flags |= IWN_TX_NEED_PADDING; 3424198429Srpaulo pad = 4 - (hdrlen & 3); 3425198429Srpaulo } else 3426198429Srpaulo pad = 0; 3427198429Srpaulo 3428198429Srpaulo if (ieee80211_radiotap_active_vap(vap)) { 3429198429Srpaulo struct iwn_tx_radiotap_header *tap = &sc->sc_txtap; 3430198429Srpaulo 3431198429Srpaulo tap->wt_flags = 0; 3432198429Srpaulo tap->wt_rate = rate; 3433198429Srpaulo 3434201209Srpaulo ieee80211_radiotap_tx(vap, m); 3435198429Srpaulo } 3436198429Srpaulo 3437198429Srpaulo tx->len = htole16(totlen); 3438198429Srpaulo tx->tid = 0; 3439220728Sbschmidt tx->id = sc->broadcast_id; 3440198429Srpaulo tx->rts_ntries = params->ibp_try1; 3441198429Srpaulo tx->data_ntries = params->ibp_try0; 3442198429Srpaulo tx->lifetime = htole32(IWN_LIFETIME_INFINITE); 3443198429Srpaulo tx->plcp = rinfo->plcp; 3444198429Srpaulo tx->rflags = rinfo->flags; 3445201209Srpaulo /* Group or management frame. */ 3446201209Srpaulo tx->linkq = 0; 3447201209Srpaulo txant = IWN_LSB(sc->txchainmask); 3448201209Srpaulo tx->rflags |= IWN_RFLAG_ANT(txant); 3449198429Srpaulo /* Set physical address of "scratch area". */ 3450220694Sbschmidt tx->loaddr = htole32(IWN_LOADDR(data->scratch_paddr)); 3451220694Sbschmidt tx->hiaddr = IWN_HIADDR(data->scratch_paddr); 3452198429Srpaulo 3453198429Srpaulo /* Copy 802.11 header in TX command. */ 3454198429Srpaulo memcpy((uint8_t *)(tx + 1), wh, hdrlen); 3455198429Srpaulo 3456198429Srpaulo /* Trim 802.11 header. */ 3457201209Srpaulo m_adj(m, hdrlen); 3458198429Srpaulo tx->security = 0; 3459198429Srpaulo tx->flags = htole32(flags); 3460198429Srpaulo 3461220700Sbschmidt error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m, segs, 3462220700Sbschmidt &nsegs, BUS_DMA_NOWAIT); 3463220700Sbschmidt if (error != 0) { 3464220700Sbschmidt if (error != EFBIG) { 3465220700Sbschmidt device_printf(sc->sc_dev, 3466220700Sbschmidt "%s: can't map mbuf (error %d)\n", __func__, error); 3467220700Sbschmidt m_freem(m); 3468220700Sbschmidt return error; 3469178676Ssam } 3470220700Sbschmidt /* Too many DMA segments, linearize mbuf. */ 3471220700Sbschmidt m1 = m_collapse(m, M_DONTWAIT, IWN_MAX_SCATTER); 3472220700Sbschmidt if (m1 == NULL) { 3473220700Sbschmidt device_printf(sc->sc_dev, 3474220700Sbschmidt "%s: could not defrag mbuf\n", __func__); 3475220700Sbschmidt m_freem(m); 3476220700Sbschmidt return ENOBUFS; 3477220700Sbschmidt } 3478220700Sbschmidt m = m1; 3479220700Sbschmidt 3480220700Sbschmidt error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m, 3481220700Sbschmidt segs, &nsegs, BUS_DMA_NOWAIT); 3482178676Ssam if (error != 0) { 3483178676Ssam device_printf(sc->sc_dev, 3484220700Sbschmidt "%s: can't map mbuf (error %d)\n", __func__, error); 3485201209Srpaulo m_freem(m); 3486178676Ssam return error; 3487178676Ssam } 3488178676Ssam } 3489178676Ssam 3490201209Srpaulo data->m = m; 3491178676Ssam data->ni = ni; 3492178676Ssam 3493178676Ssam DPRINTF(sc, IWN_DEBUG_XMIT, "%s: qid %d idx %d len %d nsegs %d\n", 3494201209Srpaulo __func__, ring->qid, ring->cur, m->m_pkthdr.len, nsegs); 3495178676Ssam 3496198429Srpaulo /* Fill TX descriptor. */ 3497220700Sbschmidt desc->nsegs = 1; 3498220700Sbschmidt if (m->m_len != 0) 3499220700Sbschmidt desc->nsegs += nsegs; 3500198429Srpaulo /* First DMA segment is used by the TX command. */ 3501201209Srpaulo desc->segs[0].addr = htole32(IWN_LOADDR(data->cmd_paddr)); 3502201209Srpaulo desc->segs[0].len = htole16(IWN_HIADDR(data->cmd_paddr) | 3503198429Srpaulo (4 + sizeof (*tx) + hdrlen + pad) << 4); 3504198429Srpaulo /* Other DMA segments are for data payload. */ 3505220700Sbschmidt seg = &segs[0]; 3506178676Ssam for (i = 1; i <= nsegs; i++) { 3507220700Sbschmidt desc->segs[i].addr = htole32(IWN_LOADDR(seg->ds_addr)); 3508220700Sbschmidt desc->segs[i].len = htole16(IWN_HIADDR(seg->ds_addr) | 3509220700Sbschmidt seg->ds_len << 4); 3510220700Sbschmidt seg++; 3511178676Ssam } 3512178676Ssam 3513201209Srpaulo bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE); 3514201209Srpaulo bus_dmamap_sync(ring->data_dmat, ring->cmd_dma.map, 3515201209Srpaulo BUS_DMASYNC_PREWRITE); 3516201209Srpaulo bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, 3517201209Srpaulo BUS_DMASYNC_PREWRITE); 3518201209Srpaulo 3519201209Srpaulo#ifdef notyet 3520198429Srpaulo /* Update TX scheduler. */ 3521220728Sbschmidt ops->update_sched(sc, ring->qid, ring->cur, tx->id, totlen); 3522201209Srpaulo#endif 3523178676Ssam 3524198429Srpaulo /* Kick TX ring. */ 3525178676Ssam ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT; 3526198429Srpaulo IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur); 3527178676Ssam 3528198429Srpaulo /* Mark TX ring as full if we reach a certain threshold. */ 3529198429Srpaulo if (++ring->queued > IWN_TX_RING_HIMARK) 3530198429Srpaulo sc->qfullmsk |= 1 << ring->qid; 3531178676Ssam 3532178676Ssam return 0; 3533178676Ssam} 3534178676Ssam 3535178676Ssamstatic int 3536178676Ssamiwn_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, 3537220720Sbschmidt const struct ieee80211_bpf_params *params) 3538178676Ssam{ 3539178676Ssam struct ieee80211com *ic = ni->ni_ic; 3540178676Ssam struct ifnet *ifp = ic->ic_ifp; 3541178676Ssam struct iwn_softc *sc = ifp->if_softc; 3542198429Srpaulo int error = 0; 3543178676Ssam 3544178676Ssam if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { 3545178676Ssam ieee80211_free_node(ni); 3546178676Ssam m_freem(m); 3547178676Ssam return ENETDOWN; 3548178676Ssam } 3549178676Ssam 3550178676Ssam IWN_LOCK(sc); 3551178676Ssam if (params == NULL) { 3552178676Ssam /* 3553178676Ssam * Legacy path; interpret frame contents to decide 3554178676Ssam * precisely how to send the frame. 3555178676Ssam */ 3556220720Sbschmidt error = iwn_tx_data(sc, m, ni); 3557178676Ssam } else { 3558178676Ssam /* 3559178676Ssam * Caller supplied explicit parameters to use in 3560178676Ssam * sending the frame. 3561178676Ssam */ 3562220720Sbschmidt error = iwn_tx_data_raw(sc, m, ni, params); 3563178676Ssam } 3564178676Ssam if (error != 0) { 3565178676Ssam /* NB: m is reclaimed on tx failure */ 3566178676Ssam ieee80211_free_node(ni); 3567178676Ssam ifp->if_oerrors++; 3568178676Ssam } 3569220667Sbschmidt sc->sc_tx_timer = 5; 3570220667Sbschmidt 3571178676Ssam IWN_UNLOCK(sc); 3572178676Ssam return error; 3573178676Ssam} 3574178676Ssam 3575206477Sbschmidtstatic void 3576198429Srpauloiwn_start(struct ifnet *ifp) 3577198429Srpaulo{ 3578198429Srpaulo struct iwn_softc *sc = ifp->if_softc; 3579198429Srpaulo 3580198429Srpaulo IWN_LOCK(sc); 3581198429Srpaulo iwn_start_locked(ifp); 3582198429Srpaulo IWN_UNLOCK(sc); 3583198429Srpaulo} 3584198429Srpaulo 3585206477Sbschmidtstatic void 3586198429Srpauloiwn_start_locked(struct ifnet *ifp) 3587198429Srpaulo{ 3588198429Srpaulo struct iwn_softc *sc = ifp->if_softc; 3589198429Srpaulo struct ieee80211_node *ni; 3590198429Srpaulo struct mbuf *m; 3591198429Srpaulo 3592198429Srpaulo IWN_LOCK_ASSERT(sc); 3593198429Srpaulo 3594220720Sbschmidt if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || 3595220720Sbschmidt (ifp->if_drv_flags & IFF_DRV_OACTIVE)) 3596220720Sbschmidt return; 3597220720Sbschmidt 3598198429Srpaulo for (;;) { 3599198429Srpaulo if (sc->qfullmsk != 0) { 3600198429Srpaulo ifp->if_drv_flags |= IFF_DRV_OACTIVE; 3601198429Srpaulo break; 3602198429Srpaulo } 3603198429Srpaulo IFQ_DRV_DEQUEUE(&ifp->if_snd, m); 3604198429Srpaulo if (m == NULL) 3605198429Srpaulo break; 3606198429Srpaulo ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; 3607220720Sbschmidt if (iwn_tx_data(sc, m, ni) != 0) { 3608220720Sbschmidt ieee80211_free_node(ni); 3609198429Srpaulo ifp->if_oerrors++; 3610220720Sbschmidt continue; 3611198429Srpaulo } 3612198429Srpaulo sc->sc_tx_timer = 5; 3613198429Srpaulo } 3614198429Srpaulo} 3615198429Srpaulo 3616178676Ssamstatic void 3617220667Sbschmidtiwn_watchdog(void *arg) 3618178676Ssam{ 3619220667Sbschmidt struct iwn_softc *sc = arg; 3620220667Sbschmidt struct ifnet *ifp = sc->sc_ifp; 3621220667Sbschmidt struct ieee80211com *ic = ifp->if_l2com; 3622178676Ssam 3623220667Sbschmidt IWN_LOCK_ASSERT(sc); 3624220667Sbschmidt 3625220667Sbschmidt KASSERT(ifp->if_drv_flags & IFF_DRV_RUNNING, ("not running")); 3626220667Sbschmidt 3627220668Sbschmidt if (sc->sc_tx_timer > 0) { 3628220668Sbschmidt if (--sc->sc_tx_timer == 0) { 3629220667Sbschmidt if_printf(ifp, "device timeout\n"); 3630220667Sbschmidt ieee80211_runtask(ic, &sc->sc_reinit_task); 3631220667Sbschmidt return; 3632220667Sbschmidt } 3633178676Ssam } 3634220667Sbschmidt callout_reset(&sc->watchdog_to, hz, iwn_watchdog, sc); 3635178676Ssam} 3636178676Ssam 3637206477Sbschmidtstatic int 3638178676Ssamiwn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 3639178676Ssam{ 3640178676Ssam struct iwn_softc *sc = ifp->if_softc; 3641178676Ssam struct ieee80211com *ic = ifp->if_l2com; 3642201209Srpaulo struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 3643178676Ssam struct ifreq *ifr = (struct ifreq *) data; 3644201209Srpaulo int error = 0, startall = 0, stop = 0; 3645178676Ssam 3646178676Ssam switch (cmd) { 3647220723Sbschmidt case SIOCGIFADDR: 3648220723Sbschmidt error = ether_ioctl(ifp, cmd, data); 3649220723Sbschmidt break; 3650178676Ssam case SIOCSIFFLAGS: 3651178704Sthompsa IWN_LOCK(sc); 3652178676Ssam if (ifp->if_flags & IFF_UP) { 3653178676Ssam if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { 3654178676Ssam iwn_init_locked(sc); 3655201209Srpaulo if (IWN_READ(sc, IWN_GP_CNTRL) & IWN_GP_CNTRL_RFKILL) 3656201209Srpaulo startall = 1; 3657201209Srpaulo else 3658201209Srpaulo stop = 1; 3659178676Ssam } 3660178676Ssam } else { 3661178676Ssam if (ifp->if_drv_flags & IFF_DRV_RUNNING) 3662178676Ssam iwn_stop_locked(sc); 3663178676Ssam } 3664178704Sthompsa IWN_UNLOCK(sc); 3665178704Sthompsa if (startall) 3666178704Sthompsa ieee80211_start_all(ic); 3667201209Srpaulo else if (vap != NULL && stop) 3668201209Srpaulo ieee80211_stop(vap); 3669178676Ssam break; 3670178676Ssam case SIOCGIFMEDIA: 3671178676Ssam error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd); 3672178676Ssam break; 3673178704Sthompsa default: 3674178704Sthompsa error = EINVAL; 3675178704Sthompsa break; 3676178676Ssam } 3677178676Ssam return error; 3678178676Ssam} 3679178676Ssam 3680178676Ssam/* 3681178676Ssam * Send a command to the firmware. 3682178676Ssam */ 3683206477Sbschmidtstatic int 3684178676Ssamiwn_cmd(struct iwn_softc *sc, int code, const void *buf, int size, int async) 3685178676Ssam{ 3686178676Ssam struct iwn_tx_ring *ring = &sc->txq[4]; 3687178676Ssam struct iwn_tx_desc *desc; 3688198429Srpaulo struct iwn_tx_data *data; 3689178676Ssam struct iwn_tx_cmd *cmd; 3690198439Srpaulo struct mbuf *m; 3691178676Ssam bus_addr_t paddr; 3692198439Srpaulo int totlen, error; 3693178676Ssam 3694178676Ssam IWN_LOCK_ASSERT(sc); 3695178676Ssam 3696178676Ssam desc = &ring->desc[ring->cur]; 3697198429Srpaulo data = &ring->data[ring->cur]; 3698198429Srpaulo totlen = 4 + size; 3699178676Ssam 3700198439Srpaulo if (size > sizeof cmd->data) { 3701198439Srpaulo /* Command is too large to fit in a descriptor. */ 3702198439Srpaulo if (totlen > MCLBYTES) 3703198439Srpaulo return EINVAL; 3704201209Srpaulo m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE); 3705198439Srpaulo if (m == NULL) 3706198439Srpaulo return ENOMEM; 3707198439Srpaulo cmd = mtod(m, struct iwn_tx_cmd *); 3708201209Srpaulo error = bus_dmamap_load(ring->data_dmat, data->map, cmd, 3709198439Srpaulo totlen, iwn_dma_map_addr, &paddr, BUS_DMA_NOWAIT); 3710198439Srpaulo if (error != 0) { 3711198439Srpaulo m_freem(m); 3712198439Srpaulo return error; 3713198439Srpaulo } 3714198439Srpaulo data->m = m; 3715198439Srpaulo } else { 3716198439Srpaulo cmd = &ring->cmd[ring->cur]; 3717198439Srpaulo paddr = data->cmd_paddr; 3718198439Srpaulo } 3719198439Srpaulo 3720178676Ssam cmd->code = code; 3721178676Ssam cmd->flags = 0; 3722178676Ssam cmd->qid = ring->qid; 3723178676Ssam cmd->idx = ring->cur; 3724178676Ssam memcpy(cmd->data, buf, size); 3725178676Ssam 3726198429Srpaulo desc->nsegs = 1; 3727198429Srpaulo desc->segs[0].addr = htole32(IWN_LOADDR(paddr)); 3728198429Srpaulo desc->segs[0].len = htole16(IWN_HIADDR(paddr) | totlen << 4); 3729178676Ssam 3730178676Ssam DPRINTF(sc, IWN_DEBUG_CMD, "%s: %s (0x%x) flags %d qid %d idx %d\n", 3731178676Ssam __func__, iwn_intr_str(cmd->code), cmd->code, 3732178676Ssam cmd->flags, cmd->qid, cmd->idx); 3733178676Ssam 3734198439Srpaulo if (size > sizeof cmd->data) { 3735201209Srpaulo bus_dmamap_sync(ring->data_dmat, data->map, 3736198439Srpaulo BUS_DMASYNC_PREWRITE); 3737198439Srpaulo } else { 3738201209Srpaulo bus_dmamap_sync(ring->data_dmat, ring->cmd_dma.map, 3739198439Srpaulo BUS_DMASYNC_PREWRITE); 3740198439Srpaulo } 3741198439Srpaulo bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, 3742198439Srpaulo BUS_DMASYNC_PREWRITE); 3743198439Srpaulo 3744201209Srpaulo#ifdef notyet 3745198429Srpaulo /* Update TX scheduler. */ 3746220728Sbschmidt ops->update_sched(sc, ring->qid, ring->cur, 0, 0); 3747201209Srpaulo#endif 3748198429Srpaulo 3749198429Srpaulo /* Kick command ring. */ 3750178676Ssam ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT; 3751198429Srpaulo IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur); 3752178676Ssam 3753198439Srpaulo return async ? 0 : msleep(desc, &sc->sc_mtx, PCATCH, "iwncmd", hz); 3754178676Ssam} 3755178676Ssam 3756206477Sbschmidtstatic int 3757198429Srpauloiwn4965_add_node(struct iwn_softc *sc, struct iwn_node_info *node, int async) 3758198429Srpaulo{ 3759198429Srpaulo struct iwn4965_node_info hnode; 3760198429Srpaulo caddr_t src, dst; 3761198429Srpaulo 3762198429Srpaulo /* 3763198429Srpaulo * We use the node structure for 5000 Series internally (it is 3764198429Srpaulo * a superset of the one for 4965AGN). We thus copy the common 3765198429Srpaulo * fields before sending the command. 3766198429Srpaulo */ 3767198429Srpaulo src = (caddr_t)node; 3768198429Srpaulo dst = (caddr_t)&hnode; 3769198429Srpaulo memcpy(dst, src, 48); 3770198429Srpaulo /* Skip TSC, RX MIC and TX MIC fields from ``src''. */ 3771198429Srpaulo memcpy(dst + 48, src + 72, 20); 3772198429Srpaulo return iwn_cmd(sc, IWN_CMD_ADD_NODE, &hnode, sizeof hnode, async); 3773198429Srpaulo} 3774198429Srpaulo 3775206477Sbschmidtstatic int 3776198429Srpauloiwn5000_add_node(struct iwn_softc *sc, struct iwn_node_info *node, int async) 3777198429Srpaulo{ 3778198429Srpaulo /* Direct mapping. */ 3779198429Srpaulo return iwn_cmd(sc, IWN_CMD_ADD_NODE, node, sizeof (*node), async); 3780198429Srpaulo} 3781198429Srpaulo 3782206477Sbschmidtstatic int 3783220715Sbschmidtiwn_set_link_quality(struct iwn_softc *sc, struct ieee80211_node *ni) 3784178676Ssam{ 3785220715Sbschmidt struct iwn_node *wn = (void *)ni; 3786220715Sbschmidt struct ieee80211_rateset *rs = &ni->ni_rates; 3787198429Srpaulo struct iwn_cmd_link_quality linkq; 3788201209Srpaulo const struct iwn_rate *rinfo; 3789220715Sbschmidt uint8_t txant; 3790220715Sbschmidt int i, txrate; 3791178676Ssam 3792198429Srpaulo /* Use the first valid TX antenna. */ 3793201209Srpaulo txant = IWN_LSB(sc->txchainmask); 3794178676Ssam 3795198429Srpaulo memset(&linkq, 0, sizeof linkq); 3796220715Sbschmidt linkq.id = wn->id; 3797198429Srpaulo linkq.antmsk_1stream = txant; 3798201209Srpaulo linkq.antmsk_2stream = IWN_ANT_AB; 3799201209Srpaulo linkq.ampdu_max = 31; 3800198429Srpaulo linkq.ampdu_threshold = 3; 3801198429Srpaulo linkq.ampdu_limit = htole16(4000); /* 4ms */ 3802198429Srpaulo 3803220715Sbschmidt /* Start at highest available bit-rate. */ 3804220715Sbschmidt txrate = rs->rs_nrates - 1; 3805178676Ssam for (i = 0; i < IWN_MAX_TX_RETRIES; i++) { 3806220715Sbschmidt rinfo = &iwn_rates[wn->ridx[txrate]]; 3807220715Sbschmidt linkq.retry[i].plcp = rinfo->plcp; 3808220715Sbschmidt linkq.retry[i].rflags = rinfo->flags; 3809198429Srpaulo linkq.retry[i].rflags |= IWN_RFLAG_ANT(txant); 3810220715Sbschmidt /* Next retry at immediate lower bit-rate. */ 3811220715Sbschmidt if (txrate > 0) 3812220715Sbschmidt txrate--; 3813178676Ssam } 3814220715Sbschmidt return iwn_cmd(sc, IWN_CMD_LINK_QUALITY, &linkq, sizeof linkq, 1); 3815178676Ssam} 3816178676Ssam 3817178676Ssam/* 3818198429Srpaulo * Broadcast node is used to send group-addressed and management frames. 3819178676Ssam */ 3820206477Sbschmidtstatic int 3821201209Srpauloiwn_add_broadcast_node(struct iwn_softc *sc, int async) 3822178676Ssam{ 3823220728Sbschmidt struct iwn_ops *ops = &sc->ops; 3824198429Srpaulo struct ifnet *ifp = sc->sc_ifp; 3825220715Sbschmidt struct ieee80211com *ic = ifp->if_l2com; 3826178676Ssam struct iwn_node_info node; 3827220715Sbschmidt struct iwn_cmd_link_quality linkq; 3828220715Sbschmidt const struct iwn_rate *rinfo; 3829220715Sbschmidt uint8_t txant; 3830220715Sbschmidt int i, error; 3831178676Ssam 3832178676Ssam memset(&node, 0, sizeof node); 3833198429Srpaulo IEEE80211_ADDR_COPY(node.macaddr, ifp->if_broadcastaddr); 3834220728Sbschmidt node.id = sc->broadcast_id; 3835198429Srpaulo DPRINTF(sc, IWN_DEBUG_RESET, "%s: adding broadcast node\n", __func__); 3836220728Sbschmidt if ((error = ops->add_node(sc, &node, async)) != 0) 3837198429Srpaulo return error; 3838178676Ssam 3839220715Sbschmidt /* Use the first valid TX antenna. */ 3840220715Sbschmidt txant = IWN_LSB(sc->txchainmask); 3841220715Sbschmidt 3842220715Sbschmidt memset(&linkq, 0, sizeof linkq); 3843220728Sbschmidt linkq.id = sc->broadcast_id; 3844220715Sbschmidt linkq.antmsk_1stream = txant; 3845220715Sbschmidt linkq.antmsk_2stream = IWN_ANT_AB; 3846220715Sbschmidt linkq.ampdu_max = 64; 3847220715Sbschmidt linkq.ampdu_threshold = 3; 3848220715Sbschmidt linkq.ampdu_limit = htole16(4000); /* 4ms */ 3849220715Sbschmidt 3850220715Sbschmidt /* Use lowest mandatory bit-rate. */ 3851220715Sbschmidt if (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan)) 3852220715Sbschmidt rinfo = &iwn_rates[IWN_RIDX_OFDM6]; 3853220715Sbschmidt else 3854220715Sbschmidt rinfo = &iwn_rates[IWN_RIDX_CCK1]; 3855220715Sbschmidt linkq.retry[0].plcp = rinfo->plcp; 3856220715Sbschmidt linkq.retry[0].rflags = rinfo->flags; 3857220715Sbschmidt linkq.retry[0].rflags |= IWN_RFLAG_ANT(txant); 3858220715Sbschmidt /* Use same bit-rate for all TX retries. */ 3859220715Sbschmidt for (i = 1; i < IWN_MAX_TX_RETRIES; i++) { 3860220715Sbschmidt linkq.retry[i].plcp = linkq.retry[0].plcp; 3861220715Sbschmidt linkq.retry[i].rflags = linkq.retry[0].rflags; 3862220715Sbschmidt } 3863220715Sbschmidt return iwn_cmd(sc, IWN_CMD_LINK_QUALITY, &linkq, sizeof linkq, async); 3864178676Ssam} 3865178676Ssam 3866206477Sbschmidtstatic int 3867220721Sbschmidtiwn_updateedca(struct ieee80211com *ic) 3868178676Ssam{ 3869178676Ssam#define IWN_EXP2(x) ((1 << (x)) - 1) /* CWmin = 2^ECWmin - 1 */ 3870178676Ssam struct iwn_softc *sc = ic->ic_ifp->if_softc; 3871178676Ssam struct iwn_edca_params cmd; 3872220721Sbschmidt int aci; 3873178676Ssam 3874178676Ssam memset(&cmd, 0, sizeof cmd); 3875178676Ssam cmd.flags = htole32(IWN_EDCA_UPDATE); 3876220721Sbschmidt for (aci = 0; aci < WME_NUM_AC; aci++) { 3877220721Sbschmidt const struct wmeParams *ac = 3878220721Sbschmidt &ic->ic_wme.wme_chanParams.cap_wmeParams[aci]; 3879220721Sbschmidt cmd.ac[aci].aifsn = ac->wmep_aifsn; 3880220721Sbschmidt cmd.ac[aci].cwmin = htole16(IWN_EXP2(ac->wmep_logcwmin)); 3881220721Sbschmidt cmd.ac[aci].cwmax = htole16(IWN_EXP2(ac->wmep_logcwmax)); 3882220721Sbschmidt cmd.ac[aci].txoplimit = 3883220721Sbschmidt htole16(IEEE80211_TXOP_TO_US(ac->wmep_txopLimit)); 3884178676Ssam } 3885201209Srpaulo IEEE80211_UNLOCK(ic); 3886178676Ssam IWN_LOCK(sc); 3887220725Sbschmidt (void)iwn_cmd(sc, IWN_CMD_EDCA_PARAMS, &cmd, sizeof cmd, 1); 3888178676Ssam IWN_UNLOCK(sc); 3889201209Srpaulo IEEE80211_LOCK(ic); 3890178676Ssam return 0; 3891178676Ssam#undef IWN_EXP2 3892178676Ssam} 3893178676Ssam 3894201209Srpaulostatic void 3895201209Srpauloiwn_update_mcast(struct ifnet *ifp) 3896201209Srpaulo{ 3897201209Srpaulo /* Ignore */ 3898201209Srpaulo} 3899201209Srpaulo 3900206477Sbschmidtstatic void 3901178676Ssamiwn_set_led(struct iwn_softc *sc, uint8_t which, uint8_t off, uint8_t on) 3902178676Ssam{ 3903178676Ssam struct iwn_cmd_led led; 3904178676Ssam 3905198429Srpaulo /* Clear microcode LED ownership. */ 3906198429Srpaulo IWN_CLRBITS(sc, IWN_LED, IWN_LED_BSM_CTRL); 3907198429Srpaulo 3908178676Ssam led.which = which; 3909198429Srpaulo led.unit = htole32(10000); /* on/off in unit of 100ms */ 3910178676Ssam led.off = off; 3911178676Ssam led.on = on; 3912198429Srpaulo (void)iwn_cmd(sc, IWN_CMD_SET_LED, &led, sizeof led, 1); 3913178676Ssam} 3914178676Ssam 3915178676Ssam/* 3916201209Srpaulo * Set the critical temperature at which the firmware will stop the radio 3917201209Srpaulo * and notify us. 3918178676Ssam */ 3919206477Sbschmidtstatic int 3920178676Ssamiwn_set_critical_temp(struct iwn_softc *sc) 3921178676Ssam{ 3922178676Ssam struct iwn_critical_temp crit; 3923201209Srpaulo int32_t temp; 3924178676Ssam 3925198429Srpaulo IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_CTEMP_STOP_RF); 3926178676Ssam 3927201209Srpaulo if (sc->hw_type == IWN_HW_REV_TYPE_5150) 3928201209Srpaulo temp = (IWN_CTOK(110) - sc->temp_off) * -5; 3929201209Srpaulo else if (sc->hw_type == IWN_HW_REV_TYPE_4965) 3930201209Srpaulo temp = IWN_CTOK(110); 3931201209Srpaulo else 3932201209Srpaulo temp = 110; 3933178676Ssam memset(&crit, 0, sizeof crit); 3934201209Srpaulo crit.tempR = htole32(temp); 3935220726Sbschmidt DPRINTF(sc, IWN_DEBUG_RESET, "setting critical temp to %d\n", temp); 3936178676Ssam return iwn_cmd(sc, IWN_CMD_SET_CRITICAL_TEMP, &crit, sizeof crit, 0); 3937178676Ssam} 3938178676Ssam 3939206477Sbschmidtstatic int 3940198429Srpauloiwn_set_timing(struct iwn_softc *sc, struct ieee80211_node *ni) 3941178676Ssam{ 3942198429Srpaulo struct iwn_cmd_timing cmd; 3943178676Ssam uint64_t val, mod; 3944178676Ssam 3945198429Srpaulo memset(&cmd, 0, sizeof cmd); 3946198429Srpaulo memcpy(&cmd.tstamp, ni->ni_tstamp.data, sizeof (uint64_t)); 3947198429Srpaulo cmd.bintval = htole16(ni->ni_intval); 3948198429Srpaulo cmd.lintval = htole16(10); 3949178676Ssam 3950198429Srpaulo /* Compute remaining time until next beacon. */ 3951220634Sbschmidt val = (uint64_t)ni->ni_intval * IEEE80211_DUR_TU; 3952198429Srpaulo mod = le64toh(cmd.tstamp) % val; 3953198429Srpaulo cmd.binitval = htole32((uint32_t)(val - mod)); 3954178676Ssam 3955198429Srpaulo DPRINTF(sc, IWN_DEBUG_RESET, "timing bintval=%u tstamp=%ju, init=%u\n", 3956198429Srpaulo ni->ni_intval, le64toh(cmd.tstamp), (uint32_t)(val - mod)); 3957178676Ssam 3958198429Srpaulo return iwn_cmd(sc, IWN_CMD_TIMING, &cmd, sizeof cmd, 1); 3959178676Ssam} 3960178676Ssam 3961206477Sbschmidtstatic void 3962198429Srpauloiwn4965_power_calibration(struct iwn_softc *sc, int temp) 3963178676Ssam{ 3964201882Skeramida struct ifnet *ifp = sc->sc_ifp; 3965201882Skeramida struct ieee80211com *ic = ifp->if_l2com; 3966201882Skeramida 3967220725Sbschmidt /* Adjust TX power if need be (delta >= 3 degC). */ 3968178676Ssam DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: temperature %d->%d\n", 3969178676Ssam __func__, sc->temp, temp); 3970198429Srpaulo if (abs(temp - sc->temp) >= 3) { 3971198429Srpaulo /* Record temperature of last calibration. */ 3972198429Srpaulo sc->temp = temp; 3973201882Skeramida (void)iwn4965_set_txpower(sc, ic->ic_bsschan, 1); 3974178676Ssam } 3975178676Ssam} 3976178676Ssam 3977178676Ssam/* 3978198429Srpaulo * Set TX power for current channel (each rate has its own power settings). 3979178676Ssam * This function takes into account the regulatory information from EEPROM, 3980178676Ssam * the current temperature and the current voltage. 3981178676Ssam */ 3982206477Sbschmidtstatic int 3983201882Skeramidaiwn4965_set_txpower(struct iwn_softc *sc, struct ieee80211_channel *ch, 3984201882Skeramida int async) 3985178676Ssam{ 3986198429Srpaulo/* Fixed-point arithmetic division using a n-bit fractional part. */ 3987178676Ssam#define fdivround(a, b, n) \ 3988178676Ssam ((((1 << n) * (a)) / (b) + (1 << n) / 2) / (1 << n)) 3989198429Srpaulo/* Linear interpolation. */ 3990178676Ssam#define interpolate(x, x1, y1, x2, y2, n) \ 3991178676Ssam ((y1) + fdivround(((int)(x) - (x1)) * ((y2) - (y1)), (x2) - (x1), n)) 3992178676Ssam 3993178676Ssam static const int tdiv[IWN_NATTEN_GROUPS] = { 9, 8, 8, 8, 6 }; 3994178676Ssam struct iwn_ucode_info *uc = &sc->ucode_info; 3995198429Srpaulo struct iwn4965_cmd_txpower cmd; 3996198429Srpaulo struct iwn4965_eeprom_chan_samples *chans; 3997220723Sbschmidt const uint8_t *rf_gain, *dsp_gain; 3998178676Ssam int32_t vdiff, tdiff; 3999178676Ssam int i, c, grp, maxpwr; 4000198429Srpaulo uint8_t chan; 4001178676Ssam 4002220687Sbschmidt /* Retrieve current channel from last RXON. */ 4003220687Sbschmidt chan = sc->rxon.chan; 4004201209Srpaulo DPRINTF(sc, IWN_DEBUG_RESET, "setting TX power for channel %d\n", 4005201209Srpaulo chan); 4006178676Ssam 4007178676Ssam memset(&cmd, 0, sizeof cmd); 4008178676Ssam cmd.band = IEEE80211_IS_CHAN_5GHZ(ch) ? 0 : 1; 4009178676Ssam cmd.chan = chan; 4010178676Ssam 4011178676Ssam if (IEEE80211_IS_CHAN_5GHZ(ch)) { 4012178676Ssam maxpwr = sc->maxpwr5GHz; 4013198429Srpaulo rf_gain = iwn4965_rf_gain_5ghz; 4014198429Srpaulo dsp_gain = iwn4965_dsp_gain_5ghz; 4015178676Ssam } else { 4016178676Ssam maxpwr = sc->maxpwr2GHz; 4017198429Srpaulo rf_gain = iwn4965_rf_gain_2ghz; 4018198429Srpaulo dsp_gain = iwn4965_dsp_gain_2ghz; 4019178676Ssam } 4020178676Ssam 4021198429Srpaulo /* Compute voltage compensation. */ 4022178676Ssam vdiff = ((int32_t)le32toh(uc->volt) - sc->eeprom_voltage) / 7; 4023178676Ssam if (vdiff > 0) 4024178676Ssam vdiff *= 2; 4025178676Ssam if (abs(vdiff) > 2) 4026178676Ssam vdiff = 0; 4027178676Ssam DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW, 4028178676Ssam "%s: voltage compensation=%d (UCODE=%d, EEPROM=%d)\n", 4029178676Ssam __func__, vdiff, le32toh(uc->volt), sc->eeprom_voltage); 4030178676Ssam 4031201209Srpaulo /* Get channel attenuation group. */ 4032178676Ssam if (chan <= 20) /* 1-20 */ 4033178676Ssam grp = 4; 4034178676Ssam else if (chan <= 43) /* 34-43 */ 4035178676Ssam grp = 0; 4036178676Ssam else if (chan <= 70) /* 44-70 */ 4037178676Ssam grp = 1; 4038178676Ssam else if (chan <= 124) /* 71-124 */ 4039178676Ssam grp = 2; 4040178676Ssam else /* 125-200 */ 4041178676Ssam grp = 3; 4042178676Ssam DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW, 4043178676Ssam "%s: chan %d, attenuation group=%d\n", __func__, chan, grp); 4044178676Ssam 4045201209Srpaulo /* Get channel sub-band. */ 4046178676Ssam for (i = 0; i < IWN_NBANDS; i++) 4047178676Ssam if (sc->bands[i].lo != 0 && 4048178676Ssam sc->bands[i].lo <= chan && chan <= sc->bands[i].hi) 4049178676Ssam break; 4050198429Srpaulo if (i == IWN_NBANDS) /* Can't happen in real-life. */ 4051198429Srpaulo return EINVAL; 4052178676Ssam chans = sc->bands[i].chans; 4053178676Ssam DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW, 4054178676Ssam "%s: chan %d sub-band=%d\n", __func__, chan, i); 4055178676Ssam 4056198429Srpaulo for (c = 0; c < 2; c++) { 4057178676Ssam uint8_t power, gain, temp; 4058178676Ssam int maxchpwr, pwr, ridx, idx; 4059178676Ssam 4060178676Ssam power = interpolate(chan, 4061178676Ssam chans[0].num, chans[0].samples[c][1].power, 4062178676Ssam chans[1].num, chans[1].samples[c][1].power, 1); 4063178676Ssam gain = interpolate(chan, 4064178676Ssam chans[0].num, chans[0].samples[c][1].gain, 4065178676Ssam chans[1].num, chans[1].samples[c][1].gain, 1); 4066178676Ssam temp = interpolate(chan, 4067178676Ssam chans[0].num, chans[0].samples[c][1].temp, 4068178676Ssam chans[1].num, chans[1].samples[c][1].temp, 1); 4069178676Ssam DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW, 4070178676Ssam "%s: Tx chain %d: power=%d gain=%d temp=%d\n", 4071178676Ssam __func__, c, power, gain, temp); 4072178676Ssam 4073198429Srpaulo /* Compute temperature compensation. */ 4074178676Ssam tdiff = ((sc->temp - temp) * 2) / tdiv[grp]; 4075178676Ssam DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW, 4076178676Ssam "%s: temperature compensation=%d (current=%d, EEPROM=%d)\n", 4077178676Ssam __func__, tdiff, sc->temp, temp); 4078178676Ssam 4079178676Ssam for (ridx = 0; ridx <= IWN_RIDX_MAX; ridx++) { 4080201209Srpaulo /* Convert dBm to half-dBm. */ 4081198429Srpaulo maxchpwr = sc->maxpwr[chan] * 2; 4082198429Srpaulo if ((ridx / 8) & 1) 4083198429Srpaulo maxchpwr -= 6; /* MIMO 2T: -3dB */ 4084178676Ssam 4085198429Srpaulo pwr = maxpwr; 4086178676Ssam 4087198429Srpaulo /* Adjust TX power based on rate. */ 4088198429Srpaulo if ((ridx % 8) == 5) 4089198429Srpaulo pwr -= 15; /* OFDM48: -7.5dB */ 4090198429Srpaulo else if ((ridx % 8) == 6) 4091198429Srpaulo pwr -= 17; /* OFDM54: -8.5dB */ 4092198429Srpaulo else if ((ridx % 8) == 7) 4093198429Srpaulo pwr -= 20; /* OFDM60: -10dB */ 4094198429Srpaulo else 4095198429Srpaulo pwr -= 10; /* Others: -5dB */ 4096178676Ssam 4097201209Srpaulo /* Do not exceed channel max TX power. */ 4098178676Ssam if (pwr > maxchpwr) 4099178676Ssam pwr = maxchpwr; 4100178676Ssam 4101178676Ssam idx = gain - (pwr - power) - tdiff - vdiff; 4102178676Ssam if ((ridx / 8) & 1) /* MIMO */ 4103178676Ssam idx += (int32_t)le32toh(uc->atten[grp][c]); 4104178676Ssam 4105178676Ssam if (cmd.band == 0) 4106178676Ssam idx += 9; /* 5GHz */ 4107178676Ssam if (ridx == IWN_RIDX_MAX) 4108178676Ssam idx += 5; /* CCK */ 4109178676Ssam 4110198429Srpaulo /* Make sure idx stays in a valid range. */ 4111178676Ssam if (idx < 0) 4112178676Ssam idx = 0; 4113198429Srpaulo else if (idx > IWN4965_MAX_PWR_INDEX) 4114198429Srpaulo idx = IWN4965_MAX_PWR_INDEX; 4115178676Ssam 4116178676Ssam DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW, 4117178676Ssam "%s: Tx chain %d, rate idx %d: power=%d\n", 4118178676Ssam __func__, c, ridx, idx); 4119178676Ssam cmd.power[ridx].rf_gain[c] = rf_gain[idx]; 4120178676Ssam cmd.power[ridx].dsp_gain[c] = dsp_gain[idx]; 4121178676Ssam } 4122178676Ssam } 4123178676Ssam 4124178676Ssam DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW, 4125178676Ssam "%s: set tx power for chan %d\n", __func__, chan); 4126178676Ssam return iwn_cmd(sc, IWN_CMD_TXPOWER, &cmd, sizeof cmd, async); 4127178676Ssam 4128178676Ssam#undef interpolate 4129178676Ssam#undef fdivround 4130178676Ssam} 4131178676Ssam 4132206477Sbschmidtstatic int 4133201882Skeramidaiwn5000_set_txpower(struct iwn_softc *sc, struct ieee80211_channel *ch, 4134201882Skeramida int async) 4135198429Srpaulo{ 4136198429Srpaulo struct iwn5000_cmd_txpower cmd; 4137198429Srpaulo 4138198429Srpaulo /* 4139198429Srpaulo * TX power calibration is handled automatically by the firmware 4140198429Srpaulo * for 5000 Series. 4141198429Srpaulo */ 4142198429Srpaulo memset(&cmd, 0, sizeof cmd); 4143198429Srpaulo cmd.global_limit = 2 * IWN5000_TXPOWER_MAX_DBM; /* 16 dBm */ 4144198429Srpaulo cmd.flags = IWN5000_TXPOWER_NO_CLOSED; 4145198429Srpaulo cmd.srv_limit = IWN5000_TXPOWER_AUTO; 4146198429Srpaulo DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: setting TX power\n", __func__); 4147198429Srpaulo return iwn_cmd(sc, IWN_CMD_TXPOWER_DBM, &cmd, sizeof cmd, async); 4148198429Srpaulo} 4149198429Srpaulo 4150178676Ssam/* 4151198429Srpaulo * Retrieve the maximum RSSI (in dBm) among receivers. 4152178676Ssam */ 4153206477Sbschmidtstatic int 4154198429Srpauloiwn4965_get_rssi(struct iwn_softc *sc, struct iwn_rx_stat *stat) 4155178676Ssam{ 4156198429Srpaulo struct iwn4965_rx_phystat *phy = (void *)stat->phybuf; 4157198429Srpaulo uint8_t mask, agc; 4158198429Srpaulo int rssi; 4159178676Ssam 4160201209Srpaulo mask = (le16toh(phy->antenna) >> 4) & IWN_ANT_ABC; 4161198429Srpaulo agc = (le16toh(phy->agc) >> 7) & 0x7f; 4162178676Ssam 4163178676Ssam rssi = 0; 4164220689Sbschmidt if (mask & IWN_ANT_A) 4165220689Sbschmidt rssi = MAX(rssi, phy->rssi[0]); 4166220689Sbschmidt if (mask & IWN_ANT_B) 4167220689Sbschmidt rssi = MAX(rssi, phy->rssi[2]); 4168220689Sbschmidt if (mask & IWN_ANT_C) 4169220689Sbschmidt rssi = MAX(rssi, phy->rssi[4]); 4170198429Srpaulo 4171220724Sbschmidt DPRINTF(sc, IWN_DEBUG_RECV, 4172220724Sbschmidt "%s: agc %d mask 0x%x rssi %d %d %d result %d\n", __func__, agc, 4173220724Sbschmidt mask, phy->rssi[0], phy->rssi[2], phy->rssi[4], 4174178676Ssam rssi - agc - IWN_RSSI_TO_DBM); 4175178676Ssam return rssi - agc - IWN_RSSI_TO_DBM; 4176178676Ssam} 4177178676Ssam 4178206477Sbschmidtstatic int 4179198429Srpauloiwn5000_get_rssi(struct iwn_softc *sc, struct iwn_rx_stat *stat) 4180198429Srpaulo{ 4181198429Srpaulo struct iwn5000_rx_phystat *phy = (void *)stat->phybuf; 4182220723Sbschmidt uint8_t agc; 4183198429Srpaulo int rssi; 4184198429Srpaulo 4185198429Srpaulo agc = (le32toh(phy->agc) >> 9) & 0x7f; 4186198429Srpaulo 4187198429Srpaulo rssi = MAX(le16toh(phy->rssi[0]) & 0xff, 4188198429Srpaulo le16toh(phy->rssi[1]) & 0xff); 4189198429Srpaulo rssi = MAX(le16toh(phy->rssi[2]) & 0xff, rssi); 4190198429Srpaulo 4191220724Sbschmidt DPRINTF(sc, IWN_DEBUG_RECV, 4192220724Sbschmidt "%s: agc %d rssi %d %d %d result %d\n", __func__, agc, 4193201822Strasz phy->rssi[0], phy->rssi[1], phy->rssi[2], 4194198429Srpaulo rssi - agc - IWN_RSSI_TO_DBM); 4195198429Srpaulo return rssi - agc - IWN_RSSI_TO_DBM; 4196198429Srpaulo} 4197198429Srpaulo 4198178676Ssam/* 4199198429Srpaulo * Retrieve the average noise (in dBm) among receivers. 4200178676Ssam */ 4201206477Sbschmidtstatic int 4202178676Ssamiwn_get_noise(const struct iwn_rx_general_stats *stats) 4203178676Ssam{ 4204178676Ssam int i, total, nbant, noise; 4205178676Ssam 4206178676Ssam total = nbant = 0; 4207178676Ssam for (i = 0; i < 3; i++) { 4208198429Srpaulo if ((noise = le32toh(stats->noise[i]) & 0xff) == 0) 4209198429Srpaulo continue; 4210198429Srpaulo total += noise; 4211198429Srpaulo nbant++; 4212178676Ssam } 4213198429Srpaulo /* There should be at least one antenna but check anyway. */ 4214178676Ssam return (nbant == 0) ? -127 : (total / nbant) - 107; 4215178676Ssam} 4216178676Ssam 4217178676Ssam/* 4218198429Srpaulo * Compute temperature (in degC) from last received statistics. 4219178676Ssam */ 4220206477Sbschmidtstatic int 4221198429Srpauloiwn4965_get_temperature(struct iwn_softc *sc) 4222178676Ssam{ 4223178676Ssam struct iwn_ucode_info *uc = &sc->ucode_info; 4224178676Ssam int32_t r1, r2, r3, r4, temp; 4225178676Ssam 4226178676Ssam r1 = le32toh(uc->temp[0].chan20MHz); 4227178676Ssam r2 = le32toh(uc->temp[1].chan20MHz); 4228178676Ssam r3 = le32toh(uc->temp[2].chan20MHz); 4229178676Ssam r4 = le32toh(sc->rawtemp); 4230178676Ssam 4231220725Sbschmidt if (r1 == r3) /* Prevents division by 0 (should not happen). */ 4232178676Ssam return 0; 4233178676Ssam 4234198429Srpaulo /* Sign-extend 23-bit R4 value to 32-bit. */ 4235220659Sbschmidt r4 = ((r4 & 0xffffff) ^ 0x800000) - 0x800000; 4236198429Srpaulo /* Compute temperature in Kelvin. */ 4237178676Ssam temp = (259 * (r4 - r2)) / (r3 - r1); 4238178676Ssam temp = (temp * 97) / 100 + 8; 4239178676Ssam 4240201209Srpaulo DPRINTF(sc, IWN_DEBUG_ANY, "temperature %dK/%dC\n", temp, 4241201209Srpaulo IWN_KTOC(temp)); 4242178676Ssam return IWN_KTOC(temp); 4243178676Ssam} 4244178676Ssam 4245206477Sbschmidtstatic int 4246198429Srpauloiwn5000_get_temperature(struct iwn_softc *sc) 4247198429Srpaulo{ 4248201209Srpaulo int32_t temp; 4249201209Srpaulo 4250198429Srpaulo /* 4251198429Srpaulo * Temperature is not used by the driver for 5000 Series because 4252220725Sbschmidt * TX power calibration is handled by firmware. 4253198429Srpaulo */ 4254201209Srpaulo temp = le32toh(sc->rawtemp); 4255201209Srpaulo if (sc->hw_type == IWN_HW_REV_TYPE_5150) { 4256201209Srpaulo temp = (temp / -5) + sc->temp_off; 4257201209Srpaulo temp = IWN_KTOC(temp); 4258201209Srpaulo } 4259201209Srpaulo return temp; 4260198429Srpaulo} 4261198429Srpaulo 4262178676Ssam/* 4263178676Ssam * Initialize sensitivity calibration state machine. 4264178676Ssam */ 4265206477Sbschmidtstatic int 4266178676Ssamiwn_init_sensitivity(struct iwn_softc *sc) 4267178676Ssam{ 4268220728Sbschmidt struct iwn_ops *ops = &sc->ops; 4269178676Ssam struct iwn_calib_state *calib = &sc->calib; 4270198429Srpaulo uint32_t flags; 4271178676Ssam int error; 4272178676Ssam 4273198429Srpaulo /* Reset calibration state machine. */ 4274178676Ssam memset(calib, 0, sizeof (*calib)); 4275178676Ssam calib->state = IWN_CALIB_STATE_INIT; 4276178676Ssam calib->cck_state = IWN_CCK_STATE_HIFA; 4277198429Srpaulo /* Set initial correlation values. */ 4278201209Srpaulo calib->ofdm_x1 = sc->limits->min_ofdm_x1; 4279201209Srpaulo calib->ofdm_mrc_x1 = sc->limits->min_ofdm_mrc_x1; 4280206444Sbschmidt calib->ofdm_x4 = sc->limits->min_ofdm_x4; 4281201209Srpaulo calib->ofdm_mrc_x4 = sc->limits->min_ofdm_mrc_x4; 4282198429Srpaulo calib->cck_x4 = 125; 4283201209Srpaulo calib->cck_mrc_x4 = sc->limits->min_cck_mrc_x4; 4284201209Srpaulo calib->energy_cck = sc->limits->energy_cck; 4285178676Ssam 4286198429Srpaulo /* Write initial sensitivity. */ 4287220726Sbschmidt if ((error = iwn_send_sensitivity(sc)) != 0) 4288178676Ssam return error; 4289178676Ssam 4290198429Srpaulo /* Write initial gains. */ 4291220728Sbschmidt if ((error = ops->init_gains(sc)) != 0) 4292198429Srpaulo return error; 4293198429Srpaulo 4294198429Srpaulo /* Request statistics at each beacon interval. */ 4295198429Srpaulo flags = 0; 4296220724Sbschmidt DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: sending request for statistics\n", 4297220724Sbschmidt __func__); 4298198429Srpaulo return iwn_cmd(sc, IWN_CMD_GET_STATISTICS, &flags, sizeof flags, 1); 4299178676Ssam} 4300178676Ssam 4301178676Ssam/* 4302178676Ssam * Collect noise and RSSI statistics for the first 20 beacons received 4303178676Ssam * after association and use them to determine connected antennas and 4304198429Srpaulo * to set differential gains. 4305178676Ssam */ 4306206477Sbschmidtstatic void 4307198429Srpauloiwn_collect_noise(struct iwn_softc *sc, 4308178676Ssam const struct iwn_rx_general_stats *stats) 4309178676Ssam{ 4310220728Sbschmidt struct iwn_ops *ops = &sc->ops; 4311178676Ssam struct iwn_calib_state *calib = &sc->calib; 4312198429Srpaulo uint32_t val; 4313198429Srpaulo int i; 4314178676Ssam 4315198429Srpaulo /* Accumulate RSSI and noise for all 3 antennas. */ 4316178676Ssam for (i = 0; i < 3; i++) { 4317178676Ssam calib->rssi[i] += le32toh(stats->rssi[i]) & 0xff; 4318178676Ssam calib->noise[i] += le32toh(stats->noise[i]) & 0xff; 4319178676Ssam } 4320198429Srpaulo /* NB: We update differential gains only once after 20 beacons. */ 4321178676Ssam if (++calib->nbeacons < 20) 4322178676Ssam return; 4323178676Ssam 4324198429Srpaulo /* Determine highest average RSSI. */ 4325198429Srpaulo val = MAX(calib->rssi[0], calib->rssi[1]); 4326198429Srpaulo val = MAX(calib->rssi[2], val); 4327178676Ssam 4328198429Srpaulo /* Determine which antennas are connected. */ 4329210110Sbschmidt sc->chainmask = sc->rxchainmask; 4330178676Ssam for (i = 0; i < 3; i++) 4331210110Sbschmidt if (val - calib->rssi[i] > 15 * 20) 4332210110Sbschmidt sc->chainmask &= ~(1 << i); 4333210110Sbschmidt DPRINTF(sc, IWN_DEBUG_CALIBRATE, 4334210110Sbschmidt "%s: RX chains mask: theoretical=0x%x, actual=0x%x\n", 4335210110Sbschmidt __func__, sc->rxchainmask, sc->chainmask); 4336210110Sbschmidt 4337198429Srpaulo /* If none of the TX antennas are connected, keep at least one. */ 4338201209Srpaulo if ((sc->chainmask & sc->txchainmask) == 0) 4339201209Srpaulo sc->chainmask |= IWN_LSB(sc->txchainmask); 4340178676Ssam 4341220728Sbschmidt (void)ops->set_gains(sc); 4342198429Srpaulo calib->state = IWN_CALIB_STATE_RUN; 4343198429Srpaulo 4344198429Srpaulo#ifdef notyet 4345198429Srpaulo /* XXX Disable RX chains with no antennas connected. */ 4346201209Srpaulo sc->rxon.rxchain = htole16(IWN_RXCHAIN_SEL(sc->chainmask)); 4347220728Sbschmidt (void)iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, sc->rxonsz, 1); 4348198429Srpaulo#endif 4349198429Srpaulo 4350198429Srpaulo#if 0 4351198429Srpaulo /* XXX: not yet */ 4352198429Srpaulo /* Enable power-saving mode if requested by user. */ 4353198429Srpaulo if (sc->sc_ic.ic_flags & IEEE80211_F_PMGTON) 4354198429Srpaulo (void)iwn_set_pslevel(sc, 0, 3, 1); 4355198429Srpaulo#endif 4356198429Srpaulo} 4357198429Srpaulo 4358206477Sbschmidtstatic int 4359198429Srpauloiwn4965_init_gains(struct iwn_softc *sc) 4360198429Srpaulo{ 4361198429Srpaulo struct iwn_phy_calib_gain cmd; 4362198429Srpaulo 4363198429Srpaulo memset(&cmd, 0, sizeof cmd); 4364198429Srpaulo cmd.code = IWN4965_PHY_CALIB_DIFF_GAIN; 4365198429Srpaulo /* Differential gains initially set to 0 for all 3 antennas. */ 4366198429Srpaulo DPRINTF(sc, IWN_DEBUG_CALIBRATE, 4367198429Srpaulo "%s: setting initial differential gains\n", __func__); 4368198429Srpaulo return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1); 4369198429Srpaulo} 4370198429Srpaulo 4371206477Sbschmidtstatic int 4372198429Srpauloiwn5000_init_gains(struct iwn_softc *sc) 4373198429Srpaulo{ 4374198429Srpaulo struct iwn_phy_calib cmd; 4375198429Srpaulo 4376198429Srpaulo memset(&cmd, 0, sizeof cmd); 4377220866Sbschmidt cmd.code = sc->reset_noise_gain; 4378198429Srpaulo cmd.ngroups = 1; 4379198429Srpaulo cmd.isvalid = 1; 4380198429Srpaulo DPRINTF(sc, IWN_DEBUG_CALIBRATE, 4381198429Srpaulo "%s: setting initial differential gains\n", __func__); 4382198429Srpaulo return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1); 4383198429Srpaulo} 4384198429Srpaulo 4385206477Sbschmidtstatic int 4386198429Srpauloiwn4965_set_gains(struct iwn_softc *sc) 4387198429Srpaulo{ 4388198429Srpaulo struct iwn_calib_state *calib = &sc->calib; 4389198429Srpaulo struct iwn_phy_calib_gain cmd; 4390198429Srpaulo int i, delta, noise; 4391198429Srpaulo 4392198429Srpaulo /* Get minimal noise among connected antennas. */ 4393201209Srpaulo noise = INT_MAX; /* NB: There's at least one antenna. */ 4394178676Ssam for (i = 0; i < 3; i++) 4395201209Srpaulo if (sc->chainmask & (1 << i)) 4396198429Srpaulo noise = MIN(calib->noise[i], noise); 4397178676Ssam 4398178676Ssam memset(&cmd, 0, sizeof cmd); 4399198429Srpaulo cmd.code = IWN4965_PHY_CALIB_DIFF_GAIN; 4400198429Srpaulo /* Set differential gains for connected antennas. */ 4401178676Ssam for (i = 0; i < 3; i++) { 4402201209Srpaulo if (sc->chainmask & (1 << i)) { 4403198429Srpaulo /* Compute attenuation (in unit of 1.5dB). */ 4404198429Srpaulo delta = (noise - (int32_t)calib->noise[i]) / 30; 4405198429Srpaulo /* NB: delta <= 0 */ 4406198429Srpaulo /* Limit to [-4.5dB,0]. */ 4407198429Srpaulo cmd.gain[i] = MIN(abs(delta), 3); 4408198429Srpaulo if (delta < 0) 4409198429Srpaulo cmd.gain[i] |= 1 << 2; /* sign bit */ 4410178676Ssam } 4411178676Ssam } 4412178676Ssam DPRINTF(sc, IWN_DEBUG_CALIBRATE, 4413198429Srpaulo "setting differential gains Ant A/B/C: %x/%x/%x (%x)\n", 4414201209Srpaulo cmd.gain[0], cmd.gain[1], cmd.gain[2], sc->chainmask); 4415198429Srpaulo return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1); 4416178676Ssam} 4417178676Ssam 4418206477Sbschmidtstatic int 4419198429Srpauloiwn5000_set_gains(struct iwn_softc *sc) 4420198429Srpaulo{ 4421198429Srpaulo struct iwn_calib_state *calib = &sc->calib; 4422198429Srpaulo struct iwn_phy_calib_gain cmd; 4423220723Sbschmidt int i, ant, div, delta; 4424198429Srpaulo 4425206444Sbschmidt /* We collected 20 beacons and !=6050 need a 1.5 factor. */ 4426206444Sbschmidt div = (sc->hw_type == IWN_HW_REV_TYPE_6050) ? 20 : 30; 4427198429Srpaulo 4428198429Srpaulo memset(&cmd, 0, sizeof cmd); 4429220866Sbschmidt cmd.code = sc->noise_gain; 4430198429Srpaulo cmd.ngroups = 1; 4431198429Srpaulo cmd.isvalid = 1; 4432201209Srpaulo /* Get first available RX antenna as referential. */ 4433201209Srpaulo ant = IWN_LSB(sc->rxchainmask); 4434201209Srpaulo /* Set differential gains for other antennas. */ 4435201209Srpaulo for (i = ant + 1; i < 3; i++) { 4436201209Srpaulo if (sc->chainmask & (1 << i)) { 4437201209Srpaulo /* The delta is relative to antenna "ant". */ 4438201209Srpaulo delta = ((int32_t)calib->noise[ant] - 4439206444Sbschmidt (int32_t)calib->noise[i]) / div; 4440198429Srpaulo /* Limit to [-4.5dB,+4.5dB]. */ 4441198429Srpaulo cmd.gain[i - 1] = MIN(abs(delta), 3); 4442198429Srpaulo if (delta < 0) 4443198429Srpaulo cmd.gain[i - 1] |= 1 << 2; /* sign bit */ 4444198429Srpaulo } 4445198429Srpaulo } 4446198429Srpaulo DPRINTF(sc, IWN_DEBUG_CALIBRATE, 4447198429Srpaulo "setting differential gains Ant B/C: %x/%x (%x)\n", 4448201209Srpaulo cmd.gain[0], cmd.gain[1], sc->chainmask); 4449198429Srpaulo return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1); 4450198429Srpaulo} 4451198429Srpaulo 4452178676Ssam/* 4453198429Srpaulo * Tune RF RX sensitivity based on the number of false alarms detected 4454178676Ssam * during the last beacon period. 4455178676Ssam */ 4456206477Sbschmidtstatic void 4457178676Ssamiwn_tune_sensitivity(struct iwn_softc *sc, const struct iwn_rx_stats *stats) 4458178676Ssam{ 4459198429Srpaulo#define inc(val, inc, max) \ 4460178676Ssam if ((val) < (max)) { \ 4461178676Ssam if ((val) < (max) - (inc)) \ 4462178676Ssam (val) += (inc); \ 4463178676Ssam else \ 4464178676Ssam (val) = (max); \ 4465178676Ssam needs_update = 1; \ 4466178676Ssam } 4467198429Srpaulo#define dec(val, dec, min) \ 4468178676Ssam if ((val) > (min)) { \ 4469178676Ssam if ((val) > (min) + (dec)) \ 4470178676Ssam (val) -= (dec); \ 4471178676Ssam else \ 4472178676Ssam (val) = (min); \ 4473178676Ssam needs_update = 1; \ 4474178676Ssam } 4475178676Ssam 4476201209Srpaulo const struct iwn_sensitivity_limits *limits = sc->limits; 4477178676Ssam struct iwn_calib_state *calib = &sc->calib; 4478178676Ssam uint32_t val, rxena, fa; 4479178676Ssam uint32_t energy[3], energy_min; 4480198439Srpaulo uint8_t noise[3], noise_ref; 4481198429Srpaulo int i, needs_update = 0; 4482178676Ssam 4483198429Srpaulo /* Check that we've been enabled long enough. */ 4484220726Sbschmidt if ((rxena = le32toh(stats->general.load)) == 0) 4485178676Ssam return; 4486178676Ssam 4487198429Srpaulo /* Compute number of false alarms since last call for OFDM. */ 4488178676Ssam fa = le32toh(stats->ofdm.bad_plcp) - calib->bad_plcp_ofdm; 4489178676Ssam fa += le32toh(stats->ofdm.fa) - calib->fa_ofdm; 4490220634Sbschmidt fa *= 200 * IEEE80211_DUR_TU; /* 200TU */ 4491178676Ssam 4492198429Srpaulo /* Save counters values for next call. */ 4493178676Ssam calib->bad_plcp_ofdm = le32toh(stats->ofdm.bad_plcp); 4494178676Ssam calib->fa_ofdm = le32toh(stats->ofdm.fa); 4495178676Ssam 4496178676Ssam if (fa > 50 * rxena) { 4497198429Srpaulo /* High false alarm count, decrease sensitivity. */ 4498178676Ssam DPRINTF(sc, IWN_DEBUG_CALIBRATE, 4499178676Ssam "%s: OFDM high false alarm count: %u\n", __func__, fa); 4500198429Srpaulo inc(calib->ofdm_x1, 1, limits->max_ofdm_x1); 4501198429Srpaulo inc(calib->ofdm_mrc_x1, 1, limits->max_ofdm_mrc_x1); 4502198429Srpaulo inc(calib->ofdm_x4, 1, limits->max_ofdm_x4); 4503198429Srpaulo inc(calib->ofdm_mrc_x4, 1, limits->max_ofdm_mrc_x4); 4504178676Ssam 4505178676Ssam } else if (fa < 5 * rxena) { 4506198429Srpaulo /* Low false alarm count, increase sensitivity. */ 4507178676Ssam DPRINTF(sc, IWN_DEBUG_CALIBRATE, 4508178676Ssam "%s: OFDM low false alarm count: %u\n", __func__, fa); 4509198429Srpaulo dec(calib->ofdm_x1, 1, limits->min_ofdm_x1); 4510198429Srpaulo dec(calib->ofdm_mrc_x1, 1, limits->min_ofdm_mrc_x1); 4511198429Srpaulo dec(calib->ofdm_x4, 1, limits->min_ofdm_x4); 4512198429Srpaulo dec(calib->ofdm_mrc_x4, 1, limits->min_ofdm_mrc_x4); 4513178676Ssam } 4514178676Ssam 4515198429Srpaulo /* Compute maximum noise among 3 receivers. */ 4516178676Ssam for (i = 0; i < 3; i++) 4517178676Ssam noise[i] = (le32toh(stats->general.noise[i]) >> 8) & 0xff; 4518198429Srpaulo val = MAX(noise[0], noise[1]); 4519198429Srpaulo val = MAX(noise[2], val); 4520198429Srpaulo /* Insert it into our samples table. */ 4521178676Ssam calib->noise_samples[calib->cur_noise_sample] = val; 4522178676Ssam calib->cur_noise_sample = (calib->cur_noise_sample + 1) % 20; 4523178676Ssam 4524198429Srpaulo /* Compute maximum noise among last 20 samples. */ 4525178676Ssam noise_ref = calib->noise_samples[0]; 4526178676Ssam for (i = 1; i < 20; i++) 4527198429Srpaulo noise_ref = MAX(noise_ref, calib->noise_samples[i]); 4528178676Ssam 4529198429Srpaulo /* Compute maximum energy among 3 receivers. */ 4530178676Ssam for (i = 0; i < 3; i++) 4531178676Ssam energy[i] = le32toh(stats->general.energy[i]); 4532198429Srpaulo val = MIN(energy[0], energy[1]); 4533198429Srpaulo val = MIN(energy[2], val); 4534198429Srpaulo /* Insert it into our samples table. */ 4535178676Ssam calib->energy_samples[calib->cur_energy_sample] = val; 4536178676Ssam calib->cur_energy_sample = (calib->cur_energy_sample + 1) % 10; 4537178676Ssam 4538198429Srpaulo /* Compute minimum energy among last 10 samples. */ 4539178676Ssam energy_min = calib->energy_samples[0]; 4540178676Ssam for (i = 1; i < 10; i++) 4541198429Srpaulo energy_min = MAX(energy_min, calib->energy_samples[i]); 4542178676Ssam energy_min += 6; 4543178676Ssam 4544198429Srpaulo /* Compute number of false alarms since last call for CCK. */ 4545178676Ssam fa = le32toh(stats->cck.bad_plcp) - calib->bad_plcp_cck; 4546178676Ssam fa += le32toh(stats->cck.fa) - calib->fa_cck; 4547220634Sbschmidt fa *= 200 * IEEE80211_DUR_TU; /* 200TU */ 4548178676Ssam 4549198429Srpaulo /* Save counters values for next call. */ 4550178676Ssam calib->bad_plcp_cck = le32toh(stats->cck.bad_plcp); 4551178676Ssam calib->fa_cck = le32toh(stats->cck.fa); 4552178676Ssam 4553178676Ssam if (fa > 50 * rxena) { 4554198429Srpaulo /* High false alarm count, decrease sensitivity. */ 4555178676Ssam DPRINTF(sc, IWN_DEBUG_CALIBRATE, 4556178676Ssam "%s: CCK high false alarm count: %u\n", __func__, fa); 4557178676Ssam calib->cck_state = IWN_CCK_STATE_HIFA; 4558178676Ssam calib->low_fa = 0; 4559178676Ssam 4560198429Srpaulo if (calib->cck_x4 > 160) { 4561178676Ssam calib->noise_ref = noise_ref; 4562178676Ssam if (calib->energy_cck > 2) 4563198429Srpaulo dec(calib->energy_cck, 2, energy_min); 4564178676Ssam } 4565198429Srpaulo if (calib->cck_x4 < 160) { 4566198429Srpaulo calib->cck_x4 = 161; 4567178676Ssam needs_update = 1; 4568178676Ssam } else 4569198429Srpaulo inc(calib->cck_x4, 3, limits->max_cck_x4); 4570178676Ssam 4571198429Srpaulo inc(calib->cck_mrc_x4, 3, limits->max_cck_mrc_x4); 4572178676Ssam 4573178676Ssam } else if (fa < 5 * rxena) { 4574198429Srpaulo /* Low false alarm count, increase sensitivity. */ 4575178676Ssam DPRINTF(sc, IWN_DEBUG_CALIBRATE, 4576178676Ssam "%s: CCK low false alarm count: %u\n", __func__, fa); 4577178676Ssam calib->cck_state = IWN_CCK_STATE_LOFA; 4578178676Ssam calib->low_fa++; 4579178676Ssam 4580198429Srpaulo if (calib->cck_state != IWN_CCK_STATE_INIT && 4581198429Srpaulo (((int32_t)calib->noise_ref - (int32_t)noise_ref) > 2 || 4582220726Sbschmidt calib->low_fa > 100)) { 4583198429Srpaulo inc(calib->energy_cck, 2, limits->min_energy_cck); 4584198429Srpaulo dec(calib->cck_x4, 3, limits->min_cck_x4); 4585198429Srpaulo dec(calib->cck_mrc_x4, 3, limits->min_cck_mrc_x4); 4586178676Ssam } 4587178676Ssam } else { 4588198429Srpaulo /* Not worth to increase or decrease sensitivity. */ 4589178676Ssam DPRINTF(sc, IWN_DEBUG_CALIBRATE, 4590178676Ssam "%s: CCK normal false alarm count: %u\n", __func__, fa); 4591178676Ssam calib->low_fa = 0; 4592178676Ssam calib->noise_ref = noise_ref; 4593178676Ssam 4594178676Ssam if (calib->cck_state == IWN_CCK_STATE_HIFA) { 4595198429Srpaulo /* Previous interval had many false alarms. */ 4596198429Srpaulo dec(calib->energy_cck, 8, energy_min); 4597178676Ssam } 4598178676Ssam calib->cck_state = IWN_CCK_STATE_INIT; 4599178676Ssam } 4600178676Ssam 4601178676Ssam if (needs_update) 4602178676Ssam (void)iwn_send_sensitivity(sc); 4603198429Srpaulo#undef dec 4604198429Srpaulo#undef inc 4605178676Ssam} 4606178676Ssam 4607206477Sbschmidtstatic int 4608178676Ssamiwn_send_sensitivity(struct iwn_softc *sc) 4609178676Ssam{ 4610178676Ssam struct iwn_calib_state *calib = &sc->calib; 4611220729Sbschmidt struct iwn_enhanced_sensitivity_cmd cmd; 4612220729Sbschmidt int len; 4613178676Ssam 4614178676Ssam memset(&cmd, 0, sizeof cmd); 4615220729Sbschmidt len = sizeof (struct iwn_sensitivity_cmd); 4616178676Ssam cmd.which = IWN_SENSITIVITY_WORKTBL; 4617198429Srpaulo /* OFDM modulation. */ 4618220726Sbschmidt cmd.corr_ofdm_x1 = htole16(calib->ofdm_x1); 4619220726Sbschmidt cmd.corr_ofdm_mrc_x1 = htole16(calib->ofdm_mrc_x1); 4620220726Sbschmidt cmd.corr_ofdm_x4 = htole16(calib->ofdm_x4); 4621220726Sbschmidt cmd.corr_ofdm_mrc_x4 = htole16(calib->ofdm_mrc_x4); 4622220726Sbschmidt cmd.energy_ofdm = htole16(sc->limits->energy_ofdm); 4623220726Sbschmidt cmd.energy_ofdm_th = htole16(62); 4624198429Srpaulo /* CCK modulation. */ 4625220726Sbschmidt cmd.corr_cck_x4 = htole16(calib->cck_x4); 4626220726Sbschmidt cmd.corr_cck_mrc_x4 = htole16(calib->cck_mrc_x4); 4627220726Sbschmidt cmd.energy_cck = htole16(calib->energy_cck); 4628198429Srpaulo /* Barker modulation: use default values. */ 4629220726Sbschmidt cmd.corr_barker = htole16(190); 4630220726Sbschmidt cmd.corr_barker_mrc = htole16(390); 4631178676Ssam 4632202986Srpaulo DPRINTF(sc, IWN_DEBUG_CALIBRATE, 4633178676Ssam "%s: set sensitivity %d/%d/%d/%d/%d/%d/%d\n", __func__, 4634198429Srpaulo calib->ofdm_x1, calib->ofdm_mrc_x1, calib->ofdm_x4, 4635198429Srpaulo calib->ofdm_mrc_x4, calib->cck_x4, 4636198429Srpaulo calib->cck_mrc_x4, calib->energy_cck); 4637220729Sbschmidt 4638220729Sbschmidt if (!(sc->sc_flags & IWN_FLAG_ENH_SENS)) 4639220729Sbschmidt goto send; 4640220729Sbschmidt /* Enhanced sensitivity settings. */ 4641220729Sbschmidt len = sizeof (struct iwn_enhanced_sensitivity_cmd); 4642220729Sbschmidt cmd.ofdm_det_slope_mrc = htole16(668); 4643220729Sbschmidt cmd.ofdm_det_icept_mrc = htole16(4); 4644220729Sbschmidt cmd.ofdm_det_slope = htole16(486); 4645220729Sbschmidt cmd.ofdm_det_icept = htole16(37); 4646220729Sbschmidt cmd.cck_det_slope_mrc = htole16(853); 4647220729Sbschmidt cmd.cck_det_icept_mrc = htole16(4); 4648220729Sbschmidt cmd.cck_det_slope = htole16(476); 4649220729Sbschmidt cmd.cck_det_icept = htole16(99); 4650220729Sbschmidtsend: 4651220729Sbschmidt return iwn_cmd(sc, IWN_CMD_SET_SENSITIVITY, &cmd, len, 1); 4652178676Ssam} 4653178676Ssam 4654198429Srpaulo/* 4655198429Srpaulo * Set STA mode power saving level (between 0 and 5). 4656198429Srpaulo * Level 0 is CAM (Continuously Aware Mode), 5 is for maximum power saving. 4657198429Srpaulo */ 4658206477Sbschmidtstatic int 4659198429Srpauloiwn_set_pslevel(struct iwn_softc *sc, int dtim, int level, int async) 4660198429Srpaulo{ 4661220723Sbschmidt struct iwn_pmgt_cmd cmd; 4662198429Srpaulo const struct iwn_pmgt *pmgt; 4663198429Srpaulo uint32_t max, skip_dtim; 4664220721Sbschmidt uint32_t reg; 4665198429Srpaulo int i; 4666198429Srpaulo 4667198429Srpaulo /* Select which PS parameters to use. */ 4668198429Srpaulo if (dtim <= 2) 4669198429Srpaulo pmgt = &iwn_pmgt[0][level]; 4670198429Srpaulo else if (dtim <= 10) 4671198429Srpaulo pmgt = &iwn_pmgt[1][level]; 4672198429Srpaulo else 4673198429Srpaulo pmgt = &iwn_pmgt[2][level]; 4674198429Srpaulo 4675198429Srpaulo memset(&cmd, 0, sizeof cmd); 4676198429Srpaulo if (level != 0) /* not CAM */ 4677198429Srpaulo cmd.flags |= htole16(IWN_PS_ALLOW_SLEEP); 4678198429Srpaulo if (level == 5) 4679198429Srpaulo cmd.flags |= htole16(IWN_PS_FAST_PD); 4680201209Srpaulo /* Retrieve PCIe Active State Power Management (ASPM). */ 4681220721Sbschmidt reg = pci_read_config(sc->sc_dev, sc->sc_cap_off + 0x10, 1); 4682220721Sbschmidt if (!(reg & 0x1)) /* L0s Entry disabled. */ 4683198429Srpaulo cmd.flags |= htole16(IWN_PS_PCI_PMGT); 4684198429Srpaulo cmd.rxtimeout = htole32(pmgt->rxtimeout * 1024); 4685198429Srpaulo cmd.txtimeout = htole32(pmgt->txtimeout * 1024); 4686198429Srpaulo 4687198429Srpaulo if (dtim == 0) { 4688198429Srpaulo dtim = 1; 4689198429Srpaulo skip_dtim = 0; 4690198429Srpaulo } else 4691198429Srpaulo skip_dtim = pmgt->skip_dtim; 4692198429Srpaulo if (skip_dtim != 0) { 4693198429Srpaulo cmd.flags |= htole16(IWN_PS_SLEEP_OVER_DTIM); 4694198429Srpaulo max = pmgt->intval[4]; 4695198429Srpaulo if (max == (uint32_t)-1) 4696198429Srpaulo max = dtim * (skip_dtim + 1); 4697198429Srpaulo else if (max > dtim) 4698198429Srpaulo max = (max / dtim) * dtim; 4699198429Srpaulo } else 4700198429Srpaulo max = dtim; 4701198429Srpaulo for (i = 0; i < 5; i++) 4702198429Srpaulo cmd.intval[i] = htole32(MIN(max, pmgt->intval[i])); 4703198429Srpaulo 4704198429Srpaulo DPRINTF(sc, IWN_DEBUG_RESET, "setting power saving level to %d\n", 4705198429Srpaulo level); 4706198429Srpaulo return iwn_cmd(sc, IWN_CMD_SET_POWER_MODE, &cmd, sizeof cmd, async); 4707198429Srpaulo} 4708198429Srpaulo 4709206477Sbschmidtstatic int 4710220662Sbschmidtiwn_send_btcoex(struct iwn_softc *sc) 4711220662Sbschmidt{ 4712220662Sbschmidt struct iwn_bluetooth cmd; 4713220662Sbschmidt 4714220662Sbschmidt memset(&cmd, 0, sizeof cmd); 4715220662Sbschmidt cmd.flags = IWN_BT_COEX_CHAN_ANN | IWN_BT_COEX_BT_PRIO; 4716220662Sbschmidt cmd.lead_time = IWN_BT_LEAD_TIME_DEF; 4717220662Sbschmidt cmd.max_kill = IWN_BT_MAX_KILL_DEF; 4718220662Sbschmidt DPRINTF(sc, IWN_DEBUG_RESET, "%s: configuring bluetooth coexistence\n", 4719220662Sbschmidt __func__); 4720220662Sbschmidt return iwn_cmd(sc, IWN_CMD_BT_COEX, &cmd, sizeof(cmd), 0); 4721220662Sbschmidt} 4722220662Sbschmidt 4723220662Sbschmidtstatic int 4724220891Sbschmidtiwn_send_advanced_btcoex(struct iwn_softc *sc) 4725220891Sbschmidt{ 4726220891Sbschmidt static const uint32_t btcoex_3wire[12] = { 4727220891Sbschmidt 0xaaaaaaaa, 0xaaaaaaaa, 0xaeaaaaaa, 0xaaaaaaaa, 4728220891Sbschmidt 0xcc00ff28, 0x0000aaaa, 0xcc00aaaa, 0x0000aaaa, 4729220891Sbschmidt 0xc0004000, 0x00004000, 0xf0005000, 0xf0005000, 4730220891Sbschmidt }; 4731220891Sbschmidt struct iwn6000_btcoex_config btconfig; 4732220891Sbschmidt struct iwn_btcoex_priotable btprio; 4733220891Sbschmidt struct iwn_btcoex_prot btprot; 4734220891Sbschmidt int error, i; 4735220891Sbschmidt 4736220891Sbschmidt memset(&btconfig, 0, sizeof btconfig); 4737220891Sbschmidt btconfig.flags = 145; 4738220891Sbschmidt btconfig.max_kill = 5; 4739220891Sbschmidt btconfig.bt3_t7_timer = 1; 4740220891Sbschmidt btconfig.kill_ack = htole32(0xffff0000); 4741220891Sbschmidt btconfig.kill_cts = htole32(0xffff0000); 4742220891Sbschmidt btconfig.sample_time = 2; 4743220891Sbschmidt btconfig.bt3_t2_timer = 0xc; 4744220891Sbschmidt for (i = 0; i < 12; i++) 4745220891Sbschmidt btconfig.lookup_table[i] = htole32(btcoex_3wire[i]); 4746220891Sbschmidt btconfig.valid = htole16(0xff); 4747220891Sbschmidt btconfig.prio_boost = 0xf0; 4748220891Sbschmidt DPRINTF(sc, IWN_DEBUG_RESET, 4749220891Sbschmidt "%s: configuring advanced bluetooth coexistence\n", __func__); 4750220891Sbschmidt error = iwn_cmd(sc, IWN_CMD_BT_COEX, &btconfig, sizeof(btconfig), 1); 4751220891Sbschmidt if (error != 0) 4752220891Sbschmidt return error; 4753220891Sbschmidt 4754220891Sbschmidt memset(&btprio, 0, sizeof btprio); 4755220891Sbschmidt btprio.calib_init1 = 0x6; 4756220891Sbschmidt btprio.calib_init2 = 0x7; 4757220891Sbschmidt btprio.calib_periodic_low1 = 0x2; 4758220891Sbschmidt btprio.calib_periodic_low2 = 0x3; 4759220891Sbschmidt btprio.calib_periodic_high1 = 0x4; 4760220891Sbschmidt btprio.calib_periodic_high2 = 0x5; 4761220891Sbschmidt btprio.dtim = 0x6; 4762220891Sbschmidt btprio.scan52 = 0x8; 4763220891Sbschmidt btprio.scan24 = 0xa; 4764220891Sbschmidt error = iwn_cmd(sc, IWN_CMD_BT_COEX_PRIOTABLE, &btprio, sizeof(btprio), 4765220891Sbschmidt 1); 4766220891Sbschmidt if (error != 0) 4767220891Sbschmidt return error; 4768220891Sbschmidt 4769220891Sbschmidt /* Force BT state machine change. */ 4770220891Sbschmidt memset(&btprot, 0, sizeof btprio); 4771220891Sbschmidt btprot.open = 1; 4772220891Sbschmidt btprot.type = 1; 4773220891Sbschmidt error = iwn_cmd(sc, IWN_CMD_BT_COEX_PROT, &btprot, sizeof(btprot), 1); 4774220891Sbschmidt if (error != 0) 4775220891Sbschmidt return error; 4776220891Sbschmidt btprot.open = 0; 4777220891Sbschmidt return iwn_cmd(sc, IWN_CMD_BT_COEX_PROT, &btprot, sizeof(btprot), 1); 4778220891Sbschmidt} 4779220891Sbschmidt 4780220891Sbschmidtstatic int 4781198429Srpauloiwn_config(struct iwn_softc *sc) 4782198429Srpaulo{ 4783220728Sbschmidt struct iwn_ops *ops = &sc->ops; 4784198429Srpaulo struct ifnet *ifp = sc->sc_ifp; 4785198429Srpaulo struct ieee80211com *ic = ifp->if_l2com; 4786201209Srpaulo uint32_t txmask; 4787220723Sbschmidt uint16_t rxchain; 4788198429Srpaulo int error; 4789198429Srpaulo 4790220676Sbschmidt if (sc->hw_type == IWN_HW_REV_TYPE_6005) { 4791220676Sbschmidt /* Set radio temperature sensor offset. */ 4792220676Sbschmidt error = iwn5000_temp_offset_calib(sc); 4793220676Sbschmidt if (error != 0) { 4794220676Sbschmidt device_printf(sc->sc_dev, 4795220676Sbschmidt "%s: could not set temperature offset\n", __func__); 4796220676Sbschmidt return error; 4797220676Sbschmidt } 4798220676Sbschmidt } 4799220676Sbschmidt 4800220725Sbschmidt /* Configure valid TX chains for >=5000 Series. */ 4801201209Srpaulo if (sc->hw_type != IWN_HW_REV_TYPE_4965) { 4802201209Srpaulo txmask = htole32(sc->txchainmask); 4803201209Srpaulo DPRINTF(sc, IWN_DEBUG_RESET, 4804201209Srpaulo "%s: configuring valid TX chains 0x%x\n", __func__, txmask); 4805201209Srpaulo error = iwn_cmd(sc, IWN5000_CMD_TX_ANT_CONFIG, &txmask, 4806201209Srpaulo sizeof txmask, 0); 4807201209Srpaulo if (error != 0) { 4808201209Srpaulo device_printf(sc->sc_dev, 4809201209Srpaulo "%s: could not configure valid TX chains, " 4810201209Srpaulo "error %d\n", __func__, error); 4811201209Srpaulo return error; 4812201209Srpaulo } 4813198429Srpaulo } 4814198429Srpaulo 4815198429Srpaulo /* Configure bluetooth coexistence. */ 4816220891Sbschmidt if (sc->sc_flags & IWN_FLAG_ADV_BTCOEX) 4817220891Sbschmidt error = iwn_send_advanced_btcoex(sc); 4818220891Sbschmidt else 4819220891Sbschmidt error = iwn_send_btcoex(sc); 4820198429Srpaulo if (error != 0) { 4821198429Srpaulo device_printf(sc->sc_dev, 4822198429Srpaulo "%s: could not configure bluetooth coexistence, error %d\n", 4823198429Srpaulo __func__, error); 4824198429Srpaulo return error; 4825198429Srpaulo } 4826198429Srpaulo 4827201209Srpaulo /* Set mode, channel, RX filter and enable RX. */ 4828198429Srpaulo memset(&sc->rxon, 0, sizeof (struct iwn_rxon)); 4829198429Srpaulo IEEE80211_ADDR_COPY(sc->rxon.myaddr, IF_LLADDR(ifp)); 4830198429Srpaulo IEEE80211_ADDR_COPY(sc->rxon.wlap, IF_LLADDR(ifp)); 4831198429Srpaulo sc->rxon.chan = ieee80211_chan2ieee(ic, ic->ic_curchan); 4832198429Srpaulo sc->rxon.flags = htole32(IWN_RXON_TSF | IWN_RXON_CTS_TO_SELF); 4833198429Srpaulo if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) 4834198429Srpaulo sc->rxon.flags |= htole32(IWN_RXON_AUTO | IWN_RXON_24GHZ); 4835198429Srpaulo switch (ic->ic_opmode) { 4836198429Srpaulo case IEEE80211_M_STA: 4837198429Srpaulo sc->rxon.mode = IWN_MODE_STA; 4838198429Srpaulo sc->rxon.filter = htole32(IWN_FILTER_MULTICAST); 4839198429Srpaulo break; 4840198429Srpaulo case IEEE80211_M_MONITOR: 4841198429Srpaulo sc->rxon.mode = IWN_MODE_MONITOR; 4842198429Srpaulo sc->rxon.filter = htole32(IWN_FILTER_MULTICAST | 4843198429Srpaulo IWN_FILTER_CTL | IWN_FILTER_PROMISC); 4844198429Srpaulo break; 4845198429Srpaulo default: 4846198429Srpaulo /* Should not get there. */ 4847198429Srpaulo break; 4848198429Srpaulo } 4849198429Srpaulo sc->rxon.cck_mask = 0x0f; /* not yet negotiated */ 4850198429Srpaulo sc->rxon.ofdm_mask = 0xff; /* not yet negotiated */ 4851198429Srpaulo sc->rxon.ht_single_mask = 0xff; 4852198429Srpaulo sc->rxon.ht_dual_mask = 0xff; 4853201209Srpaulo sc->rxon.ht_triple_mask = 0xff; 4854201209Srpaulo rxchain = 4855201209Srpaulo IWN_RXCHAIN_VALID(sc->rxchainmask) | 4856201209Srpaulo IWN_RXCHAIN_MIMO_COUNT(2) | 4857201209Srpaulo IWN_RXCHAIN_IDLE_COUNT(2); 4858198429Srpaulo sc->rxon.rxchain = htole16(rxchain); 4859198429Srpaulo DPRINTF(sc, IWN_DEBUG_RESET, "%s: setting configuration\n", __func__); 4860220728Sbschmidt error = iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, sc->rxonsz, 0); 4861198429Srpaulo if (error != 0) { 4862220726Sbschmidt device_printf(sc->sc_dev, "%s: RXON command failed\n", 4863220726Sbschmidt __func__); 4864198429Srpaulo return error; 4865198429Srpaulo } 4866198429Srpaulo 4867220726Sbschmidt if ((error = iwn_add_broadcast_node(sc, 0)) != 0) { 4868220726Sbschmidt device_printf(sc->sc_dev, "%s: could not add broadcast node\n", 4869220726Sbschmidt __func__); 4870201209Srpaulo return error; 4871201209Srpaulo } 4872201209Srpaulo 4873198429Srpaulo /* Configuration has changed, set TX power accordingly. */ 4874220728Sbschmidt if ((error = ops->set_txpower(sc, ic->ic_curchan, 0)) != 0) { 4875220726Sbschmidt device_printf(sc->sc_dev, "%s: could not set TX power\n", 4876220726Sbschmidt __func__); 4877198429Srpaulo return error; 4878198429Srpaulo } 4879198429Srpaulo 4880220726Sbschmidt if ((error = iwn_set_critical_temp(sc)) != 0) { 4881198429Srpaulo device_printf(sc->sc_dev, 4882220724Sbschmidt "%s: could not set critical temperature\n", __func__); 4883198429Srpaulo return error; 4884198429Srpaulo } 4885198429Srpaulo 4886201209Srpaulo /* Set power saving level to CAM during initialization. */ 4887220726Sbschmidt if ((error = iwn_set_pslevel(sc, 0, 0, 0)) != 0) { 4888198429Srpaulo device_printf(sc->sc_dev, 4889201209Srpaulo "%s: could not set power saving level\n", __func__); 4890198429Srpaulo return error; 4891198429Srpaulo } 4892198429Srpaulo return 0; 4893198429Srpaulo} 4894198429Srpaulo 4895220634Sbschmidt/* 4896220634Sbschmidt * Add an ssid element to a frame. 4897220634Sbschmidt */ 4898220634Sbschmidtstatic uint8_t * 4899220634Sbschmidtieee80211_add_ssid(uint8_t *frm, const uint8_t *ssid, u_int len) 4900220634Sbschmidt{ 4901220634Sbschmidt *frm++ = IEEE80211_ELEMID_SSID; 4902220634Sbschmidt *frm++ = len; 4903220634Sbschmidt memcpy(frm, ssid, len); 4904220634Sbschmidt return frm + len; 4905220634Sbschmidt} 4906220634Sbschmidt 4907206477Sbschmidtstatic int 4908198429Srpauloiwn_scan(struct iwn_softc *sc) 4909198429Srpaulo{ 4910198429Srpaulo struct ifnet *ifp = sc->sc_ifp; 4911198429Srpaulo struct ieee80211com *ic = ifp->if_l2com; 4912198429Srpaulo struct ieee80211_scan_state *ss = ic->ic_scan; /*XXX*/ 4913198429Srpaulo struct iwn_scan_hdr *hdr; 4914198429Srpaulo struct iwn_cmd_data *tx; 4915198429Srpaulo struct iwn_scan_essid *essid; 4916198429Srpaulo struct iwn_scan_chan *chan; 4917198429Srpaulo struct ieee80211_frame *wh; 4918198429Srpaulo struct ieee80211_rateset *rs; 4919198429Srpaulo struct ieee80211_channel *c; 4920220726Sbschmidt uint8_t *buf, *frm; 4921220723Sbschmidt uint16_t rxchain; 4922220726Sbschmidt uint8_t txant; 4923220634Sbschmidt int buflen, error; 4924198429Srpaulo 4925198429Srpaulo buf = malloc(IWN_SCAN_MAXSZ, M_DEVBUF, M_NOWAIT | M_ZERO); 4926198429Srpaulo if (buf == NULL) { 4927198429Srpaulo device_printf(sc->sc_dev, 4928198429Srpaulo "%s: could not allocate buffer for scan command\n", 4929198429Srpaulo __func__); 4930198429Srpaulo return ENOMEM; 4931198429Srpaulo } 4932198429Srpaulo hdr = (struct iwn_scan_hdr *)buf; 4933198429Srpaulo /* 4934198429Srpaulo * Move to the next channel if no frames are received within 10ms 4935198429Srpaulo * after sending the probe request. 4936198429Srpaulo */ 4937198429Srpaulo hdr->quiet_time = htole16(10); /* timeout in milliseconds */ 4938198429Srpaulo hdr->quiet_threshold = htole16(1); /* min # of packets */ 4939198429Srpaulo 4940198429Srpaulo /* Select antennas for scanning. */ 4941201209Srpaulo rxchain = 4942201209Srpaulo IWN_RXCHAIN_VALID(sc->rxchainmask) | 4943201209Srpaulo IWN_RXCHAIN_FORCE_MIMO_SEL(sc->rxchainmask) | 4944201209Srpaulo IWN_RXCHAIN_DRIVER_FORCE; 4945198429Srpaulo if (IEEE80211_IS_CHAN_A(ic->ic_curchan) && 4946198429Srpaulo sc->hw_type == IWN_HW_REV_TYPE_4965) { 4947198429Srpaulo /* Ant A must be avoided in 5GHz because of an HW bug. */ 4948201209Srpaulo rxchain |= IWN_RXCHAIN_FORCE_SEL(IWN_ANT_BC); 4949198429Srpaulo } else /* Use all available RX antennas. */ 4950201209Srpaulo rxchain |= IWN_RXCHAIN_FORCE_SEL(sc->rxchainmask); 4951198429Srpaulo hdr->rxchain = htole16(rxchain); 4952198429Srpaulo hdr->filter = htole32(IWN_FILTER_MULTICAST | IWN_FILTER_BEACON); 4953198429Srpaulo 4954198429Srpaulo tx = (struct iwn_cmd_data *)(hdr + 1); 4955198429Srpaulo tx->flags = htole32(IWN_TX_AUTO_SEQ); 4956220728Sbschmidt tx->id = sc->broadcast_id; 4957198429Srpaulo tx->lifetime = htole32(IWN_LIFETIME_INFINITE); 4958198429Srpaulo 4959198429Srpaulo if (IEEE80211_IS_CHAN_A(ic->ic_curchan)) { 4960198429Srpaulo /* Send probe requests at 6Mbps. */ 4961198429Srpaulo tx->plcp = iwn_rates[IWN_RIDX_OFDM6].plcp; 4962201209Srpaulo rs = &ic->ic_sup_rates[IEEE80211_MODE_11A]; 4963198429Srpaulo } else { 4964198429Srpaulo hdr->flags = htole32(IWN_RXON_24GHZ | IWN_RXON_AUTO); 4965198429Srpaulo /* Send probe requests at 1Mbps. */ 4966198429Srpaulo tx->plcp = iwn_rates[IWN_RIDX_CCK1].plcp; 4967198429Srpaulo tx->rflags = IWN_RFLAG_CCK; 4968201209Srpaulo rs = &ic->ic_sup_rates[IEEE80211_MODE_11G]; 4969198429Srpaulo } 4970198429Srpaulo /* Use the first valid TX antenna. */ 4971201209Srpaulo txant = IWN_LSB(sc->txchainmask); 4972198429Srpaulo tx->rflags |= IWN_RFLAG_ANT(txant); 4973198429Srpaulo 4974198429Srpaulo essid = (struct iwn_scan_essid *)(tx + 1); 4975198429Srpaulo if (ss->ss_ssid[0].len != 0) { 4976198429Srpaulo essid[0].id = IEEE80211_ELEMID_SSID; 4977198429Srpaulo essid[0].len = ss->ss_ssid[0].len; 4978198429Srpaulo memcpy(essid[0].data, ss->ss_ssid[0].ssid, ss->ss_ssid[0].len); 4979198429Srpaulo } 4980198429Srpaulo /* 4981198429Srpaulo * Build a probe request frame. Most of the following code is a 4982198429Srpaulo * copy & paste of what is done in net80211. 4983198429Srpaulo */ 4984198429Srpaulo wh = (struct ieee80211_frame *)(essid + 20); 4985198429Srpaulo wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | 4986198429Srpaulo IEEE80211_FC0_SUBTYPE_PROBE_REQ; 4987198429Srpaulo wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; 4988198429Srpaulo IEEE80211_ADDR_COPY(wh->i_addr1, ifp->if_broadcastaddr); 4989198429Srpaulo IEEE80211_ADDR_COPY(wh->i_addr2, IF_LLADDR(ifp)); 4990198429Srpaulo IEEE80211_ADDR_COPY(wh->i_addr3, ifp->if_broadcastaddr); 4991198429Srpaulo *(uint16_t *)&wh->i_dur[0] = 0; /* filled by HW */ 4992198429Srpaulo *(uint16_t *)&wh->i_seq[0] = 0; /* filled by HW */ 4993198429Srpaulo 4994198429Srpaulo frm = (uint8_t *)(wh + 1); 4995220634Sbschmidt frm = ieee80211_add_ssid(frm, NULL, 0); 4996220634Sbschmidt frm = ieee80211_add_rates(frm, rs); 4997220634Sbschmidt if (rs->rs_nrates > IEEE80211_RATE_SIZE) 4998220634Sbschmidt frm = ieee80211_add_xrates(frm, rs); 4999220634Sbschmidt#if 0 /* HT */ 5000220634Sbschmidt if (ic->ic_flags & IEEE80211_F_HTON) 5001220634Sbschmidt frm = ieee80211_add_htcaps(frm, ic); 5002220634Sbschmidt#endif 5003198429Srpaulo 5004198429Srpaulo /* Set length of probe request. */ 5005198429Srpaulo tx->len = htole16(frm - (uint8_t *)wh); 5006198429Srpaulo 5007198429Srpaulo c = ic->ic_curchan; 5008198429Srpaulo chan = (struct iwn_scan_chan *)frm; 5009201209Srpaulo chan->chan = htole16(ieee80211_chan2ieee(ic, c)); 5010198429Srpaulo chan->flags = 0; 5011198429Srpaulo if (ss->ss_nssid > 0) 5012198429Srpaulo chan->flags |= htole32(IWN_CHAN_NPBREQS(1)); 5013198429Srpaulo chan->dsp_gain = 0x6e; 5014201209Srpaulo if (IEEE80211_IS_CHAN_5GHZ(c) && 5015201209Srpaulo !(c->ic_flags & IEEE80211_CHAN_PASSIVE)) { 5016198429Srpaulo chan->rf_gain = 0x3b; 5017198429Srpaulo chan->active = htole16(24); 5018198429Srpaulo chan->passive = htole16(110); 5019201209Srpaulo chan->flags |= htole32(IWN_CHAN_ACTIVE); 5020201209Srpaulo } else if (IEEE80211_IS_CHAN_5GHZ(c)) { 5021201209Srpaulo chan->rf_gain = 0x3b; 5022201209Srpaulo chan->active = htole16(24); 5023201209Srpaulo if (sc->rxon.associd) 5024201209Srpaulo chan->passive = htole16(78); 5025201209Srpaulo else 5026201209Srpaulo chan->passive = htole16(110); 5027207709Sbschmidt hdr->crc_threshold = 0xffff; 5028201209Srpaulo } else if (!(c->ic_flags & IEEE80211_CHAN_PASSIVE)) { 5029201209Srpaulo chan->rf_gain = 0x28; 5030201209Srpaulo chan->active = htole16(36); 5031201209Srpaulo chan->passive = htole16(120); 5032201209Srpaulo chan->flags |= htole32(IWN_CHAN_ACTIVE); 5033198429Srpaulo } else { 5034198429Srpaulo chan->rf_gain = 0x28; 5035198429Srpaulo chan->active = htole16(36); 5036201209Srpaulo if (sc->rxon.associd) 5037201209Srpaulo chan->passive = htole16(88); 5038201209Srpaulo else 5039201209Srpaulo chan->passive = htole16(120); 5040207709Sbschmidt hdr->crc_threshold = 0xffff; 5041198429Srpaulo } 5042198429Srpaulo 5043201209Srpaulo DPRINTF(sc, IWN_DEBUG_STATE, 5044201209Srpaulo "%s: chan %u flags 0x%x rf_gain 0x%x " 5045198429Srpaulo "dsp_gain 0x%x active 0x%x passive 0x%x\n", __func__, 5046198429Srpaulo chan->chan, chan->flags, chan->rf_gain, chan->dsp_gain, 5047198429Srpaulo chan->active, chan->passive); 5048198429Srpaulo 5049201209Srpaulo hdr->nchan++; 5050201209Srpaulo chan++; 5051198429Srpaulo buflen = (uint8_t *)chan - buf; 5052198429Srpaulo hdr->len = htole16(buflen); 5053198429Srpaulo 5054198429Srpaulo DPRINTF(sc, IWN_DEBUG_STATE, "sending scan command nchan=%d\n", 5055198429Srpaulo hdr->nchan); 5056198429Srpaulo error = iwn_cmd(sc, IWN_CMD_SCAN, buf, buflen, 1); 5057198429Srpaulo free(buf, M_DEVBUF); 5058198429Srpaulo return error; 5059198429Srpaulo} 5060198429Srpaulo 5061206477Sbschmidtstatic int 5062191746Sthompsaiwn_auth(struct iwn_softc *sc, struct ieee80211vap *vap) 5063178676Ssam{ 5064220728Sbschmidt struct iwn_ops *ops = &sc->ops; 5065178676Ssam struct ifnet *ifp = sc->sc_ifp; 5066178676Ssam struct ieee80211com *ic = ifp->if_l2com; 5067178676Ssam struct ieee80211_node *ni = vap->iv_bss; 5068178676Ssam int error; 5069178676Ssam 5070201209Srpaulo /* Update adapter configuration. */ 5071198429Srpaulo IEEE80211_ADDR_COPY(sc->rxon.bssid, ni->ni_bssid); 5072220636Sbschmidt sc->rxon.chan = ieee80211_chan2ieee(ic, ni->ni_chan); 5073198429Srpaulo sc->rxon.flags = htole32(IWN_RXON_TSF | IWN_RXON_CTS_TO_SELF); 5074178676Ssam if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) 5075198429Srpaulo sc->rxon.flags |= htole32(IWN_RXON_AUTO | IWN_RXON_24GHZ); 5076198429Srpaulo if (ic->ic_flags & IEEE80211_F_SHSLOT) 5077198429Srpaulo sc->rxon.flags |= htole32(IWN_RXON_SHSLOT); 5078198429Srpaulo if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) 5079198429Srpaulo sc->rxon.flags |= htole32(IWN_RXON_SHPREAMBLE); 5080178676Ssam if (IEEE80211_IS_CHAN_A(ni->ni_chan)) { 5081198429Srpaulo sc->rxon.cck_mask = 0; 5082198429Srpaulo sc->rxon.ofdm_mask = 0x15; 5083178676Ssam } else if (IEEE80211_IS_CHAN_B(ni->ni_chan)) { 5084198429Srpaulo sc->rxon.cck_mask = 0x03; 5085198429Srpaulo sc->rxon.ofdm_mask = 0; 5086178676Ssam } else { 5087220725Sbschmidt /* Assume 802.11b/g. */ 5088198429Srpaulo sc->rxon.cck_mask = 0x0f; 5089198429Srpaulo sc->rxon.ofdm_mask = 0x15; 5090178676Ssam } 5091220724Sbschmidt DPRINTF(sc, IWN_DEBUG_STATE, "rxon chan %d flags %x cck %x ofdm %x\n", 5092220724Sbschmidt sc->rxon.chan, sc->rxon.flags, sc->rxon.cck_mask, 5093220724Sbschmidt sc->rxon.ofdm_mask); 5094220728Sbschmidt error = iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, sc->rxonsz, 1); 5095178676Ssam if (error != 0) { 5096220726Sbschmidt device_printf(sc->sc_dev, "%s: RXON command failed, error %d\n", 5097220726Sbschmidt __func__, error); 5098178676Ssam return error; 5099178676Ssam } 5100178676Ssam 5101198429Srpaulo /* Configuration has changed, set TX power accordingly. */ 5102220728Sbschmidt if ((error = ops->set_txpower(sc, ni->ni_chan, 1)) != 0) { 5103178676Ssam device_printf(sc->sc_dev, 5104220724Sbschmidt "%s: could not set TX power, error %d\n", __func__, error); 5105178676Ssam return error; 5106178676Ssam } 5107178676Ssam /* 5108201209Srpaulo * Reconfiguring RXON clears the firmware nodes table so we must 5109178676Ssam * add the broadcast node again. 5110178676Ssam */ 5111220726Sbschmidt if ((error = iwn_add_broadcast_node(sc, 1)) != 0) { 5112178676Ssam device_printf(sc->sc_dev, 5113220726Sbschmidt "%s: could not add broadcast node, error %d\n", __func__, 5114220726Sbschmidt error); 5115178676Ssam return error; 5116178676Ssam } 5117178676Ssam return 0; 5118178676Ssam} 5119178676Ssam 5120206477Sbschmidtstatic int 5121191746Sthompsaiwn_run(struct iwn_softc *sc, struct ieee80211vap *vap) 5122178676Ssam{ 5123178676Ssam#define MS(v,x) (((v) & x) >> x##_S) 5124220728Sbschmidt struct iwn_ops *ops = &sc->ops; 5125178676Ssam struct ifnet *ifp = sc->sc_ifp; 5126178676Ssam struct ieee80211com *ic = ifp->if_l2com; 5127178676Ssam struct ieee80211_node *ni = vap->iv_bss; 5128178676Ssam struct iwn_node_info node; 5129201209Srpaulo int error; 5130178676Ssam 5131178676Ssam if (ic->ic_opmode == IEEE80211_M_MONITOR) { 5132201209Srpaulo /* Link LED blinks while monitoring. */ 5133220674Sbschmidt iwn_set_led(sc, IWN_LED_LINK, 5, 5); 5134178676Ssam return 0; 5135178676Ssam } 5136220726Sbschmidt if ((error = iwn_set_timing(sc, ni)) != 0) { 5137198429Srpaulo device_printf(sc->sc_dev, 5138198429Srpaulo "%s: could not set timing, error %d\n", __func__, error); 5139198429Srpaulo return error; 5140198429Srpaulo } 5141178676Ssam 5142201209Srpaulo /* Update adapter configuration. */ 5143201209Srpaulo IEEE80211_ADDR_COPY(sc->rxon.bssid, ni->ni_bssid); 5144198429Srpaulo sc->rxon.associd = htole16(IEEE80211_AID(ni->ni_associd)); 5145220636Sbschmidt sc->rxon.chan = ieee80211_chan2ieee(ic, ni->ni_chan); 5146220636Sbschmidt sc->rxon.flags = htole32(IWN_RXON_TSF | IWN_RXON_CTS_TO_SELF); 5147201209Srpaulo if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) 5148201209Srpaulo sc->rxon.flags |= htole32(IWN_RXON_AUTO | IWN_RXON_24GHZ); 5149178676Ssam if (ic->ic_flags & IEEE80211_F_SHSLOT) 5150198429Srpaulo sc->rxon.flags |= htole32(IWN_RXON_SHSLOT); 5151178676Ssam if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) 5152198429Srpaulo sc->rxon.flags |= htole32(IWN_RXON_SHPREAMBLE); 5153201209Srpaulo if (IEEE80211_IS_CHAN_A(ni->ni_chan)) { 5154201209Srpaulo sc->rxon.cck_mask = 0; 5155201209Srpaulo sc->rxon.ofdm_mask = 0x15; 5156201209Srpaulo } else if (IEEE80211_IS_CHAN_B(ni->ni_chan)) { 5157201209Srpaulo sc->rxon.cck_mask = 0x03; 5158201209Srpaulo sc->rxon.ofdm_mask = 0; 5159201209Srpaulo } else { 5160220725Sbschmidt /* Assume 802.11b/g. */ 5161201209Srpaulo sc->rxon.cck_mask = 0x0f; 5162201209Srpaulo sc->rxon.ofdm_mask = 0x15; 5163201209Srpaulo } 5164201209Srpaulo#if 0 /* HT */ 5165178676Ssam if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) { 5166198429Srpaulo sc->rxon.flags &= ~htole32(IWN_RXON_HT); 5167178676Ssam if (IEEE80211_IS_CHAN_HT40U(ni->ni_chan)) 5168198429Srpaulo sc->rxon.flags |= htole32(IWN_RXON_HT40U); 5169178676Ssam else if (IEEE80211_IS_CHAN_HT40D(ni->ni_chan)) 5170198429Srpaulo sc->rxon.flags |= htole32(IWN_RXON_HT40D); 5171178676Ssam else 5172198429Srpaulo sc->rxon.flags |= htole32(IWN_RXON_HT20); 5173198429Srpaulo sc->rxon.rxchain = htole16( 5174198429Srpaulo IWN_RXCHAIN_VALID(3) 5175198429Srpaulo | IWN_RXCHAIN_MIMO_COUNT(3) 5176198429Srpaulo | IWN_RXCHAIN_IDLE_COUNT(1) 5177178676Ssam | IWN_RXCHAIN_MIMO_FORCE); 5178178676Ssam 5179178676Ssam maxrxampdu = MS(ni->ni_htparam, IEEE80211_HTCAP_MAXRXAMPDU); 5180178676Ssam ampdudensity = MS(ni->ni_htparam, IEEE80211_HTCAP_MPDUDENSITY); 5181178676Ssam } else 5182178676Ssam maxrxampdu = ampdudensity = 0; 5183201209Srpaulo#endif 5184198429Srpaulo sc->rxon.filter |= htole32(IWN_FILTER_BSS); 5185220724Sbschmidt DPRINTF(sc, IWN_DEBUG_STATE, "rxon chan %d flags %x\n", 5186220724Sbschmidt sc->rxon.chan, sc->rxon.flags); 5187220728Sbschmidt error = iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, sc->rxonsz, 1); 5188178676Ssam if (error != 0) { 5189178676Ssam device_printf(sc->sc_dev, 5190220726Sbschmidt "%s: could not update configuration, error %d\n", __func__, 5191220726Sbschmidt error); 5192178676Ssam return error; 5193178676Ssam } 5194178676Ssam 5195198429Srpaulo /* Configuration has changed, set TX power accordingly. */ 5196220728Sbschmidt if ((error = ops->set_txpower(sc, ni->ni_chan, 1)) != 0) { 5197178676Ssam device_printf(sc->sc_dev, 5198220724Sbschmidt "%s: could not set TX power, error %d\n", __func__, error); 5199178676Ssam return error; 5200178676Ssam } 5201178676Ssam 5202220715Sbschmidt /* Fake a join to initialize the TX rate. */ 5203220715Sbschmidt ((struct iwn_node *)ni)->id = IWN_ID_BSS; 5204220715Sbschmidt iwn_newassoc(ni, 1); 5205220715Sbschmidt 5206198429Srpaulo /* Add BSS node. */ 5207178676Ssam memset(&node, 0, sizeof node); 5208178676Ssam IEEE80211_ADDR_COPY(node.macaddr, ni->ni_macaddr); 5209178676Ssam node.id = IWN_ID_BSS; 5210201209Srpaulo#ifdef notyet 5211201209Srpaulo node.htflags = htole32(IWN_AMDPU_SIZE_FACTOR(3) | 5212201209Srpaulo IWN_AMDPU_DENSITY(5)); /* 2us */ 5213201209Srpaulo#endif 5214220724Sbschmidt DPRINTF(sc, IWN_DEBUG_STATE, "%s: adding BSS node\n", __func__); 5215220728Sbschmidt error = ops->add_node(sc, &node, 1); 5216178676Ssam if (error != 0) { 5217220724Sbschmidt device_printf(sc->sc_dev, 5218220724Sbschmidt "%s: could not add BSS node, error %d\n", __func__, error); 5219178676Ssam return error; 5220178676Ssam } 5221220724Sbschmidt DPRINTF(sc, IWN_DEBUG_STATE, "%s: setting link quality for node %d\n", 5222220724Sbschmidt __func__, node.id); 5223220726Sbschmidt if ((error = iwn_set_link_quality(sc, ni)) != 0) { 5224178676Ssam device_printf(sc->sc_dev, 5225220724Sbschmidt "%s: could not setup link quality for node %d, error %d\n", 5226178676Ssam __func__, node.id, error); 5227178676Ssam return error; 5228178676Ssam } 5229178676Ssam 5230220726Sbschmidt if ((error = iwn_init_sensitivity(sc)) != 0) { 5231178676Ssam device_printf(sc->sc_dev, 5232220726Sbschmidt "%s: could not set sensitivity, error %d\n", __func__, 5233220726Sbschmidt error); 5234178676Ssam return error; 5235178676Ssam } 5236198429Srpaulo /* Start periodic calibration timer. */ 5237178676Ssam sc->calib.state = IWN_CALIB_STATE_ASSOC; 5238220667Sbschmidt sc->calib_cnt = 0; 5239220667Sbschmidt callout_reset(&sc->calib_to, msecs_to_ticks(500), iwn_calib_timeout, 5240220667Sbschmidt sc); 5241178676Ssam 5242198429Srpaulo /* Link LED always on while associated. */ 5243178676Ssam iwn_set_led(sc, IWN_LED_LINK, 0, 1); 5244178676Ssam return 0; 5245178676Ssam#undef MS 5246178676Ssam} 5247178676Ssam 5248201209Srpaulo#if 0 /* HT */ 5249178676Ssam/* 5250201209Srpaulo * This function is called by upper layer when an ADDBA request is received 5251201209Srpaulo * from another STA and before the ADDBA response is sent. 5252201209Srpaulo */ 5253206477Sbschmidtstatic int 5254201209Srpauloiwn_ampdu_rx_start(struct ieee80211com *ic, struct ieee80211_node *ni, 5255201209Srpaulo uint8_t tid) 5256201209Srpaulo{ 5257201209Srpaulo struct ieee80211_rx_ba *ba = &ni->ni_rx_ba[tid]; 5258201209Srpaulo struct iwn_softc *sc = ic->ic_softc; 5259220728Sbschmidt struct iwn_ops *ops = &sc->ops; 5260201209Srpaulo struct iwn_node *wn = (void *)ni; 5261201209Srpaulo struct iwn_node_info node; 5262201209Srpaulo 5263201209Srpaulo memset(&node, 0, sizeof node); 5264201209Srpaulo node.id = wn->id; 5265201209Srpaulo node.control = IWN_NODE_UPDATE; 5266201209Srpaulo node.flags = IWN_FLAG_SET_ADDBA; 5267201209Srpaulo node.addba_tid = tid; 5268201209Srpaulo node.addba_ssn = htole16(ba->ba_winstart); 5269201209Srpaulo DPRINTF(sc, IWN_DEBUG_RECV, "ADDBA RA=%d TID=%d SSN=%d\n", 5270220724Sbschmidt wn->id, tid, ba->ba_winstart); 5271220728Sbschmidt return ops->add_node(sc, &node, 1); 5272201209Srpaulo} 5273201209Srpaulo 5274201209Srpaulo/* 5275201209Srpaulo * This function is called by upper layer on teardown of an HT-immediate 5276220725Sbschmidt * Block Ack agreement (eg. uppon receipt of a DELBA frame). 5277201209Srpaulo */ 5278206477Sbschmidtstatic void 5279201209Srpauloiwn_ampdu_rx_stop(struct ieee80211com *ic, struct ieee80211_node *ni, 5280201209Srpaulo uint8_t tid) 5281201209Srpaulo{ 5282201209Srpaulo struct iwn_softc *sc = ic->ic_softc; 5283220728Sbschmidt struct iwn_ops *ops = &sc->ops; 5284201209Srpaulo struct iwn_node *wn = (void *)ni; 5285201209Srpaulo struct iwn_node_info node; 5286201209Srpaulo 5287201209Srpaulo memset(&node, 0, sizeof node); 5288201209Srpaulo node.id = wn->id; 5289201209Srpaulo node.control = IWN_NODE_UPDATE; 5290201209Srpaulo node.flags = IWN_FLAG_SET_DELBA; 5291201209Srpaulo node.delba_tid = tid; 5292201209Srpaulo DPRINTF(sc, IWN_DEBUG_RECV, "DELBA RA=%d TID=%d\n", wn->id, tid); 5293220728Sbschmidt (void)ops->add_node(sc, &node, 1); 5294201209Srpaulo} 5295201209Srpaulo 5296201209Srpaulo/* 5297201209Srpaulo * This function is called by upper layer when an ADDBA response is received 5298201209Srpaulo * from another STA. 5299201209Srpaulo */ 5300206477Sbschmidtstatic int 5301201209Srpauloiwn_ampdu_tx_start(struct ieee80211com *ic, struct ieee80211_node *ni, 5302201209Srpaulo uint8_t tid) 5303201209Srpaulo{ 5304201209Srpaulo struct ieee80211_tx_ba *ba = &ni->ni_tx_ba[tid]; 5305201209Srpaulo struct iwn_softc *sc = ic->ic_softc; 5306220728Sbschmidt struct iwn_ops *ops = &sc->ops; 5307201209Srpaulo struct iwn_node *wn = (void *)ni; 5308201209Srpaulo struct iwn_node_info node; 5309201209Srpaulo int error; 5310201209Srpaulo 5311201209Srpaulo /* Enable TX for the specified RA/TID. */ 5312201209Srpaulo wn->disable_tid &= ~(1 << tid); 5313201209Srpaulo memset(&node, 0, sizeof node); 5314201209Srpaulo node.id = wn->id; 5315201209Srpaulo node.control = IWN_NODE_UPDATE; 5316201209Srpaulo node.flags = IWN_FLAG_SET_DISABLE_TID; 5317201209Srpaulo node.disable_tid = htole16(wn->disable_tid); 5318220728Sbschmidt error = ops->add_node(sc, &node, 1); 5319201209Srpaulo if (error != 0) 5320201209Srpaulo return error; 5321201209Srpaulo 5322201209Srpaulo if ((error = iwn_nic_lock(sc)) != 0) 5323201209Srpaulo return error; 5324220728Sbschmidt ops->ampdu_tx_start(sc, ni, tid, ba->ba_winstart); 5325201209Srpaulo iwn_nic_unlock(sc); 5326201209Srpaulo return 0; 5327201209Srpaulo} 5328201209Srpaulo 5329206477Sbschmidtstatic void 5330201209Srpauloiwn_ampdu_tx_stop(struct ieee80211com *ic, struct ieee80211_node *ni, 5331201209Srpaulo uint8_t tid) 5332201209Srpaulo{ 5333201209Srpaulo struct ieee80211_tx_ba *ba = &ni->ni_tx_ba[tid]; 5334201209Srpaulo struct iwn_softc *sc = ic->ic_softc; 5335220728Sbschmidt struct iwn_ops *ops = &sc->ops; 5336201209Srpaulo 5337220726Sbschmidt if (iwn_nic_lock(sc) != 0) 5338201209Srpaulo return; 5339220728Sbschmidt ops->ampdu_tx_stop(sc, tid, ba->ba_winstart); 5340201209Srpaulo iwn_nic_unlock(sc); 5341201209Srpaulo} 5342201209Srpaulo 5343206477Sbschmidtstatic void 5344201209Srpauloiwn4965_ampdu_tx_start(struct iwn_softc *sc, struct ieee80211_node *ni, 5345201209Srpaulo uint8_t tid, uint16_t ssn) 5346201209Srpaulo{ 5347201209Srpaulo struct iwn_node *wn = (void *)ni; 5348201209Srpaulo int qid = 7 + tid; 5349201209Srpaulo 5350201209Srpaulo /* Stop TX scheduler while we're changing its configuration. */ 5351201209Srpaulo iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid), 5352201209Srpaulo IWN4965_TXQ_STATUS_CHGACT); 5353201209Srpaulo 5354201209Srpaulo /* Assign RA/TID translation to the queue. */ 5355201209Srpaulo iwn_mem_write_2(sc, sc->sched_base + IWN4965_SCHED_TRANS_TBL(qid), 5356201209Srpaulo wn->id << 4 | tid); 5357201209Srpaulo 5358201209Srpaulo /* Enable chain-building mode for the queue. */ 5359201209Srpaulo iwn_prph_setbits(sc, IWN4965_SCHED_QCHAIN_SEL, 1 << qid); 5360201209Srpaulo 5361201209Srpaulo /* Set starting sequence number from the ADDBA request. */ 5362201209Srpaulo IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff)); 5363201209Srpaulo iwn_prph_write(sc, IWN4965_SCHED_QUEUE_RDPTR(qid), ssn); 5364201209Srpaulo 5365201209Srpaulo /* Set scheduler window size. */ 5366201209Srpaulo iwn_mem_write(sc, sc->sched_base + IWN4965_SCHED_QUEUE_OFFSET(qid), 5367201209Srpaulo IWN_SCHED_WINSZ); 5368201209Srpaulo /* Set scheduler frame limit. */ 5369201209Srpaulo iwn_mem_write(sc, sc->sched_base + IWN4965_SCHED_QUEUE_OFFSET(qid) + 4, 5370201209Srpaulo IWN_SCHED_LIMIT << 16); 5371201209Srpaulo 5372201209Srpaulo /* Enable interrupts for the queue. */ 5373201209Srpaulo iwn_prph_setbits(sc, IWN4965_SCHED_INTR_MASK, 1 << qid); 5374201209Srpaulo 5375201209Srpaulo /* Mark the queue as active. */ 5376201209Srpaulo iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid), 5377201209Srpaulo IWN4965_TXQ_STATUS_ACTIVE | IWN4965_TXQ_STATUS_AGGR_ENA | 5378201209Srpaulo iwn_tid2fifo[tid] << 1); 5379201209Srpaulo} 5380201209Srpaulo 5381206477Sbschmidtstatic void 5382201209Srpauloiwn4965_ampdu_tx_stop(struct iwn_softc *sc, uint8_t tid, uint16_t ssn) 5383201209Srpaulo{ 5384201209Srpaulo int qid = 7 + tid; 5385201209Srpaulo 5386201209Srpaulo /* Stop TX scheduler while we're changing its configuration. */ 5387201209Srpaulo iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid), 5388201209Srpaulo IWN4965_TXQ_STATUS_CHGACT); 5389201209Srpaulo 5390201209Srpaulo /* Set starting sequence number from the ADDBA request. */ 5391201209Srpaulo IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff)); 5392201209Srpaulo iwn_prph_write(sc, IWN4965_SCHED_QUEUE_RDPTR(qid), ssn); 5393201209Srpaulo 5394201209Srpaulo /* Disable interrupts for the queue. */ 5395201209Srpaulo iwn_prph_clrbits(sc, IWN4965_SCHED_INTR_MASK, 1 << qid); 5396201209Srpaulo 5397201209Srpaulo /* Mark the queue as inactive. */ 5398201209Srpaulo iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid), 5399201209Srpaulo IWN4965_TXQ_STATUS_INACTIVE | iwn_tid2fifo[tid] << 1); 5400201209Srpaulo} 5401201209Srpaulo 5402206477Sbschmidtstatic void 5403201209Srpauloiwn5000_ampdu_tx_start(struct iwn_softc *sc, struct ieee80211_node *ni, 5404201209Srpaulo uint8_t tid, uint16_t ssn) 5405201209Srpaulo{ 5406201209Srpaulo struct iwn_node *wn = (void *)ni; 5407201209Srpaulo int qid = 10 + tid; 5408201209Srpaulo 5409201209Srpaulo /* Stop TX scheduler while we're changing its configuration. */ 5410201209Srpaulo iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid), 5411201209Srpaulo IWN5000_TXQ_STATUS_CHGACT); 5412201209Srpaulo 5413201209Srpaulo /* Assign RA/TID translation to the queue. */ 5414201209Srpaulo iwn_mem_write_2(sc, sc->sched_base + IWN5000_SCHED_TRANS_TBL(qid), 5415201209Srpaulo wn->id << 4 | tid); 5416201209Srpaulo 5417201209Srpaulo /* Enable chain-building mode for the queue. */ 5418201209Srpaulo iwn_prph_setbits(sc, IWN5000_SCHED_QCHAIN_SEL, 1 << qid); 5419201209Srpaulo 5420201209Srpaulo /* Enable aggregation for the queue. */ 5421201209Srpaulo iwn_prph_setbits(sc, IWN5000_SCHED_AGGR_SEL, 1 << qid); 5422201209Srpaulo 5423201209Srpaulo /* Set starting sequence number from the ADDBA request. */ 5424201209Srpaulo IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff)); 5425201209Srpaulo iwn_prph_write(sc, IWN5000_SCHED_QUEUE_RDPTR(qid), ssn); 5426201209Srpaulo 5427201209Srpaulo /* Set scheduler window size and frame limit. */ 5428201209Srpaulo iwn_mem_write(sc, sc->sched_base + IWN5000_SCHED_QUEUE_OFFSET(qid) + 4, 5429201209Srpaulo IWN_SCHED_LIMIT << 16 | IWN_SCHED_WINSZ); 5430201209Srpaulo 5431201209Srpaulo /* Enable interrupts for the queue. */ 5432201209Srpaulo iwn_prph_setbits(sc, IWN5000_SCHED_INTR_MASK, 1 << qid); 5433201209Srpaulo 5434201209Srpaulo /* Mark the queue as active. */ 5435201209Srpaulo iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid), 5436201209Srpaulo IWN5000_TXQ_STATUS_ACTIVE | iwn_tid2fifo[tid]); 5437201209Srpaulo} 5438201209Srpaulo 5439206477Sbschmidtstatic void 5440201209Srpauloiwn5000_ampdu_tx_stop(struct iwn_softc *sc, uint8_t tid, uint16_t ssn) 5441201209Srpaulo{ 5442201209Srpaulo int qid = 10 + tid; 5443201209Srpaulo 5444201209Srpaulo /* Stop TX scheduler while we're changing its configuration. */ 5445201209Srpaulo iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid), 5446201209Srpaulo IWN5000_TXQ_STATUS_CHGACT); 5447201209Srpaulo 5448201209Srpaulo /* Disable aggregation for the queue. */ 5449201209Srpaulo iwn_prph_clrbits(sc, IWN5000_SCHED_AGGR_SEL, 1 << qid); 5450201209Srpaulo 5451201209Srpaulo /* Set starting sequence number from the ADDBA request. */ 5452201209Srpaulo IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff)); 5453201209Srpaulo iwn_prph_write(sc, IWN5000_SCHED_QUEUE_RDPTR(qid), ssn); 5454201209Srpaulo 5455201209Srpaulo /* Disable interrupts for the queue. */ 5456201209Srpaulo iwn_prph_clrbits(sc, IWN5000_SCHED_INTR_MASK, 1 << qid); 5457201209Srpaulo 5458201209Srpaulo /* Mark the queue as inactive. */ 5459201209Srpaulo iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid), 5460201209Srpaulo IWN5000_TXQ_STATUS_INACTIVE | iwn_tid2fifo[tid]); 5461201209Srpaulo} 5462201209Srpaulo#endif 5463201209Srpaulo 5464201209Srpaulo/* 5465220674Sbschmidt * Query calibration tables from the initialization firmware. We do this 5466220674Sbschmidt * only once at first boot. Called from a process context. 5467212853Sbschmidt */ 5468212853Sbschmidtstatic int 5469220674Sbschmidtiwn5000_query_calibration(struct iwn_softc *sc) 5470212853Sbschmidt{ 5471198429Srpaulo struct iwn5000_calib_config cmd; 5472198429Srpaulo int error; 5473178676Ssam 5474198429Srpaulo memset(&cmd, 0, sizeof cmd); 5475220674Sbschmidt cmd.ucode.once.enable = 0xffffffff; 5476220674Sbschmidt cmd.ucode.once.start = 0xffffffff; 5477220674Sbschmidt cmd.ucode.once.send = 0xffffffff; 5478220674Sbschmidt cmd.ucode.flags = 0xffffffff; 5479220674Sbschmidt DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: sending calibration query\n", 5480220674Sbschmidt __func__); 5481198429Srpaulo error = iwn_cmd(sc, IWN5000_CMD_CALIB_CONFIG, &cmd, sizeof cmd, 0); 5482198429Srpaulo if (error != 0) 5483198429Srpaulo return error; 5484178676Ssam 5485198429Srpaulo /* Wait at most two seconds for calibration to complete. */ 5486201209Srpaulo if (!(sc->sc_flags & IWN_FLAG_CALIB_DONE)) 5487220674Sbschmidt error = msleep(sc, &sc->sc_mtx, PCATCH, "iwncal", 2 * hz); 5488201209Srpaulo return error; 5489198429Srpaulo} 5490198429Srpaulo 5491198429Srpaulo/* 5492220674Sbschmidt * Send calibration results to the runtime firmware. These results were 5493220674Sbschmidt * obtained on first boot from the initialization firmware. 5494198429Srpaulo */ 5495212854Sbschmidtstatic int 5496220674Sbschmidtiwn5000_send_calibration(struct iwn_softc *sc) 5497198429Srpaulo{ 5498220674Sbschmidt int idx, error; 5499198429Srpaulo 5500220674Sbschmidt for (idx = 0; idx < 5; idx++) { 5501220674Sbschmidt if (sc->calibcmd[idx].buf == NULL) 5502220674Sbschmidt continue; /* No results available. */ 5503198429Srpaulo DPRINTF(sc, IWN_DEBUG_CALIBRATE, 5504220674Sbschmidt "send calibration result idx=%d len=%d\n", idx, 5505220674Sbschmidt sc->calibcmd[idx].len); 5506220674Sbschmidt error = iwn_cmd(sc, IWN_CMD_PHY_CALIB, sc->calibcmd[idx].buf, 5507220674Sbschmidt sc->calibcmd[idx].len, 0); 5508220674Sbschmidt if (error != 0) { 5509220674Sbschmidt device_printf(sc->sc_dev, 5510220674Sbschmidt "%s: could not send calibration result, error %d\n", 5511220674Sbschmidt __func__, error); 5512220674Sbschmidt return error; 5513220674Sbschmidt } 5514178676Ssam } 5515220674Sbschmidt return 0; 5516198429Srpaulo} 5517178676Ssam 5518206477Sbschmidtstatic int 5519201209Srpauloiwn5000_send_wimax_coex(struct iwn_softc *sc) 5520201209Srpaulo{ 5521201209Srpaulo struct iwn5000_wimax_coex wimax; 5522201209Srpaulo 5523201209Srpaulo#ifdef notyet 5524201209Srpaulo if (sc->hw_type == IWN_HW_REV_TYPE_6050) { 5525201209Srpaulo /* Enable WiMAX coexistence for combo adapters. */ 5526201209Srpaulo wimax.flags = 5527201209Srpaulo IWN_WIMAX_COEX_ASSOC_WA_UNMASK | 5528201209Srpaulo IWN_WIMAX_COEX_UNASSOC_WA_UNMASK | 5529201209Srpaulo IWN_WIMAX_COEX_STA_TABLE_VALID | 5530201209Srpaulo IWN_WIMAX_COEX_ENABLE; 5531201209Srpaulo memcpy(wimax.events, iwn6050_wimax_events, 5532201209Srpaulo sizeof iwn6050_wimax_events); 5533201209Srpaulo } else 5534201209Srpaulo#endif 5535201209Srpaulo { 5536201209Srpaulo /* Disable WiMAX coexistence. */ 5537201209Srpaulo wimax.flags = 0; 5538201209Srpaulo memset(wimax.events, 0, sizeof wimax.events); 5539201209Srpaulo } 5540201209Srpaulo DPRINTF(sc, IWN_DEBUG_RESET, "%s: Configuring WiMAX coexistence\n", 5541201209Srpaulo __func__); 5542201209Srpaulo return iwn_cmd(sc, IWN5000_CMD_WIMAX_COEX, &wimax, sizeof wimax, 0); 5543201209Srpaulo} 5544201209Srpaulo 5545220674Sbschmidtstatic int 5546220674Sbschmidtiwn5000_crystal_calib(struct iwn_softc *sc) 5547220674Sbschmidt{ 5548220674Sbschmidt struct iwn5000_phy_calib_crystal cmd; 5549220674Sbschmidt 5550220674Sbschmidt memset(&cmd, 0, sizeof cmd); 5551220674Sbschmidt cmd.code = IWN5000_PHY_CALIB_CRYSTAL; 5552220674Sbschmidt cmd.ngroups = 1; 5553220674Sbschmidt cmd.isvalid = 1; 5554220674Sbschmidt cmd.cap_pin[0] = le32toh(sc->eeprom_crystal) & 0xff; 5555220674Sbschmidt cmd.cap_pin[1] = (le32toh(sc->eeprom_crystal) >> 16) & 0xff; 5556220674Sbschmidt DPRINTF(sc, IWN_DEBUG_CALIBRATE, "sending crystal calibration %d, %d\n", 5557220674Sbschmidt cmd.cap_pin[0], cmd.cap_pin[1]); 5558220674Sbschmidt return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 0); 5559220674Sbschmidt} 5560220674Sbschmidt 5561220676Sbschmidtstatic int 5562220676Sbschmidtiwn5000_temp_offset_calib(struct iwn_softc *sc) 5563220676Sbschmidt{ 5564220676Sbschmidt struct iwn5000_phy_calib_temp_offset cmd; 5565220676Sbschmidt 5566220676Sbschmidt memset(&cmd, 0, sizeof cmd); 5567220676Sbschmidt cmd.code = IWN5000_PHY_CALIB_TEMP_OFFSET; 5568220676Sbschmidt cmd.ngroups = 1; 5569220676Sbschmidt cmd.isvalid = 1; 5570220676Sbschmidt if (sc->eeprom_temp != 0) 5571220676Sbschmidt cmd.offset = htole16(sc->eeprom_temp); 5572220676Sbschmidt else 5573220676Sbschmidt cmd.offset = htole16(IWN_DEFAULT_TEMP_OFFSET); 5574220676Sbschmidt DPRINTF(sc, IWN_DEBUG_CALIBRATE, "setting radio sensor offset to %d\n", 5575220676Sbschmidt le16toh(cmd.offset)); 5576220676Sbschmidt return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 0); 5577220676Sbschmidt} 5578220676Sbschmidt 5579198429Srpaulo/* 5580198429Srpaulo * This function is called after the runtime firmware notifies us of its 5581220725Sbschmidt * readiness (called in a process context). 5582198429Srpaulo */ 5583206477Sbschmidtstatic int 5584198429Srpauloiwn4965_post_alive(struct iwn_softc *sc) 5585198429Srpaulo{ 5586198429Srpaulo int error, qid; 5587178676Ssam 5588198429Srpaulo if ((error = iwn_nic_lock(sc)) != 0) 5589198429Srpaulo return error; 5590178676Ssam 5591201209Srpaulo /* Clear TX scheduler state in SRAM. */ 5592198429Srpaulo sc->sched_base = iwn_prph_read(sc, IWN_SCHED_SRAM_ADDR); 5593198429Srpaulo iwn_mem_set_region_4(sc, sc->sched_base + IWN4965_SCHED_CTX_OFF, 0, 5594201209Srpaulo IWN4965_SCHED_CTX_LEN / sizeof (uint32_t)); 5595178676Ssam 5596220725Sbschmidt /* Set physical address of TX scheduler rings (1KB aligned). */ 5597198429Srpaulo iwn_prph_write(sc, IWN4965_SCHED_DRAM_ADDR, sc->sched_dma.paddr >> 10); 5598178676Ssam 5599198429Srpaulo IWN_SETBITS(sc, IWN_FH_TX_CHICKEN, IWN_FH_TX_CHICKEN_SCHED_RETRY); 5600178676Ssam 5601198429Srpaulo /* Disable chain mode for all our 16 queues. */ 5602198429Srpaulo iwn_prph_write(sc, IWN4965_SCHED_QCHAIN_SEL, 0); 5603198429Srpaulo 5604198429Srpaulo for (qid = 0; qid < IWN4965_NTXQUEUES; qid++) { 5605198429Srpaulo iwn_prph_write(sc, IWN4965_SCHED_QUEUE_RDPTR(qid), 0); 5606198429Srpaulo IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | 0); 5607198429Srpaulo 5608198429Srpaulo /* Set scheduler window size. */ 5609198429Srpaulo iwn_mem_write(sc, sc->sched_base + 5610198429Srpaulo IWN4965_SCHED_QUEUE_OFFSET(qid), IWN_SCHED_WINSZ); 5611198429Srpaulo /* Set scheduler frame limit. */ 5612198429Srpaulo iwn_mem_write(sc, sc->sched_base + 5613198429Srpaulo IWN4965_SCHED_QUEUE_OFFSET(qid) + 4, 5614198429Srpaulo IWN_SCHED_LIMIT << 16); 5615178676Ssam } 5616178676Ssam 5617198429Srpaulo /* Enable interrupts for all our 16 queues. */ 5618198429Srpaulo iwn_prph_write(sc, IWN4965_SCHED_INTR_MASK, 0xffff); 5619198429Srpaulo /* Identify TX FIFO rings (0-7). */ 5620198429Srpaulo iwn_prph_write(sc, IWN4965_SCHED_TXFACT, 0xff); 5621178676Ssam 5622198429Srpaulo /* Mark TX rings (4 EDCA + cmd + 2 HCCA) as active. */ 5623198429Srpaulo for (qid = 0; qid < 7; qid++) { 5624198429Srpaulo static uint8_t qid2fifo[] = { 3, 2, 1, 0, 4, 5, 6 }; 5625198429Srpaulo iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid), 5626198429Srpaulo IWN4965_TXQ_STATUS_ACTIVE | qid2fifo[qid] << 1); 5627198429Srpaulo } 5628198429Srpaulo iwn_nic_unlock(sc); 5629198429Srpaulo return 0; 5630198429Srpaulo} 5631178676Ssam 5632198429Srpaulo/* 5633198429Srpaulo * This function is called after the initialization or runtime firmware 5634220725Sbschmidt * notifies us of its readiness (called in a process context). 5635198429Srpaulo */ 5636206477Sbschmidtstatic int 5637198429Srpauloiwn5000_post_alive(struct iwn_softc *sc) 5638198429Srpaulo{ 5639198429Srpaulo int error, qid; 5640178676Ssam 5641201209Srpaulo /* Switch to using ICT interrupt mode. */ 5642201209Srpaulo iwn5000_ict_reset(sc); 5643201209Srpaulo 5644220726Sbschmidt if ((error = iwn_nic_lock(sc)) != 0) 5645198429Srpaulo return error; 5646178676Ssam 5647201209Srpaulo /* Clear TX scheduler state in SRAM. */ 5648198429Srpaulo sc->sched_base = iwn_prph_read(sc, IWN_SCHED_SRAM_ADDR); 5649198429Srpaulo iwn_mem_set_region_4(sc, sc->sched_base + IWN5000_SCHED_CTX_OFF, 0, 5650201209Srpaulo IWN5000_SCHED_CTX_LEN / sizeof (uint32_t)); 5651178676Ssam 5652220725Sbschmidt /* Set physical address of TX scheduler rings (1KB aligned). */ 5653198429Srpaulo iwn_prph_write(sc, IWN5000_SCHED_DRAM_ADDR, sc->sched_dma.paddr >> 10); 5654178676Ssam 5655198429Srpaulo IWN_SETBITS(sc, IWN_FH_TX_CHICKEN, IWN_FH_TX_CHICKEN_SCHED_RETRY); 5656178676Ssam 5657201209Srpaulo /* Enable chain mode for all queues, except command queue. */ 5658201209Srpaulo iwn_prph_write(sc, IWN5000_SCHED_QCHAIN_SEL, 0xfffef); 5659198429Srpaulo iwn_prph_write(sc, IWN5000_SCHED_AGGR_SEL, 0); 5660178676Ssam 5661198429Srpaulo for (qid = 0; qid < IWN5000_NTXQUEUES; qid++) { 5662198429Srpaulo iwn_prph_write(sc, IWN5000_SCHED_QUEUE_RDPTR(qid), 0); 5663198429Srpaulo IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | 0); 5664198429Srpaulo 5665198429Srpaulo iwn_mem_write(sc, sc->sched_base + 5666198429Srpaulo IWN5000_SCHED_QUEUE_OFFSET(qid), 0); 5667198429Srpaulo /* Set scheduler window size and frame limit. */ 5668198429Srpaulo iwn_mem_write(sc, sc->sched_base + 5669198429Srpaulo IWN5000_SCHED_QUEUE_OFFSET(qid) + 4, 5670198429Srpaulo IWN_SCHED_LIMIT << 16 | IWN_SCHED_WINSZ); 5671178676Ssam } 5672178676Ssam 5673198429Srpaulo /* Enable interrupts for all our 20 queues. */ 5674198429Srpaulo iwn_prph_write(sc, IWN5000_SCHED_INTR_MASK, 0xfffff); 5675198429Srpaulo /* Identify TX FIFO rings (0-7). */ 5676198429Srpaulo iwn_prph_write(sc, IWN5000_SCHED_TXFACT, 0xff); 5677178676Ssam 5678198429Srpaulo /* Mark TX rings (4 EDCA + cmd + 2 HCCA) as active. */ 5679198429Srpaulo for (qid = 0; qid < 7; qid++) { 5680198429Srpaulo static uint8_t qid2fifo[] = { 3, 2, 1, 0, 7, 5, 6 }; 5681198429Srpaulo iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid), 5682198429Srpaulo IWN5000_TXQ_STATUS_ACTIVE | qid2fifo[qid]); 5683198429Srpaulo } 5684198429Srpaulo iwn_nic_unlock(sc); 5685178676Ssam 5686201209Srpaulo /* Configure WiMAX coexistence for combo adapters. */ 5687201209Srpaulo error = iwn5000_send_wimax_coex(sc); 5688178676Ssam if (error != 0) { 5689178676Ssam device_printf(sc->sc_dev, 5690198429Srpaulo "%s: could not configure WiMAX coexistence, error %d\n", 5691178676Ssam __func__, error); 5692178676Ssam return error; 5693178676Ssam } 5694220674Sbschmidt if (sc->hw_type != IWN_HW_REV_TYPE_5150) { 5695220674Sbschmidt /* Perform crystal calibration. */ 5696220674Sbschmidt error = iwn5000_crystal_calib(sc); 5697198429Srpaulo if (error != 0) { 5698198429Srpaulo device_printf(sc->sc_dev, 5699220674Sbschmidt "%s: crystal calibration failed, error %d\n", 5700220674Sbschmidt __func__, error); 5701198429Srpaulo return error; 5702198429Srpaulo } 5703220674Sbschmidt } 5704220674Sbschmidt if (!(sc->sc_flags & IWN_FLAG_CALIB_DONE)) { 5705220674Sbschmidt /* Query calibration from the initialization firmware. */ 5706220674Sbschmidt if ((error = iwn5000_query_calibration(sc)) != 0) { 5707198429Srpaulo device_printf(sc->sc_dev, 5708220674Sbschmidt "%s: could not query calibration, error %d\n", 5709198429Srpaulo __func__, error); 5710198429Srpaulo return error; 5711198429Srpaulo } 5712198429Srpaulo /* 5713201209Srpaulo * We have the calibration results now, reboot with the 5714201209Srpaulo * runtime firmware (call ourselves recursively!) 5715198429Srpaulo */ 5716198429Srpaulo iwn_hw_stop(sc); 5717198429Srpaulo error = iwn_hw_init(sc); 5718198429Srpaulo } else { 5719220674Sbschmidt /* Send calibration results to runtime firmware. */ 5720220674Sbschmidt error = iwn5000_send_calibration(sc); 5721198429Srpaulo } 5722198429Srpaulo return error; 5723198429Srpaulo} 5724178676Ssam 5725198429Srpaulo/* 5726198429Srpaulo * The firmware boot code is small and is intended to be copied directly into 5727220725Sbschmidt * the NIC internal memory (no DMA transfer). 5728198429Srpaulo */ 5729206477Sbschmidtstatic int 5730198429Srpauloiwn4965_load_bootcode(struct iwn_softc *sc, const uint8_t *ucode, int size) 5731198429Srpaulo{ 5732198429Srpaulo int error, ntries; 5733198429Srpaulo 5734198429Srpaulo size /= sizeof (uint32_t); 5735198429Srpaulo 5736220726Sbschmidt if ((error = iwn_nic_lock(sc)) != 0) 5737198429Srpaulo return error; 5738198429Srpaulo 5739198429Srpaulo /* Copy microcode image into NIC memory. */ 5740198429Srpaulo iwn_prph_write_region_4(sc, IWN_BSM_SRAM_BASE, 5741198429Srpaulo (const uint32_t *)ucode, size); 5742198429Srpaulo 5743198429Srpaulo iwn_prph_write(sc, IWN_BSM_WR_MEM_SRC, 0); 5744198429Srpaulo iwn_prph_write(sc, IWN_BSM_WR_MEM_DST, IWN_FW_TEXT_BASE); 5745198429Srpaulo iwn_prph_write(sc, IWN_BSM_WR_DWCOUNT, size); 5746198429Srpaulo 5747198429Srpaulo /* Start boot load now. */ 5748198429Srpaulo iwn_prph_write(sc, IWN_BSM_WR_CTRL, IWN_BSM_WR_CTRL_START); 5749198429Srpaulo 5750198429Srpaulo /* Wait for transfer to complete. */ 5751198429Srpaulo for (ntries = 0; ntries < 1000; ntries++) { 5752198429Srpaulo if (!(iwn_prph_read(sc, IWN_BSM_WR_CTRL) & 5753198429Srpaulo IWN_BSM_WR_CTRL_START)) 5754198429Srpaulo break; 5755198429Srpaulo DELAY(10); 5756198429Srpaulo } 5757198429Srpaulo if (ntries == 1000) { 5758198429Srpaulo device_printf(sc->sc_dev, "%s: could not load boot firmware\n", 5759198429Srpaulo __func__); 5760198429Srpaulo iwn_nic_unlock(sc); 5761198429Srpaulo return ETIMEDOUT; 5762198429Srpaulo } 5763198429Srpaulo 5764198429Srpaulo /* Enable boot after power up. */ 5765198429Srpaulo iwn_prph_write(sc, IWN_BSM_WR_CTRL, IWN_BSM_WR_CTRL_START_EN); 5766198429Srpaulo 5767198429Srpaulo iwn_nic_unlock(sc); 5768198429Srpaulo return 0; 5769178676Ssam} 5770178676Ssam 5771206477Sbschmidtstatic int 5772198429Srpauloiwn4965_load_firmware(struct iwn_softc *sc) 5773178676Ssam{ 5774198429Srpaulo struct iwn_fw_info *fw = &sc->fw; 5775198429Srpaulo struct iwn_dma_info *dma = &sc->fw_dma; 5776178676Ssam int error; 5777178676Ssam 5778198429Srpaulo /* Copy initialization sections into pre-allocated DMA-safe memory. */ 5779198429Srpaulo memcpy(dma->vaddr, fw->init.data, fw->init.datasz); 5780220661Sbschmidt bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); 5781198429Srpaulo memcpy(dma->vaddr + IWN4965_FW_DATA_MAXSZ, 5782198429Srpaulo fw->init.text, fw->init.textsz); 5783220661Sbschmidt bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); 5784198429Srpaulo 5785198429Srpaulo /* Tell adapter where to find initialization sections. */ 5786220726Sbschmidt if ((error = iwn_nic_lock(sc)) != 0) 5787198429Srpaulo return error; 5788198429Srpaulo iwn_prph_write(sc, IWN_BSM_DRAM_DATA_ADDR, dma->paddr >> 4); 5789198429Srpaulo iwn_prph_write(sc, IWN_BSM_DRAM_DATA_SIZE, fw->init.datasz); 5790198429Srpaulo iwn_prph_write(sc, IWN_BSM_DRAM_TEXT_ADDR, 5791198429Srpaulo (dma->paddr + IWN4965_FW_DATA_MAXSZ) >> 4); 5792198429Srpaulo iwn_prph_write(sc, IWN_BSM_DRAM_TEXT_SIZE, fw->init.textsz); 5793198429Srpaulo iwn_nic_unlock(sc); 5794198429Srpaulo 5795198429Srpaulo /* Load firmware boot code. */ 5796198429Srpaulo error = iwn4965_load_bootcode(sc, fw->boot.text, fw->boot.textsz); 5797178676Ssam if (error != 0) { 5798198429Srpaulo device_printf(sc->sc_dev, "%s: could not load boot firmware\n", 5799198429Srpaulo __func__); 5800178676Ssam return error; 5801178676Ssam } 5802198429Srpaulo /* Now press "execute". */ 5803198429Srpaulo IWN_WRITE(sc, IWN_RESET, 0); 5804178676Ssam 5805198429Srpaulo /* Wait at most one second for first alive notification. */ 5806220726Sbschmidt if ((error = msleep(sc, &sc->sc_mtx, PCATCH, "iwninit", hz)) != 0) { 5807178676Ssam device_printf(sc->sc_dev, 5808198429Srpaulo "%s: timeout waiting for adapter to initialize, error %d\n", 5809178676Ssam __func__, error); 5810178676Ssam return error; 5811178676Ssam } 5812178676Ssam 5813198429Srpaulo /* Retrieve current temperature for initial TX power calibration. */ 5814198429Srpaulo sc->rawtemp = sc->ucode_info.temp[3].chan20MHz; 5815198429Srpaulo sc->temp = iwn4965_get_temperature(sc); 5816178676Ssam 5817198429Srpaulo /* Copy runtime sections into pre-allocated DMA-safe memory. */ 5818198429Srpaulo memcpy(dma->vaddr, fw->main.data, fw->main.datasz); 5819220661Sbschmidt bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); 5820198429Srpaulo memcpy(dma->vaddr + IWN4965_FW_DATA_MAXSZ, 5821198429Srpaulo fw->main.text, fw->main.textsz); 5822220661Sbschmidt bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); 5823198429Srpaulo 5824198429Srpaulo /* Tell adapter where to find runtime sections. */ 5825220726Sbschmidt if ((error = iwn_nic_lock(sc)) != 0) 5826198429Srpaulo return error; 5827198429Srpaulo iwn_prph_write(sc, IWN_BSM_DRAM_DATA_ADDR, dma->paddr >> 4); 5828198429Srpaulo iwn_prph_write(sc, IWN_BSM_DRAM_DATA_SIZE, fw->main.datasz); 5829198429Srpaulo iwn_prph_write(sc, IWN_BSM_DRAM_TEXT_ADDR, 5830198429Srpaulo (dma->paddr + IWN4965_FW_DATA_MAXSZ) >> 4); 5831198429Srpaulo iwn_prph_write(sc, IWN_BSM_DRAM_TEXT_SIZE, 5832198429Srpaulo IWN_FW_UPDATED | fw->main.textsz); 5833198429Srpaulo iwn_nic_unlock(sc); 5834198429Srpaulo 5835198429Srpaulo return 0; 5836198429Srpaulo} 5837198429Srpaulo 5838206477Sbschmidtstatic int 5839198429Srpauloiwn5000_load_firmware_section(struct iwn_softc *sc, uint32_t dst, 5840198429Srpaulo const uint8_t *section, int size) 5841198429Srpaulo{ 5842198429Srpaulo struct iwn_dma_info *dma = &sc->fw_dma; 5843198429Srpaulo int error; 5844198429Srpaulo 5845198429Srpaulo /* Copy firmware section into pre-allocated DMA-safe memory. */ 5846198429Srpaulo memcpy(dma->vaddr, section, size); 5847220661Sbschmidt bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); 5848198429Srpaulo 5849220726Sbschmidt if ((error = iwn_nic_lock(sc)) != 0) 5850198429Srpaulo return error; 5851198429Srpaulo 5852198429Srpaulo IWN_WRITE(sc, IWN_FH_TX_CONFIG(IWN_SRVC_DMACHNL), 5853198429Srpaulo IWN_FH_TX_CONFIG_DMA_PAUSE); 5854198429Srpaulo 5855198429Srpaulo IWN_WRITE(sc, IWN_FH_SRAM_ADDR(IWN_SRVC_DMACHNL), dst); 5856198429Srpaulo IWN_WRITE(sc, IWN_FH_TFBD_CTRL0(IWN_SRVC_DMACHNL), 5857198429Srpaulo IWN_LOADDR(dma->paddr)); 5858198429Srpaulo IWN_WRITE(sc, IWN_FH_TFBD_CTRL1(IWN_SRVC_DMACHNL), 5859198429Srpaulo IWN_HIADDR(dma->paddr) << 28 | size); 5860198429Srpaulo IWN_WRITE(sc, IWN_FH_TXBUF_STATUS(IWN_SRVC_DMACHNL), 5861198429Srpaulo IWN_FH_TXBUF_STATUS_TBNUM(1) | 5862198429Srpaulo IWN_FH_TXBUF_STATUS_TBIDX(1) | 5863198429Srpaulo IWN_FH_TXBUF_STATUS_TFBD_VALID); 5864198429Srpaulo 5865198429Srpaulo /* Kick Flow Handler to start DMA transfer. */ 5866198429Srpaulo IWN_WRITE(sc, IWN_FH_TX_CONFIG(IWN_SRVC_DMACHNL), 5867198429Srpaulo IWN_FH_TX_CONFIG_DMA_ENA | IWN_FH_TX_CONFIG_CIRQ_HOST_ENDTFD); 5868198429Srpaulo 5869198429Srpaulo iwn_nic_unlock(sc); 5870198429Srpaulo 5871198429Srpaulo /* Wait at most five seconds for FH DMA transfer to complete. */ 5872220661Sbschmidt return msleep(sc, &sc->sc_mtx, PCATCH, "iwninit", 5 * hz); 5873198429Srpaulo} 5874198429Srpaulo 5875206477Sbschmidtstatic int 5876198429Srpauloiwn5000_load_firmware(struct iwn_softc *sc) 5877198429Srpaulo{ 5878198429Srpaulo struct iwn_fw_part *fw; 5879198429Srpaulo int error; 5880198429Srpaulo 5881198429Srpaulo /* Load the initialization firmware on first boot only. */ 5882201209Srpaulo fw = (sc->sc_flags & IWN_FLAG_CALIB_DONE) ? 5883201209Srpaulo &sc->fw.main : &sc->fw.init; 5884198429Srpaulo 5885198429Srpaulo error = iwn5000_load_firmware_section(sc, IWN_FW_TEXT_BASE, 5886198429Srpaulo fw->text, fw->textsz); 5887178676Ssam if (error != 0) { 5888178676Ssam device_printf(sc->sc_dev, 5889198429Srpaulo "%s: could not load firmware %s section, error %d\n", 5890198429Srpaulo __func__, ".text", error); 5891178676Ssam return error; 5892178676Ssam } 5893198429Srpaulo error = iwn5000_load_firmware_section(sc, IWN_FW_DATA_BASE, 5894198429Srpaulo fw->data, fw->datasz); 5895178676Ssam if (error != 0) { 5896178676Ssam device_printf(sc->sc_dev, 5897198429Srpaulo "%s: could not load firmware %s section, error %d\n", 5898198429Srpaulo __func__, ".data", error); 5899178676Ssam return error; 5900178676Ssam } 5901178676Ssam 5902198429Srpaulo /* Now press "execute". */ 5903198429Srpaulo IWN_WRITE(sc, IWN_RESET, 0); 5904198429Srpaulo return 0; 5905198429Srpaulo} 5906198429Srpaulo 5907210111Sbschmidt/* 5908210111Sbschmidt * Extract text and data sections from a legacy firmware image. 5909210111Sbschmidt */ 5910206477Sbschmidtstatic int 5911210111Sbschmidtiwn_read_firmware_leg(struct iwn_softc *sc, struct iwn_fw_info *fw) 5912198429Srpaulo{ 5913201209Srpaulo const uint32_t *ptr; 5914210111Sbschmidt size_t hdrlen = 24; 5915201209Srpaulo uint32_t rev; 5916198429Srpaulo 5917220661Sbschmidt ptr = (const uint32_t *)fw->data; 5918201209Srpaulo rev = le32toh(*ptr++); 5919210111Sbschmidt 5920201209Srpaulo /* Check firmware API version. */ 5921201209Srpaulo if (IWN_FW_API(rev) <= 1) { 5922201209Srpaulo device_printf(sc->sc_dev, 5923201209Srpaulo "%s: bad firmware, need API version >=2\n", __func__); 5924201209Srpaulo return EINVAL; 5925201209Srpaulo } 5926201209Srpaulo if (IWN_FW_API(rev) >= 3) { 5927201209Srpaulo /* Skip build number (version 2 header). */ 5928210111Sbschmidt hdrlen += 4; 5929201209Srpaulo ptr++; 5930201209Srpaulo } 5931210111Sbschmidt if (fw->size < hdrlen) { 5932220724Sbschmidt device_printf(sc->sc_dev, "%s: firmware too short: %zu bytes\n", 5933210111Sbschmidt __func__, fw->size); 5934210111Sbschmidt return EINVAL; 5935210111Sbschmidt } 5936201209Srpaulo fw->main.textsz = le32toh(*ptr++); 5937201209Srpaulo fw->main.datasz = le32toh(*ptr++); 5938201209Srpaulo fw->init.textsz = le32toh(*ptr++); 5939201209Srpaulo fw->init.datasz = le32toh(*ptr++); 5940201209Srpaulo fw->boot.textsz = le32toh(*ptr++); 5941198429Srpaulo 5942198429Srpaulo /* Check that all firmware sections fit. */ 5943210111Sbschmidt if (fw->size < hdrlen + fw->main.textsz + fw->main.datasz + 5944210111Sbschmidt fw->init.textsz + fw->init.datasz + fw->boot.textsz) { 5945220724Sbschmidt device_printf(sc->sc_dev, "%s: firmware too short: %zu bytes\n", 5946210111Sbschmidt __func__, fw->size); 5947198429Srpaulo return EINVAL; 5948178676Ssam } 5949198429Srpaulo 5950198429Srpaulo /* Get pointers to firmware sections. */ 5951201209Srpaulo fw->main.text = (const uint8_t *)ptr; 5952198429Srpaulo fw->main.data = fw->main.text + fw->main.textsz; 5953198429Srpaulo fw->init.text = fw->main.data + fw->main.datasz; 5954198429Srpaulo fw->init.data = fw->init.text + fw->init.textsz; 5955198429Srpaulo fw->boot.text = fw->init.data + fw->init.datasz; 5956178676Ssam return 0; 5957178676Ssam} 5958178676Ssam 5959210111Sbschmidt/* 5960210111Sbschmidt * Extract text and data sections from a TLV firmware image. 5961210111Sbschmidt */ 5962220661Sbschmidtstatic int 5963210111Sbschmidtiwn_read_firmware_tlv(struct iwn_softc *sc, struct iwn_fw_info *fw, 5964210111Sbschmidt uint16_t alt) 5965210111Sbschmidt{ 5966210111Sbschmidt const struct iwn_fw_tlv_hdr *hdr; 5967210111Sbschmidt const struct iwn_fw_tlv *tlv; 5968210111Sbschmidt const uint8_t *ptr, *end; 5969210111Sbschmidt uint64_t altmask; 5970220866Sbschmidt uint32_t len, tmp; 5971210111Sbschmidt 5972210111Sbschmidt if (fw->size < sizeof (*hdr)) { 5973220724Sbschmidt device_printf(sc->sc_dev, "%s: firmware too short: %zu bytes\n", 5974210111Sbschmidt __func__, fw->size); 5975210111Sbschmidt return EINVAL; 5976210111Sbschmidt } 5977210111Sbschmidt hdr = (const struct iwn_fw_tlv_hdr *)fw->data; 5978210111Sbschmidt if (hdr->signature != htole32(IWN_FW_SIGNATURE)) { 5979220724Sbschmidt device_printf(sc->sc_dev, "%s: bad firmware signature 0x%08x\n", 5980210111Sbschmidt __func__, le32toh(hdr->signature)); 5981210111Sbschmidt return EINVAL; 5982210111Sbschmidt } 5983220724Sbschmidt DPRINTF(sc, IWN_DEBUG_RESET, "FW: \"%.64s\", build 0x%x\n", hdr->descr, 5984220724Sbschmidt le32toh(hdr->build)); 5985210111Sbschmidt 5986210111Sbschmidt /* 5987210111Sbschmidt * Select the closest supported alternative that is less than 5988210111Sbschmidt * or equal to the specified one. 5989210111Sbschmidt */ 5990210111Sbschmidt altmask = le64toh(hdr->altmask); 5991210111Sbschmidt while (alt > 0 && !(altmask & (1ULL << alt))) 5992210111Sbschmidt alt--; /* Downgrade. */ 5993220724Sbschmidt DPRINTF(sc, IWN_DEBUG_RESET, "using alternative %d\n", alt); 5994210111Sbschmidt 5995210111Sbschmidt ptr = (const uint8_t *)(hdr + 1); 5996210111Sbschmidt end = (const uint8_t *)(fw->data + fw->size); 5997210111Sbschmidt 5998210111Sbschmidt /* Parse type-length-value fields. */ 5999210111Sbschmidt while (ptr + sizeof (*tlv) <= end) { 6000210111Sbschmidt tlv = (const struct iwn_fw_tlv *)ptr; 6001210111Sbschmidt len = le32toh(tlv->len); 6002210111Sbschmidt 6003210111Sbschmidt ptr += sizeof (*tlv); 6004210111Sbschmidt if (ptr + len > end) { 6005210111Sbschmidt device_printf(sc->sc_dev, 6006220724Sbschmidt "%s: firmware too short: %zu bytes\n", __func__, 6007220724Sbschmidt fw->size); 6008210111Sbschmidt return EINVAL; 6009210111Sbschmidt } 6010210111Sbschmidt /* Skip other alternatives. */ 6011210111Sbschmidt if (tlv->alt != 0 && tlv->alt != htole16(alt)) 6012210111Sbschmidt goto next; 6013210111Sbschmidt 6014210111Sbschmidt switch (le16toh(tlv->type)) { 6015210111Sbschmidt case IWN_FW_TLV_MAIN_TEXT: 6016210111Sbschmidt fw->main.text = ptr; 6017210111Sbschmidt fw->main.textsz = len; 6018210111Sbschmidt break; 6019210111Sbschmidt case IWN_FW_TLV_MAIN_DATA: 6020210111Sbschmidt fw->main.data = ptr; 6021210111Sbschmidt fw->main.datasz = len; 6022210111Sbschmidt break; 6023210111Sbschmidt case IWN_FW_TLV_INIT_TEXT: 6024210111Sbschmidt fw->init.text = ptr; 6025210111Sbschmidt fw->init.textsz = len; 6026210111Sbschmidt break; 6027210111Sbschmidt case IWN_FW_TLV_INIT_DATA: 6028210111Sbschmidt fw->init.data = ptr; 6029210111Sbschmidt fw->init.datasz = len; 6030210111Sbschmidt break; 6031210111Sbschmidt case IWN_FW_TLV_BOOT_TEXT: 6032210111Sbschmidt fw->boot.text = ptr; 6033210111Sbschmidt fw->boot.textsz = len; 6034210111Sbschmidt break; 6035220866Sbschmidt case IWN_FW_TLV_ENH_SENS: 6036220866Sbschmidt if (!len) 6037220866Sbschmidt sc->sc_flags |= IWN_FLAG_ENH_SENS; 6038220866Sbschmidt break; 6039220866Sbschmidt case IWN_FW_TLV_PHY_CALIB: 6040220866Sbschmidt tmp = htole32(*ptr); 6041220866Sbschmidt if (tmp < 253) { 6042220866Sbschmidt sc->reset_noise_gain = tmp; 6043220866Sbschmidt sc->noise_gain = tmp + 1; 6044220866Sbschmidt } 6045220866Sbschmidt break; 6046210111Sbschmidt default: 6047210111Sbschmidt DPRINTF(sc, IWN_DEBUG_RESET, 6048220724Sbschmidt "TLV type %d not handled\n", le16toh(tlv->type)); 6049210111Sbschmidt break; 6050210111Sbschmidt } 6051220726Sbschmidt next: /* TLV fields are 32-bit aligned. */ 6052210111Sbschmidt ptr += (len + 3) & ~3; 6053210111Sbschmidt } 6054210111Sbschmidt return 0; 6055210111Sbschmidt} 6056210111Sbschmidt 6057206477Sbschmidtstatic int 6058210111Sbschmidtiwn_read_firmware(struct iwn_softc *sc) 6059210111Sbschmidt{ 6060210111Sbschmidt struct iwn_fw_info *fw = &sc->fw; 6061210111Sbschmidt int error; 6062210111Sbschmidt 6063210111Sbschmidt IWN_UNLOCK(sc); 6064210111Sbschmidt 6065210111Sbschmidt memset(fw, 0, sizeof (*fw)); 6066210111Sbschmidt 6067210111Sbschmidt /* Read firmware image from filesystem. */ 6068210111Sbschmidt sc->fw_fp = firmware_get(sc->fwname); 6069210111Sbschmidt if (sc->fw_fp == NULL) { 6070220724Sbschmidt device_printf(sc->sc_dev, "%s: could not read firmware %s\n", 6071220724Sbschmidt __func__, sc->fwname); 6072210111Sbschmidt IWN_LOCK(sc); 6073210111Sbschmidt return EINVAL; 6074210111Sbschmidt } 6075210111Sbschmidt IWN_LOCK(sc); 6076210111Sbschmidt 6077210111Sbschmidt fw->size = sc->fw_fp->datasize; 6078210111Sbschmidt fw->data = (const uint8_t *)sc->fw_fp->data; 6079210111Sbschmidt if (fw->size < sizeof (uint32_t)) { 6080220724Sbschmidt device_printf(sc->sc_dev, "%s: firmware too short: %zu bytes\n", 6081210111Sbschmidt __func__, fw->size); 6082220661Sbschmidt firmware_put(sc->fw_fp, FIRMWARE_UNLOAD); 6083220661Sbschmidt sc->fw_fp = NULL; 6084210111Sbschmidt return EINVAL; 6085210111Sbschmidt } 6086210111Sbschmidt 6087210111Sbschmidt /* Retrieve text and data sections. */ 6088210111Sbschmidt if (*(const uint32_t *)fw->data != 0) /* Legacy image. */ 6089210111Sbschmidt error = iwn_read_firmware_leg(sc, fw); 6090210111Sbschmidt else 6091210111Sbschmidt error = iwn_read_firmware_tlv(sc, fw, 1); 6092210111Sbschmidt if (error != 0) { 6093210111Sbschmidt device_printf(sc->sc_dev, 6094220724Sbschmidt "%s: could not read firmware sections, error %d\n", 6095220724Sbschmidt __func__, error); 6096220661Sbschmidt firmware_put(sc->fw_fp, FIRMWARE_UNLOAD); 6097220661Sbschmidt sc->fw_fp = NULL; 6098210111Sbschmidt return error; 6099210111Sbschmidt } 6100210111Sbschmidt 6101210111Sbschmidt /* Make sure text and data sections fit in hardware memory. */ 6102220728Sbschmidt if (fw->main.textsz > sc->fw_text_maxsz || 6103220728Sbschmidt fw->main.datasz > sc->fw_data_maxsz || 6104220728Sbschmidt fw->init.textsz > sc->fw_text_maxsz || 6105220728Sbschmidt fw->init.datasz > sc->fw_data_maxsz || 6106210111Sbschmidt fw->boot.textsz > IWN_FW_BOOT_TEXT_MAXSZ || 6107210111Sbschmidt (fw->boot.textsz & 3) != 0) { 6108220724Sbschmidt device_printf(sc->sc_dev, "%s: firmware sections too large\n", 6109220724Sbschmidt __func__); 6110220661Sbschmidt firmware_put(sc->fw_fp, FIRMWARE_UNLOAD); 6111220661Sbschmidt sc->fw_fp = NULL; 6112210111Sbschmidt return EINVAL; 6113210111Sbschmidt } 6114210111Sbschmidt 6115210111Sbschmidt /* We can proceed with loading the firmware. */ 6116210111Sbschmidt return 0; 6117210111Sbschmidt} 6118210111Sbschmidt 6119210111Sbschmidtstatic int 6120198429Srpauloiwn_clock_wait(struct iwn_softc *sc) 6121198429Srpaulo{ 6122198429Srpaulo int ntries; 6123178676Ssam 6124198429Srpaulo /* Set "initialization complete" bit. */ 6125198429Srpaulo IWN_SETBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_INIT_DONE); 6126198429Srpaulo 6127198429Srpaulo /* Wait for clock stabilization. */ 6128201209Srpaulo for (ntries = 0; ntries < 2500; ntries++) { 6129198429Srpaulo if (IWN_READ(sc, IWN_GP_CNTRL) & IWN_GP_CNTRL_MAC_CLOCK_READY) 6130198429Srpaulo return 0; 6131201209Srpaulo DELAY(10); 6132178676Ssam } 6133198429Srpaulo device_printf(sc->sc_dev, 6134198429Srpaulo "%s: timeout waiting for clock stabilization\n", __func__); 6135198429Srpaulo return ETIMEDOUT; 6136198429Srpaulo} 6137178676Ssam 6138206477Sbschmidtstatic int 6139201209Srpauloiwn_apm_init(struct iwn_softc *sc) 6140198429Srpaulo{ 6141220721Sbschmidt uint32_t reg; 6142198429Srpaulo int error; 6143178676Ssam 6144220725Sbschmidt /* Disable L0s exit timer (NMI bug workaround). */ 6145198429Srpaulo IWN_SETBITS(sc, IWN_GIO_CHICKEN, IWN_GIO_CHICKEN_DIS_L0S_TIMER); 6146220725Sbschmidt /* Don't wait for ICH L0s (ICH bug workaround). */ 6147198429Srpaulo IWN_SETBITS(sc, IWN_GIO_CHICKEN, IWN_GIO_CHICKEN_L1A_NO_L0S_RX); 6148178676Ssam 6149220725Sbschmidt /* Set FH wait threshold to max (HW bug under stress workaround). */ 6150198429Srpaulo IWN_SETBITS(sc, IWN_DBG_HPET_MEM, 0xffff0000); 6151198429Srpaulo 6152201209Srpaulo /* Enable HAP INTA to move adapter from L1a to L0s. */ 6153198429Srpaulo IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_HAP_WAKE_L1A); 6154198429Srpaulo 6155201209Srpaulo /* Retrieve PCIe Active State Power Management (ASPM). */ 6156220721Sbschmidt reg = pci_read_config(sc->sc_dev, sc->sc_cap_off + 0x10, 1); 6157201209Srpaulo /* Workaround for HW instability in PCIe L0->L0s->L1 transition. */ 6158220721Sbschmidt if (reg & 0x02) /* L1 Entry enabled. */ 6159201209Srpaulo IWN_SETBITS(sc, IWN_GIO, IWN_GIO_L0S_ENA); 6160201209Srpaulo else 6161201209Srpaulo IWN_CLRBITS(sc, IWN_GIO, IWN_GIO_L0S_ENA); 6162201209Srpaulo 6163201209Srpaulo if (sc->hw_type != IWN_HW_REV_TYPE_4965 && 6164210109Sbschmidt sc->hw_type <= IWN_HW_REV_TYPE_1000) 6165198429Srpaulo IWN_SETBITS(sc, IWN_ANA_PLL, IWN_ANA_PLL_INIT); 6166198429Srpaulo 6167201209Srpaulo /* Wait for clock stabilization before accessing prph. */ 6168220726Sbschmidt if ((error = iwn_clock_wait(sc)) != 0) 6169198429Srpaulo return error; 6170198429Srpaulo 6171220726Sbschmidt if ((error = iwn_nic_lock(sc)) != 0) 6172198429Srpaulo return error; 6173201209Srpaulo if (sc->hw_type == IWN_HW_REV_TYPE_4965) { 6174220725Sbschmidt /* Enable DMA and BSM (Bootstrap State Machine). */ 6175201209Srpaulo iwn_prph_write(sc, IWN_APMG_CLK_EN, 6176201209Srpaulo IWN_APMG_CLK_CTRL_DMA_CLK_RQT | 6177201209Srpaulo IWN_APMG_CLK_CTRL_BSM_CLK_RQT); 6178201209Srpaulo } else { 6179201209Srpaulo /* Enable DMA. */ 6180201209Srpaulo iwn_prph_write(sc, IWN_APMG_CLK_EN, 6181201209Srpaulo IWN_APMG_CLK_CTRL_DMA_CLK_RQT); 6182201209Srpaulo } 6183198429Srpaulo DELAY(20); 6184201209Srpaulo /* Disable L1-Active. */ 6185198429Srpaulo iwn_prph_setbits(sc, IWN_APMG_PCI_STT, IWN_APMG_PCI_STT_L1A_DIS); 6186198429Srpaulo iwn_nic_unlock(sc); 6187198429Srpaulo 6188198429Srpaulo return 0; 6189198429Srpaulo} 6190198429Srpaulo 6191206477Sbschmidtstatic void 6192198429Srpauloiwn_apm_stop_master(struct iwn_softc *sc) 6193178676Ssam{ 6194178676Ssam int ntries; 6195178676Ssam 6196201209Srpaulo /* Stop busmaster DMA activity. */ 6197198429Srpaulo IWN_SETBITS(sc, IWN_RESET, IWN_RESET_STOP_MASTER); 6198178676Ssam for (ntries = 0; ntries < 100; ntries++) { 6199198429Srpaulo if (IWN_READ(sc, IWN_RESET) & IWN_RESET_MASTER_DISABLED) 6200198429Srpaulo return; 6201178676Ssam DELAY(10); 6202178676Ssam } 6203220726Sbschmidt device_printf(sc->sc_dev, "%s: timeout waiting for master\n", __func__); 6204178676Ssam} 6205178676Ssam 6206206477Sbschmidtstatic void 6207198429Srpauloiwn_apm_stop(struct iwn_softc *sc) 6208198429Srpaulo{ 6209198429Srpaulo iwn_apm_stop_master(sc); 6210198429Srpaulo 6211201209Srpaulo /* Reset the entire device. */ 6212198429Srpaulo IWN_SETBITS(sc, IWN_RESET, IWN_RESET_SW); 6213198429Srpaulo DELAY(10); 6214198429Srpaulo /* Clear "initialization complete" bit. */ 6215198429Srpaulo IWN_CLRBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_INIT_DONE); 6216198429Srpaulo} 6217198429Srpaulo 6218206477Sbschmidtstatic int 6219198429Srpauloiwn4965_nic_config(struct iwn_softc *sc) 6220178676Ssam{ 6221198429Srpaulo if (IWN_RFCFG_TYPE(sc->rfcfg) == 1) { 6222198429Srpaulo /* 6223198429Srpaulo * I don't believe this to be correct but this is what the 6224198429Srpaulo * vendor driver is doing. Probably the bits should not be 6225198429Srpaulo * shifted in IWN_RFCFG_*. 6226198429Srpaulo */ 6227198429Srpaulo IWN_SETBITS(sc, IWN_HW_IF_CONFIG, 6228198429Srpaulo IWN_RFCFG_TYPE(sc->rfcfg) | 6229198429Srpaulo IWN_RFCFG_STEP(sc->rfcfg) | 6230198429Srpaulo IWN_RFCFG_DASH(sc->rfcfg)); 6231198429Srpaulo } 6232198429Srpaulo IWN_SETBITS(sc, IWN_HW_IF_CONFIG, 6233198429Srpaulo IWN_HW_IF_CONFIG_RADIO_SI | IWN_HW_IF_CONFIG_MAC_SI); 6234198429Srpaulo return 0; 6235198429Srpaulo} 6236178676Ssam 6237206477Sbschmidtstatic int 6238198429Srpauloiwn5000_nic_config(struct iwn_softc *sc) 6239198429Srpaulo{ 6240198429Srpaulo uint32_t tmp; 6241198429Srpaulo int error; 6242178676Ssam 6243198429Srpaulo if (IWN_RFCFG_TYPE(sc->rfcfg) < 3) { 6244198429Srpaulo IWN_SETBITS(sc, IWN_HW_IF_CONFIG, 6245198429Srpaulo IWN_RFCFG_TYPE(sc->rfcfg) | 6246198429Srpaulo IWN_RFCFG_STEP(sc->rfcfg) | 6247198429Srpaulo IWN_RFCFG_DASH(sc->rfcfg)); 6248198429Srpaulo } 6249198429Srpaulo IWN_SETBITS(sc, IWN_HW_IF_CONFIG, 6250198429Srpaulo IWN_HW_IF_CONFIG_RADIO_SI | IWN_HW_IF_CONFIG_MAC_SI); 6251198429Srpaulo 6252220726Sbschmidt if ((error = iwn_nic_lock(sc)) != 0) 6253198429Srpaulo return error; 6254198429Srpaulo iwn_prph_setbits(sc, IWN_APMG_PS, IWN_APMG_PS_EARLY_PWROFF_DIS); 6255201209Srpaulo 6256201209Srpaulo if (sc->hw_type == IWN_HW_REV_TYPE_1000) { 6257201209Srpaulo /* 6258201209Srpaulo * Select first Switching Voltage Regulator (1.32V) to 6259201209Srpaulo * solve a stability issue related to noisy DC2DC line 6260201209Srpaulo * in the silicon of 1000 Series. 6261201209Srpaulo */ 6262201209Srpaulo tmp = iwn_prph_read(sc, IWN_APMG_DIGITAL_SVR); 6263201209Srpaulo tmp &= ~IWN_APMG_DIGITAL_SVR_VOLTAGE_MASK; 6264201209Srpaulo tmp |= IWN_APMG_DIGITAL_SVR_VOLTAGE_1_32; 6265201209Srpaulo iwn_prph_write(sc, IWN_APMG_DIGITAL_SVR, tmp); 6266201209Srpaulo } 6267198429Srpaulo iwn_nic_unlock(sc); 6268201209Srpaulo 6269201209Srpaulo if (sc->sc_flags & IWN_FLAG_INTERNAL_PA) { 6270201209Srpaulo /* Use internal power amplifier only. */ 6271201209Srpaulo IWN_WRITE(sc, IWN_GP_DRIVER, IWN_GP_DRIVER_RADIO_2X2_IPA); 6272201209Srpaulo } 6273220676Sbschmidt if ((sc->hw_type == IWN_HW_REV_TYPE_6050 || 6274220676Sbschmidt sc->hw_type == IWN_HW_REV_TYPE_6005) && sc->calib_ver >= 6) { 6275210108Sbschmidt /* Indicate that ROM calibration version is >=6. */ 6276210108Sbschmidt IWN_SETBITS(sc, IWN_GP_DRIVER, IWN_GP_DRIVER_CALIB_VER6); 6277206444Sbschmidt } 6278220729Sbschmidt if (sc->hw_type == IWN_HW_REV_TYPE_6005) 6279220729Sbschmidt IWN_SETBITS(sc, IWN_GP_DRIVER, IWN_GP_DRIVER_6050_1X2); 6280198429Srpaulo return 0; 6281198429Srpaulo} 6282198429Srpaulo 6283198429Srpaulo/* 6284198429Srpaulo * Take NIC ownership over Intel Active Management Technology (AMT). 6285198429Srpaulo */ 6286206477Sbschmidtstatic int 6287198429Srpauloiwn_hw_prepare(struct iwn_softc *sc) 6288198429Srpaulo{ 6289198429Srpaulo int ntries; 6290198429Srpaulo 6291201209Srpaulo /* Check if hardware is ready. */ 6292201209Srpaulo IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_NIC_READY); 6293201209Srpaulo for (ntries = 0; ntries < 5; ntries++) { 6294201209Srpaulo if (IWN_READ(sc, IWN_HW_IF_CONFIG) & 6295201209Srpaulo IWN_HW_IF_CONFIG_NIC_READY) 6296201209Srpaulo return 0; 6297201209Srpaulo DELAY(10); 6298201209Srpaulo } 6299201209Srpaulo 6300201209Srpaulo /* Hardware not ready, force into ready state. */ 6301198429Srpaulo IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_PREPARE); 6302198429Srpaulo for (ntries = 0; ntries < 15000; ntries++) { 6303198429Srpaulo if (!(IWN_READ(sc, IWN_HW_IF_CONFIG) & 6304198429Srpaulo IWN_HW_IF_CONFIG_PREPARE_DONE)) 6305178676Ssam break; 6306178676Ssam DELAY(10); 6307178676Ssam } 6308198429Srpaulo if (ntries == 15000) 6309178676Ssam return ETIMEDOUT; 6310198429Srpaulo 6311201209Srpaulo /* Hardware should be ready now. */ 6312198429Srpaulo IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_NIC_READY); 6313198429Srpaulo for (ntries = 0; ntries < 5; ntries++) { 6314198429Srpaulo if (IWN_READ(sc, IWN_HW_IF_CONFIG) & 6315198429Srpaulo IWN_HW_IF_CONFIG_NIC_READY) 6316198429Srpaulo return 0; 6317198429Srpaulo DELAY(10); 6318178676Ssam } 6319198429Srpaulo return ETIMEDOUT; 6320178676Ssam} 6321178676Ssam 6322206477Sbschmidtstatic int 6323198429Srpauloiwn_hw_init(struct iwn_softc *sc) 6324178676Ssam{ 6325220728Sbschmidt struct iwn_ops *ops = &sc->ops; 6326198429Srpaulo int error, chnl, qid; 6327178676Ssam 6328198429Srpaulo /* Clear pending interrupts. */ 6329198429Srpaulo IWN_WRITE(sc, IWN_INT, 0xffffffff); 6330178676Ssam 6331220726Sbschmidt if ((error = iwn_apm_init(sc)) != 0) { 6332198429Srpaulo device_printf(sc->sc_dev, 6333220726Sbschmidt "%s: could not power ON adapter, error %d\n", __func__, 6334220726Sbschmidt error); 6335198429Srpaulo return error; 6336178676Ssam } 6337178676Ssam 6338198429Srpaulo /* Select VMAIN power source. */ 6339220726Sbschmidt if ((error = iwn_nic_lock(sc)) != 0) 6340198429Srpaulo return error; 6341198429Srpaulo iwn_prph_clrbits(sc, IWN_APMG_PS, IWN_APMG_PS_PWR_SRC_MASK); 6342198429Srpaulo iwn_nic_unlock(sc); 6343178676Ssam 6344198429Srpaulo /* Perform adapter-specific initialization. */ 6345220728Sbschmidt if ((error = ops->nic_config(sc)) != 0) 6346198429Srpaulo return error; 6347178676Ssam 6348198429Srpaulo /* Initialize RX ring. */ 6349220726Sbschmidt if ((error = iwn_nic_lock(sc)) != 0) 6350198429Srpaulo return error; 6351198429Srpaulo IWN_WRITE(sc, IWN_FH_RX_CONFIG, 0); 6352198429Srpaulo IWN_WRITE(sc, IWN_FH_RX_WPTR, 0); 6353220725Sbschmidt /* Set physical address of RX ring (256-byte aligned). */ 6354198429Srpaulo IWN_WRITE(sc, IWN_FH_RX_BASE, sc->rxq.desc_dma.paddr >> 8); 6355220725Sbschmidt /* Set physical address of RX status (16-byte aligned). */ 6356198429Srpaulo IWN_WRITE(sc, IWN_FH_STATUS_WPTR, sc->rxq.stat_dma.paddr >> 4); 6357198429Srpaulo /* Enable RX. */ 6358198429Srpaulo IWN_WRITE(sc, IWN_FH_RX_CONFIG, 6359198429Srpaulo IWN_FH_RX_CONFIG_ENA | 6360198429Srpaulo IWN_FH_RX_CONFIG_IGN_RXF_EMPTY | /* HW bug workaround */ 6361198429Srpaulo IWN_FH_RX_CONFIG_IRQ_DST_HOST | 6362198429Srpaulo IWN_FH_RX_CONFIG_SINGLE_FRAME | 6363198429Srpaulo IWN_FH_RX_CONFIG_RB_TIMEOUT(0) | 6364198429Srpaulo IWN_FH_RX_CONFIG_NRBD(IWN_RX_RING_COUNT_LOG)); 6365198429Srpaulo iwn_nic_unlock(sc); 6366198429Srpaulo IWN_WRITE(sc, IWN_FH_RX_WPTR, (IWN_RX_RING_COUNT - 1) & ~7); 6367178676Ssam 6368220726Sbschmidt if ((error = iwn_nic_lock(sc)) != 0) 6369198429Srpaulo return error; 6370178676Ssam 6371198429Srpaulo /* Initialize TX scheduler. */ 6372220728Sbschmidt iwn_prph_write(sc, sc->sched_txfact_addr, 0); 6373178676Ssam 6374220725Sbschmidt /* Set physical address of "keep warm" page (16-byte aligned). */ 6375198429Srpaulo IWN_WRITE(sc, IWN_FH_KW_ADDR, sc->kw_dma.paddr >> 4); 6376198429Srpaulo 6377198429Srpaulo /* Initialize TX rings. */ 6378220728Sbschmidt for (qid = 0; qid < sc->ntxqs; qid++) { 6379198429Srpaulo struct iwn_tx_ring *txq = &sc->txq[qid]; 6380198429Srpaulo 6381220725Sbschmidt /* Set physical address of TX ring (256-byte aligned). */ 6382198429Srpaulo IWN_WRITE(sc, IWN_FH_CBBC_QUEUE(qid), 6383198429Srpaulo txq->desc_dma.paddr >> 8); 6384178676Ssam } 6385198429Srpaulo iwn_nic_unlock(sc); 6386178676Ssam 6387198429Srpaulo /* Enable DMA channels. */ 6388220728Sbschmidt for (chnl = 0; chnl < sc->ndmachnls; chnl++) { 6389198429Srpaulo IWN_WRITE(sc, IWN_FH_TX_CONFIG(chnl), 6390198429Srpaulo IWN_FH_TX_CONFIG_DMA_ENA | 6391198429Srpaulo IWN_FH_TX_CONFIG_DMA_CREDIT_ENA); 6392198429Srpaulo } 6393198429Srpaulo 6394198429Srpaulo /* Clear "radio off" and "commands blocked" bits. */ 6395198429Srpaulo IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_RFKILL); 6396198429Srpaulo IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_CMD_BLOCKED); 6397198429Srpaulo 6398198429Srpaulo /* Clear pending interrupts. */ 6399198429Srpaulo IWN_WRITE(sc, IWN_INT, 0xffffffff); 6400198429Srpaulo /* Enable interrupt coalescing. */ 6401198429Srpaulo IWN_WRITE(sc, IWN_INT_COALESCING, 512 / 8); 6402198429Srpaulo /* Enable interrupts. */ 6403201209Srpaulo IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask); 6404198429Srpaulo 6405198429Srpaulo /* _Really_ make sure "radio off" bit is cleared! */ 6406198429Srpaulo IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_RFKILL); 6407198429Srpaulo IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_RFKILL); 6408198429Srpaulo 6409220729Sbschmidt /* Enable shadow registers. */ 6410220729Sbschmidt if (sc->hw_type >= IWN_HW_REV_TYPE_6000) 6411220729Sbschmidt IWN_SETBITS(sc, IWN_SHADOW_REG_CTRL, 0x800fffff); 6412220729Sbschmidt 6413220728Sbschmidt if ((error = ops->load_firmware(sc)) != 0) { 6414178676Ssam device_printf(sc->sc_dev, 6415220726Sbschmidt "%s: could not load firmware, error %d\n", __func__, 6416220726Sbschmidt error); 6417198429Srpaulo return error; 6418178676Ssam } 6419198429Srpaulo /* Wait at most one second for firmware alive notification. */ 6420220726Sbschmidt if ((error = msleep(sc, &sc->sc_mtx, PCATCH, "iwninit", hz)) != 0) { 6421198429Srpaulo device_printf(sc->sc_dev, 6422198429Srpaulo "%s: timeout waiting for adapter to initialize, error %d\n", 6423198429Srpaulo __func__, error); 6424198429Srpaulo return error; 6425198429Srpaulo } 6426198429Srpaulo /* Do post-firmware initialization. */ 6427220728Sbschmidt return ops->post_alive(sc); 6428198429Srpaulo} 6429178676Ssam 6430206477Sbschmidtstatic void 6431198429Srpauloiwn_hw_stop(struct iwn_softc *sc) 6432198429Srpaulo{ 6433198429Srpaulo int chnl, qid, ntries; 6434178676Ssam 6435198429Srpaulo IWN_WRITE(sc, IWN_RESET, IWN_RESET_NEVO); 6436178676Ssam 6437198429Srpaulo /* Disable interrupts. */ 6438201209Srpaulo IWN_WRITE(sc, IWN_INT_MASK, 0); 6439198429Srpaulo IWN_WRITE(sc, IWN_INT, 0xffffffff); 6440198429Srpaulo IWN_WRITE(sc, IWN_FH_INT, 0xffffffff); 6441201209Srpaulo sc->sc_flags &= ~IWN_FLAG_USE_ICT; 6442178676Ssam 6443198429Srpaulo /* Make sure we no longer hold the NIC lock. */ 6444198429Srpaulo iwn_nic_unlock(sc); 6445178676Ssam 6446198429Srpaulo /* Stop TX scheduler. */ 6447220728Sbschmidt iwn_prph_write(sc, sc->sched_txfact_addr, 0); 6448178676Ssam 6449198429Srpaulo /* Stop all DMA channels. */ 6450198429Srpaulo if (iwn_nic_lock(sc) == 0) { 6451220728Sbschmidt for (chnl = 0; chnl < sc->ndmachnls; chnl++) { 6452198429Srpaulo IWN_WRITE(sc, IWN_FH_TX_CONFIG(chnl), 0); 6453198429Srpaulo for (ntries = 0; ntries < 200; ntries++) { 6454220659Sbschmidt if (IWN_READ(sc, IWN_FH_TX_STATUS) & 6455198429Srpaulo IWN_FH_TX_STATUS_IDLE(chnl)) 6456198429Srpaulo break; 6457198429Srpaulo DELAY(10); 6458198429Srpaulo } 6459198429Srpaulo } 6460198429Srpaulo iwn_nic_unlock(sc); 6461198429Srpaulo } 6462178676Ssam 6463198429Srpaulo /* Stop RX ring. */ 6464198429Srpaulo iwn_reset_rx_ring(sc, &sc->rxq); 6465178676Ssam 6466198429Srpaulo /* Reset all TX rings. */ 6467220728Sbschmidt for (qid = 0; qid < sc->ntxqs; qid++) 6468198429Srpaulo iwn_reset_tx_ring(sc, &sc->txq[qid]); 6469178676Ssam 6470198429Srpaulo if (iwn_nic_lock(sc) == 0) { 6471201209Srpaulo iwn_prph_write(sc, IWN_APMG_CLK_DIS, 6472201209Srpaulo IWN_APMG_CLK_CTRL_DMA_CLK_RQT); 6473198429Srpaulo iwn_nic_unlock(sc); 6474178676Ssam } 6475198429Srpaulo DELAY(5); 6476198429Srpaulo /* Power OFF adapter. */ 6477198429Srpaulo iwn_apm_stop(sc); 6478198429Srpaulo} 6479178676Ssam 6480206477Sbschmidtstatic void 6481220723Sbschmidtiwn_radio_on(void *arg0, int pending) 6482220723Sbschmidt{ 6483220723Sbschmidt struct iwn_softc *sc = arg0; 6484220723Sbschmidt struct ifnet *ifp = sc->sc_ifp; 6485220723Sbschmidt struct ieee80211com *ic = ifp->if_l2com; 6486220723Sbschmidt struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 6487220723Sbschmidt 6488220723Sbschmidt if (vap != NULL) { 6489220723Sbschmidt iwn_init(sc); 6490220723Sbschmidt ieee80211_init(vap); 6491220723Sbschmidt } 6492220723Sbschmidt} 6493220723Sbschmidt 6494220723Sbschmidtstatic void 6495220723Sbschmidtiwn_radio_off(void *arg0, int pending) 6496220723Sbschmidt{ 6497220723Sbschmidt struct iwn_softc *sc = arg0; 6498220723Sbschmidt struct ifnet *ifp = sc->sc_ifp; 6499220723Sbschmidt struct ieee80211com *ic = ifp->if_l2com; 6500220723Sbschmidt struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 6501220723Sbschmidt 6502220723Sbschmidt iwn_stop(sc); 6503220723Sbschmidt if (vap != NULL) 6504220723Sbschmidt ieee80211_stop(vap); 6505220723Sbschmidt 6506220723Sbschmidt /* Enable interrupts to get RF toggle notification. */ 6507220723Sbschmidt IWN_LOCK(sc); 6508220723Sbschmidt IWN_WRITE(sc, IWN_INT, 0xffffffff); 6509220723Sbschmidt IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask); 6510220723Sbschmidt IWN_UNLOCK(sc); 6511220723Sbschmidt} 6512220723Sbschmidt 6513220723Sbschmidtstatic void 6514198429Srpauloiwn_init_locked(struct iwn_softc *sc) 6515198429Srpaulo{ 6516198429Srpaulo struct ifnet *ifp = sc->sc_ifp; 6517198429Srpaulo int error; 6518178676Ssam 6519198429Srpaulo IWN_LOCK_ASSERT(sc); 6520178676Ssam 6521220726Sbschmidt if ((error = iwn_hw_prepare(sc)) != 0) { 6522220724Sbschmidt device_printf(sc->sc_dev, "%s: hardware not ready, error %d\n", 6523198429Srpaulo __func__, error); 6524198429Srpaulo goto fail; 6525198429Srpaulo } 6526198429Srpaulo 6527201209Srpaulo /* Initialize interrupt mask to default value. */ 6528201209Srpaulo sc->int_mask = IWN_INT_MASK_DEF; 6529201209Srpaulo sc->sc_flags &= ~IWN_FLAG_USE_ICT; 6530201209Srpaulo 6531198429Srpaulo /* Check that the radio is not disabled by hardware switch. */ 6532198429Srpaulo if (!(IWN_READ(sc, IWN_GP_CNTRL) & IWN_GP_CNTRL_RFKILL)) { 6533178676Ssam device_printf(sc->sc_dev, 6534201209Srpaulo "radio is disabled by hardware switch\n"); 6535201209Srpaulo /* Enable interrupts to get RF toggle notifications. */ 6536201209Srpaulo IWN_WRITE(sc, IWN_INT, 0xffffffff); 6537201209Srpaulo IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask); 6538201209Srpaulo return; 6539178676Ssam } 6540178676Ssam 6541198429Srpaulo /* Read firmware images from the filesystem. */ 6542220726Sbschmidt if ((error = iwn_read_firmware(sc)) != 0) { 6543178676Ssam device_printf(sc->sc_dev, 6544220726Sbschmidt "%s: could not read firmware, error %d\n", __func__, 6545220726Sbschmidt error); 6546198429Srpaulo goto fail; 6547178676Ssam } 6548178676Ssam 6549198429Srpaulo /* Initialize hardware and upload firmware. */ 6550198429Srpaulo error = iwn_hw_init(sc); 6551201209Srpaulo firmware_put(sc->fw_fp, FIRMWARE_UNLOAD); 6552201209Srpaulo sc->fw_fp = NULL; 6553198429Srpaulo if (error != 0) { 6554198429Srpaulo device_printf(sc->sc_dev, 6555220726Sbschmidt "%s: could not initialize hardware, error %d\n", __func__, 6556220726Sbschmidt error); 6557198429Srpaulo goto fail; 6558198429Srpaulo } 6559178676Ssam 6560198429Srpaulo /* Configure adapter now that it is ready. */ 6561220726Sbschmidt if ((error = iwn_config(sc)) != 0) { 6562178676Ssam device_printf(sc->sc_dev, 6563220726Sbschmidt "%s: could not configure device, error %d\n", __func__, 6564220726Sbschmidt error); 6565198429Srpaulo goto fail; 6566178676Ssam } 6567178676Ssam 6568178676Ssam ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 6569178676Ssam ifp->if_drv_flags |= IFF_DRV_RUNNING; 6570198429Srpaulo 6571220667Sbschmidt callout_reset(&sc->watchdog_to, hz, iwn_watchdog, sc); 6572198429Srpaulo return; 6573198429Srpaulo 6574220726Sbschmidtfail: iwn_stop_locked(sc); 6575178676Ssam} 6576178676Ssam 6577206477Sbschmidtstatic void 6578178676Ssamiwn_init(void *arg) 6579178676Ssam{ 6580178676Ssam struct iwn_softc *sc = arg; 6581178676Ssam struct ifnet *ifp = sc->sc_ifp; 6582178676Ssam struct ieee80211com *ic = ifp->if_l2com; 6583178676Ssam 6584178676Ssam IWN_LOCK(sc); 6585178676Ssam iwn_init_locked(sc); 6586178676Ssam IWN_UNLOCK(sc); 6587178676Ssam 6588178676Ssam if (ifp->if_drv_flags & IFF_DRV_RUNNING) 6589178676Ssam ieee80211_start_all(ic); 6590178676Ssam} 6591178676Ssam 6592206477Sbschmidtstatic void 6593178676Ssamiwn_stop_locked(struct iwn_softc *sc) 6594178676Ssam{ 6595178676Ssam struct ifnet *ifp = sc->sc_ifp; 6596178676Ssam 6597178676Ssam IWN_LOCK_ASSERT(sc); 6598178676Ssam 6599178676Ssam sc->sc_tx_timer = 0; 6600220667Sbschmidt callout_stop(&sc->watchdog_to); 6601220667Sbschmidt callout_stop(&sc->calib_to); 6602178676Ssam ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); 6603178676Ssam 6604198429Srpaulo /* Power OFF hardware. */ 6605198429Srpaulo iwn_hw_stop(sc); 6606198429Srpaulo} 6607178676Ssam 6608206477Sbschmidtstatic void 6609178676Ssamiwn_stop(struct iwn_softc *sc) 6610178676Ssam{ 6611178676Ssam IWN_LOCK(sc); 6612178676Ssam iwn_stop_locked(sc); 6613178676Ssam IWN_UNLOCK(sc); 6614178676Ssam} 6615178676Ssam 6616178676Ssam/* 6617178676Ssam * Callback from net80211 to start a scan. 6618178676Ssam */ 6619178676Ssamstatic void 6620178676Ssamiwn_scan_start(struct ieee80211com *ic) 6621178676Ssam{ 6622178676Ssam struct ifnet *ifp = ic->ic_ifp; 6623178676Ssam struct iwn_softc *sc = ifp->if_softc; 6624178676Ssam 6625191746Sthompsa IWN_LOCK(sc); 6626191746Sthompsa /* make the link LED blink while we're scanning */ 6627191746Sthompsa iwn_set_led(sc, IWN_LED_LINK, 20, 2); 6628191746Sthompsa IWN_UNLOCK(sc); 6629178676Ssam} 6630178676Ssam 6631178676Ssam/* 6632178676Ssam * Callback from net80211 to terminate a scan. 6633178676Ssam */ 6634178676Ssamstatic void 6635178676Ssamiwn_scan_end(struct ieee80211com *ic) 6636178676Ssam{ 6637201209Srpaulo struct ifnet *ifp = ic->ic_ifp; 6638201209Srpaulo struct iwn_softc *sc = ifp->if_softc; 6639201209Srpaulo struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 6640201209Srpaulo 6641201209Srpaulo IWN_LOCK(sc); 6642201209Srpaulo if (vap->iv_state == IEEE80211_S_RUN) { 6643201209Srpaulo /* Set link LED to ON status if we are associated */ 6644201209Srpaulo iwn_set_led(sc, IWN_LED_LINK, 0, 1); 6645201209Srpaulo } 6646201209Srpaulo IWN_UNLOCK(sc); 6647178676Ssam} 6648178676Ssam 6649178676Ssam/* 6650178676Ssam * Callback from net80211 to force a channel change. 6651178676Ssam */ 6652178676Ssamstatic void 6653178676Ssamiwn_set_channel(struct ieee80211com *ic) 6654178676Ssam{ 6655198429Srpaulo const struct ieee80211_channel *c = ic->ic_curchan; 6656178676Ssam struct ifnet *ifp = ic->ic_ifp; 6657178676Ssam struct iwn_softc *sc = ifp->if_softc; 6658178676Ssam 6659191746Sthompsa IWN_LOCK(sc); 6660201209Srpaulo sc->sc_rxtap.wr_chan_freq = htole16(c->ic_freq); 6661201209Srpaulo sc->sc_rxtap.wr_chan_flags = htole16(c->ic_flags); 6662201209Srpaulo sc->sc_txtap.wt_chan_freq = htole16(c->ic_freq); 6663201209Srpaulo sc->sc_txtap.wt_chan_flags = htole16(c->ic_flags); 6664191746Sthompsa IWN_UNLOCK(sc); 6665178676Ssam} 6666178676Ssam 6667178676Ssam/* 6668178676Ssam * Callback from net80211 to start scanning of the current channel. 6669178676Ssam */ 6670178676Ssamstatic void 6671178676Ssamiwn_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell) 6672178676Ssam{ 6673178676Ssam struct ieee80211vap *vap = ss->ss_vap; 6674178676Ssam struct iwn_softc *sc = vap->iv_ic->ic_ifp->if_softc; 6675191746Sthompsa int error; 6676178676Ssam 6677191746Sthompsa IWN_LOCK(sc); 6678191746Sthompsa error = iwn_scan(sc); 6679191746Sthompsa IWN_UNLOCK(sc); 6680191746Sthompsa if (error != 0) 6681191746Sthompsa ieee80211_cancel_scan(vap); 6682178676Ssam} 6683178676Ssam 6684178676Ssam/* 6685178676Ssam * Callback from net80211 to handle the minimum dwell time being met. 6686178676Ssam * The intent is to terminate the scan but we just let the firmware 6687178676Ssam * notify us when it's finished as we have no safe way to abort it. 6688178676Ssam */ 6689178676Ssamstatic void 6690178676Ssamiwn_scan_mindwell(struct ieee80211_scan_state *ss) 6691178676Ssam{ 6692178676Ssam /* NB: don't try to abort scan; wait for firmware to finish */ 6693178676Ssam} 6694178676Ssam 6695178676Ssamstatic void 6696198429Srpauloiwn_hw_reset(void *arg0, int pending) 6697178676Ssam{ 6698178676Ssam struct iwn_softc *sc = arg0; 6699178676Ssam struct ifnet *ifp = sc->sc_ifp; 6700178676Ssam struct ieee80211com *ic = ifp->if_l2com; 6701178676Ssam 6702201209Srpaulo iwn_stop(sc); 6703191746Sthompsa iwn_init(sc); 6704191746Sthompsa ieee80211_notify_radio(ic, 1); 6705191746Sthompsa} 6706