1*fd43cf6eSHans Rosenfeld /* $NetBSD: if_iwn.c,v 1.78 2016/06/10 13:27:14 ozaki-r Exp $ */ 2*fd43cf6eSHans Rosenfeld /* $OpenBSD: if_iwn.c,v 1.135 2014/09/10 07:22:09 dcoppa Exp $ */ 3*fd43cf6eSHans Rosenfeld 4*fd43cf6eSHans Rosenfeld /*- 5*fd43cf6eSHans Rosenfeld * Copyright (c) 2007-2010 Damien Bergamini <damien.bergamini@free.fr> 6*fd43cf6eSHans Rosenfeld * 7*fd43cf6eSHans Rosenfeld * Permission to use, copy, modify, and distribute this software for any 8*fd43cf6eSHans Rosenfeld * purpose with or without fee is hereby granted, provided that the above 9*fd43cf6eSHans Rosenfeld * copyright notice and this permission notice appear in all copies. 10*fd43cf6eSHans Rosenfeld * 11*fd43cf6eSHans Rosenfeld * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12*fd43cf6eSHans Rosenfeld * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13*fd43cf6eSHans Rosenfeld * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14*fd43cf6eSHans Rosenfeld * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15*fd43cf6eSHans Rosenfeld * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16*fd43cf6eSHans Rosenfeld * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17*fd43cf6eSHans Rosenfeld * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18*fd43cf6eSHans Rosenfeld */ 19*fd43cf6eSHans Rosenfeld 20*fd43cf6eSHans Rosenfeld /* 21*fd43cf6eSHans Rosenfeld * Copyright 2016 Hans Rosenfeld <rosenfeld@grumpf.hope-2000.org> 22*fd43cf6eSHans Rosenfeld */ 23*fd43cf6eSHans Rosenfeld 24*fd43cf6eSHans Rosenfeld /* 25*fd43cf6eSHans Rosenfeld * Driver for Intel WiFi Link 4965 and 100/1000/2000/5000/6000 Series 802.11 26*fd43cf6eSHans Rosenfeld * network adapters. 27*fd43cf6eSHans Rosenfeld */ 28*fd43cf6eSHans Rosenfeld 29*fd43cf6eSHans Rosenfeld /* 30*fd43cf6eSHans Rosenfeld * TODO: 31*fd43cf6eSHans Rosenfeld * - turn tunables into driver properties 32*fd43cf6eSHans Rosenfeld */ 33*fd43cf6eSHans Rosenfeld 34*fd43cf6eSHans Rosenfeld #undef IWN_HWCRYPTO /* XXX does not even compile yet */ 35*fd43cf6eSHans Rosenfeld 36*fd43cf6eSHans Rosenfeld #include <sys/modctl.h> 37*fd43cf6eSHans Rosenfeld #include <sys/ddi.h> 38*fd43cf6eSHans Rosenfeld #include <sys/sunddi.h> 39*fd43cf6eSHans Rosenfeld #include <sys/stat.h> 40*fd43cf6eSHans Rosenfeld 41*fd43cf6eSHans Rosenfeld #include <sys/param.h> 42*fd43cf6eSHans Rosenfeld #include <sys/sockio.h> 43*fd43cf6eSHans Rosenfeld #include <sys/proc.h> 44*fd43cf6eSHans Rosenfeld #include <sys/socket.h> 45*fd43cf6eSHans Rosenfeld #include <sys/systm.h> 46*fd43cf6eSHans Rosenfeld #include <sys/mutex.h> 47*fd43cf6eSHans Rosenfeld #include <sys/conf.h> 48*fd43cf6eSHans Rosenfeld 49*fd43cf6eSHans Rosenfeld #include <sys/pci.h> 50*fd43cf6eSHans Rosenfeld #include <sys/pcie.h> 51*fd43cf6eSHans Rosenfeld 52*fd43cf6eSHans Rosenfeld #include <net/if.h> 53*fd43cf6eSHans Rosenfeld #include <net/if_arp.h> 54*fd43cf6eSHans Rosenfeld #include <net/if_dl.h> 55*fd43cf6eSHans Rosenfeld #include <net/if_types.h> 56*fd43cf6eSHans Rosenfeld 57*fd43cf6eSHans Rosenfeld #include <netinet/in.h> 58*fd43cf6eSHans Rosenfeld #include <netinet/in_systm.h> 59*fd43cf6eSHans Rosenfeld #include <netinet/in_var.h> 60*fd43cf6eSHans Rosenfeld #include <netinet/ip.h> 61*fd43cf6eSHans Rosenfeld 62*fd43cf6eSHans Rosenfeld #include <sys/dlpi.h> 63*fd43cf6eSHans Rosenfeld #include <sys/mac_provider.h> 64*fd43cf6eSHans Rosenfeld #include <sys/mac_wifi.h> 65*fd43cf6eSHans Rosenfeld #include <sys/net80211.h> 66*fd43cf6eSHans Rosenfeld #include <sys/firmload.h> 67*fd43cf6eSHans Rosenfeld #include <sys/queue.h> 68*fd43cf6eSHans Rosenfeld #include <sys/strsun.h> 69*fd43cf6eSHans Rosenfeld #include <sys/strsubr.h> 70*fd43cf6eSHans Rosenfeld #include <sys/sysmacros.h> 71*fd43cf6eSHans Rosenfeld #include <sys/types.h> 72*fd43cf6eSHans Rosenfeld #include <sys/kstat.h> 73*fd43cf6eSHans Rosenfeld 74*fd43cf6eSHans Rosenfeld #include <sys/sdt.h> 75*fd43cf6eSHans Rosenfeld 76*fd43cf6eSHans Rosenfeld #include "if_iwncompat.h" 77*fd43cf6eSHans Rosenfeld #include "if_iwnreg.h" 78*fd43cf6eSHans Rosenfeld #include "if_iwnvar.h" 79*fd43cf6eSHans Rosenfeld #include <inet/wifi_ioctl.h> 80*fd43cf6eSHans Rosenfeld 81*fd43cf6eSHans Rosenfeld #ifdef DEBUG 82*fd43cf6eSHans Rosenfeld #define IWN_DEBUG 83*fd43cf6eSHans Rosenfeld #endif 84*fd43cf6eSHans Rosenfeld 85*fd43cf6eSHans Rosenfeld /* 86*fd43cf6eSHans Rosenfeld * regs access attributes 87*fd43cf6eSHans Rosenfeld */ 88*fd43cf6eSHans Rosenfeld static ddi_device_acc_attr_t iwn_reg_accattr = { 89*fd43cf6eSHans Rosenfeld .devacc_attr_version = DDI_DEVICE_ATTR_V0, 90*fd43cf6eSHans Rosenfeld .devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC, 91*fd43cf6eSHans Rosenfeld .devacc_attr_dataorder = DDI_STRICTORDER_ACC, 92*fd43cf6eSHans Rosenfeld .devacc_attr_access = DDI_DEFAULT_ACC 93*fd43cf6eSHans Rosenfeld }; 94*fd43cf6eSHans Rosenfeld 95*fd43cf6eSHans Rosenfeld /* 96*fd43cf6eSHans Rosenfeld * DMA access attributes for descriptor 97*fd43cf6eSHans Rosenfeld */ 98*fd43cf6eSHans Rosenfeld static ddi_device_acc_attr_t iwn_dma_descattr = { 99*fd43cf6eSHans Rosenfeld .devacc_attr_version = DDI_DEVICE_ATTR_V0, 100*fd43cf6eSHans Rosenfeld .devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC, 101*fd43cf6eSHans Rosenfeld .devacc_attr_dataorder = DDI_STRICTORDER_ACC, 102*fd43cf6eSHans Rosenfeld .devacc_attr_access = DDI_DEFAULT_ACC 103*fd43cf6eSHans Rosenfeld }; 104*fd43cf6eSHans Rosenfeld 105*fd43cf6eSHans Rosenfeld /* 106*fd43cf6eSHans Rosenfeld * DMA access attributes 107*fd43cf6eSHans Rosenfeld */ 108*fd43cf6eSHans Rosenfeld static ddi_device_acc_attr_t iwn_dma_accattr = { 109*fd43cf6eSHans Rosenfeld .devacc_attr_version = DDI_DEVICE_ATTR_V0, 110*fd43cf6eSHans Rosenfeld .devacc_attr_endian_flags = DDI_NEVERSWAP_ACC, 111*fd43cf6eSHans Rosenfeld .devacc_attr_dataorder = DDI_STRICTORDER_ACC, 112*fd43cf6eSHans Rosenfeld .devacc_attr_access = DDI_DEFAULT_ACC 113*fd43cf6eSHans Rosenfeld }; 114*fd43cf6eSHans Rosenfeld 115*fd43cf6eSHans Rosenfeld 116*fd43cf6eSHans Rosenfeld /* 117*fd43cf6eSHans Rosenfeld * Supported rates for 802.11a/b/g modes (in 500Kbps unit). 118*fd43cf6eSHans Rosenfeld */ 119*fd43cf6eSHans Rosenfeld static const struct ieee80211_rateset iwn_rateset_11a = 120*fd43cf6eSHans Rosenfeld { 8, { 12, 18, 24, 36, 48, 72, 96, 108 } }; 121*fd43cf6eSHans Rosenfeld 122*fd43cf6eSHans Rosenfeld static const struct ieee80211_rateset iwn_rateset_11b = 123*fd43cf6eSHans Rosenfeld { 4, { 2, 4, 11, 22 } }; 124*fd43cf6eSHans Rosenfeld 125*fd43cf6eSHans Rosenfeld static const struct ieee80211_rateset iwn_rateset_11g = 126*fd43cf6eSHans Rosenfeld { 12, { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 } }; 127*fd43cf6eSHans Rosenfeld 128*fd43cf6eSHans Rosenfeld static void iwn_kstat_create(struct iwn_softc *, const char *, size_t, 129*fd43cf6eSHans Rosenfeld kstat_t **, void **); 130*fd43cf6eSHans Rosenfeld static void iwn_kstat_free(kstat_t *, void *, size_t); 131*fd43cf6eSHans Rosenfeld static void iwn_kstat_init(struct iwn_softc *); 132*fd43cf6eSHans Rosenfeld static void iwn_kstat_init_2000(struct iwn_softc *); 133*fd43cf6eSHans Rosenfeld static void iwn_kstat_init_4965(struct iwn_softc *); 134*fd43cf6eSHans Rosenfeld static void iwn_kstat_init_6000(struct iwn_softc *); 135*fd43cf6eSHans Rosenfeld static void iwn_intr_teardown(struct iwn_softc *); 136*fd43cf6eSHans Rosenfeld static int iwn_intr_add(struct iwn_softc *, int); 137*fd43cf6eSHans Rosenfeld static int iwn_intr_setup(struct iwn_softc *); 138*fd43cf6eSHans Rosenfeld static int iwn_attach(dev_info_t *, ddi_attach_cmd_t); 139*fd43cf6eSHans Rosenfeld static int iwn4965_attach(struct iwn_softc *); 140*fd43cf6eSHans Rosenfeld static int iwn5000_attach(struct iwn_softc *, uint16_t); 141*fd43cf6eSHans Rosenfeld static int iwn_detach(dev_info_t *, ddi_detach_cmd_t); 142*fd43cf6eSHans Rosenfeld static int iwn_quiesce(dev_info_t *); 143*fd43cf6eSHans Rosenfeld static int iwn_nic_lock(struct iwn_softc *); 144*fd43cf6eSHans Rosenfeld static int iwn_eeprom_lock(struct iwn_softc *); 145*fd43cf6eSHans Rosenfeld static int iwn_init_otprom(struct iwn_softc *); 146*fd43cf6eSHans Rosenfeld static int iwn_read_prom_data(struct iwn_softc *, uint32_t, void *, int); 147*fd43cf6eSHans Rosenfeld static int iwn_dma_contig_alloc(struct iwn_softc *, struct iwn_dma_info *, 148*fd43cf6eSHans Rosenfeld uint_t, uint_t, void **, ddi_device_acc_attr_t *, uint_t); 149*fd43cf6eSHans Rosenfeld static void iwn_dma_contig_free(struct iwn_dma_info *); 150*fd43cf6eSHans Rosenfeld static int iwn_alloc_sched(struct iwn_softc *); 151*fd43cf6eSHans Rosenfeld static void iwn_free_sched(struct iwn_softc *); 152*fd43cf6eSHans Rosenfeld static int iwn_alloc_kw(struct iwn_softc *); 153*fd43cf6eSHans Rosenfeld static void iwn_free_kw(struct iwn_softc *); 154*fd43cf6eSHans Rosenfeld static int iwn_alloc_ict(struct iwn_softc *); 155*fd43cf6eSHans Rosenfeld static void iwn_free_ict(struct iwn_softc *); 156*fd43cf6eSHans Rosenfeld static int iwn_alloc_fwmem(struct iwn_softc *); 157*fd43cf6eSHans Rosenfeld static void iwn_free_fwmem(struct iwn_softc *); 158*fd43cf6eSHans Rosenfeld static int iwn_alloc_rx_ring(struct iwn_softc *, struct iwn_rx_ring *); 159*fd43cf6eSHans Rosenfeld static void iwn_reset_rx_ring(struct iwn_softc *, struct iwn_rx_ring *); 160*fd43cf6eSHans Rosenfeld static void iwn_free_rx_ring(struct iwn_softc *, struct iwn_rx_ring *); 161*fd43cf6eSHans Rosenfeld static int iwn_alloc_tx_ring(struct iwn_softc *, struct iwn_tx_ring *, 162*fd43cf6eSHans Rosenfeld int); 163*fd43cf6eSHans Rosenfeld static void iwn_reset_tx_ring(struct iwn_softc *, struct iwn_tx_ring *); 164*fd43cf6eSHans Rosenfeld static void iwn_free_tx_ring(struct iwn_softc *, struct iwn_tx_ring *); 165*fd43cf6eSHans Rosenfeld static void iwn5000_ict_reset(struct iwn_softc *); 166*fd43cf6eSHans Rosenfeld static int iwn_read_eeprom(struct iwn_softc *); 167*fd43cf6eSHans Rosenfeld static void iwn4965_read_eeprom(struct iwn_softc *); 168*fd43cf6eSHans Rosenfeld 169*fd43cf6eSHans Rosenfeld #ifdef IWN_DEBUG 170*fd43cf6eSHans Rosenfeld static void iwn4965_print_power_group(struct iwn_softc *, int); 171*fd43cf6eSHans Rosenfeld #endif 172*fd43cf6eSHans Rosenfeld static void iwn5000_read_eeprom(struct iwn_softc *); 173*fd43cf6eSHans Rosenfeld static void iwn_read_eeprom_channels(struct iwn_softc *, int, uint32_t); 174*fd43cf6eSHans Rosenfeld static void iwn_read_eeprom_enhinfo(struct iwn_softc *); 175*fd43cf6eSHans Rosenfeld static struct ieee80211_node *iwn_node_alloc(ieee80211com_t *); 176*fd43cf6eSHans Rosenfeld static void iwn_node_free(ieee80211_node_t *); 177*fd43cf6eSHans Rosenfeld static void iwn_newassoc(struct ieee80211_node *, int); 178*fd43cf6eSHans Rosenfeld static int iwn_newstate(struct ieee80211com *, enum ieee80211_state, int); 179*fd43cf6eSHans Rosenfeld static void iwn_iter_func(void *, struct ieee80211_node *); 180*fd43cf6eSHans Rosenfeld static void iwn_calib_timeout(void *); 181*fd43cf6eSHans Rosenfeld static void iwn_rx_phy(struct iwn_softc *, struct iwn_rx_desc *, 182*fd43cf6eSHans Rosenfeld struct iwn_rx_data *); 183*fd43cf6eSHans Rosenfeld static void iwn_rx_done(struct iwn_softc *, struct iwn_rx_desc *, 184*fd43cf6eSHans Rosenfeld struct iwn_rx_data *); 185*fd43cf6eSHans Rosenfeld #ifndef IEEE80211_NO_HT 186*fd43cf6eSHans Rosenfeld static void iwn_rx_compressed_ba(struct iwn_softc *, struct iwn_rx_desc *, 187*fd43cf6eSHans Rosenfeld struct iwn_rx_data *); 188*fd43cf6eSHans Rosenfeld #endif 189*fd43cf6eSHans Rosenfeld static void iwn5000_rx_calib_results(struct iwn_softc *, 190*fd43cf6eSHans Rosenfeld struct iwn_rx_desc *, struct iwn_rx_data *); 191*fd43cf6eSHans Rosenfeld static void iwn_rx_statistics(struct iwn_softc *, struct iwn_rx_desc *, 192*fd43cf6eSHans Rosenfeld struct iwn_rx_data *); 193*fd43cf6eSHans Rosenfeld static void iwn4965_tx_done(struct iwn_softc *, struct iwn_rx_desc *, 194*fd43cf6eSHans Rosenfeld struct iwn_rx_data *); 195*fd43cf6eSHans Rosenfeld static void iwn5000_tx_done(struct iwn_softc *, struct iwn_rx_desc *, 196*fd43cf6eSHans Rosenfeld struct iwn_rx_data *); 197*fd43cf6eSHans Rosenfeld static void iwn_tx_done(struct iwn_softc *, struct iwn_rx_desc *, int, 198*fd43cf6eSHans Rosenfeld uint8_t); 199*fd43cf6eSHans Rosenfeld static void iwn_cmd_done(struct iwn_softc *, struct iwn_rx_desc *); 200*fd43cf6eSHans Rosenfeld static void iwn_notif_intr(struct iwn_softc *); 201*fd43cf6eSHans Rosenfeld static void iwn_wakeup_intr(struct iwn_softc *); 202*fd43cf6eSHans Rosenfeld static void iwn_fatal_intr(struct iwn_softc *); 203*fd43cf6eSHans Rosenfeld static uint_t iwn_intr(caddr_t, caddr_t); 204*fd43cf6eSHans Rosenfeld static void iwn4965_update_sched(struct iwn_softc *, int, int, uint8_t, 205*fd43cf6eSHans Rosenfeld uint16_t); 206*fd43cf6eSHans Rosenfeld static void iwn5000_update_sched(struct iwn_softc *, int, int, uint8_t, 207*fd43cf6eSHans Rosenfeld uint16_t); 208*fd43cf6eSHans Rosenfeld #ifdef notyet 209*fd43cf6eSHans Rosenfeld static void iwn5000_reset_sched(struct iwn_softc *, int, int); 210*fd43cf6eSHans Rosenfeld #endif 211*fd43cf6eSHans Rosenfeld static int iwn_send(ieee80211com_t *, mblk_t *, uint8_t); 212*fd43cf6eSHans Rosenfeld static void iwn_watchdog(void *); 213*fd43cf6eSHans Rosenfeld static int iwn_cmd(struct iwn_softc *, uint8_t, void *, int, int); 214*fd43cf6eSHans Rosenfeld static int iwn4965_add_node(struct iwn_softc *, struct iwn_node_info *, 215*fd43cf6eSHans Rosenfeld int); 216*fd43cf6eSHans Rosenfeld static int iwn5000_add_node(struct iwn_softc *, struct iwn_node_info *, 217*fd43cf6eSHans Rosenfeld int); 218*fd43cf6eSHans Rosenfeld static int iwn_set_link_quality(struct iwn_softc *, 219*fd43cf6eSHans Rosenfeld struct ieee80211_node *); 220*fd43cf6eSHans Rosenfeld static int iwn_add_broadcast_node(struct iwn_softc *, int); 221*fd43cf6eSHans Rosenfeld static void iwn_set_led(struct iwn_softc *, uint8_t, uint8_t, uint8_t); 222*fd43cf6eSHans Rosenfeld static int iwn_set_critical_temp(struct iwn_softc *); 223*fd43cf6eSHans Rosenfeld static int iwn_set_timing(struct iwn_softc *, struct ieee80211_node *); 224*fd43cf6eSHans Rosenfeld static void iwn4965_power_calibration(struct iwn_softc *, int); 225*fd43cf6eSHans Rosenfeld static int iwn4965_set_txpower(struct iwn_softc *, int); 226*fd43cf6eSHans Rosenfeld static int iwn5000_set_txpower(struct iwn_softc *, int); 227*fd43cf6eSHans Rosenfeld static int iwn4965_get_rssi(const struct iwn_rx_stat *); 228*fd43cf6eSHans Rosenfeld static int iwn5000_get_rssi(const struct iwn_rx_stat *); 229*fd43cf6eSHans Rosenfeld static int iwn_get_noise(const struct iwn_rx_general_stats *); 230*fd43cf6eSHans Rosenfeld static int iwn4965_get_temperature(struct iwn_softc *); 231*fd43cf6eSHans Rosenfeld static int iwn5000_get_temperature(struct iwn_softc *); 232*fd43cf6eSHans Rosenfeld static int iwn_init_sensitivity(struct iwn_softc *); 233*fd43cf6eSHans Rosenfeld static void iwn_collect_noise(struct iwn_softc *, 234*fd43cf6eSHans Rosenfeld const struct iwn_rx_general_stats *); 235*fd43cf6eSHans Rosenfeld static int iwn4965_init_gains(struct iwn_softc *); 236*fd43cf6eSHans Rosenfeld static int iwn5000_init_gains(struct iwn_softc *); 237*fd43cf6eSHans Rosenfeld static int iwn4965_set_gains(struct iwn_softc *); 238*fd43cf6eSHans Rosenfeld static int iwn5000_set_gains(struct iwn_softc *); 239*fd43cf6eSHans Rosenfeld static void iwn_tune_sensitivity(struct iwn_softc *, 240*fd43cf6eSHans Rosenfeld const struct iwn_rx_stats *); 241*fd43cf6eSHans Rosenfeld static int iwn_send_sensitivity(struct iwn_softc *); 242*fd43cf6eSHans Rosenfeld static int iwn_set_pslevel(struct iwn_softc *, int, int, int); 243*fd43cf6eSHans Rosenfeld static int iwn5000_runtime_calib(struct iwn_softc *); 244*fd43cf6eSHans Rosenfeld 245*fd43cf6eSHans Rosenfeld static int iwn_config_bt_coex_bluetooth(struct iwn_softc *); 246*fd43cf6eSHans Rosenfeld static int iwn_config_bt_coex_prio_table(struct iwn_softc *); 247*fd43cf6eSHans Rosenfeld static int iwn_config_bt_coex_adv1(struct iwn_softc *); 248*fd43cf6eSHans Rosenfeld static int iwn_config_bt_coex_adv2(struct iwn_softc *); 249*fd43cf6eSHans Rosenfeld 250*fd43cf6eSHans Rosenfeld static int iwn_config(struct iwn_softc *); 251*fd43cf6eSHans Rosenfeld static uint16_t iwn_get_active_dwell_time(struct iwn_softc *, uint16_t, 252*fd43cf6eSHans Rosenfeld uint8_t); 253*fd43cf6eSHans Rosenfeld static uint16_t iwn_limit_dwell(struct iwn_softc *, uint16_t); 254*fd43cf6eSHans Rosenfeld static uint16_t iwn_get_passive_dwell_time(struct iwn_softc *, uint16_t); 255*fd43cf6eSHans Rosenfeld static int iwn_scan(struct iwn_softc *, uint16_t); 256*fd43cf6eSHans Rosenfeld static int iwn_auth(struct iwn_softc *); 257*fd43cf6eSHans Rosenfeld static int iwn_run(struct iwn_softc *); 258*fd43cf6eSHans Rosenfeld #ifdef IWN_HWCRYPTO 259*fd43cf6eSHans Rosenfeld static int iwn_set_key(struct ieee80211com *, struct ieee80211_node *, 260*fd43cf6eSHans Rosenfeld struct ieee80211_key *); 261*fd43cf6eSHans Rosenfeld static void iwn_delete_key(struct ieee80211com *, struct ieee80211_node *, 262*fd43cf6eSHans Rosenfeld struct ieee80211_key *); 263*fd43cf6eSHans Rosenfeld #endif 264*fd43cf6eSHans Rosenfeld static int iwn_wme_update(struct ieee80211com *); 265*fd43cf6eSHans Rosenfeld #ifndef IEEE80211_NO_HT 266*fd43cf6eSHans Rosenfeld static int iwn_ampdu_rx_start(struct ieee80211com *, 267*fd43cf6eSHans Rosenfeld struct ieee80211_node *, uint8_t); 268*fd43cf6eSHans Rosenfeld static void iwn_ampdu_rx_stop(struct ieee80211com *, 269*fd43cf6eSHans Rosenfeld struct ieee80211_node *, uint8_t); 270*fd43cf6eSHans Rosenfeld static int iwn_ampdu_tx_start(struct ieee80211com *, 271*fd43cf6eSHans Rosenfeld struct ieee80211_node *, uint8_t); 272*fd43cf6eSHans Rosenfeld static void iwn_ampdu_tx_stop(struct ieee80211com *, 273*fd43cf6eSHans Rosenfeld struct ieee80211_node *, uint8_t); 274*fd43cf6eSHans Rosenfeld static void iwn4965_ampdu_tx_start(struct iwn_softc *, 275*fd43cf6eSHans Rosenfeld struct ieee80211_node *, uint8_t, uint16_t); 276*fd43cf6eSHans Rosenfeld static void iwn4965_ampdu_tx_stop(struct iwn_softc *, 277*fd43cf6eSHans Rosenfeld uint8_t, uint16_t); 278*fd43cf6eSHans Rosenfeld static void iwn5000_ampdu_tx_start(struct iwn_softc *, 279*fd43cf6eSHans Rosenfeld struct ieee80211_node *, uint8_t, uint16_t); 280*fd43cf6eSHans Rosenfeld static void iwn5000_ampdu_tx_stop(struct iwn_softc *, 281*fd43cf6eSHans Rosenfeld uint8_t, uint16_t); 282*fd43cf6eSHans Rosenfeld #endif 283*fd43cf6eSHans Rosenfeld static int iwn5000_query_calibration(struct iwn_softc *); 284*fd43cf6eSHans Rosenfeld static int iwn5000_send_calibration(struct iwn_softc *); 285*fd43cf6eSHans Rosenfeld static int iwn5000_send_wimax_coex(struct iwn_softc *); 286*fd43cf6eSHans Rosenfeld static int iwn6000_temp_offset_calib(struct iwn_softc *); 287*fd43cf6eSHans Rosenfeld static int iwn2000_temp_offset_calib(struct iwn_softc *); 288*fd43cf6eSHans Rosenfeld static int iwn4965_post_alive(struct iwn_softc *); 289*fd43cf6eSHans Rosenfeld static int iwn5000_post_alive(struct iwn_softc *); 290*fd43cf6eSHans Rosenfeld static int iwn4965_load_bootcode(struct iwn_softc *, const uint8_t *, 291*fd43cf6eSHans Rosenfeld int); 292*fd43cf6eSHans Rosenfeld static int iwn4965_load_firmware(struct iwn_softc *); 293*fd43cf6eSHans Rosenfeld static int iwn5000_load_firmware_section(struct iwn_softc *, uint32_t, 294*fd43cf6eSHans Rosenfeld const uint8_t *, int); 295*fd43cf6eSHans Rosenfeld static int iwn5000_load_firmware(struct iwn_softc *); 296*fd43cf6eSHans Rosenfeld static int iwn_read_firmware_leg(struct iwn_softc *, 297*fd43cf6eSHans Rosenfeld struct iwn_fw_info *); 298*fd43cf6eSHans Rosenfeld static int iwn_read_firmware_tlv(struct iwn_softc *, 299*fd43cf6eSHans Rosenfeld struct iwn_fw_info *, uint16_t); 300*fd43cf6eSHans Rosenfeld static int iwn_read_firmware(struct iwn_softc *); 301*fd43cf6eSHans Rosenfeld static int iwn_clock_wait(struct iwn_softc *); 302*fd43cf6eSHans Rosenfeld static int iwn_apm_init(struct iwn_softc *); 303*fd43cf6eSHans Rosenfeld static void iwn_apm_stop_master(struct iwn_softc *); 304*fd43cf6eSHans Rosenfeld static void iwn_apm_stop(struct iwn_softc *); 305*fd43cf6eSHans Rosenfeld static int iwn4965_nic_config(struct iwn_softc *); 306*fd43cf6eSHans Rosenfeld static int iwn5000_nic_config(struct iwn_softc *); 307*fd43cf6eSHans Rosenfeld static int iwn_hw_prepare(struct iwn_softc *); 308*fd43cf6eSHans Rosenfeld static int iwn_hw_init(struct iwn_softc *); 309*fd43cf6eSHans Rosenfeld static void iwn_hw_stop(struct iwn_softc *, boolean_t); 310*fd43cf6eSHans Rosenfeld static int iwn_init(struct iwn_softc *); 311*fd43cf6eSHans Rosenfeld static void iwn_abort_scan(void *); 312*fd43cf6eSHans Rosenfeld static void iwn_periodic(void *); 313*fd43cf6eSHans Rosenfeld static int iwn_fast_recover(struct iwn_softc *); 314*fd43cf6eSHans Rosenfeld 315*fd43cf6eSHans Rosenfeld static uint8_t *ieee80211_add_ssid(uint8_t *, const uint8_t *, uint32_t); 316*fd43cf6eSHans Rosenfeld static uint8_t *ieee80211_add_rates(uint8_t *, 317*fd43cf6eSHans Rosenfeld const struct ieee80211_rateset *); 318*fd43cf6eSHans Rosenfeld static uint8_t *ieee80211_add_xrates(uint8_t *, 319*fd43cf6eSHans Rosenfeld const struct ieee80211_rateset *); 320*fd43cf6eSHans Rosenfeld 321*fd43cf6eSHans Rosenfeld static void iwn_fix_channel(struct iwn_softc *, mblk_t *, 322*fd43cf6eSHans Rosenfeld struct iwn_rx_stat *); 323*fd43cf6eSHans Rosenfeld 324*fd43cf6eSHans Rosenfeld #ifdef IWN_DEBUG 325*fd43cf6eSHans Rosenfeld 326*fd43cf6eSHans Rosenfeld #define IWN_DBG(...) iwn_dbg("?" __VA_ARGS__) 327*fd43cf6eSHans Rosenfeld 328*fd43cf6eSHans Rosenfeld static int iwn_dbg_print = 0; 329*fd43cf6eSHans Rosenfeld 330*fd43cf6eSHans Rosenfeld static void 331*fd43cf6eSHans Rosenfeld iwn_dbg(const char *fmt, ...) 332*fd43cf6eSHans Rosenfeld { 333*fd43cf6eSHans Rosenfeld va_list ap; 334*fd43cf6eSHans Rosenfeld 335*fd43cf6eSHans Rosenfeld if (iwn_dbg_print != 0) { 336*fd43cf6eSHans Rosenfeld va_start(ap, fmt); 337*fd43cf6eSHans Rosenfeld vcmn_err(CE_CONT, fmt, ap); 338*fd43cf6eSHans Rosenfeld va_end(ap); 339*fd43cf6eSHans Rosenfeld } 340*fd43cf6eSHans Rosenfeld } 341*fd43cf6eSHans Rosenfeld 342*fd43cf6eSHans Rosenfeld #else 343*fd43cf6eSHans Rosenfeld #define IWN_DBG(...) 344*fd43cf6eSHans Rosenfeld #endif 345*fd43cf6eSHans Rosenfeld 346*fd43cf6eSHans Rosenfeld /* 347*fd43cf6eSHans Rosenfeld * tunables 348*fd43cf6eSHans Rosenfeld */ 349*fd43cf6eSHans Rosenfeld 350*fd43cf6eSHans Rosenfeld /* 351*fd43cf6eSHans Rosenfeld * enable 5GHz scanning 352*fd43cf6eSHans Rosenfeld */ 353*fd43cf6eSHans Rosenfeld int iwn_enable_5ghz = 1; 354*fd43cf6eSHans Rosenfeld 355*fd43cf6eSHans Rosenfeld /* 356*fd43cf6eSHans Rosenfeld * If more than 50 consecutive beacons are missed, 357*fd43cf6eSHans Rosenfeld * we've probably lost our connection. 358*fd43cf6eSHans Rosenfeld * If more than 5 consecutive beacons are missed, 359*fd43cf6eSHans Rosenfeld * reinitialize the sensitivity state machine. 360*fd43cf6eSHans Rosenfeld */ 361*fd43cf6eSHans Rosenfeld int iwn_beacons_missed_disconnect = 50; 362*fd43cf6eSHans Rosenfeld int iwn_beacons_missed_sensitivity = 5; 363*fd43cf6eSHans Rosenfeld 364*fd43cf6eSHans Rosenfeld /* 365*fd43cf6eSHans Rosenfeld * iwn_periodic interval, in units of msec 366*fd43cf6eSHans Rosenfeld */ 367*fd43cf6eSHans Rosenfeld int iwn_periodic_interval = 100; 368*fd43cf6eSHans Rosenfeld 369*fd43cf6eSHans Rosenfeld /* 370*fd43cf6eSHans Rosenfeld * scan timeout in sec 371*fd43cf6eSHans Rosenfeld */ 372*fd43cf6eSHans Rosenfeld int iwn_scan_timeout = 20; 373*fd43cf6eSHans Rosenfeld 374*fd43cf6eSHans Rosenfeld static ether_addr_t etherbroadcastaddr = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; 375*fd43cf6eSHans Rosenfeld 376*fd43cf6eSHans Rosenfeld static void *iwn_state = NULL; 377*fd43cf6eSHans Rosenfeld 378*fd43cf6eSHans Rosenfeld /* 379*fd43cf6eSHans Rosenfeld * Mac Call Back entries 380*fd43cf6eSHans Rosenfeld */ 381*fd43cf6eSHans Rosenfeld static int iwn_m_stat(void *, uint_t, uint64_t *); 382*fd43cf6eSHans Rosenfeld static int iwn_m_start(void *); 383*fd43cf6eSHans Rosenfeld static void iwn_m_stop(void *); 384*fd43cf6eSHans Rosenfeld static int iwn_m_unicst(void *, const uint8_t *); 385*fd43cf6eSHans Rosenfeld static int iwn_m_multicst(void *, boolean_t, const uint8_t *); 386*fd43cf6eSHans Rosenfeld static int iwn_m_promisc(void *, boolean_t); 387*fd43cf6eSHans Rosenfeld static mblk_t *iwn_m_tx(void *, mblk_t *); 388*fd43cf6eSHans Rosenfeld static void iwn_m_ioctl(void *, queue_t *, mblk_t *); 389*fd43cf6eSHans Rosenfeld static int iwn_m_setprop(void *, const char *, mac_prop_id_t, uint_t, 390*fd43cf6eSHans Rosenfeld const void *); 391*fd43cf6eSHans Rosenfeld static int iwn_m_getprop(void *, const char *, mac_prop_id_t, uint_t, 392*fd43cf6eSHans Rosenfeld void *); 393*fd43cf6eSHans Rosenfeld static void iwn_m_propinfo(void *, const char *, mac_prop_id_t, 394*fd43cf6eSHans Rosenfeld mac_prop_info_handle_t); 395*fd43cf6eSHans Rosenfeld 396*fd43cf6eSHans Rosenfeld mac_callbacks_t iwn_m_callbacks = { 397*fd43cf6eSHans Rosenfeld .mc_callbacks = MC_IOCTL | MC_SETPROP | MC_GETPROP | MC_PROPINFO, 398*fd43cf6eSHans Rosenfeld .mc_getstat = iwn_m_stat, 399*fd43cf6eSHans Rosenfeld .mc_start = iwn_m_start, 400*fd43cf6eSHans Rosenfeld .mc_stop = iwn_m_stop, 401*fd43cf6eSHans Rosenfeld .mc_setpromisc = iwn_m_promisc, 402*fd43cf6eSHans Rosenfeld .mc_multicst = iwn_m_multicst, 403*fd43cf6eSHans Rosenfeld .mc_unicst = iwn_m_unicst, 404*fd43cf6eSHans Rosenfeld .mc_tx = iwn_m_tx, 405*fd43cf6eSHans Rosenfeld .mc_reserved = NULL, 406*fd43cf6eSHans Rosenfeld .mc_ioctl = iwn_m_ioctl, 407*fd43cf6eSHans Rosenfeld .mc_getcapab = NULL, 408*fd43cf6eSHans Rosenfeld .mc_open = NULL, 409*fd43cf6eSHans Rosenfeld .mc_close = NULL, 410*fd43cf6eSHans Rosenfeld .mc_setprop = iwn_m_setprop, 411*fd43cf6eSHans Rosenfeld .mc_getprop = iwn_m_getprop, 412*fd43cf6eSHans Rosenfeld .mc_propinfo = iwn_m_propinfo 413*fd43cf6eSHans Rosenfeld }; 414*fd43cf6eSHans Rosenfeld 415*fd43cf6eSHans Rosenfeld static inline uint32_t 416*fd43cf6eSHans Rosenfeld iwn_read(struct iwn_softc *sc, int reg) 417*fd43cf6eSHans Rosenfeld { 418*fd43cf6eSHans Rosenfeld /*LINTED: E_PTR_BAD_CAST_ALIGN*/ 419*fd43cf6eSHans Rosenfeld return (ddi_get32(sc->sc_regh, (uint32_t *)(sc->sc_base + reg))); 420*fd43cf6eSHans Rosenfeld } 421*fd43cf6eSHans Rosenfeld 422*fd43cf6eSHans Rosenfeld static inline void 423*fd43cf6eSHans Rosenfeld iwn_write(struct iwn_softc *sc, int reg, uint32_t val) 424*fd43cf6eSHans Rosenfeld { 425*fd43cf6eSHans Rosenfeld /*LINTED: E_PTR_BAD_CAST_ALIGN*/ 426*fd43cf6eSHans Rosenfeld ddi_put32(sc->sc_regh, (uint32_t *)(sc->sc_base + reg), val); 427*fd43cf6eSHans Rosenfeld } 428*fd43cf6eSHans Rosenfeld 429*fd43cf6eSHans Rosenfeld static inline void 430*fd43cf6eSHans Rosenfeld iwn_write_1(struct iwn_softc *sc, int reg, uint8_t val) 431*fd43cf6eSHans Rosenfeld { 432*fd43cf6eSHans Rosenfeld ddi_put8(sc->sc_regh, (uint8_t *)(sc->sc_base + reg), val); 433*fd43cf6eSHans Rosenfeld } 434*fd43cf6eSHans Rosenfeld 435*fd43cf6eSHans Rosenfeld static void 436*fd43cf6eSHans Rosenfeld iwn_kstat_create(struct iwn_softc *sc, const char *name, size_t size, 437*fd43cf6eSHans Rosenfeld kstat_t **ks, void **data) 438*fd43cf6eSHans Rosenfeld { 439*fd43cf6eSHans Rosenfeld *ks = kstat_create(ddi_driver_name(sc->sc_dip), 440*fd43cf6eSHans Rosenfeld ddi_get_instance(sc->sc_dip), name, "misc", KSTAT_TYPE_NAMED, 441*fd43cf6eSHans Rosenfeld size / sizeof (kstat_named_t), 0); 442*fd43cf6eSHans Rosenfeld if (*ks == NULL) 443*fd43cf6eSHans Rosenfeld *data = kmem_zalloc(size, KM_SLEEP); 444*fd43cf6eSHans Rosenfeld else 445*fd43cf6eSHans Rosenfeld *data = (*ks)->ks_data; 446*fd43cf6eSHans Rosenfeld } 447*fd43cf6eSHans Rosenfeld 448*fd43cf6eSHans Rosenfeld static void 449*fd43cf6eSHans Rosenfeld iwn_kstat_free(kstat_t *ks, void *data, size_t size) 450*fd43cf6eSHans Rosenfeld { 451*fd43cf6eSHans Rosenfeld if (ks != NULL) 452*fd43cf6eSHans Rosenfeld kstat_delete(ks); 453*fd43cf6eSHans Rosenfeld else if (data != NULL) 454*fd43cf6eSHans Rosenfeld kmem_free(data, size); 455*fd43cf6eSHans Rosenfeld } 456*fd43cf6eSHans Rosenfeld 457*fd43cf6eSHans Rosenfeld static void 458*fd43cf6eSHans Rosenfeld iwn_kstat_init(struct iwn_softc *sc) 459*fd43cf6eSHans Rosenfeld { 460*fd43cf6eSHans Rosenfeld if (sc->sc_ks_misc != NULL) 461*fd43cf6eSHans Rosenfeld sc->sc_ks_misc->ks_lock = &sc->sc_mtx; 462*fd43cf6eSHans Rosenfeld if (sc->sc_ks_ant != NULL) 463*fd43cf6eSHans Rosenfeld sc->sc_ks_ant->ks_lock = &sc->sc_mtx; 464*fd43cf6eSHans Rosenfeld if (sc->sc_ks_sens != NULL) 465*fd43cf6eSHans Rosenfeld sc->sc_ks_sens->ks_lock = &sc->sc_mtx; 466*fd43cf6eSHans Rosenfeld if (sc->sc_ks_timing != NULL) 467*fd43cf6eSHans Rosenfeld sc->sc_ks_timing->ks_lock = &sc->sc_mtx; 468*fd43cf6eSHans Rosenfeld if (sc->sc_ks_edca != NULL) 469*fd43cf6eSHans Rosenfeld sc->sc_ks_edca->ks_lock = &sc->sc_mtx; 470*fd43cf6eSHans Rosenfeld 471*fd43cf6eSHans Rosenfeld kstat_named_init(&sc->sc_misc->temp, 472*fd43cf6eSHans Rosenfeld "temperature", KSTAT_DATA_ULONG); 473*fd43cf6eSHans Rosenfeld kstat_named_init(&sc->sc_misc->crit_temp, 474*fd43cf6eSHans Rosenfeld "critical temperature", KSTAT_DATA_ULONG); 475*fd43cf6eSHans Rosenfeld kstat_named_init(&sc->sc_misc->pslevel, 476*fd43cf6eSHans Rosenfeld "power saving level", KSTAT_DATA_ULONG); 477*fd43cf6eSHans Rosenfeld kstat_named_init(&sc->sc_misc->noise, 478*fd43cf6eSHans Rosenfeld "noise", KSTAT_DATA_LONG); 479*fd43cf6eSHans Rosenfeld 480*fd43cf6eSHans Rosenfeld 481*fd43cf6eSHans Rosenfeld kstat_named_init(&sc->sc_ant->tx_ant, 482*fd43cf6eSHans Rosenfeld "TX mask", KSTAT_DATA_ULONG); 483*fd43cf6eSHans Rosenfeld kstat_named_init(&sc->sc_ant->rx_ant, 484*fd43cf6eSHans Rosenfeld "RX mask", KSTAT_DATA_ULONG); 485*fd43cf6eSHans Rosenfeld kstat_named_init(&sc->sc_ant->conn_ant, 486*fd43cf6eSHans Rosenfeld "connected mask", KSTAT_DATA_ULONG); 487*fd43cf6eSHans Rosenfeld kstat_named_init(&sc->sc_ant->gain[0], 488*fd43cf6eSHans Rosenfeld "gain A", KSTAT_DATA_ULONG); 489*fd43cf6eSHans Rosenfeld kstat_named_init(&sc->sc_ant->gain[1], 490*fd43cf6eSHans Rosenfeld "gain B", KSTAT_DATA_ULONG); 491*fd43cf6eSHans Rosenfeld kstat_named_init(&sc->sc_ant->gain[2], 492*fd43cf6eSHans Rosenfeld "gain C", KSTAT_DATA_ULONG); 493*fd43cf6eSHans Rosenfeld 494*fd43cf6eSHans Rosenfeld kstat_named_init(&sc->sc_sens->ofdm_x1, 495*fd43cf6eSHans Rosenfeld "OFDM X1", KSTAT_DATA_ULONG); 496*fd43cf6eSHans Rosenfeld kstat_named_init(&sc->sc_sens->ofdm_mrc_x1, 497*fd43cf6eSHans Rosenfeld "OFDM MRC X1", KSTAT_DATA_ULONG); 498*fd43cf6eSHans Rosenfeld kstat_named_init(&sc->sc_sens->ofdm_x4, 499*fd43cf6eSHans Rosenfeld "OFDM X4", KSTAT_DATA_ULONG); 500*fd43cf6eSHans Rosenfeld kstat_named_init(&sc->sc_sens->ofdm_mrc_x4, 501*fd43cf6eSHans Rosenfeld "OFDM MRC X4", KSTAT_DATA_ULONG); 502*fd43cf6eSHans Rosenfeld kstat_named_init(&sc->sc_sens->cck_x4, 503*fd43cf6eSHans Rosenfeld "CCK X4", KSTAT_DATA_ULONG); 504*fd43cf6eSHans Rosenfeld kstat_named_init(&sc->sc_sens->cck_mrc_x4, 505*fd43cf6eSHans Rosenfeld "CCK MRC X4", KSTAT_DATA_ULONG); 506*fd43cf6eSHans Rosenfeld kstat_named_init(&sc->sc_sens->energy_cck, 507*fd43cf6eSHans Rosenfeld "energy CCK", KSTAT_DATA_ULONG); 508*fd43cf6eSHans Rosenfeld 509*fd43cf6eSHans Rosenfeld kstat_named_init(&sc->sc_timing->bintval, 510*fd43cf6eSHans Rosenfeld "bintval", KSTAT_DATA_ULONG); 511*fd43cf6eSHans Rosenfeld kstat_named_init(&sc->sc_timing->tstamp, 512*fd43cf6eSHans Rosenfeld "timestamp", KSTAT_DATA_ULONGLONG); 513*fd43cf6eSHans Rosenfeld kstat_named_init(&sc->sc_timing->init, 514*fd43cf6eSHans Rosenfeld "init", KSTAT_DATA_ULONG); 515*fd43cf6eSHans Rosenfeld 516*fd43cf6eSHans Rosenfeld kstat_named_init(&sc->sc_edca->ac[0].cwmin, 517*fd43cf6eSHans Rosenfeld "background cwmin", KSTAT_DATA_ULONG); 518*fd43cf6eSHans Rosenfeld kstat_named_init(&sc->sc_edca->ac[0].cwmax, 519*fd43cf6eSHans Rosenfeld "background cwmax", KSTAT_DATA_ULONG); 520*fd43cf6eSHans Rosenfeld kstat_named_init(&sc->sc_edca->ac[0].aifsn, 521*fd43cf6eSHans Rosenfeld "background aifsn", KSTAT_DATA_ULONG); 522*fd43cf6eSHans Rosenfeld kstat_named_init(&sc->sc_edca->ac[0].txop, 523*fd43cf6eSHans Rosenfeld "background txop", KSTAT_DATA_ULONG); 524*fd43cf6eSHans Rosenfeld kstat_named_init(&sc->sc_edca->ac[1].cwmin, 525*fd43cf6eSHans Rosenfeld "best effort cwmin", KSTAT_DATA_ULONG); 526*fd43cf6eSHans Rosenfeld kstat_named_init(&sc->sc_edca->ac[1].cwmax, 527*fd43cf6eSHans Rosenfeld "best effort cwmax", KSTAT_DATA_ULONG); 528*fd43cf6eSHans Rosenfeld kstat_named_init(&sc->sc_edca->ac[1].aifsn, 529*fd43cf6eSHans Rosenfeld "best effort aifsn", KSTAT_DATA_ULONG); 530*fd43cf6eSHans Rosenfeld kstat_named_init(&sc->sc_edca->ac[1].txop, 531*fd43cf6eSHans Rosenfeld "best effort txop", KSTAT_DATA_ULONG); 532*fd43cf6eSHans Rosenfeld kstat_named_init(&sc->sc_edca->ac[2].cwmin, 533*fd43cf6eSHans Rosenfeld "video cwmin", KSTAT_DATA_ULONG); 534*fd43cf6eSHans Rosenfeld kstat_named_init(&sc->sc_edca->ac[2].cwmax, 535*fd43cf6eSHans Rosenfeld "video cwmax", KSTAT_DATA_ULONG); 536*fd43cf6eSHans Rosenfeld kstat_named_init(&sc->sc_edca->ac[2].aifsn, 537*fd43cf6eSHans Rosenfeld "video aifsn", KSTAT_DATA_ULONG); 538*fd43cf6eSHans Rosenfeld kstat_named_init(&sc->sc_edca->ac[2].txop, 539*fd43cf6eSHans Rosenfeld "video txop", KSTAT_DATA_ULONG); 540*fd43cf6eSHans Rosenfeld kstat_named_init(&sc->sc_edca->ac[3].cwmin, 541*fd43cf6eSHans Rosenfeld "voice cwmin", KSTAT_DATA_ULONG); 542*fd43cf6eSHans Rosenfeld kstat_named_init(&sc->sc_edca->ac[3].cwmax, 543*fd43cf6eSHans Rosenfeld "voice cwmax", KSTAT_DATA_ULONG); 544*fd43cf6eSHans Rosenfeld kstat_named_init(&sc->sc_edca->ac[3].aifsn, 545*fd43cf6eSHans Rosenfeld "voice aifsn", KSTAT_DATA_ULONG); 546*fd43cf6eSHans Rosenfeld kstat_named_init(&sc->sc_edca->ac[3].txop, 547*fd43cf6eSHans Rosenfeld "voice txop", KSTAT_DATA_ULONG); 548*fd43cf6eSHans Rosenfeld } 549*fd43cf6eSHans Rosenfeld 550*fd43cf6eSHans Rosenfeld static void 551*fd43cf6eSHans Rosenfeld iwn_kstat_init_2000(struct iwn_softc *sc) 552*fd43cf6eSHans Rosenfeld { 553*fd43cf6eSHans Rosenfeld if (sc->sc_ks_toff != NULL) 554*fd43cf6eSHans Rosenfeld sc->sc_ks_toff->ks_lock = &sc->sc_mtx; 555*fd43cf6eSHans Rosenfeld 556*fd43cf6eSHans Rosenfeld kstat_named_init(&sc->sc_toff.t2000->toff_lo, 557*fd43cf6eSHans Rosenfeld "temperature offset low", KSTAT_DATA_LONG); 558*fd43cf6eSHans Rosenfeld kstat_named_init(&sc->sc_toff.t2000->toff_hi, 559*fd43cf6eSHans Rosenfeld "temperature offset high", KSTAT_DATA_LONG); 560*fd43cf6eSHans Rosenfeld kstat_named_init(&sc->sc_toff.t2000->volt, 561*fd43cf6eSHans Rosenfeld "reference voltage", KSTAT_DATA_LONG); 562*fd43cf6eSHans Rosenfeld } 563*fd43cf6eSHans Rosenfeld 564*fd43cf6eSHans Rosenfeld static void 565*fd43cf6eSHans Rosenfeld iwn_kstat_init_4965(struct iwn_softc *sc) 566*fd43cf6eSHans Rosenfeld { 567*fd43cf6eSHans Rosenfeld int i, r; 568*fd43cf6eSHans Rosenfeld 569*fd43cf6eSHans Rosenfeld if (sc->sc_ks_txpower != NULL) 570*fd43cf6eSHans Rosenfeld sc->sc_ks_txpower->ks_lock = &sc->sc_mtx; 571*fd43cf6eSHans Rosenfeld 572*fd43cf6eSHans Rosenfeld kstat_named_init(&sc->sc_txpower->vdiff, 573*fd43cf6eSHans Rosenfeld "voltage comp", KSTAT_DATA_LONG); 574*fd43cf6eSHans Rosenfeld kstat_named_init(&sc->sc_txpower->chan, 575*fd43cf6eSHans Rosenfeld "channel", KSTAT_DATA_LONG); 576*fd43cf6eSHans Rosenfeld kstat_named_init(&sc->sc_txpower->group, 577*fd43cf6eSHans Rosenfeld "attenuation group", KSTAT_DATA_LONG); 578*fd43cf6eSHans Rosenfeld kstat_named_init(&sc->sc_txpower->subband, 579*fd43cf6eSHans Rosenfeld "sub-band", KSTAT_DATA_LONG); 580*fd43cf6eSHans Rosenfeld for (i = 0; i != 2; i++) { 581*fd43cf6eSHans Rosenfeld char tmp[KSTAT_STRLEN]; 582*fd43cf6eSHans Rosenfeld 583*fd43cf6eSHans Rosenfeld (void) snprintf(tmp, KSTAT_STRLEN - 1, "Ant %d power", i); 584*fd43cf6eSHans Rosenfeld kstat_named_init(&sc->sc_txpower->txchain[i].power, 585*fd43cf6eSHans Rosenfeld tmp, KSTAT_DATA_LONG); 586*fd43cf6eSHans Rosenfeld 587*fd43cf6eSHans Rosenfeld (void) snprintf(tmp, KSTAT_STRLEN - 1, "Ant %d gain", i); 588*fd43cf6eSHans Rosenfeld kstat_named_init(&sc->sc_txpower->txchain[i].gain, 589*fd43cf6eSHans Rosenfeld tmp, KSTAT_DATA_LONG); 590*fd43cf6eSHans Rosenfeld 591*fd43cf6eSHans Rosenfeld (void) snprintf(tmp, KSTAT_STRLEN - 1, "Ant %d temperature", i); 592*fd43cf6eSHans Rosenfeld kstat_named_init(&sc->sc_txpower->txchain[i].temp, 593*fd43cf6eSHans Rosenfeld tmp, KSTAT_DATA_LONG); 594*fd43cf6eSHans Rosenfeld 595*fd43cf6eSHans Rosenfeld (void) snprintf(tmp, KSTAT_STRLEN - 1, 596*fd43cf6eSHans Rosenfeld "Ant %d temperature compensation", i); 597*fd43cf6eSHans Rosenfeld kstat_named_init(&sc->sc_txpower->txchain[i].tcomp, 598*fd43cf6eSHans Rosenfeld tmp, KSTAT_DATA_LONG); 599*fd43cf6eSHans Rosenfeld 600*fd43cf6eSHans Rosenfeld for (r = 0; r != IWN_RIDX_MAX; r++) { 601*fd43cf6eSHans Rosenfeld (void) snprintf(tmp, KSTAT_STRLEN - 1, 602*fd43cf6eSHans Rosenfeld "Ant %d Rate %d RF gain", i, r); 603*fd43cf6eSHans Rosenfeld kstat_named_init( 604*fd43cf6eSHans Rosenfeld &sc->sc_txpower->txchain[i].rate[r].rf_gain, 605*fd43cf6eSHans Rosenfeld tmp, KSTAT_DATA_LONG); 606*fd43cf6eSHans Rosenfeld 607*fd43cf6eSHans Rosenfeld (void) snprintf(tmp, KSTAT_STRLEN - 1, 608*fd43cf6eSHans Rosenfeld "Ant %d Rate %d DSP gain", i, r); 609*fd43cf6eSHans Rosenfeld kstat_named_init( 610*fd43cf6eSHans Rosenfeld &sc->sc_txpower->txchain[0].rate[0].dsp_gain, 611*fd43cf6eSHans Rosenfeld tmp, KSTAT_DATA_LONG); 612*fd43cf6eSHans Rosenfeld } 613*fd43cf6eSHans Rosenfeld } 614*fd43cf6eSHans Rosenfeld } 615*fd43cf6eSHans Rosenfeld 616*fd43cf6eSHans Rosenfeld static void 617*fd43cf6eSHans Rosenfeld iwn_kstat_init_6000(struct iwn_softc *sc) 618*fd43cf6eSHans Rosenfeld { 619*fd43cf6eSHans Rosenfeld if (sc->sc_ks_toff != NULL) 620*fd43cf6eSHans Rosenfeld sc->sc_ks_toff->ks_lock = &sc->sc_mtx; 621*fd43cf6eSHans Rosenfeld 622*fd43cf6eSHans Rosenfeld kstat_named_init(&sc->sc_toff.t6000->toff, 623*fd43cf6eSHans Rosenfeld "temperature offset", KSTAT_DATA_LONG); 624*fd43cf6eSHans Rosenfeld } 625*fd43cf6eSHans Rosenfeld 626*fd43cf6eSHans Rosenfeld static void 627*fd43cf6eSHans Rosenfeld iwn_intr_teardown(struct iwn_softc *sc) 628*fd43cf6eSHans Rosenfeld { 629*fd43cf6eSHans Rosenfeld if (sc->sc_intr_htable != NULL) { 630*fd43cf6eSHans Rosenfeld if ((sc->sc_intr_cap & DDI_INTR_FLAG_BLOCK) != 0) { 631*fd43cf6eSHans Rosenfeld (void) ddi_intr_block_disable(sc->sc_intr_htable, 632*fd43cf6eSHans Rosenfeld sc->sc_intr_count); 633*fd43cf6eSHans Rosenfeld } else { 634*fd43cf6eSHans Rosenfeld (void) ddi_intr_disable(sc->sc_intr_htable[0]); 635*fd43cf6eSHans Rosenfeld } 636*fd43cf6eSHans Rosenfeld (void) ddi_intr_remove_handler(sc->sc_intr_htable[0]); 637*fd43cf6eSHans Rosenfeld (void) ddi_intr_free(sc->sc_intr_htable[0]); 638*fd43cf6eSHans Rosenfeld sc->sc_intr_htable[0] = NULL; 639*fd43cf6eSHans Rosenfeld 640*fd43cf6eSHans Rosenfeld kmem_free(sc->sc_intr_htable, sc->sc_intr_size); 641*fd43cf6eSHans Rosenfeld sc->sc_intr_size = 0; 642*fd43cf6eSHans Rosenfeld sc->sc_intr_htable = NULL; 643*fd43cf6eSHans Rosenfeld } 644*fd43cf6eSHans Rosenfeld } 645*fd43cf6eSHans Rosenfeld 646*fd43cf6eSHans Rosenfeld static int 647*fd43cf6eSHans Rosenfeld iwn_intr_add(struct iwn_softc *sc, int intr_type) 648*fd43cf6eSHans Rosenfeld { 649*fd43cf6eSHans Rosenfeld int ni, na; 650*fd43cf6eSHans Rosenfeld int ret; 651*fd43cf6eSHans Rosenfeld char *func; 652*fd43cf6eSHans Rosenfeld 653*fd43cf6eSHans Rosenfeld if (ddi_intr_get_nintrs(sc->sc_dip, intr_type, &ni) != DDI_SUCCESS) 654*fd43cf6eSHans Rosenfeld return (DDI_FAILURE); 655*fd43cf6eSHans Rosenfeld 656*fd43cf6eSHans Rosenfeld 657*fd43cf6eSHans Rosenfeld if (ddi_intr_get_navail(sc->sc_dip, intr_type, &na) != DDI_SUCCESS) 658*fd43cf6eSHans Rosenfeld return (DDI_FAILURE); 659*fd43cf6eSHans Rosenfeld 660*fd43cf6eSHans Rosenfeld sc->sc_intr_size = sizeof (ddi_intr_handle_t); 661*fd43cf6eSHans Rosenfeld sc->sc_intr_htable = kmem_zalloc(sc->sc_intr_size, KM_SLEEP); 662*fd43cf6eSHans Rosenfeld 663*fd43cf6eSHans Rosenfeld ret = ddi_intr_alloc(sc->sc_dip, sc->sc_intr_htable, intr_type, 0, 1, 664*fd43cf6eSHans Rosenfeld &sc->sc_intr_count, DDI_INTR_ALLOC_STRICT); 665*fd43cf6eSHans Rosenfeld if (ret != DDI_SUCCESS) { 666*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, "!ddi_intr_alloc() failed"); 667*fd43cf6eSHans Rosenfeld return (DDI_FAILURE); 668*fd43cf6eSHans Rosenfeld } 669*fd43cf6eSHans Rosenfeld 670*fd43cf6eSHans Rosenfeld ret = ddi_intr_get_pri(sc->sc_intr_htable[0], &sc->sc_intr_pri); 671*fd43cf6eSHans Rosenfeld if (ret != DDI_SUCCESS) { 672*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, "!ddi_intr_get_pri() failed"); 673*fd43cf6eSHans Rosenfeld return (DDI_FAILURE); 674*fd43cf6eSHans Rosenfeld } 675*fd43cf6eSHans Rosenfeld 676*fd43cf6eSHans Rosenfeld ret = ddi_intr_add_handler(sc->sc_intr_htable[0], iwn_intr, (caddr_t)sc, 677*fd43cf6eSHans Rosenfeld NULL); 678*fd43cf6eSHans Rosenfeld if (ret != DDI_SUCCESS) { 679*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, "!ddi_intr_add_handler() failed"); 680*fd43cf6eSHans Rosenfeld return (DDI_FAILURE); 681*fd43cf6eSHans Rosenfeld } 682*fd43cf6eSHans Rosenfeld 683*fd43cf6eSHans Rosenfeld ret = ddi_intr_get_cap(sc->sc_intr_htable[0], &sc->sc_intr_cap); 684*fd43cf6eSHans Rosenfeld if (ret != DDI_SUCCESS) { 685*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, "!ddi_intr_get_cap() failed"); 686*fd43cf6eSHans Rosenfeld return (DDI_FAILURE); 687*fd43cf6eSHans Rosenfeld } 688*fd43cf6eSHans Rosenfeld 689*fd43cf6eSHans Rosenfeld if ((sc->sc_intr_cap & DDI_INTR_FLAG_BLOCK) != 0) { 690*fd43cf6eSHans Rosenfeld ret = ddi_intr_block_enable(sc->sc_intr_htable, 691*fd43cf6eSHans Rosenfeld sc->sc_intr_count); 692*fd43cf6eSHans Rosenfeld func = "ddi_intr_enable_block"; 693*fd43cf6eSHans Rosenfeld } else { 694*fd43cf6eSHans Rosenfeld ret = ddi_intr_enable(sc->sc_intr_htable[0]); 695*fd43cf6eSHans Rosenfeld func = "ddi_intr_enable"; 696*fd43cf6eSHans Rosenfeld } 697*fd43cf6eSHans Rosenfeld 698*fd43cf6eSHans Rosenfeld if (ret != DDI_SUCCESS) { 699*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, "!%s() failed", func); 700*fd43cf6eSHans Rosenfeld return (DDI_FAILURE); 701*fd43cf6eSHans Rosenfeld } 702*fd43cf6eSHans Rosenfeld 703*fd43cf6eSHans Rosenfeld return (DDI_SUCCESS); 704*fd43cf6eSHans Rosenfeld } 705*fd43cf6eSHans Rosenfeld 706*fd43cf6eSHans Rosenfeld static int 707*fd43cf6eSHans Rosenfeld iwn_intr_setup(struct iwn_softc *sc) 708*fd43cf6eSHans Rosenfeld { 709*fd43cf6eSHans Rosenfeld int intr_type; 710*fd43cf6eSHans Rosenfeld int ret; 711*fd43cf6eSHans Rosenfeld 712*fd43cf6eSHans Rosenfeld ret = ddi_intr_get_supported_types(sc->sc_dip, &intr_type); 713*fd43cf6eSHans Rosenfeld if (ret != DDI_SUCCESS) { 714*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 715*fd43cf6eSHans Rosenfeld "!ddi_intr_get_supported_types() failed"); 716*fd43cf6eSHans Rosenfeld return (DDI_FAILURE); 717*fd43cf6eSHans Rosenfeld } 718*fd43cf6eSHans Rosenfeld 719*fd43cf6eSHans Rosenfeld if ((intr_type & DDI_INTR_TYPE_MSIX)) { 720*fd43cf6eSHans Rosenfeld if (iwn_intr_add(sc, DDI_INTR_TYPE_MSIX) == DDI_SUCCESS) 721*fd43cf6eSHans Rosenfeld return (DDI_SUCCESS); 722*fd43cf6eSHans Rosenfeld iwn_intr_teardown(sc); 723*fd43cf6eSHans Rosenfeld } 724*fd43cf6eSHans Rosenfeld 725*fd43cf6eSHans Rosenfeld if ((intr_type & DDI_INTR_TYPE_MSI)) { 726*fd43cf6eSHans Rosenfeld if (iwn_intr_add(sc, DDI_INTR_TYPE_MSI) == DDI_SUCCESS) 727*fd43cf6eSHans Rosenfeld return (DDI_SUCCESS); 728*fd43cf6eSHans Rosenfeld iwn_intr_teardown(sc); 729*fd43cf6eSHans Rosenfeld } 730*fd43cf6eSHans Rosenfeld 731*fd43cf6eSHans Rosenfeld if ((intr_type & DDI_INTR_TYPE_FIXED)) { 732*fd43cf6eSHans Rosenfeld if (iwn_intr_add(sc, DDI_INTR_TYPE_FIXED) == DDI_SUCCESS) 733*fd43cf6eSHans Rosenfeld return (DDI_SUCCESS); 734*fd43cf6eSHans Rosenfeld iwn_intr_teardown(sc); 735*fd43cf6eSHans Rosenfeld } 736*fd43cf6eSHans Rosenfeld 737*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, "!iwn_intr_setup() failed"); 738*fd43cf6eSHans Rosenfeld return (DDI_FAILURE); 739*fd43cf6eSHans Rosenfeld } 740*fd43cf6eSHans Rosenfeld 741*fd43cf6eSHans Rosenfeld static int 742*fd43cf6eSHans Rosenfeld iwn_pci_get_capability(ddi_acc_handle_t pcih, int cap, int *cap_off) 743*fd43cf6eSHans Rosenfeld { 744*fd43cf6eSHans Rosenfeld uint8_t ptr; 745*fd43cf6eSHans Rosenfeld uint8_t val; 746*fd43cf6eSHans Rosenfeld 747*fd43cf6eSHans Rosenfeld for (ptr = pci_config_get8(pcih, PCI_CONF_CAP_PTR); 748*fd43cf6eSHans Rosenfeld ptr != 0 && ptr != 0xff; 749*fd43cf6eSHans Rosenfeld ptr = pci_config_get8(pcih, ptr + PCI_CAP_NEXT_PTR)) { 750*fd43cf6eSHans Rosenfeld val = pci_config_get8(pcih, ptr + PCIE_CAP_ID); 751*fd43cf6eSHans Rosenfeld if (val == 0xff) 752*fd43cf6eSHans Rosenfeld return (DDI_FAILURE); 753*fd43cf6eSHans Rosenfeld 754*fd43cf6eSHans Rosenfeld if (cap != val) 755*fd43cf6eSHans Rosenfeld continue; 756*fd43cf6eSHans Rosenfeld 757*fd43cf6eSHans Rosenfeld *cap_off = ptr; 758*fd43cf6eSHans Rosenfeld return (DDI_SUCCESS); 759*fd43cf6eSHans Rosenfeld } 760*fd43cf6eSHans Rosenfeld 761*fd43cf6eSHans Rosenfeld return (DDI_FAILURE); 762*fd43cf6eSHans Rosenfeld } 763*fd43cf6eSHans Rosenfeld 764*fd43cf6eSHans Rosenfeld static int 765*fd43cf6eSHans Rosenfeld iwn_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 766*fd43cf6eSHans Rosenfeld { 767*fd43cf6eSHans Rosenfeld int instance; 768*fd43cf6eSHans Rosenfeld 769*fd43cf6eSHans Rosenfeld struct iwn_softc *sc; 770*fd43cf6eSHans Rosenfeld struct ieee80211com *ic; 771*fd43cf6eSHans Rosenfeld char strbuf[32]; 772*fd43cf6eSHans Rosenfeld wifi_data_t wd = { 0 }; 773*fd43cf6eSHans Rosenfeld mac_register_t *macp; 774*fd43cf6eSHans Rosenfeld uint32_t reg; 775*fd43cf6eSHans Rosenfeld int i, error; 776*fd43cf6eSHans Rosenfeld 777*fd43cf6eSHans Rosenfeld switch (cmd) { 778*fd43cf6eSHans Rosenfeld case DDI_ATTACH: 779*fd43cf6eSHans Rosenfeld break; 780*fd43cf6eSHans Rosenfeld 781*fd43cf6eSHans Rosenfeld case DDI_RESUME: 782*fd43cf6eSHans Rosenfeld instance = ddi_get_instance(dip); 783*fd43cf6eSHans Rosenfeld sc = ddi_get_soft_state(iwn_state, 784*fd43cf6eSHans Rosenfeld instance); 785*fd43cf6eSHans Rosenfeld ASSERT(sc != NULL); 786*fd43cf6eSHans Rosenfeld 787*fd43cf6eSHans Rosenfeld if (sc->sc_flags & IWN_FLAG_RUNNING) { 788*fd43cf6eSHans Rosenfeld (void) iwn_init(sc); 789*fd43cf6eSHans Rosenfeld } 790*fd43cf6eSHans Rosenfeld 791*fd43cf6eSHans Rosenfeld sc->sc_flags &= ~IWN_FLAG_SUSPEND; 792*fd43cf6eSHans Rosenfeld 793*fd43cf6eSHans Rosenfeld return (DDI_SUCCESS); 794*fd43cf6eSHans Rosenfeld default: 795*fd43cf6eSHans Rosenfeld return (DDI_FAILURE); 796*fd43cf6eSHans Rosenfeld } 797*fd43cf6eSHans Rosenfeld 798*fd43cf6eSHans Rosenfeld instance = ddi_get_instance(dip); 799*fd43cf6eSHans Rosenfeld 800*fd43cf6eSHans Rosenfeld if (ddi_soft_state_zalloc(iwn_state, instance) != DDI_SUCCESS) { 801*fd43cf6eSHans Rosenfeld dev_err(dip, CE_WARN, "!ddi_soft_state_zalloc() failed"); 802*fd43cf6eSHans Rosenfeld return (DDI_FAILURE); 803*fd43cf6eSHans Rosenfeld } 804*fd43cf6eSHans Rosenfeld 805*fd43cf6eSHans Rosenfeld sc = ddi_get_soft_state(iwn_state, instance); 806*fd43cf6eSHans Rosenfeld ddi_set_driver_private(dip, (caddr_t)sc); 807*fd43cf6eSHans Rosenfeld 808*fd43cf6eSHans Rosenfeld ic = &sc->sc_ic; 809*fd43cf6eSHans Rosenfeld 810*fd43cf6eSHans Rosenfeld sc->sc_dip = dip; 811*fd43cf6eSHans Rosenfeld 812*fd43cf6eSHans Rosenfeld iwn_kstat_create(sc, "hw_state", sizeof (struct iwn_ks_misc), 813*fd43cf6eSHans Rosenfeld &sc->sc_ks_misc, (void **)&sc->sc_misc); 814*fd43cf6eSHans Rosenfeld iwn_kstat_create(sc, "antennas", sizeof (struct iwn_ks_ant), 815*fd43cf6eSHans Rosenfeld &sc->sc_ks_ant, (void **)&sc->sc_ant); 816*fd43cf6eSHans Rosenfeld iwn_kstat_create(sc, "sensitivity", sizeof (struct iwn_ks_sens), 817*fd43cf6eSHans Rosenfeld &sc->sc_ks_sens, (void **)&sc->sc_sens); 818*fd43cf6eSHans Rosenfeld iwn_kstat_create(sc, "timing", sizeof (struct iwn_ks_timing), 819*fd43cf6eSHans Rosenfeld &sc->sc_ks_timing, (void **)&sc->sc_timing); 820*fd43cf6eSHans Rosenfeld iwn_kstat_create(sc, "edca", sizeof (struct iwn_ks_edca), 821*fd43cf6eSHans Rosenfeld &sc->sc_ks_edca, (void **)&sc->sc_edca); 822*fd43cf6eSHans Rosenfeld 823*fd43cf6eSHans Rosenfeld if (pci_config_setup(dip, &sc->sc_pcih) != DDI_SUCCESS) { 824*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, "!pci_config_setup() failed"); 825*fd43cf6eSHans Rosenfeld goto fail_pci_config; 826*fd43cf6eSHans Rosenfeld } 827*fd43cf6eSHans Rosenfeld 828*fd43cf6eSHans Rosenfeld /* 829*fd43cf6eSHans Rosenfeld * Get the offset of the PCI Express Capability Structure in PCI 830*fd43cf6eSHans Rosenfeld * Configuration Space. 831*fd43cf6eSHans Rosenfeld */ 832*fd43cf6eSHans Rosenfeld error = iwn_pci_get_capability(sc->sc_pcih, PCI_CAP_ID_PCI_E, 833*fd43cf6eSHans Rosenfeld &sc->sc_cap_off); 834*fd43cf6eSHans Rosenfeld if (error != DDI_SUCCESS) { 835*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 836*fd43cf6eSHans Rosenfeld "!PCIe capability structure not found!"); 837*fd43cf6eSHans Rosenfeld goto fail_pci_capab; 838*fd43cf6eSHans Rosenfeld } 839*fd43cf6eSHans Rosenfeld 840*fd43cf6eSHans Rosenfeld /* Clear device-specific "PCI retry timeout" register (41h). */ 841*fd43cf6eSHans Rosenfeld reg = pci_config_get8(sc->sc_pcih, 0x41); 842*fd43cf6eSHans Rosenfeld if (reg) 843*fd43cf6eSHans Rosenfeld pci_config_put8(sc->sc_pcih, 0x41, 0); 844*fd43cf6eSHans Rosenfeld 845*fd43cf6eSHans Rosenfeld error = ddi_regs_map_setup(dip, 1, &sc->sc_base, 0, 0, &iwn_reg_accattr, 846*fd43cf6eSHans Rosenfeld &sc->sc_regh); 847*fd43cf6eSHans Rosenfeld if (error != DDI_SUCCESS) { 848*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, "!ddi_regs_map_setup() failed"); 849*fd43cf6eSHans Rosenfeld goto fail_regs_map; 850*fd43cf6eSHans Rosenfeld } 851*fd43cf6eSHans Rosenfeld 852*fd43cf6eSHans Rosenfeld /* Clear pending interrupts. */ 853*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_INT, 0xffffffff); 854*fd43cf6eSHans Rosenfeld 855*fd43cf6eSHans Rosenfeld /* Disable all interrupts. */ 856*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_INT_MASK, 0); 857*fd43cf6eSHans Rosenfeld 858*fd43cf6eSHans Rosenfeld /* Install interrupt handler. */ 859*fd43cf6eSHans Rosenfeld if (iwn_intr_setup(sc) != DDI_SUCCESS) 860*fd43cf6eSHans Rosenfeld goto fail_intr; 861*fd43cf6eSHans Rosenfeld 862*fd43cf6eSHans Rosenfeld mutex_init(&sc->sc_mtx, NULL, MUTEX_DRIVER, 863*fd43cf6eSHans Rosenfeld DDI_INTR_PRI(sc->sc_intr_pri)); 864*fd43cf6eSHans Rosenfeld mutex_init(&sc->sc_tx_mtx, NULL, MUTEX_DRIVER, 865*fd43cf6eSHans Rosenfeld DDI_INTR_PRI(sc->sc_intr_pri)); 866*fd43cf6eSHans Rosenfeld mutex_init(&sc->sc_mt_mtx, NULL, MUTEX_DRIVER, 867*fd43cf6eSHans Rosenfeld DDI_INTR_PRI(sc->sc_intr_pri)); 868*fd43cf6eSHans Rosenfeld 869*fd43cf6eSHans Rosenfeld cv_init(&sc->sc_cmd_cv, NULL, CV_DRIVER, NULL); 870*fd43cf6eSHans Rosenfeld cv_init(&sc->sc_scan_cv, NULL, CV_DRIVER, NULL); 871*fd43cf6eSHans Rosenfeld cv_init(&sc->sc_fhdma_cv, NULL, CV_DRIVER, NULL); 872*fd43cf6eSHans Rosenfeld cv_init(&sc->sc_alive_cv, NULL, CV_DRIVER, NULL); 873*fd43cf6eSHans Rosenfeld cv_init(&sc->sc_calib_cv, NULL, CV_DRIVER, NULL); 874*fd43cf6eSHans Rosenfeld 875*fd43cf6eSHans Rosenfeld iwn_kstat_init(sc); 876*fd43cf6eSHans Rosenfeld 877*fd43cf6eSHans Rosenfeld /* Read hardware revision and attach. */ 878*fd43cf6eSHans Rosenfeld sc->hw_type = 879*fd43cf6eSHans Rosenfeld (IWN_READ(sc, IWN_HW_REV) & IWN_HW_REV_TYPE_MASK) 880*fd43cf6eSHans Rosenfeld >> IWN_HW_REV_TYPE_SHIFT; 881*fd43cf6eSHans Rosenfeld if (sc->hw_type == IWN_HW_REV_TYPE_4965) 882*fd43cf6eSHans Rosenfeld error = iwn4965_attach(sc); 883*fd43cf6eSHans Rosenfeld else 884*fd43cf6eSHans Rosenfeld error = iwn5000_attach(sc, sc->sc_devid); 885*fd43cf6eSHans Rosenfeld if (error != 0) { 886*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, "!could not attach device"); 887*fd43cf6eSHans Rosenfeld goto fail_hw; 888*fd43cf6eSHans Rosenfeld } 889*fd43cf6eSHans Rosenfeld 890*fd43cf6eSHans Rosenfeld if ((error = iwn_hw_prepare(sc)) != 0) { 891*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, "!hardware not ready"); 892*fd43cf6eSHans Rosenfeld goto fail_hw; 893*fd43cf6eSHans Rosenfeld } 894*fd43cf6eSHans Rosenfeld 895*fd43cf6eSHans Rosenfeld /* Read MAC address, channels, etc from EEPROM. */ 896*fd43cf6eSHans Rosenfeld if ((error = iwn_read_eeprom(sc)) != 0) { 897*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, "!could not read EEPROM"); 898*fd43cf6eSHans Rosenfeld goto fail_hw; 899*fd43cf6eSHans Rosenfeld } 900*fd43cf6eSHans Rosenfeld 901*fd43cf6eSHans Rosenfeld /* Allocate DMA memory for firmware transfers. */ 902*fd43cf6eSHans Rosenfeld if ((error = iwn_alloc_fwmem(sc)) != 0) { 903*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 904*fd43cf6eSHans Rosenfeld "!could not allocate memory for firmware"); 905*fd43cf6eSHans Rosenfeld goto fail_fwmem; 906*fd43cf6eSHans Rosenfeld } 907*fd43cf6eSHans Rosenfeld 908*fd43cf6eSHans Rosenfeld /* Allocate "Keep Warm" page. */ 909*fd43cf6eSHans Rosenfeld if ((error = iwn_alloc_kw(sc)) != 0) { 910*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 911*fd43cf6eSHans Rosenfeld "!could not allocate keep warm page"); 912*fd43cf6eSHans Rosenfeld goto fail_kw; 913*fd43cf6eSHans Rosenfeld } 914*fd43cf6eSHans Rosenfeld 915*fd43cf6eSHans Rosenfeld /* Allocate ICT table for 5000 Series. */ 916*fd43cf6eSHans Rosenfeld if (sc->hw_type != IWN_HW_REV_TYPE_4965 && 917*fd43cf6eSHans Rosenfeld (error = iwn_alloc_ict(sc)) != 0) { 918*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, "!could not allocate ICT table"); 919*fd43cf6eSHans Rosenfeld goto fail_ict; 920*fd43cf6eSHans Rosenfeld } 921*fd43cf6eSHans Rosenfeld 922*fd43cf6eSHans Rosenfeld /* Allocate TX scheduler "rings". */ 923*fd43cf6eSHans Rosenfeld if ((error = iwn_alloc_sched(sc)) != 0) { 924*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 925*fd43cf6eSHans Rosenfeld "!could not allocate TX scheduler rings"); 926*fd43cf6eSHans Rosenfeld goto fail_sched; 927*fd43cf6eSHans Rosenfeld } 928*fd43cf6eSHans Rosenfeld 929*fd43cf6eSHans Rosenfeld /* Allocate TX rings (16 on 4965AGN, 20 on >=5000). */ 930*fd43cf6eSHans Rosenfeld for (i = 0; i < sc->ntxqs; i++) { 931*fd43cf6eSHans Rosenfeld if ((error = iwn_alloc_tx_ring(sc, &sc->txq[i], i)) != 0) { 932*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 933*fd43cf6eSHans Rosenfeld "!could not allocate TX ring %d", i); 934*fd43cf6eSHans Rosenfeld while (--i >= 0) 935*fd43cf6eSHans Rosenfeld iwn_free_tx_ring(sc, &sc->txq[i]); 936*fd43cf6eSHans Rosenfeld goto fail_txring; 937*fd43cf6eSHans Rosenfeld } 938*fd43cf6eSHans Rosenfeld } 939*fd43cf6eSHans Rosenfeld 940*fd43cf6eSHans Rosenfeld /* Allocate RX ring. */ 941*fd43cf6eSHans Rosenfeld if ((error = iwn_alloc_rx_ring(sc, &sc->rxq)) != 0) { 942*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, "!could not allocate RX ring"); 943*fd43cf6eSHans Rosenfeld goto fail_rxring; 944*fd43cf6eSHans Rosenfeld } 945*fd43cf6eSHans Rosenfeld 946*fd43cf6eSHans Rosenfeld /* Clear pending interrupts. */ 947*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_INT, 0xffffffff); 948*fd43cf6eSHans Rosenfeld 949*fd43cf6eSHans Rosenfeld /* Count the number of available chains. */ 950*fd43cf6eSHans Rosenfeld sc->ntxchains = 951*fd43cf6eSHans Rosenfeld ((sc->txchainmask >> 2) & 1) + 952*fd43cf6eSHans Rosenfeld ((sc->txchainmask >> 1) & 1) + 953*fd43cf6eSHans Rosenfeld ((sc->txchainmask >> 0) & 1); 954*fd43cf6eSHans Rosenfeld sc->nrxchains = 955*fd43cf6eSHans Rosenfeld ((sc->rxchainmask >> 2) & 1) + 956*fd43cf6eSHans Rosenfeld ((sc->rxchainmask >> 1) & 1) + 957*fd43cf6eSHans Rosenfeld ((sc->rxchainmask >> 0) & 1); 958*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_CONT, "!MIMO %dT%dR, %s, address %s", 959*fd43cf6eSHans Rosenfeld sc->ntxchains, sc->nrxchains, sc->eeprom_domain, 960*fd43cf6eSHans Rosenfeld ieee80211_macaddr_sprintf(ic->ic_macaddr)); 961*fd43cf6eSHans Rosenfeld 962*fd43cf6eSHans Rosenfeld sc->sc_ant->tx_ant.value.ul = sc->txchainmask; 963*fd43cf6eSHans Rosenfeld sc->sc_ant->rx_ant.value.ul = sc->rxchainmask; 964*fd43cf6eSHans Rosenfeld 965*fd43cf6eSHans Rosenfeld ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ 966*fd43cf6eSHans Rosenfeld ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ 967*fd43cf6eSHans Rosenfeld ic->ic_state = IEEE80211_S_INIT; 968*fd43cf6eSHans Rosenfeld 969*fd43cf6eSHans Rosenfeld /* Set device capabilities. */ 970*fd43cf6eSHans Rosenfeld /* XXX OpenBSD has IEEE80211_C_WEP, IEEE80211_C_RSN, 971*fd43cf6eSHans Rosenfeld * and IEEE80211_C_PMGT too. */ 972*fd43cf6eSHans Rosenfeld ic->ic_caps = 973*fd43cf6eSHans Rosenfeld IEEE80211_C_IBSS | /* IBSS mode support */ 974*fd43cf6eSHans Rosenfeld IEEE80211_C_WPA | /* 802.11i */ 975*fd43cf6eSHans Rosenfeld IEEE80211_C_MONITOR | /* monitor mode supported */ 976*fd43cf6eSHans Rosenfeld IEEE80211_C_TXPMGT | /* tx power management */ 977*fd43cf6eSHans Rosenfeld IEEE80211_C_SHSLOT | /* short slot time supported */ 978*fd43cf6eSHans Rosenfeld IEEE80211_C_SHPREAMBLE | /* short preamble supported */ 979*fd43cf6eSHans Rosenfeld IEEE80211_C_WME; /* 802.11e */ 980*fd43cf6eSHans Rosenfeld 981*fd43cf6eSHans Rosenfeld #ifndef IEEE80211_NO_HT 982*fd43cf6eSHans Rosenfeld if (sc->sc_flags & IWN_FLAG_HAS_11N) { 983*fd43cf6eSHans Rosenfeld /* Set HT capabilities. */ 984*fd43cf6eSHans Rosenfeld ic->ic_htcaps = 985*fd43cf6eSHans Rosenfeld #if IWN_RBUF_SIZE == 8192 986*fd43cf6eSHans Rosenfeld IEEE80211_HTCAP_AMSDU7935 | 987*fd43cf6eSHans Rosenfeld #endif 988*fd43cf6eSHans Rosenfeld IEEE80211_HTCAP_CBW20_40 | 989*fd43cf6eSHans Rosenfeld IEEE80211_HTCAP_SGI20 | 990*fd43cf6eSHans Rosenfeld IEEE80211_HTCAP_SGI40; 991*fd43cf6eSHans Rosenfeld if (sc->hw_type != IWN_HW_REV_TYPE_4965) 992*fd43cf6eSHans Rosenfeld ic->ic_htcaps |= IEEE80211_HTCAP_GF; 993*fd43cf6eSHans Rosenfeld if (sc->hw_type == IWN_HW_REV_TYPE_6050) 994*fd43cf6eSHans Rosenfeld ic->ic_htcaps |= IEEE80211_HTCAP_SMPS_DYN; 995*fd43cf6eSHans Rosenfeld else 996*fd43cf6eSHans Rosenfeld ic->ic_htcaps |= IEEE80211_HTCAP_SMPS_DIS; 997*fd43cf6eSHans Rosenfeld } 998*fd43cf6eSHans Rosenfeld #endif /* !IEEE80211_NO_HT */ 999*fd43cf6eSHans Rosenfeld 1000*fd43cf6eSHans Rosenfeld /* Set supported legacy rates. */ 1001*fd43cf6eSHans Rosenfeld ic->ic_sup_rates[IEEE80211_MODE_11B] = iwn_rateset_11b; 1002*fd43cf6eSHans Rosenfeld ic->ic_sup_rates[IEEE80211_MODE_11G] = iwn_rateset_11g; 1003*fd43cf6eSHans Rosenfeld if (sc->sc_flags & IWN_FLAG_HAS_5GHZ) { 1004*fd43cf6eSHans Rosenfeld ic->ic_sup_rates[IEEE80211_MODE_11A] = iwn_rateset_11a; 1005*fd43cf6eSHans Rosenfeld } 1006*fd43cf6eSHans Rosenfeld #ifndef IEEE80211_NO_HT 1007*fd43cf6eSHans Rosenfeld if (sc->sc_flags & IWN_FLAG_HAS_11N) { 1008*fd43cf6eSHans Rosenfeld /* Set supported HT rates. */ 1009*fd43cf6eSHans Rosenfeld ic->ic_sup_mcs[0] = 0xff; /* MCS 0-7 */ 1010*fd43cf6eSHans Rosenfeld if (sc->nrxchains > 1) 1011*fd43cf6eSHans Rosenfeld ic->ic_sup_mcs[1] = 0xff; /* MCS 7-15 */ 1012*fd43cf6eSHans Rosenfeld if (sc->nrxchains > 2) 1013*fd43cf6eSHans Rosenfeld ic->ic_sup_mcs[2] = 0xff; /* MCS 16-23 */ 1014*fd43cf6eSHans Rosenfeld } 1015*fd43cf6eSHans Rosenfeld #endif 1016*fd43cf6eSHans Rosenfeld 1017*fd43cf6eSHans Rosenfeld /* IBSS channel undefined for now. */ 1018*fd43cf6eSHans Rosenfeld ic->ic_ibss_chan = &ic->ic_sup_channels[0]; 1019*fd43cf6eSHans Rosenfeld 1020*fd43cf6eSHans Rosenfeld ic->ic_node_newassoc = iwn_newassoc; 1021*fd43cf6eSHans Rosenfeld ic->ic_xmit = iwn_send; 1022*fd43cf6eSHans Rosenfeld #ifdef IWN_HWCRYPTO 1023*fd43cf6eSHans Rosenfeld ic->ic_crypto.cs_key_set = iwn_set_key; 1024*fd43cf6eSHans Rosenfeld ic->ic_crypto.cs_key_delete = iwn_delete_key; 1025*fd43cf6eSHans Rosenfeld #endif 1026*fd43cf6eSHans Rosenfeld ic->ic_wme.wme_update = iwn_wme_update; 1027*fd43cf6eSHans Rosenfeld #ifndef IEEE80211_NO_HT 1028*fd43cf6eSHans Rosenfeld ic->ic_ampdu_rx_start = iwn_ampdu_rx_start; 1029*fd43cf6eSHans Rosenfeld ic->ic_ampdu_rx_stop = iwn_ampdu_rx_stop; 1030*fd43cf6eSHans Rosenfeld ic->ic_ampdu_tx_start = iwn_ampdu_tx_start; 1031*fd43cf6eSHans Rosenfeld ic->ic_ampdu_tx_stop = iwn_ampdu_tx_stop; 1032*fd43cf6eSHans Rosenfeld #endif 1033*fd43cf6eSHans Rosenfeld /* 1034*fd43cf6eSHans Rosenfeld * attach to 802.11 module 1035*fd43cf6eSHans Rosenfeld */ 1036*fd43cf6eSHans Rosenfeld ieee80211_attach(ic); 1037*fd43cf6eSHans Rosenfeld 1038*fd43cf6eSHans Rosenfeld ieee80211_register_door(ic, ddi_driver_name(dip), ddi_get_instance(dip)); 1039*fd43cf6eSHans Rosenfeld 1040*fd43cf6eSHans Rosenfeld /* Override 802.11 state transition machine. */ 1041*fd43cf6eSHans Rosenfeld sc->sc_newstate = ic->ic_newstate; 1042*fd43cf6eSHans Rosenfeld ic->ic_newstate = iwn_newstate; 1043*fd43cf6eSHans Rosenfeld ic->ic_watchdog = iwn_watchdog; 1044*fd43cf6eSHans Rosenfeld 1045*fd43cf6eSHans Rosenfeld ic->ic_node_alloc = iwn_node_alloc; 1046*fd43cf6eSHans Rosenfeld ic->ic_node_free = iwn_node_free; 1047*fd43cf6eSHans Rosenfeld 1048*fd43cf6eSHans Rosenfeld ieee80211_media_init(ic); 1049*fd43cf6eSHans Rosenfeld 1050*fd43cf6eSHans Rosenfeld /* 1051*fd43cf6eSHans Rosenfeld * initialize default tx key 1052*fd43cf6eSHans Rosenfeld */ 1053*fd43cf6eSHans Rosenfeld ic->ic_def_txkey = 0; 1054*fd43cf6eSHans Rosenfeld 1055*fd43cf6eSHans Rosenfeld sc->amrr.amrr_min_success_threshold = 1; 1056*fd43cf6eSHans Rosenfeld sc->amrr.amrr_max_success_threshold = 15; 1057*fd43cf6eSHans Rosenfeld 1058*fd43cf6eSHans Rosenfeld /* 1059*fd43cf6eSHans Rosenfeld * Initialize pointer to device specific functions 1060*fd43cf6eSHans Rosenfeld */ 1061*fd43cf6eSHans Rosenfeld wd.wd_secalloc = WIFI_SEC_NONE; 1062*fd43cf6eSHans Rosenfeld wd.wd_opmode = ic->ic_opmode; 1063*fd43cf6eSHans Rosenfeld IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_macaddr); 1064*fd43cf6eSHans Rosenfeld 1065*fd43cf6eSHans Rosenfeld /* 1066*fd43cf6eSHans Rosenfeld * create relation to GLD 1067*fd43cf6eSHans Rosenfeld */ 1068*fd43cf6eSHans Rosenfeld macp = mac_alloc(MAC_VERSION); 1069*fd43cf6eSHans Rosenfeld if (NULL == macp) { 1070*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, "!mac_alloc() failed"); 1071*fd43cf6eSHans Rosenfeld goto fail_mac_alloc; 1072*fd43cf6eSHans Rosenfeld } 1073*fd43cf6eSHans Rosenfeld 1074*fd43cf6eSHans Rosenfeld macp->m_type_ident = MAC_PLUGIN_IDENT_WIFI; 1075*fd43cf6eSHans Rosenfeld macp->m_driver = sc; 1076*fd43cf6eSHans Rosenfeld macp->m_dip = dip; 1077*fd43cf6eSHans Rosenfeld macp->m_src_addr = ic->ic_macaddr; 1078*fd43cf6eSHans Rosenfeld macp->m_callbacks = &iwn_m_callbacks; 1079*fd43cf6eSHans Rosenfeld macp->m_min_sdu = 0; 1080*fd43cf6eSHans Rosenfeld macp->m_max_sdu = IEEE80211_MTU; 1081*fd43cf6eSHans Rosenfeld macp->m_pdata = &wd; 1082*fd43cf6eSHans Rosenfeld macp->m_pdata_size = sizeof (wd); 1083*fd43cf6eSHans Rosenfeld 1084*fd43cf6eSHans Rosenfeld /* 1085*fd43cf6eSHans Rosenfeld * Register the macp to mac 1086*fd43cf6eSHans Rosenfeld */ 1087*fd43cf6eSHans Rosenfeld error = mac_register(macp, &ic->ic_mach); 1088*fd43cf6eSHans Rosenfeld mac_free(macp); 1089*fd43cf6eSHans Rosenfeld if (error != DDI_SUCCESS) { 1090*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, "!mac_register() failed"); 1091*fd43cf6eSHans Rosenfeld goto fail_mac_alloc; 1092*fd43cf6eSHans Rosenfeld } 1093*fd43cf6eSHans Rosenfeld 1094*fd43cf6eSHans Rosenfeld /* 1095*fd43cf6eSHans Rosenfeld * Create minor node of type DDI_NT_NET_WIFI 1096*fd43cf6eSHans Rosenfeld */ 1097*fd43cf6eSHans Rosenfeld (void) snprintf(strbuf, sizeof (strbuf), "iwn%d", instance); 1098*fd43cf6eSHans Rosenfeld error = ddi_create_minor_node(dip, strbuf, S_IFCHR, 1099*fd43cf6eSHans Rosenfeld instance + 1, DDI_NT_NET_WIFI, 0); 1100*fd43cf6eSHans Rosenfeld if (error != DDI_SUCCESS) { 1101*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, "!ddi_create_minor_node() failed"); 1102*fd43cf6eSHans Rosenfeld goto fail_minor; 1103*fd43cf6eSHans Rosenfeld } 1104*fd43cf6eSHans Rosenfeld 1105*fd43cf6eSHans Rosenfeld /* 1106*fd43cf6eSHans Rosenfeld * Notify link is down now 1107*fd43cf6eSHans Rosenfeld */ 1108*fd43cf6eSHans Rosenfeld mac_link_update(ic->ic_mach, LINK_STATE_DOWN); 1109*fd43cf6eSHans Rosenfeld 1110*fd43cf6eSHans Rosenfeld sc->sc_periodic = ddi_periodic_add(iwn_periodic, sc, 1111*fd43cf6eSHans Rosenfeld iwn_periodic_interval * MICROSEC, 0); 1112*fd43cf6eSHans Rosenfeld 1113*fd43cf6eSHans Rosenfeld if (sc->sc_ks_misc) 1114*fd43cf6eSHans Rosenfeld kstat_install(sc->sc_ks_misc); 1115*fd43cf6eSHans Rosenfeld if (sc->sc_ks_ant) 1116*fd43cf6eSHans Rosenfeld kstat_install(sc->sc_ks_ant); 1117*fd43cf6eSHans Rosenfeld if (sc->sc_ks_sens) 1118*fd43cf6eSHans Rosenfeld kstat_install(sc->sc_ks_sens); 1119*fd43cf6eSHans Rosenfeld if (sc->sc_ks_timing) 1120*fd43cf6eSHans Rosenfeld kstat_install(sc->sc_ks_timing); 1121*fd43cf6eSHans Rosenfeld if (sc->sc_ks_edca) 1122*fd43cf6eSHans Rosenfeld kstat_install(sc->sc_ks_edca); 1123*fd43cf6eSHans Rosenfeld if (sc->sc_ks_txpower) 1124*fd43cf6eSHans Rosenfeld kstat_install(sc->sc_ks_txpower); 1125*fd43cf6eSHans Rosenfeld if (sc->sc_ks_toff) 1126*fd43cf6eSHans Rosenfeld kstat_install(sc->sc_ks_toff); 1127*fd43cf6eSHans Rosenfeld 1128*fd43cf6eSHans Rosenfeld sc->sc_flags |= IWN_FLAG_ATTACHED; 1129*fd43cf6eSHans Rosenfeld 1130*fd43cf6eSHans Rosenfeld return (DDI_SUCCESS); 1131*fd43cf6eSHans Rosenfeld 1132*fd43cf6eSHans Rosenfeld /* Free allocated memory if something failed during attachment. */ 1133*fd43cf6eSHans Rosenfeld fail_minor: 1134*fd43cf6eSHans Rosenfeld mac_unregister(ic->ic_mach); 1135*fd43cf6eSHans Rosenfeld 1136*fd43cf6eSHans Rosenfeld fail_mac_alloc: 1137*fd43cf6eSHans Rosenfeld ieee80211_detach(ic); 1138*fd43cf6eSHans Rosenfeld iwn_free_rx_ring(sc, &sc->rxq); 1139*fd43cf6eSHans Rosenfeld 1140*fd43cf6eSHans Rosenfeld fail_rxring: 1141*fd43cf6eSHans Rosenfeld for (i = 0; i < sc->ntxqs; i++) 1142*fd43cf6eSHans Rosenfeld iwn_free_tx_ring(sc, &sc->txq[i]); 1143*fd43cf6eSHans Rosenfeld 1144*fd43cf6eSHans Rosenfeld fail_txring: 1145*fd43cf6eSHans Rosenfeld iwn_free_sched(sc); 1146*fd43cf6eSHans Rosenfeld 1147*fd43cf6eSHans Rosenfeld fail_sched: 1148*fd43cf6eSHans Rosenfeld if (sc->ict != NULL) 1149*fd43cf6eSHans Rosenfeld iwn_free_ict(sc); 1150*fd43cf6eSHans Rosenfeld 1151*fd43cf6eSHans Rosenfeld fail_ict: 1152*fd43cf6eSHans Rosenfeld iwn_free_kw(sc); 1153*fd43cf6eSHans Rosenfeld 1154*fd43cf6eSHans Rosenfeld fail_kw: 1155*fd43cf6eSHans Rosenfeld iwn_free_fwmem(sc); 1156*fd43cf6eSHans Rosenfeld 1157*fd43cf6eSHans Rosenfeld fail_fwmem: 1158*fd43cf6eSHans Rosenfeld fail_hw: 1159*fd43cf6eSHans Rosenfeld iwn_intr_teardown(sc); 1160*fd43cf6eSHans Rosenfeld 1161*fd43cf6eSHans Rosenfeld iwn_kstat_free(sc->sc_ks_txpower, sc->sc_txpower, 1162*fd43cf6eSHans Rosenfeld sizeof (struct iwn_ks_txpower)); 1163*fd43cf6eSHans Rosenfeld 1164*fd43cf6eSHans Rosenfeld if (sc->hw_type == IWN_HW_REV_TYPE_6005) 1165*fd43cf6eSHans Rosenfeld iwn_kstat_free(sc->sc_ks_toff, sc->sc_toff.t6000, 1166*fd43cf6eSHans Rosenfeld sizeof (struct iwn_ks_toff_6000)); 1167*fd43cf6eSHans Rosenfeld else 1168*fd43cf6eSHans Rosenfeld iwn_kstat_free(sc->sc_ks_toff, sc->sc_toff.t2000, 1169*fd43cf6eSHans Rosenfeld sizeof (struct iwn_ks_toff_2000)); 1170*fd43cf6eSHans Rosenfeld 1171*fd43cf6eSHans Rosenfeld fail_intr: 1172*fd43cf6eSHans Rosenfeld ddi_regs_map_free(&sc->sc_regh); 1173*fd43cf6eSHans Rosenfeld 1174*fd43cf6eSHans Rosenfeld fail_regs_map: 1175*fd43cf6eSHans Rosenfeld fail_pci_capab: 1176*fd43cf6eSHans Rosenfeld pci_config_teardown(&sc->sc_pcih); 1177*fd43cf6eSHans Rosenfeld 1178*fd43cf6eSHans Rosenfeld fail_pci_config: 1179*fd43cf6eSHans Rosenfeld iwn_kstat_free(sc->sc_ks_misc, sc->sc_misc, 1180*fd43cf6eSHans Rosenfeld sizeof (struct iwn_ks_misc)); 1181*fd43cf6eSHans Rosenfeld iwn_kstat_free(sc->sc_ks_ant, sc->sc_ant, 1182*fd43cf6eSHans Rosenfeld sizeof (struct iwn_ks_ant)); 1183*fd43cf6eSHans Rosenfeld iwn_kstat_free(sc->sc_ks_sens, sc->sc_sens, 1184*fd43cf6eSHans Rosenfeld sizeof (struct iwn_ks_sens)); 1185*fd43cf6eSHans Rosenfeld iwn_kstat_free(sc->sc_ks_timing, sc->sc_timing, 1186*fd43cf6eSHans Rosenfeld sizeof (struct iwn_ks_timing)); 1187*fd43cf6eSHans Rosenfeld iwn_kstat_free(sc->sc_ks_edca, sc->sc_edca, 1188*fd43cf6eSHans Rosenfeld sizeof (struct iwn_ks_edca)); 1189*fd43cf6eSHans Rosenfeld 1190*fd43cf6eSHans Rosenfeld ddi_soft_state_free(iwn_state, instance); 1191*fd43cf6eSHans Rosenfeld 1192*fd43cf6eSHans Rosenfeld return (DDI_FAILURE); 1193*fd43cf6eSHans Rosenfeld } 1194*fd43cf6eSHans Rosenfeld 1195*fd43cf6eSHans Rosenfeld int 1196*fd43cf6eSHans Rosenfeld iwn4965_attach(struct iwn_softc *sc) 1197*fd43cf6eSHans Rosenfeld { 1198*fd43cf6eSHans Rosenfeld struct iwn_ops *ops = &sc->ops; 1199*fd43cf6eSHans Rosenfeld 1200*fd43cf6eSHans Rosenfeld ops->load_firmware = iwn4965_load_firmware; 1201*fd43cf6eSHans Rosenfeld ops->read_eeprom = iwn4965_read_eeprom; 1202*fd43cf6eSHans Rosenfeld ops->post_alive = iwn4965_post_alive; 1203*fd43cf6eSHans Rosenfeld ops->nic_config = iwn4965_nic_config; 1204*fd43cf6eSHans Rosenfeld ops->config_bt_coex = iwn_config_bt_coex_bluetooth; 1205*fd43cf6eSHans Rosenfeld ops->update_sched = iwn4965_update_sched; 1206*fd43cf6eSHans Rosenfeld ops->get_temperature = iwn4965_get_temperature; 1207*fd43cf6eSHans Rosenfeld ops->get_rssi = iwn4965_get_rssi; 1208*fd43cf6eSHans Rosenfeld ops->set_txpower = iwn4965_set_txpower; 1209*fd43cf6eSHans Rosenfeld ops->init_gains = iwn4965_init_gains; 1210*fd43cf6eSHans Rosenfeld ops->set_gains = iwn4965_set_gains; 1211*fd43cf6eSHans Rosenfeld ops->add_node = iwn4965_add_node; 1212*fd43cf6eSHans Rosenfeld ops->tx_done = iwn4965_tx_done; 1213*fd43cf6eSHans Rosenfeld #ifndef IEEE80211_NO_HT 1214*fd43cf6eSHans Rosenfeld ops->ampdu_tx_start = iwn4965_ampdu_tx_start; 1215*fd43cf6eSHans Rosenfeld ops->ampdu_tx_stop = iwn4965_ampdu_tx_stop; 1216*fd43cf6eSHans Rosenfeld #endif 1217*fd43cf6eSHans Rosenfeld sc->ntxqs = IWN4965_NTXQUEUES; 1218*fd43cf6eSHans Rosenfeld sc->ndmachnls = IWN4965_NDMACHNLS; 1219*fd43cf6eSHans Rosenfeld sc->broadcast_id = IWN4965_ID_BROADCAST; 1220*fd43cf6eSHans Rosenfeld sc->rxonsz = IWN4965_RXONSZ; 1221*fd43cf6eSHans Rosenfeld sc->schedsz = IWN4965_SCHEDSZ; 1222*fd43cf6eSHans Rosenfeld sc->fw_text_maxsz = IWN4965_FW_TEXT_MAXSZ; 1223*fd43cf6eSHans Rosenfeld sc->fw_data_maxsz = IWN4965_FW_DATA_MAXSZ; 1224*fd43cf6eSHans Rosenfeld sc->fwsz = IWN4965_FWSZ; 1225*fd43cf6eSHans Rosenfeld sc->sched_txfact_addr = IWN4965_SCHED_TXFACT; 1226*fd43cf6eSHans Rosenfeld sc->limits = &iwn4965_sensitivity_limits; 1227*fd43cf6eSHans Rosenfeld sc->fwname = "iwlwifi-4965-2.ucode"; 1228*fd43cf6eSHans Rosenfeld /* Override chains masks, ROM is known to be broken. */ 1229*fd43cf6eSHans Rosenfeld sc->txchainmask = IWN_ANT_AB; 1230*fd43cf6eSHans Rosenfeld sc->rxchainmask = IWN_ANT_ABC; 1231*fd43cf6eSHans Rosenfeld 1232*fd43cf6eSHans Rosenfeld iwn_kstat_create(sc, "txpower", sizeof (struct iwn_ks_txpower), 1233*fd43cf6eSHans Rosenfeld &sc->sc_ks_txpower, (void **)&sc->sc_txpower); 1234*fd43cf6eSHans Rosenfeld iwn_kstat_init_4965(sc); 1235*fd43cf6eSHans Rosenfeld 1236*fd43cf6eSHans Rosenfeld return 0; 1237*fd43cf6eSHans Rosenfeld } 1238*fd43cf6eSHans Rosenfeld 1239*fd43cf6eSHans Rosenfeld int 1240*fd43cf6eSHans Rosenfeld iwn5000_attach(struct iwn_softc *sc, uint16_t pid) 1241*fd43cf6eSHans Rosenfeld { 1242*fd43cf6eSHans Rosenfeld struct iwn_ops *ops = &sc->ops; 1243*fd43cf6eSHans Rosenfeld 1244*fd43cf6eSHans Rosenfeld ops->load_firmware = iwn5000_load_firmware; 1245*fd43cf6eSHans Rosenfeld ops->read_eeprom = iwn5000_read_eeprom; 1246*fd43cf6eSHans Rosenfeld ops->post_alive = iwn5000_post_alive; 1247*fd43cf6eSHans Rosenfeld ops->nic_config = iwn5000_nic_config; 1248*fd43cf6eSHans Rosenfeld ops->config_bt_coex = iwn_config_bt_coex_bluetooth; 1249*fd43cf6eSHans Rosenfeld ops->update_sched = iwn5000_update_sched; 1250*fd43cf6eSHans Rosenfeld ops->get_temperature = iwn5000_get_temperature; 1251*fd43cf6eSHans Rosenfeld ops->get_rssi = iwn5000_get_rssi; 1252*fd43cf6eSHans Rosenfeld ops->set_txpower = iwn5000_set_txpower; 1253*fd43cf6eSHans Rosenfeld ops->init_gains = iwn5000_init_gains; 1254*fd43cf6eSHans Rosenfeld ops->set_gains = iwn5000_set_gains; 1255*fd43cf6eSHans Rosenfeld ops->add_node = iwn5000_add_node; 1256*fd43cf6eSHans Rosenfeld ops->tx_done = iwn5000_tx_done; 1257*fd43cf6eSHans Rosenfeld #ifndef IEEE80211_NO_HT 1258*fd43cf6eSHans Rosenfeld ops->ampdu_tx_start = iwn5000_ampdu_tx_start; 1259*fd43cf6eSHans Rosenfeld ops->ampdu_tx_stop = iwn5000_ampdu_tx_stop; 1260*fd43cf6eSHans Rosenfeld #endif 1261*fd43cf6eSHans Rosenfeld sc->ntxqs = IWN5000_NTXQUEUES; 1262*fd43cf6eSHans Rosenfeld sc->ndmachnls = IWN5000_NDMACHNLS; 1263*fd43cf6eSHans Rosenfeld sc->broadcast_id = IWN5000_ID_BROADCAST; 1264*fd43cf6eSHans Rosenfeld sc->rxonsz = IWN5000_RXONSZ; 1265*fd43cf6eSHans Rosenfeld sc->schedsz = IWN5000_SCHEDSZ; 1266*fd43cf6eSHans Rosenfeld sc->fw_text_maxsz = IWN5000_FW_TEXT_MAXSZ; 1267*fd43cf6eSHans Rosenfeld sc->fw_data_maxsz = IWN5000_FW_DATA_MAXSZ; 1268*fd43cf6eSHans Rosenfeld sc->fwsz = IWN5000_FWSZ; 1269*fd43cf6eSHans Rosenfeld sc->sched_txfact_addr = IWN5000_SCHED_TXFACT; 1270*fd43cf6eSHans Rosenfeld 1271*fd43cf6eSHans Rosenfeld switch (sc->hw_type) { 1272*fd43cf6eSHans Rosenfeld case IWN_HW_REV_TYPE_5100: 1273*fd43cf6eSHans Rosenfeld sc->limits = &iwn5000_sensitivity_limits; 1274*fd43cf6eSHans Rosenfeld sc->fwname = "iwlwifi-5000-2.ucode"; 1275*fd43cf6eSHans Rosenfeld /* Override chains masks, ROM is known to be broken. */ 1276*fd43cf6eSHans Rosenfeld sc->txchainmask = IWN_ANT_B; 1277*fd43cf6eSHans Rosenfeld sc->rxchainmask = IWN_ANT_AB; 1278*fd43cf6eSHans Rosenfeld break; 1279*fd43cf6eSHans Rosenfeld case IWN_HW_REV_TYPE_5150: 1280*fd43cf6eSHans Rosenfeld sc->limits = &iwn5150_sensitivity_limits; 1281*fd43cf6eSHans Rosenfeld sc->fwname = "iwlwifi-5150-2.ucode"; 1282*fd43cf6eSHans Rosenfeld break; 1283*fd43cf6eSHans Rosenfeld case IWN_HW_REV_TYPE_5300: 1284*fd43cf6eSHans Rosenfeld case IWN_HW_REV_TYPE_5350: 1285*fd43cf6eSHans Rosenfeld sc->limits = &iwn5000_sensitivity_limits; 1286*fd43cf6eSHans Rosenfeld sc->fwname = "iwlwifi-5000-2.ucode"; 1287*fd43cf6eSHans Rosenfeld break; 1288*fd43cf6eSHans Rosenfeld case IWN_HW_REV_TYPE_1000: 1289*fd43cf6eSHans Rosenfeld sc->limits = &iwn1000_sensitivity_limits; 1290*fd43cf6eSHans Rosenfeld if (pid == PCI_PRODUCT_INTEL_WIFI_LINK_100_1 || 1291*fd43cf6eSHans Rosenfeld pid == PCI_PRODUCT_INTEL_WIFI_LINK_100_2) 1292*fd43cf6eSHans Rosenfeld sc->fwname = "iwlwifi-100-5.ucode"; 1293*fd43cf6eSHans Rosenfeld else 1294*fd43cf6eSHans Rosenfeld sc->fwname = "iwlwifi-1000-3.ucode"; 1295*fd43cf6eSHans Rosenfeld break; 1296*fd43cf6eSHans Rosenfeld case IWN_HW_REV_TYPE_6000: 1297*fd43cf6eSHans Rosenfeld sc->limits = &iwn6000_sensitivity_limits; 1298*fd43cf6eSHans Rosenfeld sc->fwname = "iwlwifi-6000-4.ucode"; 1299*fd43cf6eSHans Rosenfeld if (pid == PCI_PRODUCT_INTEL_WIFI_LINK_6000_IPA_1 || 1300*fd43cf6eSHans Rosenfeld pid == PCI_PRODUCT_INTEL_WIFI_LINK_6000_IPA_2) { 1301*fd43cf6eSHans Rosenfeld sc->sc_flags |= IWN_FLAG_INTERNAL_PA; 1302*fd43cf6eSHans Rosenfeld /* Override chains masks, ROM is known to be broken. */ 1303*fd43cf6eSHans Rosenfeld sc->txchainmask = IWN_ANT_BC; 1304*fd43cf6eSHans Rosenfeld sc->rxchainmask = IWN_ANT_BC; 1305*fd43cf6eSHans Rosenfeld } 1306*fd43cf6eSHans Rosenfeld break; 1307*fd43cf6eSHans Rosenfeld case IWN_HW_REV_TYPE_6050: 1308*fd43cf6eSHans Rosenfeld sc->limits = &iwn6000_sensitivity_limits; 1309*fd43cf6eSHans Rosenfeld sc->fwname = "iwlwifi-6050-5.ucode"; 1310*fd43cf6eSHans Rosenfeld break; 1311*fd43cf6eSHans Rosenfeld case IWN_HW_REV_TYPE_6005: 1312*fd43cf6eSHans Rosenfeld sc->limits = &iwn6000_sensitivity_limits; 1313*fd43cf6eSHans Rosenfeld /* Type 6030 cards return IWN_HW_REV_TYPE_6005 */ 1314*fd43cf6eSHans Rosenfeld if (pid == PCI_PRODUCT_INTEL_WIFI_LINK_1030_1 || 1315*fd43cf6eSHans Rosenfeld pid == PCI_PRODUCT_INTEL_WIFI_LINK_1030_2 || 1316*fd43cf6eSHans Rosenfeld pid == PCI_PRODUCT_INTEL_WIFI_LINK_6230_1 || 1317*fd43cf6eSHans Rosenfeld pid == PCI_PRODUCT_INTEL_WIFI_LINK_6230_2 || 1318*fd43cf6eSHans Rosenfeld pid == PCI_PRODUCT_INTEL_WIFI_LINK_6235 || 1319*fd43cf6eSHans Rosenfeld pid == PCI_PRODUCT_INTEL_WIFI_LINK_6235_2) { 1320*fd43cf6eSHans Rosenfeld sc->fwname = "iwlwifi-6000g2b-6.ucode"; 1321*fd43cf6eSHans Rosenfeld ops->config_bt_coex = iwn_config_bt_coex_adv1; 1322*fd43cf6eSHans Rosenfeld } 1323*fd43cf6eSHans Rosenfeld else 1324*fd43cf6eSHans Rosenfeld sc->fwname = "iwlwifi-6000g2a-5.ucode"; 1325*fd43cf6eSHans Rosenfeld 1326*fd43cf6eSHans Rosenfeld iwn_kstat_create(sc, "temp_offset", 1327*fd43cf6eSHans Rosenfeld sizeof (struct iwn_ks_toff_6000), 1328*fd43cf6eSHans Rosenfeld &sc->sc_ks_toff, (void **)&sc->sc_toff.t6000); 1329*fd43cf6eSHans Rosenfeld iwn_kstat_init_6000(sc); 1330*fd43cf6eSHans Rosenfeld break; 1331*fd43cf6eSHans Rosenfeld case IWN_HW_REV_TYPE_2030: 1332*fd43cf6eSHans Rosenfeld sc->limits = &iwn2000_sensitivity_limits; 1333*fd43cf6eSHans Rosenfeld sc->fwname = "iwlwifi-2030-6.ucode"; 1334*fd43cf6eSHans Rosenfeld ops->config_bt_coex = iwn_config_bt_coex_adv2; 1335*fd43cf6eSHans Rosenfeld 1336*fd43cf6eSHans Rosenfeld iwn_kstat_create(sc, "temp_offset", 1337*fd43cf6eSHans Rosenfeld sizeof (struct iwn_ks_toff_2000), 1338*fd43cf6eSHans Rosenfeld &sc->sc_ks_toff, (void **)&sc->sc_toff.t2000); 1339*fd43cf6eSHans Rosenfeld iwn_kstat_init_2000(sc); 1340*fd43cf6eSHans Rosenfeld break; 1341*fd43cf6eSHans Rosenfeld case IWN_HW_REV_TYPE_2000: 1342*fd43cf6eSHans Rosenfeld sc->limits = &iwn2000_sensitivity_limits; 1343*fd43cf6eSHans Rosenfeld sc->fwname = "iwlwifi-2000-6.ucode"; 1344*fd43cf6eSHans Rosenfeld 1345*fd43cf6eSHans Rosenfeld iwn_kstat_create(sc, "temp_offset", 1346*fd43cf6eSHans Rosenfeld sizeof (struct iwn_ks_toff_2000), 1347*fd43cf6eSHans Rosenfeld &sc->sc_ks_toff, (void **)&sc->sc_toff.t2000); 1348*fd43cf6eSHans Rosenfeld iwn_kstat_init_2000(sc); 1349*fd43cf6eSHans Rosenfeld break; 1350*fd43cf6eSHans Rosenfeld case IWN_HW_REV_TYPE_135: 1351*fd43cf6eSHans Rosenfeld sc->limits = &iwn2000_sensitivity_limits; 1352*fd43cf6eSHans Rosenfeld sc->fwname = "iwlwifi-135-6.ucode"; 1353*fd43cf6eSHans Rosenfeld ops->config_bt_coex = iwn_config_bt_coex_adv2; 1354*fd43cf6eSHans Rosenfeld 1355*fd43cf6eSHans Rosenfeld iwn_kstat_create(sc, "temp_offset", 1356*fd43cf6eSHans Rosenfeld sizeof (struct iwn_ks_toff_2000), 1357*fd43cf6eSHans Rosenfeld &sc->sc_ks_toff, (void **)&sc->sc_toff.t2000); 1358*fd43cf6eSHans Rosenfeld iwn_kstat_init_2000(sc); 1359*fd43cf6eSHans Rosenfeld break; 1360*fd43cf6eSHans Rosenfeld case IWN_HW_REV_TYPE_105: 1361*fd43cf6eSHans Rosenfeld sc->limits = &iwn2000_sensitivity_limits; 1362*fd43cf6eSHans Rosenfeld sc->fwname = "iwlwifi-105-6.ucode"; 1363*fd43cf6eSHans Rosenfeld 1364*fd43cf6eSHans Rosenfeld iwn_kstat_create(sc, "temp_offset", 1365*fd43cf6eSHans Rosenfeld sizeof (struct iwn_ks_toff_2000), 1366*fd43cf6eSHans Rosenfeld &sc->sc_ks_toff, (void **)&sc->sc_toff.t2000); 1367*fd43cf6eSHans Rosenfeld iwn_kstat_init_2000(sc); 1368*fd43cf6eSHans Rosenfeld break; 1369*fd43cf6eSHans Rosenfeld default: 1370*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, "!adapter type %d not supported", 1371*fd43cf6eSHans Rosenfeld sc->hw_type); 1372*fd43cf6eSHans Rosenfeld return ENOTSUP; 1373*fd43cf6eSHans Rosenfeld } 1374*fd43cf6eSHans Rosenfeld return 0; 1375*fd43cf6eSHans Rosenfeld } 1376*fd43cf6eSHans Rosenfeld 1377*fd43cf6eSHans Rosenfeld static int 1378*fd43cf6eSHans Rosenfeld iwn_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 1379*fd43cf6eSHans Rosenfeld { 1380*fd43cf6eSHans Rosenfeld struct iwn_softc *sc = ddi_get_driver_private(dip); 1381*fd43cf6eSHans Rosenfeld ieee80211com_t *ic = &sc->sc_ic; 1382*fd43cf6eSHans Rosenfeld int qid, error; 1383*fd43cf6eSHans Rosenfeld 1384*fd43cf6eSHans Rosenfeld switch (cmd) { 1385*fd43cf6eSHans Rosenfeld case DDI_DETACH: 1386*fd43cf6eSHans Rosenfeld break; 1387*fd43cf6eSHans Rosenfeld case DDI_SUSPEND: 1388*fd43cf6eSHans Rosenfeld sc->sc_flags &= ~IWN_FLAG_HW_ERR_RECOVER; 1389*fd43cf6eSHans Rosenfeld sc->sc_flags &= ~IWN_FLAG_RATE_AUTO_CTL; 1390*fd43cf6eSHans Rosenfeld 1391*fd43cf6eSHans Rosenfeld sc->sc_flags |= IWN_FLAG_SUSPEND; 1392*fd43cf6eSHans Rosenfeld 1393*fd43cf6eSHans Rosenfeld if (sc->sc_flags & IWN_FLAG_RUNNING) { 1394*fd43cf6eSHans Rosenfeld iwn_hw_stop(sc, B_TRUE); 1395*fd43cf6eSHans Rosenfeld ieee80211_new_state(ic, IEEE80211_S_INIT, -1); 1396*fd43cf6eSHans Rosenfeld 1397*fd43cf6eSHans Rosenfeld } 1398*fd43cf6eSHans Rosenfeld 1399*fd43cf6eSHans Rosenfeld return (DDI_SUCCESS); 1400*fd43cf6eSHans Rosenfeld default: 1401*fd43cf6eSHans Rosenfeld return (DDI_FAILURE); 1402*fd43cf6eSHans Rosenfeld } 1403*fd43cf6eSHans Rosenfeld 1404*fd43cf6eSHans Rosenfeld if (!(sc->sc_flags & IWN_FLAG_ATTACHED)) { 1405*fd43cf6eSHans Rosenfeld return (DDI_FAILURE); 1406*fd43cf6eSHans Rosenfeld } 1407*fd43cf6eSHans Rosenfeld 1408*fd43cf6eSHans Rosenfeld error = mac_disable(ic->ic_mach); 1409*fd43cf6eSHans Rosenfeld if (error != DDI_SUCCESS) 1410*fd43cf6eSHans Rosenfeld return (error); 1411*fd43cf6eSHans Rosenfeld 1412*fd43cf6eSHans Rosenfeld mutex_enter(&sc->sc_mtx); 1413*fd43cf6eSHans Rosenfeld sc->sc_flags |= IWN_FLAG_STOP_CALIB_TO; 1414*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_mtx); 1415*fd43cf6eSHans Rosenfeld 1416*fd43cf6eSHans Rosenfeld if (sc->calib_to != 0) 1417*fd43cf6eSHans Rosenfeld (void) untimeout(sc->calib_to); 1418*fd43cf6eSHans Rosenfeld sc->calib_to = 0; 1419*fd43cf6eSHans Rosenfeld 1420*fd43cf6eSHans Rosenfeld if (sc->scan_to != 0) 1421*fd43cf6eSHans Rosenfeld (void) untimeout(sc->scan_to); 1422*fd43cf6eSHans Rosenfeld sc->scan_to = 0; 1423*fd43cf6eSHans Rosenfeld 1424*fd43cf6eSHans Rosenfeld ddi_periodic_delete(sc->sc_periodic); 1425*fd43cf6eSHans Rosenfeld 1426*fd43cf6eSHans Rosenfeld /* 1427*fd43cf6eSHans Rosenfeld * stop chipset 1428*fd43cf6eSHans Rosenfeld */ 1429*fd43cf6eSHans Rosenfeld iwn_hw_stop(sc, B_TRUE); 1430*fd43cf6eSHans Rosenfeld 1431*fd43cf6eSHans Rosenfeld /* 1432*fd43cf6eSHans Rosenfeld * Unregister from GLD 1433*fd43cf6eSHans Rosenfeld */ 1434*fd43cf6eSHans Rosenfeld (void) mac_unregister(ic->ic_mach); 1435*fd43cf6eSHans Rosenfeld ieee80211_detach(ic); 1436*fd43cf6eSHans Rosenfeld 1437*fd43cf6eSHans Rosenfeld /* Uninstall interrupt handler. */ 1438*fd43cf6eSHans Rosenfeld iwn_intr_teardown(sc); 1439*fd43cf6eSHans Rosenfeld 1440*fd43cf6eSHans Rosenfeld /* Free DMA resources. */ 1441*fd43cf6eSHans Rosenfeld mutex_enter(&sc->sc_mtx); 1442*fd43cf6eSHans Rosenfeld iwn_free_rx_ring(sc, &sc->rxq); 1443*fd43cf6eSHans Rosenfeld for (qid = 0; qid < sc->ntxqs; qid++) 1444*fd43cf6eSHans Rosenfeld iwn_free_tx_ring(sc, &sc->txq[qid]); 1445*fd43cf6eSHans Rosenfeld iwn_free_sched(sc); 1446*fd43cf6eSHans Rosenfeld iwn_free_kw(sc); 1447*fd43cf6eSHans Rosenfeld if (sc->ict != NULL) 1448*fd43cf6eSHans Rosenfeld iwn_free_ict(sc); 1449*fd43cf6eSHans Rosenfeld iwn_free_fwmem(sc); 1450*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_mtx); 1451*fd43cf6eSHans Rosenfeld 1452*fd43cf6eSHans Rosenfeld iwn_kstat_free(sc->sc_ks_misc, sc->sc_misc, 1453*fd43cf6eSHans Rosenfeld sizeof (struct iwn_ks_misc)); 1454*fd43cf6eSHans Rosenfeld iwn_kstat_free(sc->sc_ks_ant, sc->sc_ant, 1455*fd43cf6eSHans Rosenfeld sizeof (struct iwn_ks_ant)); 1456*fd43cf6eSHans Rosenfeld iwn_kstat_free(sc->sc_ks_sens, sc->sc_sens, 1457*fd43cf6eSHans Rosenfeld sizeof (struct iwn_ks_sens)); 1458*fd43cf6eSHans Rosenfeld iwn_kstat_free(sc->sc_ks_timing, sc->sc_timing, 1459*fd43cf6eSHans Rosenfeld sizeof (struct iwn_ks_timing)); 1460*fd43cf6eSHans Rosenfeld iwn_kstat_free(sc->sc_ks_edca, sc->sc_edca, 1461*fd43cf6eSHans Rosenfeld sizeof (struct iwn_ks_edca)); 1462*fd43cf6eSHans Rosenfeld iwn_kstat_free(sc->sc_ks_txpower, sc->sc_txpower, 1463*fd43cf6eSHans Rosenfeld sizeof (struct iwn_ks_txpower)); 1464*fd43cf6eSHans Rosenfeld 1465*fd43cf6eSHans Rosenfeld if (sc->hw_type == IWN_HW_REV_TYPE_6005) 1466*fd43cf6eSHans Rosenfeld iwn_kstat_free(sc->sc_ks_toff, sc->sc_toff.t6000, 1467*fd43cf6eSHans Rosenfeld sizeof (struct iwn_ks_toff_6000)); 1468*fd43cf6eSHans Rosenfeld else 1469*fd43cf6eSHans Rosenfeld iwn_kstat_free(sc->sc_ks_toff, sc->sc_toff.t2000, 1470*fd43cf6eSHans Rosenfeld sizeof (struct iwn_ks_toff_2000)); 1471*fd43cf6eSHans Rosenfeld 1472*fd43cf6eSHans Rosenfeld ddi_regs_map_free(&sc->sc_regh); 1473*fd43cf6eSHans Rosenfeld pci_config_teardown(&sc->sc_pcih); 1474*fd43cf6eSHans Rosenfeld ddi_remove_minor_node(dip, NULL); 1475*fd43cf6eSHans Rosenfeld ddi_soft_state_free(iwn_state, ddi_get_instance(dip)); 1476*fd43cf6eSHans Rosenfeld 1477*fd43cf6eSHans Rosenfeld return 0; 1478*fd43cf6eSHans Rosenfeld } 1479*fd43cf6eSHans Rosenfeld 1480*fd43cf6eSHans Rosenfeld static int 1481*fd43cf6eSHans Rosenfeld iwn_quiesce(dev_info_t *dip) 1482*fd43cf6eSHans Rosenfeld { 1483*fd43cf6eSHans Rosenfeld struct iwn_softc *sc; 1484*fd43cf6eSHans Rosenfeld 1485*fd43cf6eSHans Rosenfeld sc = ddi_get_soft_state(iwn_state, ddi_get_instance(dip)); 1486*fd43cf6eSHans Rosenfeld if (sc == NULL) 1487*fd43cf6eSHans Rosenfeld return (DDI_FAILURE); 1488*fd43cf6eSHans Rosenfeld 1489*fd43cf6eSHans Rosenfeld #ifdef IWN_DEBUG 1490*fd43cf6eSHans Rosenfeld /* bypass any messages */ 1491*fd43cf6eSHans Rosenfeld iwn_dbg_print = 0; 1492*fd43cf6eSHans Rosenfeld #endif 1493*fd43cf6eSHans Rosenfeld 1494*fd43cf6eSHans Rosenfeld /* 1495*fd43cf6eSHans Rosenfeld * No more blocking is allowed while we are in the 1496*fd43cf6eSHans Rosenfeld * quiesce(9E) entry point. 1497*fd43cf6eSHans Rosenfeld */ 1498*fd43cf6eSHans Rosenfeld sc->sc_flags |= IWN_FLAG_QUIESCED; 1499*fd43cf6eSHans Rosenfeld 1500*fd43cf6eSHans Rosenfeld /* 1501*fd43cf6eSHans Rosenfeld * Disable and mask all interrupts. 1502*fd43cf6eSHans Rosenfeld */ 1503*fd43cf6eSHans Rosenfeld iwn_hw_stop(sc, B_FALSE); 1504*fd43cf6eSHans Rosenfeld 1505*fd43cf6eSHans Rosenfeld return (DDI_SUCCESS); 1506*fd43cf6eSHans Rosenfeld } 1507*fd43cf6eSHans Rosenfeld 1508*fd43cf6eSHans Rosenfeld static int 1509*fd43cf6eSHans Rosenfeld iwn_nic_lock(struct iwn_softc *sc) 1510*fd43cf6eSHans Rosenfeld { 1511*fd43cf6eSHans Rosenfeld int ntries; 1512*fd43cf6eSHans Rosenfeld 1513*fd43cf6eSHans Rosenfeld /* Request exclusive access to NIC. */ 1514*fd43cf6eSHans Rosenfeld IWN_SETBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_MAC_ACCESS_REQ); 1515*fd43cf6eSHans Rosenfeld 1516*fd43cf6eSHans Rosenfeld /* Spin until we actually get the lock. */ 1517*fd43cf6eSHans Rosenfeld for (ntries = 0; ntries < 1000; ntries++) { 1518*fd43cf6eSHans Rosenfeld if ((IWN_READ(sc, IWN_GP_CNTRL) & 1519*fd43cf6eSHans Rosenfeld (IWN_GP_CNTRL_MAC_ACCESS_ENA | IWN_GP_CNTRL_SLEEP)) == 1520*fd43cf6eSHans Rosenfeld IWN_GP_CNTRL_MAC_ACCESS_ENA) 1521*fd43cf6eSHans Rosenfeld return 0; 1522*fd43cf6eSHans Rosenfeld DELAY(10); 1523*fd43cf6eSHans Rosenfeld } 1524*fd43cf6eSHans Rosenfeld return ETIMEDOUT; 1525*fd43cf6eSHans Rosenfeld } 1526*fd43cf6eSHans Rosenfeld 1527*fd43cf6eSHans Rosenfeld static __inline void 1528*fd43cf6eSHans Rosenfeld iwn_nic_unlock(struct iwn_softc *sc) 1529*fd43cf6eSHans Rosenfeld { 1530*fd43cf6eSHans Rosenfeld IWN_CLRBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_MAC_ACCESS_REQ); 1531*fd43cf6eSHans Rosenfeld } 1532*fd43cf6eSHans Rosenfeld 1533*fd43cf6eSHans Rosenfeld static __inline uint32_t 1534*fd43cf6eSHans Rosenfeld iwn_prph_read(struct iwn_softc *sc, uint32_t addr) 1535*fd43cf6eSHans Rosenfeld { 1536*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_PRPH_RADDR, IWN_PRPH_DWORD | addr); 1537*fd43cf6eSHans Rosenfeld IWN_BARRIER_READ_WRITE(sc); 1538*fd43cf6eSHans Rosenfeld return IWN_READ(sc, IWN_PRPH_RDATA); 1539*fd43cf6eSHans Rosenfeld } 1540*fd43cf6eSHans Rosenfeld 1541*fd43cf6eSHans Rosenfeld static __inline void 1542*fd43cf6eSHans Rosenfeld iwn_prph_write(struct iwn_softc *sc, uint32_t addr, uint32_t data) 1543*fd43cf6eSHans Rosenfeld { 1544*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_PRPH_WADDR, IWN_PRPH_DWORD | addr); 1545*fd43cf6eSHans Rosenfeld IWN_BARRIER_WRITE(sc); 1546*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_PRPH_WDATA, data); 1547*fd43cf6eSHans Rosenfeld } 1548*fd43cf6eSHans Rosenfeld 1549*fd43cf6eSHans Rosenfeld static __inline void 1550*fd43cf6eSHans Rosenfeld iwn_prph_setbits(struct iwn_softc *sc, uint32_t addr, uint32_t mask) 1551*fd43cf6eSHans Rosenfeld { 1552*fd43cf6eSHans Rosenfeld iwn_prph_write(sc, addr, iwn_prph_read(sc, addr) | mask); 1553*fd43cf6eSHans Rosenfeld } 1554*fd43cf6eSHans Rosenfeld 1555*fd43cf6eSHans Rosenfeld static __inline void 1556*fd43cf6eSHans Rosenfeld iwn_prph_clrbits(struct iwn_softc *sc, uint32_t addr, uint32_t mask) 1557*fd43cf6eSHans Rosenfeld { 1558*fd43cf6eSHans Rosenfeld iwn_prph_write(sc, addr, iwn_prph_read(sc, addr) & ~mask); 1559*fd43cf6eSHans Rosenfeld } 1560*fd43cf6eSHans Rosenfeld 1561*fd43cf6eSHans Rosenfeld static __inline void 1562*fd43cf6eSHans Rosenfeld iwn_prph_write_region_4(struct iwn_softc *sc, uint32_t addr, 1563*fd43cf6eSHans Rosenfeld const uint32_t *data, int count) 1564*fd43cf6eSHans Rosenfeld { 1565*fd43cf6eSHans Rosenfeld for (; count > 0; count--, data++, addr += 4) 1566*fd43cf6eSHans Rosenfeld iwn_prph_write(sc, addr, *data); 1567*fd43cf6eSHans Rosenfeld } 1568*fd43cf6eSHans Rosenfeld 1569*fd43cf6eSHans Rosenfeld static __inline uint32_t 1570*fd43cf6eSHans Rosenfeld iwn_mem_read(struct iwn_softc *sc, uint32_t addr) 1571*fd43cf6eSHans Rosenfeld { 1572*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_MEM_RADDR, addr); 1573*fd43cf6eSHans Rosenfeld IWN_BARRIER_READ_WRITE(sc); 1574*fd43cf6eSHans Rosenfeld return IWN_READ(sc, IWN_MEM_RDATA); 1575*fd43cf6eSHans Rosenfeld } 1576*fd43cf6eSHans Rosenfeld 1577*fd43cf6eSHans Rosenfeld static __inline void 1578*fd43cf6eSHans Rosenfeld iwn_mem_write(struct iwn_softc *sc, uint32_t addr, uint32_t data) 1579*fd43cf6eSHans Rosenfeld { 1580*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_MEM_WADDR, addr); 1581*fd43cf6eSHans Rosenfeld IWN_BARRIER_WRITE(sc); 1582*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_MEM_WDATA, data); 1583*fd43cf6eSHans Rosenfeld } 1584*fd43cf6eSHans Rosenfeld 1585*fd43cf6eSHans Rosenfeld #ifndef IEEE80211_NO_HT 1586*fd43cf6eSHans Rosenfeld static __inline void 1587*fd43cf6eSHans Rosenfeld iwn_mem_write_2(struct iwn_softc *sc, uint32_t addr, uint16_t data) 1588*fd43cf6eSHans Rosenfeld { 1589*fd43cf6eSHans Rosenfeld uint32_t tmp; 1590*fd43cf6eSHans Rosenfeld 1591*fd43cf6eSHans Rosenfeld tmp = iwn_mem_read(sc, addr & ~3); 1592*fd43cf6eSHans Rosenfeld if (addr & 3) 1593*fd43cf6eSHans Rosenfeld tmp = (tmp & 0x0000ffff) | data << 16; 1594*fd43cf6eSHans Rosenfeld else 1595*fd43cf6eSHans Rosenfeld tmp = (tmp & 0xffff0000) | data; 1596*fd43cf6eSHans Rosenfeld iwn_mem_write(sc, addr & ~3, tmp); 1597*fd43cf6eSHans Rosenfeld } 1598*fd43cf6eSHans Rosenfeld #endif 1599*fd43cf6eSHans Rosenfeld 1600*fd43cf6eSHans Rosenfeld static __inline void 1601*fd43cf6eSHans Rosenfeld iwn_mem_read_region_4(struct iwn_softc *sc, uint32_t addr, uint32_t *data, 1602*fd43cf6eSHans Rosenfeld int count) 1603*fd43cf6eSHans Rosenfeld { 1604*fd43cf6eSHans Rosenfeld for (; count > 0; count--, addr += 4) 1605*fd43cf6eSHans Rosenfeld *data++ = iwn_mem_read(sc, addr); 1606*fd43cf6eSHans Rosenfeld } 1607*fd43cf6eSHans Rosenfeld 1608*fd43cf6eSHans Rosenfeld static __inline void 1609*fd43cf6eSHans Rosenfeld iwn_mem_set_region_4(struct iwn_softc *sc, uint32_t addr, uint32_t val, 1610*fd43cf6eSHans Rosenfeld int count) 1611*fd43cf6eSHans Rosenfeld { 1612*fd43cf6eSHans Rosenfeld for (; count > 0; count--, addr += 4) 1613*fd43cf6eSHans Rosenfeld iwn_mem_write(sc, addr, val); 1614*fd43cf6eSHans Rosenfeld } 1615*fd43cf6eSHans Rosenfeld 1616*fd43cf6eSHans Rosenfeld static int 1617*fd43cf6eSHans Rosenfeld iwn_eeprom_lock(struct iwn_softc *sc) 1618*fd43cf6eSHans Rosenfeld { 1619*fd43cf6eSHans Rosenfeld int i, ntries; 1620*fd43cf6eSHans Rosenfeld 1621*fd43cf6eSHans Rosenfeld for (i = 0; i < 100; i++) { 1622*fd43cf6eSHans Rosenfeld /* Request exclusive access to EEPROM. */ 1623*fd43cf6eSHans Rosenfeld IWN_SETBITS(sc, IWN_HW_IF_CONFIG, 1624*fd43cf6eSHans Rosenfeld IWN_HW_IF_CONFIG_EEPROM_LOCKED); 1625*fd43cf6eSHans Rosenfeld 1626*fd43cf6eSHans Rosenfeld /* Spin until we actually get the lock. */ 1627*fd43cf6eSHans Rosenfeld for (ntries = 0; ntries < 100; ntries++) { 1628*fd43cf6eSHans Rosenfeld if (IWN_READ(sc, IWN_HW_IF_CONFIG) & 1629*fd43cf6eSHans Rosenfeld IWN_HW_IF_CONFIG_EEPROM_LOCKED) 1630*fd43cf6eSHans Rosenfeld return 0; 1631*fd43cf6eSHans Rosenfeld DELAY(10); 1632*fd43cf6eSHans Rosenfeld } 1633*fd43cf6eSHans Rosenfeld } 1634*fd43cf6eSHans Rosenfeld return ETIMEDOUT; 1635*fd43cf6eSHans Rosenfeld } 1636*fd43cf6eSHans Rosenfeld 1637*fd43cf6eSHans Rosenfeld static __inline void 1638*fd43cf6eSHans Rosenfeld iwn_eeprom_unlock(struct iwn_softc *sc) 1639*fd43cf6eSHans Rosenfeld { 1640*fd43cf6eSHans Rosenfeld IWN_CLRBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_EEPROM_LOCKED); 1641*fd43cf6eSHans Rosenfeld } 1642*fd43cf6eSHans Rosenfeld 1643*fd43cf6eSHans Rosenfeld /* 1644*fd43cf6eSHans Rosenfeld * Initialize access by host to One Time Programmable ROM. 1645*fd43cf6eSHans Rosenfeld * NB: This kind of ROM can be found on 1000 or 6000 Series only. 1646*fd43cf6eSHans Rosenfeld */ 1647*fd43cf6eSHans Rosenfeld static int 1648*fd43cf6eSHans Rosenfeld iwn_init_otprom(struct iwn_softc *sc) 1649*fd43cf6eSHans Rosenfeld { 1650*fd43cf6eSHans Rosenfeld uint16_t prev = 0, base, next; 1651*fd43cf6eSHans Rosenfeld int count, error; 1652*fd43cf6eSHans Rosenfeld 1653*fd43cf6eSHans Rosenfeld /* Wait for clock stabilization before accessing prph. */ 1654*fd43cf6eSHans Rosenfeld if ((error = iwn_clock_wait(sc)) != 0) 1655*fd43cf6eSHans Rosenfeld return error; 1656*fd43cf6eSHans Rosenfeld 1657*fd43cf6eSHans Rosenfeld if ((error = iwn_nic_lock(sc)) != 0) 1658*fd43cf6eSHans Rosenfeld return error; 1659*fd43cf6eSHans Rosenfeld iwn_prph_setbits(sc, IWN_APMG_PS, IWN_APMG_PS_RESET_REQ); 1660*fd43cf6eSHans Rosenfeld DELAY(5); 1661*fd43cf6eSHans Rosenfeld iwn_prph_clrbits(sc, IWN_APMG_PS, IWN_APMG_PS_RESET_REQ); 1662*fd43cf6eSHans Rosenfeld iwn_nic_unlock(sc); 1663*fd43cf6eSHans Rosenfeld 1664*fd43cf6eSHans Rosenfeld /* Set auto clock gate disable bit for HW with OTP shadow RAM. */ 1665*fd43cf6eSHans Rosenfeld if (sc->hw_type != IWN_HW_REV_TYPE_1000) { 1666*fd43cf6eSHans Rosenfeld IWN_SETBITS(sc, IWN_DBG_LINK_PWR_MGMT, 1667*fd43cf6eSHans Rosenfeld IWN_RESET_LINK_PWR_MGMT_DIS); 1668*fd43cf6eSHans Rosenfeld } 1669*fd43cf6eSHans Rosenfeld IWN_CLRBITS(sc, IWN_EEPROM_GP, IWN_EEPROM_GP_IF_OWNER); 1670*fd43cf6eSHans Rosenfeld /* Clear ECC status. */ 1671*fd43cf6eSHans Rosenfeld IWN_SETBITS(sc, IWN_OTP_GP, 1672*fd43cf6eSHans Rosenfeld IWN_OTP_GP_ECC_CORR_STTS | IWN_OTP_GP_ECC_UNCORR_STTS); 1673*fd43cf6eSHans Rosenfeld 1674*fd43cf6eSHans Rosenfeld /* 1675*fd43cf6eSHans Rosenfeld * Find the block before last block (contains the EEPROM image) 1676*fd43cf6eSHans Rosenfeld * for HW without OTP shadow RAM. 1677*fd43cf6eSHans Rosenfeld */ 1678*fd43cf6eSHans Rosenfeld if (sc->hw_type == IWN_HW_REV_TYPE_1000) { 1679*fd43cf6eSHans Rosenfeld /* Switch to absolute addressing mode. */ 1680*fd43cf6eSHans Rosenfeld IWN_CLRBITS(sc, IWN_OTP_GP, IWN_OTP_GP_RELATIVE_ACCESS); 1681*fd43cf6eSHans Rosenfeld base = 0; 1682*fd43cf6eSHans Rosenfeld for (count = 0; count < IWN1000_OTP_NBLOCKS; count++) { 1683*fd43cf6eSHans Rosenfeld error = iwn_read_prom_data(sc, base, &next, 2); 1684*fd43cf6eSHans Rosenfeld if (error != 0) 1685*fd43cf6eSHans Rosenfeld return error; 1686*fd43cf6eSHans Rosenfeld if (next == 0) /* End of linked-list. */ 1687*fd43cf6eSHans Rosenfeld break; 1688*fd43cf6eSHans Rosenfeld prev = base; 1689*fd43cf6eSHans Rosenfeld base = le16toh(next); 1690*fd43cf6eSHans Rosenfeld } 1691*fd43cf6eSHans Rosenfeld if (count == 0 || count == IWN1000_OTP_NBLOCKS) 1692*fd43cf6eSHans Rosenfeld return EIO; 1693*fd43cf6eSHans Rosenfeld /* Skip "next" word. */ 1694*fd43cf6eSHans Rosenfeld sc->prom_base = prev + 1; 1695*fd43cf6eSHans Rosenfeld } 1696*fd43cf6eSHans Rosenfeld return 0; 1697*fd43cf6eSHans Rosenfeld } 1698*fd43cf6eSHans Rosenfeld 1699*fd43cf6eSHans Rosenfeld static int 1700*fd43cf6eSHans Rosenfeld iwn_read_prom_data(struct iwn_softc *sc, uint32_t addr, void *data, int count) 1701*fd43cf6eSHans Rosenfeld { 1702*fd43cf6eSHans Rosenfeld uint8_t *out = data; 1703*fd43cf6eSHans Rosenfeld uint32_t val, tmp; 1704*fd43cf6eSHans Rosenfeld int ntries; 1705*fd43cf6eSHans Rosenfeld 1706*fd43cf6eSHans Rosenfeld addr += sc->prom_base; 1707*fd43cf6eSHans Rosenfeld for (; count > 0; count -= 2, addr++) { 1708*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_EEPROM, addr << 2); 1709*fd43cf6eSHans Rosenfeld for (ntries = 0; ntries < 10; ntries++) { 1710*fd43cf6eSHans Rosenfeld val = IWN_READ(sc, IWN_EEPROM); 1711*fd43cf6eSHans Rosenfeld if (val & IWN_EEPROM_READ_VALID) 1712*fd43cf6eSHans Rosenfeld break; 1713*fd43cf6eSHans Rosenfeld DELAY(5); 1714*fd43cf6eSHans Rosenfeld } 1715*fd43cf6eSHans Rosenfeld if (ntries == 10) { 1716*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 1717*fd43cf6eSHans Rosenfeld "!timeout reading ROM at 0x%x", addr); 1718*fd43cf6eSHans Rosenfeld return ETIMEDOUT; 1719*fd43cf6eSHans Rosenfeld } 1720*fd43cf6eSHans Rosenfeld if (sc->sc_flags & IWN_FLAG_HAS_OTPROM) { 1721*fd43cf6eSHans Rosenfeld /* OTPROM, check for ECC errors. */ 1722*fd43cf6eSHans Rosenfeld tmp = IWN_READ(sc, IWN_OTP_GP); 1723*fd43cf6eSHans Rosenfeld if (tmp & IWN_OTP_GP_ECC_UNCORR_STTS) { 1724*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 1725*fd43cf6eSHans Rosenfeld "!OTPROM ECC error at 0x%x", addr); 1726*fd43cf6eSHans Rosenfeld return EIO; 1727*fd43cf6eSHans Rosenfeld } 1728*fd43cf6eSHans Rosenfeld if (tmp & IWN_OTP_GP_ECC_CORR_STTS) { 1729*fd43cf6eSHans Rosenfeld /* Correctable ECC error, clear bit. */ 1730*fd43cf6eSHans Rosenfeld IWN_SETBITS(sc, IWN_OTP_GP, 1731*fd43cf6eSHans Rosenfeld IWN_OTP_GP_ECC_CORR_STTS); 1732*fd43cf6eSHans Rosenfeld } 1733*fd43cf6eSHans Rosenfeld } 1734*fd43cf6eSHans Rosenfeld *out++ = val >> 16; 1735*fd43cf6eSHans Rosenfeld if (count > 1) 1736*fd43cf6eSHans Rosenfeld *out++ = val >> 24; 1737*fd43cf6eSHans Rosenfeld } 1738*fd43cf6eSHans Rosenfeld return 0; 1739*fd43cf6eSHans Rosenfeld } 1740*fd43cf6eSHans Rosenfeld 1741*fd43cf6eSHans Rosenfeld static int 1742*fd43cf6eSHans Rosenfeld iwn_dma_contig_alloc(struct iwn_softc *sc, struct iwn_dma_info *dma, 1743*fd43cf6eSHans Rosenfeld uint_t size, uint_t flags, void **kvap, ddi_device_acc_attr_t *acc_attr, 1744*fd43cf6eSHans Rosenfeld uint_t align) 1745*fd43cf6eSHans Rosenfeld { 1746*fd43cf6eSHans Rosenfeld ddi_dma_attr_t dma_attr = { 1747*fd43cf6eSHans Rosenfeld .dma_attr_version = DMA_ATTR_V0, 1748*fd43cf6eSHans Rosenfeld .dma_attr_addr_lo = 0, 1749*fd43cf6eSHans Rosenfeld .dma_attr_addr_hi = 0xfffffffffULL, 1750*fd43cf6eSHans Rosenfeld .dma_attr_count_max = 0xfffffffffULL, 1751*fd43cf6eSHans Rosenfeld .dma_attr_align = align, 1752*fd43cf6eSHans Rosenfeld .dma_attr_burstsizes = 0x7ff, 1753*fd43cf6eSHans Rosenfeld .dma_attr_minxfer = 1, 1754*fd43cf6eSHans Rosenfeld .dma_attr_maxxfer = 0xfffffffffULL, 1755*fd43cf6eSHans Rosenfeld .dma_attr_seg = 0xfffffffffULL, 1756*fd43cf6eSHans Rosenfeld .dma_attr_sgllen = 1, 1757*fd43cf6eSHans Rosenfeld .dma_attr_granular = 1, 1758*fd43cf6eSHans Rosenfeld .dma_attr_flags = 0, 1759*fd43cf6eSHans Rosenfeld }; 1760*fd43cf6eSHans Rosenfeld int error; 1761*fd43cf6eSHans Rosenfeld 1762*fd43cf6eSHans Rosenfeld error = ddi_dma_alloc_handle(sc->sc_dip, &dma_attr, DDI_DMA_SLEEP, NULL, 1763*fd43cf6eSHans Rosenfeld &dma->dma_hdl); 1764*fd43cf6eSHans Rosenfeld if (error != DDI_SUCCESS) { 1765*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 1766*fd43cf6eSHans Rosenfeld "ddi_dma_alloc_handle() failed, error = %d", error); 1767*fd43cf6eSHans Rosenfeld goto fail; 1768*fd43cf6eSHans Rosenfeld } 1769*fd43cf6eSHans Rosenfeld 1770*fd43cf6eSHans Rosenfeld error = ddi_dma_mem_alloc(dma->dma_hdl, size, acc_attr, 1771*fd43cf6eSHans Rosenfeld flags & (DDI_DMA_CONSISTENT | DDI_DMA_STREAMING), DDI_DMA_SLEEP, 0, 1772*fd43cf6eSHans Rosenfeld &dma->vaddr, &dma->length, &dma->acc_hdl); 1773*fd43cf6eSHans Rosenfeld if (error != DDI_SUCCESS) { 1774*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 1775*fd43cf6eSHans Rosenfeld "ddi_dma_mem_alloc() failed, error = %d", error); 1776*fd43cf6eSHans Rosenfeld goto fail2; 1777*fd43cf6eSHans Rosenfeld } 1778*fd43cf6eSHans Rosenfeld 1779*fd43cf6eSHans Rosenfeld bzero(dma->vaddr, dma->length); 1780*fd43cf6eSHans Rosenfeld 1781*fd43cf6eSHans Rosenfeld error = ddi_dma_addr_bind_handle(dma->dma_hdl, NULL, dma->vaddr, 1782*fd43cf6eSHans Rosenfeld dma->length, flags, DDI_DMA_SLEEP, NULL, &dma->cookie, 1783*fd43cf6eSHans Rosenfeld &dma->ncookies); 1784*fd43cf6eSHans Rosenfeld if (error != DDI_DMA_MAPPED) { 1785*fd43cf6eSHans Rosenfeld dma->ncookies = 0; 1786*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 1787*fd43cf6eSHans Rosenfeld "ddi_dma_addr_bind_handle() failed, error = %d", error); 1788*fd43cf6eSHans Rosenfeld goto fail3; 1789*fd43cf6eSHans Rosenfeld } 1790*fd43cf6eSHans Rosenfeld 1791*fd43cf6eSHans Rosenfeld dma->size = size; 1792*fd43cf6eSHans Rosenfeld dma->paddr = dma->cookie.dmac_laddress; 1793*fd43cf6eSHans Rosenfeld 1794*fd43cf6eSHans Rosenfeld if (kvap != NULL) 1795*fd43cf6eSHans Rosenfeld *kvap = (void *)dma->vaddr; 1796*fd43cf6eSHans Rosenfeld 1797*fd43cf6eSHans Rosenfeld return (DDI_SUCCESS); 1798*fd43cf6eSHans Rosenfeld 1799*fd43cf6eSHans Rosenfeld fail3: 1800*fd43cf6eSHans Rosenfeld ddi_dma_mem_free(&dma->acc_hdl); 1801*fd43cf6eSHans Rosenfeld fail2: 1802*fd43cf6eSHans Rosenfeld ddi_dma_free_handle(&dma->dma_hdl); 1803*fd43cf6eSHans Rosenfeld fail: 1804*fd43cf6eSHans Rosenfeld bzero(dma, sizeof (struct iwn_dma_info)); 1805*fd43cf6eSHans Rosenfeld return (DDI_FAILURE); 1806*fd43cf6eSHans Rosenfeld } 1807*fd43cf6eSHans Rosenfeld 1808*fd43cf6eSHans Rosenfeld static void 1809*fd43cf6eSHans Rosenfeld iwn_dma_contig_free(struct iwn_dma_info *dma) 1810*fd43cf6eSHans Rosenfeld { 1811*fd43cf6eSHans Rosenfeld if (dma->dma_hdl != NULL) { 1812*fd43cf6eSHans Rosenfeld if (dma->ncookies) 1813*fd43cf6eSHans Rosenfeld (void) ddi_dma_unbind_handle(dma->dma_hdl); 1814*fd43cf6eSHans Rosenfeld ddi_dma_free_handle(&dma->dma_hdl); 1815*fd43cf6eSHans Rosenfeld } 1816*fd43cf6eSHans Rosenfeld 1817*fd43cf6eSHans Rosenfeld if (dma->acc_hdl != NULL) 1818*fd43cf6eSHans Rosenfeld ddi_dma_mem_free(&dma->acc_hdl); 1819*fd43cf6eSHans Rosenfeld 1820*fd43cf6eSHans Rosenfeld bzero(dma, sizeof (struct iwn_dma_info)); 1821*fd43cf6eSHans Rosenfeld } 1822*fd43cf6eSHans Rosenfeld 1823*fd43cf6eSHans Rosenfeld static int 1824*fd43cf6eSHans Rosenfeld iwn_alloc_sched(struct iwn_softc *sc) 1825*fd43cf6eSHans Rosenfeld { 1826*fd43cf6eSHans Rosenfeld /* TX scheduler rings must be aligned on a 1KB boundary. */ 1827*fd43cf6eSHans Rosenfeld 1828*fd43cf6eSHans Rosenfeld return iwn_dma_contig_alloc(sc, &sc->sched_dma, sc->schedsz, 1829*fd43cf6eSHans Rosenfeld DDI_DMA_CONSISTENT | DDI_DMA_RDWR, (void **)&sc->sched, 1830*fd43cf6eSHans Rosenfeld &iwn_dma_accattr, 1024); 1831*fd43cf6eSHans Rosenfeld } 1832*fd43cf6eSHans Rosenfeld 1833*fd43cf6eSHans Rosenfeld static void 1834*fd43cf6eSHans Rosenfeld iwn_free_sched(struct iwn_softc *sc) 1835*fd43cf6eSHans Rosenfeld { 1836*fd43cf6eSHans Rosenfeld iwn_dma_contig_free(&sc->sched_dma); 1837*fd43cf6eSHans Rosenfeld } 1838*fd43cf6eSHans Rosenfeld 1839*fd43cf6eSHans Rosenfeld static int 1840*fd43cf6eSHans Rosenfeld iwn_alloc_kw(struct iwn_softc *sc) 1841*fd43cf6eSHans Rosenfeld { 1842*fd43cf6eSHans Rosenfeld /* "Keep Warm" page must be aligned on a 4KB boundary. */ 1843*fd43cf6eSHans Rosenfeld 1844*fd43cf6eSHans Rosenfeld return iwn_dma_contig_alloc(sc, &sc->kw_dma, IWN_KW_SIZE, 1845*fd43cf6eSHans Rosenfeld DDI_DMA_CONSISTENT | DDI_DMA_RDWR, NULL, &iwn_dma_accattr, 4096); 1846*fd43cf6eSHans Rosenfeld } 1847*fd43cf6eSHans Rosenfeld 1848*fd43cf6eSHans Rosenfeld static void 1849*fd43cf6eSHans Rosenfeld iwn_free_kw(struct iwn_softc *sc) 1850*fd43cf6eSHans Rosenfeld { 1851*fd43cf6eSHans Rosenfeld iwn_dma_contig_free(&sc->kw_dma); 1852*fd43cf6eSHans Rosenfeld } 1853*fd43cf6eSHans Rosenfeld 1854*fd43cf6eSHans Rosenfeld static int 1855*fd43cf6eSHans Rosenfeld iwn_alloc_ict(struct iwn_softc *sc) 1856*fd43cf6eSHans Rosenfeld { 1857*fd43cf6eSHans Rosenfeld /* ICT table must be aligned on a 4KB boundary. */ 1858*fd43cf6eSHans Rosenfeld 1859*fd43cf6eSHans Rosenfeld return iwn_dma_contig_alloc(sc, &sc->ict_dma, IWN_ICT_SIZE, 1860*fd43cf6eSHans Rosenfeld DDI_DMA_CONSISTENT | DDI_DMA_RDWR, (void **)&sc->ict, 1861*fd43cf6eSHans Rosenfeld &iwn_dma_descattr, 4096); 1862*fd43cf6eSHans Rosenfeld } 1863*fd43cf6eSHans Rosenfeld 1864*fd43cf6eSHans Rosenfeld static void 1865*fd43cf6eSHans Rosenfeld iwn_free_ict(struct iwn_softc *sc) 1866*fd43cf6eSHans Rosenfeld { 1867*fd43cf6eSHans Rosenfeld iwn_dma_contig_free(&sc->ict_dma); 1868*fd43cf6eSHans Rosenfeld } 1869*fd43cf6eSHans Rosenfeld 1870*fd43cf6eSHans Rosenfeld static int 1871*fd43cf6eSHans Rosenfeld iwn_alloc_fwmem(struct iwn_softc *sc) 1872*fd43cf6eSHans Rosenfeld { 1873*fd43cf6eSHans Rosenfeld /* Must be aligned on a 16-byte boundary. */ 1874*fd43cf6eSHans Rosenfeld return iwn_dma_contig_alloc(sc, &sc->fw_dma, sc->fwsz, 1875*fd43cf6eSHans Rosenfeld DDI_DMA_CONSISTENT | DDI_DMA_RDWR, NULL, &iwn_dma_accattr, 16); 1876*fd43cf6eSHans Rosenfeld } 1877*fd43cf6eSHans Rosenfeld 1878*fd43cf6eSHans Rosenfeld static void 1879*fd43cf6eSHans Rosenfeld iwn_free_fwmem(struct iwn_softc *sc) 1880*fd43cf6eSHans Rosenfeld { 1881*fd43cf6eSHans Rosenfeld iwn_dma_contig_free(&sc->fw_dma); 1882*fd43cf6eSHans Rosenfeld } 1883*fd43cf6eSHans Rosenfeld 1884*fd43cf6eSHans Rosenfeld static int 1885*fd43cf6eSHans Rosenfeld iwn_alloc_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring) 1886*fd43cf6eSHans Rosenfeld { 1887*fd43cf6eSHans Rosenfeld size_t size; 1888*fd43cf6eSHans Rosenfeld int i, error; 1889*fd43cf6eSHans Rosenfeld 1890*fd43cf6eSHans Rosenfeld ring->cur = 0; 1891*fd43cf6eSHans Rosenfeld 1892*fd43cf6eSHans Rosenfeld /* Allocate RX descriptors (256-byte aligned). */ 1893*fd43cf6eSHans Rosenfeld size = IWN_RX_RING_COUNT * sizeof (uint32_t); 1894*fd43cf6eSHans Rosenfeld error = iwn_dma_contig_alloc(sc, &ring->desc_dma, size, 1895*fd43cf6eSHans Rosenfeld DDI_DMA_CONSISTENT | DDI_DMA_RDWR, (void **)&ring->desc, 1896*fd43cf6eSHans Rosenfeld &iwn_dma_descattr, 256); 1897*fd43cf6eSHans Rosenfeld if (error != DDI_SUCCESS) { 1898*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 1899*fd43cf6eSHans Rosenfeld "!could not allocate RX ring DMA memory"); 1900*fd43cf6eSHans Rosenfeld goto fail; 1901*fd43cf6eSHans Rosenfeld } 1902*fd43cf6eSHans Rosenfeld 1903*fd43cf6eSHans Rosenfeld /* Allocate RX status area (16-byte aligned). */ 1904*fd43cf6eSHans Rosenfeld error = iwn_dma_contig_alloc(sc, &ring->stat_dma, 1905*fd43cf6eSHans Rosenfeld sizeof (struct iwn_rx_status), DDI_DMA_CONSISTENT | DDI_DMA_RDWR, 1906*fd43cf6eSHans Rosenfeld (void **)&ring->stat, &iwn_dma_descattr, 16); 1907*fd43cf6eSHans Rosenfeld if (error != DDI_SUCCESS) { 1908*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 1909*fd43cf6eSHans Rosenfeld "!could not allocate RX status DMA memory"); 1910*fd43cf6eSHans Rosenfeld goto fail; 1911*fd43cf6eSHans Rosenfeld } 1912*fd43cf6eSHans Rosenfeld 1913*fd43cf6eSHans Rosenfeld /* 1914*fd43cf6eSHans Rosenfeld * Allocate and map RX buffers. 1915*fd43cf6eSHans Rosenfeld */ 1916*fd43cf6eSHans Rosenfeld for (i = 0; i < IWN_RX_RING_COUNT; i++) { 1917*fd43cf6eSHans Rosenfeld struct iwn_rx_data *data = &ring->data[i]; 1918*fd43cf6eSHans Rosenfeld 1919*fd43cf6eSHans Rosenfeld error = iwn_dma_contig_alloc(sc, &data->dma_data, IWN_RBUF_SIZE, 1920*fd43cf6eSHans Rosenfeld DDI_DMA_CONSISTENT | DDI_DMA_READ, NULL, &iwn_dma_accattr, 1921*fd43cf6eSHans Rosenfeld 256); 1922*fd43cf6eSHans Rosenfeld if (error != DDI_SUCCESS) { 1923*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 1924*fd43cf6eSHans Rosenfeld "!could not create RX buf DMA map"); 1925*fd43cf6eSHans Rosenfeld goto fail; 1926*fd43cf6eSHans Rosenfeld } 1927*fd43cf6eSHans Rosenfeld 1928*fd43cf6eSHans Rosenfeld /* Set physical address of RX buffer (256-byte aligned). */ 1929*fd43cf6eSHans Rosenfeld ring->desc[i] = htole32(data->dma_data.paddr >> 8); 1930*fd43cf6eSHans Rosenfeld } 1931*fd43cf6eSHans Rosenfeld 1932*fd43cf6eSHans Rosenfeld (void) ddi_dma_sync(ring->desc_dma.dma_hdl, 0, 0, DDI_DMA_SYNC_FORDEV); 1933*fd43cf6eSHans Rosenfeld 1934*fd43cf6eSHans Rosenfeld return 0; 1935*fd43cf6eSHans Rosenfeld 1936*fd43cf6eSHans Rosenfeld fail: iwn_free_rx_ring(sc, ring); 1937*fd43cf6eSHans Rosenfeld return error; 1938*fd43cf6eSHans Rosenfeld } 1939*fd43cf6eSHans Rosenfeld 1940*fd43cf6eSHans Rosenfeld static void 1941*fd43cf6eSHans Rosenfeld iwn_reset_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring) 1942*fd43cf6eSHans Rosenfeld { 1943*fd43cf6eSHans Rosenfeld int ntries; 1944*fd43cf6eSHans Rosenfeld 1945*fd43cf6eSHans Rosenfeld if (iwn_nic_lock(sc) == 0) { 1946*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_FH_RX_CONFIG, 0); 1947*fd43cf6eSHans Rosenfeld for (ntries = 0; ntries < 1000; ntries++) { 1948*fd43cf6eSHans Rosenfeld if (IWN_READ(sc, IWN_FH_RX_STATUS) & 1949*fd43cf6eSHans Rosenfeld IWN_FH_RX_STATUS_IDLE) 1950*fd43cf6eSHans Rosenfeld break; 1951*fd43cf6eSHans Rosenfeld DELAY(10); 1952*fd43cf6eSHans Rosenfeld } 1953*fd43cf6eSHans Rosenfeld iwn_nic_unlock(sc); 1954*fd43cf6eSHans Rosenfeld } 1955*fd43cf6eSHans Rosenfeld ring->cur = 0; 1956*fd43cf6eSHans Rosenfeld sc->last_rx_valid = 0; 1957*fd43cf6eSHans Rosenfeld } 1958*fd43cf6eSHans Rosenfeld 1959*fd43cf6eSHans Rosenfeld static void 1960*fd43cf6eSHans Rosenfeld iwn_free_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring) 1961*fd43cf6eSHans Rosenfeld { 1962*fd43cf6eSHans Rosenfeld _NOTE(ARGUNUSED(sc)); 1963*fd43cf6eSHans Rosenfeld int i; 1964*fd43cf6eSHans Rosenfeld 1965*fd43cf6eSHans Rosenfeld iwn_dma_contig_free(&ring->desc_dma); 1966*fd43cf6eSHans Rosenfeld iwn_dma_contig_free(&ring->stat_dma); 1967*fd43cf6eSHans Rosenfeld 1968*fd43cf6eSHans Rosenfeld for (i = 0; i < IWN_RX_RING_COUNT; i++) { 1969*fd43cf6eSHans Rosenfeld struct iwn_rx_data *data = &ring->data[i]; 1970*fd43cf6eSHans Rosenfeld 1971*fd43cf6eSHans Rosenfeld if (data->dma_data.dma_hdl) 1972*fd43cf6eSHans Rosenfeld iwn_dma_contig_free(&data->dma_data); 1973*fd43cf6eSHans Rosenfeld } 1974*fd43cf6eSHans Rosenfeld } 1975*fd43cf6eSHans Rosenfeld 1976*fd43cf6eSHans Rosenfeld static int 1977*fd43cf6eSHans Rosenfeld iwn_alloc_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring, int qid) 1978*fd43cf6eSHans Rosenfeld { 1979*fd43cf6eSHans Rosenfeld uintptr_t paddr; 1980*fd43cf6eSHans Rosenfeld size_t size; 1981*fd43cf6eSHans Rosenfeld int i, error; 1982*fd43cf6eSHans Rosenfeld 1983*fd43cf6eSHans Rosenfeld ring->qid = qid; 1984*fd43cf6eSHans Rosenfeld ring->queued = 0; 1985*fd43cf6eSHans Rosenfeld ring->cur = 0; 1986*fd43cf6eSHans Rosenfeld 1987*fd43cf6eSHans Rosenfeld /* Allocate TX descriptors (256-byte aligned). */ 1988*fd43cf6eSHans Rosenfeld size = IWN_TX_RING_COUNT * sizeof (struct iwn_tx_desc); 1989*fd43cf6eSHans Rosenfeld error = iwn_dma_contig_alloc(sc, &ring->desc_dma, size, 1990*fd43cf6eSHans Rosenfeld DDI_DMA_CONSISTENT | DDI_DMA_WRITE, (void **)&ring->desc, 1991*fd43cf6eSHans Rosenfeld &iwn_dma_descattr, 256); 1992*fd43cf6eSHans Rosenfeld if (error != DDI_SUCCESS) { 1993*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 1994*fd43cf6eSHans Rosenfeld "!could not allocate TX ring DMA memory"); 1995*fd43cf6eSHans Rosenfeld goto fail; 1996*fd43cf6eSHans Rosenfeld } 1997*fd43cf6eSHans Rosenfeld /* 1998*fd43cf6eSHans Rosenfeld * We only use rings 0 through 4 (4 EDCA + cmd) so there is no need 1999*fd43cf6eSHans Rosenfeld * to allocate commands space for other rings. 2000*fd43cf6eSHans Rosenfeld * XXX Do we really need to allocate descriptors for other rings? 2001*fd43cf6eSHans Rosenfeld */ 2002*fd43cf6eSHans Rosenfeld if (qid > 4) 2003*fd43cf6eSHans Rosenfeld return 0; 2004*fd43cf6eSHans Rosenfeld 2005*fd43cf6eSHans Rosenfeld size = IWN_TX_RING_COUNT * sizeof (struct iwn_tx_cmd); 2006*fd43cf6eSHans Rosenfeld error = iwn_dma_contig_alloc(sc, &ring->cmd_dma, size, 2007*fd43cf6eSHans Rosenfeld DDI_DMA_CONSISTENT | DDI_DMA_WRITE, (void **)&ring->cmd, 2008*fd43cf6eSHans Rosenfeld &iwn_dma_accattr, 4); 2009*fd43cf6eSHans Rosenfeld if (error != DDI_SUCCESS) { 2010*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 2011*fd43cf6eSHans Rosenfeld "!could not allocate TX cmd DMA memory"); 2012*fd43cf6eSHans Rosenfeld goto fail; 2013*fd43cf6eSHans Rosenfeld } 2014*fd43cf6eSHans Rosenfeld 2015*fd43cf6eSHans Rosenfeld paddr = ring->cmd_dma.paddr; 2016*fd43cf6eSHans Rosenfeld for (i = 0; i < IWN_TX_RING_COUNT; i++) { 2017*fd43cf6eSHans Rosenfeld struct iwn_tx_data *data = &ring->data[i]; 2018*fd43cf6eSHans Rosenfeld 2019*fd43cf6eSHans Rosenfeld data->cmd_paddr = paddr; 2020*fd43cf6eSHans Rosenfeld data->scratch_paddr = paddr + 12; 2021*fd43cf6eSHans Rosenfeld paddr += sizeof (struct iwn_tx_cmd); 2022*fd43cf6eSHans Rosenfeld 2023*fd43cf6eSHans Rosenfeld error = iwn_dma_contig_alloc(sc, &data->dma_data, IWN_TBUF_SIZE, 2024*fd43cf6eSHans Rosenfeld DDI_DMA_CONSISTENT | DDI_DMA_WRITE, NULL, &iwn_dma_accattr, 2025*fd43cf6eSHans Rosenfeld 256); 2026*fd43cf6eSHans Rosenfeld if (error != DDI_SUCCESS) { 2027*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 2028*fd43cf6eSHans Rosenfeld "!could not create TX buf DMA map"); 2029*fd43cf6eSHans Rosenfeld goto fail; 2030*fd43cf6eSHans Rosenfeld } 2031*fd43cf6eSHans Rosenfeld } 2032*fd43cf6eSHans Rosenfeld return 0; 2033*fd43cf6eSHans Rosenfeld 2034*fd43cf6eSHans Rosenfeld fail: iwn_free_tx_ring(sc, ring); 2035*fd43cf6eSHans Rosenfeld return error; 2036*fd43cf6eSHans Rosenfeld } 2037*fd43cf6eSHans Rosenfeld 2038*fd43cf6eSHans Rosenfeld static void 2039*fd43cf6eSHans Rosenfeld iwn_reset_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring) 2040*fd43cf6eSHans Rosenfeld { 2041*fd43cf6eSHans Rosenfeld int i; 2042*fd43cf6eSHans Rosenfeld 2043*fd43cf6eSHans Rosenfeld if (ring->qid < 4) 2044*fd43cf6eSHans Rosenfeld for (i = 0; i < IWN_TX_RING_COUNT; i++) { 2045*fd43cf6eSHans Rosenfeld struct iwn_tx_data *data = &ring->data[i]; 2046*fd43cf6eSHans Rosenfeld 2047*fd43cf6eSHans Rosenfeld (void) ddi_dma_sync(data->dma_data.dma_hdl, 0, 0, 2048*fd43cf6eSHans Rosenfeld DDI_DMA_SYNC_FORDEV); 2049*fd43cf6eSHans Rosenfeld } 2050*fd43cf6eSHans Rosenfeld 2051*fd43cf6eSHans Rosenfeld /* Clear TX descriptors. */ 2052*fd43cf6eSHans Rosenfeld memset(ring->desc, 0, ring->desc_dma.size); 2053*fd43cf6eSHans Rosenfeld (void) ddi_dma_sync(ring->desc_dma.dma_hdl, 0, 0, DDI_DMA_SYNC_FORDEV); 2054*fd43cf6eSHans Rosenfeld sc->qfullmsk &= ~(1 << ring->qid); 2055*fd43cf6eSHans Rosenfeld ring->queued = 0; 2056*fd43cf6eSHans Rosenfeld ring->cur = 0; 2057*fd43cf6eSHans Rosenfeld } 2058*fd43cf6eSHans Rosenfeld 2059*fd43cf6eSHans Rosenfeld static void 2060*fd43cf6eSHans Rosenfeld iwn_free_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring) 2061*fd43cf6eSHans Rosenfeld { 2062*fd43cf6eSHans Rosenfeld _NOTE(ARGUNUSED(sc)); 2063*fd43cf6eSHans Rosenfeld int i; 2064*fd43cf6eSHans Rosenfeld 2065*fd43cf6eSHans Rosenfeld iwn_dma_contig_free(&ring->desc_dma); 2066*fd43cf6eSHans Rosenfeld iwn_dma_contig_free(&ring->cmd_dma); 2067*fd43cf6eSHans Rosenfeld 2068*fd43cf6eSHans Rosenfeld for (i = 0; i < IWN_TX_RING_COUNT; i++) { 2069*fd43cf6eSHans Rosenfeld struct iwn_tx_data *data = &ring->data[i]; 2070*fd43cf6eSHans Rosenfeld 2071*fd43cf6eSHans Rosenfeld if (data->dma_data.dma_hdl) 2072*fd43cf6eSHans Rosenfeld iwn_dma_contig_free(&data->dma_data); 2073*fd43cf6eSHans Rosenfeld } 2074*fd43cf6eSHans Rosenfeld } 2075*fd43cf6eSHans Rosenfeld 2076*fd43cf6eSHans Rosenfeld static void 2077*fd43cf6eSHans Rosenfeld iwn5000_ict_reset(struct iwn_softc *sc) 2078*fd43cf6eSHans Rosenfeld { 2079*fd43cf6eSHans Rosenfeld /* Disable interrupts. */ 2080*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_INT_MASK, 0); 2081*fd43cf6eSHans Rosenfeld 2082*fd43cf6eSHans Rosenfeld /* Reset ICT table. */ 2083*fd43cf6eSHans Rosenfeld memset(sc->ict, 0, IWN_ICT_SIZE); 2084*fd43cf6eSHans Rosenfeld sc->ict_cur = 0; 2085*fd43cf6eSHans Rosenfeld 2086*fd43cf6eSHans Rosenfeld /* Set physical address of ICT table (4KB aligned). */ 2087*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_DRAM_INT_TBL, IWN_DRAM_INT_TBL_ENABLE | 2088*fd43cf6eSHans Rosenfeld IWN_DRAM_INT_TBL_WRAP_CHECK | sc->ict_dma.paddr >> 12); 2089*fd43cf6eSHans Rosenfeld 2090*fd43cf6eSHans Rosenfeld /* Enable periodic RX interrupt. */ 2091*fd43cf6eSHans Rosenfeld sc->int_mask |= IWN_INT_RX_PERIODIC; 2092*fd43cf6eSHans Rosenfeld /* Switch to ICT interrupt mode in driver. */ 2093*fd43cf6eSHans Rosenfeld sc->sc_flags |= IWN_FLAG_USE_ICT; 2094*fd43cf6eSHans Rosenfeld 2095*fd43cf6eSHans Rosenfeld /* Re-enable interrupts. */ 2096*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_INT, 0xffffffff); 2097*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask); 2098*fd43cf6eSHans Rosenfeld } 2099*fd43cf6eSHans Rosenfeld 2100*fd43cf6eSHans Rosenfeld static int 2101*fd43cf6eSHans Rosenfeld iwn_read_eeprom(struct iwn_softc *sc) 2102*fd43cf6eSHans Rosenfeld { 2103*fd43cf6eSHans Rosenfeld struct iwn_ops *ops = &sc->ops; 2104*fd43cf6eSHans Rosenfeld struct ieee80211com *ic = &sc->sc_ic; 2105*fd43cf6eSHans Rosenfeld uint16_t val; 2106*fd43cf6eSHans Rosenfeld int error; 2107*fd43cf6eSHans Rosenfeld 2108*fd43cf6eSHans Rosenfeld /* Check whether adapter has an EEPROM or an OTPROM. */ 2109*fd43cf6eSHans Rosenfeld if (sc->hw_type >= IWN_HW_REV_TYPE_1000 && 2110*fd43cf6eSHans Rosenfeld (IWN_READ(sc, IWN_OTP_GP) & IWN_OTP_GP_DEV_SEL_OTP)) 2111*fd43cf6eSHans Rosenfeld sc->sc_flags |= IWN_FLAG_HAS_OTPROM; 2112*fd43cf6eSHans Rosenfeld IWN_DBG("%s found", 2113*fd43cf6eSHans Rosenfeld (sc->sc_flags & IWN_FLAG_HAS_OTPROM) ? "OTPROM" : "EEPROM"); 2114*fd43cf6eSHans Rosenfeld 2115*fd43cf6eSHans Rosenfeld /* Adapter has to be powered on for EEPROM access to work. */ 2116*fd43cf6eSHans Rosenfeld if ((error = iwn_apm_init(sc)) != 0) { 2117*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 2118*fd43cf6eSHans Rosenfeld "!could not power ON adapter"); 2119*fd43cf6eSHans Rosenfeld return error; 2120*fd43cf6eSHans Rosenfeld } 2121*fd43cf6eSHans Rosenfeld 2122*fd43cf6eSHans Rosenfeld if ((IWN_READ(sc, IWN_EEPROM_GP) & 0x7) == 0) { 2123*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 2124*fd43cf6eSHans Rosenfeld "!bad ROM signature"); 2125*fd43cf6eSHans Rosenfeld return EIO; 2126*fd43cf6eSHans Rosenfeld } 2127*fd43cf6eSHans Rosenfeld if ((error = iwn_eeprom_lock(sc)) != 0) { 2128*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 2129*fd43cf6eSHans Rosenfeld "!could not lock ROM (error=%d)", error); 2130*fd43cf6eSHans Rosenfeld return error; 2131*fd43cf6eSHans Rosenfeld } 2132*fd43cf6eSHans Rosenfeld if (sc->sc_flags & IWN_FLAG_HAS_OTPROM) { 2133*fd43cf6eSHans Rosenfeld if ((error = iwn_init_otprom(sc)) != 0) { 2134*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 2135*fd43cf6eSHans Rosenfeld "!could not initialize OTPROM"); 2136*fd43cf6eSHans Rosenfeld return error; 2137*fd43cf6eSHans Rosenfeld } 2138*fd43cf6eSHans Rosenfeld } 2139*fd43cf6eSHans Rosenfeld 2140*fd43cf6eSHans Rosenfeld iwn_read_prom_data(sc, IWN_EEPROM_SKU_CAP, &val, 2); 2141*fd43cf6eSHans Rosenfeld IWN_DBG("SKU capabilities=0x%04x", le16toh(val)); 2142*fd43cf6eSHans Rosenfeld /* Check if HT support is bonded out. */ 2143*fd43cf6eSHans Rosenfeld if (val & htole16(IWN_EEPROM_SKU_CAP_11N)) 2144*fd43cf6eSHans Rosenfeld sc->sc_flags |= IWN_FLAG_HAS_11N; 2145*fd43cf6eSHans Rosenfeld 2146*fd43cf6eSHans Rosenfeld iwn_read_prom_data(sc, IWN_EEPROM_RFCFG, &val, 2); 2147*fd43cf6eSHans Rosenfeld sc->rfcfg = le16toh(val); 2148*fd43cf6eSHans Rosenfeld IWN_DBG("radio config=0x%04x", sc->rfcfg); 2149*fd43cf6eSHans Rosenfeld /* Read Tx/Rx chains from ROM unless it's known to be broken. */ 2150*fd43cf6eSHans Rosenfeld if (sc->txchainmask == 0) 2151*fd43cf6eSHans Rosenfeld sc->txchainmask = IWN_RFCFG_TXANTMSK(sc->rfcfg); 2152*fd43cf6eSHans Rosenfeld if (sc->rxchainmask == 0) 2153*fd43cf6eSHans Rosenfeld sc->rxchainmask = IWN_RFCFG_RXANTMSK(sc->rfcfg); 2154*fd43cf6eSHans Rosenfeld 2155*fd43cf6eSHans Rosenfeld /* Read MAC address. */ 2156*fd43cf6eSHans Rosenfeld iwn_read_prom_data(sc, IWN_EEPROM_MAC, ic->ic_macaddr, 6); 2157*fd43cf6eSHans Rosenfeld 2158*fd43cf6eSHans Rosenfeld /* Read adapter-specific information from EEPROM. */ 2159*fd43cf6eSHans Rosenfeld ops->read_eeprom(sc); 2160*fd43cf6eSHans Rosenfeld 2161*fd43cf6eSHans Rosenfeld iwn_apm_stop(sc); /* Power OFF adapter. */ 2162*fd43cf6eSHans Rosenfeld 2163*fd43cf6eSHans Rosenfeld iwn_eeprom_unlock(sc); 2164*fd43cf6eSHans Rosenfeld return 0; 2165*fd43cf6eSHans Rosenfeld } 2166*fd43cf6eSHans Rosenfeld 2167*fd43cf6eSHans Rosenfeld static void 2168*fd43cf6eSHans Rosenfeld iwn4965_read_eeprom(struct iwn_softc *sc) 2169*fd43cf6eSHans Rosenfeld { 2170*fd43cf6eSHans Rosenfeld uint32_t addr; 2171*fd43cf6eSHans Rosenfeld uint16_t val; 2172*fd43cf6eSHans Rosenfeld int i; 2173*fd43cf6eSHans Rosenfeld 2174*fd43cf6eSHans Rosenfeld /* Read regulatory domain (4 ASCII characters). */ 2175*fd43cf6eSHans Rosenfeld iwn_read_prom_data(sc, IWN4965_EEPROM_DOMAIN, sc->eeprom_domain, 4); 2176*fd43cf6eSHans Rosenfeld 2177*fd43cf6eSHans Rosenfeld /* Read the list of authorized channels (20MHz ones only). */ 2178*fd43cf6eSHans Rosenfeld for (i = 0; i < 5; i++) { 2179*fd43cf6eSHans Rosenfeld addr = iwn4965_regulatory_bands[i]; 2180*fd43cf6eSHans Rosenfeld iwn_read_eeprom_channels(sc, i, addr); 2181*fd43cf6eSHans Rosenfeld } 2182*fd43cf6eSHans Rosenfeld 2183*fd43cf6eSHans Rosenfeld /* Read maximum allowed TX power for 2GHz and 5GHz bands. */ 2184*fd43cf6eSHans Rosenfeld iwn_read_prom_data(sc, IWN4965_EEPROM_MAXPOW, &val, 2); 2185*fd43cf6eSHans Rosenfeld sc->maxpwr2GHz = val & 0xff; 2186*fd43cf6eSHans Rosenfeld sc->maxpwr5GHz = val >> 8; 2187*fd43cf6eSHans Rosenfeld /* Check that EEPROM values are within valid range. */ 2188*fd43cf6eSHans Rosenfeld if (sc->maxpwr5GHz < 20 || sc->maxpwr5GHz > 50) 2189*fd43cf6eSHans Rosenfeld sc->maxpwr5GHz = 38; 2190*fd43cf6eSHans Rosenfeld if (sc->maxpwr2GHz < 20 || sc->maxpwr2GHz > 50) 2191*fd43cf6eSHans Rosenfeld sc->maxpwr2GHz = 38; 2192*fd43cf6eSHans Rosenfeld IWN_DBG("maxpwr 2GHz=%d 5GHz=%d", sc->maxpwr2GHz, sc->maxpwr5GHz); 2193*fd43cf6eSHans Rosenfeld 2194*fd43cf6eSHans Rosenfeld /* Read samples for each TX power group. */ 2195*fd43cf6eSHans Rosenfeld iwn_read_prom_data(sc, IWN4965_EEPROM_BANDS, sc->bands, 2196*fd43cf6eSHans Rosenfeld sizeof sc->bands); 2197*fd43cf6eSHans Rosenfeld 2198*fd43cf6eSHans Rosenfeld /* Read voltage at which samples were taken. */ 2199*fd43cf6eSHans Rosenfeld iwn_read_prom_data(sc, IWN4965_EEPROM_VOLTAGE, &val, 2); 2200*fd43cf6eSHans Rosenfeld sc->eeprom_voltage = (int16_t)le16toh(val); 2201*fd43cf6eSHans Rosenfeld IWN_DBG("voltage=%d (in 0.3V)", sc->eeprom_voltage); 2202*fd43cf6eSHans Rosenfeld 2203*fd43cf6eSHans Rosenfeld #ifdef IWN_DEBUG 2204*fd43cf6eSHans Rosenfeld /* Print samples. */ 2205*fd43cf6eSHans Rosenfeld if (iwn_dbg_print != 0) { 2206*fd43cf6eSHans Rosenfeld for (i = 0; i < IWN_NBANDS; i++) 2207*fd43cf6eSHans Rosenfeld iwn4965_print_power_group(sc, i); 2208*fd43cf6eSHans Rosenfeld } 2209*fd43cf6eSHans Rosenfeld #endif 2210*fd43cf6eSHans Rosenfeld } 2211*fd43cf6eSHans Rosenfeld 2212*fd43cf6eSHans Rosenfeld #ifdef IWN_DEBUG 2213*fd43cf6eSHans Rosenfeld static void 2214*fd43cf6eSHans Rosenfeld iwn4965_print_power_group(struct iwn_softc *sc, int i) 2215*fd43cf6eSHans Rosenfeld { 2216*fd43cf6eSHans Rosenfeld struct iwn4965_eeprom_band *band = &sc->bands[i]; 2217*fd43cf6eSHans Rosenfeld struct iwn4965_eeprom_chan_samples *chans = band->chans; 2218*fd43cf6eSHans Rosenfeld int j, c; 2219*fd43cf6eSHans Rosenfeld 2220*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_CONT, "!===band %d===", i); 2221*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_CONT, "!chan lo=%d, chan hi=%d", band->lo, 2222*fd43cf6eSHans Rosenfeld band->hi); 2223*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_CONT, "!chan1 num=%d", chans[0].num); 2224*fd43cf6eSHans Rosenfeld for (c = 0; c < 2; c++) { 2225*fd43cf6eSHans Rosenfeld for (j = 0; j < IWN_NSAMPLES; j++) { 2226*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_CONT, "!chain %d, sample %d: " 2227*fd43cf6eSHans Rosenfeld "temp=%d gain=%d power=%d pa_det=%d", c, j, 2228*fd43cf6eSHans Rosenfeld chans[0].samples[c][j].temp, 2229*fd43cf6eSHans Rosenfeld chans[0].samples[c][j].gain, 2230*fd43cf6eSHans Rosenfeld chans[0].samples[c][j].power, 2231*fd43cf6eSHans Rosenfeld chans[0].samples[c][j].pa_det); 2232*fd43cf6eSHans Rosenfeld } 2233*fd43cf6eSHans Rosenfeld } 2234*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_CONT, "!chan2 num=%d", chans[1].num); 2235*fd43cf6eSHans Rosenfeld for (c = 0; c < 2; c++) { 2236*fd43cf6eSHans Rosenfeld for (j = 0; j < IWN_NSAMPLES; j++) { 2237*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_CONT, "!chain %d, sample %d: " 2238*fd43cf6eSHans Rosenfeld "temp=%d gain=%d power=%d pa_det=%d", c, j, 2239*fd43cf6eSHans Rosenfeld chans[1].samples[c][j].temp, 2240*fd43cf6eSHans Rosenfeld chans[1].samples[c][j].gain, 2241*fd43cf6eSHans Rosenfeld chans[1].samples[c][j].power, 2242*fd43cf6eSHans Rosenfeld chans[1].samples[c][j].pa_det); 2243*fd43cf6eSHans Rosenfeld } 2244*fd43cf6eSHans Rosenfeld } 2245*fd43cf6eSHans Rosenfeld } 2246*fd43cf6eSHans Rosenfeld #endif 2247*fd43cf6eSHans Rosenfeld 2248*fd43cf6eSHans Rosenfeld static void 2249*fd43cf6eSHans Rosenfeld iwn5000_read_eeprom(struct iwn_softc *sc) 2250*fd43cf6eSHans Rosenfeld { 2251*fd43cf6eSHans Rosenfeld struct iwn5000_eeprom_calib_hdr hdr; 2252*fd43cf6eSHans Rosenfeld int32_t volt; 2253*fd43cf6eSHans Rosenfeld uint32_t base, addr; 2254*fd43cf6eSHans Rosenfeld uint16_t val; 2255*fd43cf6eSHans Rosenfeld int i; 2256*fd43cf6eSHans Rosenfeld 2257*fd43cf6eSHans Rosenfeld /* Read regulatory domain (4 ASCII characters). */ 2258*fd43cf6eSHans Rosenfeld iwn_read_prom_data(sc, IWN5000_EEPROM_REG, &val, 2); 2259*fd43cf6eSHans Rosenfeld base = le16toh(val); 2260*fd43cf6eSHans Rosenfeld iwn_read_prom_data(sc, base + IWN5000_EEPROM_DOMAIN, 2261*fd43cf6eSHans Rosenfeld sc->eeprom_domain, 4); 2262*fd43cf6eSHans Rosenfeld 2263*fd43cf6eSHans Rosenfeld /* Read the list of authorized channels (20MHz ones only). */ 2264*fd43cf6eSHans Rosenfeld for (i = 0; i < 5; i++) { 2265*fd43cf6eSHans Rosenfeld addr = base + iwn5000_regulatory_bands[i]; 2266*fd43cf6eSHans Rosenfeld iwn_read_eeprom_channels(sc, i, addr); 2267*fd43cf6eSHans Rosenfeld } 2268*fd43cf6eSHans Rosenfeld 2269*fd43cf6eSHans Rosenfeld /* Read enhanced TX power information for 6000 Series. */ 2270*fd43cf6eSHans Rosenfeld if (sc->hw_type >= IWN_HW_REV_TYPE_6000) 2271*fd43cf6eSHans Rosenfeld iwn_read_eeprom_enhinfo(sc); 2272*fd43cf6eSHans Rosenfeld 2273*fd43cf6eSHans Rosenfeld iwn_read_prom_data(sc, IWN5000_EEPROM_CAL, &val, 2); 2274*fd43cf6eSHans Rosenfeld base = le16toh(val); 2275*fd43cf6eSHans Rosenfeld iwn_read_prom_data(sc, base, &hdr, sizeof hdr); 2276*fd43cf6eSHans Rosenfeld IWN_DBG("calib version=%u pa type=%u voltage=%u", 2277*fd43cf6eSHans Rosenfeld hdr.version, hdr.pa_type, le16toh(hdr.volt)); 2278*fd43cf6eSHans Rosenfeld sc->calib_ver = hdr.version; 2279*fd43cf6eSHans Rosenfeld 2280*fd43cf6eSHans Rosenfeld if (sc->hw_type == IWN_HW_REV_TYPE_2030 || 2281*fd43cf6eSHans Rosenfeld sc->hw_type == IWN_HW_REV_TYPE_2000 || 2282*fd43cf6eSHans Rosenfeld sc->hw_type == IWN_HW_REV_TYPE_135 || 2283*fd43cf6eSHans Rosenfeld sc->hw_type == IWN_HW_REV_TYPE_105) { 2284*fd43cf6eSHans Rosenfeld sc->eeprom_voltage = le16toh(hdr.volt); 2285*fd43cf6eSHans Rosenfeld iwn_read_prom_data(sc, base + IWN5000_EEPROM_TEMP, &val, 2); 2286*fd43cf6eSHans Rosenfeld sc->eeprom_temp = le16toh(val); 2287*fd43cf6eSHans Rosenfeld iwn_read_prom_data(sc, base + IWN2000_EEPROM_RAWTEMP, &val, 2); 2288*fd43cf6eSHans Rosenfeld sc->eeprom_rawtemp = le16toh(val); 2289*fd43cf6eSHans Rosenfeld } 2290*fd43cf6eSHans Rosenfeld 2291*fd43cf6eSHans Rosenfeld if (sc->hw_type == IWN_HW_REV_TYPE_5150) { 2292*fd43cf6eSHans Rosenfeld /* Compute temperature offset. */ 2293*fd43cf6eSHans Rosenfeld iwn_read_prom_data(sc, base + IWN5000_EEPROM_TEMP, &val, 2); 2294*fd43cf6eSHans Rosenfeld sc->eeprom_temp = le16toh(val); 2295*fd43cf6eSHans Rosenfeld iwn_read_prom_data(sc, base + IWN5000_EEPROM_VOLT, &val, 2); 2296*fd43cf6eSHans Rosenfeld volt = le16toh(val); 2297*fd43cf6eSHans Rosenfeld sc->temp_off = sc->eeprom_temp - (volt / -5); 2298*fd43cf6eSHans Rosenfeld IWN_DBG("temp=%d volt=%d offset=%dK", 2299*fd43cf6eSHans Rosenfeld sc->eeprom_temp, volt, sc->temp_off); 2300*fd43cf6eSHans Rosenfeld } else { 2301*fd43cf6eSHans Rosenfeld /* Read crystal calibration. */ 2302*fd43cf6eSHans Rosenfeld iwn_read_prom_data(sc, base + IWN5000_EEPROM_CRYSTAL, 2303*fd43cf6eSHans Rosenfeld &sc->eeprom_crystal, sizeof (uint32_t)); 2304*fd43cf6eSHans Rosenfeld IWN_DBG("crystal calibration 0x%08x", 2305*fd43cf6eSHans Rosenfeld le32toh(sc->eeprom_crystal)); 2306*fd43cf6eSHans Rosenfeld } 2307*fd43cf6eSHans Rosenfeld } 2308*fd43cf6eSHans Rosenfeld 2309*fd43cf6eSHans Rosenfeld static void 2310*fd43cf6eSHans Rosenfeld iwn_read_eeprom_channels(struct iwn_softc *sc, int n, uint32_t addr) 2311*fd43cf6eSHans Rosenfeld { 2312*fd43cf6eSHans Rosenfeld struct ieee80211com *ic = &sc->sc_ic; 2313*fd43cf6eSHans Rosenfeld const struct iwn_chan_band *band = &iwn_bands[n]; 2314*fd43cf6eSHans Rosenfeld struct iwn_eeprom_chan channels[IWN_MAX_CHAN_PER_BAND]; 2315*fd43cf6eSHans Rosenfeld uint8_t chan; 2316*fd43cf6eSHans Rosenfeld int i; 2317*fd43cf6eSHans Rosenfeld 2318*fd43cf6eSHans Rosenfeld iwn_read_prom_data(sc, addr, channels, 2319*fd43cf6eSHans Rosenfeld band->nchan * sizeof (struct iwn_eeprom_chan)); 2320*fd43cf6eSHans Rosenfeld 2321*fd43cf6eSHans Rosenfeld for (i = 0; i < band->nchan; i++) { 2322*fd43cf6eSHans Rosenfeld if (!(channels[i].flags & IWN_EEPROM_CHAN_VALID)) 2323*fd43cf6eSHans Rosenfeld continue; 2324*fd43cf6eSHans Rosenfeld 2325*fd43cf6eSHans Rosenfeld chan = band->chan[i]; 2326*fd43cf6eSHans Rosenfeld 2327*fd43cf6eSHans Rosenfeld if (n == 0) { /* 2GHz band */ 2328*fd43cf6eSHans Rosenfeld ic->ic_sup_channels[chan].ich_freq = 2329*fd43cf6eSHans Rosenfeld ieee80211_ieee2mhz(chan, IEEE80211_CHAN_2GHZ); 2330*fd43cf6eSHans Rosenfeld ic->ic_sup_channels[chan].ich_flags = 2331*fd43cf6eSHans Rosenfeld IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM | 2332*fd43cf6eSHans Rosenfeld IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ; 2333*fd43cf6eSHans Rosenfeld 2334*fd43cf6eSHans Rosenfeld } else { /* 5GHz band */ 2335*fd43cf6eSHans Rosenfeld /* 2336*fd43cf6eSHans Rosenfeld * Some adapters support channels 7, 8, 11 and 12 2337*fd43cf6eSHans Rosenfeld * both in the 2GHz and 4.9GHz bands. 2338*fd43cf6eSHans Rosenfeld * Because of limitations in our net80211 layer, 2339*fd43cf6eSHans Rosenfeld * we don't support them in the 4.9GHz band. 2340*fd43cf6eSHans Rosenfeld */ 2341*fd43cf6eSHans Rosenfeld if (chan <= 14) 2342*fd43cf6eSHans Rosenfeld continue; 2343*fd43cf6eSHans Rosenfeld 2344*fd43cf6eSHans Rosenfeld ic->ic_sup_channels[chan].ich_freq = 2345*fd43cf6eSHans Rosenfeld ieee80211_ieee2mhz(chan, IEEE80211_CHAN_5GHZ); 2346*fd43cf6eSHans Rosenfeld ic->ic_sup_channels[chan].ich_flags = 2347*fd43cf6eSHans Rosenfeld IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM; 2348*fd43cf6eSHans Rosenfeld /* We have at least one valid 5GHz channel. */ 2349*fd43cf6eSHans Rosenfeld sc->sc_flags |= IWN_FLAG_HAS_5GHZ; 2350*fd43cf6eSHans Rosenfeld } 2351*fd43cf6eSHans Rosenfeld 2352*fd43cf6eSHans Rosenfeld /* Is active scan allowed on this channel? */ 2353*fd43cf6eSHans Rosenfeld if (!(channels[i].flags & IWN_EEPROM_CHAN_ACTIVE)) { 2354*fd43cf6eSHans Rosenfeld ic->ic_sup_channels[chan].ich_flags |= 2355*fd43cf6eSHans Rosenfeld IEEE80211_CHAN_PASSIVE; 2356*fd43cf6eSHans Rosenfeld } 2357*fd43cf6eSHans Rosenfeld 2358*fd43cf6eSHans Rosenfeld /* Save maximum allowed TX power for this channel. */ 2359*fd43cf6eSHans Rosenfeld sc->maxpwr[chan] = channels[i].maxpwr; 2360*fd43cf6eSHans Rosenfeld 2361*fd43cf6eSHans Rosenfeld IWN_DBG("adding chan %d flags=0x%x maxpwr=%d", 2362*fd43cf6eSHans Rosenfeld chan, channels[i].flags, sc->maxpwr[chan]); 2363*fd43cf6eSHans Rosenfeld } 2364*fd43cf6eSHans Rosenfeld } 2365*fd43cf6eSHans Rosenfeld 2366*fd43cf6eSHans Rosenfeld static void 2367*fd43cf6eSHans Rosenfeld iwn_read_eeprom_enhinfo(struct iwn_softc *sc) 2368*fd43cf6eSHans Rosenfeld { 2369*fd43cf6eSHans Rosenfeld struct iwn_eeprom_enhinfo enhinfo[35]; 2370*fd43cf6eSHans Rosenfeld uint16_t val, base; 2371*fd43cf6eSHans Rosenfeld int8_t maxpwr; 2372*fd43cf6eSHans Rosenfeld int i; 2373*fd43cf6eSHans Rosenfeld 2374*fd43cf6eSHans Rosenfeld iwn_read_prom_data(sc, IWN5000_EEPROM_REG, &val, 2); 2375*fd43cf6eSHans Rosenfeld base = le16toh(val); 2376*fd43cf6eSHans Rosenfeld iwn_read_prom_data(sc, base + IWN6000_EEPROM_ENHINFO, 2377*fd43cf6eSHans Rosenfeld enhinfo, sizeof enhinfo); 2378*fd43cf6eSHans Rosenfeld 2379*fd43cf6eSHans Rosenfeld memset(sc->enh_maxpwr, 0, sizeof sc->enh_maxpwr); 2380*fd43cf6eSHans Rosenfeld for (i = 0; i < __arraycount(enhinfo); i++) { 2381*fd43cf6eSHans Rosenfeld if (enhinfo[i].chan == 0 || enhinfo[i].reserved != 0) 2382*fd43cf6eSHans Rosenfeld continue; /* Skip invalid entries. */ 2383*fd43cf6eSHans Rosenfeld 2384*fd43cf6eSHans Rosenfeld maxpwr = 0; 2385*fd43cf6eSHans Rosenfeld if (sc->txchainmask & IWN_ANT_A) 2386*fd43cf6eSHans Rosenfeld maxpwr = MAX(maxpwr, enhinfo[i].chain[0]); 2387*fd43cf6eSHans Rosenfeld if (sc->txchainmask & IWN_ANT_B) 2388*fd43cf6eSHans Rosenfeld maxpwr = MAX(maxpwr, enhinfo[i].chain[1]); 2389*fd43cf6eSHans Rosenfeld if (sc->txchainmask & IWN_ANT_C) 2390*fd43cf6eSHans Rosenfeld maxpwr = MAX(maxpwr, enhinfo[i].chain[2]); 2391*fd43cf6eSHans Rosenfeld if (sc->ntxchains == 2) 2392*fd43cf6eSHans Rosenfeld maxpwr = MAX(maxpwr, enhinfo[i].mimo2); 2393*fd43cf6eSHans Rosenfeld else if (sc->ntxchains == 3) 2394*fd43cf6eSHans Rosenfeld maxpwr = MAX(maxpwr, enhinfo[i].mimo3); 2395*fd43cf6eSHans Rosenfeld maxpwr /= 2; /* Convert half-dBm to dBm. */ 2396*fd43cf6eSHans Rosenfeld 2397*fd43cf6eSHans Rosenfeld IWN_DBG("enhinfo %d, maxpwr=%d", i, maxpwr); 2398*fd43cf6eSHans Rosenfeld sc->enh_maxpwr[i] = maxpwr; 2399*fd43cf6eSHans Rosenfeld } 2400*fd43cf6eSHans Rosenfeld } 2401*fd43cf6eSHans Rosenfeld 2402*fd43cf6eSHans Rosenfeld static struct ieee80211_node * 2403*fd43cf6eSHans Rosenfeld iwn_node_alloc(ieee80211com_t *ic) 2404*fd43cf6eSHans Rosenfeld { 2405*fd43cf6eSHans Rosenfeld _NOTE(ARGUNUSED(ic)); 2406*fd43cf6eSHans Rosenfeld return (kmem_zalloc(sizeof (struct iwn_node), KM_NOSLEEP)); 2407*fd43cf6eSHans Rosenfeld } 2408*fd43cf6eSHans Rosenfeld 2409*fd43cf6eSHans Rosenfeld static void 2410*fd43cf6eSHans Rosenfeld iwn_node_free(ieee80211_node_t *in) 2411*fd43cf6eSHans Rosenfeld { 2412*fd43cf6eSHans Rosenfeld ASSERT(in != NULL); 2413*fd43cf6eSHans Rosenfeld ASSERT(in->in_ic != NULL); 2414*fd43cf6eSHans Rosenfeld 2415*fd43cf6eSHans Rosenfeld if (in->in_wpa_ie != NULL) 2416*fd43cf6eSHans Rosenfeld ieee80211_free(in->in_wpa_ie); 2417*fd43cf6eSHans Rosenfeld 2418*fd43cf6eSHans Rosenfeld if (in->in_wme_ie != NULL) 2419*fd43cf6eSHans Rosenfeld ieee80211_free(in->in_wme_ie); 2420*fd43cf6eSHans Rosenfeld 2421*fd43cf6eSHans Rosenfeld if (in->in_htcap_ie != NULL) 2422*fd43cf6eSHans Rosenfeld ieee80211_free(in->in_htcap_ie); 2423*fd43cf6eSHans Rosenfeld 2424*fd43cf6eSHans Rosenfeld kmem_free(in, sizeof (struct iwn_node)); 2425*fd43cf6eSHans Rosenfeld } 2426*fd43cf6eSHans Rosenfeld 2427*fd43cf6eSHans Rosenfeld static void 2428*fd43cf6eSHans Rosenfeld iwn_newassoc(struct ieee80211_node *ni, int isnew) 2429*fd43cf6eSHans Rosenfeld { 2430*fd43cf6eSHans Rosenfeld _NOTE(ARGUNUSED(isnew)); 2431*fd43cf6eSHans Rosenfeld struct iwn_softc *sc = (struct iwn_softc *)&ni->in_ic; 2432*fd43cf6eSHans Rosenfeld struct iwn_node *wn = (void *)ni; 2433*fd43cf6eSHans Rosenfeld uint8_t rate, ridx; 2434*fd43cf6eSHans Rosenfeld int i; 2435*fd43cf6eSHans Rosenfeld 2436*fd43cf6eSHans Rosenfeld ieee80211_amrr_node_init(&sc->amrr, &wn->amn); 2437*fd43cf6eSHans Rosenfeld /* 2438*fd43cf6eSHans Rosenfeld * Select a medium rate and depend on AMRR to raise/lower it. 2439*fd43cf6eSHans Rosenfeld */ 2440*fd43cf6eSHans Rosenfeld ni->in_txrate = ni->in_rates.ir_nrates / 2; 2441*fd43cf6eSHans Rosenfeld 2442*fd43cf6eSHans Rosenfeld for (i = 0; i < ni->in_rates.ir_nrates; i++) { 2443*fd43cf6eSHans Rosenfeld rate = ni->in_rates.ir_rates[i] & IEEE80211_RATE_VAL; 2444*fd43cf6eSHans Rosenfeld /* Map 802.11 rate to HW rate index. */ 2445*fd43cf6eSHans Rosenfeld for (ridx = 0; ridx <= IWN_RIDX_MAX; ridx++) 2446*fd43cf6eSHans Rosenfeld if (iwn_rates[ridx].rate == rate) 2447*fd43cf6eSHans Rosenfeld break; 2448*fd43cf6eSHans Rosenfeld wn->ridx[i] = ridx; 2449*fd43cf6eSHans Rosenfeld } 2450*fd43cf6eSHans Rosenfeld } 2451*fd43cf6eSHans Rosenfeld 2452*fd43cf6eSHans Rosenfeld static int 2453*fd43cf6eSHans Rosenfeld iwn_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) 2454*fd43cf6eSHans Rosenfeld { 2455*fd43cf6eSHans Rosenfeld struct iwn_softc *sc = (struct iwn_softc *)ic; 2456*fd43cf6eSHans Rosenfeld enum ieee80211_state ostate; 2457*fd43cf6eSHans Rosenfeld int error; 2458*fd43cf6eSHans Rosenfeld 2459*fd43cf6eSHans Rosenfeld mutex_enter(&sc->sc_mtx); 2460*fd43cf6eSHans Rosenfeld sc->sc_flags |= IWN_FLAG_STOP_CALIB_TO; 2461*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_mtx); 2462*fd43cf6eSHans Rosenfeld 2463*fd43cf6eSHans Rosenfeld (void) untimeout(sc->calib_to); 2464*fd43cf6eSHans Rosenfeld sc->calib_to = 0; 2465*fd43cf6eSHans Rosenfeld 2466*fd43cf6eSHans Rosenfeld mutex_enter(&sc->sc_mtx); 2467*fd43cf6eSHans Rosenfeld ostate = ic->ic_state; 2468*fd43cf6eSHans Rosenfeld 2469*fd43cf6eSHans Rosenfeld DTRACE_PROBE5(new__state, int, sc->sc_flags, 2470*fd43cf6eSHans Rosenfeld enum ieee80211_state, ostate, 2471*fd43cf6eSHans Rosenfeld const char *, ieee80211_state_name[ostate], 2472*fd43cf6eSHans Rosenfeld enum ieee80211_state, nstate, 2473*fd43cf6eSHans Rosenfeld const char *, ieee80211_state_name[nstate]); 2474*fd43cf6eSHans Rosenfeld 2475*fd43cf6eSHans Rosenfeld if ((sc->sc_flags & IWN_FLAG_RADIO_OFF) && nstate != IEEE80211_S_INIT) { 2476*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_mtx); 2477*fd43cf6eSHans Rosenfeld return (IWN_FAIL); 2478*fd43cf6eSHans Rosenfeld } 2479*fd43cf6eSHans Rosenfeld 2480*fd43cf6eSHans Rosenfeld if (!(sc->sc_flags & IWN_FLAG_HW_INITED) && 2481*fd43cf6eSHans Rosenfeld nstate != IEEE80211_S_INIT) { 2482*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_mtx); 2483*fd43cf6eSHans Rosenfeld return (IWN_FAIL); 2484*fd43cf6eSHans Rosenfeld } 2485*fd43cf6eSHans Rosenfeld 2486*fd43cf6eSHans Rosenfeld switch (nstate) { 2487*fd43cf6eSHans Rosenfeld case IEEE80211_S_SCAN: 2488*fd43cf6eSHans Rosenfeld /* XXX Do not abort a running scan. */ 2489*fd43cf6eSHans Rosenfeld if (sc->sc_flags & IWN_FLAG_SCANNING) { 2490*fd43cf6eSHans Rosenfeld if (ostate != nstate) 2491*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, "!scan request(%d)" 2492*fd43cf6eSHans Rosenfeld " while scanning(%d) ignored", nstate, 2493*fd43cf6eSHans Rosenfeld ostate); 2494*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_mtx); 2495*fd43cf6eSHans Rosenfeld return (0); 2496*fd43cf6eSHans Rosenfeld } 2497*fd43cf6eSHans Rosenfeld 2498*fd43cf6eSHans Rosenfeld bcopy(&sc->rxon, &sc->rxon_save, sizeof (sc->rxon)); 2499*fd43cf6eSHans Rosenfeld sc->sc_ostate = ostate; 2500*fd43cf6eSHans Rosenfeld 2501*fd43cf6eSHans Rosenfeld /* XXX Not sure if call and flags are needed. */ 2502*fd43cf6eSHans Rosenfeld ieee80211_node_table_reset(&ic->ic_scan); 2503*fd43cf6eSHans Rosenfeld ic->ic_flags |= IEEE80211_F_SCAN | IEEE80211_F_ASCAN; 2504*fd43cf6eSHans Rosenfeld sc->sc_flags |= IWN_FLAG_SCANNING_2GHZ; 2505*fd43cf6eSHans Rosenfeld 2506*fd43cf6eSHans Rosenfeld /* Make the link LED blink while we're scanning. */ 2507*fd43cf6eSHans Rosenfeld iwn_set_led(sc, IWN_LED_LINK, 10, 10); 2508*fd43cf6eSHans Rosenfeld 2509*fd43cf6eSHans Rosenfeld ic->ic_state = nstate; 2510*fd43cf6eSHans Rosenfeld 2511*fd43cf6eSHans Rosenfeld error = iwn_scan(sc, IEEE80211_CHAN_2GHZ); 2512*fd43cf6eSHans Rosenfeld if (error != 0) { 2513*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 2514*fd43cf6eSHans Rosenfeld "!could not initiate scan"); 2515*fd43cf6eSHans Rosenfeld sc->sc_flags &= ~IWN_FLAG_SCANNING; 2516*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_mtx); 2517*fd43cf6eSHans Rosenfeld return (error); 2518*fd43cf6eSHans Rosenfeld } 2519*fd43cf6eSHans Rosenfeld 2520*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_mtx); 2521*fd43cf6eSHans Rosenfeld sc->scan_to = timeout(iwn_abort_scan, sc, iwn_scan_timeout * 2522*fd43cf6eSHans Rosenfeld drv_usectohz(MICROSEC)); 2523*fd43cf6eSHans Rosenfeld return (error); 2524*fd43cf6eSHans Rosenfeld 2525*fd43cf6eSHans Rosenfeld case IEEE80211_S_ASSOC: 2526*fd43cf6eSHans Rosenfeld if (ostate != IEEE80211_S_RUN) { 2527*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_mtx); 2528*fd43cf6eSHans Rosenfeld break; 2529*fd43cf6eSHans Rosenfeld } 2530*fd43cf6eSHans Rosenfeld /* FALLTHROUGH */ 2531*fd43cf6eSHans Rosenfeld case IEEE80211_S_AUTH: 2532*fd43cf6eSHans Rosenfeld /* Reset state to handle reassociations correctly. */ 2533*fd43cf6eSHans Rosenfeld sc->rxon.associd = 0; 2534*fd43cf6eSHans Rosenfeld sc->rxon.filter &= ~htole32(IWN_FILTER_BSS); 2535*fd43cf6eSHans Rosenfeld sc->calib.state = IWN_CALIB_STATE_INIT; 2536*fd43cf6eSHans Rosenfeld 2537*fd43cf6eSHans Rosenfeld if ((error = iwn_auth(sc)) != 0) { 2538*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_mtx); 2539*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 2540*fd43cf6eSHans Rosenfeld "!could not move to auth state"); 2541*fd43cf6eSHans Rosenfeld return error; 2542*fd43cf6eSHans Rosenfeld } 2543*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_mtx); 2544*fd43cf6eSHans Rosenfeld break; 2545*fd43cf6eSHans Rosenfeld 2546*fd43cf6eSHans Rosenfeld case IEEE80211_S_RUN: 2547*fd43cf6eSHans Rosenfeld if ((error = iwn_run(sc)) != 0) { 2548*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_mtx); 2549*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 2550*fd43cf6eSHans Rosenfeld "!could not move to run state"); 2551*fd43cf6eSHans Rosenfeld return error; 2552*fd43cf6eSHans Rosenfeld } 2553*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_mtx); 2554*fd43cf6eSHans Rosenfeld break; 2555*fd43cf6eSHans Rosenfeld 2556*fd43cf6eSHans Rosenfeld case IEEE80211_S_INIT: 2557*fd43cf6eSHans Rosenfeld sc->sc_flags &= ~IWN_FLAG_SCANNING; 2558*fd43cf6eSHans Rosenfeld sc->calib.state = IWN_CALIB_STATE_INIT; 2559*fd43cf6eSHans Rosenfeld 2560*fd43cf6eSHans Rosenfeld /* 2561*fd43cf6eSHans Rosenfeld * set LED off after init 2562*fd43cf6eSHans Rosenfeld */ 2563*fd43cf6eSHans Rosenfeld iwn_set_led(sc, IWN_LED_LINK, 1, 0); 2564*fd43cf6eSHans Rosenfeld 2565*fd43cf6eSHans Rosenfeld cv_signal(&sc->sc_scan_cv); 2566*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_mtx); 2567*fd43cf6eSHans Rosenfeld if (sc->scan_to != 0) 2568*fd43cf6eSHans Rosenfeld (void) untimeout(sc->scan_to); 2569*fd43cf6eSHans Rosenfeld sc->scan_to = 0; 2570*fd43cf6eSHans Rosenfeld break; 2571*fd43cf6eSHans Rosenfeld } 2572*fd43cf6eSHans Rosenfeld 2573*fd43cf6eSHans Rosenfeld error = sc->sc_newstate(ic, nstate, arg); 2574*fd43cf6eSHans Rosenfeld 2575*fd43cf6eSHans Rosenfeld if (nstate == IEEE80211_S_RUN) 2576*fd43cf6eSHans Rosenfeld ieee80211_start_watchdog(ic, 1); 2577*fd43cf6eSHans Rosenfeld 2578*fd43cf6eSHans Rosenfeld return (error); 2579*fd43cf6eSHans Rosenfeld } 2580*fd43cf6eSHans Rosenfeld 2581*fd43cf6eSHans Rosenfeld static void 2582*fd43cf6eSHans Rosenfeld iwn_iter_func(void *arg, struct ieee80211_node *ni) 2583*fd43cf6eSHans Rosenfeld { 2584*fd43cf6eSHans Rosenfeld struct iwn_softc *sc = arg; 2585*fd43cf6eSHans Rosenfeld struct iwn_node *wn = (struct iwn_node *)ni; 2586*fd43cf6eSHans Rosenfeld 2587*fd43cf6eSHans Rosenfeld ieee80211_amrr_choose(&sc->amrr, ni, &wn->amn); 2588*fd43cf6eSHans Rosenfeld } 2589*fd43cf6eSHans Rosenfeld 2590*fd43cf6eSHans Rosenfeld static void 2591*fd43cf6eSHans Rosenfeld iwn_calib_timeout(void *arg) 2592*fd43cf6eSHans Rosenfeld { 2593*fd43cf6eSHans Rosenfeld struct iwn_softc *sc = arg; 2594*fd43cf6eSHans Rosenfeld struct ieee80211com *ic = &sc->sc_ic; 2595*fd43cf6eSHans Rosenfeld 2596*fd43cf6eSHans Rosenfeld mutex_enter(&sc->sc_mtx); 2597*fd43cf6eSHans Rosenfeld 2598*fd43cf6eSHans Rosenfeld if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) { 2599*fd43cf6eSHans Rosenfeld if (ic->ic_opmode == IEEE80211_M_STA) 2600*fd43cf6eSHans Rosenfeld iwn_iter_func(sc, ic->ic_bss); 2601*fd43cf6eSHans Rosenfeld else 2602*fd43cf6eSHans Rosenfeld ieee80211_iterate_nodes(&ic->ic_sta, iwn_iter_func, sc); 2603*fd43cf6eSHans Rosenfeld } 2604*fd43cf6eSHans Rosenfeld /* Force automatic TX power calibration every 60 secs. */ 2605*fd43cf6eSHans Rosenfeld if (++sc->calib_cnt >= 120) { 2606*fd43cf6eSHans Rosenfeld uint32_t flags = 0; 2607*fd43cf6eSHans Rosenfeld 2608*fd43cf6eSHans Rosenfeld DTRACE_PROBE(get__statistics); 2609*fd43cf6eSHans Rosenfeld (void)iwn_cmd(sc, IWN_CMD_GET_STATISTICS, &flags, 2610*fd43cf6eSHans Rosenfeld sizeof flags, 1); 2611*fd43cf6eSHans Rosenfeld sc->calib_cnt = 0; 2612*fd43cf6eSHans Rosenfeld } 2613*fd43cf6eSHans Rosenfeld 2614*fd43cf6eSHans Rosenfeld /* Automatic rate control triggered every 500ms. */ 2615*fd43cf6eSHans Rosenfeld if ((sc->sc_flags & IWN_FLAG_STOP_CALIB_TO) == 0) 2616*fd43cf6eSHans Rosenfeld sc->calib_to = timeout(iwn_calib_timeout, sc, 2617*fd43cf6eSHans Rosenfeld drv_usectohz(500000)); 2618*fd43cf6eSHans Rosenfeld 2619*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_mtx); 2620*fd43cf6eSHans Rosenfeld } 2621*fd43cf6eSHans Rosenfeld 2622*fd43cf6eSHans Rosenfeld /* 2623*fd43cf6eSHans Rosenfeld * Process an RX_PHY firmware notification. This is usually immediately 2624*fd43cf6eSHans Rosenfeld * followed by an MPDU_RX_DONE notification. 2625*fd43cf6eSHans Rosenfeld */ 2626*fd43cf6eSHans Rosenfeld static void 2627*fd43cf6eSHans Rosenfeld iwn_rx_phy(struct iwn_softc *sc, struct iwn_rx_desc *desc, 2628*fd43cf6eSHans Rosenfeld struct iwn_rx_data *data) 2629*fd43cf6eSHans Rosenfeld { 2630*fd43cf6eSHans Rosenfeld struct iwn_rx_stat *stat = (struct iwn_rx_stat *)(desc + 1); 2631*fd43cf6eSHans Rosenfeld 2632*fd43cf6eSHans Rosenfeld (void) ddi_dma_sync(data->dma_data.dma_hdl, sizeof (*desc), 2633*fd43cf6eSHans Rosenfeld sizeof (*stat), DDI_DMA_SYNC_FORKERNEL); 2634*fd43cf6eSHans Rosenfeld 2635*fd43cf6eSHans Rosenfeld DTRACE_PROBE1(rx__phy, struct iwn_rx_stat *, stat); 2636*fd43cf6eSHans Rosenfeld 2637*fd43cf6eSHans Rosenfeld /* Save RX statistics, they will be used on MPDU_RX_DONE. */ 2638*fd43cf6eSHans Rosenfeld memcpy(&sc->last_rx_stat, stat, sizeof (*stat)); 2639*fd43cf6eSHans Rosenfeld sc->last_rx_valid = 1; 2640*fd43cf6eSHans Rosenfeld } 2641*fd43cf6eSHans Rosenfeld 2642*fd43cf6eSHans Rosenfeld /* 2643*fd43cf6eSHans Rosenfeld * Process an RX_DONE (4965AGN only) or MPDU_RX_DONE firmware notification. 2644*fd43cf6eSHans Rosenfeld * Each MPDU_RX_DONE notification must be preceded by an RX_PHY one. 2645*fd43cf6eSHans Rosenfeld */ 2646*fd43cf6eSHans Rosenfeld static void 2647*fd43cf6eSHans Rosenfeld iwn_rx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, 2648*fd43cf6eSHans Rosenfeld struct iwn_rx_data *data) 2649*fd43cf6eSHans Rosenfeld { 2650*fd43cf6eSHans Rosenfeld struct iwn_ops *ops = &sc->ops; 2651*fd43cf6eSHans Rosenfeld struct ieee80211com *ic = &sc->sc_ic; 2652*fd43cf6eSHans Rosenfeld struct iwn_rx_ring *ring = &sc->rxq; 2653*fd43cf6eSHans Rosenfeld struct ieee80211_frame *wh; 2654*fd43cf6eSHans Rosenfeld struct ieee80211_node *ni; 2655*fd43cf6eSHans Rosenfeld mblk_t *m; 2656*fd43cf6eSHans Rosenfeld struct iwn_rx_stat *stat; 2657*fd43cf6eSHans Rosenfeld char *head; 2658*fd43cf6eSHans Rosenfeld uint32_t flags; 2659*fd43cf6eSHans Rosenfeld int len, rssi; 2660*fd43cf6eSHans Rosenfeld 2661*fd43cf6eSHans Rosenfeld if (desc->type == IWN_MPDU_RX_DONE) { 2662*fd43cf6eSHans Rosenfeld /* Check for prior RX_PHY notification. */ 2663*fd43cf6eSHans Rosenfeld if (!sc->last_rx_valid) { 2664*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 2665*fd43cf6eSHans Rosenfeld "missing RX_PHY"); 2666*fd43cf6eSHans Rosenfeld return; 2667*fd43cf6eSHans Rosenfeld } 2668*fd43cf6eSHans Rosenfeld sc->last_rx_valid = 0; 2669*fd43cf6eSHans Rosenfeld stat = &sc->last_rx_stat; 2670*fd43cf6eSHans Rosenfeld } else 2671*fd43cf6eSHans Rosenfeld stat = (struct iwn_rx_stat *)(desc + 1); 2672*fd43cf6eSHans Rosenfeld 2673*fd43cf6eSHans Rosenfeld (void) ddi_dma_sync(data->dma_data.dma_hdl, 0, 0, 2674*fd43cf6eSHans Rosenfeld DDI_DMA_SYNC_FORKERNEL); 2675*fd43cf6eSHans Rosenfeld 2676*fd43cf6eSHans Rosenfeld if (stat->cfg_phy_len > IWN_STAT_MAXLEN) { 2677*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 2678*fd43cf6eSHans Rosenfeld "!invalid RX statistic header"); 2679*fd43cf6eSHans Rosenfeld return; 2680*fd43cf6eSHans Rosenfeld } 2681*fd43cf6eSHans Rosenfeld if (desc->type == IWN_MPDU_RX_DONE) { 2682*fd43cf6eSHans Rosenfeld struct iwn_rx_mpdu *mpdu = (struct iwn_rx_mpdu *)(desc + 1); 2683*fd43cf6eSHans Rosenfeld head = (char *)(mpdu + 1); 2684*fd43cf6eSHans Rosenfeld len = le16toh(mpdu->len); 2685*fd43cf6eSHans Rosenfeld } else { 2686*fd43cf6eSHans Rosenfeld head = (char *)(stat + 1) + stat->cfg_phy_len; 2687*fd43cf6eSHans Rosenfeld len = le16toh(stat->len); 2688*fd43cf6eSHans Rosenfeld } 2689*fd43cf6eSHans Rosenfeld /*LINTED: E_PTR_BAD_CAST_ALIGN*/ 2690*fd43cf6eSHans Rosenfeld flags = le32toh(*(uint32_t *)(head + len)); 2691*fd43cf6eSHans Rosenfeld 2692*fd43cf6eSHans Rosenfeld /* Discard frames with a bad FCS early. */ 2693*fd43cf6eSHans Rosenfeld if ((flags & IWN_RX_NOERROR) != IWN_RX_NOERROR) { 2694*fd43cf6eSHans Rosenfeld sc->sc_rx_err++; 2695*fd43cf6eSHans Rosenfeld ic->ic_stats.is_fcs_errors++; 2696*fd43cf6eSHans Rosenfeld return; 2697*fd43cf6eSHans Rosenfeld } 2698*fd43cf6eSHans Rosenfeld /* Discard frames that are too short. */ 2699*fd43cf6eSHans Rosenfeld if (len < sizeof (*wh)) { 2700*fd43cf6eSHans Rosenfeld sc->sc_rx_err++; 2701*fd43cf6eSHans Rosenfeld return; 2702*fd43cf6eSHans Rosenfeld } 2703*fd43cf6eSHans Rosenfeld 2704*fd43cf6eSHans Rosenfeld m = allocb(len, BPRI_MED); 2705*fd43cf6eSHans Rosenfeld if (m == NULL) { 2706*fd43cf6eSHans Rosenfeld sc->sc_rx_nobuf++; 2707*fd43cf6eSHans Rosenfeld return; 2708*fd43cf6eSHans Rosenfeld } 2709*fd43cf6eSHans Rosenfeld 2710*fd43cf6eSHans Rosenfeld /* Update RX descriptor. */ 2711*fd43cf6eSHans Rosenfeld ring->desc[ring->cur] = 2712*fd43cf6eSHans Rosenfeld htole32(data->dma_data.paddr >> 8); 2713*fd43cf6eSHans Rosenfeld (void) ddi_dma_sync(ring->desc_dma.dma_hdl, 2714*fd43cf6eSHans Rosenfeld ring->cur * sizeof (uint32_t), sizeof (uint32_t), 2715*fd43cf6eSHans Rosenfeld DDI_DMA_SYNC_FORDEV); 2716*fd43cf6eSHans Rosenfeld 2717*fd43cf6eSHans Rosenfeld /* Grab a reference to the source node. */ 2718*fd43cf6eSHans Rosenfeld wh = (struct ieee80211_frame*)head; 2719*fd43cf6eSHans Rosenfeld ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame *)wh); 2720*fd43cf6eSHans Rosenfeld 2721*fd43cf6eSHans Rosenfeld /* XXX OpenBSD adds decryption here (see also comments in iwn_tx). */ 2722*fd43cf6eSHans Rosenfeld /* NetBSD does decryption in ieee80211_input. */ 2723*fd43cf6eSHans Rosenfeld 2724*fd43cf6eSHans Rosenfeld rssi = ops->get_rssi(stat); 2725*fd43cf6eSHans Rosenfeld 2726*fd43cf6eSHans Rosenfeld /* 2727*fd43cf6eSHans Rosenfeld * convert dBm to percentage 2728*fd43cf6eSHans Rosenfeld */ 2729*fd43cf6eSHans Rosenfeld rssi = (100 * 75 * 75 - (-20 - rssi) * (15 * 75 + 62 * (-20 - rssi))) 2730*fd43cf6eSHans Rosenfeld / (75 * 75); 2731*fd43cf6eSHans Rosenfeld if (rssi > 100) 2732*fd43cf6eSHans Rosenfeld rssi = 100; 2733*fd43cf6eSHans Rosenfeld else if (rssi < 1) 2734*fd43cf6eSHans Rosenfeld rssi = 1; 2735*fd43cf6eSHans Rosenfeld 2736*fd43cf6eSHans Rosenfeld bcopy(wh, m->b_wptr, len); 2737*fd43cf6eSHans Rosenfeld m->b_wptr += len; 2738*fd43cf6eSHans Rosenfeld 2739*fd43cf6eSHans Rosenfeld /* XXX Added for NetBSD: scans never stop without it */ 2740*fd43cf6eSHans Rosenfeld if (ic->ic_state == IEEE80211_S_SCAN) 2741*fd43cf6eSHans Rosenfeld iwn_fix_channel(sc, m, stat); 2742*fd43cf6eSHans Rosenfeld 2743*fd43cf6eSHans Rosenfeld /* Send the frame to the 802.11 layer. */ 2744*fd43cf6eSHans Rosenfeld ieee80211_input(ic, m, ni, rssi, 0); 2745*fd43cf6eSHans Rosenfeld 2746*fd43cf6eSHans Rosenfeld /* Node is no longer needed. */ 2747*fd43cf6eSHans Rosenfeld ieee80211_free_node(ni); 2748*fd43cf6eSHans Rosenfeld } 2749*fd43cf6eSHans Rosenfeld 2750*fd43cf6eSHans Rosenfeld #ifndef IEEE80211_NO_HT 2751*fd43cf6eSHans Rosenfeld /* Process an incoming Compressed BlockAck. */ 2752*fd43cf6eSHans Rosenfeld static void 2753*fd43cf6eSHans Rosenfeld iwn_rx_compressed_ba(struct iwn_softc *sc, struct iwn_rx_desc *desc, 2754*fd43cf6eSHans Rosenfeld struct iwn_rx_data *data) 2755*fd43cf6eSHans Rosenfeld { 2756*fd43cf6eSHans Rosenfeld struct iwn_compressed_ba *ba = (struct iwn_compressed_ba *)(desc + 1); 2757*fd43cf6eSHans Rosenfeld struct iwn_tx_ring *txq; 2758*fd43cf6eSHans Rosenfeld 2759*fd43cf6eSHans Rosenfeld (void) ddi_dma_sync(data->dma_data.dma_hdl, sizeof (*desc), 2760*fd43cf6eSHans Rosenfeld sizeof (*ba), DDI_DMA_SYNC_FORKERNEL); 2761*fd43cf6eSHans Rosenfeld 2762*fd43cf6eSHans Rosenfeld txq = &sc->txq[le16toh(ba->qid)]; 2763*fd43cf6eSHans Rosenfeld /* XXX TBD */ 2764*fd43cf6eSHans Rosenfeld } 2765*fd43cf6eSHans Rosenfeld #endif 2766*fd43cf6eSHans Rosenfeld 2767*fd43cf6eSHans Rosenfeld /* 2768*fd43cf6eSHans Rosenfeld * Process a CALIBRATION_RESULT notification sent by the initialization 2769*fd43cf6eSHans Rosenfeld * firmware on response to a CMD_CALIB_CONFIG command (5000 only). 2770*fd43cf6eSHans Rosenfeld */ 2771*fd43cf6eSHans Rosenfeld static void 2772*fd43cf6eSHans Rosenfeld iwn5000_rx_calib_results(struct iwn_softc *sc, struct iwn_rx_desc *desc, 2773*fd43cf6eSHans Rosenfeld struct iwn_rx_data *data) 2774*fd43cf6eSHans Rosenfeld { 2775*fd43cf6eSHans Rosenfeld struct iwn_phy_calib *calib = (struct iwn_phy_calib *)(desc + 1); 2776*fd43cf6eSHans Rosenfeld int len, idx = -1; 2777*fd43cf6eSHans Rosenfeld 2778*fd43cf6eSHans Rosenfeld /* Runtime firmware should not send such a notification. */ 2779*fd43cf6eSHans Rosenfeld if (sc->sc_flags & IWN_FLAG_CALIB_DONE) 2780*fd43cf6eSHans Rosenfeld return; 2781*fd43cf6eSHans Rosenfeld 2782*fd43cf6eSHans Rosenfeld len = (le32toh(desc->len) & 0x3fff) - 4; 2783*fd43cf6eSHans Rosenfeld (void) ddi_dma_sync(data->dma_data.dma_hdl, sizeof (*desc), len, 2784*fd43cf6eSHans Rosenfeld DDI_DMA_SYNC_FORKERNEL); 2785*fd43cf6eSHans Rosenfeld 2786*fd43cf6eSHans Rosenfeld switch (calib->code) { 2787*fd43cf6eSHans Rosenfeld case IWN5000_PHY_CALIB_DC: 2788*fd43cf6eSHans Rosenfeld if (sc->hw_type == IWN_HW_REV_TYPE_5150 || 2789*fd43cf6eSHans Rosenfeld sc->hw_type == IWN_HW_REV_TYPE_2030 || 2790*fd43cf6eSHans Rosenfeld sc->hw_type == IWN_HW_REV_TYPE_2000 || 2791*fd43cf6eSHans Rosenfeld sc->hw_type == IWN_HW_REV_TYPE_135 || 2792*fd43cf6eSHans Rosenfeld sc->hw_type == IWN_HW_REV_TYPE_105) 2793*fd43cf6eSHans Rosenfeld idx = 0; 2794*fd43cf6eSHans Rosenfeld break; 2795*fd43cf6eSHans Rosenfeld case IWN5000_PHY_CALIB_LO: 2796*fd43cf6eSHans Rosenfeld idx = 1; 2797*fd43cf6eSHans Rosenfeld break; 2798*fd43cf6eSHans Rosenfeld case IWN5000_PHY_CALIB_TX_IQ: 2799*fd43cf6eSHans Rosenfeld idx = 2; 2800*fd43cf6eSHans Rosenfeld break; 2801*fd43cf6eSHans Rosenfeld case IWN5000_PHY_CALIB_TX_IQ_PERIODIC: 2802*fd43cf6eSHans Rosenfeld if (sc->hw_type < IWN_HW_REV_TYPE_6000 && 2803*fd43cf6eSHans Rosenfeld sc->hw_type != IWN_HW_REV_TYPE_5150) 2804*fd43cf6eSHans Rosenfeld idx = 3; 2805*fd43cf6eSHans Rosenfeld break; 2806*fd43cf6eSHans Rosenfeld case IWN5000_PHY_CALIB_BASE_BAND: 2807*fd43cf6eSHans Rosenfeld idx = 4; 2808*fd43cf6eSHans Rosenfeld break; 2809*fd43cf6eSHans Rosenfeld } 2810*fd43cf6eSHans Rosenfeld if (idx == -1) /* Ignore other results. */ 2811*fd43cf6eSHans Rosenfeld return; 2812*fd43cf6eSHans Rosenfeld 2813*fd43cf6eSHans Rosenfeld /* Save calibration result. */ 2814*fd43cf6eSHans Rosenfeld if (sc->calibcmd[idx].buf != NULL) 2815*fd43cf6eSHans Rosenfeld kmem_free(sc->calibcmd[idx].buf, sc->calibcmd[idx].len); 2816*fd43cf6eSHans Rosenfeld sc->calibcmd[idx].buf = kmem_zalloc(len, KM_NOSLEEP); 2817*fd43cf6eSHans Rosenfeld if (sc->calibcmd[idx].buf == NULL) { 2818*fd43cf6eSHans Rosenfeld return; 2819*fd43cf6eSHans Rosenfeld } 2820*fd43cf6eSHans Rosenfeld sc->calibcmd[idx].len = len; 2821*fd43cf6eSHans Rosenfeld memcpy(sc->calibcmd[idx].buf, calib, len); 2822*fd43cf6eSHans Rosenfeld } 2823*fd43cf6eSHans Rosenfeld 2824*fd43cf6eSHans Rosenfeld /* 2825*fd43cf6eSHans Rosenfeld * Process an RX_STATISTICS or BEACON_STATISTICS firmware notification. 2826*fd43cf6eSHans Rosenfeld * The latter is sent by the firmware after each received beacon. 2827*fd43cf6eSHans Rosenfeld */ 2828*fd43cf6eSHans Rosenfeld static void 2829*fd43cf6eSHans Rosenfeld iwn_rx_statistics(struct iwn_softc *sc, struct iwn_rx_desc *desc, 2830*fd43cf6eSHans Rosenfeld struct iwn_rx_data *data) 2831*fd43cf6eSHans Rosenfeld { 2832*fd43cf6eSHans Rosenfeld struct iwn_ops *ops = &sc->ops; 2833*fd43cf6eSHans Rosenfeld struct ieee80211com *ic = &sc->sc_ic; 2834*fd43cf6eSHans Rosenfeld struct iwn_calib_state *calib = &sc->calib; 2835*fd43cf6eSHans Rosenfeld struct iwn_stats *stats = (struct iwn_stats *)(desc + 1); 2836*fd43cf6eSHans Rosenfeld int temp = 0; 2837*fd43cf6eSHans Rosenfeld 2838*fd43cf6eSHans Rosenfeld /* Ignore statistics received during a scan. */ 2839*fd43cf6eSHans Rosenfeld if (ic->ic_state != IEEE80211_S_RUN) 2840*fd43cf6eSHans Rosenfeld return; 2841*fd43cf6eSHans Rosenfeld 2842*fd43cf6eSHans Rosenfeld (void) ddi_dma_sync(data->dma_data.dma_hdl, sizeof (*desc), 2843*fd43cf6eSHans Rosenfeld sizeof (*stats), DDI_DMA_SYNC_FORKERNEL); 2844*fd43cf6eSHans Rosenfeld 2845*fd43cf6eSHans Rosenfeld sc->calib_cnt = 0; /* Reset TX power calibration timeout. */ 2846*fd43cf6eSHans Rosenfeld 2847*fd43cf6eSHans Rosenfeld /* Test if temperature has changed. */ 2848*fd43cf6eSHans Rosenfeld if (stats->general.temp != sc->rawtemp) { 2849*fd43cf6eSHans Rosenfeld /* Convert "raw" temperature to degC. */ 2850*fd43cf6eSHans Rosenfeld sc->rawtemp = stats->general.temp; 2851*fd43cf6eSHans Rosenfeld temp = ops->get_temperature(sc); 2852*fd43cf6eSHans Rosenfeld sc->sc_misc->temp.value.ul = temp; 2853*fd43cf6eSHans Rosenfeld 2854*fd43cf6eSHans Rosenfeld /* Update TX power if need be (4965AGN only). */ 2855*fd43cf6eSHans Rosenfeld if (sc->hw_type == IWN_HW_REV_TYPE_4965) 2856*fd43cf6eSHans Rosenfeld iwn4965_power_calibration(sc, temp); 2857*fd43cf6eSHans Rosenfeld } 2858*fd43cf6eSHans Rosenfeld 2859*fd43cf6eSHans Rosenfeld DTRACE_PROBE2(rx__statistics, struct iwn_stats *, stats, int, temp); 2860*fd43cf6eSHans Rosenfeld 2861*fd43cf6eSHans Rosenfeld if (desc->type != IWN_BEACON_STATISTICS) 2862*fd43cf6eSHans Rosenfeld return; /* Reply to a statistics request. */ 2863*fd43cf6eSHans Rosenfeld 2864*fd43cf6eSHans Rosenfeld sc->noise = iwn_get_noise(&stats->rx.general); 2865*fd43cf6eSHans Rosenfeld sc->sc_misc->noise.value.l = sc->noise; 2866*fd43cf6eSHans Rosenfeld 2867*fd43cf6eSHans Rosenfeld /* Test that RSSI and noise are present in stats report. */ 2868*fd43cf6eSHans Rosenfeld if (le32toh(stats->rx.general.flags) != 1) { 2869*fd43cf6eSHans Rosenfeld return; 2870*fd43cf6eSHans Rosenfeld } 2871*fd43cf6eSHans Rosenfeld 2872*fd43cf6eSHans Rosenfeld /* 2873*fd43cf6eSHans Rosenfeld * XXX Differential gain calibration makes the 6005 firmware 2874*fd43cf6eSHans Rosenfeld * crap out, so skip it for now. This effectively disables 2875*fd43cf6eSHans Rosenfeld * sensitivity tuning as well. 2876*fd43cf6eSHans Rosenfeld */ 2877*fd43cf6eSHans Rosenfeld if (sc->hw_type == IWN_HW_REV_TYPE_6005) 2878*fd43cf6eSHans Rosenfeld return; 2879*fd43cf6eSHans Rosenfeld 2880*fd43cf6eSHans Rosenfeld if (calib->state == IWN_CALIB_STATE_ASSOC) 2881*fd43cf6eSHans Rosenfeld iwn_collect_noise(sc, &stats->rx.general); 2882*fd43cf6eSHans Rosenfeld else if (calib->state == IWN_CALIB_STATE_RUN) 2883*fd43cf6eSHans Rosenfeld iwn_tune_sensitivity(sc, &stats->rx); 2884*fd43cf6eSHans Rosenfeld } 2885*fd43cf6eSHans Rosenfeld 2886*fd43cf6eSHans Rosenfeld /* 2887*fd43cf6eSHans Rosenfeld * Process a TX_DONE firmware notification. Unfortunately, the 4965AGN 2888*fd43cf6eSHans Rosenfeld * and 5000 adapters have different incompatible TX status formats. 2889*fd43cf6eSHans Rosenfeld */ 2890*fd43cf6eSHans Rosenfeld static void 2891*fd43cf6eSHans Rosenfeld iwn4965_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, 2892*fd43cf6eSHans Rosenfeld struct iwn_rx_data *data) 2893*fd43cf6eSHans Rosenfeld { 2894*fd43cf6eSHans Rosenfeld struct iwn4965_tx_stat *stat = (struct iwn4965_tx_stat *)(desc + 1); 2895*fd43cf6eSHans Rosenfeld 2896*fd43cf6eSHans Rosenfeld (void) ddi_dma_sync(data->dma_data.dma_hdl, sizeof (*desc), 2897*fd43cf6eSHans Rosenfeld sizeof (*stat), DDI_DMA_SYNC_FORKERNEL); 2898*fd43cf6eSHans Rosenfeld iwn_tx_done(sc, desc, stat->ackfailcnt, le32toh(stat->status) & 0xff); 2899*fd43cf6eSHans Rosenfeld } 2900*fd43cf6eSHans Rosenfeld 2901*fd43cf6eSHans Rosenfeld static void 2902*fd43cf6eSHans Rosenfeld iwn5000_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, 2903*fd43cf6eSHans Rosenfeld struct iwn_rx_data *data) 2904*fd43cf6eSHans Rosenfeld { 2905*fd43cf6eSHans Rosenfeld struct iwn5000_tx_stat *stat = (struct iwn5000_tx_stat *)(desc + 1); 2906*fd43cf6eSHans Rosenfeld 2907*fd43cf6eSHans Rosenfeld #ifdef notyet 2908*fd43cf6eSHans Rosenfeld /* Reset TX scheduler slot. */ 2909*fd43cf6eSHans Rosenfeld iwn5000_reset_sched(sc, desc->qid & 0xf, desc->idx); 2910*fd43cf6eSHans Rosenfeld #endif 2911*fd43cf6eSHans Rosenfeld 2912*fd43cf6eSHans Rosenfeld (void) ddi_dma_sync(data->dma_data.dma_hdl, sizeof (*desc), 2913*fd43cf6eSHans Rosenfeld sizeof (*stat), DDI_DMA_SYNC_FORKERNEL); 2914*fd43cf6eSHans Rosenfeld iwn_tx_done(sc, desc, stat->ackfailcnt, le16toh(stat->status) & 0xff); 2915*fd43cf6eSHans Rosenfeld } 2916*fd43cf6eSHans Rosenfeld 2917*fd43cf6eSHans Rosenfeld /* 2918*fd43cf6eSHans Rosenfeld * Adapter-independent backend for TX_DONE firmware notifications. 2919*fd43cf6eSHans Rosenfeld */ 2920*fd43cf6eSHans Rosenfeld static void 2921*fd43cf6eSHans Rosenfeld iwn_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, int ackfailcnt, 2922*fd43cf6eSHans Rosenfeld uint8_t status) 2923*fd43cf6eSHans Rosenfeld { 2924*fd43cf6eSHans Rosenfeld struct iwn_tx_ring *ring = &sc->txq[desc->qid & 0xf]; 2925*fd43cf6eSHans Rosenfeld struct iwn_tx_data *data = &ring->data[desc->idx]; 2926*fd43cf6eSHans Rosenfeld struct iwn_node *wn = (struct iwn_node *)data->ni; 2927*fd43cf6eSHans Rosenfeld 2928*fd43cf6eSHans Rosenfeld /* Update rate control statistics. */ 2929*fd43cf6eSHans Rosenfeld wn->amn.amn_txcnt++; 2930*fd43cf6eSHans Rosenfeld if (ackfailcnt > 0) 2931*fd43cf6eSHans Rosenfeld wn->amn.amn_retrycnt++; 2932*fd43cf6eSHans Rosenfeld 2933*fd43cf6eSHans Rosenfeld if (status != 1 && status != 2) 2934*fd43cf6eSHans Rosenfeld sc->sc_tx_err++; 2935*fd43cf6eSHans Rosenfeld else 2936*fd43cf6eSHans Rosenfeld sc->sc_ic.ic_stats.is_tx_frags++; 2937*fd43cf6eSHans Rosenfeld 2938*fd43cf6eSHans Rosenfeld ieee80211_free_node(data->ni); 2939*fd43cf6eSHans Rosenfeld data->ni = NULL; 2940*fd43cf6eSHans Rosenfeld 2941*fd43cf6eSHans Rosenfeld mutex_enter(&sc->sc_tx_mtx); 2942*fd43cf6eSHans Rosenfeld sc->sc_tx_timer = 0; 2943*fd43cf6eSHans Rosenfeld if (--ring->queued < IWN_TX_RING_LOMARK) { 2944*fd43cf6eSHans Rosenfeld sc->qfullmsk &= ~(1 << ring->qid); 2945*fd43cf6eSHans Rosenfeld } 2946*fd43cf6eSHans Rosenfeld mac_tx_update(sc->sc_ic.ic_mach); 2947*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_tx_mtx); 2948*fd43cf6eSHans Rosenfeld } 2949*fd43cf6eSHans Rosenfeld 2950*fd43cf6eSHans Rosenfeld /* 2951*fd43cf6eSHans Rosenfeld * Process a "command done" firmware notification. This is where we wakeup 2952*fd43cf6eSHans Rosenfeld * processes waiting for a synchronous command completion. 2953*fd43cf6eSHans Rosenfeld */ 2954*fd43cf6eSHans Rosenfeld static void 2955*fd43cf6eSHans Rosenfeld iwn_cmd_done(struct iwn_softc *sc, struct iwn_rx_desc *desc) 2956*fd43cf6eSHans Rosenfeld { 2957*fd43cf6eSHans Rosenfeld struct iwn_tx_ring *ring = &sc->txq[IWN_CMD_QUEUE_NUM]; 2958*fd43cf6eSHans Rosenfeld struct iwn_tx_data *data; 2959*fd43cf6eSHans Rosenfeld 2960*fd43cf6eSHans Rosenfeld if ((desc->qid & 0xf) != IWN_CMD_QUEUE_NUM) 2961*fd43cf6eSHans Rosenfeld return; /* Not a command ack. */ 2962*fd43cf6eSHans Rosenfeld 2963*fd43cf6eSHans Rosenfeld data = &ring->data[desc->idx]; 2964*fd43cf6eSHans Rosenfeld 2965*fd43cf6eSHans Rosenfeld (void) ddi_dma_sync(data->dma_data.dma_hdl, 0, 0, DDI_DMA_SYNC_FORDEV); 2966*fd43cf6eSHans Rosenfeld 2967*fd43cf6eSHans Rosenfeld /* If the command was mapped in an extra buffer, free it. */ 2968*fd43cf6eSHans Rosenfeld if (data->cmd_dma.dma_hdl) { 2969*fd43cf6eSHans Rosenfeld (void) ddi_dma_sync(data->cmd_dma.dma_hdl, 0, 0, 2970*fd43cf6eSHans Rosenfeld DDI_DMA_SYNC_FORDEV); 2971*fd43cf6eSHans Rosenfeld iwn_dma_contig_free(&data->cmd_dma); 2972*fd43cf6eSHans Rosenfeld } 2973*fd43cf6eSHans Rosenfeld 2974*fd43cf6eSHans Rosenfeld mutex_enter(&sc->sc_mtx); 2975*fd43cf6eSHans Rosenfeld sc->sc_cmd_flag = SC_CMD_FLG_DONE; 2976*fd43cf6eSHans Rosenfeld cv_signal(&sc->sc_cmd_cv); 2977*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_mtx); 2978*fd43cf6eSHans Rosenfeld } 2979*fd43cf6eSHans Rosenfeld 2980*fd43cf6eSHans Rosenfeld /* 2981*fd43cf6eSHans Rosenfeld * Process an INT_FH_RX or INT_SW_RX interrupt. 2982*fd43cf6eSHans Rosenfeld */ 2983*fd43cf6eSHans Rosenfeld static void 2984*fd43cf6eSHans Rosenfeld iwn_notif_intr(struct iwn_softc *sc) 2985*fd43cf6eSHans Rosenfeld { 2986*fd43cf6eSHans Rosenfeld struct iwn_ops *ops = &sc->ops; 2987*fd43cf6eSHans Rosenfeld struct ieee80211com *ic = &sc->sc_ic; 2988*fd43cf6eSHans Rosenfeld uint16_t hw; 2989*fd43cf6eSHans Rosenfeld 2990*fd43cf6eSHans Rosenfeld ASSERT(sc != NULL); 2991*fd43cf6eSHans Rosenfeld 2992*fd43cf6eSHans Rosenfeld (void) ddi_dma_sync(sc->rxq.stat_dma.dma_hdl, 0, 0, 2993*fd43cf6eSHans Rosenfeld DDI_DMA_SYNC_FORKERNEL); 2994*fd43cf6eSHans Rosenfeld 2995*fd43cf6eSHans Rosenfeld hw = le16toh(sc->rxq.stat->closed_count) & 0xfff; 2996*fd43cf6eSHans Rosenfeld while (sc->rxq.cur != hw) { 2997*fd43cf6eSHans Rosenfeld struct iwn_rx_data *data = &sc->rxq.data[sc->rxq.cur]; 2998*fd43cf6eSHans Rosenfeld struct iwn_rx_desc *desc; 2999*fd43cf6eSHans Rosenfeld 3000*fd43cf6eSHans Rosenfeld (void) ddi_dma_sync(data->dma_data.dma_hdl, 0, sizeof (*desc), 3001*fd43cf6eSHans Rosenfeld DDI_DMA_SYNC_FORKERNEL); 3002*fd43cf6eSHans Rosenfeld desc = (struct iwn_rx_desc *)data->dma_data.vaddr; 3003*fd43cf6eSHans Rosenfeld 3004*fd43cf6eSHans Rosenfeld DTRACE_PROBE1(notification__intr, struct iwn_rx_desc *, desc); 3005*fd43cf6eSHans Rosenfeld 3006*fd43cf6eSHans Rosenfeld if (!(desc->qid & 0x80)) /* Reply to a command. */ 3007*fd43cf6eSHans Rosenfeld iwn_cmd_done(sc, desc); 3008*fd43cf6eSHans Rosenfeld 3009*fd43cf6eSHans Rosenfeld switch (desc->type) { 3010*fd43cf6eSHans Rosenfeld case IWN_RX_PHY: 3011*fd43cf6eSHans Rosenfeld iwn_rx_phy(sc, desc, data); 3012*fd43cf6eSHans Rosenfeld break; 3013*fd43cf6eSHans Rosenfeld 3014*fd43cf6eSHans Rosenfeld case IWN_RX_DONE: /* 4965AGN only. */ 3015*fd43cf6eSHans Rosenfeld case IWN_MPDU_RX_DONE: 3016*fd43cf6eSHans Rosenfeld /* An 802.11 frame has been received. */ 3017*fd43cf6eSHans Rosenfeld iwn_rx_done(sc, desc, data); 3018*fd43cf6eSHans Rosenfeld break; 3019*fd43cf6eSHans Rosenfeld #ifndef IEEE80211_NO_HT 3020*fd43cf6eSHans Rosenfeld case IWN_RX_COMPRESSED_BA: 3021*fd43cf6eSHans Rosenfeld /* A Compressed BlockAck has been received. */ 3022*fd43cf6eSHans Rosenfeld iwn_rx_compressed_ba(sc, desc, data); 3023*fd43cf6eSHans Rosenfeld break; 3024*fd43cf6eSHans Rosenfeld #endif 3025*fd43cf6eSHans Rosenfeld case IWN_TX_DONE: 3026*fd43cf6eSHans Rosenfeld /* An 802.11 frame has been transmitted. */ 3027*fd43cf6eSHans Rosenfeld ops->tx_done(sc, desc, data); 3028*fd43cf6eSHans Rosenfeld break; 3029*fd43cf6eSHans Rosenfeld 3030*fd43cf6eSHans Rosenfeld case IWN_RX_STATISTICS: 3031*fd43cf6eSHans Rosenfeld case IWN_BEACON_STATISTICS: 3032*fd43cf6eSHans Rosenfeld mutex_enter(&sc->sc_mtx); 3033*fd43cf6eSHans Rosenfeld iwn_rx_statistics(sc, desc, data); 3034*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_mtx); 3035*fd43cf6eSHans Rosenfeld break; 3036*fd43cf6eSHans Rosenfeld 3037*fd43cf6eSHans Rosenfeld case IWN_BEACON_MISSED: 3038*fd43cf6eSHans Rosenfeld { 3039*fd43cf6eSHans Rosenfeld struct iwn_beacon_missed *miss = 3040*fd43cf6eSHans Rosenfeld (struct iwn_beacon_missed *)(desc + 1); 3041*fd43cf6eSHans Rosenfeld 3042*fd43cf6eSHans Rosenfeld (void) ddi_dma_sync(data->dma_data.dma_hdl, 3043*fd43cf6eSHans Rosenfeld sizeof (*desc), sizeof (*miss), 3044*fd43cf6eSHans Rosenfeld DDI_DMA_SYNC_FORKERNEL); 3045*fd43cf6eSHans Rosenfeld /* 3046*fd43cf6eSHans Rosenfeld * If more than iwn_beacons_missed_disconnect 3047*fd43cf6eSHans Rosenfeld * consecutive beacons are missed, we've probably lost 3048*fd43cf6eSHans Rosenfeld * our connection. 3049*fd43cf6eSHans Rosenfeld * If more than iwn_beacons_missed_sensitivity 3050*fd43cf6eSHans Rosenfeld * consecutive beacons are missed, reinitialize the 3051*fd43cf6eSHans Rosenfeld * sensitivity state machine. 3052*fd43cf6eSHans Rosenfeld */ 3053*fd43cf6eSHans Rosenfeld DTRACE_PROBE1(beacons__missed, 3054*fd43cf6eSHans Rosenfeld struct iwn_beacon_missed *, miss); 3055*fd43cf6eSHans Rosenfeld if (ic->ic_state == IEEE80211_S_RUN) { 3056*fd43cf6eSHans Rosenfeld if (le32toh(miss->consecutive) 3057*fd43cf6eSHans Rosenfeld > iwn_beacons_missed_disconnect) { 3058*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 3059*fd43cf6eSHans Rosenfeld "!iwn_notif_intr(): %d consecutive " 3060*fd43cf6eSHans Rosenfeld "beacons missed, disconnecting", 3061*fd43cf6eSHans Rosenfeld le32toh(miss->consecutive)); 3062*fd43cf6eSHans Rosenfeld ieee80211_new_state(ic, 3063*fd43cf6eSHans Rosenfeld IEEE80211_S_INIT, -1); 3064*fd43cf6eSHans Rosenfeld } else if (le32toh(miss->consecutive) 3065*fd43cf6eSHans Rosenfeld > iwn_beacons_missed_sensitivity) { 3066*fd43cf6eSHans Rosenfeld mutex_enter(&sc->sc_mtx); 3067*fd43cf6eSHans Rosenfeld (void)iwn_init_sensitivity(sc); 3068*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_mtx); 3069*fd43cf6eSHans Rosenfeld } 3070*fd43cf6eSHans Rosenfeld } 3071*fd43cf6eSHans Rosenfeld break; 3072*fd43cf6eSHans Rosenfeld } 3073*fd43cf6eSHans Rosenfeld case IWN_UC_READY: 3074*fd43cf6eSHans Rosenfeld { 3075*fd43cf6eSHans Rosenfeld struct iwn_ucode_info *uc = 3076*fd43cf6eSHans Rosenfeld (struct iwn_ucode_info *)(desc + 1); 3077*fd43cf6eSHans Rosenfeld 3078*fd43cf6eSHans Rosenfeld /* The microcontroller is ready. */ 3079*fd43cf6eSHans Rosenfeld (void) ddi_dma_sync(data->dma_data.dma_hdl, 3080*fd43cf6eSHans Rosenfeld sizeof (*desc), sizeof (*uc), 3081*fd43cf6eSHans Rosenfeld DDI_DMA_SYNC_FORKERNEL); 3082*fd43cf6eSHans Rosenfeld DTRACE_PROBE1(uc__ready, struct iwn_ucode_info *, uc) 3083*fd43cf6eSHans Rosenfeld 3084*fd43cf6eSHans Rosenfeld if (le32toh(uc->valid) != 1) { 3085*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 3086*fd43cf6eSHans Rosenfeld "!microcontroller initialization failed"); 3087*fd43cf6eSHans Rosenfeld break; 3088*fd43cf6eSHans Rosenfeld } 3089*fd43cf6eSHans Rosenfeld if (uc->subtype == IWN_UCODE_INIT) { 3090*fd43cf6eSHans Rosenfeld /* Save microcontroller report. */ 3091*fd43cf6eSHans Rosenfeld memcpy(&sc->ucode_info, uc, sizeof (*uc)); 3092*fd43cf6eSHans Rosenfeld } 3093*fd43cf6eSHans Rosenfeld /* Save the address of the error log in SRAM. */ 3094*fd43cf6eSHans Rosenfeld sc->errptr = le32toh(uc->errptr); 3095*fd43cf6eSHans Rosenfeld break; 3096*fd43cf6eSHans Rosenfeld } 3097*fd43cf6eSHans Rosenfeld case IWN_STATE_CHANGED: 3098*fd43cf6eSHans Rosenfeld { 3099*fd43cf6eSHans Rosenfeld /*LINTED: E_PTR_BAD_CAST_ALIGN*/ 3100*fd43cf6eSHans Rosenfeld uint32_t *status = (uint32_t *)(desc + 1); 3101*fd43cf6eSHans Rosenfeld 3102*fd43cf6eSHans Rosenfeld /* Enabled/disabled notification. */ 3103*fd43cf6eSHans Rosenfeld (void) ddi_dma_sync(data->dma_data.dma_hdl, 3104*fd43cf6eSHans Rosenfeld sizeof (*desc), sizeof (*status), 3105*fd43cf6eSHans Rosenfeld DDI_DMA_SYNC_FORKERNEL); 3106*fd43cf6eSHans Rosenfeld DTRACE_PROBE1(state__changed, uint32_t, *status); 3107*fd43cf6eSHans Rosenfeld 3108*fd43cf6eSHans Rosenfeld if (le32toh(*status) & 1) { 3109*fd43cf6eSHans Rosenfeld /* The radio button has to be pushed. */ 3110*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 3111*fd43cf6eSHans Rosenfeld "!Radio transmitter is off"); 3112*fd43cf6eSHans Rosenfeld /* Turn the interface down. */ 3113*fd43cf6eSHans Rosenfeld mutex_enter(&sc->sc_mtx); 3114*fd43cf6eSHans Rosenfeld sc->sc_flags |= 3115*fd43cf6eSHans Rosenfeld IWN_FLAG_HW_ERR_RECOVER | 3116*fd43cf6eSHans Rosenfeld IWN_FLAG_RADIO_OFF; 3117*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_mtx); 3118*fd43cf6eSHans Rosenfeld ieee80211_new_state(&sc->sc_ic, 3119*fd43cf6eSHans Rosenfeld IEEE80211_S_INIT, -1); 3120*fd43cf6eSHans Rosenfeld 3121*fd43cf6eSHans Rosenfeld return; /* No further processing. */ 3122*fd43cf6eSHans Rosenfeld } 3123*fd43cf6eSHans Rosenfeld break; 3124*fd43cf6eSHans Rosenfeld } 3125*fd43cf6eSHans Rosenfeld case IWN_START_SCAN: 3126*fd43cf6eSHans Rosenfeld { 3127*fd43cf6eSHans Rosenfeld struct iwn_start_scan *scan = 3128*fd43cf6eSHans Rosenfeld (struct iwn_start_scan *)(desc + 1); 3129*fd43cf6eSHans Rosenfeld 3130*fd43cf6eSHans Rosenfeld (void) ddi_dma_sync(data->dma_data.dma_hdl, 3131*fd43cf6eSHans Rosenfeld sizeof (*desc), sizeof (*scan), 3132*fd43cf6eSHans Rosenfeld DDI_DMA_SYNC_FORKERNEL); 3133*fd43cf6eSHans Rosenfeld DTRACE_PROBE2(start__scan, uint8_t, scan->chan, 3134*fd43cf6eSHans Rosenfeld uint32_t, le32toh(scan->status)); 3135*fd43cf6eSHans Rosenfeld 3136*fd43cf6eSHans Rosenfeld /* Fix current channel. */ 3137*fd43cf6eSHans Rosenfeld ic->ic_curchan = ic->ic_bss->in_chan = 3138*fd43cf6eSHans Rosenfeld &ic->ic_sup_channels[scan->chan]; 3139*fd43cf6eSHans Rosenfeld break; 3140*fd43cf6eSHans Rosenfeld } 3141*fd43cf6eSHans Rosenfeld case IWN_STOP_SCAN: 3142*fd43cf6eSHans Rosenfeld { 3143*fd43cf6eSHans Rosenfeld struct iwn_stop_scan *scan = 3144*fd43cf6eSHans Rosenfeld (struct iwn_stop_scan *)(desc + 1); 3145*fd43cf6eSHans Rosenfeld 3146*fd43cf6eSHans Rosenfeld (void) ddi_dma_sync(data->dma_data.dma_hdl, 3147*fd43cf6eSHans Rosenfeld sizeof (*desc), sizeof (*scan), 3148*fd43cf6eSHans Rosenfeld DDI_DMA_SYNC_FORKERNEL); 3149*fd43cf6eSHans Rosenfeld DTRACE_PROBE3(stop__scan, uint8_t, scan->chan, 3150*fd43cf6eSHans Rosenfeld uint32_t, le32toh(scan->status), 3151*fd43cf6eSHans Rosenfeld uint8_t, scan->nchan); 3152*fd43cf6eSHans Rosenfeld 3153*fd43cf6eSHans Rosenfeld if (iwn_enable_5ghz != 0 && 3154*fd43cf6eSHans Rosenfeld (sc->sc_flags & IWN_FLAG_SCANNING_2GHZ) && 3155*fd43cf6eSHans Rosenfeld (sc->sc_flags & IWN_FLAG_HAS_5GHZ)) { 3156*fd43cf6eSHans Rosenfeld /* 3157*fd43cf6eSHans Rosenfeld * We just finished scanning 2GHz channels, 3158*fd43cf6eSHans Rosenfeld * start scanning 5GHz ones. 3159*fd43cf6eSHans Rosenfeld */ 3160*fd43cf6eSHans Rosenfeld mutex_enter(&sc->sc_mtx); 3161*fd43cf6eSHans Rosenfeld sc->sc_flags |= IWN_FLAG_SCANNING_5GHZ; 3162*fd43cf6eSHans Rosenfeld sc->sc_flags &= ~IWN_FLAG_SCANNING_2GHZ; 3163*fd43cf6eSHans Rosenfeld if (iwn_scan(sc, IEEE80211_CHAN_5GHZ) == 0) { 3164*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_mtx); 3165*fd43cf6eSHans Rosenfeld break; 3166*fd43cf6eSHans Rosenfeld } 3167*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_mtx); 3168*fd43cf6eSHans Rosenfeld } 3169*fd43cf6eSHans Rosenfeld ieee80211_end_scan(ic); 3170*fd43cf6eSHans Rosenfeld mutex_enter(&sc->sc_mtx); 3171*fd43cf6eSHans Rosenfeld sc->sc_flags &= ~IWN_FLAG_SCANNING; 3172*fd43cf6eSHans Rosenfeld cv_signal(&sc->sc_scan_cv); 3173*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_mtx); 3174*fd43cf6eSHans Rosenfeld (void) untimeout(sc->scan_to); 3175*fd43cf6eSHans Rosenfeld sc->scan_to = 0; 3176*fd43cf6eSHans Rosenfeld break; 3177*fd43cf6eSHans Rosenfeld } 3178*fd43cf6eSHans Rosenfeld case IWN5000_CALIBRATION_RESULT: 3179*fd43cf6eSHans Rosenfeld iwn5000_rx_calib_results(sc, desc, data); 3180*fd43cf6eSHans Rosenfeld break; 3181*fd43cf6eSHans Rosenfeld 3182*fd43cf6eSHans Rosenfeld case IWN5000_CALIBRATION_DONE: 3183*fd43cf6eSHans Rosenfeld mutex_enter(&sc->sc_mtx); 3184*fd43cf6eSHans Rosenfeld sc->sc_flags |= IWN_FLAG_CALIB_DONE; 3185*fd43cf6eSHans Rosenfeld cv_signal(&sc->sc_calib_cv); 3186*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_mtx); 3187*fd43cf6eSHans Rosenfeld break; 3188*fd43cf6eSHans Rosenfeld } 3189*fd43cf6eSHans Rosenfeld 3190*fd43cf6eSHans Rosenfeld sc->rxq.cur = (sc->rxq.cur + 1) % IWN_RX_RING_COUNT; 3191*fd43cf6eSHans Rosenfeld } 3192*fd43cf6eSHans Rosenfeld 3193*fd43cf6eSHans Rosenfeld /* Tell the firmware what we have processed. */ 3194*fd43cf6eSHans Rosenfeld hw = (hw == 0) ? IWN_RX_RING_COUNT - 1 : hw - 1; 3195*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_FH_RX_WPTR, hw & ~7); 3196*fd43cf6eSHans Rosenfeld } 3197*fd43cf6eSHans Rosenfeld 3198*fd43cf6eSHans Rosenfeld /* 3199*fd43cf6eSHans Rosenfeld * Process an INT_WAKEUP interrupt raised when the microcontroller wakes up 3200*fd43cf6eSHans Rosenfeld * from power-down sleep mode. 3201*fd43cf6eSHans Rosenfeld */ 3202*fd43cf6eSHans Rosenfeld static void 3203*fd43cf6eSHans Rosenfeld iwn_wakeup_intr(struct iwn_softc *sc) 3204*fd43cf6eSHans Rosenfeld { 3205*fd43cf6eSHans Rosenfeld int qid; 3206*fd43cf6eSHans Rosenfeld 3207*fd43cf6eSHans Rosenfeld DTRACE_PROBE(wakeup__intr); 3208*fd43cf6eSHans Rosenfeld 3209*fd43cf6eSHans Rosenfeld /* Wakeup RX and TX rings. */ 3210*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_FH_RX_WPTR, sc->rxq.cur & ~7); 3211*fd43cf6eSHans Rosenfeld for (qid = 0; qid < sc->ntxqs; qid++) { 3212*fd43cf6eSHans Rosenfeld struct iwn_tx_ring *ring = &sc->txq[qid]; 3213*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | ring->cur); 3214*fd43cf6eSHans Rosenfeld } 3215*fd43cf6eSHans Rosenfeld } 3216*fd43cf6eSHans Rosenfeld 3217*fd43cf6eSHans Rosenfeld /* 3218*fd43cf6eSHans Rosenfeld * Dump the error log of the firmware when a firmware panic occurs. Although 3219*fd43cf6eSHans Rosenfeld * we can't debug the firmware because it is neither open source nor free, it 3220*fd43cf6eSHans Rosenfeld * can help us to identify certain classes of problems. 3221*fd43cf6eSHans Rosenfeld */ 3222*fd43cf6eSHans Rosenfeld static void 3223*fd43cf6eSHans Rosenfeld iwn_fatal_intr(struct iwn_softc *sc) 3224*fd43cf6eSHans Rosenfeld { 3225*fd43cf6eSHans Rosenfeld struct iwn_fw_dump dump; 3226*fd43cf6eSHans Rosenfeld int i; 3227*fd43cf6eSHans Rosenfeld 3228*fd43cf6eSHans Rosenfeld /* Force a complete recalibration on next init. */ 3229*fd43cf6eSHans Rosenfeld sc->sc_flags &= ~IWN_FLAG_CALIB_DONE; 3230*fd43cf6eSHans Rosenfeld 3231*fd43cf6eSHans Rosenfeld /* Check that the error log address is valid. */ 3232*fd43cf6eSHans Rosenfeld if (sc->errptr < IWN_FW_DATA_BASE || 3233*fd43cf6eSHans Rosenfeld sc->errptr + sizeof (dump) > 3234*fd43cf6eSHans Rosenfeld IWN_FW_DATA_BASE + sc->fw_data_maxsz) { 3235*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 3236*fd43cf6eSHans Rosenfeld "!bad firmware error log address 0x%08x", sc->errptr); 3237*fd43cf6eSHans Rosenfeld return; 3238*fd43cf6eSHans Rosenfeld } 3239*fd43cf6eSHans Rosenfeld if (iwn_nic_lock(sc) != 0) { 3240*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 3241*fd43cf6eSHans Rosenfeld "!could not read firmware error log"); 3242*fd43cf6eSHans Rosenfeld return; 3243*fd43cf6eSHans Rosenfeld } 3244*fd43cf6eSHans Rosenfeld /* Read firmware error log from SRAM. */ 3245*fd43cf6eSHans Rosenfeld /*LINTED: E_PTR_BAD_CAST_ALIGN*/ 3246*fd43cf6eSHans Rosenfeld iwn_mem_read_region_4(sc, sc->errptr, (uint32_t *)&dump, 3247*fd43cf6eSHans Rosenfeld sizeof (dump) / sizeof (uint32_t)); 3248*fd43cf6eSHans Rosenfeld iwn_nic_unlock(sc); 3249*fd43cf6eSHans Rosenfeld 3250*fd43cf6eSHans Rosenfeld if (dump.valid == 0) { 3251*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 3252*fd43cf6eSHans Rosenfeld "!firmware error log is empty"); 3253*fd43cf6eSHans Rosenfeld return; 3254*fd43cf6eSHans Rosenfeld } 3255*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, "!firmware error log:"); 3256*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_CONT, "! error type = \"%s\" (0x%08X)", 3257*fd43cf6eSHans Rosenfeld (dump.id < __arraycount(iwn_fw_errmsg)) ? 3258*fd43cf6eSHans Rosenfeld iwn_fw_errmsg[dump.id] : "UNKNOWN", 3259*fd43cf6eSHans Rosenfeld dump.id); 3260*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_CONT, "! program counter = 0x%08X", dump.pc); 3261*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_CONT, "! source line = 0x%08X", 3262*fd43cf6eSHans Rosenfeld dump.src_line); 3263*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_CONT, "! error data = 0x%08X%08X", 3264*fd43cf6eSHans Rosenfeld dump.error_data[0], dump.error_data[1]); 3265*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_CONT, "! branch link = 0x%08X%08X", 3266*fd43cf6eSHans Rosenfeld dump.branch_link[0], dump.branch_link[1]); 3267*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_CONT, "! interrupt link = 0x%08X%08X", 3268*fd43cf6eSHans Rosenfeld dump.interrupt_link[0], dump.interrupt_link[1]); 3269*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_CONT, "! time = %u", dump.time[0]); 3270*fd43cf6eSHans Rosenfeld 3271*fd43cf6eSHans Rosenfeld /* Dump driver status (TX and RX rings) while we're here. */ 3272*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, "!driver status:"); 3273*fd43cf6eSHans Rosenfeld for (i = 0; i < sc->ntxqs; i++) { 3274*fd43cf6eSHans Rosenfeld struct iwn_tx_ring *ring = &sc->txq[i]; 3275*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 3276*fd43cf6eSHans Rosenfeld "! tx ring %2d: qid=%2d cur=%3d queued=%3d", 3277*fd43cf6eSHans Rosenfeld i, ring->qid, ring->cur, ring->queued); 3278*fd43cf6eSHans Rosenfeld } 3279*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, "! rx ring: cur=%d", sc->rxq.cur); 3280*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, "! 802.11 state %d", sc->sc_ic.ic_state); 3281*fd43cf6eSHans Rosenfeld } 3282*fd43cf6eSHans Rosenfeld 3283*fd43cf6eSHans Rosenfeld /*ARGSUSED1*/ 3284*fd43cf6eSHans Rosenfeld static uint_t 3285*fd43cf6eSHans Rosenfeld iwn_intr(caddr_t arg, caddr_t unused) 3286*fd43cf6eSHans Rosenfeld { 3287*fd43cf6eSHans Rosenfeld _NOTE(ARGUNUSED(unused)); 3288*fd43cf6eSHans Rosenfeld /*LINTED: E_PTR_BAD_CAST_ALIGN*/ 3289*fd43cf6eSHans Rosenfeld struct iwn_softc *sc = (struct iwn_softc *)arg; 3290*fd43cf6eSHans Rosenfeld uint32_t r1, r2, tmp; 3291*fd43cf6eSHans Rosenfeld 3292*fd43cf6eSHans Rosenfeld if (sc == NULL) 3293*fd43cf6eSHans Rosenfeld return (DDI_INTR_UNCLAIMED); 3294*fd43cf6eSHans Rosenfeld 3295*fd43cf6eSHans Rosenfeld /* Disable interrupts. */ 3296*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_INT_MASK, 0); 3297*fd43cf6eSHans Rosenfeld 3298*fd43cf6eSHans Rosenfeld /* Read interrupts from ICT (fast) or from registers (slow). */ 3299*fd43cf6eSHans Rosenfeld if (sc->sc_flags & IWN_FLAG_USE_ICT) { 3300*fd43cf6eSHans Rosenfeld (void) ddi_dma_sync(sc->ict_dma.dma_hdl, 0, 0, 3301*fd43cf6eSHans Rosenfeld DDI_DMA_SYNC_FORKERNEL); 3302*fd43cf6eSHans Rosenfeld tmp = 0; 3303*fd43cf6eSHans Rosenfeld while (sc->ict[sc->ict_cur] != 0) { 3304*fd43cf6eSHans Rosenfeld tmp |= sc->ict[sc->ict_cur]; 3305*fd43cf6eSHans Rosenfeld sc->ict[sc->ict_cur] = 0; /* Acknowledge. */ 3306*fd43cf6eSHans Rosenfeld sc->ict_cur = (sc->ict_cur + 1) % IWN_ICT_COUNT; 3307*fd43cf6eSHans Rosenfeld } 3308*fd43cf6eSHans Rosenfeld (void) ddi_dma_sync(sc->ict_dma.dma_hdl, 0, 0, 3309*fd43cf6eSHans Rosenfeld DDI_DMA_SYNC_FORDEV); 3310*fd43cf6eSHans Rosenfeld tmp = le32toh(tmp); 3311*fd43cf6eSHans Rosenfeld if (tmp == 0xffffffff) /* Shouldn't happen. */ 3312*fd43cf6eSHans Rosenfeld tmp = 0; 3313*fd43cf6eSHans Rosenfeld else if (tmp & 0xc0000) /* Workaround a HW bug. */ 3314*fd43cf6eSHans Rosenfeld tmp |= 0x8000; 3315*fd43cf6eSHans Rosenfeld r1 = (tmp & 0xff00) << 16 | (tmp & 0xff); 3316*fd43cf6eSHans Rosenfeld r2 = 0; /* Unused. */ 3317*fd43cf6eSHans Rosenfeld } else { 3318*fd43cf6eSHans Rosenfeld r1 = IWN_READ(sc, IWN_INT); 3319*fd43cf6eSHans Rosenfeld if (r1 == 0xffffffff || (r1 & 0xfffffff0) == 0xa5a5a5a0) 3320*fd43cf6eSHans Rosenfeld return (DDI_INTR_UNCLAIMED); /* Hardware gone! */ 3321*fd43cf6eSHans Rosenfeld r2 = IWN_READ(sc, IWN_FH_INT); 3322*fd43cf6eSHans Rosenfeld } 3323*fd43cf6eSHans Rosenfeld if (r1 == 0 && r2 == 0) { 3324*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask); 3325*fd43cf6eSHans Rosenfeld return (DDI_INTR_UNCLAIMED); /* Interrupt not for us. */ 3326*fd43cf6eSHans Rosenfeld } 3327*fd43cf6eSHans Rosenfeld 3328*fd43cf6eSHans Rosenfeld /* Acknowledge interrupts. */ 3329*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_INT, r1); 3330*fd43cf6eSHans Rosenfeld if (!(sc->sc_flags & IWN_FLAG_USE_ICT)) 3331*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_FH_INT, r2); 3332*fd43cf6eSHans Rosenfeld 3333*fd43cf6eSHans Rosenfeld if (r1 & IWN_INT_RF_TOGGLED) { 3334*fd43cf6eSHans Rosenfeld tmp = IWN_READ(sc, IWN_GP_CNTRL); 3335*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_NOTE, 3336*fd43cf6eSHans Rosenfeld "!RF switch: radio %s", 3337*fd43cf6eSHans Rosenfeld (tmp & IWN_GP_CNTRL_RFKILL) ? "enabled" : "disabled"); 3338*fd43cf6eSHans Rosenfeld } 3339*fd43cf6eSHans Rosenfeld if (r1 & IWN_INT_CT_REACHED) { 3340*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 3341*fd43cf6eSHans Rosenfeld "!critical temperature reached!"); 3342*fd43cf6eSHans Rosenfeld } 3343*fd43cf6eSHans Rosenfeld if (r1 & (IWN_INT_SW_ERR | IWN_INT_HW_ERR)) { 3344*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 3345*fd43cf6eSHans Rosenfeld "!fatal firmware error"); 3346*fd43cf6eSHans Rosenfeld /* Dump firmware error log and stop. */ 3347*fd43cf6eSHans Rosenfeld iwn_fatal_intr(sc); 3348*fd43cf6eSHans Rosenfeld iwn_hw_stop(sc, B_TRUE); 3349*fd43cf6eSHans Rosenfeld if (!IWN_CHK_FAST_RECOVER(sc)) 3350*fd43cf6eSHans Rosenfeld ieee80211_new_state(&sc->sc_ic, IEEE80211_S_INIT, -1); 3351*fd43cf6eSHans Rosenfeld mutex_enter(&sc->sc_mtx); 3352*fd43cf6eSHans Rosenfeld sc->sc_flags |= IWN_FLAG_HW_ERR_RECOVER; 3353*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_mtx); 3354*fd43cf6eSHans Rosenfeld 3355*fd43cf6eSHans Rosenfeld return (DDI_INTR_CLAIMED); 3356*fd43cf6eSHans Rosenfeld } 3357*fd43cf6eSHans Rosenfeld if ((r1 & (IWN_INT_FH_RX | IWN_INT_SW_RX | IWN_INT_RX_PERIODIC)) || 3358*fd43cf6eSHans Rosenfeld (r2 & IWN_FH_INT_RX)) { 3359*fd43cf6eSHans Rosenfeld if (sc->sc_flags & IWN_FLAG_USE_ICT) { 3360*fd43cf6eSHans Rosenfeld int ena = (r1 & (IWN_INT_FH_RX | IWN_INT_SW_RX)); 3361*fd43cf6eSHans Rosenfeld 3362*fd43cf6eSHans Rosenfeld if (ena) 3363*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_FH_INT, IWN_FH_INT_RX); 3364*fd43cf6eSHans Rosenfeld IWN_WRITE_1(sc, IWN_INT_PERIODIC, 3365*fd43cf6eSHans Rosenfeld IWN_INT_PERIODIC_DIS); 3366*fd43cf6eSHans Rosenfeld iwn_notif_intr(sc); 3367*fd43cf6eSHans Rosenfeld if (ena) 3368*fd43cf6eSHans Rosenfeld IWN_WRITE_1(sc, IWN_INT_PERIODIC, 3369*fd43cf6eSHans Rosenfeld IWN_INT_PERIODIC_ENA); 3370*fd43cf6eSHans Rosenfeld } else { 3371*fd43cf6eSHans Rosenfeld iwn_notif_intr(sc); 3372*fd43cf6eSHans Rosenfeld } 3373*fd43cf6eSHans Rosenfeld } 3374*fd43cf6eSHans Rosenfeld 3375*fd43cf6eSHans Rosenfeld if ((r1 & IWN_INT_FH_TX) || (r2 & IWN_FH_INT_TX)) { 3376*fd43cf6eSHans Rosenfeld if (sc->sc_flags & IWN_FLAG_USE_ICT) 3377*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_FH_INT, IWN_FH_INT_TX); 3378*fd43cf6eSHans Rosenfeld mutex_enter(&sc->sc_mtx); 3379*fd43cf6eSHans Rosenfeld sc->sc_flags |= IWN_FLAG_FW_DMA; 3380*fd43cf6eSHans Rosenfeld cv_signal(&sc->sc_fhdma_cv); 3381*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_mtx); 3382*fd43cf6eSHans Rosenfeld } 3383*fd43cf6eSHans Rosenfeld 3384*fd43cf6eSHans Rosenfeld if (r1 & IWN_INT_ALIVE) { 3385*fd43cf6eSHans Rosenfeld mutex_enter(&sc->sc_mtx); 3386*fd43cf6eSHans Rosenfeld sc->sc_flags |= IWN_FLAG_FW_ALIVE; 3387*fd43cf6eSHans Rosenfeld cv_signal(&sc->sc_alive_cv); 3388*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_mtx); 3389*fd43cf6eSHans Rosenfeld } 3390*fd43cf6eSHans Rosenfeld 3391*fd43cf6eSHans Rosenfeld if (r1 & IWN_INT_WAKEUP) 3392*fd43cf6eSHans Rosenfeld iwn_wakeup_intr(sc); 3393*fd43cf6eSHans Rosenfeld 3394*fd43cf6eSHans Rosenfeld /* Re-enable interrupts. */ 3395*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask); 3396*fd43cf6eSHans Rosenfeld return (DDI_INTR_CLAIMED); 3397*fd43cf6eSHans Rosenfeld } 3398*fd43cf6eSHans Rosenfeld 3399*fd43cf6eSHans Rosenfeld /* 3400*fd43cf6eSHans Rosenfeld * Update TX scheduler ring when transmitting an 802.11 frame (4965AGN and 3401*fd43cf6eSHans Rosenfeld * 5000 adapters use a slightly different format). 3402*fd43cf6eSHans Rosenfeld */ 3403*fd43cf6eSHans Rosenfeld static void 3404*fd43cf6eSHans Rosenfeld iwn4965_update_sched(struct iwn_softc *sc, int qid, int idx, uint8_t id, 3405*fd43cf6eSHans Rosenfeld uint16_t len) 3406*fd43cf6eSHans Rosenfeld { 3407*fd43cf6eSHans Rosenfeld _NOTE(ARGUNUSED(id)); 3408*fd43cf6eSHans Rosenfeld int w_idx = qid * IWN4965_SCHED_COUNT + idx; 3409*fd43cf6eSHans Rosenfeld uint16_t *w = &sc->sched[w_idx]; 3410*fd43cf6eSHans Rosenfeld 3411*fd43cf6eSHans Rosenfeld *w = htole16(len + 8); 3412*fd43cf6eSHans Rosenfeld (void) ddi_dma_sync(sc->sched_dma.dma_hdl, w_idx * sizeof (uint16_t), 3413*fd43cf6eSHans Rosenfeld sizeof (uint16_t), DDI_DMA_SYNC_FORDEV); 3414*fd43cf6eSHans Rosenfeld if (idx < IWN_SCHED_WINSZ) { 3415*fd43cf6eSHans Rosenfeld *(w + IWN_TX_RING_COUNT) = *w; 3416*fd43cf6eSHans Rosenfeld (void) ddi_dma_sync(sc->sched_dma.dma_hdl, 3417*fd43cf6eSHans Rosenfeld (w_idx + IWN_TX_RING_COUNT) * sizeof (uint16_t), 3418*fd43cf6eSHans Rosenfeld sizeof (uint16_t), DDI_DMA_SYNC_FORDEV); 3419*fd43cf6eSHans Rosenfeld } 3420*fd43cf6eSHans Rosenfeld } 3421*fd43cf6eSHans Rosenfeld 3422*fd43cf6eSHans Rosenfeld static void 3423*fd43cf6eSHans Rosenfeld iwn5000_update_sched(struct iwn_softc *sc, int qid, int idx, uint8_t id, 3424*fd43cf6eSHans Rosenfeld uint16_t len) 3425*fd43cf6eSHans Rosenfeld { 3426*fd43cf6eSHans Rosenfeld int w_idx = qid * IWN5000_SCHED_COUNT + idx; 3427*fd43cf6eSHans Rosenfeld uint16_t *w = &sc->sched[w_idx]; 3428*fd43cf6eSHans Rosenfeld 3429*fd43cf6eSHans Rosenfeld *w = htole16(id << 12 | (len + 8)); 3430*fd43cf6eSHans Rosenfeld (void) ddi_dma_sync(sc->sched_dma.dma_hdl, w_idx * sizeof (uint16_t), 3431*fd43cf6eSHans Rosenfeld sizeof (uint16_t), DDI_DMA_SYNC_FORDEV); 3432*fd43cf6eSHans Rosenfeld if (idx < IWN_SCHED_WINSZ) { 3433*fd43cf6eSHans Rosenfeld *(w + IWN_TX_RING_COUNT) = *w; 3434*fd43cf6eSHans Rosenfeld (void) ddi_dma_sync(sc->sched_dma.dma_hdl, 3435*fd43cf6eSHans Rosenfeld (w_idx + IWN_TX_RING_COUNT) * sizeof (uint16_t), 3436*fd43cf6eSHans Rosenfeld sizeof (uint16_t), DDI_DMA_SYNC_FORDEV); 3437*fd43cf6eSHans Rosenfeld } 3438*fd43cf6eSHans Rosenfeld } 3439*fd43cf6eSHans Rosenfeld 3440*fd43cf6eSHans Rosenfeld #ifdef notyet 3441*fd43cf6eSHans Rosenfeld static void 3442*fd43cf6eSHans Rosenfeld iwn5000_reset_sched(struct iwn_softc *sc, int qid, int idx) 3443*fd43cf6eSHans Rosenfeld { 3444*fd43cf6eSHans Rosenfeld int w_idx = qid * IWN5000_SCHED_COUNT + idx; 3445*fd43cf6eSHans Rosenfeld uint16_t *w = &sc->sched[w_idx]; 3446*fd43cf6eSHans Rosenfeld 3447*fd43cf6eSHans Rosenfeld *w = (*w & htole16(0xf000)) | htole16(1); 3448*fd43cf6eSHans Rosenfeld (void) ddi_dma_sync(sc->sched_dma.dma_hdl, w_idx * sizeof (uint16_t), 3449*fd43cf6eSHans Rosenfeld sizeof (uint16_t), DDI_DMA_SYNC_FORDEV); 3450*fd43cf6eSHans Rosenfeld if (idx < IWN_SCHED_WINSZ) { 3451*fd43cf6eSHans Rosenfeld *(w + IWN_TX_RING_COUNT) = *w; 3452*fd43cf6eSHans Rosenfeld (void) ddi_dma_sync(sc->sched_dma.dma_hdl, 3453*fd43cf6eSHans Rosenfeld (w_idx + IWN_TX_RING_COUNT) * sizeof (uint16_t), 3454*fd43cf6eSHans Rosenfeld sizeof (uint16_t), DDI_DMA_SYNC_FORDEV); 3455*fd43cf6eSHans Rosenfeld } 3456*fd43cf6eSHans Rosenfeld } 3457*fd43cf6eSHans Rosenfeld #endif 3458*fd43cf6eSHans Rosenfeld 3459*fd43cf6eSHans Rosenfeld /* 3460*fd43cf6eSHans Rosenfeld * This function is only for compatibility with Net80211 module. 3461*fd43cf6eSHans Rosenfeld * iwn_qosparam_to_hw() is the actual function updating EDCA 3462*fd43cf6eSHans Rosenfeld * parameters to hardware. 3463*fd43cf6eSHans Rosenfeld */ 3464*fd43cf6eSHans Rosenfeld static int 3465*fd43cf6eSHans Rosenfeld iwn_wme_update(struct ieee80211com *ic) 3466*fd43cf6eSHans Rosenfeld { 3467*fd43cf6eSHans Rosenfeld _NOTE(ARGUNUSED(ic)); 3468*fd43cf6eSHans Rosenfeld return (0); 3469*fd43cf6eSHans Rosenfeld } 3470*fd43cf6eSHans Rosenfeld 3471*fd43cf6eSHans Rosenfeld static int 3472*fd43cf6eSHans Rosenfeld iwn_wme_to_qos_ac(struct iwn_softc *sc, int wme_ac) 3473*fd43cf6eSHans Rosenfeld { 3474*fd43cf6eSHans Rosenfeld int qos_ac; 3475*fd43cf6eSHans Rosenfeld 3476*fd43cf6eSHans Rosenfeld switch (wme_ac) { 3477*fd43cf6eSHans Rosenfeld case WME_AC_BE: 3478*fd43cf6eSHans Rosenfeld qos_ac = QOS_AC_BK; 3479*fd43cf6eSHans Rosenfeld break; 3480*fd43cf6eSHans Rosenfeld case WME_AC_BK: 3481*fd43cf6eSHans Rosenfeld qos_ac = QOS_AC_BE; 3482*fd43cf6eSHans Rosenfeld break; 3483*fd43cf6eSHans Rosenfeld case WME_AC_VI: 3484*fd43cf6eSHans Rosenfeld qos_ac = QOS_AC_VI; 3485*fd43cf6eSHans Rosenfeld break; 3486*fd43cf6eSHans Rosenfeld case WME_AC_VO: 3487*fd43cf6eSHans Rosenfeld qos_ac = QOS_AC_VO; 3488*fd43cf6eSHans Rosenfeld break; 3489*fd43cf6eSHans Rosenfeld default: 3490*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, "!iwn_wme_to_qos_ac(): " 3491*fd43cf6eSHans Rosenfeld "WME AC index is not in suitable range.\n"); 3492*fd43cf6eSHans Rosenfeld qos_ac = QOS_AC_INVALID; 3493*fd43cf6eSHans Rosenfeld break; 3494*fd43cf6eSHans Rosenfeld } 3495*fd43cf6eSHans Rosenfeld 3496*fd43cf6eSHans Rosenfeld return (qos_ac); 3497*fd43cf6eSHans Rosenfeld } 3498*fd43cf6eSHans Rosenfeld 3499*fd43cf6eSHans Rosenfeld static uint16_t 3500*fd43cf6eSHans Rosenfeld iwn_cw_e_to_cw(uint8_t cw_e) 3501*fd43cf6eSHans Rosenfeld { 3502*fd43cf6eSHans Rosenfeld uint16_t cw = 1; 3503*fd43cf6eSHans Rosenfeld 3504*fd43cf6eSHans Rosenfeld while (cw_e > 0) { 3505*fd43cf6eSHans Rosenfeld cw <<= 1; 3506*fd43cf6eSHans Rosenfeld cw_e--; 3507*fd43cf6eSHans Rosenfeld } 3508*fd43cf6eSHans Rosenfeld 3509*fd43cf6eSHans Rosenfeld cw -= 1; 3510*fd43cf6eSHans Rosenfeld return (cw); 3511*fd43cf6eSHans Rosenfeld } 3512*fd43cf6eSHans Rosenfeld 3513*fd43cf6eSHans Rosenfeld static int 3514*fd43cf6eSHans Rosenfeld iwn_wmeparam_check(struct iwn_softc *sc, struct wmeParams *wmeparam) 3515*fd43cf6eSHans Rosenfeld { 3516*fd43cf6eSHans Rosenfeld int i; 3517*fd43cf6eSHans Rosenfeld 3518*fd43cf6eSHans Rosenfeld for (i = 0; i < WME_NUM_AC; i++) { 3519*fd43cf6eSHans Rosenfeld 3520*fd43cf6eSHans Rosenfeld if ((wmeparam[i].wmep_logcwmax > QOS_CW_RANGE_MAX) || 3521*fd43cf6eSHans Rosenfeld (wmeparam[i].wmep_logcwmin >= wmeparam[i].wmep_logcwmax)) { 3522*fd43cf6eSHans Rosenfeld cmn_err(CE_WARN, "iwn_wmeparam_check(): " 3523*fd43cf6eSHans Rosenfeld "Contention window is not in suitable range.\n"); 3524*fd43cf6eSHans Rosenfeld return (IWN_FAIL); 3525*fd43cf6eSHans Rosenfeld } 3526*fd43cf6eSHans Rosenfeld 3527*fd43cf6eSHans Rosenfeld if ((wmeparam[i].wmep_aifsn < QOS_AIFSN_MIN) || 3528*fd43cf6eSHans Rosenfeld (wmeparam[i].wmep_aifsn > QOS_AIFSN_MAX)) { 3529*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, "!iwn_wmeparam_check(): " 3530*fd43cf6eSHans Rosenfeld "Arbitration interframe space number" 3531*fd43cf6eSHans Rosenfeld "is not in suitable range.\n"); 3532*fd43cf6eSHans Rosenfeld return (IWN_FAIL); 3533*fd43cf6eSHans Rosenfeld } 3534*fd43cf6eSHans Rosenfeld } 3535*fd43cf6eSHans Rosenfeld 3536*fd43cf6eSHans Rosenfeld return (IWN_SUCCESS); 3537*fd43cf6eSHans Rosenfeld } 3538*fd43cf6eSHans Rosenfeld 3539*fd43cf6eSHans Rosenfeld /* 3540*fd43cf6eSHans Rosenfeld * This function updates EDCA parameters into hardware. 3541*fd43cf6eSHans Rosenfeld * FIFO0-background, FIFO1-best effort, FIFO2-video, FIFO3-voice. 3542*fd43cf6eSHans Rosenfeld */ 3543*fd43cf6eSHans Rosenfeld static int 3544*fd43cf6eSHans Rosenfeld iwn_qosparam_to_hw(struct iwn_softc *sc, int async) 3545*fd43cf6eSHans Rosenfeld { 3546*fd43cf6eSHans Rosenfeld ieee80211com_t *ic = &sc->sc_ic; 3547*fd43cf6eSHans Rosenfeld ieee80211_node_t *in = ic->ic_bss; 3548*fd43cf6eSHans Rosenfeld struct wmeParams *wmeparam; 3549*fd43cf6eSHans Rosenfeld struct iwn_edca_params edcaparam; 3550*fd43cf6eSHans Rosenfeld int i, j; 3551*fd43cf6eSHans Rosenfeld int err = IWN_FAIL; 3552*fd43cf6eSHans Rosenfeld 3553*fd43cf6eSHans Rosenfeld if ((in->in_flags & IEEE80211_NODE_QOS) && 3554*fd43cf6eSHans Rosenfeld (IEEE80211_M_STA == ic->ic_opmode)) { 3555*fd43cf6eSHans Rosenfeld wmeparam = ic->ic_wme.wme_chanParams.cap_wmeParams; 3556*fd43cf6eSHans Rosenfeld } else { 3557*fd43cf6eSHans Rosenfeld return (IWN_SUCCESS); 3558*fd43cf6eSHans Rosenfeld } 3559*fd43cf6eSHans Rosenfeld 3560*fd43cf6eSHans Rosenfeld (void) memset(&edcaparam, 0, sizeof (edcaparam)); 3561*fd43cf6eSHans Rosenfeld 3562*fd43cf6eSHans Rosenfeld err = iwn_wmeparam_check(sc, wmeparam); 3563*fd43cf6eSHans Rosenfeld if (err != IWN_SUCCESS) { 3564*fd43cf6eSHans Rosenfeld return (err); 3565*fd43cf6eSHans Rosenfeld } 3566*fd43cf6eSHans Rosenfeld 3567*fd43cf6eSHans Rosenfeld if (in->in_flags & IEEE80211_NODE_QOS) { 3568*fd43cf6eSHans Rosenfeld edcaparam.flags |= QOS_PARAM_FLG_UPDATE_EDCA; 3569*fd43cf6eSHans Rosenfeld } 3570*fd43cf6eSHans Rosenfeld 3571*fd43cf6eSHans Rosenfeld if (in->in_flags & (IEEE80211_NODE_QOS | IEEE80211_NODE_HT)) { 3572*fd43cf6eSHans Rosenfeld edcaparam.flags |= QOS_PARAM_FLG_TGN; 3573*fd43cf6eSHans Rosenfeld } 3574*fd43cf6eSHans Rosenfeld 3575*fd43cf6eSHans Rosenfeld for (i = 0; i < WME_NUM_AC; i++) { 3576*fd43cf6eSHans Rosenfeld 3577*fd43cf6eSHans Rosenfeld j = iwn_wme_to_qos_ac(sc, i); 3578*fd43cf6eSHans Rosenfeld if (j < QOS_AC_BK || j > QOS_AC_VO) { 3579*fd43cf6eSHans Rosenfeld return (IWN_FAIL); 3580*fd43cf6eSHans Rosenfeld } 3581*fd43cf6eSHans Rosenfeld 3582*fd43cf6eSHans Rosenfeld sc->sc_edca->ac[j].cwmin.value.ul = edcaparam.ac[j].cwmin = 3583*fd43cf6eSHans Rosenfeld iwn_cw_e_to_cw(wmeparam[i].wmep_logcwmin); 3584*fd43cf6eSHans Rosenfeld sc->sc_edca->ac[j].cwmax.value.ul = edcaparam.ac[j].cwmax = 3585*fd43cf6eSHans Rosenfeld iwn_cw_e_to_cw(wmeparam[i].wmep_logcwmax); 3586*fd43cf6eSHans Rosenfeld sc->sc_edca->ac[j].aifsn.value.ul = edcaparam.ac[j].aifsn = 3587*fd43cf6eSHans Rosenfeld wmeparam[i].wmep_aifsn; 3588*fd43cf6eSHans Rosenfeld sc->sc_edca->ac[j].txop.value.ul = edcaparam.ac[j].txoplimit = 3589*fd43cf6eSHans Rosenfeld (uint16_t)(wmeparam[i].wmep_txopLimit * 32); 3590*fd43cf6eSHans Rosenfeld } 3591*fd43cf6eSHans Rosenfeld 3592*fd43cf6eSHans Rosenfeld err = iwn_cmd(sc, IWN_CMD_EDCA_PARAMS, &edcaparam, 3593*fd43cf6eSHans Rosenfeld sizeof (edcaparam), async); 3594*fd43cf6eSHans Rosenfeld if (err != IWN_SUCCESS) { 3595*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, "!iwn_qosparam_to_hw(): " 3596*fd43cf6eSHans Rosenfeld "failed to update QoS parameters into hardware."); 3597*fd43cf6eSHans Rosenfeld return (err); 3598*fd43cf6eSHans Rosenfeld } 3599*fd43cf6eSHans Rosenfeld 3600*fd43cf6eSHans Rosenfeld return (err); 3601*fd43cf6eSHans Rosenfeld } 3602*fd43cf6eSHans Rosenfeld 3603*fd43cf6eSHans Rosenfeld static inline int 3604*fd43cf6eSHans Rosenfeld iwn_wme_tid_qos_ac(int tid) 3605*fd43cf6eSHans Rosenfeld { 3606*fd43cf6eSHans Rosenfeld switch (tid) { 3607*fd43cf6eSHans Rosenfeld case 1: 3608*fd43cf6eSHans Rosenfeld case 2: 3609*fd43cf6eSHans Rosenfeld return (QOS_AC_BK); 3610*fd43cf6eSHans Rosenfeld case 0: 3611*fd43cf6eSHans Rosenfeld case 3: 3612*fd43cf6eSHans Rosenfeld return (QOS_AC_BE); 3613*fd43cf6eSHans Rosenfeld case 4: 3614*fd43cf6eSHans Rosenfeld case 5: 3615*fd43cf6eSHans Rosenfeld return (QOS_AC_VI); 3616*fd43cf6eSHans Rosenfeld case 6: 3617*fd43cf6eSHans Rosenfeld case 7: 3618*fd43cf6eSHans Rosenfeld return (QOS_AC_VO); 3619*fd43cf6eSHans Rosenfeld } 3620*fd43cf6eSHans Rosenfeld 3621*fd43cf6eSHans Rosenfeld return (QOS_AC_BE); 3622*fd43cf6eSHans Rosenfeld } 3623*fd43cf6eSHans Rosenfeld 3624*fd43cf6eSHans Rosenfeld static inline int 3625*fd43cf6eSHans Rosenfeld iwn_qos_ac_to_txq(int qos_ac) 3626*fd43cf6eSHans Rosenfeld { 3627*fd43cf6eSHans Rosenfeld switch (qos_ac) { 3628*fd43cf6eSHans Rosenfeld case QOS_AC_BK: 3629*fd43cf6eSHans Rosenfeld return (QOS_AC_BK_TO_TXQ); 3630*fd43cf6eSHans Rosenfeld case QOS_AC_BE: 3631*fd43cf6eSHans Rosenfeld return (QOS_AC_BE_TO_TXQ); 3632*fd43cf6eSHans Rosenfeld case QOS_AC_VI: 3633*fd43cf6eSHans Rosenfeld return (QOS_AC_VI_TO_TXQ); 3634*fd43cf6eSHans Rosenfeld case QOS_AC_VO: 3635*fd43cf6eSHans Rosenfeld return (QOS_AC_VO_TO_TXQ); 3636*fd43cf6eSHans Rosenfeld } 3637*fd43cf6eSHans Rosenfeld 3638*fd43cf6eSHans Rosenfeld return (QOS_AC_BE_TO_TXQ); 3639*fd43cf6eSHans Rosenfeld } 3640*fd43cf6eSHans Rosenfeld 3641*fd43cf6eSHans Rosenfeld static int 3642*fd43cf6eSHans Rosenfeld iwn_wme_tid_to_txq(struct iwn_softc *sc, int tid) 3643*fd43cf6eSHans Rosenfeld { 3644*fd43cf6eSHans Rosenfeld int queue_n = TXQ_FOR_AC_INVALID; 3645*fd43cf6eSHans Rosenfeld int qos_ac; 3646*fd43cf6eSHans Rosenfeld 3647*fd43cf6eSHans Rosenfeld if (tid < WME_TID_MIN || 3648*fd43cf6eSHans Rosenfeld tid > WME_TID_MAX) { 3649*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, "!wme_tid_to_txq(): " 3650*fd43cf6eSHans Rosenfeld "TID is not in suitable range."); 3651*fd43cf6eSHans Rosenfeld return (queue_n); 3652*fd43cf6eSHans Rosenfeld } 3653*fd43cf6eSHans Rosenfeld 3654*fd43cf6eSHans Rosenfeld qos_ac = iwn_wme_tid_qos_ac(tid); 3655*fd43cf6eSHans Rosenfeld queue_n = iwn_qos_ac_to_txq(qos_ac); 3656*fd43cf6eSHans Rosenfeld 3657*fd43cf6eSHans Rosenfeld return (queue_n); 3658*fd43cf6eSHans Rosenfeld } 3659*fd43cf6eSHans Rosenfeld 3660*fd43cf6eSHans Rosenfeld static int 3661*fd43cf6eSHans Rosenfeld iwn_send(ieee80211com_t *ic, mblk_t *mp, uint8_t type) 3662*fd43cf6eSHans Rosenfeld { 3663*fd43cf6eSHans Rosenfeld struct iwn_softc *sc = (struct iwn_softc *)ic; 3664*fd43cf6eSHans Rosenfeld struct iwn_node *wn; 3665*fd43cf6eSHans Rosenfeld struct iwn_tx_ring *ring; 3666*fd43cf6eSHans Rosenfeld struct iwn_tx_desc *desc; 3667*fd43cf6eSHans Rosenfeld struct iwn_tx_data *data; 3668*fd43cf6eSHans Rosenfeld struct iwn_tx_cmd *cmd; 3669*fd43cf6eSHans Rosenfeld struct iwn_cmd_data *tx; 3670*fd43cf6eSHans Rosenfeld ieee80211_node_t *in; 3671*fd43cf6eSHans Rosenfeld const struct iwn_rate *rinfo; 3672*fd43cf6eSHans Rosenfeld struct ieee80211_frame *wh; 3673*fd43cf6eSHans Rosenfeld struct ieee80211_key *k = NULL; 3674*fd43cf6eSHans Rosenfeld uint32_t flags; 3675*fd43cf6eSHans Rosenfeld uint_t hdrlen; 3676*fd43cf6eSHans Rosenfeld uint8_t ridx, txant; 3677*fd43cf6eSHans Rosenfeld int i, totlen, seglen, pad; 3678*fd43cf6eSHans Rosenfeld int txq_id = NON_QOS_TXQ; 3679*fd43cf6eSHans Rosenfeld struct ieee80211_qosframe *qwh = NULL; 3680*fd43cf6eSHans Rosenfeld uint8_t tid = WME_TID_INVALID; 3681*fd43cf6eSHans Rosenfeld ddi_dma_cookie_t cookie; 3682*fd43cf6eSHans Rosenfeld mblk_t *m0, *m; 3683*fd43cf6eSHans Rosenfeld int mblen, off; 3684*fd43cf6eSHans Rosenfeld 3685*fd43cf6eSHans Rosenfeld int noack = 0; 3686*fd43cf6eSHans Rosenfeld 3687*fd43cf6eSHans Rosenfeld if (ic == NULL) 3688*fd43cf6eSHans Rosenfeld return (EIO); 3689*fd43cf6eSHans Rosenfeld 3690*fd43cf6eSHans Rosenfeld if ((mp == NULL) || (MBLKL(mp) <= 0)) 3691*fd43cf6eSHans Rosenfeld return (EIO); 3692*fd43cf6eSHans Rosenfeld 3693*fd43cf6eSHans Rosenfeld if (sc->sc_flags & IWN_FLAG_SUSPEND) { 3694*fd43cf6eSHans Rosenfeld freemsg(mp); 3695*fd43cf6eSHans Rosenfeld sc->sc_tx_err++; 3696*fd43cf6eSHans Rosenfeld return(EIO); 3697*fd43cf6eSHans Rosenfeld } 3698*fd43cf6eSHans Rosenfeld 3699*fd43cf6eSHans Rosenfeld wh = (struct ieee80211_frame *)mp->b_rptr; 3700*fd43cf6eSHans Rosenfeld 3701*fd43cf6eSHans Rosenfeld hdrlen = ieee80211_hdrspace(ic, mp->b_rptr); 3702*fd43cf6eSHans Rosenfeld 3703*fd43cf6eSHans Rosenfeld /* 3704*fd43cf6eSHans Rosenfeld * determine send which AP or station in IBSS 3705*fd43cf6eSHans Rosenfeld */ 3706*fd43cf6eSHans Rosenfeld in = ieee80211_find_txnode(ic, wh->i_addr1); 3707*fd43cf6eSHans Rosenfeld if (in == NULL) { 3708*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, "!iwn_send(): " 3709*fd43cf6eSHans Rosenfeld "failed to find tx node"); 3710*fd43cf6eSHans Rosenfeld freemsg(mp); 3711*fd43cf6eSHans Rosenfeld sc->sc_tx_err++; 3712*fd43cf6eSHans Rosenfeld return(EIO); 3713*fd43cf6eSHans Rosenfeld } 3714*fd43cf6eSHans Rosenfeld 3715*fd43cf6eSHans Rosenfeld wn = (struct iwn_node *)in; 3716*fd43cf6eSHans Rosenfeld 3717*fd43cf6eSHans Rosenfeld /* 3718*fd43cf6eSHans Rosenfeld * Determine TX queue according to traffic ID in frame 3719*fd43cf6eSHans Rosenfeld * if working in QoS mode. 3720*fd43cf6eSHans Rosenfeld */ 3721*fd43cf6eSHans Rosenfeld if (in->in_flags & IEEE80211_NODE_QOS) { 3722*fd43cf6eSHans Rosenfeld if ((type & IEEE80211_FC0_TYPE_MASK) == 3723*fd43cf6eSHans Rosenfeld IEEE80211_FC0_TYPE_DATA) { 3724*fd43cf6eSHans Rosenfeld if (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_QOS) { 3725*fd43cf6eSHans Rosenfeld qwh = (struct ieee80211_qosframe *)wh; 3726*fd43cf6eSHans Rosenfeld 3727*fd43cf6eSHans Rosenfeld tid = qwh->i_qos[0] & IEEE80211_QOS_TID; 3728*fd43cf6eSHans Rosenfeld txq_id = iwn_wme_tid_to_txq(sc, tid); 3729*fd43cf6eSHans Rosenfeld 3730*fd43cf6eSHans Rosenfeld if (txq_id < TXQ_FOR_AC_MIN || 3731*fd43cf6eSHans Rosenfeld (txq_id > TXQ_FOR_AC_MAX)) { 3732*fd43cf6eSHans Rosenfeld freemsg(mp); 3733*fd43cf6eSHans Rosenfeld sc->sc_tx_err++; 3734*fd43cf6eSHans Rosenfeld return(EIO); 3735*fd43cf6eSHans Rosenfeld } 3736*fd43cf6eSHans Rosenfeld } else { 3737*fd43cf6eSHans Rosenfeld txq_id = NON_QOS_TXQ; 3738*fd43cf6eSHans Rosenfeld } 3739*fd43cf6eSHans Rosenfeld } else if ((type & IEEE80211_FC0_TYPE_MASK) == 3740*fd43cf6eSHans Rosenfeld IEEE80211_FC0_TYPE_MGT) { 3741*fd43cf6eSHans Rosenfeld txq_id = QOS_TXQ_FOR_MGT; 3742*fd43cf6eSHans Rosenfeld } else { 3743*fd43cf6eSHans Rosenfeld txq_id = NON_QOS_TXQ; 3744*fd43cf6eSHans Rosenfeld } 3745*fd43cf6eSHans Rosenfeld } else { 3746*fd43cf6eSHans Rosenfeld txq_id = NON_QOS_TXQ; 3747*fd43cf6eSHans Rosenfeld } 3748*fd43cf6eSHans Rosenfeld 3749*fd43cf6eSHans Rosenfeld if (sc->qfullmsk & (1 << txq_id)) { 3750*fd43cf6eSHans Rosenfeld sc->sc_tx_err++; 3751*fd43cf6eSHans Rosenfeld /* net80211-initiated send */ 3752*fd43cf6eSHans Rosenfeld if ((type & IEEE80211_FC0_TYPE_MASK) != 3753*fd43cf6eSHans Rosenfeld IEEE80211_FC0_TYPE_DATA) 3754*fd43cf6eSHans Rosenfeld freemsg(mp); 3755*fd43cf6eSHans Rosenfeld return (EAGAIN); 3756*fd43cf6eSHans Rosenfeld } 3757*fd43cf6eSHans Rosenfeld 3758*fd43cf6eSHans Rosenfeld /* Choose a TX rate index. */ 3759*fd43cf6eSHans Rosenfeld if (IEEE80211_IS_MULTICAST(wh->i_addr1) || 3760*fd43cf6eSHans Rosenfeld type != IEEE80211_FC0_TYPE_DATA) { 3761*fd43cf6eSHans Rosenfeld ridx = (ic->ic_curmode == IEEE80211_MODE_11A) ? 3762*fd43cf6eSHans Rosenfeld IWN_RIDX_OFDM6 : IWN_RIDX_CCK1; 3763*fd43cf6eSHans Rosenfeld } else if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE) { 3764*fd43cf6eSHans Rosenfeld ridx = sc->fixed_ridx; 3765*fd43cf6eSHans Rosenfeld } else 3766*fd43cf6eSHans Rosenfeld ridx = wn->ridx[in->in_txrate]; 3767*fd43cf6eSHans Rosenfeld rinfo = &iwn_rates[ridx]; 3768*fd43cf6eSHans Rosenfeld 3769*fd43cf6eSHans Rosenfeld m = allocb(msgdsize(mp) + 32, BPRI_MED); 3770*fd43cf6eSHans Rosenfeld if (m) { 3771*fd43cf6eSHans Rosenfeld for (off = 0, m0 = mp; m0 != NULL; m0 = m0->b_cont) { 3772*fd43cf6eSHans Rosenfeld mblen = MBLKL(m0); 3773*fd43cf6eSHans Rosenfeld bcopy(m0->b_rptr, m->b_rptr + off, mblen); 3774*fd43cf6eSHans Rosenfeld off += mblen; 3775*fd43cf6eSHans Rosenfeld } 3776*fd43cf6eSHans Rosenfeld 3777*fd43cf6eSHans Rosenfeld m->b_wptr += off; 3778*fd43cf6eSHans Rosenfeld 3779*fd43cf6eSHans Rosenfeld freemsg(mp); 3780*fd43cf6eSHans Rosenfeld mp = m; 3781*fd43cf6eSHans Rosenfeld 3782*fd43cf6eSHans Rosenfeld wh = (struct ieee80211_frame *)mp->b_rptr; 3783*fd43cf6eSHans Rosenfeld } else { 3784*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, "!iwn_send(): can't copy"); 3785*fd43cf6eSHans Rosenfeld /* net80211-initiated send */ 3786*fd43cf6eSHans Rosenfeld if ((type & IEEE80211_FC0_TYPE_MASK) != 3787*fd43cf6eSHans Rosenfeld IEEE80211_FC0_TYPE_DATA) 3788*fd43cf6eSHans Rosenfeld freemsg(mp); 3789*fd43cf6eSHans Rosenfeld return (EAGAIN); 3790*fd43cf6eSHans Rosenfeld } 3791*fd43cf6eSHans Rosenfeld 3792*fd43cf6eSHans Rosenfeld 3793*fd43cf6eSHans Rosenfeld /* 3794*fd43cf6eSHans Rosenfeld * Net80211 module encapsulate outbound data frames. 3795*fd43cf6eSHans Rosenfeld * Add some fields of 80211 frame. 3796*fd43cf6eSHans Rosenfeld */ 3797*fd43cf6eSHans Rosenfeld if ((type & IEEE80211_FC0_TYPE_MASK) == 3798*fd43cf6eSHans Rosenfeld IEEE80211_FC0_TYPE_DATA) 3799*fd43cf6eSHans Rosenfeld (void) ieee80211_encap(ic, mp, in); 3800*fd43cf6eSHans Rosenfeld 3801*fd43cf6eSHans Rosenfeld /* Encrypt the frame if need be. */ 3802*fd43cf6eSHans Rosenfeld if (wh->i_fc[1] & IEEE80211_FC1_WEP) { 3803*fd43cf6eSHans Rosenfeld k = ieee80211_crypto_encap(ic, mp); 3804*fd43cf6eSHans Rosenfeld if (k == NULL) { 3805*fd43cf6eSHans Rosenfeld freemsg(mp); 3806*fd43cf6eSHans Rosenfeld return(EIO); 3807*fd43cf6eSHans Rosenfeld } 3808*fd43cf6eSHans Rosenfeld /* Packet header may have moved, reset our local pointer. */ 3809*fd43cf6eSHans Rosenfeld wh = (struct ieee80211_frame *)mp->b_rptr; 3810*fd43cf6eSHans Rosenfeld } 3811*fd43cf6eSHans Rosenfeld totlen = msgdsize(mp); 3812*fd43cf6eSHans Rosenfeld 3813*fd43cf6eSHans Rosenfeld mutex_enter(&sc->sc_tx_mtx); 3814*fd43cf6eSHans Rosenfeld ring = &sc->txq[txq_id]; 3815*fd43cf6eSHans Rosenfeld desc = &ring->desc[ring->cur]; 3816*fd43cf6eSHans Rosenfeld data = &ring->data[ring->cur]; 3817*fd43cf6eSHans Rosenfeld 3818*fd43cf6eSHans Rosenfeld /* Prepare TX firmware command. */ 3819*fd43cf6eSHans Rosenfeld cmd = &ring->cmd[ring->cur]; 3820*fd43cf6eSHans Rosenfeld cmd->code = IWN_CMD_TX_DATA; 3821*fd43cf6eSHans Rosenfeld cmd->flags = 0; 3822*fd43cf6eSHans Rosenfeld cmd->qid = ring->qid; 3823*fd43cf6eSHans Rosenfeld cmd->idx = ring->cur; 3824*fd43cf6eSHans Rosenfeld 3825*fd43cf6eSHans Rosenfeld tx = (struct iwn_cmd_data *)cmd->data; 3826*fd43cf6eSHans Rosenfeld /* NB: No need to clear tx, all fields are reinitialized here. */ 3827*fd43cf6eSHans Rosenfeld tx->scratch = 0; /* clear "scratch" area */ 3828*fd43cf6eSHans Rosenfeld 3829*fd43cf6eSHans Rosenfeld flags = 0; 3830*fd43cf6eSHans Rosenfeld if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { 3831*fd43cf6eSHans Rosenfeld /* Unicast frame, check if an ACK is expected. */ 3832*fd43cf6eSHans Rosenfeld if (!noack) 3833*fd43cf6eSHans Rosenfeld flags |= IWN_TX_NEED_ACK; 3834*fd43cf6eSHans Rosenfeld } 3835*fd43cf6eSHans Rosenfeld 3836*fd43cf6eSHans Rosenfeld if ((wh->i_fc[0] & 3837*fd43cf6eSHans Rosenfeld (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) == 3838*fd43cf6eSHans Rosenfeld (IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_BAR)) 3839*fd43cf6eSHans Rosenfeld flags |= IWN_TX_IMM_BA; /* Cannot happen yet. */ 3840*fd43cf6eSHans Rosenfeld 3841*fd43cf6eSHans Rosenfeld ASSERT((flags & IWN_TX_IMM_BA) == 0); 3842*fd43cf6eSHans Rosenfeld 3843*fd43cf6eSHans Rosenfeld if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG) 3844*fd43cf6eSHans Rosenfeld flags |= IWN_TX_MORE_FRAG; /* Cannot happen yet. */ 3845*fd43cf6eSHans Rosenfeld 3846*fd43cf6eSHans Rosenfeld ASSERT((flags & IWN_TX_MORE_FRAG) == 0); 3847*fd43cf6eSHans Rosenfeld 3848*fd43cf6eSHans Rosenfeld /* Check if frame must be protected using RTS/CTS or CTS-to-self. */ 3849*fd43cf6eSHans Rosenfeld if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { 3850*fd43cf6eSHans Rosenfeld /* NB: Group frames are sent using CCK in 802.11b/g. */ 3851*fd43cf6eSHans Rosenfeld if (totlen + IEEE80211_CRC_LEN > ic->ic_rtsthreshold) { 3852*fd43cf6eSHans Rosenfeld flags |= IWN_TX_NEED_RTS; 3853*fd43cf6eSHans Rosenfeld } else if ((ic->ic_flags & IEEE80211_F_USEPROT) && 3854*fd43cf6eSHans Rosenfeld ridx >= IWN_RIDX_OFDM6) { 3855*fd43cf6eSHans Rosenfeld if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) 3856*fd43cf6eSHans Rosenfeld flags |= IWN_TX_NEED_CTS; 3857*fd43cf6eSHans Rosenfeld else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) 3858*fd43cf6eSHans Rosenfeld flags |= IWN_TX_NEED_RTS; 3859*fd43cf6eSHans Rosenfeld } 3860*fd43cf6eSHans Rosenfeld if (flags & (IWN_TX_NEED_RTS | IWN_TX_NEED_CTS)) { 3861*fd43cf6eSHans Rosenfeld if (sc->hw_type != IWN_HW_REV_TYPE_4965) { 3862*fd43cf6eSHans Rosenfeld /* 5000 autoselects RTS/CTS or CTS-to-self. */ 3863*fd43cf6eSHans Rosenfeld flags &= ~(IWN_TX_NEED_RTS | IWN_TX_NEED_CTS); 3864*fd43cf6eSHans Rosenfeld flags |= IWN_TX_NEED_PROTECTION; 3865*fd43cf6eSHans Rosenfeld } else 3866*fd43cf6eSHans Rosenfeld flags |= IWN_TX_FULL_TXOP; 3867*fd43cf6eSHans Rosenfeld } 3868*fd43cf6eSHans Rosenfeld } 3869*fd43cf6eSHans Rosenfeld 3870*fd43cf6eSHans Rosenfeld if (IEEE80211_IS_MULTICAST(wh->i_addr1) || 3871*fd43cf6eSHans Rosenfeld type != IEEE80211_FC0_TYPE_DATA) 3872*fd43cf6eSHans Rosenfeld tx->id = sc->broadcast_id; 3873*fd43cf6eSHans Rosenfeld else 3874*fd43cf6eSHans Rosenfeld tx->id = wn->id; 3875*fd43cf6eSHans Rosenfeld 3876*fd43cf6eSHans Rosenfeld if (type == IEEE80211_FC0_TYPE_MGT) { 3877*fd43cf6eSHans Rosenfeld uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; 3878*fd43cf6eSHans Rosenfeld 3879*fd43cf6eSHans Rosenfeld #ifndef IEEE80211_STA_ONLY 3880*fd43cf6eSHans Rosenfeld /* Tell HW to set timestamp in probe responses. */ 3881*fd43cf6eSHans Rosenfeld /* XXX NetBSD rev 1.11 added probe requests here but */ 3882*fd43cf6eSHans Rosenfeld /* probe requests do not take timestamps (from Bergamini). */ 3883*fd43cf6eSHans Rosenfeld if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP) 3884*fd43cf6eSHans Rosenfeld flags |= IWN_TX_INSERT_TSTAMP; 3885*fd43cf6eSHans Rosenfeld #endif 3886*fd43cf6eSHans Rosenfeld /* XXX NetBSD rev 1.11 and 1.20 added AUTH/DAUTH and RTS/CTS */ 3887*fd43cf6eSHans Rosenfeld /* changes here. These are not needed (from Bergamini). */ 3888*fd43cf6eSHans Rosenfeld if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ || 3889*fd43cf6eSHans Rosenfeld subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) 3890*fd43cf6eSHans Rosenfeld tx->timeout = htole16(3); 3891*fd43cf6eSHans Rosenfeld else 3892*fd43cf6eSHans Rosenfeld tx->timeout = htole16(2); 3893*fd43cf6eSHans Rosenfeld } else 3894*fd43cf6eSHans Rosenfeld tx->timeout = htole16(0); 3895*fd43cf6eSHans Rosenfeld 3896*fd43cf6eSHans Rosenfeld if (hdrlen & 3) { 3897*fd43cf6eSHans Rosenfeld /* First segment length must be a multiple of 4. */ 3898*fd43cf6eSHans Rosenfeld flags |= IWN_TX_NEED_PADDING; 3899*fd43cf6eSHans Rosenfeld pad = 4 - (hdrlen & 3); 3900*fd43cf6eSHans Rosenfeld } else 3901*fd43cf6eSHans Rosenfeld pad = 0; 3902*fd43cf6eSHans Rosenfeld 3903*fd43cf6eSHans Rosenfeld if (tid != WME_TID_INVALID) { 3904*fd43cf6eSHans Rosenfeld flags &= ~IWN_TX_AUTO_SEQ; 3905*fd43cf6eSHans Rosenfeld } else { 3906*fd43cf6eSHans Rosenfeld flags |= IWN_TX_AUTO_SEQ; 3907*fd43cf6eSHans Rosenfeld tid = 0; 3908*fd43cf6eSHans Rosenfeld } 3909*fd43cf6eSHans Rosenfeld 3910*fd43cf6eSHans Rosenfeld tx->len = htole16(totlen); 3911*fd43cf6eSHans Rosenfeld tx->tid = tid; 3912*fd43cf6eSHans Rosenfeld tx->rts_ntries = 60; 3913*fd43cf6eSHans Rosenfeld tx->data_ntries = 15; 3914*fd43cf6eSHans Rosenfeld tx->lifetime = htole32(IWN_LIFETIME_INFINITE); 3915*fd43cf6eSHans Rosenfeld tx->plcp = rinfo->plcp; 3916*fd43cf6eSHans Rosenfeld tx->rflags = rinfo->flags; 3917*fd43cf6eSHans Rosenfeld if (tx->id == sc->broadcast_id) { 3918*fd43cf6eSHans Rosenfeld /* Group or management frame. */ 3919*fd43cf6eSHans Rosenfeld tx->linkq = 0; 3920*fd43cf6eSHans Rosenfeld /* XXX Alternate between antenna A and B? */ 3921*fd43cf6eSHans Rosenfeld txant = IWN_LSB(sc->txchainmask); 3922*fd43cf6eSHans Rosenfeld tx->rflags |= IWN_RFLAG_ANT(txant); 3923*fd43cf6eSHans Rosenfeld } else { 3924*fd43cf6eSHans Rosenfeld tx->linkq = in->in_rates.ir_nrates - in->in_txrate - 1; 3925*fd43cf6eSHans Rosenfeld flags |= IWN_TX_LINKQ; /* enable MRR */ 3926*fd43cf6eSHans Rosenfeld } 3927*fd43cf6eSHans Rosenfeld /* Set physical address of "scratch area". */ 3928*fd43cf6eSHans Rosenfeld tx->loaddr = htole32(IWN_LOADDR(data->scratch_paddr)); 3929*fd43cf6eSHans Rosenfeld tx->hiaddr = IWN_HIADDR(data->scratch_paddr); 3930*fd43cf6eSHans Rosenfeld 3931*fd43cf6eSHans Rosenfeld /* Copy 802.11 header in TX command. */ 3932*fd43cf6eSHans Rosenfeld /* XXX NetBSD changed this in rev 1.20 */ 3933*fd43cf6eSHans Rosenfeld memcpy(((uint8_t *)tx) + sizeof(*tx), wh, hdrlen); 3934*fd43cf6eSHans Rosenfeld mp->b_rptr += hdrlen; 3935*fd43cf6eSHans Rosenfeld 3936*fd43cf6eSHans Rosenfeld bcopy(mp->b_rptr, data->dma_data.vaddr, totlen - hdrlen); 3937*fd43cf6eSHans Rosenfeld tx->security = 0; 3938*fd43cf6eSHans Rosenfeld tx->flags = htole32(flags); 3939*fd43cf6eSHans Rosenfeld 3940*fd43cf6eSHans Rosenfeld data->ni = in; 3941*fd43cf6eSHans Rosenfeld 3942*fd43cf6eSHans Rosenfeld DTRACE_PROBE4(tx, int, ring->qid, int, ring->cur, size_t, MBLKL(mp), 3943*fd43cf6eSHans Rosenfeld int, data->dma_data.ncookies); 3944*fd43cf6eSHans Rosenfeld 3945*fd43cf6eSHans Rosenfeld /* Fill TX descriptor. */ 3946*fd43cf6eSHans Rosenfeld desc->nsegs = 1 + data->dma_data.ncookies; 3947*fd43cf6eSHans Rosenfeld /* First DMA segment is used by the TX command. */ 3948*fd43cf6eSHans Rosenfeld desc->segs[0].addr = htole32(IWN_LOADDR(data->cmd_paddr)); 3949*fd43cf6eSHans Rosenfeld desc->segs[0].len = htole16(IWN_HIADDR(data->cmd_paddr) | 3950*fd43cf6eSHans Rosenfeld (4 + sizeof (*tx) + hdrlen + pad) << 4); 3951*fd43cf6eSHans Rosenfeld 3952*fd43cf6eSHans Rosenfeld /* Other DMA segments are for data payload. */ 3953*fd43cf6eSHans Rosenfeld cookie = data->dma_data.cookie; 3954*fd43cf6eSHans Rosenfeld for (i = 1, seglen = totlen - hdrlen; 3955*fd43cf6eSHans Rosenfeld i <= data->dma_data.ncookies; 3956*fd43cf6eSHans Rosenfeld i++, seglen -= cookie.dmac_size) { 3957*fd43cf6eSHans Rosenfeld desc->segs[i].addr = htole32(IWN_LOADDR(cookie.dmac_laddress)); 3958*fd43cf6eSHans Rosenfeld desc->segs[i].len = htole16(IWN_HIADDR(cookie.dmac_laddress) | 3959*fd43cf6eSHans Rosenfeld seglen << 4); 3960*fd43cf6eSHans Rosenfeld if (i < data->dma_data.ncookies) 3961*fd43cf6eSHans Rosenfeld ddi_dma_nextcookie(data->dma_data.dma_hdl, &cookie); 3962*fd43cf6eSHans Rosenfeld } 3963*fd43cf6eSHans Rosenfeld 3964*fd43cf6eSHans Rosenfeld (void) ddi_dma_sync(data->dma_data.dma_hdl, 0, 0, DDI_DMA_SYNC_FORDEV); 3965*fd43cf6eSHans Rosenfeld (void) ddi_dma_sync(ring->cmd_dma.dma_hdl, ring->cur * sizeof (*cmd), 3966*fd43cf6eSHans Rosenfeld sizeof (*cmd), DDI_DMA_SYNC_FORDEV); 3967*fd43cf6eSHans Rosenfeld (void) ddi_dma_sync(ring->desc_dma.dma_hdl, ring->cur * sizeof (*desc), 3968*fd43cf6eSHans Rosenfeld sizeof (*desc), DDI_DMA_SYNC_FORDEV); 3969*fd43cf6eSHans Rosenfeld 3970*fd43cf6eSHans Rosenfeld /* Update TX scheduler. */ 3971*fd43cf6eSHans Rosenfeld sc->ops.update_sched(sc, ring->qid, ring->cur, tx->id, totlen); 3972*fd43cf6eSHans Rosenfeld 3973*fd43cf6eSHans Rosenfeld /* Kick TX ring. */ 3974*fd43cf6eSHans Rosenfeld ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT; 3975*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur); 3976*fd43cf6eSHans Rosenfeld 3977*fd43cf6eSHans Rosenfeld /* Mark TX ring as full if we reach a certain threshold. */ 3978*fd43cf6eSHans Rosenfeld if (++ring->queued > IWN_TX_RING_HIMARK) 3979*fd43cf6eSHans Rosenfeld sc->qfullmsk |= 1 << ring->qid; 3980*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_tx_mtx); 3981*fd43cf6eSHans Rosenfeld freemsg(mp); 3982*fd43cf6eSHans Rosenfeld 3983*fd43cf6eSHans Rosenfeld ic->ic_stats.is_tx_bytes += totlen; 3984*fd43cf6eSHans Rosenfeld 3985*fd43cf6eSHans Rosenfeld mutex_enter(&sc->sc_mt_mtx); 3986*fd43cf6eSHans Rosenfeld if (sc->sc_tx_timer == 0) 3987*fd43cf6eSHans Rosenfeld sc->sc_tx_timer = 5; 3988*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_mt_mtx); 3989*fd43cf6eSHans Rosenfeld 3990*fd43cf6eSHans Rosenfeld return 0; 3991*fd43cf6eSHans Rosenfeld } 3992*fd43cf6eSHans Rosenfeld 3993*fd43cf6eSHans Rosenfeld static mblk_t * 3994*fd43cf6eSHans Rosenfeld iwn_m_tx(void *arg, mblk_t *mp) 3995*fd43cf6eSHans Rosenfeld { 3996*fd43cf6eSHans Rosenfeld struct iwn_softc *sc; 3997*fd43cf6eSHans Rosenfeld ieee80211com_t *ic; 3998*fd43cf6eSHans Rosenfeld mblk_t *next; 3999*fd43cf6eSHans Rosenfeld 4000*fd43cf6eSHans Rosenfeld sc = (struct iwn_softc *)arg; 4001*fd43cf6eSHans Rosenfeld ASSERT(sc != NULL); 4002*fd43cf6eSHans Rosenfeld ic = &sc->sc_ic; 4003*fd43cf6eSHans Rosenfeld 4004*fd43cf6eSHans Rosenfeld if (sc->sc_flags & IWN_FLAG_SUSPEND) { 4005*fd43cf6eSHans Rosenfeld freemsgchain(mp); 4006*fd43cf6eSHans Rosenfeld return (NULL); 4007*fd43cf6eSHans Rosenfeld } 4008*fd43cf6eSHans Rosenfeld 4009*fd43cf6eSHans Rosenfeld if (ic->ic_state != IEEE80211_S_RUN) { 4010*fd43cf6eSHans Rosenfeld freemsgchain(mp); 4011*fd43cf6eSHans Rosenfeld return (NULL); 4012*fd43cf6eSHans Rosenfeld } 4013*fd43cf6eSHans Rosenfeld 4014*fd43cf6eSHans Rosenfeld if ((sc->sc_flags & IWN_FLAG_HW_ERR_RECOVER)) { 4015*fd43cf6eSHans Rosenfeld freemsgchain(mp); 4016*fd43cf6eSHans Rosenfeld return (NULL); 4017*fd43cf6eSHans Rosenfeld } 4018*fd43cf6eSHans Rosenfeld 4019*fd43cf6eSHans Rosenfeld while (mp != NULL) { 4020*fd43cf6eSHans Rosenfeld next = mp->b_next; 4021*fd43cf6eSHans Rosenfeld mp->b_next = NULL; 4022*fd43cf6eSHans Rosenfeld if (iwn_send(ic, mp, IEEE80211_FC0_TYPE_DATA) == EAGAIN) { 4023*fd43cf6eSHans Rosenfeld mp->b_next = next; 4024*fd43cf6eSHans Rosenfeld break; 4025*fd43cf6eSHans Rosenfeld } 4026*fd43cf6eSHans Rosenfeld mp = next; 4027*fd43cf6eSHans Rosenfeld } 4028*fd43cf6eSHans Rosenfeld 4029*fd43cf6eSHans Rosenfeld return (mp); 4030*fd43cf6eSHans Rosenfeld } 4031*fd43cf6eSHans Rosenfeld 4032*fd43cf6eSHans Rosenfeld static void 4033*fd43cf6eSHans Rosenfeld iwn_watchdog(void *arg) 4034*fd43cf6eSHans Rosenfeld { 4035*fd43cf6eSHans Rosenfeld struct iwn_softc *sc = (struct iwn_softc *)arg; 4036*fd43cf6eSHans Rosenfeld ieee80211com_t *ic = &sc->sc_ic; 4037*fd43cf6eSHans Rosenfeld timeout_id_t timeout_id = ic->ic_watchdog_timer; 4038*fd43cf6eSHans Rosenfeld 4039*fd43cf6eSHans Rosenfeld ieee80211_stop_watchdog(ic); 4040*fd43cf6eSHans Rosenfeld 4041*fd43cf6eSHans Rosenfeld mutex_enter(&sc->sc_mt_mtx); 4042*fd43cf6eSHans Rosenfeld if (sc->sc_tx_timer > 0) { 4043*fd43cf6eSHans Rosenfeld if (--sc->sc_tx_timer == 0) { 4044*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, "!device timeout"); 4045*fd43cf6eSHans Rosenfeld sc->sc_flags |= IWN_FLAG_HW_ERR_RECOVER; 4046*fd43cf6eSHans Rosenfeld sc->sc_ostate = IEEE80211_S_RUN; 4047*fd43cf6eSHans Rosenfeld DTRACE_PROBE(recover__send__fail); 4048*fd43cf6eSHans Rosenfeld } 4049*fd43cf6eSHans Rosenfeld } 4050*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_mt_mtx); 4051*fd43cf6eSHans Rosenfeld 4052*fd43cf6eSHans Rosenfeld if ((ic->ic_state != IEEE80211_S_AUTH) && 4053*fd43cf6eSHans Rosenfeld (ic->ic_state != IEEE80211_S_ASSOC)) 4054*fd43cf6eSHans Rosenfeld return; 4055*fd43cf6eSHans Rosenfeld 4056*fd43cf6eSHans Rosenfeld if (ic->ic_bss->in_fails > 10) { 4057*fd43cf6eSHans Rosenfeld DTRACE_PROBE2(watchdog__reset, timeout_id_t, timeout_id, 4058*fd43cf6eSHans Rosenfeld struct ieee80211node *, ic->ic_bss); 4059*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, "!iwn_watchdog reset"); 4060*fd43cf6eSHans Rosenfeld ieee80211_new_state(ic, IEEE80211_S_INIT, -1); 4061*fd43cf6eSHans Rosenfeld } else { 4062*fd43cf6eSHans Rosenfeld ic->ic_bss->in_fails++; 4063*fd43cf6eSHans Rosenfeld 4064*fd43cf6eSHans Rosenfeld DTRACE_PROBE2(watchdog__timeout, timeout_id_t, timeout_id, 4065*fd43cf6eSHans Rosenfeld struct ieee80211node *, ic->ic_bss); 4066*fd43cf6eSHans Rosenfeld 4067*fd43cf6eSHans Rosenfeld ieee80211_watchdog(ic); 4068*fd43cf6eSHans Rosenfeld } 4069*fd43cf6eSHans Rosenfeld } 4070*fd43cf6eSHans Rosenfeld 4071*fd43cf6eSHans Rosenfeld static void 4072*fd43cf6eSHans Rosenfeld iwn_m_ioctl(void *arg, queue_t *wq, mblk_t *mp) 4073*fd43cf6eSHans Rosenfeld { 4074*fd43cf6eSHans Rosenfeld struct iwn_softc *sc; 4075*fd43cf6eSHans Rosenfeld struct ieee80211com *ic; 4076*fd43cf6eSHans Rosenfeld int error = 0; 4077*fd43cf6eSHans Rosenfeld 4078*fd43cf6eSHans Rosenfeld sc = (struct iwn_softc *)arg; 4079*fd43cf6eSHans Rosenfeld ASSERT(sc != NULL); 4080*fd43cf6eSHans Rosenfeld ic = &sc->sc_ic; 4081*fd43cf6eSHans Rosenfeld 4082*fd43cf6eSHans Rosenfeld mutex_enter(&sc->sc_mtx); 4083*fd43cf6eSHans Rosenfeld while (sc->sc_flags & IWN_FLAG_SCANNING) 4084*fd43cf6eSHans Rosenfeld cv_wait(&sc->sc_scan_cv, &sc->sc_mtx); 4085*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_mtx); 4086*fd43cf6eSHans Rosenfeld 4087*fd43cf6eSHans Rosenfeld error = ieee80211_ioctl(ic, wq, mp); 4088*fd43cf6eSHans Rosenfeld if (error == ENETRESET) { 4089*fd43cf6eSHans Rosenfeld /* 4090*fd43cf6eSHans Rosenfeld * This is special for the hidden AP connection. 4091*fd43cf6eSHans Rosenfeld * In any case, we should make sure only one 'scan' 4092*fd43cf6eSHans Rosenfeld * in the driver for a 'connect' CLI command. So 4093*fd43cf6eSHans Rosenfeld * when connecting to a hidden AP, the scan is just 4094*fd43cf6eSHans Rosenfeld * sent out to the air when we know the desired 4095*fd43cf6eSHans Rosenfeld * essid of the AP we want to connect. 4096*fd43cf6eSHans Rosenfeld */ 4097*fd43cf6eSHans Rosenfeld if (ic->ic_des_esslen) { 4098*fd43cf6eSHans Rosenfeld if (sc->sc_flags & IWN_FLAG_RUNNING) { 4099*fd43cf6eSHans Rosenfeld DTRACE_PROBE(netreset); 4100*fd43cf6eSHans Rosenfeld iwn_m_stop(sc); 4101*fd43cf6eSHans Rosenfeld (void) iwn_m_start(sc); 4102*fd43cf6eSHans Rosenfeld (void) ieee80211_new_state(ic, 4103*fd43cf6eSHans Rosenfeld IEEE80211_S_SCAN, -1); 4104*fd43cf6eSHans Rosenfeld } 4105*fd43cf6eSHans Rosenfeld } 4106*fd43cf6eSHans Rosenfeld } 4107*fd43cf6eSHans Rosenfeld } 4108*fd43cf6eSHans Rosenfeld 4109*fd43cf6eSHans Rosenfeld /* 4110*fd43cf6eSHans Rosenfeld * Call back functions for get/set property 4111*fd43cf6eSHans Rosenfeld */ 4112*fd43cf6eSHans Rosenfeld static int 4113*fd43cf6eSHans Rosenfeld iwn_m_getprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num, 4114*fd43cf6eSHans Rosenfeld uint_t wldp_length, void *wldp_buf) 4115*fd43cf6eSHans Rosenfeld { 4116*fd43cf6eSHans Rosenfeld struct iwn_softc *sc; 4117*fd43cf6eSHans Rosenfeld 4118*fd43cf6eSHans Rosenfeld sc = (struct iwn_softc *)arg; 4119*fd43cf6eSHans Rosenfeld ASSERT(sc != NULL); 4120*fd43cf6eSHans Rosenfeld 4121*fd43cf6eSHans Rosenfeld return (ieee80211_getprop(&sc->sc_ic, pr_name, wldp_pr_num, 4122*fd43cf6eSHans Rosenfeld wldp_length, wldp_buf)); 4123*fd43cf6eSHans Rosenfeld } 4124*fd43cf6eSHans Rosenfeld 4125*fd43cf6eSHans Rosenfeld static void 4126*fd43cf6eSHans Rosenfeld iwn_m_propinfo(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num, 4127*fd43cf6eSHans Rosenfeld mac_prop_info_handle_t prh) 4128*fd43cf6eSHans Rosenfeld { 4129*fd43cf6eSHans Rosenfeld struct iwn_softc *sc; 4130*fd43cf6eSHans Rosenfeld 4131*fd43cf6eSHans Rosenfeld sc = (struct iwn_softc *)arg; 4132*fd43cf6eSHans Rosenfeld ASSERT(sc != NULL); 4133*fd43cf6eSHans Rosenfeld 4134*fd43cf6eSHans Rosenfeld ieee80211_propinfo(&sc->sc_ic, pr_name, wldp_pr_num, prh); 4135*fd43cf6eSHans Rosenfeld } 4136*fd43cf6eSHans Rosenfeld 4137*fd43cf6eSHans Rosenfeld static int 4138*fd43cf6eSHans Rosenfeld iwn_m_setprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num, 4139*fd43cf6eSHans Rosenfeld uint_t wldp_length, const void *wldp_buf) 4140*fd43cf6eSHans Rosenfeld { 4141*fd43cf6eSHans Rosenfeld struct iwn_softc *sc; 4142*fd43cf6eSHans Rosenfeld ieee80211com_t *ic; 4143*fd43cf6eSHans Rosenfeld int err = EINVAL; 4144*fd43cf6eSHans Rosenfeld 4145*fd43cf6eSHans Rosenfeld sc = (struct iwn_softc *)arg; 4146*fd43cf6eSHans Rosenfeld ASSERT(sc != NULL); 4147*fd43cf6eSHans Rosenfeld ic = &sc->sc_ic; 4148*fd43cf6eSHans Rosenfeld 4149*fd43cf6eSHans Rosenfeld mutex_enter(&sc->sc_mtx); 4150*fd43cf6eSHans Rosenfeld while (sc->sc_flags & IWN_FLAG_SCANNING) 4151*fd43cf6eSHans Rosenfeld cv_wait(&sc->sc_scan_cv, &sc->sc_mtx); 4152*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_mtx); 4153*fd43cf6eSHans Rosenfeld 4154*fd43cf6eSHans Rosenfeld err = ieee80211_setprop(ic, pr_name, wldp_pr_num, wldp_length, 4155*fd43cf6eSHans Rosenfeld wldp_buf); 4156*fd43cf6eSHans Rosenfeld 4157*fd43cf6eSHans Rosenfeld if (err == ENETRESET) { 4158*fd43cf6eSHans Rosenfeld if (ic->ic_des_esslen) { 4159*fd43cf6eSHans Rosenfeld if (sc->sc_flags & IWN_FLAG_RUNNING) { 4160*fd43cf6eSHans Rosenfeld DTRACE_PROBE(netreset); 4161*fd43cf6eSHans Rosenfeld iwn_m_stop(sc); 4162*fd43cf6eSHans Rosenfeld (void) iwn_m_start(sc); 4163*fd43cf6eSHans Rosenfeld (void) ieee80211_new_state(ic, 4164*fd43cf6eSHans Rosenfeld IEEE80211_S_SCAN, -1); 4165*fd43cf6eSHans Rosenfeld } 4166*fd43cf6eSHans Rosenfeld } 4167*fd43cf6eSHans Rosenfeld err = 0; 4168*fd43cf6eSHans Rosenfeld } 4169*fd43cf6eSHans Rosenfeld 4170*fd43cf6eSHans Rosenfeld return (err); 4171*fd43cf6eSHans Rosenfeld } 4172*fd43cf6eSHans Rosenfeld 4173*fd43cf6eSHans Rosenfeld /* 4174*fd43cf6eSHans Rosenfeld * invoked by GLD get statistics from NIC and driver 4175*fd43cf6eSHans Rosenfeld */ 4176*fd43cf6eSHans Rosenfeld static int 4177*fd43cf6eSHans Rosenfeld iwn_m_stat(void *arg, uint_t stat, uint64_t *val) 4178*fd43cf6eSHans Rosenfeld { 4179*fd43cf6eSHans Rosenfeld struct iwn_softc *sc; 4180*fd43cf6eSHans Rosenfeld ieee80211com_t *ic; 4181*fd43cf6eSHans Rosenfeld ieee80211_node_t *in; 4182*fd43cf6eSHans Rosenfeld 4183*fd43cf6eSHans Rosenfeld sc = (struct iwn_softc *)arg; 4184*fd43cf6eSHans Rosenfeld ASSERT(sc != NULL); 4185*fd43cf6eSHans Rosenfeld ic = &sc->sc_ic; 4186*fd43cf6eSHans Rosenfeld 4187*fd43cf6eSHans Rosenfeld mutex_enter(&sc->sc_mtx); 4188*fd43cf6eSHans Rosenfeld 4189*fd43cf6eSHans Rosenfeld switch (stat) { 4190*fd43cf6eSHans Rosenfeld case MAC_STAT_IFSPEED: 4191*fd43cf6eSHans Rosenfeld in = ic->ic_bss; 4192*fd43cf6eSHans Rosenfeld *val = ((IEEE80211_FIXED_RATE_NONE == ic->ic_fixed_rate) ? 4193*fd43cf6eSHans Rosenfeld IEEE80211_RATE(in->in_txrate) : 4194*fd43cf6eSHans Rosenfeld ic->ic_fixed_rate) / 2 * 1000000; 4195*fd43cf6eSHans Rosenfeld break; 4196*fd43cf6eSHans Rosenfeld case MAC_STAT_NOXMTBUF: 4197*fd43cf6eSHans Rosenfeld *val = sc->sc_tx_nobuf; 4198*fd43cf6eSHans Rosenfeld break; 4199*fd43cf6eSHans Rosenfeld case MAC_STAT_NORCVBUF: 4200*fd43cf6eSHans Rosenfeld *val = sc->sc_rx_nobuf; 4201*fd43cf6eSHans Rosenfeld break; 4202*fd43cf6eSHans Rosenfeld case MAC_STAT_IERRORS: 4203*fd43cf6eSHans Rosenfeld *val = sc->sc_rx_err; 4204*fd43cf6eSHans Rosenfeld break; 4205*fd43cf6eSHans Rosenfeld case MAC_STAT_RBYTES: 4206*fd43cf6eSHans Rosenfeld *val = ic->ic_stats.is_rx_bytes; 4207*fd43cf6eSHans Rosenfeld break; 4208*fd43cf6eSHans Rosenfeld case MAC_STAT_IPACKETS: 4209*fd43cf6eSHans Rosenfeld *val = ic->ic_stats.is_rx_frags; 4210*fd43cf6eSHans Rosenfeld break; 4211*fd43cf6eSHans Rosenfeld case MAC_STAT_OBYTES: 4212*fd43cf6eSHans Rosenfeld *val = ic->ic_stats.is_tx_bytes; 4213*fd43cf6eSHans Rosenfeld break; 4214*fd43cf6eSHans Rosenfeld case MAC_STAT_OPACKETS: 4215*fd43cf6eSHans Rosenfeld *val = ic->ic_stats.is_tx_frags; 4216*fd43cf6eSHans Rosenfeld break; 4217*fd43cf6eSHans Rosenfeld case MAC_STAT_OERRORS: 4218*fd43cf6eSHans Rosenfeld case WIFI_STAT_TX_FAILED: 4219*fd43cf6eSHans Rosenfeld *val = sc->sc_tx_err; 4220*fd43cf6eSHans Rosenfeld break; 4221*fd43cf6eSHans Rosenfeld case WIFI_STAT_TX_RETRANS: 4222*fd43cf6eSHans Rosenfeld *val = sc->sc_tx_retries; 4223*fd43cf6eSHans Rosenfeld break; 4224*fd43cf6eSHans Rosenfeld case WIFI_STAT_FCS_ERRORS: 4225*fd43cf6eSHans Rosenfeld case WIFI_STAT_WEP_ERRORS: 4226*fd43cf6eSHans Rosenfeld case WIFI_STAT_TX_FRAGS: 4227*fd43cf6eSHans Rosenfeld case WIFI_STAT_MCAST_TX: 4228*fd43cf6eSHans Rosenfeld case WIFI_STAT_RTS_SUCCESS: 4229*fd43cf6eSHans Rosenfeld case WIFI_STAT_RTS_FAILURE: 4230*fd43cf6eSHans Rosenfeld case WIFI_STAT_ACK_FAILURE: 4231*fd43cf6eSHans Rosenfeld case WIFI_STAT_RX_FRAGS: 4232*fd43cf6eSHans Rosenfeld case WIFI_STAT_MCAST_RX: 4233*fd43cf6eSHans Rosenfeld case WIFI_STAT_RX_DUPS: 4234*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_mtx); 4235*fd43cf6eSHans Rosenfeld return (ieee80211_stat(ic, stat, val)); 4236*fd43cf6eSHans Rosenfeld default: 4237*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_mtx); 4238*fd43cf6eSHans Rosenfeld return (ENOTSUP); 4239*fd43cf6eSHans Rosenfeld } 4240*fd43cf6eSHans Rosenfeld 4241*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_mtx); 4242*fd43cf6eSHans Rosenfeld 4243*fd43cf6eSHans Rosenfeld return (0); 4244*fd43cf6eSHans Rosenfeld 4245*fd43cf6eSHans Rosenfeld } 4246*fd43cf6eSHans Rosenfeld 4247*fd43cf6eSHans Rosenfeld /* 4248*fd43cf6eSHans Rosenfeld * invoked by GLD to configure NIC 4249*fd43cf6eSHans Rosenfeld */ 4250*fd43cf6eSHans Rosenfeld static int 4251*fd43cf6eSHans Rosenfeld iwn_m_unicst(void *arg, const uint8_t *macaddr) 4252*fd43cf6eSHans Rosenfeld { 4253*fd43cf6eSHans Rosenfeld struct iwn_softc *sc; 4254*fd43cf6eSHans Rosenfeld ieee80211com_t *ic; 4255*fd43cf6eSHans Rosenfeld int err = IWN_SUCCESS; 4256*fd43cf6eSHans Rosenfeld 4257*fd43cf6eSHans Rosenfeld sc = (struct iwn_softc *)arg; 4258*fd43cf6eSHans Rosenfeld ASSERT(sc != NULL); 4259*fd43cf6eSHans Rosenfeld ic = &sc->sc_ic; 4260*fd43cf6eSHans Rosenfeld 4261*fd43cf6eSHans Rosenfeld if (!IEEE80211_ADDR_EQ(ic->ic_macaddr, macaddr)) { 4262*fd43cf6eSHans Rosenfeld mutex_enter(&sc->sc_mtx); 4263*fd43cf6eSHans Rosenfeld IEEE80211_ADDR_COPY(ic->ic_macaddr, macaddr); 4264*fd43cf6eSHans Rosenfeld err = iwn_config(sc); 4265*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_mtx); 4266*fd43cf6eSHans Rosenfeld if (err != IWN_SUCCESS) { 4267*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, "!iwn_m_unicst(): " 4268*fd43cf6eSHans Rosenfeld "failed to configure device"); 4269*fd43cf6eSHans Rosenfeld goto fail; 4270*fd43cf6eSHans Rosenfeld } 4271*fd43cf6eSHans Rosenfeld } 4272*fd43cf6eSHans Rosenfeld 4273*fd43cf6eSHans Rosenfeld return (err); 4274*fd43cf6eSHans Rosenfeld 4275*fd43cf6eSHans Rosenfeld fail: 4276*fd43cf6eSHans Rosenfeld return (err); 4277*fd43cf6eSHans Rosenfeld } 4278*fd43cf6eSHans Rosenfeld 4279*fd43cf6eSHans Rosenfeld /*ARGSUSED*/ 4280*fd43cf6eSHans Rosenfeld static int 4281*fd43cf6eSHans Rosenfeld iwn_m_multicst(void *arg, boolean_t add, const uint8_t *m) 4282*fd43cf6eSHans Rosenfeld { 4283*fd43cf6eSHans Rosenfeld return (IWN_SUCCESS); 4284*fd43cf6eSHans Rosenfeld } 4285*fd43cf6eSHans Rosenfeld 4286*fd43cf6eSHans Rosenfeld /*ARGSUSED*/ 4287*fd43cf6eSHans Rosenfeld static int 4288*fd43cf6eSHans Rosenfeld iwn_m_promisc(void *arg, boolean_t on) 4289*fd43cf6eSHans Rosenfeld { 4290*fd43cf6eSHans Rosenfeld _NOTE(ARGUNUSED(on)); 4291*fd43cf6eSHans Rosenfeld 4292*fd43cf6eSHans Rosenfeld return (IWN_SUCCESS); 4293*fd43cf6eSHans Rosenfeld } 4294*fd43cf6eSHans Rosenfeld 4295*fd43cf6eSHans Rosenfeld static void 4296*fd43cf6eSHans Rosenfeld iwn_abort_scan(void *arg) 4297*fd43cf6eSHans Rosenfeld { 4298*fd43cf6eSHans Rosenfeld struct iwn_softc *sc = (struct iwn_softc *)arg; 4299*fd43cf6eSHans Rosenfeld ieee80211com_t *ic = &sc->sc_ic; 4300*fd43cf6eSHans Rosenfeld 4301*fd43cf6eSHans Rosenfeld mutex_enter(&sc->sc_mtx); 4302*fd43cf6eSHans Rosenfeld if ((sc->sc_flags & IWN_FLAG_SCANNING) == 0) 4303*fd43cf6eSHans Rosenfeld return; 4304*fd43cf6eSHans Rosenfeld 4305*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 4306*fd43cf6eSHans Rosenfeld "!aborting scan, flags = %x, state = %s", 4307*fd43cf6eSHans Rosenfeld sc->sc_flags, ieee80211_state_name[ic->ic_state]); 4308*fd43cf6eSHans Rosenfeld sc->sc_flags &= ~IWN_FLAG_SCANNING; 4309*fd43cf6eSHans Rosenfeld iwn_hw_stop(sc, B_FALSE); 4310*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_mtx); 4311*fd43cf6eSHans Rosenfeld 4312*fd43cf6eSHans Rosenfeld sc->scan_to = 0; 4313*fd43cf6eSHans Rosenfeld (void) iwn_init(sc); 4314*fd43cf6eSHans Rosenfeld ieee80211_new_state(ic, IEEE80211_S_INIT, -1); 4315*fd43cf6eSHans Rosenfeld } 4316*fd43cf6eSHans Rosenfeld 4317*fd43cf6eSHans Rosenfeld /* 4318*fd43cf6eSHans Rosenfeld * periodic function to deal with RF switch and HW error recovery 4319*fd43cf6eSHans Rosenfeld */ 4320*fd43cf6eSHans Rosenfeld static void 4321*fd43cf6eSHans Rosenfeld iwn_periodic(void *arg) 4322*fd43cf6eSHans Rosenfeld { 4323*fd43cf6eSHans Rosenfeld struct iwn_softc *sc = (struct iwn_softc *)arg; 4324*fd43cf6eSHans Rosenfeld ieee80211com_t *ic = &sc->sc_ic; 4325*fd43cf6eSHans Rosenfeld int err; 4326*fd43cf6eSHans Rosenfeld uint32_t tmp; 4327*fd43cf6eSHans Rosenfeld 4328*fd43cf6eSHans Rosenfeld mutex_enter(&sc->sc_mtx); 4329*fd43cf6eSHans Rosenfeld tmp = IWN_READ(sc, IWN_GP_CNTRL); 4330*fd43cf6eSHans Rosenfeld if (tmp & IWN_GP_CNTRL_RFKILL) { 4331*fd43cf6eSHans Rosenfeld sc->sc_flags &= ~IWN_FLAG_RADIO_OFF; 4332*fd43cf6eSHans Rosenfeld } else { 4333*fd43cf6eSHans Rosenfeld sc->sc_flags |= IWN_FLAG_RADIO_OFF; 4334*fd43cf6eSHans Rosenfeld } 4335*fd43cf6eSHans Rosenfeld 4336*fd43cf6eSHans Rosenfeld /* 4337*fd43cf6eSHans Rosenfeld * If the RF is OFF, do nothing. 4338*fd43cf6eSHans Rosenfeld */ 4339*fd43cf6eSHans Rosenfeld if (sc->sc_flags & IWN_FLAG_RADIO_OFF) { 4340*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_mtx); 4341*fd43cf6eSHans Rosenfeld return; 4342*fd43cf6eSHans Rosenfeld } 4343*fd43cf6eSHans Rosenfeld 4344*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_mtx); 4345*fd43cf6eSHans Rosenfeld 4346*fd43cf6eSHans Rosenfeld /* 4347*fd43cf6eSHans Rosenfeld * recovery fatal error 4348*fd43cf6eSHans Rosenfeld */ 4349*fd43cf6eSHans Rosenfeld if (ic->ic_mach && 4350*fd43cf6eSHans Rosenfeld (sc->sc_flags & IWN_FLAG_HW_ERR_RECOVER)) { 4351*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 4352*fd43cf6eSHans Rosenfeld "!trying to restore previous state"); 4353*fd43cf6eSHans Rosenfeld 4354*fd43cf6eSHans Rosenfeld mutex_enter(&sc->sc_mtx); 4355*fd43cf6eSHans Rosenfeld sc->sc_flags |= IWN_FLAG_STOP_CALIB_TO; 4356*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_mtx); 4357*fd43cf6eSHans Rosenfeld 4358*fd43cf6eSHans Rosenfeld if (sc->calib_to != 0) 4359*fd43cf6eSHans Rosenfeld (void) untimeout(sc->calib_to); 4360*fd43cf6eSHans Rosenfeld sc->calib_to = 0; 4361*fd43cf6eSHans Rosenfeld 4362*fd43cf6eSHans Rosenfeld if (sc->scan_to != 0) 4363*fd43cf6eSHans Rosenfeld (void) untimeout(sc->scan_to); 4364*fd43cf6eSHans Rosenfeld sc->scan_to = 0; 4365*fd43cf6eSHans Rosenfeld 4366*fd43cf6eSHans Rosenfeld iwn_hw_stop(sc, B_TRUE); 4367*fd43cf6eSHans Rosenfeld 4368*fd43cf6eSHans Rosenfeld if (IWN_CHK_FAST_RECOVER(sc)) { 4369*fd43cf6eSHans Rosenfeld /* save runtime configuration */ 4370*fd43cf6eSHans Rosenfeld bcopy(&sc->rxon, &sc->rxon_save, sizeof (sc->rxon)); 4371*fd43cf6eSHans Rosenfeld } else { 4372*fd43cf6eSHans Rosenfeld ieee80211_new_state(ic, IEEE80211_S_INIT, -1); 4373*fd43cf6eSHans Rosenfeld } 4374*fd43cf6eSHans Rosenfeld 4375*fd43cf6eSHans Rosenfeld err = iwn_init(sc); 4376*fd43cf6eSHans Rosenfeld if (err != IWN_SUCCESS) 4377*fd43cf6eSHans Rosenfeld return; 4378*fd43cf6eSHans Rosenfeld 4379*fd43cf6eSHans Rosenfeld mutex_enter(&sc->sc_mtx); 4380*fd43cf6eSHans Rosenfeld sc->sc_flags |= IWN_FLAG_RUNNING; 4381*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_mtx); 4382*fd43cf6eSHans Rosenfeld 4383*fd43cf6eSHans Rosenfeld if (!IWN_CHK_FAST_RECOVER(sc) || 4384*fd43cf6eSHans Rosenfeld iwn_fast_recover(sc) != IWN_SUCCESS) { 4385*fd43cf6eSHans Rosenfeld mutex_enter(&sc->sc_mtx); 4386*fd43cf6eSHans Rosenfeld sc->sc_flags &= ~IWN_FLAG_HW_ERR_RECOVER; 4387*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_mtx); 4388*fd43cf6eSHans Rosenfeld if (sc->sc_ostate != IEEE80211_S_INIT) { 4389*fd43cf6eSHans Rosenfeld ieee80211_new_state(ic, IEEE80211_S_SCAN, 0); 4390*fd43cf6eSHans Rosenfeld } 4391*fd43cf6eSHans Rosenfeld } 4392*fd43cf6eSHans Rosenfeld } 4393*fd43cf6eSHans Rosenfeld } 4394*fd43cf6eSHans Rosenfeld 4395*fd43cf6eSHans Rosenfeld /* 4396*fd43cf6eSHans Rosenfeld * Send a command to the firmware. 4397*fd43cf6eSHans Rosenfeld */ 4398*fd43cf6eSHans Rosenfeld static int 4399*fd43cf6eSHans Rosenfeld iwn_cmd(struct iwn_softc *sc, uint8_t code, void *buf, int size, int async) 4400*fd43cf6eSHans Rosenfeld { 4401*fd43cf6eSHans Rosenfeld struct iwn_tx_ring *ring = &sc->txq[IWN_CMD_QUEUE_NUM]; 4402*fd43cf6eSHans Rosenfeld struct iwn_tx_desc *desc; 4403*fd43cf6eSHans Rosenfeld struct iwn_tx_data *data; 4404*fd43cf6eSHans Rosenfeld struct iwn_tx_cmd *cmd; 4405*fd43cf6eSHans Rosenfeld clock_t clk; 4406*fd43cf6eSHans Rosenfeld uintptr_t paddr; 4407*fd43cf6eSHans Rosenfeld int totlen, ret; 4408*fd43cf6eSHans Rosenfeld 4409*fd43cf6eSHans Rosenfeld ASSERT(mutex_owned(&sc->sc_mtx)); 4410*fd43cf6eSHans Rosenfeld 4411*fd43cf6eSHans Rosenfeld desc = &ring->desc[ring->cur]; 4412*fd43cf6eSHans Rosenfeld data = &ring->data[ring->cur]; 4413*fd43cf6eSHans Rosenfeld totlen = 4 + size; 4414*fd43cf6eSHans Rosenfeld 4415*fd43cf6eSHans Rosenfeld if (size > sizeof (cmd->data)) { 4416*fd43cf6eSHans Rosenfeld /* Command is too large to fit in a descriptor. */ 4417*fd43cf6eSHans Rosenfeld if (iwn_dma_contig_alloc(sc, &data->cmd_dma, totlen, 4418*fd43cf6eSHans Rosenfeld DDI_DMA_CONSISTENT | DDI_DMA_RDWR, (void **)&cmd, 4419*fd43cf6eSHans Rosenfeld &iwn_dma_accattr, 1) != DDI_SUCCESS) 4420*fd43cf6eSHans Rosenfeld return ENOBUFS; 4421*fd43cf6eSHans Rosenfeld paddr = data->cmd_dma.paddr; 4422*fd43cf6eSHans Rosenfeld } else { 4423*fd43cf6eSHans Rosenfeld cmd = &ring->cmd[ring->cur]; 4424*fd43cf6eSHans Rosenfeld paddr = data->cmd_paddr; 4425*fd43cf6eSHans Rosenfeld } 4426*fd43cf6eSHans Rosenfeld 4427*fd43cf6eSHans Rosenfeld cmd->code = code; 4428*fd43cf6eSHans Rosenfeld cmd->flags = 0; 4429*fd43cf6eSHans Rosenfeld cmd->qid = ring->qid; 4430*fd43cf6eSHans Rosenfeld cmd->idx = ring->cur; 4431*fd43cf6eSHans Rosenfeld bzero(cmd->data, size); 4432*fd43cf6eSHans Rosenfeld memcpy(cmd->data, buf, size); 4433*fd43cf6eSHans Rosenfeld 4434*fd43cf6eSHans Rosenfeld bzero(desc, sizeof(*desc)); 4435*fd43cf6eSHans Rosenfeld desc->nsegs = 1; 4436*fd43cf6eSHans Rosenfeld desc->segs[0].addr = htole32(IWN_LOADDR(paddr)); 4437*fd43cf6eSHans Rosenfeld desc->segs[0].len = htole16(IWN_HIADDR(paddr) | totlen << 4); 4438*fd43cf6eSHans Rosenfeld 4439*fd43cf6eSHans Rosenfeld if (size > sizeof cmd->data) { 4440*fd43cf6eSHans Rosenfeld (void) ddi_dma_sync(data->cmd_dma.dma_hdl, 0, totlen, 4441*fd43cf6eSHans Rosenfeld DDI_DMA_SYNC_FORDEV); 4442*fd43cf6eSHans Rosenfeld } else { 4443*fd43cf6eSHans Rosenfeld (void) ddi_dma_sync(ring->cmd_dma.dma_hdl, 4444*fd43cf6eSHans Rosenfeld ring->cur * sizeof (*cmd), 4445*fd43cf6eSHans Rosenfeld totlen, DDI_DMA_SYNC_FORDEV); 4446*fd43cf6eSHans Rosenfeld } 4447*fd43cf6eSHans Rosenfeld (void) ddi_dma_sync(ring->desc_dma.dma_hdl, 4448*fd43cf6eSHans Rosenfeld ring->cur * sizeof (*desc), 4449*fd43cf6eSHans Rosenfeld sizeof (*desc), DDI_DMA_SYNC_FORDEV); 4450*fd43cf6eSHans Rosenfeld 4451*fd43cf6eSHans Rosenfeld /* Update TX scheduler. */ 4452*fd43cf6eSHans Rosenfeld sc->ops.update_sched(sc, ring->qid, ring->cur, 0, 0); 4453*fd43cf6eSHans Rosenfeld 4454*fd43cf6eSHans Rosenfeld /* Kick command ring. */ 4455*fd43cf6eSHans Rosenfeld ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT; 4456*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur); 4457*fd43cf6eSHans Rosenfeld 4458*fd43cf6eSHans Rosenfeld if (async) 4459*fd43cf6eSHans Rosenfeld return (IWN_SUCCESS); 4460*fd43cf6eSHans Rosenfeld 4461*fd43cf6eSHans Rosenfeld sc->sc_cmd_flag = SC_CMD_FLG_NONE; 4462*fd43cf6eSHans Rosenfeld clk = ddi_get_lbolt() + drv_usectohz(2000000); 4463*fd43cf6eSHans Rosenfeld while (sc->sc_cmd_flag != SC_CMD_FLG_DONE) 4464*fd43cf6eSHans Rosenfeld if (cv_timedwait(&sc->sc_cmd_cv, &sc->sc_mtx, clk) < 0) 4465*fd43cf6eSHans Rosenfeld break; 4466*fd43cf6eSHans Rosenfeld 4467*fd43cf6eSHans Rosenfeld ret = (sc->sc_cmd_flag == SC_CMD_FLG_DONE) ? IWN_SUCCESS : IWN_FAIL; 4468*fd43cf6eSHans Rosenfeld sc->sc_cmd_flag = SC_CMD_FLG_NONE; 4469*fd43cf6eSHans Rosenfeld 4470*fd43cf6eSHans Rosenfeld return (ret); 4471*fd43cf6eSHans Rosenfeld } 4472*fd43cf6eSHans Rosenfeld 4473*fd43cf6eSHans Rosenfeld static int 4474*fd43cf6eSHans Rosenfeld iwn4965_add_node(struct iwn_softc *sc, struct iwn_node_info *node, int async) 4475*fd43cf6eSHans Rosenfeld { 4476*fd43cf6eSHans Rosenfeld struct iwn4965_node_info hnode; 4477*fd43cf6eSHans Rosenfeld char *src, *dst; 4478*fd43cf6eSHans Rosenfeld 4479*fd43cf6eSHans Rosenfeld /* 4480*fd43cf6eSHans Rosenfeld * We use the node structure for 5000 Series internally (it is 4481*fd43cf6eSHans Rosenfeld * a superset of the one for 4965AGN). We thus copy the common 4482*fd43cf6eSHans Rosenfeld * fields before sending the command. 4483*fd43cf6eSHans Rosenfeld */ 4484*fd43cf6eSHans Rosenfeld src = (char *)node; 4485*fd43cf6eSHans Rosenfeld dst = (char *)&hnode; 4486*fd43cf6eSHans Rosenfeld memcpy(dst, src, 48); 4487*fd43cf6eSHans Rosenfeld /* Skip TSC, RX MIC and TX MIC fields from ``src''. */ 4488*fd43cf6eSHans Rosenfeld memcpy(dst + 48, src + 72, 20); 4489*fd43cf6eSHans Rosenfeld return iwn_cmd(sc, IWN_CMD_ADD_NODE, &hnode, sizeof hnode, async); 4490*fd43cf6eSHans Rosenfeld } 4491*fd43cf6eSHans Rosenfeld 4492*fd43cf6eSHans Rosenfeld static int 4493*fd43cf6eSHans Rosenfeld iwn5000_add_node(struct iwn_softc *sc, struct iwn_node_info *node, int async) 4494*fd43cf6eSHans Rosenfeld { 4495*fd43cf6eSHans Rosenfeld /* Direct mapping. */ 4496*fd43cf6eSHans Rosenfeld return iwn_cmd(sc, IWN_CMD_ADD_NODE, node, sizeof (*node), async); 4497*fd43cf6eSHans Rosenfeld } 4498*fd43cf6eSHans Rosenfeld 4499*fd43cf6eSHans Rosenfeld static int 4500*fd43cf6eSHans Rosenfeld iwn_set_link_quality(struct iwn_softc *sc, struct ieee80211_node *ni) 4501*fd43cf6eSHans Rosenfeld { 4502*fd43cf6eSHans Rosenfeld struct iwn_node *wn = (void *)ni; 4503*fd43cf6eSHans Rosenfeld struct ieee80211_rateset *rs = &ni->in_rates; 4504*fd43cf6eSHans Rosenfeld struct iwn_cmd_link_quality linkq; 4505*fd43cf6eSHans Rosenfeld const struct iwn_rate *rinfo; 4506*fd43cf6eSHans Rosenfeld uint8_t txant; 4507*fd43cf6eSHans Rosenfeld int i, txrate; 4508*fd43cf6eSHans Rosenfeld 4509*fd43cf6eSHans Rosenfeld /* Use the first valid TX antenna. */ 4510*fd43cf6eSHans Rosenfeld txant = IWN_LSB(sc->txchainmask); 4511*fd43cf6eSHans Rosenfeld 4512*fd43cf6eSHans Rosenfeld memset(&linkq, 0, sizeof linkq); 4513*fd43cf6eSHans Rosenfeld linkq.id = wn->id; 4514*fd43cf6eSHans Rosenfeld linkq.antmsk_1stream = txant; 4515*fd43cf6eSHans Rosenfeld linkq.antmsk_2stream = IWN_ANT_AB; 4516*fd43cf6eSHans Rosenfeld linkq.ampdu_max = 31; 4517*fd43cf6eSHans Rosenfeld linkq.ampdu_threshold = 3; 4518*fd43cf6eSHans Rosenfeld linkq.ampdu_limit = htole16(4000); /* 4ms */ 4519*fd43cf6eSHans Rosenfeld 4520*fd43cf6eSHans Rosenfeld /* Start at highest available bit-rate. */ 4521*fd43cf6eSHans Rosenfeld txrate = rs->ir_nrates - 1; 4522*fd43cf6eSHans Rosenfeld for (i = 0; i < IWN_MAX_TX_RETRIES; i++) { 4523*fd43cf6eSHans Rosenfeld rinfo = &iwn_rates[wn->ridx[txrate]]; 4524*fd43cf6eSHans Rosenfeld linkq.retry[i].plcp = rinfo->plcp; 4525*fd43cf6eSHans Rosenfeld linkq.retry[i].rflags = rinfo->flags; 4526*fd43cf6eSHans Rosenfeld linkq.retry[i].rflags |= IWN_RFLAG_ANT(txant); 4527*fd43cf6eSHans Rosenfeld /* Next retry at immediate lower bit-rate. */ 4528*fd43cf6eSHans Rosenfeld if (txrate > 0) 4529*fd43cf6eSHans Rosenfeld txrate--; 4530*fd43cf6eSHans Rosenfeld } 4531*fd43cf6eSHans Rosenfeld return iwn_cmd(sc, IWN_CMD_LINK_QUALITY, &linkq, sizeof linkq, 1); 4532*fd43cf6eSHans Rosenfeld } 4533*fd43cf6eSHans Rosenfeld 4534*fd43cf6eSHans Rosenfeld /* 4535*fd43cf6eSHans Rosenfeld * Broadcast node is used to send group-addressed and management frames. 4536*fd43cf6eSHans Rosenfeld */ 4537*fd43cf6eSHans Rosenfeld static int 4538*fd43cf6eSHans Rosenfeld iwn_add_broadcast_node(struct iwn_softc *sc, int async) 4539*fd43cf6eSHans Rosenfeld { 4540*fd43cf6eSHans Rosenfeld struct iwn_ops *ops = &sc->ops; 4541*fd43cf6eSHans Rosenfeld struct iwn_node_info node; 4542*fd43cf6eSHans Rosenfeld struct iwn_cmd_link_quality linkq; 4543*fd43cf6eSHans Rosenfeld const struct iwn_rate *rinfo; 4544*fd43cf6eSHans Rosenfeld uint8_t txant; 4545*fd43cf6eSHans Rosenfeld int i, error; 4546*fd43cf6eSHans Rosenfeld 4547*fd43cf6eSHans Rosenfeld memset(&node, 0, sizeof node); 4548*fd43cf6eSHans Rosenfeld IEEE80211_ADDR_COPY(node.macaddr, etherbroadcastaddr); 4549*fd43cf6eSHans Rosenfeld node.id = sc->broadcast_id; 4550*fd43cf6eSHans Rosenfeld DTRACE_PROBE(add__broadcast__node); 4551*fd43cf6eSHans Rosenfeld if ((error = ops->add_node(sc, &node, async)) != 0) 4552*fd43cf6eSHans Rosenfeld return error; 4553*fd43cf6eSHans Rosenfeld 4554*fd43cf6eSHans Rosenfeld /* Use the first valid TX antenna. */ 4555*fd43cf6eSHans Rosenfeld txant = IWN_LSB(sc->txchainmask); 4556*fd43cf6eSHans Rosenfeld 4557*fd43cf6eSHans Rosenfeld memset(&linkq, 0, sizeof linkq); 4558*fd43cf6eSHans Rosenfeld linkq.id = sc->broadcast_id; 4559*fd43cf6eSHans Rosenfeld linkq.antmsk_1stream = txant; 4560*fd43cf6eSHans Rosenfeld linkq.antmsk_2stream = IWN_ANT_AB; 4561*fd43cf6eSHans Rosenfeld linkq.ampdu_max = 64; 4562*fd43cf6eSHans Rosenfeld linkq.ampdu_threshold = 3; 4563*fd43cf6eSHans Rosenfeld linkq.ampdu_limit = htole16(4000); /* 4ms */ 4564*fd43cf6eSHans Rosenfeld 4565*fd43cf6eSHans Rosenfeld /* Use lowest mandatory bit-rate. */ 4566*fd43cf6eSHans Rosenfeld rinfo = (sc->sc_ic.ic_curmode != IEEE80211_MODE_11A) ? 4567*fd43cf6eSHans Rosenfeld &iwn_rates[IWN_RIDX_CCK1] : &iwn_rates[IWN_RIDX_OFDM6]; 4568*fd43cf6eSHans Rosenfeld linkq.retry[0].plcp = rinfo->plcp; 4569*fd43cf6eSHans Rosenfeld linkq.retry[0].rflags = rinfo->flags; 4570*fd43cf6eSHans Rosenfeld linkq.retry[0].rflags |= IWN_RFLAG_ANT(txant); 4571*fd43cf6eSHans Rosenfeld /* Use same bit-rate for all TX retries. */ 4572*fd43cf6eSHans Rosenfeld for (i = 1; i < IWN_MAX_TX_RETRIES; i++) { 4573*fd43cf6eSHans Rosenfeld linkq.retry[i].plcp = linkq.retry[0].plcp; 4574*fd43cf6eSHans Rosenfeld linkq.retry[i].rflags = linkq.retry[0].rflags; 4575*fd43cf6eSHans Rosenfeld } 4576*fd43cf6eSHans Rosenfeld return iwn_cmd(sc, IWN_CMD_LINK_QUALITY, &linkq, sizeof linkq, async); 4577*fd43cf6eSHans Rosenfeld } 4578*fd43cf6eSHans Rosenfeld 4579*fd43cf6eSHans Rosenfeld static void 4580*fd43cf6eSHans Rosenfeld iwn_set_led(struct iwn_softc *sc, uint8_t which, uint8_t off, uint8_t on) 4581*fd43cf6eSHans Rosenfeld { 4582*fd43cf6eSHans Rosenfeld struct iwn_cmd_led led; 4583*fd43cf6eSHans Rosenfeld 4584*fd43cf6eSHans Rosenfeld /* Clear microcode LED ownership. */ 4585*fd43cf6eSHans Rosenfeld IWN_CLRBITS(sc, IWN_LED, IWN_LED_BSM_CTRL); 4586*fd43cf6eSHans Rosenfeld 4587*fd43cf6eSHans Rosenfeld led.which = which; 4588*fd43cf6eSHans Rosenfeld led.unit = htole32(10000); /* on/off in unit of 100ms */ 4589*fd43cf6eSHans Rosenfeld led.off = off; 4590*fd43cf6eSHans Rosenfeld led.on = on; 4591*fd43cf6eSHans Rosenfeld DTRACE_PROBE1(led__change, const char *, 4592*fd43cf6eSHans Rosenfeld (off != 0 && on != 0) ? "blinking" : 4593*fd43cf6eSHans Rosenfeld (off != 0) ? "off" : "on"); 4594*fd43cf6eSHans Rosenfeld (void)iwn_cmd(sc, IWN_CMD_SET_LED, &led, sizeof led, 1); 4595*fd43cf6eSHans Rosenfeld } 4596*fd43cf6eSHans Rosenfeld 4597*fd43cf6eSHans Rosenfeld /* 4598*fd43cf6eSHans Rosenfeld * Set the critical temperature at which the firmware will stop the radio 4599*fd43cf6eSHans Rosenfeld * and notify us. 4600*fd43cf6eSHans Rosenfeld */ 4601*fd43cf6eSHans Rosenfeld static int 4602*fd43cf6eSHans Rosenfeld iwn_set_critical_temp(struct iwn_softc *sc) 4603*fd43cf6eSHans Rosenfeld { 4604*fd43cf6eSHans Rosenfeld struct iwn_critical_temp crit; 4605*fd43cf6eSHans Rosenfeld int32_t temp; 4606*fd43cf6eSHans Rosenfeld 4607*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_CTEMP_STOP_RF); 4608*fd43cf6eSHans Rosenfeld 4609*fd43cf6eSHans Rosenfeld if (sc->hw_type == IWN_HW_REV_TYPE_5150) 4610*fd43cf6eSHans Rosenfeld temp = (IWN_CTOK(110) - sc->temp_off) * -5; 4611*fd43cf6eSHans Rosenfeld else if (sc->hw_type == IWN_HW_REV_TYPE_4965) 4612*fd43cf6eSHans Rosenfeld temp = IWN_CTOK(110); 4613*fd43cf6eSHans Rosenfeld else 4614*fd43cf6eSHans Rosenfeld temp = 110; 4615*fd43cf6eSHans Rosenfeld 4616*fd43cf6eSHans Rosenfeld sc->sc_misc->crit_temp.value.ul = temp; 4617*fd43cf6eSHans Rosenfeld 4618*fd43cf6eSHans Rosenfeld memset(&crit, 0, sizeof crit); 4619*fd43cf6eSHans Rosenfeld crit.tempR = htole32(temp); 4620*fd43cf6eSHans Rosenfeld return iwn_cmd(sc, IWN_CMD_SET_CRITICAL_TEMP, &crit, sizeof crit, 0); 4621*fd43cf6eSHans Rosenfeld } 4622*fd43cf6eSHans Rosenfeld 4623*fd43cf6eSHans Rosenfeld static int 4624*fd43cf6eSHans Rosenfeld iwn_set_timing(struct iwn_softc *sc, struct ieee80211_node *ni) 4625*fd43cf6eSHans Rosenfeld { 4626*fd43cf6eSHans Rosenfeld struct iwn_cmd_timing cmd; 4627*fd43cf6eSHans Rosenfeld uint64_t val, mod; 4628*fd43cf6eSHans Rosenfeld 4629*fd43cf6eSHans Rosenfeld memset(&cmd, 0, sizeof cmd); 4630*fd43cf6eSHans Rosenfeld memcpy(&cmd.tstamp, ni->in_tstamp.data, sizeof (uint64_t)); 4631*fd43cf6eSHans Rosenfeld cmd.bintval = htole16(ni->in_intval); 4632*fd43cf6eSHans Rosenfeld cmd.lintval = htole16(10); 4633*fd43cf6eSHans Rosenfeld 4634*fd43cf6eSHans Rosenfeld /* Compute remaining time until next beacon. */ 4635*fd43cf6eSHans Rosenfeld val = (uint64_t)ni->in_intval * 1024; /* msecs -> usecs */ 4636*fd43cf6eSHans Rosenfeld mod = le64toh(cmd.tstamp) % val; 4637*fd43cf6eSHans Rosenfeld cmd.binitval = htole32((uint32_t)(val - mod)); 4638*fd43cf6eSHans Rosenfeld 4639*fd43cf6eSHans Rosenfeld sc->sc_timing->bintval.value.ul = ni->in_intval; 4640*fd43cf6eSHans Rosenfeld sc->sc_timing->tstamp.value.ul = ni->in_tstamp.tsf; 4641*fd43cf6eSHans Rosenfeld sc->sc_timing->init.value.ul = (uint32_t)(val - mod); 4642*fd43cf6eSHans Rosenfeld 4643*fd43cf6eSHans Rosenfeld return iwn_cmd(sc, IWN_CMD_TIMING, &cmd, sizeof cmd, 1); 4644*fd43cf6eSHans Rosenfeld } 4645*fd43cf6eSHans Rosenfeld 4646*fd43cf6eSHans Rosenfeld static void 4647*fd43cf6eSHans Rosenfeld iwn4965_power_calibration(struct iwn_softc *sc, int temp) 4648*fd43cf6eSHans Rosenfeld { 4649*fd43cf6eSHans Rosenfeld /* Adjust TX power if need be (delta >= 3 degC). */ 4650*fd43cf6eSHans Rosenfeld IWN_DBG("temperature %d->%d", sc->temp, temp); 4651*fd43cf6eSHans Rosenfeld if (abs(temp - sc->temp) >= 3) { 4652*fd43cf6eSHans Rosenfeld /* Record temperature of last calibration. */ 4653*fd43cf6eSHans Rosenfeld sc->temp = temp; 4654*fd43cf6eSHans Rosenfeld (void)iwn4965_set_txpower(sc, 1); 4655*fd43cf6eSHans Rosenfeld } 4656*fd43cf6eSHans Rosenfeld } 4657*fd43cf6eSHans Rosenfeld 4658*fd43cf6eSHans Rosenfeld /* 4659*fd43cf6eSHans Rosenfeld * Set TX power for current channel (each rate has its own power settings). 4660*fd43cf6eSHans Rosenfeld * This function takes into account the regulatory information from EEPROM, 4661*fd43cf6eSHans Rosenfeld * the current temperature and the current voltage. 4662*fd43cf6eSHans Rosenfeld */ 4663*fd43cf6eSHans Rosenfeld static int 4664*fd43cf6eSHans Rosenfeld iwn4965_set_txpower(struct iwn_softc *sc, int async) 4665*fd43cf6eSHans Rosenfeld { 4666*fd43cf6eSHans Rosenfeld /* Fixed-point arithmetic division using a n-bit fractional part. */ 4667*fd43cf6eSHans Rosenfeld #define fdivround(a, b, n) \ 4668*fd43cf6eSHans Rosenfeld ((((1 << n) * (a)) / (b) + (1 << n) / 2) / (1 << n)) 4669*fd43cf6eSHans Rosenfeld /* Linear interpolation. */ 4670*fd43cf6eSHans Rosenfeld #define interpolate(x, x1, y1, x2, y2, n) \ 4671*fd43cf6eSHans Rosenfeld ((y1) + fdivround(((int)(x) - (x1)) * ((y2) - (y1)), (x2) - (x1), n)) 4672*fd43cf6eSHans Rosenfeld 4673*fd43cf6eSHans Rosenfeld static const int tdiv[IWN_NATTEN_GROUPS] = { 9, 8, 8, 8, 6 }; 4674*fd43cf6eSHans Rosenfeld struct ieee80211com *ic = &sc->sc_ic; 4675*fd43cf6eSHans Rosenfeld struct iwn_ucode_info *uc = &sc->ucode_info; 4676*fd43cf6eSHans Rosenfeld struct ieee80211_channel *ch; 4677*fd43cf6eSHans Rosenfeld struct iwn4965_cmd_txpower cmd; 4678*fd43cf6eSHans Rosenfeld struct iwn4965_eeprom_chan_samples *chans; 4679*fd43cf6eSHans Rosenfeld const uint8_t *rf_gain, *dsp_gain; 4680*fd43cf6eSHans Rosenfeld int32_t vdiff, tdiff; 4681*fd43cf6eSHans Rosenfeld int i, c, grp, maxpwr; 4682*fd43cf6eSHans Rosenfeld uint8_t chan; 4683*fd43cf6eSHans Rosenfeld 4684*fd43cf6eSHans Rosenfeld /* Retrieve current channel from last RXON. */ 4685*fd43cf6eSHans Rosenfeld chan = sc->rxon.chan; 4686*fd43cf6eSHans Rosenfeld sc->sc_txpower->chan.value.l = chan; 4687*fd43cf6eSHans Rosenfeld ch = &ic->ic_sup_channels[chan]; 4688*fd43cf6eSHans Rosenfeld 4689*fd43cf6eSHans Rosenfeld memset(&cmd, 0, sizeof cmd); 4690*fd43cf6eSHans Rosenfeld cmd.band = IEEE80211_IS_CHAN_5GHZ(ch) ? 0 : 1; 4691*fd43cf6eSHans Rosenfeld cmd.chan = chan; 4692*fd43cf6eSHans Rosenfeld 4693*fd43cf6eSHans Rosenfeld if (IEEE80211_IS_CHAN_5GHZ(ch)) { 4694*fd43cf6eSHans Rosenfeld maxpwr = sc->maxpwr5GHz; 4695*fd43cf6eSHans Rosenfeld rf_gain = iwn4965_rf_gain_5ghz; 4696*fd43cf6eSHans Rosenfeld dsp_gain = iwn4965_dsp_gain_5ghz; 4697*fd43cf6eSHans Rosenfeld } else { 4698*fd43cf6eSHans Rosenfeld maxpwr = sc->maxpwr2GHz; 4699*fd43cf6eSHans Rosenfeld rf_gain = iwn4965_rf_gain_2ghz; 4700*fd43cf6eSHans Rosenfeld dsp_gain = iwn4965_dsp_gain_2ghz; 4701*fd43cf6eSHans Rosenfeld } 4702*fd43cf6eSHans Rosenfeld 4703*fd43cf6eSHans Rosenfeld /* Compute voltage compensation. */ 4704*fd43cf6eSHans Rosenfeld vdiff = ((int32_t)le32toh(uc->volt) - sc->eeprom_voltage) / 7; 4705*fd43cf6eSHans Rosenfeld if (vdiff > 0) 4706*fd43cf6eSHans Rosenfeld vdiff *= 2; 4707*fd43cf6eSHans Rosenfeld if (abs(vdiff) > 2) 4708*fd43cf6eSHans Rosenfeld vdiff = 0; 4709*fd43cf6eSHans Rosenfeld sc->sc_txpower->vdiff.value.l = vdiff; 4710*fd43cf6eSHans Rosenfeld 4711*fd43cf6eSHans Rosenfeld /* Get channel attenuation group. */ 4712*fd43cf6eSHans Rosenfeld if (chan <= 20) /* 1-20 */ 4713*fd43cf6eSHans Rosenfeld grp = 4; 4714*fd43cf6eSHans Rosenfeld else if (chan <= 43) /* 34-43 */ 4715*fd43cf6eSHans Rosenfeld grp = 0; 4716*fd43cf6eSHans Rosenfeld else if (chan <= 70) /* 44-70 */ 4717*fd43cf6eSHans Rosenfeld grp = 1; 4718*fd43cf6eSHans Rosenfeld else if (chan <= 124) /* 71-124 */ 4719*fd43cf6eSHans Rosenfeld grp = 2; 4720*fd43cf6eSHans Rosenfeld else /* 125-200 */ 4721*fd43cf6eSHans Rosenfeld grp = 3; 4722*fd43cf6eSHans Rosenfeld sc->sc_txpower->group.value.l = grp; 4723*fd43cf6eSHans Rosenfeld 4724*fd43cf6eSHans Rosenfeld /* Get channel sub-band. */ 4725*fd43cf6eSHans Rosenfeld for (i = 0; i < IWN_NBANDS; i++) 4726*fd43cf6eSHans Rosenfeld if (sc->bands[i].lo != 0 && 4727*fd43cf6eSHans Rosenfeld sc->bands[i].lo <= chan && chan <= sc->bands[i].hi) 4728*fd43cf6eSHans Rosenfeld break; 4729*fd43cf6eSHans Rosenfeld if (i == IWN_NBANDS) /* Can't happen in real-life. */ 4730*fd43cf6eSHans Rosenfeld return EINVAL; 4731*fd43cf6eSHans Rosenfeld chans = sc->bands[i].chans; 4732*fd43cf6eSHans Rosenfeld sc->sc_txpower->subband.value.l = i; 4733*fd43cf6eSHans Rosenfeld 4734*fd43cf6eSHans Rosenfeld for (c = 0; c < 2; c++) { 4735*fd43cf6eSHans Rosenfeld uint8_t power, gain, temp; 4736*fd43cf6eSHans Rosenfeld int maxchpwr, pwr, ridx, idx; 4737*fd43cf6eSHans Rosenfeld 4738*fd43cf6eSHans Rosenfeld power = interpolate(chan, 4739*fd43cf6eSHans Rosenfeld chans[0].num, chans[0].samples[c][1].power, 4740*fd43cf6eSHans Rosenfeld chans[1].num, chans[1].samples[c][1].power, 1); 4741*fd43cf6eSHans Rosenfeld gain = interpolate(chan, 4742*fd43cf6eSHans Rosenfeld chans[0].num, chans[0].samples[c][1].gain, 4743*fd43cf6eSHans Rosenfeld chans[1].num, chans[1].samples[c][1].gain, 1); 4744*fd43cf6eSHans Rosenfeld temp = interpolate(chan, 4745*fd43cf6eSHans Rosenfeld chans[0].num, chans[0].samples[c][1].temp, 4746*fd43cf6eSHans Rosenfeld chans[1].num, chans[1].samples[c][1].temp, 1); 4747*fd43cf6eSHans Rosenfeld sc->sc_txpower->txchain[c].power.value.l = power; 4748*fd43cf6eSHans Rosenfeld sc->sc_txpower->txchain[c].gain.value.l = gain; 4749*fd43cf6eSHans Rosenfeld sc->sc_txpower->txchain[c].temp.value.l = temp; 4750*fd43cf6eSHans Rosenfeld 4751*fd43cf6eSHans Rosenfeld /* Compute temperature compensation. */ 4752*fd43cf6eSHans Rosenfeld tdiff = ((sc->temp - temp) * 2) / tdiv[grp]; 4753*fd43cf6eSHans Rosenfeld sc->sc_txpower->txchain[c].tcomp.value.l = tdiff; 4754*fd43cf6eSHans Rosenfeld 4755*fd43cf6eSHans Rosenfeld for (ridx = 0; ridx <= IWN_RIDX_MAX; ridx++) { 4756*fd43cf6eSHans Rosenfeld /* Convert dBm to half-dBm. */ 4757*fd43cf6eSHans Rosenfeld maxchpwr = sc->maxpwr[chan] * 2; 4758*fd43cf6eSHans Rosenfeld if ((ridx / 8) & 1) 4759*fd43cf6eSHans Rosenfeld maxchpwr -= 6; /* MIMO 2T: -3dB */ 4760*fd43cf6eSHans Rosenfeld 4761*fd43cf6eSHans Rosenfeld pwr = maxpwr; 4762*fd43cf6eSHans Rosenfeld 4763*fd43cf6eSHans Rosenfeld /* Adjust TX power based on rate. */ 4764*fd43cf6eSHans Rosenfeld if ((ridx % 8) == 5) 4765*fd43cf6eSHans Rosenfeld pwr -= 15; /* OFDM48: -7.5dB */ 4766*fd43cf6eSHans Rosenfeld else if ((ridx % 8) == 6) 4767*fd43cf6eSHans Rosenfeld pwr -= 17; /* OFDM54: -8.5dB */ 4768*fd43cf6eSHans Rosenfeld else if ((ridx % 8) == 7) 4769*fd43cf6eSHans Rosenfeld pwr -= 20; /* OFDM60: -10dB */ 4770*fd43cf6eSHans Rosenfeld else 4771*fd43cf6eSHans Rosenfeld pwr -= 10; /* Others: -5dB */ 4772*fd43cf6eSHans Rosenfeld 4773*fd43cf6eSHans Rosenfeld /* Do not exceed channel max TX power. */ 4774*fd43cf6eSHans Rosenfeld if (pwr > maxchpwr) 4775*fd43cf6eSHans Rosenfeld pwr = maxchpwr; 4776*fd43cf6eSHans Rosenfeld 4777*fd43cf6eSHans Rosenfeld idx = gain - (pwr - power) - tdiff - vdiff; 4778*fd43cf6eSHans Rosenfeld if ((ridx / 8) & 1) /* MIMO */ 4779*fd43cf6eSHans Rosenfeld idx += (int32_t)le32toh(uc->atten[grp][c]); 4780*fd43cf6eSHans Rosenfeld 4781*fd43cf6eSHans Rosenfeld if (cmd.band == 0) 4782*fd43cf6eSHans Rosenfeld idx += 9; /* 5GHz */ 4783*fd43cf6eSHans Rosenfeld if (ridx == IWN_RIDX_MAX) 4784*fd43cf6eSHans Rosenfeld idx += 5; /* CCK */ 4785*fd43cf6eSHans Rosenfeld 4786*fd43cf6eSHans Rosenfeld /* Make sure idx stays in a valid range. */ 4787*fd43cf6eSHans Rosenfeld if (idx < 0) 4788*fd43cf6eSHans Rosenfeld idx = 0; 4789*fd43cf6eSHans Rosenfeld else if (idx > IWN4965_MAX_PWR_INDEX) 4790*fd43cf6eSHans Rosenfeld idx = IWN4965_MAX_PWR_INDEX; 4791*fd43cf6eSHans Rosenfeld 4792*fd43cf6eSHans Rosenfeld sc->sc_txpower->txchain[c].rate[ridx].rf_gain.value.l = 4793*fd43cf6eSHans Rosenfeld cmd.power[ridx].rf_gain[c] = rf_gain[idx]; 4794*fd43cf6eSHans Rosenfeld sc->sc_txpower->txchain[c].rate[ridx].dsp_gain.value.l = 4795*fd43cf6eSHans Rosenfeld cmd.power[ridx].dsp_gain[c] = dsp_gain[idx]; 4796*fd43cf6eSHans Rosenfeld } 4797*fd43cf6eSHans Rosenfeld } 4798*fd43cf6eSHans Rosenfeld 4799*fd43cf6eSHans Rosenfeld return iwn_cmd(sc, IWN_CMD_TXPOWER, &cmd, sizeof cmd, async); 4800*fd43cf6eSHans Rosenfeld 4801*fd43cf6eSHans Rosenfeld #undef interpolate 4802*fd43cf6eSHans Rosenfeld #undef fdivround 4803*fd43cf6eSHans Rosenfeld } 4804*fd43cf6eSHans Rosenfeld 4805*fd43cf6eSHans Rosenfeld static int 4806*fd43cf6eSHans Rosenfeld iwn5000_set_txpower(struct iwn_softc *sc, int async) 4807*fd43cf6eSHans Rosenfeld { 4808*fd43cf6eSHans Rosenfeld struct iwn5000_cmd_txpower cmd; 4809*fd43cf6eSHans Rosenfeld 4810*fd43cf6eSHans Rosenfeld /* 4811*fd43cf6eSHans Rosenfeld * TX power calibration is handled automatically by the firmware 4812*fd43cf6eSHans Rosenfeld * for 5000 Series. 4813*fd43cf6eSHans Rosenfeld */ 4814*fd43cf6eSHans Rosenfeld memset(&cmd, 0, sizeof cmd); 4815*fd43cf6eSHans Rosenfeld cmd.global_limit = 2 * IWN5000_TXPOWER_MAX_DBM; /* 16 dBm */ 4816*fd43cf6eSHans Rosenfeld cmd.flags = IWN5000_TXPOWER_NO_CLOSED; 4817*fd43cf6eSHans Rosenfeld cmd.srv_limit = IWN5000_TXPOWER_AUTO; 4818*fd43cf6eSHans Rosenfeld return iwn_cmd(sc, IWN_CMD_TXPOWER_DBM, &cmd, sizeof cmd, async); 4819*fd43cf6eSHans Rosenfeld } 4820*fd43cf6eSHans Rosenfeld 4821*fd43cf6eSHans Rosenfeld /* 4822*fd43cf6eSHans Rosenfeld * Retrieve the maximum RSSI (in dBm) among receivers. 4823*fd43cf6eSHans Rosenfeld */ 4824*fd43cf6eSHans Rosenfeld static int 4825*fd43cf6eSHans Rosenfeld iwn4965_get_rssi(const struct iwn_rx_stat *stat) 4826*fd43cf6eSHans Rosenfeld { 4827*fd43cf6eSHans Rosenfeld const struct iwn4965_rx_phystat *phy = (const void *)stat->phybuf; 4828*fd43cf6eSHans Rosenfeld uint8_t mask, agc; 4829*fd43cf6eSHans Rosenfeld int rssi; 4830*fd43cf6eSHans Rosenfeld 4831*fd43cf6eSHans Rosenfeld mask = (le16toh(phy->antenna) >> 4) & IWN_ANT_ABC; 4832*fd43cf6eSHans Rosenfeld agc = (le16toh(phy->agc) >> 7) & 0x7f; 4833*fd43cf6eSHans Rosenfeld 4834*fd43cf6eSHans Rosenfeld rssi = 0; 4835*fd43cf6eSHans Rosenfeld if (mask & IWN_ANT_A) 4836*fd43cf6eSHans Rosenfeld rssi = MAX(rssi, phy->rssi[0]); 4837*fd43cf6eSHans Rosenfeld if (mask & IWN_ANT_B) 4838*fd43cf6eSHans Rosenfeld rssi = MAX(rssi, phy->rssi[2]); 4839*fd43cf6eSHans Rosenfeld if (mask & IWN_ANT_C) 4840*fd43cf6eSHans Rosenfeld rssi = MAX(rssi, phy->rssi[4]); 4841*fd43cf6eSHans Rosenfeld 4842*fd43cf6eSHans Rosenfeld return rssi - agc - IWN_RSSI_TO_DBM; 4843*fd43cf6eSHans Rosenfeld } 4844*fd43cf6eSHans Rosenfeld 4845*fd43cf6eSHans Rosenfeld static int 4846*fd43cf6eSHans Rosenfeld iwn5000_get_rssi(const struct iwn_rx_stat *stat) 4847*fd43cf6eSHans Rosenfeld { 4848*fd43cf6eSHans Rosenfeld const struct iwn5000_rx_phystat *phy = (const void *)stat->phybuf; 4849*fd43cf6eSHans Rosenfeld uint8_t agc; 4850*fd43cf6eSHans Rosenfeld int rssi; 4851*fd43cf6eSHans Rosenfeld 4852*fd43cf6eSHans Rosenfeld agc = (le32toh(phy->agc) >> 9) & 0x7f; 4853*fd43cf6eSHans Rosenfeld 4854*fd43cf6eSHans Rosenfeld rssi = MAX(le16toh(phy->rssi[0]) & 0xff, 4855*fd43cf6eSHans Rosenfeld le16toh(phy->rssi[1]) & 0xff); 4856*fd43cf6eSHans Rosenfeld rssi = MAX(le16toh(phy->rssi[2]) & 0xff, rssi); 4857*fd43cf6eSHans Rosenfeld 4858*fd43cf6eSHans Rosenfeld return rssi - agc - IWN_RSSI_TO_DBM; 4859*fd43cf6eSHans Rosenfeld } 4860*fd43cf6eSHans Rosenfeld 4861*fd43cf6eSHans Rosenfeld /* 4862*fd43cf6eSHans Rosenfeld * Retrieve the average noise (in dBm) among receivers. 4863*fd43cf6eSHans Rosenfeld */ 4864*fd43cf6eSHans Rosenfeld static int 4865*fd43cf6eSHans Rosenfeld iwn_get_noise(const struct iwn_rx_general_stats *stats) 4866*fd43cf6eSHans Rosenfeld { 4867*fd43cf6eSHans Rosenfeld int i, total, nbant, noise; 4868*fd43cf6eSHans Rosenfeld 4869*fd43cf6eSHans Rosenfeld total = nbant = 0; 4870*fd43cf6eSHans Rosenfeld for (i = 0; i < 3; i++) { 4871*fd43cf6eSHans Rosenfeld if ((noise = le32toh(stats->noise[i]) & 0xff) == 0) 4872*fd43cf6eSHans Rosenfeld continue; 4873*fd43cf6eSHans Rosenfeld total += noise; 4874*fd43cf6eSHans Rosenfeld nbant++; 4875*fd43cf6eSHans Rosenfeld } 4876*fd43cf6eSHans Rosenfeld /* There should be at least one antenna but check anyway. */ 4877*fd43cf6eSHans Rosenfeld return (nbant == 0) ? -127 : (total / nbant) - 107; 4878*fd43cf6eSHans Rosenfeld } 4879*fd43cf6eSHans Rosenfeld 4880*fd43cf6eSHans Rosenfeld /* 4881*fd43cf6eSHans Rosenfeld * Compute temperature (in degC) from last received statistics. 4882*fd43cf6eSHans Rosenfeld */ 4883*fd43cf6eSHans Rosenfeld static int 4884*fd43cf6eSHans Rosenfeld iwn4965_get_temperature(struct iwn_softc *sc) 4885*fd43cf6eSHans Rosenfeld { 4886*fd43cf6eSHans Rosenfeld struct iwn_ucode_info *uc = &sc->ucode_info; 4887*fd43cf6eSHans Rosenfeld int32_t r1, r2, r3, r4, temp; 4888*fd43cf6eSHans Rosenfeld 4889*fd43cf6eSHans Rosenfeld r1 = le32toh(uc->temp[0].chan20MHz); 4890*fd43cf6eSHans Rosenfeld r2 = le32toh(uc->temp[1].chan20MHz); 4891*fd43cf6eSHans Rosenfeld r3 = le32toh(uc->temp[2].chan20MHz); 4892*fd43cf6eSHans Rosenfeld r4 = le32toh(sc->rawtemp); 4893*fd43cf6eSHans Rosenfeld 4894*fd43cf6eSHans Rosenfeld if (r1 == r3) /* Prevents division by 0 (should not happen). */ 4895*fd43cf6eSHans Rosenfeld return 0; 4896*fd43cf6eSHans Rosenfeld 4897*fd43cf6eSHans Rosenfeld /* Sign-extend 23-bit R4 value to 32-bit. */ 4898*fd43cf6eSHans Rosenfeld r4 = ((r4 & 0xffffff) ^ 0x800000) - 0x800000; 4899*fd43cf6eSHans Rosenfeld /* Compute temperature in Kelvin. */ 4900*fd43cf6eSHans Rosenfeld temp = (259 * (r4 - r2)) / (r3 - r1); 4901*fd43cf6eSHans Rosenfeld temp = (temp * 97) / 100 + 8; 4902*fd43cf6eSHans Rosenfeld 4903*fd43cf6eSHans Rosenfeld return IWN_KTOC(temp); 4904*fd43cf6eSHans Rosenfeld } 4905*fd43cf6eSHans Rosenfeld 4906*fd43cf6eSHans Rosenfeld static int 4907*fd43cf6eSHans Rosenfeld iwn5000_get_temperature(struct iwn_softc *sc) 4908*fd43cf6eSHans Rosenfeld { 4909*fd43cf6eSHans Rosenfeld int32_t temp; 4910*fd43cf6eSHans Rosenfeld 4911*fd43cf6eSHans Rosenfeld /* 4912*fd43cf6eSHans Rosenfeld * Temperature is not used by the driver for 5000 Series because 4913*fd43cf6eSHans Rosenfeld * TX power calibration is handled by firmware. We export it to 4914*fd43cf6eSHans Rosenfeld * users through a kstat though. 4915*fd43cf6eSHans Rosenfeld */ 4916*fd43cf6eSHans Rosenfeld temp = le32toh(sc->rawtemp); 4917*fd43cf6eSHans Rosenfeld if (sc->hw_type == IWN_HW_REV_TYPE_5150) { 4918*fd43cf6eSHans Rosenfeld temp = (temp / -5) + sc->temp_off; 4919*fd43cf6eSHans Rosenfeld temp = IWN_KTOC(temp); 4920*fd43cf6eSHans Rosenfeld } 4921*fd43cf6eSHans Rosenfeld return temp; 4922*fd43cf6eSHans Rosenfeld } 4923*fd43cf6eSHans Rosenfeld 4924*fd43cf6eSHans Rosenfeld /* 4925*fd43cf6eSHans Rosenfeld * Initialize sensitivity calibration state machine. 4926*fd43cf6eSHans Rosenfeld */ 4927*fd43cf6eSHans Rosenfeld static int 4928*fd43cf6eSHans Rosenfeld iwn_init_sensitivity(struct iwn_softc *sc) 4929*fd43cf6eSHans Rosenfeld { 4930*fd43cf6eSHans Rosenfeld struct iwn_ops *ops = &sc->ops; 4931*fd43cf6eSHans Rosenfeld struct iwn_calib_state *calib = &sc->calib; 4932*fd43cf6eSHans Rosenfeld uint32_t flags; 4933*fd43cf6eSHans Rosenfeld int error; 4934*fd43cf6eSHans Rosenfeld 4935*fd43cf6eSHans Rosenfeld /* Reset calibration state machine. */ 4936*fd43cf6eSHans Rosenfeld memset(calib, 0, sizeof (*calib)); 4937*fd43cf6eSHans Rosenfeld calib->state = IWN_CALIB_STATE_INIT; 4938*fd43cf6eSHans Rosenfeld calib->cck_state = IWN_CCK_STATE_HIFA; 4939*fd43cf6eSHans Rosenfeld /* Set initial correlation values. */ 4940*fd43cf6eSHans Rosenfeld calib->ofdm_x1 = sc->limits->min_ofdm_x1; 4941*fd43cf6eSHans Rosenfeld calib->ofdm_mrc_x1 = sc->limits->min_ofdm_mrc_x1; 4942*fd43cf6eSHans Rosenfeld calib->ofdm_x4 = sc->limits->min_ofdm_x4; 4943*fd43cf6eSHans Rosenfeld calib->ofdm_mrc_x4 = sc->limits->min_ofdm_mrc_x4; 4944*fd43cf6eSHans Rosenfeld calib->cck_x4 = 125; 4945*fd43cf6eSHans Rosenfeld calib->cck_mrc_x4 = sc->limits->min_cck_mrc_x4; 4946*fd43cf6eSHans Rosenfeld calib->energy_cck = sc->limits->energy_cck; 4947*fd43cf6eSHans Rosenfeld 4948*fd43cf6eSHans Rosenfeld /* Write initial sensitivity. */ 4949*fd43cf6eSHans Rosenfeld if ((error = iwn_send_sensitivity(sc)) != 0) 4950*fd43cf6eSHans Rosenfeld return error; 4951*fd43cf6eSHans Rosenfeld 4952*fd43cf6eSHans Rosenfeld /* Write initial gains. */ 4953*fd43cf6eSHans Rosenfeld if ((error = ops->init_gains(sc)) != 0) 4954*fd43cf6eSHans Rosenfeld return error; 4955*fd43cf6eSHans Rosenfeld 4956*fd43cf6eSHans Rosenfeld /* Request statistics at each beacon interval. */ 4957*fd43cf6eSHans Rosenfeld flags = 0; 4958*fd43cf6eSHans Rosenfeld return iwn_cmd(sc, IWN_CMD_GET_STATISTICS, &flags, sizeof flags, 1); 4959*fd43cf6eSHans Rosenfeld } 4960*fd43cf6eSHans Rosenfeld 4961*fd43cf6eSHans Rosenfeld /* 4962*fd43cf6eSHans Rosenfeld * Collect noise and RSSI statistics for the first 20 beacons received 4963*fd43cf6eSHans Rosenfeld * after association and use them to determine connected antennas and 4964*fd43cf6eSHans Rosenfeld * to set differential gains. 4965*fd43cf6eSHans Rosenfeld */ 4966*fd43cf6eSHans Rosenfeld static void 4967*fd43cf6eSHans Rosenfeld iwn_collect_noise(struct iwn_softc *sc, 4968*fd43cf6eSHans Rosenfeld const struct iwn_rx_general_stats *stats) 4969*fd43cf6eSHans Rosenfeld { 4970*fd43cf6eSHans Rosenfeld struct iwn_ops *ops = &sc->ops; 4971*fd43cf6eSHans Rosenfeld struct iwn_calib_state *calib = &sc->calib; 4972*fd43cf6eSHans Rosenfeld uint32_t val; 4973*fd43cf6eSHans Rosenfeld int i; 4974*fd43cf6eSHans Rosenfeld 4975*fd43cf6eSHans Rosenfeld /* Accumulate RSSI and noise for all 3 antennas. */ 4976*fd43cf6eSHans Rosenfeld for (i = 0; i < 3; i++) { 4977*fd43cf6eSHans Rosenfeld calib->rssi[i] += le32toh(stats->rssi[i]) & 0xff; 4978*fd43cf6eSHans Rosenfeld calib->noise[i] += le32toh(stats->noise[i]) & 0xff; 4979*fd43cf6eSHans Rosenfeld } 4980*fd43cf6eSHans Rosenfeld /* NB: We update differential gains only once after 20 beacons. */ 4981*fd43cf6eSHans Rosenfeld if (++calib->nbeacons < 20) 4982*fd43cf6eSHans Rosenfeld return; 4983*fd43cf6eSHans Rosenfeld 4984*fd43cf6eSHans Rosenfeld /* Determine highest average RSSI. */ 4985*fd43cf6eSHans Rosenfeld val = MAX(calib->rssi[0], calib->rssi[1]); 4986*fd43cf6eSHans Rosenfeld val = MAX(calib->rssi[2], val); 4987*fd43cf6eSHans Rosenfeld 4988*fd43cf6eSHans Rosenfeld /* Determine which antennas are connected. */ 4989*fd43cf6eSHans Rosenfeld sc->chainmask = sc->rxchainmask; 4990*fd43cf6eSHans Rosenfeld for (i = 0; i < 3; i++) 4991*fd43cf6eSHans Rosenfeld if (val - calib->rssi[i] > 15 * 20) 4992*fd43cf6eSHans Rosenfeld sc->chainmask &= ~(1 << i); 4993*fd43cf6eSHans Rosenfeld 4994*fd43cf6eSHans Rosenfeld sc->sc_ant->conn_ant.value.ul = sc->chainmask; 4995*fd43cf6eSHans Rosenfeld 4996*fd43cf6eSHans Rosenfeld /* If none of the TX antennas are connected, keep at least one. */ 4997*fd43cf6eSHans Rosenfeld if ((sc->chainmask & sc->txchainmask) == 0) 4998*fd43cf6eSHans Rosenfeld sc->chainmask |= IWN_LSB(sc->txchainmask); 4999*fd43cf6eSHans Rosenfeld 5000*fd43cf6eSHans Rosenfeld (void)ops->set_gains(sc); 5001*fd43cf6eSHans Rosenfeld calib->state = IWN_CALIB_STATE_RUN; 5002*fd43cf6eSHans Rosenfeld 5003*fd43cf6eSHans Rosenfeld #ifdef notyet 5004*fd43cf6eSHans Rosenfeld /* XXX Disable RX chains with no antennas connected. */ 5005*fd43cf6eSHans Rosenfeld sc->rxon.rxchain = htole16(IWN_RXCHAIN_SEL(sc->chainmask)); 5006*fd43cf6eSHans Rosenfeld DTRACE_PROBE2(rxon, struct iwn_rxon *, &sc->rxon, int, sc->rxonsz); 5007*fd43cf6eSHans Rosenfeld (void)iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, sc->rxonsz, 1); 5008*fd43cf6eSHans Rosenfeld #endif 5009*fd43cf6eSHans Rosenfeld 5010*fd43cf6eSHans Rosenfeld /* Enable power-saving mode if requested by user. */ 5011*fd43cf6eSHans Rosenfeld if (sc->sc_ic.ic_flags & IEEE80211_F_PMGTON) 5012*fd43cf6eSHans Rosenfeld (void)iwn_set_pslevel(sc, 0, 3, 1); 5013*fd43cf6eSHans Rosenfeld } 5014*fd43cf6eSHans Rosenfeld 5015*fd43cf6eSHans Rosenfeld static int 5016*fd43cf6eSHans Rosenfeld iwn4965_init_gains(struct iwn_softc *sc) 5017*fd43cf6eSHans Rosenfeld { 5018*fd43cf6eSHans Rosenfeld struct iwn_phy_calib_gain cmd; 5019*fd43cf6eSHans Rosenfeld 5020*fd43cf6eSHans Rosenfeld memset(&cmd, 0, sizeof cmd); 5021*fd43cf6eSHans Rosenfeld cmd.code = IWN4965_PHY_CALIB_DIFF_GAIN; 5022*fd43cf6eSHans Rosenfeld /* Differential gains initially set to 0 for all 3 antennas. */ 5023*fd43cf6eSHans Rosenfeld return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1); 5024*fd43cf6eSHans Rosenfeld } 5025*fd43cf6eSHans Rosenfeld 5026*fd43cf6eSHans Rosenfeld static int 5027*fd43cf6eSHans Rosenfeld iwn5000_init_gains(struct iwn_softc *sc) 5028*fd43cf6eSHans Rosenfeld { 5029*fd43cf6eSHans Rosenfeld struct iwn_phy_calib cmd; 5030*fd43cf6eSHans Rosenfeld 5031*fd43cf6eSHans Rosenfeld memset(&cmd, 0, sizeof cmd); 5032*fd43cf6eSHans Rosenfeld cmd.code = sc->reset_noise_gain; 5033*fd43cf6eSHans Rosenfeld cmd.ngroups = 1; 5034*fd43cf6eSHans Rosenfeld cmd.isvalid = 1; 5035*fd43cf6eSHans Rosenfeld return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1); 5036*fd43cf6eSHans Rosenfeld } 5037*fd43cf6eSHans Rosenfeld 5038*fd43cf6eSHans Rosenfeld static int 5039*fd43cf6eSHans Rosenfeld iwn4965_set_gains(struct iwn_softc *sc) 5040*fd43cf6eSHans Rosenfeld { 5041*fd43cf6eSHans Rosenfeld struct iwn_calib_state *calib = &sc->calib; 5042*fd43cf6eSHans Rosenfeld struct iwn_phy_calib_gain cmd; 5043*fd43cf6eSHans Rosenfeld int i, delta, noise; 5044*fd43cf6eSHans Rosenfeld 5045*fd43cf6eSHans Rosenfeld /* Get minimal noise among connected antennas. */ 5046*fd43cf6eSHans Rosenfeld noise = INT_MAX; /* NB: There's at least one antenna. */ 5047*fd43cf6eSHans Rosenfeld for (i = 0; i < 3; i++) 5048*fd43cf6eSHans Rosenfeld if (sc->chainmask & (1 << i)) 5049*fd43cf6eSHans Rosenfeld noise = MIN(calib->noise[i], noise); 5050*fd43cf6eSHans Rosenfeld 5051*fd43cf6eSHans Rosenfeld memset(&cmd, 0, sizeof cmd); 5052*fd43cf6eSHans Rosenfeld cmd.code = IWN4965_PHY_CALIB_DIFF_GAIN; 5053*fd43cf6eSHans Rosenfeld /* Set differential gains for connected antennas. */ 5054*fd43cf6eSHans Rosenfeld for (i = 0; i < 3; i++) { 5055*fd43cf6eSHans Rosenfeld if (sc->chainmask & (1 << i)) { 5056*fd43cf6eSHans Rosenfeld /* Compute attenuation (in unit of 1.5dB). */ 5057*fd43cf6eSHans Rosenfeld delta = (noise - calib->noise[i]) / 30; 5058*fd43cf6eSHans Rosenfeld /* NB: delta <= 0 */ 5059*fd43cf6eSHans Rosenfeld /* Limit to [-4.5dB,0]. */ 5060*fd43cf6eSHans Rosenfeld cmd.gain[i] = (uint8_t)MIN(abs(delta), 3); 5061*fd43cf6eSHans Rosenfeld if (delta < 0) 5062*fd43cf6eSHans Rosenfeld cmd.gain[i] |= 1 << 2; /* sign bit */ 5063*fd43cf6eSHans Rosenfeld sc->sc_ant->gain[i].value.ul = cmd.gain[i]; 5064*fd43cf6eSHans Rosenfeld } 5065*fd43cf6eSHans Rosenfeld } 5066*fd43cf6eSHans Rosenfeld return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1); 5067*fd43cf6eSHans Rosenfeld } 5068*fd43cf6eSHans Rosenfeld 5069*fd43cf6eSHans Rosenfeld static int 5070*fd43cf6eSHans Rosenfeld iwn5000_set_gains(struct iwn_softc *sc) 5071*fd43cf6eSHans Rosenfeld { 5072*fd43cf6eSHans Rosenfeld struct iwn_calib_state *calib = &sc->calib; 5073*fd43cf6eSHans Rosenfeld struct iwn_phy_calib_gain cmd; 5074*fd43cf6eSHans Rosenfeld int i, ant, div, delta; 5075*fd43cf6eSHans Rosenfeld 5076*fd43cf6eSHans Rosenfeld /* We collected 20 beacons and !=6050 need a 1.5 factor. */ 5077*fd43cf6eSHans Rosenfeld div = (sc->hw_type == IWN_HW_REV_TYPE_6050) ? 20 : 30; 5078*fd43cf6eSHans Rosenfeld 5079*fd43cf6eSHans Rosenfeld memset(&cmd, 0, sizeof cmd); 5080*fd43cf6eSHans Rosenfeld cmd.code = sc->noise_gain; 5081*fd43cf6eSHans Rosenfeld cmd.ngroups = 1; 5082*fd43cf6eSHans Rosenfeld cmd.isvalid = 1; 5083*fd43cf6eSHans Rosenfeld /* Get first available RX antenna as referential. */ 5084*fd43cf6eSHans Rosenfeld ant = IWN_LSB(sc->rxchainmask); 5085*fd43cf6eSHans Rosenfeld /* Set differential gains for other antennas. */ 5086*fd43cf6eSHans Rosenfeld for (i = ant + 1; i < 3; i++) { 5087*fd43cf6eSHans Rosenfeld if (sc->chainmask & (1 << i)) { 5088*fd43cf6eSHans Rosenfeld /* The delta is relative to antenna "ant". */ 5089*fd43cf6eSHans Rosenfeld delta = (calib->noise[ant] - calib->noise[i]) / div; 5090*fd43cf6eSHans Rosenfeld /* Limit to [-4.5dB,+4.5dB]. */ 5091*fd43cf6eSHans Rosenfeld cmd.gain[i - 1] = (uint8_t)MIN(abs(delta), 3); 5092*fd43cf6eSHans Rosenfeld if (delta < 0) 5093*fd43cf6eSHans Rosenfeld cmd.gain[i - 1] |= 1 << 2; /* sign bit */ 5094*fd43cf6eSHans Rosenfeld sc->sc_ant->gain[i - 1].value.ul 5095*fd43cf6eSHans Rosenfeld = cmd.gain[i - 1]; 5096*fd43cf6eSHans Rosenfeld } 5097*fd43cf6eSHans Rosenfeld } 5098*fd43cf6eSHans Rosenfeld return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1); 5099*fd43cf6eSHans Rosenfeld } 5100*fd43cf6eSHans Rosenfeld 5101*fd43cf6eSHans Rosenfeld /* 5102*fd43cf6eSHans Rosenfeld * Tune RF RX sensitivity based on the number of false alarms detected 5103*fd43cf6eSHans Rosenfeld * during the last beacon period. 5104*fd43cf6eSHans Rosenfeld */ 5105*fd43cf6eSHans Rosenfeld static void 5106*fd43cf6eSHans Rosenfeld iwn_tune_sensitivity(struct iwn_softc *sc, const struct iwn_rx_stats *stats) 5107*fd43cf6eSHans Rosenfeld { 5108*fd43cf6eSHans Rosenfeld #define inc(val, inc, max) \ 5109*fd43cf6eSHans Rosenfeld if ((val) < (max)) { \ 5110*fd43cf6eSHans Rosenfeld if ((val) < (max) - (inc)) \ 5111*fd43cf6eSHans Rosenfeld (val) += (inc); \ 5112*fd43cf6eSHans Rosenfeld else \ 5113*fd43cf6eSHans Rosenfeld (val) = (max); \ 5114*fd43cf6eSHans Rosenfeld needs_update = 1; \ 5115*fd43cf6eSHans Rosenfeld } 5116*fd43cf6eSHans Rosenfeld #define dec(val, dec, min) \ 5117*fd43cf6eSHans Rosenfeld if ((val) > (min)) { \ 5118*fd43cf6eSHans Rosenfeld if ((val) > (min) + (dec)) \ 5119*fd43cf6eSHans Rosenfeld (val) -= (dec); \ 5120*fd43cf6eSHans Rosenfeld else \ 5121*fd43cf6eSHans Rosenfeld (val) = (min); \ 5122*fd43cf6eSHans Rosenfeld needs_update = 1; \ 5123*fd43cf6eSHans Rosenfeld } 5124*fd43cf6eSHans Rosenfeld 5125*fd43cf6eSHans Rosenfeld const struct iwn_sensitivity_limits *limits = sc->limits; 5126*fd43cf6eSHans Rosenfeld struct iwn_calib_state *calib = &sc->calib; 5127*fd43cf6eSHans Rosenfeld uint32_t val, rxena, fa; 5128*fd43cf6eSHans Rosenfeld uint32_t energy[3], energy_min; 5129*fd43cf6eSHans Rosenfeld uint8_t noise[3], noise_ref; 5130*fd43cf6eSHans Rosenfeld int i, needs_update = 0; 5131*fd43cf6eSHans Rosenfeld 5132*fd43cf6eSHans Rosenfeld /* Check that we've been enabled long enough. */ 5133*fd43cf6eSHans Rosenfeld if ((rxena = le32toh(stats->general.load)) == 0) 5134*fd43cf6eSHans Rosenfeld return; 5135*fd43cf6eSHans Rosenfeld 5136*fd43cf6eSHans Rosenfeld /* Compute number of false alarms since last call for OFDM. */ 5137*fd43cf6eSHans Rosenfeld fa = le32toh(stats->ofdm.bad_plcp) - calib->bad_plcp_ofdm; 5138*fd43cf6eSHans Rosenfeld fa += le32toh(stats->ofdm.fa) - calib->fa_ofdm; 5139*fd43cf6eSHans Rosenfeld fa *= 200 * 1024; /* 200TU */ 5140*fd43cf6eSHans Rosenfeld 5141*fd43cf6eSHans Rosenfeld /* Save counters values for next call. */ 5142*fd43cf6eSHans Rosenfeld calib->bad_plcp_ofdm = le32toh(stats->ofdm.bad_plcp); 5143*fd43cf6eSHans Rosenfeld calib->fa_ofdm = le32toh(stats->ofdm.fa); 5144*fd43cf6eSHans Rosenfeld 5145*fd43cf6eSHans Rosenfeld if (fa > 50 * rxena) { 5146*fd43cf6eSHans Rosenfeld /* High false alarm count, decrease sensitivity. */ 5147*fd43cf6eSHans Rosenfeld IWN_DBG("OFDM high false alarm count: %u", fa); 5148*fd43cf6eSHans Rosenfeld inc(calib->ofdm_x1, 1, limits->max_ofdm_x1); 5149*fd43cf6eSHans Rosenfeld inc(calib->ofdm_mrc_x1, 1, limits->max_ofdm_mrc_x1); 5150*fd43cf6eSHans Rosenfeld inc(calib->ofdm_x4, 1, limits->max_ofdm_x4); 5151*fd43cf6eSHans Rosenfeld inc(calib->ofdm_mrc_x4, 1, limits->max_ofdm_mrc_x4); 5152*fd43cf6eSHans Rosenfeld 5153*fd43cf6eSHans Rosenfeld } else if (fa < 5 * rxena) { 5154*fd43cf6eSHans Rosenfeld /* Low false alarm count, increase sensitivity. */ 5155*fd43cf6eSHans Rosenfeld IWN_DBG("OFDM low false alarm count: %u", fa); 5156*fd43cf6eSHans Rosenfeld dec(calib->ofdm_x1, 1, limits->min_ofdm_x1); 5157*fd43cf6eSHans Rosenfeld dec(calib->ofdm_mrc_x1, 1, limits->min_ofdm_mrc_x1); 5158*fd43cf6eSHans Rosenfeld dec(calib->ofdm_x4, 1, limits->min_ofdm_x4); 5159*fd43cf6eSHans Rosenfeld dec(calib->ofdm_mrc_x4, 1, limits->min_ofdm_mrc_x4); 5160*fd43cf6eSHans Rosenfeld } 5161*fd43cf6eSHans Rosenfeld 5162*fd43cf6eSHans Rosenfeld /* Compute maximum noise among 3 receivers. */ 5163*fd43cf6eSHans Rosenfeld for (i = 0; i < 3; i++) 5164*fd43cf6eSHans Rosenfeld noise[i] = (le32toh(stats->general.noise[i]) >> 8) & 0xff; 5165*fd43cf6eSHans Rosenfeld val = MAX(noise[0], noise[1]); 5166*fd43cf6eSHans Rosenfeld val = MAX(noise[2], val); 5167*fd43cf6eSHans Rosenfeld /* Insert it into our samples table. */ 5168*fd43cf6eSHans Rosenfeld calib->noise_samples[calib->cur_noise_sample] = (uint8_t)val; 5169*fd43cf6eSHans Rosenfeld calib->cur_noise_sample = (calib->cur_noise_sample + 1) % 20; 5170*fd43cf6eSHans Rosenfeld 5171*fd43cf6eSHans Rosenfeld /* Compute maximum noise among last 20 samples. */ 5172*fd43cf6eSHans Rosenfeld noise_ref = calib->noise_samples[0]; 5173*fd43cf6eSHans Rosenfeld for (i = 1; i < 20; i++) 5174*fd43cf6eSHans Rosenfeld noise_ref = MAX(noise_ref, calib->noise_samples[i]); 5175*fd43cf6eSHans Rosenfeld 5176*fd43cf6eSHans Rosenfeld /* Compute maximum energy among 3 receivers. */ 5177*fd43cf6eSHans Rosenfeld for (i = 0; i < 3; i++) 5178*fd43cf6eSHans Rosenfeld energy[i] = le32toh(stats->general.energy[i]); 5179*fd43cf6eSHans Rosenfeld val = MIN(energy[0], energy[1]); 5180*fd43cf6eSHans Rosenfeld val = MIN(energy[2], val); 5181*fd43cf6eSHans Rosenfeld /* Insert it into our samples table. */ 5182*fd43cf6eSHans Rosenfeld calib->energy_samples[calib->cur_energy_sample] = val; 5183*fd43cf6eSHans Rosenfeld calib->cur_energy_sample = (calib->cur_energy_sample + 1) % 10; 5184*fd43cf6eSHans Rosenfeld 5185*fd43cf6eSHans Rosenfeld /* Compute minimum energy among last 10 samples. */ 5186*fd43cf6eSHans Rosenfeld energy_min = calib->energy_samples[0]; 5187*fd43cf6eSHans Rosenfeld for (i = 1; i < 10; i++) 5188*fd43cf6eSHans Rosenfeld energy_min = MAX(energy_min, calib->energy_samples[i]); 5189*fd43cf6eSHans Rosenfeld energy_min += 6; 5190*fd43cf6eSHans Rosenfeld 5191*fd43cf6eSHans Rosenfeld /* Compute number of false alarms since last call for CCK. */ 5192*fd43cf6eSHans Rosenfeld fa = le32toh(stats->cck.bad_plcp) - calib->bad_plcp_cck; 5193*fd43cf6eSHans Rosenfeld fa += le32toh(stats->cck.fa) - calib->fa_cck; 5194*fd43cf6eSHans Rosenfeld fa *= 200 * 1024; /* 200TU */ 5195*fd43cf6eSHans Rosenfeld 5196*fd43cf6eSHans Rosenfeld /* Save counters values for next call. */ 5197*fd43cf6eSHans Rosenfeld calib->bad_plcp_cck = le32toh(stats->cck.bad_plcp); 5198*fd43cf6eSHans Rosenfeld calib->fa_cck = le32toh(stats->cck.fa); 5199*fd43cf6eSHans Rosenfeld 5200*fd43cf6eSHans Rosenfeld if (fa > 50 * rxena) { 5201*fd43cf6eSHans Rosenfeld /* High false alarm count, decrease sensitivity. */ 5202*fd43cf6eSHans Rosenfeld IWN_DBG("CCK high false alarm count: %u", fa); 5203*fd43cf6eSHans Rosenfeld calib->cck_state = IWN_CCK_STATE_HIFA; 5204*fd43cf6eSHans Rosenfeld calib->low_fa = 0; 5205*fd43cf6eSHans Rosenfeld 5206*fd43cf6eSHans Rosenfeld if (calib->cck_x4 > 160) { 5207*fd43cf6eSHans Rosenfeld calib->noise_ref = noise_ref; 5208*fd43cf6eSHans Rosenfeld if (calib->energy_cck > 2) 5209*fd43cf6eSHans Rosenfeld dec(calib->energy_cck, 2, energy_min); 5210*fd43cf6eSHans Rosenfeld } 5211*fd43cf6eSHans Rosenfeld if (calib->cck_x4 < 160) { 5212*fd43cf6eSHans Rosenfeld calib->cck_x4 = 161; 5213*fd43cf6eSHans Rosenfeld needs_update = 1; 5214*fd43cf6eSHans Rosenfeld } else 5215*fd43cf6eSHans Rosenfeld inc(calib->cck_x4, 3, limits->max_cck_x4); 5216*fd43cf6eSHans Rosenfeld 5217*fd43cf6eSHans Rosenfeld inc(calib->cck_mrc_x4, 3, limits->max_cck_mrc_x4); 5218*fd43cf6eSHans Rosenfeld 5219*fd43cf6eSHans Rosenfeld } else if (fa < 5 * rxena) { 5220*fd43cf6eSHans Rosenfeld /* Low false alarm count, increase sensitivity. */ 5221*fd43cf6eSHans Rosenfeld IWN_DBG("CCK low false alarm count: %u", fa); 5222*fd43cf6eSHans Rosenfeld calib->cck_state = IWN_CCK_STATE_LOFA; 5223*fd43cf6eSHans Rosenfeld calib->low_fa++; 5224*fd43cf6eSHans Rosenfeld 5225*fd43cf6eSHans Rosenfeld if (calib->cck_state != IWN_CCK_STATE_INIT && 5226*fd43cf6eSHans Rosenfeld (((int32_t)calib->noise_ref - (int32_t)noise_ref) > 2 || 5227*fd43cf6eSHans Rosenfeld calib->low_fa > 100)) { 5228*fd43cf6eSHans Rosenfeld inc(calib->energy_cck, 2, limits->min_energy_cck); 5229*fd43cf6eSHans Rosenfeld dec(calib->cck_x4, 3, limits->min_cck_x4); 5230*fd43cf6eSHans Rosenfeld dec(calib->cck_mrc_x4, 3, limits->min_cck_mrc_x4); 5231*fd43cf6eSHans Rosenfeld } 5232*fd43cf6eSHans Rosenfeld } else { 5233*fd43cf6eSHans Rosenfeld /* Not worth to increase or decrease sensitivity. */ 5234*fd43cf6eSHans Rosenfeld IWN_DBG("CCK normal false alarm count: %u", fa); 5235*fd43cf6eSHans Rosenfeld calib->low_fa = 0; 5236*fd43cf6eSHans Rosenfeld calib->noise_ref = noise_ref; 5237*fd43cf6eSHans Rosenfeld 5238*fd43cf6eSHans Rosenfeld if (calib->cck_state == IWN_CCK_STATE_HIFA) { 5239*fd43cf6eSHans Rosenfeld /* Previous interval had many false alarms. */ 5240*fd43cf6eSHans Rosenfeld dec(calib->energy_cck, 8, energy_min); 5241*fd43cf6eSHans Rosenfeld } 5242*fd43cf6eSHans Rosenfeld calib->cck_state = IWN_CCK_STATE_INIT; 5243*fd43cf6eSHans Rosenfeld } 5244*fd43cf6eSHans Rosenfeld 5245*fd43cf6eSHans Rosenfeld if (needs_update) 5246*fd43cf6eSHans Rosenfeld (void)iwn_send_sensitivity(sc); 5247*fd43cf6eSHans Rosenfeld #undef dec 5248*fd43cf6eSHans Rosenfeld #undef inc 5249*fd43cf6eSHans Rosenfeld } 5250*fd43cf6eSHans Rosenfeld 5251*fd43cf6eSHans Rosenfeld static int 5252*fd43cf6eSHans Rosenfeld iwn_send_sensitivity(struct iwn_softc *sc) 5253*fd43cf6eSHans Rosenfeld { 5254*fd43cf6eSHans Rosenfeld struct iwn_calib_state *calib = &sc->calib; 5255*fd43cf6eSHans Rosenfeld struct iwn_enhanced_sensitivity_cmd cmd; 5256*fd43cf6eSHans Rosenfeld int len; 5257*fd43cf6eSHans Rosenfeld 5258*fd43cf6eSHans Rosenfeld memset(&cmd, 0, sizeof cmd); 5259*fd43cf6eSHans Rosenfeld len = sizeof (struct iwn_sensitivity_cmd); 5260*fd43cf6eSHans Rosenfeld cmd.which = IWN_SENSITIVITY_WORKTBL; 5261*fd43cf6eSHans Rosenfeld /* OFDM modulation. */ 5262*fd43cf6eSHans Rosenfeld cmd.corr_ofdm_x1 = htole16(calib->ofdm_x1); 5263*fd43cf6eSHans Rosenfeld cmd.corr_ofdm_mrc_x1 = htole16(calib->ofdm_mrc_x1); 5264*fd43cf6eSHans Rosenfeld cmd.corr_ofdm_x4 = htole16(calib->ofdm_x4); 5265*fd43cf6eSHans Rosenfeld cmd.corr_ofdm_mrc_x4 = htole16(calib->ofdm_mrc_x4); 5266*fd43cf6eSHans Rosenfeld cmd.energy_ofdm = htole16(sc->limits->energy_ofdm); 5267*fd43cf6eSHans Rosenfeld cmd.energy_ofdm_th = htole16(62); 5268*fd43cf6eSHans Rosenfeld /* CCK modulation. */ 5269*fd43cf6eSHans Rosenfeld cmd.corr_cck_x4 = htole16(calib->cck_x4); 5270*fd43cf6eSHans Rosenfeld cmd.corr_cck_mrc_x4 = htole16(calib->cck_mrc_x4); 5271*fd43cf6eSHans Rosenfeld cmd.energy_cck = htole16(calib->energy_cck); 5272*fd43cf6eSHans Rosenfeld /* Barker modulation: use default values. */ 5273*fd43cf6eSHans Rosenfeld cmd.corr_barker = htole16(190); 5274*fd43cf6eSHans Rosenfeld cmd.corr_barker_mrc = htole16(390); 5275*fd43cf6eSHans Rosenfeld if (!(sc->sc_flags & IWN_FLAG_ENH_SENS)) 5276*fd43cf6eSHans Rosenfeld goto send; 5277*fd43cf6eSHans Rosenfeld /* Enhanced sensitivity settings. */ 5278*fd43cf6eSHans Rosenfeld len = sizeof (struct iwn_enhanced_sensitivity_cmd); 5279*fd43cf6eSHans Rosenfeld cmd.ofdm_det_slope_mrc = htole16(668); 5280*fd43cf6eSHans Rosenfeld cmd.ofdm_det_icept_mrc = htole16(4); 5281*fd43cf6eSHans Rosenfeld cmd.ofdm_det_slope = htole16(486); 5282*fd43cf6eSHans Rosenfeld cmd.ofdm_det_icept = htole16(37); 5283*fd43cf6eSHans Rosenfeld cmd.cck_det_slope_mrc = htole16(853); 5284*fd43cf6eSHans Rosenfeld cmd.cck_det_icept_mrc = htole16(4); 5285*fd43cf6eSHans Rosenfeld cmd.cck_det_slope = htole16(476); 5286*fd43cf6eSHans Rosenfeld cmd.cck_det_icept = htole16(99); 5287*fd43cf6eSHans Rosenfeld send: 5288*fd43cf6eSHans Rosenfeld 5289*fd43cf6eSHans Rosenfeld sc->sc_sens->ofdm_x1.value.ul = calib->ofdm_x1; 5290*fd43cf6eSHans Rosenfeld sc->sc_sens->ofdm_mrc_x1.value.ul = calib->ofdm_mrc_x1; 5291*fd43cf6eSHans Rosenfeld sc->sc_sens->ofdm_x4.value.ul = calib->ofdm_x4; 5292*fd43cf6eSHans Rosenfeld sc->sc_sens->ofdm_mrc_x4.value.ul = calib->ofdm_mrc_x4; 5293*fd43cf6eSHans Rosenfeld sc->sc_sens->cck_x4.value.ul = calib->cck_x4; 5294*fd43cf6eSHans Rosenfeld sc->sc_sens->cck_mrc_x4.value.ul = calib->cck_mrc_x4; 5295*fd43cf6eSHans Rosenfeld sc->sc_sens->energy_cck.value.ul = calib->energy_cck; 5296*fd43cf6eSHans Rosenfeld 5297*fd43cf6eSHans Rosenfeld return iwn_cmd(sc, IWN_CMD_SET_SENSITIVITY, &cmd, len, 1); 5298*fd43cf6eSHans Rosenfeld } 5299*fd43cf6eSHans Rosenfeld 5300*fd43cf6eSHans Rosenfeld /* 5301*fd43cf6eSHans Rosenfeld * Set STA mode power saving level (between 0 and 5). 5302*fd43cf6eSHans Rosenfeld * Level 0 is CAM (Continuously Aware Mode), 5 is for maximum power saving. 5303*fd43cf6eSHans Rosenfeld */ 5304*fd43cf6eSHans Rosenfeld static int 5305*fd43cf6eSHans Rosenfeld iwn_set_pslevel(struct iwn_softc *sc, int dtim, int level, int async) 5306*fd43cf6eSHans Rosenfeld { 5307*fd43cf6eSHans Rosenfeld struct iwn_pmgt_cmd cmd; 5308*fd43cf6eSHans Rosenfeld const struct iwn_pmgt *pmgt; 5309*fd43cf6eSHans Rosenfeld uint32_t maxp, skip_dtim; 5310*fd43cf6eSHans Rosenfeld uint32_t reg; 5311*fd43cf6eSHans Rosenfeld int i; 5312*fd43cf6eSHans Rosenfeld 5313*fd43cf6eSHans Rosenfeld /* Select which PS parameters to use. */ 5314*fd43cf6eSHans Rosenfeld if (dtim <= 2) 5315*fd43cf6eSHans Rosenfeld pmgt = &iwn_pmgt[0][level]; 5316*fd43cf6eSHans Rosenfeld else if (dtim <= 10) 5317*fd43cf6eSHans Rosenfeld pmgt = &iwn_pmgt[1][level]; 5318*fd43cf6eSHans Rosenfeld else 5319*fd43cf6eSHans Rosenfeld pmgt = &iwn_pmgt[2][level]; 5320*fd43cf6eSHans Rosenfeld 5321*fd43cf6eSHans Rosenfeld memset(&cmd, 0, sizeof cmd); 5322*fd43cf6eSHans Rosenfeld if (level != 0) /* not CAM */ 5323*fd43cf6eSHans Rosenfeld cmd.flags |= htole16(IWN_PS_ALLOW_SLEEP); 5324*fd43cf6eSHans Rosenfeld if (level == 5) 5325*fd43cf6eSHans Rosenfeld cmd.flags |= htole16(IWN_PS_FAST_PD); 5326*fd43cf6eSHans Rosenfeld /* Retrieve PCIe Active State Power Management (ASPM). */ 5327*fd43cf6eSHans Rosenfeld reg = pci_config_get32(sc->sc_pcih, 5328*fd43cf6eSHans Rosenfeld sc->sc_cap_off + PCIE_LINKCTL); 5329*fd43cf6eSHans Rosenfeld if (!(reg & PCIE_LINKCTL_ASPM_CTL_L0S)) /* L0s Entry disabled. */ 5330*fd43cf6eSHans Rosenfeld cmd.flags |= htole16(IWN_PS_PCI_PMGT); 5331*fd43cf6eSHans Rosenfeld cmd.rxtimeout = htole32(pmgt->rxtimeout * 1024); 5332*fd43cf6eSHans Rosenfeld cmd.txtimeout = htole32(pmgt->txtimeout * 1024); 5333*fd43cf6eSHans Rosenfeld 5334*fd43cf6eSHans Rosenfeld if (dtim == 0) { 5335*fd43cf6eSHans Rosenfeld dtim = 1; 5336*fd43cf6eSHans Rosenfeld skip_dtim = 0; 5337*fd43cf6eSHans Rosenfeld } else 5338*fd43cf6eSHans Rosenfeld skip_dtim = pmgt->skip_dtim; 5339*fd43cf6eSHans Rosenfeld if (skip_dtim != 0) { 5340*fd43cf6eSHans Rosenfeld cmd.flags |= htole16(IWN_PS_SLEEP_OVER_DTIM); 5341*fd43cf6eSHans Rosenfeld maxp = pmgt->intval[4]; 5342*fd43cf6eSHans Rosenfeld if (maxp == (uint32_t)-1) 5343*fd43cf6eSHans Rosenfeld maxp = dtim * (skip_dtim + 1); 5344*fd43cf6eSHans Rosenfeld else if (maxp > dtim) 5345*fd43cf6eSHans Rosenfeld maxp = (maxp / dtim) * dtim; 5346*fd43cf6eSHans Rosenfeld } else 5347*fd43cf6eSHans Rosenfeld maxp = dtim; 5348*fd43cf6eSHans Rosenfeld for (i = 0; i < 5; i++) 5349*fd43cf6eSHans Rosenfeld cmd.intval[i] = htole32(MIN(maxp, pmgt->intval[i])); 5350*fd43cf6eSHans Rosenfeld 5351*fd43cf6eSHans Rosenfeld sc->sc_misc->pslevel.value.ul = level; 5352*fd43cf6eSHans Rosenfeld return iwn_cmd(sc, IWN_CMD_SET_POWER_MODE, &cmd, sizeof cmd, async); 5353*fd43cf6eSHans Rosenfeld } 5354*fd43cf6eSHans Rosenfeld 5355*fd43cf6eSHans Rosenfeld int 5356*fd43cf6eSHans Rosenfeld iwn5000_runtime_calib(struct iwn_softc *sc) 5357*fd43cf6eSHans Rosenfeld { 5358*fd43cf6eSHans Rosenfeld struct iwn5000_calib_config cmd; 5359*fd43cf6eSHans Rosenfeld 5360*fd43cf6eSHans Rosenfeld memset(&cmd, 0, sizeof cmd); 5361*fd43cf6eSHans Rosenfeld cmd.ucode.once.enable = 0xffffffff; 5362*fd43cf6eSHans Rosenfeld cmd.ucode.once.start = IWN5000_CALIB_DC; 5363*fd43cf6eSHans Rosenfeld return iwn_cmd(sc, IWN5000_CMD_CALIB_CONFIG, &cmd, sizeof(cmd), 0); 5364*fd43cf6eSHans Rosenfeld } 5365*fd43cf6eSHans Rosenfeld 5366*fd43cf6eSHans Rosenfeld static int 5367*fd43cf6eSHans Rosenfeld iwn_config_bt_coex_bluetooth(struct iwn_softc *sc) 5368*fd43cf6eSHans Rosenfeld { 5369*fd43cf6eSHans Rosenfeld struct iwn_bluetooth bluetooth; 5370*fd43cf6eSHans Rosenfeld 5371*fd43cf6eSHans Rosenfeld memset(&bluetooth, 0, sizeof bluetooth); 5372*fd43cf6eSHans Rosenfeld bluetooth.flags = IWN_BT_COEX_ENABLE; 5373*fd43cf6eSHans Rosenfeld bluetooth.lead_time = IWN_BT_LEAD_TIME_DEF; 5374*fd43cf6eSHans Rosenfeld bluetooth.max_kill = IWN_BT_MAX_KILL_DEF; 5375*fd43cf6eSHans Rosenfeld 5376*fd43cf6eSHans Rosenfeld return iwn_cmd(sc, IWN_CMD_BT_COEX, &bluetooth, sizeof bluetooth, 0); 5377*fd43cf6eSHans Rosenfeld } 5378*fd43cf6eSHans Rosenfeld 5379*fd43cf6eSHans Rosenfeld static int 5380*fd43cf6eSHans Rosenfeld iwn_config_bt_coex_prio_table(struct iwn_softc *sc) 5381*fd43cf6eSHans Rosenfeld { 5382*fd43cf6eSHans Rosenfeld uint8_t prio_table[16]; 5383*fd43cf6eSHans Rosenfeld 5384*fd43cf6eSHans Rosenfeld memset(&prio_table, 0, sizeof prio_table); 5385*fd43cf6eSHans Rosenfeld prio_table[ 0] = 6; /* init calibration 1 */ 5386*fd43cf6eSHans Rosenfeld prio_table[ 1] = 7; /* init calibration 2 */ 5387*fd43cf6eSHans Rosenfeld prio_table[ 2] = 2; /* periodic calib low 1 */ 5388*fd43cf6eSHans Rosenfeld prio_table[ 3] = 3; /* periodic calib low 2 */ 5389*fd43cf6eSHans Rosenfeld prio_table[ 4] = 4; /* periodic calib high 1 */ 5390*fd43cf6eSHans Rosenfeld prio_table[ 5] = 5; /* periodic calib high 2 */ 5391*fd43cf6eSHans Rosenfeld prio_table[ 6] = 6; /* dtim */ 5392*fd43cf6eSHans Rosenfeld prio_table[ 7] = 8; /* scan52 */ 5393*fd43cf6eSHans Rosenfeld prio_table[ 8] = 10; /* scan24 */ 5394*fd43cf6eSHans Rosenfeld 5395*fd43cf6eSHans Rosenfeld return iwn_cmd(sc, IWN_CMD_BT_COEX_PRIO_TABLE, 5396*fd43cf6eSHans Rosenfeld &prio_table, sizeof prio_table, 0); 5397*fd43cf6eSHans Rosenfeld } 5398*fd43cf6eSHans Rosenfeld 5399*fd43cf6eSHans Rosenfeld static int 5400*fd43cf6eSHans Rosenfeld iwn_config_bt_coex_adv_config(struct iwn_softc *sc, struct iwn_bt_basic *basic, 5401*fd43cf6eSHans Rosenfeld size_t len) 5402*fd43cf6eSHans Rosenfeld { 5403*fd43cf6eSHans Rosenfeld struct iwn_btcoex_prot btprot; 5404*fd43cf6eSHans Rosenfeld int error; 5405*fd43cf6eSHans Rosenfeld 5406*fd43cf6eSHans Rosenfeld basic->bt.flags = IWN_BT_COEX_ENABLE; 5407*fd43cf6eSHans Rosenfeld basic->bt.lead_time = IWN_BT_LEAD_TIME_DEF; 5408*fd43cf6eSHans Rosenfeld basic->bt.max_kill = IWN_BT_MAX_KILL_DEF; 5409*fd43cf6eSHans Rosenfeld basic->bt.bt3_timer_t7_value = IWN_BT_BT3_T7_DEF; 5410*fd43cf6eSHans Rosenfeld basic->bt.kill_ack_mask = IWN_BT_KILL_ACK_MASK_DEF; 5411*fd43cf6eSHans Rosenfeld basic->bt.kill_cts_mask = IWN_BT_KILL_CTS_MASK_DEF; 5412*fd43cf6eSHans Rosenfeld basic->bt3_prio_sample_time = IWN_BT_BT3_PRIO_SAMPLE_DEF; 5413*fd43cf6eSHans Rosenfeld basic->bt3_timer_t2_value = IWN_BT_BT3_T2_DEF; 5414*fd43cf6eSHans Rosenfeld basic->bt3_lookup_table[ 0] = htole32(0xaaaaaaaa); /* Normal */ 5415*fd43cf6eSHans Rosenfeld basic->bt3_lookup_table[ 1] = htole32(0xaaaaaaaa); 5416*fd43cf6eSHans Rosenfeld basic->bt3_lookup_table[ 2] = htole32(0xaeaaaaaa); 5417*fd43cf6eSHans Rosenfeld basic->bt3_lookup_table[ 3] = htole32(0xaaaaaaaa); 5418*fd43cf6eSHans Rosenfeld basic->bt3_lookup_table[ 4] = htole32(0xcc00ff28); 5419*fd43cf6eSHans Rosenfeld basic->bt3_lookup_table[ 5] = htole32(0x0000aaaa); 5420*fd43cf6eSHans Rosenfeld basic->bt3_lookup_table[ 6] = htole32(0xcc00aaaa); 5421*fd43cf6eSHans Rosenfeld basic->bt3_lookup_table[ 7] = htole32(0x0000aaaa); 5422*fd43cf6eSHans Rosenfeld basic->bt3_lookup_table[ 8] = htole32(0xc0004000); 5423*fd43cf6eSHans Rosenfeld basic->bt3_lookup_table[ 9] = htole32(0x00004000); 5424*fd43cf6eSHans Rosenfeld basic->bt3_lookup_table[10] = htole32(0xf0005000); 5425*fd43cf6eSHans Rosenfeld basic->bt3_lookup_table[11] = htole32(0xf0005000); 5426*fd43cf6eSHans Rosenfeld basic->reduce_txpower = 0; /* as not implemented */ 5427*fd43cf6eSHans Rosenfeld basic->valid = IWN_BT_ALL_VALID_MASK; 5428*fd43cf6eSHans Rosenfeld 5429*fd43cf6eSHans Rosenfeld error = iwn_cmd(sc, IWN_CMD_BT_COEX, &basic, len, 0); 5430*fd43cf6eSHans Rosenfeld if (error != 0) { 5431*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 5432*fd43cf6eSHans Rosenfeld "!could not configure advanced bluetooth coexistence"); 5433*fd43cf6eSHans Rosenfeld return error; 5434*fd43cf6eSHans Rosenfeld } 5435*fd43cf6eSHans Rosenfeld 5436*fd43cf6eSHans Rosenfeld error = iwn_config_bt_coex_prio_table(sc); 5437*fd43cf6eSHans Rosenfeld if (error != 0) { 5438*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 5439*fd43cf6eSHans Rosenfeld "!could not configure send BT priority table"); 5440*fd43cf6eSHans Rosenfeld return error; 5441*fd43cf6eSHans Rosenfeld } 5442*fd43cf6eSHans Rosenfeld 5443*fd43cf6eSHans Rosenfeld /* Force BT state machine change */ 5444*fd43cf6eSHans Rosenfeld memset(&btprot, 0, sizeof btprot); 5445*fd43cf6eSHans Rosenfeld btprot.open = 1; 5446*fd43cf6eSHans Rosenfeld btprot.type = 1; 5447*fd43cf6eSHans Rosenfeld error = iwn_cmd(sc, IWN_CMD_BT_COEX_PROT, &btprot, sizeof btprot, 1); 5448*fd43cf6eSHans Rosenfeld if (error != 0) { 5449*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, "!could not open BT protcol"); 5450*fd43cf6eSHans Rosenfeld return error; 5451*fd43cf6eSHans Rosenfeld } 5452*fd43cf6eSHans Rosenfeld 5453*fd43cf6eSHans Rosenfeld btprot.open = 0; 5454*fd43cf6eSHans Rosenfeld error = iwn_cmd(sc, IWN_CMD_BT_COEX_PROT, &btprot, sizeof btprot, 1); 5455*fd43cf6eSHans Rosenfeld if (error != 0) { 5456*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, "!could not close BT protcol"); 5457*fd43cf6eSHans Rosenfeld return error; 5458*fd43cf6eSHans Rosenfeld } 5459*fd43cf6eSHans Rosenfeld return 0; 5460*fd43cf6eSHans Rosenfeld } 5461*fd43cf6eSHans Rosenfeld 5462*fd43cf6eSHans Rosenfeld static int 5463*fd43cf6eSHans Rosenfeld iwn_config_bt_coex_adv1(struct iwn_softc *sc) 5464*fd43cf6eSHans Rosenfeld { 5465*fd43cf6eSHans Rosenfeld struct iwn_bt_adv1 d; 5466*fd43cf6eSHans Rosenfeld 5467*fd43cf6eSHans Rosenfeld memset(&d, 0, sizeof d); 5468*fd43cf6eSHans Rosenfeld d.prio_boost = IWN_BT_PRIO_BOOST_DEF; 5469*fd43cf6eSHans Rosenfeld d.tx_prio_boost = 0; 5470*fd43cf6eSHans Rosenfeld d.rx_prio_boost = 0; 5471*fd43cf6eSHans Rosenfeld return iwn_config_bt_coex_adv_config(sc, &d.basic, sizeof d); 5472*fd43cf6eSHans Rosenfeld } 5473*fd43cf6eSHans Rosenfeld 5474*fd43cf6eSHans Rosenfeld static int 5475*fd43cf6eSHans Rosenfeld iwn_config_bt_coex_adv2(struct iwn_softc *sc) 5476*fd43cf6eSHans Rosenfeld { 5477*fd43cf6eSHans Rosenfeld struct iwn_bt_adv2 d; 5478*fd43cf6eSHans Rosenfeld 5479*fd43cf6eSHans Rosenfeld memset(&d, 0, sizeof d); 5480*fd43cf6eSHans Rosenfeld d.prio_boost = IWN_BT_PRIO_BOOST_DEF; 5481*fd43cf6eSHans Rosenfeld d.tx_prio_boost = 0; 5482*fd43cf6eSHans Rosenfeld d.rx_prio_boost = 0; 5483*fd43cf6eSHans Rosenfeld return iwn_config_bt_coex_adv_config(sc, &d.basic, sizeof d); 5484*fd43cf6eSHans Rosenfeld } 5485*fd43cf6eSHans Rosenfeld 5486*fd43cf6eSHans Rosenfeld static int 5487*fd43cf6eSHans Rosenfeld iwn_config(struct iwn_softc *sc) 5488*fd43cf6eSHans Rosenfeld { 5489*fd43cf6eSHans Rosenfeld struct iwn_ops *ops = &sc->ops; 5490*fd43cf6eSHans Rosenfeld struct ieee80211com *ic = &sc->sc_ic; 5491*fd43cf6eSHans Rosenfeld uint32_t txmask; 5492*fd43cf6eSHans Rosenfeld uint16_t rxchain; 5493*fd43cf6eSHans Rosenfeld int error; 5494*fd43cf6eSHans Rosenfeld 5495*fd43cf6eSHans Rosenfeld error = ops->config_bt_coex(sc); 5496*fd43cf6eSHans Rosenfeld if (error != 0) { 5497*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 5498*fd43cf6eSHans Rosenfeld "!could not configure bluetooth coexistence"); 5499*fd43cf6eSHans Rosenfeld return error; 5500*fd43cf6eSHans Rosenfeld } 5501*fd43cf6eSHans Rosenfeld 5502*fd43cf6eSHans Rosenfeld /* Set radio temperature sensor offset. */ 5503*fd43cf6eSHans Rosenfeld if (sc->hw_type == IWN_HW_REV_TYPE_6005) { 5504*fd43cf6eSHans Rosenfeld error = iwn6000_temp_offset_calib(sc); 5505*fd43cf6eSHans Rosenfeld if (error != 0) { 5506*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 5507*fd43cf6eSHans Rosenfeld "!could not set temperature offset"); 5508*fd43cf6eSHans Rosenfeld return error; 5509*fd43cf6eSHans Rosenfeld } 5510*fd43cf6eSHans Rosenfeld } 5511*fd43cf6eSHans Rosenfeld 5512*fd43cf6eSHans Rosenfeld if (sc->hw_type == IWN_HW_REV_TYPE_2030 || 5513*fd43cf6eSHans Rosenfeld sc->hw_type == IWN_HW_REV_TYPE_2000 || 5514*fd43cf6eSHans Rosenfeld sc->hw_type == IWN_HW_REV_TYPE_135 || 5515*fd43cf6eSHans Rosenfeld sc->hw_type == IWN_HW_REV_TYPE_105) { 5516*fd43cf6eSHans Rosenfeld error = iwn2000_temp_offset_calib(sc); 5517*fd43cf6eSHans Rosenfeld if (error != 0) { 5518*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 5519*fd43cf6eSHans Rosenfeld "!could not set temperature offset"); 5520*fd43cf6eSHans Rosenfeld return error; 5521*fd43cf6eSHans Rosenfeld } 5522*fd43cf6eSHans Rosenfeld } 5523*fd43cf6eSHans Rosenfeld 5524*fd43cf6eSHans Rosenfeld if (sc->hw_type == IWN_HW_REV_TYPE_6050 || 5525*fd43cf6eSHans Rosenfeld sc->hw_type == IWN_HW_REV_TYPE_6005) { 5526*fd43cf6eSHans Rosenfeld /* Configure runtime DC calibration. */ 5527*fd43cf6eSHans Rosenfeld error = iwn5000_runtime_calib(sc); 5528*fd43cf6eSHans Rosenfeld if (error != 0) { 5529*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 5530*fd43cf6eSHans Rosenfeld "!could not configure runtime calibration"); 5531*fd43cf6eSHans Rosenfeld return error; 5532*fd43cf6eSHans Rosenfeld } 5533*fd43cf6eSHans Rosenfeld } 5534*fd43cf6eSHans Rosenfeld 5535*fd43cf6eSHans Rosenfeld /* Configure valid TX chains for 5000 Series. */ 5536*fd43cf6eSHans Rosenfeld if (sc->hw_type != IWN_HW_REV_TYPE_4965) { 5537*fd43cf6eSHans Rosenfeld txmask = htole32(sc->txchainmask); 5538*fd43cf6eSHans Rosenfeld error = iwn_cmd(sc, IWN5000_CMD_TX_ANT_CONFIG, &txmask, 5539*fd43cf6eSHans Rosenfeld sizeof txmask, 0); 5540*fd43cf6eSHans Rosenfeld if (error != 0) { 5541*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 5542*fd43cf6eSHans Rosenfeld "!could not configure valid TX chains"); 5543*fd43cf6eSHans Rosenfeld return error; 5544*fd43cf6eSHans Rosenfeld } 5545*fd43cf6eSHans Rosenfeld } 5546*fd43cf6eSHans Rosenfeld 5547*fd43cf6eSHans Rosenfeld /* Set mode, channel, RX filter and enable RX. */ 5548*fd43cf6eSHans Rosenfeld memset(&sc->rxon, 0, sizeof (struct iwn_rxon)); 5549*fd43cf6eSHans Rosenfeld IEEE80211_ADDR_COPY(sc->rxon.myaddr, ic->ic_macaddr); 5550*fd43cf6eSHans Rosenfeld IEEE80211_ADDR_COPY(sc->rxon.wlap, ic->ic_macaddr); 5551*fd43cf6eSHans Rosenfeld sc->rxon.chan = ieee80211_chan2ieee(ic, ic->ic_ibss_chan); 5552*fd43cf6eSHans Rosenfeld sc->rxon.flags = htole32(IWN_RXON_TSF | IWN_RXON_CTS_TO_SELF); 5553*fd43cf6eSHans Rosenfeld if (IEEE80211_IS_CHAN_2GHZ(ic->ic_ibss_chan)) 5554*fd43cf6eSHans Rosenfeld sc->rxon.flags |= htole32(IWN_RXON_AUTO | IWN_RXON_24GHZ); 5555*fd43cf6eSHans Rosenfeld switch (ic->ic_opmode) { 5556*fd43cf6eSHans Rosenfeld case IEEE80211_M_IBSS: 5557*fd43cf6eSHans Rosenfeld sc->rxon.mode = IWN_MODE_IBSS; 5558*fd43cf6eSHans Rosenfeld sc->rxon.filter = htole32(IWN_FILTER_MULTICAST); 5559*fd43cf6eSHans Rosenfeld break; 5560*fd43cf6eSHans Rosenfeld case IEEE80211_M_STA: 5561*fd43cf6eSHans Rosenfeld sc->rxon.mode = IWN_MODE_STA; 5562*fd43cf6eSHans Rosenfeld sc->rxon.filter = htole32(IWN_FILTER_MULTICAST); 5563*fd43cf6eSHans Rosenfeld break; 5564*fd43cf6eSHans Rosenfeld case IEEE80211_M_MONITOR: 5565*fd43cf6eSHans Rosenfeld sc->rxon.mode = IWN_MODE_MONITOR; 5566*fd43cf6eSHans Rosenfeld sc->rxon.filter = htole32(IWN_FILTER_MULTICAST | 5567*fd43cf6eSHans Rosenfeld IWN_FILTER_CTL | IWN_FILTER_PROMISC); 5568*fd43cf6eSHans Rosenfeld break; 5569*fd43cf6eSHans Rosenfeld default: 5570*fd43cf6eSHans Rosenfeld /* Should not get there. */ 5571*fd43cf6eSHans Rosenfeld ASSERT(ic->ic_opmode == IEEE80211_M_IBSS || 5572*fd43cf6eSHans Rosenfeld ic->ic_opmode == IEEE80211_M_STA || 5573*fd43cf6eSHans Rosenfeld ic->ic_opmode == IEEE80211_M_MONITOR); 5574*fd43cf6eSHans Rosenfeld break; 5575*fd43cf6eSHans Rosenfeld } 5576*fd43cf6eSHans Rosenfeld sc->rxon.cck_mask = 0x0f; /* not yet negotiated */ 5577*fd43cf6eSHans Rosenfeld sc->rxon.ofdm_mask = 0xff; /* not yet negotiated */ 5578*fd43cf6eSHans Rosenfeld sc->rxon.ht_single_mask = 0xff; 5579*fd43cf6eSHans Rosenfeld sc->rxon.ht_dual_mask = 0xff; 5580*fd43cf6eSHans Rosenfeld sc->rxon.ht_triple_mask = 0xff; 5581*fd43cf6eSHans Rosenfeld rxchain = 5582*fd43cf6eSHans Rosenfeld IWN_RXCHAIN_VALID(sc->rxchainmask) | 5583*fd43cf6eSHans Rosenfeld IWN_RXCHAIN_MIMO_COUNT(2) | 5584*fd43cf6eSHans Rosenfeld IWN_RXCHAIN_IDLE_COUNT(2); 5585*fd43cf6eSHans Rosenfeld sc->rxon.rxchain = htole16(rxchain); 5586*fd43cf6eSHans Rosenfeld DTRACE_PROBE2(rxon, struct iwn_rxon *, &sc->rxon, int, sc->rxonsz); 5587*fd43cf6eSHans Rosenfeld error = iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, sc->rxonsz, 0); 5588*fd43cf6eSHans Rosenfeld if (error != 0) { 5589*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 5590*fd43cf6eSHans Rosenfeld "!RXON command failed"); 5591*fd43cf6eSHans Rosenfeld return error; 5592*fd43cf6eSHans Rosenfeld } 5593*fd43cf6eSHans Rosenfeld 5594*fd43cf6eSHans Rosenfeld if ((error = iwn_add_broadcast_node(sc, 0)) != 0) { 5595*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 5596*fd43cf6eSHans Rosenfeld "!could not add broadcast node"); 5597*fd43cf6eSHans Rosenfeld return error; 5598*fd43cf6eSHans Rosenfeld } 5599*fd43cf6eSHans Rosenfeld 5600*fd43cf6eSHans Rosenfeld /* Configuration has changed, set TX power accordingly. */ 5601*fd43cf6eSHans Rosenfeld if ((error = ops->set_txpower(sc, 0)) != 0) { 5602*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 5603*fd43cf6eSHans Rosenfeld "!could not set TX power"); 5604*fd43cf6eSHans Rosenfeld return error; 5605*fd43cf6eSHans Rosenfeld } 5606*fd43cf6eSHans Rosenfeld 5607*fd43cf6eSHans Rosenfeld if ((error = iwn_set_critical_temp(sc)) != 0) { 5608*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 5609*fd43cf6eSHans Rosenfeld "!could not set critical temperature"); 5610*fd43cf6eSHans Rosenfeld return error; 5611*fd43cf6eSHans Rosenfeld } 5612*fd43cf6eSHans Rosenfeld 5613*fd43cf6eSHans Rosenfeld /* Set power saving level to CAM during initialization. */ 5614*fd43cf6eSHans Rosenfeld if ((error = iwn_set_pslevel(sc, 0, 0, 0)) != 0) { 5615*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 5616*fd43cf6eSHans Rosenfeld "!could not set power saving level"); 5617*fd43cf6eSHans Rosenfeld return error; 5618*fd43cf6eSHans Rosenfeld } 5619*fd43cf6eSHans Rosenfeld return 0; 5620*fd43cf6eSHans Rosenfeld } 5621*fd43cf6eSHans Rosenfeld 5622*fd43cf6eSHans Rosenfeld static uint16_t 5623*fd43cf6eSHans Rosenfeld iwn_get_active_dwell_time(struct iwn_softc *sc, uint16_t flags, 5624*fd43cf6eSHans Rosenfeld uint8_t n_probes) 5625*fd43cf6eSHans Rosenfeld { 5626*fd43cf6eSHans Rosenfeld _NOTE(ARGUNUSED(sc)); 5627*fd43cf6eSHans Rosenfeld 5628*fd43cf6eSHans Rosenfeld /* No channel? Default to 2GHz settings */ 5629*fd43cf6eSHans Rosenfeld if (flags & IEEE80211_CHAN_2GHZ) 5630*fd43cf6eSHans Rosenfeld return IWN_ACTIVE_DWELL_TIME_2GHZ + 5631*fd43cf6eSHans Rosenfeld IWN_ACTIVE_DWELL_FACTOR_2GHZ * (n_probes + 1); 5632*fd43cf6eSHans Rosenfeld 5633*fd43cf6eSHans Rosenfeld /* 5GHz dwell time */ 5634*fd43cf6eSHans Rosenfeld return IWN_ACTIVE_DWELL_TIME_5GHZ + 5635*fd43cf6eSHans Rosenfeld IWN_ACTIVE_DWELL_FACTOR_5GHZ * (n_probes + 1); 5636*fd43cf6eSHans Rosenfeld } 5637*fd43cf6eSHans Rosenfeld 5638*fd43cf6eSHans Rosenfeld /* 5639*fd43cf6eSHans Rosenfeld * Limit the total dwell time to 85% of the beacon interval. 5640*fd43cf6eSHans Rosenfeld * 5641*fd43cf6eSHans Rosenfeld * Returns the dwell time in milliseconds. 5642*fd43cf6eSHans Rosenfeld */ 5643*fd43cf6eSHans Rosenfeld static uint16_t 5644*fd43cf6eSHans Rosenfeld iwn_limit_dwell(struct iwn_softc *sc, uint16_t dwell_time) 5645*fd43cf6eSHans Rosenfeld { 5646*fd43cf6eSHans Rosenfeld _NOTE(ARGUNUSED(dwell_time)); 5647*fd43cf6eSHans Rosenfeld 5648*fd43cf6eSHans Rosenfeld struct ieee80211com *ic = &sc->sc_ic; 5649*fd43cf6eSHans Rosenfeld struct ieee80211_node *ni = ic->ic_bss; 5650*fd43cf6eSHans Rosenfeld int bintval = 0; 5651*fd43cf6eSHans Rosenfeld 5652*fd43cf6eSHans Rosenfeld /* bintval is in TU (1.024mS) */ 5653*fd43cf6eSHans Rosenfeld if (ni != NULL) 5654*fd43cf6eSHans Rosenfeld bintval = ni->in_intval; 5655*fd43cf6eSHans Rosenfeld 5656*fd43cf6eSHans Rosenfeld /* 5657*fd43cf6eSHans Rosenfeld * If it's non-zero, we should calculate the minimum of 5658*fd43cf6eSHans Rosenfeld * it and the DWELL_BASE. 5659*fd43cf6eSHans Rosenfeld * 5660*fd43cf6eSHans Rosenfeld * XXX Yes, the math should take into account that bintval 5661*fd43cf6eSHans Rosenfeld * is 1.024mS, not 1mS.. 5662*fd43cf6eSHans Rosenfeld */ 5663*fd43cf6eSHans Rosenfeld if (bintval > 0) 5664*fd43cf6eSHans Rosenfeld return MIN(IWN_PASSIVE_DWELL_BASE, ((bintval * 85) / 100)); 5665*fd43cf6eSHans Rosenfeld 5666*fd43cf6eSHans Rosenfeld /* No association context? Default */ 5667*fd43cf6eSHans Rosenfeld return IWN_PASSIVE_DWELL_BASE; 5668*fd43cf6eSHans Rosenfeld } 5669*fd43cf6eSHans Rosenfeld 5670*fd43cf6eSHans Rosenfeld static uint16_t 5671*fd43cf6eSHans Rosenfeld iwn_get_passive_dwell_time(struct iwn_softc *sc, uint16_t flags) 5672*fd43cf6eSHans Rosenfeld { 5673*fd43cf6eSHans Rosenfeld uint16_t passive; 5674*fd43cf6eSHans Rosenfeld if (flags & IEEE80211_CHAN_2GHZ) 5675*fd43cf6eSHans Rosenfeld passive = IWN_PASSIVE_DWELL_BASE + IWN_PASSIVE_DWELL_TIME_2GHZ; 5676*fd43cf6eSHans Rosenfeld else 5677*fd43cf6eSHans Rosenfeld passive = IWN_PASSIVE_DWELL_BASE + IWN_PASSIVE_DWELL_TIME_5GHZ; 5678*fd43cf6eSHans Rosenfeld 5679*fd43cf6eSHans Rosenfeld /* Clamp to the beacon interval if we're associated */ 5680*fd43cf6eSHans Rosenfeld return iwn_limit_dwell(sc, passive); 5681*fd43cf6eSHans Rosenfeld } 5682*fd43cf6eSHans Rosenfeld 5683*fd43cf6eSHans Rosenfeld static int 5684*fd43cf6eSHans Rosenfeld iwn_scan(struct iwn_softc *sc, uint16_t flags) 5685*fd43cf6eSHans Rosenfeld { 5686*fd43cf6eSHans Rosenfeld struct ieee80211com *ic = &sc->sc_ic; 5687*fd43cf6eSHans Rosenfeld struct iwn_scan_hdr *hdr; 5688*fd43cf6eSHans Rosenfeld struct iwn_cmd_data *tx; 5689*fd43cf6eSHans Rosenfeld struct iwn_scan_essid *essid; 5690*fd43cf6eSHans Rosenfeld struct iwn_scan_chan *chan; 5691*fd43cf6eSHans Rosenfeld struct ieee80211_frame *wh; 5692*fd43cf6eSHans Rosenfeld struct ieee80211_rateset *rs; 5693*fd43cf6eSHans Rosenfeld struct ieee80211_channel *c; 5694*fd43cf6eSHans Rosenfeld uint8_t *buf, *frm; 5695*fd43cf6eSHans Rosenfeld uint16_t rxchain, dwell_active, dwell_passive; 5696*fd43cf6eSHans Rosenfeld uint8_t txant; 5697*fd43cf6eSHans Rosenfeld int buflen, error, is_active; 5698*fd43cf6eSHans Rosenfeld 5699*fd43cf6eSHans Rosenfeld buf = kmem_zalloc(IWN_SCAN_MAXSZ, KM_NOSLEEP); 5700*fd43cf6eSHans Rosenfeld if (buf == NULL) { 5701*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 5702*fd43cf6eSHans Rosenfeld "!could not allocate buffer for scan command"); 5703*fd43cf6eSHans Rosenfeld return ENOMEM; 5704*fd43cf6eSHans Rosenfeld } 5705*fd43cf6eSHans Rosenfeld hdr = (struct iwn_scan_hdr *)buf; 5706*fd43cf6eSHans Rosenfeld /* 5707*fd43cf6eSHans Rosenfeld * Move to the next channel if no frames are received within 20ms 5708*fd43cf6eSHans Rosenfeld * after sending the probe request. 5709*fd43cf6eSHans Rosenfeld */ 5710*fd43cf6eSHans Rosenfeld hdr->quiet_time = htole16(20); /* timeout in milliseconds */ 5711*fd43cf6eSHans Rosenfeld hdr->quiet_threshold = htole16(1); /* min # of packets */ 5712*fd43cf6eSHans Rosenfeld 5713*fd43cf6eSHans Rosenfeld /* Select antennas for scanning. */ 5714*fd43cf6eSHans Rosenfeld rxchain = 5715*fd43cf6eSHans Rosenfeld IWN_RXCHAIN_VALID(sc->rxchainmask) | 5716*fd43cf6eSHans Rosenfeld IWN_RXCHAIN_FORCE_MIMO_SEL(sc->rxchainmask) | 5717*fd43cf6eSHans Rosenfeld IWN_RXCHAIN_DRIVER_FORCE; 5718*fd43cf6eSHans Rosenfeld if ((flags & IEEE80211_CHAN_5GHZ) && 5719*fd43cf6eSHans Rosenfeld sc->hw_type == IWN_HW_REV_TYPE_4965) { 5720*fd43cf6eSHans Rosenfeld /* Ant A must be avoided in 5GHz because of an HW bug. */ 5721*fd43cf6eSHans Rosenfeld rxchain |= IWN_RXCHAIN_FORCE_SEL(IWN_ANT_BC); 5722*fd43cf6eSHans Rosenfeld } else /* Use all available RX antennas. */ 5723*fd43cf6eSHans Rosenfeld rxchain |= IWN_RXCHAIN_FORCE_SEL(sc->rxchainmask); 5724*fd43cf6eSHans Rosenfeld hdr->rxchain = htole16(rxchain); 5725*fd43cf6eSHans Rosenfeld hdr->filter = htole32(IWN_FILTER_MULTICAST | IWN_FILTER_BEACON); 5726*fd43cf6eSHans Rosenfeld 5727*fd43cf6eSHans Rosenfeld tx = (struct iwn_cmd_data *)(hdr + 1); 5728*fd43cf6eSHans Rosenfeld tx->flags = htole32(IWN_TX_AUTO_SEQ); 5729*fd43cf6eSHans Rosenfeld tx->id = sc->broadcast_id; 5730*fd43cf6eSHans Rosenfeld tx->lifetime = htole32(IWN_LIFETIME_INFINITE); 5731*fd43cf6eSHans Rosenfeld 5732*fd43cf6eSHans Rosenfeld if (flags & IEEE80211_CHAN_5GHZ) { 5733*fd43cf6eSHans Rosenfeld /* Send probe requests at 6Mbps. */ 5734*fd43cf6eSHans Rosenfeld tx->plcp = iwn_rates[IWN_RIDX_OFDM6].plcp; 5735*fd43cf6eSHans Rosenfeld rs = &ic->ic_sup_rates[IEEE80211_MODE_11A]; 5736*fd43cf6eSHans Rosenfeld } else { 5737*fd43cf6eSHans Rosenfeld hdr->flags = htole32(IWN_RXON_24GHZ | IWN_RXON_AUTO); 5738*fd43cf6eSHans Rosenfeld /* Send probe requests at 1Mbps. */ 5739*fd43cf6eSHans Rosenfeld tx->plcp = iwn_rates[IWN_RIDX_CCK1].plcp; 5740*fd43cf6eSHans Rosenfeld tx->rflags = IWN_RFLAG_CCK; 5741*fd43cf6eSHans Rosenfeld rs = &ic->ic_sup_rates[IEEE80211_MODE_11G]; 5742*fd43cf6eSHans Rosenfeld } 5743*fd43cf6eSHans Rosenfeld 5744*fd43cf6eSHans Rosenfeld hdr->crc_threshold = 0xffff; 5745*fd43cf6eSHans Rosenfeld 5746*fd43cf6eSHans Rosenfeld /* Use the first valid TX antenna. */ 5747*fd43cf6eSHans Rosenfeld txant = IWN_LSB(sc->txchainmask); 5748*fd43cf6eSHans Rosenfeld tx->rflags |= IWN_RFLAG_ANT(txant); 5749*fd43cf6eSHans Rosenfeld 5750*fd43cf6eSHans Rosenfeld /* 5751*fd43cf6eSHans Rosenfeld * Only do active scanning if we're announcing a probe request 5752*fd43cf6eSHans Rosenfeld * for a given SSID (or more, if we ever add it to the driver.) 5753*fd43cf6eSHans Rosenfeld */ 5754*fd43cf6eSHans Rosenfeld is_active = 0; 5755*fd43cf6eSHans Rosenfeld 5756*fd43cf6eSHans Rosenfeld essid = (struct iwn_scan_essid *)(tx + 1); 5757*fd43cf6eSHans Rosenfeld if (ic->ic_des_esslen != 0) { 5758*fd43cf6eSHans Rosenfeld char essidstr[IEEE80211_NWID_LEN+1]; 5759*fd43cf6eSHans Rosenfeld memcpy(essidstr, ic->ic_des_essid, ic->ic_des_esslen); 5760*fd43cf6eSHans Rosenfeld essidstr[ic->ic_des_esslen] = '\0'; 5761*fd43cf6eSHans Rosenfeld 5762*fd43cf6eSHans Rosenfeld DTRACE_PROBE1(scan__direct, char *, essidstr); 5763*fd43cf6eSHans Rosenfeld 5764*fd43cf6eSHans Rosenfeld essid[0].id = IEEE80211_ELEMID_SSID; 5765*fd43cf6eSHans Rosenfeld essid[0].len = ic->ic_des_esslen; 5766*fd43cf6eSHans Rosenfeld memcpy(essid[0].data, ic->ic_des_essid, ic->ic_des_esslen); 5767*fd43cf6eSHans Rosenfeld 5768*fd43cf6eSHans Rosenfeld is_active = 1; 5769*fd43cf6eSHans Rosenfeld /* hdr->crc_threshold = 0x1; */ 5770*fd43cf6eSHans Rosenfeld hdr->scan_flags = htole32(IWN_SCAN_PASSIVE2ACTIVE); 5771*fd43cf6eSHans Rosenfeld } 5772*fd43cf6eSHans Rosenfeld /* 5773*fd43cf6eSHans Rosenfeld * Build a probe request frame. Most of the following code is a 5774*fd43cf6eSHans Rosenfeld * copy & paste of what is done in net80211. 5775*fd43cf6eSHans Rosenfeld */ 5776*fd43cf6eSHans Rosenfeld wh = (struct ieee80211_frame *)(essid + 20); 5777*fd43cf6eSHans Rosenfeld wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | 5778*fd43cf6eSHans Rosenfeld IEEE80211_FC0_SUBTYPE_PROBE_REQ; 5779*fd43cf6eSHans Rosenfeld wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; 5780*fd43cf6eSHans Rosenfeld IEEE80211_ADDR_COPY(wh->i_addr1, etherbroadcastaddr); 5781*fd43cf6eSHans Rosenfeld IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_macaddr); 5782*fd43cf6eSHans Rosenfeld IEEE80211_ADDR_COPY(wh->i_addr3, etherbroadcastaddr); 5783*fd43cf6eSHans Rosenfeld wh->i_dur[0] = wh->i_dur[1] = 0; /* filled by HW */ 5784*fd43cf6eSHans Rosenfeld wh->i_seq[0] = wh->i_seq[1] = 0; /* filled by HW */ 5785*fd43cf6eSHans Rosenfeld 5786*fd43cf6eSHans Rosenfeld frm = (uint8_t *)(wh + 1); 5787*fd43cf6eSHans Rosenfeld frm = ieee80211_add_ssid(frm, ic->ic_des_essid, ic->ic_des_esslen); 5788*fd43cf6eSHans Rosenfeld frm = ieee80211_add_rates(frm, rs); 5789*fd43cf6eSHans Rosenfeld #ifndef IEEE80211_NO_HT 5790*fd43cf6eSHans Rosenfeld if (ic->ic_flags & IEEE80211_F_HTON) 5791*fd43cf6eSHans Rosenfeld frm = ieee80211_add_htcaps(frm, ic); 5792*fd43cf6eSHans Rosenfeld #endif 5793*fd43cf6eSHans Rosenfeld if (rs->ir_nrates > IEEE80211_RATE_SIZE) 5794*fd43cf6eSHans Rosenfeld frm = ieee80211_add_xrates(frm, rs); 5795*fd43cf6eSHans Rosenfeld 5796*fd43cf6eSHans Rosenfeld /* Set length of probe request. */ 5797*fd43cf6eSHans Rosenfeld /*LINTED: E_PTRDIFF_OVERFLOW*/ 5798*fd43cf6eSHans Rosenfeld tx->len = htole16(frm - (uint8_t *)wh); 5799*fd43cf6eSHans Rosenfeld 5800*fd43cf6eSHans Rosenfeld 5801*fd43cf6eSHans Rosenfeld /* 5802*fd43cf6eSHans Rosenfeld * If active scanning is requested but a certain channel is 5803*fd43cf6eSHans Rosenfeld * marked passive, we can do active scanning if we detect 5804*fd43cf6eSHans Rosenfeld * transmissions. 5805*fd43cf6eSHans Rosenfeld * 5806*fd43cf6eSHans Rosenfeld * There is an issue with some firmware versions that triggers 5807*fd43cf6eSHans Rosenfeld * a sysassert on a "good CRC threshold" of zero (== disabled), 5808*fd43cf6eSHans Rosenfeld * on a radar channel even though this means that we should NOT 5809*fd43cf6eSHans Rosenfeld * send probes. 5810*fd43cf6eSHans Rosenfeld * 5811*fd43cf6eSHans Rosenfeld * The "good CRC threshold" is the number of frames that we 5812*fd43cf6eSHans Rosenfeld * need to receive during our dwell time on a channel before 5813*fd43cf6eSHans Rosenfeld * sending out probes -- setting this to a huge value will 5814*fd43cf6eSHans Rosenfeld * mean we never reach it, but at the same time work around 5815*fd43cf6eSHans Rosenfeld * the aforementioned issue. Thus use IWN_GOOD_CRC_TH_NEVER 5816*fd43cf6eSHans Rosenfeld * here instead of IWN_GOOD_CRC_TH_DISABLED. 5817*fd43cf6eSHans Rosenfeld * 5818*fd43cf6eSHans Rosenfeld * This was fixed in later versions along with some other 5819*fd43cf6eSHans Rosenfeld * scan changes, and the threshold behaves as a flag in those 5820*fd43cf6eSHans Rosenfeld * versions. 5821*fd43cf6eSHans Rosenfeld */ 5822*fd43cf6eSHans Rosenfeld 5823*fd43cf6eSHans Rosenfeld /* 5824*fd43cf6eSHans Rosenfeld * If we're doing active scanning, set the crc_threshold 5825*fd43cf6eSHans Rosenfeld * to a suitable value. This is different to active veruss 5826*fd43cf6eSHans Rosenfeld * passive scanning depending upon the channel flags; the 5827*fd43cf6eSHans Rosenfeld * firmware will obey that particular check for us. 5828*fd43cf6eSHans Rosenfeld */ 5829*fd43cf6eSHans Rosenfeld if (sc->tlv_feature_flags & IWN_UCODE_TLV_FLAGS_NEWSCAN) 5830*fd43cf6eSHans Rosenfeld hdr->crc_threshold = is_active ? 5831*fd43cf6eSHans Rosenfeld IWN_GOOD_CRC_TH_DEFAULT : IWN_GOOD_CRC_TH_DISABLED; 5832*fd43cf6eSHans Rosenfeld else 5833*fd43cf6eSHans Rosenfeld hdr->crc_threshold = is_active ? 5834*fd43cf6eSHans Rosenfeld IWN_GOOD_CRC_TH_DEFAULT : IWN_GOOD_CRC_TH_NEVER; 5835*fd43cf6eSHans Rosenfeld 5836*fd43cf6eSHans Rosenfeld chan = (struct iwn_scan_chan *)frm; 5837*fd43cf6eSHans Rosenfeld for (c = &ic->ic_sup_channels[1]; 5838*fd43cf6eSHans Rosenfeld c <= &ic->ic_sup_channels[IEEE80211_CHAN_MAX]; c++) { 5839*fd43cf6eSHans Rosenfeld if ((c->ich_flags & flags) != flags) 5840*fd43cf6eSHans Rosenfeld continue; 5841*fd43cf6eSHans Rosenfeld chan->chan = htole16(ieee80211_chan2ieee(ic, c)); 5842*fd43cf6eSHans Rosenfeld chan->flags = 0; 5843*fd43cf6eSHans Rosenfeld if (!(c->ich_flags & IEEE80211_CHAN_PASSIVE)) 5844*fd43cf6eSHans Rosenfeld chan->flags |= htole32(IWN_CHAN_ACTIVE); 5845*fd43cf6eSHans Rosenfeld if (ic->ic_des_esslen != 0) 5846*fd43cf6eSHans Rosenfeld chan->flags |= htole32(IWN_CHAN_NPBREQS(1)); 5847*fd43cf6eSHans Rosenfeld 5848*fd43cf6eSHans Rosenfeld /* 5849*fd43cf6eSHans Rosenfeld * Calculate the active/passive dwell times. 5850*fd43cf6eSHans Rosenfeld */ 5851*fd43cf6eSHans Rosenfeld 5852*fd43cf6eSHans Rosenfeld dwell_active = iwn_get_active_dwell_time(sc, flags, is_active); 5853*fd43cf6eSHans Rosenfeld dwell_passive = iwn_get_passive_dwell_time(sc, flags); 5854*fd43cf6eSHans Rosenfeld 5855*fd43cf6eSHans Rosenfeld /* Make sure they're valid */ 5856*fd43cf6eSHans Rosenfeld if (dwell_passive <= dwell_active) 5857*fd43cf6eSHans Rosenfeld dwell_passive = dwell_active + 1; 5858*fd43cf6eSHans Rosenfeld 5859*fd43cf6eSHans Rosenfeld chan->active = htole16(dwell_active); 5860*fd43cf6eSHans Rosenfeld chan->passive = htole16(dwell_passive); 5861*fd43cf6eSHans Rosenfeld 5862*fd43cf6eSHans Rosenfeld chan->dsp_gain = 0x6e; 5863*fd43cf6eSHans Rosenfeld if (IEEE80211_IS_CHAN_5GHZ(c)) { 5864*fd43cf6eSHans Rosenfeld chan->rf_gain = 0x3b; 5865*fd43cf6eSHans Rosenfeld } else { 5866*fd43cf6eSHans Rosenfeld chan->rf_gain = 0x28; 5867*fd43cf6eSHans Rosenfeld } 5868*fd43cf6eSHans Rosenfeld DTRACE_PROBE5(add__channel, uint8_t, chan->chan, 5869*fd43cf6eSHans Rosenfeld uint32_t, chan->flags, uint8_t, chan->rf_gain, 5870*fd43cf6eSHans Rosenfeld uint16_t, chan->active, uint16_t, chan->passive); 5871*fd43cf6eSHans Rosenfeld hdr->nchan++; 5872*fd43cf6eSHans Rosenfeld chan++; 5873*fd43cf6eSHans Rosenfeld } 5874*fd43cf6eSHans Rosenfeld 5875*fd43cf6eSHans Rosenfeld /*LINTED: E_PTRDIFF_OVERFLOW*/ 5876*fd43cf6eSHans Rosenfeld buflen = (uint8_t *)chan - buf; 5877*fd43cf6eSHans Rosenfeld hdr->len = htole16(buflen); 5878*fd43cf6eSHans Rosenfeld 5879*fd43cf6eSHans Rosenfeld error = iwn_cmd(sc, IWN_CMD_SCAN, buf, buflen, 1); 5880*fd43cf6eSHans Rosenfeld kmem_free(buf, IWN_SCAN_MAXSZ); 5881*fd43cf6eSHans Rosenfeld return error; 5882*fd43cf6eSHans Rosenfeld } 5883*fd43cf6eSHans Rosenfeld 5884*fd43cf6eSHans Rosenfeld static int 5885*fd43cf6eSHans Rosenfeld iwn_auth(struct iwn_softc *sc) 5886*fd43cf6eSHans Rosenfeld { 5887*fd43cf6eSHans Rosenfeld struct iwn_ops *ops = &sc->ops; 5888*fd43cf6eSHans Rosenfeld struct ieee80211com *ic = &sc->sc_ic; 5889*fd43cf6eSHans Rosenfeld struct ieee80211_node *ni = ic->ic_bss; 5890*fd43cf6eSHans Rosenfeld int error; 5891*fd43cf6eSHans Rosenfeld 5892*fd43cf6eSHans Rosenfeld ASSERT(ni->in_chan != NULL); 5893*fd43cf6eSHans Rosenfeld 5894*fd43cf6eSHans Rosenfeld /* Update adapter configuration. */ 5895*fd43cf6eSHans Rosenfeld IEEE80211_ADDR_COPY(sc->rxon.bssid, ni->in_bssid); 5896*fd43cf6eSHans Rosenfeld sc->rxon.chan = ieee80211_chan2ieee(ic, ni->in_chan); 5897*fd43cf6eSHans Rosenfeld sc->rxon.flags = htole32(IWN_RXON_TSF | IWN_RXON_CTS_TO_SELF); 5898*fd43cf6eSHans Rosenfeld if ((ni->in_chan != IEEE80211_CHAN_ANYC) && 5899*fd43cf6eSHans Rosenfeld IEEE80211_IS_CHAN_2GHZ(ni->in_chan)) 5900*fd43cf6eSHans Rosenfeld sc->rxon.flags |= htole32(IWN_RXON_AUTO | IWN_RXON_24GHZ); 5901*fd43cf6eSHans Rosenfeld if (ic->ic_flags & IEEE80211_F_SHSLOT) 5902*fd43cf6eSHans Rosenfeld sc->rxon.flags |= htole32(IWN_RXON_SHSLOT); 5903*fd43cf6eSHans Rosenfeld if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) 5904*fd43cf6eSHans Rosenfeld sc->rxon.flags |= htole32(IWN_RXON_SHPREAMBLE); 5905*fd43cf6eSHans Rosenfeld switch (ic->ic_curmode) { 5906*fd43cf6eSHans Rosenfeld case IEEE80211_MODE_11A: 5907*fd43cf6eSHans Rosenfeld sc->rxon.cck_mask = 0; 5908*fd43cf6eSHans Rosenfeld sc->rxon.ofdm_mask = 0x15; 5909*fd43cf6eSHans Rosenfeld break; 5910*fd43cf6eSHans Rosenfeld case IEEE80211_MODE_11B: 5911*fd43cf6eSHans Rosenfeld sc->rxon.cck_mask = 0x03; 5912*fd43cf6eSHans Rosenfeld sc->rxon.ofdm_mask = 0; 5913*fd43cf6eSHans Rosenfeld break; 5914*fd43cf6eSHans Rosenfeld default: /* Assume 802.11b/g. */ 5915*fd43cf6eSHans Rosenfeld sc->rxon.cck_mask = 0x0f; 5916*fd43cf6eSHans Rosenfeld sc->rxon.ofdm_mask = 0x15; 5917*fd43cf6eSHans Rosenfeld } 5918*fd43cf6eSHans Rosenfeld DTRACE_PROBE2(rxon, struct iwn_rxon *, &sc->rxon, int, sc->rxonsz); 5919*fd43cf6eSHans Rosenfeld error = iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, sc->rxonsz, 1); 5920*fd43cf6eSHans Rosenfeld if (error != 0) { 5921*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 5922*fd43cf6eSHans Rosenfeld "!RXON command failed"); 5923*fd43cf6eSHans Rosenfeld return error; 5924*fd43cf6eSHans Rosenfeld } 5925*fd43cf6eSHans Rosenfeld 5926*fd43cf6eSHans Rosenfeld /* Configuration has changed, set TX power accordingly. */ 5927*fd43cf6eSHans Rosenfeld if ((error = ops->set_txpower(sc, 1)) != 0) { 5928*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 5929*fd43cf6eSHans Rosenfeld "!could not set TX power"); 5930*fd43cf6eSHans Rosenfeld return error; 5931*fd43cf6eSHans Rosenfeld } 5932*fd43cf6eSHans Rosenfeld /* 5933*fd43cf6eSHans Rosenfeld * Reconfiguring RXON clears the firmware nodes table so we must 5934*fd43cf6eSHans Rosenfeld * add the broadcast node again. 5935*fd43cf6eSHans Rosenfeld */ 5936*fd43cf6eSHans Rosenfeld if ((error = iwn_add_broadcast_node(sc, 1)) != 0) { 5937*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 5938*fd43cf6eSHans Rosenfeld "!could not add broadcast node"); 5939*fd43cf6eSHans Rosenfeld return error; 5940*fd43cf6eSHans Rosenfeld } 5941*fd43cf6eSHans Rosenfeld return 0; 5942*fd43cf6eSHans Rosenfeld } 5943*fd43cf6eSHans Rosenfeld 5944*fd43cf6eSHans Rosenfeld static int 5945*fd43cf6eSHans Rosenfeld iwn_fast_recover(struct iwn_softc *sc) 5946*fd43cf6eSHans Rosenfeld { 5947*fd43cf6eSHans Rosenfeld int err = IWN_FAIL; 5948*fd43cf6eSHans Rosenfeld 5949*fd43cf6eSHans Rosenfeld mutex_enter(&sc->sc_mtx); 5950*fd43cf6eSHans Rosenfeld 5951*fd43cf6eSHans Rosenfeld /* restore runtime configuration */ 5952*fd43cf6eSHans Rosenfeld bcopy(&sc->rxon_save, &sc->rxon, 5953*fd43cf6eSHans Rosenfeld sizeof (sc->rxon)); 5954*fd43cf6eSHans Rosenfeld 5955*fd43cf6eSHans Rosenfeld sc->rxon.associd = 0; 5956*fd43cf6eSHans Rosenfeld sc->rxon.filter &= ~htole32(IWN_FILTER_BSS); 5957*fd43cf6eSHans Rosenfeld 5958*fd43cf6eSHans Rosenfeld if ((err = iwn_auth(sc)) != IWN_SUCCESS) { 5959*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, "!iwn_fast_recover(): " 5960*fd43cf6eSHans Rosenfeld "could not setup authentication"); 5961*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_mtx); 5962*fd43cf6eSHans Rosenfeld return (err); 5963*fd43cf6eSHans Rosenfeld } 5964*fd43cf6eSHans Rosenfeld 5965*fd43cf6eSHans Rosenfeld bcopy(&sc->rxon_save, &sc->rxon, sizeof (sc->rxon)); 5966*fd43cf6eSHans Rosenfeld 5967*fd43cf6eSHans Rosenfeld /* update adapter's configuration */ 5968*fd43cf6eSHans Rosenfeld err = iwn_run(sc); 5969*fd43cf6eSHans Rosenfeld if (err != IWN_SUCCESS) { 5970*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, "!iwn_fast_recover(): " 5971*fd43cf6eSHans Rosenfeld "failed to setup association"); 5972*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_mtx); 5973*fd43cf6eSHans Rosenfeld return (err); 5974*fd43cf6eSHans Rosenfeld } 5975*fd43cf6eSHans Rosenfeld /* set LED on */ 5976*fd43cf6eSHans Rosenfeld iwn_set_led(sc, IWN_LED_LINK, 0, 1); 5977*fd43cf6eSHans Rosenfeld 5978*fd43cf6eSHans Rosenfeld sc->sc_flags &= ~IWN_FLAG_HW_ERR_RECOVER; 5979*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_mtx); 5980*fd43cf6eSHans Rosenfeld 5981*fd43cf6eSHans Rosenfeld /* start queue */ 5982*fd43cf6eSHans Rosenfeld DTRACE_PROBE(resume__xmit); 5983*fd43cf6eSHans Rosenfeld 5984*fd43cf6eSHans Rosenfeld return (IWN_SUCCESS); 5985*fd43cf6eSHans Rosenfeld } 5986*fd43cf6eSHans Rosenfeld 5987*fd43cf6eSHans Rosenfeld static int 5988*fd43cf6eSHans Rosenfeld iwn_run(struct iwn_softc *sc) 5989*fd43cf6eSHans Rosenfeld { 5990*fd43cf6eSHans Rosenfeld struct iwn_ops *ops = &sc->ops; 5991*fd43cf6eSHans Rosenfeld struct ieee80211com *ic = &sc->sc_ic; 5992*fd43cf6eSHans Rosenfeld struct ieee80211_node *ni = ic->ic_bss; 5993*fd43cf6eSHans Rosenfeld struct iwn_node_info node; 5994*fd43cf6eSHans Rosenfeld int error; 5995*fd43cf6eSHans Rosenfeld 5996*fd43cf6eSHans Rosenfeld if (ic->ic_opmode == IEEE80211_M_MONITOR) { 5997*fd43cf6eSHans Rosenfeld /* Link LED blinks while monitoring. */ 5998*fd43cf6eSHans Rosenfeld iwn_set_led(sc, IWN_LED_LINK, 5, 5); 5999*fd43cf6eSHans Rosenfeld return 0; 6000*fd43cf6eSHans Rosenfeld } 6001*fd43cf6eSHans Rosenfeld if ((error = iwn_set_timing(sc, ni)) != 0) { 6002*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 6003*fd43cf6eSHans Rosenfeld "!could not set timing"); 6004*fd43cf6eSHans Rosenfeld return error; 6005*fd43cf6eSHans Rosenfeld } 6006*fd43cf6eSHans Rosenfeld 6007*fd43cf6eSHans Rosenfeld /* Update adapter configuration. */ 6008*fd43cf6eSHans Rosenfeld IEEE80211_ADDR_COPY(sc->rxon.bssid, ni->in_bssid); 6009*fd43cf6eSHans Rosenfeld sc->rxon.associd = htole16(IEEE80211_AID(ni->in_associd)); 6010*fd43cf6eSHans Rosenfeld /* Short preamble and slot time are negotiated when associating. */ 6011*fd43cf6eSHans Rosenfeld sc->rxon.flags &= ~htole32(IWN_RXON_SHPREAMBLE | IWN_RXON_SHSLOT); 6012*fd43cf6eSHans Rosenfeld if (ic->ic_flags & IEEE80211_F_SHSLOT) 6013*fd43cf6eSHans Rosenfeld sc->rxon.flags |= htole32(IWN_RXON_SHSLOT); 6014*fd43cf6eSHans Rosenfeld if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) 6015*fd43cf6eSHans Rosenfeld sc->rxon.flags |= htole32(IWN_RXON_SHPREAMBLE); 6016*fd43cf6eSHans Rosenfeld sc->rxon.filter |= htole32(IWN_FILTER_BSS); 6017*fd43cf6eSHans Rosenfeld if (ic->ic_opmode != IEEE80211_M_STA && 6018*fd43cf6eSHans Rosenfeld ic->ic_opmode != IEEE80211_M_IBSS) 6019*fd43cf6eSHans Rosenfeld sc->rxon.filter |= htole32(IWN_FILTER_BEACON); 6020*fd43cf6eSHans Rosenfeld DTRACE_PROBE2(rxon, struct iwn_rxon *, &sc->rxon, int, sc->rxonsz); 6021*fd43cf6eSHans Rosenfeld error = iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, sc->rxonsz, 1); 6022*fd43cf6eSHans Rosenfeld if (error != 0) { 6023*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 6024*fd43cf6eSHans Rosenfeld "!could not update configuration"); 6025*fd43cf6eSHans Rosenfeld return error; 6026*fd43cf6eSHans Rosenfeld } 6027*fd43cf6eSHans Rosenfeld 6028*fd43cf6eSHans Rosenfeld /* Configuration has changed, set TX power accordingly. */ 6029*fd43cf6eSHans Rosenfeld if ((error = ops->set_txpower(sc, 1)) != 0) { 6030*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 6031*fd43cf6eSHans Rosenfeld "!could not set TX power"); 6032*fd43cf6eSHans Rosenfeld return error; 6033*fd43cf6eSHans Rosenfeld } 6034*fd43cf6eSHans Rosenfeld 6035*fd43cf6eSHans Rosenfeld /* Fake a join to initialize the TX rate. */ 6036*fd43cf6eSHans Rosenfeld ((struct iwn_node *)ni)->id = IWN_ID_BSS; 6037*fd43cf6eSHans Rosenfeld iwn_newassoc(ni, 1); 6038*fd43cf6eSHans Rosenfeld 6039*fd43cf6eSHans Rosenfeld /* Add BSS node. */ 6040*fd43cf6eSHans Rosenfeld memset(&node, 0, sizeof node); 6041*fd43cf6eSHans Rosenfeld IEEE80211_ADDR_COPY(node.macaddr, ni->in_macaddr); 6042*fd43cf6eSHans Rosenfeld node.id = IWN_ID_BSS; 6043*fd43cf6eSHans Rosenfeld #ifdef notyet 6044*fd43cf6eSHans Rosenfeld node.htflags = htole32(IWN_AMDPU_SIZE_FACTOR(3) | 6045*fd43cf6eSHans Rosenfeld IWN_AMDPU_DENSITY(5)); /* 2us */ 6046*fd43cf6eSHans Rosenfeld #endif 6047*fd43cf6eSHans Rosenfeld error = ops->add_node(sc, &node, 1); 6048*fd43cf6eSHans Rosenfeld if (error != 0) { 6049*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 6050*fd43cf6eSHans Rosenfeld "!could not add BSS node"); 6051*fd43cf6eSHans Rosenfeld return error; 6052*fd43cf6eSHans Rosenfeld } 6053*fd43cf6eSHans Rosenfeld if ((error = iwn_set_link_quality(sc, ni)) != 0) { 6054*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 6055*fd43cf6eSHans Rosenfeld "!could not setup link quality for node %d", node.id); 6056*fd43cf6eSHans Rosenfeld return error; 6057*fd43cf6eSHans Rosenfeld } 6058*fd43cf6eSHans Rosenfeld 6059*fd43cf6eSHans Rosenfeld if ((error = iwn_init_sensitivity(sc)) != 0) { 6060*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 6061*fd43cf6eSHans Rosenfeld "!could not set sensitivity"); 6062*fd43cf6eSHans Rosenfeld return error; 6063*fd43cf6eSHans Rosenfeld } 6064*fd43cf6eSHans Rosenfeld 6065*fd43cf6eSHans Rosenfeld if ((error = iwn_qosparam_to_hw(sc, 1)) != 0) { 6066*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 6067*fd43cf6eSHans Rosenfeld "!could not set QoS params"); 6068*fd43cf6eSHans Rosenfeld return (error); 6069*fd43cf6eSHans Rosenfeld } 6070*fd43cf6eSHans Rosenfeld 6071*fd43cf6eSHans Rosenfeld /* Start periodic calibration timer. */ 6072*fd43cf6eSHans Rosenfeld sc->sc_flags &= ~IWN_FLAG_STOP_CALIB_TO; 6073*fd43cf6eSHans Rosenfeld sc->calib.state = IWN_CALIB_STATE_ASSOC; 6074*fd43cf6eSHans Rosenfeld sc->calib_cnt = 0; 6075*fd43cf6eSHans Rosenfeld sc->calib_to = timeout(iwn_calib_timeout, sc, drv_usectohz(500000)); 6076*fd43cf6eSHans Rosenfeld 6077*fd43cf6eSHans Rosenfeld /* Link LED always on while associated. */ 6078*fd43cf6eSHans Rosenfeld iwn_set_led(sc, IWN_LED_LINK, 0, 1); 6079*fd43cf6eSHans Rosenfeld return 0; 6080*fd43cf6eSHans Rosenfeld } 6081*fd43cf6eSHans Rosenfeld 6082*fd43cf6eSHans Rosenfeld #ifdef IWN_HWCRYPTO 6083*fd43cf6eSHans Rosenfeld /* 6084*fd43cf6eSHans Rosenfeld * We support CCMP hardware encryption/decryption of unicast frames only. 6085*fd43cf6eSHans Rosenfeld * HW support for TKIP really sucks. We should let TKIP die anyway. 6086*fd43cf6eSHans Rosenfeld */ 6087*fd43cf6eSHans Rosenfeld static int 6088*fd43cf6eSHans Rosenfeld iwn_set_key(struct ieee80211com *ic, struct ieee80211_node *ni, 6089*fd43cf6eSHans Rosenfeld struct ieee80211_key *k) 6090*fd43cf6eSHans Rosenfeld { 6091*fd43cf6eSHans Rosenfeld struct iwn_softc *sc = ic->ic_softc; 6092*fd43cf6eSHans Rosenfeld struct iwn_ops *ops = &sc->ops; 6093*fd43cf6eSHans Rosenfeld struct iwn_node *wn = (void *)ni; 6094*fd43cf6eSHans Rosenfeld struct iwn_node_info node; 6095*fd43cf6eSHans Rosenfeld uint16_t kflags; 6096*fd43cf6eSHans Rosenfeld 6097*fd43cf6eSHans Rosenfeld if ((k->k_flags & IEEE80211_KEY_GROUP) || 6098*fd43cf6eSHans Rosenfeld k->k_cipher != IEEE80211_CIPHER_CCMP) 6099*fd43cf6eSHans Rosenfeld return ieee80211_set_key(ic, ni, k); 6100*fd43cf6eSHans Rosenfeld 6101*fd43cf6eSHans Rosenfeld kflags = IWN_KFLAG_CCMP | IWN_KFLAG_MAP | IWN_KFLAG_KID(k->k_id); 6102*fd43cf6eSHans Rosenfeld if (k->k_flags & IEEE80211_KEY_GROUP) 6103*fd43cf6eSHans Rosenfeld kflags |= IWN_KFLAG_GROUP; 6104*fd43cf6eSHans Rosenfeld 6105*fd43cf6eSHans Rosenfeld memset(&node, 0, sizeof node); 6106*fd43cf6eSHans Rosenfeld node.id = (k->k_flags & IEEE80211_KEY_GROUP) ? 6107*fd43cf6eSHans Rosenfeld sc->broadcast_id : wn->id; 6108*fd43cf6eSHans Rosenfeld node.control = IWN_NODE_UPDATE; 6109*fd43cf6eSHans Rosenfeld node.flags = IWN_FLAG_SET_KEY; 6110*fd43cf6eSHans Rosenfeld node.kflags = htole16(kflags); 6111*fd43cf6eSHans Rosenfeld node.kid = k->k_id; 6112*fd43cf6eSHans Rosenfeld memcpy(node.key, k->k_key, k->k_len); 6113*fd43cf6eSHans Rosenfeld DTRACE_PROBE2(set__key, int, k->k_id, int, node.id); 6114*fd43cf6eSHans Rosenfeld return ops->add_node(sc, &node, 1); 6115*fd43cf6eSHans Rosenfeld } 6116*fd43cf6eSHans Rosenfeld 6117*fd43cf6eSHans Rosenfeld static void 6118*fd43cf6eSHans Rosenfeld iwn_delete_key(struct ieee80211com *ic, struct ieee80211_node *ni, 6119*fd43cf6eSHans Rosenfeld struct ieee80211_key *k) 6120*fd43cf6eSHans Rosenfeld { 6121*fd43cf6eSHans Rosenfeld struct iwn_softc *sc = ic->ic_softc; 6122*fd43cf6eSHans Rosenfeld struct iwn_ops *ops = &sc->ops; 6123*fd43cf6eSHans Rosenfeld struct iwn_node *wn = (void *)ni; 6124*fd43cf6eSHans Rosenfeld struct iwn_node_info node; 6125*fd43cf6eSHans Rosenfeld 6126*fd43cf6eSHans Rosenfeld if ((k->k_flags & IEEE80211_KEY_GROUP) || 6127*fd43cf6eSHans Rosenfeld k->k_cipher != IEEE80211_CIPHER_CCMP) { 6128*fd43cf6eSHans Rosenfeld /* See comment about other ciphers above. */ 6129*fd43cf6eSHans Rosenfeld ieee80211_delete_key(ic, ni, k); 6130*fd43cf6eSHans Rosenfeld return; 6131*fd43cf6eSHans Rosenfeld } 6132*fd43cf6eSHans Rosenfeld if (ic->ic_state != IEEE80211_S_RUN) 6133*fd43cf6eSHans Rosenfeld return; /* Nothing to do. */ 6134*fd43cf6eSHans Rosenfeld memset(&node, 0, sizeof node); 6135*fd43cf6eSHans Rosenfeld node.id = (k->k_flags & IEEE80211_KEY_GROUP) ? 6136*fd43cf6eSHans Rosenfeld sc->broadcast_id : wn->id; 6137*fd43cf6eSHans Rosenfeld node.control = IWN_NODE_UPDATE; 6138*fd43cf6eSHans Rosenfeld node.flags = IWN_FLAG_SET_KEY; 6139*fd43cf6eSHans Rosenfeld node.kflags = htole16(IWN_KFLAG_INVALID); 6140*fd43cf6eSHans Rosenfeld node.kid = 0xff; 6141*fd43cf6eSHans Rosenfeld DTRACE_PROBE1(del__key, int, node.id); 6142*fd43cf6eSHans Rosenfeld (void)ops->add_node(sc, &node, 1); 6143*fd43cf6eSHans Rosenfeld } 6144*fd43cf6eSHans Rosenfeld #endif 6145*fd43cf6eSHans Rosenfeld 6146*fd43cf6eSHans Rosenfeld #ifndef IEEE80211_NO_HT 6147*fd43cf6eSHans Rosenfeld /* 6148*fd43cf6eSHans Rosenfeld * This function is called by upper layer when an ADDBA request is received 6149*fd43cf6eSHans Rosenfeld * from another STA and before the ADDBA response is sent. 6150*fd43cf6eSHans Rosenfeld */ 6151*fd43cf6eSHans Rosenfeld static int 6152*fd43cf6eSHans Rosenfeld iwn_ampdu_rx_start(struct ieee80211com *ic, struct ieee80211_node *ni, 6153*fd43cf6eSHans Rosenfeld uint8_t tid) 6154*fd43cf6eSHans Rosenfeld { 6155*fd43cf6eSHans Rosenfeld struct ieee80211_rx_ba *ba = &ni->in_rx_ba[tid]; 6156*fd43cf6eSHans Rosenfeld struct iwn_softc *sc = ic->ic_softc; 6157*fd43cf6eSHans Rosenfeld struct iwn_ops *ops = &sc->ops; 6158*fd43cf6eSHans Rosenfeld struct iwn_node *wn = (void *)ni; 6159*fd43cf6eSHans Rosenfeld struct iwn_node_info node; 6160*fd43cf6eSHans Rosenfeld 6161*fd43cf6eSHans Rosenfeld memset(&node, 0, sizeof node); 6162*fd43cf6eSHans Rosenfeld node.id = wn->id; 6163*fd43cf6eSHans Rosenfeld node.control = IWN_NODE_UPDATE; 6164*fd43cf6eSHans Rosenfeld node.flags = IWN_FLAG_SET_ADDBA; 6165*fd43cf6eSHans Rosenfeld node.addba_tid = tid; 6166*fd43cf6eSHans Rosenfeld node.addba_ssn = htole16(ba->ba_winstart); 6167*fd43cf6eSHans Rosenfeld DTRACE_PROBE3(addba, uint8_t, wn->id, uint8_t, tid, int, ba->ba_winstart); 6168*fd43cf6eSHans Rosenfeld return ops->add_node(sc, &node, 1); 6169*fd43cf6eSHans Rosenfeld } 6170*fd43cf6eSHans Rosenfeld 6171*fd43cf6eSHans Rosenfeld /* 6172*fd43cf6eSHans Rosenfeld * This function is called by upper layer on teardown of an HT-immediate 6173*fd43cf6eSHans Rosenfeld * Block Ack agreement (eg. uppon receipt of a DELBA frame). 6174*fd43cf6eSHans Rosenfeld */ 6175*fd43cf6eSHans Rosenfeld static void 6176*fd43cf6eSHans Rosenfeld iwn_ampdu_rx_stop(struct ieee80211com *ic, struct ieee80211_node *ni, 6177*fd43cf6eSHans Rosenfeld uint8_t tid) 6178*fd43cf6eSHans Rosenfeld { 6179*fd43cf6eSHans Rosenfeld struct iwn_softc *sc = ic->ic_softc; 6180*fd43cf6eSHans Rosenfeld struct iwn_ops *ops = &sc->ops; 6181*fd43cf6eSHans Rosenfeld struct iwn_node *wn = (void *)ni; 6182*fd43cf6eSHans Rosenfeld struct iwn_node_info node; 6183*fd43cf6eSHans Rosenfeld 6184*fd43cf6eSHans Rosenfeld memset(&node, 0, sizeof node); 6185*fd43cf6eSHans Rosenfeld node.id = wn->id; 6186*fd43cf6eSHans Rosenfeld node.control = IWN_NODE_UPDATE; 6187*fd43cf6eSHans Rosenfeld node.flags = IWN_FLAG_SET_DELBA; 6188*fd43cf6eSHans Rosenfeld node.delba_tid = tid; 6189*fd43cf6eSHans Rosenfeld DTRACE_PROBE2(delba, uint8_t, wn->id, uint8_t, tid); 6190*fd43cf6eSHans Rosenfeld (void)ops->add_node(sc, &node, 1); 6191*fd43cf6eSHans Rosenfeld } 6192*fd43cf6eSHans Rosenfeld 6193*fd43cf6eSHans Rosenfeld /* 6194*fd43cf6eSHans Rosenfeld * This function is called by upper layer when an ADDBA response is received 6195*fd43cf6eSHans Rosenfeld * from another STA. 6196*fd43cf6eSHans Rosenfeld */ 6197*fd43cf6eSHans Rosenfeld static int 6198*fd43cf6eSHans Rosenfeld iwn_ampdu_tx_start(struct ieee80211com *ic, struct ieee80211_node *ni, 6199*fd43cf6eSHans Rosenfeld uint8_t tid) 6200*fd43cf6eSHans Rosenfeld { 6201*fd43cf6eSHans Rosenfeld struct ieee80211_tx_ba *ba = &ni->in_tx_ba[tid]; 6202*fd43cf6eSHans Rosenfeld struct iwn_softc *sc = ic->ic_softc; 6203*fd43cf6eSHans Rosenfeld struct iwn_ops *ops = &sc->ops; 6204*fd43cf6eSHans Rosenfeld struct iwn_node *wn = (void *)ni; 6205*fd43cf6eSHans Rosenfeld struct iwn_node_info node; 6206*fd43cf6eSHans Rosenfeld int error; 6207*fd43cf6eSHans Rosenfeld 6208*fd43cf6eSHans Rosenfeld /* Enable TX for the specified RA/TID. */ 6209*fd43cf6eSHans Rosenfeld wn->disable_tid &= ~(1 << tid); 6210*fd43cf6eSHans Rosenfeld memset(&node, 0, sizeof node); 6211*fd43cf6eSHans Rosenfeld node.id = wn->id; 6212*fd43cf6eSHans Rosenfeld node.control = IWN_NODE_UPDATE; 6213*fd43cf6eSHans Rosenfeld node.flags = IWN_FLAG_SET_DISABLE_TID; 6214*fd43cf6eSHans Rosenfeld node.disable_tid = htole16(wn->disable_tid); 6215*fd43cf6eSHans Rosenfeld error = ops->add_node(sc, &node, 1); 6216*fd43cf6eSHans Rosenfeld if (error != 0) 6217*fd43cf6eSHans Rosenfeld return error; 6218*fd43cf6eSHans Rosenfeld 6219*fd43cf6eSHans Rosenfeld if ((error = iwn_nic_lock(sc)) != 0) 6220*fd43cf6eSHans Rosenfeld return error; 6221*fd43cf6eSHans Rosenfeld ops->ampdu_tx_start(sc, ni, tid, ba->ba_winstart); 6222*fd43cf6eSHans Rosenfeld iwn_nic_unlock(sc); 6223*fd43cf6eSHans Rosenfeld return 0; 6224*fd43cf6eSHans Rosenfeld } 6225*fd43cf6eSHans Rosenfeld 6226*fd43cf6eSHans Rosenfeld static void 6227*fd43cf6eSHans Rosenfeld iwn_ampdu_tx_stop(struct ieee80211com *ic, struct ieee80211_node *ni, 6228*fd43cf6eSHans Rosenfeld uint8_t tid) 6229*fd43cf6eSHans Rosenfeld { 6230*fd43cf6eSHans Rosenfeld struct ieee80211_tx_ba *ba = &ni->in_tx_ba[tid]; 6231*fd43cf6eSHans Rosenfeld struct iwn_softc *sc = ic->ic_softc; 6232*fd43cf6eSHans Rosenfeld struct iwn_ops *ops = &sc->ops; 6233*fd43cf6eSHans Rosenfeld 6234*fd43cf6eSHans Rosenfeld if (iwn_nic_lock(sc) != 0) 6235*fd43cf6eSHans Rosenfeld return; 6236*fd43cf6eSHans Rosenfeld ops->ampdu_tx_stop(sc, tid, ba->ba_winstart); 6237*fd43cf6eSHans Rosenfeld iwn_nic_unlock(sc); 6238*fd43cf6eSHans Rosenfeld } 6239*fd43cf6eSHans Rosenfeld 6240*fd43cf6eSHans Rosenfeld static void 6241*fd43cf6eSHans Rosenfeld iwn4965_ampdu_tx_start(struct iwn_softc *sc, struct ieee80211_node *ni, 6242*fd43cf6eSHans Rosenfeld uint8_t tid, uint16_t ssn) 6243*fd43cf6eSHans Rosenfeld { 6244*fd43cf6eSHans Rosenfeld struct iwn_node *wn = (void *)ni; 6245*fd43cf6eSHans Rosenfeld int qid = 7 + tid; 6246*fd43cf6eSHans Rosenfeld 6247*fd43cf6eSHans Rosenfeld /* Stop TX scheduler while we're changing its configuration. */ 6248*fd43cf6eSHans Rosenfeld iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid), 6249*fd43cf6eSHans Rosenfeld IWN4965_TXQ_STATUS_CHGACT); 6250*fd43cf6eSHans Rosenfeld 6251*fd43cf6eSHans Rosenfeld /* Assign RA/TID translation to the queue. */ 6252*fd43cf6eSHans Rosenfeld iwn_mem_write_2(sc, sc->sched_base + IWN4965_SCHED_TRANS_TBL(qid), 6253*fd43cf6eSHans Rosenfeld wn->id << 4 | tid); 6254*fd43cf6eSHans Rosenfeld 6255*fd43cf6eSHans Rosenfeld /* Enable chain-building mode for the queue. */ 6256*fd43cf6eSHans Rosenfeld iwn_prph_setbits(sc, IWN4965_SCHED_QCHAIN_SEL, 1 << qid); 6257*fd43cf6eSHans Rosenfeld 6258*fd43cf6eSHans Rosenfeld /* Set starting sequence number from the ADDBA request. */ 6259*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff)); 6260*fd43cf6eSHans Rosenfeld iwn_prph_write(sc, IWN4965_SCHED_QUEUE_RDPTR(qid), ssn); 6261*fd43cf6eSHans Rosenfeld 6262*fd43cf6eSHans Rosenfeld /* Set scheduler window size. */ 6263*fd43cf6eSHans Rosenfeld iwn_mem_write(sc, sc->sched_base + IWN4965_SCHED_QUEUE_OFFSET(qid), 6264*fd43cf6eSHans Rosenfeld IWN_SCHED_WINSZ); 6265*fd43cf6eSHans Rosenfeld /* Set scheduler frame limit. */ 6266*fd43cf6eSHans Rosenfeld iwn_mem_write(sc, sc->sched_base + IWN4965_SCHED_QUEUE_OFFSET(qid) + 4, 6267*fd43cf6eSHans Rosenfeld IWN_SCHED_LIMIT << 16); 6268*fd43cf6eSHans Rosenfeld 6269*fd43cf6eSHans Rosenfeld /* Enable interrupts for the queue. */ 6270*fd43cf6eSHans Rosenfeld iwn_prph_setbits(sc, IWN4965_SCHED_INTR_MASK, 1 << qid); 6271*fd43cf6eSHans Rosenfeld 6272*fd43cf6eSHans Rosenfeld /* Mark the queue as active. */ 6273*fd43cf6eSHans Rosenfeld iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid), 6274*fd43cf6eSHans Rosenfeld IWN4965_TXQ_STATUS_ACTIVE | IWN4965_TXQ_STATUS_AGGR_ENA | 6275*fd43cf6eSHans Rosenfeld iwn_tid2fifo[tid] << 1); 6276*fd43cf6eSHans Rosenfeld } 6277*fd43cf6eSHans Rosenfeld 6278*fd43cf6eSHans Rosenfeld static void 6279*fd43cf6eSHans Rosenfeld iwn4965_ampdu_tx_stop(struct iwn_softc *sc, uint8_t tid, uint16_t ssn) 6280*fd43cf6eSHans Rosenfeld { 6281*fd43cf6eSHans Rosenfeld int qid = 7 + tid; 6282*fd43cf6eSHans Rosenfeld 6283*fd43cf6eSHans Rosenfeld /* Stop TX scheduler while we're changing its configuration. */ 6284*fd43cf6eSHans Rosenfeld iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid), 6285*fd43cf6eSHans Rosenfeld IWN4965_TXQ_STATUS_CHGACT); 6286*fd43cf6eSHans Rosenfeld 6287*fd43cf6eSHans Rosenfeld /* Set starting sequence number from the ADDBA request. */ 6288*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff)); 6289*fd43cf6eSHans Rosenfeld iwn_prph_write(sc, IWN4965_SCHED_QUEUE_RDPTR(qid), ssn); 6290*fd43cf6eSHans Rosenfeld 6291*fd43cf6eSHans Rosenfeld /* Disable interrupts for the queue. */ 6292*fd43cf6eSHans Rosenfeld iwn_prph_clrbits(sc, IWN4965_SCHED_INTR_MASK, 1 << qid); 6293*fd43cf6eSHans Rosenfeld 6294*fd43cf6eSHans Rosenfeld /* Mark the queue as inactive. */ 6295*fd43cf6eSHans Rosenfeld iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid), 6296*fd43cf6eSHans Rosenfeld IWN4965_TXQ_STATUS_INACTIVE | iwn_tid2fifo[tid] << 1); 6297*fd43cf6eSHans Rosenfeld } 6298*fd43cf6eSHans Rosenfeld 6299*fd43cf6eSHans Rosenfeld static void 6300*fd43cf6eSHans Rosenfeld iwn5000_ampdu_tx_start(struct iwn_softc *sc, struct ieee80211_node *ni, 6301*fd43cf6eSHans Rosenfeld uint8_t tid, uint16_t ssn) 6302*fd43cf6eSHans Rosenfeld { 6303*fd43cf6eSHans Rosenfeld struct iwn_node *wn = (void *)ni; 6304*fd43cf6eSHans Rosenfeld int qid = 10 + tid; 6305*fd43cf6eSHans Rosenfeld 6306*fd43cf6eSHans Rosenfeld /* Stop TX scheduler while we're changing its configuration. */ 6307*fd43cf6eSHans Rosenfeld iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid), 6308*fd43cf6eSHans Rosenfeld IWN5000_TXQ_STATUS_CHGACT); 6309*fd43cf6eSHans Rosenfeld 6310*fd43cf6eSHans Rosenfeld /* Assign RA/TID translation to the queue. */ 6311*fd43cf6eSHans Rosenfeld iwn_mem_write_2(sc, sc->sched_base + IWN5000_SCHED_TRANS_TBL(qid), 6312*fd43cf6eSHans Rosenfeld wn->id << 4 | tid); 6313*fd43cf6eSHans Rosenfeld 6314*fd43cf6eSHans Rosenfeld /* Enable chain-building mode for the queue. */ 6315*fd43cf6eSHans Rosenfeld iwn_prph_setbits(sc, IWN5000_SCHED_QCHAIN_SEL, 1 << qid); 6316*fd43cf6eSHans Rosenfeld 6317*fd43cf6eSHans Rosenfeld /* Enable aggregation for the queue. */ 6318*fd43cf6eSHans Rosenfeld iwn_prph_setbits(sc, IWN5000_SCHED_AGGR_SEL, 1 << qid); 6319*fd43cf6eSHans Rosenfeld 6320*fd43cf6eSHans Rosenfeld /* Set starting sequence number from the ADDBA request. */ 6321*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff)); 6322*fd43cf6eSHans Rosenfeld iwn_prph_write(sc, IWN5000_SCHED_QUEUE_RDPTR(qid), ssn); 6323*fd43cf6eSHans Rosenfeld 6324*fd43cf6eSHans Rosenfeld /* Set scheduler window size and frame limit. */ 6325*fd43cf6eSHans Rosenfeld iwn_mem_write(sc, sc->sched_base + IWN5000_SCHED_QUEUE_OFFSET(qid) + 4, 6326*fd43cf6eSHans Rosenfeld IWN_SCHED_LIMIT << 16 | IWN_SCHED_WINSZ); 6327*fd43cf6eSHans Rosenfeld 6328*fd43cf6eSHans Rosenfeld /* Enable interrupts for the queue. */ 6329*fd43cf6eSHans Rosenfeld iwn_prph_setbits(sc, IWN5000_SCHED_INTR_MASK, 1 << qid); 6330*fd43cf6eSHans Rosenfeld 6331*fd43cf6eSHans Rosenfeld /* Mark the queue as active. */ 6332*fd43cf6eSHans Rosenfeld iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid), 6333*fd43cf6eSHans Rosenfeld IWN5000_TXQ_STATUS_ACTIVE | iwn_tid2fifo[tid]); 6334*fd43cf6eSHans Rosenfeld } 6335*fd43cf6eSHans Rosenfeld 6336*fd43cf6eSHans Rosenfeld static void 6337*fd43cf6eSHans Rosenfeld iwn5000_ampdu_tx_stop(struct iwn_softc *sc, uint8_t tid, uint16_t ssn) 6338*fd43cf6eSHans Rosenfeld { 6339*fd43cf6eSHans Rosenfeld int qid = 10 + tid; 6340*fd43cf6eSHans Rosenfeld 6341*fd43cf6eSHans Rosenfeld /* Stop TX scheduler while we're changing its configuration. */ 6342*fd43cf6eSHans Rosenfeld iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid), 6343*fd43cf6eSHans Rosenfeld IWN5000_TXQ_STATUS_CHGACT); 6344*fd43cf6eSHans Rosenfeld 6345*fd43cf6eSHans Rosenfeld /* Disable aggregation for the queue. */ 6346*fd43cf6eSHans Rosenfeld iwn_prph_clrbits(sc, IWN5000_SCHED_AGGR_SEL, 1 << qid); 6347*fd43cf6eSHans Rosenfeld 6348*fd43cf6eSHans Rosenfeld /* Set starting sequence number from the ADDBA request. */ 6349*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff)); 6350*fd43cf6eSHans Rosenfeld iwn_prph_write(sc, IWN5000_SCHED_QUEUE_RDPTR(qid), ssn); 6351*fd43cf6eSHans Rosenfeld 6352*fd43cf6eSHans Rosenfeld /* Disable interrupts for the queue. */ 6353*fd43cf6eSHans Rosenfeld iwn_prph_clrbits(sc, IWN5000_SCHED_INTR_MASK, 1 << qid); 6354*fd43cf6eSHans Rosenfeld 6355*fd43cf6eSHans Rosenfeld /* Mark the queue as inactive. */ 6356*fd43cf6eSHans Rosenfeld iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid), 6357*fd43cf6eSHans Rosenfeld IWN5000_TXQ_STATUS_INACTIVE | iwn_tid2fifo[tid]); 6358*fd43cf6eSHans Rosenfeld } 6359*fd43cf6eSHans Rosenfeld #endif /* !IEEE80211_NO_HT */ 6360*fd43cf6eSHans Rosenfeld 6361*fd43cf6eSHans Rosenfeld /* 6362*fd43cf6eSHans Rosenfeld * Query calibration tables from the initialization firmware. We do this 6363*fd43cf6eSHans Rosenfeld * only once at first boot. Called from a process context. 6364*fd43cf6eSHans Rosenfeld */ 6365*fd43cf6eSHans Rosenfeld static int 6366*fd43cf6eSHans Rosenfeld iwn5000_query_calibration(struct iwn_softc *sc) 6367*fd43cf6eSHans Rosenfeld { 6368*fd43cf6eSHans Rosenfeld struct iwn5000_calib_config cmd; 6369*fd43cf6eSHans Rosenfeld int error; 6370*fd43cf6eSHans Rosenfeld clock_t clk; 6371*fd43cf6eSHans Rosenfeld 6372*fd43cf6eSHans Rosenfeld ASSERT(mutex_owned(&sc->sc_mtx)); 6373*fd43cf6eSHans Rosenfeld 6374*fd43cf6eSHans Rosenfeld memset(&cmd, 0, sizeof cmd); 6375*fd43cf6eSHans Rosenfeld cmd.ucode.once.enable = 0xffffffff; 6376*fd43cf6eSHans Rosenfeld cmd.ucode.once.start = 0xffffffff; 6377*fd43cf6eSHans Rosenfeld cmd.ucode.once.send = 0xffffffff; 6378*fd43cf6eSHans Rosenfeld cmd.ucode.flags = 0xffffffff; 6379*fd43cf6eSHans Rosenfeld error = iwn_cmd(sc, IWN5000_CMD_CALIB_CONFIG, &cmd, sizeof cmd, 0); 6380*fd43cf6eSHans Rosenfeld if (error != 0) 6381*fd43cf6eSHans Rosenfeld return error; 6382*fd43cf6eSHans Rosenfeld 6383*fd43cf6eSHans Rosenfeld /* Wait at most two seconds for calibration to complete. */ 6384*fd43cf6eSHans Rosenfeld clk = ddi_get_lbolt() + drv_usectohz(2000000); 6385*fd43cf6eSHans Rosenfeld while (!(sc->sc_flags & IWN_FLAG_CALIB_DONE)) 6386*fd43cf6eSHans Rosenfeld if (cv_timedwait(&sc->sc_calib_cv, &sc->sc_mtx, clk) < 0) 6387*fd43cf6eSHans Rosenfeld return (IWN_FAIL); 6388*fd43cf6eSHans Rosenfeld 6389*fd43cf6eSHans Rosenfeld return (IWN_SUCCESS); 6390*fd43cf6eSHans Rosenfeld } 6391*fd43cf6eSHans Rosenfeld 6392*fd43cf6eSHans Rosenfeld /* 6393*fd43cf6eSHans Rosenfeld * Send calibration results to the runtime firmware. These results were 6394*fd43cf6eSHans Rosenfeld * obtained on first boot from the initialization firmware. 6395*fd43cf6eSHans Rosenfeld */ 6396*fd43cf6eSHans Rosenfeld static int 6397*fd43cf6eSHans Rosenfeld iwn5000_send_calibration(struct iwn_softc *sc) 6398*fd43cf6eSHans Rosenfeld { 6399*fd43cf6eSHans Rosenfeld int idx, error; 6400*fd43cf6eSHans Rosenfeld 6401*fd43cf6eSHans Rosenfeld for (idx = 0; idx < 5; idx++) { 6402*fd43cf6eSHans Rosenfeld if (sc->calibcmd[idx].buf == NULL) 6403*fd43cf6eSHans Rosenfeld continue; /* No results available. */ 6404*fd43cf6eSHans Rosenfeld error = iwn_cmd(sc, IWN_CMD_PHY_CALIB, sc->calibcmd[idx].buf, 6405*fd43cf6eSHans Rosenfeld sc->calibcmd[idx].len, 0); 6406*fd43cf6eSHans Rosenfeld if (error != 0) { 6407*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 6408*fd43cf6eSHans Rosenfeld "!could not send calibration result"); 6409*fd43cf6eSHans Rosenfeld return error; 6410*fd43cf6eSHans Rosenfeld } 6411*fd43cf6eSHans Rosenfeld } 6412*fd43cf6eSHans Rosenfeld return 0; 6413*fd43cf6eSHans Rosenfeld } 6414*fd43cf6eSHans Rosenfeld 6415*fd43cf6eSHans Rosenfeld static int 6416*fd43cf6eSHans Rosenfeld iwn5000_send_wimax_coex(struct iwn_softc *sc) 6417*fd43cf6eSHans Rosenfeld { 6418*fd43cf6eSHans Rosenfeld struct iwn5000_wimax_coex wimax; 6419*fd43cf6eSHans Rosenfeld 6420*fd43cf6eSHans Rosenfeld #ifdef notyet 6421*fd43cf6eSHans Rosenfeld if (sc->hw_type == IWN_HW_REV_TYPE_6050) { 6422*fd43cf6eSHans Rosenfeld /* Enable WiMAX coexistence for combo adapters. */ 6423*fd43cf6eSHans Rosenfeld wimax.flags = 6424*fd43cf6eSHans Rosenfeld IWN_WIMAX_COEX_ASSOC_WA_UNMASK | 6425*fd43cf6eSHans Rosenfeld IWN_WIMAX_COEX_UNASSOC_WA_UNMASK | 6426*fd43cf6eSHans Rosenfeld IWN_WIMAX_COEX_STA_TABLE_VALID | 6427*fd43cf6eSHans Rosenfeld IWN_WIMAX_COEX_ENABLE; 6428*fd43cf6eSHans Rosenfeld memcpy(wimax.events, iwn6050_wimax_events, 6429*fd43cf6eSHans Rosenfeld sizeof iwn6050_wimax_events); 6430*fd43cf6eSHans Rosenfeld } else 6431*fd43cf6eSHans Rosenfeld #endif 6432*fd43cf6eSHans Rosenfeld { 6433*fd43cf6eSHans Rosenfeld /* Disable WiMAX coexistence. */ 6434*fd43cf6eSHans Rosenfeld wimax.flags = 0; 6435*fd43cf6eSHans Rosenfeld memset(wimax.events, 0, sizeof wimax.events); 6436*fd43cf6eSHans Rosenfeld } 6437*fd43cf6eSHans Rosenfeld return iwn_cmd(sc, IWN5000_CMD_WIMAX_COEX, &wimax, sizeof wimax, 0); 6438*fd43cf6eSHans Rosenfeld } 6439*fd43cf6eSHans Rosenfeld 6440*fd43cf6eSHans Rosenfeld static int 6441*fd43cf6eSHans Rosenfeld iwn6000_temp_offset_calib(struct iwn_softc *sc) 6442*fd43cf6eSHans Rosenfeld { 6443*fd43cf6eSHans Rosenfeld struct iwn6000_phy_calib_temp_offset cmd; 6444*fd43cf6eSHans Rosenfeld 6445*fd43cf6eSHans Rosenfeld memset(&cmd, 0, sizeof cmd); 6446*fd43cf6eSHans Rosenfeld cmd.code = IWN6000_PHY_CALIB_TEMP_OFFSET; 6447*fd43cf6eSHans Rosenfeld cmd.ngroups = 1; 6448*fd43cf6eSHans Rosenfeld cmd.isvalid = 1; 6449*fd43cf6eSHans Rosenfeld if (sc->eeprom_temp != 0) 6450*fd43cf6eSHans Rosenfeld cmd.offset = htole16(sc->eeprom_temp); 6451*fd43cf6eSHans Rosenfeld else 6452*fd43cf6eSHans Rosenfeld cmd.offset = htole16(IWN_DEFAULT_TEMP_OFFSET); 6453*fd43cf6eSHans Rosenfeld sc->sc_toff.t6000->toff.value.l = le16toh(cmd.offset); 6454*fd43cf6eSHans Rosenfeld return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 0); 6455*fd43cf6eSHans Rosenfeld } 6456*fd43cf6eSHans Rosenfeld 6457*fd43cf6eSHans Rosenfeld static int 6458*fd43cf6eSHans Rosenfeld iwn2000_temp_offset_calib(struct iwn_softc *sc) 6459*fd43cf6eSHans Rosenfeld { 6460*fd43cf6eSHans Rosenfeld struct iwn2000_phy_calib_temp_offset cmd; 6461*fd43cf6eSHans Rosenfeld 6462*fd43cf6eSHans Rosenfeld memset(&cmd, 0, sizeof cmd); 6463*fd43cf6eSHans Rosenfeld cmd.code = IWN2000_PHY_CALIB_TEMP_OFFSET; 6464*fd43cf6eSHans Rosenfeld cmd.ngroups = 1; 6465*fd43cf6eSHans Rosenfeld cmd.isvalid = 1; 6466*fd43cf6eSHans Rosenfeld if (sc->eeprom_rawtemp != 0) { 6467*fd43cf6eSHans Rosenfeld cmd.offset_low = htole16(sc->eeprom_rawtemp); 6468*fd43cf6eSHans Rosenfeld cmd.offset_high = htole16(sc->eeprom_temp); 6469*fd43cf6eSHans Rosenfeld } else { 6470*fd43cf6eSHans Rosenfeld cmd.offset_low = htole16(IWN_DEFAULT_TEMP_OFFSET); 6471*fd43cf6eSHans Rosenfeld cmd.offset_high = htole16(IWN_DEFAULT_TEMP_OFFSET); 6472*fd43cf6eSHans Rosenfeld } 6473*fd43cf6eSHans Rosenfeld cmd.burnt_voltage_ref = htole16(sc->eeprom_voltage); 6474*fd43cf6eSHans Rosenfeld sc->sc_toff.t2000->toff_lo.value.l = le16toh(cmd.offset_low); 6475*fd43cf6eSHans Rosenfeld sc->sc_toff.t2000->toff_hi.value.l = le16toh(cmd.offset_high); 6476*fd43cf6eSHans Rosenfeld sc->sc_toff.t2000->volt.value.l = le16toh(cmd.burnt_voltage_ref); 6477*fd43cf6eSHans Rosenfeld 6478*fd43cf6eSHans Rosenfeld return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 0); 6479*fd43cf6eSHans Rosenfeld } 6480*fd43cf6eSHans Rosenfeld 6481*fd43cf6eSHans Rosenfeld /* 6482*fd43cf6eSHans Rosenfeld * This function is called after the runtime firmware notifies us of its 6483*fd43cf6eSHans Rosenfeld * readiness (called in a process context). 6484*fd43cf6eSHans Rosenfeld */ 6485*fd43cf6eSHans Rosenfeld static int 6486*fd43cf6eSHans Rosenfeld iwn4965_post_alive(struct iwn_softc *sc) 6487*fd43cf6eSHans Rosenfeld { 6488*fd43cf6eSHans Rosenfeld int error, qid; 6489*fd43cf6eSHans Rosenfeld 6490*fd43cf6eSHans Rosenfeld if ((error = iwn_nic_lock(sc)) != 0) 6491*fd43cf6eSHans Rosenfeld return error; 6492*fd43cf6eSHans Rosenfeld 6493*fd43cf6eSHans Rosenfeld /* Clear TX scheduler state in SRAM. */ 6494*fd43cf6eSHans Rosenfeld sc->sched_base = iwn_prph_read(sc, IWN_SCHED_SRAM_ADDR); 6495*fd43cf6eSHans Rosenfeld iwn_mem_set_region_4(sc, sc->sched_base + IWN4965_SCHED_CTX_OFF, 0, 6496*fd43cf6eSHans Rosenfeld IWN4965_SCHED_CTX_LEN / sizeof (uint32_t)); 6497*fd43cf6eSHans Rosenfeld 6498*fd43cf6eSHans Rosenfeld /* Set physical address of TX scheduler rings (1KB aligned). */ 6499*fd43cf6eSHans Rosenfeld iwn_prph_write(sc, IWN4965_SCHED_DRAM_ADDR, sc->sched_dma.paddr >> 10); 6500*fd43cf6eSHans Rosenfeld 6501*fd43cf6eSHans Rosenfeld IWN_SETBITS(sc, IWN_FH_TX_CHICKEN, IWN_FH_TX_CHICKEN_SCHED_RETRY); 6502*fd43cf6eSHans Rosenfeld 6503*fd43cf6eSHans Rosenfeld /* Disable chain mode for all our 16 queues. */ 6504*fd43cf6eSHans Rosenfeld iwn_prph_write(sc, IWN4965_SCHED_QCHAIN_SEL, 0); 6505*fd43cf6eSHans Rosenfeld 6506*fd43cf6eSHans Rosenfeld for (qid = 0; qid < IWN4965_NTXQUEUES; qid++) { 6507*fd43cf6eSHans Rosenfeld iwn_prph_write(sc, IWN4965_SCHED_QUEUE_RDPTR(qid), 0); 6508*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | 0); 6509*fd43cf6eSHans Rosenfeld 6510*fd43cf6eSHans Rosenfeld /* Set scheduler window size. */ 6511*fd43cf6eSHans Rosenfeld iwn_mem_write(sc, sc->sched_base + 6512*fd43cf6eSHans Rosenfeld IWN4965_SCHED_QUEUE_OFFSET(qid), IWN_SCHED_WINSZ); 6513*fd43cf6eSHans Rosenfeld /* Set scheduler frame limit. */ 6514*fd43cf6eSHans Rosenfeld iwn_mem_write(sc, sc->sched_base + 6515*fd43cf6eSHans Rosenfeld IWN4965_SCHED_QUEUE_OFFSET(qid) + 4, 6516*fd43cf6eSHans Rosenfeld IWN_SCHED_LIMIT << 16); 6517*fd43cf6eSHans Rosenfeld } 6518*fd43cf6eSHans Rosenfeld 6519*fd43cf6eSHans Rosenfeld /* Enable interrupts for all our 16 queues. */ 6520*fd43cf6eSHans Rosenfeld iwn_prph_write(sc, IWN4965_SCHED_INTR_MASK, 0xffff); 6521*fd43cf6eSHans Rosenfeld /* Identify TX FIFO rings (0-7). */ 6522*fd43cf6eSHans Rosenfeld iwn_prph_write(sc, IWN4965_SCHED_TXFACT, 0xff); 6523*fd43cf6eSHans Rosenfeld 6524*fd43cf6eSHans Rosenfeld /* Mark TX rings (4 EDCA + cmd + 2 HCCA) as active. */ 6525*fd43cf6eSHans Rosenfeld for (qid = 0; qid < 7; qid++) { 6526*fd43cf6eSHans Rosenfeld static uint8_t qid2fifo[] = { 3, 2, 1, 0, 4, 5, 6 }; 6527*fd43cf6eSHans Rosenfeld iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid), 6528*fd43cf6eSHans Rosenfeld IWN4965_TXQ_STATUS_ACTIVE | qid2fifo[qid] << 1); 6529*fd43cf6eSHans Rosenfeld } 6530*fd43cf6eSHans Rosenfeld iwn_nic_unlock(sc); 6531*fd43cf6eSHans Rosenfeld return 0; 6532*fd43cf6eSHans Rosenfeld } 6533*fd43cf6eSHans Rosenfeld 6534*fd43cf6eSHans Rosenfeld /* 6535*fd43cf6eSHans Rosenfeld * This function is called after the initialization or runtime firmware 6536*fd43cf6eSHans Rosenfeld * notifies us of its readiness (called in a process context). 6537*fd43cf6eSHans Rosenfeld */ 6538*fd43cf6eSHans Rosenfeld static int 6539*fd43cf6eSHans Rosenfeld iwn5000_post_alive(struct iwn_softc *sc) 6540*fd43cf6eSHans Rosenfeld { 6541*fd43cf6eSHans Rosenfeld int error, qid; 6542*fd43cf6eSHans Rosenfeld 6543*fd43cf6eSHans Rosenfeld /* Switch to using ICT interrupt mode. */ 6544*fd43cf6eSHans Rosenfeld iwn5000_ict_reset(sc); 6545*fd43cf6eSHans Rosenfeld 6546*fd43cf6eSHans Rosenfeld if ((error = iwn_nic_lock(sc)) != 0) 6547*fd43cf6eSHans Rosenfeld return error; 6548*fd43cf6eSHans Rosenfeld 6549*fd43cf6eSHans Rosenfeld /* Clear TX scheduler state in SRAM. */ 6550*fd43cf6eSHans Rosenfeld sc->sched_base = iwn_prph_read(sc, IWN_SCHED_SRAM_ADDR); 6551*fd43cf6eSHans Rosenfeld iwn_mem_set_region_4(sc, sc->sched_base + IWN5000_SCHED_CTX_OFF, 0, 6552*fd43cf6eSHans Rosenfeld IWN5000_SCHED_CTX_LEN / sizeof (uint32_t)); 6553*fd43cf6eSHans Rosenfeld 6554*fd43cf6eSHans Rosenfeld /* Set physical address of TX scheduler rings (1KB aligned). */ 6555*fd43cf6eSHans Rosenfeld iwn_prph_write(sc, IWN5000_SCHED_DRAM_ADDR, sc->sched_dma.paddr >> 10); 6556*fd43cf6eSHans Rosenfeld 6557*fd43cf6eSHans Rosenfeld IWN_SETBITS(sc, IWN_FH_TX_CHICKEN, IWN_FH_TX_CHICKEN_SCHED_RETRY); 6558*fd43cf6eSHans Rosenfeld 6559*fd43cf6eSHans Rosenfeld /* Enable chain mode for all queues, except command queue. */ 6560*fd43cf6eSHans Rosenfeld iwn_prph_write(sc, IWN5000_SCHED_QCHAIN_SEL, 0xfffef); 6561*fd43cf6eSHans Rosenfeld iwn_prph_write(sc, IWN5000_SCHED_AGGR_SEL, 0); 6562*fd43cf6eSHans Rosenfeld 6563*fd43cf6eSHans Rosenfeld for (qid = 0; qid < IWN5000_NTXQUEUES; qid++) { 6564*fd43cf6eSHans Rosenfeld iwn_prph_write(sc, IWN5000_SCHED_QUEUE_RDPTR(qid), 0); 6565*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | 0); 6566*fd43cf6eSHans Rosenfeld 6567*fd43cf6eSHans Rosenfeld iwn_mem_write(sc, sc->sched_base + 6568*fd43cf6eSHans Rosenfeld IWN5000_SCHED_QUEUE_OFFSET(qid), 0); 6569*fd43cf6eSHans Rosenfeld /* Set scheduler window size and frame limit. */ 6570*fd43cf6eSHans Rosenfeld iwn_mem_write(sc, sc->sched_base + 6571*fd43cf6eSHans Rosenfeld IWN5000_SCHED_QUEUE_OFFSET(qid) + 4, 6572*fd43cf6eSHans Rosenfeld IWN_SCHED_LIMIT << 16 | IWN_SCHED_WINSZ); 6573*fd43cf6eSHans Rosenfeld } 6574*fd43cf6eSHans Rosenfeld 6575*fd43cf6eSHans Rosenfeld /* Enable interrupts for all our 20 queues. */ 6576*fd43cf6eSHans Rosenfeld iwn_prph_write(sc, IWN5000_SCHED_INTR_MASK, 0xfffff); 6577*fd43cf6eSHans Rosenfeld /* Identify TX FIFO rings (0-7). */ 6578*fd43cf6eSHans Rosenfeld iwn_prph_write(sc, IWN5000_SCHED_TXFACT, 0xff); 6579*fd43cf6eSHans Rosenfeld 6580*fd43cf6eSHans Rosenfeld /* Mark TX rings (4 EDCA + cmd + 2 HCCA) as active. */ 6581*fd43cf6eSHans Rosenfeld for (qid = 0; qid < 7; qid++) { 6582*fd43cf6eSHans Rosenfeld static uint8_t qid2fifo[] = { 3, 2, 1, 0, 7, 5, 6 }; 6583*fd43cf6eSHans Rosenfeld iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid), 6584*fd43cf6eSHans Rosenfeld IWN5000_TXQ_STATUS_ACTIVE | qid2fifo[qid]); 6585*fd43cf6eSHans Rosenfeld } 6586*fd43cf6eSHans Rosenfeld iwn_nic_unlock(sc); 6587*fd43cf6eSHans Rosenfeld 6588*fd43cf6eSHans Rosenfeld /* Configure WiMAX coexistence for combo adapters. */ 6589*fd43cf6eSHans Rosenfeld error = iwn5000_send_wimax_coex(sc); 6590*fd43cf6eSHans Rosenfeld if (error != 0) { 6591*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 6592*fd43cf6eSHans Rosenfeld "!could not configure WiMAX coexistence"); 6593*fd43cf6eSHans Rosenfeld return error; 6594*fd43cf6eSHans Rosenfeld } 6595*fd43cf6eSHans Rosenfeld if (sc->hw_type != IWN_HW_REV_TYPE_5150) { 6596*fd43cf6eSHans Rosenfeld struct iwn5000_phy_calib_crystal cmd; 6597*fd43cf6eSHans Rosenfeld 6598*fd43cf6eSHans Rosenfeld /* Perform crystal calibration. */ 6599*fd43cf6eSHans Rosenfeld memset(&cmd, 0, sizeof cmd); 6600*fd43cf6eSHans Rosenfeld cmd.code = IWN5000_PHY_CALIB_CRYSTAL; 6601*fd43cf6eSHans Rosenfeld cmd.ngroups = 1; 6602*fd43cf6eSHans Rosenfeld cmd.isvalid = 1; 6603*fd43cf6eSHans Rosenfeld cmd.cap_pin[0] = le32toh(sc->eeprom_crystal) & 0xff; 6604*fd43cf6eSHans Rosenfeld cmd.cap_pin[1] = (le32toh(sc->eeprom_crystal) >> 16) & 0xff; 6605*fd43cf6eSHans Rosenfeld error = iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 0); 6606*fd43cf6eSHans Rosenfeld if (error != 0) { 6607*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 6608*fd43cf6eSHans Rosenfeld "!crystal calibration failed"); 6609*fd43cf6eSHans Rosenfeld return error; 6610*fd43cf6eSHans Rosenfeld } 6611*fd43cf6eSHans Rosenfeld } 6612*fd43cf6eSHans Rosenfeld if (!(sc->sc_flags & IWN_FLAG_CALIB_DONE)) { 6613*fd43cf6eSHans Rosenfeld /* Query calibration from the initialization firmware. */ 6614*fd43cf6eSHans Rosenfeld if ((error = iwn5000_query_calibration(sc)) != 0) { 6615*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 6616*fd43cf6eSHans Rosenfeld "!could not query calibration"); 6617*fd43cf6eSHans Rosenfeld return error; 6618*fd43cf6eSHans Rosenfeld } 6619*fd43cf6eSHans Rosenfeld /* 6620*fd43cf6eSHans Rosenfeld * We have the calibration results now, reboot with the 6621*fd43cf6eSHans Rosenfeld * runtime firmware (call ourselves recursively!) 6622*fd43cf6eSHans Rosenfeld */ 6623*fd43cf6eSHans Rosenfeld iwn_hw_stop(sc, B_FALSE); 6624*fd43cf6eSHans Rosenfeld error = iwn_hw_init(sc); 6625*fd43cf6eSHans Rosenfeld } else { 6626*fd43cf6eSHans Rosenfeld /* Send calibration results to runtime firmware. */ 6627*fd43cf6eSHans Rosenfeld error = iwn5000_send_calibration(sc); 6628*fd43cf6eSHans Rosenfeld } 6629*fd43cf6eSHans Rosenfeld return error; 6630*fd43cf6eSHans Rosenfeld } 6631*fd43cf6eSHans Rosenfeld 6632*fd43cf6eSHans Rosenfeld /* 6633*fd43cf6eSHans Rosenfeld * The firmware boot code is small and is intended to be copied directy into 6634*fd43cf6eSHans Rosenfeld * the NIC internal memory (no DMA transfer). 6635*fd43cf6eSHans Rosenfeld */ 6636*fd43cf6eSHans Rosenfeld static int 6637*fd43cf6eSHans Rosenfeld iwn4965_load_bootcode(struct iwn_softc *sc, const uint8_t *ucode, int size) 6638*fd43cf6eSHans Rosenfeld { 6639*fd43cf6eSHans Rosenfeld int error, ntries; 6640*fd43cf6eSHans Rosenfeld 6641*fd43cf6eSHans Rosenfeld size /= sizeof (uint32_t); 6642*fd43cf6eSHans Rosenfeld 6643*fd43cf6eSHans Rosenfeld if ((error = iwn_nic_lock(sc)) != 0) 6644*fd43cf6eSHans Rosenfeld return error; 6645*fd43cf6eSHans Rosenfeld 6646*fd43cf6eSHans Rosenfeld /* Copy microcode image into NIC memory. */ 6647*fd43cf6eSHans Rosenfeld iwn_prph_write_region_4(sc, IWN_BSM_SRAM_BASE, 6648*fd43cf6eSHans Rosenfeld /*LINTED: E_PTR_BAD_CAST_ALIGN*/ 6649*fd43cf6eSHans Rosenfeld (const uint32_t *)ucode, size); 6650*fd43cf6eSHans Rosenfeld 6651*fd43cf6eSHans Rosenfeld iwn_prph_write(sc, IWN_BSM_WR_MEM_SRC, 0); 6652*fd43cf6eSHans Rosenfeld iwn_prph_write(sc, IWN_BSM_WR_MEM_DST, IWN_FW_TEXT_BASE); 6653*fd43cf6eSHans Rosenfeld iwn_prph_write(sc, IWN_BSM_WR_DWCOUNT, size); 6654*fd43cf6eSHans Rosenfeld 6655*fd43cf6eSHans Rosenfeld /* Start boot load now. */ 6656*fd43cf6eSHans Rosenfeld iwn_prph_write(sc, IWN_BSM_WR_CTRL, IWN_BSM_WR_CTRL_START); 6657*fd43cf6eSHans Rosenfeld 6658*fd43cf6eSHans Rosenfeld /* Wait for transfer to complete. */ 6659*fd43cf6eSHans Rosenfeld for (ntries = 0; ntries < 1000; ntries++) { 6660*fd43cf6eSHans Rosenfeld if (!(iwn_prph_read(sc, IWN_BSM_WR_CTRL) & 6661*fd43cf6eSHans Rosenfeld IWN_BSM_WR_CTRL_START)) 6662*fd43cf6eSHans Rosenfeld break; 6663*fd43cf6eSHans Rosenfeld DELAY(10); 6664*fd43cf6eSHans Rosenfeld } 6665*fd43cf6eSHans Rosenfeld if (ntries == 1000) { 6666*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 6667*fd43cf6eSHans Rosenfeld "!could not load boot firmware"); 6668*fd43cf6eSHans Rosenfeld iwn_nic_unlock(sc); 6669*fd43cf6eSHans Rosenfeld return ETIMEDOUT; 6670*fd43cf6eSHans Rosenfeld } 6671*fd43cf6eSHans Rosenfeld 6672*fd43cf6eSHans Rosenfeld /* Enable boot after power up. */ 6673*fd43cf6eSHans Rosenfeld iwn_prph_write(sc, IWN_BSM_WR_CTRL, IWN_BSM_WR_CTRL_START_EN); 6674*fd43cf6eSHans Rosenfeld 6675*fd43cf6eSHans Rosenfeld iwn_nic_unlock(sc); 6676*fd43cf6eSHans Rosenfeld return 0; 6677*fd43cf6eSHans Rosenfeld } 6678*fd43cf6eSHans Rosenfeld 6679*fd43cf6eSHans Rosenfeld static int 6680*fd43cf6eSHans Rosenfeld iwn4965_load_firmware(struct iwn_softc *sc) 6681*fd43cf6eSHans Rosenfeld { 6682*fd43cf6eSHans Rosenfeld struct iwn_fw_info *fw = &sc->fw; 6683*fd43cf6eSHans Rosenfeld struct iwn_dma_info *dma = &sc->fw_dma; 6684*fd43cf6eSHans Rosenfeld int error; 6685*fd43cf6eSHans Rosenfeld clock_t clk; 6686*fd43cf6eSHans Rosenfeld 6687*fd43cf6eSHans Rosenfeld ASSERT(mutex_owned(&sc->sc_mtx)); 6688*fd43cf6eSHans Rosenfeld 6689*fd43cf6eSHans Rosenfeld /* Copy initialization sections into pre-allocated DMA-safe memory. */ 6690*fd43cf6eSHans Rosenfeld memcpy(dma->vaddr, fw->init.data, fw->init.datasz); 6691*fd43cf6eSHans Rosenfeld memcpy((char *)dma->vaddr + IWN4965_FW_DATA_MAXSZ, 6692*fd43cf6eSHans Rosenfeld fw->init.text, fw->init.textsz); 6693*fd43cf6eSHans Rosenfeld (void) ddi_dma_sync(dma->dma_hdl, 0, 0, DDI_DMA_SYNC_FORDEV); 6694*fd43cf6eSHans Rosenfeld 6695*fd43cf6eSHans Rosenfeld /* Tell adapter where to find initialization sections. */ 6696*fd43cf6eSHans Rosenfeld if ((error = iwn_nic_lock(sc)) != 0) 6697*fd43cf6eSHans Rosenfeld return error; 6698*fd43cf6eSHans Rosenfeld iwn_prph_write(sc, IWN_BSM_DRAM_DATA_ADDR, dma->paddr >> 4); 6699*fd43cf6eSHans Rosenfeld iwn_prph_write(sc, IWN_BSM_DRAM_DATA_SIZE, fw->init.datasz); 6700*fd43cf6eSHans Rosenfeld iwn_prph_write(sc, IWN_BSM_DRAM_TEXT_ADDR, 6701*fd43cf6eSHans Rosenfeld (dma->paddr + IWN4965_FW_DATA_MAXSZ) >> 4); 6702*fd43cf6eSHans Rosenfeld iwn_prph_write(sc, IWN_BSM_DRAM_TEXT_SIZE, fw->init.textsz); 6703*fd43cf6eSHans Rosenfeld iwn_nic_unlock(sc); 6704*fd43cf6eSHans Rosenfeld 6705*fd43cf6eSHans Rosenfeld /* Load firmware boot code. */ 6706*fd43cf6eSHans Rosenfeld error = iwn4965_load_bootcode(sc, fw->boot.text, fw->boot.textsz); 6707*fd43cf6eSHans Rosenfeld if (error != 0) { 6708*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 6709*fd43cf6eSHans Rosenfeld "!could not load boot firmware"); 6710*fd43cf6eSHans Rosenfeld return error; 6711*fd43cf6eSHans Rosenfeld } 6712*fd43cf6eSHans Rosenfeld /* Now press "execute". */ 6713*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_RESET, 0); 6714*fd43cf6eSHans Rosenfeld 6715*fd43cf6eSHans Rosenfeld /* Wait at most one second for first alive notification. */ 6716*fd43cf6eSHans Rosenfeld clk = ddi_get_lbolt() + drv_usectohz(1000000); 6717*fd43cf6eSHans Rosenfeld while ((sc->sc_flags & IWN_FLAG_FW_ALIVE) == 0) { 6718*fd43cf6eSHans Rosenfeld if (cv_timedwait(&sc->sc_alive_cv, &sc->sc_mtx, clk) < 0) { 6719*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 6720*fd43cf6eSHans Rosenfeld "!timeout waiting for adapter to initialize"); 6721*fd43cf6eSHans Rosenfeld return (IWN_FAIL); 6722*fd43cf6eSHans Rosenfeld } 6723*fd43cf6eSHans Rosenfeld } 6724*fd43cf6eSHans Rosenfeld 6725*fd43cf6eSHans Rosenfeld /* Retrieve current temperature for initial TX power calibration. */ 6726*fd43cf6eSHans Rosenfeld sc->rawtemp = sc->ucode_info.temp[3].chan20MHz; 6727*fd43cf6eSHans Rosenfeld sc->temp = iwn4965_get_temperature(sc); 6728*fd43cf6eSHans Rosenfeld sc->sc_misc->temp.value.ul = sc->temp; 6729*fd43cf6eSHans Rosenfeld 6730*fd43cf6eSHans Rosenfeld /* Copy runtime sections into pre-allocated DMA-safe memory. */ 6731*fd43cf6eSHans Rosenfeld memcpy(dma->vaddr, fw->main.data, fw->main.datasz); 6732*fd43cf6eSHans Rosenfeld memcpy((char *)dma->vaddr + IWN4965_FW_DATA_MAXSZ, 6733*fd43cf6eSHans Rosenfeld fw->main.text, fw->main.textsz); 6734*fd43cf6eSHans Rosenfeld (void) ddi_dma_sync(dma->dma_hdl, 0, 0, DDI_DMA_SYNC_FORDEV); 6735*fd43cf6eSHans Rosenfeld 6736*fd43cf6eSHans Rosenfeld /* Tell adapter where to find runtime sections. */ 6737*fd43cf6eSHans Rosenfeld if ((error = iwn_nic_lock(sc)) != 0) 6738*fd43cf6eSHans Rosenfeld return error; 6739*fd43cf6eSHans Rosenfeld iwn_prph_write(sc, IWN_BSM_DRAM_DATA_ADDR, dma->paddr >> 4); 6740*fd43cf6eSHans Rosenfeld iwn_prph_write(sc, IWN_BSM_DRAM_DATA_SIZE, fw->main.datasz); 6741*fd43cf6eSHans Rosenfeld iwn_prph_write(sc, IWN_BSM_DRAM_TEXT_ADDR, 6742*fd43cf6eSHans Rosenfeld (dma->paddr + IWN4965_FW_DATA_MAXSZ) >> 4); 6743*fd43cf6eSHans Rosenfeld iwn_prph_write(sc, IWN_BSM_DRAM_TEXT_SIZE, 6744*fd43cf6eSHans Rosenfeld IWN_FW_UPDATED | fw->main.textsz); 6745*fd43cf6eSHans Rosenfeld iwn_nic_unlock(sc); 6746*fd43cf6eSHans Rosenfeld 6747*fd43cf6eSHans Rosenfeld return 0; 6748*fd43cf6eSHans Rosenfeld } 6749*fd43cf6eSHans Rosenfeld 6750*fd43cf6eSHans Rosenfeld static int 6751*fd43cf6eSHans Rosenfeld iwn5000_load_firmware_section(struct iwn_softc *sc, uint32_t dst, 6752*fd43cf6eSHans Rosenfeld const uint8_t *section, int size) 6753*fd43cf6eSHans Rosenfeld { 6754*fd43cf6eSHans Rosenfeld struct iwn_dma_info *dma = &sc->fw_dma; 6755*fd43cf6eSHans Rosenfeld int error; 6756*fd43cf6eSHans Rosenfeld clock_t clk; 6757*fd43cf6eSHans Rosenfeld 6758*fd43cf6eSHans Rosenfeld ASSERT(mutex_owned(&sc->sc_mtx)); 6759*fd43cf6eSHans Rosenfeld 6760*fd43cf6eSHans Rosenfeld /* Copy firmware section into pre-allocated DMA-safe memory. */ 6761*fd43cf6eSHans Rosenfeld memcpy(dma->vaddr, section, size); 6762*fd43cf6eSHans Rosenfeld (void) ddi_dma_sync(dma->dma_hdl, 0, 0, DDI_DMA_SYNC_FORDEV); 6763*fd43cf6eSHans Rosenfeld 6764*fd43cf6eSHans Rosenfeld if ((error = iwn_nic_lock(sc)) != 0) 6765*fd43cf6eSHans Rosenfeld return error; 6766*fd43cf6eSHans Rosenfeld 6767*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_FH_TX_CONFIG(IWN_SRVC_DMACHNL), 6768*fd43cf6eSHans Rosenfeld IWN_FH_TX_CONFIG_DMA_PAUSE); 6769*fd43cf6eSHans Rosenfeld 6770*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_FH_SRAM_ADDR(IWN_SRVC_DMACHNL), dst); 6771*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_FH_TFBD_CTRL0(IWN_SRVC_DMACHNL), 6772*fd43cf6eSHans Rosenfeld IWN_LOADDR(dma->paddr)); 6773*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_FH_TFBD_CTRL1(IWN_SRVC_DMACHNL), 6774*fd43cf6eSHans Rosenfeld IWN_HIADDR(dma->paddr) << 28 | size); 6775*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_FH_TXBUF_STATUS(IWN_SRVC_DMACHNL), 6776*fd43cf6eSHans Rosenfeld IWN_FH_TXBUF_STATUS_TBNUM(1) | 6777*fd43cf6eSHans Rosenfeld IWN_FH_TXBUF_STATUS_TBIDX(1) | 6778*fd43cf6eSHans Rosenfeld IWN_FH_TXBUF_STATUS_TFBD_VALID); 6779*fd43cf6eSHans Rosenfeld 6780*fd43cf6eSHans Rosenfeld /* Kick Flow Handler to start DMA transfer. */ 6781*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_FH_TX_CONFIG(IWN_SRVC_DMACHNL), 6782*fd43cf6eSHans Rosenfeld IWN_FH_TX_CONFIG_DMA_ENA | IWN_FH_TX_CONFIG_CIRQ_HOST_ENDTFD); 6783*fd43cf6eSHans Rosenfeld 6784*fd43cf6eSHans Rosenfeld iwn_nic_unlock(sc); 6785*fd43cf6eSHans Rosenfeld 6786*fd43cf6eSHans Rosenfeld /* Wait at most five seconds for FH DMA transfer to complete. */ 6787*fd43cf6eSHans Rosenfeld clk = ddi_get_lbolt() + drv_usectohz(5000000); 6788*fd43cf6eSHans Rosenfeld while ((sc->sc_flags & IWN_FLAG_FW_DMA) == 0) { 6789*fd43cf6eSHans Rosenfeld if (cv_timedwait(&sc->sc_fhdma_cv, &sc->sc_mtx, clk) < 0) 6790*fd43cf6eSHans Rosenfeld return (IWN_FAIL); 6791*fd43cf6eSHans Rosenfeld } 6792*fd43cf6eSHans Rosenfeld sc->sc_flags &= ~IWN_FLAG_FW_DMA; 6793*fd43cf6eSHans Rosenfeld 6794*fd43cf6eSHans Rosenfeld return (IWN_SUCCESS); 6795*fd43cf6eSHans Rosenfeld } 6796*fd43cf6eSHans Rosenfeld 6797*fd43cf6eSHans Rosenfeld static int 6798*fd43cf6eSHans Rosenfeld iwn5000_load_firmware(struct iwn_softc *sc) 6799*fd43cf6eSHans Rosenfeld { 6800*fd43cf6eSHans Rosenfeld struct iwn_fw_part *fw; 6801*fd43cf6eSHans Rosenfeld int error; 6802*fd43cf6eSHans Rosenfeld 6803*fd43cf6eSHans Rosenfeld /* Load the initialization firmware on first boot only. */ 6804*fd43cf6eSHans Rosenfeld fw = (sc->sc_flags & IWN_FLAG_CALIB_DONE) ? 6805*fd43cf6eSHans Rosenfeld &sc->fw.main : &sc->fw.init; 6806*fd43cf6eSHans Rosenfeld 6807*fd43cf6eSHans Rosenfeld error = iwn5000_load_firmware_section(sc, IWN_FW_TEXT_BASE, 6808*fd43cf6eSHans Rosenfeld fw->text, fw->textsz); 6809*fd43cf6eSHans Rosenfeld if (error != 0) { 6810*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 6811*fd43cf6eSHans Rosenfeld "!could not load firmware %s section", ".text"); 6812*fd43cf6eSHans Rosenfeld return error; 6813*fd43cf6eSHans Rosenfeld } 6814*fd43cf6eSHans Rosenfeld error = iwn5000_load_firmware_section(sc, IWN_FW_DATA_BASE, 6815*fd43cf6eSHans Rosenfeld fw->data, fw->datasz); 6816*fd43cf6eSHans Rosenfeld if (error != 0) { 6817*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 6818*fd43cf6eSHans Rosenfeld "!could not load firmware %s section", ".data"); 6819*fd43cf6eSHans Rosenfeld return error; 6820*fd43cf6eSHans Rosenfeld } 6821*fd43cf6eSHans Rosenfeld 6822*fd43cf6eSHans Rosenfeld /* Now press "execute". */ 6823*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_RESET, 0); 6824*fd43cf6eSHans Rosenfeld return 0; 6825*fd43cf6eSHans Rosenfeld } 6826*fd43cf6eSHans Rosenfeld 6827*fd43cf6eSHans Rosenfeld /* 6828*fd43cf6eSHans Rosenfeld * Extract text and data sections from a legacy firmware image. 6829*fd43cf6eSHans Rosenfeld */ 6830*fd43cf6eSHans Rosenfeld static int 6831*fd43cf6eSHans Rosenfeld iwn_read_firmware_leg(struct iwn_softc *sc, struct iwn_fw_info *fw) 6832*fd43cf6eSHans Rosenfeld { 6833*fd43cf6eSHans Rosenfeld _NOTE(ARGUNUSED(sc)); 6834*fd43cf6eSHans Rosenfeld const uint32_t *ptr; 6835*fd43cf6eSHans Rosenfeld size_t hdrlen = 24; 6836*fd43cf6eSHans Rosenfeld uint32_t rev; 6837*fd43cf6eSHans Rosenfeld 6838*fd43cf6eSHans Rosenfeld /*LINTED: E_PTR_BAD_CAST_ALIGN*/ 6839*fd43cf6eSHans Rosenfeld ptr = (const uint32_t *)fw->data; 6840*fd43cf6eSHans Rosenfeld rev = le32toh(*ptr++); 6841*fd43cf6eSHans Rosenfeld 6842*fd43cf6eSHans Rosenfeld /* Check firmware API version. */ 6843*fd43cf6eSHans Rosenfeld if (IWN_FW_API(rev) <= 1) { 6844*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 6845*fd43cf6eSHans Rosenfeld "!bad firmware, need API version >=2"); 6846*fd43cf6eSHans Rosenfeld return EINVAL; 6847*fd43cf6eSHans Rosenfeld } 6848*fd43cf6eSHans Rosenfeld if (IWN_FW_API(rev) >= 3) { 6849*fd43cf6eSHans Rosenfeld /* Skip build number (version 2 header). */ 6850*fd43cf6eSHans Rosenfeld hdrlen += 4; 6851*fd43cf6eSHans Rosenfeld ptr++; 6852*fd43cf6eSHans Rosenfeld } 6853*fd43cf6eSHans Rosenfeld if (fw->size < hdrlen) { 6854*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 6855*fd43cf6eSHans Rosenfeld "!firmware too short: %lld bytes", (longlong_t)fw->size); 6856*fd43cf6eSHans Rosenfeld return EINVAL; 6857*fd43cf6eSHans Rosenfeld } 6858*fd43cf6eSHans Rosenfeld fw->main.textsz = le32toh(*ptr++); 6859*fd43cf6eSHans Rosenfeld fw->main.datasz = le32toh(*ptr++); 6860*fd43cf6eSHans Rosenfeld fw->init.textsz = le32toh(*ptr++); 6861*fd43cf6eSHans Rosenfeld fw->init.datasz = le32toh(*ptr++); 6862*fd43cf6eSHans Rosenfeld fw->boot.textsz = le32toh(*ptr++); 6863*fd43cf6eSHans Rosenfeld 6864*fd43cf6eSHans Rosenfeld /* Check that all firmware sections fit. */ 6865*fd43cf6eSHans Rosenfeld if (fw->size < hdrlen + fw->main.textsz + fw->main.datasz + 6866*fd43cf6eSHans Rosenfeld fw->init.textsz + fw->init.datasz + fw->boot.textsz) { 6867*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 6868*fd43cf6eSHans Rosenfeld "!firmware too short: %lld bytes", (longlong_t)fw->size); 6869*fd43cf6eSHans Rosenfeld return EINVAL; 6870*fd43cf6eSHans Rosenfeld } 6871*fd43cf6eSHans Rosenfeld 6872*fd43cf6eSHans Rosenfeld /* Get pointers to firmware sections. */ 6873*fd43cf6eSHans Rosenfeld fw->main.text = (const uint8_t *)ptr; 6874*fd43cf6eSHans Rosenfeld fw->main.data = fw->main.text + fw->main.textsz; 6875*fd43cf6eSHans Rosenfeld fw->init.text = fw->main.data + fw->main.datasz; 6876*fd43cf6eSHans Rosenfeld fw->init.data = fw->init.text + fw->init.textsz; 6877*fd43cf6eSHans Rosenfeld fw->boot.text = fw->init.data + fw->init.datasz; 6878*fd43cf6eSHans Rosenfeld return 0; 6879*fd43cf6eSHans Rosenfeld } 6880*fd43cf6eSHans Rosenfeld 6881*fd43cf6eSHans Rosenfeld /* 6882*fd43cf6eSHans Rosenfeld * Extract text and data sections from a TLV firmware image. 6883*fd43cf6eSHans Rosenfeld */ 6884*fd43cf6eSHans Rosenfeld static int 6885*fd43cf6eSHans Rosenfeld iwn_read_firmware_tlv(struct iwn_softc *sc, struct iwn_fw_info *fw, 6886*fd43cf6eSHans Rosenfeld uint16_t alt) 6887*fd43cf6eSHans Rosenfeld { 6888*fd43cf6eSHans Rosenfeld _NOTE(ARGUNUSED(sc)); 6889*fd43cf6eSHans Rosenfeld const struct iwn_fw_tlv_hdr *hdr; 6890*fd43cf6eSHans Rosenfeld const struct iwn_fw_tlv *tlv; 6891*fd43cf6eSHans Rosenfeld const uint8_t *ptr, *end; 6892*fd43cf6eSHans Rosenfeld uint64_t altmask; 6893*fd43cf6eSHans Rosenfeld uint32_t len; 6894*fd43cf6eSHans Rosenfeld 6895*fd43cf6eSHans Rosenfeld if (fw->size < sizeof (*hdr)) { 6896*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 6897*fd43cf6eSHans Rosenfeld "!firmware too short: %lld bytes", (longlong_t)fw->size); 6898*fd43cf6eSHans Rosenfeld return EINVAL; 6899*fd43cf6eSHans Rosenfeld } 6900*fd43cf6eSHans Rosenfeld hdr = (const struct iwn_fw_tlv_hdr *)fw->data; 6901*fd43cf6eSHans Rosenfeld if (hdr->signature != htole32(IWN_FW_SIGNATURE)) { 6902*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 6903*fd43cf6eSHans Rosenfeld "!bad firmware signature 0x%08x", le32toh(hdr->signature)); 6904*fd43cf6eSHans Rosenfeld return EINVAL; 6905*fd43cf6eSHans Rosenfeld } 6906*fd43cf6eSHans Rosenfeld 6907*fd43cf6eSHans Rosenfeld /* 6908*fd43cf6eSHans Rosenfeld * Select the closest supported alternative that is less than 6909*fd43cf6eSHans Rosenfeld * or equal to the specified one. 6910*fd43cf6eSHans Rosenfeld */ 6911*fd43cf6eSHans Rosenfeld altmask = le64toh(hdr->altmask); 6912*fd43cf6eSHans Rosenfeld while (alt > 0 && !(altmask & (1ULL << alt))) 6913*fd43cf6eSHans Rosenfeld alt--; /* Downgrade. */ 6914*fd43cf6eSHans Rosenfeld IWN_DBG("using alternative %d", alt); 6915*fd43cf6eSHans Rosenfeld 6916*fd43cf6eSHans Rosenfeld ptr = (const uint8_t *)(hdr + 1); 6917*fd43cf6eSHans Rosenfeld end = (const uint8_t *)(fw->data + fw->size); 6918*fd43cf6eSHans Rosenfeld 6919*fd43cf6eSHans Rosenfeld /* Parse type-length-value fields. */ 6920*fd43cf6eSHans Rosenfeld while (ptr + sizeof (*tlv) <= end) { 6921*fd43cf6eSHans Rosenfeld tlv = (const struct iwn_fw_tlv *)ptr; 6922*fd43cf6eSHans Rosenfeld len = le32toh(tlv->len); 6923*fd43cf6eSHans Rosenfeld 6924*fd43cf6eSHans Rosenfeld ptr += sizeof (*tlv); 6925*fd43cf6eSHans Rosenfeld if (ptr + len > end) { 6926*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 6927*fd43cf6eSHans Rosenfeld "!firmware too short: %lld bytes", 6928*fd43cf6eSHans Rosenfeld (longlong_t)fw->size); 6929*fd43cf6eSHans Rosenfeld return EINVAL; 6930*fd43cf6eSHans Rosenfeld } 6931*fd43cf6eSHans Rosenfeld /* Skip other alternatives. */ 6932*fd43cf6eSHans Rosenfeld if (tlv->alt != 0 && le16toh(tlv->alt) != alt) { 6933*fd43cf6eSHans Rosenfeld IWN_DBG("skipping other alternative"); 6934*fd43cf6eSHans Rosenfeld goto next; 6935*fd43cf6eSHans Rosenfeld } 6936*fd43cf6eSHans Rosenfeld 6937*fd43cf6eSHans Rosenfeld switch (le16toh(tlv->type)) { 6938*fd43cf6eSHans Rosenfeld case IWN_FW_TLV_MAIN_TEXT: 6939*fd43cf6eSHans Rosenfeld fw->main.text = ptr; 6940*fd43cf6eSHans Rosenfeld fw->main.textsz = len; 6941*fd43cf6eSHans Rosenfeld break; 6942*fd43cf6eSHans Rosenfeld case IWN_FW_TLV_MAIN_DATA: 6943*fd43cf6eSHans Rosenfeld fw->main.data = ptr; 6944*fd43cf6eSHans Rosenfeld fw->main.datasz = len; 6945*fd43cf6eSHans Rosenfeld break; 6946*fd43cf6eSHans Rosenfeld case IWN_FW_TLV_INIT_TEXT: 6947*fd43cf6eSHans Rosenfeld fw->init.text = ptr; 6948*fd43cf6eSHans Rosenfeld fw->init.textsz = len; 6949*fd43cf6eSHans Rosenfeld break; 6950*fd43cf6eSHans Rosenfeld case IWN_FW_TLV_INIT_DATA: 6951*fd43cf6eSHans Rosenfeld fw->init.data = ptr; 6952*fd43cf6eSHans Rosenfeld fw->init.datasz = len; 6953*fd43cf6eSHans Rosenfeld break; 6954*fd43cf6eSHans Rosenfeld case IWN_FW_TLV_BOOT_TEXT: 6955*fd43cf6eSHans Rosenfeld fw->boot.text = ptr; 6956*fd43cf6eSHans Rosenfeld fw->boot.textsz = len; 6957*fd43cf6eSHans Rosenfeld break; 6958*fd43cf6eSHans Rosenfeld case IWN_FW_TLV_ENH_SENS: 6959*fd43cf6eSHans Rosenfeld if (len != 0) { 6960*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 6961*fd43cf6eSHans Rosenfeld "!TLV type %d has invalid size %u", 6962*fd43cf6eSHans Rosenfeld le16toh(tlv->type), len); 6963*fd43cf6eSHans Rosenfeld goto next; 6964*fd43cf6eSHans Rosenfeld } 6965*fd43cf6eSHans Rosenfeld sc->sc_flags |= IWN_FLAG_ENH_SENS; 6966*fd43cf6eSHans Rosenfeld break; 6967*fd43cf6eSHans Rosenfeld case IWN_FW_TLV_PHY_CALIB: 6968*fd43cf6eSHans Rosenfeld if (len != sizeof(uint32_t)) { 6969*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 6970*fd43cf6eSHans Rosenfeld "!TLV type %d has invalid size %u", 6971*fd43cf6eSHans Rosenfeld le16toh(tlv->type), len); 6972*fd43cf6eSHans Rosenfeld goto next; 6973*fd43cf6eSHans Rosenfeld } 6974*fd43cf6eSHans Rosenfeld if (le32toh(*ptr) <= IWN5000_PHY_CALIB_MAX) { 6975*fd43cf6eSHans Rosenfeld sc->reset_noise_gain = le32toh(*ptr); 6976*fd43cf6eSHans Rosenfeld sc->noise_gain = le32toh(*ptr) + 1; 6977*fd43cf6eSHans Rosenfeld } 6978*fd43cf6eSHans Rosenfeld break; 6979*fd43cf6eSHans Rosenfeld case IWN_FW_TLV_FLAGS: 6980*fd43cf6eSHans Rosenfeld if (len < sizeof(uint32_t)) 6981*fd43cf6eSHans Rosenfeld break; 6982*fd43cf6eSHans Rosenfeld if (len % sizeof(uint32_t)) 6983*fd43cf6eSHans Rosenfeld break; 6984*fd43cf6eSHans Rosenfeld sc->tlv_feature_flags = le32toh(*ptr); 6985*fd43cf6eSHans Rosenfeld IWN_DBG("feature: 0x%08x", sc->tlv_feature_flags); 6986*fd43cf6eSHans Rosenfeld break; 6987*fd43cf6eSHans Rosenfeld default: 6988*fd43cf6eSHans Rosenfeld IWN_DBG("TLV type %d not handled", le16toh(tlv->type)); 6989*fd43cf6eSHans Rosenfeld break; 6990*fd43cf6eSHans Rosenfeld } 6991*fd43cf6eSHans Rosenfeld next: /* TLV fields are 32-bit aligned. */ 6992*fd43cf6eSHans Rosenfeld ptr += (len + 3) & ~3; 6993*fd43cf6eSHans Rosenfeld } 6994*fd43cf6eSHans Rosenfeld return 0; 6995*fd43cf6eSHans Rosenfeld } 6996*fd43cf6eSHans Rosenfeld 6997*fd43cf6eSHans Rosenfeld static int 6998*fd43cf6eSHans Rosenfeld iwn_read_firmware(struct iwn_softc *sc) 6999*fd43cf6eSHans Rosenfeld { 7000*fd43cf6eSHans Rosenfeld struct iwn_fw_info *fw = &sc->fw; 7001*fd43cf6eSHans Rosenfeld firmware_handle_t fwh; 7002*fd43cf6eSHans Rosenfeld int error; 7003*fd43cf6eSHans Rosenfeld 7004*fd43cf6eSHans Rosenfeld /* 7005*fd43cf6eSHans Rosenfeld * Some PHY calibration commands are firmware-dependent; these 7006*fd43cf6eSHans Rosenfeld * are the default values that will be overridden if 7007*fd43cf6eSHans Rosenfeld * necessary. 7008*fd43cf6eSHans Rosenfeld */ 7009*fd43cf6eSHans Rosenfeld sc->reset_noise_gain = IWN5000_PHY_CALIB_RESET_NOISE_GAIN; 7010*fd43cf6eSHans Rosenfeld sc->noise_gain = IWN5000_PHY_CALIB_NOISE_GAIN; 7011*fd43cf6eSHans Rosenfeld 7012*fd43cf6eSHans Rosenfeld /* Initialize for error returns */ 7013*fd43cf6eSHans Rosenfeld fw->data = NULL; 7014*fd43cf6eSHans Rosenfeld fw->size = 0; 7015*fd43cf6eSHans Rosenfeld 7016*fd43cf6eSHans Rosenfeld /* Open firmware image. */ 7017*fd43cf6eSHans Rosenfeld if ((error = firmware_open("iwn", sc->fwname, &fwh)) != 0) { 7018*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 7019*fd43cf6eSHans Rosenfeld "!could not get firmware handle %s", sc->fwname); 7020*fd43cf6eSHans Rosenfeld return error; 7021*fd43cf6eSHans Rosenfeld } 7022*fd43cf6eSHans Rosenfeld fw->size = firmware_get_size(fwh); 7023*fd43cf6eSHans Rosenfeld if (fw->size < sizeof (uint32_t)) { 7024*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 7025*fd43cf6eSHans Rosenfeld "!firmware too short: %lld bytes", (longlong_t)fw->size); 7026*fd43cf6eSHans Rosenfeld (void) firmware_close(fwh); 7027*fd43cf6eSHans Rosenfeld return EINVAL; 7028*fd43cf6eSHans Rosenfeld } 7029*fd43cf6eSHans Rosenfeld 7030*fd43cf6eSHans Rosenfeld /* Read the firmware. */ 7031*fd43cf6eSHans Rosenfeld fw->data = kmem_alloc(fw->size, KM_SLEEP); 7032*fd43cf6eSHans Rosenfeld error = firmware_read(fwh, 0, fw->data, fw->size); 7033*fd43cf6eSHans Rosenfeld (void) firmware_close(fwh); 7034*fd43cf6eSHans Rosenfeld if (error != 0) { 7035*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 7036*fd43cf6eSHans Rosenfeld "!could not read firmware %s", sc->fwname); 7037*fd43cf6eSHans Rosenfeld goto out; 7038*fd43cf6eSHans Rosenfeld } 7039*fd43cf6eSHans Rosenfeld 7040*fd43cf6eSHans Rosenfeld /* Retrieve text and data sections. */ 7041*fd43cf6eSHans Rosenfeld /*LINTED: E_PTR_BAD_CAST_ALIGN*/ 7042*fd43cf6eSHans Rosenfeld if (*(const uint32_t *)fw->data != 0) /* Legacy image. */ 7043*fd43cf6eSHans Rosenfeld error = iwn_read_firmware_leg(sc, fw); 7044*fd43cf6eSHans Rosenfeld else 7045*fd43cf6eSHans Rosenfeld error = iwn_read_firmware_tlv(sc, fw, 1); 7046*fd43cf6eSHans Rosenfeld if (error != 0) { 7047*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 7048*fd43cf6eSHans Rosenfeld "!could not read firmware sections"); 7049*fd43cf6eSHans Rosenfeld goto out; 7050*fd43cf6eSHans Rosenfeld } 7051*fd43cf6eSHans Rosenfeld 7052*fd43cf6eSHans Rosenfeld /* Make sure text and data sections fit in hardware memory. */ 7053*fd43cf6eSHans Rosenfeld if (fw->main.textsz > sc->fw_text_maxsz || 7054*fd43cf6eSHans Rosenfeld fw->main.datasz > sc->fw_data_maxsz || 7055*fd43cf6eSHans Rosenfeld fw->init.textsz > sc->fw_text_maxsz || 7056*fd43cf6eSHans Rosenfeld fw->init.datasz > sc->fw_data_maxsz || 7057*fd43cf6eSHans Rosenfeld fw->boot.textsz > IWN_FW_BOOT_TEXT_MAXSZ || 7058*fd43cf6eSHans Rosenfeld (fw->boot.textsz & 3) != 0) { 7059*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 7060*fd43cf6eSHans Rosenfeld "!firmware sections too large"); 7061*fd43cf6eSHans Rosenfeld goto out; 7062*fd43cf6eSHans Rosenfeld } 7063*fd43cf6eSHans Rosenfeld 7064*fd43cf6eSHans Rosenfeld /* We can proceed with loading the firmware. */ 7065*fd43cf6eSHans Rosenfeld return 0; 7066*fd43cf6eSHans Rosenfeld out: 7067*fd43cf6eSHans Rosenfeld kmem_free(fw->data, fw->size); 7068*fd43cf6eSHans Rosenfeld fw->data = NULL; 7069*fd43cf6eSHans Rosenfeld fw->size = 0; 7070*fd43cf6eSHans Rosenfeld return error ? error : EINVAL; 7071*fd43cf6eSHans Rosenfeld } 7072*fd43cf6eSHans Rosenfeld 7073*fd43cf6eSHans Rosenfeld static int 7074*fd43cf6eSHans Rosenfeld iwn_clock_wait(struct iwn_softc *sc) 7075*fd43cf6eSHans Rosenfeld { 7076*fd43cf6eSHans Rosenfeld int ntries; 7077*fd43cf6eSHans Rosenfeld 7078*fd43cf6eSHans Rosenfeld /* Set "initialization complete" bit. */ 7079*fd43cf6eSHans Rosenfeld IWN_SETBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_INIT_DONE); 7080*fd43cf6eSHans Rosenfeld 7081*fd43cf6eSHans Rosenfeld /* Wait for clock stabilization. */ 7082*fd43cf6eSHans Rosenfeld for (ntries = 0; ntries < 2500; ntries++) { 7083*fd43cf6eSHans Rosenfeld if (IWN_READ(sc, IWN_GP_CNTRL) & IWN_GP_CNTRL_MAC_CLOCK_READY) 7084*fd43cf6eSHans Rosenfeld return 0; 7085*fd43cf6eSHans Rosenfeld DELAY(10); 7086*fd43cf6eSHans Rosenfeld } 7087*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 7088*fd43cf6eSHans Rosenfeld "!timeout waiting for clock stabilization"); 7089*fd43cf6eSHans Rosenfeld return ETIMEDOUT; 7090*fd43cf6eSHans Rosenfeld } 7091*fd43cf6eSHans Rosenfeld 7092*fd43cf6eSHans Rosenfeld static int 7093*fd43cf6eSHans Rosenfeld iwn_apm_init(struct iwn_softc *sc) 7094*fd43cf6eSHans Rosenfeld { 7095*fd43cf6eSHans Rosenfeld uint32_t reg; 7096*fd43cf6eSHans Rosenfeld int error; 7097*fd43cf6eSHans Rosenfeld 7098*fd43cf6eSHans Rosenfeld /* Disable L0s exit timer (NMI bug workaround). */ 7099*fd43cf6eSHans Rosenfeld IWN_SETBITS(sc, IWN_GIO_CHICKEN, IWN_GIO_CHICKEN_DIS_L0S_TIMER); 7100*fd43cf6eSHans Rosenfeld /* Don't wait for ICH L0s (ICH bug workaround). */ 7101*fd43cf6eSHans Rosenfeld IWN_SETBITS(sc, IWN_GIO_CHICKEN, IWN_GIO_CHICKEN_L1A_NO_L0S_RX); 7102*fd43cf6eSHans Rosenfeld 7103*fd43cf6eSHans Rosenfeld /* Set FH wait threshold to max (HW bug under stress workaround). */ 7104*fd43cf6eSHans Rosenfeld IWN_SETBITS(sc, IWN_DBG_HPET_MEM, 0xffff0000); 7105*fd43cf6eSHans Rosenfeld 7106*fd43cf6eSHans Rosenfeld /* Enable HAP INTA to move adapter from L1a to L0s. */ 7107*fd43cf6eSHans Rosenfeld IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_HAP_WAKE_L1A); 7108*fd43cf6eSHans Rosenfeld 7109*fd43cf6eSHans Rosenfeld /* Retrieve PCIe Active State Power Management (ASPM). */ 7110*fd43cf6eSHans Rosenfeld reg = pci_config_get32(sc->sc_pcih, 7111*fd43cf6eSHans Rosenfeld sc->sc_cap_off + PCIE_LINKCTL); 7112*fd43cf6eSHans Rosenfeld /* Workaround for HW instability in PCIe L0->L0s->L1 transition. */ 7113*fd43cf6eSHans Rosenfeld if (reg & PCIE_LINKCTL_ASPM_CTL_L1) /* L1 Entry enabled. */ 7114*fd43cf6eSHans Rosenfeld IWN_SETBITS(sc, IWN_GIO, IWN_GIO_L0S_ENA); 7115*fd43cf6eSHans Rosenfeld else 7116*fd43cf6eSHans Rosenfeld IWN_CLRBITS(sc, IWN_GIO, IWN_GIO_L0S_ENA); 7117*fd43cf6eSHans Rosenfeld 7118*fd43cf6eSHans Rosenfeld if (sc->hw_type != IWN_HW_REV_TYPE_4965 && 7119*fd43cf6eSHans Rosenfeld sc->hw_type <= IWN_HW_REV_TYPE_1000) 7120*fd43cf6eSHans Rosenfeld IWN_SETBITS(sc, IWN_ANA_PLL, IWN_ANA_PLL_INIT); 7121*fd43cf6eSHans Rosenfeld 7122*fd43cf6eSHans Rosenfeld /* Wait for clock stabilization before accessing prph. */ 7123*fd43cf6eSHans Rosenfeld if ((error = iwn_clock_wait(sc)) != 0) 7124*fd43cf6eSHans Rosenfeld return error; 7125*fd43cf6eSHans Rosenfeld 7126*fd43cf6eSHans Rosenfeld if ((error = iwn_nic_lock(sc)) != 0) 7127*fd43cf6eSHans Rosenfeld return error; 7128*fd43cf6eSHans Rosenfeld if (sc->hw_type == IWN_HW_REV_TYPE_4965) { 7129*fd43cf6eSHans Rosenfeld /* Enable DMA and BSM (Bootstrap State Machine). */ 7130*fd43cf6eSHans Rosenfeld iwn_prph_write(sc, IWN_APMG_CLK_EN, 7131*fd43cf6eSHans Rosenfeld IWN_APMG_CLK_CTRL_DMA_CLK_RQT | 7132*fd43cf6eSHans Rosenfeld IWN_APMG_CLK_CTRL_BSM_CLK_RQT); 7133*fd43cf6eSHans Rosenfeld } else { 7134*fd43cf6eSHans Rosenfeld /* Enable DMA. */ 7135*fd43cf6eSHans Rosenfeld iwn_prph_write(sc, IWN_APMG_CLK_EN, 7136*fd43cf6eSHans Rosenfeld IWN_APMG_CLK_CTRL_DMA_CLK_RQT); 7137*fd43cf6eSHans Rosenfeld } 7138*fd43cf6eSHans Rosenfeld DELAY(20); 7139*fd43cf6eSHans Rosenfeld /* Disable L1-Active. */ 7140*fd43cf6eSHans Rosenfeld iwn_prph_setbits(sc, IWN_APMG_PCI_STT, IWN_APMG_PCI_STT_L1A_DIS); 7141*fd43cf6eSHans Rosenfeld iwn_nic_unlock(sc); 7142*fd43cf6eSHans Rosenfeld 7143*fd43cf6eSHans Rosenfeld return 0; 7144*fd43cf6eSHans Rosenfeld } 7145*fd43cf6eSHans Rosenfeld 7146*fd43cf6eSHans Rosenfeld static void 7147*fd43cf6eSHans Rosenfeld iwn_apm_stop_master(struct iwn_softc *sc) 7148*fd43cf6eSHans Rosenfeld { 7149*fd43cf6eSHans Rosenfeld int ntries; 7150*fd43cf6eSHans Rosenfeld 7151*fd43cf6eSHans Rosenfeld /* Stop busmaster DMA activity. */ 7152*fd43cf6eSHans Rosenfeld IWN_SETBITS(sc, IWN_RESET, IWN_RESET_STOP_MASTER); 7153*fd43cf6eSHans Rosenfeld for (ntries = 0; ntries < 100; ntries++) { 7154*fd43cf6eSHans Rosenfeld if (IWN_READ(sc, IWN_RESET) & IWN_RESET_MASTER_DISABLED) 7155*fd43cf6eSHans Rosenfeld return; 7156*fd43cf6eSHans Rosenfeld DELAY(10); 7157*fd43cf6eSHans Rosenfeld } 7158*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 7159*fd43cf6eSHans Rosenfeld "!timeout waiting for master"); 7160*fd43cf6eSHans Rosenfeld } 7161*fd43cf6eSHans Rosenfeld 7162*fd43cf6eSHans Rosenfeld static void 7163*fd43cf6eSHans Rosenfeld iwn_apm_stop(struct iwn_softc *sc) 7164*fd43cf6eSHans Rosenfeld { 7165*fd43cf6eSHans Rosenfeld iwn_apm_stop_master(sc); 7166*fd43cf6eSHans Rosenfeld 7167*fd43cf6eSHans Rosenfeld /* Reset the entire device. */ 7168*fd43cf6eSHans Rosenfeld IWN_SETBITS(sc, IWN_RESET, IWN_RESET_SW); 7169*fd43cf6eSHans Rosenfeld DELAY(10); 7170*fd43cf6eSHans Rosenfeld /* Clear "initialization complete" bit. */ 7171*fd43cf6eSHans Rosenfeld IWN_CLRBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_INIT_DONE); 7172*fd43cf6eSHans Rosenfeld } 7173*fd43cf6eSHans Rosenfeld 7174*fd43cf6eSHans Rosenfeld static int 7175*fd43cf6eSHans Rosenfeld iwn4965_nic_config(struct iwn_softc *sc) 7176*fd43cf6eSHans Rosenfeld { 7177*fd43cf6eSHans Rosenfeld if (IWN_RFCFG_TYPE(sc->rfcfg) == 1) { 7178*fd43cf6eSHans Rosenfeld /* 7179*fd43cf6eSHans Rosenfeld * I don't believe this to be correct but this is what the 7180*fd43cf6eSHans Rosenfeld * vendor driver is doing. Probably the bits should not be 7181*fd43cf6eSHans Rosenfeld * shifted in IWN_RFCFG_*. 7182*fd43cf6eSHans Rosenfeld */ 7183*fd43cf6eSHans Rosenfeld IWN_SETBITS(sc, IWN_HW_IF_CONFIG, 7184*fd43cf6eSHans Rosenfeld IWN_RFCFG_TYPE(sc->rfcfg) | 7185*fd43cf6eSHans Rosenfeld IWN_RFCFG_STEP(sc->rfcfg) | 7186*fd43cf6eSHans Rosenfeld IWN_RFCFG_DASH(sc->rfcfg)); 7187*fd43cf6eSHans Rosenfeld } 7188*fd43cf6eSHans Rosenfeld IWN_SETBITS(sc, IWN_HW_IF_CONFIG, 7189*fd43cf6eSHans Rosenfeld IWN_HW_IF_CONFIG_RADIO_SI | IWN_HW_IF_CONFIG_MAC_SI); 7190*fd43cf6eSHans Rosenfeld return 0; 7191*fd43cf6eSHans Rosenfeld } 7192*fd43cf6eSHans Rosenfeld 7193*fd43cf6eSHans Rosenfeld static int 7194*fd43cf6eSHans Rosenfeld iwn5000_nic_config(struct iwn_softc *sc) 7195*fd43cf6eSHans Rosenfeld { 7196*fd43cf6eSHans Rosenfeld uint32_t tmp; 7197*fd43cf6eSHans Rosenfeld int error; 7198*fd43cf6eSHans Rosenfeld 7199*fd43cf6eSHans Rosenfeld if (IWN_RFCFG_TYPE(sc->rfcfg) < 3) { 7200*fd43cf6eSHans Rosenfeld IWN_SETBITS(sc, IWN_HW_IF_CONFIG, 7201*fd43cf6eSHans Rosenfeld IWN_RFCFG_TYPE(sc->rfcfg) | 7202*fd43cf6eSHans Rosenfeld IWN_RFCFG_STEP(sc->rfcfg) | 7203*fd43cf6eSHans Rosenfeld IWN_RFCFG_DASH(sc->rfcfg)); 7204*fd43cf6eSHans Rosenfeld } 7205*fd43cf6eSHans Rosenfeld IWN_SETBITS(sc, IWN_HW_IF_CONFIG, 7206*fd43cf6eSHans Rosenfeld IWN_HW_IF_CONFIG_RADIO_SI | IWN_HW_IF_CONFIG_MAC_SI); 7207*fd43cf6eSHans Rosenfeld 7208*fd43cf6eSHans Rosenfeld if ((error = iwn_nic_lock(sc)) != 0) 7209*fd43cf6eSHans Rosenfeld return error; 7210*fd43cf6eSHans Rosenfeld iwn_prph_setbits(sc, IWN_APMG_PS, IWN_APMG_PS_EARLY_PWROFF_DIS); 7211*fd43cf6eSHans Rosenfeld 7212*fd43cf6eSHans Rosenfeld if (sc->hw_type == IWN_HW_REV_TYPE_1000) { 7213*fd43cf6eSHans Rosenfeld /* 7214*fd43cf6eSHans Rosenfeld * Select first Switching Voltage Regulator (1.32V) to 7215*fd43cf6eSHans Rosenfeld * solve a stability issue related to noisy DC2DC line 7216*fd43cf6eSHans Rosenfeld * in the silicon of 1000 Series. 7217*fd43cf6eSHans Rosenfeld */ 7218*fd43cf6eSHans Rosenfeld tmp = iwn_prph_read(sc, IWN_APMG_DIGITAL_SVR); 7219*fd43cf6eSHans Rosenfeld tmp &= ~IWN_APMG_DIGITAL_SVR_VOLTAGE_MASK; 7220*fd43cf6eSHans Rosenfeld tmp |= IWN_APMG_DIGITAL_SVR_VOLTAGE_1_32; 7221*fd43cf6eSHans Rosenfeld iwn_prph_write(sc, IWN_APMG_DIGITAL_SVR, tmp); 7222*fd43cf6eSHans Rosenfeld } 7223*fd43cf6eSHans Rosenfeld iwn_nic_unlock(sc); 7224*fd43cf6eSHans Rosenfeld 7225*fd43cf6eSHans Rosenfeld if (sc->sc_flags & IWN_FLAG_INTERNAL_PA) { 7226*fd43cf6eSHans Rosenfeld /* Use internal power amplifier only. */ 7227*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_GP_DRIVER, IWN_GP_DRIVER_RADIO_2X2_IPA); 7228*fd43cf6eSHans Rosenfeld } 7229*fd43cf6eSHans Rosenfeld if ((sc->hw_type == IWN_HW_REV_TYPE_6050 || 7230*fd43cf6eSHans Rosenfeld sc->hw_type == IWN_HW_REV_TYPE_6005) && sc->calib_ver >= 6) { 7231*fd43cf6eSHans Rosenfeld /* Indicate that ROM calibration version is >=6. */ 7232*fd43cf6eSHans Rosenfeld IWN_SETBITS(sc, IWN_GP_DRIVER, IWN_GP_DRIVER_CALIB_VER6); 7233*fd43cf6eSHans Rosenfeld } 7234*fd43cf6eSHans Rosenfeld if (sc->hw_type == IWN_HW_REV_TYPE_6005) 7235*fd43cf6eSHans Rosenfeld IWN_SETBITS(sc, IWN_GP_DRIVER, IWN_GP_DRIVER_6050_1X2); 7236*fd43cf6eSHans Rosenfeld if (sc->hw_type == IWN_HW_REV_TYPE_2030 || 7237*fd43cf6eSHans Rosenfeld sc->hw_type == IWN_HW_REV_TYPE_2000 || 7238*fd43cf6eSHans Rosenfeld sc->hw_type == IWN_HW_REV_TYPE_135 || 7239*fd43cf6eSHans Rosenfeld sc->hw_type == IWN_HW_REV_TYPE_105) 7240*fd43cf6eSHans Rosenfeld IWN_SETBITS(sc, IWN_GP_DRIVER, IWN_GP_DRIVER_RADIO_IQ_INVERT); 7241*fd43cf6eSHans Rosenfeld return 0; 7242*fd43cf6eSHans Rosenfeld } 7243*fd43cf6eSHans Rosenfeld 7244*fd43cf6eSHans Rosenfeld /* 7245*fd43cf6eSHans Rosenfeld * Take NIC ownership over Intel Active Management Technology (AMT). 7246*fd43cf6eSHans Rosenfeld */ 7247*fd43cf6eSHans Rosenfeld static int 7248*fd43cf6eSHans Rosenfeld iwn_hw_prepare(struct iwn_softc *sc) 7249*fd43cf6eSHans Rosenfeld { 7250*fd43cf6eSHans Rosenfeld int ntries; 7251*fd43cf6eSHans Rosenfeld 7252*fd43cf6eSHans Rosenfeld /* Check if hardware is ready. */ 7253*fd43cf6eSHans Rosenfeld IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_NIC_READY); 7254*fd43cf6eSHans Rosenfeld for (ntries = 0; ntries < 5; ntries++) { 7255*fd43cf6eSHans Rosenfeld if (IWN_READ(sc, IWN_HW_IF_CONFIG) & 7256*fd43cf6eSHans Rosenfeld IWN_HW_IF_CONFIG_NIC_READY) 7257*fd43cf6eSHans Rosenfeld return 0; 7258*fd43cf6eSHans Rosenfeld DELAY(10); 7259*fd43cf6eSHans Rosenfeld } 7260*fd43cf6eSHans Rosenfeld 7261*fd43cf6eSHans Rosenfeld /* Hardware not ready, force into ready state. */ 7262*fd43cf6eSHans Rosenfeld IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_PREPARE); 7263*fd43cf6eSHans Rosenfeld for (ntries = 0; ntries < 15000; ntries++) { 7264*fd43cf6eSHans Rosenfeld if (!(IWN_READ(sc, IWN_HW_IF_CONFIG) & 7265*fd43cf6eSHans Rosenfeld IWN_HW_IF_CONFIG_PREPARE_DONE)) 7266*fd43cf6eSHans Rosenfeld break; 7267*fd43cf6eSHans Rosenfeld DELAY(10); 7268*fd43cf6eSHans Rosenfeld } 7269*fd43cf6eSHans Rosenfeld if (ntries == 15000) 7270*fd43cf6eSHans Rosenfeld return ETIMEDOUT; 7271*fd43cf6eSHans Rosenfeld 7272*fd43cf6eSHans Rosenfeld /* Hardware should be ready now. */ 7273*fd43cf6eSHans Rosenfeld IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_NIC_READY); 7274*fd43cf6eSHans Rosenfeld for (ntries = 0; ntries < 5; ntries++) { 7275*fd43cf6eSHans Rosenfeld if (IWN_READ(sc, IWN_HW_IF_CONFIG) & 7276*fd43cf6eSHans Rosenfeld IWN_HW_IF_CONFIG_NIC_READY) 7277*fd43cf6eSHans Rosenfeld return 0; 7278*fd43cf6eSHans Rosenfeld DELAY(10); 7279*fd43cf6eSHans Rosenfeld } 7280*fd43cf6eSHans Rosenfeld return ETIMEDOUT; 7281*fd43cf6eSHans Rosenfeld } 7282*fd43cf6eSHans Rosenfeld 7283*fd43cf6eSHans Rosenfeld static int 7284*fd43cf6eSHans Rosenfeld iwn_hw_init(struct iwn_softc *sc) 7285*fd43cf6eSHans Rosenfeld { 7286*fd43cf6eSHans Rosenfeld struct iwn_ops *ops = &sc->ops; 7287*fd43cf6eSHans Rosenfeld int error, chnl, qid; 7288*fd43cf6eSHans Rosenfeld clock_t clk; 7289*fd43cf6eSHans Rosenfeld uint32_t rx_config; 7290*fd43cf6eSHans Rosenfeld 7291*fd43cf6eSHans Rosenfeld ASSERT(mutex_owned(&sc->sc_mtx)); 7292*fd43cf6eSHans Rosenfeld 7293*fd43cf6eSHans Rosenfeld /* Clear pending interrupts. */ 7294*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_INT, 0xffffffff); 7295*fd43cf6eSHans Rosenfeld 7296*fd43cf6eSHans Rosenfeld if ((error = iwn_apm_init(sc)) != 0) { 7297*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 7298*fd43cf6eSHans Rosenfeld "!could not power ON adapter"); 7299*fd43cf6eSHans Rosenfeld return error; 7300*fd43cf6eSHans Rosenfeld } 7301*fd43cf6eSHans Rosenfeld 7302*fd43cf6eSHans Rosenfeld /* Select VMAIN power source. */ 7303*fd43cf6eSHans Rosenfeld if ((error = iwn_nic_lock(sc)) != 0) 7304*fd43cf6eSHans Rosenfeld return error; 7305*fd43cf6eSHans Rosenfeld iwn_prph_clrbits(sc, IWN_APMG_PS, IWN_APMG_PS_PWR_SRC_MASK); 7306*fd43cf6eSHans Rosenfeld iwn_nic_unlock(sc); 7307*fd43cf6eSHans Rosenfeld 7308*fd43cf6eSHans Rosenfeld /* Perform adapter-specific initialization. */ 7309*fd43cf6eSHans Rosenfeld if ((error = ops->nic_config(sc)) != 0) 7310*fd43cf6eSHans Rosenfeld return error; 7311*fd43cf6eSHans Rosenfeld 7312*fd43cf6eSHans Rosenfeld /* Initialize RX ring. */ 7313*fd43cf6eSHans Rosenfeld if ((error = iwn_nic_lock(sc)) != 0) 7314*fd43cf6eSHans Rosenfeld return error; 7315*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_FH_RX_CONFIG, 0); 7316*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_FH_RX_WPTR, 0); 7317*fd43cf6eSHans Rosenfeld /* Set physical address of RX ring (256-byte aligned). */ 7318*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_FH_RX_BASE, sc->rxq.desc_dma.paddr >> 8); 7319*fd43cf6eSHans Rosenfeld /* Set physical address of RX status (16-byte aligned). */ 7320*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_FH_STATUS_WPTR, sc->rxq.stat_dma.paddr >> 4); 7321*fd43cf6eSHans Rosenfeld /* Enable RX. */ 7322*fd43cf6eSHans Rosenfeld rx_config = 7323*fd43cf6eSHans Rosenfeld IWN_FH_RX_CONFIG_ENA | 7324*fd43cf6eSHans Rosenfeld #if IWN_RBUF_SIZE == 8192 7325*fd43cf6eSHans Rosenfeld IWN_FH_RX_CONFIG_RB_SIZE_8K | 7326*fd43cf6eSHans Rosenfeld #endif 7327*fd43cf6eSHans Rosenfeld IWN_FH_RX_CONFIG_IGN_RXF_EMPTY | /* HW bug workaround */ 7328*fd43cf6eSHans Rosenfeld IWN_FH_RX_CONFIG_IRQ_DST_HOST | 7329*fd43cf6eSHans Rosenfeld IWN_FH_RX_CONFIG_SINGLE_FRAME | 7330*fd43cf6eSHans Rosenfeld IWN_FH_RX_CONFIG_RB_TIMEOUT(0) | 7331*fd43cf6eSHans Rosenfeld IWN_FH_RX_CONFIG_NRBD(IWN_RX_RING_COUNT_LOG); 7332*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_FH_RX_CONFIG, rx_config); 7333*fd43cf6eSHans Rosenfeld iwn_nic_unlock(sc); 7334*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_FH_RX_WPTR, (IWN_RX_RING_COUNT - 1) & ~7); 7335*fd43cf6eSHans Rosenfeld 7336*fd43cf6eSHans Rosenfeld if ((error = iwn_nic_lock(sc)) != 0) 7337*fd43cf6eSHans Rosenfeld return error; 7338*fd43cf6eSHans Rosenfeld 7339*fd43cf6eSHans Rosenfeld /* Initialize TX scheduler. */ 7340*fd43cf6eSHans Rosenfeld iwn_prph_write(sc, sc->sched_txfact_addr, 0); 7341*fd43cf6eSHans Rosenfeld 7342*fd43cf6eSHans Rosenfeld /* Set physical address of "keep warm" page (16-byte aligned). */ 7343*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_FH_KW_ADDR, sc->kw_dma.paddr >> 4); 7344*fd43cf6eSHans Rosenfeld 7345*fd43cf6eSHans Rosenfeld /* Initialize TX rings. */ 7346*fd43cf6eSHans Rosenfeld for (qid = 0; qid < sc->ntxqs; qid++) { 7347*fd43cf6eSHans Rosenfeld struct iwn_tx_ring *txq = &sc->txq[qid]; 7348*fd43cf6eSHans Rosenfeld 7349*fd43cf6eSHans Rosenfeld /* Set physical address of TX ring (256-byte aligned). */ 7350*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_FH_CBBC_QUEUE(qid), 7351*fd43cf6eSHans Rosenfeld txq->desc_dma.paddr >> 8); 7352*fd43cf6eSHans Rosenfeld } 7353*fd43cf6eSHans Rosenfeld iwn_nic_unlock(sc); 7354*fd43cf6eSHans Rosenfeld 7355*fd43cf6eSHans Rosenfeld /* Enable DMA channels. */ 7356*fd43cf6eSHans Rosenfeld for (chnl = 0; chnl < sc->ndmachnls; chnl++) { 7357*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_FH_TX_CONFIG(chnl), 7358*fd43cf6eSHans Rosenfeld IWN_FH_TX_CONFIG_DMA_ENA | 7359*fd43cf6eSHans Rosenfeld IWN_FH_TX_CONFIG_DMA_CREDIT_ENA); 7360*fd43cf6eSHans Rosenfeld } 7361*fd43cf6eSHans Rosenfeld 7362*fd43cf6eSHans Rosenfeld /* Clear "radio off" and "commands blocked" bits. */ 7363*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_RFKILL); 7364*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_CMD_BLOCKED); 7365*fd43cf6eSHans Rosenfeld 7366*fd43cf6eSHans Rosenfeld /* Clear pending interrupts. */ 7367*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_INT, 0xffffffff); 7368*fd43cf6eSHans Rosenfeld /* Enable interrupt coalescing. */ 7369*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_INT_COALESCING, 512 / 32); 7370*fd43cf6eSHans Rosenfeld /* Enable interrupts. */ 7371*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask); 7372*fd43cf6eSHans Rosenfeld 7373*fd43cf6eSHans Rosenfeld /* _Really_ make sure "radio off" bit is cleared! */ 7374*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_RFKILL); 7375*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_RFKILL); 7376*fd43cf6eSHans Rosenfeld 7377*fd43cf6eSHans Rosenfeld /* Enable shadow registers. */ 7378*fd43cf6eSHans Rosenfeld if (sc->hw_type >= IWN_HW_REV_TYPE_6000) 7379*fd43cf6eSHans Rosenfeld IWN_SETBITS(sc, IWN_SHADOW_REG_CTRL, 0x800fffff); 7380*fd43cf6eSHans Rosenfeld 7381*fd43cf6eSHans Rosenfeld if ((error = ops->load_firmware(sc)) != 0) { 7382*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 7383*fd43cf6eSHans Rosenfeld "!could not load firmware"); 7384*fd43cf6eSHans Rosenfeld return error; 7385*fd43cf6eSHans Rosenfeld } 7386*fd43cf6eSHans Rosenfeld /* Wait at most one second for firmware alive notification. */ 7387*fd43cf6eSHans Rosenfeld clk = ddi_get_lbolt() + drv_usectohz(1000000); 7388*fd43cf6eSHans Rosenfeld while ((sc->sc_flags & IWN_FLAG_FW_ALIVE) == 0) { 7389*fd43cf6eSHans Rosenfeld if (cv_timedwait(&sc->sc_alive_cv, &sc->sc_mtx, clk) < 0) { 7390*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 7391*fd43cf6eSHans Rosenfeld "!timeout waiting for adapter to initialize"); 7392*fd43cf6eSHans Rosenfeld return (IWN_FAIL); 7393*fd43cf6eSHans Rosenfeld } 7394*fd43cf6eSHans Rosenfeld } 7395*fd43cf6eSHans Rosenfeld /* Do post-firmware initialization. */ 7396*fd43cf6eSHans Rosenfeld return ops->post_alive(sc); 7397*fd43cf6eSHans Rosenfeld } 7398*fd43cf6eSHans Rosenfeld 7399*fd43cf6eSHans Rosenfeld static void 7400*fd43cf6eSHans Rosenfeld iwn_hw_stop(struct iwn_softc *sc, boolean_t lock) 7401*fd43cf6eSHans Rosenfeld { 7402*fd43cf6eSHans Rosenfeld int chnl, qid, ntries; 7403*fd43cf6eSHans Rosenfeld 7404*fd43cf6eSHans Rosenfeld if (lock) { 7405*fd43cf6eSHans Rosenfeld mutex_enter(&sc->sc_mtx); 7406*fd43cf6eSHans Rosenfeld } 7407*fd43cf6eSHans Rosenfeld 7408*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_RESET, IWN_RESET_NEVO); 7409*fd43cf6eSHans Rosenfeld 7410*fd43cf6eSHans Rosenfeld /* Disable interrupts. */ 7411*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_INT_MASK, 0); 7412*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_INT, 0xffffffff); 7413*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_FH_INT, 0xffffffff); 7414*fd43cf6eSHans Rosenfeld sc->sc_flags &= ~IWN_FLAG_USE_ICT; 7415*fd43cf6eSHans Rosenfeld 7416*fd43cf6eSHans Rosenfeld /* Make sure we no longer hold the NIC lock. */ 7417*fd43cf6eSHans Rosenfeld iwn_nic_unlock(sc); 7418*fd43cf6eSHans Rosenfeld 7419*fd43cf6eSHans Rosenfeld /* Stop TX scheduler. */ 7420*fd43cf6eSHans Rosenfeld iwn_prph_write(sc, sc->sched_txfact_addr, 0); 7421*fd43cf6eSHans Rosenfeld 7422*fd43cf6eSHans Rosenfeld /* Stop all DMA channels. */ 7423*fd43cf6eSHans Rosenfeld if (iwn_nic_lock(sc) == 0) { 7424*fd43cf6eSHans Rosenfeld for (chnl = 0; chnl < sc->ndmachnls; chnl++) { 7425*fd43cf6eSHans Rosenfeld IWN_WRITE(sc, IWN_FH_TX_CONFIG(chnl), 0); 7426*fd43cf6eSHans Rosenfeld for (ntries = 0; ntries < 200; ntries++) { 7427*fd43cf6eSHans Rosenfeld if (IWN_READ(sc, IWN_FH_TX_STATUS) & 7428*fd43cf6eSHans Rosenfeld IWN_FH_TX_STATUS_IDLE(chnl)) 7429*fd43cf6eSHans Rosenfeld break; 7430*fd43cf6eSHans Rosenfeld DELAY(10); 7431*fd43cf6eSHans Rosenfeld } 7432*fd43cf6eSHans Rosenfeld } 7433*fd43cf6eSHans Rosenfeld iwn_nic_unlock(sc); 7434*fd43cf6eSHans Rosenfeld } 7435*fd43cf6eSHans Rosenfeld 7436*fd43cf6eSHans Rosenfeld /* Stop RX ring. */ 7437*fd43cf6eSHans Rosenfeld iwn_reset_rx_ring(sc, &sc->rxq); 7438*fd43cf6eSHans Rosenfeld 7439*fd43cf6eSHans Rosenfeld /* Reset all TX rings. */ 7440*fd43cf6eSHans Rosenfeld for (qid = 0; qid < sc->ntxqs; qid++) 7441*fd43cf6eSHans Rosenfeld iwn_reset_tx_ring(sc, &sc->txq[qid]); 7442*fd43cf6eSHans Rosenfeld 7443*fd43cf6eSHans Rosenfeld if (iwn_nic_lock(sc) == 0) { 7444*fd43cf6eSHans Rosenfeld iwn_prph_write(sc, IWN_APMG_CLK_DIS, 7445*fd43cf6eSHans Rosenfeld IWN_APMG_CLK_CTRL_DMA_CLK_RQT); 7446*fd43cf6eSHans Rosenfeld iwn_nic_unlock(sc); 7447*fd43cf6eSHans Rosenfeld } 7448*fd43cf6eSHans Rosenfeld DELAY(5); 7449*fd43cf6eSHans Rosenfeld /* Power OFF adapter. */ 7450*fd43cf6eSHans Rosenfeld iwn_apm_stop(sc); 7451*fd43cf6eSHans Rosenfeld 7452*fd43cf6eSHans Rosenfeld sc->sc_flags &= ~(IWN_FLAG_HW_INITED | IWN_FLAG_FW_ALIVE); 7453*fd43cf6eSHans Rosenfeld 7454*fd43cf6eSHans Rosenfeld if (lock) { 7455*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_mtx); 7456*fd43cf6eSHans Rosenfeld } 7457*fd43cf6eSHans Rosenfeld } 7458*fd43cf6eSHans Rosenfeld 7459*fd43cf6eSHans Rosenfeld static int 7460*fd43cf6eSHans Rosenfeld iwn_init(struct iwn_softc *sc) 7461*fd43cf6eSHans Rosenfeld { 7462*fd43cf6eSHans Rosenfeld int error; 7463*fd43cf6eSHans Rosenfeld 7464*fd43cf6eSHans Rosenfeld mutex_enter(&sc->sc_mtx); 7465*fd43cf6eSHans Rosenfeld if (sc->sc_flags & IWN_FLAG_HW_INITED) 7466*fd43cf6eSHans Rosenfeld goto out; 7467*fd43cf6eSHans Rosenfeld if ((error = iwn_hw_prepare(sc)) != 0) { 7468*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, "!hardware not ready"); 7469*fd43cf6eSHans Rosenfeld goto fail; 7470*fd43cf6eSHans Rosenfeld } 7471*fd43cf6eSHans Rosenfeld 7472*fd43cf6eSHans Rosenfeld /* Check that the radio is not disabled by hardware switch. */ 7473*fd43cf6eSHans Rosenfeld if (!(IWN_READ(sc, IWN_GP_CNTRL) & IWN_GP_CNTRL_RFKILL)) { 7474*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, 7475*fd43cf6eSHans Rosenfeld "!radio is disabled by hardware switch"); 7476*fd43cf6eSHans Rosenfeld error = EPERM; /* :-) */ 7477*fd43cf6eSHans Rosenfeld goto fail; 7478*fd43cf6eSHans Rosenfeld } 7479*fd43cf6eSHans Rosenfeld 7480*fd43cf6eSHans Rosenfeld /* Read firmware images from the filesystem. */ 7481*fd43cf6eSHans Rosenfeld if ((error = iwn_read_firmware(sc)) != 0) { 7482*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, "!could not read firmware"); 7483*fd43cf6eSHans Rosenfeld goto fail; 7484*fd43cf6eSHans Rosenfeld } 7485*fd43cf6eSHans Rosenfeld 7486*fd43cf6eSHans Rosenfeld /* Initialize interrupt mask to default value. */ 7487*fd43cf6eSHans Rosenfeld sc->int_mask = IWN_INT_MASK_DEF; 7488*fd43cf6eSHans Rosenfeld sc->sc_flags &= ~IWN_FLAG_USE_ICT; 7489*fd43cf6eSHans Rosenfeld 7490*fd43cf6eSHans Rosenfeld /* Initialize hardware and upload firmware. */ 7491*fd43cf6eSHans Rosenfeld ASSERT(sc->fw.data != NULL && sc->fw.size > 0); 7492*fd43cf6eSHans Rosenfeld error = iwn_hw_init(sc); 7493*fd43cf6eSHans Rosenfeld if (error != 0) { 7494*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, "!could not initialize hardware"); 7495*fd43cf6eSHans Rosenfeld goto fail; 7496*fd43cf6eSHans Rosenfeld } 7497*fd43cf6eSHans Rosenfeld 7498*fd43cf6eSHans Rosenfeld /* Configure adapter now that it is ready. */ 7499*fd43cf6eSHans Rosenfeld if ((error = iwn_config(sc)) != 0) { 7500*fd43cf6eSHans Rosenfeld dev_err(sc->sc_dip, CE_WARN, "!could not configure device"); 7501*fd43cf6eSHans Rosenfeld goto fail; 7502*fd43cf6eSHans Rosenfeld } 7503*fd43cf6eSHans Rosenfeld 7504*fd43cf6eSHans Rosenfeld sc->sc_flags |= IWN_FLAG_HW_INITED; 7505*fd43cf6eSHans Rosenfeld out: 7506*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_mtx); 7507*fd43cf6eSHans Rosenfeld return 0; 7508*fd43cf6eSHans Rosenfeld 7509*fd43cf6eSHans Rosenfeld fail: 7510*fd43cf6eSHans Rosenfeld iwn_hw_stop(sc, B_FALSE); 7511*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_mtx); 7512*fd43cf6eSHans Rosenfeld return error; 7513*fd43cf6eSHans Rosenfeld } 7514*fd43cf6eSHans Rosenfeld 7515*fd43cf6eSHans Rosenfeld /* 7516*fd43cf6eSHans Rosenfeld * XXX code from usr/src/uts/common/io/net80211/net880211_output.c 7517*fd43cf6eSHans Rosenfeld * Copyright (c) 2001 Atsushi Onoe 7518*fd43cf6eSHans Rosenfeld * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting 7519*fd43cf6eSHans Rosenfeld * Copyright (c) 2007-2009 Damien Bergamini 7520*fd43cf6eSHans Rosenfeld * All rights reserved. 7521*fd43cf6eSHans Rosenfeld */ 7522*fd43cf6eSHans Rosenfeld 7523*fd43cf6eSHans Rosenfeld /* 7524*fd43cf6eSHans Rosenfeld * Add SSID element to a frame 7525*fd43cf6eSHans Rosenfeld */ 7526*fd43cf6eSHans Rosenfeld static uint8_t * 7527*fd43cf6eSHans Rosenfeld ieee80211_add_ssid(uint8_t *frm, const uint8_t *ssid, uint32_t len) 7528*fd43cf6eSHans Rosenfeld { 7529*fd43cf6eSHans Rosenfeld *frm++ = IEEE80211_ELEMID_SSID; 7530*fd43cf6eSHans Rosenfeld *frm++ = (uint8_t)len; 7531*fd43cf6eSHans Rosenfeld bcopy(ssid, frm, len); 7532*fd43cf6eSHans Rosenfeld return (frm + len); 7533*fd43cf6eSHans Rosenfeld } 7534*fd43cf6eSHans Rosenfeld 7535*fd43cf6eSHans Rosenfeld /* 7536*fd43cf6eSHans Rosenfeld * Add supported rates information element to a frame. 7537*fd43cf6eSHans Rosenfeld */ 7538*fd43cf6eSHans Rosenfeld static uint8_t * 7539*fd43cf6eSHans Rosenfeld ieee80211_add_rates(uint8_t *frm, const struct ieee80211_rateset *rs) 7540*fd43cf6eSHans Rosenfeld { 7541*fd43cf6eSHans Rosenfeld uint8_t nrates; 7542*fd43cf6eSHans Rosenfeld 7543*fd43cf6eSHans Rosenfeld *frm++ = IEEE80211_ELEMID_RATES; 7544*fd43cf6eSHans Rosenfeld nrates = rs->ir_nrates; 7545*fd43cf6eSHans Rosenfeld if (nrates > IEEE80211_RATE_SIZE) 7546*fd43cf6eSHans Rosenfeld nrates = IEEE80211_RATE_SIZE; 7547*fd43cf6eSHans Rosenfeld *frm++ = nrates; 7548*fd43cf6eSHans Rosenfeld bcopy(rs->ir_rates, frm, nrates); 7549*fd43cf6eSHans Rosenfeld return (frm + nrates); 7550*fd43cf6eSHans Rosenfeld } 7551*fd43cf6eSHans Rosenfeld 7552*fd43cf6eSHans Rosenfeld /* 7553*fd43cf6eSHans Rosenfeld * Add extended supported rates element to a frame, usually for 11g mode 7554*fd43cf6eSHans Rosenfeld */ 7555*fd43cf6eSHans Rosenfeld static uint8_t * 7556*fd43cf6eSHans Rosenfeld ieee80211_add_xrates(uint8_t *frm, const struct ieee80211_rateset *rs) 7557*fd43cf6eSHans Rosenfeld { 7558*fd43cf6eSHans Rosenfeld if (rs->ir_nrates > IEEE80211_RATE_SIZE) { 7559*fd43cf6eSHans Rosenfeld uint8_t nrates = rs->ir_nrates - IEEE80211_RATE_SIZE; 7560*fd43cf6eSHans Rosenfeld 7561*fd43cf6eSHans Rosenfeld *frm++ = IEEE80211_ELEMID_XRATES; 7562*fd43cf6eSHans Rosenfeld *frm++ = nrates; 7563*fd43cf6eSHans Rosenfeld bcopy(rs->ir_rates + IEEE80211_RATE_SIZE, frm, nrates); 7564*fd43cf6eSHans Rosenfeld frm += nrates; 7565*fd43cf6eSHans Rosenfeld } 7566*fd43cf6eSHans Rosenfeld return (frm); 7567*fd43cf6eSHans Rosenfeld } 7568*fd43cf6eSHans Rosenfeld 7569*fd43cf6eSHans Rosenfeld /* 7570*fd43cf6eSHans Rosenfeld * XXX: Hack to set the current channel to the value advertised in beacons or 7571*fd43cf6eSHans Rosenfeld * probe responses. Only used during AP detection. 7572*fd43cf6eSHans Rosenfeld * XXX: Duplicated from if_iwi.c 7573*fd43cf6eSHans Rosenfeld */ 7574*fd43cf6eSHans Rosenfeld static void 7575*fd43cf6eSHans Rosenfeld iwn_fix_channel(struct iwn_softc *sc, mblk_t *m, 7576*fd43cf6eSHans Rosenfeld struct iwn_rx_stat *stat) 7577*fd43cf6eSHans Rosenfeld { 7578*fd43cf6eSHans Rosenfeld struct ieee80211com *ic = &sc->sc_ic; 7579*fd43cf6eSHans Rosenfeld struct ieee80211_frame *wh; 7580*fd43cf6eSHans Rosenfeld uint8_t subtype; 7581*fd43cf6eSHans Rosenfeld uint8_t *frm, *efrm; 7582*fd43cf6eSHans Rosenfeld 7583*fd43cf6eSHans Rosenfeld wh = (struct ieee80211_frame *)m->b_rptr; 7584*fd43cf6eSHans Rosenfeld 7585*fd43cf6eSHans Rosenfeld if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_MGT) 7586*fd43cf6eSHans Rosenfeld return; 7587*fd43cf6eSHans Rosenfeld 7588*fd43cf6eSHans Rosenfeld subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; 7589*fd43cf6eSHans Rosenfeld 7590*fd43cf6eSHans Rosenfeld if (subtype != IEEE80211_FC0_SUBTYPE_BEACON && 7591*fd43cf6eSHans Rosenfeld subtype != IEEE80211_FC0_SUBTYPE_PROBE_RESP) 7592*fd43cf6eSHans Rosenfeld return; 7593*fd43cf6eSHans Rosenfeld 7594*fd43cf6eSHans Rosenfeld if (sc->sc_flags & IWN_FLAG_SCANNING_5GHZ) { 7595*fd43cf6eSHans Rosenfeld int chan = le16toh(stat->chan); 7596*fd43cf6eSHans Rosenfeld if (chan < __arraycount(ic->ic_sup_channels)) 7597*fd43cf6eSHans Rosenfeld ic->ic_curchan = &ic->ic_sup_channels[chan]; 7598*fd43cf6eSHans Rosenfeld return; 7599*fd43cf6eSHans Rosenfeld } 7600*fd43cf6eSHans Rosenfeld 7601*fd43cf6eSHans Rosenfeld frm = (uint8_t *)(wh + 1); 7602*fd43cf6eSHans Rosenfeld efrm = (uint8_t *)m->b_wptr; 7603*fd43cf6eSHans Rosenfeld 7604*fd43cf6eSHans Rosenfeld frm += 12; /* skip tstamp, bintval and capinfo fields */ 7605*fd43cf6eSHans Rosenfeld while (frm < efrm) { 7606*fd43cf6eSHans Rosenfeld if (*frm == IEEE80211_ELEMID_DSPARMS) 7607*fd43cf6eSHans Rosenfeld #if IEEE80211_CHAN_MAX < 255 7608*fd43cf6eSHans Rosenfeld if (frm[2] <= IEEE80211_CHAN_MAX) 7609*fd43cf6eSHans Rosenfeld #endif 7610*fd43cf6eSHans Rosenfeld ic->ic_curchan = &ic->ic_sup_channels[frm[2]]; 7611*fd43cf6eSHans Rosenfeld 7612*fd43cf6eSHans Rosenfeld frm += frm[1] + 2; 7613*fd43cf6eSHans Rosenfeld } 7614*fd43cf6eSHans Rosenfeld } 7615*fd43cf6eSHans Rosenfeld 7616*fd43cf6eSHans Rosenfeld /* 7617*fd43cf6eSHans Rosenfeld * invoked by GLD to start or open NIC 7618*fd43cf6eSHans Rosenfeld */ 7619*fd43cf6eSHans Rosenfeld static int 7620*fd43cf6eSHans Rosenfeld iwn_m_start(void *arg) 7621*fd43cf6eSHans Rosenfeld { 7622*fd43cf6eSHans Rosenfeld struct iwn_softc *sc; 7623*fd43cf6eSHans Rosenfeld ieee80211com_t *ic; 7624*fd43cf6eSHans Rosenfeld int err = IWN_FAIL; 7625*fd43cf6eSHans Rosenfeld 7626*fd43cf6eSHans Rosenfeld sc = (struct iwn_softc *)arg; 7627*fd43cf6eSHans Rosenfeld ASSERT(sc != NULL); 7628*fd43cf6eSHans Rosenfeld ic = &sc->sc_ic; 7629*fd43cf6eSHans Rosenfeld 7630*fd43cf6eSHans Rosenfeld err = iwn_init(sc); 7631*fd43cf6eSHans Rosenfeld if (err != IWN_SUCCESS) { 7632*fd43cf6eSHans Rosenfeld /* 7633*fd43cf6eSHans Rosenfeld * If initialization failed because the RF switch is off, 7634*fd43cf6eSHans Rosenfeld * return success anyway to make the 'plumb' succeed. 7635*fd43cf6eSHans Rosenfeld * The iwn_thread() tries to re-init background. 7636*fd43cf6eSHans Rosenfeld */ 7637*fd43cf6eSHans Rosenfeld if (err == EPERM && 7638*fd43cf6eSHans Rosenfeld !(IWN_READ(sc, IWN_GP_CNTRL) & IWN_GP_CNTRL_RFKILL)) { 7639*fd43cf6eSHans Rosenfeld mutex_enter(&sc->sc_mtx); 7640*fd43cf6eSHans Rosenfeld sc->sc_flags |= IWN_FLAG_HW_ERR_RECOVER; 7641*fd43cf6eSHans Rosenfeld sc->sc_flags |= IWN_FLAG_RADIO_OFF; 7642*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_mtx); 7643*fd43cf6eSHans Rosenfeld return (IWN_SUCCESS); 7644*fd43cf6eSHans Rosenfeld } 7645*fd43cf6eSHans Rosenfeld 7646*fd43cf6eSHans Rosenfeld return (err); 7647*fd43cf6eSHans Rosenfeld } 7648*fd43cf6eSHans Rosenfeld 7649*fd43cf6eSHans Rosenfeld ieee80211_new_state(ic, IEEE80211_S_INIT, -1); 7650*fd43cf6eSHans Rosenfeld 7651*fd43cf6eSHans Rosenfeld mutex_enter(&sc->sc_mtx); 7652*fd43cf6eSHans Rosenfeld sc->sc_flags |= IWN_FLAG_RUNNING; 7653*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_mtx); 7654*fd43cf6eSHans Rosenfeld 7655*fd43cf6eSHans Rosenfeld return (IWN_SUCCESS); 7656*fd43cf6eSHans Rosenfeld } 7657*fd43cf6eSHans Rosenfeld 7658*fd43cf6eSHans Rosenfeld /* 7659*fd43cf6eSHans Rosenfeld * invoked by GLD to stop or down NIC 7660*fd43cf6eSHans Rosenfeld */ 7661*fd43cf6eSHans Rosenfeld static void 7662*fd43cf6eSHans Rosenfeld iwn_m_stop(void *arg) 7663*fd43cf6eSHans Rosenfeld { 7664*fd43cf6eSHans Rosenfeld struct iwn_softc *sc; 7665*fd43cf6eSHans Rosenfeld ieee80211com_t *ic; 7666*fd43cf6eSHans Rosenfeld 7667*fd43cf6eSHans Rosenfeld sc = (struct iwn_softc *)arg; 7668*fd43cf6eSHans Rosenfeld ASSERT(sc != NULL); 7669*fd43cf6eSHans Rosenfeld ic = &sc->sc_ic; 7670*fd43cf6eSHans Rosenfeld 7671*fd43cf6eSHans Rosenfeld iwn_hw_stop(sc, B_TRUE); 7672*fd43cf6eSHans Rosenfeld 7673*fd43cf6eSHans Rosenfeld /* 7674*fd43cf6eSHans Rosenfeld * release buffer for calibration 7675*fd43cf6eSHans Rosenfeld */ 7676*fd43cf6eSHans Rosenfeld 7677*fd43cf6eSHans Rosenfeld ieee80211_stop_watchdog(ic); 7678*fd43cf6eSHans Rosenfeld ieee80211_new_state(ic, IEEE80211_S_INIT, -1); 7679*fd43cf6eSHans Rosenfeld 7680*fd43cf6eSHans Rosenfeld mutex_enter(&sc->sc_mtx); 7681*fd43cf6eSHans Rosenfeld sc->sc_flags &= ~IWN_FLAG_HW_ERR_RECOVER; 7682*fd43cf6eSHans Rosenfeld sc->sc_flags &= ~IWN_FLAG_RATE_AUTO_CTL; 7683*fd43cf6eSHans Rosenfeld 7684*fd43cf6eSHans Rosenfeld sc->sc_flags &= ~IWN_FLAG_RUNNING; 7685*fd43cf6eSHans Rosenfeld sc->sc_flags &= ~IWN_FLAG_SCANNING; 7686*fd43cf6eSHans Rosenfeld mutex_exit(&sc->sc_mtx); 7687*fd43cf6eSHans Rosenfeld } 7688*fd43cf6eSHans Rosenfeld 7689*fd43cf6eSHans Rosenfeld 7690*fd43cf6eSHans Rosenfeld /* 7691*fd43cf6eSHans Rosenfeld * Module Loading Data & Entry Points 7692*fd43cf6eSHans Rosenfeld */ 7693*fd43cf6eSHans Rosenfeld DDI_DEFINE_STREAM_OPS(iwn_devops, nulldev, nulldev, iwn_attach, 7694*fd43cf6eSHans Rosenfeld iwn_detach, nodev, NULL, D_MP, NULL, iwn_quiesce); 7695*fd43cf6eSHans Rosenfeld 7696*fd43cf6eSHans Rosenfeld static struct modldrv iwn_modldrv = { 7697*fd43cf6eSHans Rosenfeld &mod_driverops, 7698*fd43cf6eSHans Rosenfeld "Intel WiFi Link 4965 and 1000/5000/6000 series driver", 7699*fd43cf6eSHans Rosenfeld &iwn_devops 7700*fd43cf6eSHans Rosenfeld }; 7701*fd43cf6eSHans Rosenfeld 7702*fd43cf6eSHans Rosenfeld static struct modlinkage iwn_modlinkage = { 7703*fd43cf6eSHans Rosenfeld MODREV_1, 7704*fd43cf6eSHans Rosenfeld &iwn_modldrv, 7705*fd43cf6eSHans Rosenfeld NULL 7706*fd43cf6eSHans Rosenfeld }; 7707*fd43cf6eSHans Rosenfeld 7708*fd43cf6eSHans Rosenfeld int 7709*fd43cf6eSHans Rosenfeld _init(void) 7710*fd43cf6eSHans Rosenfeld { 7711*fd43cf6eSHans Rosenfeld int status; 7712*fd43cf6eSHans Rosenfeld 7713*fd43cf6eSHans Rosenfeld status = ddi_soft_state_init(&iwn_state, 7714*fd43cf6eSHans Rosenfeld sizeof (struct iwn_softc), 1); 7715*fd43cf6eSHans Rosenfeld if (status != DDI_SUCCESS) 7716*fd43cf6eSHans Rosenfeld return (status); 7717*fd43cf6eSHans Rosenfeld 7718*fd43cf6eSHans Rosenfeld mac_init_ops(&iwn_devops, "iwn"); 7719*fd43cf6eSHans Rosenfeld status = mod_install(&iwn_modlinkage); 7720*fd43cf6eSHans Rosenfeld if (status != DDI_SUCCESS) { 7721*fd43cf6eSHans Rosenfeld mac_fini_ops(&iwn_devops); 7722*fd43cf6eSHans Rosenfeld ddi_soft_state_fini(&iwn_state); 7723*fd43cf6eSHans Rosenfeld } 7724*fd43cf6eSHans Rosenfeld 7725*fd43cf6eSHans Rosenfeld return (status); 7726*fd43cf6eSHans Rosenfeld } 7727*fd43cf6eSHans Rosenfeld 7728*fd43cf6eSHans Rosenfeld int 7729*fd43cf6eSHans Rosenfeld _fini(void) 7730*fd43cf6eSHans Rosenfeld { 7731*fd43cf6eSHans Rosenfeld int status; 7732*fd43cf6eSHans Rosenfeld 7733*fd43cf6eSHans Rosenfeld status = mod_remove(&iwn_modlinkage); 7734*fd43cf6eSHans Rosenfeld if (status == DDI_SUCCESS) { 7735*fd43cf6eSHans Rosenfeld mac_fini_ops(&iwn_devops); 7736*fd43cf6eSHans Rosenfeld ddi_soft_state_fini(&iwn_state); 7737*fd43cf6eSHans Rosenfeld } 7738*fd43cf6eSHans Rosenfeld 7739*fd43cf6eSHans Rosenfeld return (status); 7740*fd43cf6eSHans Rosenfeld } 7741*fd43cf6eSHans Rosenfeld 7742*fd43cf6eSHans Rosenfeld int 7743*fd43cf6eSHans Rosenfeld _info(struct modinfo *mip) 7744*fd43cf6eSHans Rosenfeld { 7745*fd43cf6eSHans Rosenfeld return (mod_info(&iwn_modlinkage, mip)); 7746*fd43cf6eSHans Rosenfeld } 7747