Commit 47ff65c4 authored by Daniel C Halperin's avatar Daniel C Halperin Committed by John W. Linville

iwlwifi: fix bugs in beacon configuration

When sending beacon commands to the uCode, we must
inform it of the offset in the beacon frame of the
TIM Element so it can transmit packets from the
correct queue. This functionality is implemented
in iwl_set_beacon_tim().

Fix a bug setting the rate_n_flags for the beacon
packet. First, it should not use the station table's
rate (it's a management frame), and second it needs
to properly configure the TX antennas.

Finally, also, clean up and comment relevant functions.
Signed-off-by: default avatarDaniel C Halperin <daniel.c.halperin@intel.com>
Signed-off-by: default avatarJohannes Berg <johannes@sipsolutions.net>
Signed-off-by: default avatarReinette Chatre <reinette.chatre@intel.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent e43ab94d
...@@ -310,7 +310,7 @@ static void iwl_free_frame(struct iwl_priv *priv, struct iwl_frame *frame) ...@@ -310,7 +310,7 @@ static void iwl_free_frame(struct iwl_priv *priv, struct iwl_frame *frame)
list_add(&frame->list, &priv->free_frames); list_add(&frame->list, &priv->free_frames);
} }
static unsigned int iwl_fill_beacon_frame(struct iwl_priv *priv, static u32 iwl_fill_beacon_frame(struct iwl_priv *priv,
struct ieee80211_hdr *hdr, struct ieee80211_hdr *hdr,
int left) int left)
{ {
...@@ -327,34 +327,74 @@ static unsigned int iwl_fill_beacon_frame(struct iwl_priv *priv, ...@@ -327,34 +327,74 @@ static unsigned int iwl_fill_beacon_frame(struct iwl_priv *priv,
return priv->ibss_beacon->len; return priv->ibss_beacon->len;
} }
/* Parse the beacon frame to find the TIM element and set tim_idx & tim_size */
static void iwl_set_beacon_tim(struct iwl_priv *priv,
struct iwl_tx_beacon_cmd *tx_beacon_cmd,
u8 *beacon, u32 frame_size)
{
u16 tim_idx;
struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)beacon;
/*
* The index is relative to frame start but we start looking at the
* variable-length part of the beacon.
*/
tim_idx = mgmt->u.beacon.variable - beacon;
/* Parse variable-length elements of beacon to find WLAN_EID_TIM */
while ((tim_idx < (frame_size - 2)) &&
(beacon[tim_idx] != WLAN_EID_TIM))
tim_idx += beacon[tim_idx+1] + 2;
/* If TIM field was found, set variables */
if ((tim_idx < (frame_size - 1)) && (beacon[tim_idx] == WLAN_EID_TIM)) {
tx_beacon_cmd->tim_idx = cpu_to_le16(tim_idx);
tx_beacon_cmd->tim_size = beacon[tim_idx+1];
} else
IWL_WARN(priv, "Unable to find TIM Element in beacon\n");
}
static unsigned int iwl_hw_get_beacon_cmd(struct iwl_priv *priv, static unsigned int iwl_hw_get_beacon_cmd(struct iwl_priv *priv,
struct iwl_frame *frame, u8 rate) struct iwl_frame *frame)
{ {
struct iwl_tx_beacon_cmd *tx_beacon_cmd; struct iwl_tx_beacon_cmd *tx_beacon_cmd;
unsigned int frame_size; u32 frame_size;
u32 rate_flags;
u32 rate;
/*
* We have to set up the TX command, the TX Beacon command, and the
* beacon contents.
*/
/* Initialize memory */
tx_beacon_cmd = &frame->u.beacon; tx_beacon_cmd = &frame->u.beacon;
memset(tx_beacon_cmd, 0, sizeof(*tx_beacon_cmd)); memset(tx_beacon_cmd, 0, sizeof(*tx_beacon_cmd));
tx_beacon_cmd->tx.sta_id = priv->hw_params.bcast_sta_id; /* Set up TX beacon contents */
tx_beacon_cmd->tx.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE;
frame_size = iwl_fill_beacon_frame(priv, tx_beacon_cmd->frame, frame_size = iwl_fill_beacon_frame(priv, tx_beacon_cmd->frame,
sizeof(frame->u) - sizeof(*tx_beacon_cmd)); sizeof(frame->u) - sizeof(*tx_beacon_cmd));
if (WARN_ON_ONCE(frame_size > MAX_MPDU_SIZE))
return 0;
BUG_ON(frame_size > MAX_MPDU_SIZE); /* Set up TX command fields */
tx_beacon_cmd->tx.len = cpu_to_le16((u16)frame_size); tx_beacon_cmd->tx.len = cpu_to_le16((u16)frame_size);
tx_beacon_cmd->tx.sta_id = priv->hw_params.bcast_sta_id;
tx_beacon_cmd->tx.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE;
tx_beacon_cmd->tx.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK |
TX_CMD_FLG_TSF_MSK | TX_CMD_FLG_STA_RATE_MSK;
if ((rate == IWL_RATE_1M_PLCP) || (rate >= IWL_RATE_2M_PLCP)) /* Set up TX beacon command fields */
tx_beacon_cmd->tx.rate_n_flags = iwl_set_beacon_tim(priv, tx_beacon_cmd, (u8 *)tx_beacon_cmd->frame,
iwl_hw_set_rate_n_flags(rate, RATE_MCS_CCK_MSK); frame_size);
else
tx_beacon_cmd->tx.rate_n_flags =
iwl_hw_set_rate_n_flags(rate, 0);
tx_beacon_cmd->tx.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK | /* Set up packet rate and flags */
TX_CMD_FLG_TSF_MSK | rate = iwl_rate_get_lowest_plcp(priv);
TX_CMD_FLG_STA_RATE_MSK; priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant);
rate_flags = iwl_ant_idx_to_flags(priv->mgmt_tx_ant);
if ((rate >= IWL_FIRST_CCK_RATE) && (rate <= IWL_LAST_CCK_RATE))
rate_flags |= RATE_MCS_CCK_MSK;
tx_beacon_cmd->tx.rate_n_flags = iwl_hw_set_rate_n_flags(rate,
rate_flags);
return sizeof(*tx_beacon_cmd) + frame_size; return sizeof(*tx_beacon_cmd) + frame_size;
} }
...@@ -363,19 +403,20 @@ static int iwl_send_beacon_cmd(struct iwl_priv *priv) ...@@ -363,19 +403,20 @@ static int iwl_send_beacon_cmd(struct iwl_priv *priv)
struct iwl_frame *frame; struct iwl_frame *frame;
unsigned int frame_size; unsigned int frame_size;
int rc; int rc;
u8 rate;
frame = iwl_get_free_frame(priv); frame = iwl_get_free_frame(priv);
if (!frame) { if (!frame) {
IWL_ERR(priv, "Could not obtain free frame buffer for beacon " IWL_ERR(priv, "Could not obtain free frame buffer for beacon "
"command.\n"); "command.\n");
return -ENOMEM; return -ENOMEM;
} }
rate = iwl_rate_get_lowest_plcp(priv); frame_size = iwl_hw_get_beacon_cmd(priv, frame);
if (!frame_size) {
frame_size = iwl_hw_get_beacon_cmd(priv, frame, rate); IWL_ERR(priv, "Error configuring the beacon command\n");
iwl_free_frame(priv, frame);
return -EINVAL;
}
rc = iwl_send_cmd_pdu(priv, REPLY_TX_BEACON, frame_size, rc = iwl_send_cmd_pdu(priv, REPLY_TX_BEACON, frame_size,
&frame->u.cmd[0]); &frame->u.cmd[0]);
......
...@@ -208,6 +208,7 @@ u8 iwl_toggle_tx_ant(struct iwl_priv *priv, u8 ant) ...@@ -208,6 +208,7 @@ u8 iwl_toggle_tx_ant(struct iwl_priv *priv, u8 ant)
} }
return ant; return ant;
} }
EXPORT_SYMBOL(iwl_toggle_tx_ant);
const u8 iwl_bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; const u8 iwl_bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
EXPORT_SYMBOL(iwl_bcast_addr); EXPORT_SYMBOL(iwl_bcast_addr);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment