Commit a2b0f02e authored by Wey-Yi Guy's avatar Wey-Yi Guy Committed by John W. Linville
Browse files

iwlwifi: support "pure 40MHz" in RXON command



Fix the bug when using 11n "pure 40MHz" mode cause uCode
crashing by adding support for "pure 40MHz" in RX_ON command flag.
the "mode" field (bits 25:26) has value of 0-3
    0 = 20 MHz only
    1 = 40MHz only
    2 = Mixed
    3 = Reserved
Control Channel ID (bit 22) is valid only in Mixed mode.

Signed-off-by: default avatarWey-Yi Guy <wey-yi.w.guy@intel.com>
Signed-off-by: default avatarReinette Chatre <reinette.chatre@intel.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent a9c146b3
Loading
Loading
Loading
Loading
+7 −5
Original line number Diff line number Diff line
@@ -293,10 +293,12 @@ restart:
	queue_work(priv->workqueue, &priv->restart);
}

static int is_fat_channel(__le32 rxon_flags)
static bool is_fat_channel(__le32 rxon_flags)
{
	return (rxon_flags & RXON_FLG_CHANNEL_MODE_PURE_40_MSK) ||
		(rxon_flags & RXON_FLG_CHANNEL_MODE_MIXED_MSK);
	int chan_mod = le32_to_cpu(rxon_flags & RXON_FLG_CHANNEL_MODE_MSK)
				    >> RXON_FLG_CHANNEL_MODE_POS;
	return ((chan_mod == CHANNEL_MODE_PURE_40) ||
		  (chan_mod == CHANNEL_MODE_MIXED));
}

/*
@@ -1490,7 +1492,7 @@ static int iwl4965_send_tx_power(struct iwl_priv *priv)
	struct iwl4965_txpowertable_cmd cmd = { 0 };
	int ret;
	u8 band = 0;
	u8 is_fat = 0;
	bool is_fat = false;
	u8 ctrl_chan_high = 0;

	if (test_bit(STATUS_SCANNING, &priv->status)) {
@@ -1568,7 +1570,7 @@ static int iwl4965_hw_channel_switch(struct iwl_priv *priv, u16 channel)
{
	int rc;
	u8 band = 0;
	u8 is_fat = 0;
	bool is_fat = false;
	u8 ctrl_chan_high = 0;
	struct iwl4965_channel_switch_cmd cmd = { 0 };
	const struct iwl_channel_info *ch_info;
+12 −2
Original line number Diff line number Diff line
@@ -614,8 +614,18 @@ enum {

#define RXON_FLG_CHANNEL_MODE_POS		(25)
#define RXON_FLG_CHANNEL_MODE_MSK		cpu_to_le32(0x3 << 25)
#define RXON_FLG_CHANNEL_MODE_PURE_40_MSK	cpu_to_le32(0x1 << 25)
#define RXON_FLG_CHANNEL_MODE_MIXED_MSK		cpu_to_le32(0x2 << 25)

/* channel mode */
enum {
	CHANNEL_MODE_LEGACY = 0,
	CHANNEL_MODE_PURE_40 = 1,
	CHANNEL_MODE_MIXED = 2,
	CHANNEL_MODE_RESERVED = 3,
};
#define RXON_FLG_CHANNEL_MODE_LEGACY	cpu_to_le32(CHANNEL_MODE_LEGACY << RXON_FLG_CHANNEL_MODE_POS)
#define RXON_FLG_CHANNEL_MODE_PURE_40	cpu_to_le32(CHANNEL_MODE_PURE_40 << RXON_FLG_CHANNEL_MODE_POS)
#define RXON_FLG_CHANNEL_MODE_MIXED	cpu_to_le32(CHANNEL_MODE_MIXED << RXON_FLG_CHANNEL_MODE_POS)

/* CTS to self (if spec allows) flag */
#define RXON_FLG_SELF_CTS_EN			cpu_to_le32(0x1<<30)

+49 −35
Original line number Diff line number Diff line
@@ -617,16 +617,20 @@ u8 iwl_is_fat_tx_allowed(struct iwl_priv *priv,
	struct iwl_ht_info *iwl_ht_conf = &priv->current_ht_config;

	if ((!iwl_ht_conf->is_ht) ||
	   (iwl_ht_conf->supported_chan_width != IWL_CHANNEL_WIDTH_40MHZ) ||
	   (iwl_ht_conf->extension_chan_offset == IEEE80211_HT_PARAM_CHA_SEC_NONE))
	    (iwl_ht_conf->supported_chan_width != IWL_CHANNEL_WIDTH_40MHZ))
		return 0;

	/* We do not check for IEEE80211_HT_CAP_SUP_WIDTH_20_40
	 * the bit will not set if it is pure 40MHz case
	 */
	if (sta_ht_inf) {
		if ((!sta_ht_inf->ht_supported) ||
		   (!(sta_ht_inf->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)))
		if (!sta_ht_inf->ht_supported)
			return 0;
	}

	if (iwl_ht_conf->ht_protection & IEEE80211_HT_OP_MODE_PROTECTION_20MHZ)
		return 1;
	else
		return iwl_is_channel_extension(priv, priv->band,
				le16_to_cpu(priv->staging_rxon.channel),
				iwl_ht_conf->extension_chan_offset);
@@ -799,41 +803,50 @@ EXPORT_SYMBOL(iwl_rate_get_lowest_plcp);
void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_info *ht_info)
{
	struct iwl_rxon_cmd *rxon = &priv->staging_rxon;
	u32 val;

	if (!ht_info->is_ht) {
		rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MIXED_MSK |
			RXON_FLG_CHANNEL_MODE_PURE_40_MSK |
		rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MSK |
			RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK |
			RXON_FLG_FAT_PROT_MSK |
			RXON_FLG_HT_PROT_MSK);
		return;
	}

	/* Set up channel bandwidth:  20 MHz only, or 20/40 mixed if fat ok */
	if (iwl_is_fat_tx_allowed(priv, NULL))
		rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED_MSK;
	else
		rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MIXED_MSK |
				 RXON_FLG_CHANNEL_MODE_PURE_40_MSK);

	/* FIXME: if the definition of ht_protection changed, the "translation"
	 * will be needed for rxon->flags
	 */
	rxon->flags |= cpu_to_le32(ht_info->ht_protection << RXON_FLG_HT_OPERATING_MODE_POS);

	/* Set up channel bandwidth:
	 * 20 MHz only, 20/40 mixed or pure 40 if fat ok */
	/* clear the HT channel mode before set the mode */
	rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MSK |
			 RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK);
	if (iwl_is_fat_tx_allowed(priv, NULL)) {
		/* pure 40 fat */
		if (rxon->flags & RXON_FLG_FAT_PROT_MSK)
			rxon->flags |= RXON_FLG_CHANNEL_MODE_PURE_40;
		else {
			/* Note: control channel is opposite of extension channel */
			switch (ht_info->extension_chan_offset) {
			case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
				rxon->flags &= ~(RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK);
				rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED;
				break;
			case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
				rxon->flags |= RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK;
				rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED;
				break;
			case IEEE80211_HT_PARAM_CHA_SEC_NONE:
			default:
		rxon->flags &= ~RXON_FLG_CHANNEL_MODE_MIXED_MSK;
				/* channel location only valid if in Mixed mode */
				IWL_ERR(priv, "invalid extension channel offset\n");
				break;
			}

	val = ht_info->ht_protection;

	rxon->flags |= cpu_to_le32(val << RXON_FLG_HT_OPERATING_MODE_POS);
		}
	} else {
		rxon->flags |= RXON_FLG_CHANNEL_MODE_LEGACY;
	}

	if (priv->cfg->ops->hcmd->set_rxon_chain)
		priv->cfg->ops->hcmd->set_rxon_chain(priv);
@@ -1122,8 +1135,9 @@ void iwl_connection_init_rx_config(struct iwl_priv *priv, int mode)
	priv->staging_rxon.cck_basic_rates =
	    (IWL_CCK_RATES_MASK >> IWL_FIRST_CCK_RATE) & 0xF;

	priv->staging_rxon.flags &= ~(RXON_FLG_CHANNEL_MODE_MIXED_MSK |
					RXON_FLG_CHANNEL_MODE_PURE_40_MSK);
	/* clear both MIX and PURE40 mode flag */
	priv->staging_rxon.flags &= ~(RXON_FLG_CHANNEL_MODE_MIXED |
					RXON_FLG_CHANNEL_MODE_PURE_40);
	memcpy(priv->staging_rxon.node_addr, priv->mac_addr, ETH_ALEN);
	memcpy(priv->staging_rxon.wlap_bssid_addr, priv->mac_addr, ETH_ALEN);
	priv->staging_rxon.ofdm_ht_single_stream_basic_rates = 0xff;
+4 −1
Original line number Diff line number Diff line
@@ -586,6 +586,7 @@ static void iwl_bg_request_scan(struct work_struct *data)
	u8 rx_ant = priv->hw_params.valid_rx_ant;
	u8 rate;
	bool is_active = false;
	int  chan_mod;

	conf = ieee80211_get_hw_conf(priv->hw);

@@ -703,7 +704,9 @@ static void iwl_bg_request_scan(struct work_struct *data)
	if (priv->scan_bands & BIT(IEEE80211_BAND_2GHZ)) {
		band = IEEE80211_BAND_2GHZ;
		scan->flags = RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK;
		if (priv->active_rxon.flags & RXON_FLG_CHANNEL_MODE_PURE_40_MSK) {
		chan_mod = le32_to_cpu(priv->active_rxon.flags & RXON_FLG_CHANNEL_MODE_MSK)
				       >> RXON_FLG_CHANNEL_MODE_POS;
		if (chan_mod == CHANNEL_MODE_PURE_40) {
			rate = IWL_RATE_6M_PLCP;
		} else {
			rate = IWL_RATE_1M_PLCP;