Skip to content

Commit

Permalink
iwlwifi: support channel switch offload in driver
Browse files Browse the repository at this point in the history
Support channel switch in driver as a separated mac80211 callback
function instead of part of mac_config callback; by moving to this
approach, uCode can have more control of channel switch timing.

Signed-off-by: Wey-Yi Guy <wey-yi.w.guy@intel.com>
Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>
  • Loading branch information
Wey-Yi Guy authored and Reinette Chatre committed Jun 6, 2010
1 parent a0ee74c commit 79d0732
Showing 6 changed files with 257 additions and 51 deletions.
52 changes: 43 additions & 9 deletions drivers/net/wireless/iwlwifi/iwl-4965.c
Original file line number Diff line number Diff line change
@@ -1445,19 +1445,23 @@ static int iwl4965_send_rxon_assoc(struct iwl_priv *priv)
return ret;
}

static int iwl4965_hw_channel_switch(struct iwl_priv *priv, u16 channel)
static int iwl4965_hw_channel_switch(struct iwl_priv *priv,
struct ieee80211_channel_switch *ch_switch)
{
int rc;
u8 band = 0;
bool is_ht40 = false;
u8 ctrl_chan_high = 0;
struct iwl4965_channel_switch_cmd cmd;
const struct iwl_channel_info *ch_info;

u32 switch_time_in_usec, ucode_switch_time;
u16 ch;
u32 tsf_low;
u8 switch_count;
u16 beacon_interval = le16_to_cpu(priv->rxon_timing.beacon_interval);
struct ieee80211_vif *vif = priv->vif;
band = priv->band == IEEE80211_BAND_2GHZ;

ch_info = iwl_get_channel_info(priv, priv->band, channel);

is_ht40 = is_ht40_channel(priv->staging_rxon.flags);

if (is_ht40 &&
@@ -1466,26 +1470,56 @@ static int iwl4965_hw_channel_switch(struct iwl_priv *priv, u16 channel)

cmd.band = band;
cmd.expect_beacon = 0;
cmd.channel = cpu_to_le16(channel);
ch = ieee80211_frequency_to_channel(ch_switch->channel->center_freq);
cmd.channel = cpu_to_le16(ch);
cmd.rxon_flags = priv->staging_rxon.flags;
cmd.rxon_filter_flags = priv->staging_rxon.filter_flags;
cmd.switch_time = cpu_to_le32(priv->ucode_beacon_time);
switch_count = ch_switch->count;
tsf_low = ch_switch->timestamp & 0x0ffffffff;
/*
* calculate the ucode channel switch time
* adding TSF as one of the factor for when to switch
*/
if ((priv->ucode_beacon_time > tsf_low) && beacon_interval) {
if (switch_count > ((priv->ucode_beacon_time - tsf_low) /
beacon_interval)) {
switch_count -= (priv->ucode_beacon_time -
tsf_low) / beacon_interval;
} else
switch_count = 0;
}
if (switch_count <= 1)
cmd.switch_time = cpu_to_le32(priv->ucode_beacon_time);
else {
switch_time_in_usec =
vif->bss_conf.beacon_int * switch_count * TIME_UNIT;
ucode_switch_time = iwl_usecs_to_beacons(priv,
switch_time_in_usec,
beacon_interval);
cmd.switch_time = iwl_add_beacon_time(priv,
priv->ucode_beacon_time,
ucode_switch_time,
beacon_interval);
}
IWL_DEBUG_11H(priv, "uCode time for the switch is 0x%x\n",
cmd.switch_time);
ch_info = iwl_get_channel_info(priv, priv->band, ch);
if (ch_info)
cmd.expect_beacon = is_channel_radar(ch_info);
else {
IWL_ERR(priv, "invalid channel switch from %u to %u\n",
priv->active_rxon.channel, channel);
priv->active_rxon.channel, ch);
return -EFAULT;
}

rc = iwl4965_fill_txpower_tbl(priv, band, channel, is_ht40,
rc = iwl4965_fill_txpower_tbl(priv, band, ch, is_ht40,
ctrl_chan_high, &cmd.tx_power);
if (rc) {
IWL_DEBUG_11H(priv, "error:%d fill txpower_tbl\n", rc);
return rc;
}

priv->switch_rxon.channel = cpu_to_le16(channel);
priv->switch_rxon.channel = cmd.channel;
priv->switch_rxon.switch_in_progress = true;

return iwl_send_cmd_pdu(priv, REPLY_CHANNEL_SWITCH, sizeof(cmd), &cmd);
52 changes: 44 additions & 8 deletions drivers/net/wireless/iwlwifi/iwl-5000.c
Original file line number Diff line number Diff line change
@@ -271,33 +271,69 @@ static void iwl5150_temperature(struct iwl_priv *priv)
iwl_tt_handler(priv);
}

static int iwl5000_hw_channel_switch(struct iwl_priv *priv, u16 channel)
static int iwl5000_hw_channel_switch(struct iwl_priv *priv,
struct ieee80211_channel_switch *ch_switch)
{
struct iwl5000_channel_switch_cmd cmd;
const struct iwl_channel_info *ch_info;
u32 switch_time_in_usec, ucode_switch_time;
u16 ch;
u32 tsf_low;
u8 switch_count;
u16 beacon_interval = le16_to_cpu(priv->rxon_timing.beacon_interval);
struct ieee80211_vif *vif = priv->vif;
struct iwl_host_cmd hcmd = {
.id = REPLY_CHANNEL_SWITCH,
.len = sizeof(cmd),
.flags = CMD_SIZE_HUGE,
.data = &cmd,
};

IWL_DEBUG_11H(priv, "channel switch from %d to %d\n",
priv->active_rxon.channel, channel);
cmd.band = priv->band == IEEE80211_BAND_2GHZ;
cmd.channel = cpu_to_le16(channel);
ch = ieee80211_frequency_to_channel(ch_switch->channel->center_freq);
IWL_DEBUG_11H(priv, "channel switch from %d to %d\n",
priv->active_rxon.channel, ch);
cmd.channel = cpu_to_le16(ch);
cmd.rxon_flags = priv->staging_rxon.flags;
cmd.rxon_filter_flags = priv->staging_rxon.filter_flags;
cmd.switch_time = cpu_to_le32(priv->ucode_beacon_time);
ch_info = iwl_get_channel_info(priv, priv->band, channel);
switch_count = ch_switch->count;
tsf_low = ch_switch->timestamp & 0x0ffffffff;
/*
* calculate the ucode channel switch time
* adding TSF as one of the factor for when to switch
*/
if ((priv->ucode_beacon_time > tsf_low) && beacon_interval) {
if (switch_count > ((priv->ucode_beacon_time - tsf_low) /
beacon_interval)) {
switch_count -= (priv->ucode_beacon_time -
tsf_low) / beacon_interval;
} else
switch_count = 0;
}
if (switch_count <= 1)
cmd.switch_time = cpu_to_le32(priv->ucode_beacon_time);
else {
switch_time_in_usec =
vif->bss_conf.beacon_int * switch_count * TIME_UNIT;
ucode_switch_time = iwl_usecs_to_beacons(priv,
switch_time_in_usec,
beacon_interval);
cmd.switch_time = iwl_add_beacon_time(priv,
priv->ucode_beacon_time,
ucode_switch_time,
beacon_interval);
}
IWL_DEBUG_11H(priv, "uCode time for the switch is 0x%x\n",
cmd.switch_time);
ch_info = iwl_get_channel_info(priv, priv->band, ch);
if (ch_info)
cmd.expect_beacon = is_channel_radar(ch_info);
else {
IWL_ERR(priv, "invalid channel switch from %u to %u\n",
priv->active_rxon.channel, channel);
priv->active_rxon.channel, ch);
return -EFAULT;
}
priv->switch_rxon.channel = cpu_to_le16(channel);
priv->switch_rxon.channel = cmd.channel;
priv->switch_rxon.switch_in_progress = true;

return iwl_send_cmd_sync(priv, &hcmd);
53 changes: 44 additions & 9 deletions drivers/net/wireless/iwlwifi/iwl-6000.c
Original file line number Diff line number Diff line change
@@ -239,34 +239,69 @@ static int iwl6050_hw_set_hw_params(struct iwl_priv *priv)
return 0;
}

static int iwl6000_hw_channel_switch(struct iwl_priv *priv, u16 channel)
static int iwl6000_hw_channel_switch(struct iwl_priv *priv,
struct ieee80211_channel_switch *ch_switch)
{
struct iwl6000_channel_switch_cmd cmd;
const struct iwl_channel_info *ch_info;
u32 switch_time_in_usec, ucode_switch_time;
u16 ch;
u32 tsf_low;
u8 switch_count;
u16 beacon_interval = le16_to_cpu(priv->rxon_timing.beacon_interval);
struct ieee80211_vif *vif = priv->vif;
struct iwl_host_cmd hcmd = {
.id = REPLY_CHANNEL_SWITCH,
.len = sizeof(cmd),
.flags = CMD_SIZE_HUGE,
.data = &cmd,
};

IWL_DEBUG_11H(priv, "channel switch from %d to %d\n",
priv->active_rxon.channel, channel);

cmd.band = priv->band == IEEE80211_BAND_2GHZ;
cmd.channel = cpu_to_le16(channel);
ch = ieee80211_frequency_to_channel(ch_switch->channel->center_freq);
IWL_DEBUG_11H(priv, "channel switch from %u to %u\n",
priv->active_rxon.channel, ch);
cmd.channel = cpu_to_le16(ch);
cmd.rxon_flags = priv->staging_rxon.flags;
cmd.rxon_filter_flags = priv->staging_rxon.filter_flags;
cmd.switch_time = cpu_to_le32(priv->ucode_beacon_time);
ch_info = iwl_get_channel_info(priv, priv->band, channel);
switch_count = ch_switch->count;
tsf_low = ch_switch->timestamp & 0x0ffffffff;
/*
* calculate the ucode channel switch time
* adding TSF as one of the factor for when to switch
*/
if ((priv->ucode_beacon_time > tsf_low) && beacon_interval) {
if (switch_count > ((priv->ucode_beacon_time - tsf_low) /
beacon_interval)) {
switch_count -= (priv->ucode_beacon_time -
tsf_low) / beacon_interval;
} else
switch_count = 0;
}
if (switch_count <= 1)
cmd.switch_time = cpu_to_le32(priv->ucode_beacon_time);
else {
switch_time_in_usec =
vif->bss_conf.beacon_int * switch_count * TIME_UNIT;
ucode_switch_time = iwl_usecs_to_beacons(priv,
switch_time_in_usec,
beacon_interval);
cmd.switch_time = iwl_add_beacon_time(priv,
priv->ucode_beacon_time,
ucode_switch_time,
beacon_interval);
}
IWL_DEBUG_11H(priv, "uCode time for the switch is 0x%x\n",
cmd.switch_time);
ch_info = iwl_get_channel_info(priv, priv->band, ch);
if (ch_info)
cmd.expect_beacon = is_channel_radar(ch_info);
else {
IWL_ERR(priv, "invalid channel switch from %u to %u\n",
priv->active_rxon.channel, channel);
priv->active_rxon.channel, ch);
return -EFAULT;
}
priv->switch_rxon.channel = cpu_to_le16(channel);
priv->switch_rxon.channel = cmd.channel;
priv->switch_rxon.switch_in_progress = true;

return iwl_send_cmd_sync(priv, &hcmd);
95 changes: 94 additions & 1 deletion drivers/net/wireless/iwlwifi/iwl-agn.c
Original file line number Diff line number Diff line change
@@ -120,7 +120,7 @@ int iwl_commit_rxon(struct iwl_priv *priv)
(priv->switch_rxon.channel != priv->staging_rxon.channel)) {
IWL_DEBUG_11H(priv, "abort channel switch on %d\n",
le16_to_cpu(priv->switch_rxon.channel));
priv->switch_rxon.switch_in_progress = false;
iwl_chswitch_done(priv, false);
}

/* If we don't need to send a full RXON, we can use
@@ -3325,6 +3325,98 @@ static int iwlagn_mac_sta_add(struct ieee80211_hw *hw,
return 0;
}

static void iwl_mac_channel_switch(struct ieee80211_hw *hw,
struct ieee80211_channel_switch *ch_switch)
{
struct iwl_priv *priv = hw->priv;
const struct iwl_channel_info *ch_info;
struct ieee80211_conf *conf = &hw->conf;
struct iwl_ht_config *ht_conf = &priv->current_ht_config;
u16 ch;
unsigned long flags = 0;

IWL_DEBUG_MAC80211(priv, "enter\n");

if (iwl_is_rfkill(priv))
goto out_exit;

if (test_bit(STATUS_EXIT_PENDING, &priv->status) ||
test_bit(STATUS_SCANNING, &priv->status))
goto out_exit;

if (!iwl_is_associated(priv))
goto out_exit;

/* channel switch in progress */
if (priv->switch_rxon.switch_in_progress == true)
goto out_exit;

mutex_lock(&priv->mutex);
if (priv->cfg->ops->lib->set_channel_switch) {

ch = ieee80211_frequency_to_channel(
ch_switch->channel->center_freq);
if (le16_to_cpu(priv->active_rxon.channel) != ch) {
ch_info = iwl_get_channel_info(priv,
conf->channel->band,
ch);
if (!is_channel_valid(ch_info)) {
IWL_DEBUG_MAC80211(priv, "invalid channel\n");
goto out;
}
spin_lock_irqsave(&priv->lock, flags);

priv->current_ht_config.smps = conf->smps_mode;

/* Configure HT40 channels */
ht_conf->is_ht = conf_is_ht(conf);
if (ht_conf->is_ht) {
if (conf_is_ht40_minus(conf)) {
ht_conf->extension_chan_offset =
IEEE80211_HT_PARAM_CHA_SEC_BELOW;
ht_conf->is_40mhz = true;
} else if (conf_is_ht40_plus(conf)) {
ht_conf->extension_chan_offset =
IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
ht_conf->is_40mhz = true;
} else {
ht_conf->extension_chan_offset =
IEEE80211_HT_PARAM_CHA_SEC_NONE;
ht_conf->is_40mhz = false;
}
} else
ht_conf->is_40mhz = false;

/* if we are switching from ht to 2.4 clear flags
* from any ht related info since 2.4 does not
* support ht */
if ((le16_to_cpu(priv->staging_rxon.channel) != ch))
priv->staging_rxon.flags = 0;

iwl_set_rxon_channel(priv, conf->channel);
iwl_set_rxon_ht(priv, ht_conf);
iwl_set_flags_for_band(priv, conf->channel->band,
priv->vif);
spin_unlock_irqrestore(&priv->lock, flags);

iwl_set_rate(priv);
/*
* at this point, staging_rxon has the
* configuration for channel switch
*/
if (priv->cfg->ops->lib->set_channel_switch(priv,
ch_switch))
priv->switch_rxon.switch_in_progress = false;
}
}
out:
mutex_unlock(&priv->mutex);
out_exit:
if (!priv->switch_rxon.switch_in_progress)
ieee80211_chswitch_done(priv->vif, false);
IWL_DEBUG_MAC80211(priv, "leave\n");
}

/*****************************************************************************
*
* sysfs attributes
@@ -3646,6 +3738,7 @@ static struct ieee80211_ops iwl_hw_ops = {
.sta_notify = iwl_mac_sta_notify,
.sta_add = iwlagn_mac_sta_add,
.sta_remove = iwl_mac_sta_remove,
.channel_switch = iwl_mac_channel_switch,
};

static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
Loading

0 comments on commit 79d0732

Please sign in to comment.