if_iwn.c revision 221657
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 221657 2011-05-08 12:23:01Z 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); 157206474Sbschmidtstatic void iwn_read_eeprom_ht40(struct iwn_softc *, int); 158220726Sbschmidtstatic void iwn_read_eeprom_channels(struct iwn_softc *, int, uint32_t); 159220723Sbschmidtstatic struct iwn_eeprom_chan *iwn_find_eeprom_channel(struct iwn_softc *, 160220723Sbschmidt struct ieee80211_channel *); 161220723Sbschmidtstatic int iwn_setregdomain(struct ieee80211com *, 162220723Sbschmidt struct ieee80211_regdomain *, int, 163220726Sbschmidt struct ieee80211_channel[]); 164206477Sbschmidtstatic void iwn_read_eeprom_enhinfo(struct iwn_softc *); 165206477Sbschmidtstatic struct ieee80211_node *iwn_node_alloc(struct ieee80211vap *, 166198429Srpaulo const uint8_t mac[IEEE80211_ADDR_LEN]); 167220715Sbschmidtstatic void iwn_newassoc(struct ieee80211_node *, int); 168206477Sbschmidtstatic int iwn_media_change(struct ifnet *); 169206477Sbschmidtstatic int iwn_newstate(struct ieee80211vap *, enum ieee80211_state, int); 170220667Sbschmidtstatic void iwn_calib_timeout(void *); 171206477Sbschmidtstatic void iwn_rx_phy(struct iwn_softc *, struct iwn_rx_desc *, 172198429Srpaulo struct iwn_rx_data *); 173206477Sbschmidtstatic void iwn_rx_done(struct iwn_softc *, struct iwn_rx_desc *, 174178676Ssam struct iwn_rx_data *); 175206477Sbschmidtstatic void iwn_rx_compressed_ba(struct iwn_softc *, struct iwn_rx_desc *, 176201209Srpaulo struct iwn_rx_data *); 177220674Sbschmidtstatic void iwn5000_rx_calib_results(struct iwn_softc *, 178220674Sbschmidt struct iwn_rx_desc *, struct iwn_rx_data *); 179206477Sbschmidtstatic void iwn_rx_statistics(struct iwn_softc *, struct iwn_rx_desc *, 180198429Srpaulo struct iwn_rx_data *); 181206477Sbschmidtstatic void iwn4965_tx_done(struct iwn_softc *, struct iwn_rx_desc *, 182198429Srpaulo struct iwn_rx_data *); 183206477Sbschmidtstatic void iwn5000_tx_done(struct iwn_softc *, struct iwn_rx_desc *, 184198429Srpaulo struct iwn_rx_data *); 185206477Sbschmidtstatic void iwn_tx_done(struct iwn_softc *, struct iwn_rx_desc *, int, 186198429Srpaulo uint8_t); 187221651Sbschmidtstatic void iwn_ampdu_tx_done(struct iwn_softc *, int, int, int, void *); 188206477Sbschmidtstatic void iwn_cmd_done(struct iwn_softc *, struct iwn_rx_desc *); 189206477Sbschmidtstatic void iwn_notif_intr(struct iwn_softc *); 190206477Sbschmidtstatic void iwn_wakeup_intr(struct iwn_softc *); 191206477Sbschmidtstatic void iwn_rftoggle_intr(struct iwn_softc *); 192206477Sbschmidtstatic void iwn_fatal_intr(struct iwn_softc *); 193206477Sbschmidtstatic void iwn_intr(void *); 194206477Sbschmidtstatic void iwn4965_update_sched(struct iwn_softc *, int, int, uint8_t, 195198429Srpaulo uint16_t); 196206477Sbschmidtstatic void iwn5000_update_sched(struct iwn_softc *, int, int, uint8_t, 197198429Srpaulo uint16_t); 198206475Sbschmidt#ifdef notyet 199206477Sbschmidtstatic void iwn5000_reset_sched(struct iwn_softc *, int, int); 200206475Sbschmidt#endif 201206477Sbschmidtstatic int iwn_tx_data(struct iwn_softc *, struct mbuf *, 202220720Sbschmidt struct ieee80211_node *); 203220720Sbschmidtstatic int iwn_tx_data_raw(struct iwn_softc *, struct mbuf *, 204220720Sbschmidt struct ieee80211_node *, 205220720Sbschmidt const struct ieee80211_bpf_params *params); 206198429Srpaulostatic int iwn_raw_xmit(struct ieee80211_node *, struct mbuf *, 207198429Srpaulo const struct ieee80211_bpf_params *); 208206477Sbschmidtstatic void iwn_start(struct ifnet *); 209206477Sbschmidtstatic void iwn_start_locked(struct ifnet *); 210220667Sbschmidtstatic void iwn_watchdog(void *); 211206477Sbschmidtstatic int iwn_ioctl(struct ifnet *, u_long, caddr_t); 212206477Sbschmidtstatic int iwn_cmd(struct iwn_softc *, int, const void *, int, int); 213206477Sbschmidtstatic int iwn4965_add_node(struct iwn_softc *, struct iwn_node_info *, 214198429Srpaulo int); 215206477Sbschmidtstatic int iwn5000_add_node(struct iwn_softc *, struct iwn_node_info *, 216198429Srpaulo int); 217220715Sbschmidtstatic int iwn_set_link_quality(struct iwn_softc *, 218220715Sbschmidt struct ieee80211_node *); 219206477Sbschmidtstatic int iwn_add_broadcast_node(struct iwn_softc *, int); 220220721Sbschmidtstatic int iwn_updateedca(struct ieee80211com *); 221201209Srpaulostatic void iwn_update_mcast(struct ifnet *); 222206477Sbschmidtstatic void iwn_set_led(struct iwn_softc *, uint8_t, uint8_t, uint8_t); 223206477Sbschmidtstatic int iwn_set_critical_temp(struct iwn_softc *); 224206477Sbschmidtstatic int iwn_set_timing(struct iwn_softc *, struct ieee80211_node *); 225206477Sbschmidtstatic void iwn4965_power_calibration(struct iwn_softc *, int); 226206477Sbschmidtstatic int iwn4965_set_txpower(struct iwn_softc *, 227201882Skeramida struct ieee80211_channel *, int); 228206477Sbschmidtstatic int iwn5000_set_txpower(struct iwn_softc *, 229201882Skeramida struct ieee80211_channel *, int); 230206477Sbschmidtstatic int iwn4965_get_rssi(struct iwn_softc *, struct iwn_rx_stat *); 231206477Sbschmidtstatic int iwn5000_get_rssi(struct iwn_softc *, struct iwn_rx_stat *); 232206477Sbschmidtstatic int iwn_get_noise(const struct iwn_rx_general_stats *); 233206477Sbschmidtstatic int iwn4965_get_temperature(struct iwn_softc *); 234206477Sbschmidtstatic int iwn5000_get_temperature(struct iwn_softc *); 235206477Sbschmidtstatic int iwn_init_sensitivity(struct iwn_softc *); 236206477Sbschmidtstatic void iwn_collect_noise(struct iwn_softc *, 237178676Ssam const struct iwn_rx_general_stats *); 238206477Sbschmidtstatic int iwn4965_init_gains(struct iwn_softc *); 239206477Sbschmidtstatic int iwn5000_init_gains(struct iwn_softc *); 240206477Sbschmidtstatic int iwn4965_set_gains(struct iwn_softc *); 241206477Sbschmidtstatic int iwn5000_set_gains(struct iwn_softc *); 242206477Sbschmidtstatic void iwn_tune_sensitivity(struct iwn_softc *, 243178676Ssam const struct iwn_rx_stats *); 244206477Sbschmidtstatic int iwn_send_sensitivity(struct iwn_softc *); 245206477Sbschmidtstatic int iwn_set_pslevel(struct iwn_softc *, int, int, int); 246220662Sbschmidtstatic int iwn_send_btcoex(struct iwn_softc *); 247220891Sbschmidtstatic int iwn_send_advanced_btcoex(struct iwn_softc *); 248206477Sbschmidtstatic int iwn_config(struct iwn_softc *); 249220634Sbschmidtstatic uint8_t *ieee80211_add_ssid(uint8_t *, const uint8_t *, u_int); 250206477Sbschmidtstatic int iwn_scan(struct iwn_softc *); 251206477Sbschmidtstatic int iwn_auth(struct iwn_softc *, struct ieee80211vap *vap); 252206477Sbschmidtstatic int iwn_run(struct iwn_softc *, struct ieee80211vap *vap); 253221650Sbschmidtstatic int iwn_ampdu_rx_start(struct ieee80211_node *, 254221650Sbschmidt struct ieee80211_rx_ampdu *, int, int, int); 255221650Sbschmidtstatic void iwn_ampdu_rx_stop(struct ieee80211_node *, 256221650Sbschmidt struct ieee80211_rx_ampdu *); 257221651Sbschmidtstatic int iwn_addba_request(struct ieee80211_node *, 258221651Sbschmidt struct ieee80211_tx_ampdu *, int, int, int); 259221651Sbschmidtstatic int iwn_addba_response(struct ieee80211_node *, 260221651Sbschmidt struct ieee80211_tx_ampdu *, int, int, int); 261206474Sbschmidtstatic int iwn_ampdu_tx_start(struct ieee80211com *, 262206474Sbschmidt struct ieee80211_node *, uint8_t); 263221651Sbschmidtstatic void iwn_ampdu_tx_stop(struct ieee80211_node *, 264221651Sbschmidt struct ieee80211_tx_ampdu *); 265206474Sbschmidtstatic void iwn4965_ampdu_tx_start(struct iwn_softc *, 266221651Sbschmidt struct ieee80211_node *, int, uint8_t, uint16_t); 267221651Sbschmidtstatic void iwn4965_ampdu_tx_stop(struct iwn_softc *, int, 268220726Sbschmidt uint8_t, uint16_t); 269206474Sbschmidtstatic void iwn5000_ampdu_tx_start(struct iwn_softc *, 270221651Sbschmidt struct ieee80211_node *, int, uint8_t, uint16_t); 271221651Sbschmidtstatic void iwn5000_ampdu_tx_stop(struct iwn_softc *, int, 272220726Sbschmidt uint8_t, uint16_t); 273220674Sbschmidtstatic int iwn5000_query_calibration(struct iwn_softc *); 274220674Sbschmidtstatic int iwn5000_send_calibration(struct iwn_softc *); 275206477Sbschmidtstatic int iwn5000_send_wimax_coex(struct iwn_softc *); 276220677Sbschmidtstatic int iwn5000_crystal_calib(struct iwn_softc *); 277220676Sbschmidtstatic int iwn5000_temp_offset_calib(struct iwn_softc *); 278206477Sbschmidtstatic int iwn4965_post_alive(struct iwn_softc *); 279206477Sbschmidtstatic int iwn5000_post_alive(struct iwn_softc *); 280206477Sbschmidtstatic int iwn4965_load_bootcode(struct iwn_softc *, const uint8_t *, 281198429Srpaulo int); 282206477Sbschmidtstatic int iwn4965_load_firmware(struct iwn_softc *); 283206477Sbschmidtstatic int iwn5000_load_firmware_section(struct iwn_softc *, uint32_t, 284198429Srpaulo const uint8_t *, int); 285206477Sbschmidtstatic int iwn5000_load_firmware(struct iwn_softc *); 286210111Sbschmidtstatic int iwn_read_firmware_leg(struct iwn_softc *, 287210111Sbschmidt struct iwn_fw_info *); 288210111Sbschmidtstatic int iwn_read_firmware_tlv(struct iwn_softc *, 289210111Sbschmidt struct iwn_fw_info *, uint16_t); 290206477Sbschmidtstatic int iwn_read_firmware(struct iwn_softc *); 291206477Sbschmidtstatic int iwn_clock_wait(struct iwn_softc *); 292206477Sbschmidtstatic int iwn_apm_init(struct iwn_softc *); 293206477Sbschmidtstatic void iwn_apm_stop_master(struct iwn_softc *); 294206477Sbschmidtstatic void iwn_apm_stop(struct iwn_softc *); 295206477Sbschmidtstatic int iwn4965_nic_config(struct iwn_softc *); 296206477Sbschmidtstatic int iwn5000_nic_config(struct iwn_softc *); 297206477Sbschmidtstatic int iwn_hw_prepare(struct iwn_softc *); 298206477Sbschmidtstatic int iwn_hw_init(struct iwn_softc *); 299206477Sbschmidtstatic void iwn_hw_stop(struct iwn_softc *); 300220723Sbschmidtstatic void iwn_radio_on(void *, int); 301220723Sbschmidtstatic void iwn_radio_off(void *, int); 302206477Sbschmidtstatic void iwn_init_locked(struct iwn_softc *); 303206477Sbschmidtstatic void iwn_init(void *); 304206477Sbschmidtstatic void iwn_stop_locked(struct iwn_softc *); 305206477Sbschmidtstatic void iwn_stop(struct iwn_softc *); 306220726Sbschmidtstatic void iwn_scan_start(struct ieee80211com *); 307220726Sbschmidtstatic void iwn_scan_end(struct ieee80211com *); 308220726Sbschmidtstatic void iwn_set_channel(struct ieee80211com *); 309220726Sbschmidtstatic void iwn_scan_curchan(struct ieee80211_scan_state *, unsigned long); 310220726Sbschmidtstatic void iwn_scan_mindwell(struct ieee80211_scan_state *); 311198429Srpaulostatic void iwn_hw_reset(void *, int); 312178676Ssam 313178676Ssam#define IWN_DEBUG 314178676Ssam#ifdef IWN_DEBUG 315178676Ssamenum { 316178676Ssam IWN_DEBUG_XMIT = 0x00000001, /* basic xmit operation */ 317178676Ssam IWN_DEBUG_RECV = 0x00000002, /* basic recv operation */ 318178676Ssam IWN_DEBUG_STATE = 0x00000004, /* 802.11 state transitions */ 319178676Ssam IWN_DEBUG_TXPOW = 0x00000008, /* tx power processing */ 320178676Ssam IWN_DEBUG_RESET = 0x00000010, /* reset processing */ 321178676Ssam IWN_DEBUG_OPS = 0x00000020, /* iwn_ops processing */ 322178676Ssam IWN_DEBUG_BEACON = 0x00000040, /* beacon handling */ 323178676Ssam IWN_DEBUG_WATCHDOG = 0x00000080, /* watchdog timeout */ 324178676Ssam IWN_DEBUG_INTR = 0x00000100, /* ISR */ 325178676Ssam IWN_DEBUG_CALIBRATE = 0x00000200, /* periodic calibration */ 326178676Ssam IWN_DEBUG_NODE = 0x00000400, /* node management */ 327178676Ssam IWN_DEBUG_LED = 0x00000800, /* led management */ 328178676Ssam IWN_DEBUG_CMD = 0x00001000, /* cmd submission */ 329178676Ssam IWN_DEBUG_FATAL = 0x80000000, /* fatal errors */ 330178676Ssam IWN_DEBUG_ANY = 0xffffffff 331178676Ssam}; 332178676Ssam 333178676Ssam#define DPRINTF(sc, m, fmt, ...) do { \ 334178676Ssam if (sc->sc_debug & (m)) \ 335178676Ssam printf(fmt, __VA_ARGS__); \ 336178676Ssam} while (0) 337178676Ssam 338220723Sbschmidtstatic const char * 339220723Sbschmidtiwn_intr_str(uint8_t cmd) 340220723Sbschmidt{ 341220723Sbschmidt switch (cmd) { 342220723Sbschmidt /* Notifications */ 343220723Sbschmidt case IWN_UC_READY: return "UC_READY"; 344220723Sbschmidt case IWN_ADD_NODE_DONE: return "ADD_NODE_DONE"; 345220723Sbschmidt case IWN_TX_DONE: return "TX_DONE"; 346220723Sbschmidt case IWN_START_SCAN: return "START_SCAN"; 347220723Sbschmidt case IWN_STOP_SCAN: return "STOP_SCAN"; 348220723Sbschmidt case IWN_RX_STATISTICS: return "RX_STATS"; 349220723Sbschmidt case IWN_BEACON_STATISTICS: return "BEACON_STATS"; 350220723Sbschmidt case IWN_STATE_CHANGED: return "STATE_CHANGED"; 351220723Sbschmidt case IWN_BEACON_MISSED: return "BEACON_MISSED"; 352220723Sbschmidt case IWN_RX_PHY: return "RX_PHY"; 353220723Sbschmidt case IWN_MPDU_RX_DONE: return "MPDU_RX_DONE"; 354220723Sbschmidt case IWN_RX_DONE: return "RX_DONE"; 355220723Sbschmidt 356220723Sbschmidt /* Command Notifications */ 357220723Sbschmidt case IWN_CMD_RXON: return "IWN_CMD_RXON"; 358220723Sbschmidt case IWN_CMD_RXON_ASSOC: return "IWN_CMD_RXON_ASSOC"; 359220723Sbschmidt case IWN_CMD_EDCA_PARAMS: return "IWN_CMD_EDCA_PARAMS"; 360220723Sbschmidt case IWN_CMD_TIMING: return "IWN_CMD_TIMING"; 361220723Sbschmidt case IWN_CMD_LINK_QUALITY: return "IWN_CMD_LINK_QUALITY"; 362220723Sbschmidt case IWN_CMD_SET_LED: return "IWN_CMD_SET_LED"; 363220723Sbschmidt case IWN5000_CMD_WIMAX_COEX: return "IWN5000_CMD_WIMAX_COEX"; 364220723Sbschmidt case IWN5000_CMD_CALIB_CONFIG: return "IWN5000_CMD_CALIB_CONFIG"; 365220723Sbschmidt case IWN5000_CMD_CALIB_RESULT: return "IWN5000_CMD_CALIB_RESULT"; 366220723Sbschmidt case IWN5000_CMD_CALIB_COMPLETE: return "IWN5000_CMD_CALIB_COMPLETE"; 367220723Sbschmidt case IWN_CMD_SET_POWER_MODE: return "IWN_CMD_SET_POWER_MODE"; 368220723Sbschmidt case IWN_CMD_SCAN: return "IWN_CMD_SCAN"; 369220723Sbschmidt case IWN_CMD_SCAN_RESULTS: return "IWN_CMD_SCAN_RESULTS"; 370220723Sbschmidt case IWN_CMD_TXPOWER: return "IWN_CMD_TXPOWER"; 371220723Sbschmidt case IWN_CMD_TXPOWER_DBM: return "IWN_CMD_TXPOWER_DBM"; 372220723Sbschmidt case IWN5000_CMD_TX_ANT_CONFIG: return "IWN5000_CMD_TX_ANT_CONFIG"; 373220723Sbschmidt case IWN_CMD_BT_COEX: return "IWN_CMD_BT_COEX"; 374220723Sbschmidt case IWN_CMD_SET_CRITICAL_TEMP: return "IWN_CMD_SET_CRITICAL_TEMP"; 375220723Sbschmidt case IWN_CMD_SET_SENSITIVITY: return "IWN_CMD_SET_SENSITIVITY"; 376220723Sbschmidt case IWN_CMD_PHY_CALIB: return "IWN_CMD_PHY_CALIB"; 377220723Sbschmidt } 378220723Sbschmidt return "UNKNOWN INTR NOTIF/CMD"; 379220723Sbschmidt} 380178676Ssam#else 381178676Ssam#define DPRINTF(sc, m, fmt, ...) do { (void) sc; } while (0) 382178676Ssam#endif 383178676Ssam 384220723Sbschmidtstatic device_method_t iwn_methods[] = { 385220723Sbschmidt /* Device interface */ 386220723Sbschmidt DEVMETHOD(device_probe, iwn_probe), 387220723Sbschmidt DEVMETHOD(device_attach, iwn_attach), 388220723Sbschmidt DEVMETHOD(device_detach, iwn_detach), 389220723Sbschmidt DEVMETHOD(device_shutdown, iwn_shutdown), 390220723Sbschmidt DEVMETHOD(device_suspend, iwn_suspend), 391220723Sbschmidt DEVMETHOD(device_resume, iwn_resume), 392220723Sbschmidt { 0, 0 } 393178676Ssam}; 394178676Ssam 395220723Sbschmidtstatic driver_t iwn_driver = { 396220723Sbschmidt "iwn", 397220723Sbschmidt iwn_methods, 398220726Sbschmidt sizeof(struct iwn_softc) 399178676Ssam}; 400220723Sbschmidtstatic devclass_t iwn_devclass; 401178676Ssam 402220723SbschmidtDRIVER_MODULE(iwn, pci, iwn_driver, iwn_devclass, 0, 0); 403220726Sbschmidt 404220723SbschmidtMODULE_DEPEND(iwn, firmware, 1, 1, 1); 405220723SbschmidtMODULE_DEPEND(iwn, pci, 1, 1, 1); 406220723SbschmidtMODULE_DEPEND(iwn, wlan, 1, 1, 1); 407220723Sbschmidt 408178676Ssamstatic int 409178676Ssamiwn_probe(device_t dev) 410178676Ssam{ 411198429Srpaulo const struct iwn_ident *ident; 412178676Ssam 413198429Srpaulo for (ident = iwn_ident_table; ident->name != NULL; ident++) { 414198429Srpaulo if (pci_get_vendor(dev) == ident->vendor && 415198429Srpaulo pci_get_device(dev) == ident->device) { 416198429Srpaulo device_set_desc(dev, ident->name); 417198429Srpaulo return 0; 418198429Srpaulo } 419198429Srpaulo } 420198429Srpaulo return ENXIO; 421178676Ssam} 422178676Ssam 423178676Ssamstatic int 424178676Ssamiwn_attach(device_t dev) 425178676Ssam{ 426178676Ssam struct iwn_softc *sc = (struct iwn_softc *)device_get_softc(dev); 427178676Ssam struct ieee80211com *ic; 428178676Ssam struct ifnet *ifp; 429220721Sbschmidt uint32_t reg; 430184233Smav int i, error, result; 431190526Ssam uint8_t macaddr[IEEE80211_ADDR_LEN]; 432178676Ssam 433178676Ssam sc->sc_dev = dev; 434178676Ssam 435198429Srpaulo /* 436198429Srpaulo * Get the offset of the PCI Express Capability Structure in PCI 437198429Srpaulo * Configuration Space. 438198429Srpaulo */ 439219902Sjhb error = pci_find_cap(dev, PCIY_EXPRESS, &sc->sc_cap_off); 440198429Srpaulo if (error != 0) { 441198429Srpaulo device_printf(dev, "PCIe capability structure not found!\n"); 442198429Srpaulo return error; 443178676Ssam } 444178676Ssam 445198429Srpaulo /* Clear device-specific "PCI retry timeout" register (41h). */ 446178676Ssam pci_write_config(dev, 0x41, 0, 1); 447178676Ssam 448198429Srpaulo /* Hardware bug workaround. */ 449220721Sbschmidt reg = pci_read_config(dev, PCIR_COMMAND, 1); 450220721Sbschmidt if (reg & PCIM_CMD_INTxDIS) { 451198429Srpaulo DPRINTF(sc, IWN_DEBUG_RESET, "%s: PCIe INTx Disable set\n", 452198429Srpaulo __func__); 453220721Sbschmidt reg &= ~PCIM_CMD_INTxDIS; 454220721Sbschmidt pci_write_config(dev, PCIR_COMMAND, reg, 1); 455198429Srpaulo } 456198429Srpaulo 457198429Srpaulo /* Enable bus-mastering. */ 458178676Ssam pci_enable_busmaster(dev); 459178676Ssam 460198429Srpaulo sc->mem_rid = PCIR_BAR(0); 461178676Ssam sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid, 462198429Srpaulo RF_ACTIVE); 463220726Sbschmidt if (sc->mem == NULL) { 464220724Sbschmidt device_printf(dev, "can't map mem space\n"); 465198429Srpaulo error = ENOMEM; 466178676Ssam return error; 467178676Ssam } 468178676Ssam sc->sc_st = rman_get_bustag(sc->mem); 469178676Ssam sc->sc_sh = rman_get_bushandle(sc->mem); 470220726Sbschmidt 471178676Ssam sc->irq_rid = 0; 472184233Smav if ((result = pci_msi_count(dev)) == 1 && 473184233Smav pci_alloc_msi(dev, &result) == 0) 474184233Smav sc->irq_rid = 1; 475220725Sbschmidt /* Install interrupt handler. */ 476178676Ssam sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irq_rid, 477198429Srpaulo RF_ACTIVE | RF_SHAREABLE); 478178676Ssam if (sc->irq == NULL) { 479220724Sbschmidt device_printf(dev, "can't map interrupt\n"); 480178676Ssam error = ENOMEM; 481198429Srpaulo goto fail; 482178676Ssam } 483178676Ssam 484178676Ssam IWN_LOCK_INIT(sc); 485178676Ssam 486220728Sbschmidt /* Read hardware revision and attach. */ 487220728Sbschmidt sc->hw_type = (IWN_READ(sc, IWN_HW_REV) >> 4) & 0xf; 488220728Sbschmidt if (sc->hw_type == IWN_HW_REV_TYPE_4965) 489220728Sbschmidt error = iwn4965_attach(sc, pci_get_device(dev)); 490220728Sbschmidt else 491220728Sbschmidt error = iwn5000_attach(sc, pci_get_device(dev)); 492220728Sbschmidt if (error != 0) { 493220728Sbschmidt device_printf(dev, "could not attach device, error %d\n", 494220728Sbschmidt error); 495198429Srpaulo goto fail; 496198429Srpaulo } 497198429Srpaulo 498220726Sbschmidt if ((error = iwn_hw_prepare(sc)) != 0) { 499198429Srpaulo device_printf(dev, "hardware not ready, error %d\n", error); 500178676Ssam goto fail; 501178676Ssam } 502178676Ssam 503198429Srpaulo /* Allocate DMA memory for firmware transfers. */ 504220726Sbschmidt if ((error = iwn_alloc_fwmem(sc)) != 0) { 505178676Ssam device_printf(dev, 506198429Srpaulo "could not allocate memory for firmware, error %d\n", 507198429Srpaulo error); 508178676Ssam goto fail; 509178676Ssam } 510178676Ssam 511198429Srpaulo /* Allocate "Keep Warm" page. */ 512220726Sbschmidt if ((error = iwn_alloc_kw(sc)) != 0) { 513178676Ssam device_printf(dev, 514220724Sbschmidt "could not allocate keep warm page, error %d\n", error); 515178676Ssam goto fail; 516178676Ssam } 517178676Ssam 518201209Srpaulo /* Allocate ICT table for 5000 Series. */ 519201209Srpaulo if (sc->hw_type != IWN_HW_REV_TYPE_4965 && 520201209Srpaulo (error = iwn_alloc_ict(sc)) != 0) { 521220724Sbschmidt device_printf(dev, "could not allocate ICT table, error %d\n", 522220724Sbschmidt error); 523201209Srpaulo goto fail; 524201209Srpaulo } 525201209Srpaulo 526198429Srpaulo /* Allocate TX scheduler "rings". */ 527220726Sbschmidt if ((error = iwn_alloc_sched(sc)) != 0) { 528178676Ssam device_printf(dev, 529220726Sbschmidt "could not allocate TX scheduler rings, error %d\n", error); 530178676Ssam goto fail; 531178676Ssam } 532178676Ssam 533220725Sbschmidt /* Allocate TX rings (16 on 4965AGN, 20 on >=5000). */ 534220728Sbschmidt for (i = 0; i < sc->ntxqs; i++) { 535220726Sbschmidt if ((error = iwn_alloc_tx_ring(sc, &sc->txq[i], i)) != 0) { 536178676Ssam device_printf(dev, 537220724Sbschmidt "could not allocate TX ring %d, error %d\n", i, 538220724Sbschmidt error); 539178676Ssam goto fail; 540178676Ssam } 541178676Ssam } 542178676Ssam 543198429Srpaulo /* Allocate RX ring. */ 544220726Sbschmidt if ((error = iwn_alloc_rx_ring(sc, &sc->rxq)) != 0) { 545220724Sbschmidt device_printf(dev, "could not allocate RX ring, error %d\n", 546220724Sbschmidt error); 547178676Ssam goto fail; 548178676Ssam } 549178676Ssam 550198429Srpaulo /* Clear pending interrupts. */ 551198429Srpaulo IWN_WRITE(sc, IWN_INT, 0xffffffff); 552198429Srpaulo 553178676Ssam ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211); 554178676Ssam if (ifp == NULL) { 555178676Ssam device_printf(dev, "can not allocate ifnet structure\n"); 556178676Ssam goto fail; 557178676Ssam } 558220726Sbschmidt 559178676Ssam ic = ifp->if_l2com; 560198429Srpaulo ic->ic_ifp = ifp; 561178676Ssam ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ 562178676Ssam ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ 563178676Ssam 564198429Srpaulo /* Set device capabilities. */ 565178676Ssam ic->ic_caps = 566178957Ssam IEEE80211_C_STA /* station mode supported */ 567178957Ssam | IEEE80211_C_MONITOR /* monitor mode supported */ 568178676Ssam | IEEE80211_C_TXPMGT /* tx power management */ 569178676Ssam | IEEE80211_C_SHSLOT /* short slot time supported */ 570178676Ssam | IEEE80211_C_WPA 571178676Ssam | IEEE80211_C_SHPREAMBLE /* short preamble supported */ 572178676Ssam#if 0 573178676Ssam | IEEE80211_C_IBSS /* ibss/adhoc mode */ 574178676Ssam#endif 575178676Ssam | IEEE80211_C_WME /* WME */ 576178676Ssam ; 577221640Sbschmidt if (sc->hw_type != IWN_HW_REV_TYPE_4965) 578221640Sbschmidt ic->ic_caps |= IEEE80211_C_BGSCAN; /* background scanning */ 579221640Sbschmidt 580221642Sbschmidt /* Read MAC address, channels, etc from EEPROM. */ 581221642Sbschmidt if ((error = iwn_read_eeprom(sc, macaddr)) != 0) { 582221642Sbschmidt device_printf(dev, "could not read EEPROM, error %d\n", 583221642Sbschmidt error); 584221642Sbschmidt goto fail; 585221642Sbschmidt } 586221642Sbschmidt 587221642Sbschmidt /* Count the number of available chains. */ 588221642Sbschmidt sc->ntxchains = 589221642Sbschmidt ((sc->txchainmask >> 2) & 1) + 590221642Sbschmidt ((sc->txchainmask >> 1) & 1) + 591221642Sbschmidt ((sc->txchainmask >> 0) & 1); 592221642Sbschmidt sc->nrxchains = 593221642Sbschmidt ((sc->rxchainmask >> 2) & 1) + 594221642Sbschmidt ((sc->rxchainmask >> 1) & 1) + 595221642Sbschmidt ((sc->rxchainmask >> 0) & 1); 596221642Sbschmidt if (bootverbose) { 597221642Sbschmidt device_printf(dev, "MIMO %dT%dR, %.4s, address %6D\n", 598221642Sbschmidt sc->ntxchains, sc->nrxchains, sc->eeprom_domain, 599221642Sbschmidt macaddr, ":"); 600221642Sbschmidt } 601221642Sbschmidt 602221657Sbschmidt if (sc->sc_flags & IWN_FLAG_HAS_11N) { 603221657Sbschmidt ic->ic_rxstream = sc->nrxchains; 604221657Sbschmidt ic->ic_txstream = sc->ntxchains; 605221657Sbschmidt ic->ic_htcaps = 606221657Sbschmidt IEEE80211_HTCAP_SMPS_OFF /* SMPS mode disabled */ 607221657Sbschmidt | IEEE80211_HTCAP_SHORTGI20 /* short GI in 20MHz */ 608221657Sbschmidt#ifdef notyet 609221657Sbschmidt | IEEE80211_HTCAP_CHWIDTH40 /* 40MHz channel width*/ 610221657Sbschmidt | IEEE80211_HTCAP_SHORTGI40 /* short GI in 40MHz */ 611221657Sbschmidt | IEEE80211_HTCAP_GREENFIELD 612201209Srpaulo#if IWN_RBUF_SIZE == 8192 613221657Sbschmidt | IEEE80211_HTCAP_MAXAMSDU_7935 /* max A-MSDU length */ 614221657Sbschmidt#else 615221657Sbschmidt | IEEE80211_HTCAP_MAXAMSDU_3839 /* max A-MSDU length */ 616178678Ssam#endif 617201209Srpaulo#endif 618221657Sbschmidt /* s/w capabilities */ 619221657Sbschmidt | IEEE80211_HTC_HT /* HT operation */ 620221657Sbschmidt | IEEE80211_HTC_AMPDU /* tx A-MPDU */ 621221657Sbschmidt#ifdef notyet 622221657Sbschmidt | IEEE80211_HTC_AMSDU /* tx A-MSDU */ 623201209Srpaulo#endif 624221657Sbschmidt ; 625221657Sbschmidt } 626201209Srpaulo 627178676Ssam if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 628178676Ssam ifp->if_softc = sc; 629178676Ssam ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 630178676Ssam ifp->if_init = iwn_init; 631178676Ssam ifp->if_ioctl = iwn_ioctl; 632178676Ssam ifp->if_start = iwn_start; 633207554Ssobomax IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); 634207554Ssobomax ifp->if_snd.ifq_drv_maxlen = ifqmaxlen; 635178676Ssam IFQ_SET_READY(&ifp->if_snd); 636178676Ssam 637190526Ssam ieee80211_ifattach(ic, macaddr); 638178676Ssam ic->ic_vap_create = iwn_vap_create; 639178676Ssam ic->ic_vap_delete = iwn_vap_delete; 640178676Ssam ic->ic_raw_xmit = iwn_raw_xmit; 641178676Ssam ic->ic_node_alloc = iwn_node_alloc; 642221650Sbschmidt sc->sc_ampdu_rx_start = ic->ic_ampdu_rx_start; 643220723Sbschmidt ic->ic_ampdu_rx_start = iwn_ampdu_rx_start; 644221650Sbschmidt sc->sc_ampdu_rx_stop = ic->ic_ampdu_rx_stop; 645220723Sbschmidt ic->ic_ampdu_rx_stop = iwn_ampdu_rx_stop; 646221651Sbschmidt sc->sc_addba_request = ic->ic_addba_request; 647221651Sbschmidt ic->ic_addba_request = iwn_addba_request; 648221651Sbschmidt sc->sc_addba_response = ic->ic_addba_response; 649221651Sbschmidt ic->ic_addba_response = iwn_addba_response; 650221651Sbschmidt sc->sc_addba_stop = ic->ic_addba_stop; 651221651Sbschmidt ic->ic_addba_stop = iwn_ampdu_tx_stop; 652220715Sbschmidt ic->ic_newassoc = iwn_newassoc; 653220721Sbschmidt ic->ic_wme.wme_update = iwn_updateedca; 654201209Srpaulo ic->ic_update_mcast = iwn_update_mcast; 655198429Srpaulo ic->ic_scan_start = iwn_scan_start; 656198429Srpaulo ic->ic_scan_end = iwn_scan_end; 657198429Srpaulo ic->ic_set_channel = iwn_set_channel; 658198429Srpaulo ic->ic_scan_curchan = iwn_scan_curchan; 659198429Srpaulo ic->ic_scan_mindwell = iwn_scan_mindwell; 660201209Srpaulo ic->ic_setregdomain = iwn_setregdomain; 661178676Ssam 662198429Srpaulo iwn_radiotap_attach(sc); 663220667Sbschmidt 664220667Sbschmidt callout_init_mtx(&sc->calib_to, &sc->sc_mtx, 0); 665220667Sbschmidt callout_init_mtx(&sc->watchdog_to, &sc->sc_mtx, 0); 666220726Sbschmidt TASK_INIT(&sc->sc_reinit_task, 0, iwn_hw_reset, sc); 667220726Sbschmidt TASK_INIT(&sc->sc_radioon_task, 0, iwn_radio_on, sc); 668220726Sbschmidt TASK_INIT(&sc->sc_radiooff_task, 0, iwn_radio_off, sc); 669220667Sbschmidt 670178676Ssam iwn_sysctlattach(sc); 671178676Ssam 672198429Srpaulo /* 673198429Srpaulo * Hook our interrupt after all initialization is complete. 674198429Srpaulo */ 675198429Srpaulo error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET | INTR_MPSAFE, 676178676Ssam NULL, iwn_intr, sc, &sc->sc_ih); 677198429Srpaulo if (error != 0) { 678220724Sbschmidt device_printf(dev, "can't establish interrupt, error %d\n", 679198429Srpaulo error); 680198429Srpaulo goto fail; 681198429Srpaulo } 682178676Ssam 683220724Sbschmidt if (bootverbose) 684220724Sbschmidt ieee80211_announce(ic); 685178676Ssam return 0; 686178676Ssamfail: 687220635Sbschmidt iwn_detach(dev); 688178676Ssam return error; 689178676Ssam} 690178676Ssam 691220728Sbschmidtstatic int 692220728Sbschmidtiwn4965_attach(struct iwn_softc *sc, uint16_t pid) 693178676Ssam{ 694220728Sbschmidt struct iwn_ops *ops = &sc->ops; 695198429Srpaulo 696220728Sbschmidt ops->load_firmware = iwn4965_load_firmware; 697220728Sbschmidt ops->read_eeprom = iwn4965_read_eeprom; 698220728Sbschmidt ops->post_alive = iwn4965_post_alive; 699220728Sbschmidt ops->nic_config = iwn4965_nic_config; 700220728Sbschmidt ops->update_sched = iwn4965_update_sched; 701220728Sbschmidt ops->get_temperature = iwn4965_get_temperature; 702220728Sbschmidt ops->get_rssi = iwn4965_get_rssi; 703220728Sbschmidt ops->set_txpower = iwn4965_set_txpower; 704220728Sbschmidt ops->init_gains = iwn4965_init_gains; 705220728Sbschmidt ops->set_gains = iwn4965_set_gains; 706220728Sbschmidt ops->add_node = iwn4965_add_node; 707220728Sbschmidt ops->tx_done = iwn4965_tx_done; 708220728Sbschmidt ops->ampdu_tx_start = iwn4965_ampdu_tx_start; 709220728Sbschmidt ops->ampdu_tx_stop = iwn4965_ampdu_tx_stop; 710220728Sbschmidt sc->ntxqs = IWN4965_NTXQUEUES; 711221651Sbschmidt sc->firstaggqueue = IWN4965_FIRSTAGGQUEUE; 712220728Sbschmidt sc->ndmachnls = IWN4965_NDMACHNLS; 713220728Sbschmidt sc->broadcast_id = IWN4965_ID_BROADCAST; 714220728Sbschmidt sc->rxonsz = IWN4965_RXONSZ; 715220728Sbschmidt sc->schedsz = IWN4965_SCHEDSZ; 716220728Sbschmidt sc->fw_text_maxsz = IWN4965_FW_TEXT_MAXSZ; 717220728Sbschmidt sc->fw_data_maxsz = IWN4965_FW_DATA_MAXSZ; 718220728Sbschmidt sc->fwsz = IWN4965_FWSZ; 719220728Sbschmidt sc->sched_txfact_addr = IWN4965_SCHED_TXFACT; 720220728Sbschmidt sc->limits = &iwn4965_sensitivity_limits; 721220728Sbschmidt sc->fwname = "iwn4965fw"; 722220728Sbschmidt /* Override chains masks, ROM is known to be broken. */ 723220728Sbschmidt sc->txchainmask = IWN_ANT_AB; 724220728Sbschmidt sc->rxchainmask = IWN_ANT_ABC; 725220728Sbschmidt 726220728Sbschmidt return 0; 727220728Sbschmidt} 728220728Sbschmidt 729220728Sbschmidtstatic int 730220728Sbschmidtiwn5000_attach(struct iwn_softc *sc, uint16_t pid) 731220728Sbschmidt{ 732220728Sbschmidt struct iwn_ops *ops = &sc->ops; 733220728Sbschmidt 734220728Sbschmidt ops->load_firmware = iwn5000_load_firmware; 735220728Sbschmidt ops->read_eeprom = iwn5000_read_eeprom; 736220728Sbschmidt ops->post_alive = iwn5000_post_alive; 737220728Sbschmidt ops->nic_config = iwn5000_nic_config; 738220728Sbschmidt ops->update_sched = iwn5000_update_sched; 739220728Sbschmidt ops->get_temperature = iwn5000_get_temperature; 740220728Sbschmidt ops->get_rssi = iwn5000_get_rssi; 741220728Sbschmidt ops->set_txpower = iwn5000_set_txpower; 742220728Sbschmidt ops->init_gains = iwn5000_init_gains; 743220728Sbschmidt ops->set_gains = iwn5000_set_gains; 744220728Sbschmidt ops->add_node = iwn5000_add_node; 745220728Sbschmidt ops->tx_done = iwn5000_tx_done; 746220728Sbschmidt ops->ampdu_tx_start = iwn5000_ampdu_tx_start; 747220728Sbschmidt ops->ampdu_tx_stop = iwn5000_ampdu_tx_stop; 748220728Sbschmidt sc->ntxqs = IWN5000_NTXQUEUES; 749221651Sbschmidt sc->firstaggqueue = IWN5000_FIRSTAGGQUEUE; 750220728Sbschmidt sc->ndmachnls = IWN5000_NDMACHNLS; 751220728Sbschmidt sc->broadcast_id = IWN5000_ID_BROADCAST; 752220728Sbschmidt sc->rxonsz = IWN5000_RXONSZ; 753220728Sbschmidt sc->schedsz = IWN5000_SCHEDSZ; 754220728Sbschmidt sc->fw_text_maxsz = IWN5000_FW_TEXT_MAXSZ; 755220728Sbschmidt sc->fw_data_maxsz = IWN5000_FW_DATA_MAXSZ; 756220728Sbschmidt sc->fwsz = IWN5000_FWSZ; 757220728Sbschmidt sc->sched_txfact_addr = IWN5000_SCHED_TXFACT; 758220866Sbschmidt sc->reset_noise_gain = IWN5000_PHY_CALIB_RESET_NOISE_GAIN; 759220866Sbschmidt sc->noise_gain = IWN5000_PHY_CALIB_NOISE_GAIN; 760220728Sbschmidt 761198429Srpaulo switch (sc->hw_type) { 762198429Srpaulo case IWN_HW_REV_TYPE_5100: 763201209Srpaulo sc->limits = &iwn5000_sensitivity_limits; 764198439Srpaulo sc->fwname = "iwn5000fw"; 765220727Sbschmidt /* Override chains masks, ROM is known to be broken. */ 766201209Srpaulo sc->txchainmask = IWN_ANT_B; 767201209Srpaulo sc->rxchainmask = IWN_ANT_AB; 768198429Srpaulo break; 769198429Srpaulo case IWN_HW_REV_TYPE_5150: 770201209Srpaulo sc->limits = &iwn5150_sensitivity_limits; 771198439Srpaulo sc->fwname = "iwn5150fw"; 772198429Srpaulo break; 773198429Srpaulo case IWN_HW_REV_TYPE_5300: 774198429Srpaulo case IWN_HW_REV_TYPE_5350: 775201209Srpaulo sc->limits = &iwn5000_sensitivity_limits; 776198439Srpaulo sc->fwname = "iwn5000fw"; 777198429Srpaulo break; 778198429Srpaulo case IWN_HW_REV_TYPE_1000: 779206444Sbschmidt sc->limits = &iwn1000_sensitivity_limits; 780198439Srpaulo sc->fwname = "iwn1000fw"; 781198429Srpaulo break; 782198429Srpaulo case IWN_HW_REV_TYPE_6000: 783201209Srpaulo sc->limits = &iwn6000_sensitivity_limits; 784198439Srpaulo sc->fwname = "iwn6000fw"; 785220728Sbschmidt if (pid == 0x422c || pid == 0x4239) { 786201209Srpaulo sc->sc_flags |= IWN_FLAG_INTERNAL_PA; 787220727Sbschmidt /* Override chains masks, ROM is known to be broken. */ 788201209Srpaulo sc->txchainmask = IWN_ANT_BC; 789201209Srpaulo sc->rxchainmask = IWN_ANT_BC; 790201209Srpaulo } 791198429Srpaulo break; 792198429Srpaulo case IWN_HW_REV_TYPE_6050: 793201209Srpaulo sc->limits = &iwn6000_sensitivity_limits; 794210109Sbschmidt sc->fwname = "iwn6050fw"; 795220867Sbschmidt /* Override chains masks, ROM is known to be broken. */ 796220867Sbschmidt sc->txchainmask = IWN_ANT_AB; 797220867Sbschmidt sc->rxchainmask = IWN_ANT_AB; 798198429Srpaulo break; 799210109Sbschmidt case IWN_HW_REV_TYPE_6005: 800210109Sbschmidt sc->limits = &iwn6000_sensitivity_limits; 801220894Sbschmidt if (pid != 0x0082 && pid != 0x0085) { 802220894Sbschmidt sc->fwname = "iwn6000g2bfw"; 803220891Sbschmidt sc->sc_flags |= IWN_FLAG_ADV_BTCOEX; 804220894Sbschmidt } else 805220894Sbschmidt sc->fwname = "iwn6000g2afw"; 806210109Sbschmidt break; 807198429Srpaulo default: 808198429Srpaulo device_printf(sc->sc_dev, "adapter type %d not supported\n", 809198429Srpaulo sc->hw_type); 810220728Sbschmidt return ENOTSUP; 811198429Srpaulo } 812220728Sbschmidt return 0; 813178676Ssam} 814178676Ssam 815178676Ssam/* 816198429Srpaulo * Attach the interface to 802.11 radiotap. 817178676Ssam */ 818206477Sbschmidtstatic void 819198429Srpauloiwn_radiotap_attach(struct iwn_softc *sc) 820178676Ssam{ 821178676Ssam struct ifnet *ifp = sc->sc_ifp; 822178676Ssam struct ieee80211com *ic = ifp->if_l2com; 823178676Ssam 824198429Srpaulo ieee80211_radiotap_attach(ic, 825198429Srpaulo &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), 826198429Srpaulo IWN_TX_RADIOTAP_PRESENT, 827198429Srpaulo &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), 828198429Srpaulo IWN_RX_RADIOTAP_PRESENT); 829178676Ssam} 830178676Ssam 831220723Sbschmidtstatic void 832220723Sbschmidtiwn_sysctlattach(struct iwn_softc *sc) 833220723Sbschmidt{ 834220723Sbschmidt struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); 835220723Sbschmidt struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); 836220723Sbschmidt 837220723Sbschmidt#ifdef IWN_DEBUG 838220723Sbschmidt sc->sc_debug = 0; 839220723Sbschmidt SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 840220723Sbschmidt "debug", CTLFLAG_RW, &sc->sc_debug, 0, "control debugging printfs"); 841220723Sbschmidt#endif 842220723Sbschmidt} 843220723Sbschmidt 844178676Ssamstatic struct ieee80211vap * 845178676Ssamiwn_vap_create(struct ieee80211com *ic, 846220726Sbschmidt const char name[IFNAMSIZ], int unit, int opmode, int flags, 847220726Sbschmidt const uint8_t bssid[IEEE80211_ADDR_LEN], 848220726Sbschmidt const uint8_t mac[IEEE80211_ADDR_LEN]) 849178676Ssam{ 850178676Ssam struct iwn_vap *ivp; 851178676Ssam struct ieee80211vap *vap; 852178676Ssam 853178676Ssam if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ 854178676Ssam return NULL; 855178676Ssam ivp = (struct iwn_vap *) malloc(sizeof(struct iwn_vap), 856178676Ssam M_80211_VAP, M_NOWAIT | M_ZERO); 857178676Ssam if (ivp == NULL) 858178676Ssam return NULL; 859178676Ssam vap = &ivp->iv_vap; 860178676Ssam ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac); 861178676Ssam vap->iv_bmissthreshold = 10; /* override default */ 862198429Srpaulo /* Override with driver methods. */ 863178676Ssam ivp->iv_newstate = vap->iv_newstate; 864178676Ssam vap->iv_newstate = iwn_newstate; 865178676Ssam 866206358Srpaulo ieee80211_ratectl_init(vap); 867198429Srpaulo /* Complete setup. */ 868206476Sbschmidt ieee80211_vap_attach(vap, iwn_media_change, ieee80211_media_status); 869178676Ssam ic->ic_opmode = opmode; 870178676Ssam return vap; 871178676Ssam} 872178676Ssam 873178676Ssamstatic void 874178676Ssamiwn_vap_delete(struct ieee80211vap *vap) 875178676Ssam{ 876178676Ssam struct iwn_vap *ivp = IWN_VAP(vap); 877178676Ssam 878206358Srpaulo ieee80211_ratectl_deinit(vap); 879178676Ssam ieee80211_vap_detach(vap); 880178676Ssam free(ivp, M_80211_VAP); 881178676Ssam} 882178676Ssam 883206477Sbschmidtstatic int 884220635Sbschmidtiwn_detach(device_t dev) 885178676Ssam{ 886178676Ssam struct iwn_softc *sc = device_get_softc(dev); 887198429Srpaulo struct ifnet *ifp = sc->sc_ifp; 888198429Srpaulo struct ieee80211com *ic; 889220721Sbschmidt int qid; 890178676Ssam 891198429Srpaulo if (ifp != NULL) { 892198429Srpaulo ic = ifp->if_l2com; 893198429Srpaulo 894198429Srpaulo ieee80211_draintask(ic, &sc->sc_reinit_task); 895198429Srpaulo ieee80211_draintask(ic, &sc->sc_radioon_task); 896198429Srpaulo ieee80211_draintask(ic, &sc->sc_radiooff_task); 897198429Srpaulo 898198429Srpaulo iwn_stop(sc); 899220667Sbschmidt callout_drain(&sc->watchdog_to); 900220667Sbschmidt callout_drain(&sc->calib_to); 901198429Srpaulo ieee80211_ifdetach(ic); 902198429Srpaulo } 903198429Srpaulo 904220725Sbschmidt /* Uninstall interrupt handler. */ 905220723Sbschmidt if (sc->irq != NULL) { 906220723Sbschmidt bus_teardown_intr(dev, sc->irq, sc->sc_ih); 907220723Sbschmidt bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq); 908220723Sbschmidt if (sc->irq_rid == 1) 909220723Sbschmidt pci_release_msi(dev); 910220723Sbschmidt } 911220723Sbschmidt 912201209Srpaulo /* Free DMA resources. */ 913198429Srpaulo iwn_free_rx_ring(sc, &sc->rxq); 914220728Sbschmidt for (qid = 0; qid < sc->ntxqs; qid++) 915220728Sbschmidt iwn_free_tx_ring(sc, &sc->txq[qid]); 916198429Srpaulo iwn_free_sched(sc); 917198429Srpaulo iwn_free_kw(sc); 918201209Srpaulo if (sc->ict != NULL) 919201209Srpaulo iwn_free_ict(sc); 920198429Srpaulo iwn_free_fwmem(sc); 921198429Srpaulo 922198429Srpaulo if (sc->mem != NULL) 923198429Srpaulo bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem); 924198429Srpaulo 925198429Srpaulo if (ifp != NULL) 926198429Srpaulo if_free(ifp); 927198429Srpaulo 928198429Srpaulo IWN_LOCK_DESTROY(sc); 929178676Ssam return 0; 930178676Ssam} 931178676Ssam 932178676Ssamstatic int 933220723Sbschmidtiwn_shutdown(device_t dev) 934220723Sbschmidt{ 935220723Sbschmidt struct iwn_softc *sc = device_get_softc(dev); 936220723Sbschmidt 937220723Sbschmidt iwn_stop(sc); 938220723Sbschmidt return 0; 939220723Sbschmidt} 940220723Sbschmidt 941220723Sbschmidtstatic int 942220723Sbschmidtiwn_suspend(device_t dev) 943220723Sbschmidt{ 944220723Sbschmidt struct iwn_softc *sc = device_get_softc(dev); 945220723Sbschmidt struct ifnet *ifp = sc->sc_ifp; 946220723Sbschmidt struct ieee80211com *ic = ifp->if_l2com; 947220723Sbschmidt struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 948220723Sbschmidt 949220723Sbschmidt iwn_stop(sc); 950220723Sbschmidt if (vap != NULL) 951220723Sbschmidt ieee80211_stop(vap); 952220723Sbschmidt return 0; 953220723Sbschmidt} 954220723Sbschmidt 955220723Sbschmidtstatic int 956220723Sbschmidtiwn_resume(device_t dev) 957220723Sbschmidt{ 958220723Sbschmidt struct iwn_softc *sc = device_get_softc(dev); 959220723Sbschmidt struct ifnet *ifp = sc->sc_ifp; 960220723Sbschmidt struct ieee80211com *ic = ifp->if_l2com; 961220723Sbschmidt struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 962220723Sbschmidt 963220723Sbschmidt /* Clear device-specific "PCI retry timeout" register (41h). */ 964220723Sbschmidt pci_write_config(dev, 0x41, 0, 1); 965220723Sbschmidt 966220723Sbschmidt if (ifp->if_flags & IFF_UP) { 967220723Sbschmidt iwn_init(sc); 968220723Sbschmidt if (vap != NULL) 969220723Sbschmidt ieee80211_init(vap); 970220723Sbschmidt if (ifp->if_drv_flags & IFF_DRV_RUNNING) 971220723Sbschmidt iwn_start(ifp); 972220723Sbschmidt } 973220723Sbschmidt return 0; 974220723Sbschmidt} 975220723Sbschmidt 976220723Sbschmidtstatic int 977198429Srpauloiwn_nic_lock(struct iwn_softc *sc) 978178676Ssam{ 979198429Srpaulo int ntries; 980178676Ssam 981198429Srpaulo /* Request exclusive access to NIC. */ 982198429Srpaulo IWN_SETBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_MAC_ACCESS_REQ); 983178676Ssam 984198429Srpaulo /* Spin until we actually get the lock. */ 985198429Srpaulo for (ntries = 0; ntries < 1000; ntries++) { 986198429Srpaulo if ((IWN_READ(sc, IWN_GP_CNTRL) & 987220726Sbschmidt (IWN_GP_CNTRL_MAC_ACCESS_ENA | IWN_GP_CNTRL_SLEEP)) == 988198429Srpaulo IWN_GP_CNTRL_MAC_ACCESS_ENA) 989198429Srpaulo return 0; 990198429Srpaulo DELAY(10); 991198429Srpaulo } 992198429Srpaulo return ETIMEDOUT; 993198429Srpaulo} 994198429Srpaulo 995198429Srpaulostatic __inline void 996198429Srpauloiwn_nic_unlock(struct iwn_softc *sc) 997198429Srpaulo{ 998198429Srpaulo IWN_CLRBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_MAC_ACCESS_REQ); 999198429Srpaulo} 1000198429Srpaulo 1001198429Srpaulostatic __inline uint32_t 1002198429Srpauloiwn_prph_read(struct iwn_softc *sc, uint32_t addr) 1003198429Srpaulo{ 1004198429Srpaulo IWN_WRITE(sc, IWN_PRPH_RADDR, IWN_PRPH_DWORD | addr); 1005201209Srpaulo IWN_BARRIER_READ_WRITE(sc); 1006198429Srpaulo return IWN_READ(sc, IWN_PRPH_RDATA); 1007198429Srpaulo} 1008198429Srpaulo 1009198429Srpaulostatic __inline void 1010198429Srpauloiwn_prph_write(struct iwn_softc *sc, uint32_t addr, uint32_t data) 1011198429Srpaulo{ 1012198429Srpaulo IWN_WRITE(sc, IWN_PRPH_WADDR, IWN_PRPH_DWORD | addr); 1013201209Srpaulo IWN_BARRIER_WRITE(sc); 1014198429Srpaulo IWN_WRITE(sc, IWN_PRPH_WDATA, data); 1015198429Srpaulo} 1016198429Srpaulo 1017198429Srpaulostatic __inline void 1018198429Srpauloiwn_prph_setbits(struct iwn_softc *sc, uint32_t addr, uint32_t mask) 1019198429Srpaulo{ 1020198429Srpaulo iwn_prph_write(sc, addr, iwn_prph_read(sc, addr) | mask); 1021198429Srpaulo} 1022198429Srpaulo 1023198429Srpaulostatic __inline void 1024198429Srpauloiwn_prph_clrbits(struct iwn_softc *sc, uint32_t addr, uint32_t mask) 1025198429Srpaulo{ 1026198429Srpaulo iwn_prph_write(sc, addr, iwn_prph_read(sc, addr) & ~mask); 1027198429Srpaulo} 1028198429Srpaulo 1029198429Srpaulostatic __inline void 1030198429Srpauloiwn_prph_write_region_4(struct iwn_softc *sc, uint32_t addr, 1031198429Srpaulo const uint32_t *data, int count) 1032198429Srpaulo{ 1033198429Srpaulo for (; count > 0; count--, data++, addr += 4) 1034198429Srpaulo iwn_prph_write(sc, addr, *data); 1035198429Srpaulo} 1036198429Srpaulo 1037198429Srpaulostatic __inline uint32_t 1038198429Srpauloiwn_mem_read(struct iwn_softc *sc, uint32_t addr) 1039198429Srpaulo{ 1040198429Srpaulo IWN_WRITE(sc, IWN_MEM_RADDR, addr); 1041201209Srpaulo IWN_BARRIER_READ_WRITE(sc); 1042198429Srpaulo return IWN_READ(sc, IWN_MEM_RDATA); 1043198429Srpaulo} 1044198429Srpaulo 1045198429Srpaulostatic __inline void 1046198429Srpauloiwn_mem_write(struct iwn_softc *sc, uint32_t addr, uint32_t data) 1047198429Srpaulo{ 1048198429Srpaulo IWN_WRITE(sc, IWN_MEM_WADDR, addr); 1049201209Srpaulo IWN_BARRIER_WRITE(sc); 1050198429Srpaulo IWN_WRITE(sc, IWN_MEM_WDATA, data); 1051198429Srpaulo} 1052198429Srpaulo 1053198429Srpaulostatic __inline void 1054198429Srpauloiwn_mem_write_2(struct iwn_softc *sc, uint32_t addr, uint16_t data) 1055198429Srpaulo{ 1056198429Srpaulo uint32_t tmp; 1057198429Srpaulo 1058198429Srpaulo tmp = iwn_mem_read(sc, addr & ~3); 1059198429Srpaulo if (addr & 3) 1060198429Srpaulo tmp = (tmp & 0x0000ffff) | data << 16; 1061198429Srpaulo else 1062198429Srpaulo tmp = (tmp & 0xffff0000) | data; 1063198429Srpaulo iwn_mem_write(sc, addr & ~3, tmp); 1064198429Srpaulo} 1065198429Srpaulo 1066198429Srpaulostatic __inline void 1067198429Srpauloiwn_mem_read_region_4(struct iwn_softc *sc, uint32_t addr, uint32_t *data, 1068198429Srpaulo int count) 1069198429Srpaulo{ 1070198429Srpaulo for (; count > 0; count--, addr += 4) 1071198429Srpaulo *data++ = iwn_mem_read(sc, addr); 1072198429Srpaulo} 1073198429Srpaulo 1074198429Srpaulostatic __inline void 1075198429Srpauloiwn_mem_set_region_4(struct iwn_softc *sc, uint32_t addr, uint32_t val, 1076198429Srpaulo int count) 1077198429Srpaulo{ 1078198429Srpaulo for (; count > 0; count--, addr += 4) 1079198429Srpaulo iwn_mem_write(sc, addr, val); 1080198429Srpaulo} 1081198429Srpaulo 1082206477Sbschmidtstatic int 1083198429Srpauloiwn_eeprom_lock(struct iwn_softc *sc) 1084198429Srpaulo{ 1085198429Srpaulo int i, ntries; 1086198429Srpaulo 1087198429Srpaulo for (i = 0; i < 100; i++) { 1088198429Srpaulo /* Request exclusive access to EEPROM. */ 1089198429Srpaulo IWN_SETBITS(sc, IWN_HW_IF_CONFIG, 1090198429Srpaulo IWN_HW_IF_CONFIG_EEPROM_LOCKED); 1091198429Srpaulo 1092198429Srpaulo /* Spin until we actually get the lock. */ 1093198429Srpaulo for (ntries = 0; ntries < 100; ntries++) { 1094198429Srpaulo if (IWN_READ(sc, IWN_HW_IF_CONFIG) & 1095198429Srpaulo IWN_HW_IF_CONFIG_EEPROM_LOCKED) 1096198429Srpaulo return 0; 1097198429Srpaulo DELAY(10); 1098198429Srpaulo } 1099198429Srpaulo } 1100198429Srpaulo return ETIMEDOUT; 1101198429Srpaulo} 1102198429Srpaulo 1103198429Srpaulostatic __inline void 1104198429Srpauloiwn_eeprom_unlock(struct iwn_softc *sc) 1105198429Srpaulo{ 1106198429Srpaulo IWN_CLRBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_EEPROM_LOCKED); 1107198429Srpaulo} 1108198429Srpaulo 1109198429Srpaulo/* 1110198429Srpaulo * Initialize access by host to One Time Programmable ROM. 1111198429Srpaulo * NB: This kind of ROM can be found on 1000 or 6000 Series only. 1112198429Srpaulo */ 1113206477Sbschmidtstatic int 1114198429Srpauloiwn_init_otprom(struct iwn_softc *sc) 1115198429Srpaulo{ 1116203934Sbschmidt uint16_t prev, base, next; 1117201209Srpaulo int count, error; 1118198429Srpaulo 1119201209Srpaulo /* Wait for clock stabilization before accessing prph. */ 1120220726Sbschmidt if ((error = iwn_clock_wait(sc)) != 0) 1121198429Srpaulo return error; 1122198429Srpaulo 1123220726Sbschmidt if ((error = iwn_nic_lock(sc)) != 0) 1124198429Srpaulo return error; 1125198429Srpaulo iwn_prph_setbits(sc, IWN_APMG_PS, IWN_APMG_PS_RESET_REQ); 1126198429Srpaulo DELAY(5); 1127198429Srpaulo iwn_prph_clrbits(sc, IWN_APMG_PS, IWN_APMG_PS_RESET_REQ); 1128198429Srpaulo iwn_nic_unlock(sc); 1129198429Srpaulo 1130201209Srpaulo /* Set auto clock gate disable bit for HW with OTP shadow RAM. */ 1131201209Srpaulo if (sc->hw_type != IWN_HW_REV_TYPE_1000) { 1132201209Srpaulo IWN_SETBITS(sc, IWN_DBG_LINK_PWR_MGMT, 1133201209Srpaulo IWN_RESET_LINK_PWR_MGMT_DIS); 1134201209Srpaulo } 1135198429Srpaulo IWN_CLRBITS(sc, IWN_EEPROM_GP, IWN_EEPROM_GP_IF_OWNER); 1136198429Srpaulo /* Clear ECC status. */ 1137198429Srpaulo IWN_SETBITS(sc, IWN_OTP_GP, 1138198429Srpaulo IWN_OTP_GP_ECC_CORR_STTS | IWN_OTP_GP_ECC_UNCORR_STTS); 1139198429Srpaulo 1140201209Srpaulo /* 1141203934Sbschmidt * Find the block before last block (contains the EEPROM image) 1142203934Sbschmidt * for HW without OTP shadow RAM. 1143201209Srpaulo */ 1144201209Srpaulo if (sc->hw_type == IWN_HW_REV_TYPE_1000) { 1145201209Srpaulo /* Switch to absolute addressing mode. */ 1146201209Srpaulo IWN_CLRBITS(sc, IWN_OTP_GP, IWN_OTP_GP_RELATIVE_ACCESS); 1147203934Sbschmidt base = prev = 0; 1148201209Srpaulo for (count = 0; count < IWN1000_OTP_NBLOCKS; count++) { 1149201209Srpaulo error = iwn_read_prom_data(sc, base, &next, 2); 1150201209Srpaulo if (error != 0) 1151201209Srpaulo return error; 1152201209Srpaulo if (next == 0) /* End of linked-list. */ 1153201209Srpaulo break; 1154203934Sbschmidt prev = base; 1155201209Srpaulo base = le16toh(next); 1156201209Srpaulo } 1157203934Sbschmidt if (count == 0 || count == IWN1000_OTP_NBLOCKS) 1158201209Srpaulo return EIO; 1159201209Srpaulo /* Skip "next" word. */ 1160203934Sbschmidt sc->prom_base = prev + 1; 1161201209Srpaulo } 1162178676Ssam return 0; 1163178676Ssam} 1164178676Ssam 1165206477Sbschmidtstatic int 1166198429Srpauloiwn_read_prom_data(struct iwn_softc *sc, uint32_t addr, void *data, int count) 1167198429Srpaulo{ 1168220723Sbschmidt uint8_t *out = data; 1169198429Srpaulo uint32_t val, tmp; 1170198429Srpaulo int ntries; 1171198429Srpaulo 1172201209Srpaulo addr += sc->prom_base; 1173198429Srpaulo for (; count > 0; count -= 2, addr++) { 1174198429Srpaulo IWN_WRITE(sc, IWN_EEPROM, addr << 2); 1175201209Srpaulo for (ntries = 0; ntries < 10; ntries++) { 1176198429Srpaulo val = IWN_READ(sc, IWN_EEPROM); 1177198429Srpaulo if (val & IWN_EEPROM_READ_VALID) 1178198429Srpaulo break; 1179198429Srpaulo DELAY(5); 1180198429Srpaulo } 1181201209Srpaulo if (ntries == 10) { 1182198429Srpaulo device_printf(sc->sc_dev, 1183198429Srpaulo "timeout reading ROM at 0x%x\n", addr); 1184198429Srpaulo return ETIMEDOUT; 1185198429Srpaulo } 1186198429Srpaulo if (sc->sc_flags & IWN_FLAG_HAS_OTPROM) { 1187198429Srpaulo /* OTPROM, check for ECC errors. */ 1188198429Srpaulo tmp = IWN_READ(sc, IWN_OTP_GP); 1189198429Srpaulo if (tmp & IWN_OTP_GP_ECC_UNCORR_STTS) { 1190198429Srpaulo device_printf(sc->sc_dev, 1191198429Srpaulo "OTPROM ECC error at 0x%x\n", addr); 1192198429Srpaulo return EIO; 1193198429Srpaulo } 1194198429Srpaulo if (tmp & IWN_OTP_GP_ECC_CORR_STTS) { 1195198429Srpaulo /* Correctable ECC error, clear bit. */ 1196198429Srpaulo IWN_SETBITS(sc, IWN_OTP_GP, 1197198429Srpaulo IWN_OTP_GP_ECC_CORR_STTS); 1198198429Srpaulo } 1199198429Srpaulo } 1200198429Srpaulo *out++ = val >> 16; 1201198429Srpaulo if (count > 1) 1202198429Srpaulo *out++ = val >> 24; 1203198429Srpaulo } 1204198429Srpaulo return 0; 1205198429Srpaulo} 1206198429Srpaulo 1207178676Ssamstatic void 1208178676Ssamiwn_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nsegs, int error) 1209178676Ssam{ 1210198429Srpaulo if (error != 0) 1211198429Srpaulo return; 1212198429Srpaulo KASSERT(nsegs == 1, ("too many DMA segments, %d should be 1", nsegs)); 1213198429Srpaulo *(bus_addr_t *)arg = segs[0].ds_addr; 1214178676Ssam} 1215178676Ssam 1216198429Srpaulostatic int 1217178676Ssamiwn_dma_contig_alloc(struct iwn_softc *sc, struct iwn_dma_info *dma, 1218220691Sbschmidt void **kvap, bus_size_t size, bus_size_t alignment) 1219178676Ssam{ 1220198429Srpaulo int error; 1221178676Ssam 1222220723Sbschmidt dma->tag = NULL; 1223178676Ssam dma->size = size; 1224178676Ssam 1225198429Srpaulo error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), alignment, 1226178676Ssam 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, size, 1227220691Sbschmidt 1, size, BUS_DMA_NOWAIT, NULL, NULL, &dma->tag); 1228220711Sbschmidt if (error != 0) 1229178676Ssam goto fail; 1230220711Sbschmidt 1231178676Ssam error = bus_dmamem_alloc(dma->tag, (void **)&dma->vaddr, 1232220691Sbschmidt BUS_DMA_NOWAIT | BUS_DMA_ZERO | BUS_DMA_COHERENT, &dma->map); 1233220711Sbschmidt if (error != 0) 1234178676Ssam goto fail; 1235220711Sbschmidt 1236220691Sbschmidt error = bus_dmamap_load(dma->tag, dma->map, dma->vaddr, size, 1237220691Sbschmidt iwn_dma_map_addr, &dma->paddr, BUS_DMA_NOWAIT); 1238220711Sbschmidt if (error != 0) 1239178676Ssam goto fail; 1240178676Ssam 1241220704Sbschmidt bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); 1242220704Sbschmidt 1243178676Ssam if (kvap != NULL) 1244178676Ssam *kvap = dma->vaddr; 1245220726Sbschmidt 1246178676Ssam return 0; 1247220726Sbschmidt 1248220726Sbschmidtfail: iwn_dma_contig_free(dma); 1249178676Ssam return error; 1250178676Ssam} 1251178676Ssam 1252206477Sbschmidtstatic void 1253178676Ssamiwn_dma_contig_free(struct iwn_dma_info *dma) 1254178676Ssam{ 1255220701Sbschmidt if (dma->map != NULL) { 1256220701Sbschmidt if (dma->vaddr != NULL) { 1257220701Sbschmidt bus_dmamap_sync(dma->tag, dma->map, 1258220701Sbschmidt BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 1259220701Sbschmidt bus_dmamap_unload(dma->tag, dma->map); 1260178676Ssam bus_dmamem_free(dma->tag, &dma->vaddr, dma->map); 1261220701Sbschmidt dma->vaddr = NULL; 1262178676Ssam } 1263220701Sbschmidt bus_dmamap_destroy(dma->tag, dma->map); 1264220701Sbschmidt dma->map = NULL; 1265220701Sbschmidt } 1266220701Sbschmidt if (dma->tag != NULL) { 1267178676Ssam bus_dma_tag_destroy(dma->tag); 1268220701Sbschmidt dma->tag = NULL; 1269178676Ssam } 1270178676Ssam} 1271178676Ssam 1272206477Sbschmidtstatic int 1273198429Srpauloiwn_alloc_sched(struct iwn_softc *sc) 1274178676Ssam{ 1275198429Srpaulo /* TX scheduler rings must be aligned on a 1KB boundary. */ 1276220691Sbschmidt return iwn_dma_contig_alloc(sc, &sc->sched_dma, (void **)&sc->sched, 1277220728Sbschmidt sc->schedsz, 1024); 1278178676Ssam} 1279178676Ssam 1280206477Sbschmidtstatic void 1281198429Srpauloiwn_free_sched(struct iwn_softc *sc) 1282178676Ssam{ 1283198429Srpaulo iwn_dma_contig_free(&sc->sched_dma); 1284178676Ssam} 1285178676Ssam 1286206477Sbschmidtstatic int 1287178676Ssamiwn_alloc_kw(struct iwn_softc *sc) 1288178676Ssam{ 1289198429Srpaulo /* "Keep Warm" page must be aligned on a 4KB boundary. */ 1290220691Sbschmidt return iwn_dma_contig_alloc(sc, &sc->kw_dma, NULL, 4096, 4096); 1291178676Ssam} 1292178676Ssam 1293206477Sbschmidtstatic void 1294178676Ssamiwn_free_kw(struct iwn_softc *sc) 1295178676Ssam{ 1296178676Ssam iwn_dma_contig_free(&sc->kw_dma); 1297178676Ssam} 1298178676Ssam 1299206477Sbschmidtstatic int 1300201209Srpauloiwn_alloc_ict(struct iwn_softc *sc) 1301201209Srpaulo{ 1302201209Srpaulo /* ICT table must be aligned on a 4KB boundary. */ 1303220691Sbschmidt return iwn_dma_contig_alloc(sc, &sc->ict_dma, (void **)&sc->ict, 1304220691Sbschmidt IWN_ICT_SIZE, 4096); 1305201209Srpaulo} 1306201209Srpaulo 1307206477Sbschmidtstatic void 1308201209Srpauloiwn_free_ict(struct iwn_softc *sc) 1309201209Srpaulo{ 1310201209Srpaulo iwn_dma_contig_free(&sc->ict_dma); 1311201209Srpaulo} 1312201209Srpaulo 1313206477Sbschmidtstatic int 1314178676Ssamiwn_alloc_fwmem(struct iwn_softc *sc) 1315178676Ssam{ 1316198429Srpaulo /* Must be aligned on a 16-byte boundary. */ 1317220728Sbschmidt return iwn_dma_contig_alloc(sc, &sc->fw_dma, NULL, sc->fwsz, 16); 1318178676Ssam} 1319178676Ssam 1320206477Sbschmidtstatic void 1321178676Ssamiwn_free_fwmem(struct iwn_softc *sc) 1322178676Ssam{ 1323178676Ssam iwn_dma_contig_free(&sc->fw_dma); 1324178676Ssam} 1325178676Ssam 1326206477Sbschmidtstatic int 1327178676Ssamiwn_alloc_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring) 1328178676Ssam{ 1329198429Srpaulo bus_size_t size; 1330178676Ssam int i, error; 1331178676Ssam 1332178676Ssam ring->cur = 0; 1333178676Ssam 1334198429Srpaulo /* Allocate RX descriptors (256-byte aligned). */ 1335198429Srpaulo size = IWN_RX_RING_COUNT * sizeof (uint32_t); 1336220691Sbschmidt error = iwn_dma_contig_alloc(sc, &ring->desc_dma, (void **)&ring->desc, 1337220691Sbschmidt size, 256); 1338178676Ssam if (error != 0) { 1339178676Ssam device_printf(sc->sc_dev, 1340220711Sbschmidt "%s: could not allocate RX ring DMA memory, error %d\n", 1341178676Ssam __func__, error); 1342178676Ssam goto fail; 1343178676Ssam } 1344178676Ssam 1345220702Sbschmidt /* Allocate RX status area (16-byte aligned). */ 1346220702Sbschmidt error = iwn_dma_contig_alloc(sc, &ring->stat_dma, (void **)&ring->stat, 1347220702Sbschmidt sizeof (struct iwn_rx_status), 16); 1348198429Srpaulo if (error != 0) { 1349198429Srpaulo device_printf(sc->sc_dev, 1350220711Sbschmidt "%s: could not allocate RX status DMA memory, error %d\n", 1351178676Ssam __func__, error); 1352198429Srpaulo goto fail; 1353198429Srpaulo } 1354178676Ssam 1355220702Sbschmidt /* Create RX buffer DMA tag. */ 1356220702Sbschmidt error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, 1357220702Sbschmidt BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, 1358220702Sbschmidt IWN_RBUF_SIZE, 1, IWN_RBUF_SIZE, BUS_DMA_NOWAIT, NULL, NULL, 1359220702Sbschmidt &ring->data_dmat); 1360198429Srpaulo if (error != 0) { 1361198429Srpaulo device_printf(sc->sc_dev, 1362220711Sbschmidt "%s: could not create RX buf DMA tag, error %d\n", 1363198429Srpaulo __func__, error); 1364198429Srpaulo goto fail; 1365198429Srpaulo } 1366198429Srpaulo 1367178676Ssam /* 1368198429Srpaulo * Allocate and map RX buffers. 1369178676Ssam */ 1370178676Ssam for (i = 0; i < IWN_RX_RING_COUNT; i++) { 1371178676Ssam struct iwn_rx_data *data = &ring->data[i]; 1372178676Ssam bus_addr_t paddr; 1373178676Ssam 1374201209Srpaulo error = bus_dmamap_create(ring->data_dmat, 0, &data->map); 1375178676Ssam if (error != 0) { 1376178676Ssam device_printf(sc->sc_dev, 1377220711Sbschmidt "%s: could not create RX buf DMA map, error %d\n", 1378178676Ssam __func__, error); 1379178676Ssam goto fail; 1380178676Ssam } 1381198429Srpaulo 1382220692Sbschmidt data->m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, 1383220692Sbschmidt IWN_RBUF_SIZE); 1384198439Srpaulo if (data->m == NULL) { 1385178676Ssam device_printf(sc->sc_dev, 1386220711Sbschmidt "%s: could not allocate RX mbuf\n", __func__); 1387220710Sbschmidt error = ENOBUFS; 1388178676Ssam goto fail; 1389178676Ssam } 1390198429Srpaulo 1391201209Srpaulo error = bus_dmamap_load(ring->data_dmat, data->map, 1392220692Sbschmidt mtod(data->m, void *), IWN_RBUF_SIZE, iwn_dma_map_addr, 1393220692Sbschmidt &paddr, BUS_DMA_NOWAIT); 1394178676Ssam if (error != 0 && error != EFBIG) { 1395178676Ssam device_printf(sc->sc_dev, 1396220711Sbschmidt "%s: can't not map mbuf, error %d\n", __func__, 1397220711Sbschmidt error); 1398178676Ssam goto fail; 1399178676Ssam } 1400178676Ssam 1401198429Srpaulo /* Set physical address of RX buffer (256-byte aligned). */ 1402178676Ssam ring->desc[i] = htole32(paddr >> 8); 1403178676Ssam } 1404220726Sbschmidt 1405178676Ssam bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, 1406178676Ssam BUS_DMASYNC_PREWRITE); 1407220726Sbschmidt 1408178676Ssam return 0; 1409220726Sbschmidt 1410220726Sbschmidtfail: iwn_free_rx_ring(sc, ring); 1411178676Ssam return error; 1412178676Ssam} 1413178676Ssam 1414206477Sbschmidtstatic void 1415178676Ssamiwn_reset_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring) 1416178676Ssam{ 1417178676Ssam int ntries; 1418178676Ssam 1419198429Srpaulo if (iwn_nic_lock(sc) == 0) { 1420198429Srpaulo IWN_WRITE(sc, IWN_FH_RX_CONFIG, 0); 1421198429Srpaulo for (ntries = 0; ntries < 1000; ntries++) { 1422198429Srpaulo if (IWN_READ(sc, IWN_FH_RX_STATUS) & 1423198429Srpaulo IWN_FH_RX_STATUS_IDLE) 1424198429Srpaulo break; 1425198429Srpaulo DELAY(10); 1426198429Srpaulo } 1427198429Srpaulo iwn_nic_unlock(sc); 1428198429Srpaulo } 1429178676Ssam ring->cur = 0; 1430198429Srpaulo sc->last_rx_valid = 0; 1431178676Ssam} 1432178676Ssam 1433206477Sbschmidtstatic void 1434178676Ssamiwn_free_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring) 1435178676Ssam{ 1436178676Ssam int i; 1437178676Ssam 1438178676Ssam iwn_dma_contig_free(&ring->desc_dma); 1439198429Srpaulo iwn_dma_contig_free(&ring->stat_dma); 1440178676Ssam 1441198429Srpaulo for (i = 0; i < IWN_RX_RING_COUNT; i++) { 1442198429Srpaulo struct iwn_rx_data *data = &ring->data[i]; 1443198429Srpaulo 1444198429Srpaulo if (data->m != NULL) { 1445201209Srpaulo bus_dmamap_sync(ring->data_dmat, data->map, 1446198439Srpaulo BUS_DMASYNC_POSTREAD); 1447201209Srpaulo bus_dmamap_unload(ring->data_dmat, data->map); 1448198429Srpaulo m_freem(data->m); 1449220710Sbschmidt data->m = NULL; 1450198429Srpaulo } 1451201209Srpaulo if (data->map != NULL) 1452201209Srpaulo bus_dmamap_destroy(ring->data_dmat, data->map); 1453198429Srpaulo } 1454220701Sbschmidt if (ring->data_dmat != NULL) { 1455220701Sbschmidt bus_dma_tag_destroy(ring->data_dmat); 1456220701Sbschmidt ring->data_dmat = NULL; 1457220701Sbschmidt } 1458178676Ssam} 1459178676Ssam 1460206477Sbschmidtstatic int 1461178676Ssamiwn_alloc_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring, int qid) 1462178676Ssam{ 1463220723Sbschmidt bus_addr_t paddr; 1464178676Ssam bus_size_t size; 1465178676Ssam int i, error; 1466178676Ssam 1467178676Ssam ring->qid = qid; 1468178676Ssam ring->queued = 0; 1469178676Ssam ring->cur = 0; 1470178676Ssam 1471220725Sbschmidt /* Allocate TX descriptors (256-byte aligned). */ 1472220726Sbschmidt size = IWN_TX_RING_COUNT * sizeof (struct iwn_tx_desc); 1473220691Sbschmidt error = iwn_dma_contig_alloc(sc, &ring->desc_dma, (void **)&ring->desc, 1474220691Sbschmidt size, 256); 1475178676Ssam if (error != 0) { 1476178676Ssam device_printf(sc->sc_dev, 1477198429Srpaulo "%s: could not allocate TX ring DMA memory, error %d\n", 1478178676Ssam __func__, error); 1479178676Ssam goto fail; 1480178676Ssam } 1481198429Srpaulo 1482220726Sbschmidt size = IWN_TX_RING_COUNT * sizeof (struct iwn_tx_cmd); 1483220691Sbschmidt error = iwn_dma_contig_alloc(sc, &ring->cmd_dma, (void **)&ring->cmd, 1484220691Sbschmidt size, 4); 1485178676Ssam if (error != 0) { 1486178676Ssam device_printf(sc->sc_dev, 1487198429Srpaulo "%s: could not allocate TX cmd DMA memory, error %d\n", 1488178676Ssam __func__, error); 1489178676Ssam goto fail; 1490178676Ssam } 1491178676Ssam 1492198429Srpaulo error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, 1493220726Sbschmidt BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, 1494220726Sbschmidt IWN_MAX_SCATTER - 1, MCLBYTES, BUS_DMA_NOWAIT, NULL, NULL, 1495220726Sbschmidt &ring->data_dmat); 1496198429Srpaulo if (error != 0) { 1497198429Srpaulo device_printf(sc->sc_dev, 1498220711Sbschmidt "%s: could not create TX buf DMA tag, error %d\n", 1499178676Ssam __func__, error); 1500198429Srpaulo goto fail; 1501198429Srpaulo } 1502178676Ssam 1503198429Srpaulo paddr = ring->cmd_dma.paddr; 1504178676Ssam for (i = 0; i < IWN_TX_RING_COUNT; i++) { 1505178676Ssam struct iwn_tx_data *data = &ring->data[i]; 1506178676Ssam 1507198429Srpaulo data->cmd_paddr = paddr; 1508198429Srpaulo data->scratch_paddr = paddr + 12; 1509198429Srpaulo paddr += sizeof (struct iwn_tx_cmd); 1510198429Srpaulo 1511201209Srpaulo error = bus_dmamap_create(ring->data_dmat, 0, &data->map); 1512178676Ssam if (error != 0) { 1513178676Ssam device_printf(sc->sc_dev, 1514220711Sbschmidt "%s: could not create TX buf DMA map, error %d\n", 1515178676Ssam __func__, error); 1516178676Ssam goto fail; 1517178676Ssam } 1518178676Ssam } 1519178676Ssam return 0; 1520220726Sbschmidt 1521220726Sbschmidtfail: iwn_free_tx_ring(sc, ring); 1522178676Ssam return error; 1523178676Ssam} 1524178676Ssam 1525206477Sbschmidtstatic void 1526178676Ssamiwn_reset_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring) 1527178676Ssam{ 1528198429Srpaulo int i; 1529178676Ssam 1530178676Ssam for (i = 0; i < IWN_TX_RING_COUNT; i++) { 1531178676Ssam struct iwn_tx_data *data = &ring->data[i]; 1532178676Ssam 1533178676Ssam if (data->m != NULL) { 1534220704Sbschmidt bus_dmamap_sync(ring->data_dmat, data->map, 1535220704Sbschmidt BUS_DMASYNC_POSTWRITE); 1536201209Srpaulo bus_dmamap_unload(ring->data_dmat, data->map); 1537178676Ssam m_freem(data->m); 1538178676Ssam data->m = NULL; 1539178676Ssam } 1540178676Ssam } 1541198429Srpaulo /* Clear TX descriptors. */ 1542198429Srpaulo memset(ring->desc, 0, ring->desc_dma.size); 1543198439Srpaulo bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, 1544198439Srpaulo BUS_DMASYNC_PREWRITE); 1545198429Srpaulo sc->qfullmsk &= ~(1 << ring->qid); 1546178676Ssam ring->queued = 0; 1547178676Ssam ring->cur = 0; 1548178676Ssam} 1549178676Ssam 1550206477Sbschmidtstatic void 1551178676Ssamiwn_free_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring) 1552178676Ssam{ 1553178676Ssam int i; 1554178676Ssam 1555178676Ssam iwn_dma_contig_free(&ring->desc_dma); 1556178676Ssam iwn_dma_contig_free(&ring->cmd_dma); 1557178676Ssam 1558201209Srpaulo for (i = 0; i < IWN_TX_RING_COUNT; i++) { 1559201209Srpaulo struct iwn_tx_data *data = &ring->data[i]; 1560178676Ssam 1561201209Srpaulo if (data->m != NULL) { 1562201209Srpaulo bus_dmamap_sync(ring->data_dmat, data->map, 1563201209Srpaulo BUS_DMASYNC_POSTWRITE); 1564201209Srpaulo bus_dmamap_unload(ring->data_dmat, data->map); 1565201209Srpaulo m_freem(data->m); 1566178676Ssam } 1567201209Srpaulo if (data->map != NULL) 1568201209Srpaulo bus_dmamap_destroy(ring->data_dmat, data->map); 1569178676Ssam } 1570220701Sbschmidt if (ring->data_dmat != NULL) { 1571220701Sbschmidt bus_dma_tag_destroy(ring->data_dmat); 1572220701Sbschmidt ring->data_dmat = NULL; 1573220701Sbschmidt } 1574178676Ssam} 1575178676Ssam 1576206477Sbschmidtstatic void 1577201209Srpauloiwn5000_ict_reset(struct iwn_softc *sc) 1578201209Srpaulo{ 1579201209Srpaulo /* Disable interrupts. */ 1580201209Srpaulo IWN_WRITE(sc, IWN_INT_MASK, 0); 1581201209Srpaulo 1582201209Srpaulo /* Reset ICT table. */ 1583201209Srpaulo memset(sc->ict, 0, IWN_ICT_SIZE); 1584201209Srpaulo sc->ict_cur = 0; 1585201209Srpaulo 1586220725Sbschmidt /* Set physical address of ICT table (4KB aligned). */ 1587201209Srpaulo DPRINTF(sc, IWN_DEBUG_RESET, "%s: enabling ICT\n", __func__); 1588201209Srpaulo IWN_WRITE(sc, IWN_DRAM_INT_TBL, IWN_DRAM_INT_TBL_ENABLE | 1589201209Srpaulo IWN_DRAM_INT_TBL_WRAP_CHECK | sc->ict_dma.paddr >> 12); 1590201209Srpaulo 1591201209Srpaulo /* Enable periodic RX interrupt. */ 1592201209Srpaulo sc->int_mask |= IWN_INT_RX_PERIODIC; 1593201209Srpaulo /* Switch to ICT interrupt mode in driver. */ 1594201209Srpaulo sc->sc_flags |= IWN_FLAG_USE_ICT; 1595201209Srpaulo 1596201209Srpaulo /* Re-enable interrupts. */ 1597201209Srpaulo IWN_WRITE(sc, IWN_INT, 0xffffffff); 1598201209Srpaulo IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask); 1599201209Srpaulo} 1600201209Srpaulo 1601206477Sbschmidtstatic int 1602198429Srpauloiwn_read_eeprom(struct iwn_softc *sc, uint8_t macaddr[IEEE80211_ADDR_LEN]) 1603178676Ssam{ 1604220728Sbschmidt struct iwn_ops *ops = &sc->ops; 1605220723Sbschmidt uint16_t val; 1606198429Srpaulo int error; 1607178676Ssam 1608198429Srpaulo /* Check whether adapter has an EEPROM or an OTPROM. */ 1609198429Srpaulo if (sc->hw_type >= IWN_HW_REV_TYPE_1000 && 1610198429Srpaulo (IWN_READ(sc, IWN_OTP_GP) & IWN_OTP_GP_DEV_SEL_OTP)) 1611198429Srpaulo sc->sc_flags |= IWN_FLAG_HAS_OTPROM; 1612198429Srpaulo DPRINTF(sc, IWN_DEBUG_RESET, "%s found\n", 1613198429Srpaulo (sc->sc_flags & IWN_FLAG_HAS_OTPROM) ? "OTPROM" : "EEPROM"); 1614178676Ssam 1615201209Srpaulo /* Adapter has to be powered on for EEPROM access to work. */ 1616220726Sbschmidt if ((error = iwn_apm_init(sc)) != 0) { 1617201209Srpaulo device_printf(sc->sc_dev, 1618220726Sbschmidt "%s: could not power ON adapter, error %d\n", __func__, 1619220726Sbschmidt error); 1620201209Srpaulo return error; 1621201209Srpaulo } 1622201209Srpaulo 1623198429Srpaulo if ((IWN_READ(sc, IWN_EEPROM_GP) & 0x7) == 0) { 1624198429Srpaulo device_printf(sc->sc_dev, "%s: bad ROM signature\n", __func__); 1625198429Srpaulo return EIO; 1626198429Srpaulo } 1627220726Sbschmidt if ((error = iwn_eeprom_lock(sc)) != 0) { 1628220726Sbschmidt device_printf(sc->sc_dev, "%s: could not lock ROM, error %d\n", 1629198429Srpaulo __func__, error); 1630198429Srpaulo return error; 1631198429Srpaulo } 1632201209Srpaulo if (sc->sc_flags & IWN_FLAG_HAS_OTPROM) { 1633220726Sbschmidt if ((error = iwn_init_otprom(sc)) != 0) { 1634201209Srpaulo device_printf(sc->sc_dev, 1635201209Srpaulo "%s: could not initialize OTPROM, error %d\n", 1636201209Srpaulo __func__, error); 1637201209Srpaulo return error; 1638201209Srpaulo } 1639198429Srpaulo } 1640178676Ssam 1641220729Sbschmidt iwn_read_prom_data(sc, IWN_EEPROM_SKU_CAP, &val, 2); 1642220729Sbschmidt DPRINTF(sc, IWN_DEBUG_RESET, "SKU capabilities=0x%04x\n", le16toh(val)); 1643220729Sbschmidt /* Check if HT support is bonded out. */ 1644220729Sbschmidt if (val & htole16(IWN_EEPROM_SKU_CAP_11N)) 1645220729Sbschmidt sc->sc_flags |= IWN_FLAG_HAS_11N; 1646220729Sbschmidt 1647198429Srpaulo iwn_read_prom_data(sc, IWN_EEPROM_RFCFG, &val, 2); 1648198429Srpaulo sc->rfcfg = le16toh(val); 1649198429Srpaulo DPRINTF(sc, IWN_DEBUG_RESET, "radio config=0x%04x\n", sc->rfcfg); 1650220727Sbschmidt /* Read Tx/Rx chains from ROM unless it's known to be broken. */ 1651220727Sbschmidt if (sc->txchainmask == 0) 1652220727Sbschmidt sc->txchainmask = IWN_RFCFG_TXANTMSK(sc->rfcfg); 1653220727Sbschmidt if (sc->rxchainmask == 0) 1654220727Sbschmidt sc->rxchainmask = IWN_RFCFG_RXANTMSK(sc->rfcfg); 1655178676Ssam 1656198429Srpaulo /* Read MAC address. */ 1657198429Srpaulo iwn_read_prom_data(sc, IWN_EEPROM_MAC, macaddr, 6); 1658178676Ssam 1659198429Srpaulo /* Read adapter-specific information from EEPROM. */ 1660220728Sbschmidt ops->read_eeprom(sc); 1661178676Ssam 1662201209Srpaulo iwn_apm_stop(sc); /* Power OFF adapter. */ 1663201209Srpaulo 1664198429Srpaulo iwn_eeprom_unlock(sc); 1665198429Srpaulo return 0; 1666178676Ssam} 1667178676Ssam 1668206477Sbschmidtstatic void 1669198429Srpauloiwn4965_read_eeprom(struct iwn_softc *sc) 1670178676Ssam{ 1671201209Srpaulo uint32_t addr; 1672220723Sbschmidt uint16_t val; 1673198429Srpaulo int i; 1674178676Ssam 1675220725Sbschmidt /* Read regulatory domain (4 ASCII characters). */ 1676198429Srpaulo iwn_read_prom_data(sc, IWN4965_EEPROM_DOMAIN, sc->eeprom_domain, 4); 1677178676Ssam 1678220725Sbschmidt /* Read the list of authorized channels (20MHz ones only). */ 1679221636Sbschmidt for (i = 0; i < 7; i++) { 1680201209Srpaulo addr = iwn4965_regulatory_bands[i]; 1681201209Srpaulo iwn_read_eeprom_channels(sc, i, addr); 1682201209Srpaulo } 1683198429Srpaulo 1684198429Srpaulo /* Read maximum allowed TX power for 2GHz and 5GHz bands. */ 1685198429Srpaulo iwn_read_prom_data(sc, IWN4965_EEPROM_MAXPOW, &val, 2); 1686198429Srpaulo sc->maxpwr2GHz = val & 0xff; 1687198429Srpaulo sc->maxpwr5GHz = val >> 8; 1688198429Srpaulo /* Check that EEPROM values are within valid range. */ 1689198429Srpaulo if (sc->maxpwr5GHz < 20 || sc->maxpwr5GHz > 50) 1690198429Srpaulo sc->maxpwr5GHz = 38; 1691198429Srpaulo if (sc->maxpwr2GHz < 20 || sc->maxpwr2GHz > 50) 1692198429Srpaulo sc->maxpwr2GHz = 38; 1693198429Srpaulo DPRINTF(sc, IWN_DEBUG_RESET, "maxpwr 2GHz=%d 5GHz=%d\n", 1694198429Srpaulo sc->maxpwr2GHz, sc->maxpwr5GHz); 1695198429Srpaulo 1696198429Srpaulo /* Read samples for each TX power group. */ 1697198429Srpaulo iwn_read_prom_data(sc, IWN4965_EEPROM_BANDS, sc->bands, 1698198429Srpaulo sizeof sc->bands); 1699198429Srpaulo 1700198429Srpaulo /* Read voltage at which samples were taken. */ 1701198429Srpaulo iwn_read_prom_data(sc, IWN4965_EEPROM_VOLTAGE, &val, 2); 1702198429Srpaulo sc->eeprom_voltage = (int16_t)le16toh(val); 1703198429Srpaulo DPRINTF(sc, IWN_DEBUG_RESET, "voltage=%d (in 0.3V)\n", 1704198429Srpaulo sc->eeprom_voltage); 1705198429Srpaulo 1706198429Srpaulo#ifdef IWN_DEBUG 1707198429Srpaulo /* Print samples. */ 1708201209Srpaulo if (sc->sc_debug & IWN_DEBUG_ANY) { 1709198429Srpaulo for (i = 0; i < IWN_NBANDS; i++) 1710198429Srpaulo iwn4965_print_power_group(sc, i); 1711178676Ssam } 1712198429Srpaulo#endif 1713178676Ssam} 1714178676Ssam 1715198429Srpaulo#ifdef IWN_DEBUG 1716206477Sbschmidtstatic void 1717198429Srpauloiwn4965_print_power_group(struct iwn_softc *sc, int i) 1718178676Ssam{ 1719198429Srpaulo struct iwn4965_eeprom_band *band = &sc->bands[i]; 1720198429Srpaulo struct iwn4965_eeprom_chan_samples *chans = band->chans; 1721198429Srpaulo int j, c; 1722178676Ssam 1723198429Srpaulo printf("===band %d===\n", i); 1724198429Srpaulo printf("chan lo=%d, chan hi=%d\n", band->lo, band->hi); 1725198429Srpaulo printf("chan1 num=%d\n", chans[0].num); 1726198429Srpaulo for (c = 0; c < 2; c++) { 1727198429Srpaulo for (j = 0; j < IWN_NSAMPLES; j++) { 1728198429Srpaulo printf("chain %d, sample %d: temp=%d gain=%d " 1729198429Srpaulo "power=%d pa_det=%d\n", c, j, 1730198429Srpaulo chans[0].samples[c][j].temp, 1731198429Srpaulo chans[0].samples[c][j].gain, 1732198429Srpaulo chans[0].samples[c][j].power, 1733198429Srpaulo chans[0].samples[c][j].pa_det); 1734198429Srpaulo } 1735198429Srpaulo } 1736198429Srpaulo printf("chan2 num=%d\n", chans[1].num); 1737198429Srpaulo for (c = 0; c < 2; c++) { 1738198429Srpaulo for (j = 0; j < IWN_NSAMPLES; j++) { 1739198429Srpaulo printf("chain %d, sample %d: temp=%d gain=%d " 1740198429Srpaulo "power=%d pa_det=%d\n", c, j, 1741198429Srpaulo chans[1].samples[c][j].temp, 1742198429Srpaulo chans[1].samples[c][j].gain, 1743198429Srpaulo chans[1].samples[c][j].power, 1744198429Srpaulo chans[1].samples[c][j].pa_det); 1745198429Srpaulo } 1746198429Srpaulo } 1747178676Ssam} 1748198429Srpaulo#endif 1749178676Ssam 1750206477Sbschmidtstatic void 1751198429Srpauloiwn5000_read_eeprom(struct iwn_softc *sc) 1752178676Ssam{ 1753206444Sbschmidt struct iwn5000_eeprom_calib_hdr hdr; 1754220674Sbschmidt int32_t volt; 1755220723Sbschmidt uint32_t base, addr; 1756220723Sbschmidt uint16_t val; 1757198429Srpaulo int i; 1758178676Ssam 1759220725Sbschmidt /* Read regulatory domain (4 ASCII characters). */ 1760198429Srpaulo iwn_read_prom_data(sc, IWN5000_EEPROM_REG, &val, 2); 1761198429Srpaulo base = le16toh(val); 1762198429Srpaulo iwn_read_prom_data(sc, base + IWN5000_EEPROM_DOMAIN, 1763198429Srpaulo sc->eeprom_domain, 4); 1764178676Ssam 1765220725Sbschmidt /* Read the list of authorized channels (20MHz ones only). */ 1766221636Sbschmidt for (i = 0; i < 7; i++) { 1767221635Sbschmidt if (sc->hw_type >= IWN_HW_REV_TYPE_6000) 1768221635Sbschmidt addr = base + iwn6000_regulatory_bands[i]; 1769221635Sbschmidt else 1770221635Sbschmidt addr = base + iwn5000_regulatory_bands[i]; 1771201209Srpaulo iwn_read_eeprom_channels(sc, i, addr); 1772198429Srpaulo } 1773178676Ssam 1774201209Srpaulo /* Read enhanced TX power information for 6000 Series. */ 1775201209Srpaulo if (sc->hw_type >= IWN_HW_REV_TYPE_6000) 1776201209Srpaulo iwn_read_eeprom_enhinfo(sc); 1777201209Srpaulo 1778198429Srpaulo iwn_read_prom_data(sc, IWN5000_EEPROM_CAL, &val, 2); 1779198429Srpaulo base = le16toh(val); 1780206444Sbschmidt iwn_read_prom_data(sc, base, &hdr, sizeof hdr); 1781206444Sbschmidt DPRINTF(sc, IWN_DEBUG_CALIBRATE, 1782220726Sbschmidt "%s: calib version=%u pa type=%u voltage=%u\n", __func__, 1783220726Sbschmidt hdr.version, hdr.pa_type, le16toh(hdr.volt)); 1784210108Sbschmidt sc->calib_ver = hdr.version; 1785206444Sbschmidt 1786198429Srpaulo if (sc->hw_type == IWN_HW_REV_TYPE_5150) { 1787201209Srpaulo /* Compute temperature offset. */ 1788198429Srpaulo iwn_read_prom_data(sc, base + IWN5000_EEPROM_TEMP, &val, 2); 1789220674Sbschmidt sc->eeprom_temp = le16toh(val); 1790198429Srpaulo iwn_read_prom_data(sc, base + IWN5000_EEPROM_VOLT, &val, 2); 1791198429Srpaulo volt = le16toh(val); 1792220674Sbschmidt sc->temp_off = sc->eeprom_temp - (volt / -5); 1793201209Srpaulo DPRINTF(sc, IWN_DEBUG_CALIBRATE, "temp=%d volt=%d offset=%dK\n", 1794220674Sbschmidt sc->eeprom_temp, volt, sc->temp_off); 1795220674Sbschmidt } else { 1796220674Sbschmidt /* Read crystal calibration. */ 1797220674Sbschmidt iwn_read_prom_data(sc, base + IWN5000_EEPROM_CRYSTAL, 1798220674Sbschmidt &sc->eeprom_crystal, sizeof (uint32_t)); 1799220674Sbschmidt DPRINTF(sc, IWN_DEBUG_CALIBRATE, "crystal calibration 0x%08x\n", 1800220674Sbschmidt le32toh(sc->eeprom_crystal)); 1801178676Ssam } 1802178676Ssam} 1803178676Ssam 1804201209Srpaulo/* 1805201209Srpaulo * Translate EEPROM flags to net80211. 1806201209Srpaulo */ 1807201209Srpaulostatic uint32_t 1808201209Srpauloiwn_eeprom_channel_flags(struct iwn_eeprom_chan *channel) 1809201209Srpaulo{ 1810201209Srpaulo uint32_t nflags; 1811201209Srpaulo 1812201209Srpaulo nflags = 0; 1813201209Srpaulo if ((channel->flags & IWN_EEPROM_CHAN_ACTIVE) == 0) 1814201209Srpaulo nflags |= IEEE80211_CHAN_PASSIVE; 1815201209Srpaulo if ((channel->flags & IWN_EEPROM_CHAN_IBSS) == 0) 1816201209Srpaulo nflags |= IEEE80211_CHAN_NOADHOC; 1817201209Srpaulo if (channel->flags & IWN_EEPROM_CHAN_RADAR) { 1818201209Srpaulo nflags |= IEEE80211_CHAN_DFS; 1819201209Srpaulo /* XXX apparently IBSS may still be marked */ 1820201209Srpaulo nflags |= IEEE80211_CHAN_NOADHOC; 1821201209Srpaulo } 1822201209Srpaulo 1823201209Srpaulo return nflags; 1824201209Srpaulo} 1825201209Srpaulo 1826198429Srpaulostatic void 1827201209Srpauloiwn_read_eeprom_band(struct iwn_softc *sc, int n) 1828178676Ssam{ 1829198429Srpaulo struct ifnet *ifp = sc->sc_ifp; 1830198429Srpaulo struct ieee80211com *ic = ifp->if_l2com; 1831201209Srpaulo struct iwn_eeprom_chan *channels = sc->eeprom_channels[n]; 1832201209Srpaulo const struct iwn_chan_band *band = &iwn_bands[n]; 1833198429Srpaulo struct ieee80211_channel *c; 1834220687Sbschmidt uint8_t chan; 1835220687Sbschmidt int i, nflags; 1836178676Ssam 1837198429Srpaulo for (i = 0; i < band->nchan; i++) { 1838198429Srpaulo if (!(channels[i].flags & IWN_EEPROM_CHAN_VALID)) { 1839198429Srpaulo DPRINTF(sc, IWN_DEBUG_RESET, 1840198429Srpaulo "skip chan %d flags 0x%x maxpwr %d\n", 1841198429Srpaulo band->chan[i], channels[i].flags, 1842198429Srpaulo channels[i].maxpwr); 1843198429Srpaulo continue; 1844198429Srpaulo } 1845198429Srpaulo chan = band->chan[i]; 1846201209Srpaulo nflags = iwn_eeprom_channel_flags(&channels[i]); 1847178676Ssam 1848198429Srpaulo c = &ic->ic_channels[ic->ic_nchans++]; 1849198429Srpaulo c->ic_ieee = chan; 1850198429Srpaulo c->ic_maxregpower = channels[i].maxpwr; 1851198429Srpaulo c->ic_maxpower = 2*c->ic_maxregpower; 1852206445Sbschmidt 1853201209Srpaulo if (n == 0) { /* 2GHz band */ 1854220726Sbschmidt c->ic_freq = ieee80211_ieee2mhz(chan, IEEE80211_CHAN_G); 1855198429Srpaulo /* G =>'s B is supported */ 1856198429Srpaulo c->ic_flags = IEEE80211_CHAN_B | nflags; 1857198429Srpaulo c = &ic->ic_channels[ic->ic_nchans++]; 1858198429Srpaulo c[0] = c[-1]; 1859198429Srpaulo c->ic_flags = IEEE80211_CHAN_G | nflags; 1860198429Srpaulo } else { /* 5GHz band */ 1861220726Sbschmidt c->ic_freq = ieee80211_ieee2mhz(chan, IEEE80211_CHAN_A); 1862198429Srpaulo c->ic_flags = IEEE80211_CHAN_A | nflags; 1863178676Ssam } 1864220723Sbschmidt 1865220723Sbschmidt /* Save maximum allowed TX power for this channel. */ 1866220723Sbschmidt sc->maxpwr[chan] = channels[i].maxpwr; 1867220723Sbschmidt 1868220723Sbschmidt DPRINTF(sc, IWN_DEBUG_RESET, 1869220726Sbschmidt "add chan %d flags 0x%x maxpwr %d\n", chan, 1870220726Sbschmidt channels[i].flags, channels[i].maxpwr); 1871220723Sbschmidt 1872221636Sbschmidt if (sc->sc_flags & IWN_FLAG_HAS_11N) { 1873221636Sbschmidt /* add HT20, HT40 added separately */ 1874221636Sbschmidt c = &ic->ic_channels[ic->ic_nchans++]; 1875221636Sbschmidt c[0] = c[-1]; 1876221636Sbschmidt c->ic_flags |= IEEE80211_CHAN_HT20; 1877221636Sbschmidt } 1878178676Ssam } 1879178676Ssam} 1880178676Ssam 1881198429Srpaulostatic void 1882201209Srpauloiwn_read_eeprom_ht40(struct iwn_softc *sc, int n) 1883178676Ssam{ 1884198429Srpaulo struct ifnet *ifp = sc->sc_ifp; 1885198429Srpaulo struct ieee80211com *ic = ifp->if_l2com; 1886201209Srpaulo struct iwn_eeprom_chan *channels = sc->eeprom_channels[n]; 1887201209Srpaulo const struct iwn_chan_band *band = &iwn_bands[n]; 1888198429Srpaulo struct ieee80211_channel *c, *cent, *extc; 1889221636Sbschmidt uint8_t chan; 1890221636Sbschmidt int i, nflags; 1891178676Ssam 1892221636Sbschmidt if (!(sc->sc_flags & IWN_FLAG_HAS_11N)) 1893221636Sbschmidt return; 1894221636Sbschmidt 1895198429Srpaulo for (i = 0; i < band->nchan; i++) { 1896221636Sbschmidt if (!(channels[i].flags & IWN_EEPROM_CHAN_VALID)) { 1897198429Srpaulo DPRINTF(sc, IWN_DEBUG_RESET, 1898198429Srpaulo "skip chan %d flags 0x%x maxpwr %d\n", 1899198429Srpaulo band->chan[i], channels[i].flags, 1900198429Srpaulo channels[i].maxpwr); 1901198429Srpaulo continue; 1902198429Srpaulo } 1903221636Sbschmidt chan = band->chan[i]; 1904221636Sbschmidt nflags = iwn_eeprom_channel_flags(&channels[i]); 1905221636Sbschmidt 1906198429Srpaulo /* 1907198429Srpaulo * Each entry defines an HT40 channel pair; find the 1908198429Srpaulo * center channel, then the extension channel above. 1909198429Srpaulo */ 1910221636Sbschmidt cent = ieee80211_find_channel_byieee(ic, chan, 1911221636Sbschmidt (n == 5 ? IEEE80211_CHAN_G : IEEE80211_CHAN_A)); 1912198429Srpaulo if (cent == NULL) { /* XXX shouldn't happen */ 1913198429Srpaulo device_printf(sc->sc_dev, 1914221636Sbschmidt "%s: no entry for channel %d\n", __func__, chan); 1915198429Srpaulo continue; 1916198429Srpaulo } 1917198429Srpaulo extc = ieee80211_find_channel(ic, cent->ic_freq+20, 1918221636Sbschmidt (n == 5 ? IEEE80211_CHAN_G : IEEE80211_CHAN_A)); 1919198429Srpaulo if (extc == NULL) { 1920198429Srpaulo DPRINTF(sc, IWN_DEBUG_RESET, 1921221636Sbschmidt "%s: skip chan %d, extension channel not found\n", 1922221636Sbschmidt __func__, chan); 1923198429Srpaulo continue; 1924198429Srpaulo } 1925178676Ssam 1926198429Srpaulo DPRINTF(sc, IWN_DEBUG_RESET, 1927198429Srpaulo "add ht40 chan %d flags 0x%x maxpwr %d\n", 1928221636Sbschmidt chan, channels[i].flags, channels[i].maxpwr); 1929178676Ssam 1930198429Srpaulo c = &ic->ic_channels[ic->ic_nchans++]; 1931198429Srpaulo c[0] = cent[0]; 1932198429Srpaulo c->ic_extieee = extc->ic_ieee; 1933198429Srpaulo c->ic_flags &= ~IEEE80211_CHAN_HT; 1934221636Sbschmidt c->ic_flags |= IEEE80211_CHAN_HT40U | nflags; 1935198429Srpaulo c = &ic->ic_channels[ic->ic_nchans++]; 1936198429Srpaulo c[0] = extc[0]; 1937198429Srpaulo c->ic_extieee = cent->ic_ieee; 1938198429Srpaulo c->ic_flags &= ~IEEE80211_CHAN_HT; 1939221636Sbschmidt c->ic_flags |= IEEE80211_CHAN_HT40D | nflags; 1940178676Ssam } 1941198429Srpaulo} 1942178676Ssam 1943198429Srpaulostatic void 1944201209Srpauloiwn_read_eeprom_channels(struct iwn_softc *sc, int n, uint32_t addr) 1945198429Srpaulo{ 1946198429Srpaulo struct ifnet *ifp = sc->sc_ifp; 1947198429Srpaulo struct ieee80211com *ic = ifp->if_l2com; 1948178676Ssam 1949201209Srpaulo iwn_read_prom_data(sc, addr, &sc->eeprom_channels[n], 1950201209Srpaulo iwn_bands[n].nchan * sizeof (struct iwn_eeprom_chan)); 1951201209Srpaulo 1952198429Srpaulo if (n < 5) 1953201209Srpaulo iwn_read_eeprom_band(sc, n); 1954198429Srpaulo else 1955201209Srpaulo iwn_read_eeprom_ht40(sc, n); 1956198429Srpaulo ieee80211_sort_channels(ic->ic_channels, ic->ic_nchans); 1957178676Ssam} 1958178676Ssam 1959220723Sbschmidtstatic struct iwn_eeprom_chan * 1960220723Sbschmidtiwn_find_eeprom_channel(struct iwn_softc *sc, struct ieee80211_channel *c) 1961220723Sbschmidt{ 1962221636Sbschmidt int band, chan, i, j; 1963220723Sbschmidt 1964221636Sbschmidt if (IEEE80211_IS_CHAN_HT40(c)) { 1965221636Sbschmidt band = IEEE80211_IS_CHAN_5GHZ(c) ? 6 : 5; 1966221636Sbschmidt if (IEEE80211_IS_CHAN_HT40D(c)) 1967221636Sbschmidt chan = c->ic_extieee; 1968221636Sbschmidt else 1969221636Sbschmidt chan = c->ic_ieee; 1970221636Sbschmidt for (i = 0; i < iwn_bands[band].nchan; i++) { 1971221636Sbschmidt if (iwn_bands[band].chan[i] == chan) 1972221636Sbschmidt return &sc->eeprom_channels[band][i]; 1973220723Sbschmidt } 1974221636Sbschmidt } else { 1975221636Sbschmidt for (j = 0; j < 5; j++) { 1976221636Sbschmidt for (i = 0; i < iwn_bands[j].nchan; i++) { 1977221636Sbschmidt if (iwn_bands[j].chan[i] == c->ic_ieee) 1978221636Sbschmidt return &sc->eeprom_channels[j][i]; 1979221636Sbschmidt } 1980221636Sbschmidt } 1981220723Sbschmidt } 1982220723Sbschmidt return NULL; 1983220723Sbschmidt} 1984220723Sbschmidt 1985220723Sbschmidt/* 1986220723Sbschmidt * Enforce flags read from EEPROM. 1987220723Sbschmidt */ 1988220723Sbschmidtstatic int 1989220723Sbschmidtiwn_setregdomain(struct ieee80211com *ic, struct ieee80211_regdomain *rd, 1990220723Sbschmidt int nchan, struct ieee80211_channel chans[]) 1991220723Sbschmidt{ 1992220723Sbschmidt struct iwn_softc *sc = ic->ic_ifp->if_softc; 1993220723Sbschmidt int i; 1994220723Sbschmidt 1995220723Sbschmidt for (i = 0; i < nchan; i++) { 1996220723Sbschmidt struct ieee80211_channel *c = &chans[i]; 1997220723Sbschmidt struct iwn_eeprom_chan *channel; 1998220723Sbschmidt 1999220723Sbschmidt channel = iwn_find_eeprom_channel(sc, c); 2000220723Sbschmidt if (channel == NULL) { 2001220723Sbschmidt if_printf(ic->ic_ifp, 2002220723Sbschmidt "%s: invalid channel %u freq %u/0x%x\n", 2003220723Sbschmidt __func__, c->ic_ieee, c->ic_freq, c->ic_flags); 2004220723Sbschmidt return EINVAL; 2005220723Sbschmidt } 2006220723Sbschmidt c->ic_flags |= iwn_eeprom_channel_flags(channel); 2007220723Sbschmidt } 2008220723Sbschmidt 2009220723Sbschmidt return 0; 2010220723Sbschmidt} 2011220723Sbschmidt 2012201209Srpaulo#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) 2013201209Srpaulo 2014206477Sbschmidtstatic void 2015201209Srpauloiwn_read_eeprom_enhinfo(struct iwn_softc *sc) 2016201209Srpaulo{ 2017201209Srpaulo struct iwn_eeprom_enhinfo enhinfo[35]; 2018221637Sbschmidt struct ifnet *ifp = sc->sc_ifp; 2019221637Sbschmidt struct ieee80211com *ic = ifp->if_l2com; 2020221637Sbschmidt struct ieee80211_channel *c; 2021201209Srpaulo uint16_t val, base; 2022201209Srpaulo int8_t maxpwr; 2023221637Sbschmidt uint8_t flags; 2024221637Sbschmidt int i, j; 2025201209Srpaulo 2026201209Srpaulo iwn_read_prom_data(sc, IWN5000_EEPROM_REG, &val, 2); 2027201209Srpaulo base = le16toh(val); 2028201209Srpaulo iwn_read_prom_data(sc, base + IWN6000_EEPROM_ENHINFO, 2029201209Srpaulo enhinfo, sizeof enhinfo); 2030201209Srpaulo 2031201209Srpaulo for (i = 0; i < nitems(enhinfo); i++) { 2032221637Sbschmidt flags = enhinfo[i].flags; 2033221637Sbschmidt if (!(flags & IWN_ENHINFO_VALID)) 2034201209Srpaulo continue; /* Skip invalid entries. */ 2035201209Srpaulo 2036201209Srpaulo maxpwr = 0; 2037201209Srpaulo if (sc->txchainmask & IWN_ANT_A) 2038201209Srpaulo maxpwr = MAX(maxpwr, enhinfo[i].chain[0]); 2039201209Srpaulo if (sc->txchainmask & IWN_ANT_B) 2040201209Srpaulo maxpwr = MAX(maxpwr, enhinfo[i].chain[1]); 2041201209Srpaulo if (sc->txchainmask & IWN_ANT_C) 2042201209Srpaulo maxpwr = MAX(maxpwr, enhinfo[i].chain[2]); 2043201209Srpaulo if (sc->ntxchains == 2) 2044201209Srpaulo maxpwr = MAX(maxpwr, enhinfo[i].mimo2); 2045201209Srpaulo else if (sc->ntxchains == 3) 2046201209Srpaulo maxpwr = MAX(maxpwr, enhinfo[i].mimo3); 2047201209Srpaulo 2048221637Sbschmidt for (j = 0; j < ic->ic_nchans; j++) { 2049221637Sbschmidt c = &ic->ic_channels[j]; 2050221637Sbschmidt if ((flags & IWN_ENHINFO_5GHZ)) { 2051221637Sbschmidt if (!IEEE80211_IS_CHAN_A(c)) 2052221637Sbschmidt continue; 2053221637Sbschmidt } else if ((flags & IWN_ENHINFO_OFDM)) { 2054221637Sbschmidt if (!IEEE80211_IS_CHAN_G(c)) 2055221637Sbschmidt continue; 2056221637Sbschmidt } else if (!IEEE80211_IS_CHAN_B(c)) 2057221637Sbschmidt continue; 2058221637Sbschmidt if ((flags & IWN_ENHINFO_HT40)) { 2059221637Sbschmidt if (!IEEE80211_IS_CHAN_HT40(c)) 2060221637Sbschmidt continue; 2061221637Sbschmidt } else { 2062221637Sbschmidt if (IEEE80211_IS_CHAN_HT40(c)) 2063221637Sbschmidt continue; 2064221637Sbschmidt } 2065221637Sbschmidt if (enhinfo[i].chan != 0 && 2066221637Sbschmidt enhinfo[i].chan != c->ic_ieee) 2067221637Sbschmidt continue; 2068221637Sbschmidt 2069221637Sbschmidt DPRINTF(sc, IWN_DEBUG_RESET, 2070221637Sbschmidt "channel %d(%x), maxpwr %d\n", c->ic_ieee, 2071221637Sbschmidt c->ic_flags, maxpwr / 2); 2072221637Sbschmidt c->ic_maxregpower = maxpwr / 2; 2073221637Sbschmidt c->ic_maxpower = maxpwr; 2074221637Sbschmidt } 2075201209Srpaulo } 2076201209Srpaulo} 2077201209Srpaulo 2078206477Sbschmidtstatic struct ieee80211_node * 2079198429Srpauloiwn_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) 2080178676Ssam{ 2081198429Srpaulo return malloc(sizeof (struct iwn_node), M_80211_NODE,M_NOWAIT | M_ZERO); 2082198429Srpaulo} 2083178676Ssam 2084221648Sbschmidtstatic __inline int 2085221648Sbschmidtrate2plcp(int rate) 2086221648Sbschmidt{ 2087221648Sbschmidt switch (rate & 0xff) { 2088221648Sbschmidt case 12: return 0xd; 2089221648Sbschmidt case 18: return 0xf; 2090221648Sbschmidt case 24: return 0x5; 2091221648Sbschmidt case 36: return 0x7; 2092221648Sbschmidt case 48: return 0x9; 2093221648Sbschmidt case 72: return 0xb; 2094221648Sbschmidt case 96: return 0x1; 2095221648Sbschmidt case 108: return 0x3; 2096221648Sbschmidt case 2: return 10; 2097221648Sbschmidt case 4: return 20; 2098221648Sbschmidt case 11: return 55; 2099221648Sbschmidt case 22: return 110; 2100221648Sbschmidt } 2101221648Sbschmidt return 0; 2102221648Sbschmidt} 2103221648Sbschmidt 2104220715Sbschmidtstatic void 2105220715Sbschmidtiwn_newassoc(struct ieee80211_node *ni, int isnew) 2106220715Sbschmidt{ 2107221648Sbschmidt struct ieee80211com *ic = ni->ni_ic; 2108221648Sbschmidt struct iwn_softc *sc = ic->ic_ifp->if_softc; 2109220715Sbschmidt struct iwn_node *wn = (void *)ni; 2110221649Sbschmidt uint8_t txant1, txant2; 2111221648Sbschmidt int i, plcp, rate, ridx; 2112220715Sbschmidt 2113221648Sbschmidt /* Use the first valid TX antenna. */ 2114221649Sbschmidt txant1 = IWN_LSB(sc->txchainmask); 2115221649Sbschmidt txant2 = IWN_LSB(sc->txchainmask & ~txant1); 2116221648Sbschmidt 2117221649Sbschmidt if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) { 2118221649Sbschmidt ridx = ni->ni_rates.rs_nrates - 1; 2119221649Sbschmidt for (i = ni->ni_htrates.rs_nrates - 1; i >= 0; i--) { 2120221649Sbschmidt plcp = ni->ni_htrates.rs_rates[i] | IWN_RFLAG_MCS; 2121221649Sbschmidt if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) { 2122221649Sbschmidt plcp |= IWN_RFLAG_HT40; 2123221649Sbschmidt if (ni->ni_htcap & IEEE80211_HTCAP_SHORTGI40) 2124221649Sbschmidt plcp |= IWN_RFLAG_SGI; 2125221649Sbschmidt } else if (ni->ni_htcap & IEEE80211_HTCAP_SHORTGI20) 2126221649Sbschmidt plcp |= IWN_RFLAG_SGI; 2127221649Sbschmidt if (i > 7) 2128221649Sbschmidt plcp |= IWN_RFLAG_ANT(txant1 | txant2); 2129221649Sbschmidt else 2130221649Sbschmidt plcp |= IWN_RFLAG_ANT(txant1); 2131221649Sbschmidt if (ridx >= 0) { 2132221649Sbschmidt rate = ni->ni_rates.rs_rates[ridx]; 2133221649Sbschmidt rate &= IEEE80211_RATE_VAL; 2134221649Sbschmidt wn->ridx[rate] = plcp; 2135221649Sbschmidt } 2136221649Sbschmidt wn->ridx[IEEE80211_RATE_MCS | i] = plcp; 2137221649Sbschmidt ridx--; 2138221649Sbschmidt } 2139221649Sbschmidt } else { 2140221649Sbschmidt for (i = 0; i < ni->ni_rates.rs_nrates; i++) { 2141221649Sbschmidt rate = ni->ni_rates.rs_rates[i] & IEEE80211_RATE_VAL; 2142221648Sbschmidt 2143221649Sbschmidt plcp = rate2plcp(rate); 2144221649Sbschmidt ridx = ic->ic_rt->rateCodeToIndex[rate]; 2145221649Sbschmidt if (ridx < IWN_RIDX_OFDM6 && 2146221649Sbschmidt IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) 2147221649Sbschmidt plcp |= IWN_RFLAG_CCK; 2148221649Sbschmidt plcp |= IWN_RFLAG_ANT(txant1); 2149221649Sbschmidt wn->ridx[rate] = htole32(plcp); 2150221649Sbschmidt } 2151220715Sbschmidt } 2152220715Sbschmidt} 2153220715Sbschmidt 2154206477Sbschmidtstatic int 2155198429Srpauloiwn_media_change(struct ifnet *ifp) 2156178676Ssam{ 2157220726Sbschmidt int error; 2158220726Sbschmidt 2159220726Sbschmidt error = ieee80211_media_change(ifp); 2160198429Srpaulo /* NB: only the fixed rate can change and that doesn't need a reset */ 2161198429Srpaulo return (error == ENETRESET ? 0 : error); 2162198429Srpaulo} 2163178676Ssam 2164206477Sbschmidtstatic int 2165198429Srpauloiwn_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) 2166198429Srpaulo{ 2167198429Srpaulo struct iwn_vap *ivp = IWN_VAP(vap); 2168198429Srpaulo struct ieee80211com *ic = vap->iv_ic; 2169198429Srpaulo struct iwn_softc *sc = ic->ic_ifp->if_softc; 2170220688Sbschmidt int error = 0; 2171178676Ssam 2172198429Srpaulo DPRINTF(sc, IWN_DEBUG_STATE, "%s: %s -> %s\n", __func__, 2173220726Sbschmidt ieee80211_state_name[vap->iv_state], ieee80211_state_name[nstate]); 2174178676Ssam 2175198429Srpaulo IEEE80211_UNLOCK(ic); 2176198429Srpaulo IWN_LOCK(sc); 2177220667Sbschmidt callout_stop(&sc->calib_to); 2178178676Ssam 2179210114Sbschmidt switch (nstate) { 2180210114Sbschmidt case IEEE80211_S_ASSOC: 2181210114Sbschmidt if (vap->iv_state != IEEE80211_S_RUN) 2182210114Sbschmidt break; 2183210114Sbschmidt /* FALLTHROUGH */ 2184210114Sbschmidt case IEEE80211_S_AUTH: 2185210114Sbschmidt if (vap->iv_state == IEEE80211_S_AUTH) 2186210114Sbschmidt break; 2187210114Sbschmidt 2188210114Sbschmidt /* 2189210114Sbschmidt * !AUTH -> AUTH transition requires state reset to handle 2190210114Sbschmidt * reassociations correctly. 2191210114Sbschmidt */ 2192198439Srpaulo sc->rxon.associd = 0; 2193198439Srpaulo sc->rxon.filter &= ~htole32(IWN_FILTER_BSS); 2194220667Sbschmidt sc->calib.state = IWN_CALIB_STATE_INIT; 2195220667Sbschmidt 2196220688Sbschmidt if ((error = iwn_auth(sc, vap)) != 0) { 2197220688Sbschmidt device_printf(sc->sc_dev, 2198220688Sbschmidt "%s: could not move to auth state\n", __func__); 2199220688Sbschmidt } 2200210114Sbschmidt break; 2201210114Sbschmidt 2202210114Sbschmidt case IEEE80211_S_RUN: 2203198429Srpaulo /* 2204210114Sbschmidt * RUN -> RUN transition; Just restart the timers. 2205210114Sbschmidt */ 2206220667Sbschmidt if (vap->iv_state == IEEE80211_S_RUN) { 2207220667Sbschmidt sc->calib_cnt = 0; 2208210114Sbschmidt break; 2209210114Sbschmidt } 2210210114Sbschmidt 2211210114Sbschmidt /* 2212198429Srpaulo * !RUN -> RUN requires setting the association id 2213198429Srpaulo * which is done with a firmware cmd. We also defer 2214198429Srpaulo * starting the timers until that work is done. 2215198429Srpaulo */ 2216220688Sbschmidt if ((error = iwn_run(sc, vap)) != 0) { 2217220688Sbschmidt device_printf(sc->sc_dev, 2218220688Sbschmidt "%s: could not move to run state\n", __func__); 2219220688Sbschmidt } 2220210114Sbschmidt break; 2221210114Sbschmidt 2222220667Sbschmidt case IEEE80211_S_INIT: 2223220667Sbschmidt sc->calib.state = IWN_CALIB_STATE_INIT; 2224220667Sbschmidt break; 2225220667Sbschmidt 2226210114Sbschmidt default: 2227210114Sbschmidt break; 2228178676Ssam } 2229198429Srpaulo IWN_UNLOCK(sc); 2230198429Srpaulo IEEE80211_LOCK(ic); 2231220688Sbschmidt if (error != 0) 2232220688Sbschmidt return error; 2233198429Srpaulo return ivp->iv_newstate(vap, nstate, arg); 2234178676Ssam} 2235178676Ssam 2236220667Sbschmidtstatic void 2237220667Sbschmidtiwn_calib_timeout(void *arg) 2238220667Sbschmidt{ 2239220667Sbschmidt struct iwn_softc *sc = arg; 2240220667Sbschmidt 2241220667Sbschmidt IWN_LOCK_ASSERT(sc); 2242220667Sbschmidt 2243220667Sbschmidt /* Force automatic TX power calibration every 60 secs. */ 2244220667Sbschmidt if (++sc->calib_cnt >= 120) { 2245220667Sbschmidt uint32_t flags = 0; 2246220667Sbschmidt 2247220667Sbschmidt DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s\n", 2248220667Sbschmidt "sending request for statistics"); 2249220667Sbschmidt (void)iwn_cmd(sc, IWN_CMD_GET_STATISTICS, &flags, 2250220667Sbschmidt sizeof flags, 1); 2251220667Sbschmidt sc->calib_cnt = 0; 2252220667Sbschmidt } 2253220667Sbschmidt callout_reset(&sc->calib_to, msecs_to_ticks(500), iwn_calib_timeout, 2254220667Sbschmidt sc); 2255220667Sbschmidt} 2256220667Sbschmidt 2257198429Srpaulo/* 2258198429Srpaulo * Process an RX_PHY firmware notification. This is usually immediately 2259198429Srpaulo * followed by an MPDU_RX_DONE notification. 2260198429Srpaulo */ 2261206477Sbschmidtstatic void 2262198429Srpauloiwn_rx_phy(struct iwn_softc *sc, struct iwn_rx_desc *desc, 2263198429Srpaulo struct iwn_rx_data *data) 2264178676Ssam{ 2265198429Srpaulo struct iwn_rx_stat *stat = (struct iwn_rx_stat *)(desc + 1); 2266198429Srpaulo 2267198429Srpaulo DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: received PHY stats\n", __func__); 2268202986Srpaulo bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD); 2269198429Srpaulo 2270198429Srpaulo /* Save RX statistics, they will be used on MPDU_RX_DONE. */ 2271198429Srpaulo memcpy(&sc->last_rx_stat, stat, sizeof (*stat)); 2272198429Srpaulo sc->last_rx_valid = 1; 2273178676Ssam} 2274178676Ssam 2275198429Srpaulo/* 2276198429Srpaulo * Process an RX_DONE (4965AGN only) or MPDU_RX_DONE firmware notification. 2277198429Srpaulo * Each MPDU_RX_DONE notification must be preceded by an RX_PHY one. 2278198429Srpaulo */ 2279206477Sbschmidtstatic void 2280198429Srpauloiwn_rx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, 2281178676Ssam struct iwn_rx_data *data) 2282178676Ssam{ 2283220728Sbschmidt struct iwn_ops *ops = &sc->ops; 2284178676Ssam struct ifnet *ifp = sc->sc_ifp; 2285178676Ssam struct ieee80211com *ic = ifp->if_l2com; 2286178676Ssam struct iwn_rx_ring *ring = &sc->rxq; 2287178676Ssam struct ieee80211_frame *wh; 2288178676Ssam struct ieee80211_node *ni; 2289198429Srpaulo struct mbuf *m, *m1; 2290178676Ssam struct iwn_rx_stat *stat; 2291178676Ssam caddr_t head; 2292178676Ssam bus_addr_t paddr; 2293198429Srpaulo uint32_t flags; 2294198429Srpaulo int error, len, rssi, nf; 2295178676Ssam 2296198429Srpaulo if (desc->type == IWN_MPDU_RX_DONE) { 2297198429Srpaulo /* Check for prior RX_PHY notification. */ 2298178676Ssam if (!sc->last_rx_valid) { 2299178676Ssam DPRINTF(sc, IWN_DEBUG_ANY, 2300201209Srpaulo "%s: missing RX_PHY\n", __func__); 2301178676Ssam return; 2302178676Ssam } 2303178676Ssam stat = &sc->last_rx_stat; 2304178676Ssam } else 2305178676Ssam stat = (struct iwn_rx_stat *)(desc + 1); 2306178676Ssam 2307201209Srpaulo bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD); 2308198439Srpaulo 2309178676Ssam if (stat->cfg_phy_len > IWN_STAT_MAXLEN) { 2310178676Ssam device_printf(sc->sc_dev, 2311220724Sbschmidt "%s: invalid RX statistic header, len %d\n", __func__, 2312220724Sbschmidt stat->cfg_phy_len); 2313178676Ssam return; 2314178676Ssam } 2315198429Srpaulo if (desc->type == IWN_MPDU_RX_DONE) { 2316198429Srpaulo struct iwn_rx_mpdu *mpdu = (struct iwn_rx_mpdu *)(desc + 1); 2317198429Srpaulo head = (caddr_t)(mpdu + 1); 2318198429Srpaulo len = le16toh(mpdu->len); 2319178676Ssam } else { 2320178676Ssam head = (caddr_t)(stat + 1) + stat->cfg_phy_len; 2321178676Ssam len = le16toh(stat->len); 2322178676Ssam } 2323178676Ssam 2324198429Srpaulo flags = le32toh(*(uint32_t *)(head + len)); 2325198429Srpaulo 2326198429Srpaulo /* Discard frames with a bad FCS early. */ 2327198429Srpaulo if ((flags & IWN_RX_NOERROR) != IWN_RX_NOERROR) { 2328220724Sbschmidt DPRINTF(sc, IWN_DEBUG_RECV, "%s: RX flags error %x\n", 2329198429Srpaulo __func__, flags); 2330178676Ssam ifp->if_ierrors++; 2331178676Ssam return; 2332178676Ssam } 2333198429Srpaulo /* Discard frames that are too short. */ 2334198429Srpaulo if (len < sizeof (*wh)) { 2335178676Ssam DPRINTF(sc, IWN_DEBUG_RECV, "%s: frame too short: %d\n", 2336178676Ssam __func__, len); 2337178676Ssam ifp->if_ierrors++; 2338178676Ssam return; 2339178676Ssam } 2340178676Ssam 2341220692Sbschmidt m1 = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, IWN_RBUF_SIZE); 2342198429Srpaulo if (m1 == NULL) { 2343178676Ssam DPRINTF(sc, IWN_DEBUG_ANY, "%s: no mbuf to restock ring\n", 2344178676Ssam __func__); 2345178676Ssam ifp->if_ierrors++; 2346178676Ssam return; 2347178676Ssam } 2348201209Srpaulo bus_dmamap_unload(ring->data_dmat, data->map); 2349201209Srpaulo 2350220692Sbschmidt error = bus_dmamap_load(ring->data_dmat, data->map, mtod(m1, void *), 2351220692Sbschmidt IWN_RBUF_SIZE, iwn_dma_map_addr, &paddr, BUS_DMA_NOWAIT); 2352178676Ssam if (error != 0 && error != EFBIG) { 2353178676Ssam device_printf(sc->sc_dev, 2354178676Ssam "%s: bus_dmamap_load failed, error %d\n", __func__, error); 2355198429Srpaulo m_freem(m1); 2356220693Sbschmidt 2357220693Sbschmidt /* Try to reload the old mbuf. */ 2358220693Sbschmidt error = bus_dmamap_load(ring->data_dmat, data->map, 2359220693Sbschmidt mtod(data->m, void *), IWN_RBUF_SIZE, iwn_dma_map_addr, 2360220693Sbschmidt &paddr, BUS_DMA_NOWAIT); 2361220693Sbschmidt if (error != 0 && error != EFBIG) { 2362220693Sbschmidt panic("%s: could not load old RX mbuf", __func__); 2363220693Sbschmidt } 2364220693Sbschmidt /* Physical address may have changed. */ 2365220693Sbschmidt ring->desc[ring->cur] = htole32(paddr >> 8); 2366220693Sbschmidt bus_dmamap_sync(ring->data_dmat, ring->desc_dma.map, 2367220693Sbschmidt BUS_DMASYNC_PREWRITE); 2368178676Ssam ifp->if_ierrors++; 2369178676Ssam return; 2370178676Ssam } 2371178676Ssam 2372178676Ssam m = data->m; 2373198429Srpaulo data->m = m1; 2374198429Srpaulo /* Update RX descriptor. */ 2375198429Srpaulo ring->desc[ring->cur] = htole32(paddr >> 8); 2376201209Srpaulo bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, 2377201209Srpaulo BUS_DMASYNC_PREWRITE); 2378198429Srpaulo 2379198429Srpaulo /* Finalize mbuf. */ 2380178676Ssam m->m_pkthdr.rcvif = ifp; 2381178676Ssam m->m_data = head; 2382178676Ssam m->m_pkthdr.len = m->m_len = len; 2383178676Ssam 2384198429Srpaulo /* Grab a reference to the source node. */ 2385178676Ssam wh = mtod(m, struct ieee80211_frame *); 2386178676Ssam ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh); 2387178676Ssam nf = (ni != NULL && ni->ni_vap->iv_state == IEEE80211_S_RUN && 2388178676Ssam (ic->ic_flags & IEEE80211_F_SCAN) == 0) ? sc->noise : -95; 2389178676Ssam 2390220728Sbschmidt rssi = ops->get_rssi(sc, stat); 2391220689Sbschmidt 2392192468Ssam if (ieee80211_radiotap_active(ic)) { 2393178676Ssam struct iwn_rx_radiotap_header *tap = &sc->sc_rxtap; 2394178676Ssam 2395178676Ssam tap->wr_flags = 0; 2396201209Srpaulo if (stat->flags & htole16(IWN_STAT_FLAG_SHPREAMBLE)) 2397192468Ssam tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; 2398220723Sbschmidt tap->wr_dbm_antsignal = (int8_t)rssi; 2399220723Sbschmidt tap->wr_dbm_antnoise = (int8_t)nf; 2400220723Sbschmidt tap->wr_tsft = stat->tstamp; 2401201209Srpaulo switch (stat->rate) { 2402201209Srpaulo /* CCK rates. */ 2403201209Srpaulo case 10: tap->wr_rate = 2; break; 2404201209Srpaulo case 20: tap->wr_rate = 4; break; 2405201209Srpaulo case 55: tap->wr_rate = 11; break; 2406201209Srpaulo case 110: tap->wr_rate = 22; break; 2407201209Srpaulo /* OFDM rates. */ 2408201209Srpaulo case 0xd: tap->wr_rate = 12; break; 2409201209Srpaulo case 0xf: tap->wr_rate = 18; break; 2410201209Srpaulo case 0x5: tap->wr_rate = 24; break; 2411201209Srpaulo case 0x7: tap->wr_rate = 36; break; 2412201209Srpaulo case 0x9: tap->wr_rate = 48; break; 2413201209Srpaulo case 0xb: tap->wr_rate = 72; break; 2414201209Srpaulo case 0x1: tap->wr_rate = 96; break; 2415201209Srpaulo case 0x3: tap->wr_rate = 108; break; 2416201209Srpaulo /* Unknown rate: should not happen. */ 2417201209Srpaulo default: tap->wr_rate = 0; 2418201209Srpaulo } 2419178676Ssam } 2420178676Ssam 2421178676Ssam IWN_UNLOCK(sc); 2422178676Ssam 2423198429Srpaulo /* Send the frame to the 802.11 layer. */ 2424178676Ssam if (ni != NULL) { 2425221650Sbschmidt if (ni->ni_flags & IEEE80211_NODE_HT) 2426221650Sbschmidt m->m_flags |= M_AMPDU; 2427220726Sbschmidt (void)ieee80211_input(ni, m, rssi - nf, nf); 2428198429Srpaulo /* Node is no longer needed. */ 2429178676Ssam ieee80211_free_node(ni); 2430178676Ssam } else 2431220726Sbschmidt (void)ieee80211_input_all(ic, m, rssi - nf, nf); 2432178676Ssam 2433178676Ssam IWN_LOCK(sc); 2434178676Ssam} 2435178676Ssam 2436201209Srpaulo/* Process an incoming Compressed BlockAck. */ 2437206477Sbschmidtstatic void 2438201209Srpauloiwn_rx_compressed_ba(struct iwn_softc *sc, struct iwn_rx_desc *desc, 2439201209Srpaulo struct iwn_rx_data *data) 2440201209Srpaulo{ 2441221651Sbschmidt struct ifnet *ifp = sc->sc_ifp; 2442221651Sbschmidt struct iwn_node *wn; 2443221651Sbschmidt struct ieee80211_node *ni; 2444201209Srpaulo struct iwn_compressed_ba *ba = (struct iwn_compressed_ba *)(desc + 1); 2445201209Srpaulo struct iwn_tx_ring *txq; 2446221651Sbschmidt struct ieee80211_tx_ampdu *tap; 2447221651Sbschmidt uint64_t bitmap; 2448221651Sbschmidt uint8_t tid; 2449221651Sbschmidt int ackfailcnt = 0, i, shift; 2450201209Srpaulo 2451220704Sbschmidt bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD); 2452220704Sbschmidt 2453221651Sbschmidt txq = &sc->txq[le16toh(ba->qid)]; 2454221651Sbschmidt tap = sc->qid2tap[le16toh(ba->qid)]; 2455221651Sbschmidt tid = WME_AC_TO_TID(tap->txa_ac); 2456221651Sbschmidt ni = tap->txa_ni; 2457221651Sbschmidt wn = (void *)ni; 2458221651Sbschmidt 2459221651Sbschmidt if (wn->agg[tid].bitmap == 0) 2460221651Sbschmidt return; 2461221651Sbschmidt 2462221651Sbschmidt shift = wn->agg[tid].startidx - ((le16toh(ba->seq) >> 4) & 0xff); 2463221651Sbschmidt if (shift < 0) 2464221651Sbschmidt shift += 0x100; 2465221651Sbschmidt 2466221651Sbschmidt if (wn->agg[tid].nframes > (64 - shift)) 2467221651Sbschmidt return; 2468221651Sbschmidt 2469221651Sbschmidt bitmap = (le64toh(ba->bitmap) >> shift) & wn->agg[tid].bitmap; 2470221651Sbschmidt for (i = 0; bitmap; i++) { 2471221651Sbschmidt if ((bitmap & 1) == 0) { 2472221651Sbschmidt ifp->if_oerrors++; 2473221651Sbschmidt ieee80211_ratectl_tx_complete(ni->ni_vap, ni, 2474221651Sbschmidt IEEE80211_RATECTL_TX_FAILURE, &ackfailcnt, NULL); 2475221651Sbschmidt } else { 2476221651Sbschmidt ifp->if_opackets++; 2477221651Sbschmidt ieee80211_ratectl_tx_complete(ni->ni_vap, ni, 2478221651Sbschmidt IEEE80211_RATECTL_TX_SUCCESS, &ackfailcnt, NULL); 2479221651Sbschmidt } 2480221651Sbschmidt bitmap >>= 1; 2481221651Sbschmidt } 2482201209Srpaulo} 2483201209Srpaulo 2484198429Srpaulo/* 2485220674Sbschmidt * Process a CALIBRATION_RESULT notification sent by the initialization 2486220674Sbschmidt * firmware on response to a CMD_CALIB_CONFIG command (5000 only). 2487220674Sbschmidt */ 2488220674Sbschmidtstatic void 2489220674Sbschmidtiwn5000_rx_calib_results(struct iwn_softc *sc, struct iwn_rx_desc *desc, 2490220674Sbschmidt struct iwn_rx_data *data) 2491220674Sbschmidt{ 2492220674Sbschmidt struct iwn_phy_calib *calib = (struct iwn_phy_calib *)(desc + 1); 2493220674Sbschmidt int len, idx = -1; 2494220674Sbschmidt 2495220674Sbschmidt /* Runtime firmware should not send such a notification. */ 2496220674Sbschmidt if (sc->sc_flags & IWN_FLAG_CALIB_DONE) 2497220674Sbschmidt return; 2498220674Sbschmidt 2499220674Sbschmidt len = (le32toh(desc->len) & 0x3fff) - 4; 2500220674Sbschmidt bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD); 2501220674Sbschmidt 2502220674Sbschmidt switch (calib->code) { 2503220674Sbschmidt case IWN5000_PHY_CALIB_DC: 2504220867Sbschmidt if ((sc->sc_flags & IWN_FLAG_INTERNAL_PA) == 0 && 2505220867Sbschmidt (sc->hw_type == IWN_HW_REV_TYPE_5150 || 2506220867Sbschmidt sc->hw_type >= IWN_HW_REV_TYPE_6000)) 2507220674Sbschmidt idx = 0; 2508220674Sbschmidt break; 2509220674Sbschmidt case IWN5000_PHY_CALIB_LO: 2510220674Sbschmidt idx = 1; 2511220674Sbschmidt break; 2512220674Sbschmidt case IWN5000_PHY_CALIB_TX_IQ: 2513220674Sbschmidt idx = 2; 2514220674Sbschmidt break; 2515220674Sbschmidt case IWN5000_PHY_CALIB_TX_IQ_PERIODIC: 2516220674Sbschmidt if (sc->hw_type < IWN_HW_REV_TYPE_6000 && 2517220674Sbschmidt sc->hw_type != IWN_HW_REV_TYPE_5150) 2518220674Sbschmidt idx = 3; 2519220674Sbschmidt break; 2520220674Sbschmidt case IWN5000_PHY_CALIB_BASE_BAND: 2521220674Sbschmidt idx = 4; 2522220674Sbschmidt break; 2523220674Sbschmidt } 2524220674Sbschmidt if (idx == -1) /* Ignore other results. */ 2525220674Sbschmidt return; 2526220674Sbschmidt 2527220674Sbschmidt /* Save calibration result. */ 2528220674Sbschmidt if (sc->calibcmd[idx].buf != NULL) 2529220674Sbschmidt free(sc->calibcmd[idx].buf, M_DEVBUF); 2530220674Sbschmidt sc->calibcmd[idx].buf = malloc(len, M_DEVBUF, M_NOWAIT); 2531220674Sbschmidt if (sc->calibcmd[idx].buf == NULL) { 2532220674Sbschmidt DPRINTF(sc, IWN_DEBUG_CALIBRATE, 2533220674Sbschmidt "not enough memory for calibration result %d\n", 2534220674Sbschmidt calib->code); 2535220674Sbschmidt return; 2536220674Sbschmidt } 2537220674Sbschmidt DPRINTF(sc, IWN_DEBUG_CALIBRATE, 2538220674Sbschmidt "saving calibration result code=%d len=%d\n", calib->code, len); 2539220674Sbschmidt sc->calibcmd[idx].len = len; 2540220674Sbschmidt memcpy(sc->calibcmd[idx].buf, calib, len); 2541220674Sbschmidt} 2542220674Sbschmidt 2543220674Sbschmidt/* 2544198429Srpaulo * Process an RX_STATISTICS or BEACON_STATISTICS firmware notification. 2545198429Srpaulo * The latter is sent by the firmware after each received beacon. 2546198429Srpaulo */ 2547206477Sbschmidtstatic void 2548198429Srpauloiwn_rx_statistics(struct iwn_softc *sc, struct iwn_rx_desc *desc, 2549198429Srpaulo struct iwn_rx_data *data) 2550198429Srpaulo{ 2551220728Sbschmidt struct iwn_ops *ops = &sc->ops; 2552178676Ssam struct ifnet *ifp = sc->sc_ifp; 2553178676Ssam struct ieee80211com *ic = ifp->if_l2com; 2554178676Ssam struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 2555178676Ssam struct iwn_calib_state *calib = &sc->calib; 2556178676Ssam struct iwn_stats *stats = (struct iwn_stats *)(desc + 1); 2557198429Srpaulo int temp; 2558178676Ssam 2559220725Sbschmidt /* Ignore statistics received during a scan. */ 2560178676Ssam if (vap->iv_state != IEEE80211_S_RUN || 2561178676Ssam (ic->ic_flags & IEEE80211_F_SCAN)) 2562178676Ssam return; 2563178676Ssam 2564202986Srpaulo bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD); 2565220724Sbschmidt 2566220724Sbschmidt DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: received statistics, cmd %d\n", 2567220724Sbschmidt __func__, desc->type); 2568220667Sbschmidt sc->calib_cnt = 0; /* Reset TX power calibration timeout. */ 2569178676Ssam 2570198429Srpaulo /* Test if temperature has changed. */ 2571178676Ssam if (stats->general.temp != sc->rawtemp) { 2572198429Srpaulo /* Convert "raw" temperature to degC. */ 2573178676Ssam sc->rawtemp = stats->general.temp; 2574220728Sbschmidt temp = ops->get_temperature(sc); 2575178676Ssam DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: temperature %d\n", 2576178676Ssam __func__, temp); 2577178676Ssam 2578220725Sbschmidt /* Update TX power if need be (4965AGN only). */ 2579198429Srpaulo if (sc->hw_type == IWN_HW_REV_TYPE_4965) 2580198429Srpaulo iwn4965_power_calibration(sc, temp); 2581178676Ssam } 2582178676Ssam 2583178676Ssam if (desc->type != IWN_BEACON_STATISTICS) 2584198429Srpaulo return; /* Reply to a statistics request. */ 2585178676Ssam 2586178676Ssam sc->noise = iwn_get_noise(&stats->rx.general); 2587178676Ssam DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: noise %d\n", __func__, sc->noise); 2588178676Ssam 2589198429Srpaulo /* Test that RSSI and noise are present in stats report. */ 2590198429Srpaulo if (le32toh(stats->rx.general.flags) != 1) { 2591178676Ssam DPRINTF(sc, IWN_DEBUG_ANY, "%s\n", 2592178676Ssam "received statistics without RSSI"); 2593178676Ssam return; 2594178676Ssam } 2595178676Ssam 2596178676Ssam if (calib->state == IWN_CALIB_STATE_ASSOC) 2597198429Srpaulo iwn_collect_noise(sc, &stats->rx.general); 2598178676Ssam else if (calib->state == IWN_CALIB_STATE_RUN) 2599178676Ssam iwn_tune_sensitivity(sc, &stats->rx); 2600178676Ssam} 2601178676Ssam 2602198429Srpaulo/* 2603198429Srpaulo * Process a TX_DONE firmware notification. Unfortunately, the 4965AGN 2604198429Srpaulo * and 5000 adapters have different incompatible TX status formats. 2605198429Srpaulo */ 2606206477Sbschmidtstatic void 2607198429Srpauloiwn4965_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, 2608198429Srpaulo struct iwn_rx_data *data) 2609178676Ssam{ 2610198429Srpaulo struct iwn4965_tx_stat *stat = (struct iwn4965_tx_stat *)(desc + 1); 2611221651Sbschmidt struct iwn_tx_ring *ring; 2612221651Sbschmidt int qid; 2613198429Srpaulo 2614221651Sbschmidt qid = desc->qid & 0xf; 2615221651Sbschmidt ring = &sc->txq[qid]; 2616221651Sbschmidt 2617198429Srpaulo DPRINTF(sc, IWN_DEBUG_XMIT, "%s: " 2618198429Srpaulo "qid %d idx %d retries %d nkill %d rate %x duration %d status %x\n", 2619201209Srpaulo __func__, desc->qid, desc->idx, stat->ackfailcnt, 2620201209Srpaulo stat->btkillcnt, stat->rate, le16toh(stat->duration), 2621198429Srpaulo le32toh(stat->status)); 2622201209Srpaulo 2623207001Sbschmidt bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD); 2624221651Sbschmidt if (qid >= sc->firstaggqueue) { 2625221651Sbschmidt iwn_ampdu_tx_done(sc, qid, desc->idx, stat->nframes, 2626221651Sbschmidt &stat->status); 2627221651Sbschmidt } else { 2628221651Sbschmidt iwn_tx_done(sc, desc, stat->ackfailcnt, 2629221651Sbschmidt le32toh(stat->status) & 0xff); 2630221651Sbschmidt } 2631198429Srpaulo} 2632198429Srpaulo 2633206477Sbschmidtstatic void 2634198429Srpauloiwn5000_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, 2635198429Srpaulo struct iwn_rx_data *data) 2636198429Srpaulo{ 2637198429Srpaulo struct iwn5000_tx_stat *stat = (struct iwn5000_tx_stat *)(desc + 1); 2638221651Sbschmidt struct iwn_tx_ring *ring; 2639221651Sbschmidt int qid; 2640198429Srpaulo 2641221651Sbschmidt qid = desc->qid & 0xf; 2642221651Sbschmidt ring = &sc->txq[qid]; 2643221651Sbschmidt 2644198429Srpaulo DPRINTF(sc, IWN_DEBUG_XMIT, "%s: " 2645198429Srpaulo "qid %d idx %d retries %d nkill %d rate %x duration %d status %x\n", 2646201209Srpaulo __func__, desc->qid, desc->idx, stat->ackfailcnt, 2647201209Srpaulo stat->btkillcnt, stat->rate, le16toh(stat->duration), 2648198429Srpaulo le32toh(stat->status)); 2649198429Srpaulo 2650201209Srpaulo#ifdef notyet 2651198429Srpaulo /* Reset TX scheduler slot. */ 2652198429Srpaulo iwn5000_reset_sched(sc, desc->qid & 0xf, desc->idx); 2653201209Srpaulo#endif 2654202986Srpaulo 2655207001Sbschmidt bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD); 2656221651Sbschmidt if (qid >= sc->firstaggqueue) { 2657221651Sbschmidt iwn_ampdu_tx_done(sc, qid, desc->idx, stat->nframes, 2658221651Sbschmidt &stat->status); 2659221651Sbschmidt } else { 2660221651Sbschmidt iwn_tx_done(sc, desc, stat->ackfailcnt, 2661221651Sbschmidt le16toh(stat->status) & 0xff); 2662221651Sbschmidt } 2663198429Srpaulo} 2664198429Srpaulo 2665198429Srpaulo/* 2666198429Srpaulo * Adapter-independent backend for TX_DONE firmware notifications. 2667198429Srpaulo */ 2668206477Sbschmidtstatic void 2669201209Srpauloiwn_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, int ackfailcnt, 2670198429Srpaulo uint8_t status) 2671198429Srpaulo{ 2672178676Ssam struct ifnet *ifp = sc->sc_ifp; 2673178676Ssam struct iwn_tx_ring *ring = &sc->txq[desc->qid & 0xf]; 2674178676Ssam struct iwn_tx_data *data = &ring->data[desc->idx]; 2675178676Ssam struct mbuf *m; 2676178676Ssam struct ieee80211_node *ni; 2677206358Srpaulo struct ieee80211vap *vap; 2678178676Ssam 2679178676Ssam KASSERT(data->ni != NULL, ("no node")); 2680178676Ssam 2681198429Srpaulo /* Unmap and free mbuf. */ 2682201209Srpaulo bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTWRITE); 2683201209Srpaulo bus_dmamap_unload(ring->data_dmat, data->map); 2684178676Ssam m = data->m, data->m = NULL; 2685178676Ssam ni = data->ni, data->ni = NULL; 2686206358Srpaulo vap = ni->ni_vap; 2687178676Ssam 2688178676Ssam if (m->m_flags & M_TXCB) { 2689178676Ssam /* 2690178676Ssam * Channels marked for "radar" require traffic to be received 2691178676Ssam * to unlock before we can transmit. Until traffic is seen 2692178676Ssam * any attempt to transmit is returned immediately with status 2693178676Ssam * set to IWN_TX_FAIL_TX_LOCKED. Unfortunately this can easily 2694178676Ssam * happen on first authenticate after scanning. To workaround 2695178676Ssam * this we ignore a failure of this sort in AUTH state so the 2696178676Ssam * 802.11 layer will fall back to using a timeout to wait for 2697178676Ssam * the AUTH reply. This allows the firmware time to see 2698178676Ssam * traffic so a subsequent retry of AUTH succeeds. It's 2699178676Ssam * unclear why the firmware does not maintain state for 2700178676Ssam * channels recently visited as this would allow immediate 2701178676Ssam * use of the channel after a scan (where we see traffic). 2702178676Ssam */ 2703178676Ssam if (status == IWN_TX_FAIL_TX_LOCKED && 2704178676Ssam ni->ni_vap->iv_state == IEEE80211_S_AUTH) 2705178676Ssam ieee80211_process_callback(ni, m, 0); 2706178676Ssam else 2707178676Ssam ieee80211_process_callback(ni, m, 2708178676Ssam (status & IWN_TX_FAIL) != 0); 2709178676Ssam } 2710201209Srpaulo 2711201209Srpaulo /* 2712201209Srpaulo * Update rate control statistics for the node. 2713201209Srpaulo */ 2714220719Sbschmidt if (status & IWN_TX_FAIL) { 2715201209Srpaulo ifp->if_oerrors++; 2716206358Srpaulo ieee80211_ratectl_tx_complete(vap, ni, 2717206358Srpaulo IEEE80211_RATECTL_TX_FAILURE, &ackfailcnt, NULL); 2718201209Srpaulo } else { 2719220719Sbschmidt ifp->if_opackets++; 2720206358Srpaulo ieee80211_ratectl_tx_complete(vap, ni, 2721206358Srpaulo IEEE80211_RATECTL_TX_SUCCESS, &ackfailcnt, NULL); 2722201209Srpaulo } 2723178676Ssam m_freem(m); 2724178676Ssam ieee80211_free_node(ni); 2725178676Ssam 2726178676Ssam sc->sc_tx_timer = 0; 2727198429Srpaulo if (--ring->queued < IWN_TX_RING_LOMARK) { 2728198429Srpaulo sc->qfullmsk &= ~(1 << ring->qid); 2729198429Srpaulo if (sc->qfullmsk == 0 && 2730198429Srpaulo (ifp->if_drv_flags & IFF_DRV_OACTIVE)) { 2731198429Srpaulo ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 2732198429Srpaulo iwn_start_locked(ifp); 2733198429Srpaulo } 2734198429Srpaulo } 2735178676Ssam} 2736178676Ssam 2737198429Srpaulo/* 2738198429Srpaulo * Process a "command done" firmware notification. This is where we wakeup 2739198429Srpaulo * processes waiting for a synchronous command completion. 2740198429Srpaulo */ 2741206477Sbschmidtstatic void 2742198429Srpauloiwn_cmd_done(struct iwn_softc *sc, struct iwn_rx_desc *desc) 2743178676Ssam{ 2744178676Ssam struct iwn_tx_ring *ring = &sc->txq[4]; 2745178676Ssam struct iwn_tx_data *data; 2746178676Ssam 2747178676Ssam if ((desc->qid & 0xf) != 4) 2748198429Srpaulo return; /* Not a command ack. */ 2749178676Ssam 2750178676Ssam data = &ring->data[desc->idx]; 2751178676Ssam 2752198429Srpaulo /* If the command was mapped in an mbuf, free it. */ 2753178676Ssam if (data->m != NULL) { 2754220704Sbschmidt bus_dmamap_sync(ring->data_dmat, data->map, 2755220704Sbschmidt BUS_DMASYNC_POSTWRITE); 2756201209Srpaulo bus_dmamap_unload(ring->data_dmat, data->map); 2757178676Ssam m_freem(data->m); 2758178676Ssam data->m = NULL; 2759178676Ssam } 2760198439Srpaulo wakeup(&ring->desc[desc->idx]); 2761178676Ssam} 2762178676Ssam 2763221651Sbschmidtstatic void 2764221651Sbschmidtiwn_ampdu_tx_done(struct iwn_softc *sc, int qid, int idx, int nframes, 2765221651Sbschmidt void *stat) 2766221651Sbschmidt{ 2767221651Sbschmidt struct ifnet *ifp = sc->sc_ifp; 2768221651Sbschmidt struct iwn_tx_ring *ring = &sc->txq[qid]; 2769221651Sbschmidt struct iwn_tx_data *data; 2770221651Sbschmidt struct mbuf *m; 2771221651Sbschmidt struct iwn_node *wn; 2772221651Sbschmidt struct ieee80211_node *ni; 2773221651Sbschmidt struct ieee80211vap *vap; 2774221651Sbschmidt struct ieee80211_tx_ampdu *tap; 2775221651Sbschmidt uint64_t bitmap; 2776221651Sbschmidt uint32_t *status = stat; 2777221651Sbschmidt uint16_t *aggstatus = stat; 2778221651Sbschmidt uint8_t tid; 2779221651Sbschmidt int bit, i, lastidx, seqno, shift, start; 2780221651Sbschmidt 2781221651Sbschmidt#ifdef NOT_YET 2782221651Sbschmidt if (nframes == 1) { 2783221651Sbschmidt if ((*status & 0xff) != 1 && (*status & 0xff) != 2) 2784221651Sbschmidt printf("ieee80211_send_bar()\n"); 2785221651Sbschmidt } 2786221651Sbschmidt#endif 2787221651Sbschmidt 2788221651Sbschmidt bitmap = 0; 2789221651Sbschmidt start = idx; 2790221651Sbschmidt for (i = 0; i < nframes; i++) { 2791221651Sbschmidt if (le16toh(aggstatus[i * 2]) & 0xc) 2792221651Sbschmidt continue; 2793221651Sbschmidt 2794221651Sbschmidt idx = le16toh(aggstatus[2*i + 1]) & 0xff; 2795221651Sbschmidt bit = idx - start; 2796221651Sbschmidt shift = 0; 2797221651Sbschmidt if (bit >= 64) { 2798221651Sbschmidt shift = 0x100 - idx + start; 2799221651Sbschmidt bit = 0; 2800221651Sbschmidt start = idx; 2801221651Sbschmidt } else if (bit <= -64) 2802221651Sbschmidt bit = 0x100 - start + idx; 2803221651Sbschmidt else if (bit < 0) { 2804221651Sbschmidt shift = start - idx; 2805221651Sbschmidt start = idx; 2806221651Sbschmidt bit = 0; 2807221651Sbschmidt } 2808221651Sbschmidt bitmap = bitmap << shift; 2809221651Sbschmidt bitmap |= 1ULL << bit; 2810221651Sbschmidt } 2811221651Sbschmidt tap = sc->qid2tap[qid]; 2812221651Sbschmidt tid = WME_AC_TO_TID(tap->txa_ac); 2813221651Sbschmidt wn = (void *)tap->txa_ni; 2814221651Sbschmidt wn->agg[tid].bitmap = bitmap; 2815221651Sbschmidt wn->agg[tid].startidx = start; 2816221651Sbschmidt wn->agg[tid].nframes = nframes; 2817221651Sbschmidt 2818221651Sbschmidt seqno = le32toh(*(status + nframes)) & 0xfff; 2819221651Sbschmidt for (lastidx = (seqno & 0xff); ring->read != lastidx;) { 2820221651Sbschmidt data = &ring->data[ring->read]; 2821221651Sbschmidt 2822221651Sbschmidt KASSERT(data->ni != NULL, ("no node")); 2823221651Sbschmidt 2824221651Sbschmidt /* Unmap and free mbuf. */ 2825221651Sbschmidt bus_dmamap_sync(ring->data_dmat, data->map, 2826221651Sbschmidt BUS_DMASYNC_POSTWRITE); 2827221651Sbschmidt bus_dmamap_unload(ring->data_dmat, data->map); 2828221651Sbschmidt m = data->m, data->m = NULL; 2829221651Sbschmidt ni = data->ni, data->ni = NULL; 2830221651Sbschmidt vap = ni->ni_vap; 2831221651Sbschmidt 2832221651Sbschmidt if (m->m_flags & M_TXCB) 2833221651Sbschmidt ieee80211_process_callback(ni, m, 1); 2834221651Sbschmidt 2835221651Sbschmidt m_freem(m); 2836221651Sbschmidt ieee80211_free_node(ni); 2837221651Sbschmidt 2838221651Sbschmidt ring->queued--; 2839221651Sbschmidt ring->read = (ring->read + 1) % IWN_TX_RING_COUNT; 2840221651Sbschmidt } 2841221651Sbschmidt 2842221651Sbschmidt sc->sc_tx_timer = 0; 2843221651Sbschmidt if (ring->queued < IWN_TX_RING_LOMARK) { 2844221651Sbschmidt sc->qfullmsk &= ~(1 << ring->qid); 2845221651Sbschmidt if (sc->qfullmsk == 0 && 2846221651Sbschmidt (ifp->if_drv_flags & IFF_DRV_OACTIVE)) { 2847221651Sbschmidt ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 2848221651Sbschmidt iwn_start_locked(ifp); 2849221651Sbschmidt } 2850221651Sbschmidt } 2851221651Sbschmidt} 2852221651Sbschmidt 2853198429Srpaulo/* 2854198429Srpaulo * Process an INT_FH_RX or INT_SW_RX interrupt. 2855198429Srpaulo */ 2856206477Sbschmidtstatic void 2857178676Ssamiwn_notif_intr(struct iwn_softc *sc) 2858178676Ssam{ 2859220728Sbschmidt struct iwn_ops *ops = &sc->ops; 2860178676Ssam struct ifnet *ifp = sc->sc_ifp; 2861178676Ssam struct ieee80211com *ic = ifp->if_l2com; 2862178676Ssam struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 2863178676Ssam uint16_t hw; 2864178676Ssam 2865198429Srpaulo bus_dmamap_sync(sc->rxq.stat_dma.tag, sc->rxq.stat_dma.map, 2866198429Srpaulo BUS_DMASYNC_POSTREAD); 2867198429Srpaulo 2868198429Srpaulo hw = le16toh(sc->rxq.stat->closed_count) & 0xfff; 2869178676Ssam while (sc->rxq.cur != hw) { 2870178676Ssam struct iwn_rx_data *data = &sc->rxq.data[sc->rxq.cur]; 2871198429Srpaulo struct iwn_rx_desc *desc; 2872178676Ssam 2873201209Srpaulo bus_dmamap_sync(sc->rxq.data_dmat, data->map, 2874201209Srpaulo BUS_DMASYNC_POSTREAD); 2875198429Srpaulo desc = mtod(data->m, struct iwn_rx_desc *); 2876198429Srpaulo 2877178676Ssam DPRINTF(sc, IWN_DEBUG_RECV, 2878178676Ssam "%s: qid %x idx %d flags %x type %d(%s) len %d\n", 2879198439Srpaulo __func__, desc->qid & 0xf, desc->idx, desc->flags, 2880178676Ssam desc->type, iwn_intr_str(desc->type), 2881178676Ssam le16toh(desc->len)); 2882178676Ssam 2883198429Srpaulo if (!(desc->qid & 0x80)) /* Reply to a command. */ 2884198429Srpaulo iwn_cmd_done(sc, desc); 2885178676Ssam 2886178676Ssam switch (desc->type) { 2887198429Srpaulo case IWN_RX_PHY: 2888198429Srpaulo iwn_rx_phy(sc, desc, data); 2889178676Ssam break; 2890178676Ssam 2891198429Srpaulo case IWN_RX_DONE: /* 4965AGN only. */ 2892198429Srpaulo case IWN_MPDU_RX_DONE: 2893198429Srpaulo /* An 802.11 frame has been received. */ 2894198429Srpaulo iwn_rx_done(sc, desc, data); 2895178676Ssam break; 2896178676Ssam 2897201209Srpaulo case IWN_RX_COMPRESSED_BA: 2898201209Srpaulo /* A Compressed BlockAck has been received. */ 2899201209Srpaulo iwn_rx_compressed_ba(sc, desc, data); 2900201209Srpaulo break; 2901201209Srpaulo 2902178676Ssam case IWN_TX_DONE: 2903198429Srpaulo /* An 802.11 frame has been transmitted. */ 2904220728Sbschmidt ops->tx_done(sc, desc, data); 2905178676Ssam break; 2906178676Ssam 2907178676Ssam case IWN_RX_STATISTICS: 2908178676Ssam case IWN_BEACON_STATISTICS: 2909198429Srpaulo iwn_rx_statistics(sc, desc, data); 2910178676Ssam break; 2911178676Ssam 2912198429Srpaulo case IWN_BEACON_MISSED: 2913198429Srpaulo { 2914178676Ssam struct iwn_beacon_missed *miss = 2915178676Ssam (struct iwn_beacon_missed *)(desc + 1); 2916202986Srpaulo int misses; 2917178676Ssam 2918201209Srpaulo bus_dmamap_sync(sc->rxq.data_dmat, data->map, 2919201209Srpaulo BUS_DMASYNC_POSTREAD); 2920202986Srpaulo misses = le32toh(miss->consecutive); 2921201209Srpaulo 2922178676Ssam DPRINTF(sc, IWN_DEBUG_STATE, 2923178676Ssam "%s: beacons missed %d/%d\n", __func__, 2924178676Ssam misses, le32toh(miss->total)); 2925178676Ssam /* 2926178676Ssam * If more than 5 consecutive beacons are missed, 2927178676Ssam * reinitialize the sensitivity state machine. 2928178676Ssam */ 2929220660Sbschmidt if (vap->iv_state == IEEE80211_S_RUN && 2930220660Sbschmidt (ic->ic_flags & IEEE80211_F_SCAN) != 0) { 2931220660Sbschmidt if (misses > 5) 2932220660Sbschmidt (void)iwn_init_sensitivity(sc); 2933220660Sbschmidt if (misses >= vap->iv_bmissthreshold) { 2934220660Sbschmidt IWN_UNLOCK(sc); 2935220660Sbschmidt ieee80211_beacon_miss(ic); 2936220660Sbschmidt IWN_LOCK(sc); 2937220660Sbschmidt } 2938201209Srpaulo } 2939178676Ssam break; 2940178676Ssam } 2941198429Srpaulo case IWN_UC_READY: 2942198429Srpaulo { 2943178676Ssam struct iwn_ucode_info *uc = 2944178676Ssam (struct iwn_ucode_info *)(desc + 1); 2945178676Ssam 2946198429Srpaulo /* The microcontroller is ready. */ 2947201209Srpaulo bus_dmamap_sync(sc->rxq.data_dmat, data->map, 2948201209Srpaulo BUS_DMASYNC_POSTREAD); 2949178676Ssam DPRINTF(sc, IWN_DEBUG_RESET, 2950178676Ssam "microcode alive notification version=%d.%d " 2951178676Ssam "subtype=%x alive=%x\n", uc->major, uc->minor, 2952178676Ssam uc->subtype, le32toh(uc->valid)); 2953178676Ssam 2954178676Ssam if (le32toh(uc->valid) != 1) { 2955178676Ssam device_printf(sc->sc_dev, 2956198429Srpaulo "microcontroller initialization failed"); 2957178676Ssam break; 2958178676Ssam } 2959178676Ssam if (uc->subtype == IWN_UCODE_INIT) { 2960201209Srpaulo /* Save microcontroller report. */ 2961178676Ssam memcpy(&sc->ucode_info, uc, sizeof (*uc)); 2962178676Ssam } 2963198429Srpaulo /* Save the address of the error log in SRAM. */ 2964198429Srpaulo sc->errptr = le32toh(uc->errptr); 2965178676Ssam break; 2966178676Ssam } 2967198429Srpaulo case IWN_STATE_CHANGED: 2968198429Srpaulo { 2969178676Ssam uint32_t *status = (uint32_t *)(desc + 1); 2970178676Ssam 2971178676Ssam /* 2972178676Ssam * State change allows hardware switch change to be 2973178676Ssam * noted. However, we handle this in iwn_intr as we 2974178676Ssam * get both the enable/disble intr. 2975178676Ssam */ 2976201209Srpaulo bus_dmamap_sync(sc->rxq.data_dmat, data->map, 2977201209Srpaulo BUS_DMASYNC_POSTREAD); 2978178676Ssam DPRINTF(sc, IWN_DEBUG_INTR, "state changed to %x\n", 2979178676Ssam le32toh(*status)); 2980178676Ssam break; 2981178676Ssam } 2982198429Srpaulo case IWN_START_SCAN: 2983198429Srpaulo { 2984178676Ssam struct iwn_start_scan *scan = 2985178676Ssam (struct iwn_start_scan *)(desc + 1); 2986178676Ssam 2987201209Srpaulo bus_dmamap_sync(sc->rxq.data_dmat, data->map, 2988201209Srpaulo BUS_DMASYNC_POSTREAD); 2989178676Ssam DPRINTF(sc, IWN_DEBUG_ANY, 2990178676Ssam "%s: scanning channel %d status %x\n", 2991178676Ssam __func__, scan->chan, le32toh(scan->status)); 2992178676Ssam break; 2993178676Ssam } 2994198429Srpaulo case IWN_STOP_SCAN: 2995198429Srpaulo { 2996178676Ssam struct iwn_stop_scan *scan = 2997178676Ssam (struct iwn_stop_scan *)(desc + 1); 2998178676Ssam 2999201209Srpaulo bus_dmamap_sync(sc->rxq.data_dmat, data->map, 3000201209Srpaulo BUS_DMASYNC_POSTREAD); 3001178676Ssam DPRINTF(sc, IWN_DEBUG_STATE, 3002178676Ssam "scan finished nchan=%d status=%d chan=%d\n", 3003178676Ssam scan->nchan, scan->status, scan->chan); 3004178676Ssam 3005201209Srpaulo IWN_UNLOCK(sc); 3006191746Sthompsa ieee80211_scan_next(vap); 3007201209Srpaulo IWN_LOCK(sc); 3008178676Ssam break; 3009178676Ssam } 3010198429Srpaulo case IWN5000_CALIBRATION_RESULT: 3011220674Sbschmidt iwn5000_rx_calib_results(sc, desc, data); 3012198429Srpaulo break; 3013198429Srpaulo 3014198429Srpaulo case IWN5000_CALIBRATION_DONE: 3015201209Srpaulo sc->sc_flags |= IWN_FLAG_CALIB_DONE; 3016198429Srpaulo wakeup(sc); 3017198429Srpaulo break; 3018178676Ssam } 3019198429Srpaulo 3020178676Ssam sc->rxq.cur = (sc->rxq.cur + 1) % IWN_RX_RING_COUNT; 3021178676Ssam } 3022178676Ssam 3023198429Srpaulo /* Tell the firmware what we have processed. */ 3024178676Ssam hw = (hw == 0) ? IWN_RX_RING_COUNT - 1 : hw - 1; 3025198429Srpaulo IWN_WRITE(sc, IWN_FH_RX_WPTR, hw & ~7); 3026178676Ssam} 3027178676Ssam 3028198429Srpaulo/* 3029198429Srpaulo * Process an INT_WAKEUP interrupt raised when the microcontroller wakes up 3030198429Srpaulo * from power-down sleep mode. 3031198429Srpaulo */ 3032206477Sbschmidtstatic void 3033198429Srpauloiwn_wakeup_intr(struct iwn_softc *sc) 3034198429Srpaulo{ 3035198429Srpaulo int qid; 3036198429Srpaulo 3037198429Srpaulo DPRINTF(sc, IWN_DEBUG_RESET, "%s: ucode wakeup from power-down sleep\n", 3038198429Srpaulo __func__); 3039198429Srpaulo 3040198429Srpaulo /* Wakeup RX and TX rings. */ 3041198429Srpaulo IWN_WRITE(sc, IWN_FH_RX_WPTR, sc->rxq.cur & ~7); 3042220728Sbschmidt for (qid = 0; qid < sc->ntxqs; qid++) { 3043198429Srpaulo struct iwn_tx_ring *ring = &sc->txq[qid]; 3044198429Srpaulo IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | ring->cur); 3045198429Srpaulo } 3046198429Srpaulo} 3047198429Srpaulo 3048206477Sbschmidtstatic void 3049191746Sthompsaiwn_rftoggle_intr(struct iwn_softc *sc) 3050191746Sthompsa{ 3051191746Sthompsa struct ifnet *ifp = sc->sc_ifp; 3052191746Sthompsa struct ieee80211com *ic = ifp->if_l2com; 3053198429Srpaulo uint32_t tmp = IWN_READ(sc, IWN_GP_CNTRL); 3054191746Sthompsa 3055191746Sthompsa IWN_LOCK_ASSERT(sc); 3056191746Sthompsa 3057191746Sthompsa device_printf(sc->sc_dev, "RF switch: radio %s\n", 3058198429Srpaulo (tmp & IWN_GP_CNTRL_RFKILL) ? "enabled" : "disabled"); 3059198429Srpaulo if (tmp & IWN_GP_CNTRL_RFKILL) 3060191746Sthompsa ieee80211_runtask(ic, &sc->sc_radioon_task); 3061191746Sthompsa else 3062191746Sthompsa ieee80211_runtask(ic, &sc->sc_radiooff_task); 3063191746Sthompsa} 3064191746Sthompsa 3065198429Srpaulo/* 3066198429Srpaulo * Dump the error log of the firmware when a firmware panic occurs. Although 3067198429Srpaulo * we can't debug the firmware because it is neither open source nor free, it 3068198429Srpaulo * can help us to identify certain classes of problems. 3069198429Srpaulo */ 3070206477Sbschmidtstatic void 3071201209Srpauloiwn_fatal_intr(struct iwn_softc *sc) 3072191746Sthompsa{ 3073198429Srpaulo struct iwn_fw_dump dump; 3074198429Srpaulo int i; 3075191746Sthompsa 3076191746Sthompsa IWN_LOCK_ASSERT(sc); 3077191746Sthompsa 3078201209Srpaulo /* Force a complete recalibration on next init. */ 3079201209Srpaulo sc->sc_flags &= ~IWN_FLAG_CALIB_DONE; 3080201209Srpaulo 3081198429Srpaulo /* Check that the error log address is valid. */ 3082198429Srpaulo if (sc->errptr < IWN_FW_DATA_BASE || 3083198429Srpaulo sc->errptr + sizeof (dump) > 3084220728Sbschmidt IWN_FW_DATA_BASE + sc->fw_data_maxsz) { 3085220726Sbschmidt printf("%s: bad firmware error log address 0x%08x\n", __func__, 3086220726Sbschmidt sc->errptr); 3087198429Srpaulo return; 3088198429Srpaulo } 3089198429Srpaulo if (iwn_nic_lock(sc) != 0) { 3090220726Sbschmidt printf("%s: could not read firmware error log\n", __func__); 3091198429Srpaulo return; 3092198429Srpaulo } 3093198429Srpaulo /* Read firmware error log from SRAM. */ 3094198429Srpaulo iwn_mem_read_region_4(sc, sc->errptr, (uint32_t *)&dump, 3095198429Srpaulo sizeof (dump) / sizeof (uint32_t)); 3096198429Srpaulo iwn_nic_unlock(sc); 3097198429Srpaulo 3098198429Srpaulo if (dump.valid == 0) { 3099220726Sbschmidt printf("%s: firmware error log is empty\n", __func__); 3100198429Srpaulo return; 3101198429Srpaulo } 3102198429Srpaulo printf("firmware error log:\n"); 3103198429Srpaulo printf(" error type = \"%s\" (0x%08X)\n", 3104198429Srpaulo (dump.id < nitems(iwn_fw_errmsg)) ? 3105198429Srpaulo iwn_fw_errmsg[dump.id] : "UNKNOWN", 3106198429Srpaulo dump.id); 3107198429Srpaulo printf(" program counter = 0x%08X\n", dump.pc); 3108198429Srpaulo printf(" source line = 0x%08X\n", dump.src_line); 3109198429Srpaulo printf(" error data = 0x%08X%08X\n", 3110198429Srpaulo dump.error_data[0], dump.error_data[1]); 3111198429Srpaulo printf(" branch link = 0x%08X%08X\n", 3112198429Srpaulo dump.branch_link[0], dump.branch_link[1]); 3113198429Srpaulo printf(" interrupt link = 0x%08X%08X\n", 3114198429Srpaulo dump.interrupt_link[0], dump.interrupt_link[1]); 3115198429Srpaulo printf(" time = %u\n", dump.time[0]); 3116198429Srpaulo 3117198429Srpaulo /* Dump driver status (TX and RX rings) while we're here. */ 3118198429Srpaulo printf("driver status:\n"); 3119220728Sbschmidt for (i = 0; i < sc->ntxqs; i++) { 3120198429Srpaulo struct iwn_tx_ring *ring = &sc->txq[i]; 3121198429Srpaulo printf(" tx ring %2d: qid=%-2d cur=%-3d queued=%-3d\n", 3122198429Srpaulo i, ring->qid, ring->cur, ring->queued); 3123198429Srpaulo } 3124198429Srpaulo printf(" rx ring: cur=%d\n", sc->rxq.cur); 3125191746Sthompsa} 3126191746Sthompsa 3127206477Sbschmidtstatic void 3128178676Ssamiwn_intr(void *arg) 3129178676Ssam{ 3130178676Ssam struct iwn_softc *sc = arg; 3131198429Srpaulo struct ifnet *ifp = sc->sc_ifp; 3132201209Srpaulo uint32_t r1, r2, tmp; 3133178676Ssam 3134178676Ssam IWN_LOCK(sc); 3135178676Ssam 3136198429Srpaulo /* Disable interrupts. */ 3137201209Srpaulo IWN_WRITE(sc, IWN_INT_MASK, 0); 3138178676Ssam 3139201209Srpaulo /* Read interrupts from ICT (fast) or from registers (slow). */ 3140201209Srpaulo if (sc->sc_flags & IWN_FLAG_USE_ICT) { 3141201209Srpaulo tmp = 0; 3142201209Srpaulo while (sc->ict[sc->ict_cur] != 0) { 3143201209Srpaulo tmp |= sc->ict[sc->ict_cur]; 3144201209Srpaulo sc->ict[sc->ict_cur] = 0; /* Acknowledge. */ 3145201209Srpaulo sc->ict_cur = (sc->ict_cur + 1) % IWN_ICT_COUNT; 3146201209Srpaulo } 3147201209Srpaulo tmp = le32toh(tmp); 3148206444Sbschmidt if (tmp == 0xffffffff) /* Shouldn't happen. */ 3149206444Sbschmidt tmp = 0; 3150206444Sbschmidt else if (tmp & 0xc0000) /* Workaround a HW bug. */ 3151206444Sbschmidt tmp |= 0x8000; 3152201209Srpaulo r1 = (tmp & 0xff00) << 16 | (tmp & 0xff); 3153201209Srpaulo r2 = 0; /* Unused. */ 3154201209Srpaulo } else { 3155201209Srpaulo r1 = IWN_READ(sc, IWN_INT); 3156201209Srpaulo if (r1 == 0xffffffff || (r1 & 0xfffffff0) == 0xa5a5a5a0) 3157201209Srpaulo return; /* Hardware gone! */ 3158201209Srpaulo r2 = IWN_READ(sc, IWN_FH_INT); 3159201209Srpaulo } 3160178676Ssam 3161198429Srpaulo DPRINTF(sc, IWN_DEBUG_INTR, "interrupt reg1=%x reg2=%x\n", r1, r2); 3162198429Srpaulo 3163201209Srpaulo if (r1 == 0 && r2 == 0) 3164198429Srpaulo goto done; /* Interrupt not for us. */ 3165178676Ssam 3166198429Srpaulo /* Acknowledge interrupts. */ 3167198429Srpaulo IWN_WRITE(sc, IWN_INT, r1); 3168201209Srpaulo if (!(sc->sc_flags & IWN_FLAG_USE_ICT)) 3169201209Srpaulo IWN_WRITE(sc, IWN_FH_INT, r2); 3170178676Ssam 3171198429Srpaulo if (r1 & IWN_INT_RF_TOGGLED) { 3172191746Sthompsa iwn_rftoggle_intr(sc); 3173201209Srpaulo goto done; 3174198429Srpaulo } 3175198429Srpaulo if (r1 & IWN_INT_CT_REACHED) { 3176198429Srpaulo device_printf(sc->sc_dev, "%s: critical temperature reached!\n", 3177198429Srpaulo __func__); 3178198429Srpaulo } 3179198429Srpaulo if (r1 & (IWN_INT_SW_ERR | IWN_INT_HW_ERR)) { 3180220724Sbschmidt device_printf(sc->sc_dev, "%s: fatal firmware error\n", 3181220724Sbschmidt __func__); 3182220725Sbschmidt /* Dump firmware error log and stop. */ 3183201209Srpaulo iwn_fatal_intr(sc); 3184201209Srpaulo ifp->if_flags &= ~IFF_UP; 3185201209Srpaulo iwn_stop_locked(sc); 3186178676Ssam goto done; 3187178676Ssam } 3188201209Srpaulo if ((r1 & (IWN_INT_FH_RX | IWN_INT_SW_RX | IWN_INT_RX_PERIODIC)) || 3189201209Srpaulo (r2 & IWN_FH_INT_RX)) { 3190201209Srpaulo if (sc->sc_flags & IWN_FLAG_USE_ICT) { 3191201209Srpaulo if (r1 & (IWN_INT_FH_RX | IWN_INT_SW_RX)) 3192201209Srpaulo IWN_WRITE(sc, IWN_FH_INT, IWN_FH_INT_RX); 3193201209Srpaulo IWN_WRITE_1(sc, IWN_INT_PERIODIC, 3194201209Srpaulo IWN_INT_PERIODIC_DIS); 3195201209Srpaulo iwn_notif_intr(sc); 3196201209Srpaulo if (r1 & (IWN_INT_FH_RX | IWN_INT_SW_RX)) { 3197201209Srpaulo IWN_WRITE_1(sc, IWN_INT_PERIODIC, 3198201209Srpaulo IWN_INT_PERIODIC_ENA); 3199201209Srpaulo } 3200201209Srpaulo } else 3201201209Srpaulo iwn_notif_intr(sc); 3202201209Srpaulo } 3203178676Ssam 3204201209Srpaulo if ((r1 & IWN_INT_FH_TX) || (r2 & IWN_FH_INT_TX)) { 3205201209Srpaulo if (sc->sc_flags & IWN_FLAG_USE_ICT) 3206201209Srpaulo IWN_WRITE(sc, IWN_FH_INT, IWN_FH_INT_TX); 3207198429Srpaulo wakeup(sc); /* FH DMA transfer completed. */ 3208201209Srpaulo } 3209198429Srpaulo 3210198429Srpaulo if (r1 & IWN_INT_ALIVE) 3211198429Srpaulo wakeup(sc); /* Firmware is alive. */ 3212198429Srpaulo 3213198429Srpaulo if (r1 & IWN_INT_WAKEUP) 3214198429Srpaulo iwn_wakeup_intr(sc); 3215198429Srpaulo 3216201209Srpaulodone: 3217198429Srpaulo /* Re-enable interrupts. */ 3218201209Srpaulo if (ifp->if_flags & IFF_UP) 3219201209Srpaulo IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask); 3220198429Srpaulo 3221178676Ssam IWN_UNLOCK(sc); 3222178676Ssam} 3223178676Ssam 3224198429Srpaulo/* 3225198429Srpaulo * Update TX scheduler ring when transmitting an 802.11 frame (4965AGN and 3226220725Sbschmidt * 5000 adapters use a slightly different format). 3227198429Srpaulo */ 3228206477Sbschmidtstatic void 3229198429Srpauloiwn4965_update_sched(struct iwn_softc *sc, int qid, int idx, uint8_t id, 3230198429Srpaulo uint16_t len) 3231178676Ssam{ 3232198429Srpaulo uint16_t *w = &sc->sched[qid * IWN4965_SCHED_COUNT + idx]; 3233178676Ssam 3234198429Srpaulo *w = htole16(len + 8); 3235198439Srpaulo bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map, 3236198439Srpaulo BUS_DMASYNC_PREWRITE); 3237201209Srpaulo if (idx < IWN_SCHED_WINSZ) { 3238198429Srpaulo *(w + IWN_TX_RING_COUNT) = *w; 3239198439Srpaulo bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map, 3240198439Srpaulo BUS_DMASYNC_PREWRITE); 3241198439Srpaulo } 3242198429Srpaulo} 3243198429Srpaulo 3244206477Sbschmidtstatic void 3245198429Srpauloiwn5000_update_sched(struct iwn_softc *sc, int qid, int idx, uint8_t id, 3246198429Srpaulo uint16_t len) 3247198429Srpaulo{ 3248198429Srpaulo uint16_t *w = &sc->sched[qid * IWN5000_SCHED_COUNT + idx]; 3249198429Srpaulo 3250198429Srpaulo *w = htole16(id << 12 | (len + 8)); 3251198439Srpaulo bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map, 3252198439Srpaulo BUS_DMASYNC_PREWRITE); 3253198439Srpaulo if (idx < IWN_SCHED_WINSZ) { 3254198429Srpaulo *(w + IWN_TX_RING_COUNT) = *w; 3255198439Srpaulo bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map, 3256198439Srpaulo BUS_DMASYNC_PREWRITE); 3257198439Srpaulo } 3258198429Srpaulo} 3259198429Srpaulo 3260206475Sbschmidt#ifdef notyet 3261206477Sbschmidtstatic void 3262198429Srpauloiwn5000_reset_sched(struct iwn_softc *sc, int qid, int idx) 3263198429Srpaulo{ 3264198429Srpaulo uint16_t *w = &sc->sched[qid * IWN5000_SCHED_COUNT + idx]; 3265198429Srpaulo 3266198429Srpaulo *w = (*w & htole16(0xf000)) | htole16(1); 3267198439Srpaulo bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map, 3268198439Srpaulo BUS_DMASYNC_PREWRITE); 3269198439Srpaulo if (idx < IWN_SCHED_WINSZ) { 3270198429Srpaulo *(w + IWN_TX_RING_COUNT) = *w; 3271198439Srpaulo bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map, 3272206443Sbschmidt BUS_DMASYNC_PREWRITE); 3273198439Srpaulo } 3274198429Srpaulo} 3275206475Sbschmidt#endif 3276198429Srpaulo 3277206477Sbschmidtstatic int 3278220720Sbschmidtiwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni) 3279178676Ssam{ 3280221651Sbschmidt struct iwn_ops *ops = &sc->ops; 3281198429Srpaulo const struct ieee80211_txparam *tp; 3282178676Ssam struct ieee80211vap *vap = ni->ni_vap; 3283178676Ssam struct ieee80211com *ic = ni->ni_ic; 3284198429Srpaulo struct iwn_node *wn = (void *)ni; 3285220720Sbschmidt struct iwn_tx_ring *ring; 3286178676Ssam struct iwn_tx_desc *desc; 3287178676Ssam struct iwn_tx_data *data; 3288178676Ssam struct iwn_tx_cmd *cmd; 3289178676Ssam struct iwn_cmd_data *tx; 3290178676Ssam struct ieee80211_frame *wh; 3291198429Srpaulo struct ieee80211_key *k = NULL; 3292220700Sbschmidt struct mbuf *m1; 3293178676Ssam uint32_t flags; 3294220720Sbschmidt uint16_t qos; 3295178676Ssam u_int hdrlen; 3296220723Sbschmidt bus_dma_segment_t *seg, segs[IWN_MAX_SCATTER]; 3297220723Sbschmidt uint8_t tid, ridx, txant, type; 3298220720Sbschmidt int ac, i, totlen, error, pad, nsegs = 0, rate; 3299178676Ssam 3300178676Ssam IWN_LOCK_ASSERT(sc); 3301178676Ssam 3302198429Srpaulo wh = mtod(m, struct ieee80211_frame *); 3303198429Srpaulo hdrlen = ieee80211_anyhdrsize(wh); 3304178676Ssam type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; 3305178676Ssam 3306220720Sbschmidt /* Select EDCA Access Category and TX ring for this frame. */ 3307220720Sbschmidt if (IEEE80211_QOS_HAS_SEQ(wh)) { 3308220720Sbschmidt qos = ((const struct ieee80211_qosframe *)wh)->i_qos[0]; 3309220720Sbschmidt tid = qos & IEEE80211_QOS_TID; 3310220720Sbschmidt } else { 3311220720Sbschmidt qos = 0; 3312220720Sbschmidt tid = 0; 3313220720Sbschmidt } 3314220720Sbschmidt ac = M_WME_GETAC(m); 3315220720Sbschmidt 3316221651Sbschmidt if (IEEE80211_AMPDU_RUNNING(&ni->ni_tx_ampdu[ac])) { 3317221651Sbschmidt struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[ac]; 3318221651Sbschmidt 3319221651Sbschmidt ring = &sc->txq[*(int *)tap->txa_private]; 3320221651Sbschmidt *(uint16_t *)wh->i_seq = 3321221651Sbschmidt htole16(ni->ni_txseqs[tid] << IEEE80211_SEQ_SEQ_SHIFT); 3322221651Sbschmidt ni->ni_txseqs[tid]++; 3323221651Sbschmidt } else { 3324221651Sbschmidt ring = &sc->txq[ac]; 3325221651Sbschmidt } 3326198429Srpaulo desc = &ring->desc[ring->cur]; 3327198429Srpaulo data = &ring->data[ring->cur]; 3328198429Srpaulo 3329198429Srpaulo /* Choose a TX rate index. */ 3330201209Srpaulo tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)]; 3331178676Ssam if (type == IEEE80211_FC0_TYPE_MGT) 3332178676Ssam rate = tp->mgmtrate; 3333198429Srpaulo else if (IEEE80211_IS_MULTICAST(wh->i_addr1)) 3334178676Ssam rate = tp->mcastrate; 3335178676Ssam else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) 3336178676Ssam rate = tp->ucastrate; 3337178676Ssam else { 3338206358Srpaulo /* XXX pass pktlen */ 3339206358Srpaulo (void) ieee80211_ratectl_rate(ni, NULL, 0); 3340178676Ssam rate = ni->ni_txrate; 3341178676Ssam } 3342221648Sbschmidt ridx = ic->ic_rt->rateCodeToIndex[rate]; 3343178676Ssam 3344198429Srpaulo /* Encrypt the frame if need be. */ 3345178676Ssam if (wh->i_fc[1] & IEEE80211_FC1_WEP) { 3346220725Sbschmidt /* Retrieve key for TX. */ 3347198429Srpaulo k = ieee80211_crypto_encap(ni, m); 3348178676Ssam if (k == NULL) { 3349198429Srpaulo m_freem(m); 3350178676Ssam return ENOBUFS; 3351178676Ssam } 3352220725Sbschmidt /* 802.11 header may have moved. */ 3353198429Srpaulo wh = mtod(m, struct ieee80211_frame *); 3354198429Srpaulo } 3355198429Srpaulo totlen = m->m_pkthdr.len; 3356178676Ssam 3357192468Ssam if (ieee80211_radiotap_active_vap(vap)) { 3358178676Ssam struct iwn_tx_radiotap_header *tap = &sc->sc_txtap; 3359178676Ssam 3360178676Ssam tap->wt_flags = 0; 3361221648Sbschmidt tap->wt_rate = rate; 3362178676Ssam if (k != NULL) 3363178676Ssam tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP; 3364178676Ssam 3365198429Srpaulo ieee80211_radiotap_tx(vap, m); 3366178676Ssam } 3367178676Ssam 3368198429Srpaulo /* Prepare TX firmware command. */ 3369198429Srpaulo cmd = &ring->cmd[ring->cur]; 3370198429Srpaulo cmd->code = IWN_CMD_TX_DATA; 3371198429Srpaulo cmd->flags = 0; 3372198429Srpaulo cmd->qid = ring->qid; 3373198429Srpaulo cmd->idx = ring->cur; 3374198429Srpaulo 3375198429Srpaulo tx = (struct iwn_cmd_data *)cmd->data; 3376198429Srpaulo /* NB: No need to clear tx, all fields are reinitialized here. */ 3377198429Srpaulo tx->scratch = 0; /* clear "scratch" area */ 3378198429Srpaulo 3379198429Srpaulo flags = 0; 3380220720Sbschmidt if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { 3381220720Sbschmidt /* Unicast frame, check if an ACK is expected. */ 3382220720Sbschmidt if (!qos || (qos & IEEE80211_QOS_ACKPOLICY) != 3383220720Sbschmidt IEEE80211_QOS_ACKPOLICY_NOACK) 3384220720Sbschmidt flags |= IWN_TX_NEED_ACK; 3385220720Sbschmidt } 3386198429Srpaulo if ((wh->i_fc[0] & 3387198429Srpaulo (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) == 3388198429Srpaulo (IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_BAR)) 3389198429Srpaulo flags |= IWN_TX_IMM_BA; /* Cannot happen yet. */ 3390178676Ssam 3391198429Srpaulo if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG) 3392198429Srpaulo flags |= IWN_TX_MORE_FRAG; /* Cannot happen yet. */ 3393178676Ssam 3394198429Srpaulo /* Check if frame must be protected using RTS/CTS or CTS-to-self. */ 3395198429Srpaulo if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { 3396198429Srpaulo /* NB: Group frames are sent using CCK in 802.11b/g. */ 3397198429Srpaulo if (totlen + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) { 3398198429Srpaulo flags |= IWN_TX_NEED_RTS; 3399178676Ssam } else if ((ic->ic_flags & IEEE80211_F_USEPROT) && 3400201209Srpaulo ridx >= IWN_RIDX_OFDM6) { 3401178676Ssam if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) 3402198429Srpaulo flags |= IWN_TX_NEED_CTS; 3403178676Ssam else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) 3404198429Srpaulo flags |= IWN_TX_NEED_RTS; 3405178676Ssam } 3406198429Srpaulo if (flags & (IWN_TX_NEED_RTS | IWN_TX_NEED_CTS)) { 3407198429Srpaulo if (sc->hw_type != IWN_HW_REV_TYPE_4965) { 3408198429Srpaulo /* 5000 autoselects RTS/CTS or CTS-to-self. */ 3409198429Srpaulo flags &= ~(IWN_TX_NEED_RTS | IWN_TX_NEED_CTS); 3410198429Srpaulo flags |= IWN_TX_NEED_PROTECTION; 3411198429Srpaulo } else 3412198429Srpaulo flags |= IWN_TX_FULL_TXOP; 3413198429Srpaulo } 3414201209Srpaulo } 3415178676Ssam 3416198429Srpaulo if (IEEE80211_IS_MULTICAST(wh->i_addr1) || 3417198429Srpaulo type != IEEE80211_FC0_TYPE_DATA) 3418220728Sbschmidt tx->id = sc->broadcast_id; 3419198429Srpaulo else 3420198429Srpaulo tx->id = wn->id; 3421198429Srpaulo 3422178676Ssam if (type == IEEE80211_FC0_TYPE_MGT) { 3423178676Ssam uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; 3424178676Ssam 3425198429Srpaulo /* Tell HW to set timestamp in probe responses. */ 3426178676Ssam if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP) 3427178676Ssam flags |= IWN_TX_INSERT_TSTAMP; 3428178676Ssam if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ || 3429178676Ssam subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) 3430198429Srpaulo tx->timeout = htole16(3); 3431178676Ssam else 3432198429Srpaulo tx->timeout = htole16(2); 3433178676Ssam } else 3434198429Srpaulo tx->timeout = htole16(0); 3435178676Ssam 3436178676Ssam if (hdrlen & 3) { 3437201209Srpaulo /* First segment length must be a multiple of 4. */ 3438178676Ssam flags |= IWN_TX_NEED_PADDING; 3439178676Ssam pad = 4 - (hdrlen & 3); 3440178676Ssam } else 3441178676Ssam pad = 0; 3442178676Ssam 3443198429Srpaulo tx->len = htole16(totlen); 3444220720Sbschmidt tx->tid = tid; 3445201209Srpaulo tx->rts_ntries = 60; 3446201209Srpaulo tx->data_ntries = 15; 3447178676Ssam tx->lifetime = htole32(IWN_LIFETIME_INFINITE); 3448221648Sbschmidt tx->rate = wn->ridx[rate]; 3449220728Sbschmidt if (tx->id == sc->broadcast_id) { 3450201209Srpaulo /* Group or management frame. */ 3451201209Srpaulo tx->linkq = 0; 3452198429Srpaulo /* XXX Alternate between antenna A and B? */ 3453201209Srpaulo txant = IWN_LSB(sc->txchainmask); 3454221648Sbschmidt tx->rate |= htole32(IWN_RFLAG_ANT(txant)); 3455201209Srpaulo } else { 3456220715Sbschmidt tx->linkq = ni->ni_rates.rs_nrates - ridx - 1; 3457201209Srpaulo flags |= IWN_TX_LINKQ; /* enable MRR */ 3458201209Srpaulo } 3459198429Srpaulo /* Set physical address of "scratch area". */ 3460201209Srpaulo tx->loaddr = htole32(IWN_LOADDR(data->scratch_paddr)); 3461201209Srpaulo tx->hiaddr = IWN_HIADDR(data->scratch_paddr); 3462178676Ssam 3463198429Srpaulo /* Copy 802.11 header in TX command. */ 3464178676Ssam memcpy((uint8_t *)(tx + 1), wh, hdrlen); 3465178676Ssam 3466198429Srpaulo /* Trim 802.11 header. */ 3467198429Srpaulo m_adj(m, hdrlen); 3468198429Srpaulo tx->security = 0; 3469198429Srpaulo tx->flags = htole32(flags); 3470198429Srpaulo 3471220700Sbschmidt error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m, segs, 3472220700Sbschmidt &nsegs, BUS_DMA_NOWAIT); 3473220700Sbschmidt if (error != 0) { 3474220700Sbschmidt if (error != EFBIG) { 3475220700Sbschmidt device_printf(sc->sc_dev, 3476220700Sbschmidt "%s: can't map mbuf (error %d)\n", __func__, error); 3477220700Sbschmidt m_freem(m); 3478220700Sbschmidt return error; 3479178676Ssam } 3480220700Sbschmidt /* Too many DMA segments, linearize mbuf. */ 3481220700Sbschmidt m1 = m_collapse(m, M_DONTWAIT, IWN_MAX_SCATTER); 3482220700Sbschmidt if (m1 == NULL) { 3483220700Sbschmidt device_printf(sc->sc_dev, 3484220700Sbschmidt "%s: could not defrag mbuf\n", __func__); 3485220700Sbschmidt m_freem(m); 3486220700Sbschmidt return ENOBUFS; 3487220700Sbschmidt } 3488220700Sbschmidt m = m1; 3489220700Sbschmidt 3490220700Sbschmidt error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m, 3491220700Sbschmidt segs, &nsegs, BUS_DMA_NOWAIT); 3492178676Ssam if (error != 0) { 3493178676Ssam device_printf(sc->sc_dev, 3494220700Sbschmidt "%s: can't map mbuf (error %d)\n", __func__, error); 3495198429Srpaulo m_freem(m); 3496178676Ssam return error; 3497178676Ssam } 3498178676Ssam } 3499178676Ssam 3500198429Srpaulo data->m = m; 3501178676Ssam data->ni = ni; 3502178676Ssam 3503178676Ssam DPRINTF(sc, IWN_DEBUG_XMIT, "%s: qid %d idx %d len %d nsegs %d\n", 3504198429Srpaulo __func__, ring->qid, ring->cur, m->m_pkthdr.len, nsegs); 3505178676Ssam 3506198429Srpaulo /* Fill TX descriptor. */ 3507220700Sbschmidt desc->nsegs = 1; 3508220700Sbschmidt if (m->m_len != 0) 3509220700Sbschmidt desc->nsegs += nsegs; 3510198429Srpaulo /* First DMA segment is used by the TX command. */ 3511201209Srpaulo desc->segs[0].addr = htole32(IWN_LOADDR(data->cmd_paddr)); 3512201209Srpaulo desc->segs[0].len = htole16(IWN_HIADDR(data->cmd_paddr) | 3513198429Srpaulo (4 + sizeof (*tx) + hdrlen + pad) << 4); 3514198429Srpaulo /* Other DMA segments are for data payload. */ 3515220700Sbschmidt seg = &segs[0]; 3516178676Ssam for (i = 1; i <= nsegs; i++) { 3517220700Sbschmidt desc->segs[i].addr = htole32(IWN_LOADDR(seg->ds_addr)); 3518220700Sbschmidt desc->segs[i].len = htole16(IWN_HIADDR(seg->ds_addr) | 3519220700Sbschmidt seg->ds_len << 4); 3520220700Sbschmidt seg++; 3521178676Ssam } 3522178676Ssam 3523201209Srpaulo bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE); 3524201209Srpaulo bus_dmamap_sync(ring->data_dmat, ring->cmd_dma.map, 3525198429Srpaulo BUS_DMASYNC_PREWRITE); 3526198429Srpaulo bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, 3527198429Srpaulo BUS_DMASYNC_PREWRITE); 3528178676Ssam 3529198429Srpaulo /* Update TX scheduler. */ 3530220728Sbschmidt ops->update_sched(sc, ring->qid, ring->cur, tx->id, totlen); 3531178676Ssam 3532198429Srpaulo /* Kick TX ring. */ 3533178676Ssam ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT; 3534198429Srpaulo IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur); 3535178676Ssam 3536198429Srpaulo /* Mark TX ring as full if we reach a certain threshold. */ 3537198429Srpaulo if (++ring->queued > IWN_TX_RING_HIMARK) 3538198429Srpaulo sc->qfullmsk |= 1 << ring->qid; 3539178676Ssam 3540178676Ssam return 0; 3541178676Ssam} 3542178676Ssam 3543178676Ssamstatic int 3544201209Srpauloiwn_tx_data_raw(struct iwn_softc *sc, struct mbuf *m, 3545220720Sbschmidt struct ieee80211_node *ni, const struct ieee80211_bpf_params *params) 3546178676Ssam{ 3547221651Sbschmidt struct iwn_ops *ops = &sc->ops; 3548178676Ssam struct ifnet *ifp = sc->sc_ifp; 3549198429Srpaulo struct ieee80211vap *vap = ni->ni_vap; 3550198429Srpaulo struct ieee80211com *ic = ifp->if_l2com; 3551198429Srpaulo struct iwn_tx_cmd *cmd; 3552198429Srpaulo struct iwn_cmd_data *tx; 3553198429Srpaulo struct ieee80211_frame *wh; 3554220720Sbschmidt struct iwn_tx_ring *ring; 3555178676Ssam struct iwn_tx_desc *desc; 3556178676Ssam struct iwn_tx_data *data; 3557220700Sbschmidt struct mbuf *m1; 3558220700Sbschmidt bus_dma_segment_t *seg, segs[IWN_MAX_SCATTER]; 3559198429Srpaulo uint32_t flags; 3560198429Srpaulo u_int hdrlen; 3561220720Sbschmidt int ac, totlen, error, pad, nsegs = 0, i, rate; 3562201209Srpaulo uint8_t ridx, type, txant; 3563178676Ssam 3564198429Srpaulo IWN_LOCK_ASSERT(sc); 3565178676Ssam 3566201209Srpaulo wh = mtod(m, struct ieee80211_frame *); 3567198429Srpaulo hdrlen = ieee80211_anyhdrsize(wh); 3568198429Srpaulo type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; 3569198429Srpaulo 3570220720Sbschmidt ac = params->ibp_pri & 3; 3571220720Sbschmidt 3572220720Sbschmidt ring = &sc->txq[ac]; 3573178676Ssam desc = &ring->desc[ring->cur]; 3574178676Ssam data = &ring->data[ring->cur]; 3575178676Ssam 3576198429Srpaulo /* Choose a TX rate index. */ 3577198429Srpaulo rate = params->ibp_rate0; 3578221648Sbschmidt ridx = ic->ic_rt->rateCodeToIndex[rate]; 3579221648Sbschmidt if (ridx == (uint8_t)-1) { 3580198429Srpaulo /* XXX fall back to mcast/mgmt rate? */ 3581201209Srpaulo m_freem(m); 3582198429Srpaulo return EINVAL; 3583198429Srpaulo } 3584198429Srpaulo 3585201209Srpaulo totlen = m->m_pkthdr.len; 3586198429Srpaulo 3587201209Srpaulo /* Prepare TX firmware command. */ 3588198429Srpaulo cmd = &ring->cmd[ring->cur]; 3589198429Srpaulo cmd->code = IWN_CMD_TX_DATA; 3590198429Srpaulo cmd->flags = 0; 3591198429Srpaulo cmd->qid = ring->qid; 3592198429Srpaulo cmd->idx = ring->cur; 3593198429Srpaulo 3594198429Srpaulo tx = (struct iwn_cmd_data *)cmd->data; 3595201209Srpaulo /* NB: No need to clear tx, all fields are reinitialized here. */ 3596198429Srpaulo tx->scratch = 0; /* clear "scratch" area */ 3597198429Srpaulo 3598198429Srpaulo flags = 0; 3599198429Srpaulo if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0) 3600198429Srpaulo flags |= IWN_TX_NEED_ACK; 3601198429Srpaulo if (params->ibp_flags & IEEE80211_BPF_RTS) { 3602198429Srpaulo if (sc->hw_type != IWN_HW_REV_TYPE_4965) { 3603198429Srpaulo /* 5000 autoselects RTS/CTS or CTS-to-self. */ 3604198429Srpaulo flags &= ~IWN_TX_NEED_RTS; 3605198429Srpaulo flags |= IWN_TX_NEED_PROTECTION; 3606198429Srpaulo } else 3607198429Srpaulo flags |= IWN_TX_NEED_RTS | IWN_TX_FULL_TXOP; 3608198429Srpaulo } 3609198429Srpaulo if (params->ibp_flags & IEEE80211_BPF_CTS) { 3610198429Srpaulo if (sc->hw_type != IWN_HW_REV_TYPE_4965) { 3611198429Srpaulo /* 5000 autoselects RTS/CTS or CTS-to-self. */ 3612198429Srpaulo flags &= ~IWN_TX_NEED_CTS; 3613198429Srpaulo flags |= IWN_TX_NEED_PROTECTION; 3614198429Srpaulo } else 3615198429Srpaulo flags |= IWN_TX_NEED_CTS | IWN_TX_FULL_TXOP; 3616198429Srpaulo } 3617198429Srpaulo if (type == IEEE80211_FC0_TYPE_MGT) { 3618198429Srpaulo uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; 3619198429Srpaulo 3620220725Sbschmidt /* Tell HW to set timestamp in probe responses. */ 3621198429Srpaulo if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP) 3622198429Srpaulo flags |= IWN_TX_INSERT_TSTAMP; 3623198429Srpaulo 3624198429Srpaulo if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ || 3625198429Srpaulo subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) 3626198429Srpaulo tx->timeout = htole16(3); 3627198429Srpaulo else 3628198429Srpaulo tx->timeout = htole16(2); 3629198429Srpaulo } else 3630198429Srpaulo tx->timeout = htole16(0); 3631198429Srpaulo 3632198429Srpaulo if (hdrlen & 3) { 3633201209Srpaulo /* First segment length must be a multiple of 4. */ 3634198429Srpaulo flags |= IWN_TX_NEED_PADDING; 3635198429Srpaulo pad = 4 - (hdrlen & 3); 3636198429Srpaulo } else 3637198429Srpaulo pad = 0; 3638198429Srpaulo 3639198429Srpaulo if (ieee80211_radiotap_active_vap(vap)) { 3640198429Srpaulo struct iwn_tx_radiotap_header *tap = &sc->sc_txtap; 3641198429Srpaulo 3642198429Srpaulo tap->wt_flags = 0; 3643198429Srpaulo tap->wt_rate = rate; 3644198429Srpaulo 3645201209Srpaulo ieee80211_radiotap_tx(vap, m); 3646198429Srpaulo } 3647198429Srpaulo 3648198429Srpaulo tx->len = htole16(totlen); 3649198429Srpaulo tx->tid = 0; 3650220728Sbschmidt tx->id = sc->broadcast_id; 3651198429Srpaulo tx->rts_ntries = params->ibp_try1; 3652198429Srpaulo tx->data_ntries = params->ibp_try0; 3653198429Srpaulo tx->lifetime = htole32(IWN_LIFETIME_INFINITE); 3654221648Sbschmidt tx->rate = htole32(rate2plcp(rate)); 3655221648Sbschmidt if (ridx < IWN_RIDX_OFDM6 && 3656221648Sbschmidt IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) 3657221648Sbschmidt tx->rate |= htole32(IWN_RFLAG_CCK); 3658201209Srpaulo /* Group or management frame. */ 3659201209Srpaulo tx->linkq = 0; 3660201209Srpaulo txant = IWN_LSB(sc->txchainmask); 3661221648Sbschmidt tx->rate |= htole32(IWN_RFLAG_ANT(txant)); 3662198429Srpaulo /* Set physical address of "scratch area". */ 3663220694Sbschmidt tx->loaddr = htole32(IWN_LOADDR(data->scratch_paddr)); 3664220694Sbschmidt tx->hiaddr = IWN_HIADDR(data->scratch_paddr); 3665198429Srpaulo 3666198429Srpaulo /* Copy 802.11 header in TX command. */ 3667198429Srpaulo memcpy((uint8_t *)(tx + 1), wh, hdrlen); 3668198429Srpaulo 3669198429Srpaulo /* Trim 802.11 header. */ 3670201209Srpaulo m_adj(m, hdrlen); 3671198429Srpaulo tx->security = 0; 3672198429Srpaulo tx->flags = htole32(flags); 3673198429Srpaulo 3674220700Sbschmidt error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m, segs, 3675220700Sbschmidt &nsegs, BUS_DMA_NOWAIT); 3676220700Sbschmidt if (error != 0) { 3677220700Sbschmidt if (error != EFBIG) { 3678220700Sbschmidt device_printf(sc->sc_dev, 3679220700Sbschmidt "%s: can't map mbuf (error %d)\n", __func__, error); 3680220700Sbschmidt m_freem(m); 3681220700Sbschmidt return error; 3682178676Ssam } 3683220700Sbschmidt /* Too many DMA segments, linearize mbuf. */ 3684220700Sbschmidt m1 = m_collapse(m, M_DONTWAIT, IWN_MAX_SCATTER); 3685220700Sbschmidt if (m1 == NULL) { 3686220700Sbschmidt device_printf(sc->sc_dev, 3687220700Sbschmidt "%s: could not defrag mbuf\n", __func__); 3688220700Sbschmidt m_freem(m); 3689220700Sbschmidt return ENOBUFS; 3690220700Sbschmidt } 3691220700Sbschmidt m = m1; 3692220700Sbschmidt 3693220700Sbschmidt error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m, 3694220700Sbschmidt segs, &nsegs, BUS_DMA_NOWAIT); 3695178676Ssam if (error != 0) { 3696178676Ssam device_printf(sc->sc_dev, 3697220700Sbschmidt "%s: can't map mbuf (error %d)\n", __func__, error); 3698201209Srpaulo m_freem(m); 3699178676Ssam return error; 3700178676Ssam } 3701178676Ssam } 3702178676Ssam 3703201209Srpaulo data->m = m; 3704178676Ssam data->ni = ni; 3705178676Ssam 3706178676Ssam DPRINTF(sc, IWN_DEBUG_XMIT, "%s: qid %d idx %d len %d nsegs %d\n", 3707201209Srpaulo __func__, ring->qid, ring->cur, m->m_pkthdr.len, nsegs); 3708178676Ssam 3709198429Srpaulo /* Fill TX descriptor. */ 3710220700Sbschmidt desc->nsegs = 1; 3711220700Sbschmidt if (m->m_len != 0) 3712220700Sbschmidt desc->nsegs += nsegs; 3713198429Srpaulo /* First DMA segment is used by the TX command. */ 3714201209Srpaulo desc->segs[0].addr = htole32(IWN_LOADDR(data->cmd_paddr)); 3715201209Srpaulo desc->segs[0].len = htole16(IWN_HIADDR(data->cmd_paddr) | 3716198429Srpaulo (4 + sizeof (*tx) + hdrlen + pad) << 4); 3717198429Srpaulo /* Other DMA segments are for data payload. */ 3718220700Sbschmidt seg = &segs[0]; 3719178676Ssam for (i = 1; i <= nsegs; i++) { 3720220700Sbschmidt desc->segs[i].addr = htole32(IWN_LOADDR(seg->ds_addr)); 3721220700Sbschmidt desc->segs[i].len = htole16(IWN_HIADDR(seg->ds_addr) | 3722220700Sbschmidt seg->ds_len << 4); 3723220700Sbschmidt seg++; 3724178676Ssam } 3725178676Ssam 3726201209Srpaulo bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE); 3727201209Srpaulo bus_dmamap_sync(ring->data_dmat, ring->cmd_dma.map, 3728201209Srpaulo BUS_DMASYNC_PREWRITE); 3729201209Srpaulo bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, 3730201209Srpaulo BUS_DMASYNC_PREWRITE); 3731201209Srpaulo 3732198429Srpaulo /* Update TX scheduler. */ 3733220728Sbschmidt ops->update_sched(sc, ring->qid, ring->cur, tx->id, totlen); 3734178676Ssam 3735198429Srpaulo /* Kick TX ring. */ 3736178676Ssam ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT; 3737198429Srpaulo IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur); 3738178676Ssam 3739198429Srpaulo /* Mark TX ring as full if we reach a certain threshold. */ 3740198429Srpaulo if (++ring->queued > IWN_TX_RING_HIMARK) 3741198429Srpaulo sc->qfullmsk |= 1 << ring->qid; 3742178676Ssam 3743178676Ssam return 0; 3744178676Ssam} 3745178676Ssam 3746178676Ssamstatic int 3747178676Ssamiwn_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, 3748220720Sbschmidt const struct ieee80211_bpf_params *params) 3749178676Ssam{ 3750178676Ssam struct ieee80211com *ic = ni->ni_ic; 3751178676Ssam struct ifnet *ifp = ic->ic_ifp; 3752178676Ssam struct iwn_softc *sc = ifp->if_softc; 3753198429Srpaulo int error = 0; 3754178676Ssam 3755178676Ssam if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { 3756178676Ssam ieee80211_free_node(ni); 3757178676Ssam m_freem(m); 3758178676Ssam return ENETDOWN; 3759178676Ssam } 3760178676Ssam 3761178676Ssam IWN_LOCK(sc); 3762178676Ssam if (params == NULL) { 3763178676Ssam /* 3764178676Ssam * Legacy path; interpret frame contents to decide 3765178676Ssam * precisely how to send the frame. 3766178676Ssam */ 3767220720Sbschmidt error = iwn_tx_data(sc, m, ni); 3768178676Ssam } else { 3769178676Ssam /* 3770178676Ssam * Caller supplied explicit parameters to use in 3771178676Ssam * sending the frame. 3772178676Ssam */ 3773220720Sbschmidt error = iwn_tx_data_raw(sc, m, ni, params); 3774178676Ssam } 3775178676Ssam if (error != 0) { 3776178676Ssam /* NB: m is reclaimed on tx failure */ 3777178676Ssam ieee80211_free_node(ni); 3778178676Ssam ifp->if_oerrors++; 3779178676Ssam } 3780220667Sbschmidt sc->sc_tx_timer = 5; 3781220667Sbschmidt 3782178676Ssam IWN_UNLOCK(sc); 3783178676Ssam return error; 3784178676Ssam} 3785178676Ssam 3786206477Sbschmidtstatic void 3787198429Srpauloiwn_start(struct ifnet *ifp) 3788198429Srpaulo{ 3789198429Srpaulo struct iwn_softc *sc = ifp->if_softc; 3790198429Srpaulo 3791198429Srpaulo IWN_LOCK(sc); 3792198429Srpaulo iwn_start_locked(ifp); 3793198429Srpaulo IWN_UNLOCK(sc); 3794198429Srpaulo} 3795198429Srpaulo 3796206477Sbschmidtstatic void 3797198429Srpauloiwn_start_locked(struct ifnet *ifp) 3798198429Srpaulo{ 3799198429Srpaulo struct iwn_softc *sc = ifp->if_softc; 3800198429Srpaulo struct ieee80211_node *ni; 3801198429Srpaulo struct mbuf *m; 3802198429Srpaulo 3803198429Srpaulo IWN_LOCK_ASSERT(sc); 3804198429Srpaulo 3805220720Sbschmidt if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || 3806220720Sbschmidt (ifp->if_drv_flags & IFF_DRV_OACTIVE)) 3807220720Sbschmidt return; 3808220720Sbschmidt 3809198429Srpaulo for (;;) { 3810198429Srpaulo if (sc->qfullmsk != 0) { 3811198429Srpaulo ifp->if_drv_flags |= IFF_DRV_OACTIVE; 3812198429Srpaulo break; 3813198429Srpaulo } 3814198429Srpaulo IFQ_DRV_DEQUEUE(&ifp->if_snd, m); 3815198429Srpaulo if (m == NULL) 3816198429Srpaulo break; 3817198429Srpaulo ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; 3818220720Sbschmidt if (iwn_tx_data(sc, m, ni) != 0) { 3819220720Sbschmidt ieee80211_free_node(ni); 3820198429Srpaulo ifp->if_oerrors++; 3821220720Sbschmidt continue; 3822198429Srpaulo } 3823198429Srpaulo sc->sc_tx_timer = 5; 3824198429Srpaulo } 3825198429Srpaulo} 3826198429Srpaulo 3827178676Ssamstatic void 3828220667Sbschmidtiwn_watchdog(void *arg) 3829178676Ssam{ 3830220667Sbschmidt struct iwn_softc *sc = arg; 3831220667Sbschmidt struct ifnet *ifp = sc->sc_ifp; 3832220667Sbschmidt struct ieee80211com *ic = ifp->if_l2com; 3833178676Ssam 3834220667Sbschmidt IWN_LOCK_ASSERT(sc); 3835220667Sbschmidt 3836220667Sbschmidt KASSERT(ifp->if_drv_flags & IFF_DRV_RUNNING, ("not running")); 3837220667Sbschmidt 3838220668Sbschmidt if (sc->sc_tx_timer > 0) { 3839220668Sbschmidt if (--sc->sc_tx_timer == 0) { 3840220667Sbschmidt if_printf(ifp, "device timeout\n"); 3841220667Sbschmidt ieee80211_runtask(ic, &sc->sc_reinit_task); 3842220667Sbschmidt return; 3843220667Sbschmidt } 3844178676Ssam } 3845220667Sbschmidt callout_reset(&sc->watchdog_to, hz, iwn_watchdog, sc); 3846178676Ssam} 3847178676Ssam 3848206477Sbschmidtstatic int 3849178676Ssamiwn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 3850178676Ssam{ 3851178676Ssam struct iwn_softc *sc = ifp->if_softc; 3852178676Ssam struct ieee80211com *ic = ifp->if_l2com; 3853201209Srpaulo struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 3854178676Ssam struct ifreq *ifr = (struct ifreq *) data; 3855201209Srpaulo int error = 0, startall = 0, stop = 0; 3856178676Ssam 3857178676Ssam switch (cmd) { 3858220723Sbschmidt case SIOCGIFADDR: 3859220723Sbschmidt error = ether_ioctl(ifp, cmd, data); 3860220723Sbschmidt break; 3861178676Ssam case SIOCSIFFLAGS: 3862178704Sthompsa IWN_LOCK(sc); 3863178676Ssam if (ifp->if_flags & IFF_UP) { 3864178676Ssam if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { 3865178676Ssam iwn_init_locked(sc); 3866201209Srpaulo if (IWN_READ(sc, IWN_GP_CNTRL) & IWN_GP_CNTRL_RFKILL) 3867201209Srpaulo startall = 1; 3868201209Srpaulo else 3869201209Srpaulo stop = 1; 3870178676Ssam } 3871178676Ssam } else { 3872178676Ssam if (ifp->if_drv_flags & IFF_DRV_RUNNING) 3873178676Ssam iwn_stop_locked(sc); 3874178676Ssam } 3875178704Sthompsa IWN_UNLOCK(sc); 3876178704Sthompsa if (startall) 3877178704Sthompsa ieee80211_start_all(ic); 3878201209Srpaulo else if (vap != NULL && stop) 3879201209Srpaulo ieee80211_stop(vap); 3880178676Ssam break; 3881178676Ssam case SIOCGIFMEDIA: 3882178676Ssam error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd); 3883178676Ssam break; 3884178704Sthompsa default: 3885178704Sthompsa error = EINVAL; 3886178704Sthompsa break; 3887178676Ssam } 3888178676Ssam return error; 3889178676Ssam} 3890178676Ssam 3891178676Ssam/* 3892178676Ssam * Send a command to the firmware. 3893178676Ssam */ 3894206477Sbschmidtstatic int 3895178676Ssamiwn_cmd(struct iwn_softc *sc, int code, const void *buf, int size, int async) 3896178676Ssam{ 3897221651Sbschmidt struct iwn_ops *ops = &sc->ops; 3898178676Ssam struct iwn_tx_ring *ring = &sc->txq[4]; 3899178676Ssam struct iwn_tx_desc *desc; 3900198429Srpaulo struct iwn_tx_data *data; 3901178676Ssam struct iwn_tx_cmd *cmd; 3902198439Srpaulo struct mbuf *m; 3903178676Ssam bus_addr_t paddr; 3904198439Srpaulo int totlen, error; 3905178676Ssam 3906221650Sbschmidt if (async == 0) 3907221650Sbschmidt IWN_LOCK_ASSERT(sc); 3908178676Ssam 3909178676Ssam desc = &ring->desc[ring->cur]; 3910198429Srpaulo data = &ring->data[ring->cur]; 3911198429Srpaulo totlen = 4 + size; 3912178676Ssam 3913198439Srpaulo if (size > sizeof cmd->data) { 3914198439Srpaulo /* Command is too large to fit in a descriptor. */ 3915198439Srpaulo if (totlen > MCLBYTES) 3916198439Srpaulo return EINVAL; 3917201209Srpaulo m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE); 3918198439Srpaulo if (m == NULL) 3919198439Srpaulo return ENOMEM; 3920198439Srpaulo cmd = mtod(m, struct iwn_tx_cmd *); 3921201209Srpaulo error = bus_dmamap_load(ring->data_dmat, data->map, cmd, 3922198439Srpaulo totlen, iwn_dma_map_addr, &paddr, BUS_DMA_NOWAIT); 3923198439Srpaulo if (error != 0) { 3924198439Srpaulo m_freem(m); 3925198439Srpaulo return error; 3926198439Srpaulo } 3927198439Srpaulo data->m = m; 3928198439Srpaulo } else { 3929198439Srpaulo cmd = &ring->cmd[ring->cur]; 3930198439Srpaulo paddr = data->cmd_paddr; 3931198439Srpaulo } 3932198439Srpaulo 3933178676Ssam cmd->code = code; 3934178676Ssam cmd->flags = 0; 3935178676Ssam cmd->qid = ring->qid; 3936178676Ssam cmd->idx = ring->cur; 3937178676Ssam memcpy(cmd->data, buf, size); 3938178676Ssam 3939198429Srpaulo desc->nsegs = 1; 3940198429Srpaulo desc->segs[0].addr = htole32(IWN_LOADDR(paddr)); 3941198429Srpaulo desc->segs[0].len = htole16(IWN_HIADDR(paddr) | totlen << 4); 3942178676Ssam 3943178676Ssam DPRINTF(sc, IWN_DEBUG_CMD, "%s: %s (0x%x) flags %d qid %d idx %d\n", 3944178676Ssam __func__, iwn_intr_str(cmd->code), cmd->code, 3945178676Ssam cmd->flags, cmd->qid, cmd->idx); 3946178676Ssam 3947198439Srpaulo if (size > sizeof cmd->data) { 3948201209Srpaulo bus_dmamap_sync(ring->data_dmat, data->map, 3949198439Srpaulo BUS_DMASYNC_PREWRITE); 3950198439Srpaulo } else { 3951201209Srpaulo bus_dmamap_sync(ring->data_dmat, ring->cmd_dma.map, 3952198439Srpaulo BUS_DMASYNC_PREWRITE); 3953198439Srpaulo } 3954198439Srpaulo bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, 3955198439Srpaulo BUS_DMASYNC_PREWRITE); 3956198439Srpaulo 3957198429Srpaulo /* Update TX scheduler. */ 3958220728Sbschmidt ops->update_sched(sc, ring->qid, ring->cur, 0, 0); 3959198429Srpaulo 3960198429Srpaulo /* Kick command ring. */ 3961178676Ssam ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT; 3962198429Srpaulo IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur); 3963178676Ssam 3964198439Srpaulo return async ? 0 : msleep(desc, &sc->sc_mtx, PCATCH, "iwncmd", hz); 3965178676Ssam} 3966178676Ssam 3967206477Sbschmidtstatic int 3968198429Srpauloiwn4965_add_node(struct iwn_softc *sc, struct iwn_node_info *node, int async) 3969198429Srpaulo{ 3970198429Srpaulo struct iwn4965_node_info hnode; 3971198429Srpaulo caddr_t src, dst; 3972198429Srpaulo 3973198429Srpaulo /* 3974198429Srpaulo * We use the node structure for 5000 Series internally (it is 3975198429Srpaulo * a superset of the one for 4965AGN). We thus copy the common 3976198429Srpaulo * fields before sending the command. 3977198429Srpaulo */ 3978198429Srpaulo src = (caddr_t)node; 3979198429Srpaulo dst = (caddr_t)&hnode; 3980198429Srpaulo memcpy(dst, src, 48); 3981198429Srpaulo /* Skip TSC, RX MIC and TX MIC fields from ``src''. */ 3982198429Srpaulo memcpy(dst + 48, src + 72, 20); 3983198429Srpaulo return iwn_cmd(sc, IWN_CMD_ADD_NODE, &hnode, sizeof hnode, async); 3984198429Srpaulo} 3985198429Srpaulo 3986206477Sbschmidtstatic int 3987198429Srpauloiwn5000_add_node(struct iwn_softc *sc, struct iwn_node_info *node, int async) 3988198429Srpaulo{ 3989198429Srpaulo /* Direct mapping. */ 3990198429Srpaulo return iwn_cmd(sc, IWN_CMD_ADD_NODE, node, sizeof (*node), async); 3991198429Srpaulo} 3992198429Srpaulo 3993206477Sbschmidtstatic int 3994220715Sbschmidtiwn_set_link_quality(struct iwn_softc *sc, struct ieee80211_node *ni) 3995178676Ssam{ 3996220715Sbschmidt struct iwn_node *wn = (void *)ni; 3997220715Sbschmidt struct ieee80211_rateset *rs = &ni->ni_rates; 3998198429Srpaulo struct iwn_cmd_link_quality linkq; 3999220715Sbschmidt uint8_t txant; 4000221648Sbschmidt int i, rate, txrate; 4001178676Ssam 4002198429Srpaulo /* Use the first valid TX antenna. */ 4003201209Srpaulo txant = IWN_LSB(sc->txchainmask); 4004178676Ssam 4005198429Srpaulo memset(&linkq, 0, sizeof linkq); 4006220715Sbschmidt linkq.id = wn->id; 4007198429Srpaulo linkq.antmsk_1stream = txant; 4008201209Srpaulo linkq.antmsk_2stream = IWN_ANT_AB; 4009221651Sbschmidt linkq.ampdu_max = 64; 4010198429Srpaulo linkq.ampdu_threshold = 3; 4011198429Srpaulo linkq.ampdu_limit = htole16(4000); /* 4ms */ 4012198429Srpaulo 4013220715Sbschmidt /* Start at highest available bit-rate. */ 4014221649Sbschmidt if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) 4015221649Sbschmidt txrate = ni->ni_htrates.rs_nrates - 1; 4016221649Sbschmidt else 4017221649Sbschmidt txrate = rs->rs_nrates - 1; 4018178676Ssam for (i = 0; i < IWN_MAX_TX_RETRIES; i++) { 4019221649Sbschmidt if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) 4020221649Sbschmidt rate = IEEE80211_RATE_MCS | txrate; 4021221649Sbschmidt else 4022221649Sbschmidt rate = rs->rs_rates[txrate] & IEEE80211_RATE_VAL; 4023221648Sbschmidt linkq.retry[i] = wn->ridx[rate]; 4024221648Sbschmidt 4025221649Sbschmidt if ((le32toh(wn->ridx[rate]) & IWN_RFLAG_MCS) && 4026221649Sbschmidt (le32toh(wn->ridx[rate]) & 0xff) > 7) 4027221649Sbschmidt linkq.mimo = i + 1; 4028221649Sbschmidt 4029220715Sbschmidt /* Next retry at immediate lower bit-rate. */ 4030220715Sbschmidt if (txrate > 0) 4031220715Sbschmidt txrate--; 4032178676Ssam } 4033220715Sbschmidt return iwn_cmd(sc, IWN_CMD_LINK_QUALITY, &linkq, sizeof linkq, 1); 4034178676Ssam} 4035178676Ssam 4036178676Ssam/* 4037198429Srpaulo * Broadcast node is used to send group-addressed and management frames. 4038178676Ssam */ 4039206477Sbschmidtstatic int 4040201209Srpauloiwn_add_broadcast_node(struct iwn_softc *sc, int async) 4041178676Ssam{ 4042220728Sbschmidt struct iwn_ops *ops = &sc->ops; 4043198429Srpaulo struct ifnet *ifp = sc->sc_ifp; 4044220715Sbschmidt struct ieee80211com *ic = ifp->if_l2com; 4045178676Ssam struct iwn_node_info node; 4046220715Sbschmidt struct iwn_cmd_link_quality linkq; 4047220715Sbschmidt uint8_t txant; 4048220715Sbschmidt int i, error; 4049178676Ssam 4050178676Ssam memset(&node, 0, sizeof node); 4051198429Srpaulo IEEE80211_ADDR_COPY(node.macaddr, ifp->if_broadcastaddr); 4052220728Sbschmidt node.id = sc->broadcast_id; 4053198429Srpaulo DPRINTF(sc, IWN_DEBUG_RESET, "%s: adding broadcast node\n", __func__); 4054220728Sbschmidt if ((error = ops->add_node(sc, &node, async)) != 0) 4055198429Srpaulo return error; 4056178676Ssam 4057220715Sbschmidt /* Use the first valid TX antenna. */ 4058220715Sbschmidt txant = IWN_LSB(sc->txchainmask); 4059220715Sbschmidt 4060220715Sbschmidt memset(&linkq, 0, sizeof linkq); 4061220728Sbschmidt linkq.id = sc->broadcast_id; 4062220715Sbschmidt linkq.antmsk_1stream = txant; 4063220715Sbschmidt linkq.antmsk_2stream = IWN_ANT_AB; 4064220715Sbschmidt linkq.ampdu_max = 64; 4065220715Sbschmidt linkq.ampdu_threshold = 3; 4066220715Sbschmidt linkq.ampdu_limit = htole16(4000); /* 4ms */ 4067220715Sbschmidt 4068220715Sbschmidt /* Use lowest mandatory bit-rate. */ 4069220715Sbschmidt if (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan)) 4070221648Sbschmidt linkq.retry[0] = htole32(0xd); 4071220715Sbschmidt else 4072221648Sbschmidt linkq.retry[0] = htole32(10 | IWN_RFLAG_CCK); 4073221648Sbschmidt linkq.retry[0] |= htole32(IWN_RFLAG_ANT(txant)); 4074220715Sbschmidt /* Use same bit-rate for all TX retries. */ 4075220715Sbschmidt for (i = 1; i < IWN_MAX_TX_RETRIES; i++) { 4076221648Sbschmidt linkq.retry[i] = linkq.retry[0]; 4077220715Sbschmidt } 4078220715Sbschmidt return iwn_cmd(sc, IWN_CMD_LINK_QUALITY, &linkq, sizeof linkq, async); 4079178676Ssam} 4080178676Ssam 4081206477Sbschmidtstatic int 4082220721Sbschmidtiwn_updateedca(struct ieee80211com *ic) 4083178676Ssam{ 4084178676Ssam#define IWN_EXP2(x) ((1 << (x)) - 1) /* CWmin = 2^ECWmin - 1 */ 4085178676Ssam struct iwn_softc *sc = ic->ic_ifp->if_softc; 4086178676Ssam struct iwn_edca_params cmd; 4087220721Sbschmidt int aci; 4088178676Ssam 4089178676Ssam memset(&cmd, 0, sizeof cmd); 4090178676Ssam cmd.flags = htole32(IWN_EDCA_UPDATE); 4091220721Sbschmidt for (aci = 0; aci < WME_NUM_AC; aci++) { 4092220721Sbschmidt const struct wmeParams *ac = 4093220721Sbschmidt &ic->ic_wme.wme_chanParams.cap_wmeParams[aci]; 4094220721Sbschmidt cmd.ac[aci].aifsn = ac->wmep_aifsn; 4095220721Sbschmidt cmd.ac[aci].cwmin = htole16(IWN_EXP2(ac->wmep_logcwmin)); 4096220721Sbschmidt cmd.ac[aci].cwmax = htole16(IWN_EXP2(ac->wmep_logcwmax)); 4097220721Sbschmidt cmd.ac[aci].txoplimit = 4098220721Sbschmidt htole16(IEEE80211_TXOP_TO_US(ac->wmep_txopLimit)); 4099178676Ssam } 4100201209Srpaulo IEEE80211_UNLOCK(ic); 4101178676Ssam IWN_LOCK(sc); 4102220725Sbschmidt (void)iwn_cmd(sc, IWN_CMD_EDCA_PARAMS, &cmd, sizeof cmd, 1); 4103178676Ssam IWN_UNLOCK(sc); 4104201209Srpaulo IEEE80211_LOCK(ic); 4105178676Ssam return 0; 4106178676Ssam#undef IWN_EXP2 4107178676Ssam} 4108178676Ssam 4109201209Srpaulostatic void 4110201209Srpauloiwn_update_mcast(struct ifnet *ifp) 4111201209Srpaulo{ 4112201209Srpaulo /* Ignore */ 4113201209Srpaulo} 4114201209Srpaulo 4115206477Sbschmidtstatic void 4116178676Ssamiwn_set_led(struct iwn_softc *sc, uint8_t which, uint8_t off, uint8_t on) 4117178676Ssam{ 4118178676Ssam struct iwn_cmd_led led; 4119178676Ssam 4120198429Srpaulo /* Clear microcode LED ownership. */ 4121198429Srpaulo IWN_CLRBITS(sc, IWN_LED, IWN_LED_BSM_CTRL); 4122198429Srpaulo 4123178676Ssam led.which = which; 4124198429Srpaulo led.unit = htole32(10000); /* on/off in unit of 100ms */ 4125178676Ssam led.off = off; 4126178676Ssam led.on = on; 4127198429Srpaulo (void)iwn_cmd(sc, IWN_CMD_SET_LED, &led, sizeof led, 1); 4128178676Ssam} 4129178676Ssam 4130178676Ssam/* 4131201209Srpaulo * Set the critical temperature at which the firmware will stop the radio 4132201209Srpaulo * and notify us. 4133178676Ssam */ 4134206477Sbschmidtstatic int 4135178676Ssamiwn_set_critical_temp(struct iwn_softc *sc) 4136178676Ssam{ 4137178676Ssam struct iwn_critical_temp crit; 4138201209Srpaulo int32_t temp; 4139178676Ssam 4140198429Srpaulo IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_CTEMP_STOP_RF); 4141178676Ssam 4142201209Srpaulo if (sc->hw_type == IWN_HW_REV_TYPE_5150) 4143201209Srpaulo temp = (IWN_CTOK(110) - sc->temp_off) * -5; 4144201209Srpaulo else if (sc->hw_type == IWN_HW_REV_TYPE_4965) 4145201209Srpaulo temp = IWN_CTOK(110); 4146201209Srpaulo else 4147201209Srpaulo temp = 110; 4148178676Ssam memset(&crit, 0, sizeof crit); 4149201209Srpaulo crit.tempR = htole32(temp); 4150220726Sbschmidt DPRINTF(sc, IWN_DEBUG_RESET, "setting critical temp to %d\n", temp); 4151178676Ssam return iwn_cmd(sc, IWN_CMD_SET_CRITICAL_TEMP, &crit, sizeof crit, 0); 4152178676Ssam} 4153178676Ssam 4154206477Sbschmidtstatic int 4155198429Srpauloiwn_set_timing(struct iwn_softc *sc, struct ieee80211_node *ni) 4156178676Ssam{ 4157198429Srpaulo struct iwn_cmd_timing cmd; 4158178676Ssam uint64_t val, mod; 4159178676Ssam 4160198429Srpaulo memset(&cmd, 0, sizeof cmd); 4161198429Srpaulo memcpy(&cmd.tstamp, ni->ni_tstamp.data, sizeof (uint64_t)); 4162198429Srpaulo cmd.bintval = htole16(ni->ni_intval); 4163198429Srpaulo cmd.lintval = htole16(10); 4164178676Ssam 4165198429Srpaulo /* Compute remaining time until next beacon. */ 4166220634Sbschmidt val = (uint64_t)ni->ni_intval * IEEE80211_DUR_TU; 4167198429Srpaulo mod = le64toh(cmd.tstamp) % val; 4168198429Srpaulo cmd.binitval = htole32((uint32_t)(val - mod)); 4169178676Ssam 4170198429Srpaulo DPRINTF(sc, IWN_DEBUG_RESET, "timing bintval=%u tstamp=%ju, init=%u\n", 4171198429Srpaulo ni->ni_intval, le64toh(cmd.tstamp), (uint32_t)(val - mod)); 4172178676Ssam 4173198429Srpaulo return iwn_cmd(sc, IWN_CMD_TIMING, &cmd, sizeof cmd, 1); 4174178676Ssam} 4175178676Ssam 4176206477Sbschmidtstatic void 4177198429Srpauloiwn4965_power_calibration(struct iwn_softc *sc, int temp) 4178178676Ssam{ 4179201882Skeramida struct ifnet *ifp = sc->sc_ifp; 4180201882Skeramida struct ieee80211com *ic = ifp->if_l2com; 4181201882Skeramida 4182220725Sbschmidt /* Adjust TX power if need be (delta >= 3 degC). */ 4183178676Ssam DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: temperature %d->%d\n", 4184178676Ssam __func__, sc->temp, temp); 4185198429Srpaulo if (abs(temp - sc->temp) >= 3) { 4186198429Srpaulo /* Record temperature of last calibration. */ 4187198429Srpaulo sc->temp = temp; 4188201882Skeramida (void)iwn4965_set_txpower(sc, ic->ic_bsschan, 1); 4189178676Ssam } 4190178676Ssam} 4191178676Ssam 4192178676Ssam/* 4193198429Srpaulo * Set TX power for current channel (each rate has its own power settings). 4194178676Ssam * This function takes into account the regulatory information from EEPROM, 4195178676Ssam * the current temperature and the current voltage. 4196178676Ssam */ 4197206477Sbschmidtstatic int 4198201882Skeramidaiwn4965_set_txpower(struct iwn_softc *sc, struct ieee80211_channel *ch, 4199201882Skeramida int async) 4200178676Ssam{ 4201198429Srpaulo/* Fixed-point arithmetic division using a n-bit fractional part. */ 4202178676Ssam#define fdivround(a, b, n) \ 4203178676Ssam ((((1 << n) * (a)) / (b) + (1 << n) / 2) / (1 << n)) 4204198429Srpaulo/* Linear interpolation. */ 4205178676Ssam#define interpolate(x, x1, y1, x2, y2, n) \ 4206178676Ssam ((y1) + fdivround(((int)(x) - (x1)) * ((y2) - (y1)), (x2) - (x1), n)) 4207178676Ssam 4208178676Ssam static const int tdiv[IWN_NATTEN_GROUPS] = { 9, 8, 8, 8, 6 }; 4209178676Ssam struct iwn_ucode_info *uc = &sc->ucode_info; 4210198429Srpaulo struct iwn4965_cmd_txpower cmd; 4211198429Srpaulo struct iwn4965_eeprom_chan_samples *chans; 4212220723Sbschmidt const uint8_t *rf_gain, *dsp_gain; 4213178676Ssam int32_t vdiff, tdiff; 4214178676Ssam int i, c, grp, maxpwr; 4215198429Srpaulo uint8_t chan; 4216178676Ssam 4217220687Sbschmidt /* Retrieve current channel from last RXON. */ 4218220687Sbschmidt chan = sc->rxon.chan; 4219201209Srpaulo DPRINTF(sc, IWN_DEBUG_RESET, "setting TX power for channel %d\n", 4220201209Srpaulo chan); 4221178676Ssam 4222178676Ssam memset(&cmd, 0, sizeof cmd); 4223178676Ssam cmd.band = IEEE80211_IS_CHAN_5GHZ(ch) ? 0 : 1; 4224178676Ssam cmd.chan = chan; 4225178676Ssam 4226178676Ssam if (IEEE80211_IS_CHAN_5GHZ(ch)) { 4227178676Ssam maxpwr = sc->maxpwr5GHz; 4228198429Srpaulo rf_gain = iwn4965_rf_gain_5ghz; 4229198429Srpaulo dsp_gain = iwn4965_dsp_gain_5ghz; 4230178676Ssam } else { 4231178676Ssam maxpwr = sc->maxpwr2GHz; 4232198429Srpaulo rf_gain = iwn4965_rf_gain_2ghz; 4233198429Srpaulo dsp_gain = iwn4965_dsp_gain_2ghz; 4234178676Ssam } 4235178676Ssam 4236198429Srpaulo /* Compute voltage compensation. */ 4237178676Ssam vdiff = ((int32_t)le32toh(uc->volt) - sc->eeprom_voltage) / 7; 4238178676Ssam if (vdiff > 0) 4239178676Ssam vdiff *= 2; 4240178676Ssam if (abs(vdiff) > 2) 4241178676Ssam vdiff = 0; 4242178676Ssam DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW, 4243178676Ssam "%s: voltage compensation=%d (UCODE=%d, EEPROM=%d)\n", 4244178676Ssam __func__, vdiff, le32toh(uc->volt), sc->eeprom_voltage); 4245178676Ssam 4246201209Srpaulo /* Get channel attenuation group. */ 4247178676Ssam if (chan <= 20) /* 1-20 */ 4248178676Ssam grp = 4; 4249178676Ssam else if (chan <= 43) /* 34-43 */ 4250178676Ssam grp = 0; 4251178676Ssam else if (chan <= 70) /* 44-70 */ 4252178676Ssam grp = 1; 4253178676Ssam else if (chan <= 124) /* 71-124 */ 4254178676Ssam grp = 2; 4255178676Ssam else /* 125-200 */ 4256178676Ssam grp = 3; 4257178676Ssam DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW, 4258178676Ssam "%s: chan %d, attenuation group=%d\n", __func__, chan, grp); 4259178676Ssam 4260201209Srpaulo /* Get channel sub-band. */ 4261178676Ssam for (i = 0; i < IWN_NBANDS; i++) 4262178676Ssam if (sc->bands[i].lo != 0 && 4263178676Ssam sc->bands[i].lo <= chan && chan <= sc->bands[i].hi) 4264178676Ssam break; 4265198429Srpaulo if (i == IWN_NBANDS) /* Can't happen in real-life. */ 4266198429Srpaulo return EINVAL; 4267178676Ssam chans = sc->bands[i].chans; 4268178676Ssam DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW, 4269178676Ssam "%s: chan %d sub-band=%d\n", __func__, chan, i); 4270178676Ssam 4271198429Srpaulo for (c = 0; c < 2; c++) { 4272178676Ssam uint8_t power, gain, temp; 4273178676Ssam int maxchpwr, pwr, ridx, idx; 4274178676Ssam 4275178676Ssam power = interpolate(chan, 4276178676Ssam chans[0].num, chans[0].samples[c][1].power, 4277178676Ssam chans[1].num, chans[1].samples[c][1].power, 1); 4278178676Ssam gain = interpolate(chan, 4279178676Ssam chans[0].num, chans[0].samples[c][1].gain, 4280178676Ssam chans[1].num, chans[1].samples[c][1].gain, 1); 4281178676Ssam temp = interpolate(chan, 4282178676Ssam chans[0].num, chans[0].samples[c][1].temp, 4283178676Ssam chans[1].num, chans[1].samples[c][1].temp, 1); 4284178676Ssam DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW, 4285178676Ssam "%s: Tx chain %d: power=%d gain=%d temp=%d\n", 4286178676Ssam __func__, c, power, gain, temp); 4287178676Ssam 4288198429Srpaulo /* Compute temperature compensation. */ 4289178676Ssam tdiff = ((sc->temp - temp) * 2) / tdiv[grp]; 4290178676Ssam DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW, 4291178676Ssam "%s: temperature compensation=%d (current=%d, EEPROM=%d)\n", 4292178676Ssam __func__, tdiff, sc->temp, temp); 4293178676Ssam 4294178676Ssam for (ridx = 0; ridx <= IWN_RIDX_MAX; ridx++) { 4295201209Srpaulo /* Convert dBm to half-dBm. */ 4296198429Srpaulo maxchpwr = sc->maxpwr[chan] * 2; 4297198429Srpaulo if ((ridx / 8) & 1) 4298198429Srpaulo maxchpwr -= 6; /* MIMO 2T: -3dB */ 4299178676Ssam 4300198429Srpaulo pwr = maxpwr; 4301178676Ssam 4302198429Srpaulo /* Adjust TX power based on rate. */ 4303198429Srpaulo if ((ridx % 8) == 5) 4304198429Srpaulo pwr -= 15; /* OFDM48: -7.5dB */ 4305198429Srpaulo else if ((ridx % 8) == 6) 4306198429Srpaulo pwr -= 17; /* OFDM54: -8.5dB */ 4307198429Srpaulo else if ((ridx % 8) == 7) 4308198429Srpaulo pwr -= 20; /* OFDM60: -10dB */ 4309198429Srpaulo else 4310198429Srpaulo pwr -= 10; /* Others: -5dB */ 4311178676Ssam 4312201209Srpaulo /* Do not exceed channel max TX power. */ 4313178676Ssam if (pwr > maxchpwr) 4314178676Ssam pwr = maxchpwr; 4315178676Ssam 4316178676Ssam idx = gain - (pwr - power) - tdiff - vdiff; 4317178676Ssam if ((ridx / 8) & 1) /* MIMO */ 4318178676Ssam idx += (int32_t)le32toh(uc->atten[grp][c]); 4319178676Ssam 4320178676Ssam if (cmd.band == 0) 4321178676Ssam idx += 9; /* 5GHz */ 4322178676Ssam if (ridx == IWN_RIDX_MAX) 4323178676Ssam idx += 5; /* CCK */ 4324178676Ssam 4325198429Srpaulo /* Make sure idx stays in a valid range. */ 4326178676Ssam if (idx < 0) 4327178676Ssam idx = 0; 4328198429Srpaulo else if (idx > IWN4965_MAX_PWR_INDEX) 4329198429Srpaulo idx = IWN4965_MAX_PWR_INDEX; 4330178676Ssam 4331178676Ssam DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW, 4332178676Ssam "%s: Tx chain %d, rate idx %d: power=%d\n", 4333178676Ssam __func__, c, ridx, idx); 4334178676Ssam cmd.power[ridx].rf_gain[c] = rf_gain[idx]; 4335178676Ssam cmd.power[ridx].dsp_gain[c] = dsp_gain[idx]; 4336178676Ssam } 4337178676Ssam } 4338178676Ssam 4339178676Ssam DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW, 4340178676Ssam "%s: set tx power for chan %d\n", __func__, chan); 4341178676Ssam return iwn_cmd(sc, IWN_CMD_TXPOWER, &cmd, sizeof cmd, async); 4342178676Ssam 4343178676Ssam#undef interpolate 4344178676Ssam#undef fdivround 4345178676Ssam} 4346178676Ssam 4347206477Sbschmidtstatic int 4348201882Skeramidaiwn5000_set_txpower(struct iwn_softc *sc, struct ieee80211_channel *ch, 4349201882Skeramida int async) 4350198429Srpaulo{ 4351198429Srpaulo struct iwn5000_cmd_txpower cmd; 4352198429Srpaulo 4353198429Srpaulo /* 4354198429Srpaulo * TX power calibration is handled automatically by the firmware 4355198429Srpaulo * for 5000 Series. 4356198429Srpaulo */ 4357198429Srpaulo memset(&cmd, 0, sizeof cmd); 4358198429Srpaulo cmd.global_limit = 2 * IWN5000_TXPOWER_MAX_DBM; /* 16 dBm */ 4359198429Srpaulo cmd.flags = IWN5000_TXPOWER_NO_CLOSED; 4360198429Srpaulo cmd.srv_limit = IWN5000_TXPOWER_AUTO; 4361198429Srpaulo DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: setting TX power\n", __func__); 4362198429Srpaulo return iwn_cmd(sc, IWN_CMD_TXPOWER_DBM, &cmd, sizeof cmd, async); 4363198429Srpaulo} 4364198429Srpaulo 4365178676Ssam/* 4366198429Srpaulo * Retrieve the maximum RSSI (in dBm) among receivers. 4367178676Ssam */ 4368206477Sbschmidtstatic int 4369198429Srpauloiwn4965_get_rssi(struct iwn_softc *sc, struct iwn_rx_stat *stat) 4370178676Ssam{ 4371198429Srpaulo struct iwn4965_rx_phystat *phy = (void *)stat->phybuf; 4372198429Srpaulo uint8_t mask, agc; 4373198429Srpaulo int rssi; 4374178676Ssam 4375201209Srpaulo mask = (le16toh(phy->antenna) >> 4) & IWN_ANT_ABC; 4376198429Srpaulo agc = (le16toh(phy->agc) >> 7) & 0x7f; 4377178676Ssam 4378178676Ssam rssi = 0; 4379220689Sbschmidt if (mask & IWN_ANT_A) 4380220689Sbschmidt rssi = MAX(rssi, phy->rssi[0]); 4381220689Sbschmidt if (mask & IWN_ANT_B) 4382220689Sbschmidt rssi = MAX(rssi, phy->rssi[2]); 4383220689Sbschmidt if (mask & IWN_ANT_C) 4384220689Sbschmidt rssi = MAX(rssi, phy->rssi[4]); 4385198429Srpaulo 4386220724Sbschmidt DPRINTF(sc, IWN_DEBUG_RECV, 4387220724Sbschmidt "%s: agc %d mask 0x%x rssi %d %d %d result %d\n", __func__, agc, 4388220724Sbschmidt mask, phy->rssi[0], phy->rssi[2], phy->rssi[4], 4389178676Ssam rssi - agc - IWN_RSSI_TO_DBM); 4390178676Ssam return rssi - agc - IWN_RSSI_TO_DBM; 4391178676Ssam} 4392178676Ssam 4393206477Sbschmidtstatic int 4394198429Srpauloiwn5000_get_rssi(struct iwn_softc *sc, struct iwn_rx_stat *stat) 4395198429Srpaulo{ 4396198429Srpaulo struct iwn5000_rx_phystat *phy = (void *)stat->phybuf; 4397220723Sbschmidt uint8_t agc; 4398198429Srpaulo int rssi; 4399198429Srpaulo 4400198429Srpaulo agc = (le32toh(phy->agc) >> 9) & 0x7f; 4401198429Srpaulo 4402198429Srpaulo rssi = MAX(le16toh(phy->rssi[0]) & 0xff, 4403198429Srpaulo le16toh(phy->rssi[1]) & 0xff); 4404198429Srpaulo rssi = MAX(le16toh(phy->rssi[2]) & 0xff, rssi); 4405198429Srpaulo 4406220724Sbschmidt DPRINTF(sc, IWN_DEBUG_RECV, 4407220724Sbschmidt "%s: agc %d rssi %d %d %d result %d\n", __func__, agc, 4408201822Strasz phy->rssi[0], phy->rssi[1], phy->rssi[2], 4409198429Srpaulo rssi - agc - IWN_RSSI_TO_DBM); 4410198429Srpaulo return rssi - agc - IWN_RSSI_TO_DBM; 4411198429Srpaulo} 4412198429Srpaulo 4413178676Ssam/* 4414198429Srpaulo * Retrieve the average noise (in dBm) among receivers. 4415178676Ssam */ 4416206477Sbschmidtstatic int 4417178676Ssamiwn_get_noise(const struct iwn_rx_general_stats *stats) 4418178676Ssam{ 4419178676Ssam int i, total, nbant, noise; 4420178676Ssam 4421178676Ssam total = nbant = 0; 4422178676Ssam for (i = 0; i < 3; i++) { 4423198429Srpaulo if ((noise = le32toh(stats->noise[i]) & 0xff) == 0) 4424198429Srpaulo continue; 4425198429Srpaulo total += noise; 4426198429Srpaulo nbant++; 4427178676Ssam } 4428198429Srpaulo /* There should be at least one antenna but check anyway. */ 4429178676Ssam return (nbant == 0) ? -127 : (total / nbant) - 107; 4430178676Ssam} 4431178676Ssam 4432178676Ssam/* 4433198429Srpaulo * Compute temperature (in degC) from last received statistics. 4434178676Ssam */ 4435206477Sbschmidtstatic int 4436198429Srpauloiwn4965_get_temperature(struct iwn_softc *sc) 4437178676Ssam{ 4438178676Ssam struct iwn_ucode_info *uc = &sc->ucode_info; 4439178676Ssam int32_t r1, r2, r3, r4, temp; 4440178676Ssam 4441178676Ssam r1 = le32toh(uc->temp[0].chan20MHz); 4442178676Ssam r2 = le32toh(uc->temp[1].chan20MHz); 4443178676Ssam r3 = le32toh(uc->temp[2].chan20MHz); 4444178676Ssam r4 = le32toh(sc->rawtemp); 4445178676Ssam 4446220725Sbschmidt if (r1 == r3) /* Prevents division by 0 (should not happen). */ 4447178676Ssam return 0; 4448178676Ssam 4449198429Srpaulo /* Sign-extend 23-bit R4 value to 32-bit. */ 4450220659Sbschmidt r4 = ((r4 & 0xffffff) ^ 0x800000) - 0x800000; 4451198429Srpaulo /* Compute temperature in Kelvin. */ 4452178676Ssam temp = (259 * (r4 - r2)) / (r3 - r1); 4453178676Ssam temp = (temp * 97) / 100 + 8; 4454178676Ssam 4455201209Srpaulo DPRINTF(sc, IWN_DEBUG_ANY, "temperature %dK/%dC\n", temp, 4456201209Srpaulo IWN_KTOC(temp)); 4457178676Ssam return IWN_KTOC(temp); 4458178676Ssam} 4459178676Ssam 4460206477Sbschmidtstatic int 4461198429Srpauloiwn5000_get_temperature(struct iwn_softc *sc) 4462198429Srpaulo{ 4463201209Srpaulo int32_t temp; 4464201209Srpaulo 4465198429Srpaulo /* 4466198429Srpaulo * Temperature is not used by the driver for 5000 Series because 4467220725Sbschmidt * TX power calibration is handled by firmware. 4468198429Srpaulo */ 4469201209Srpaulo temp = le32toh(sc->rawtemp); 4470201209Srpaulo if (sc->hw_type == IWN_HW_REV_TYPE_5150) { 4471201209Srpaulo temp = (temp / -5) + sc->temp_off; 4472201209Srpaulo temp = IWN_KTOC(temp); 4473201209Srpaulo } 4474201209Srpaulo return temp; 4475198429Srpaulo} 4476198429Srpaulo 4477178676Ssam/* 4478178676Ssam * Initialize sensitivity calibration state machine. 4479178676Ssam */ 4480206477Sbschmidtstatic int 4481178676Ssamiwn_init_sensitivity(struct iwn_softc *sc) 4482178676Ssam{ 4483220728Sbschmidt struct iwn_ops *ops = &sc->ops; 4484178676Ssam struct iwn_calib_state *calib = &sc->calib; 4485198429Srpaulo uint32_t flags; 4486178676Ssam int error; 4487178676Ssam 4488198429Srpaulo /* Reset calibration state machine. */ 4489178676Ssam memset(calib, 0, sizeof (*calib)); 4490178676Ssam calib->state = IWN_CALIB_STATE_INIT; 4491178676Ssam calib->cck_state = IWN_CCK_STATE_HIFA; 4492198429Srpaulo /* Set initial correlation values. */ 4493201209Srpaulo calib->ofdm_x1 = sc->limits->min_ofdm_x1; 4494201209Srpaulo calib->ofdm_mrc_x1 = sc->limits->min_ofdm_mrc_x1; 4495206444Sbschmidt calib->ofdm_x4 = sc->limits->min_ofdm_x4; 4496201209Srpaulo calib->ofdm_mrc_x4 = sc->limits->min_ofdm_mrc_x4; 4497198429Srpaulo calib->cck_x4 = 125; 4498201209Srpaulo calib->cck_mrc_x4 = sc->limits->min_cck_mrc_x4; 4499201209Srpaulo calib->energy_cck = sc->limits->energy_cck; 4500178676Ssam 4501198429Srpaulo /* Write initial sensitivity. */ 4502220726Sbschmidt if ((error = iwn_send_sensitivity(sc)) != 0) 4503178676Ssam return error; 4504178676Ssam 4505198429Srpaulo /* Write initial gains. */ 4506220728Sbschmidt if ((error = ops->init_gains(sc)) != 0) 4507198429Srpaulo return error; 4508198429Srpaulo 4509198429Srpaulo /* Request statistics at each beacon interval. */ 4510198429Srpaulo flags = 0; 4511220724Sbschmidt DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: sending request for statistics\n", 4512220724Sbschmidt __func__); 4513198429Srpaulo return iwn_cmd(sc, IWN_CMD_GET_STATISTICS, &flags, sizeof flags, 1); 4514178676Ssam} 4515178676Ssam 4516178676Ssam/* 4517178676Ssam * Collect noise and RSSI statistics for the first 20 beacons received 4518178676Ssam * after association and use them to determine connected antennas and 4519198429Srpaulo * to set differential gains. 4520178676Ssam */ 4521206477Sbschmidtstatic void 4522198429Srpauloiwn_collect_noise(struct iwn_softc *sc, 4523178676Ssam const struct iwn_rx_general_stats *stats) 4524178676Ssam{ 4525220728Sbschmidt struct iwn_ops *ops = &sc->ops; 4526178676Ssam struct iwn_calib_state *calib = &sc->calib; 4527198429Srpaulo uint32_t val; 4528198429Srpaulo int i; 4529178676Ssam 4530198429Srpaulo /* Accumulate RSSI and noise for all 3 antennas. */ 4531178676Ssam for (i = 0; i < 3; i++) { 4532178676Ssam calib->rssi[i] += le32toh(stats->rssi[i]) & 0xff; 4533178676Ssam calib->noise[i] += le32toh(stats->noise[i]) & 0xff; 4534178676Ssam } 4535198429Srpaulo /* NB: We update differential gains only once after 20 beacons. */ 4536178676Ssam if (++calib->nbeacons < 20) 4537178676Ssam return; 4538178676Ssam 4539198429Srpaulo /* Determine highest average RSSI. */ 4540198429Srpaulo val = MAX(calib->rssi[0], calib->rssi[1]); 4541198429Srpaulo val = MAX(calib->rssi[2], val); 4542178676Ssam 4543198429Srpaulo /* Determine which antennas are connected. */ 4544210110Sbschmidt sc->chainmask = sc->rxchainmask; 4545178676Ssam for (i = 0; i < 3; i++) 4546210110Sbschmidt if (val - calib->rssi[i] > 15 * 20) 4547210110Sbschmidt sc->chainmask &= ~(1 << i); 4548210110Sbschmidt DPRINTF(sc, IWN_DEBUG_CALIBRATE, 4549210110Sbschmidt "%s: RX chains mask: theoretical=0x%x, actual=0x%x\n", 4550210110Sbschmidt __func__, sc->rxchainmask, sc->chainmask); 4551210110Sbschmidt 4552198429Srpaulo /* If none of the TX antennas are connected, keep at least one. */ 4553201209Srpaulo if ((sc->chainmask & sc->txchainmask) == 0) 4554201209Srpaulo sc->chainmask |= IWN_LSB(sc->txchainmask); 4555178676Ssam 4556220728Sbschmidt (void)ops->set_gains(sc); 4557198429Srpaulo calib->state = IWN_CALIB_STATE_RUN; 4558198429Srpaulo 4559198429Srpaulo#ifdef notyet 4560198429Srpaulo /* XXX Disable RX chains with no antennas connected. */ 4561201209Srpaulo sc->rxon.rxchain = htole16(IWN_RXCHAIN_SEL(sc->chainmask)); 4562220728Sbschmidt (void)iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, sc->rxonsz, 1); 4563198429Srpaulo#endif 4564198429Srpaulo 4565198429Srpaulo#if 0 4566198429Srpaulo /* XXX: not yet */ 4567198429Srpaulo /* Enable power-saving mode if requested by user. */ 4568198429Srpaulo if (sc->sc_ic.ic_flags & IEEE80211_F_PMGTON) 4569198429Srpaulo (void)iwn_set_pslevel(sc, 0, 3, 1); 4570198429Srpaulo#endif 4571198429Srpaulo} 4572198429Srpaulo 4573206477Sbschmidtstatic int 4574198429Srpauloiwn4965_init_gains(struct iwn_softc *sc) 4575198429Srpaulo{ 4576198429Srpaulo struct iwn_phy_calib_gain cmd; 4577198429Srpaulo 4578198429Srpaulo memset(&cmd, 0, sizeof cmd); 4579198429Srpaulo cmd.code = IWN4965_PHY_CALIB_DIFF_GAIN; 4580198429Srpaulo /* Differential gains initially set to 0 for all 3 antennas. */ 4581198429Srpaulo DPRINTF(sc, IWN_DEBUG_CALIBRATE, 4582198429Srpaulo "%s: setting initial differential gains\n", __func__); 4583198429Srpaulo return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1); 4584198429Srpaulo} 4585198429Srpaulo 4586206477Sbschmidtstatic int 4587198429Srpauloiwn5000_init_gains(struct iwn_softc *sc) 4588198429Srpaulo{ 4589198429Srpaulo struct iwn_phy_calib cmd; 4590198429Srpaulo 4591198429Srpaulo memset(&cmd, 0, sizeof cmd); 4592220866Sbschmidt cmd.code = sc->reset_noise_gain; 4593198429Srpaulo cmd.ngroups = 1; 4594198429Srpaulo cmd.isvalid = 1; 4595198429Srpaulo DPRINTF(sc, IWN_DEBUG_CALIBRATE, 4596198429Srpaulo "%s: setting initial differential gains\n", __func__); 4597198429Srpaulo return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1); 4598198429Srpaulo} 4599198429Srpaulo 4600206477Sbschmidtstatic int 4601198429Srpauloiwn4965_set_gains(struct iwn_softc *sc) 4602198429Srpaulo{ 4603198429Srpaulo struct iwn_calib_state *calib = &sc->calib; 4604198429Srpaulo struct iwn_phy_calib_gain cmd; 4605198429Srpaulo int i, delta, noise; 4606198429Srpaulo 4607198429Srpaulo /* Get minimal noise among connected antennas. */ 4608201209Srpaulo noise = INT_MAX; /* NB: There's at least one antenna. */ 4609178676Ssam for (i = 0; i < 3; i++) 4610201209Srpaulo if (sc->chainmask & (1 << i)) 4611198429Srpaulo noise = MIN(calib->noise[i], noise); 4612178676Ssam 4613178676Ssam memset(&cmd, 0, sizeof cmd); 4614198429Srpaulo cmd.code = IWN4965_PHY_CALIB_DIFF_GAIN; 4615198429Srpaulo /* Set differential gains for connected antennas. */ 4616178676Ssam for (i = 0; i < 3; i++) { 4617201209Srpaulo if (sc->chainmask & (1 << i)) { 4618198429Srpaulo /* Compute attenuation (in unit of 1.5dB). */ 4619198429Srpaulo delta = (noise - (int32_t)calib->noise[i]) / 30; 4620198429Srpaulo /* NB: delta <= 0 */ 4621198429Srpaulo /* Limit to [-4.5dB,0]. */ 4622198429Srpaulo cmd.gain[i] = MIN(abs(delta), 3); 4623198429Srpaulo if (delta < 0) 4624198429Srpaulo cmd.gain[i] |= 1 << 2; /* sign bit */ 4625178676Ssam } 4626178676Ssam } 4627178676Ssam DPRINTF(sc, IWN_DEBUG_CALIBRATE, 4628198429Srpaulo "setting differential gains Ant A/B/C: %x/%x/%x (%x)\n", 4629201209Srpaulo cmd.gain[0], cmd.gain[1], cmd.gain[2], sc->chainmask); 4630198429Srpaulo return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1); 4631178676Ssam} 4632178676Ssam 4633206477Sbschmidtstatic int 4634198429Srpauloiwn5000_set_gains(struct iwn_softc *sc) 4635198429Srpaulo{ 4636198429Srpaulo struct iwn_calib_state *calib = &sc->calib; 4637198429Srpaulo struct iwn_phy_calib_gain cmd; 4638220723Sbschmidt int i, ant, div, delta; 4639198429Srpaulo 4640206444Sbschmidt /* We collected 20 beacons and !=6050 need a 1.5 factor. */ 4641206444Sbschmidt div = (sc->hw_type == IWN_HW_REV_TYPE_6050) ? 20 : 30; 4642198429Srpaulo 4643198429Srpaulo memset(&cmd, 0, sizeof cmd); 4644220866Sbschmidt cmd.code = sc->noise_gain; 4645198429Srpaulo cmd.ngroups = 1; 4646198429Srpaulo cmd.isvalid = 1; 4647201209Srpaulo /* Get first available RX antenna as referential. */ 4648201209Srpaulo ant = IWN_LSB(sc->rxchainmask); 4649201209Srpaulo /* Set differential gains for other antennas. */ 4650201209Srpaulo for (i = ant + 1; i < 3; i++) { 4651201209Srpaulo if (sc->chainmask & (1 << i)) { 4652201209Srpaulo /* The delta is relative to antenna "ant". */ 4653201209Srpaulo delta = ((int32_t)calib->noise[ant] - 4654206444Sbschmidt (int32_t)calib->noise[i]) / div; 4655198429Srpaulo /* Limit to [-4.5dB,+4.5dB]. */ 4656198429Srpaulo cmd.gain[i - 1] = MIN(abs(delta), 3); 4657198429Srpaulo if (delta < 0) 4658198429Srpaulo cmd.gain[i - 1] |= 1 << 2; /* sign bit */ 4659198429Srpaulo } 4660198429Srpaulo } 4661198429Srpaulo DPRINTF(sc, IWN_DEBUG_CALIBRATE, 4662198429Srpaulo "setting differential gains Ant B/C: %x/%x (%x)\n", 4663201209Srpaulo cmd.gain[0], cmd.gain[1], sc->chainmask); 4664198429Srpaulo return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1); 4665198429Srpaulo} 4666198429Srpaulo 4667178676Ssam/* 4668198429Srpaulo * Tune RF RX sensitivity based on the number of false alarms detected 4669178676Ssam * during the last beacon period. 4670178676Ssam */ 4671206477Sbschmidtstatic void 4672178676Ssamiwn_tune_sensitivity(struct iwn_softc *sc, const struct iwn_rx_stats *stats) 4673178676Ssam{ 4674198429Srpaulo#define inc(val, inc, max) \ 4675178676Ssam if ((val) < (max)) { \ 4676178676Ssam if ((val) < (max) - (inc)) \ 4677178676Ssam (val) += (inc); \ 4678178676Ssam else \ 4679178676Ssam (val) = (max); \ 4680178676Ssam needs_update = 1; \ 4681178676Ssam } 4682198429Srpaulo#define dec(val, dec, min) \ 4683178676Ssam if ((val) > (min)) { \ 4684178676Ssam if ((val) > (min) + (dec)) \ 4685178676Ssam (val) -= (dec); \ 4686178676Ssam else \ 4687178676Ssam (val) = (min); \ 4688178676Ssam needs_update = 1; \ 4689178676Ssam } 4690178676Ssam 4691201209Srpaulo const struct iwn_sensitivity_limits *limits = sc->limits; 4692178676Ssam struct iwn_calib_state *calib = &sc->calib; 4693178676Ssam uint32_t val, rxena, fa; 4694178676Ssam uint32_t energy[3], energy_min; 4695198439Srpaulo uint8_t noise[3], noise_ref; 4696198429Srpaulo int i, needs_update = 0; 4697178676Ssam 4698198429Srpaulo /* Check that we've been enabled long enough. */ 4699220726Sbschmidt if ((rxena = le32toh(stats->general.load)) == 0) 4700178676Ssam return; 4701178676Ssam 4702198429Srpaulo /* Compute number of false alarms since last call for OFDM. */ 4703178676Ssam fa = le32toh(stats->ofdm.bad_plcp) - calib->bad_plcp_ofdm; 4704178676Ssam fa += le32toh(stats->ofdm.fa) - calib->fa_ofdm; 4705220634Sbschmidt fa *= 200 * IEEE80211_DUR_TU; /* 200TU */ 4706178676Ssam 4707198429Srpaulo /* Save counters values for next call. */ 4708178676Ssam calib->bad_plcp_ofdm = le32toh(stats->ofdm.bad_plcp); 4709178676Ssam calib->fa_ofdm = le32toh(stats->ofdm.fa); 4710178676Ssam 4711178676Ssam if (fa > 50 * rxena) { 4712198429Srpaulo /* High false alarm count, decrease sensitivity. */ 4713178676Ssam DPRINTF(sc, IWN_DEBUG_CALIBRATE, 4714178676Ssam "%s: OFDM high false alarm count: %u\n", __func__, fa); 4715198429Srpaulo inc(calib->ofdm_x1, 1, limits->max_ofdm_x1); 4716198429Srpaulo inc(calib->ofdm_mrc_x1, 1, limits->max_ofdm_mrc_x1); 4717198429Srpaulo inc(calib->ofdm_x4, 1, limits->max_ofdm_x4); 4718198429Srpaulo inc(calib->ofdm_mrc_x4, 1, limits->max_ofdm_mrc_x4); 4719178676Ssam 4720178676Ssam } else if (fa < 5 * rxena) { 4721198429Srpaulo /* Low false alarm count, increase sensitivity. */ 4722178676Ssam DPRINTF(sc, IWN_DEBUG_CALIBRATE, 4723178676Ssam "%s: OFDM low false alarm count: %u\n", __func__, fa); 4724198429Srpaulo dec(calib->ofdm_x1, 1, limits->min_ofdm_x1); 4725198429Srpaulo dec(calib->ofdm_mrc_x1, 1, limits->min_ofdm_mrc_x1); 4726198429Srpaulo dec(calib->ofdm_x4, 1, limits->min_ofdm_x4); 4727198429Srpaulo dec(calib->ofdm_mrc_x4, 1, limits->min_ofdm_mrc_x4); 4728178676Ssam } 4729178676Ssam 4730198429Srpaulo /* Compute maximum noise among 3 receivers. */ 4731178676Ssam for (i = 0; i < 3; i++) 4732178676Ssam noise[i] = (le32toh(stats->general.noise[i]) >> 8) & 0xff; 4733198429Srpaulo val = MAX(noise[0], noise[1]); 4734198429Srpaulo val = MAX(noise[2], val); 4735198429Srpaulo /* Insert it into our samples table. */ 4736178676Ssam calib->noise_samples[calib->cur_noise_sample] = val; 4737178676Ssam calib->cur_noise_sample = (calib->cur_noise_sample + 1) % 20; 4738178676Ssam 4739198429Srpaulo /* Compute maximum noise among last 20 samples. */ 4740178676Ssam noise_ref = calib->noise_samples[0]; 4741178676Ssam for (i = 1; i < 20; i++) 4742198429Srpaulo noise_ref = MAX(noise_ref, calib->noise_samples[i]); 4743178676Ssam 4744198429Srpaulo /* Compute maximum energy among 3 receivers. */ 4745178676Ssam for (i = 0; i < 3; i++) 4746178676Ssam energy[i] = le32toh(stats->general.energy[i]); 4747198429Srpaulo val = MIN(energy[0], energy[1]); 4748198429Srpaulo val = MIN(energy[2], val); 4749198429Srpaulo /* Insert it into our samples table. */ 4750178676Ssam calib->energy_samples[calib->cur_energy_sample] = val; 4751178676Ssam calib->cur_energy_sample = (calib->cur_energy_sample + 1) % 10; 4752178676Ssam 4753198429Srpaulo /* Compute minimum energy among last 10 samples. */ 4754178676Ssam energy_min = calib->energy_samples[0]; 4755178676Ssam for (i = 1; i < 10; i++) 4756198429Srpaulo energy_min = MAX(energy_min, calib->energy_samples[i]); 4757178676Ssam energy_min += 6; 4758178676Ssam 4759198429Srpaulo /* Compute number of false alarms since last call for CCK. */ 4760178676Ssam fa = le32toh(stats->cck.bad_plcp) - calib->bad_plcp_cck; 4761178676Ssam fa += le32toh(stats->cck.fa) - calib->fa_cck; 4762220634Sbschmidt fa *= 200 * IEEE80211_DUR_TU; /* 200TU */ 4763178676Ssam 4764198429Srpaulo /* Save counters values for next call. */ 4765178676Ssam calib->bad_plcp_cck = le32toh(stats->cck.bad_plcp); 4766178676Ssam calib->fa_cck = le32toh(stats->cck.fa); 4767178676Ssam 4768178676Ssam if (fa > 50 * rxena) { 4769198429Srpaulo /* High false alarm count, decrease sensitivity. */ 4770178676Ssam DPRINTF(sc, IWN_DEBUG_CALIBRATE, 4771178676Ssam "%s: CCK high false alarm count: %u\n", __func__, fa); 4772178676Ssam calib->cck_state = IWN_CCK_STATE_HIFA; 4773178676Ssam calib->low_fa = 0; 4774178676Ssam 4775198429Srpaulo if (calib->cck_x4 > 160) { 4776178676Ssam calib->noise_ref = noise_ref; 4777178676Ssam if (calib->energy_cck > 2) 4778198429Srpaulo dec(calib->energy_cck, 2, energy_min); 4779178676Ssam } 4780198429Srpaulo if (calib->cck_x4 < 160) { 4781198429Srpaulo calib->cck_x4 = 161; 4782178676Ssam needs_update = 1; 4783178676Ssam } else 4784198429Srpaulo inc(calib->cck_x4, 3, limits->max_cck_x4); 4785178676Ssam 4786198429Srpaulo inc(calib->cck_mrc_x4, 3, limits->max_cck_mrc_x4); 4787178676Ssam 4788178676Ssam } else if (fa < 5 * rxena) { 4789198429Srpaulo /* Low false alarm count, increase sensitivity. */ 4790178676Ssam DPRINTF(sc, IWN_DEBUG_CALIBRATE, 4791178676Ssam "%s: CCK low false alarm count: %u\n", __func__, fa); 4792178676Ssam calib->cck_state = IWN_CCK_STATE_LOFA; 4793178676Ssam calib->low_fa++; 4794178676Ssam 4795198429Srpaulo if (calib->cck_state != IWN_CCK_STATE_INIT && 4796198429Srpaulo (((int32_t)calib->noise_ref - (int32_t)noise_ref) > 2 || 4797220726Sbschmidt calib->low_fa > 100)) { 4798198429Srpaulo inc(calib->energy_cck, 2, limits->min_energy_cck); 4799198429Srpaulo dec(calib->cck_x4, 3, limits->min_cck_x4); 4800198429Srpaulo dec(calib->cck_mrc_x4, 3, limits->min_cck_mrc_x4); 4801178676Ssam } 4802178676Ssam } else { 4803198429Srpaulo /* Not worth to increase or decrease sensitivity. */ 4804178676Ssam DPRINTF(sc, IWN_DEBUG_CALIBRATE, 4805178676Ssam "%s: CCK normal false alarm count: %u\n", __func__, fa); 4806178676Ssam calib->low_fa = 0; 4807178676Ssam calib->noise_ref = noise_ref; 4808178676Ssam 4809178676Ssam if (calib->cck_state == IWN_CCK_STATE_HIFA) { 4810198429Srpaulo /* Previous interval had many false alarms. */ 4811198429Srpaulo dec(calib->energy_cck, 8, energy_min); 4812178676Ssam } 4813178676Ssam calib->cck_state = IWN_CCK_STATE_INIT; 4814178676Ssam } 4815178676Ssam 4816178676Ssam if (needs_update) 4817178676Ssam (void)iwn_send_sensitivity(sc); 4818198429Srpaulo#undef dec 4819198429Srpaulo#undef inc 4820178676Ssam} 4821178676Ssam 4822206477Sbschmidtstatic int 4823178676Ssamiwn_send_sensitivity(struct iwn_softc *sc) 4824178676Ssam{ 4825178676Ssam struct iwn_calib_state *calib = &sc->calib; 4826220729Sbschmidt struct iwn_enhanced_sensitivity_cmd cmd; 4827220729Sbschmidt int len; 4828178676Ssam 4829178676Ssam memset(&cmd, 0, sizeof cmd); 4830220729Sbschmidt len = sizeof (struct iwn_sensitivity_cmd); 4831178676Ssam cmd.which = IWN_SENSITIVITY_WORKTBL; 4832198429Srpaulo /* OFDM modulation. */ 4833220726Sbschmidt cmd.corr_ofdm_x1 = htole16(calib->ofdm_x1); 4834220726Sbschmidt cmd.corr_ofdm_mrc_x1 = htole16(calib->ofdm_mrc_x1); 4835220726Sbschmidt cmd.corr_ofdm_x4 = htole16(calib->ofdm_x4); 4836220726Sbschmidt cmd.corr_ofdm_mrc_x4 = htole16(calib->ofdm_mrc_x4); 4837220726Sbschmidt cmd.energy_ofdm = htole16(sc->limits->energy_ofdm); 4838220726Sbschmidt cmd.energy_ofdm_th = htole16(62); 4839198429Srpaulo /* CCK modulation. */ 4840220726Sbschmidt cmd.corr_cck_x4 = htole16(calib->cck_x4); 4841220726Sbschmidt cmd.corr_cck_mrc_x4 = htole16(calib->cck_mrc_x4); 4842220726Sbschmidt cmd.energy_cck = htole16(calib->energy_cck); 4843198429Srpaulo /* Barker modulation: use default values. */ 4844220726Sbschmidt cmd.corr_barker = htole16(190); 4845220726Sbschmidt cmd.corr_barker_mrc = htole16(390); 4846178676Ssam 4847202986Srpaulo DPRINTF(sc, IWN_DEBUG_CALIBRATE, 4848178676Ssam "%s: set sensitivity %d/%d/%d/%d/%d/%d/%d\n", __func__, 4849198429Srpaulo calib->ofdm_x1, calib->ofdm_mrc_x1, calib->ofdm_x4, 4850198429Srpaulo calib->ofdm_mrc_x4, calib->cck_x4, 4851198429Srpaulo calib->cck_mrc_x4, calib->energy_cck); 4852220729Sbschmidt 4853220729Sbschmidt if (!(sc->sc_flags & IWN_FLAG_ENH_SENS)) 4854220729Sbschmidt goto send; 4855220729Sbschmidt /* Enhanced sensitivity settings. */ 4856220729Sbschmidt len = sizeof (struct iwn_enhanced_sensitivity_cmd); 4857220729Sbschmidt cmd.ofdm_det_slope_mrc = htole16(668); 4858220729Sbschmidt cmd.ofdm_det_icept_mrc = htole16(4); 4859220729Sbschmidt cmd.ofdm_det_slope = htole16(486); 4860220729Sbschmidt cmd.ofdm_det_icept = htole16(37); 4861220729Sbschmidt cmd.cck_det_slope_mrc = htole16(853); 4862220729Sbschmidt cmd.cck_det_icept_mrc = htole16(4); 4863220729Sbschmidt cmd.cck_det_slope = htole16(476); 4864220729Sbschmidt cmd.cck_det_icept = htole16(99); 4865220729Sbschmidtsend: 4866220729Sbschmidt return iwn_cmd(sc, IWN_CMD_SET_SENSITIVITY, &cmd, len, 1); 4867178676Ssam} 4868178676Ssam 4869198429Srpaulo/* 4870198429Srpaulo * Set STA mode power saving level (between 0 and 5). 4871198429Srpaulo * Level 0 is CAM (Continuously Aware Mode), 5 is for maximum power saving. 4872198429Srpaulo */ 4873206477Sbschmidtstatic int 4874198429Srpauloiwn_set_pslevel(struct iwn_softc *sc, int dtim, int level, int async) 4875198429Srpaulo{ 4876220723Sbschmidt struct iwn_pmgt_cmd cmd; 4877198429Srpaulo const struct iwn_pmgt *pmgt; 4878198429Srpaulo uint32_t max, skip_dtim; 4879220721Sbschmidt uint32_t reg; 4880198429Srpaulo int i; 4881198429Srpaulo 4882198429Srpaulo /* Select which PS parameters to use. */ 4883198429Srpaulo if (dtim <= 2) 4884198429Srpaulo pmgt = &iwn_pmgt[0][level]; 4885198429Srpaulo else if (dtim <= 10) 4886198429Srpaulo pmgt = &iwn_pmgt[1][level]; 4887198429Srpaulo else 4888198429Srpaulo pmgt = &iwn_pmgt[2][level]; 4889198429Srpaulo 4890198429Srpaulo memset(&cmd, 0, sizeof cmd); 4891198429Srpaulo if (level != 0) /* not CAM */ 4892198429Srpaulo cmd.flags |= htole16(IWN_PS_ALLOW_SLEEP); 4893198429Srpaulo if (level == 5) 4894198429Srpaulo cmd.flags |= htole16(IWN_PS_FAST_PD); 4895201209Srpaulo /* Retrieve PCIe Active State Power Management (ASPM). */ 4896220721Sbschmidt reg = pci_read_config(sc->sc_dev, sc->sc_cap_off + 0x10, 1); 4897220721Sbschmidt if (!(reg & 0x1)) /* L0s Entry disabled. */ 4898198429Srpaulo cmd.flags |= htole16(IWN_PS_PCI_PMGT); 4899198429Srpaulo cmd.rxtimeout = htole32(pmgt->rxtimeout * 1024); 4900198429Srpaulo cmd.txtimeout = htole32(pmgt->txtimeout * 1024); 4901198429Srpaulo 4902198429Srpaulo if (dtim == 0) { 4903198429Srpaulo dtim = 1; 4904198429Srpaulo skip_dtim = 0; 4905198429Srpaulo } else 4906198429Srpaulo skip_dtim = pmgt->skip_dtim; 4907198429Srpaulo if (skip_dtim != 0) { 4908198429Srpaulo cmd.flags |= htole16(IWN_PS_SLEEP_OVER_DTIM); 4909198429Srpaulo max = pmgt->intval[4]; 4910198429Srpaulo if (max == (uint32_t)-1) 4911198429Srpaulo max = dtim * (skip_dtim + 1); 4912198429Srpaulo else if (max > dtim) 4913198429Srpaulo max = (max / dtim) * dtim; 4914198429Srpaulo } else 4915198429Srpaulo max = dtim; 4916198429Srpaulo for (i = 0; i < 5; i++) 4917198429Srpaulo cmd.intval[i] = htole32(MIN(max, pmgt->intval[i])); 4918198429Srpaulo 4919198429Srpaulo DPRINTF(sc, IWN_DEBUG_RESET, "setting power saving level to %d\n", 4920198429Srpaulo level); 4921198429Srpaulo return iwn_cmd(sc, IWN_CMD_SET_POWER_MODE, &cmd, sizeof cmd, async); 4922198429Srpaulo} 4923198429Srpaulo 4924206477Sbschmidtstatic int 4925220662Sbschmidtiwn_send_btcoex(struct iwn_softc *sc) 4926220662Sbschmidt{ 4927220662Sbschmidt struct iwn_bluetooth cmd; 4928220662Sbschmidt 4929220662Sbschmidt memset(&cmd, 0, sizeof cmd); 4930220662Sbschmidt cmd.flags = IWN_BT_COEX_CHAN_ANN | IWN_BT_COEX_BT_PRIO; 4931220662Sbschmidt cmd.lead_time = IWN_BT_LEAD_TIME_DEF; 4932220662Sbschmidt cmd.max_kill = IWN_BT_MAX_KILL_DEF; 4933220662Sbschmidt DPRINTF(sc, IWN_DEBUG_RESET, "%s: configuring bluetooth coexistence\n", 4934220662Sbschmidt __func__); 4935220662Sbschmidt return iwn_cmd(sc, IWN_CMD_BT_COEX, &cmd, sizeof(cmd), 0); 4936220662Sbschmidt} 4937220662Sbschmidt 4938220662Sbschmidtstatic int 4939220891Sbschmidtiwn_send_advanced_btcoex(struct iwn_softc *sc) 4940220891Sbschmidt{ 4941220891Sbschmidt static const uint32_t btcoex_3wire[12] = { 4942220891Sbschmidt 0xaaaaaaaa, 0xaaaaaaaa, 0xaeaaaaaa, 0xaaaaaaaa, 4943220891Sbschmidt 0xcc00ff28, 0x0000aaaa, 0xcc00aaaa, 0x0000aaaa, 4944220891Sbschmidt 0xc0004000, 0x00004000, 0xf0005000, 0xf0005000, 4945220891Sbschmidt }; 4946220891Sbschmidt struct iwn6000_btcoex_config btconfig; 4947220891Sbschmidt struct iwn_btcoex_priotable btprio; 4948220891Sbschmidt struct iwn_btcoex_prot btprot; 4949220891Sbschmidt int error, i; 4950220891Sbschmidt 4951220891Sbschmidt memset(&btconfig, 0, sizeof btconfig); 4952220891Sbschmidt btconfig.flags = 145; 4953220891Sbschmidt btconfig.max_kill = 5; 4954220891Sbschmidt btconfig.bt3_t7_timer = 1; 4955220891Sbschmidt btconfig.kill_ack = htole32(0xffff0000); 4956220891Sbschmidt btconfig.kill_cts = htole32(0xffff0000); 4957220891Sbschmidt btconfig.sample_time = 2; 4958220891Sbschmidt btconfig.bt3_t2_timer = 0xc; 4959220891Sbschmidt for (i = 0; i < 12; i++) 4960220891Sbschmidt btconfig.lookup_table[i] = htole32(btcoex_3wire[i]); 4961220891Sbschmidt btconfig.valid = htole16(0xff); 4962220891Sbschmidt btconfig.prio_boost = 0xf0; 4963220891Sbschmidt DPRINTF(sc, IWN_DEBUG_RESET, 4964220891Sbschmidt "%s: configuring advanced bluetooth coexistence\n", __func__); 4965220891Sbschmidt error = iwn_cmd(sc, IWN_CMD_BT_COEX, &btconfig, sizeof(btconfig), 1); 4966220891Sbschmidt if (error != 0) 4967220891Sbschmidt return error; 4968220891Sbschmidt 4969220891Sbschmidt memset(&btprio, 0, sizeof btprio); 4970220891Sbschmidt btprio.calib_init1 = 0x6; 4971220891Sbschmidt btprio.calib_init2 = 0x7; 4972220891Sbschmidt btprio.calib_periodic_low1 = 0x2; 4973220891Sbschmidt btprio.calib_periodic_low2 = 0x3; 4974220891Sbschmidt btprio.calib_periodic_high1 = 0x4; 4975220891Sbschmidt btprio.calib_periodic_high2 = 0x5; 4976220891Sbschmidt btprio.dtim = 0x6; 4977220891Sbschmidt btprio.scan52 = 0x8; 4978220891Sbschmidt btprio.scan24 = 0xa; 4979220891Sbschmidt error = iwn_cmd(sc, IWN_CMD_BT_COEX_PRIOTABLE, &btprio, sizeof(btprio), 4980220891Sbschmidt 1); 4981220891Sbschmidt if (error != 0) 4982220891Sbschmidt return error; 4983220891Sbschmidt 4984220891Sbschmidt /* Force BT state machine change. */ 4985220891Sbschmidt memset(&btprot, 0, sizeof btprio); 4986220891Sbschmidt btprot.open = 1; 4987220891Sbschmidt btprot.type = 1; 4988220891Sbschmidt error = iwn_cmd(sc, IWN_CMD_BT_COEX_PROT, &btprot, sizeof(btprot), 1); 4989220891Sbschmidt if (error != 0) 4990220891Sbschmidt return error; 4991220891Sbschmidt btprot.open = 0; 4992220891Sbschmidt return iwn_cmd(sc, IWN_CMD_BT_COEX_PROT, &btprot, sizeof(btprot), 1); 4993220891Sbschmidt} 4994220891Sbschmidt 4995220891Sbschmidtstatic int 4996198429Srpauloiwn_config(struct iwn_softc *sc) 4997198429Srpaulo{ 4998220728Sbschmidt struct iwn_ops *ops = &sc->ops; 4999198429Srpaulo struct ifnet *ifp = sc->sc_ifp; 5000198429Srpaulo struct ieee80211com *ic = ifp->if_l2com; 5001201209Srpaulo uint32_t txmask; 5002220723Sbschmidt uint16_t rxchain; 5003198429Srpaulo int error; 5004198429Srpaulo 5005220676Sbschmidt if (sc->hw_type == IWN_HW_REV_TYPE_6005) { 5006220676Sbschmidt /* Set radio temperature sensor offset. */ 5007220676Sbschmidt error = iwn5000_temp_offset_calib(sc); 5008220676Sbschmidt if (error != 0) { 5009220676Sbschmidt device_printf(sc->sc_dev, 5010220676Sbschmidt "%s: could not set temperature offset\n", __func__); 5011220676Sbschmidt return error; 5012220676Sbschmidt } 5013220676Sbschmidt } 5014220676Sbschmidt 5015220725Sbschmidt /* Configure valid TX chains for >=5000 Series. */ 5016201209Srpaulo if (sc->hw_type != IWN_HW_REV_TYPE_4965) { 5017201209Srpaulo txmask = htole32(sc->txchainmask); 5018201209Srpaulo DPRINTF(sc, IWN_DEBUG_RESET, 5019201209Srpaulo "%s: configuring valid TX chains 0x%x\n", __func__, txmask); 5020201209Srpaulo error = iwn_cmd(sc, IWN5000_CMD_TX_ANT_CONFIG, &txmask, 5021201209Srpaulo sizeof txmask, 0); 5022201209Srpaulo if (error != 0) { 5023201209Srpaulo device_printf(sc->sc_dev, 5024201209Srpaulo "%s: could not configure valid TX chains, " 5025201209Srpaulo "error %d\n", __func__, error); 5026201209Srpaulo return error; 5027201209Srpaulo } 5028198429Srpaulo } 5029198429Srpaulo 5030198429Srpaulo /* Configure bluetooth coexistence. */ 5031220891Sbschmidt if (sc->sc_flags & IWN_FLAG_ADV_BTCOEX) 5032220891Sbschmidt error = iwn_send_advanced_btcoex(sc); 5033220891Sbschmidt else 5034220891Sbschmidt error = iwn_send_btcoex(sc); 5035198429Srpaulo if (error != 0) { 5036198429Srpaulo device_printf(sc->sc_dev, 5037198429Srpaulo "%s: could not configure bluetooth coexistence, error %d\n", 5038198429Srpaulo __func__, error); 5039198429Srpaulo return error; 5040198429Srpaulo } 5041198429Srpaulo 5042201209Srpaulo /* Set mode, channel, RX filter and enable RX. */ 5043198429Srpaulo memset(&sc->rxon, 0, sizeof (struct iwn_rxon)); 5044198429Srpaulo IEEE80211_ADDR_COPY(sc->rxon.myaddr, IF_LLADDR(ifp)); 5045198429Srpaulo IEEE80211_ADDR_COPY(sc->rxon.wlap, IF_LLADDR(ifp)); 5046198429Srpaulo sc->rxon.chan = ieee80211_chan2ieee(ic, ic->ic_curchan); 5047198429Srpaulo sc->rxon.flags = htole32(IWN_RXON_TSF | IWN_RXON_CTS_TO_SELF); 5048198429Srpaulo if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) 5049198429Srpaulo sc->rxon.flags |= htole32(IWN_RXON_AUTO | IWN_RXON_24GHZ); 5050198429Srpaulo switch (ic->ic_opmode) { 5051198429Srpaulo case IEEE80211_M_STA: 5052198429Srpaulo sc->rxon.mode = IWN_MODE_STA; 5053198429Srpaulo sc->rxon.filter = htole32(IWN_FILTER_MULTICAST); 5054198429Srpaulo break; 5055198429Srpaulo case IEEE80211_M_MONITOR: 5056198429Srpaulo sc->rxon.mode = IWN_MODE_MONITOR; 5057198429Srpaulo sc->rxon.filter = htole32(IWN_FILTER_MULTICAST | 5058198429Srpaulo IWN_FILTER_CTL | IWN_FILTER_PROMISC); 5059198429Srpaulo break; 5060198429Srpaulo default: 5061198429Srpaulo /* Should not get there. */ 5062198429Srpaulo break; 5063198429Srpaulo } 5064198429Srpaulo sc->rxon.cck_mask = 0x0f; /* not yet negotiated */ 5065198429Srpaulo sc->rxon.ofdm_mask = 0xff; /* not yet negotiated */ 5066198429Srpaulo sc->rxon.ht_single_mask = 0xff; 5067198429Srpaulo sc->rxon.ht_dual_mask = 0xff; 5068201209Srpaulo sc->rxon.ht_triple_mask = 0xff; 5069201209Srpaulo rxchain = 5070201209Srpaulo IWN_RXCHAIN_VALID(sc->rxchainmask) | 5071201209Srpaulo IWN_RXCHAIN_MIMO_COUNT(2) | 5072201209Srpaulo IWN_RXCHAIN_IDLE_COUNT(2); 5073198429Srpaulo sc->rxon.rxchain = htole16(rxchain); 5074198429Srpaulo DPRINTF(sc, IWN_DEBUG_RESET, "%s: setting configuration\n", __func__); 5075220728Sbschmidt error = iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, sc->rxonsz, 0); 5076198429Srpaulo if (error != 0) { 5077220726Sbschmidt device_printf(sc->sc_dev, "%s: RXON command failed\n", 5078220726Sbschmidt __func__); 5079198429Srpaulo return error; 5080198429Srpaulo } 5081198429Srpaulo 5082220726Sbschmidt if ((error = iwn_add_broadcast_node(sc, 0)) != 0) { 5083220726Sbschmidt device_printf(sc->sc_dev, "%s: could not add broadcast node\n", 5084220726Sbschmidt __func__); 5085201209Srpaulo return error; 5086201209Srpaulo } 5087201209Srpaulo 5088198429Srpaulo /* Configuration has changed, set TX power accordingly. */ 5089220728Sbschmidt if ((error = ops->set_txpower(sc, ic->ic_curchan, 0)) != 0) { 5090220726Sbschmidt device_printf(sc->sc_dev, "%s: could not set TX power\n", 5091220726Sbschmidt __func__); 5092198429Srpaulo return error; 5093198429Srpaulo } 5094198429Srpaulo 5095220726Sbschmidt if ((error = iwn_set_critical_temp(sc)) != 0) { 5096198429Srpaulo device_printf(sc->sc_dev, 5097220724Sbschmidt "%s: could not set critical temperature\n", __func__); 5098198429Srpaulo return error; 5099198429Srpaulo } 5100198429Srpaulo 5101201209Srpaulo /* Set power saving level to CAM during initialization. */ 5102220726Sbschmidt if ((error = iwn_set_pslevel(sc, 0, 0, 0)) != 0) { 5103198429Srpaulo device_printf(sc->sc_dev, 5104201209Srpaulo "%s: could not set power saving level\n", __func__); 5105198429Srpaulo return error; 5106198429Srpaulo } 5107198429Srpaulo return 0; 5108198429Srpaulo} 5109198429Srpaulo 5110220634Sbschmidt/* 5111220634Sbschmidt * Add an ssid element to a frame. 5112220634Sbschmidt */ 5113220634Sbschmidtstatic uint8_t * 5114220634Sbschmidtieee80211_add_ssid(uint8_t *frm, const uint8_t *ssid, u_int len) 5115220634Sbschmidt{ 5116220634Sbschmidt *frm++ = IEEE80211_ELEMID_SSID; 5117220634Sbschmidt *frm++ = len; 5118220634Sbschmidt memcpy(frm, ssid, len); 5119220634Sbschmidt return frm + len; 5120220634Sbschmidt} 5121220634Sbschmidt 5122206477Sbschmidtstatic int 5123198429Srpauloiwn_scan(struct iwn_softc *sc) 5124198429Srpaulo{ 5125198429Srpaulo struct ifnet *ifp = sc->sc_ifp; 5126198429Srpaulo struct ieee80211com *ic = ifp->if_l2com; 5127198429Srpaulo struct ieee80211_scan_state *ss = ic->ic_scan; /*XXX*/ 5128221641Sbschmidt struct ieee80211_node *ni = ss->ss_vap->iv_bss; 5129198429Srpaulo struct iwn_scan_hdr *hdr; 5130198429Srpaulo struct iwn_cmd_data *tx; 5131198429Srpaulo struct iwn_scan_essid *essid; 5132198429Srpaulo struct iwn_scan_chan *chan; 5133198429Srpaulo struct ieee80211_frame *wh; 5134198429Srpaulo struct ieee80211_rateset *rs; 5135198429Srpaulo struct ieee80211_channel *c; 5136220726Sbschmidt uint8_t *buf, *frm; 5137220723Sbschmidt uint16_t rxchain; 5138220726Sbschmidt uint8_t txant; 5139220634Sbschmidt int buflen, error; 5140198429Srpaulo 5141198429Srpaulo buf = malloc(IWN_SCAN_MAXSZ, M_DEVBUF, M_NOWAIT | M_ZERO); 5142198429Srpaulo if (buf == NULL) { 5143198429Srpaulo device_printf(sc->sc_dev, 5144198429Srpaulo "%s: could not allocate buffer for scan command\n", 5145198429Srpaulo __func__); 5146198429Srpaulo return ENOMEM; 5147198429Srpaulo } 5148198429Srpaulo hdr = (struct iwn_scan_hdr *)buf; 5149198429Srpaulo /* 5150198429Srpaulo * Move to the next channel if no frames are received within 10ms 5151198429Srpaulo * after sending the probe request. 5152198429Srpaulo */ 5153198429Srpaulo hdr->quiet_time = htole16(10); /* timeout in milliseconds */ 5154198429Srpaulo hdr->quiet_threshold = htole16(1); /* min # of packets */ 5155198429Srpaulo 5156198429Srpaulo /* Select antennas for scanning. */ 5157201209Srpaulo rxchain = 5158201209Srpaulo IWN_RXCHAIN_VALID(sc->rxchainmask) | 5159201209Srpaulo IWN_RXCHAIN_FORCE_MIMO_SEL(sc->rxchainmask) | 5160201209Srpaulo IWN_RXCHAIN_DRIVER_FORCE; 5161198429Srpaulo if (IEEE80211_IS_CHAN_A(ic->ic_curchan) && 5162198429Srpaulo sc->hw_type == IWN_HW_REV_TYPE_4965) { 5163198429Srpaulo /* Ant A must be avoided in 5GHz because of an HW bug. */ 5164201209Srpaulo rxchain |= IWN_RXCHAIN_FORCE_SEL(IWN_ANT_BC); 5165198429Srpaulo } else /* Use all available RX antennas. */ 5166201209Srpaulo rxchain |= IWN_RXCHAIN_FORCE_SEL(sc->rxchainmask); 5167198429Srpaulo hdr->rxchain = htole16(rxchain); 5168198429Srpaulo hdr->filter = htole32(IWN_FILTER_MULTICAST | IWN_FILTER_BEACON); 5169198429Srpaulo 5170198429Srpaulo tx = (struct iwn_cmd_data *)(hdr + 1); 5171198429Srpaulo tx->flags = htole32(IWN_TX_AUTO_SEQ); 5172220728Sbschmidt tx->id = sc->broadcast_id; 5173198429Srpaulo tx->lifetime = htole32(IWN_LIFETIME_INFINITE); 5174198429Srpaulo 5175198429Srpaulo if (IEEE80211_IS_CHAN_A(ic->ic_curchan)) { 5176198429Srpaulo /* Send probe requests at 6Mbps. */ 5177221648Sbschmidt tx->rate = htole32(0xd); 5178201209Srpaulo rs = &ic->ic_sup_rates[IEEE80211_MODE_11A]; 5179198429Srpaulo } else { 5180198429Srpaulo hdr->flags = htole32(IWN_RXON_24GHZ | IWN_RXON_AUTO); 5181198429Srpaulo /* Send probe requests at 1Mbps. */ 5182221648Sbschmidt tx->rate = htole32(10 | IWN_RFLAG_CCK); 5183201209Srpaulo rs = &ic->ic_sup_rates[IEEE80211_MODE_11G]; 5184198429Srpaulo } 5185198429Srpaulo /* Use the first valid TX antenna. */ 5186201209Srpaulo txant = IWN_LSB(sc->txchainmask); 5187221648Sbschmidt tx->rate |= htole32(IWN_RFLAG_ANT(txant)); 5188198429Srpaulo 5189198429Srpaulo essid = (struct iwn_scan_essid *)(tx + 1); 5190198429Srpaulo if (ss->ss_ssid[0].len != 0) { 5191198429Srpaulo essid[0].id = IEEE80211_ELEMID_SSID; 5192198429Srpaulo essid[0].len = ss->ss_ssid[0].len; 5193198429Srpaulo memcpy(essid[0].data, ss->ss_ssid[0].ssid, ss->ss_ssid[0].len); 5194198429Srpaulo } 5195198429Srpaulo /* 5196198429Srpaulo * Build a probe request frame. Most of the following code is a 5197198429Srpaulo * copy & paste of what is done in net80211. 5198198429Srpaulo */ 5199198429Srpaulo wh = (struct ieee80211_frame *)(essid + 20); 5200198429Srpaulo wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | 5201198429Srpaulo IEEE80211_FC0_SUBTYPE_PROBE_REQ; 5202198429Srpaulo wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; 5203198429Srpaulo IEEE80211_ADDR_COPY(wh->i_addr1, ifp->if_broadcastaddr); 5204198429Srpaulo IEEE80211_ADDR_COPY(wh->i_addr2, IF_LLADDR(ifp)); 5205198429Srpaulo IEEE80211_ADDR_COPY(wh->i_addr3, ifp->if_broadcastaddr); 5206198429Srpaulo *(uint16_t *)&wh->i_dur[0] = 0; /* filled by HW */ 5207198429Srpaulo *(uint16_t *)&wh->i_seq[0] = 0; /* filled by HW */ 5208198429Srpaulo 5209198429Srpaulo frm = (uint8_t *)(wh + 1); 5210220634Sbschmidt frm = ieee80211_add_ssid(frm, NULL, 0); 5211220634Sbschmidt frm = ieee80211_add_rates(frm, rs); 5212220634Sbschmidt if (rs->rs_nrates > IEEE80211_RATE_SIZE) 5213220634Sbschmidt frm = ieee80211_add_xrates(frm, rs); 5214221641Sbschmidt if (ic->ic_htcaps & IEEE80211_HTC_HT) 5215221641Sbschmidt frm = ieee80211_add_htcap(frm, ni); 5216198429Srpaulo 5217198429Srpaulo /* Set length of probe request. */ 5218198429Srpaulo tx->len = htole16(frm - (uint8_t *)wh); 5219198429Srpaulo 5220198429Srpaulo c = ic->ic_curchan; 5221198429Srpaulo chan = (struct iwn_scan_chan *)frm; 5222201209Srpaulo chan->chan = htole16(ieee80211_chan2ieee(ic, c)); 5223198429Srpaulo chan->flags = 0; 5224198429Srpaulo if (ss->ss_nssid > 0) 5225198429Srpaulo chan->flags |= htole32(IWN_CHAN_NPBREQS(1)); 5226198429Srpaulo chan->dsp_gain = 0x6e; 5227201209Srpaulo if (IEEE80211_IS_CHAN_5GHZ(c) && 5228201209Srpaulo !(c->ic_flags & IEEE80211_CHAN_PASSIVE)) { 5229198429Srpaulo chan->rf_gain = 0x3b; 5230198429Srpaulo chan->active = htole16(24); 5231198429Srpaulo chan->passive = htole16(110); 5232201209Srpaulo chan->flags |= htole32(IWN_CHAN_ACTIVE); 5233201209Srpaulo } else if (IEEE80211_IS_CHAN_5GHZ(c)) { 5234201209Srpaulo chan->rf_gain = 0x3b; 5235201209Srpaulo chan->active = htole16(24); 5236201209Srpaulo if (sc->rxon.associd) 5237201209Srpaulo chan->passive = htole16(78); 5238201209Srpaulo else 5239201209Srpaulo chan->passive = htole16(110); 5240207709Sbschmidt hdr->crc_threshold = 0xffff; 5241201209Srpaulo } else if (!(c->ic_flags & IEEE80211_CHAN_PASSIVE)) { 5242201209Srpaulo chan->rf_gain = 0x28; 5243201209Srpaulo chan->active = htole16(36); 5244201209Srpaulo chan->passive = htole16(120); 5245201209Srpaulo chan->flags |= htole32(IWN_CHAN_ACTIVE); 5246198429Srpaulo } else { 5247198429Srpaulo chan->rf_gain = 0x28; 5248198429Srpaulo chan->active = htole16(36); 5249201209Srpaulo if (sc->rxon.associd) 5250201209Srpaulo chan->passive = htole16(88); 5251201209Srpaulo else 5252201209Srpaulo chan->passive = htole16(120); 5253207709Sbschmidt hdr->crc_threshold = 0xffff; 5254198429Srpaulo } 5255198429Srpaulo 5256201209Srpaulo DPRINTF(sc, IWN_DEBUG_STATE, 5257201209Srpaulo "%s: chan %u flags 0x%x rf_gain 0x%x " 5258198429Srpaulo "dsp_gain 0x%x active 0x%x passive 0x%x\n", __func__, 5259198429Srpaulo chan->chan, chan->flags, chan->rf_gain, chan->dsp_gain, 5260198429Srpaulo chan->active, chan->passive); 5261198429Srpaulo 5262201209Srpaulo hdr->nchan++; 5263201209Srpaulo chan++; 5264198429Srpaulo buflen = (uint8_t *)chan - buf; 5265198429Srpaulo hdr->len = htole16(buflen); 5266198429Srpaulo 5267198429Srpaulo DPRINTF(sc, IWN_DEBUG_STATE, "sending scan command nchan=%d\n", 5268198429Srpaulo hdr->nchan); 5269198429Srpaulo error = iwn_cmd(sc, IWN_CMD_SCAN, buf, buflen, 1); 5270198429Srpaulo free(buf, M_DEVBUF); 5271198429Srpaulo return error; 5272198429Srpaulo} 5273198429Srpaulo 5274206477Sbschmidtstatic int 5275191746Sthompsaiwn_auth(struct iwn_softc *sc, struct ieee80211vap *vap) 5276178676Ssam{ 5277220728Sbschmidt struct iwn_ops *ops = &sc->ops; 5278178676Ssam struct ifnet *ifp = sc->sc_ifp; 5279178676Ssam struct ieee80211com *ic = ifp->if_l2com; 5280178676Ssam struct ieee80211_node *ni = vap->iv_bss; 5281178676Ssam int error; 5282178676Ssam 5283201209Srpaulo /* Update adapter configuration. */ 5284198429Srpaulo IEEE80211_ADDR_COPY(sc->rxon.bssid, ni->ni_bssid); 5285220636Sbschmidt sc->rxon.chan = ieee80211_chan2ieee(ic, ni->ni_chan); 5286198429Srpaulo sc->rxon.flags = htole32(IWN_RXON_TSF | IWN_RXON_CTS_TO_SELF); 5287178676Ssam if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) 5288198429Srpaulo sc->rxon.flags |= htole32(IWN_RXON_AUTO | IWN_RXON_24GHZ); 5289198429Srpaulo if (ic->ic_flags & IEEE80211_F_SHSLOT) 5290198429Srpaulo sc->rxon.flags |= htole32(IWN_RXON_SHSLOT); 5291198429Srpaulo if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) 5292198429Srpaulo sc->rxon.flags |= htole32(IWN_RXON_SHPREAMBLE); 5293178676Ssam if (IEEE80211_IS_CHAN_A(ni->ni_chan)) { 5294198429Srpaulo sc->rxon.cck_mask = 0; 5295198429Srpaulo sc->rxon.ofdm_mask = 0x15; 5296178676Ssam } else if (IEEE80211_IS_CHAN_B(ni->ni_chan)) { 5297198429Srpaulo sc->rxon.cck_mask = 0x03; 5298198429Srpaulo sc->rxon.ofdm_mask = 0; 5299178676Ssam } else { 5300220725Sbschmidt /* Assume 802.11b/g. */ 5301198429Srpaulo sc->rxon.cck_mask = 0x0f; 5302198429Srpaulo sc->rxon.ofdm_mask = 0x15; 5303178676Ssam } 5304220724Sbschmidt DPRINTF(sc, IWN_DEBUG_STATE, "rxon chan %d flags %x cck %x ofdm %x\n", 5305220724Sbschmidt sc->rxon.chan, sc->rxon.flags, sc->rxon.cck_mask, 5306220724Sbschmidt sc->rxon.ofdm_mask); 5307220728Sbschmidt error = iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, sc->rxonsz, 1); 5308178676Ssam if (error != 0) { 5309220726Sbschmidt device_printf(sc->sc_dev, "%s: RXON command failed, error %d\n", 5310220726Sbschmidt __func__, error); 5311178676Ssam return error; 5312178676Ssam } 5313178676Ssam 5314198429Srpaulo /* Configuration has changed, set TX power accordingly. */ 5315220728Sbschmidt if ((error = ops->set_txpower(sc, ni->ni_chan, 1)) != 0) { 5316178676Ssam device_printf(sc->sc_dev, 5317220724Sbschmidt "%s: could not set TX power, error %d\n", __func__, error); 5318178676Ssam return error; 5319178676Ssam } 5320178676Ssam /* 5321201209Srpaulo * Reconfiguring RXON clears the firmware nodes table so we must 5322178676Ssam * add the broadcast node again. 5323178676Ssam */ 5324220726Sbschmidt if ((error = iwn_add_broadcast_node(sc, 1)) != 0) { 5325178676Ssam device_printf(sc->sc_dev, 5326220726Sbschmidt "%s: could not add broadcast node, error %d\n", __func__, 5327220726Sbschmidt error); 5328178676Ssam return error; 5329178676Ssam } 5330178676Ssam return 0; 5331178676Ssam} 5332178676Ssam 5333206477Sbschmidtstatic int 5334191746Sthompsaiwn_run(struct iwn_softc *sc, struct ieee80211vap *vap) 5335178676Ssam{ 5336220728Sbschmidt struct iwn_ops *ops = &sc->ops; 5337178676Ssam struct ifnet *ifp = sc->sc_ifp; 5338178676Ssam struct ieee80211com *ic = ifp->if_l2com; 5339178676Ssam struct ieee80211_node *ni = vap->iv_bss; 5340178676Ssam struct iwn_node_info node; 5341221653Sbschmidt uint32_t htflags = 0; 5342201209Srpaulo int error; 5343178676Ssam 5344178676Ssam if (ic->ic_opmode == IEEE80211_M_MONITOR) { 5345201209Srpaulo /* Link LED blinks while monitoring. */ 5346220674Sbschmidt iwn_set_led(sc, IWN_LED_LINK, 5, 5); 5347178676Ssam return 0; 5348178676Ssam } 5349220726Sbschmidt if ((error = iwn_set_timing(sc, ni)) != 0) { 5350198429Srpaulo device_printf(sc->sc_dev, 5351198429Srpaulo "%s: could not set timing, error %d\n", __func__, error); 5352198429Srpaulo return error; 5353198429Srpaulo } 5354178676Ssam 5355201209Srpaulo /* Update adapter configuration. */ 5356201209Srpaulo IEEE80211_ADDR_COPY(sc->rxon.bssid, ni->ni_bssid); 5357198429Srpaulo sc->rxon.associd = htole16(IEEE80211_AID(ni->ni_associd)); 5358220636Sbschmidt sc->rxon.chan = ieee80211_chan2ieee(ic, ni->ni_chan); 5359220636Sbschmidt sc->rxon.flags = htole32(IWN_RXON_TSF | IWN_RXON_CTS_TO_SELF); 5360201209Srpaulo if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) 5361201209Srpaulo sc->rxon.flags |= htole32(IWN_RXON_AUTO | IWN_RXON_24GHZ); 5362178676Ssam if (ic->ic_flags & IEEE80211_F_SHSLOT) 5363198429Srpaulo sc->rxon.flags |= htole32(IWN_RXON_SHSLOT); 5364178676Ssam if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) 5365198429Srpaulo sc->rxon.flags |= htole32(IWN_RXON_SHPREAMBLE); 5366201209Srpaulo if (IEEE80211_IS_CHAN_A(ni->ni_chan)) { 5367201209Srpaulo sc->rxon.cck_mask = 0; 5368201209Srpaulo sc->rxon.ofdm_mask = 0x15; 5369201209Srpaulo } else if (IEEE80211_IS_CHAN_B(ni->ni_chan)) { 5370201209Srpaulo sc->rxon.cck_mask = 0x03; 5371201209Srpaulo sc->rxon.ofdm_mask = 0; 5372201209Srpaulo } else { 5373220725Sbschmidt /* Assume 802.11b/g. */ 5374201209Srpaulo sc->rxon.cck_mask = 0x0f; 5375201209Srpaulo sc->rxon.ofdm_mask = 0x15; 5376201209Srpaulo } 5377178676Ssam if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) { 5378221653Sbschmidt htflags |= IWN_RXON_HT_PROTMODE(ic->ic_curhtprotmode); 5379221653Sbschmidt if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) { 5380221653Sbschmidt switch (ic->ic_curhtprotmode) { 5381221653Sbschmidt case IEEE80211_HTINFO_OPMODE_HT20PR: 5382221653Sbschmidt htflags |= IWN_RXON_HT_MODEPURE40; 5383221653Sbschmidt break; 5384221653Sbschmidt default: 5385221653Sbschmidt htflags |= IWN_RXON_HT_MODEMIXED; 5386221653Sbschmidt break; 5387221653Sbschmidt } 5388221653Sbschmidt } 5389221653Sbschmidt if (IEEE80211_IS_CHAN_HT40D(ni->ni_chan)) 5390221653Sbschmidt htflags |= IWN_RXON_HT_HT40MINUS; 5391221653Sbschmidt } 5392221653Sbschmidt sc->rxon.flags |= htole32(htflags); 5393198429Srpaulo sc->rxon.filter |= htole32(IWN_FILTER_BSS); 5394220724Sbschmidt DPRINTF(sc, IWN_DEBUG_STATE, "rxon chan %d flags %x\n", 5395220724Sbschmidt sc->rxon.chan, sc->rxon.flags); 5396220728Sbschmidt error = iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, sc->rxonsz, 1); 5397178676Ssam if (error != 0) { 5398178676Ssam device_printf(sc->sc_dev, 5399220726Sbschmidt "%s: could not update configuration, error %d\n", __func__, 5400220726Sbschmidt error); 5401178676Ssam return error; 5402178676Ssam } 5403178676Ssam 5404198429Srpaulo /* Configuration has changed, set TX power accordingly. */ 5405220728Sbschmidt if ((error = ops->set_txpower(sc, ni->ni_chan, 1)) != 0) { 5406178676Ssam device_printf(sc->sc_dev, 5407220724Sbschmidt "%s: could not set TX power, error %d\n", __func__, error); 5408178676Ssam return error; 5409178676Ssam } 5410178676Ssam 5411220715Sbschmidt /* Fake a join to initialize the TX rate. */ 5412220715Sbschmidt ((struct iwn_node *)ni)->id = IWN_ID_BSS; 5413220715Sbschmidt iwn_newassoc(ni, 1); 5414220715Sbschmidt 5415198429Srpaulo /* Add BSS node. */ 5416178676Ssam memset(&node, 0, sizeof node); 5417178676Ssam IEEE80211_ADDR_COPY(node.macaddr, ni->ni_macaddr); 5418178676Ssam node.id = IWN_ID_BSS; 5419221653Sbschmidt if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) { 5420221653Sbschmidt switch (ni->ni_htcap & IEEE80211_HTCAP_SMPS) { 5421221653Sbschmidt case IEEE80211_HTCAP_SMPS_ENA: 5422221653Sbschmidt node.htflags |= htole32(IWN_SMPS_MIMO_DIS); 5423221653Sbschmidt break; 5424221653Sbschmidt case IEEE80211_HTCAP_SMPS_DYNAMIC: 5425221653Sbschmidt node.htflags |= htole32(IWN_SMPS_MIMO_PROT); 5426221653Sbschmidt break; 5427221653Sbschmidt } 5428221653Sbschmidt node.htflags |= htole32(IWN_AMDPU_SIZE_FACTOR(3) | 5429221653Sbschmidt IWN_AMDPU_DENSITY(5)); /* 4us */ 5430221653Sbschmidt if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) 5431221653Sbschmidt node.htflags |= htole32(IWN_NODE_HT40); 5432221653Sbschmidt } 5433220724Sbschmidt DPRINTF(sc, IWN_DEBUG_STATE, "%s: adding BSS node\n", __func__); 5434220728Sbschmidt error = ops->add_node(sc, &node, 1); 5435178676Ssam if (error != 0) { 5436220724Sbschmidt device_printf(sc->sc_dev, 5437220724Sbschmidt "%s: could not add BSS node, error %d\n", __func__, error); 5438178676Ssam return error; 5439178676Ssam } 5440220724Sbschmidt DPRINTF(sc, IWN_DEBUG_STATE, "%s: setting link quality for node %d\n", 5441220724Sbschmidt __func__, node.id); 5442220726Sbschmidt if ((error = iwn_set_link_quality(sc, ni)) != 0) { 5443178676Ssam device_printf(sc->sc_dev, 5444220724Sbschmidt "%s: could not setup link quality for node %d, error %d\n", 5445178676Ssam __func__, node.id, error); 5446178676Ssam return error; 5447178676Ssam } 5448178676Ssam 5449220726Sbschmidt if ((error = iwn_init_sensitivity(sc)) != 0) { 5450178676Ssam device_printf(sc->sc_dev, 5451220726Sbschmidt "%s: could not set sensitivity, error %d\n", __func__, 5452220726Sbschmidt error); 5453178676Ssam return error; 5454178676Ssam } 5455198429Srpaulo /* Start periodic calibration timer. */ 5456178676Ssam sc->calib.state = IWN_CALIB_STATE_ASSOC; 5457220667Sbschmidt sc->calib_cnt = 0; 5458220667Sbschmidt callout_reset(&sc->calib_to, msecs_to_ticks(500), iwn_calib_timeout, 5459220667Sbschmidt sc); 5460178676Ssam 5461198429Srpaulo /* Link LED always on while associated. */ 5462178676Ssam iwn_set_led(sc, IWN_LED_LINK, 0, 1); 5463178676Ssam return 0; 5464178676Ssam} 5465178676Ssam 5466178676Ssam/* 5467201209Srpaulo * This function is called by upper layer when an ADDBA request is received 5468201209Srpaulo * from another STA and before the ADDBA response is sent. 5469201209Srpaulo */ 5470206477Sbschmidtstatic int 5471221650Sbschmidtiwn_ampdu_rx_start(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap, 5472221650Sbschmidt int baparamset, int batimeout, int baseqctl) 5473201209Srpaulo{ 5474221650Sbschmidt#define MS(_v, _f) (((_v) & _f) >> _f##_S) 5475221650Sbschmidt struct iwn_softc *sc = ni->ni_ic->ic_ifp->if_softc; 5476220728Sbschmidt struct iwn_ops *ops = &sc->ops; 5477201209Srpaulo struct iwn_node *wn = (void *)ni; 5478201209Srpaulo struct iwn_node_info node; 5479221650Sbschmidt uint16_t ssn; 5480221650Sbschmidt uint8_t tid; 5481221650Sbschmidt int error; 5482201209Srpaulo 5483221650Sbschmidt tid = MS(le16toh(baparamset), IEEE80211_BAPS_TID); 5484221650Sbschmidt ssn = MS(le16toh(baseqctl), IEEE80211_BASEQ_START); 5485221650Sbschmidt 5486201209Srpaulo memset(&node, 0, sizeof node); 5487201209Srpaulo node.id = wn->id; 5488201209Srpaulo node.control = IWN_NODE_UPDATE; 5489201209Srpaulo node.flags = IWN_FLAG_SET_ADDBA; 5490201209Srpaulo node.addba_tid = tid; 5491221650Sbschmidt node.addba_ssn = htole16(ssn); 5492201209Srpaulo DPRINTF(sc, IWN_DEBUG_RECV, "ADDBA RA=%d TID=%d SSN=%d\n", 5493221650Sbschmidt wn->id, tid, ssn); 5494221650Sbschmidt error = ops->add_node(sc, &node, 1); 5495221650Sbschmidt if (error != 0) 5496221650Sbschmidt return error; 5497221650Sbschmidt return sc->sc_ampdu_rx_start(ni, rap, baparamset, batimeout, baseqctl); 5498221650Sbschmidt#undef MS 5499201209Srpaulo} 5500201209Srpaulo 5501201209Srpaulo/* 5502201209Srpaulo * This function is called by upper layer on teardown of an HT-immediate 5503220725Sbschmidt * Block Ack agreement (eg. uppon receipt of a DELBA frame). 5504201209Srpaulo */ 5505206477Sbschmidtstatic void 5506221650Sbschmidtiwn_ampdu_rx_stop(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap) 5507201209Srpaulo{ 5508221650Sbschmidt struct ieee80211com *ic = ni->ni_ic; 5509221650Sbschmidt struct iwn_softc *sc = ic->ic_ifp->if_softc; 5510220728Sbschmidt struct iwn_ops *ops = &sc->ops; 5511201209Srpaulo struct iwn_node *wn = (void *)ni; 5512201209Srpaulo struct iwn_node_info node; 5513221650Sbschmidt uint8_t tid; 5514201209Srpaulo 5515221650Sbschmidt /* XXX: tid as an argument */ 5516221650Sbschmidt for (tid = 0; tid < WME_NUM_TID; tid++) { 5517221650Sbschmidt if (&ni->ni_rx_ampdu[tid] == rap) 5518221650Sbschmidt break; 5519221650Sbschmidt } 5520221650Sbschmidt 5521201209Srpaulo memset(&node, 0, sizeof node); 5522201209Srpaulo node.id = wn->id; 5523201209Srpaulo node.control = IWN_NODE_UPDATE; 5524201209Srpaulo node.flags = IWN_FLAG_SET_DELBA; 5525201209Srpaulo node.delba_tid = tid; 5526201209Srpaulo DPRINTF(sc, IWN_DEBUG_RECV, "DELBA RA=%d TID=%d\n", wn->id, tid); 5527220728Sbschmidt (void)ops->add_node(sc, &node, 1); 5528221650Sbschmidt sc->sc_ampdu_rx_stop(ni, rap); 5529201209Srpaulo} 5530201209Srpaulo 5531221651Sbschmidtstatic int 5532221651Sbschmidtiwn_addba_request(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap, 5533221651Sbschmidt int dialogtoken, int baparamset, int batimeout) 5534221651Sbschmidt{ 5535221651Sbschmidt struct iwn_softc *sc = ni->ni_ic->ic_ifp->if_softc; 5536221651Sbschmidt int qid; 5537221651Sbschmidt 5538221651Sbschmidt for (qid = sc->firstaggqueue; qid < sc->ntxqs; qid++) { 5539221651Sbschmidt if (sc->qid2tap[qid] == NULL) 5540221651Sbschmidt break; 5541221651Sbschmidt } 5542221651Sbschmidt if (qid == sc->ntxqs) { 5543221651Sbschmidt DPRINTF(sc, IWN_DEBUG_XMIT, "%s: not free aggregation queue\n", 5544221651Sbschmidt __func__); 5545221651Sbschmidt return 0; 5546221651Sbschmidt } 5547221651Sbschmidt tap->txa_private = malloc(sizeof(int), M_DEVBUF, M_NOWAIT); 5548221651Sbschmidt if (tap->txa_private == NULL) { 5549221651Sbschmidt device_printf(sc->sc_dev, 5550221651Sbschmidt "%s: failed to alloc TX aggregation structure\n", __func__); 5551221651Sbschmidt return 0; 5552221651Sbschmidt } 5553221651Sbschmidt sc->qid2tap[qid] = tap; 5554221651Sbschmidt *(int *)tap->txa_private = qid; 5555221651Sbschmidt return sc->sc_addba_request(ni, tap, dialogtoken, baparamset, 5556221651Sbschmidt batimeout); 5557221651Sbschmidt} 5558221651Sbschmidt 5559221651Sbschmidtstatic int 5560221651Sbschmidtiwn_addba_response(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap, 5561221651Sbschmidt int code, int baparamset, int batimeout) 5562221651Sbschmidt{ 5563221651Sbschmidt struct iwn_softc *sc = ni->ni_ic->ic_ifp->if_softc; 5564221651Sbschmidt int qid = *(int *)tap->txa_private; 5565221651Sbschmidt uint8_t tid = WME_AC_TO_TID(tap->txa_ac); 5566221651Sbschmidt int ret; 5567221651Sbschmidt 5568221651Sbschmidt if (code == IEEE80211_STATUS_SUCCESS) { 5569221651Sbschmidt ni->ni_txseqs[tid] = tap->txa_start & 0xfff; 5570221651Sbschmidt ret = iwn_ampdu_tx_start(ni->ni_ic, ni, tid); 5571221651Sbschmidt if (ret != 1) 5572221651Sbschmidt return ret; 5573221651Sbschmidt } else { 5574221651Sbschmidt sc->qid2tap[qid] = NULL; 5575221651Sbschmidt free(tap->txa_private, M_DEVBUF); 5576221651Sbschmidt tap->txa_private = NULL; 5577221651Sbschmidt } 5578221651Sbschmidt return sc->sc_addba_response(ni, tap, code, baparamset, batimeout); 5579221651Sbschmidt} 5580221651Sbschmidt 5581201209Srpaulo/* 5582201209Srpaulo * This function is called by upper layer when an ADDBA response is received 5583201209Srpaulo * from another STA. 5584201209Srpaulo */ 5585206477Sbschmidtstatic int 5586201209Srpauloiwn_ampdu_tx_start(struct ieee80211com *ic, struct ieee80211_node *ni, 5587201209Srpaulo uint8_t tid) 5588201209Srpaulo{ 5589221651Sbschmidt struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[TID_TO_WME_AC(tid)]; 5590221651Sbschmidt struct iwn_softc *sc = ni->ni_ic->ic_ifp->if_softc; 5591220728Sbschmidt struct iwn_ops *ops = &sc->ops; 5592201209Srpaulo struct iwn_node *wn = (void *)ni; 5593201209Srpaulo struct iwn_node_info node; 5594221651Sbschmidt int error, qid; 5595201209Srpaulo 5596201209Srpaulo /* Enable TX for the specified RA/TID. */ 5597201209Srpaulo wn->disable_tid &= ~(1 << tid); 5598201209Srpaulo memset(&node, 0, sizeof node); 5599201209Srpaulo node.id = wn->id; 5600201209Srpaulo node.control = IWN_NODE_UPDATE; 5601201209Srpaulo node.flags = IWN_FLAG_SET_DISABLE_TID; 5602201209Srpaulo node.disable_tid = htole16(wn->disable_tid); 5603220728Sbschmidt error = ops->add_node(sc, &node, 1); 5604201209Srpaulo if (error != 0) 5605221651Sbschmidt return 0; 5606201209Srpaulo 5607201209Srpaulo if ((error = iwn_nic_lock(sc)) != 0) 5608221651Sbschmidt return 0; 5609221651Sbschmidt qid = *(int *)tap->txa_private; 5610221651Sbschmidt ops->ampdu_tx_start(sc, ni, qid, tid, tap->txa_start & 0xfff); 5611201209Srpaulo iwn_nic_unlock(sc); 5612221651Sbschmidt 5613221651Sbschmidt iwn_set_link_quality(sc, ni); 5614221651Sbschmidt return 1; 5615201209Srpaulo} 5616201209Srpaulo 5617206477Sbschmidtstatic void 5618221651Sbschmidtiwn_ampdu_tx_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap) 5619201209Srpaulo{ 5620221651Sbschmidt struct iwn_softc *sc = ni->ni_ic->ic_ifp->if_softc; 5621220728Sbschmidt struct iwn_ops *ops = &sc->ops; 5622221651Sbschmidt uint8_t tid = WME_AC_TO_TID(tap->txa_ac); 5623221651Sbschmidt int qid; 5624201209Srpaulo 5625221651Sbschmidt if (tap->txa_private == NULL) 5626221651Sbschmidt return; 5627221651Sbschmidt 5628221651Sbschmidt qid = *(int *)tap->txa_private; 5629220726Sbschmidt if (iwn_nic_lock(sc) != 0) 5630201209Srpaulo return; 5631221651Sbschmidt ops->ampdu_tx_stop(sc, qid, tid, tap->txa_start & 0xfff); 5632201209Srpaulo iwn_nic_unlock(sc); 5633221651Sbschmidt sc->qid2tap[qid] = NULL; 5634221651Sbschmidt free(tap->txa_private, M_DEVBUF); 5635221651Sbschmidt tap->txa_private = NULL; 5636201209Srpaulo} 5637201209Srpaulo 5638206477Sbschmidtstatic void 5639201209Srpauloiwn4965_ampdu_tx_start(struct iwn_softc *sc, struct ieee80211_node *ni, 5640221651Sbschmidt int qid, uint8_t tid, uint16_t ssn) 5641201209Srpaulo{ 5642201209Srpaulo struct iwn_node *wn = (void *)ni; 5643201209Srpaulo 5644201209Srpaulo /* Stop TX scheduler while we're changing its configuration. */ 5645201209Srpaulo iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid), 5646201209Srpaulo IWN4965_TXQ_STATUS_CHGACT); 5647201209Srpaulo 5648201209Srpaulo /* Assign RA/TID translation to the queue. */ 5649201209Srpaulo iwn_mem_write_2(sc, sc->sched_base + IWN4965_SCHED_TRANS_TBL(qid), 5650201209Srpaulo wn->id << 4 | tid); 5651201209Srpaulo 5652201209Srpaulo /* Enable chain-building mode for the queue. */ 5653201209Srpaulo iwn_prph_setbits(sc, IWN4965_SCHED_QCHAIN_SEL, 1 << qid); 5654201209Srpaulo 5655201209Srpaulo /* Set starting sequence number from the ADDBA request. */ 5656221651Sbschmidt sc->txq[qid].cur = sc->txq[qid].read = (ssn & 0xff); 5657201209Srpaulo IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff)); 5658201209Srpaulo iwn_prph_write(sc, IWN4965_SCHED_QUEUE_RDPTR(qid), ssn); 5659201209Srpaulo 5660201209Srpaulo /* Set scheduler window size. */ 5661201209Srpaulo iwn_mem_write(sc, sc->sched_base + IWN4965_SCHED_QUEUE_OFFSET(qid), 5662201209Srpaulo IWN_SCHED_WINSZ); 5663201209Srpaulo /* Set scheduler frame limit. */ 5664201209Srpaulo iwn_mem_write(sc, sc->sched_base + IWN4965_SCHED_QUEUE_OFFSET(qid) + 4, 5665201209Srpaulo IWN_SCHED_LIMIT << 16); 5666201209Srpaulo 5667201209Srpaulo /* Enable interrupts for the queue. */ 5668201209Srpaulo iwn_prph_setbits(sc, IWN4965_SCHED_INTR_MASK, 1 << qid); 5669201209Srpaulo 5670201209Srpaulo /* Mark the queue as active. */ 5671201209Srpaulo iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid), 5672201209Srpaulo IWN4965_TXQ_STATUS_ACTIVE | IWN4965_TXQ_STATUS_AGGR_ENA | 5673201209Srpaulo iwn_tid2fifo[tid] << 1); 5674201209Srpaulo} 5675201209Srpaulo 5676206477Sbschmidtstatic void 5677221651Sbschmidtiwn4965_ampdu_tx_stop(struct iwn_softc *sc, int qid, uint8_t tid, uint16_t ssn) 5678201209Srpaulo{ 5679201209Srpaulo /* Stop TX scheduler while we're changing its configuration. */ 5680201209Srpaulo iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid), 5681201209Srpaulo IWN4965_TXQ_STATUS_CHGACT); 5682201209Srpaulo 5683201209Srpaulo /* Set starting sequence number from the ADDBA request. */ 5684201209Srpaulo IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff)); 5685201209Srpaulo iwn_prph_write(sc, IWN4965_SCHED_QUEUE_RDPTR(qid), ssn); 5686201209Srpaulo 5687201209Srpaulo /* Disable interrupts for the queue. */ 5688201209Srpaulo iwn_prph_clrbits(sc, IWN4965_SCHED_INTR_MASK, 1 << qid); 5689201209Srpaulo 5690201209Srpaulo /* Mark the queue as inactive. */ 5691201209Srpaulo iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid), 5692201209Srpaulo IWN4965_TXQ_STATUS_INACTIVE | iwn_tid2fifo[tid] << 1); 5693201209Srpaulo} 5694201209Srpaulo 5695206477Sbschmidtstatic void 5696201209Srpauloiwn5000_ampdu_tx_start(struct iwn_softc *sc, struct ieee80211_node *ni, 5697221651Sbschmidt int qid, uint8_t tid, uint16_t ssn) 5698201209Srpaulo{ 5699201209Srpaulo struct iwn_node *wn = (void *)ni; 5700201209Srpaulo 5701201209Srpaulo /* Stop TX scheduler while we're changing its configuration. */ 5702201209Srpaulo iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid), 5703201209Srpaulo IWN5000_TXQ_STATUS_CHGACT); 5704201209Srpaulo 5705201209Srpaulo /* Assign RA/TID translation to the queue. */ 5706201209Srpaulo iwn_mem_write_2(sc, sc->sched_base + IWN5000_SCHED_TRANS_TBL(qid), 5707201209Srpaulo wn->id << 4 | tid); 5708201209Srpaulo 5709201209Srpaulo /* Enable chain-building mode for the queue. */ 5710201209Srpaulo iwn_prph_setbits(sc, IWN5000_SCHED_QCHAIN_SEL, 1 << qid); 5711201209Srpaulo 5712201209Srpaulo /* Enable aggregation for the queue. */ 5713201209Srpaulo iwn_prph_setbits(sc, IWN5000_SCHED_AGGR_SEL, 1 << qid); 5714201209Srpaulo 5715201209Srpaulo /* Set starting sequence number from the ADDBA request. */ 5716221651Sbschmidt sc->txq[qid].cur = sc->txq[qid].read = (ssn & 0xff); 5717201209Srpaulo IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff)); 5718201209Srpaulo iwn_prph_write(sc, IWN5000_SCHED_QUEUE_RDPTR(qid), ssn); 5719201209Srpaulo 5720201209Srpaulo /* Set scheduler window size and frame limit. */ 5721201209Srpaulo iwn_mem_write(sc, sc->sched_base + IWN5000_SCHED_QUEUE_OFFSET(qid) + 4, 5722201209Srpaulo IWN_SCHED_LIMIT << 16 | IWN_SCHED_WINSZ); 5723201209Srpaulo 5724201209Srpaulo /* Enable interrupts for the queue. */ 5725201209Srpaulo iwn_prph_setbits(sc, IWN5000_SCHED_INTR_MASK, 1 << qid); 5726201209Srpaulo 5727201209Srpaulo /* Mark the queue as active. */ 5728201209Srpaulo iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid), 5729201209Srpaulo IWN5000_TXQ_STATUS_ACTIVE | iwn_tid2fifo[tid]); 5730201209Srpaulo} 5731201209Srpaulo 5732206477Sbschmidtstatic void 5733221651Sbschmidtiwn5000_ampdu_tx_stop(struct iwn_softc *sc, int qid, uint8_t tid, uint16_t ssn) 5734201209Srpaulo{ 5735201209Srpaulo /* Stop TX scheduler while we're changing its configuration. */ 5736201209Srpaulo iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid), 5737201209Srpaulo IWN5000_TXQ_STATUS_CHGACT); 5738201209Srpaulo 5739201209Srpaulo /* Disable aggregation for the queue. */ 5740201209Srpaulo iwn_prph_clrbits(sc, IWN5000_SCHED_AGGR_SEL, 1 << qid); 5741201209Srpaulo 5742201209Srpaulo /* Set starting sequence number from the ADDBA request. */ 5743201209Srpaulo IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff)); 5744201209Srpaulo iwn_prph_write(sc, IWN5000_SCHED_QUEUE_RDPTR(qid), ssn); 5745201209Srpaulo 5746201209Srpaulo /* Disable interrupts for the queue. */ 5747201209Srpaulo iwn_prph_clrbits(sc, IWN5000_SCHED_INTR_MASK, 1 << qid); 5748201209Srpaulo 5749201209Srpaulo /* Mark the queue as inactive. */ 5750201209Srpaulo iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid), 5751201209Srpaulo IWN5000_TXQ_STATUS_INACTIVE | iwn_tid2fifo[tid]); 5752201209Srpaulo} 5753201209Srpaulo 5754201209Srpaulo/* 5755220674Sbschmidt * Query calibration tables from the initialization firmware. We do this 5756220674Sbschmidt * only once at first boot. Called from a process context. 5757212853Sbschmidt */ 5758212853Sbschmidtstatic int 5759220674Sbschmidtiwn5000_query_calibration(struct iwn_softc *sc) 5760212853Sbschmidt{ 5761198429Srpaulo struct iwn5000_calib_config cmd; 5762198429Srpaulo int error; 5763178676Ssam 5764198429Srpaulo memset(&cmd, 0, sizeof cmd); 5765220674Sbschmidt cmd.ucode.once.enable = 0xffffffff; 5766220674Sbschmidt cmd.ucode.once.start = 0xffffffff; 5767220674Sbschmidt cmd.ucode.once.send = 0xffffffff; 5768220674Sbschmidt cmd.ucode.flags = 0xffffffff; 5769220674Sbschmidt DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: sending calibration query\n", 5770220674Sbschmidt __func__); 5771198429Srpaulo error = iwn_cmd(sc, IWN5000_CMD_CALIB_CONFIG, &cmd, sizeof cmd, 0); 5772198429Srpaulo if (error != 0) 5773198429Srpaulo return error; 5774178676Ssam 5775198429Srpaulo /* Wait at most two seconds for calibration to complete. */ 5776201209Srpaulo if (!(sc->sc_flags & IWN_FLAG_CALIB_DONE)) 5777220674Sbschmidt error = msleep(sc, &sc->sc_mtx, PCATCH, "iwncal", 2 * hz); 5778201209Srpaulo return error; 5779198429Srpaulo} 5780198429Srpaulo 5781198429Srpaulo/* 5782220674Sbschmidt * Send calibration results to the runtime firmware. These results were 5783220674Sbschmidt * obtained on first boot from the initialization firmware. 5784198429Srpaulo */ 5785212854Sbschmidtstatic int 5786220674Sbschmidtiwn5000_send_calibration(struct iwn_softc *sc) 5787198429Srpaulo{ 5788220674Sbschmidt int idx, error; 5789198429Srpaulo 5790220674Sbschmidt for (idx = 0; idx < 5; idx++) { 5791220674Sbschmidt if (sc->calibcmd[idx].buf == NULL) 5792220674Sbschmidt continue; /* No results available. */ 5793198429Srpaulo DPRINTF(sc, IWN_DEBUG_CALIBRATE, 5794220674Sbschmidt "send calibration result idx=%d len=%d\n", idx, 5795220674Sbschmidt sc->calibcmd[idx].len); 5796220674Sbschmidt error = iwn_cmd(sc, IWN_CMD_PHY_CALIB, sc->calibcmd[idx].buf, 5797220674Sbschmidt sc->calibcmd[idx].len, 0); 5798220674Sbschmidt if (error != 0) { 5799220674Sbschmidt device_printf(sc->sc_dev, 5800220674Sbschmidt "%s: could not send calibration result, error %d\n", 5801220674Sbschmidt __func__, error); 5802220674Sbschmidt return error; 5803220674Sbschmidt } 5804178676Ssam } 5805220674Sbschmidt return 0; 5806198429Srpaulo} 5807178676Ssam 5808206477Sbschmidtstatic int 5809201209Srpauloiwn5000_send_wimax_coex(struct iwn_softc *sc) 5810201209Srpaulo{ 5811201209Srpaulo struct iwn5000_wimax_coex wimax; 5812201209Srpaulo 5813201209Srpaulo#ifdef notyet 5814201209Srpaulo if (sc->hw_type == IWN_HW_REV_TYPE_6050) { 5815201209Srpaulo /* Enable WiMAX coexistence for combo adapters. */ 5816201209Srpaulo wimax.flags = 5817201209Srpaulo IWN_WIMAX_COEX_ASSOC_WA_UNMASK | 5818201209Srpaulo IWN_WIMAX_COEX_UNASSOC_WA_UNMASK | 5819201209Srpaulo IWN_WIMAX_COEX_STA_TABLE_VALID | 5820201209Srpaulo IWN_WIMAX_COEX_ENABLE; 5821201209Srpaulo memcpy(wimax.events, iwn6050_wimax_events, 5822201209Srpaulo sizeof iwn6050_wimax_events); 5823201209Srpaulo } else 5824201209Srpaulo#endif 5825201209Srpaulo { 5826201209Srpaulo /* Disable WiMAX coexistence. */ 5827201209Srpaulo wimax.flags = 0; 5828201209Srpaulo memset(wimax.events, 0, sizeof wimax.events); 5829201209Srpaulo } 5830201209Srpaulo DPRINTF(sc, IWN_DEBUG_RESET, "%s: Configuring WiMAX coexistence\n", 5831201209Srpaulo __func__); 5832201209Srpaulo return iwn_cmd(sc, IWN5000_CMD_WIMAX_COEX, &wimax, sizeof wimax, 0); 5833201209Srpaulo} 5834201209Srpaulo 5835220674Sbschmidtstatic int 5836220674Sbschmidtiwn5000_crystal_calib(struct iwn_softc *sc) 5837220674Sbschmidt{ 5838220674Sbschmidt struct iwn5000_phy_calib_crystal cmd; 5839220674Sbschmidt 5840220674Sbschmidt memset(&cmd, 0, sizeof cmd); 5841220674Sbschmidt cmd.code = IWN5000_PHY_CALIB_CRYSTAL; 5842220674Sbschmidt cmd.ngroups = 1; 5843220674Sbschmidt cmd.isvalid = 1; 5844220674Sbschmidt cmd.cap_pin[0] = le32toh(sc->eeprom_crystal) & 0xff; 5845220674Sbschmidt cmd.cap_pin[1] = (le32toh(sc->eeprom_crystal) >> 16) & 0xff; 5846220674Sbschmidt DPRINTF(sc, IWN_DEBUG_CALIBRATE, "sending crystal calibration %d, %d\n", 5847220674Sbschmidt cmd.cap_pin[0], cmd.cap_pin[1]); 5848220674Sbschmidt return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 0); 5849220674Sbschmidt} 5850220674Sbschmidt 5851220676Sbschmidtstatic int 5852220676Sbschmidtiwn5000_temp_offset_calib(struct iwn_softc *sc) 5853220676Sbschmidt{ 5854220676Sbschmidt struct iwn5000_phy_calib_temp_offset cmd; 5855220676Sbschmidt 5856220676Sbschmidt memset(&cmd, 0, sizeof cmd); 5857220676Sbschmidt cmd.code = IWN5000_PHY_CALIB_TEMP_OFFSET; 5858220676Sbschmidt cmd.ngroups = 1; 5859220676Sbschmidt cmd.isvalid = 1; 5860220676Sbschmidt if (sc->eeprom_temp != 0) 5861220676Sbschmidt cmd.offset = htole16(sc->eeprom_temp); 5862220676Sbschmidt else 5863220676Sbschmidt cmd.offset = htole16(IWN_DEFAULT_TEMP_OFFSET); 5864220676Sbschmidt DPRINTF(sc, IWN_DEBUG_CALIBRATE, "setting radio sensor offset to %d\n", 5865220676Sbschmidt le16toh(cmd.offset)); 5866220676Sbschmidt return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 0); 5867220676Sbschmidt} 5868220676Sbschmidt 5869198429Srpaulo/* 5870198429Srpaulo * This function is called after the runtime firmware notifies us of its 5871220725Sbschmidt * readiness (called in a process context). 5872198429Srpaulo */ 5873206477Sbschmidtstatic int 5874198429Srpauloiwn4965_post_alive(struct iwn_softc *sc) 5875198429Srpaulo{ 5876198429Srpaulo int error, qid; 5877178676Ssam 5878198429Srpaulo if ((error = iwn_nic_lock(sc)) != 0) 5879198429Srpaulo return error; 5880178676Ssam 5881201209Srpaulo /* Clear TX scheduler state in SRAM. */ 5882198429Srpaulo sc->sched_base = iwn_prph_read(sc, IWN_SCHED_SRAM_ADDR); 5883198429Srpaulo iwn_mem_set_region_4(sc, sc->sched_base + IWN4965_SCHED_CTX_OFF, 0, 5884201209Srpaulo IWN4965_SCHED_CTX_LEN / sizeof (uint32_t)); 5885178676Ssam 5886220725Sbschmidt /* Set physical address of TX scheduler rings (1KB aligned). */ 5887198429Srpaulo iwn_prph_write(sc, IWN4965_SCHED_DRAM_ADDR, sc->sched_dma.paddr >> 10); 5888178676Ssam 5889198429Srpaulo IWN_SETBITS(sc, IWN_FH_TX_CHICKEN, IWN_FH_TX_CHICKEN_SCHED_RETRY); 5890178676Ssam 5891198429Srpaulo /* Disable chain mode for all our 16 queues. */ 5892198429Srpaulo iwn_prph_write(sc, IWN4965_SCHED_QCHAIN_SEL, 0); 5893198429Srpaulo 5894198429Srpaulo for (qid = 0; qid < IWN4965_NTXQUEUES; qid++) { 5895198429Srpaulo iwn_prph_write(sc, IWN4965_SCHED_QUEUE_RDPTR(qid), 0); 5896198429Srpaulo IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | 0); 5897198429Srpaulo 5898198429Srpaulo /* Set scheduler window size. */ 5899198429Srpaulo iwn_mem_write(sc, sc->sched_base + 5900198429Srpaulo IWN4965_SCHED_QUEUE_OFFSET(qid), IWN_SCHED_WINSZ); 5901198429Srpaulo /* Set scheduler frame limit. */ 5902198429Srpaulo iwn_mem_write(sc, sc->sched_base + 5903198429Srpaulo IWN4965_SCHED_QUEUE_OFFSET(qid) + 4, 5904198429Srpaulo IWN_SCHED_LIMIT << 16); 5905178676Ssam } 5906178676Ssam 5907198429Srpaulo /* Enable interrupts for all our 16 queues. */ 5908198429Srpaulo iwn_prph_write(sc, IWN4965_SCHED_INTR_MASK, 0xffff); 5909198429Srpaulo /* Identify TX FIFO rings (0-7). */ 5910198429Srpaulo iwn_prph_write(sc, IWN4965_SCHED_TXFACT, 0xff); 5911178676Ssam 5912198429Srpaulo /* Mark TX rings (4 EDCA + cmd + 2 HCCA) as active. */ 5913198429Srpaulo for (qid = 0; qid < 7; qid++) { 5914198429Srpaulo static uint8_t qid2fifo[] = { 3, 2, 1, 0, 4, 5, 6 }; 5915198429Srpaulo iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid), 5916198429Srpaulo IWN4965_TXQ_STATUS_ACTIVE | qid2fifo[qid] << 1); 5917198429Srpaulo } 5918198429Srpaulo iwn_nic_unlock(sc); 5919198429Srpaulo return 0; 5920198429Srpaulo} 5921178676Ssam 5922198429Srpaulo/* 5923198429Srpaulo * This function is called after the initialization or runtime firmware 5924220725Sbschmidt * notifies us of its readiness (called in a process context). 5925198429Srpaulo */ 5926206477Sbschmidtstatic int 5927198429Srpauloiwn5000_post_alive(struct iwn_softc *sc) 5928198429Srpaulo{ 5929198429Srpaulo int error, qid; 5930178676Ssam 5931201209Srpaulo /* Switch to using ICT interrupt mode. */ 5932201209Srpaulo iwn5000_ict_reset(sc); 5933201209Srpaulo 5934220726Sbschmidt if ((error = iwn_nic_lock(sc)) != 0) 5935198429Srpaulo return error; 5936178676Ssam 5937201209Srpaulo /* Clear TX scheduler state in SRAM. */ 5938198429Srpaulo sc->sched_base = iwn_prph_read(sc, IWN_SCHED_SRAM_ADDR); 5939198429Srpaulo iwn_mem_set_region_4(sc, sc->sched_base + IWN5000_SCHED_CTX_OFF, 0, 5940201209Srpaulo IWN5000_SCHED_CTX_LEN / sizeof (uint32_t)); 5941178676Ssam 5942220725Sbschmidt /* Set physical address of TX scheduler rings (1KB aligned). */ 5943198429Srpaulo iwn_prph_write(sc, IWN5000_SCHED_DRAM_ADDR, sc->sched_dma.paddr >> 10); 5944178676Ssam 5945198429Srpaulo IWN_SETBITS(sc, IWN_FH_TX_CHICKEN, IWN_FH_TX_CHICKEN_SCHED_RETRY); 5946178676Ssam 5947201209Srpaulo /* Enable chain mode for all queues, except command queue. */ 5948201209Srpaulo iwn_prph_write(sc, IWN5000_SCHED_QCHAIN_SEL, 0xfffef); 5949198429Srpaulo iwn_prph_write(sc, IWN5000_SCHED_AGGR_SEL, 0); 5950178676Ssam 5951198429Srpaulo for (qid = 0; qid < IWN5000_NTXQUEUES; qid++) { 5952198429Srpaulo iwn_prph_write(sc, IWN5000_SCHED_QUEUE_RDPTR(qid), 0); 5953198429Srpaulo IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | 0); 5954198429Srpaulo 5955198429Srpaulo iwn_mem_write(sc, sc->sched_base + 5956198429Srpaulo IWN5000_SCHED_QUEUE_OFFSET(qid), 0); 5957198429Srpaulo /* Set scheduler window size and frame limit. */ 5958198429Srpaulo iwn_mem_write(sc, sc->sched_base + 5959198429Srpaulo IWN5000_SCHED_QUEUE_OFFSET(qid) + 4, 5960198429Srpaulo IWN_SCHED_LIMIT << 16 | IWN_SCHED_WINSZ); 5961178676Ssam } 5962178676Ssam 5963198429Srpaulo /* Enable interrupts for all our 20 queues. */ 5964198429Srpaulo iwn_prph_write(sc, IWN5000_SCHED_INTR_MASK, 0xfffff); 5965198429Srpaulo /* Identify TX FIFO rings (0-7). */ 5966198429Srpaulo iwn_prph_write(sc, IWN5000_SCHED_TXFACT, 0xff); 5967178676Ssam 5968198429Srpaulo /* Mark TX rings (4 EDCA + cmd + 2 HCCA) as active. */ 5969198429Srpaulo for (qid = 0; qid < 7; qid++) { 5970198429Srpaulo static uint8_t qid2fifo[] = { 3, 2, 1, 0, 7, 5, 6 }; 5971198429Srpaulo iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid), 5972198429Srpaulo IWN5000_TXQ_STATUS_ACTIVE | qid2fifo[qid]); 5973198429Srpaulo } 5974198429Srpaulo iwn_nic_unlock(sc); 5975178676Ssam 5976201209Srpaulo /* Configure WiMAX coexistence for combo adapters. */ 5977201209Srpaulo error = iwn5000_send_wimax_coex(sc); 5978178676Ssam if (error != 0) { 5979178676Ssam device_printf(sc->sc_dev, 5980198429Srpaulo "%s: could not configure WiMAX coexistence, error %d\n", 5981178676Ssam __func__, error); 5982178676Ssam return error; 5983178676Ssam } 5984220674Sbschmidt if (sc->hw_type != IWN_HW_REV_TYPE_5150) { 5985220674Sbschmidt /* Perform crystal calibration. */ 5986220674Sbschmidt error = iwn5000_crystal_calib(sc); 5987198429Srpaulo if (error != 0) { 5988198429Srpaulo device_printf(sc->sc_dev, 5989220674Sbschmidt "%s: crystal calibration failed, error %d\n", 5990220674Sbschmidt __func__, error); 5991198429Srpaulo return error; 5992198429Srpaulo } 5993220674Sbschmidt } 5994220674Sbschmidt if (!(sc->sc_flags & IWN_FLAG_CALIB_DONE)) { 5995220674Sbschmidt /* Query calibration from the initialization firmware. */ 5996220674Sbschmidt if ((error = iwn5000_query_calibration(sc)) != 0) { 5997198429Srpaulo device_printf(sc->sc_dev, 5998220674Sbschmidt "%s: could not query calibration, error %d\n", 5999198429Srpaulo __func__, error); 6000198429Srpaulo return error; 6001198429Srpaulo } 6002198429Srpaulo /* 6003201209Srpaulo * We have the calibration results now, reboot with the 6004201209Srpaulo * runtime firmware (call ourselves recursively!) 6005198429Srpaulo */ 6006198429Srpaulo iwn_hw_stop(sc); 6007198429Srpaulo error = iwn_hw_init(sc); 6008198429Srpaulo } else { 6009220674Sbschmidt /* Send calibration results to runtime firmware. */ 6010220674Sbschmidt error = iwn5000_send_calibration(sc); 6011198429Srpaulo } 6012198429Srpaulo return error; 6013198429Srpaulo} 6014178676Ssam 6015198429Srpaulo/* 6016198429Srpaulo * The firmware boot code is small and is intended to be copied directly into 6017220725Sbschmidt * the NIC internal memory (no DMA transfer). 6018198429Srpaulo */ 6019206477Sbschmidtstatic int 6020198429Srpauloiwn4965_load_bootcode(struct iwn_softc *sc, const uint8_t *ucode, int size) 6021198429Srpaulo{ 6022198429Srpaulo int error, ntries; 6023198429Srpaulo 6024198429Srpaulo size /= sizeof (uint32_t); 6025198429Srpaulo 6026220726Sbschmidt if ((error = iwn_nic_lock(sc)) != 0) 6027198429Srpaulo return error; 6028198429Srpaulo 6029198429Srpaulo /* Copy microcode image into NIC memory. */ 6030198429Srpaulo iwn_prph_write_region_4(sc, IWN_BSM_SRAM_BASE, 6031198429Srpaulo (const uint32_t *)ucode, size); 6032198429Srpaulo 6033198429Srpaulo iwn_prph_write(sc, IWN_BSM_WR_MEM_SRC, 0); 6034198429Srpaulo iwn_prph_write(sc, IWN_BSM_WR_MEM_DST, IWN_FW_TEXT_BASE); 6035198429Srpaulo iwn_prph_write(sc, IWN_BSM_WR_DWCOUNT, size); 6036198429Srpaulo 6037198429Srpaulo /* Start boot load now. */ 6038198429Srpaulo iwn_prph_write(sc, IWN_BSM_WR_CTRL, IWN_BSM_WR_CTRL_START); 6039198429Srpaulo 6040198429Srpaulo /* Wait for transfer to complete. */ 6041198429Srpaulo for (ntries = 0; ntries < 1000; ntries++) { 6042198429Srpaulo if (!(iwn_prph_read(sc, IWN_BSM_WR_CTRL) & 6043198429Srpaulo IWN_BSM_WR_CTRL_START)) 6044198429Srpaulo break; 6045198429Srpaulo DELAY(10); 6046198429Srpaulo } 6047198429Srpaulo if (ntries == 1000) { 6048198429Srpaulo device_printf(sc->sc_dev, "%s: could not load boot firmware\n", 6049198429Srpaulo __func__); 6050198429Srpaulo iwn_nic_unlock(sc); 6051198429Srpaulo return ETIMEDOUT; 6052198429Srpaulo } 6053198429Srpaulo 6054198429Srpaulo /* Enable boot after power up. */ 6055198429Srpaulo iwn_prph_write(sc, IWN_BSM_WR_CTRL, IWN_BSM_WR_CTRL_START_EN); 6056198429Srpaulo 6057198429Srpaulo iwn_nic_unlock(sc); 6058198429Srpaulo return 0; 6059178676Ssam} 6060178676Ssam 6061206477Sbschmidtstatic int 6062198429Srpauloiwn4965_load_firmware(struct iwn_softc *sc) 6063178676Ssam{ 6064198429Srpaulo struct iwn_fw_info *fw = &sc->fw; 6065198429Srpaulo struct iwn_dma_info *dma = &sc->fw_dma; 6066178676Ssam int error; 6067178676Ssam 6068198429Srpaulo /* Copy initialization sections into pre-allocated DMA-safe memory. */ 6069198429Srpaulo memcpy(dma->vaddr, fw->init.data, fw->init.datasz); 6070220661Sbschmidt bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); 6071198429Srpaulo memcpy(dma->vaddr + IWN4965_FW_DATA_MAXSZ, 6072198429Srpaulo fw->init.text, fw->init.textsz); 6073220661Sbschmidt bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); 6074198429Srpaulo 6075198429Srpaulo /* Tell adapter where to find initialization sections. */ 6076220726Sbschmidt if ((error = iwn_nic_lock(sc)) != 0) 6077198429Srpaulo return error; 6078198429Srpaulo iwn_prph_write(sc, IWN_BSM_DRAM_DATA_ADDR, dma->paddr >> 4); 6079198429Srpaulo iwn_prph_write(sc, IWN_BSM_DRAM_DATA_SIZE, fw->init.datasz); 6080198429Srpaulo iwn_prph_write(sc, IWN_BSM_DRAM_TEXT_ADDR, 6081198429Srpaulo (dma->paddr + IWN4965_FW_DATA_MAXSZ) >> 4); 6082198429Srpaulo iwn_prph_write(sc, IWN_BSM_DRAM_TEXT_SIZE, fw->init.textsz); 6083198429Srpaulo iwn_nic_unlock(sc); 6084198429Srpaulo 6085198429Srpaulo /* Load firmware boot code. */ 6086198429Srpaulo error = iwn4965_load_bootcode(sc, fw->boot.text, fw->boot.textsz); 6087178676Ssam if (error != 0) { 6088198429Srpaulo device_printf(sc->sc_dev, "%s: could not load boot firmware\n", 6089198429Srpaulo __func__); 6090178676Ssam return error; 6091178676Ssam } 6092198429Srpaulo /* Now press "execute". */ 6093198429Srpaulo IWN_WRITE(sc, IWN_RESET, 0); 6094178676Ssam 6095198429Srpaulo /* Wait at most one second for first alive notification. */ 6096220726Sbschmidt if ((error = msleep(sc, &sc->sc_mtx, PCATCH, "iwninit", hz)) != 0) { 6097178676Ssam device_printf(sc->sc_dev, 6098198429Srpaulo "%s: timeout waiting for adapter to initialize, error %d\n", 6099178676Ssam __func__, error); 6100178676Ssam return error; 6101178676Ssam } 6102178676Ssam 6103198429Srpaulo /* Retrieve current temperature for initial TX power calibration. */ 6104198429Srpaulo sc->rawtemp = sc->ucode_info.temp[3].chan20MHz; 6105198429Srpaulo sc->temp = iwn4965_get_temperature(sc); 6106178676Ssam 6107198429Srpaulo /* Copy runtime sections into pre-allocated DMA-safe memory. */ 6108198429Srpaulo memcpy(dma->vaddr, fw->main.data, fw->main.datasz); 6109220661Sbschmidt bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); 6110198429Srpaulo memcpy(dma->vaddr + IWN4965_FW_DATA_MAXSZ, 6111198429Srpaulo fw->main.text, fw->main.textsz); 6112220661Sbschmidt bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); 6113198429Srpaulo 6114198429Srpaulo /* Tell adapter where to find runtime sections. */ 6115220726Sbschmidt if ((error = iwn_nic_lock(sc)) != 0) 6116198429Srpaulo return error; 6117198429Srpaulo iwn_prph_write(sc, IWN_BSM_DRAM_DATA_ADDR, dma->paddr >> 4); 6118198429Srpaulo iwn_prph_write(sc, IWN_BSM_DRAM_DATA_SIZE, fw->main.datasz); 6119198429Srpaulo iwn_prph_write(sc, IWN_BSM_DRAM_TEXT_ADDR, 6120198429Srpaulo (dma->paddr + IWN4965_FW_DATA_MAXSZ) >> 4); 6121198429Srpaulo iwn_prph_write(sc, IWN_BSM_DRAM_TEXT_SIZE, 6122198429Srpaulo IWN_FW_UPDATED | fw->main.textsz); 6123198429Srpaulo iwn_nic_unlock(sc); 6124198429Srpaulo 6125198429Srpaulo return 0; 6126198429Srpaulo} 6127198429Srpaulo 6128206477Sbschmidtstatic int 6129198429Srpauloiwn5000_load_firmware_section(struct iwn_softc *sc, uint32_t dst, 6130198429Srpaulo const uint8_t *section, int size) 6131198429Srpaulo{ 6132198429Srpaulo struct iwn_dma_info *dma = &sc->fw_dma; 6133198429Srpaulo int error; 6134198429Srpaulo 6135198429Srpaulo /* Copy firmware section into pre-allocated DMA-safe memory. */ 6136198429Srpaulo memcpy(dma->vaddr, section, size); 6137220661Sbschmidt bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); 6138198429Srpaulo 6139220726Sbschmidt if ((error = iwn_nic_lock(sc)) != 0) 6140198429Srpaulo return error; 6141198429Srpaulo 6142198429Srpaulo IWN_WRITE(sc, IWN_FH_TX_CONFIG(IWN_SRVC_DMACHNL), 6143198429Srpaulo IWN_FH_TX_CONFIG_DMA_PAUSE); 6144198429Srpaulo 6145198429Srpaulo IWN_WRITE(sc, IWN_FH_SRAM_ADDR(IWN_SRVC_DMACHNL), dst); 6146198429Srpaulo IWN_WRITE(sc, IWN_FH_TFBD_CTRL0(IWN_SRVC_DMACHNL), 6147198429Srpaulo IWN_LOADDR(dma->paddr)); 6148198429Srpaulo IWN_WRITE(sc, IWN_FH_TFBD_CTRL1(IWN_SRVC_DMACHNL), 6149198429Srpaulo IWN_HIADDR(dma->paddr) << 28 | size); 6150198429Srpaulo IWN_WRITE(sc, IWN_FH_TXBUF_STATUS(IWN_SRVC_DMACHNL), 6151198429Srpaulo IWN_FH_TXBUF_STATUS_TBNUM(1) | 6152198429Srpaulo IWN_FH_TXBUF_STATUS_TBIDX(1) | 6153198429Srpaulo IWN_FH_TXBUF_STATUS_TFBD_VALID); 6154198429Srpaulo 6155198429Srpaulo /* Kick Flow Handler to start DMA transfer. */ 6156198429Srpaulo IWN_WRITE(sc, IWN_FH_TX_CONFIG(IWN_SRVC_DMACHNL), 6157198429Srpaulo IWN_FH_TX_CONFIG_DMA_ENA | IWN_FH_TX_CONFIG_CIRQ_HOST_ENDTFD); 6158198429Srpaulo 6159198429Srpaulo iwn_nic_unlock(sc); 6160198429Srpaulo 6161198429Srpaulo /* Wait at most five seconds for FH DMA transfer to complete. */ 6162220661Sbschmidt return msleep(sc, &sc->sc_mtx, PCATCH, "iwninit", 5 * hz); 6163198429Srpaulo} 6164198429Srpaulo 6165206477Sbschmidtstatic int 6166198429Srpauloiwn5000_load_firmware(struct iwn_softc *sc) 6167198429Srpaulo{ 6168198429Srpaulo struct iwn_fw_part *fw; 6169198429Srpaulo int error; 6170198429Srpaulo 6171198429Srpaulo /* Load the initialization firmware on first boot only. */ 6172201209Srpaulo fw = (sc->sc_flags & IWN_FLAG_CALIB_DONE) ? 6173201209Srpaulo &sc->fw.main : &sc->fw.init; 6174198429Srpaulo 6175198429Srpaulo error = iwn5000_load_firmware_section(sc, IWN_FW_TEXT_BASE, 6176198429Srpaulo fw->text, fw->textsz); 6177178676Ssam if (error != 0) { 6178178676Ssam device_printf(sc->sc_dev, 6179198429Srpaulo "%s: could not load firmware %s section, error %d\n", 6180198429Srpaulo __func__, ".text", error); 6181178676Ssam return error; 6182178676Ssam } 6183198429Srpaulo error = iwn5000_load_firmware_section(sc, IWN_FW_DATA_BASE, 6184198429Srpaulo fw->data, fw->datasz); 6185178676Ssam if (error != 0) { 6186178676Ssam device_printf(sc->sc_dev, 6187198429Srpaulo "%s: could not load firmware %s section, error %d\n", 6188198429Srpaulo __func__, ".data", error); 6189178676Ssam return error; 6190178676Ssam } 6191178676Ssam 6192198429Srpaulo /* Now press "execute". */ 6193198429Srpaulo IWN_WRITE(sc, IWN_RESET, 0); 6194198429Srpaulo return 0; 6195198429Srpaulo} 6196198429Srpaulo 6197210111Sbschmidt/* 6198210111Sbschmidt * Extract text and data sections from a legacy firmware image. 6199210111Sbschmidt */ 6200206477Sbschmidtstatic int 6201210111Sbschmidtiwn_read_firmware_leg(struct iwn_softc *sc, struct iwn_fw_info *fw) 6202198429Srpaulo{ 6203201209Srpaulo const uint32_t *ptr; 6204210111Sbschmidt size_t hdrlen = 24; 6205201209Srpaulo uint32_t rev; 6206198429Srpaulo 6207220661Sbschmidt ptr = (const uint32_t *)fw->data; 6208201209Srpaulo rev = le32toh(*ptr++); 6209210111Sbschmidt 6210201209Srpaulo /* Check firmware API version. */ 6211201209Srpaulo if (IWN_FW_API(rev) <= 1) { 6212201209Srpaulo device_printf(sc->sc_dev, 6213201209Srpaulo "%s: bad firmware, need API version >=2\n", __func__); 6214201209Srpaulo return EINVAL; 6215201209Srpaulo } 6216201209Srpaulo if (IWN_FW_API(rev) >= 3) { 6217201209Srpaulo /* Skip build number (version 2 header). */ 6218210111Sbschmidt hdrlen += 4; 6219201209Srpaulo ptr++; 6220201209Srpaulo } 6221210111Sbschmidt if (fw->size < hdrlen) { 6222220724Sbschmidt device_printf(sc->sc_dev, "%s: firmware too short: %zu bytes\n", 6223210111Sbschmidt __func__, fw->size); 6224210111Sbschmidt return EINVAL; 6225210111Sbschmidt } 6226201209Srpaulo fw->main.textsz = le32toh(*ptr++); 6227201209Srpaulo fw->main.datasz = le32toh(*ptr++); 6228201209Srpaulo fw->init.textsz = le32toh(*ptr++); 6229201209Srpaulo fw->init.datasz = le32toh(*ptr++); 6230201209Srpaulo fw->boot.textsz = le32toh(*ptr++); 6231198429Srpaulo 6232198429Srpaulo /* Check that all firmware sections fit. */ 6233210111Sbschmidt if (fw->size < hdrlen + fw->main.textsz + fw->main.datasz + 6234210111Sbschmidt fw->init.textsz + fw->init.datasz + fw->boot.textsz) { 6235220724Sbschmidt device_printf(sc->sc_dev, "%s: firmware too short: %zu bytes\n", 6236210111Sbschmidt __func__, fw->size); 6237198429Srpaulo return EINVAL; 6238178676Ssam } 6239198429Srpaulo 6240198429Srpaulo /* Get pointers to firmware sections. */ 6241201209Srpaulo fw->main.text = (const uint8_t *)ptr; 6242198429Srpaulo fw->main.data = fw->main.text + fw->main.textsz; 6243198429Srpaulo fw->init.text = fw->main.data + fw->main.datasz; 6244198429Srpaulo fw->init.data = fw->init.text + fw->init.textsz; 6245198429Srpaulo fw->boot.text = fw->init.data + fw->init.datasz; 6246178676Ssam return 0; 6247178676Ssam} 6248178676Ssam 6249210111Sbschmidt/* 6250210111Sbschmidt * Extract text and data sections from a TLV firmware image. 6251210111Sbschmidt */ 6252220661Sbschmidtstatic int 6253210111Sbschmidtiwn_read_firmware_tlv(struct iwn_softc *sc, struct iwn_fw_info *fw, 6254210111Sbschmidt uint16_t alt) 6255210111Sbschmidt{ 6256210111Sbschmidt const struct iwn_fw_tlv_hdr *hdr; 6257210111Sbschmidt const struct iwn_fw_tlv *tlv; 6258210111Sbschmidt const uint8_t *ptr, *end; 6259210111Sbschmidt uint64_t altmask; 6260220866Sbschmidt uint32_t len, tmp; 6261210111Sbschmidt 6262210111Sbschmidt if (fw->size < sizeof (*hdr)) { 6263220724Sbschmidt device_printf(sc->sc_dev, "%s: firmware too short: %zu bytes\n", 6264210111Sbschmidt __func__, fw->size); 6265210111Sbschmidt return EINVAL; 6266210111Sbschmidt } 6267210111Sbschmidt hdr = (const struct iwn_fw_tlv_hdr *)fw->data; 6268210111Sbschmidt if (hdr->signature != htole32(IWN_FW_SIGNATURE)) { 6269220724Sbschmidt device_printf(sc->sc_dev, "%s: bad firmware signature 0x%08x\n", 6270210111Sbschmidt __func__, le32toh(hdr->signature)); 6271210111Sbschmidt return EINVAL; 6272210111Sbschmidt } 6273220724Sbschmidt DPRINTF(sc, IWN_DEBUG_RESET, "FW: \"%.64s\", build 0x%x\n", hdr->descr, 6274220724Sbschmidt le32toh(hdr->build)); 6275210111Sbschmidt 6276210111Sbschmidt /* 6277210111Sbschmidt * Select the closest supported alternative that is less than 6278210111Sbschmidt * or equal to the specified one. 6279210111Sbschmidt */ 6280210111Sbschmidt altmask = le64toh(hdr->altmask); 6281210111Sbschmidt while (alt > 0 && !(altmask & (1ULL << alt))) 6282210111Sbschmidt alt--; /* Downgrade. */ 6283220724Sbschmidt DPRINTF(sc, IWN_DEBUG_RESET, "using alternative %d\n", alt); 6284210111Sbschmidt 6285210111Sbschmidt ptr = (const uint8_t *)(hdr + 1); 6286210111Sbschmidt end = (const uint8_t *)(fw->data + fw->size); 6287210111Sbschmidt 6288210111Sbschmidt /* Parse type-length-value fields. */ 6289210111Sbschmidt while (ptr + sizeof (*tlv) <= end) { 6290210111Sbschmidt tlv = (const struct iwn_fw_tlv *)ptr; 6291210111Sbschmidt len = le32toh(tlv->len); 6292210111Sbschmidt 6293210111Sbschmidt ptr += sizeof (*tlv); 6294210111Sbschmidt if (ptr + len > end) { 6295210111Sbschmidt device_printf(sc->sc_dev, 6296220724Sbschmidt "%s: firmware too short: %zu bytes\n", __func__, 6297220724Sbschmidt fw->size); 6298210111Sbschmidt return EINVAL; 6299210111Sbschmidt } 6300210111Sbschmidt /* Skip other alternatives. */ 6301210111Sbschmidt if (tlv->alt != 0 && tlv->alt != htole16(alt)) 6302210111Sbschmidt goto next; 6303210111Sbschmidt 6304210111Sbschmidt switch (le16toh(tlv->type)) { 6305210111Sbschmidt case IWN_FW_TLV_MAIN_TEXT: 6306210111Sbschmidt fw->main.text = ptr; 6307210111Sbschmidt fw->main.textsz = len; 6308210111Sbschmidt break; 6309210111Sbschmidt case IWN_FW_TLV_MAIN_DATA: 6310210111Sbschmidt fw->main.data = ptr; 6311210111Sbschmidt fw->main.datasz = len; 6312210111Sbschmidt break; 6313210111Sbschmidt case IWN_FW_TLV_INIT_TEXT: 6314210111Sbschmidt fw->init.text = ptr; 6315210111Sbschmidt fw->init.textsz = len; 6316210111Sbschmidt break; 6317210111Sbschmidt case IWN_FW_TLV_INIT_DATA: 6318210111Sbschmidt fw->init.data = ptr; 6319210111Sbschmidt fw->init.datasz = len; 6320210111Sbschmidt break; 6321210111Sbschmidt case IWN_FW_TLV_BOOT_TEXT: 6322210111Sbschmidt fw->boot.text = ptr; 6323210111Sbschmidt fw->boot.textsz = len; 6324210111Sbschmidt break; 6325220866Sbschmidt case IWN_FW_TLV_ENH_SENS: 6326220866Sbschmidt if (!len) 6327220866Sbschmidt sc->sc_flags |= IWN_FLAG_ENH_SENS; 6328220866Sbschmidt break; 6329220866Sbschmidt case IWN_FW_TLV_PHY_CALIB: 6330220866Sbschmidt tmp = htole32(*ptr); 6331220866Sbschmidt if (tmp < 253) { 6332220866Sbschmidt sc->reset_noise_gain = tmp; 6333220866Sbschmidt sc->noise_gain = tmp + 1; 6334220866Sbschmidt } 6335220866Sbschmidt break; 6336210111Sbschmidt default: 6337210111Sbschmidt DPRINTF(sc, IWN_DEBUG_RESET, 6338220724Sbschmidt "TLV type %d not handled\n", le16toh(tlv->type)); 6339210111Sbschmidt break; 6340210111Sbschmidt } 6341220726Sbschmidt next: /* TLV fields are 32-bit aligned. */ 6342210111Sbschmidt ptr += (len + 3) & ~3; 6343210111Sbschmidt } 6344210111Sbschmidt return 0; 6345210111Sbschmidt} 6346210111Sbschmidt 6347206477Sbschmidtstatic int 6348210111Sbschmidtiwn_read_firmware(struct iwn_softc *sc) 6349210111Sbschmidt{ 6350210111Sbschmidt struct iwn_fw_info *fw = &sc->fw; 6351210111Sbschmidt int error; 6352210111Sbschmidt 6353210111Sbschmidt IWN_UNLOCK(sc); 6354210111Sbschmidt 6355210111Sbschmidt memset(fw, 0, sizeof (*fw)); 6356210111Sbschmidt 6357210111Sbschmidt /* Read firmware image from filesystem. */ 6358210111Sbschmidt sc->fw_fp = firmware_get(sc->fwname); 6359210111Sbschmidt if (sc->fw_fp == NULL) { 6360220724Sbschmidt device_printf(sc->sc_dev, "%s: could not read firmware %s\n", 6361220724Sbschmidt __func__, sc->fwname); 6362210111Sbschmidt IWN_LOCK(sc); 6363210111Sbschmidt return EINVAL; 6364210111Sbschmidt } 6365210111Sbschmidt IWN_LOCK(sc); 6366210111Sbschmidt 6367210111Sbschmidt fw->size = sc->fw_fp->datasize; 6368210111Sbschmidt fw->data = (const uint8_t *)sc->fw_fp->data; 6369210111Sbschmidt if (fw->size < sizeof (uint32_t)) { 6370220724Sbschmidt device_printf(sc->sc_dev, "%s: firmware too short: %zu bytes\n", 6371210111Sbschmidt __func__, fw->size); 6372220661Sbschmidt firmware_put(sc->fw_fp, FIRMWARE_UNLOAD); 6373220661Sbschmidt sc->fw_fp = NULL; 6374210111Sbschmidt return EINVAL; 6375210111Sbschmidt } 6376210111Sbschmidt 6377210111Sbschmidt /* Retrieve text and data sections. */ 6378210111Sbschmidt if (*(const uint32_t *)fw->data != 0) /* Legacy image. */ 6379210111Sbschmidt error = iwn_read_firmware_leg(sc, fw); 6380210111Sbschmidt else 6381210111Sbschmidt error = iwn_read_firmware_tlv(sc, fw, 1); 6382210111Sbschmidt if (error != 0) { 6383210111Sbschmidt device_printf(sc->sc_dev, 6384220724Sbschmidt "%s: could not read firmware sections, error %d\n", 6385220724Sbschmidt __func__, error); 6386220661Sbschmidt firmware_put(sc->fw_fp, FIRMWARE_UNLOAD); 6387220661Sbschmidt sc->fw_fp = NULL; 6388210111Sbschmidt return error; 6389210111Sbschmidt } 6390210111Sbschmidt 6391210111Sbschmidt /* Make sure text and data sections fit in hardware memory. */ 6392220728Sbschmidt if (fw->main.textsz > sc->fw_text_maxsz || 6393220728Sbschmidt fw->main.datasz > sc->fw_data_maxsz || 6394220728Sbschmidt fw->init.textsz > sc->fw_text_maxsz || 6395220728Sbschmidt fw->init.datasz > sc->fw_data_maxsz || 6396210111Sbschmidt fw->boot.textsz > IWN_FW_BOOT_TEXT_MAXSZ || 6397210111Sbschmidt (fw->boot.textsz & 3) != 0) { 6398220724Sbschmidt device_printf(sc->sc_dev, "%s: firmware sections too large\n", 6399220724Sbschmidt __func__); 6400220661Sbschmidt firmware_put(sc->fw_fp, FIRMWARE_UNLOAD); 6401220661Sbschmidt sc->fw_fp = NULL; 6402210111Sbschmidt return EINVAL; 6403210111Sbschmidt } 6404210111Sbschmidt 6405210111Sbschmidt /* We can proceed with loading the firmware. */ 6406210111Sbschmidt return 0; 6407210111Sbschmidt} 6408210111Sbschmidt 6409210111Sbschmidtstatic int 6410198429Srpauloiwn_clock_wait(struct iwn_softc *sc) 6411198429Srpaulo{ 6412198429Srpaulo int ntries; 6413178676Ssam 6414198429Srpaulo /* Set "initialization complete" bit. */ 6415198429Srpaulo IWN_SETBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_INIT_DONE); 6416198429Srpaulo 6417198429Srpaulo /* Wait for clock stabilization. */ 6418201209Srpaulo for (ntries = 0; ntries < 2500; ntries++) { 6419198429Srpaulo if (IWN_READ(sc, IWN_GP_CNTRL) & IWN_GP_CNTRL_MAC_CLOCK_READY) 6420198429Srpaulo return 0; 6421201209Srpaulo DELAY(10); 6422178676Ssam } 6423198429Srpaulo device_printf(sc->sc_dev, 6424198429Srpaulo "%s: timeout waiting for clock stabilization\n", __func__); 6425198429Srpaulo return ETIMEDOUT; 6426198429Srpaulo} 6427178676Ssam 6428206477Sbschmidtstatic int 6429201209Srpauloiwn_apm_init(struct iwn_softc *sc) 6430198429Srpaulo{ 6431220721Sbschmidt uint32_t reg; 6432198429Srpaulo int error; 6433178676Ssam 6434220725Sbschmidt /* Disable L0s exit timer (NMI bug workaround). */ 6435198429Srpaulo IWN_SETBITS(sc, IWN_GIO_CHICKEN, IWN_GIO_CHICKEN_DIS_L0S_TIMER); 6436220725Sbschmidt /* Don't wait for ICH L0s (ICH bug workaround). */ 6437198429Srpaulo IWN_SETBITS(sc, IWN_GIO_CHICKEN, IWN_GIO_CHICKEN_L1A_NO_L0S_RX); 6438178676Ssam 6439220725Sbschmidt /* Set FH wait threshold to max (HW bug under stress workaround). */ 6440198429Srpaulo IWN_SETBITS(sc, IWN_DBG_HPET_MEM, 0xffff0000); 6441198429Srpaulo 6442201209Srpaulo /* Enable HAP INTA to move adapter from L1a to L0s. */ 6443198429Srpaulo IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_HAP_WAKE_L1A); 6444198429Srpaulo 6445201209Srpaulo /* Retrieve PCIe Active State Power Management (ASPM). */ 6446220721Sbschmidt reg = pci_read_config(sc->sc_dev, sc->sc_cap_off + 0x10, 1); 6447201209Srpaulo /* Workaround for HW instability in PCIe L0->L0s->L1 transition. */ 6448220721Sbschmidt if (reg & 0x02) /* L1 Entry enabled. */ 6449201209Srpaulo IWN_SETBITS(sc, IWN_GIO, IWN_GIO_L0S_ENA); 6450201209Srpaulo else 6451201209Srpaulo IWN_CLRBITS(sc, IWN_GIO, IWN_GIO_L0S_ENA); 6452201209Srpaulo 6453201209Srpaulo if (sc->hw_type != IWN_HW_REV_TYPE_4965 && 6454210109Sbschmidt sc->hw_type <= IWN_HW_REV_TYPE_1000) 6455198429Srpaulo IWN_SETBITS(sc, IWN_ANA_PLL, IWN_ANA_PLL_INIT); 6456198429Srpaulo 6457201209Srpaulo /* Wait for clock stabilization before accessing prph. */ 6458220726Sbschmidt if ((error = iwn_clock_wait(sc)) != 0) 6459198429Srpaulo return error; 6460198429Srpaulo 6461220726Sbschmidt if ((error = iwn_nic_lock(sc)) != 0) 6462198429Srpaulo return error; 6463201209Srpaulo if (sc->hw_type == IWN_HW_REV_TYPE_4965) { 6464220725Sbschmidt /* Enable DMA and BSM (Bootstrap State Machine). */ 6465201209Srpaulo iwn_prph_write(sc, IWN_APMG_CLK_EN, 6466201209Srpaulo IWN_APMG_CLK_CTRL_DMA_CLK_RQT | 6467201209Srpaulo IWN_APMG_CLK_CTRL_BSM_CLK_RQT); 6468201209Srpaulo } else { 6469201209Srpaulo /* Enable DMA. */ 6470201209Srpaulo iwn_prph_write(sc, IWN_APMG_CLK_EN, 6471201209Srpaulo IWN_APMG_CLK_CTRL_DMA_CLK_RQT); 6472201209Srpaulo } 6473198429Srpaulo DELAY(20); 6474201209Srpaulo /* Disable L1-Active. */ 6475198429Srpaulo iwn_prph_setbits(sc, IWN_APMG_PCI_STT, IWN_APMG_PCI_STT_L1A_DIS); 6476198429Srpaulo iwn_nic_unlock(sc); 6477198429Srpaulo 6478198429Srpaulo return 0; 6479198429Srpaulo} 6480198429Srpaulo 6481206477Sbschmidtstatic void 6482198429Srpauloiwn_apm_stop_master(struct iwn_softc *sc) 6483178676Ssam{ 6484178676Ssam int ntries; 6485178676Ssam 6486201209Srpaulo /* Stop busmaster DMA activity. */ 6487198429Srpaulo IWN_SETBITS(sc, IWN_RESET, IWN_RESET_STOP_MASTER); 6488178676Ssam for (ntries = 0; ntries < 100; ntries++) { 6489198429Srpaulo if (IWN_READ(sc, IWN_RESET) & IWN_RESET_MASTER_DISABLED) 6490198429Srpaulo return; 6491178676Ssam DELAY(10); 6492178676Ssam } 6493220726Sbschmidt device_printf(sc->sc_dev, "%s: timeout waiting for master\n", __func__); 6494178676Ssam} 6495178676Ssam 6496206477Sbschmidtstatic void 6497198429Srpauloiwn_apm_stop(struct iwn_softc *sc) 6498198429Srpaulo{ 6499198429Srpaulo iwn_apm_stop_master(sc); 6500198429Srpaulo 6501201209Srpaulo /* Reset the entire device. */ 6502198429Srpaulo IWN_SETBITS(sc, IWN_RESET, IWN_RESET_SW); 6503198429Srpaulo DELAY(10); 6504198429Srpaulo /* Clear "initialization complete" bit. */ 6505198429Srpaulo IWN_CLRBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_INIT_DONE); 6506198429Srpaulo} 6507198429Srpaulo 6508206477Sbschmidtstatic int 6509198429Srpauloiwn4965_nic_config(struct iwn_softc *sc) 6510178676Ssam{ 6511198429Srpaulo if (IWN_RFCFG_TYPE(sc->rfcfg) == 1) { 6512198429Srpaulo /* 6513198429Srpaulo * I don't believe this to be correct but this is what the 6514198429Srpaulo * vendor driver is doing. Probably the bits should not be 6515198429Srpaulo * shifted in IWN_RFCFG_*. 6516198429Srpaulo */ 6517198429Srpaulo IWN_SETBITS(sc, IWN_HW_IF_CONFIG, 6518198429Srpaulo IWN_RFCFG_TYPE(sc->rfcfg) | 6519198429Srpaulo IWN_RFCFG_STEP(sc->rfcfg) | 6520198429Srpaulo IWN_RFCFG_DASH(sc->rfcfg)); 6521198429Srpaulo } 6522198429Srpaulo IWN_SETBITS(sc, IWN_HW_IF_CONFIG, 6523198429Srpaulo IWN_HW_IF_CONFIG_RADIO_SI | IWN_HW_IF_CONFIG_MAC_SI); 6524198429Srpaulo return 0; 6525198429Srpaulo} 6526178676Ssam 6527206477Sbschmidtstatic int 6528198429Srpauloiwn5000_nic_config(struct iwn_softc *sc) 6529198429Srpaulo{ 6530198429Srpaulo uint32_t tmp; 6531198429Srpaulo int error; 6532178676Ssam 6533198429Srpaulo if (IWN_RFCFG_TYPE(sc->rfcfg) < 3) { 6534198429Srpaulo IWN_SETBITS(sc, IWN_HW_IF_CONFIG, 6535198429Srpaulo IWN_RFCFG_TYPE(sc->rfcfg) | 6536198429Srpaulo IWN_RFCFG_STEP(sc->rfcfg) | 6537198429Srpaulo IWN_RFCFG_DASH(sc->rfcfg)); 6538198429Srpaulo } 6539198429Srpaulo IWN_SETBITS(sc, IWN_HW_IF_CONFIG, 6540198429Srpaulo IWN_HW_IF_CONFIG_RADIO_SI | IWN_HW_IF_CONFIG_MAC_SI); 6541198429Srpaulo 6542220726Sbschmidt if ((error = iwn_nic_lock(sc)) != 0) 6543198429Srpaulo return error; 6544198429Srpaulo iwn_prph_setbits(sc, IWN_APMG_PS, IWN_APMG_PS_EARLY_PWROFF_DIS); 6545201209Srpaulo 6546201209Srpaulo if (sc->hw_type == IWN_HW_REV_TYPE_1000) { 6547201209Srpaulo /* 6548201209Srpaulo * Select first Switching Voltage Regulator (1.32V) to 6549201209Srpaulo * solve a stability issue related to noisy DC2DC line 6550201209Srpaulo * in the silicon of 1000 Series. 6551201209Srpaulo */ 6552201209Srpaulo tmp = iwn_prph_read(sc, IWN_APMG_DIGITAL_SVR); 6553201209Srpaulo tmp &= ~IWN_APMG_DIGITAL_SVR_VOLTAGE_MASK; 6554201209Srpaulo tmp |= IWN_APMG_DIGITAL_SVR_VOLTAGE_1_32; 6555201209Srpaulo iwn_prph_write(sc, IWN_APMG_DIGITAL_SVR, tmp); 6556201209Srpaulo } 6557198429Srpaulo iwn_nic_unlock(sc); 6558201209Srpaulo 6559201209Srpaulo if (sc->sc_flags & IWN_FLAG_INTERNAL_PA) { 6560201209Srpaulo /* Use internal power amplifier only. */ 6561201209Srpaulo IWN_WRITE(sc, IWN_GP_DRIVER, IWN_GP_DRIVER_RADIO_2X2_IPA); 6562201209Srpaulo } 6563220676Sbschmidt if ((sc->hw_type == IWN_HW_REV_TYPE_6050 || 6564220676Sbschmidt sc->hw_type == IWN_HW_REV_TYPE_6005) && sc->calib_ver >= 6) { 6565210108Sbschmidt /* Indicate that ROM calibration version is >=6. */ 6566210108Sbschmidt IWN_SETBITS(sc, IWN_GP_DRIVER, IWN_GP_DRIVER_CALIB_VER6); 6567206444Sbschmidt } 6568220729Sbschmidt if (sc->hw_type == IWN_HW_REV_TYPE_6005) 6569220729Sbschmidt IWN_SETBITS(sc, IWN_GP_DRIVER, IWN_GP_DRIVER_6050_1X2); 6570198429Srpaulo return 0; 6571198429Srpaulo} 6572198429Srpaulo 6573198429Srpaulo/* 6574198429Srpaulo * Take NIC ownership over Intel Active Management Technology (AMT). 6575198429Srpaulo */ 6576206477Sbschmidtstatic int 6577198429Srpauloiwn_hw_prepare(struct iwn_softc *sc) 6578198429Srpaulo{ 6579198429Srpaulo int ntries; 6580198429Srpaulo 6581201209Srpaulo /* Check if hardware is ready. */ 6582201209Srpaulo IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_NIC_READY); 6583201209Srpaulo for (ntries = 0; ntries < 5; ntries++) { 6584201209Srpaulo if (IWN_READ(sc, IWN_HW_IF_CONFIG) & 6585201209Srpaulo IWN_HW_IF_CONFIG_NIC_READY) 6586201209Srpaulo return 0; 6587201209Srpaulo DELAY(10); 6588201209Srpaulo } 6589201209Srpaulo 6590201209Srpaulo /* Hardware not ready, force into ready state. */ 6591198429Srpaulo IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_PREPARE); 6592198429Srpaulo for (ntries = 0; ntries < 15000; ntries++) { 6593198429Srpaulo if (!(IWN_READ(sc, IWN_HW_IF_CONFIG) & 6594198429Srpaulo IWN_HW_IF_CONFIG_PREPARE_DONE)) 6595178676Ssam break; 6596178676Ssam DELAY(10); 6597178676Ssam } 6598198429Srpaulo if (ntries == 15000) 6599178676Ssam return ETIMEDOUT; 6600198429Srpaulo 6601201209Srpaulo /* Hardware should be ready now. */ 6602198429Srpaulo IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_NIC_READY); 6603198429Srpaulo for (ntries = 0; ntries < 5; ntries++) { 6604198429Srpaulo if (IWN_READ(sc, IWN_HW_IF_CONFIG) & 6605198429Srpaulo IWN_HW_IF_CONFIG_NIC_READY) 6606198429Srpaulo return 0; 6607198429Srpaulo DELAY(10); 6608178676Ssam } 6609198429Srpaulo return ETIMEDOUT; 6610178676Ssam} 6611178676Ssam 6612206477Sbschmidtstatic int 6613198429Srpauloiwn_hw_init(struct iwn_softc *sc) 6614178676Ssam{ 6615220728Sbschmidt struct iwn_ops *ops = &sc->ops; 6616198429Srpaulo int error, chnl, qid; 6617178676Ssam 6618198429Srpaulo /* Clear pending interrupts. */ 6619198429Srpaulo IWN_WRITE(sc, IWN_INT, 0xffffffff); 6620178676Ssam 6621220726Sbschmidt if ((error = iwn_apm_init(sc)) != 0) { 6622198429Srpaulo device_printf(sc->sc_dev, 6623220726Sbschmidt "%s: could not power ON adapter, error %d\n", __func__, 6624220726Sbschmidt error); 6625198429Srpaulo return error; 6626178676Ssam } 6627178676Ssam 6628198429Srpaulo /* Select VMAIN power source. */ 6629220726Sbschmidt if ((error = iwn_nic_lock(sc)) != 0) 6630198429Srpaulo return error; 6631198429Srpaulo iwn_prph_clrbits(sc, IWN_APMG_PS, IWN_APMG_PS_PWR_SRC_MASK); 6632198429Srpaulo iwn_nic_unlock(sc); 6633178676Ssam 6634198429Srpaulo /* Perform adapter-specific initialization. */ 6635220728Sbschmidt if ((error = ops->nic_config(sc)) != 0) 6636198429Srpaulo return error; 6637178676Ssam 6638198429Srpaulo /* Initialize RX ring. */ 6639220726Sbschmidt if ((error = iwn_nic_lock(sc)) != 0) 6640198429Srpaulo return error; 6641198429Srpaulo IWN_WRITE(sc, IWN_FH_RX_CONFIG, 0); 6642198429Srpaulo IWN_WRITE(sc, IWN_FH_RX_WPTR, 0); 6643220725Sbschmidt /* Set physical address of RX ring (256-byte aligned). */ 6644198429Srpaulo IWN_WRITE(sc, IWN_FH_RX_BASE, sc->rxq.desc_dma.paddr >> 8); 6645220725Sbschmidt /* Set physical address of RX status (16-byte aligned). */ 6646198429Srpaulo IWN_WRITE(sc, IWN_FH_STATUS_WPTR, sc->rxq.stat_dma.paddr >> 4); 6647198429Srpaulo /* Enable RX. */ 6648198429Srpaulo IWN_WRITE(sc, IWN_FH_RX_CONFIG, 6649198429Srpaulo IWN_FH_RX_CONFIG_ENA | 6650198429Srpaulo IWN_FH_RX_CONFIG_IGN_RXF_EMPTY | /* HW bug workaround */ 6651198429Srpaulo IWN_FH_RX_CONFIG_IRQ_DST_HOST | 6652198429Srpaulo IWN_FH_RX_CONFIG_SINGLE_FRAME | 6653198429Srpaulo IWN_FH_RX_CONFIG_RB_TIMEOUT(0) | 6654198429Srpaulo IWN_FH_RX_CONFIG_NRBD(IWN_RX_RING_COUNT_LOG)); 6655198429Srpaulo iwn_nic_unlock(sc); 6656198429Srpaulo IWN_WRITE(sc, IWN_FH_RX_WPTR, (IWN_RX_RING_COUNT - 1) & ~7); 6657178676Ssam 6658220726Sbschmidt if ((error = iwn_nic_lock(sc)) != 0) 6659198429Srpaulo return error; 6660178676Ssam 6661198429Srpaulo /* Initialize TX scheduler. */ 6662220728Sbschmidt iwn_prph_write(sc, sc->sched_txfact_addr, 0); 6663178676Ssam 6664220725Sbschmidt /* Set physical address of "keep warm" page (16-byte aligned). */ 6665198429Srpaulo IWN_WRITE(sc, IWN_FH_KW_ADDR, sc->kw_dma.paddr >> 4); 6666198429Srpaulo 6667198429Srpaulo /* Initialize TX rings. */ 6668220728Sbschmidt for (qid = 0; qid < sc->ntxqs; qid++) { 6669198429Srpaulo struct iwn_tx_ring *txq = &sc->txq[qid]; 6670198429Srpaulo 6671220725Sbschmidt /* Set physical address of TX ring (256-byte aligned). */ 6672198429Srpaulo IWN_WRITE(sc, IWN_FH_CBBC_QUEUE(qid), 6673198429Srpaulo txq->desc_dma.paddr >> 8); 6674178676Ssam } 6675198429Srpaulo iwn_nic_unlock(sc); 6676178676Ssam 6677198429Srpaulo /* Enable DMA channels. */ 6678220728Sbschmidt for (chnl = 0; chnl < sc->ndmachnls; chnl++) { 6679198429Srpaulo IWN_WRITE(sc, IWN_FH_TX_CONFIG(chnl), 6680198429Srpaulo IWN_FH_TX_CONFIG_DMA_ENA | 6681198429Srpaulo IWN_FH_TX_CONFIG_DMA_CREDIT_ENA); 6682198429Srpaulo } 6683198429Srpaulo 6684198429Srpaulo /* Clear "radio off" and "commands blocked" bits. */ 6685198429Srpaulo IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_RFKILL); 6686198429Srpaulo IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_CMD_BLOCKED); 6687198429Srpaulo 6688198429Srpaulo /* Clear pending interrupts. */ 6689198429Srpaulo IWN_WRITE(sc, IWN_INT, 0xffffffff); 6690198429Srpaulo /* Enable interrupt coalescing. */ 6691198429Srpaulo IWN_WRITE(sc, IWN_INT_COALESCING, 512 / 8); 6692198429Srpaulo /* Enable interrupts. */ 6693201209Srpaulo IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask); 6694198429Srpaulo 6695198429Srpaulo /* _Really_ make sure "radio off" bit is cleared! */ 6696198429Srpaulo IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_RFKILL); 6697198429Srpaulo IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_RFKILL); 6698198429Srpaulo 6699220729Sbschmidt /* Enable shadow registers. */ 6700220729Sbschmidt if (sc->hw_type >= IWN_HW_REV_TYPE_6000) 6701220729Sbschmidt IWN_SETBITS(sc, IWN_SHADOW_REG_CTRL, 0x800fffff); 6702220729Sbschmidt 6703220728Sbschmidt if ((error = ops->load_firmware(sc)) != 0) { 6704178676Ssam device_printf(sc->sc_dev, 6705220726Sbschmidt "%s: could not load firmware, error %d\n", __func__, 6706220726Sbschmidt error); 6707198429Srpaulo return error; 6708178676Ssam } 6709198429Srpaulo /* Wait at most one second for firmware alive notification. */ 6710220726Sbschmidt if ((error = msleep(sc, &sc->sc_mtx, PCATCH, "iwninit", hz)) != 0) { 6711198429Srpaulo device_printf(sc->sc_dev, 6712198429Srpaulo "%s: timeout waiting for adapter to initialize, error %d\n", 6713198429Srpaulo __func__, error); 6714198429Srpaulo return error; 6715198429Srpaulo } 6716198429Srpaulo /* Do post-firmware initialization. */ 6717220728Sbschmidt return ops->post_alive(sc); 6718198429Srpaulo} 6719178676Ssam 6720206477Sbschmidtstatic void 6721198429Srpauloiwn_hw_stop(struct iwn_softc *sc) 6722198429Srpaulo{ 6723198429Srpaulo int chnl, qid, ntries; 6724178676Ssam 6725198429Srpaulo IWN_WRITE(sc, IWN_RESET, IWN_RESET_NEVO); 6726178676Ssam 6727198429Srpaulo /* Disable interrupts. */ 6728201209Srpaulo IWN_WRITE(sc, IWN_INT_MASK, 0); 6729198429Srpaulo IWN_WRITE(sc, IWN_INT, 0xffffffff); 6730198429Srpaulo IWN_WRITE(sc, IWN_FH_INT, 0xffffffff); 6731201209Srpaulo sc->sc_flags &= ~IWN_FLAG_USE_ICT; 6732178676Ssam 6733198429Srpaulo /* Make sure we no longer hold the NIC lock. */ 6734198429Srpaulo iwn_nic_unlock(sc); 6735178676Ssam 6736198429Srpaulo /* Stop TX scheduler. */ 6737220728Sbschmidt iwn_prph_write(sc, sc->sched_txfact_addr, 0); 6738178676Ssam 6739198429Srpaulo /* Stop all DMA channels. */ 6740198429Srpaulo if (iwn_nic_lock(sc) == 0) { 6741220728Sbschmidt for (chnl = 0; chnl < sc->ndmachnls; chnl++) { 6742198429Srpaulo IWN_WRITE(sc, IWN_FH_TX_CONFIG(chnl), 0); 6743198429Srpaulo for (ntries = 0; ntries < 200; ntries++) { 6744220659Sbschmidt if (IWN_READ(sc, IWN_FH_TX_STATUS) & 6745198429Srpaulo IWN_FH_TX_STATUS_IDLE(chnl)) 6746198429Srpaulo break; 6747198429Srpaulo DELAY(10); 6748198429Srpaulo } 6749198429Srpaulo } 6750198429Srpaulo iwn_nic_unlock(sc); 6751198429Srpaulo } 6752178676Ssam 6753198429Srpaulo /* Stop RX ring. */ 6754198429Srpaulo iwn_reset_rx_ring(sc, &sc->rxq); 6755178676Ssam 6756198429Srpaulo /* Reset all TX rings. */ 6757220728Sbschmidt for (qid = 0; qid < sc->ntxqs; qid++) 6758198429Srpaulo iwn_reset_tx_ring(sc, &sc->txq[qid]); 6759178676Ssam 6760198429Srpaulo if (iwn_nic_lock(sc) == 0) { 6761201209Srpaulo iwn_prph_write(sc, IWN_APMG_CLK_DIS, 6762201209Srpaulo IWN_APMG_CLK_CTRL_DMA_CLK_RQT); 6763198429Srpaulo iwn_nic_unlock(sc); 6764178676Ssam } 6765198429Srpaulo DELAY(5); 6766198429Srpaulo /* Power OFF adapter. */ 6767198429Srpaulo iwn_apm_stop(sc); 6768198429Srpaulo} 6769178676Ssam 6770206477Sbschmidtstatic void 6771220723Sbschmidtiwn_radio_on(void *arg0, int pending) 6772220723Sbschmidt{ 6773220723Sbschmidt struct iwn_softc *sc = arg0; 6774220723Sbschmidt struct ifnet *ifp = sc->sc_ifp; 6775220723Sbschmidt struct ieee80211com *ic = ifp->if_l2com; 6776220723Sbschmidt struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 6777220723Sbschmidt 6778220723Sbschmidt if (vap != NULL) { 6779220723Sbschmidt iwn_init(sc); 6780220723Sbschmidt ieee80211_init(vap); 6781220723Sbschmidt } 6782220723Sbschmidt} 6783220723Sbschmidt 6784220723Sbschmidtstatic void 6785220723Sbschmidtiwn_radio_off(void *arg0, int pending) 6786220723Sbschmidt{ 6787220723Sbschmidt struct iwn_softc *sc = arg0; 6788220723Sbschmidt struct ifnet *ifp = sc->sc_ifp; 6789220723Sbschmidt struct ieee80211com *ic = ifp->if_l2com; 6790220723Sbschmidt struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 6791220723Sbschmidt 6792220723Sbschmidt iwn_stop(sc); 6793220723Sbschmidt if (vap != NULL) 6794220723Sbschmidt ieee80211_stop(vap); 6795220723Sbschmidt 6796220723Sbschmidt /* Enable interrupts to get RF toggle notification. */ 6797220723Sbschmidt IWN_LOCK(sc); 6798220723Sbschmidt IWN_WRITE(sc, IWN_INT, 0xffffffff); 6799220723Sbschmidt IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask); 6800220723Sbschmidt IWN_UNLOCK(sc); 6801220723Sbschmidt} 6802220723Sbschmidt 6803220723Sbschmidtstatic void 6804198429Srpauloiwn_init_locked(struct iwn_softc *sc) 6805198429Srpaulo{ 6806198429Srpaulo struct ifnet *ifp = sc->sc_ifp; 6807198429Srpaulo int error; 6808178676Ssam 6809198429Srpaulo IWN_LOCK_ASSERT(sc); 6810178676Ssam 6811220726Sbschmidt if ((error = iwn_hw_prepare(sc)) != 0) { 6812220724Sbschmidt device_printf(sc->sc_dev, "%s: hardware not ready, error %d\n", 6813198429Srpaulo __func__, error); 6814198429Srpaulo goto fail; 6815198429Srpaulo } 6816198429Srpaulo 6817201209Srpaulo /* Initialize interrupt mask to default value. */ 6818201209Srpaulo sc->int_mask = IWN_INT_MASK_DEF; 6819201209Srpaulo sc->sc_flags &= ~IWN_FLAG_USE_ICT; 6820201209Srpaulo 6821198429Srpaulo /* Check that the radio is not disabled by hardware switch. */ 6822198429Srpaulo if (!(IWN_READ(sc, IWN_GP_CNTRL) & IWN_GP_CNTRL_RFKILL)) { 6823178676Ssam device_printf(sc->sc_dev, 6824201209Srpaulo "radio is disabled by hardware switch\n"); 6825201209Srpaulo /* Enable interrupts to get RF toggle notifications. */ 6826201209Srpaulo IWN_WRITE(sc, IWN_INT, 0xffffffff); 6827201209Srpaulo IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask); 6828201209Srpaulo return; 6829178676Ssam } 6830178676Ssam 6831198429Srpaulo /* Read firmware images from the filesystem. */ 6832220726Sbschmidt if ((error = iwn_read_firmware(sc)) != 0) { 6833178676Ssam device_printf(sc->sc_dev, 6834220726Sbschmidt "%s: could not read firmware, error %d\n", __func__, 6835220726Sbschmidt error); 6836198429Srpaulo goto fail; 6837178676Ssam } 6838178676Ssam 6839198429Srpaulo /* Initialize hardware and upload firmware. */ 6840198429Srpaulo error = iwn_hw_init(sc); 6841201209Srpaulo firmware_put(sc->fw_fp, FIRMWARE_UNLOAD); 6842201209Srpaulo sc->fw_fp = NULL; 6843198429Srpaulo if (error != 0) { 6844198429Srpaulo device_printf(sc->sc_dev, 6845220726Sbschmidt "%s: could not initialize hardware, error %d\n", __func__, 6846220726Sbschmidt error); 6847198429Srpaulo goto fail; 6848198429Srpaulo } 6849178676Ssam 6850198429Srpaulo /* Configure adapter now that it is ready. */ 6851220726Sbschmidt if ((error = iwn_config(sc)) != 0) { 6852178676Ssam device_printf(sc->sc_dev, 6853220726Sbschmidt "%s: could not configure device, error %d\n", __func__, 6854220726Sbschmidt error); 6855198429Srpaulo goto fail; 6856178676Ssam } 6857178676Ssam 6858178676Ssam ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 6859178676Ssam ifp->if_drv_flags |= IFF_DRV_RUNNING; 6860198429Srpaulo 6861220667Sbschmidt callout_reset(&sc->watchdog_to, hz, iwn_watchdog, sc); 6862198429Srpaulo return; 6863198429Srpaulo 6864220726Sbschmidtfail: iwn_stop_locked(sc); 6865178676Ssam} 6866178676Ssam 6867206477Sbschmidtstatic void 6868178676Ssamiwn_init(void *arg) 6869178676Ssam{ 6870178676Ssam struct iwn_softc *sc = arg; 6871178676Ssam struct ifnet *ifp = sc->sc_ifp; 6872178676Ssam struct ieee80211com *ic = ifp->if_l2com; 6873178676Ssam 6874178676Ssam IWN_LOCK(sc); 6875178676Ssam iwn_init_locked(sc); 6876178676Ssam IWN_UNLOCK(sc); 6877178676Ssam 6878178676Ssam if (ifp->if_drv_flags & IFF_DRV_RUNNING) 6879178676Ssam ieee80211_start_all(ic); 6880178676Ssam} 6881178676Ssam 6882206477Sbschmidtstatic void 6883178676Ssamiwn_stop_locked(struct iwn_softc *sc) 6884178676Ssam{ 6885178676Ssam struct ifnet *ifp = sc->sc_ifp; 6886178676Ssam 6887178676Ssam IWN_LOCK_ASSERT(sc); 6888178676Ssam 6889178676Ssam sc->sc_tx_timer = 0; 6890220667Sbschmidt callout_stop(&sc->watchdog_to); 6891220667Sbschmidt callout_stop(&sc->calib_to); 6892178676Ssam ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); 6893178676Ssam 6894198429Srpaulo /* Power OFF hardware. */ 6895198429Srpaulo iwn_hw_stop(sc); 6896198429Srpaulo} 6897178676Ssam 6898206477Sbschmidtstatic void 6899178676Ssamiwn_stop(struct iwn_softc *sc) 6900178676Ssam{ 6901178676Ssam IWN_LOCK(sc); 6902178676Ssam iwn_stop_locked(sc); 6903178676Ssam IWN_UNLOCK(sc); 6904178676Ssam} 6905178676Ssam 6906178676Ssam/* 6907178676Ssam * Callback from net80211 to start a scan. 6908178676Ssam */ 6909178676Ssamstatic void 6910178676Ssamiwn_scan_start(struct ieee80211com *ic) 6911178676Ssam{ 6912178676Ssam struct ifnet *ifp = ic->ic_ifp; 6913178676Ssam struct iwn_softc *sc = ifp->if_softc; 6914178676Ssam 6915191746Sthompsa IWN_LOCK(sc); 6916191746Sthompsa /* make the link LED blink while we're scanning */ 6917191746Sthompsa iwn_set_led(sc, IWN_LED_LINK, 20, 2); 6918191746Sthompsa IWN_UNLOCK(sc); 6919178676Ssam} 6920178676Ssam 6921178676Ssam/* 6922178676Ssam * Callback from net80211 to terminate a scan. 6923178676Ssam */ 6924178676Ssamstatic void 6925178676Ssamiwn_scan_end(struct ieee80211com *ic) 6926178676Ssam{ 6927201209Srpaulo struct ifnet *ifp = ic->ic_ifp; 6928201209Srpaulo struct iwn_softc *sc = ifp->if_softc; 6929201209Srpaulo struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 6930201209Srpaulo 6931201209Srpaulo IWN_LOCK(sc); 6932201209Srpaulo if (vap->iv_state == IEEE80211_S_RUN) { 6933201209Srpaulo /* Set link LED to ON status if we are associated */ 6934201209Srpaulo iwn_set_led(sc, IWN_LED_LINK, 0, 1); 6935201209Srpaulo } 6936201209Srpaulo IWN_UNLOCK(sc); 6937178676Ssam} 6938178676Ssam 6939178676Ssam/* 6940178676Ssam * Callback from net80211 to force a channel change. 6941178676Ssam */ 6942178676Ssamstatic void 6943178676Ssamiwn_set_channel(struct ieee80211com *ic) 6944178676Ssam{ 6945198429Srpaulo const struct ieee80211_channel *c = ic->ic_curchan; 6946178676Ssam struct ifnet *ifp = ic->ic_ifp; 6947178676Ssam struct iwn_softc *sc = ifp->if_softc; 6948178676Ssam 6949191746Sthompsa IWN_LOCK(sc); 6950201209Srpaulo sc->sc_rxtap.wr_chan_freq = htole16(c->ic_freq); 6951201209Srpaulo sc->sc_rxtap.wr_chan_flags = htole16(c->ic_flags); 6952201209Srpaulo sc->sc_txtap.wt_chan_freq = htole16(c->ic_freq); 6953201209Srpaulo sc->sc_txtap.wt_chan_flags = htole16(c->ic_flags); 6954191746Sthompsa IWN_UNLOCK(sc); 6955178676Ssam} 6956178676Ssam 6957178676Ssam/* 6958178676Ssam * Callback from net80211 to start scanning of the current channel. 6959178676Ssam */ 6960178676Ssamstatic void 6961178676Ssamiwn_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell) 6962178676Ssam{ 6963178676Ssam struct ieee80211vap *vap = ss->ss_vap; 6964178676Ssam struct iwn_softc *sc = vap->iv_ic->ic_ifp->if_softc; 6965191746Sthompsa int error; 6966178676Ssam 6967191746Sthompsa IWN_LOCK(sc); 6968191746Sthompsa error = iwn_scan(sc); 6969191746Sthompsa IWN_UNLOCK(sc); 6970191746Sthompsa if (error != 0) 6971191746Sthompsa ieee80211_cancel_scan(vap); 6972178676Ssam} 6973178676Ssam 6974178676Ssam/* 6975178676Ssam * Callback from net80211 to handle the minimum dwell time being met. 6976178676Ssam * The intent is to terminate the scan but we just let the firmware 6977178676Ssam * notify us when it's finished as we have no safe way to abort it. 6978178676Ssam */ 6979178676Ssamstatic void 6980178676Ssamiwn_scan_mindwell(struct ieee80211_scan_state *ss) 6981178676Ssam{ 6982178676Ssam /* NB: don't try to abort scan; wait for firmware to finish */ 6983178676Ssam} 6984178676Ssam 6985178676Ssamstatic void 6986198429Srpauloiwn_hw_reset(void *arg0, int pending) 6987178676Ssam{ 6988178676Ssam struct iwn_softc *sc = arg0; 6989178676Ssam struct ifnet *ifp = sc->sc_ifp; 6990178676Ssam struct ieee80211com *ic = ifp->if_l2com; 6991178676Ssam 6992201209Srpaulo iwn_stop(sc); 6993191746Sthompsa iwn_init(sc); 6994191746Sthompsa ieee80211_notify_radio(ic, 1); 6995191746Sthompsa} 6996