summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/uts/common/io/iwk/iwk2.c2085
-rw-r--r--usr/src/uts/common/io/iwk/iwk2_var.h18
-rw-r--r--usr/src/uts/common/io/iwk/iwk_calibration.h1128
-rw-r--r--usr/src/uts/common/io/iwk/iwk_eeprom.h10
-rw-r--r--usr/src/uts/common/io/iwk/iwk_hw.h20
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)
*