diff options
Diffstat (limited to 'usr/src')
-rw-r--r-- | usr/src/uts/common/io/iwk/iwk2.c | 2085 | ||||
-rw-r--r-- | usr/src/uts/common/io/iwk/iwk2_var.h | 18 | ||||
-rw-r--r-- | usr/src/uts/common/io/iwk/iwk_calibration.h | 1128 | ||||
-rw-r--r-- | usr/src/uts/common/io/iwk/iwk_eeprom.h | 10 | ||||
-rw-r--r-- | usr/src/uts/common/io/iwk/iwk_hw.h | 20 |
5 files changed, 3053 insertions, 208 deletions
diff --git a/usr/src/uts/common/io/iwk/iwk2.c b/usr/src/uts/common/io/iwk/iwk2.c index 0d87701db7..1147fa87e5 100644 --- a/usr/src/uts/common/io/iwk/iwk2.c +++ b/usr/src/uts/common/io/iwk/iwk2.c @@ -26,8 +26,6 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * Driver for Intel PRO/Wireless 4965AGN(kedron) 802.11 network adapters. */ @@ -58,6 +56,7 @@ #include <sys/policy.h> #include <sys/pci.h> +#include "iwk_calibration.h" #include "iwk_hw.h" #include "iwk_eeprom.h" #include "iwk2_var.h" @@ -79,6 +78,7 @@ #define IWK_DEBUG_RATECTL (1 << 12) #define IWK_DEBUG_RADIO (1 << 13) #define IWK_DEBUG_RESUME (1 << 14) +#define IWK_DEBUG_CALIBRATION (1 << 15) uint32_t iwk_dbg_flags = 0; #define IWK_DBG(x) \ iwk_dbg x @@ -261,12 +261,12 @@ static void iwk_rx_intr(iwk_sc_t *, iwk_rx_desc_t *, static void iwk_tx_intr(iwk_sc_t *, iwk_rx_desc_t *, iwk_rx_data_t *); static void iwk_cmd_intr(iwk_sc_t *, iwk_rx_desc_t *); -static uint_t iwk_intr(caddr_t); +static uint_t iwk_intr(caddr_t, caddr_t); static int iwk_eep_load(iwk_sc_t *sc); static void iwk_get_mac_from_eep(iwk_sc_t *sc); static int iwk_eep_sem_down(iwk_sc_t *sc); static void iwk_eep_sem_up(iwk_sc_t *sc); -static uint_t iwk_rx_softintr(caddr_t); +static uint_t iwk_rx_softintr(caddr_t, caddr_t); static uint8_t iwk_rate_to_plcp(int); static int iwk_cmd(iwk_sc_t *, int, const void *, int, int); static void iwk_set_led(iwk_sc_t *, uint8_t, uint8_t, uint8_t); @@ -281,9 +281,39 @@ static void iwk_stop(iwk_sc_t *); static void iwk_amrr_init(iwk_amrr_t *); static void iwk_amrr_timeout(iwk_sc_t *); static void iwk_amrr_ratectl(void *, ieee80211_node_t *); - -static int iwk_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); -static int iwk_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); +static int32_t iwk_curr_tempera(iwk_sc_t *sc); +static int iwk_tx_power_calibration(iwk_sc_t *sc); +static inline int iwk_is_24G_band(iwk_sc_t *sc); +static inline int iwk_is_fat_channel(iwk_sc_t *sc); +static int iwk_txpower_grp(uint16_t channel); +static struct iwk_eep_channel *iwk_get_eep_channel(iwk_sc_t *sc, + uint16_t channel, + int is_24G, int is_fat, int is_hi_chan); +static int32_t iwk_band_number(iwk_sc_t *sc, uint16_t channel); +static int iwk_division(int32_t num, int32_t denom, int32_t *res); +static int32_t iwk_interpolate_value(int32_t x, int32_t x1, int32_t y1, + int32_t x2, int32_t y2); +static int iwk_channel_interpolate(iwk_sc_t *sc, uint16_t channel, + struct iwk_eep_calib_channel_info *chan_info); +static int32_t iwk_voltage_compensation(int32_t eep_voltage, + int32_t curr_voltage); +static int32_t iwk_min_power_index(int32_t rate_pow_idx, int32_t is_24G); +static int iwk_txpower_table_cmd_init(iwk_sc_t *sc, + struct iwk_tx_power_db *tp_db); +static void iwk_statistics_notify(iwk_sc_t *sc, iwk_rx_desc_t *desc); +static int iwk_is_associated(iwk_sc_t *sc); +static int iwk_rxgain_diff_init(iwk_sc_t *sc); +static int iwk_rxgain_diff(iwk_sc_t *sc); +static int iwk_rx_sens_init(iwk_sc_t *sc); +static int iwk_rx_sens(iwk_sc_t *sc); +static int iwk_cck_sens(iwk_sc_t *sc, uint32_t actual_rx_time); +static int iwk_ofdm_sens(iwk_sc_t *sc, uint32_t actual_rx_time); + +static void iwk_write_event_log(iwk_sc_t *); +static void iwk_write_error_log(iwk_sc_t *); + +static int iwk_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); +static int iwk_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); /* * GLD specific operations @@ -294,7 +324,7 @@ static void iwk_m_stop(void *arg); static int iwk_m_unicst(void *arg, const uint8_t *macaddr); static int iwk_m_multicst(void *arg, boolean_t add, const uint8_t *m); static int iwk_m_promisc(void *arg, boolean_t on); -static mblk_t *iwk_m_tx(void *arg, mblk_t *mp); +static mblk_t *iwk_m_tx(void *arg, mblk_t *mp); static void iwk_m_ioctl(void *arg, queue_t *wq, mblk_t *mp); static void iwk_destroy_locks(iwk_sc_t *sc); @@ -419,6 +449,10 @@ iwk_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) wifi_data_t wd = { 0 }; mac_register_t *macp; + int intr_type; + int intr_count; + int intr_actual; + switch (cmd) { case DDI_ATTACH: break; @@ -480,25 +514,50 @@ iwk_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) goto attach_fail2a; } - /* - * Initialize mutexs and condvars - */ - err = ddi_get_iblock_cookie(dip, 0, &sc->sc_iblk); + err = ddi_intr_get_supported_types(dip, &intr_type); + if ((err != DDI_SUCCESS) || (!(intr_type & DDI_INTR_TYPE_FIXED))) { + cmn_err(CE_WARN, "iwk_attach(): " + "Fixed type interrupt is not supported\n"); + goto attach_fail_intr_a; + } + + err = ddi_intr_get_nintrs(dip, DDI_INTR_TYPE_FIXED, &intr_count); + if ((err != DDI_SUCCESS) || (intr_count != 1)) { + cmn_err(CE_WARN, "iwk_attach(): " + "No fixed interrupts\n"); + goto attach_fail_intr_a; + } + + sc->sc_intr_htable = kmem_zalloc(sizeof (ddi_intr_handle_t), KM_SLEEP); + + err = ddi_intr_alloc(dip, sc->sc_intr_htable, DDI_INTR_TYPE_FIXED, 0, + intr_count, &intr_actual, 0); + if ((err != DDI_SUCCESS) || (intr_actual != 1)) { + cmn_err(CE_WARN, "iwk_attach(): " + "ddi_intr_alloc() failed 0x%x\n", err); + goto attach_fail_intr_b; + } + + err = ddi_intr_get_pri(sc->sc_intr_htable[0], &sc->sc_intr_pri); if (err != DDI_SUCCESS) { - cmn_err(CE_WARN, - "iwk_attach(): failed to do ddi_get_iblock_cookie()\n"); - goto attach_fail2b; + cmn_err(CE_WARN, "iwk_attach(): " + "ddi_intr_get_pri() failed 0x%x\n", err); + goto attach_fail_intr_c; } - mutex_init(&sc->sc_glock, NULL, MUTEX_DRIVER, sc->sc_iblk); - mutex_init(&sc->sc_tx_lock, NULL, MUTEX_DRIVER, sc->sc_iblk); + + mutex_init(&sc->sc_glock, NULL, MUTEX_DRIVER, + DDI_INTR_PRI(sc->sc_intr_pri)); + mutex_init(&sc->sc_tx_lock, NULL, MUTEX_DRIVER, + DDI_INTR_PRI(sc->sc_intr_pri)); + mutex_init(&sc->sc_mt_lock, NULL, MUTEX_DRIVER, + DDI_INTR_PRI(sc->sc_intr_pri)); + cv_init(&sc->sc_fw_cv, NULL, CV_DRIVER, NULL); cv_init(&sc->sc_cmd_cv, NULL, CV_DRIVER, NULL); cv_init(&sc->sc_tx_cv, "tx-ring", CV_DRIVER, NULL); /* * initialize the mfthread */ - mutex_init(&sc->sc_mt_lock, NULL, MUTEX_DRIVER, - (void *) sc->sc_iblk); cv_init(&sc->sc_mt_cv, NULL, CV_DRIVER, NULL); sc->sc_mf_thread = NULL; sc->sc_mf_thread_switch = 0; @@ -508,7 +567,8 @@ iwk_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) */ err = iwk_alloc_shared(sc); if (err != DDI_SUCCESS) { - cmn_err(CE_WARN, "failed to allocate shared page\n"); + cmn_err(CE_WARN, "iwk_attach(): " + "failed to allocate shared page\n"); goto attach_fail3; } @@ -517,7 +577,8 @@ iwk_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) */ err = iwk_alloc_kw(sc); if (err != DDI_SUCCESS) { - cmn_err(CE_WARN, "failed to allocate keep warm page\n"); + cmn_err(CE_WARN, "iwk_attach(): " + "failed to allocate keep warm page\n"); goto attach_fail3a; } @@ -526,7 +587,8 @@ iwk_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) */ err = iwk_preinit(sc); if (err != DDI_SUCCESS) { - cmn_err(CE_WARN, "failed to init hardware\n"); + cmn_err(CE_WARN, "iwk_attach(): " + "failed to init hardware\n"); goto attach_fail4; } @@ -579,6 +641,7 @@ iwk_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) * Support WPA/WPA2 */ ic->ic_caps |= IEEE80211_C_WPA; + /* set supported .11b and .11g rates */ ic->ic_sup_rates[IEEE80211_MODE_11B] = iwk_rateset_11b; ic->ic_sup_rates[IEEE80211_MODE_11G] = iwk_rateset_11g; @@ -591,7 +654,7 @@ iwk_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM | IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ; } - ic->ic_ibss_chan = &ic->ic_sup_channels[0]; + ic->ic_xmit = iwk_send; /* * init Wifi layer @@ -610,6 +673,7 @@ iwk_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) */ sc->sc_newstate = ic->ic_newstate; ic->ic_newstate = iwk_newstate; + sc->sc_recv_mgmt = ic->ic_recv_mgmt; ic->ic_node_alloc = iwk_node_alloc; ic->ic_node_free = iwk_node_free; ic->ic_crypto.cs_key_set = iwk_key_set; @@ -618,27 +682,32 @@ iwk_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) * initialize default tx key */ ic->ic_def_txkey = 0; - - err = ddi_add_softintr(dip, DDI_SOFTINT_LOW, - &sc->sc_rx_softint_id, &sc->sc_iblk, NULL, iwk_rx_softintr, - (caddr_t)sc); + err = ddi_intr_add_softint(dip, &sc->sc_soft_hdl, DDI_INTR_SOFTPRI_MAX, + iwk_rx_softintr, (caddr_t)sc); if (err != DDI_SUCCESS) { - cmn_err(CE_WARN, - "iwk_attach(): failed to do ddi_add_softintr()\n"); + cmn_err(CE_WARN, "iwk_attach(): " + "add soft interrupt failed\n"); goto attach_fail7; } /* * Add the interrupt handler */ - err = ddi_add_intr(dip, 0, &sc->sc_iblk, NULL, - iwk_intr, (caddr_t)sc); + err = ddi_intr_add_handler(sc->sc_intr_htable[0], iwk_intr, + (caddr_t)sc, NULL); if (err != DDI_SUCCESS) { - cmn_err(CE_WARN, - "iwk_attach(): failed to do ddi_add_intr()\n"); + cmn_err(CE_WARN, "iwk_attach(): " + "ddi_intr_add_handle() failed\n"); goto attach_fail8; } + err = ddi_intr_enable(sc->sc_intr_htable[0]); + if (err != DDI_SUCCESS) { + cmn_err(CE_WARN, "iwk_attach(): " + "ddi_intr_enable() failed\n"); + goto attach_fail_intr_d; + } + /* * Initialize pointer to device specific functions */ @@ -693,7 +762,6 @@ iwk_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) * create the mf thread to handle the link status, * recovery fatal error, etc. */ - sc->sc_mf_thread_switch = 1; if (sc->sc_mf_thread == NULL) sc->sc_mf_thread = thread_create((caddr_t)NULL, 0, @@ -703,10 +771,13 @@ iwk_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) return (DDI_SUCCESS); attach_fail9: - ddi_remove_intr(dip, 0, sc->sc_iblk); + (void) ddi_intr_disable(sc->sc_intr_htable[0]); +attach_fail_intr_d: + (void) ddi_intr_remove_handler(sc->sc_intr_htable[0]); + attach_fail8: - ddi_remove_softintr(sc->sc_rx_softint_id); - sc->sc_rx_softint_id = NULL; + (void) ddi_intr_remove_softint(sc->sc_soft_hdl); + sc->sc_soft_hdl = NULL; attach_fail7: ieee80211_detach(ic); attach_fail6: @@ -719,7 +790,11 @@ attach_fail3a: iwk_free_shared(sc); attach_fail3: iwk_destroy_locks(sc); -attach_fail2b: +attach_fail_intr_c: + (void) ddi_intr_free(sc->sc_intr_htable[0]); +attach_fail_intr_b: + kmem_free(sc->sc_intr_htable, sizeof (ddi_intr_handle_t)); +attach_fail_intr_a: ddi_regs_map_free(&sc->sc_handle); attach_fail2a: ddi_regs_map_free(&sc->sc_cfg_handle); @@ -757,6 +832,10 @@ iwk_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) if (!(sc->sc_flags & IWK_F_ATTACHED)) return (DDI_FAILURE); + err = mac_disable(sc->sc_ic.ic_mach); + if (err != DDI_SUCCESS) + return (err); + /* * Destroy the mf_thread */ @@ -774,9 +853,7 @@ iwk_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) /* * Unregiste from the MAC layer subsystem */ - err = mac_unregister(sc->sc_ic.ic_mach); - if (err != DDI_SUCCESS) - return (err); + (void) mac_unregister(sc->sc_ic.ic_mach); mutex_enter(&sc->sc_glock); iwk_free_fw_dma(sc); @@ -785,9 +862,13 @@ iwk_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) iwk_free_shared(sc); mutex_exit(&sc->sc_glock); - ddi_remove_intr(dip, 0, sc->sc_iblk); - ddi_remove_softintr(sc->sc_rx_softint_id); - sc->sc_rx_softint_id = NULL; + (void) ddi_intr_disable(sc->sc_intr_htable[0]); + (void) ddi_intr_remove_handler(sc->sc_intr_htable[0]); + (void) ddi_intr_free(sc->sc_intr_htable[0]); + kmem_free(sc->sc_intr_htable, sizeof (ddi_intr_handle_t)); + + (void) ddi_intr_remove_softint(sc->sc_soft_hdl); + sc->sc_soft_hdl = NULL; /* * detach ieee80211 @@ -1254,9 +1335,9 @@ iwk_alloc_tx_ring(iwk_sc_t *sc, iwk_tx_ring_t *ring, data->paddr_desc = paddr_desc_h + _PTRDIFF(data->desc, desc_h); data->cmd = cmd_h + i; /* (i % slots); */ + /* ((i % slots) * sizeof (iwk_cmd_t)); */ data->paddr_cmd = paddr_cmd_h + _PTRDIFF(data->cmd, cmd_h); - /* ((i % slots) * sizeof (iwk_cmd_t)); */ } dma_p = &ring->data[0].dma_data; IWK_DBG((IWK_DEBUG_DMA, "tx buffer[0][ncookies:%d addr:%lx " @@ -1397,13 +1478,13 @@ iwk_newstate(ieee80211com_t *ic, enum ieee80211_state nstate, int arg) { iwk_sc_t *sc = (iwk_sc_t *)ic; ieee80211_node_t *in = ic->ic_bss; - iwk_tx_power_table_cmd_t txpower; enum ieee80211_state ostate = ic->ic_state; int i, err = IWK_SUCCESS; mutex_enter(&sc->sc_glock); switch (nstate) { case IEEE80211_S_SCAN: + ic->ic_state = nstate; if (ostate == IEEE80211_S_INIT) { ic->ic_flags |= IEEE80211_F_SCAN | IEEE80211_F_ASCAN; /* let LED blink when scanning */ @@ -1414,11 +1495,11 @@ iwk_newstate(ieee80211com_t *ic, enum ieee80211_state nstate, int arg) "could not initiate scan\n")); ic->ic_flags &= ~(IEEE80211_F_SCAN | IEEE80211_F_ASCAN); + ic->ic_state = ostate; mutex_exit(&sc->sc_glock); return (err); } } - ic->ic_state = nstate; sc->sc_clk = 0; mutex_exit(&sc->sc_glock); return (IWK_SUCCESS); @@ -1447,70 +1528,60 @@ iwk_newstate(ieee80211com_t *ic, enum ieee80211_state nstate, int arg) iwk_set_led(sc, 2, 10, 10); break; } - - if (ic->ic_opmode != IEEE80211_M_STA) { - (void) iwk_hw_set_before_auth(sc); - /* need setup beacon here */ - } IWK_DBG((IWK_DEBUG_80211, "iwk: associated.")); - /* update adapter's configuration */ - sc->sc_config.assoc_id = sc->sc_assoc_id & 0x3fff; - /* short preamble/slot time are negotiated when associating */ - sc->sc_config.flags &= ~LE_32(RXON_FLG_SHORT_PREAMBLE_MSK | - RXON_FLG_SHORT_SLOT_MSK); - - if (ic->ic_flags & IEEE80211_F_SHSLOT) - sc->sc_config.flags |= LE_32(RXON_FLG_SHORT_SLOT_MSK); + /* none IBSS mode */ + if (ic->ic_opmode != IEEE80211_M_IBSS) { + /* update adapter's configuration */ + sc->sc_config.assoc_id = sc->sc_assoc_id & 0x3fff; + /* + * short preamble/slot time are + * negotiated when associating + */ + sc->sc_config.flags &= + ~LE_32(RXON_FLG_SHORT_PREAMBLE_MSK | + RXON_FLG_SHORT_SLOT_MSK); + + if (ic->ic_flags & IEEE80211_F_SHSLOT) + sc->sc_config.flags |= + LE_32(RXON_FLG_SHORT_SLOT_MSK); + + if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) + sc->sc_config.flags |= + LE_32(RXON_FLG_SHORT_PREAMBLE_MSK); - if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) - sc->sc_config.flags |= - LE_32(RXON_FLG_SHORT_PREAMBLE_MSK); - - sc->sc_config.filter_flags |= LE_32(RXON_FILTER_ASSOC_MSK); - - if (ic->ic_opmode != IEEE80211_M_STA) sc->sc_config.filter_flags |= - LE_32(RXON_FILTER_BCON_AWARE_MSK); - - IWK_DBG((IWK_DEBUG_80211, "config chan %d flags %x" - " filter_flags %x\n", - sc->sc_config.chan, sc->sc_config.flags, - sc->sc_config.filter_flags)); - err = iwk_cmd(sc, REPLY_RXON, &sc->sc_config, - sizeof (iwk_rxon_cmd_t), 1); - if (err != IWK_SUCCESS) { - IWK_DBG((IWK_DEBUG_80211, - "could not update configuration\n")); - mutex_exit(&sc->sc_glock); - return (err); + LE_32(RXON_FILTER_ASSOC_MSK); + + if (ic->ic_opmode != IEEE80211_M_STA) + sc->sc_config.filter_flags |= + LE_32(RXON_FILTER_BCON_AWARE_MSK); + + IWK_DBG((IWK_DEBUG_80211, "config chan %d flags %x" + " filter_flags %x\n", + sc->sc_config.chan, sc->sc_config.flags, + sc->sc_config.filter_flags)); + err = iwk_cmd(sc, REPLY_RXON, &sc->sc_config, + sizeof (iwk_rxon_cmd_t), 1); + if (err != IWK_SUCCESS) { + IWK_DBG((IWK_DEBUG_80211, + "could not update configuration\n")); + mutex_exit(&sc->sc_glock); + return (err); + } } + /* obtain current temperature of chipset */ + sc->sc_tempera = iwk_curr_tempera(sc); + /* - * set Tx power for 2.4GHz channels - * (need further investigation. fix tx power at present) - * This cmd should be issued each time the reply_rxon cmd is - * invoked. + * make Tx power calibration to determine + * the gains of DSP and radio */ - (void) memset(&txpower, 0, sizeof (txpower)); - txpower.band = 1; /* for 2.4G */ - txpower.channel = sc->sc_config.chan; - txpower.channel_normal_width = 0; - for (i = 0; i < POWER_TABLE_NUM_HT_OFDM_ENTRIES; i++) { - txpower.tx_power.ht_ofdm_power[i].s.ramon_tx_gain = - 0x3f3f; - txpower.tx_power.ht_ofdm_power[i].s.dsp_predis_atten = - 110 | (110 << 8); - } - txpower.tx_power.ht_ofdm_power[POWER_TABLE_NUM_HT_OFDM_ENTRIES] - .s.ramon_tx_gain = 0x3f3f; - txpower.tx_power.ht_ofdm_power[POWER_TABLE_NUM_HT_OFDM_ENTRIES] - .s.dsp_predis_atten = 110 | (110 << 8); - err = iwk_cmd(sc, REPLY_TX_PWR_TABLE_CMD, &txpower, - sizeof (txpower), 1); - if (err != IWK_SUCCESS) { - cmn_err(CE_WARN, "iwk_newstate(): failed to " - "set txpower\n"); + err = iwk_tx_power_calibration(sc); + if (err) { + cmn_err(CE_WARN, "iwk_newstate(): " + "failed to set tx power table\n"); return (err); } @@ -1541,7 +1612,37 @@ iwk_newstate(ieee80211com_t *ic, enum ieee80211_state nstate, int arg) } mutex_exit(&sc->sc_glock); - return (sc->sc_newstate(ic, nstate, arg)); + + err = sc->sc_newstate(ic, nstate, arg); + + if (nstate == IEEE80211_S_RUN) { + + mutex_enter(&sc->sc_glock); + + /* + * make initialization for Receiver + * sensitivity calibration + */ + err = iwk_rx_sens_init(sc); + if (err) { + cmn_err(CE_WARN, "iwk_newstate(): " + "failed to init RX sensitivity\n"); + return (err); + } + + /* make initialization for Receiver gain balance */ + err = iwk_rxgain_diff_init(sc); + if (err) { + cmn_err(CE_WARN, "iwk_newstate(): " + "failed to init phy calibration\n"); + return (err); + } + + mutex_exit(&sc->sc_glock); + + } + + return (err); } /*ARGSUSED*/ @@ -1561,8 +1662,8 @@ static int iwk_key_set(ieee80211com_t *ic, const struct ieee80211_key *k, default: return (0); } - sc->sc_config.filter_flags &= ~(RXON_FILTER_DIS_DECRYPT_MSK - | RXON_FILTER_DIS_GRP_DECRYPT_MSK); + sc->sc_config.filter_flags &= ~(RXON_FILTER_DIS_DECRYPT_MSK | + RXON_FILTER_DIS_GRP_DECRYPT_MSK); mutex_enter(&sc->sc_glock); @@ -1634,15 +1735,12 @@ iwk_mac_access_exit(iwk_sc_t *sc) tmp & ~CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); } -/* - * this function defined here for future use. - * static uint32_t - * iwk_mem_read(iwk_sc_t *sc, uint32_t addr) - * { - * IWK_WRITE(sc, HBUS_TARG_MEM_RADDR, addr); - * return (IWK_READ(sc, HBUS_TARG_MEM_RDAT)); - * } - */ +static uint32_t +iwk_mem_read(iwk_sc_t *sc, uint32_t addr) +{ + IWK_WRITE(sc, HBUS_TARG_MEM_RADDR, addr); + return (IWK_READ(sc, HBUS_TARG_MEM_RDAT)); +} static void iwk_mem_write(iwk_sc_t *sc, uint32_t addr, uint32_t data) @@ -1770,8 +1868,8 @@ iwk_rx_intr(iwk_sc_t *sc, iwk_rx_desc_t *desc, iwk_rx_data_t *data) phyinfo = (struct iwk_rx_non_cfg_phy *)stat->non_cfg_phy; agc = (phyinfo->agc_info & IWK_AGC_DB_MASK) >> IWK_AGC_DB_POS; mrssi = 0; - ants = (stat->phy_flags & RX_PHY_FLAGS_ANTENNAE_MASK) - >> RX_PHY_FLAGS_ANTENNAE_OFFSET; + ants = (stat->phy_flags & RX_PHY_FLAGS_ANTENNAE_MASK) >> + RX_PHY_FLAGS_ANTENNAE_OFFSET; for (i = 0; i < 3; i++) { if (ants & (1 << i)) mrssi = MAX(mrssi, phyinfo->rssi_info[i << 1]); @@ -1780,8 +1878,8 @@ iwk_rx_intr(iwk_sc_t *sc, iwk_rx_desc_t *desc, iwk_rx_data_t *data) /* * convert dBm to percentage ??? */ - rssi = (100 * 75 * 75 - (-20 - t) * (15 * 75 + 62 * (-20 - t))) - / (75 * 75); + rssi = (100 * 75 * 75 - (-20 - t) * (15 * 75 + 62 * (-20 - t))) / + (75 * 75); if (rssi > 100) rssi = 100; if (rssi < 1) @@ -1999,7 +2097,8 @@ iwk_ucode_alive(iwk_sc_t *sc, iwk_rx_desc_t *desc) } static uint_t -iwk_rx_softintr(caddr_t arg) +/* LINTED: argument unused in function: unused */ +iwk_rx_softintr(caddr_t arg, caddr_t unused) { iwk_sc_t *sc = (iwk_sc_t *)arg; ieee80211com_t *ic = &sc->sc_ic; @@ -2034,7 +2133,10 @@ iwk_rx_softintr(caddr_t arg) /* a command other than a tx need to be replied */ if (!(desc->hdr.qid & 0x80) && (desc->hdr.type != REPLY_RX_PHY_CMD) && - (desc->hdr.type != REPLY_TX)) + (desc->hdr.type != REPLY_TX) && + (desc->hdr.type != REPLY_TX_PWR_TABLE_CMD) && + (desc->hdr.type != REPLY_PHY_CALIBRATION_CMD) && + (desc->hdr.type != SENSITIVITY_CMD)) iwk_cmd_intr(sc, desc); switch (desc->hdr.type) { @@ -2065,7 +2167,8 @@ iwk_rx_softintr(caddr_t arg) * button is pushed again(ON) */ cmn_err(CE_NOTE, - "iwk: Radio transmitter is off\n"); + "iwk_rx_softintr(): " + "Radio transmitter is off\n"); sc->sc_ostate = sc->sc_ic.ic_state; ieee80211_new_state(&sc->sc_ic, IEEE80211_S_INIT, -1); @@ -2088,8 +2191,16 @@ iwk_rx_softintr(caddr_t arg) } case SCAN_COMPLETE_NOTIFICATION: IWK_DBG((IWK_DEBUG_SCAN, "scan finished\n")); + sc->sc_flags &= ~IWK_F_SCANNING; ieee80211_end_scan(ic); break; + case STATISTICS_NOTIFICATION: + { + /* handle statistics notification */ + iwk_statistics_notify(sc, desc); + break; + } + } sc->sc_rxq.cur = (sc->sc_rxq.cur + 1) % RX_QUEUE_SIZE; @@ -2112,7 +2223,8 @@ iwk_rx_softintr(caddr_t arg) } static uint_t -iwk_intr(caddr_t arg) +/* LINTED: argument unused in function: unused */ +iwk_intr(caddr_t arg, caddr_t unused) { iwk_sc_t *sc = (iwk_sc_t *)arg; uint32_t r, rfh; @@ -2140,14 +2252,18 @@ iwk_intr(caddr_t arg) IWK_WRITE(sc, CSR_INT, r); IWK_WRITE(sc, CSR_FH_INT_STATUS, rfh); - if (sc->sc_rx_softint_id == NULL) { + if (sc->sc_soft_hdl == NULL) { mutex_exit(&sc->sc_glock); return (DDI_INTR_CLAIMED); } - if (r & (BIT_INT_SWERROR | BIT_INT_ERR)) { IWK_DBG((IWK_DEBUG_FW, "fatal firmware error\n")); mutex_exit(&sc->sc_glock); +#ifdef DEBUG + /* dump event and error logs to dmesg */ + iwk_write_error_log(sc); + iwk_write_event_log(sc); +#endif /* DEBUG */ iwk_stop(sc); sc->sc_ostate = sc->sc_ic.ic_state; ieee80211_new_state(&sc->sc_ic, IEEE80211_S_INIT, -1); @@ -2162,7 +2278,7 @@ iwk_intr(caddr_t arg) if ((r & (BIT_INT_FH_RX | BIT_INT_SW_RX)) || (rfh & FH_INT_RX_MASK)) { sc->sc_rx_softint_pending = 1; - ddi_trigger_softintr(sc->sc_rx_softint_id); + (void) ddi_intr_trigger_softint(sc->sc_soft_hdl, NULL); } if (r & BIT_INT_ALIVE) { @@ -2415,7 +2531,8 @@ iwk_send(ieee80211com_t *ic, mblk_t *mp, uint8_t type) if (IEEE80211_IS_MULTICAST(wh->i_addr1)) { tx->sta_id = IWK_BROADCAST_ID; } else { - tx->sta_id = IWK_AP_ID; + if (ic->ic_opmode != IEEE80211_M_IBSS) + tx->sta_id = IWK_AP_ID; } if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == @@ -2484,8 +2601,8 @@ iwk_send(ieee80211com_t *ic, mblk_t *mp, uint8_t type) mutex_exit(&sc->sc_tx_lock); /* kick ring */ - sc->sc_shared->queues_byte_cnt_tbls[ring->qid].tfd_offset[ring->cur].val - = 8 + len; + sc->sc_shared->queues_byte_cnt_tbls[ring->qid]. + tfd_offset[ring->cur].val = 8 + len; if (ring->cur < IWK_MAX_WIN_SIZE) { sc->sc_shared->queues_byte_cnt_tbls[ring->qid]. tfd_offset[IWK_QUEUE_SIZE + ring->cur].val = 8 + len; @@ -2517,6 +2634,7 @@ iwk_m_ioctl(void* arg, queue_t *wq, mblk_t *mp) int err; err = ieee80211_ioctl(ic, wq, mp); + if (err == ENETRESET) { /* * This is special for the hidden AP connection. @@ -2791,6 +2909,7 @@ iwk_cmd(iwk_sc_t *sc, int code, const void *buf, int size, int async) iwk_tx_ring_t *ring = &sc->sc_txq[IWK_CMD_QUEUE_NUM]; iwk_tx_desc_t *desc; iwk_cmd_t *cmd; + clock_t clk; ASSERT(size <= sizeof (cmd->data)); ASSERT(mutex_owned(&sc->sc_glock)); @@ -2812,11 +2931,11 @@ iwk_cmd(iwk_sc_t *sc, int code, const void *buf, int size, int async) desc->pa[0].val1 = ((4 + size) << 4) & 0xfff0; /* kick cmd ring XXX */ - sc->sc_shared->queues_byte_cnt_tbls[ring->qid] - .tfd_offset[ring->cur].val = 8; + sc->sc_shared->queues_byte_cnt_tbls[ring->qid]. + tfd_offset[ring->cur].val = 8; if (ring->cur < IWK_MAX_WIN_SIZE) { - sc->sc_shared->queues_byte_cnt_tbls[ring->qid] - .tfd_offset[IWK_QUEUE_SIZE + ring->cur].val = 8; + sc->sc_shared->queues_byte_cnt_tbls[ring->qid]. + tfd_offset[IWK_QUEUE_SIZE + ring->cur].val = 8; } ring->cur = (ring->cur + 1) % ring->count; IWK_WRITE(sc, HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur); @@ -2824,12 +2943,11 @@ iwk_cmd(iwk_sc_t *sc, int code, const void *buf, int size, int async) if (async) return (IWK_SUCCESS); else { - clock_t clk; sc->sc_flags &= ~IWK_F_CMD_DONE; clk = ddi_get_lbolt() + drv_usectohz(2000000); while (!(sc->sc_flags & IWK_F_CMD_DONE)) { - if (cv_timedwait(&sc->sc_cmd_cv, &sc->sc_glock, clk) - < 0) + if (cv_timedwait(&sc->sc_cmd_cv, &sc->sc_glock, clk) < + 0) break; } if (sc->sc_flags & IWK_F_CMD_DONE) @@ -2857,7 +2975,6 @@ iwk_hw_set_before_auth(iwk_sc_t *sc) { ieee80211com_t *ic = &sc->sc_ic; ieee80211_node_t *in = ic->ic_bss; - iwk_tx_power_table_cmd_t txpower; iwk_add_sta_t node; iwk_link_quality_cmd_t link_quality; struct ieee80211_rateset rs; @@ -2910,29 +3027,14 @@ iwk_hw_set_before_auth(iwk_sc_t *sc) return (err); } - /* - * set Tx power for 2.4GHz channels - * (need further investigation. fix tx power at present) - */ - (void) memset(&txpower, 0, sizeof (txpower)); - txpower.band = 1; /* for 2.4G */ - txpower.channel = sc->sc_config.chan; - txpower.channel_normal_width = 0; - for (i = 0; i < POWER_TABLE_NUM_HT_OFDM_ENTRIES; i++) { - txpower.tx_power.ht_ofdm_power[i].s - .ramon_tx_gain = 0x3f3f; - txpower.tx_power.ht_ofdm_power[i].s - .dsp_predis_atten = 110 | (110 << 8); - } - txpower.tx_power.ht_ofdm_power[POWER_TABLE_NUM_HT_OFDM_ENTRIES]. - s.ramon_tx_gain = 0x3f3f; - txpower.tx_power.ht_ofdm_power[POWER_TABLE_NUM_HT_OFDM_ENTRIES]. - s.dsp_predis_atten = 110 | (110 << 8); - err = iwk_cmd(sc, REPLY_TX_PWR_TABLE_CMD, &txpower, - sizeof (txpower), 1); - if (err != IWK_SUCCESS) { + /* obtain current temperature of chipset */ + sc->sc_tempera = iwk_curr_tempera(sc); + + /* make Tx power calibration to determine the gains of DSP and radio */ + err = iwk_tx_power_calibration(sc); + if (err) { cmn_err(CE_WARN, "iwk_hw_set_before_auth():" - " failed to set txpower\n"); + "failed to set tx power table\n"); return (err); } @@ -2942,8 +3044,8 @@ iwk_hw_set_before_auth(iwk_sc_t *sc) node.id = IWK_AP_ID; err = iwk_cmd(sc, REPLY_ADD_STA, &node, sizeof (node), 1); if (err != IWK_SUCCESS) { - cmn_err(CE_WARN, "iwk_hw_set_before_auth():" - " failed to add BSS node\n"); + cmn_err(CE_WARN, "iwk_hw_set_before_auth(): " + "failed to add BSS node\n"); return (err); } @@ -2999,6 +3101,8 @@ iwk_scan(iwk_sc_t *sc) uint8_t *frm; int i, pktlen, nrates; + sc->sc_flags |= IWK_F_SCANNING; + data = &ring->data[ring->cur]; desc = data->desc; cmd = (iwk_cmd_t *)data->dma_data.mem_va; @@ -3117,11 +3221,11 @@ iwk_scan(iwk_sc_t *sc) * maybe for cmd, filling the byte cnt table is not necessary. * anyway, we fill it here. */ - sc->sc_shared->queues_byte_cnt_tbls[ring->qid] - .tfd_offset[ring->cur].val = 8; + sc->sc_shared->queues_byte_cnt_tbls[ring->qid]. + tfd_offset[ring->cur].val = 8; if (ring->cur < IWK_MAX_WIN_SIZE) { - sc->sc_shared->queues_byte_cnt_tbls[ring->qid] - .tfd_offset[IWK_QUEUE_SIZE + ring->cur].val = 8; + sc->sc_shared->queues_byte_cnt_tbls[ring->qid]. + tfd_offset[IWK_QUEUE_SIZE + ring->cur].val = 8; } /* kick cmd ring */ @@ -3135,7 +3239,6 @@ static int iwk_config(iwk_sc_t *sc) { ieee80211com_t *ic = &sc->sc_ic; - iwk_tx_power_table_cmd_t txpower; iwk_powertable_cmd_t powertable; iwk_bt_cmd_t bt; iwk_add_sta_t node; @@ -3174,8 +3277,8 @@ iwk_config(iwk_sc_t *sc) IEEE80211_ADDR_COPY(sc->sc_config.node_addr, ic->ic_macaddr); IEEE80211_ADDR_COPY(sc->sc_config.wlap_bssid, ic->ic_macaddr); sc->sc_config.chan = ieee80211_chan2ieee(ic, ic->ic_curchan); - sc->sc_config.flags = (RXON_FLG_TSF2HOST_MSK | RXON_FLG_AUTO_DETECT_MSK - | RXON_FLG_BAND_24G_MSK); + sc->sc_config.flags = (RXON_FLG_TSF2HOST_MSK | + RXON_FLG_AUTO_DETECT_MSK | RXON_FLG_BAND_24G_MSK); sc->sc_config.flags &= (~RXON_FLG_CCK_MSK); switch (ic->ic_opmode) { case IEEE80211_M_STA: @@ -3184,9 +3287,12 @@ iwk_config(iwk_sc_t *sc) RXON_FILTER_DIS_DECRYPT_MSK | RXON_FILTER_DIS_GRP_DECRYPT_MSK); break; - case IEEE80211_M_IBSS: case IEEE80211_M_AHDEMO: sc->sc_config.dev_type = RXON_DEV_TYPE_IBSS; + sc->sc_config.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; + sc->sc_config.filter_flags = LE_32(RXON_FILTER_ACCEPT_GRP_MSK | + RXON_FILTER_DIS_DECRYPT_MSK | + RXON_FILTER_DIS_GRP_DECRYPT_MSK); break; case IEEE80211_M_HOSTAP: sc->sc_config.dev_type = RXON_DEV_TYPE_AP; @@ -3217,29 +3323,14 @@ iwk_config(iwk_sc_t *sc) "failed to set configure command\n"); return (err); } + /* obtain current temperature of chipset */ + sc->sc_tempera = iwk_curr_tempera(sc); - /* - * set Tx power for 2.4GHz channels - * (need further investigation. fix tx power at present) - */ - (void) memset(&txpower, 0, sizeof (txpower)); - txpower.band = 1; /* for 2.4G */ - txpower.channel = sc->sc_config.chan; - txpower.channel_normal_width = 0; - for (i = 0; i < POWER_TABLE_NUM_HT_OFDM_ENTRIES; i++) { - txpower.tx_power.ht_ofdm_power[i] - .s.ramon_tx_gain = 0x3f3f; - txpower.tx_power.ht_ofdm_power[i] - .s.dsp_predis_atten = 110 | (110 << 8); - } - txpower.tx_power.ht_ofdm_power[POWER_TABLE_NUM_HT_OFDM_ENTRIES] - .s.ramon_tx_gain = 0x3f3f; - txpower.tx_power.ht_ofdm_power[POWER_TABLE_NUM_HT_OFDM_ENTRIES] - .s.dsp_predis_atten = 110 | (110 << 8); - err = iwk_cmd(sc, REPLY_TX_PWR_TABLE_CMD, &txpower, - sizeof (txpower), 0); - if (err != IWK_SUCCESS) { - cmn_err(CE_WARN, "iwk_config(): failed to set txpower\n"); + /* make Tx power calibration to determine the gains of DSP and radio */ + err = iwk_tx_power_calibration(sc); + if (err) { + cmn_err(CE_WARN, "iwk_config(): " + "failed to set tx power table\n"); return (err); } @@ -3378,7 +3469,8 @@ iwk_preinit(iwk_sc_t *sc) tmp = IWK_READ(sc, CSR_SW_VER); tmp |= CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI | - CSR_HW_IF_CONFIG_REG_BIT_MAC_SI | CSR_HW_IF_CONFIG_REG_BIT_KEDRON_R; + CSR_HW_IF_CONFIG_REG_BIT_MAC_SI | + CSR_HW_IF_CONFIG_REG_BIT_KEDRON_R; IWK_WRITE(sc, CSR_SW_VER, tmp); /* make sure power supply on each part of the hardware */ @@ -3587,7 +3679,7 @@ iwk_init(iwk_sc_t *sc) break; } if (n == 2) { - cmn_err(CE_WARN, "iwk_init(): " "failed to load firmware\n"); + cmn_err(CE_WARN, "iwk_init(): failed to load firmware\n"); goto fail1; } /* ..and wait at most one second for adapter to initialize */ @@ -3753,3 +3845,1610 @@ iwk_amrr_ratectl(void *arg, ieee80211_node_t *in) if (is_enough(amrr) || need_change) reset_cnt(amrr); } + +/* + * calculate 4965 chipset's kelvin temperature according to + * the data of init alive and satistics notification. + * The details is described in iwk_calibration.h file + */ +static int32_t iwk_curr_tempera(iwk_sc_t *sc) +{ + int32_t tempera; + int32_t r1, r2, r3; + uint32_t r4_u; + int32_t r4_s; + + if (iwk_is_fat_channel(sc)) { + r1 = (int32_t)(sc->sc_card_alive_init.therm_r1[1]); + r2 = (int32_t)(sc->sc_card_alive_init.therm_r2[1]); + r3 = (int32_t)(sc->sc_card_alive_init.therm_r3[1]); + r4_u = sc->sc_card_alive_init.therm_r4[1]; + } else { + r1 = (int32_t)(sc->sc_card_alive_init.therm_r1[0]); + r2 = (int32_t)(sc->sc_card_alive_init.therm_r2[0]); + r3 = (int32_t)(sc->sc_card_alive_init.therm_r3[0]); + r4_u = sc->sc_card_alive_init.therm_r4[0]; + } + + if (sc->sc_flags & IWK_F_STATISTICS) { + r4_s = (int32_t)(sc->sc_statistics.general.temperature << + (31-23)) >> (31-23); + } else { + r4_s = (int32_t)(r4_u << (31-23)) >> (31-23); + } + + IWK_DBG((IWK_DEBUG_CALIBRATION, "temperature R[1-4]: %d %d %d %d\n", + r1, r2, r3, r4_s)); + + if (r3 == r1) { + cmn_err(CE_WARN, "iwk_curr_tempera(): " + "failed to calculate temperature" + "because r3 = r1\n"); + return (DDI_FAILURE); + } + + tempera = TEMPERATURE_CALIB_A_VAL * (r4_s - r2); + tempera /= (r3 - r1); + tempera = (tempera*97) / 100 + TEMPERATURE_CALIB_KELVIN_OFFSET; + + IWK_DBG((IWK_DEBUG_CALIBRATION, "calculated temperature: %dK, %dC\n", + tempera, KELVIN_TO_CELSIUS(tempera))); + + return (tempera); +} + +/* Determine whether 4965 is using 2.4 GHz band */ +static inline int iwk_is_24G_band(iwk_sc_t *sc) +{ + return (sc->sc_config.flags & RXON_FLG_BAND_24G_MSK); +} + +/* Determine whether 4965 is using fat channel */ +static inline int iwk_is_fat_channel(iwk_sc_t *sc) +{ + return ((sc->sc_config.flags & RXON_FLG_CHANNEL_MODE_PURE_40_MSK) || + (sc->sc_config.flags & RXON_FLG_CHANNEL_MODE_MIXED_MSK)); +} + +/* + * In MIMO mode, determine which group 4965's current channel belong to. + * For more infomation about "channel group", + * please refer to iwk_calibration.h file + */ +static int iwk_txpower_grp(uint16_t channel) +{ + if (channel >= CALIB_IWK_TX_ATTEN_GR5_FCH && + channel <= CALIB_IWK_TX_ATTEN_GR5_LCH) { + return (CALIB_CH_GROUP_5); + } + + if (channel >= CALIB_IWK_TX_ATTEN_GR1_FCH && + channel <= CALIB_IWK_TX_ATTEN_GR1_LCH) { + return (CALIB_CH_GROUP_1); + } + + if (channel >= CALIB_IWK_TX_ATTEN_GR2_FCH && + channel <= CALIB_IWK_TX_ATTEN_GR2_LCH) { + return (CALIB_CH_GROUP_2); + } + + if (channel >= CALIB_IWK_TX_ATTEN_GR3_FCH && + channel <= CALIB_IWK_TX_ATTEN_GR3_LCH) { + return (CALIB_CH_GROUP_3); + } + + if (channel >= CALIB_IWK_TX_ATTEN_GR4_FCH && + channel <= CALIB_IWK_TX_ATTEN_GR4_LCH) { + return (CALIB_CH_GROUP_4); + } + + cmn_err(CE_WARN, "iwk_txpower_grp(): " + "can't find txpower group for channel %d.\n", channel); + + return (DDI_FAILURE); +} + +/* 2.4 GHz */ +static uint16_t iwk_eep_band_1[14] = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 +}; + +/* 5.2 GHz bands */ +static uint16_t iwk_eep_band_2[13] = { + 183, 184, 185, 187, 188, 189, 192, 196, 7, 8, 11, 12, 16 +}; + +static uint16_t iwk_eep_band_3[12] = { + 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64 +}; + +static uint16_t iwk_eep_band_4[11] = { + 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140 +}; + +static uint16_t iwk_eep_band_5[6] = { + 145, 149, 153, 157, 161, 165 +}; + +static uint16_t iwk_eep_band_6[7] = { + 1, 2, 3, 4, 5, 6, 7 +}; + +static uint16_t iwk_eep_band_7[11] = { + 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157 +}; + +/* Get regulatory data from eeprom for a given channel */ +static struct iwk_eep_channel *iwk_get_eep_channel(iwk_sc_t *sc, + uint16_t channel, + int is_24G, int is_fat, int is_hi_chan) +{ + int32_t i; + uint16_t chan; + + if (is_fat) { /* 11n mode */ + + if (is_hi_chan) { + chan = channel - 4; + } else { + chan = channel; + } + + for (i = 0; i < 7; i++) { + if (iwk_eep_band_6[i] == chan) { + return (&sc->sc_eep_map.band_24_channels[i]); + } + } + for (i = 0; i < 11; i++) { + if (iwk_eep_band_7[i] == chan) { + return (&sc->sc_eep_map.band_52_channels[i]); + } + } + } else if (is_24G) { /* 2.4 GHz band */ + for (i = 0; i < 14; i++) { + if (iwk_eep_band_1[i] == channel) { + return (&sc->sc_eep_map.band_1_channels[i]); + } + } + } else { /* 5 GHz band */ + for (i = 0; i < 13; i++) { + if (iwk_eep_band_2[i] == channel) { + return (&sc->sc_eep_map.band_2_channels[i]); + } + } + for (i = 0; i < 12; i++) { + if (iwk_eep_band_3[i] == channel) { + return (&sc->sc_eep_map.band_3_channels[i]); + } + } + for (i = 0; i < 11; i++) { + if (iwk_eep_band_4[i] == channel) { + return (&sc->sc_eep_map.band_4_channels[i]); + } + } + for (i = 0; i < 6; i++) { + if (iwk_eep_band_5[i] == channel) { + return (&sc->sc_eep_map.band_5_channels[i]); + } + } + } + + return (NULL); +} + +/* + * Determine which subband a given channel belongs + * to in 2.4 GHz or 5 GHz band + */ +static int32_t iwk_band_number(iwk_sc_t *sc, uint16_t channel) +{ + int32_t b_n = -1; + + for (b_n = 0; b_n < EEP_TX_POWER_BANDS; b_n++) { + if (0 == sc->sc_eep_map.calib_info.band_info_tbl[b_n].ch_from) { + continue; + } + + if ((channel >= + (uint16_t)sc->sc_eep_map.calib_info. + band_info_tbl[b_n].ch_from) && + (channel <= + (uint16_t)sc->sc_eep_map.calib_info. + band_info_tbl[b_n].ch_to)) { + break; + } + } + + return (b_n); +} + +/* Make a special division for interpolation operation */ +static int iwk_division(int32_t num, int32_t denom, int32_t *res) +{ + int32_t sign = 1; + + if (num < 0) { + sign = -sign; + num = -num; + } + + if (denom < 0) { + sign = -sign; + denom = -denom; + } + + *res = ((num*2 + denom) / (denom*2)) * sign; + + return (IWK_SUCCESS); +} + +/* Make interpolation operation */ +static int32_t iwk_interpolate_value(int32_t x, int32_t x1, int32_t y1, + int32_t x2, int32_t y2) +{ + int32_t val; + + if (x2 == x1) { + return (y1); + } else { + (void) iwk_division((x2-x)*(y1-y2), (x2-x1), &val); + return (val + y2); + } +} + +/* Get interpolation measurement data of a given channel for all chains. */ +static int iwk_channel_interpolate(iwk_sc_t *sc, uint16_t channel, + struct iwk_eep_calib_channel_info *chan_info) +{ + int32_t ban_n; + uint32_t ch1_n, ch2_n; + int32_t c, m; + struct iwk_eep_calib_measure *m1_p, *m2_p, *m_p; + + /* determine subband number */ + ban_n = iwk_band_number(sc, channel); + if (ban_n >= EEP_TX_POWER_BANDS) { + return (DDI_FAILURE); + } + + ch1_n = + (uint32_t)sc->sc_eep_map.calib_info.band_info_tbl[ban_n].ch1.ch_num; + ch2_n = + (uint32_t)sc->sc_eep_map.calib_info.band_info_tbl[ban_n].ch2.ch_num; + + chan_info->ch_num = (uint8_t)channel; /* given channel number */ + + /* + * go through all chains on chipset + */ + for (c = 0; c < EEP_TX_POWER_TX_CHAINS; c++) { + /* + * go through all factory measurements + */ + for (m = 0; m < EEP_TX_POWER_MEASUREMENTS; m++) { + m1_p = + &(sc->sc_eep_map.calib_info. + band_info_tbl[ban_n].ch1.measure[c][m]); + m2_p = + &(sc->sc_eep_map.calib_info.band_info_tbl[ban_n]. + ch2.measure[c][m]); + m_p = &(chan_info->measure[c][m]); + + /* + * make interpolation to get actual + * Tx power for given channel + */ + m_p->actual_pow = iwk_interpolate_value(channel, + ch1_n, m1_p->actual_pow, + ch2_n, m2_p->actual_pow); + + /* make interpolation to get index into gain table */ + m_p->gain_idx = iwk_interpolate_value(channel, + ch1_n, m1_p->gain_idx, + ch2_n, m2_p->gain_idx); + + /* make interpolation to get chipset temperature */ + m_p->temperature = iwk_interpolate_value(channel, + ch1_n, m1_p->temperature, + ch2_n, m2_p->temperature); + + /* + * make interpolation to get power + * amp detector level + */ + m_p->pa_det = iwk_interpolate_value(channel, ch1_n, + m1_p->pa_det, + ch2_n, m2_p->pa_det); + } + } + + return (IWK_SUCCESS); +} + +/* + * Calculate voltage compensation for Tx power. For more infomation, + * please refer to iwk_calibration.h file + */ +static int32_t iwk_voltage_compensation(int32_t eep_voltage, + int32_t curr_voltage) +{ + int32_t vol_comp = 0; + + if ((TX_POWER_IWK_ILLEGAL_VOLTAGE == eep_voltage) || + (TX_POWER_IWK_ILLEGAL_VOLTAGE == curr_voltage)) { + return (vol_comp); + } + + (void) iwk_division(curr_voltage-eep_voltage, + TX_POWER_IWK_VOLTAGE_CODES_PER_03V, &vol_comp); + + if (curr_voltage > eep_voltage) { + vol_comp *= 2; + } + if ((vol_comp < -2) || (vol_comp > 2)) { + vol_comp = 0; + } + + return (vol_comp); +} + +/* + * Thermal compensation values for txpower for various frequency ranges ... + * ratios from 3:1 to 4.5:1 of degrees (Celsius) per half-dB gain adjust + */ +static struct iwk_txpower_tempera_comp { + int32_t degrees_per_05db_a; + int32_t degrees_per_05db_a_denom; +} txpower_tempera_comp_table[CALIB_CH_GROUP_MAX] = { + {9, 2}, /* group 0 5.2, ch 34-43 */ + {4, 1}, /* group 1 5.2, ch 44-70 */ + {4, 1}, /* group 2 5.2, ch 71-124 */ + {4, 1}, /* group 3 5.2, ch 125-200 */ + {3, 1} /* group 4 2.4, ch all */ +}; + +/* + * bit-rate-dependent table to prevent Tx distortion, in half-dB units, + * for OFDM 6, 12, 18, 24, 36, 48, 54, 60 MBit, and CCK all rates. + */ +static int32_t back_off_table[] = { + 10, 10, 10, 10, 10, 15, 17, 20, /* OFDM SISO 20 MHz */ + 10, 10, 10, 10, 10, 15, 17, 20, /* OFDM MIMO 20 MHz */ + 10, 10, 10, 10, 10, 15, 17, 20, /* OFDM SISO 40 MHz */ + 10, 10, 10, 10, 10, 15, 17, 20, /* OFDM MIMO 40 MHz */ + 10 /* CCK */ +}; + +/* determine minimum Tx power index in gain table */ +static int32_t iwk_min_power_index(int32_t rate_pow_idx, int32_t is_24G) +{ + if ((!is_24G) && ((rate_pow_idx & 7) <= 4)) { + return (MIN_TX_GAIN_INDEX_52GHZ_EXT); + } + + return (MIN_TX_GAIN_INDEX); +} + +/* + * Determine DSP and radio gain according to temperature and other factors. + * This function is the majority of Tx power calibration + */ +static int iwk_txpower_table_cmd_init(iwk_sc_t *sc, + struct iwk_tx_power_db *tp_db) +{ + int is_24G, is_fat, is_high_chan, is_mimo; + int c, r; + int32_t target_power; + int32_t tx_grp = CALIB_CH_GROUP_MAX; + uint16_t channel; + uint8_t saturation_power; + int32_t regu_power; + int32_t curr_regu_power; + struct iwk_eep_channel *eep_chan_p; + struct iwk_eep_calib_channel_info eep_chan_calib; + int32_t eep_voltage, init_voltage; + int32_t voltage_compensation; + int32_t temperature; + int32_t degrees_per_05db_num; + int32_t degrees_per_05db_denom; + struct iwk_eep_calib_measure *measure_p; + int32_t interpo_temp; + int32_t power_limit; + int32_t atten_value; + int32_t tempera_comp[2]; + int32_t interpo_gain_idx[2]; + int32_t interpo_actual_pow[2]; + union iwk_tx_power_dual_stream txpower_gains; + int32_t txpower_gains_idx; + + channel = sc->sc_config.chan; + + /* 2.4 GHz or 5 GHz band */ + is_24G = iwk_is_24G_band(sc); + + /* fat channel or not */ + is_fat = iwk_is_fat_channel(sc); + + /* + * using low half channel number or high half channel number + * identify fat channel + */ + if (is_fat && (sc->sc_config.flags & + RXON_FLG_CONTROL_CHANNEL_LOC_HIGH_MSK)) { + is_high_chan = 1; + } + + if ((channel > 0) && (channel < 200)) { + /* get regulatory channel data from eeprom */ + eep_chan_p = iwk_get_eep_channel(sc, channel, is_24G, + is_fat, is_high_chan); + if (NULL == eep_chan_p) { + cmn_err(CE_WARN, + "iwk_txpower_table_cmd_init(): " + "can't get channel infomation\n"); + return (DDI_FAILURE); + } + } else { + cmn_err(CE_WARN, "iwk_txpower_table_cmd_init(): " + "channel(%d) isn't in proper range\n", + channel); + return (DDI_FAILURE); + } + + /* initial value of Tx power */ + sc->sc_user_txpower = (int32_t)eep_chan_p->max_power_avg; + if (sc->sc_user_txpower < IWK_TX_POWER_TARGET_POWER_MIN) { + cmn_err(CE_WARN, "iwk_txpower_table_cmd_init(): " + "user TX power is too weak\n"); + return (DDI_FAILURE); + } else if (sc->sc_user_txpower > IWK_TX_POWER_TARGET_POWER_MAX) { + cmn_err(CE_WARN, "iwk_txpower_table_cmd_init(): " + "user TX power is too strong\n"); + return (DDI_FAILURE); + } + + target_power = 2 * sc->sc_user_txpower; + + /* determine which group current channel belongs to */ + tx_grp = iwk_txpower_grp(channel); + if (tx_grp < 0) { + return (tx_grp); + } + + + if (is_fat) { + if (is_high_chan) { + channel -= 2; + } else { + channel += 2; + } + } + + /* determine saturation power */ + if (is_24G) { + saturation_power = + sc->sc_eep_map.calib_info.saturation_power24; + } else { + saturation_power = + sc->sc_eep_map.calib_info.saturation_power52; + } + + if (saturation_power < IWK_TX_POWER_SATURATION_MIN || + saturation_power > IWK_TX_POWER_SATURATION_MAX) { + if (is_24G) { + saturation_power = IWK_TX_POWER_DEFAULT_SATURATION_24; + } else { + saturation_power = IWK_TX_POWER_DEFAULT_SATURATION_52; + } + } + + /* determine regulatory power */ + regu_power = (int32_t)eep_chan_p->max_power_avg * 2; + if ((regu_power < IWK_TX_POWER_REGULATORY_MIN) || + (regu_power > IWK_TX_POWER_REGULATORY_MAX)) { + if (is_24G) { + regu_power = IWK_TX_POWER_DEFAULT_REGULATORY_24; + } else { + regu_power = IWK_TX_POWER_DEFAULT_REGULATORY_52; + } + } + + /* + * get measurement data for current channel + * suach as temperature,index to gain table,actual Tx power + */ + (void) iwk_channel_interpolate(sc, channel, &eep_chan_calib); + + eep_voltage = (int32_t)sc->sc_eep_map.calib_info.voltage; + init_voltage = (int32_t)sc->sc_card_alive_init.voltage; + + /* calculate voltage compensation to Tx power */ + voltage_compensation = + iwk_voltage_compensation(eep_voltage, init_voltage); + + if (sc->sc_tempera >= IWK_TX_POWER_TEMPERATURE_MIN) { + temperature = sc->sc_tempera; + } else { + temperature = IWK_TX_POWER_TEMPERATURE_MIN; + } + if (sc->sc_tempera <= IWK_TX_POWER_TEMPERATURE_MAX) { + temperature = sc->sc_tempera; + } else { + temperature = IWK_TX_POWER_TEMPERATURE_MAX; + } + temperature = KELVIN_TO_CELSIUS(temperature); + + degrees_per_05db_num = + txpower_tempera_comp_table[tx_grp].degrees_per_05db_a; + degrees_per_05db_denom = + txpower_tempera_comp_table[tx_grp].degrees_per_05db_a_denom; + + for (c = 0; c < 2; c++) { /* go through all chains */ + measure_p = &eep_chan_calib.measure[c][1]; + interpo_temp = measure_p->temperature; + + /* determine temperature compensation to Tx power */ + (void) iwk_division( + (temperature-interpo_temp)*degrees_per_05db_denom, + degrees_per_05db_num, &tempera_comp[c]); + + interpo_gain_idx[c] = measure_p->gain_idx; + interpo_actual_pow[c] = measure_p->actual_pow; + } + + /* + * go through all rate entries in Tx power table + */ + for (r = 0; r < POWER_TABLE_NUM_ENTRIES; r++) { + if (r & 0x8) { + /* need to lower regulatory power for MIMO mode */ + curr_regu_power = regu_power - + IWK_TX_POWER_MIMO_REGULATORY_COMPENSATION; + is_mimo = 1; + } else { + curr_regu_power = regu_power; + is_mimo = 0; + } + + power_limit = saturation_power - back_off_table[r]; + if (power_limit > curr_regu_power) { + /* final Tx power limit */ + power_limit = curr_regu_power; + } + + if (target_power > power_limit) { + target_power = power_limit; /* final target Tx power */ + } + + for (c = 0; c < 2; c++) { /* go through all Tx chains */ + if (is_mimo) { + atten_value = + sc->sc_card_alive_init.tx_atten[tx_grp][c]; + } else { + atten_value = 0; + } + + /* + * calculate index in gain table + * this step is very important + */ + txpower_gains_idx = interpo_gain_idx[c] - + (target_power - interpo_actual_pow[c]) - + tempera_comp[c] - voltage_compensation + + atten_value; + + if (txpower_gains_idx < + iwk_min_power_index(r, is_24G)) { + txpower_gains_idx = + iwk_min_power_index(r, is_24G); + } + + if (!is_24G) { + /* + * support negative index for 5 GHz + * band + */ + txpower_gains_idx += 9; + } + + if (POWER_TABLE_CCK_ENTRY == r) { + /* for CCK mode, make necessary attenuaton */ + txpower_gains_idx += + IWK_TX_POWER_CCK_COMPENSATION_C_STEP; + } + + if (txpower_gains_idx > 107) { + txpower_gains_idx = 107; + } else if (txpower_gains_idx < 0) { + txpower_gains_idx = 0; + } + + /* search DSP and radio gains in gain table */ + txpower_gains.s.radio_tx_gain[c] = + gains_table[is_24G][txpower_gains_idx].radio; + txpower_gains.s.dsp_predis_atten[c] = + gains_table[is_24G][txpower_gains_idx].dsp; + + IWK_DBG((IWK_DEBUG_CALIBRATION, + "rate_index: %d, " + "gain_index %d, c: %d,is_mimo: %d\n", + r, txpower_gains_idx, c, is_mimo)); + } + + /* initialize Tx power table */ + if (r < POWER_TABLE_NUM_HT_OFDM_ENTRIES) { + tp_db->ht_ofdm_power[r].dw = txpower_gains.dw; + } else { + tp_db->legacy_cck_power.dw = txpower_gains.dw; + } + } + + return (IWK_SUCCESS); +} + +/* + * make Tx power calibration to adjust Tx power. + * This is completed by sending out Tx power table command. + */ +static int iwk_tx_power_calibration(iwk_sc_t *sc) +{ + iwk_tx_power_table_cmd_t cmd; + int rv; + + if (sc->sc_flags & IWK_F_SCANNING) { + return (IWK_SUCCESS); + } + + /* necessary initialization to Tx power table command */ + cmd.band = (uint8_t)iwk_is_24G_band(sc); + cmd.channel = sc->sc_config.chan; + cmd.channel_normal_width = 0; + + /* initialize Tx power table */ + rv = iwk_txpower_table_cmd_init(sc, &cmd.tx_power); + if (rv) { + cmn_err(CE_NOTE, "rv= %d\n", rv); + return (rv); + } + + /* send out Tx power table command */ + rv = iwk_cmd(sc, REPLY_TX_PWR_TABLE_CMD, &cmd, sizeof (cmd), 1); + if (rv) { + return (rv); + } + + /* record current temperature */ + sc->sc_last_tempera = sc->sc_tempera; + + return (IWK_SUCCESS); +} + +/* This function is the handler of statistics notification from uCode */ +static void iwk_statistics_notify(iwk_sc_t *sc, iwk_rx_desc_t *desc) +{ + int is_diff; + struct iwk_notif_statistics *statistics_p = + (struct iwk_notif_statistics *)(desc + 1); + + mutex_enter(&sc->sc_glock); + + is_diff = (sc->sc_statistics.general.temperature != + statistics_p->general.temperature) || + ((sc->sc_statistics.flag & STATISTICS_REPLY_FLG_FAT_MODE_MSK) != + (statistics_p->flag & STATISTICS_REPLY_FLG_FAT_MODE_MSK)); + + /* update statistics data */ + (void) memcpy(&sc->sc_statistics, statistics_p, + sizeof (struct iwk_notif_statistics)); + + sc->sc_flags |= IWK_F_STATISTICS; + + if (!(sc->sc_flags & IWK_F_SCANNING)) { + /* make Receiver gain balance calibration */ + (void) iwk_rxgain_diff(sc); + + /* make Receiver sensitivity calibration */ + (void) iwk_rx_sens(sc); + } + + + if (!is_diff) { + mutex_exit(&sc->sc_glock); + return; + } + + /* calibration current temperature of 4965 chipset */ + sc->sc_tempera = iwk_curr_tempera(sc); + + /* distinct temperature change will trigger Tx power calibration */ + if (((sc->sc_tempera - sc->sc_last_tempera) >= 3) || + ((sc->sc_last_tempera - sc->sc_tempera) >= 3)) { + /* make Tx power calibration */ + (void) iwk_tx_power_calibration(sc); + } + + mutex_exit(&sc->sc_glock); +} + +/* Determine this station is in associated state or not */ +static int iwk_is_associated(iwk_sc_t *sc) +{ + return (sc->sc_config.filter_flags & RXON_FILTER_ASSOC_MSK); +} + +/* Make necessary preparation for Receiver gain balance calibration */ +static int iwk_rxgain_diff_init(iwk_sc_t *sc) +{ + int i, rv; + struct iwk_calibration_cmd cmd; + struct iwk_rx_gain_diff *gain_diff_p; + + gain_diff_p = &sc->sc_rxgain_diff; + + (void) memset(gain_diff_p, 0, sizeof (struct iwk_rx_gain_diff)); + (void) memset(&cmd, 0, sizeof (struct iwk_calibration_cmd)); + + for (i = 0; i < RX_CHAINS_NUM; i++) { + gain_diff_p->gain_diff_chain[i] = CHAIN_GAIN_DIFF_INIT_VAL; + } + + if (iwk_is_associated(sc)) { + cmd.opCode = PHY_CALIBRATE_DIFF_GAIN_CMD; + cmd.diff_gain_a = 0; + cmd.diff_gain_b = 0; + cmd.diff_gain_c = 0; + + /* assume the gains of every Rx chains is balanceable */ + rv = iwk_cmd(sc, REPLY_PHY_CALIBRATION_CMD, &cmd, + sizeof (cmd), 1); + if (rv) { + return (rv); + } + + gain_diff_p->state = IWK_GAIN_DIFF_ACCUMULATE; + } + + return (IWK_SUCCESS); +} + +/* + * make Receiver gain balance to balance Rx gain between Rx chains + * and determine which chain is disconnected + */ +static int iwk_rxgain_diff(iwk_sc_t *sc) +{ + int i, is_24G, rv; + int max_beacon_chain_n; + int min_noise_chain_n; + uint16_t channel_n; + int32_t beacon_diff; + int32_t noise_diff; + uint32_t noise_chain_a, noise_chain_b, noise_chain_c; + uint32_t beacon_chain_a, beacon_chain_b, beacon_chain_c; + struct iwk_calibration_cmd cmd; + uint32_t beacon_aver[RX_CHAINS_NUM] = {0xFFFFFFFF}; + uint32_t noise_aver[RX_CHAINS_NUM] = {0xFFFFFFFF}; + struct statistics_rx_non_phy *rx_general_p = + &sc->sc_statistics.rx.general; + struct iwk_rx_gain_diff *gain_diff_p = &sc->sc_rxgain_diff; + + if (INTERFERENCE_DATA_AVAILABLE != + rx_general_p->interference_data_flag) { + return (IWK_SUCCESS); + } + + if (IWK_GAIN_DIFF_ACCUMULATE != gain_diff_p->state) { + return (IWK_SUCCESS); + } + + is_24G = iwk_is_24G_band(sc); + channel_n = sc->sc_config.chan; /* channel number */ + + if ((channel_n != (sc->sc_statistics.flag >> 16)) || + ((STATISTICS_REPLY_FLG_BAND_24G_MSK == + (sc->sc_statistics.flag & STATISTICS_REPLY_FLG_BAND_24G_MSK)) && + !is_24G)) { + return (IWK_SUCCESS); + } + + /* Rx chain's noise strength from statistics notification */ + noise_chain_a = rx_general_p->beacon_silence_rssi_a & 0xFF; + noise_chain_b = rx_general_p->beacon_silence_rssi_b & 0xFF; + noise_chain_c = rx_general_p->beacon_silence_rssi_c & 0xFF; + + /* Rx chain's beacon strength from statistics notification */ + beacon_chain_a = rx_general_p->beacon_rssi_a & 0xFF; + beacon_chain_b = rx_general_p->beacon_rssi_b & 0xFF; + beacon_chain_c = rx_general_p->beacon_rssi_c & 0xFF; + + gain_diff_p->beacon_count++; + + /* accumulate chain's noise strength */ + gain_diff_p->noise_stren_a += noise_chain_a; + gain_diff_p->noise_stren_b += noise_chain_b; + gain_diff_p->noise_stren_c += noise_chain_c; + + /* accumulate chain's beacon strength */ + gain_diff_p->beacon_stren_a += beacon_chain_a; + gain_diff_p->beacon_stren_b += beacon_chain_b; + gain_diff_p->beacon_stren_c += beacon_chain_c; + + if (BEACON_NUM_20 == gain_diff_p->beacon_count) { + /* calculate average beacon strength */ + beacon_aver[0] = (gain_diff_p->beacon_stren_a) / BEACON_NUM_20; + beacon_aver[1] = (gain_diff_p->beacon_stren_b) / BEACON_NUM_20; + beacon_aver[2] = (gain_diff_p->beacon_stren_c) / BEACON_NUM_20; + + /* calculate average noise strength */ + noise_aver[0] = (gain_diff_p->noise_stren_a) / BEACON_NUM_20; + noise_aver[1] = (gain_diff_p->noise_stren_b) / BEACON_NUM_20; + noise_aver[2] = (gain_diff_p->noise_stren_b) / BEACON_NUM_20; + + /* determine maximum beacon strength among 3 chains */ + if ((beacon_aver[0] >= beacon_aver[1]) && + (beacon_aver[0] >= beacon_aver[2])) { + max_beacon_chain_n = 0; + gain_diff_p->connected_chains = 1 << 0; + } else if (beacon_aver[1] >= beacon_aver[2]) { + max_beacon_chain_n = 1; + gain_diff_p->connected_chains = 1 << 1; + } else { + max_beacon_chain_n = 2; + gain_diff_p->connected_chains = 1 << 2; + } + + /* determine which chain is disconnected */ + for (i = 0; i < RX_CHAINS_NUM; i++) { + if (i != max_beacon_chain_n) { + beacon_diff = beacon_aver[max_beacon_chain_n] - + beacon_aver[i]; + if (beacon_diff > MAX_ALLOWED_DIFF) { + gain_diff_p->disconnect_chain[i] = 1; + } else { + gain_diff_p->connected_chains |= + (1 << i); + } + } + } + + /* + * if chain A and B are both disconnected, + * assume the stronger in beacon strength is connected + */ + if (gain_diff_p->disconnect_chain[0] && + gain_diff_p->disconnect_chain[1]) { + if (beacon_aver[0] >= beacon_aver[1]) { + gain_diff_p->disconnect_chain[0] = 0; + gain_diff_p->connected_chains |= (1 << 0); + } else { + gain_diff_p->disconnect_chain[1] = 0; + gain_diff_p->connected_chains |= (1 << 1); + } + } + + /* determine minimum noise strength among 3 chains */ + if (!gain_diff_p->disconnect_chain[0]) { + min_noise_chain_n = 0; + + for (i = 0; i < RX_CHAINS_NUM; i++) { + if (!gain_diff_p->disconnect_chain[i] && + (noise_aver[i] <= + noise_aver[min_noise_chain_n])) { + min_noise_chain_n = i; + } + + } + } else { + min_noise_chain_n = 1; + + for (i = 0; i < RX_CHAINS_NUM; i++) { + if (!gain_diff_p->disconnect_chain[i] && + (noise_aver[i] <= + noise_aver[min_noise_chain_n])) { + min_noise_chain_n = i; + } + } + } + + gain_diff_p->gain_diff_chain[min_noise_chain_n] = 0; + + /* determine gain difference between chains */ + for (i = 0; i < RX_CHAINS_NUM; i++) { + if (!gain_diff_p->disconnect_chain[i] && + (CHAIN_GAIN_DIFF_INIT_VAL == + gain_diff_p->gain_diff_chain[i])) { + + noise_diff = noise_aver[i] - + noise_aver[min_noise_chain_n]; + gain_diff_p->gain_diff_chain[i] = + (uint8_t)((noise_diff * 10) / 15); + + if (gain_diff_p->gain_diff_chain[i] > 3) { + gain_diff_p->gain_diff_chain[i] = 3; + } + + gain_diff_p->gain_diff_chain[i] |= (1 << 2); + } else { + gain_diff_p->gain_diff_chain[i] = 0; + } + } + + if (!gain_diff_p->gain_diff_send) { + gain_diff_p->gain_diff_send = 1; + + (void) memset(&cmd, 0, sizeof (cmd)); + + cmd.opCode = PHY_CALIBRATE_DIFF_GAIN_CMD; + cmd.diff_gain_a = gain_diff_p->gain_diff_chain[0]; + cmd.diff_gain_b = gain_diff_p->gain_diff_chain[1]; + cmd.diff_gain_c = gain_diff_p->gain_diff_chain[2]; + + /* + * send out PHY calibration command to + * adjust every chain's Rx gain + */ + rv = iwk_cmd(sc, REPLY_PHY_CALIBRATION_CMD, + &cmd, sizeof (cmd), 1); + if (rv) { + return (rv); + } + + gain_diff_p->state = IWK_GAIN_DIFF_CALIBRATED; + } + + gain_diff_p->beacon_stren_a = 0; + gain_diff_p->beacon_stren_b = 0; + gain_diff_p->beacon_stren_c = 0; + + gain_diff_p->noise_stren_a = 0; + gain_diff_p->noise_stren_b = 0; + gain_diff_p->noise_stren_c = 0; + } + + return (IWK_SUCCESS); +} + +/* Make necessary preparation for Receiver sensitivity calibration */ +static int iwk_rx_sens_init(iwk_sc_t *sc) +{ + int i, rv; + struct iwk_rx_sensitivity_cmd cmd; + struct iwk_rx_sensitivity *rx_sens_p = &sc->sc_rx_sens; + + (void) memset(&cmd, 0, sizeof (struct iwk_rx_sensitivity_cmd)); + (void) memset(rx_sens_p, 0, sizeof (struct iwk_rx_sensitivity)); + + rx_sens_p->auto_corr_ofdm_x4 = 90; + rx_sens_p->auto_corr_mrc_ofdm_x4 = 170; + rx_sens_p->auto_corr_ofdm_x1 = 105; + rx_sens_p->auto_corr_mrc_ofdm_x1 = 220; + + rx_sens_p->auto_corr_cck_x4 = 125; + rx_sens_p->auto_corr_mrc_cck_x4 = 200; + rx_sens_p->min_energy_det_cck = 100; + + rx_sens_p->flags &= (~IWK_SENSITIVITY_CALIB_ALLOW_MSK); + rx_sens_p->flags &= (~IWK_SENSITIVITY_OFDM_UPDATE_MSK); + rx_sens_p->flags &= (~IWK_SENSITIVITY_CCK_UPDATE_MSK); + + rx_sens_p->last_bad_plcp_cnt_ofdm = 0; + rx_sens_p->last_false_alarm_cnt_ofdm = 0; + rx_sens_p->last_bad_plcp_cnt_cck = 0; + rx_sens_p->last_false_alarm_cnt_cck = 0; + + rx_sens_p->cck_curr_state = IWK_TOO_MANY_FALSE_ALARM; + rx_sens_p->cck_prev_state = IWK_TOO_MANY_FALSE_ALARM; + rx_sens_p->cck_no_false_alarm_num = 0; + rx_sens_p->cck_beacon_idx = 0; + + for (i = 0; i < 10; i++) { + rx_sens_p->cck_beacon_min[i] = 0; + } + + rx_sens_p->cck_noise_idx = 0; + rx_sens_p->cck_noise_ref = 0; + + for (i = 0; i < 20; i++) { + rx_sens_p->cck_noise_max[i] = 0; + } + + rx_sens_p->cck_noise_diff = 0; + rx_sens_p->cck_no_false_alarm_num = 0; + + cmd.control = IWK_SENSITIVITY_CONTROL_WORK_TABLE; + + cmd.table[AUTO_CORR32_X4_TH_ADD_MIN_IDX] = + rx_sens_p->auto_corr_ofdm_x4; + cmd.table[AUTO_CORR32_X4_TH_ADD_MIN_MRC_IDX] = + rx_sens_p->auto_corr_mrc_ofdm_x4; + cmd.table[AUTO_CORR32_X1_TH_ADD_MIN_IDX] = + rx_sens_p->auto_corr_ofdm_x1; + cmd.table[AUTO_CORR32_X1_TH_ADD_MIN_MRC_IDX] = + rx_sens_p->auto_corr_mrc_ofdm_x1; + + cmd.table[AUTO_CORR40_X4_TH_ADD_MIN_IDX] = + rx_sens_p->auto_corr_cck_x4; + cmd.table[AUTO_CORR40_X4_TH_ADD_MIN_MRC_IDX] = + rx_sens_p->auto_corr_mrc_cck_x4; + cmd.table[MIN_ENERGY_CCK_DET_IDX] = rx_sens_p->min_energy_det_cck; + + cmd.table[MIN_ENERGY_OFDM_DET_IDX] = 100; + cmd.table[BARKER_CORR_TH_ADD_MIN_IDX] = 190; + cmd.table[BARKER_CORR_TH_ADD_MIN_MRC_IDX] = 390; + cmd.table[PTAM_ENERGY_TH_IDX] = 62; + + /* at first, set up Rx to maximum sensitivity */ + rv = iwk_cmd(sc, SENSITIVITY_CMD, &cmd, sizeof (cmd), 1); + if (rv) { + cmn_err(CE_WARN, "iwk_rx_sens_init(): " + "in the process of initialization, " + "failed to send rx sensitivity command\n"); + return (rv); + } + + rx_sens_p->flags |= IWK_SENSITIVITY_CALIB_ALLOW_MSK; + + return (IWK_SUCCESS); +} + +/* + * make Receiver sensitivity calibration to adjust every chain's Rx sensitivity. + * for more infomation, please refer to iwk_calibration.h file + */ +static int iwk_rx_sens(iwk_sc_t *sc) +{ + int rv; + uint32_t actual_rx_time; + struct statistics_rx_non_phy *rx_general_p = + &sc->sc_statistics.rx.general; + struct iwk_rx_sensitivity *rx_sens_p = &sc->sc_rx_sens; + struct iwk_rx_sensitivity_cmd cmd; + + if (!(rx_sens_p->flags & IWK_SENSITIVITY_CALIB_ALLOW_MSK)) { + cmn_err(CE_WARN, "iwk_rx_sens(): " + "sensitivity initialization has not finished.\n"); + return (DDI_FAILURE); + } + + if (INTERFERENCE_DATA_AVAILABLE != + rx_general_p->interference_data_flag) { + cmn_err(CE_WARN, "iwk_rx_sens(): " + "can't make rx sensitivity calibration," + "because of invalid statistics\n"); + return (DDI_FAILURE); + } + + actual_rx_time = rx_general_p->channel_load; + if (!actual_rx_time) { + cmn_err(CE_WARN, "iwk_rx_sens(): " + "can't make rx sensitivity calibration," + "because has not enough rx time\n"); + return (DDI_FAILURE); + } + + /* make Rx sensitivity calibration for OFDM mode */ + rv = iwk_ofdm_sens(sc, actual_rx_time); + if (rv) { + return (rv); + } + + /* make Rx sensitivity calibration for CCK mode */ + rv = iwk_cck_sens(sc, actual_rx_time); + if (rv) { + return (rv); + } + + /* + * if the sum of false alarm had not changed, nothing will be done + */ + if ((!(rx_sens_p->flags & IWK_SENSITIVITY_OFDM_UPDATE_MSK)) && + (!(rx_sens_p->flags & IWK_SENSITIVITY_CCK_UPDATE_MSK))) { + return (IWK_SUCCESS); + } + + cmd.control = IWK_SENSITIVITY_CONTROL_WORK_TABLE; + + cmd.table[AUTO_CORR32_X4_TH_ADD_MIN_IDX] = + rx_sens_p->auto_corr_ofdm_x4; + cmd.table[AUTO_CORR32_X4_TH_ADD_MIN_MRC_IDX] = + rx_sens_p->auto_corr_mrc_ofdm_x4; + cmd.table[AUTO_CORR32_X1_TH_ADD_MIN_IDX] = + rx_sens_p->auto_corr_ofdm_x1; + cmd.table[AUTO_CORR32_X1_TH_ADD_MIN_MRC_IDX] = + rx_sens_p->auto_corr_mrc_ofdm_x1; + + cmd.table[AUTO_CORR40_X4_TH_ADD_MIN_IDX] = + rx_sens_p->auto_corr_cck_x4; + cmd.table[AUTO_CORR40_X4_TH_ADD_MIN_MRC_IDX] = + rx_sens_p->auto_corr_mrc_cck_x4; + cmd.table[MIN_ENERGY_CCK_DET_IDX] = + rx_sens_p->min_energy_det_cck; + + cmd.table[MIN_ENERGY_OFDM_DET_IDX] = 100; + cmd.table[BARKER_CORR_TH_ADD_MIN_IDX] = 190; + cmd.table[BARKER_CORR_TH_ADD_MIN_MRC_IDX] = 390; + cmd.table[PTAM_ENERGY_TH_IDX] = 62; + + /* + * send sensitivity command to complete actual sensitivity calibration + */ + rv = iwk_cmd(sc, SENSITIVITY_CMD, &cmd, sizeof (cmd), 1); + if (rv) { + cmn_err(CE_WARN, "iwk_rx_sens(): " + "fail to send rx sensitivity command\n"); + return (rv); + } + + return (IWK_SUCCESS); + +} + +/* + * make Rx sensitivity calibration for CCK mode. + * This is preparing parameters for Sensitivity command + */ +static int iwk_cck_sens(iwk_sc_t *sc, uint32_t actual_rx_time) +{ + int i; + uint8_t noise_a, noise_b, noise_c; + uint8_t max_noise_abc, max_noise_20; + uint32_t beacon_a, beacon_b, beacon_c; + uint32_t min_beacon_abc, max_beacon_10; + uint32_t cck_fa, cck_bp; + uint32_t cck_sum_fa_bp; + uint32_t temp; + struct statistics_rx_non_phy *rx_general_p = + &sc->sc_statistics.rx.general; + struct iwk_rx_sensitivity *rx_sens_p = &sc->sc_rx_sens; + + cck_fa = sc->sc_statistics.rx.cck.false_alarm_cnt; + cck_bp = sc->sc_statistics.rx.cck.plcp_err; + + /* accumulate false alarm */ + if (rx_sens_p->last_false_alarm_cnt_cck > cck_fa) { + temp = rx_sens_p->last_false_alarm_cnt_cck; + rx_sens_p->last_false_alarm_cnt_cck = cck_fa; + cck_fa += (0xFFFFFFFF - temp); + } else { + cck_fa -= rx_sens_p->last_false_alarm_cnt_cck; + rx_sens_p->last_false_alarm_cnt_cck += cck_fa; + } + + /* accumulate bad plcp */ + if (rx_sens_p->last_bad_plcp_cnt_cck > cck_bp) { + temp = rx_sens_p->last_bad_plcp_cnt_cck; + rx_sens_p->last_bad_plcp_cnt_cck = cck_bp; + cck_bp += (0xFFFFFFFF - temp); + } else { + cck_bp -= rx_sens_p->last_bad_plcp_cnt_cck; + rx_sens_p->last_bad_plcp_cnt_cck += cck_bp; + } + + /* + * calculate relative value + */ + cck_sum_fa_bp = (cck_fa + cck_bp) * 200 * 1024; + rx_sens_p->cck_noise_diff = 0; + + noise_a = + (uint8_t)((rx_general_p->beacon_silence_rssi_a & 0xFF00) >> 8); + noise_b = + (uint8_t)((rx_general_p->beacon_silence_rssi_b & 0xFF00) >> 8); + noise_c = + (uint8_t)((rx_general_p->beacon_silence_rssi_c & 0xFF00) >> 8); + + beacon_a = rx_general_p->beacon_energy_a; + beacon_b = rx_general_p->beacon_energy_b; + beacon_c = rx_general_p->beacon_energy_c; + + /* determine maximum noise among 3 chains */ + if ((noise_a >= noise_b) && (noise_a >= noise_c)) { + max_noise_abc = noise_a; + } else if (noise_b >= noise_c) { + max_noise_abc = noise_b; + } else { + max_noise_abc = noise_c; + } + + /* record maximum noise among 3 chains */ + rx_sens_p->cck_noise_max[rx_sens_p->cck_noise_idx] = max_noise_abc; + rx_sens_p->cck_noise_idx++; + if (rx_sens_p->cck_noise_idx >= 20) { + rx_sens_p->cck_noise_idx = 0; + } + + /* determine maximum noise among 20 max noise */ + max_noise_20 = rx_sens_p->cck_noise_max[0]; + for (i = 0; i < 20; i++) { + if (rx_sens_p->cck_noise_max[i] >= max_noise_20) { + max_noise_20 = rx_sens_p->cck_noise_max[i]; + } + } + + /* determine minimum beacon among 3 chains */ + if ((beacon_a <= beacon_b) && (beacon_a <= beacon_c)) { + min_beacon_abc = beacon_a; + } else if (beacon_b <= beacon_c) { + min_beacon_abc = beacon_b; + } else { + min_beacon_abc = beacon_c; + } + + /* record miminum beacon among 3 chains */ + rx_sens_p->cck_beacon_min[rx_sens_p->cck_beacon_idx] = min_beacon_abc; + rx_sens_p->cck_beacon_idx++; + if (rx_sens_p->cck_beacon_idx >= 10) { + rx_sens_p->cck_beacon_idx = 0; + } + + /* determine maximum beacon among 10 miminum beacon among 3 chains */ + max_beacon_10 = rx_sens_p->cck_beacon_min[0]; + for (i = 0; i < 10; i++) { + if (rx_sens_p->cck_beacon_min[i] >= max_beacon_10) { + max_beacon_10 = rx_sens_p->cck_beacon_min[i]; + } + } + + /* add a little margin */ + max_beacon_10 += 6; + + /* record the count of having no false alarms */ + if (cck_sum_fa_bp < (5 * actual_rx_time)) { + rx_sens_p->cck_no_false_alarm_num++; + } else { + rx_sens_p->cck_no_false_alarm_num = 0; + } + + /* + * adjust parameters in sensitivity command + * according to different status. + * for more infomation, please refer to iwk_calibration.h file + */ + if (cck_sum_fa_bp > (50 * actual_rx_time)) { + rx_sens_p->cck_curr_state = IWK_TOO_MANY_FALSE_ALARM; + + if (rx_sens_p->auto_corr_cck_x4 > 160) { + rx_sens_p->cck_noise_ref = max_noise_20; + + if (rx_sens_p->min_energy_det_cck > 2) { + rx_sens_p->min_energy_det_cck -= 2; + } + } + + if (rx_sens_p->auto_corr_cck_x4 < 160) { + rx_sens_p->auto_corr_cck_x4 = 160 + 1; + } else { + if ((rx_sens_p->auto_corr_cck_x4 + 3) < 200) { + rx_sens_p->auto_corr_cck_x4 += 3; + } else { + rx_sens_p->auto_corr_cck_x4 = 200; + } + } + + if ((rx_sens_p->auto_corr_mrc_cck_x4 + 3) < 400) { + rx_sens_p->auto_corr_mrc_cck_x4 += 3; + } else { + rx_sens_p->auto_corr_mrc_cck_x4 = 400; + } + + rx_sens_p->flags |= IWK_SENSITIVITY_CCK_UPDATE_MSK; + + } else if (cck_sum_fa_bp < (5 * actual_rx_time)) { + rx_sens_p->cck_curr_state = IWK_TOO_FEW_FALSE_ALARM; + + rx_sens_p->cck_noise_diff = (int32_t)rx_sens_p->cck_noise_ref - + (int32_t)max_noise_20; + + if ((rx_sens_p->cck_prev_state != IWK_TOO_MANY_FALSE_ALARM) && + ((rx_sens_p->cck_noise_diff > 2) || + (rx_sens_p->cck_no_false_alarm_num > 100))) { + if ((rx_sens_p->min_energy_det_cck + 2) < 97) { + rx_sens_p->min_energy_det_cck += 2; + } else { + rx_sens_p->min_energy_det_cck = 97; + } + + if ((rx_sens_p->auto_corr_cck_x4 - 3) > 125) { + rx_sens_p->auto_corr_cck_x4 -= 3; + } else { + rx_sens_p->auto_corr_cck_x4 = 125; + } + + if ((rx_sens_p->auto_corr_mrc_cck_x4 -3) > 200) { + rx_sens_p->auto_corr_mrc_cck_x4 -= 3; + } else { + rx_sens_p->auto_corr_mrc_cck_x4 = 200; + } + + rx_sens_p->flags |= IWK_SENSITIVITY_CCK_UPDATE_MSK; + } else { + rx_sens_p->flags &= (~IWK_SENSITIVITY_CCK_UPDATE_MSK); + } + } else { + rx_sens_p->cck_curr_state = IWK_GOOD_RANGE_FALSE_ALARM; + + rx_sens_p->cck_noise_ref = max_noise_20; + + if (IWK_TOO_MANY_FALSE_ALARM == rx_sens_p->cck_prev_state) { + rx_sens_p->min_energy_det_cck -= 8; + } + + rx_sens_p->flags &= (~IWK_SENSITIVITY_CCK_UPDATE_MSK); + } + + if (rx_sens_p->min_energy_det_cck < max_beacon_10) { + rx_sens_p->min_energy_det_cck = (uint16_t)max_beacon_10; + } + + rx_sens_p->cck_prev_state = rx_sens_p->cck_curr_state; + + return (IWK_SUCCESS); +} + +/* + * make Rx sensitivity calibration for OFDM mode. + * This is preparing parameters for Sensitivity command + */ +static int iwk_ofdm_sens(iwk_sc_t *sc, uint32_t actual_rx_time) +{ + uint32_t temp; + uint16_t temp1; + uint32_t ofdm_fa, ofdm_bp; + uint32_t ofdm_sum_fa_bp; + struct iwk_rx_sensitivity *rx_sens_p = &sc->sc_rx_sens; + + ofdm_fa = sc->sc_statistics.rx.ofdm.false_alarm_cnt; + ofdm_bp = sc->sc_statistics.rx.ofdm.plcp_err; + + /* accumulate false alarm */ + if (rx_sens_p->last_false_alarm_cnt_ofdm > ofdm_fa) { + temp = rx_sens_p->last_false_alarm_cnt_ofdm; + rx_sens_p->last_false_alarm_cnt_ofdm = ofdm_fa; + ofdm_fa += (0xFFFFFFFF - temp); + } else { + ofdm_fa -= rx_sens_p->last_false_alarm_cnt_ofdm; + rx_sens_p->last_false_alarm_cnt_ofdm += ofdm_fa; + } + + /* accumulate bad plcp */ + if (rx_sens_p->last_bad_plcp_cnt_ofdm > ofdm_bp) { + temp = rx_sens_p->last_bad_plcp_cnt_ofdm; + rx_sens_p->last_bad_plcp_cnt_ofdm = ofdm_bp; + ofdm_bp += (0xFFFFFFFF - temp); + } else { + ofdm_bp -= rx_sens_p->last_bad_plcp_cnt_ofdm; + rx_sens_p->last_bad_plcp_cnt_ofdm += ofdm_bp; + } + + ofdm_sum_fa_bp = (ofdm_fa + ofdm_bp) * 200 * 1024; /* relative value */ + + /* + * adjust parameter in sensitivity command according to different status + */ + if (ofdm_sum_fa_bp > (50 * actual_rx_time)) { + temp1 = rx_sens_p->auto_corr_ofdm_x4 + 1; + rx_sens_p->auto_corr_ofdm_x4 = (temp1 <= 120) ? temp1 : 120; + + temp1 = rx_sens_p->auto_corr_mrc_ofdm_x4 + 1; + rx_sens_p->auto_corr_mrc_ofdm_x4 = + (temp1 <= 210) ? temp1 : 210; + + temp1 = rx_sens_p->auto_corr_ofdm_x1 + 1; + rx_sens_p->auto_corr_ofdm_x1 = (temp1 <= 140) ? temp1 : 140; + + temp1 = rx_sens_p->auto_corr_mrc_ofdm_x1 + 1; + rx_sens_p->auto_corr_mrc_ofdm_x1 = + (temp1 <= 270) ? temp1 : 270; + + rx_sens_p->flags |= IWK_SENSITIVITY_OFDM_UPDATE_MSK; + + } else if (ofdm_sum_fa_bp < (5 * actual_rx_time)) { + temp1 = rx_sens_p->auto_corr_ofdm_x4 - 1; + rx_sens_p->auto_corr_ofdm_x4 = (temp1 >= 85) ? temp1 : 85; + + temp1 = rx_sens_p->auto_corr_mrc_ofdm_x4 - 1; + rx_sens_p->auto_corr_mrc_ofdm_x4 = + (temp1 >= 170) ? temp1 : 170; + + temp1 = rx_sens_p->auto_corr_ofdm_x1 - 1; + rx_sens_p->auto_corr_ofdm_x1 = (temp1 >= 105) ? temp1 : 105; + + temp1 = rx_sens_p->auto_corr_mrc_ofdm_x1 - 1; + rx_sens_p->auto_corr_mrc_ofdm_x1 = + (temp1 >= 220) ? temp1 : 220; + + rx_sens_p->flags |= IWK_SENSITIVITY_OFDM_UPDATE_MSK; + + } else { + rx_sens_p->flags &= (~IWK_SENSITIVITY_OFDM_UPDATE_MSK); + } + + return (IWK_SUCCESS); +} + +/* + * 1) log_event_table_ptr indicates base of the event log. This traces + * a 256-entry history of uCode execution within a circular buffer. + * Its header format is: + * + * uint32_t log_size; log capacity (in number of entries) + * uint32_t type; (1) timestamp with each entry, (0) no timestamp + * uint32_t wraps; # times uCode has wrapped to top of circular buffer + * uint32_t write_index; next circular buffer entry that uCode would fill + * + * The header is followed by the circular buffer of log entries. Entries + * with timestamps have the following format: + * + * uint32_t event_id; range 0 - 1500 + * uint32_t timestamp; low 32 bits of TSF (of network, if associated) + * uint32_t data; event_id-specific data value + * + * Entries without timestamps contain only event_id and data. + */ + +/* + * iwk_write_event_log - Write event log to dmesg + */ +static void iwk_write_event_log(iwk_sc_t *sc) +{ + uint32_t log_event_table_ptr; /* Start address of event table */ + uint32_t startptr; /* Start address of log data */ + uint32_t logptr; /* address of log data entry */ + uint32_t i, n, num_events; + uint32_t event_id, data1, data2; /* log data */ + + uint32_t log_size; /* log capacity (in number of entries) */ + uint32_t type; /* (1)timestamp with each entry,(0) no timestamp */ + uint32_t wraps; /* # times uCode has wrapped to */ + /* the top of circular buffer */ + uint32_t idx; /* index of entry to be filled in next */ + + log_event_table_ptr = sc->sc_card_alive_run.log_event_table_ptr; + if (!(log_event_table_ptr)) { + IWK_DBG((IWK_DEBUG_EEPROM, "NULL event table pointer\n")); + return; + } + + iwk_mac_access_enter(sc); + + /* Read log header */ + log_size = iwk_mem_read(sc, log_event_table_ptr); + log_event_table_ptr += sizeof (uint32_t); /* addr of "type" */ + type = iwk_mem_read(sc, log_event_table_ptr); + log_event_table_ptr += sizeof (uint32_t); /* addr of "wraps" */ + wraps = iwk_mem_read(sc, log_event_table_ptr); + log_event_table_ptr += sizeof (uint32_t); /* addr of "idx" */ + idx = iwk_mem_read(sc, log_event_table_ptr); + startptr = log_event_table_ptr + + sizeof (uint32_t); /* addr of start of log data */ + if (!log_size & !wraps) { + IWK_DBG((IWK_DEBUG_EEPROM, "Empty log\n")); + iwk_mac_access_exit(sc); + return; + } + + if (!wraps) { + num_events = idx; + logptr = startptr; + } else { + num_events = log_size - idx; + n = type ? 2 : 3; + logptr = startptr + (idx * n * sizeof (uint32_t)); + } + + for (i = 0; i < num_events; i++) { + event_id = iwk_mem_read(sc, logptr); + logptr += sizeof (uint32_t); + data1 = iwk_mem_read(sc, logptr); + logptr += sizeof (uint32_t); + if (type == 0) { /* no timestamp */ + IWK_DBG((IWK_DEBUG_EEPROM, "Event ID=%d, Data=%x0x", + event_id, data1)); + } else { /* timestamp */ + data2 = iwk_mem_read(sc, logptr); + printf("Time=%d, Event ID=%d, Data=0x%x\n", + data1, event_id, data2); + IWK_DBG((IWK_DEBUG_EEPROM, + "Time=%d, Event ID=%d, Data=0x%x\n", + data1, event_id, data2)); + logptr += sizeof (uint32_t); + } + } + + /* + * Print the wrapped around entries, if any + */ + if (wraps) { + logptr = startptr; + for (i = 0; i < idx; i++) { + event_id = iwk_mem_read(sc, logptr); + logptr += sizeof (uint32_t); + data1 = iwk_mem_read(sc, logptr); + logptr += sizeof (uint32_t); + if (type == 0) { /* no timestamp */ + IWK_DBG((IWK_DEBUG_EEPROM, + "Event ID=%d, Data=%x0x", event_id, data1)); + } else { /* timestamp */ + data2 = iwk_mem_read(sc, logptr); + IWK_DBG((IWK_DEBUG_EEPROM, + "Time = %d, Event ID=%d, Data=0x%x\n", + data1, event_id, data2)); + logptr += sizeof (uint32_t); + } + } + } + + iwk_mac_access_exit(sc); +} + +/* + * error_event_table_ptr indicates base of the error log. This contains + * information about any uCode error that occurs. For 4965, the format is: + * + * uint32_t valid; (nonzero) valid, (0) log is empty + * uint32_t error_id; type of error + * uint32_t pc; program counter + * uint32_t blink1; branch link + * uint32_t blink2; branch link + * uint32_t ilink1; interrupt link + * uint32_t ilink2; interrupt link + * uint32_t data1; error-specific data + * uint32_t data2; error-specific data + * uint32_t line; source code line of error + * uint32_t bcon_time; beacon timer + * uint32_t tsf_low; network timestamp function timer + * uint32_t tsf_hi; network timestamp function timer + */ +/* + * iwk_write_error_log - Write error log to dmesg + */ +static void iwk_write_error_log(iwk_sc_t *sc) +{ + uint32_t err_ptr; /* Start address of error log */ + uint32_t valid; /* is error log valid */ + + err_ptr = sc->sc_card_alive_run.error_event_table_ptr; + if (!(err_ptr)) { + IWK_DBG((IWK_DEBUG_EEPROM, "NULL error table pointer\n")); + return; + } + + iwk_mac_access_enter(sc); + + valid = iwk_mem_read(sc, err_ptr); + if (!(valid)) { + IWK_DBG((IWK_DEBUG_EEPROM, "Error data not valid\n")); + iwk_mac_access_exit(sc); + return; + } + err_ptr += sizeof (uint32_t); + IWK_DBG((IWK_DEBUG_EEPROM, "err=%d ", iwk_mem_read(sc, err_ptr))); + err_ptr += sizeof (uint32_t); + IWK_DBG((IWK_DEBUG_EEPROM, "pc=0x%X ", iwk_mem_read(sc, err_ptr))); + err_ptr += sizeof (uint32_t); + IWK_DBG((IWK_DEBUG_EEPROM, + "branch link1=0x%X ", iwk_mem_read(sc, err_ptr))); + err_ptr += sizeof (uint32_t); + IWK_DBG((IWK_DEBUG_EEPROM, + "branch link2=0x%X ", iwk_mem_read(sc, err_ptr))); + err_ptr += sizeof (uint32_t); + IWK_DBG((IWK_DEBUG_EEPROM, + "interrupt link1=0x%X ", iwk_mem_read(sc, err_ptr))); + err_ptr += sizeof (uint32_t); + IWK_DBG((IWK_DEBUG_EEPROM, + "interrupt link2=0x%X ", iwk_mem_read(sc, err_ptr))); + err_ptr += sizeof (uint32_t); + IWK_DBG((IWK_DEBUG_EEPROM, "data1=0x%X ", iwk_mem_read(sc, err_ptr))); + err_ptr += sizeof (uint32_t); + IWK_DBG((IWK_DEBUG_EEPROM, "data2=0x%X ", iwk_mem_read(sc, err_ptr))); + err_ptr += sizeof (uint32_t); + IWK_DBG((IWK_DEBUG_EEPROM, "line=%d ", iwk_mem_read(sc, err_ptr))); + err_ptr += sizeof (uint32_t); + IWK_DBG((IWK_DEBUG_EEPROM, "bcon_time=%d ", iwk_mem_read(sc, err_ptr))); + err_ptr += sizeof (uint32_t); + IWK_DBG((IWK_DEBUG_EEPROM, "tsf_low=%d ", iwk_mem_read(sc, err_ptr))); + err_ptr += sizeof (uint32_t); + IWK_DBG((IWK_DEBUG_EEPROM, "tsf_hi=%d\n", iwk_mem_read(sc, err_ptr))); + + iwk_mac_access_exit(sc); +} diff --git a/usr/src/uts/common/io/iwk/iwk2_var.h b/usr/src/uts/common/io/iwk/iwk2_var.h index 0980061902..2c0e109470 100644 --- a/usr/src/uts/common/io/iwk/iwk2_var.h +++ b/usr/src/uts/common/io/iwk/iwk2_var.h @@ -29,8 +29,6 @@ #ifndef _IWK_VAR_H #define _IWK_VAR_H -#pragma ident "%Z%%M% %I% %E% SMI" - #ifdef __cplusplus extern "C" { #endif @@ -97,6 +95,8 @@ typedef struct iwk_softc { dev_info_t *sc_dip; int (*sc_newstate)(struct ieee80211com *, enum ieee80211_state, int); + void (*sc_recv_mgmt)(ieee80211com_t *, mblk_t *, + ieee80211_node_t *, int, int, uint32_t); enum ieee80211_state sc_ostate; kmutex_t sc_glock; kmutex_t sc_mt_lock; @@ -141,7 +141,8 @@ typedef struct iwk_softc { caddr_t sc_cfg_base; ddi_acc_handle_t sc_handle; caddr_t sc_base; - ddi_iblock_cookie_t sc_iblk; + ddi_intr_handle_t *sc_intr_htable; + uint_t sc_intr_pri; iwk_rxon_cmd_t sc_config; struct iwk_eep sc_eep_map; /* eeprom map */ @@ -150,10 +151,17 @@ typedef struct iwk_softc { struct iwk_alive_resp sc_card_alive_run; struct iwk_init_alive_resp sc_card_alive_init; + int32_t sc_tempera; + int32_t sc_last_tempera; + int32_t sc_user_txpower; + struct iwk_notif_statistics sc_statistics; + struct iwk_rx_gain_diff sc_rxgain_diff; + struct iwk_rx_sensitivity sc_rx_sens; + uint32_t sc_tx_timer; uint8_t *sc_fw_bin; - ddi_softintr_t sc_rx_softint_id; + ddi_softint_handle_t sc_soft_hdl; uint32_t sc_rx_softint_pending; uint32_t sc_need_reschedule; @@ -176,6 +184,8 @@ typedef struct iwk_softc { #define IWK_F_SCANNING (1 << 6) #define IWK_F_SUSPEND (1 << 7) #define IWK_F_RADIO_OFF (1 << 8) +#define IWK_F_STATISTICS (1 << 9) +#define IWK_F_READY (1 << 10) #define IWK_SUCCESS 0 #define IWK_FAIL EIO diff --git a/usr/src/uts/common/io/iwk/iwk_calibration.h b/usr/src/uts/common/io/iwk/iwk_calibration.h new file mode 100644 index 0000000000..47c4161f07 --- /dev/null +++ b/usr/src/uts/common/io/iwk/iwk_calibration.h @@ -0,0 +1,1128 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Copyright (c) 2008, Intel Corporation + * All rights reserved. + */ + +/* + * Sun elects to have this file available under and governed by the BSD + * license (see below for full license text). However, the following + * notice accompanied the original version of this file: + */ + +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2005 - 2007 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU Geeral Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * James P. Ketrenos <ipw2100-admin@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2007 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef _IWK_CALIBRATION_H_ +#define _IWK_CALIBRATION_H_ + +/* + * Most Tx and Rx calibration is done by uCode during the initialization + * phase of uCode boot. Driver must calibrate only: + * + * 1) Tx power (depends on temperature) + * 2) Receiver gain balance (and detect disconnected antennas) + * 3) Receiver sensitivity (to optimize signal detection) + */ + +/* START TEMPERATURE */ + +/* + * 4965 temperature calculation. + * + * The driver must calculate the device temperature before calculating + * a txpower setting (amplifier gain is temperature dependent). The + * calculation uses 4 measurements, 3 of which (R1, R2, R3) are calibration + * values used for the life of the driver, and one of which (R4) is the + * real-time temperature indicator. + * + * uCode provides all 4 values to the driver via the "initialize alive" + * notification (see struct iwk_init_alive_resp). After the runtime uCode + * image loads, uCode updates the R4 value via statistics notifications + * (see STATISTICS_NOTIFICATION), which occur after each received beacon + * when associated, or can be requested via REPLY_STATISTICS_CMD. + * + * NOTE: uCode provides the R4 value as a 23-bit signed value. Driver + * must sign-extend to 32 bits before applying formula below. + * + * Formula: + * + * degrees Kelvin = ((97 * 259 * (R4 - R2) / (R3 - R1)) / 100) + 8 + * + * NOTE: The basic formula is 259 * (R4-R2) / (R3-R1). The 97/100 is + * an additional correction, which should be centered around 0 degrees + * Celsius (273 degrees Kelvin). The 8 (3 percent of 273) compensates for + * centering the 97/100 correction around 0 degrees K. + * + * Add 273 to Kelvin value to find degrees Celsius, for comparing current + * temperature with factory-measured temperatures when calculating txpower + * settings. + */ + +/* END TEMPERATURE */ + +/* START TXPOWER */ + +/* + * 4965 txpower calculations rely on information from three sources: + * + * 1) EEPROM + * 2) "initialize" alive notification + * 3) statistics notifications + * + * EEPROM data consists of: + * + * 1) Regulatory information (max txpower and channel usage flags) is provided + * separately for each channel that can possibly supported by 4965. + * 40 MHz wide (.11n fat) channels are listed separately from 20 MHz + * (legacy) channels. + * + * See struct iwk_eep_channel for format, and struct iwk_eep for + * locations in EEPROM. + * + * 2) Factory txpower calibration information is provided separately for + * sub-bands of contiguous channels. 2.4GHz has just one sub-band, + * but 5 GHz has several sub-bands. + * + * In addition, per-band (2.4 and 5 Ghz) saturation txpowers are provided. + * + * See struct iwk_eep_calib_info (and the tree of structures contained + * within it) for format, and struct iwk_eep for locations in EEPROM. + * + * "Initialization alive" notification (see struct iwk_init_alive_resp) + * consists of: + * + * 1) Temperature calculation parameters. + * + * 2) Power supply voltage measurement. + * + * 3) Tx gain compensation to balance 2 transmitters for MIMO use. + * + * Statistics notifications deliver: + * + * 1) Current values for temperature param R4. + */ + +/* + * To calculate a txpower setting for a given desired target txpower, channel, + * modulation bit rate, and transmitter chain (4965 has 2 transmitters to + * support MIMO and transmit diversity), driver must do the following: + * + * 1) Compare desired txpower vs. (EEPROM) regulatory limit for this channel. + * Do not exceed regulatory limit; reduce target txpower if necessary. + * + * If setting up txpowers for MIMO rates (rate indexes 8-15, 24-31), + * 2 transmitters will be used simultaneously; driver must reduce the + * regulatory limit by 3 dB (half-power) for each transmitter, so the + * combined total output of the 2 transmitters is within regulatory limits. + * + * + * 2) Compare target txpower vs. (EEPROM) saturation txpower *reduced by + * backoff for this bit rate*. Do not exceed (saturation - backoff[rate]); + * reduce target txpower if necessary. + * + * Backoff values below are in 1/2 dB units (equivalent to steps in + * txpower gain tables): + * + * OFDM 6 - 36 MBit: 10 steps (5 dB) + * OFDM 48 MBit: 15 steps (7.5 dB) + * OFDM 54 MBit: 17 steps (8.5 dB) + * OFDM 60 MBit: 20 steps (10 dB) + * CCK all rates: 10 steps (5 dB) + * + * Backoff values apply to saturation txpower on a per-transmitter basis; + * when using MIMO (2 transmitters), each transmitter uses the same + * saturation level provided in EEPROM, and the same backoff values; + * no reduction (such as with regulatory txpower limits) is required. + * + * Saturation and Backoff values apply equally to 20 Mhz (legacy) channel + * widths and 40 Mhz (.11n fat) channel widths; there is no separate + * factory measurement for fat channels. + * + * The result of this step is the final target txpower. The rest of + * the steps figure out the proper settings for the device. + * + * + * 3) Determine (EEPROM) calibration subband for the target channel, by + * comparing against first and last channels in each subband + * (see struct iwk_eep_calib_subband_info). + * + * + * 4) Linearly interpolate (EEPROM) factory calibration measurement sets, + * referencing the 2 factory-measured (sample) channels within the subband. + * + * Interpolation is based on difference between target channel's frequency + * and the sample channels' frequencies. Since channel numbers are based + * on frequency (5 MHz between each channel number), this is equivalent + * to interpolating based on channel number differences. + * + * Note that the sample channels may or may not be the channels at the + * edges of the subband. The target channel may be "outside" of the + * span of the sampled channels. + * + * Driver may choose the pair (for 2 Tx chains) of measurements (see + * struct iwk_eep_calib_channel_info) for which the actual measured + * txpower comes closest to the desired txpower. Usually, though, + * the middle set of measurements is closest to the regulatory limits, + * and is therefore a good choice for all txpower calculations. + * + * Driver should interpolate both members of the chosen measurement pair, + * i.e. for both Tx chains (radio transmitters), unless the driver knows + * that only one of the chains will be used (e.g. only one tx antenna + * connected, but this should be unusual). + * + * Driver should interpolate factory values for temperature, gain table + * index, and actual power. The power amplifier detector values are + * not used by the driver. + * + * If the target channel happens to be one of the sample channels, the + * results should agree with the sample channel's measurements! + * + * + * 5) Find difference between desired txpower and (interpolated) + * factory-measured txpower. Using (interpolated) factory gain table index + * as a starting point, adjust this index lower to increase txpower, + * or higher to decrease txpower, until the target txpower is reached. + * Each step in the gain table is 1/2 dB. + * + * For example, if factory measured txpower is 16 dBm, and target txpower + * is 13 dBm, add 6 steps to the factory gain index to reduce txpower + * by 3 dB. + * + * + * 6) Find difference between current device temperature and (interpolated) + * factory-measured temperature for sub-band. Factory values are in + * degrees Celsius. To calculate current temperature, see comments for + * "4965 temperature calculation". + * + * If current temperature is higher than factory temperature, driver must + * increase gain (lower gain table index), and vice versa. + * + * Temperature affects gain differently for different channels: + * + * 2.4 GHz all channels: 3.5 degrees per half-dB step + * 5 GHz channels 34-43: 4.5 degrees per half-dB step + * 5 GHz channels >= 44: 4.0 degrees per half-dB step + * + * NOTE: Temperature can increase rapidly when transmitting, especially + * with heavy traffic at high txpowers. Driver should update + * temperature calculations often under these conditions to + * maintain strong txpower in the face of rising temperature. + * + * + * 7) Find difference between current power supply voltage indicator + * (from "initialize alive") and factory-measured power supply voltage + * indicator (EEPROM). + * + * If the current voltage is higher (indicator is lower) than factory + * voltage, gain should be reduced (gain table index increased) by: + * + * (eeprom - current) / 7 + * + * If the current voltage is lower (indicator is higher) than factory + * voltage, gain should be increased (gain table index decreased) by: + * + * 2 * (current - eeprom) / 7 + * + * If number of index steps in either direction turns out to be > 2, + * something is wrong ... just use 0. + * + * NOTE: Voltage compensation is independent of band/channel. + * + * NOTE: "Initialize" uCode measures current voltage, which is assumed + * to be constant after this initial measurement. Voltage + * compensation for txpower (number of steps in gain table) + * may be calculated once and used until the next uCode bootload. + * + * + * 8) If setting up txpowers for MIMO rates (rate indexes 8-15, 24-31), + * adjust txpower for each transmitter chain, so txpower is balanced + * between the two chains. There are 5 pairs of tx_atten[group][chain] + * values in "initialize alive", one pair for each of 5 channel ranges: + * + * Group 0: 5 GHz channel 34-43 + * Group 1: 5 GHz channel 44-70 + * Group 2: 5 GHz channel 71-124 + * Group 3: 5 GHz channel 125-200 + * Group 4: 2.4 GHz all channels + * + * Add the tx_atten[group][chain] value to the index for the target chain. + * The values are signed, but are in pairs of 0 and a non-negative number, + * so as to reduce gain (if necessary) of the "hotter" channel. This + * avoids any need to double-check for regulatory compliance after + * this step. + * + * + * 9) If setting up for a CCK rate, lower the gain by adding a CCK compensation + * value to the index: + * + * Hardware rev B: 9 steps (4.5 dB) + * Hardware rev C: 5 steps (2.5 dB) + * + * Hardware rev for 4965 can be determined by reading CSR_HW_REV_WA_REG, + * bits [3:2], 1 = B, 2 = C. + * + * NOTE: This compensation is in addition to any saturation backoff that + * might have been applied in an earlier step. + * + * + * 10) Select the gain table, based on band (2.4 vs 5 GHz). + * + * Limit the adjusted index to stay within the table! + * + * + * 11) Read gain table entries for DSP and radio gain, place into appropriate + * location(s) in command. + */ + +/* Temperature calibration offset is 3% 0C in Kelvin */ +#define TEMPERATURE_CALIB_KELVIN_OFFSET 8 +#define TEMPERATURE_CALIB_A_VAL 259 + +#define KELVIN_TO_CELSIUS(x) ((x)-273) +#define CELSIUS_TO_KELVIN(x) ((x)+273) + +/* First and last channels of all groups */ +#define CALIB_IWK_TX_ATTEN_GR1_FCH 34 +#define CALIB_IWK_TX_ATTEN_GR1_LCH 43 +#define CALIB_IWK_TX_ATTEN_GR2_FCH 44 +#define CALIB_IWK_TX_ATTEN_GR2_LCH 70 +#define CALIB_IWK_TX_ATTEN_GR3_FCH 71 +#define CALIB_IWK_TX_ATTEN_GR3_LCH 124 +#define CALIB_IWK_TX_ATTEN_GR4_FCH 125 +#define CALIB_IWK_TX_ATTEN_GR4_LCH 200 +#define CALIB_IWK_TX_ATTEN_GR5_FCH 1 +#define CALIB_IWK_TX_ATTEN_GR5_LCH 20 + +/* Limit range of txpower output target to be between these values */ +#define IWK_TX_POWER_TARGET_POWER_MIN (0) /* 0 dBm = 1 milliwatt */ +#define IWK_TX_POWER_TARGET_POWER_MAX (16) /* 16 dBm */ + +#define TX_POWER_IWK_ILLEGAL_VOLTAGE (-10000) + +/* + * 4965 power supply voltage compensation + */ +#define TX_POWER_IWK_VOLTAGE_CODES_PER_03V (7) + +/* Limit range of calculated temperature to be between these Kelvin values */ +#define IWK_TX_POWER_TEMPERATURE_MIN (263) +#define IWK_TX_POWER_TEMPERATURE_MAX (410) + +union iwk_tx_power_dual_stream { + struct { + uint8_t radio_tx_gain[2]; + uint8_t dsp_predis_atten[2]; + } s; + uint32_t dw; +}; + +#define POWER_TABLE_NUM_ENTRIES (33) +#define POWER_TABLE_CCK_ENTRY (32) + +/* + * When MIMO is used (2 transmitters operating simultaneously), driver should + * limit each transmitter to deliver a max of 3 dB below the regulatory limit + * for the device. That is, half power for each transmitter, so total power + * is within regulatory limits. + * + * The value "6" represents number of steps in gain table to reduce power. + * Each step is 1/2 dB. + */ +#define IWK_TX_POWER_MIMO_REGULATORY_COMPENSATION (6) + +/* + * CCK gain compensation. + * + * When calculating txpowers for CCK, after making sure that the target power + * is within regulatory and saturation limits, driver must additionally + * back off gain by adding these values to the gain table index. + */ +#define IWK_TX_POWER_CCK_COMPENSATION_C_STEP (5) + +/* + * Gain tables. + * + * The following tables contain pair of values for setting txpower, i.e. + * gain settings for the output of the device's digital signal processor (DSP), + * and for the analog gain structure of the transmitter. + * + * Each entry in the gain tables represents a step of 1/2 dB. Note that these + * are *relative* steps, not indications of absolute output power. Output + * power varies with temperature, voltage, and channel frequency, and also + * requires consideration of average power (to satisfy regulatory constraints), + * and peak power (to avoid distortion of the output signal). + * + * Each entry contains two values: + * 1) DSP gain (or sometimes called DSP attenuation). This is a fine-grained + * linear value that multiplies the output of the digital signal processor, + * before being sent to the analog radio. + * 2) Radio gain. This sets the analog gain of the radio Tx path. + * It is a coarser setting, and behaves in a logarithmic (dB) fashion. + * + * EEPROM contains factory calibration data for txpower. This maps actual + * measured txpower levels to gain settings in the "well known" tables + * below ("well-known" means here that both factory calibration *and* the + * driver work with the same table). + * + * There are separate tables for 2.4 GHz and 5 GHz bands. The 5 GHz table + * has an extension (into negative indexes), in case the driver needs to + * boost power setting for high device temperatures (higher than would be + * present during factory calibration). A 5 Ghz EEPROM index of "40" + * corresponds to the 49th entry in the table used by the driver. + */ +#define MIN_TX_GAIN_INDEX (0) /* highest gain, lowest idx, 2.4 */ +#define MIN_TX_GAIN_INDEX_52GHZ_EXT (-9) /* highest gain, lowest idx, 5 */ + +struct gain_entry { + uint8_t dsp; + uint8_t radio; +}; + +static const struct gain_entry gains_table[2][108] = { + /* 5.2GHz power gain index table */ + { + {123, 0x3F}, /* highest txpower */ + {117, 0x3F}, + {110, 0x3F}, + {104, 0x3F}, + {98, 0x3F}, + {110, 0x3E}, + {104, 0x3E}, + {98, 0x3E}, + {110, 0x3D}, + {104, 0x3D}, + {98, 0x3D}, + {110, 0x3C}, + {104, 0x3C}, + {98, 0x3C}, + {110, 0x3B}, + {104, 0x3B}, + {98, 0x3B}, + {110, 0x3A}, + {104, 0x3A}, + {98, 0x3A}, + {110, 0x39}, + {104, 0x39}, + {98, 0x39}, + {110, 0x38}, + {104, 0x38}, + {98, 0x38}, + {110, 0x37}, + {104, 0x37}, + {98, 0x37}, + {110, 0x36}, + {104, 0x36}, + {98, 0x36}, + {110, 0x35}, + {104, 0x35}, + {98, 0x35}, + {110, 0x34}, + {104, 0x34}, + {98, 0x34}, + {110, 0x33}, + {104, 0x33}, + {98, 0x33}, + {110, 0x32}, + {104, 0x32}, + {98, 0x32}, + {110, 0x31}, + {104, 0x31}, + {98, 0x31}, + {110, 0x30}, + {104, 0x30}, + {98, 0x30}, + {110, 0x25}, + {104, 0x25}, + {98, 0x25}, + {110, 0x24}, + {104, 0x24}, + {98, 0x24}, + {110, 0x23}, + {104, 0x23}, + {98, 0x23}, + {110, 0x22}, + {104, 0x18}, + {98, 0x18}, + {110, 0x17}, + {104, 0x17}, + {98, 0x17}, + {110, 0x16}, + {104, 0x16}, + {98, 0x16}, + {110, 0x15}, + {104, 0x15}, + {98, 0x15}, + {110, 0x14}, + {104, 0x14}, + {98, 0x14}, + {110, 0x13}, + {104, 0x13}, + {98, 0x13}, + {110, 0x12}, + {104, 0x08}, + {98, 0x08}, + {110, 0x07}, + {104, 0x07}, + {98, 0x07}, + {110, 0x06}, + {104, 0x06}, + {98, 0x06}, + {110, 0x05}, + {104, 0x05}, + {98, 0x05}, + {110, 0x04}, + {104, 0x04}, + {98, 0x04}, + {110, 0x03}, + {104, 0x03}, + {98, 0x03}, + {110, 0x02}, + {104, 0x02}, + {98, 0x02}, + {110, 0x01}, + {104, 0x01}, + {98, 0x01}, + {110, 0x00}, + {104, 0x00}, + {98, 0x00}, + {93, 0x00}, + {88, 0x00}, + {83, 0x00}, + {78, 0x00}, + }, + /* 2.4GHz power gain index table */ + { + {110, 0x3f}, /* highest txpower */ + {104, 0x3f}, + {98, 0x3f}, + {110, 0x3e}, + {104, 0x3e}, + {98, 0x3e}, + {110, 0x3d}, + {104, 0x3d}, + {98, 0x3d}, + {110, 0x3c}, + {104, 0x3c}, + {98, 0x3c}, + {110, 0x3b}, + {104, 0x3b}, + {98, 0x3b}, + {110, 0x3a}, + {104, 0x3a}, + {98, 0x3a}, + {110, 0x39}, + {104, 0x39}, + {98, 0x39}, + {110, 0x38}, + {104, 0x38}, + {98, 0x38}, + {110, 0x37}, + {104, 0x37}, + {98, 0x37}, + {110, 0x36}, + {104, 0x36}, + {98, 0x36}, + {110, 0x35}, + {104, 0x35}, + {98, 0x35}, + {110, 0x34}, + {104, 0x34}, + {98, 0x34}, + {110, 0x33}, + {104, 0x33}, + {98, 0x33}, + {110, 0x32}, + {104, 0x32}, + {98, 0x32}, + {110, 0x31}, + {104, 0x31}, + {98, 0x31}, + {110, 0x30}, + {104, 0x30}, + {98, 0x30}, + {110, 0x6}, + {104, 0x6}, + {98, 0x6}, + {110, 0x5}, + {104, 0x5}, + {98, 0x5}, + {110, 0x4}, + {104, 0x4}, + {98, 0x4}, + {110, 0x3}, + {104, 0x3}, + {98, 0x3}, + {110, 0x2}, + {104, 0x2}, + {98, 0x2}, + {110, 0x1}, + {104, 0x1}, + {98, 0x1}, + {110, 0x0}, + {104, 0x0}, + {98, 0x0}, + {97, 0}, + {96, 0}, + {95, 0}, + {94, 0}, + {93, 0}, + {92, 0}, + {91, 0}, + {90, 0}, + {89, 0}, + {88, 0}, + {87, 0}, + {86, 0}, + {85, 0}, + {84, 0}, + {83, 0}, + {82, 0}, + {81, 0}, + {80, 0}, + {79, 0}, + {78, 0}, + {77, 0}, + {76, 0}, + {75, 0}, + {74, 0}, + {73, 0}, + {72, 0}, + {71, 0}, + {70, 0}, + {69, 0}, + {68, 0}, + {67, 0}, + {66, 0}, + {65, 0}, + {64, 0}, + {63, 0}, + {62, 0}, + {61, 0}, + {60, 0}, + {59, 0}, + } +}; + +/* END TXPOWER */ + +struct statistics_div { + uint32_t tx_on_a; + uint32_t tx_on_b; + uint32_t exec_time; + uint32_t probe_time; + uint32_t reserved1; + uint32_t reserved2; +}; + +struct statistics_dbg { + uint32_t burst_check; + uint32_t burst_count; + uint32_t reserved[4]; +}; + + +struct statistics_general { + uint32_t temperature; + uint32_t temperature_m; + struct statistics_dbg dbg; + uint32_t sleep_time; + uint32_t slots_out; + uint32_t slots_idle; + uint32_t ttl_timestamp; + struct statistics_div div; + uint32_t rx_enable_counter; + uint32_t reserved1; + uint32_t reserved2; + uint32_t reserved3; +}; + + +struct statistics_tx_non_phy_agg { + uint32_t ba_timeout; + uint32_t ba_reschedule_frames; + uint32_t scd_query_agg_frame_cnt; + uint32_t scd_query_no_agg; + uint32_t scd_query_agg; + uint32_t scd_query_mismatch; + uint32_t frame_not_ready; + uint32_t underrun; + uint32_t bt_prio_kill; + uint32_t rx_ba_rsp_cnt; + uint32_t reserved2; + uint32_t reserved3; +}; + + +struct statistics_tx { + uint32_t preamble_cnt; + uint32_t rx_detected_cnt; + uint32_t bt_prio_defer_cnt; + uint32_t bt_prio_kill_cnt; + uint32_t few_bytes_cnt; + uint32_t cts_timeout; + uint32_t ack_timeout; + uint32_t expected_ack_cnt; + uint32_t actual_ack_cnt; + uint32_t dump_msdu_cnt; + uint32_t burst_abort_next_frame_mismatch_cnt; + uint32_t burst_abort_missing_next_frame_cnt; + uint32_t cts_timeout_collision; + uint32_t ack_or_ba_timeout_collision; + struct statistics_tx_non_phy_agg agg; +}; + + +struct statistics_rx_ht_phy { + uint32_t plcp_err; + uint32_t overrun_err; + uint32_t early_overrun_err; + uint32_t crc32_good; + uint32_t crc32_err; + uint32_t mh_format_err; + uint32_t agg_crc32_good; + uint32_t agg_mpdu_cnt; + uint32_t agg_cnt; + uint32_t reserved2; +}; + +struct statistics_rx_non_phy { + uint32_t bogus_cts; /* CTS received when not expecting CTS */ + uint32_t bogus_ack; /* ACK received when not expecting ACK */ + uint32_t non_bssid_frames; /* number of frames with BSSID that */ + /* doesn't belong to the STA BSSID */ + uint32_t filtered_frames; /* count frames that were dumped in the */ + /* filtering process */ + uint32_t non_channel_beacons; /* beacons with our bss id but not on */ + /* our serving channel */ + uint32_t channel_beacons; /* beacons with our bss id and in our */ + /* serving channel */ + uint32_t num_missed_bcon; /* number of missed beacons */ + uint32_t adc_rx_saturation_time; /* count in 0.8us units the time */ + /* the ADC was in saturation */ + uint32_t ina_detection_search_time; /* total time (in 0.8us) */ + /* searched for INA */ + uint32_t beacon_silence_rssi_a; /* RSSI silence after beacon frame */ + uint32_t beacon_silence_rssi_b; /* RSSI silence after beacon frame */ + uint32_t beacon_silence_rssi_c; /* RSSI silence after beacon frame */ + uint32_t interference_data_flag; /* flag for interference data */ + /* availability. 1 when data is */ + /* available. */ + uint32_t channel_load; /* counts RX Enable time */ + uint32_t dsp_false_alarms; /* DSP false alarm (both OFDM */ + /* and CCK) counter */ + uint32_t beacon_rssi_a; + uint32_t beacon_rssi_b; + uint32_t beacon_rssi_c; + uint32_t beacon_energy_a; + uint32_t beacon_energy_b; + uint32_t beacon_energy_c; +}; + +struct statistics_rx_phy { + uint32_t ina_cnt; + uint32_t fina_cnt; + uint32_t plcp_err; + uint32_t crc32_err; + uint32_t overrun_err; + uint32_t early_overrun_err; + uint32_t crc32_good; + uint32_t false_alarm_cnt; + uint32_t fina_sync_err_cnt; + uint32_t sfd_timeout; + uint32_t fina_timeout; + uint32_t unresponded_rts; + uint32_t rxe_frame_limit_overrun; + uint32_t sent_ack_cnt; + uint32_t sent_cts_cnt; + uint32_t sent_ba_rsp_cnt; + uint32_t dsp_self_kill; + uint32_t mh_format_err; + uint32_t re_acq_main_rssi_sum; + uint32_t reserved3; +}; + +struct statistics_rx { + struct statistics_rx_phy ofdm; + struct statistics_rx_phy cck; + struct statistics_rx_non_phy general; + struct statistics_rx_ht_phy ofdm_ht; +}; + +struct iwk_notif_statistics { + uint32_t flag; + struct statistics_rx rx; + struct statistics_tx tx; + struct statistics_general general; +}; + +/* START Receiver gain balance */ + +/* + * REPLY_PHY_CALIBRATION_CMD = 0xb0 (command, has simple generic response) + * + * This command sets the relative gains of 4965's 3 radio receiver chains. + * + * After the first association, driver should accumulate signal and noise + * statistics from the STATISTICS_NOTIFICATIONs that follow the first 20 + * beacons from the associated network (don't collect statistics that come + * in from scanning, or any other non-network source). + * + * DISCONNECTED ANTENNA: + * + * Driver should determine which antennas are actually connected, by comparing + * average beacon signal levels for the 3 Rx chains. Accumulate (add) the + * following values over 20 beacons, one accumulator for each of the chains + * a/b/c, from struct statistics_rx_non_phy: + * + * beacon_rssi_[abc] & 0x0FF (unsigned, units in dB) + * + * Find the strongest signal from among a/b/c. Compare the other two to the + * strongest. If any signal is more than 15 dB (times 20, unless you + * divide the accumulated values by 20) below the strongest, the driver + * considers that antenna to be disconnected, and should not try to use that + * antenna/chain for Rx or Tx. If both A and B seem to be disconnected, + * driver should declare the stronger one as connected, and attempt to use it + * (A and B are the only 2 Tx chains!). + * + * + * RX BALANCE: + * + * Driver should balance the 3 receivers (but just the ones that are connected + * to antennas, see above) for gain, by comparing the average signal levels + * detected during the silence after each beacon (background noise). + * Accumulate (add) the following values over 20 beacons, one accumulator for + * each of the chains a/b/c, from struct statistics_rx_non_phy: + * + * beacon_silence_rssi_[abc] & 0x0FF (unsigned, units in dB) + * + * Find the weakest background noise level from among a/b/c. This Rx chain + * will be the reference, with 0 gain adjustment. Attenuate other channels by + * finding noise difference: + * + * (accum_noise[i] - accum_noise[reference]) / 30 + * + * The "30" adjusts the dB in the 20 accumulated samples to units of 1.5 dB. + * For use in diff_gain_[abc] fields of struct iwk_calibration_cmd, the + * driver should limit the difference results to a range of 0-3 (0-4.5 dB), + * and set bit 2 to indicate "reduce gain". The value for the reference + * (weakest) chain should be "0". + * + * diff_gain_[abc] bit fields: + * 2: (1) reduce gain, (0) increase gain + * 1-0: amount of gain, units of 1.5 dB + */ + +#define RX_CHAINS_NUM (3) +#define CHAIN_GAIN_DIFF_INIT_VAL (4) + +#define IWK_GAIN_DIFF_ALIVE (0) +#define IWK_GAIN_DIFF_ACCUMULATE (1) +#define IWK_GAIN_DIFF_CALIBRATED (2) + +#define INTERFERENCE_DATA_AVAILABLE (1) +#define BEACON_NUM_20 (20) +#define MAX_ALLOWED_DIFF (15) + +struct iwk_rx_gain_diff { + uint8_t state; + uint16_t beacon_count; + uint8_t gain_diff_send; + uint32_t beacon_stren_a; + uint32_t beacon_stren_b; + uint32_t beacon_stren_c; + uint32_t noise_stren_a; + uint32_t noise_stren_b; + uint32_t noise_stren_c; + uint8_t disconnect_chain[RX_CHAINS_NUM]; + uint8_t connected_chains; + uint8_t gain_diff_chain[RX_CHAINS_NUM]; +}; + +/* END Receiver gain balance */ + +/* START Receiver sensitivity */ + +/* + * SENSITIVITY_CMD = 0xa8 + * + * This command sets up the Rx signal detector for a sensitivity level that + * is high enough to lock onto all signals within the associated network, + * but low enough to ignore signals that are below a certain threshold, so as + * not to have too many "false alarms". False alarms are signals that the + * Rx DSP tries to lock onto, but then discards after determining that they + * are noise. + * + * The optimum number of false alarms is between 5 and 50 per 200 TUs + * (200 * 1024 uSecs, i.e. 204.8 milliseconds) of actual Rx time (i.e. + * time listening, not transmitting). Driver must adjust sensitivity so that + * the ratio of actual false alarms to actual Rx time falls within this range. + * + * While associated, uCode delivers STATISTICS_NOTIFICATIONs after each + * received beacon. These provide information to the driver to analyze the + * sensitivity. Don't analyze statistics that come in from scanning, or any + * other non-associated-network source. Pertinent statistics include: + * + * From "general" statistics (struct statistics_rx_non_phy): + * + * (beacon_energy_[abc] & 0x0FF00) >> 8 (unsigned, higher value is lower level) + * Measure of energy of desired signal. Used for establishing a level + * below which the device does not detect signals. + * + * (beacon_silence_rssi_[abc] & 0x0FF00) >> 8 (unsigned, units in dB) + * Measure of background noise in silent period after beacon. + * + * channel_load + * uSecs of actual Rx time during beacon period (varies according to + * how much time was spent transmitting). + * + * From "cck" and "ofdm" statistics (struct statistics_rx_phy), separately: + * + * false_alarm_cnt + * Signal locks abandoned early (before phy-level header). + * + * plcp_err + * Signal locks abandoned late (during phy-level header). + * + * NOTE: Both false_alarm_cnt and plcp_err increment monotonically from + * beacon to beacon, i.e. each value is an accumulation of all errors + * before and including the latest beacon. Values will wrap around to 0 + * after counting up to 2^32 - 1. Driver must differentiate vs. + * previous beacon's values to determine # false alarms in the current + * beacon period. + * + * Total number of false alarms = false_alarms + plcp_errs + * + * For OFDM, adjust the following table entries in struct iwk_rx_sensitivity_cmd + * (notice that the start points for OFDM are at or close to settings for + * maximum sensitivity): + * + * START / MIN / MAX + * HD_AUTO_CORR32_X1_TH_ADD_MIN_INDEX 90 / 85 / 120 + * HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_INDEX 170 / 170 / 210 + * HD_AUTO_CORR32_X4_TH_ADD_MIN_INDEX 105 / 105 / 140 + * HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_INDEX 220 / 220 / 270 + * + * If actual rate of OFDM false alarms (+ plcp_errors) is too high + * (greater than 50 for each 204.8 msecs listening), reduce sensitivity + * by *adding* 1 to all 4 of the table entries above, up to the max for + * each entry. Conversely, if false alarm rate is too low (less than 5 + * for each 204.8 msecs listening), *subtract* 1 from each entry to + * increase sensitivity. + * + * For CCK sensitivity, keep track of the following: + * + * 1). 20-beacon history of maximum background noise, indicated by + * (beacon_silence_rssi_[abc] & 0x0FF00), units in dB, across the + * 3 receivers. For any given beacon, the "silence reference" is + * the maximum of last 60 samples (20 beacons * 3 receivers). + * + * 2). 10-beacon history of strongest signal level, as indicated + * by (beacon_energy_[abc] & 0x0FF00) >> 8, across the 3 receivers, + * i.e. the strength of the signal through the best receiver at the + * moment. These measurements are "upside down", with lower values + * for stronger signals, so max energy will be *minimum* value. + * + * Then for any given beacon, the driver must determine the *weakest* + * of the strongest signals; this is the minimum level that needs to be + * successfully detected, when using the best receiver at the moment. + * "Max cck energy" is the maximum (higher value means lower energy!) + * of the last 10 minima. Once this is determined, driver must add + * a little margin by adding "6" to it. + * + * 3). Number of consecutive beacon periods with too few false alarms. + * Reset this to 0 at the first beacon period that falls within the + * "good" range (5 to 50 false alarms per 204.8 milliseconds rx). + * + * Then, adjust the following CCK table entries in struct iwk_rx_sensitivity_cmd + * (notice that the start points for CCK are at maximum sensitivity): + * + * START / MIN / MAX + * HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX 125 / 125 / 200 + * HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX 200 / 200 / 400 + * HD_MIN_ENERGY_CCK_DET_INDEX 100 / 0 / 100 + * + * If actual rate of CCK false alarms (+ plcp_errors) is too high + * (greater than 50 for each 204.8 msecs listening), method for reducing + * sensitivity is: + * + * 1) *Add* 3 to value in HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX, + * up to max 400. + * + * 2) If current value in HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX is < 160, + * sensitivity has been reduced a significant amount; bring it up to + * a moderate 161. Otherwise, *add* 3, up to max 200. + * + * 3) a) If current value in HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX is > 160, + * sensitivity has been reduced only a moderate or small amount; + * *subtract* 2 from value in HD_MIN_ENERGY_CCK_DET_INDEX, + * down to min 0. Otherwise (if gain has been significantly reduced), + * don't change the HD_MIN_ENERGY_CCK_DET_INDEX value. + * + * b) Save a snapshot of the "silence reference". + * + * If actual rate of CCK false alarms (+ plcp_errors) is too low + * (less than 5 for each 204.8 msecs listening), method for increasing + * sensitivity is used only if: + * + * 1a) Previous beacon did not have too many false alarms + * 1b) AND difference between previous "silence reference" and current + * "silence reference" (prev - current) is 2 or more, + * OR 2) 100 or more consecutive beacon periods have had rate of + * less than 5 false alarms per 204.8 milliseconds rx time. + * + * Method for increasing sensitivity: + * + * 1) *Subtract* 3 from value in HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX, + * down to min 125. + * + * 2) *Subtract* 3 from value in HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX, + * down to min 200. + * + * 3) *Add* 2 to value in HD_MIN_ENERGY_CCK_DET_INDEX, up to max 100. + * + * If actual rate of CCK false alarms (+ plcp_errors) is within good range + * (between 5 and 50 for each 204.8 msecs listening): + * + * 1) Save a snapshot of the silence reference. + * + * 2) If previous beacon had too many CCK false alarms (+ plcp_errors), + * give some extra margin to energy threshold by *subtracting* 8 + * from value in HD_MIN_ENERGY_CCK_DET_INDEX. + * + * For all cases (too few, too many, good range), make sure that the CCK + * detection threshold (energy) is below the energy level for robust + * detection over the past 10 beacon periods, the "Max cck energy". + * Lower values mean higher energy; this means making sure that the value + * in HD_MIN_ENERGY_CCK_DET_INDEX is at or *above* "Max cck energy". + * + * Driver should set the following entries to fixed values: + * + * HD_MIN_ENERGY_OFDM_DET_INDEX 100 + * HD_BARKER_CORR_TH_ADD_MIN_INDEX 190 + * HD_BARKER_CORR_TH_ADD_MIN_MRC_INDEX 390 + * HD_OFDM_ENERGY_TH_IN_INDEX 62 + */ + +#define IWK_SENSITIVITY_CALIB_ALLOW_MSK (1 << 0) +#define IWK_SENSITIVITY_OFDM_UPDATE_MSK (1 << 1) +#define IWK_SENSITIVITY_CCK_UPDATE_MSK (1 << 2) + +#define MIN_ENERGY_CCK_DET_IDX (0) +#define MIN_ENERGY_OFDM_DET_IDX (1) +#define AUTO_CORR32_X1_TH_ADD_MIN_IDX (2) +#define AUTO_CORR32_X1_TH_ADD_MIN_MRC_IDX (3) +#define AUTO_CORR40_X4_TH_ADD_MIN_MRC_IDX (4) +#define AUTO_CORR32_X4_TH_ADD_MIN_IDX (5) +#define AUTO_CORR32_X4_TH_ADD_MIN_MRC_IDX (6) +#define BARKER_CORR_TH_ADD_MIN_IDX (7) +#define BARKER_CORR_TH_ADD_MIN_MRC_IDX (8) +#define AUTO_CORR40_X4_TH_ADD_MIN_IDX (9) +#define PTAM_ENERGY_TH_IDX (10) + +#define IWK_GOOD_RANGE_FALSE_ALARM (0) +#define IWK_TOO_MANY_FALSE_ALARM (1) +#define IWK_TOO_FEW_FALSE_ALARM (2) + +#define IWK_SENSITIVITY_CONTROL_DEFAULT_TABLE (0) +#define IWK_SENSITIVITY_CONTROL_WORK_TABLE (1) + +struct iwk_rx_sensitivity_cmd { + uint16_t control; + uint16_t table[11]; +}; + +struct iwk_rx_sensitivity { + uint16_t auto_corr_ofdm_x4; + uint16_t auto_corr_mrc_ofdm_x4; + uint16_t auto_corr_ofdm_x1; + uint16_t auto_corr_mrc_ofdm_x1; + + uint16_t auto_corr_cck_x4; + uint16_t auto_corr_mrc_cck_x4; + uint16_t min_energy_det_cck; + + uint16_t flags; + + uint32_t last_bad_plcp_cnt_ofdm; + uint32_t last_false_alarm_cnt_ofdm; + uint32_t last_bad_plcp_cnt_cck; + uint32_t last_false_alarm_cnt_cck; + + uint32_t cck_curr_state; + uint32_t cck_prev_state; + uint32_t cck_beacon_min[10]; + uint32_t cck_beacon_idx; + uint8_t cck_noise_max[20]; + uint32_t cck_noise_ref; + uint32_t cck_noise_idx; + int32_t cck_noise_diff; + uint32_t cck_no_false_alarm_num; +}; + +/* END Receiver sensitivity */ + +#endif /* _IWK_CALIBRATION_H_ */ diff --git a/usr/src/uts/common/io/iwk/iwk_eeprom.h b/usr/src/uts/common/io/iwk/iwk_eeprom.h index 1b2905f0fe..0bf03d8a93 100644 --- a/usr/src/uts/common/io/iwk/iwk_eeprom.h +++ b/usr/src/uts/common/io/iwk/iwk_eeprom.h @@ -76,8 +76,6 @@ #ifndef _IWK_EEPROM_H_ #define _IWK_EEPROM_H_ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * This file defines EEPROM related constants, enums, and inline functions. */ @@ -187,9 +185,9 @@ struct iwl_eeprom_temperature_corr { }; #define EEP_TX_POWER_TX_CHAINS (2) -#define EEP_TX_POWER_BANDS (8) -#define EEP_TX_POWER_MEASURE (3) -#define EEP_TX_POWER_VERSION (2) +#define EEP_TX_POWER_BANDS (8) +#define EEP_TX_POWER_MEASUREMENTS (3) +#define EEP_TX_POWER_VERSION (2) #define EEP_TX_POWER_VERSION_NEW (5) struct iwk_eep_calib_measure { @@ -202,7 +200,7 @@ struct iwk_eep_calib_measure { struct iwk_eep_calib_channel_info { uint8_t ch_num; struct iwk_eep_calib_measure - measure[EEP_TX_POWER_TX_CHAINS][EEP_TX_POWER_MEASURE]; + measure[EEP_TX_POWER_TX_CHAINS][EEP_TX_POWER_MEASUREMENTS]; }; struct iwk_eep_calib_subband_info { diff --git a/usr/src/uts/common/io/iwk/iwk_hw.h b/usr/src/uts/common/io/iwk/iwk_hw.h index 1d4a53e1ab..a2edaddb9d 100644 --- a/usr/src/uts/common/io/iwk/iwk_hw.h +++ b/usr/src/uts/common/io/iwk/iwk_hw.h @@ -76,8 +76,6 @@ #ifndef _IWK_HW_H_ #define _IWK_HW_H_ -#pragma ident "%Z%%M% %I% %E% SMI" - #ifdef __cplusplus extern "C" { #endif @@ -2642,6 +2640,7 @@ enum { /* Multi-Station support */ REPLY_ADD_STA = 0x18, + REPLY_REMOVE_ALL_STA = 0x1a, /* RX, TX */ @@ -2877,9 +2876,9 @@ typedef struct iwk_calibration_cmd { uint8_t opCode; uint8_t flags; uint16_t reserved; - char *diff_gain_a; - char *diff_gain_b; - char *diff_gain_c; + char diff_gain_a; + char diff_gain_b; + char diff_gain_c; uint8_t reserved1; } iwk_calibation_cmd_t; @@ -3049,6 +3048,17 @@ typedef struct iwk_tx_cmd { } iwk_tx_cmd_t; /* + * structure for command "TX beacon" + */ +typedef struct iwk_tx_beacon_cmd { + iwk_tx_cmd_t config; + uint16_t tim_idx; + uint8_t tim_size; + uint8_t reserved; + uint8_t bcon_frame[2342]; +} iwk_tx_beacon_cmd_t; + +/* * LEDs Command & Response * REPLY_LEDS_CMD = 0x48 (command, has simple generic response) * |