#include "iwl-calib.h"
#include "iwl-sta.h"
+static int iwl4965_send_tx_power(struct iwl_priv *priv);
+
/* module parameters */
static struct iwl_mod_params iwl4965_mod_params = {
.num_of_queues = IWL49_NUM_QUEUES,
/* Regardless of if we are assocaited, we must reconfigure the
* TX power since frames can be sent on non-radar channels while
* not associated */
- iwl4965_hw_reg_send_txpower(priv);
+ iwl4965_send_tx_power(priv);
/* Update last_temperature to keep is_calib_needed from running
* when it isn't needed... */
cmd, NULL);
return ret;
}
-int iwl4965_hw_reg_set_txpower(struct iwl_priv *priv, s8 power)
-{
- IWL_ERROR("TODO: Implement iwl4965_hw_reg_set_txpower!\n");
- return -EINVAL;
-}
static s32 iwl4965_math_div_round(s32 num, s32 denom, s32 *res)
{
return comp;
}
-static const struct iwl_channel_info *
-iwl4965_get_channel_txpower_info(struct iwl_priv *priv,
- enum ieee80211_band band, u16 channel)
-{
- const struct iwl_channel_info *ch_info;
-
- ch_info = iwl_get_channel_info(priv, band, channel);
-
- if (!is_channel_valid(ch_info))
- return NULL;
-
- return ch_info;
-}
-
static s32 iwl4965_get_tx_atten_grp(u16 channel)
{
if (channel >= CALIB_IWL_TX_ATTEN_GR5_FCH &&
s32 factory_actual_pwr[2];
s32 power_index;
- /* Sanity check requested level (dBm) */
- if (priv->user_txpower_limit < IWL_TX_POWER_TARGET_POWER_MIN) {
- IWL_WARNING("Requested user TXPOWER %d below limit.\n",
- priv->user_txpower_limit);
- return -EINVAL;
- }
- if (priv->user_txpower_limit > IWL_TX_POWER_TARGET_POWER_MAX) {
- IWL_WARNING("Requested user TXPOWER %d above limit.\n",
- priv->user_txpower_limit);
- return -EINVAL;
- }
-
/* user_txpower_limit is in dBm, convert to half-dBm (half-dB units
* are used for indexing into txpower table) */
- user_target_power = 2 * priv->user_txpower_limit;
+ user_target_power = 2 * priv->tx_power_user_lmt;
/* Get current (RXON) channel, band, width */
- ch_info =
- iwl4965_get_channel_txpower_info(priv, priv->band, channel);
-
IWL_DEBUG_TXPOWER("chan %d band %d is_fat %d\n", channel, band,
is_fat);
- if (!ch_info)
+ ch_info = iwl_get_channel_info(priv, priv->band, channel);
+
+ if (!is_channel_valid(ch_info))
return -EINVAL;
/* get txatten group, used to select 1) thermal txpower adjustment
}
/**
- * iwl4965_hw_reg_send_txpower - Configure the TXPOWER level user limit
+ * iwl4965_send_tx_power - Configure the TXPOWER level user limit
*
* Uses the active RXON for channel, band, and characteristics (fat, high)
- * The power limit is taken from priv->user_txpower_limit.
+ * The power limit is taken from priv->tx_power_user_lmt.
*/
-int iwl4965_hw_reg_send_txpower(struct iwl_priv *priv)
+static int iwl4965_send_tx_power(struct iwl_priv *priv)
{
struct iwl4965_txpowertable_cmd cmd = { 0 };
int ret;
},
.radio_kill_sw = iwl4965_radio_kill_sw,
.set_power = iwl4965_set_power,
+ .send_tx_power = iwl4965_send_tx_power,
.update_chain_flags = iwl4965_update_chain_flags,
};
return ret;
}
+static int iwl5000_send_tx_power(struct iwl_priv *priv)
+{
+ struct iwl5000_tx_power_dbm_cmd tx_power_cmd;
+
+ /* half dBm need to multiply */
+ tx_power_cmd.global_lmt = (s8)(2 * priv->tx_power_user_lmt);
+ tx_power_cmd.flags = 0;
+ tx_power_cmd.srv_chan_lmt = IWL50_TX_POWER_AUTO;
+ return iwl_send_cmd_pdu_async(priv, REPLY_TX_POWER_DBM_CMD,
+ sizeof(tx_power_cmd), &tx_power_cmd,
+ NULL);
+}
+
static struct iwl_hcmd_ops iwl5000_hcmd = {
.rxon_assoc = iwl5000_send_rxon_assoc,
.load_ucode = iwl5000_load_ucode,
.init_alive_start = iwl5000_init_alive_start,
.alive_notify = iwl5000_alive_notify,
+ .send_tx_power = iwl5000_send_tx_power,
.apm_ops = {
.init = iwl5000_apm_init,
.reset = iwl5000_apm_reset,
/* Miscellaneous commands */
QUIET_NOTIFICATION = 0x96, /* not used */
REPLY_TX_PWR_TABLE_CMD = 0x97,
+ REPLY_TX_POWER_DBM_CMD = 0x98,
MEASURE_ABORT_NOTIFICATION = 0x99, /* not used */
/* Bluetooth device coexistance config command */
struct tx_power_dual_stream power_tbl[POWER_TABLE_NUM_ENTRIES];
} __attribute__ ((packed));
+/**
+ * Commad REPLY_TX_POWER_DBM_CMD = 0x98
+ * struct iwl5000_tx_power_dbm_cmd
+ */
+#define IWL50_TX_POWER_AUTO 0x7f
+struct iwl5000_tx_power_dbm_cmd {
+ s8 global_lmt; /*in half-dBm (e.g. 30 = 15 dBm) */
+ u8 flags;
+ s8 srv_chan_lmt; /*in half-dBm (e.g. 30 = 15 dBm) */
+ u8 reserved;
+} __attribute__ ((packed));
/******************************************************************************
* (0a)
geo_ch->flags |= ch->fat_extension_channel;
- if (ch->max_power_avg > priv->max_channel_txpower_limit)
- priv->max_channel_txpower_limit =
- ch->max_power_avg;
+ if (ch->max_power_avg > priv->tx_power_channel_lmt)
+ priv->tx_power_channel_lmt = ch->max_power_avg;
} else {
geo_ch->flags |= IEEE80211_CHAN_DISABLED;
}
priv->rates_mask = IWL_RATES_MASK;
/* If power management is turned on, default to AC mode */
priv->power_mode = IWL_POWER_AC;
- priv->user_txpower_limit = IWL_DEFAULT_TX_POWER;
+ priv->tx_power_user_lmt = IWL_TX_POWER_TARGET_POWER_MAX;
ret = iwl_init_channel_map(priv);
if (ret) {
}
EXPORT_SYMBOL(iwl_free_calib_results);
+int iwl_set_tx_power(struct iwl_priv *priv, s8 tx_power, bool force)
+{
+ int ret = 0;
+ if (tx_power < IWL_TX_POWER_TARGET_POWER_MIN) {
+ IWL_WARNING("Requested user TXPOWER %d below limit.\n",
+ priv->tx_power_user_lmt);
+ return -EINVAL;
+ }
+
+ if (tx_power > IWL_TX_POWER_TARGET_POWER_MAX) {
+ IWL_WARNING("Requested user TXPOWER %d above limit.\n",
+ priv->tx_power_user_lmt);
+ return -EINVAL;
+ }
+
+ if (priv->tx_power_user_lmt != tx_power)
+ force = true;
+
+ priv->tx_power_user_lmt = tx_power;
+
+ if (force && priv->cfg->ops->lib->send_tx_power)
+ ret = priv->cfg->ops->lib->send_tx_power(priv);
+
+ return ret;
+}
+EXPORT_SYMBOL(iwl_set_tx_power);
+
+
void iwl_uninit_drv(struct iwl_priv *priv)
{
iwl_free_calib_results(priv);
}
EXPORT_SYMBOL(iwl_uninit_drv);
+
+
/* Low level driver call this function to update iwlcore with
* driver status.
*/
} apm_ops;
/* power */
int (*set_power)(struct iwl_priv *priv, void *cmd);
+ int (*send_tx_power) (struct iwl_priv *priv);
void (*update_chain_flags)(struct iwl_priv *priv);
/* eeprom operations (as defined in iwl-eeprom.h) */
struct iwl_eeprom_ops eeprom_ops;
int iwl_tx_agg_stop(struct iwl_priv *priv , const u8 *ra, u16 tid);
int iwl_txq_check_empty(struct iwl_priv *priv, int sta_id, u8 tid, int txq_id);
+/*****************************************************
+ * TX power
+ ****************************************************/
+int iwl_set_tx_power(struct iwl_priv *priv, s8 tx_power, bool force);
+
/*****************************************************
* S e n d i n g H o s t C o m m a n d s *
*****************************************************/
struct ieee80211_tx_info *info,
struct ieee80211_hdr *hdr,
int sta_id, int tx_id);
-extern int iwl4965_hw_reg_send_txpower(struct iwl_priv *priv);
-extern int iwl4965_hw_reg_set_txpower(struct iwl_priv *priv, s8 power);
extern void iwl4965_hw_rx_statistics(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb);
extern void iwl4965_disable_events(struct iwl_priv *priv);
struct delayed_work alive_start;
struct delayed_work scan_check;
struct delayed_work post_associate;
-
-#define IWL_DEFAULT_TX_POWER 0x0F
- s8 user_txpower_limit;
- s8 max_channel_txpower_limit;
+ /* TX Power */
+ s8 tx_power_user_lmt;
+ s8 tx_power_channel_lmt;
#ifdef CONFIG_PM
u32 pm_state[16];
if (!is_channel_valid(ch_info))
return -1;
- IWL_DEBUG_INFO("FAT Ch. %d [%sGHz] %s%s%s%s%s(0x%02x"
- " %ddBm): Ad-Hoc %ssupported\n",
+ IWL_DEBUG_INFO("FAT Ch. %d [%sGHz] %s%s%s%s%s(0x%02x %ddBm):"
+ " Ad-Hoc %ssupported\n",
ch_info->channel,
is_channel_a_band(ch_info) ?
"5.2" : "2.4",
ch_info->scan_power = eeprom_ch_info[ch].max_power_avg;
ch_info->min_power = 0;
- IWL_DEBUG_INFO("Ch. %d [%sGHz] %s%s%s%s%s%s(0x%02x"
- " %ddBm): Ad-Hoc %ssupported\n",
+ IWL_DEBUG_INFO("Ch. %d [%sGHz] %s%s%s%s%s%s(0x%02x %ddBm):"
+ " Ad-Hoc %ssupported\n",
ch_info->channel,
is_channel_a_band(ch_info) ?
"5.2" : "2.4",
/* Set the user_txpower_limit to the highest power
* supported by any channel */
if (eeprom_ch_info[ch].max_power_avg >
- priv->user_txpower_limit)
- priv->user_txpower_limit =
+ priv->tx_power_user_lmt)
+ priv->tx_power_user_lmt =
eeprom_ch_info[ch].max_power_avg;
ch_info++;
/* If we issue a new RXON command which required a tune then we must
* send a new TXPOWER command or we won't be able to Tx any frames */
- rc = iwl4965_hw_reg_send_txpower(priv);
+ rc = iwl_set_tx_power(priv, priv->tx_power_user_lmt, true);
if (rc) {
- IWL_ERROR("Error setting Tx power (%d).\n", rc);
+ IWL_ERROR("Error sending TX power (%d).\n", rc);
return rc;
}
struct iwl_priv *priv =
container_of(work, struct iwl_priv, scan_completed);
- IWL_DEBUG(IWL_DL_SCAN, "SCAN complete scan\n");
+ IWL_DEBUG_SCAN("SCAN complete scan\n");
if (test_bit(STATUS_EXIT_PENDING, &priv->status))
return;
/* Since setting the TXPOWER may have been deferred while
* performing the scan, fire one off */
mutex_lock(&priv->mutex);
- iwl4965_hw_reg_send_txpower(priv);
+ iwl_set_tx_power(priv, priv->tx_power_user_lmt, true);
mutex_unlock(&priv->mutex);
}
goto out;
}
+ IWL_DEBUG_MAC80211("TX Power old=%d new=%d\n",
+ priv->tx_power_user_lmt, conf->power_level);
+
+ iwl_set_tx_power(priv, conf->power_level, false);
+
iwl4965_set_rate(priv);
if (memcmp(&priv->active_rxon,
struct device_attribute *attr, char *buf)
{
struct iwl_priv *priv = (struct iwl_priv *)d->driver_data;
- return sprintf(buf, "%d\n", priv->user_txpower_limit);
+ return sprintf(buf, "%d\n", priv->tx_power_user_lmt);
}
static ssize_t store_tx_power(struct device *d,
printk(KERN_INFO DRV_NAME
": %s is not in decimal form.\n", buf);
else
- iwl4965_hw_reg_set_txpower(priv, val);
+ iwl_set_tx_power(priv, val, false);
return count;
}