Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next-2.6

This commit is contained in:
David S. Miller 2008-09-11 15:46:02 -07:00
commit c655705037
51 changed files with 3861 additions and 3334 deletions

View File

@ -224,7 +224,7 @@ static void setup_ht_cap(struct ieee80211_ht_info *ht_info)
ht_info->ht_supported = 1; ht_info->ht_supported = 1;
ht_info->cap = (u16)IEEE80211_HT_CAP_SUP_WIDTH ht_info->cap = (u16)IEEE80211_HT_CAP_SUP_WIDTH
|(u16)IEEE80211_HT_CAP_MIMO_PS |(u16)IEEE80211_HT_CAP_SM_PS
|(u16)IEEE80211_HT_CAP_SGI_40 |(u16)IEEE80211_HT_CAP_SGI_40
|(u16)IEEE80211_HT_CAP_DSSSCCK40; |(u16)IEEE80211_HT_CAP_DSSSCCK40;

View File

@ -585,8 +585,6 @@ enum {
struct b43_qos_params { struct b43_qos_params {
/* The QOS parameters */ /* The QOS parameters */
struct ieee80211_tx_queue_params p; struct ieee80211_tx_queue_params p;
/* Does this need to get uploaded to hardware? */
bool need_hw_update;
}; };
struct b43_wldev; struct b43_wldev;
@ -648,11 +646,8 @@ struct b43_wl {
bool beacon_templates_virgin; /* Never wrote the templates? */ bool beacon_templates_virgin; /* Never wrote the templates? */
struct work_struct beacon_update_trigger; struct work_struct beacon_update_trigger;
/* The current QOS parameters for the 4 queues. /* The current QOS parameters for the 4 queues. */
* This is protected by the irq_lock. */
struct b43_qos_params qos_params[4]; struct b43_qos_params qos_params[4];
/* Workqueue for updating QOS parameters in hardware. */
struct work_struct qos_update_work;
/* Work for adjustment of the transmission power. /* Work for adjustment of the transmission power.
* This is scheduled when we determine that the actual TX output * This is scheduled when we determine that the actual TX output

View File

@ -3059,36 +3059,31 @@ static void b43_qos_params_upload(struct b43_wldev *dev,
} }
} }
/* Update the QOS parameters in hardware. */ /* Mapping of mac80211 queue numbers to b43 QoS SHM offsets. */
static void b43_qos_update(struct b43_wldev *dev) static const u16 b43_qos_shm_offsets[] = {
/* [mac80211-queue-nr] = SHM_OFFSET, */
[0] = B43_QOS_VOICE,
[1] = B43_QOS_VIDEO,
[2] = B43_QOS_BESTEFFORT,
[3] = B43_QOS_BACKGROUND,
};
/* Update all QOS parameters in hardware. */
static void b43_qos_upload_all(struct b43_wldev *dev)
{ {
struct b43_wl *wl = dev->wl; struct b43_wl *wl = dev->wl;
struct b43_qos_params *params; struct b43_qos_params *params;
unsigned long flags;
unsigned int i; unsigned int i;
/* Mapping of mac80211 queues to b43 SHM offsets. */ BUILD_BUG_ON(ARRAY_SIZE(b43_qos_shm_offsets) !=
static const u16 qos_shm_offsets[] = { ARRAY_SIZE(wl->qos_params));
[0] = B43_QOS_VOICE,
[1] = B43_QOS_VIDEO,
[2] = B43_QOS_BESTEFFORT,
[3] = B43_QOS_BACKGROUND,
};
BUILD_BUG_ON(ARRAY_SIZE(qos_shm_offsets) != ARRAY_SIZE(wl->qos_params));
b43_mac_suspend(dev); b43_mac_suspend(dev);
spin_lock_irqsave(&wl->irq_lock, flags);
for (i = 0; i < ARRAY_SIZE(wl->qos_params); i++) { for (i = 0; i < ARRAY_SIZE(wl->qos_params); i++) {
params = &(wl->qos_params[i]); params = &(wl->qos_params[i]);
if (params->need_hw_update) { b43_qos_params_upload(dev, &(params->p),
b43_qos_params_upload(dev, &(params->p), b43_qos_shm_offsets[i]);
qos_shm_offsets[i]);
params->need_hw_update = 0;
}
} }
spin_unlock_irqrestore(&wl->irq_lock, flags);
b43_mac_enable(dev); b43_mac_enable(dev);
} }
@ -3097,25 +3092,50 @@ static void b43_qos_clear(struct b43_wl *wl)
struct b43_qos_params *params; struct b43_qos_params *params;
unsigned int i; unsigned int i;
/* Initialize QoS parameters to sane defaults. */
BUILD_BUG_ON(ARRAY_SIZE(b43_qos_shm_offsets) !=
ARRAY_SIZE(wl->qos_params));
for (i = 0; i < ARRAY_SIZE(wl->qos_params); i++) { for (i = 0; i < ARRAY_SIZE(wl->qos_params); i++) {
params = &(wl->qos_params[i]); params = &(wl->qos_params[i]);
memset(&(params->p), 0, sizeof(params->p)); switch (b43_qos_shm_offsets[i]) {
params->p.aifs = -1; case B43_QOS_VOICE:
params->need_hw_update = 1; params->p.txop = 0;
params->p.aifs = 2;
params->p.cw_min = 0x0001;
params->p.cw_max = 0x0001;
break;
case B43_QOS_VIDEO:
params->p.txop = 0;
params->p.aifs = 2;
params->p.cw_min = 0x0001;
params->p.cw_max = 0x0001;
break;
case B43_QOS_BESTEFFORT:
params->p.txop = 0;
params->p.aifs = 3;
params->p.cw_min = 0x0001;
params->p.cw_max = 0x03FF;
break;
case B43_QOS_BACKGROUND:
params->p.txop = 0;
params->p.aifs = 7;
params->p.cw_min = 0x0001;
params->p.cw_max = 0x03FF;
break;
default:
B43_WARN_ON(1);
}
} }
} }
/* Initialize the core's QOS capabilities */ /* Initialize the core's QOS capabilities */
static void b43_qos_init(struct b43_wldev *dev) static void b43_qos_init(struct b43_wldev *dev)
{ {
struct b43_wl *wl = dev->wl;
unsigned int i;
/* Upload the current QOS parameters. */ /* Upload the current QOS parameters. */
for (i = 0; i < ARRAY_SIZE(wl->qos_params); i++) b43_qos_upload_all(dev);
wl->qos_params[i].need_hw_update = 1;
b43_qos_update(dev);
/* Enable QOS support. */ /* Enable QOS support. */
b43_hf_write(dev, b43_hf_read(dev) | B43_HF_EDCF); b43_hf_write(dev, b43_hf_read(dev) | B43_HF_EDCF);
@ -3124,25 +3144,13 @@ static void b43_qos_init(struct b43_wldev *dev)
| B43_MMIO_IFSCTL_USE_EDCF); | B43_MMIO_IFSCTL_USE_EDCF);
} }
static void b43_qos_update_work(struct work_struct *work)
{
struct b43_wl *wl = container_of(work, struct b43_wl, qos_update_work);
struct b43_wldev *dev;
mutex_lock(&wl->mutex);
dev = wl->current_dev;
if (likely(dev && (b43_status(dev) >= B43_STAT_INITIALIZED)))
b43_qos_update(dev);
mutex_unlock(&wl->mutex);
}
static int b43_op_conf_tx(struct ieee80211_hw *hw, u16 _queue, static int b43_op_conf_tx(struct ieee80211_hw *hw, u16 _queue,
const struct ieee80211_tx_queue_params *params) const struct ieee80211_tx_queue_params *params)
{ {
struct b43_wl *wl = hw_to_b43_wl(hw); struct b43_wl *wl = hw_to_b43_wl(hw);
unsigned long flags; struct b43_wldev *dev;
unsigned int queue = (unsigned int)_queue; unsigned int queue = (unsigned int)_queue;
struct b43_qos_params *p; int err = -ENODEV;
if (queue >= ARRAY_SIZE(wl->qos_params)) { if (queue >= ARRAY_SIZE(wl->qos_params)) {
/* Queue not available or don't support setting /* Queue not available or don't support setting
@ -3150,16 +3158,25 @@ static int b43_op_conf_tx(struct ieee80211_hw *hw, u16 _queue,
* confuse mac80211. */ * confuse mac80211. */
return 0; return 0;
} }
BUILD_BUG_ON(ARRAY_SIZE(b43_qos_shm_offsets) !=
ARRAY_SIZE(wl->qos_params));
spin_lock_irqsave(&wl->irq_lock, flags); mutex_lock(&wl->mutex);
p = &(wl->qos_params[queue]); dev = wl->current_dev;
memcpy(&(p->p), params, sizeof(p->p)); if (unlikely(!dev || (b43_status(dev) < B43_STAT_INITIALIZED)))
p->need_hw_update = 1; goto out_unlock;
spin_unlock_irqrestore(&wl->irq_lock, flags);
queue_work(hw->workqueue, &wl->qos_update_work); memcpy(&(wl->qos_params[queue].p), params, sizeof(*params));
b43_mac_suspend(dev);
b43_qos_params_upload(dev, &(wl->qos_params[queue].p),
b43_qos_shm_offsets[queue]);
b43_mac_enable(dev);
err = 0;
return 0; out_unlock:
mutex_unlock(&wl->mutex);
return err;
} }
static int b43_op_get_tx_stats(struct ieee80211_hw *hw, static int b43_op_get_tx_stats(struct ieee80211_hw *hw,
@ -4186,7 +4203,6 @@ static void b43_op_stop(struct ieee80211_hw *hw)
struct b43_wldev *dev = wl->current_dev; struct b43_wldev *dev = wl->current_dev;
b43_rfkill_exit(dev); b43_rfkill_exit(dev);
cancel_work_sync(&(wl->qos_update_work));
cancel_work_sync(&(wl->beacon_update_trigger)); cancel_work_sync(&(wl->beacon_update_trigger));
mutex_lock(&wl->mutex); mutex_lock(&wl->mutex);
@ -4585,7 +4601,6 @@ static int b43_wireless_init(struct ssb_device *dev)
spin_lock_init(&wl->shm_lock); spin_lock_init(&wl->shm_lock);
mutex_init(&wl->mutex); mutex_init(&wl->mutex);
INIT_LIST_HEAD(&wl->devlist); INIT_LIST_HEAD(&wl->devlist);
INIT_WORK(&wl->qos_update_work, b43_qos_update_work);
INIT_WORK(&wl->beacon_update_trigger, b43_beacon_update_trigger_work); INIT_WORK(&wl->beacon_update_trigger, b43_beacon_update_trigger_work);
INIT_WORK(&wl->txpower_adjust_work, b43_phy_txpower_adjust_work); INIT_WORK(&wl->txpower_adjust_work, b43_phy_txpower_adjust_work);

View File

@ -386,7 +386,6 @@ static void b43_set_original_gains(struct b43_wldev *dev)
void b43_nrssi_hw_write(struct b43_wldev *dev, u16 offset, s16 val) void b43_nrssi_hw_write(struct b43_wldev *dev, u16 offset, s16 val)
{ {
b43_phy_write(dev, B43_PHY_NRSSILT_CTRL, offset); b43_phy_write(dev, B43_PHY_NRSSILT_CTRL, offset);
mmiowb();
b43_phy_write(dev, B43_PHY_NRSSILT_DATA, (u16) val); b43_phy_write(dev, B43_PHY_NRSSILT_DATA, (u16) val);
} }

View File

@ -595,12 +595,14 @@ static void b43legacy_phy_initb5(struct b43legacy_wldev *dev)
0x0035) & 0xFFC0) | 0x0064); 0x0035) & 0xFFC0) | 0x0064);
b43legacy_phy_write(dev, 0x005D, (b43legacy_phy_read(dev, b43legacy_phy_write(dev, 0x005D, (b43legacy_phy_read(dev,
0x005D) & 0xFF80) | 0x000A); 0x005D) & 0xFF80) | 0x000A);
b43legacy_phy_write(dev, 0x5B, 0x0000);
b43legacy_phy_write(dev, 0x5C, 0x0000);
} }
if (dev->bad_frames_preempt) if (dev->bad_frames_preempt)
b43legacy_phy_write(dev, B43legacy_PHY_RADIO_BITFIELD, b43legacy_phy_write(dev, B43legacy_PHY_RADIO_BITFIELD,
b43legacy_phy_read(dev, b43legacy_phy_read(dev,
B43legacy_PHY_RADIO_BITFIELD) | (1 << 11)); B43legacy_PHY_RADIO_BITFIELD) | (1 << 12));
if (phy->analog == 1) { if (phy->analog == 1) {
b43legacy_phy_write(dev, 0x0026, 0xCE00); b43legacy_phy_write(dev, 0x0026, 0xCE00);
@ -753,7 +755,7 @@ static void b43legacy_phy_initb6(struct b43legacy_wldev *dev)
b43legacy_radio_write16(dev, 0x0050, 0x0020); b43legacy_radio_write16(dev, 0x0050, 0x0020);
} }
if (phy->radio_rev <= 2) { if (phy->radio_rev <= 2) {
b43legacy_radio_write16(dev, 0x007C, 0x0020); b43legacy_radio_write16(dev, 0x0050, 0x0020);
b43legacy_radio_write16(dev, 0x005A, 0x0070); b43legacy_radio_write16(dev, 0x005A, 0x0070);
b43legacy_radio_write16(dev, 0x005B, 0x007B); b43legacy_radio_write16(dev, 0x005B, 0x007B);
b43legacy_radio_write16(dev, 0x005C, 0x00B0); b43legacy_radio_write16(dev, 0x005C, 0x00B0);
@ -771,7 +773,7 @@ static void b43legacy_phy_initb6(struct b43legacy_wldev *dev)
b43legacy_phy_write(dev, 0x002A, 0x8AC0); b43legacy_phy_write(dev, 0x002A, 0x8AC0);
b43legacy_phy_write(dev, 0x0038, 0x0668); b43legacy_phy_write(dev, 0x0038, 0x0668);
b43legacy_radio_set_txpower_bg(dev, 0xFFFF, 0xFFFF, 0xFFFF); b43legacy_radio_set_txpower_bg(dev, 0xFFFF, 0xFFFF, 0xFFFF);
if (phy->radio_rev <= 5) if (phy->radio_rev == 4 || phy->radio_rev == 5)
b43legacy_phy_write(dev, 0x005D, (b43legacy_phy_read(dev, b43legacy_phy_write(dev, 0x005D, (b43legacy_phy_read(dev,
0x005D) & 0xFF80) | 0x0003); 0x005D) & 0xFF80) | 0x0003);
if (phy->radio_rev <= 2) if (phy->radio_rev <= 2)
@ -1010,7 +1012,7 @@ static void b43legacy_phy_initg(struct b43legacy_wldev *dev)
b43legacy_phy_initb5(dev); b43legacy_phy_initb5(dev);
else else
b43legacy_phy_initb6(dev); b43legacy_phy_initb6(dev);
if (phy->rev >= 2 || phy->gmode) if (phy->rev >= 2 && phy->gmode)
b43legacy_phy_inita(dev); b43legacy_phy_inita(dev);
if (phy->rev >= 2) { if (phy->rev >= 2) {
@ -1025,18 +1027,22 @@ static void b43legacy_phy_initg(struct b43legacy_wldev *dev)
b43legacy_phy_write(dev, 0x0811, 0x0400); b43legacy_phy_write(dev, 0x0811, 0x0400);
b43legacy_phy_write(dev, 0x0015, 0x00C0); b43legacy_phy_write(dev, 0x0015, 0x00C0);
} }
if (phy->rev >= 2 || phy->gmode) { if (phy->gmode) {
tmp = b43legacy_phy_read(dev, 0x0400) & 0xFF; tmp = b43legacy_phy_read(dev, 0x0400) & 0xFF;
if (tmp == 3 || tmp == 5) { if (tmp == 3) {
b43legacy_phy_write(dev, 0x04C2, 0x1816);
b43legacy_phy_write(dev, 0x04C3, 0x8606);
}
if (tmp == 4 || tmp == 5) {
b43legacy_phy_write(dev, 0x04C2, 0x1816); b43legacy_phy_write(dev, 0x04C2, 0x1816);
b43legacy_phy_write(dev, 0x04C3, 0x8006); b43legacy_phy_write(dev, 0x04C3, 0x8006);
if (tmp == 5) b43legacy_phy_write(dev, 0x04CC,
b43legacy_phy_write(dev, 0x04CC, (b43legacy_phy_read(dev,
(b43legacy_phy_read(dev, 0x04CC) & 0x00FF) |
0x04CC) & 0x00FF) | 0x1F00);
0x1F00);
} }
b43legacy_phy_write(dev, 0x047E, 0x0078); if (phy->rev >= 2)
b43legacy_phy_write(dev, 0x047E, 0x0078);
} }
if (phy->radio_rev == 8) { if (phy->radio_rev == 8) {
b43legacy_phy_write(dev, 0x0801, b43legacy_phy_read(dev, 0x0801) b43legacy_phy_write(dev, 0x0801, b43legacy_phy_read(dev, 0x0801)
@ -1078,7 +1084,7 @@ static void b43legacy_phy_initg(struct b43legacy_wldev *dev)
else else
b43legacy_phy_write(dev, 0x002F, 0x0202); b43legacy_phy_write(dev, 0x002F, 0x0202);
} }
if (phy->gmode || phy->rev >= 2) { if (phy->gmode) {
b43legacy_phy_lo_adjust(dev, 0); b43legacy_phy_lo_adjust(dev, 0);
b43legacy_phy_write(dev, 0x080F, 0x8078); b43legacy_phy_write(dev, 0x080F, 0x8078);
} }

View File

@ -624,7 +624,7 @@ void b43legacy_handle_hwtxstatus(struct b43legacy_wldev *dev,
tmp = hw->count; tmp = hw->count;
status.frame_count = (tmp >> 4); status.frame_count = (tmp >> 4);
status.rts_count = (tmp & 0x0F); status.rts_count = (tmp & 0x0F);
tmp = hw->flags; tmp = hw->flags << 1;
status.supp_reason = ((tmp & 0x1C) >> 2); status.supp_reason = ((tmp & 0x1C) >> 2);
status.pm_indicated = !!(tmp & 0x80); status.pm_indicated = !!(tmp & 0x80);
status.intermediate = !!(tmp & 0x40); status.intermediate = !!(tmp & 0x40);

View File

@ -681,19 +681,7 @@ static void iwl3945_rx_reply_rx(struct iwl3945_priv *priv,
priv->last_rx_noise = rx_status.noise; priv->last_rx_noise = rx_status.noise;
} }
if (priv->iw_mode == IEEE80211_IF_TYPE_MNTR) { iwl3945_pass_packet_to_mac80211(priv, rxb, &rx_status);
iwl3945_pass_packet_to_mac80211(priv, rxb, &rx_status);
return;
}
switch (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_FTYPE) {
case IEEE80211_FTYPE_MGMT:
case IEEE80211_FTYPE_DATA:
/* fall through */
default:
iwl3945_pass_packet_to_mac80211(priv, rxb, &rx_status);
break;
}
} }
int iwl3945_hw_txq_attach_buf_to_tfd(struct iwl3945_priv *priv, void *ptr, int iwl3945_hw_txq_attach_buf_to_tfd(struct iwl3945_priv *priv, void *ptr,

View File

@ -1607,8 +1607,8 @@ static int iwl4965_send_rxon_assoc(struct iwl_priv *priv)
return ret; return ret;
} }
#ifdef IEEE80211_CONF_CHANNEL_SWITCH
int iwl4965_hw_channel_switch(struct iwl_priv *priv, u16 channel) static int iwl4965_hw_channel_switch(struct iwl_priv *priv, u16 channel)
{ {
int rc; int rc;
u8 band = 0; u8 band = 0;
@ -1648,6 +1648,7 @@ int iwl4965_hw_channel_switch(struct iwl_priv *priv, u16 channel)
rc = iwl_send_cmd_pdu(priv, REPLY_CHANNEL_SWITCH, sizeof(cmd), &cmd); rc = iwl_send_cmd_pdu(priv, REPLY_CHANNEL_SWITCH, sizeof(cmd), &cmd);
return rc; return rc;
} }
#endif
static int iwl4965_shared_mem_rx_idx(struct iwl_priv *priv) static int iwl4965_shared_mem_rx_idx(struct iwl_priv *priv)
{ {

View File

@ -436,7 +436,7 @@ static int rs_collect_tx_data(struct iwl_rate_scale_data *windows,
/* Shift bitmap by one frame (throw away oldest history), /* Shift bitmap by one frame (throw away oldest history),
* OR in "1", and increment "success" if this * OR in "1", and increment "success" if this
* frame was successful. */ * frame was successful. */
window->data <<= 1;; window->data <<= 1;
if (successes > 0) { if (successes > 0) {
window->success_counter++; window->success_counter++;
window->data |= 0x1; window->data |= 0x1;
@ -1128,6 +1128,7 @@ static s32 rs_get_best_rate(struct iwl_priv *priv,
/* Higher rate not available, use the original */ /* Higher rate not available, use the original */
} else { } else {
new_rate = rate;
break; break;
} }
} }
@ -1153,8 +1154,8 @@ static int rs_switch_to_mimo2(struct iwl_priv *priv,
!sta->ht_info.ht_supported) !sta->ht_info.ht_supported)
return -1; return -1;
if (((sta->ht_info.cap & IEEE80211_HT_CAP_MIMO_PS) >> 2) if (((sta->ht_info.cap & IEEE80211_HT_CAP_SM_PS) >> 2)
== IWL_MIMO_PS_STATIC) == WLAN_HT_CAP_SM_PS_STATIC)
return -1; return -1;
/* Need both Tx chains/antennas to support MIMO */ /* Need both Tx chains/antennas to support MIMO */
@ -1281,15 +1282,23 @@ static int rs_move_legacy_other(struct iwl_priv *priv,
(sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
u8 start_action = tbl->action; u8 start_action = tbl->action;
u8 valid_tx_ant = priv->hw_params.valid_tx_ant; u8 valid_tx_ant = priv->hw_params.valid_tx_ant;
u8 tx_chains_num = priv->hw_params.tx_chains_num;
int ret = 0; int ret = 0;
for (; ;) { for (; ;) {
switch (tbl->action) { switch (tbl->action) {
case IWL_LEGACY_SWITCH_ANTENNA: case IWL_LEGACY_SWITCH_ANTENNA1:
case IWL_LEGACY_SWITCH_ANTENNA2:
IWL_DEBUG_RATE("LQ: Legacy toggle Antenna\n"); IWL_DEBUG_RATE("LQ: Legacy toggle Antenna\n");
lq_sta->action_counter++; lq_sta->action_counter++;
if ((tbl->action == IWL_LEGACY_SWITCH_ANTENNA1 &&
tx_chains_num <= 1) ||
(tbl->action == IWL_LEGACY_SWITCH_ANTENNA2 &&
tx_chains_num <= 2))
break;
/* Don't change antenna if success has been great */ /* Don't change antenna if success has been great */
if (window->success_ratio >= IWL_RS_GOOD_RATIO) if (window->success_ratio >= IWL_RS_GOOD_RATIO)
break; break;
@ -1299,7 +1308,7 @@ static int rs_move_legacy_other(struct iwl_priv *priv,
if (rs_toggle_antenna(valid_tx_ant, if (rs_toggle_antenna(valid_tx_ant,
&search_tbl->current_rate, search_tbl)) { &search_tbl->current_rate, search_tbl)) {
lq_sta->search_better_tbl = 1; rs_set_expected_tpt_table(lq_sta, search_tbl);
goto out; goto out;
} }
break; break;
@ -1312,43 +1321,54 @@ static int rs_move_legacy_other(struct iwl_priv *priv,
ret = rs_switch_to_siso(priv, lq_sta, conf, sta, ret = rs_switch_to_siso(priv, lq_sta, conf, sta,
search_tbl, index); search_tbl, index);
if (!ret) { if (!ret) {
lq_sta->search_better_tbl = 1;
lq_sta->action_counter = 0; lq_sta->action_counter = 0;
goto out; goto out;
} }
break; break;
case IWL_LEGACY_SWITCH_MIMO2: case IWL_LEGACY_SWITCH_MIMO2_AB:
case IWL_LEGACY_SWITCH_MIMO2_AC:
case IWL_LEGACY_SWITCH_MIMO2_BC:
IWL_DEBUG_RATE("LQ: Legacy switch to MIMO2\n"); IWL_DEBUG_RATE("LQ: Legacy switch to MIMO2\n");
/* Set up search table to try MIMO */ /* Set up search table to try MIMO */
memcpy(search_tbl, tbl, sz); memcpy(search_tbl, tbl, sz);
search_tbl->is_SGI = 0; search_tbl->is_SGI = 0;
search_tbl->ant_type = ANT_AB;/*FIXME:RS*/
/*FIXME:RS:need to check ant validity*/ if (tbl->action == IWL_LEGACY_SWITCH_MIMO2_AB)
search_tbl->ant_type = ANT_AB;
else if (tbl->action == IWL_LEGACY_SWITCH_MIMO2_AC)
search_tbl->ant_type = ANT_AC;
else
search_tbl->ant_type = ANT_BC;
if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type))
break;
ret = rs_switch_to_mimo2(priv, lq_sta, conf, sta, ret = rs_switch_to_mimo2(priv, lq_sta, conf, sta,
search_tbl, index); search_tbl, index);
if (!ret) { if (!ret) {
lq_sta->search_better_tbl = 1;
lq_sta->action_counter = 0; lq_sta->action_counter = 0;
goto out; goto out;
} }
break; break;
} }
tbl->action++; tbl->action++;
if (tbl->action > IWL_LEGACY_SWITCH_MIMO2) if (tbl->action > IWL_LEGACY_SWITCH_MIMO2_BC)
tbl->action = IWL_LEGACY_SWITCH_ANTENNA; tbl->action = IWL_LEGACY_SWITCH_ANTENNA1;
if (tbl->action == start_action) if (tbl->action == start_action)
break; break;
} }
search_tbl->lq_type = LQ_NONE;
return 0; return 0;
out: out:
lq_sta->search_better_tbl = 1;
tbl->action++; tbl->action++;
if (tbl->action > IWL_LEGACY_SWITCH_MIMO2) if (tbl->action > IWL_LEGACY_SWITCH_MIMO2_BC)
tbl->action = IWL_LEGACY_SWITCH_ANTENNA; tbl->action = IWL_LEGACY_SWITCH_ANTENNA1;
return 0; return 0;
} }
@ -1370,34 +1390,51 @@ static int rs_move_siso_to_other(struct iwl_priv *priv,
(sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
u8 start_action = tbl->action; u8 start_action = tbl->action;
u8 valid_tx_ant = priv->hw_params.valid_tx_ant; u8 valid_tx_ant = priv->hw_params.valid_tx_ant;
u8 tx_chains_num = priv->hw_params.tx_chains_num;
int ret; int ret;
for (;;) { for (;;) {
lq_sta->action_counter++; lq_sta->action_counter++;
switch (tbl->action) { switch (tbl->action) {
case IWL_SISO_SWITCH_ANTENNA: case IWL_SISO_SWITCH_ANTENNA1:
case IWL_SISO_SWITCH_ANTENNA2:
IWL_DEBUG_RATE("LQ: SISO toggle Antenna\n"); IWL_DEBUG_RATE("LQ: SISO toggle Antenna\n");
if ((tbl->action == IWL_SISO_SWITCH_ANTENNA1 &&
tx_chains_num <= 1) ||
(tbl->action == IWL_SISO_SWITCH_ANTENNA2 &&
tx_chains_num <= 2))
break;
if (window->success_ratio >= IWL_RS_GOOD_RATIO) if (window->success_ratio >= IWL_RS_GOOD_RATIO)
break; break;
memcpy(search_tbl, tbl, sz); memcpy(search_tbl, tbl, sz);
if (rs_toggle_antenna(valid_tx_ant, if (rs_toggle_antenna(valid_tx_ant,
&search_tbl->current_rate, search_tbl)) { &search_tbl->current_rate, search_tbl))
lq_sta->search_better_tbl = 1;
goto out; goto out;
}
break; break;
case IWL_SISO_SWITCH_MIMO2: case IWL_SISO_SWITCH_MIMO2_AB:
case IWL_SISO_SWITCH_MIMO2_AC:
case IWL_SISO_SWITCH_MIMO2_BC:
IWL_DEBUG_RATE("LQ: SISO switch to MIMO2\n"); IWL_DEBUG_RATE("LQ: SISO switch to MIMO2\n");
memcpy(search_tbl, tbl, sz); memcpy(search_tbl, tbl, sz);
search_tbl->is_SGI = 0; search_tbl->is_SGI = 0;
search_tbl->ant_type = ANT_AB; /*FIXME:RS*/
if (tbl->action == IWL_SISO_SWITCH_MIMO2_AB)
search_tbl->ant_type = ANT_AB;
else if (tbl->action == IWL_SISO_SWITCH_MIMO2_AC)
search_tbl->ant_type = ANT_AC;
else
search_tbl->ant_type = ANT_BC;
if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type))
break;
ret = rs_switch_to_mimo2(priv, lq_sta, conf, sta, ret = rs_switch_to_mimo2(priv, lq_sta, conf, sta,
search_tbl, index); search_tbl, index);
if (!ret) { if (!ret)
lq_sta->search_better_tbl = 1;
goto out; goto out;
}
break; break;
case IWL_SISO_SWITCH_GI: case IWL_SISO_SWITCH_GI:
if (!tbl->is_fat && if (!tbl->is_fat &&
@ -1427,22 +1464,23 @@ static int rs_move_siso_to_other(struct iwl_priv *priv,
} }
search_tbl->current_rate = rate_n_flags_from_tbl( search_tbl->current_rate = rate_n_flags_from_tbl(
search_tbl, index, is_green); search_tbl, index, is_green);
lq_sta->search_better_tbl = 1;
goto out; goto out;
} }
tbl->action++; tbl->action++;
if (tbl->action > IWL_SISO_SWITCH_GI) if (tbl->action > IWL_SISO_SWITCH_GI)
tbl->action = IWL_SISO_SWITCH_ANTENNA; tbl->action = IWL_SISO_SWITCH_ANTENNA1;
if (tbl->action == start_action) if (tbl->action == start_action)
break; break;
} }
search_tbl->lq_type = LQ_NONE;
return 0; return 0;
out: out:
lq_sta->search_better_tbl = 1;
tbl->action++; tbl->action++;
if (tbl->action > IWL_SISO_SWITCH_GI) if (tbl->action > IWL_SISO_SWITCH_GI)
tbl->action = IWL_SISO_SWITCH_ANTENNA; tbl->action = IWL_SISO_SWITCH_ANTENNA1;
return 0; return 0;
} }
@ -1458,37 +1496,58 @@ static int rs_move_mimo_to_other(struct iwl_priv *priv,
struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
struct iwl_scale_tbl_info *search_tbl = struct iwl_scale_tbl_info *search_tbl =
&(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]);
struct iwl_rate_scale_data *window = &(tbl->win[index]);
u32 sz = (sizeof(struct iwl_scale_tbl_info) - u32 sz = (sizeof(struct iwl_scale_tbl_info) -
(sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
u8 start_action = tbl->action; u8 start_action = tbl->action;
/*u8 valid_tx_ant = priv->hw_params.valid_tx_ant;*/ u8 valid_tx_ant = priv->hw_params.valid_tx_ant;
u8 tx_chains_num = priv->hw_params.tx_chains_num;
int ret; int ret;
for (;;) { for (;;) {
lq_sta->action_counter++; lq_sta->action_counter++;
switch (tbl->action) { switch (tbl->action) {
case IWL_MIMO_SWITCH_ANTENNA_A: case IWL_MIMO2_SWITCH_ANTENNA1:
case IWL_MIMO_SWITCH_ANTENNA_B: case IWL_MIMO2_SWITCH_ANTENNA2:
IWL_DEBUG_RATE("LQ: MIMO toggle Antennas\n");
if (tx_chains_num <= 2)
break;
if (window->success_ratio >= IWL_RS_GOOD_RATIO)
break;
memcpy(search_tbl, tbl, sz);
if (rs_toggle_antenna(valid_tx_ant,
&search_tbl->current_rate, search_tbl))
goto out;
break;
case IWL_MIMO2_SWITCH_SISO_A:
case IWL_MIMO2_SWITCH_SISO_B:
case IWL_MIMO2_SWITCH_SISO_C:
IWL_DEBUG_RATE("LQ: MIMO2 switch to SISO\n"); IWL_DEBUG_RATE("LQ: MIMO2 switch to SISO\n");
/* Set up new search table for SISO */ /* Set up new search table for SISO */
memcpy(search_tbl, tbl, sz); memcpy(search_tbl, tbl, sz);
/*FIXME:RS:need to check ant validity + C*/ if (tbl->action == IWL_MIMO2_SWITCH_SISO_A)
if (tbl->action == IWL_MIMO_SWITCH_ANTENNA_A)
search_tbl->ant_type = ANT_A; search_tbl->ant_type = ANT_A;
else else if (tbl->action == IWL_MIMO2_SWITCH_SISO_B)
search_tbl->ant_type = ANT_B; search_tbl->ant_type = ANT_B;
else
search_tbl->ant_type = ANT_C;
if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type))
break;
ret = rs_switch_to_siso(priv, lq_sta, conf, sta, ret = rs_switch_to_siso(priv, lq_sta, conf, sta,
search_tbl, index); search_tbl, index);
if (!ret) { if (!ret)
lq_sta->search_better_tbl = 1;
goto out; goto out;
}
break; break;
case IWL_MIMO_SWITCH_GI: case IWL_MIMO2_SWITCH_GI:
if (!tbl->is_fat && if (!tbl->is_fat &&
!(priv->current_ht_config.sgf & !(priv->current_ht_config.sgf &
HT_SHORT_GI_20MHZ)) HT_SHORT_GI_20MHZ))
@ -1517,23 +1576,23 @@ static int rs_move_mimo_to_other(struct iwl_priv *priv,
} }
search_tbl->current_rate = rate_n_flags_from_tbl( search_tbl->current_rate = rate_n_flags_from_tbl(
search_tbl, index, is_green); search_tbl, index, is_green);
lq_sta->search_better_tbl = 1;
goto out; goto out;
} }
tbl->action++; tbl->action++;
if (tbl->action > IWL_MIMO_SWITCH_GI) if (tbl->action > IWL_MIMO2_SWITCH_GI)
tbl->action = IWL_MIMO_SWITCH_ANTENNA_A; tbl->action = IWL_MIMO2_SWITCH_ANTENNA1;
if (tbl->action == start_action) if (tbl->action == start_action)
break; break;
} }
search_tbl->lq_type = LQ_NONE;
return 0; return 0;
out: out:
lq_sta->search_better_tbl = 1;
tbl->action++; tbl->action++;
if (tbl->action > IWL_MIMO_SWITCH_GI) if (tbl->action > IWL_MIMO2_SWITCH_GI)
tbl->action = IWL_MIMO_SWITCH_ANTENNA_A; tbl->action = IWL_MIMO2_SWITCH_ANTENNA1;
return 0; return 0;
} }
@ -1748,19 +1807,13 @@ static void rs_rate_scale_perform(struct iwl_priv *priv,
rs_stay_in_table(lq_sta); rs_stay_in_table(lq_sta);
goto out; goto out;
}
/* Else we have enough samples; calculate estimate of /* Else we have enough samples; calculate estimate of
* actual average throughput */ * actual average throughput */
} else {
/*FIXME:RS remove this else if we don't get this error*/ BUG_ON(window->average_tpt != ((window->success_ratio *
if (window->average_tpt != ((window->success_ratio * tbl->expected_tpt[index] + 64) / 128));
tbl->expected_tpt[index] + 64) / 128)) {
IWL_ERROR("expected_tpt should have been calculated"
" by now\n");
window->average_tpt = ((window->success_ratio *
tbl->expected_tpt[index] + 64) / 128);
}
}
/* If we are searching for better modulation mode, check success. */ /* If we are searching for better modulation mode, check success. */
if (lq_sta->search_better_tbl) { if (lq_sta->search_better_tbl) {
@ -1770,7 +1823,7 @@ static void rs_rate_scale_perform(struct iwl_priv *priv,
* continuing to use the setup that we've been trying. */ * continuing to use the setup that we've been trying. */
if (window->average_tpt > lq_sta->last_tpt) { if (window->average_tpt > lq_sta->last_tpt) {
IWL_DEBUG_RATE("LQ: SWITCHING TO CURRENT TABLE " IWL_DEBUG_RATE("LQ: SWITCHING TO NEW TABLE "
"suc=%d cur-tpt=%d old-tpt=%d\n", "suc=%d cur-tpt=%d old-tpt=%d\n",
window->success_ratio, window->success_ratio,
window->average_tpt, window->average_tpt,
@ -2183,7 +2236,7 @@ static void rs_rate_init(void *priv_rate, void *priv_sta,
for (i = 0; i < IWL_RATE_COUNT; i++) for (i = 0; i < IWL_RATE_COUNT; i++)
rs_rate_scale_clear_window(&lq_sta->lq_info[j].win[i]); rs_rate_scale_clear_window(&lq_sta->lq_info[j].win[i]);
IWL_DEBUG_RATE("LQ: *** rate scale global init ***\n"); IWL_DEBUG_RATE("LQ: *** rate scale station global init ***\n");
/* TODO: what is a good starting rate for STA? About middle? Maybe not /* TODO: what is a good starting rate for STA? About middle? Maybe not
* the lowest or the highest rate.. Could consider using RSSI from * the lowest or the highest rate.. Could consider using RSSI from
* previous packets? Need to have IEEE 802.1X auth succeed immediately * previous packets? Need to have IEEE 802.1X auth succeed immediately

View File

@ -206,21 +206,28 @@ enum {
#define IWL_RATE_DECREASE_TH 1920 /* 15% */ #define IWL_RATE_DECREASE_TH 1920 /* 15% */
/* possible actions when in legacy mode */ /* possible actions when in legacy mode */
#define IWL_LEGACY_SWITCH_ANTENNA 0 #define IWL_LEGACY_SWITCH_ANTENNA1 0
#define IWL_LEGACY_SWITCH_SISO 1 #define IWL_LEGACY_SWITCH_ANTENNA2 1
#define IWL_LEGACY_SWITCH_MIMO2 2 #define IWL_LEGACY_SWITCH_SISO 2
#define IWL_LEGACY_SWITCH_MIMO2_AB 3
#define IWL_LEGACY_SWITCH_MIMO2_AC 4
#define IWL_LEGACY_SWITCH_MIMO2_BC 5
/* possible actions when in siso mode */ /* possible actions when in siso mode */
#define IWL_SISO_SWITCH_ANTENNA 0 #define IWL_SISO_SWITCH_ANTENNA1 0
#define IWL_SISO_SWITCH_MIMO2 1 #define IWL_SISO_SWITCH_ANTENNA2 1
#define IWL_SISO_SWITCH_GI 2 #define IWL_SISO_SWITCH_MIMO2_AB 2
#define IWL_SISO_SWITCH_MIMO2_AC 3
#define IWL_SISO_SWITCH_MIMO2_BC 4
#define IWL_SISO_SWITCH_GI 5
/* possible actions when in mimo mode */ /* possible actions when in mimo mode */
#define IWL_MIMO_SWITCH_ANTENNA_A 0 #define IWL_MIMO2_SWITCH_ANTENNA1 0
#define IWL_MIMO_SWITCH_ANTENNA_B 1 #define IWL_MIMO2_SWITCH_ANTENNA2 1
#define IWL_MIMO_SWITCH_GI 2 #define IWL_MIMO2_SWITCH_SISO_A 2
#define IWL_MIMO2_SWITCH_SISO_B 3
/*FIXME:RS:separate MIMO2/3 transitions*/ #define IWL_MIMO2_SWITCH_SISO_C 4
#define IWL_MIMO2_SWITCH_GI 5
/*FIXME:RS:add posible acctions for MIMO3*/ /*FIXME:RS:add posible acctions for MIMO3*/

View File

@ -485,7 +485,7 @@ static u8 iwl4965_rate_get_lowest_plcp(struct iwl_priv *priv)
return IWL_RATE_6M_PLCP; return IWL_RATE_6M_PLCP;
} }
unsigned int iwl4965_hw_get_beacon_cmd(struct iwl_priv *priv, static unsigned int iwl4965_hw_get_beacon_cmd(struct iwl_priv *priv,
struct iwl_frame *frame, u8 rate) struct iwl_frame *frame, u8 rate)
{ {
struct iwl_tx_beacon_cmd *tx_beacon_cmd; struct iwl_tx_beacon_cmd *tx_beacon_cmd;
@ -564,8 +564,6 @@ static void iwl4965_ht_conf(struct iwl_priv *priv,
if (!iwl_conf->is_ht) if (!iwl_conf->is_ht)
return; return;
priv->ps_mode = (u8)((ht_conf->cap & IEEE80211_HT_CAP_MIMO_PS) >> 2);
if (ht_conf->cap & IEEE80211_HT_CAP_SGI_20) if (ht_conf->cap & IEEE80211_HT_CAP_SGI_20)
iwl_conf->sgf |= HT_SHORT_GI_20MHZ; iwl_conf->sgf |= HT_SHORT_GI_20MHZ;
if (ht_conf->cap & IEEE80211_HT_CAP_SGI_40) if (ht_conf->cap & IEEE80211_HT_CAP_SGI_40)
@ -586,6 +584,8 @@ static void iwl4965_ht_conf(struct iwl_priv *priv,
iwl_conf->supported_chan_width = 0; iwl_conf->supported_chan_width = 0;
} }
iwl_conf->sm_ps = (u8)((ht_conf->cap & IEEE80211_HT_CAP_SM_PS) >> 2);
memcpy(iwl_conf->supp_mcs_set, ht_conf->supp_mcs_set, 16); memcpy(iwl_conf->supp_mcs_set, ht_conf->supp_mcs_set, 16);
iwl_conf->control_channel = ht_bss_conf->primary_channel; iwl_conf->control_channel = ht_bss_conf->primary_channel;
@ -2558,7 +2558,11 @@ static void iwl4965_post_associate(struct iwl_priv *priv)
iwl_activate_qos(priv, 0); iwl_activate_qos(priv, 0);
spin_unlock_irqrestore(&priv->lock, flags); spin_unlock_irqrestore(&priv->lock, flags);
iwl_power_enable_management(priv); /* the chain noise calibration will enabled PM upon completion
* If chain noise has already been run, then we need to enable
* power management here */
if (priv->chain_noise_data.state == IWL_CHAIN_NOISE_DONE)
iwl_power_enable_management(priv);
/* Enable Rx differential gain and sensitivity calibrations */ /* Enable Rx differential gain and sensitivity calibrations */
iwl_chain_noise_reset(priv); iwl_chain_noise_reset(priv);

View File

@ -808,13 +808,11 @@ void iwl_chain_noise_calibration(struct iwl_priv *priv,
} }
} }
/* Save for use within RXON, TX, SCAN commands, etc. */
priv->chain_noise_data.active_chains = active_chains;
IWL_DEBUG_CALIB("active_chains (bitwise) = 0x%x\n", IWL_DEBUG_CALIB("active_chains (bitwise) = 0x%x\n",
active_chains); active_chains);
/* Save for use within RXON, TX, SCAN commands, etc. */
/*priv->valid_antenna = active_chains;*/
/*FIXME: should be reflected in RX chains in RXON */
/* Analyze noise for rx balance */ /* Analyze noise for rx balance */
average_noise[0] = ((data->chain_noise_a)/CAL_NUM_OF_BEACONS); average_noise[0] = ((data->chain_noise_a)/CAL_NUM_OF_BEACONS);
average_noise[1] = ((data->chain_noise_b)/CAL_NUM_OF_BEACONS); average_noise[1] = ((data->chain_noise_b)/CAL_NUM_OF_BEACONS);
@ -839,6 +837,15 @@ void iwl_chain_noise_calibration(struct iwl_priv *priv,
priv->cfg->ops->utils->gain_computation(priv, average_noise, priv->cfg->ops->utils->gain_computation(priv, average_noise,
min_average_noise_antenna_i, min_average_noise); min_average_noise_antenna_i, min_average_noise);
/* Some power changes may have been made during the calibration.
* Update and commit the RXON
*/
if (priv->cfg->ops->lib->update_chain_flags)
priv->cfg->ops->lib->update_chain_flags(priv);
data->state = IWL_CHAIN_NOISE_DONE;
iwl_power_enable_management(priv);
} }
EXPORT_SYMBOL(iwl_chain_noise_calibration); EXPORT_SYMBOL(iwl_chain_noise_calibration);

View File

@ -399,8 +399,8 @@ static void iwlcore_init_ht_hw_capab(const struct iwl_priv *priv,
ht_info->cap |= (u16)IEEE80211_HT_CAP_GRN_FLD; ht_info->cap |= (u16)IEEE80211_HT_CAP_GRN_FLD;
ht_info->cap |= (u16)IEEE80211_HT_CAP_SGI_20; ht_info->cap |= (u16)IEEE80211_HT_CAP_SGI_20;
ht_info->cap |= (u16)(IEEE80211_HT_CAP_MIMO_PS & ht_info->cap |= (u16)(IEEE80211_HT_CAP_SM_PS &
(IWL_MIMO_PS_NONE << 2)); (WLAN_HT_CAP_SM_PS_DISABLED << 2));
max_bit_rate = MAX_BIT_RATE_20_MHZ; max_bit_rate = MAX_BIT_RATE_20_MHZ;
if (priv->hw_params.fat_channel & BIT(band)) { if (priv->hw_params.fat_channel & BIT(band)) {
@ -709,7 +709,8 @@ static int iwl_get_active_rx_chain_count(struct iwl_priv *priv)
bool is_cam = !test_bit(STATUS_POWER_PMI, &priv->status); bool is_cam = !test_bit(STATUS_POWER_PMI, &priv->status);
/* # of Rx chains to use when expecting MIMO. */ /* # of Rx chains to use when expecting MIMO. */
if (is_single || (!is_cam && (priv->ps_mode == IWL_MIMO_PS_STATIC))) if (is_single || (!is_cam && (priv->current_ht_config.sm_ps ==
WLAN_HT_CAP_SM_PS_STATIC)))
return 2; return 2;
else else
return 3; return 3;
@ -720,17 +721,18 @@ static int iwl_get_idle_rx_chain_count(struct iwl_priv *priv, int active_cnt)
int idle_cnt; int idle_cnt;
bool is_cam = !test_bit(STATUS_POWER_PMI, &priv->status); bool is_cam = !test_bit(STATUS_POWER_PMI, &priv->status);
/* # Rx chains when idling and maybe trying to save power */ /* # Rx chains when idling and maybe trying to save power */
switch (priv->ps_mode) { switch (priv->current_ht_config.sm_ps) {
case IWL_MIMO_PS_STATIC: case WLAN_HT_CAP_SM_PS_STATIC:
case IWL_MIMO_PS_DYNAMIC: case WLAN_HT_CAP_SM_PS_DYNAMIC:
idle_cnt = (is_cam) ? 2 : 1; idle_cnt = (is_cam) ? 2 : 1;
break; break;
case IWL_MIMO_PS_NONE: case WLAN_HT_CAP_SM_PS_DISABLED:
idle_cnt = (is_cam) ? active_cnt : 1; idle_cnt = (is_cam) ? active_cnt : 1;
break; break;
case IWL_MIMO_PS_INVALID: case WLAN_HT_CAP_SM_PS_INVALID:
default: default:
IWL_ERROR("invalide mimo ps mode %d\n", priv->ps_mode); IWL_ERROR("invalide mimo ps mode %d\n",
priv->current_ht_config.sm_ps);
WARN_ON(1); WARN_ON(1);
idle_cnt = -1; idle_cnt = -1;
break; break;
@ -738,6 +740,17 @@ static int iwl_get_idle_rx_chain_count(struct iwl_priv *priv, int active_cnt)
return idle_cnt; return idle_cnt;
} }
/* up to 4 chains */
static u8 iwl_count_chain_bitmap(u32 chain_bitmap)
{
u8 res;
res = (chain_bitmap & BIT(0)) >> 0;
res += (chain_bitmap & BIT(1)) >> 1;
res += (chain_bitmap & BIT(2)) >> 2;
res += (chain_bitmap & BIT(4)) >> 4;
return res;
}
/** /**
* iwl_set_rxon_chain - Set up Rx chain usage in "staging" RXON image * iwl_set_rxon_chain - Set up Rx chain usage in "staging" RXON image
* *
@ -748,25 +761,35 @@ void iwl_set_rxon_chain(struct iwl_priv *priv)
{ {
bool is_single = is_single_rx_stream(priv); bool is_single = is_single_rx_stream(priv);
bool is_cam = !test_bit(STATUS_POWER_PMI, &priv->status); bool is_cam = !test_bit(STATUS_POWER_PMI, &priv->status);
u8 idle_rx_cnt, active_rx_cnt; u8 idle_rx_cnt, active_rx_cnt, valid_rx_cnt;
u32 active_chains;
u16 rx_chain; u16 rx_chain;
/* Tell uCode which antennas are actually connected. /* Tell uCode which antennas are actually connected.
* Before first association, we assume all antennas are connected. * Before first association, we assume all antennas are connected.
* Just after first association, iwl_chain_noise_calibration() * Just after first association, iwl_chain_noise_calibration()
* checks which antennas actually *are* connected. */ * checks which antennas actually *are* connected. */
rx_chain = priv->hw_params.valid_rx_ant << RXON_RX_CHAIN_VALID_POS; if (priv->chain_noise_data.active_chains)
active_chains = priv->chain_noise_data.active_chains;
else
active_chains = priv->hw_params.valid_rx_ant;
rx_chain = active_chains << RXON_RX_CHAIN_VALID_POS;
/* How many receivers should we use? */ /* How many receivers should we use? */
active_rx_cnt = iwl_get_active_rx_chain_count(priv); active_rx_cnt = iwl_get_active_rx_chain_count(priv);
idle_rx_cnt = iwl_get_idle_rx_chain_count(priv, active_rx_cnt); idle_rx_cnt = iwl_get_idle_rx_chain_count(priv, active_rx_cnt);
/* correct rx chain count accoridng hw settings */
if (priv->hw_params.rx_chains_num < active_rx_cnt)
active_rx_cnt = priv->hw_params.rx_chains_num;
if (priv->hw_params.rx_chains_num < idle_rx_cnt) /* correct rx chain count according hw settings
idle_rx_cnt = priv->hw_params.rx_chains_num; * and chain noise calibration
*/
valid_rx_cnt = iwl_count_chain_bitmap(active_chains);
if (valid_rx_cnt < active_rx_cnt)
active_rx_cnt = valid_rx_cnt;
if (valid_rx_cnt < idle_rx_cnt)
idle_rx_cnt = valid_rx_cnt;
rx_chain |= active_rx_cnt << RXON_RX_CHAIN_MIMO_CNT_POS; rx_chain |= active_rx_cnt << RXON_RX_CHAIN_MIMO_CNT_POS;
rx_chain |= idle_rx_cnt << RXON_RX_CHAIN_CNT_POS; rx_chain |= idle_rx_cnt << RXON_RX_CHAIN_CNT_POS;
@ -778,7 +801,7 @@ void iwl_set_rxon_chain(struct iwl_priv *priv)
else else
priv->staging_rxon.rx_chain &= ~RXON_RX_CHAIN_MIMO_FORCE_MSK; priv->staging_rxon.rx_chain &= ~RXON_RX_CHAIN_MIMO_FORCE_MSK;
IWL_DEBUG_ASSOC("rx_chain=0x%Xi active=%d idle=%d\n", IWL_DEBUG_ASSOC("rx_chain=0x%X active=%d idle=%d\n",
priv->staging_rxon.rx_chain, priv->staging_rxon.rx_chain,
active_rx_cnt, idle_rx_cnt); active_rx_cnt, idle_rx_cnt);
@ -912,7 +935,7 @@ int iwl_init_drv(struct iwl_priv *priv)
priv->iw_mode = IEEE80211_IF_TYPE_STA; priv->iw_mode = IEEE80211_IF_TYPE_STA;
priv->use_ant_b_for_management_frame = 1; /* start with ant B */ priv->use_ant_b_for_management_frame = 1; /* start with ant B */
priv->ps_mode = IWL_MIMO_PS_NONE; priv->current_ht_config.sm_ps = WLAN_HT_CAP_SM_PS_DISABLED;
/* Choose which receivers/antennas to use */ /* Choose which receivers/antennas to use */
iwl_set_rxon_chain(priv); iwl_set_rxon_chain(priv);
@ -1135,7 +1158,6 @@ int iwl_verify_ucode(struct iwl_priv *priv)
} }
EXPORT_SYMBOL(iwl_verify_ucode); EXPORT_SYMBOL(iwl_verify_ucode);
static const char *desc_lookup(int i) static const char *desc_lookup(int i)
{ {
switch (i) { switch (i) {
@ -1216,9 +1238,9 @@ EXPORT_SYMBOL(iwl_dump_nic_error_log);
/** /**
* iwl_print_event_log - Dump error event log to syslog * iwl_print_event_log - Dump error event log to syslog
* *
* NOTE: Must be called with iwl4965_grab_nic_access() already obtained! * NOTE: Must be called with iwl_grab_nic_access() already obtained!
*/ */
void iwl_print_event_log(struct iwl_priv *priv, u32 start_idx, static void iwl_print_event_log(struct iwl_priv *priv, u32 start_idx,
u32 num_events, u32 mode) u32 num_events, u32 mode)
{ {
u32 i; u32 i;
@ -1259,8 +1281,6 @@ void iwl_print_event_log(struct iwl_priv *priv, u32 start_idx,
} }
} }
} }
EXPORT_SYMBOL(iwl_print_event_log);
void iwl_dump_nic_event_log(struct iwl_priv *priv) void iwl_dump_nic_event_log(struct iwl_priv *priv)
{ {

View File

@ -184,7 +184,6 @@ struct iwl_cfg {
struct ieee80211_hw *iwl_alloc_all(struct iwl_cfg *cfg, struct ieee80211_hw *iwl_alloc_all(struct iwl_cfg *cfg,
struct ieee80211_ops *hw_ops); struct ieee80211_ops *hw_ops);
void iwl_hw_detect(struct iwl_priv *priv); void iwl_hw_detect(struct iwl_priv *priv);
void iwl_clear_stations_table(struct iwl_priv *priv); void iwl_clear_stations_table(struct iwl_priv *priv);
void iwl_reset_qos(struct iwl_priv *priv); void iwl_reset_qos(struct iwl_priv *priv);
void iwl_set_rxon_chain(struct iwl_priv *priv); void iwl_set_rxon_chain(struct iwl_priv *priv);
@ -215,7 +214,6 @@ void iwl_rx_replenish(struct iwl_priv *priv);
int iwl_rx_init(struct iwl_priv *priv, struct iwl_rx_queue *rxq); int iwl_rx_init(struct iwl_priv *priv, struct iwl_rx_queue *rxq);
int iwl_rx_agg_start(struct iwl_priv *priv, const u8 *addr, int tid, u16 ssn); int iwl_rx_agg_start(struct iwl_priv *priv, const u8 *addr, int tid, u16 ssn);
int iwl_rx_agg_stop(struct iwl_priv *priv, const u8 *addr, int tid); int iwl_rx_agg_stop(struct iwl_priv *priv, const u8 *addr, int tid);
/* FIXME: remove when TX is moved to iwl core */
int iwl_rx_queue_restock(struct iwl_priv *priv); int iwl_rx_queue_restock(struct iwl_priv *priv);
int iwl_rx_queue_space(const struct iwl_rx_queue *q); int iwl_rx_queue_space(const struct iwl_rx_queue *q);
void iwl_rx_allocate(struct iwl_priv *priv); void iwl_rx_allocate(struct iwl_priv *priv);
@ -234,11 +232,7 @@ void iwl_rx_statistics(struct iwl_priv *priv,
******************************************************/ ******************************************************/
int iwl_txq_ctx_reset(struct iwl_priv *priv); int iwl_txq_ctx_reset(struct iwl_priv *priv);
int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb); int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb);
/* FIXME: remove when free Tx is fully merged into iwlcore */
int iwl_hw_txq_free_tfd(struct iwl_priv *priv, struct iwl_tx_queue *txq);
void iwl_hw_txq_ctx_free(struct iwl_priv *priv); void iwl_hw_txq_ctx_free(struct iwl_priv *priv);
int iwl_hw_txq_attach_buf_to_tfd(struct iwl_priv *priv, void *tfd,
dma_addr_t addr, u16 len);
int iwl_txq_update_write_ptr(struct iwl_priv *priv, struct iwl_tx_queue *txq); int iwl_txq_update_write_ptr(struct iwl_priv *priv, struct iwl_tx_queue *txq);
int iwl_tx_agg_start(struct iwl_priv *priv, const u8 *ra, u16 tid, u16 *ssn); int iwl_tx_agg_start(struct iwl_priv *priv, const u8 *ra, u16 tid, u16 *ssn);
int iwl_tx_agg_stop(struct iwl_priv *priv , const u8 *ra, u16 tid); int iwl_tx_agg_stop(struct iwl_priv *priv , const u8 *ra, u16 tid);
@ -253,6 +247,7 @@ int iwl_set_tx_power(struct iwl_priv *priv, s8 tx_power, bool force);
* RF -Kill - here and not in iwl-rfkill.h to be available when * RF -Kill - here and not in iwl-rfkill.h to be available when
* RF-kill subsystem is not compiled. * RF-kill subsystem is not compiled.
****************************************************/ ****************************************************/
void iwl_rf_kill(struct iwl_priv *priv);
void iwl_radio_kill_sw_disable_radio(struct iwl_priv *priv); void iwl_radio_kill_sw_disable_radio(struct iwl_priv *priv);
int iwl_radio_kill_sw_enable_radio(struct iwl_priv *priv); int iwl_radio_kill_sw_enable_radio(struct iwl_priv *priv);
@ -283,7 +278,6 @@ static inline __le32 iwl_hw_set_rate_n_flags(u8 rate, u32 flags)
void iwl_init_scan_params(struct iwl_priv *priv); void iwl_init_scan_params(struct iwl_priv *priv);
int iwl_scan_cancel(struct iwl_priv *priv); int iwl_scan_cancel(struct iwl_priv *priv);
int iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms); int iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms);
const char *iwl_escape_essid(const char *essid, u8 essid_len);
int iwl_scan_initiate(struct iwl_priv *priv); int iwl_scan_initiate(struct iwl_priv *priv);
void iwl_setup_rx_scan_handlers(struct iwl_priv *priv); void iwl_setup_rx_scan_handlers(struct iwl_priv *priv);
void iwl_setup_scan_deferred_work(struct iwl_priv *priv); void iwl_setup_scan_deferred_work(struct iwl_priv *priv);
@ -316,8 +310,6 @@ int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd);
/***************************************************** /*****************************************************
* Error Handling Debugging * Error Handling Debugging
******************************************************/ ******************************************************/
void iwl_print_event_log(struct iwl_priv *priv, u32 start_idx,
u32 num_events, u32 mode);
void iwl_dump_nic_error_log(struct iwl_priv *priv); void iwl_dump_nic_error_log(struct iwl_priv *priv);
void iwl_dump_nic_event_log(struct iwl_priv *priv); void iwl_dump_nic_event_log(struct iwl_priv *priv);

View File

@ -406,6 +406,7 @@ struct iwl_ht_info {
/* self configuration data */ /* self configuration data */
u8 is_ht; u8 is_ht;
u8 supported_chan_width; u8 supported_chan_width;
u8 sm_ps;
u8 is_green_field; u8 is_green_field;
u8 sgf; /* HT_SHORT_GI_* short guard interval */ u8 sgf; /* HT_SHORT_GI_* short guard interval */
u8 max_amsdu_size; u8 max_amsdu_size;
@ -564,50 +565,31 @@ struct iwl_hw_params {
#define IWL_RX_STATS(x) (&x->u.rx_frame.stats) #define IWL_RX_STATS(x) (&x->u.rx_frame.stats)
#define IWL_RX_DATA(x) (IWL_RX_HDR(x)->payload) #define IWL_RX_DATA(x) (IWL_RX_HDR(x)->payload)
/****************************************************************************** /******************************************************************************
* *
* Functions implemented in iwl-base.c which are forward declared here * Functions implemented in core module which are forward declared here
* for use by iwl-*.c * for use by iwl-[4-5].c
* *
*****************************************************************************/ * NOTE: The implementation of these functions are not hardware specific
struct iwl_addsta_cmd; * which is why they are in the core module files.
extern int iwl_send_add_sta(struct iwl_priv *priv,
struct iwl_addsta_cmd *sta, u8 flags);
u8 iwl_add_station_flags(struct iwl_priv *priv, const u8 *addr, int is_ap,
u8 flags, struct ieee80211_ht_info *ht_info);
extern unsigned int iwl4965_fill_beacon_frame(struct iwl_priv *priv,
struct ieee80211_hdr *hdr,
const u8 *dest, int left);
extern void iwl4965_update_chain_flags(struct iwl_priv *priv);
int iwl4965_set_pwr_src(struct iwl_priv *priv, enum iwl_pwr_src src);
extern int iwl4965_set_power(struct iwl_priv *priv, void *cmd);
extern const u8 iwl_bcast_addr[ETH_ALEN];
/******************************************************************************
*
* Functions implemented in iwl-[34]*.c which are forward declared here
* for use by iwl-base.c
*
* NOTE: The implementation of these functions are hardware specific
* which is why they are in the hardware specific files (vs. iwl-base.c)
* *
* Naming convention -- * Naming convention --
* iwl4965_ <-- Its part of iwlwifi (should be changed to iwl4965_) * iwl_ <-- Is part of iwlwifi
* iwl4965_hw_ <-- Hardware specific (implemented in iwl-XXXX.c by all HW)
* iwlXXXX_ <-- Hardware specific (implemented in iwl-XXXX.c for XXXX) * iwlXXXX_ <-- Hardware specific (implemented in iwl-XXXX.c for XXXX)
* iwl4965_bg_ <-- Called from work queue context * iwl4965_bg_ <-- Called from work queue context
* iwl4965_mac_ <-- mac80211 callback * iwl4965_mac_ <-- mac80211 callback
* *
****************************************************************************/ ****************************************************************************/
struct iwl_addsta_cmd;
extern int iwl_send_add_sta(struct iwl_priv *priv,
struct iwl_addsta_cmd *sta, u8 flags);
extern u8 iwl_add_station_flags(struct iwl_priv *priv, const u8 *addr,
int is_ap, u8 flags, struct ieee80211_ht_info *ht_info);
extern void iwl4965_update_chain_flags(struct iwl_priv *priv);
extern int iwl4965_set_pwr_src(struct iwl_priv *priv, enum iwl_pwr_src src);
extern const u8 iwl_bcast_addr[ETH_ALEN];
extern int iwl_rxq_stop(struct iwl_priv *priv); extern int iwl_rxq_stop(struct iwl_priv *priv);
extern void iwl_txq_ctx_stop(struct iwl_priv *priv); extern void iwl_txq_ctx_stop(struct iwl_priv *priv);
extern unsigned int iwl4965_hw_get_beacon_cmd(struct iwl_priv *priv,
struct iwl_frame *frame, u8 rate);
extern void iwl4965_disable_events(struct iwl_priv *priv);
extern int iwl4965_hw_channel_switch(struct iwl_priv *priv, u16 channel);
extern int iwl_queue_space(const struct iwl_queue *q); extern int iwl_queue_space(const struct iwl_queue *q);
static inline int iwl_queue_used(const struct iwl_queue *q, int i) static inline int iwl_queue_used(const struct iwl_queue *q, int i)
{ {
@ -644,11 +626,6 @@ struct iwl_kw {
#define IWL_CHANNEL_WIDTH_20MHZ 0 #define IWL_CHANNEL_WIDTH_20MHZ 0
#define IWL_CHANNEL_WIDTH_40MHZ 1 #define IWL_CHANNEL_WIDTH_40MHZ 1
#define IWL_MIMO_PS_STATIC 0
#define IWL_MIMO_PS_NONE 3
#define IWL_MIMO_PS_DYNAMIC 1
#define IWL_MIMO_PS_INVALID 2
#define IWL_OPERATION_MODE_AUTO 0 #define IWL_OPERATION_MODE_AUTO 0
#define IWL_OPERATION_MODE_HT_ONLY 1 #define IWL_OPERATION_MODE_HT_ONLY 1
#define IWL_OPERATION_MODE_MIXED 2 #define IWL_OPERATION_MODE_MIXED 2
@ -703,8 +680,9 @@ enum iwl4965_false_alarm_state {
enum iwl4965_chain_noise_state { enum iwl4965_chain_noise_state {
IWL_CHAIN_NOISE_ALIVE = 0, /* must be 0 */ IWL_CHAIN_NOISE_ALIVE = 0, /* must be 0 */
IWL_CHAIN_NOISE_ACCUMULATE = 1, IWL_CHAIN_NOISE_ACCUMULATE,
IWL_CHAIN_NOISE_CALIBRATED = 2, IWL_CHAIN_NOISE_CALIBRATED,
IWL_CHAIN_NOISE_DONE,
}; };
enum iwl4965_calib_enabled_state { enum iwl4965_calib_enabled_state {
@ -762,17 +740,18 @@ struct iwl_sensitivity_data {
/* Chain noise (differential Rx gain) calib data */ /* Chain noise (differential Rx gain) calib data */
struct iwl_chain_noise_data { struct iwl_chain_noise_data {
u8 state; u32 active_chains;
u16 beacon_count;
u32 chain_noise_a; u32 chain_noise_a;
u32 chain_noise_b; u32 chain_noise_b;
u32 chain_noise_c; u32 chain_noise_c;
u32 chain_signal_a; u32 chain_signal_a;
u32 chain_signal_b; u32 chain_signal_b;
u32 chain_signal_c; u32 chain_signal_c;
u16 beacon_count;
u8 disconn_array[NUM_RX_CHAINS]; u8 disconn_array[NUM_RX_CHAINS];
u8 delta_gain_code[NUM_RX_CHAINS]; u8 delta_gain_code[NUM_RX_CHAINS];
u8 radio_write; u8 radio_write;
u8 state;
}; };
#define EEPROM_SEM_TIMEOUT 10 /* milliseconds */ #define EEPROM_SEM_TIMEOUT 10 /* milliseconds */
@ -995,7 +974,6 @@ struct iwl_priv {
* hardware */ * hardware */
u16 assoc_id; u16 assoc_id;
u16 assoc_capability; u16 assoc_capability;
u8 ps_mode;
struct iwl_qos_info qos_data; struct iwl_qos_info qos_data;

View File

@ -250,17 +250,26 @@ static int iwl_update_power_command(struct iwl_priv *priv,
/* /*
* calucaute the final power mode index * compute the final power mode index
*/ */
int iwl_power_update_mode(struct iwl_priv *priv, u8 refresh) int iwl_power_update_mode(struct iwl_priv *priv, bool force)
{ {
struct iwl_power_mgr *setting = &(priv->power_data); struct iwl_power_mgr *setting = &(priv->power_data);
int ret = 0; int ret = 0;
u16 uninitialized_var(final_mode); u16 uninitialized_var(final_mode);
/* If on battery, set to 3, /* Don't update the RX chain when chain noise calibration is running */
* if plugged into AC power, set to CAM ("continuously aware mode"), if (priv->chain_noise_data.state != IWL_CHAIN_NOISE_DONE &&
* else user level */ priv->chain_noise_data.state != IWL_CHAIN_NOISE_ALIVE) {
IWL_DEBUG_POWER("Cannot update the power, chain noise "
"calibration running: %d\n",
priv->chain_noise_data.state);
return -EAGAIN;
}
/* If on battery, set to 3,
* if plugged into AC power, set to CAM ("continuously aware mode"),
* else user level */
switch (setting->system_power_setting) { switch (setting->system_power_setting) {
case IWL_POWER_SYS_AUTO: case IWL_POWER_SYS_AUTO:
@ -285,7 +294,7 @@ int iwl_power_update_mode(struct iwl_priv *priv, u8 refresh)
final_mode = IWL_POWER_MODE_CAM; final_mode = IWL_POWER_MODE_CAM;
if (!iwl_is_rfkill(priv) && !setting->power_disabled && if (!iwl_is_rfkill(priv) && !setting->power_disabled &&
((setting->power_mode != final_mode) || refresh)) { ((setting->power_mode != final_mode) || force)) {
struct iwl_powertable_cmd cmd; struct iwl_powertable_cmd cmd;
if (final_mode != IWL_POWER_MODE_CAM) if (final_mode != IWL_POWER_MODE_CAM)
@ -359,35 +368,26 @@ EXPORT_SYMBOL(iwl_power_enable_management);
/* set user_power_setting */ /* set user_power_setting */
int iwl_power_set_user_mode(struct iwl_priv *priv, u16 mode) int iwl_power_set_user_mode(struct iwl_priv *priv, u16 mode)
{ {
int ret = 0;
if (mode > IWL_POWER_LIMIT) if (mode > IWL_POWER_LIMIT)
return -EINVAL; return -EINVAL;
priv->power_data.user_power_setting = mode; priv->power_data.user_power_setting = mode;
ret = iwl_power_update_mode(priv, 0); return iwl_power_update_mode(priv, 0);
return ret;
} }
EXPORT_SYMBOL(iwl_power_set_user_mode); EXPORT_SYMBOL(iwl_power_set_user_mode);
/* set system_power_setting. This should be set by over all /* set system_power_setting. This should be set by over all
* PM application. * PM application.
*/ */
int iwl_power_set_system_mode(struct iwl_priv *priv, u16 mode) int iwl_power_set_system_mode(struct iwl_priv *priv, u16 mode)
{ {
int ret = 0;
if (mode > IWL_POWER_LIMIT) if (mode > IWL_POWER_LIMIT)
return -EINVAL; return -EINVAL;
priv->power_data.system_power_setting = mode; priv->power_data.system_power_setting = mode;
ret = iwl_power_update_mode(priv, 0); return iwl_power_update_mode(priv, 0);
return ret;
} }
EXPORT_SYMBOL(iwl_power_set_system_mode); EXPORT_SYMBOL(iwl_power_set_system_mode);

View File

@ -72,7 +72,7 @@ struct iwl_power_mgr {
/* final power level that used to calculate final power command */ /* final power level that used to calculate final power command */
u8 power_mode; u8 power_mode;
u8 user_power_setting; /* set by user through mac80211 or sysfs */ u8 user_power_setting; /* set by user through mac80211 or sysfs */
u8 system_power_setting; /* set by kernel syatem tools */ u8 system_power_setting; /* set by kernel system tools */
u8 critical_power_setting; /* set if driver over heated */ u8 critical_power_setting; /* set if driver over heated */
u8 is_battery_active; /* DC/AC power */ u8 is_battery_active; /* DC/AC power */
u8 power_disabled; /* flag to disable using power saving level */ u8 power_disabled; /* flag to disable using power saving level */
@ -80,7 +80,7 @@ struct iwl_power_mgr {
void iwl_setup_power_deferred_work(struct iwl_priv *priv); void iwl_setup_power_deferred_work(struct iwl_priv *priv);
void iwl_power_cancel_timeout(struct iwl_priv *priv); void iwl_power_cancel_timeout(struct iwl_priv *priv);
int iwl_power_update_mode(struct iwl_priv *priv, u8 refresh); int iwl_power_update_mode(struct iwl_priv *priv, bool force);
int iwl_power_disable_management(struct iwl_priv *priv, u32 ms); int iwl_power_disable_management(struct iwl_priv *priv, u32 ms);
int iwl_power_enable_management(struct iwl_priv *priv); int iwl_power_enable_management(struct iwl_priv *priv);
int iwl_power_set_user_mode(struct iwl_priv *priv, u16 mode); int iwl_power_set_user_mode(struct iwl_priv *priv, u16 mode);

View File

@ -88,7 +88,7 @@ static int iwl_is_empty_essid(const char *essid, int essid_len)
const char *iwl_escape_essid(const char *essid, u8 essid_len) static const char *iwl_escape_essid(const char *essid, u8 essid_len)
{ {
static char escaped[IW_ESSID_MAX_SIZE * 2 + 1]; static char escaped[IW_ESSID_MAX_SIZE * 2 + 1];
const char *s = essid; const char *s = essid;
@ -111,7 +111,6 @@ const char *iwl_escape_essid(const char *essid, u8 essid_len)
*d = '\0'; *d = '\0';
return escaped; return escaped;
} }
EXPORT_SYMBOL(iwl_escape_essid);
/** /**
* iwl_scan_cancel - Cancel any currently executing HW scan * iwl_scan_cancel - Cancel any currently executing HW scan

View File

@ -191,20 +191,20 @@ static void iwl_set_ht_add_station(struct iwl_priv *priv, u8 index,
if (!sta_ht_inf || !sta_ht_inf->ht_supported) if (!sta_ht_inf || !sta_ht_inf->ht_supported)
goto done; goto done;
mimo_ps_mode = (sta_ht_inf->cap & IEEE80211_HT_CAP_MIMO_PS) >> 2; mimo_ps_mode = (sta_ht_inf->cap & IEEE80211_HT_CAP_SM_PS) >> 2;
sta_flags = priv->stations[index].sta.station_flags; sta_flags = priv->stations[index].sta.station_flags;
sta_flags &= ~(STA_FLG_RTS_MIMO_PROT_MSK | STA_FLG_MIMO_DIS_MSK); sta_flags &= ~(STA_FLG_RTS_MIMO_PROT_MSK | STA_FLG_MIMO_DIS_MSK);
switch (mimo_ps_mode) { switch (mimo_ps_mode) {
case WLAN_HT_CAP_MIMO_PS_STATIC: case WLAN_HT_CAP_SM_PS_STATIC:
sta_flags |= STA_FLG_MIMO_DIS_MSK; sta_flags |= STA_FLG_MIMO_DIS_MSK;
break; break;
case WLAN_HT_CAP_MIMO_PS_DYNAMIC: case WLAN_HT_CAP_SM_PS_DYNAMIC:
sta_flags |= STA_FLG_RTS_MIMO_PROT_MSK; sta_flags |= STA_FLG_RTS_MIMO_PROT_MSK;
break; break;
case WLAN_HT_CAP_MIMO_PS_DISABLED: case WLAN_HT_CAP_SM_PS_DISABLED:
break; break;
default: default:
IWL_WARNING("Invalid MIMO PS mode %d\n", mimo_ps_mode); IWL_WARNING("Invalid MIMO PS mode %d\n", mimo_ps_mode);

View File

@ -63,7 +63,7 @@ static const u16 default_tid_to_tx_fifo[] = {
* Does NOT advance any TFD circular buffer read/write indexes * Does NOT advance any TFD circular buffer read/write indexes
* Does NOT free the TFD itself (which is within circular buffer) * Does NOT free the TFD itself (which is within circular buffer)
*/ */
int iwl_hw_txq_free_tfd(struct iwl_priv *priv, struct iwl_tx_queue *txq) static int iwl_hw_txq_free_tfd(struct iwl_priv *priv, struct iwl_tx_queue *txq)
{ {
struct iwl_tfd_frame *bd_tmp = (struct iwl_tfd_frame *)&txq->bd[0]; struct iwl_tfd_frame *bd_tmp = (struct iwl_tfd_frame *)&txq->bd[0];
struct iwl_tfd_frame *bd = &bd_tmp[txq->q.read_ptr]; struct iwl_tfd_frame *bd = &bd_tmp[txq->q.read_ptr];
@ -115,10 +115,8 @@ int iwl_hw_txq_free_tfd(struct iwl_priv *priv, struct iwl_tx_queue *txq)
} }
return 0; return 0;
} }
EXPORT_SYMBOL(iwl_hw_txq_free_tfd);
static int iwl_hw_txq_attach_buf_to_tfd(struct iwl_priv *priv, void *ptr,
int iwl_hw_txq_attach_buf_to_tfd(struct iwl_priv *priv, void *ptr,
dma_addr_t addr, u16 len) dma_addr_t addr, u16 len)
{ {
int index, is_odd; int index, is_odd;
@ -151,7 +149,6 @@ int iwl_hw_txq_attach_buf_to_tfd(struct iwl_priv *priv, void *ptr,
return 0; return 0;
} }
EXPORT_SYMBOL(iwl_hw_txq_attach_buf_to_tfd);
/** /**
* iwl_txq_update_write_ptr - Send new write index to hardware * iwl_txq_update_write_ptr - Send new write index to hardware
@ -478,7 +475,6 @@ void iwl_hw_txq_ctx_free(struct iwl_priv *priv)
} }
EXPORT_SYMBOL(iwl_hw_txq_ctx_free); EXPORT_SYMBOL(iwl_hw_txq_ctx_free);
/** /**
* iwl_txq_ctx_reset - Reset TX queue context * iwl_txq_ctx_reset - Reset TX queue context
* Destroys all DMA structures and initialise them again * Destroys all DMA structures and initialise them again
@ -545,6 +541,7 @@ int iwl_txq_ctx_reset(struct iwl_priv *priv)
error_kw: error_kw:
return ret; return ret;
} }
/** /**
* iwl_txq_ctx_stop - Stop all Tx DMA channels, free Tx queue memory * iwl_txq_ctx_stop - Stop all Tx DMA channels, free Tx queue memory
*/ */

View File

@ -1971,6 +1971,70 @@ void lbs_ps_confirm_sleep(struct lbs_private *priv)
} }
/**
* @brief Configures the transmission power control functionality.
*
* @param priv A pointer to struct lbs_private structure
* @param enable Transmission power control enable
* @param p0 Power level when link quality is good (dBm).
* @param p1 Power level when link quality is fair (dBm).
* @param p2 Power level when link quality is poor (dBm).
* @param usesnr Use Signal to Noise Ratio in TPC
*
* @return 0 on success
*/
int lbs_set_tpc_cfg(struct lbs_private *priv, int enable, int8_t p0, int8_t p1,
int8_t p2, int usesnr)
{
struct cmd_ds_802_11_tpc_cfg cmd;
int ret;
memset(&cmd, 0, sizeof(cmd));
cmd.hdr.size = cpu_to_le16(sizeof(cmd));
cmd.action = cpu_to_le16(CMD_ACT_SET);
cmd.enable = !!enable;
cmd.usesnr = !!enable;
cmd.P0 = p0;
cmd.P1 = p1;
cmd.P2 = p2;
ret = lbs_cmd_with_response(priv, CMD_802_11_TPC_CFG, &cmd);
return ret;
}
/**
* @brief Configures the power adaptation settings.
*
* @param priv A pointer to struct lbs_private structure
* @param enable Power adaptation enable
* @param p0 Power level for 1, 2, 5.5 and 11 Mbps (dBm).
* @param p1 Power level for 6, 9, 12, 18, 22, 24 and 36 Mbps (dBm).
* @param p2 Power level for 48 and 54 Mbps (dBm).
*
* @return 0 on Success
*/
int lbs_set_power_adapt_cfg(struct lbs_private *priv, int enable, int8_t p0,
int8_t p1, int8_t p2)
{
struct cmd_ds_802_11_pa_cfg cmd;
int ret;
memset(&cmd, 0, sizeof(cmd));
cmd.hdr.size = cpu_to_le16(sizeof(cmd));
cmd.action = cpu_to_le16(CMD_ACT_SET);
cmd.enable = !!enable;
cmd.P0 = p0;
cmd.P1 = p1;
cmd.P2 = p2;
ret = lbs_cmd_with_response(priv, CMD_802_11_PA_CFG , &cmd);
return ret;
}
static struct cmd_ctrl_node *__lbs_cmd_async(struct lbs_private *priv, static struct cmd_ctrl_node *__lbs_cmd_async(struct lbs_private *priv,
uint16_t command, struct cmd_header *in_cmd, int in_cmd_size, uint16_t command, struct cmd_header *in_cmd, int in_cmd_size,
int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *), int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *),

View File

@ -26,6 +26,12 @@ int __lbs_cmd(struct lbs_private *priv, uint16_t command,
int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *), int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *),
unsigned long callback_arg); unsigned long callback_arg);
int lbs_set_power_adapt_cfg(struct lbs_private *priv, int enable, int8_t p0,
int8_t p1, int8_t p2);
int lbs_set_tpc_cfg(struct lbs_private *priv, int enable, int8_t p0, int8_t p1,
int8_t p2, int usesnr);
int lbs_cmd_copyback(struct lbs_private *priv, unsigned long extra, int lbs_cmd_copyback(struct lbs_private *priv, unsigned long extra,
struct cmd_header *resp); struct cmd_header *resp);

View File

@ -189,6 +189,15 @@ static inline void lbs_deb_hex(unsigned int grp, const char *prompt, u8 *buf, in
#define MRVDRV_CMD_UPLD_RDY 0x0008 #define MRVDRV_CMD_UPLD_RDY 0x0008
#define MRVDRV_CARDEVENT 0x0010 #define MRVDRV_CARDEVENT 0x0010
/* Automatic TX control default levels */
#define POW_ADAPT_DEFAULT_P0 13
#define POW_ADAPT_DEFAULT_P1 15
#define POW_ADAPT_DEFAULT_P2 18
#define TPC_DEFAULT_P0 5
#define TPC_DEFAULT_P1 10
#define TPC_DEFAULT_P2 13
/** TxPD status */ /** TxPD status */
/* Station firmware use TxPD status field to report final Tx transmit /* Station firmware use TxPD status field to report final Tx transmit

View File

@ -83,6 +83,7 @@
#define CMD_802_11_INACTIVITY_TIMEOUT 0x0067 #define CMD_802_11_INACTIVITY_TIMEOUT 0x0067
#define CMD_802_11_SLEEP_PERIOD 0x0068 #define CMD_802_11_SLEEP_PERIOD 0x0068
#define CMD_802_11_TPC_CFG 0x0072 #define CMD_802_11_TPC_CFG 0x0072
#define CMD_802_11_PA_CFG 0x0073
#define CMD_802_11_FW_WAKE_METHOD 0x0074 #define CMD_802_11_FW_WAKE_METHOD 0x0074
#define CMD_802_11_SUBSCRIBE_EVENT 0x0075 #define CMD_802_11_SUBSCRIBE_EVENT 0x0075
#define CMD_802_11_RATE_ADAPT_RATESET 0x0076 #define CMD_802_11_RATE_ADAPT_RATESET 0x0076

View File

@ -607,14 +607,28 @@ struct cmd_ds_802_11_eeprom_access {
} __attribute__ ((packed)); } __attribute__ ((packed));
struct cmd_ds_802_11_tpc_cfg { struct cmd_ds_802_11_tpc_cfg {
struct cmd_header hdr;
__le16 action; __le16 action;
u8 enable; uint8_t enable;
s8 P0; int8_t P0;
s8 P1; int8_t P1;
s8 P2; int8_t P2;
u8 usesnr; uint8_t usesnr;
} __attribute__ ((packed)); } __attribute__ ((packed));
struct cmd_ds_802_11_pa_cfg {
struct cmd_header hdr;
__le16 action;
uint8_t enable;
int8_t P0;
int8_t P1;
int8_t P2;
} __attribute__ ((packed));
struct cmd_ds_802_11_led_ctrl { struct cmd_ds_802_11_led_ctrl {
__le16 action; __le16 action;
__le16 numled; __le16 numled;

View File

@ -1205,7 +1205,13 @@ void lbs_remove_card(struct lbs_private *priv)
cancel_delayed_work_sync(&priv->scan_work); cancel_delayed_work_sync(&priv->scan_work);
cancel_delayed_work_sync(&priv->assoc_work); cancel_delayed_work_sync(&priv->assoc_work);
cancel_work_sync(&priv->mcast_work); cancel_work_sync(&priv->mcast_work);
/* worker thread destruction blocks on the in-flight command which
* should have been cleared already in lbs_stop_card().
*/
lbs_deb_main("destroying worker thread\n");
destroy_workqueue(priv->work_thread); destroy_workqueue(priv->work_thread);
lbs_deb_main("done destroying worker thread\n");
if (priv->psmode == LBS802_11POWERMODEMAX_PSP) { if (priv->psmode == LBS802_11POWERMODEMAX_PSP) {
priv->psmode = LBS802_11POWERMODECAM; priv->psmode = LBS802_11POWERMODECAM;
@ -1323,14 +1329,26 @@ void lbs_stop_card(struct lbs_private *priv)
device_remove_file(&dev->dev, &dev_attr_lbs_rtap); device_remove_file(&dev->dev, &dev_attr_lbs_rtap);
} }
/* Flush pending command nodes */ /* Delete the timeout of the currently processing command */
del_timer_sync(&priv->command_timer); del_timer_sync(&priv->command_timer);
/* Flush pending command nodes */
spin_lock_irqsave(&priv->driver_lock, flags); spin_lock_irqsave(&priv->driver_lock, flags);
lbs_deb_main("clearing pending commands\n");
list_for_each_entry(cmdnode, &priv->cmdpendingq, list) { list_for_each_entry(cmdnode, &priv->cmdpendingq, list) {
cmdnode->result = -ENOENT; cmdnode->result = -ENOENT;
cmdnode->cmdwaitqwoken = 1; cmdnode->cmdwaitqwoken = 1;
wake_up_interruptible(&cmdnode->cmdwait_q); wake_up_interruptible(&cmdnode->cmdwait_q);
} }
/* Flush the command the card is currently processing */
if (priv->cur_cmd) {
lbs_deb_main("clearing current command\n");
priv->cur_cmd->result = -ENOENT;
priv->cur_cmd->cmdwaitqwoken = 1;
wake_up_interruptible(&priv->cur_cmd->cmdwait_q);
}
lbs_deb_main("done clearing commands\n");
spin_unlock_irqrestore(&priv->driver_lock, flags); spin_unlock_irqrestore(&priv->driver_lock, flags);
unregister_netdev(dev); unregister_netdev(dev);

View File

@ -1820,7 +1820,21 @@ static int lbs_set_txpow(struct net_device *dev, struct iw_request_info *info,
} }
if (vwrq->fixed == 0) { if (vwrq->fixed == 0) {
/* Auto power control */ /* User requests automatic tx power control, however there are
* many auto tx settings. For now use firmware defaults until
* we come up with a good way to expose these to the user. */
if (priv->fwrelease < 0x09000000) {
ret = lbs_set_power_adapt_cfg(priv, 1,
POW_ADAPT_DEFAULT_P0,
POW_ADAPT_DEFAULT_P1,
POW_ADAPT_DEFAULT_P2);
if (ret)
goto out;
}
ret = lbs_set_tpc_cfg(priv, 0, TPC_DEFAULT_P0, TPC_DEFAULT_P1,
TPC_DEFAULT_P2, 1);
if (ret)
goto out;
dbm = priv->txpower_max; dbm = priv->txpower_max;
} else { } else {
/* Userspace check in iwrange if it should use dBm or mW, /* Userspace check in iwrange if it should use dBm or mW,
@ -1830,7 +1844,8 @@ static int lbs_set_txpow(struct net_device *dev, struct iw_request_info *info,
goto out; goto out;
} }
/* Validate requested power level against firmware allowed levels */ /* Validate requested power level against firmware allowed
* levels */
if (priv->txpower_min && (dbm < priv->txpower_min)) { if (priv->txpower_min && (dbm < priv->txpower_min)) {
ret = -EINVAL; ret = -EINVAL;
goto out; goto out;
@ -1840,6 +1855,18 @@ static int lbs_set_txpow(struct net_device *dev, struct iw_request_info *info,
ret = -EINVAL; ret = -EINVAL;
goto out; goto out;
} }
if (priv->fwrelease < 0x09000000) {
ret = lbs_set_power_adapt_cfg(priv, 0,
POW_ADAPT_DEFAULT_P0,
POW_ADAPT_DEFAULT_P1,
POW_ADAPT_DEFAULT_P2);
if (ret)
goto out;
}
ret = lbs_set_tpc_cfg(priv, 0, TPC_DEFAULT_P0, TPC_DEFAULT_P1,
TPC_DEFAULT_P2, 1);
if (ret)
goto out;
} }
/* If the radio was off, turn it on */ /* If the radio was off, turn it on */

View File

@ -80,6 +80,7 @@ struct p54_common {
struct pda_channel_output_limit *output_limit; struct pda_channel_output_limit *output_limit;
unsigned int output_limit_len; unsigned int output_limit_len;
struct pda_pa_curve_data *curve_data; struct pda_pa_curve_data *curve_data;
unsigned int filter_flags;
u16 rxhw; u16 rxhw;
u8 version; u8 version;
u8 rx_antenna; u8 rx_antenna;
@ -87,7 +88,15 @@ struct p54_common {
void *cached_vdcf; void *cached_vdcf;
unsigned int fw_var; unsigned int fw_var;
unsigned int fw_interface; unsigned int fw_interface;
unsigned int output_power;
u32 tsf_low32;
u32 tsf_high32;
struct ieee80211_tx_queue_stats tx_stats[8]; struct ieee80211_tx_queue_stats tx_stats[8];
struct ieee80211_low_level_stats stats;
struct timer_list stats_timer;
struct completion stats_comp;
void *cached_stats;
int noise;
void *eeprom; void *eeprom;
struct completion eeprom_comp; struct completion eeprom_comp;
}; };

View File

@ -27,7 +27,7 @@ MODULE_DESCRIPTION("Softmac Prism54 common code");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_ALIAS("prism54common"); MODULE_ALIAS("prism54common");
static struct ieee80211_rate p54_rates[] = { static struct ieee80211_rate p54_bgrates[] = {
{ .bitrate = 10, .hw_value = 0, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, { .bitrate = 10, .hw_value = 0, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
{ .bitrate = 20, .hw_value = 1, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, { .bitrate = 20, .hw_value = 1, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
{ .bitrate = 55, .hw_value = 2, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, { .bitrate = 55, .hw_value = 2, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
@ -42,7 +42,7 @@ static struct ieee80211_rate p54_rates[] = {
{ .bitrate = 540, .hw_value = 11, }, { .bitrate = 540, .hw_value = 11, },
}; };
static struct ieee80211_channel p54_channels[] = { static struct ieee80211_channel p54_bgchannels[] = {
{ .center_freq = 2412, .hw_value = 1, }, { .center_freq = 2412, .hw_value = 1, },
{ .center_freq = 2417, .hw_value = 2, }, { .center_freq = 2417, .hw_value = 2, },
{ .center_freq = 2422, .hw_value = 3, }, { .center_freq = 2422, .hw_value = 3, },
@ -60,10 +60,66 @@ static struct ieee80211_channel p54_channels[] = {
}; };
static struct ieee80211_supported_band band_2GHz = { static struct ieee80211_supported_band band_2GHz = {
.channels = p54_channels, .channels = p54_bgchannels,
.n_channels = ARRAY_SIZE(p54_channels), .n_channels = ARRAY_SIZE(p54_bgchannels),
.bitrates = p54_rates, .bitrates = p54_bgrates,
.n_bitrates = ARRAY_SIZE(p54_rates), .n_bitrates = ARRAY_SIZE(p54_bgrates),
};
static struct ieee80211_rate p54_arates[] = {
{ .bitrate = 60, .hw_value = 4, },
{ .bitrate = 90, .hw_value = 5, },
{ .bitrate = 120, .hw_value = 6, },
{ .bitrate = 180, .hw_value = 7, },
{ .bitrate = 240, .hw_value = 8, },
{ .bitrate = 360, .hw_value = 9, },
{ .bitrate = 480, .hw_value = 10, },
{ .bitrate = 540, .hw_value = 11, },
};
static struct ieee80211_channel p54_achannels[] = {
{ .center_freq = 4920 },
{ .center_freq = 4940 },
{ .center_freq = 4960 },
{ .center_freq = 4980 },
{ .center_freq = 5040 },
{ .center_freq = 5060 },
{ .center_freq = 5080 },
{ .center_freq = 5170 },
{ .center_freq = 5180 },
{ .center_freq = 5190 },
{ .center_freq = 5200 },
{ .center_freq = 5210 },
{ .center_freq = 5220 },
{ .center_freq = 5230 },
{ .center_freq = 5240 },
{ .center_freq = 5260 },
{ .center_freq = 5280 },
{ .center_freq = 5300 },
{ .center_freq = 5320 },
{ .center_freq = 5500 },
{ .center_freq = 5520 },
{ .center_freq = 5540 },
{ .center_freq = 5560 },
{ .center_freq = 5580 },
{ .center_freq = 5600 },
{ .center_freq = 5620 },
{ .center_freq = 5640 },
{ .center_freq = 5660 },
{ .center_freq = 5680 },
{ .center_freq = 5700 },
{ .center_freq = 5745 },
{ .center_freq = 5765 },
{ .center_freq = 5785 },
{ .center_freq = 5805 },
{ .center_freq = 5825 },
};
static struct ieee80211_supported_band band_5GHz = {
.channels = p54_achannels,
.n_channels = ARRAY_SIZE(p54_achannels),
.bitrates = p54_arates,
.n_bitrates = ARRAY_SIZE(p54_arates),
}; };
int p54_parse_firmware(struct ieee80211_hw *dev, const struct firmware *fw) int p54_parse_firmware(struct ieee80211_hw *dev, const struct firmware *fw)
@ -252,6 +308,7 @@ static int p54_convert_rev1(struct ieee80211_hw *dev,
const char* p54_rf_chips[] = { "NULL", "Indigo?", "Duette", const char* p54_rf_chips[] = { "NULL", "Indigo?", "Duette",
"Frisbee", "Xbow", "Longbow" }; "Frisbee", "Xbow", "Longbow" };
static int p54_init_xbow_synth(struct ieee80211_hw *dev);
int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len) int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len)
{ {
@ -371,20 +428,20 @@ int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len)
} }
switch (priv->rxhw) { switch (priv->rxhw) {
case 4: /* XBow */ case 4: /* XBow */
case 1: /* Indigo? */ p54_init_xbow_synth(dev);
case 2: /* Duette */ case 1: /* Indigo? */
/* TODO: 5GHz initialization goes here */ case 2: /* Duette */
dev->wiphy->bands[IEEE80211_BAND_5GHZ] = &band_5GHz;
case 3: /* Frisbee */ case 3: /* Frisbee */
case 5: /* Longbow */ case 5: /* Longbow */
dev->wiphy->bands[IEEE80211_BAND_2GHZ] = &band_2GHz; dev->wiphy->bands[IEEE80211_BAND_2GHZ] = &band_2GHz;
break; break;
default: default:
printk(KERN_ERR "%s: unsupported RF-Chip\n", printk(KERN_ERR "%s: unsupported RF-Chip\n",
wiphy_name(dev->wiphy)); wiphy_name(dev->wiphy));
err = -EINVAL; err = -EINVAL;
goto err; goto err;
} }
if (!is_valid_ether_addr(dev->wiphy->perm_addr)) { if (!is_valid_ether_addr(dev->wiphy->perm_addr)) {
@ -424,21 +481,43 @@ int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len)
} }
EXPORT_SYMBOL_GPL(p54_parse_eeprom); EXPORT_SYMBOL_GPL(p54_parse_eeprom);
static int p54_rssi_to_dbm(struct ieee80211_hw *dev, int rssi)
{
/* TODO: get the rssi_add & rssi_mul data from the eeprom */
return ((rssi * 0x83) / 64 - 400) / 4;
}
static int p54_rx_data(struct ieee80211_hw *dev, struct sk_buff *skb) static int p54_rx_data(struct ieee80211_hw *dev, struct sk_buff *skb)
{ {
struct p54_common *priv = dev->priv;
struct p54_rx_hdr *hdr = (struct p54_rx_hdr *) skb->data; struct p54_rx_hdr *hdr = (struct p54_rx_hdr *) skb->data;
struct ieee80211_rx_status rx_status = {0}; struct ieee80211_rx_status rx_status = {0};
u16 freq = le16_to_cpu(hdr->freq); u16 freq = le16_to_cpu(hdr->freq);
size_t header_len = sizeof(*hdr); size_t header_len = sizeof(*hdr);
u32 tsf32;
rx_status.signal = hdr->rssi; if (!(hdr->magic & cpu_to_le16(0x0001))) {
if (priv->filter_flags & FIF_FCSFAIL)
rx_status.flag |= RX_FLAG_FAILED_FCS_CRC;
else
return 0;
}
rx_status.signal = p54_rssi_to_dbm(dev, hdr->rssi);
rx_status.noise = priv->noise;
/* XX correct? */ /* XX correct? */
rx_status.qual = (100 * hdr->rssi) / 127; rx_status.qual = (100 * hdr->rssi) / 127;
rx_status.rate_idx = hdr->rate & 0xf; rx_status.rate_idx = hdr->rate & 0xf;
rx_status.freq = freq; rx_status.freq = freq;
rx_status.band = IEEE80211_BAND_2GHZ; rx_status.band = IEEE80211_BAND_2GHZ;
rx_status.antenna = hdr->antenna; rx_status.antenna = hdr->antenna;
rx_status.mactime = le64_to_cpu(hdr->timestamp);
tsf32 = le32_to_cpu(hdr->tsf32);
if (tsf32 < priv->tsf_low32)
priv->tsf_high32++;
rx_status.mactime = ((u64)priv->tsf_high32) << 32 | tsf32;
priv->tsf_low32 = tsf32;
rx_status.flag |= RX_FLAG_TSFT; rx_status.flag |= RX_FLAG_TSFT;
if (hdr->magic & cpu_to_le16(0x4000)) if (hdr->magic & cpu_to_le16(0x4000))
@ -511,7 +590,8 @@ static void p54_rx_frame_sent(struct ieee80211_hw *dev, struct sk_buff *skb)
info->status.excessive_retries = 1; info->status.excessive_retries = 1;
} }
info->status.retry_count = payload->retries - 1; info->status.retry_count = payload->retries - 1;
info->status.ack_signal = le16_to_cpu(payload->ack_rssi); info->status.ack_signal = p54_rssi_to_dbm(dev,
le16_to_cpu(payload->ack_rssi));
skb_pull(entry, sizeof(*hdr) + pad + sizeof(*entry_data)); skb_pull(entry, sizeof(*hdr) + pad + sizeof(*entry_data));
ieee80211_tx_status_irqsafe(dev, entry); ieee80211_tx_status_irqsafe(dev, entry);
goto out; goto out;
@ -542,6 +622,27 @@ static void p54_rx_eeprom_readback(struct ieee80211_hw *dev,
complete(&priv->eeprom_comp); complete(&priv->eeprom_comp);
} }
static void p54_rx_stats(struct ieee80211_hw *dev, struct sk_buff *skb)
{
struct p54_common *priv = dev->priv;
struct p54_control_hdr *hdr = (struct p54_control_hdr *) skb->data;
struct p54_statistics *stats = (struct p54_statistics *) hdr->data;
u32 tsf32 = le32_to_cpu(stats->tsf32);
if (tsf32 < priv->tsf_low32)
priv->tsf_high32++;
priv->tsf_low32 = tsf32;
priv->stats.dot11RTSFailureCount = le32_to_cpu(stats->rts_fail);
priv->stats.dot11RTSSuccessCount = le32_to_cpu(stats->rts_success);
priv->stats.dot11FCSErrorCount = le32_to_cpu(stats->rx_bad_fcs);
priv->noise = p54_rssi_to_dbm(dev, le32_to_cpu(stats->noise));
complete(&priv->stats_comp);
mod_timer(&priv->stats_timer, jiffies + 5 * HZ);
}
static int p54_rx_control(struct ieee80211_hw *dev, struct sk_buff *skb) static int p54_rx_control(struct ieee80211_hw *dev, struct sk_buff *skb)
{ {
struct p54_control_hdr *hdr = (struct p54_control_hdr *) skb->data; struct p54_control_hdr *hdr = (struct p54_control_hdr *) skb->data;
@ -552,6 +653,9 @@ static int p54_rx_control(struct ieee80211_hw *dev, struct sk_buff *skb)
break; break;
case P54_CONTROL_TYPE_BBP: case P54_CONTROL_TYPE_BBP:
break; break;
case P54_CONTROL_TYPE_STAT_READBACK:
p54_rx_stats(dev, skb);
break;
case P54_CONTROL_TYPE_EEPROM_READBACK: case P54_CONTROL_TYPE_EEPROM_READBACK:
p54_rx_eeprom_readback(dev, skb); p54_rx_eeprom_readback(dev, skb);
break; break;
@ -753,7 +857,7 @@ static int p54_tx(struct ieee80211_hw *dev, struct sk_buff *skb)
txhdr->hw_queue = skb_get_queue_mapping(skb) + 4; txhdr->hw_queue = skb_get_queue_mapping(skb) + 4;
txhdr->tx_antenna = (info->antenna_sel_tx == 0) ? txhdr->tx_antenna = (info->antenna_sel_tx == 0) ?
2 : info->antenna_sel_tx - 1; 2 : info->antenna_sel_tx - 1;
txhdr->output_power = 0x7f; // HW Maximum txhdr->output_power = priv->output_power;
txhdr->cts_rate = (info->flags & IEEE80211_TX_CTL_NO_ACK) ? txhdr->cts_rate = (info->flags & IEEE80211_TX_CTL_NO_ACK) ?
0 : cts_rate; 0 : cts_rate;
if (padding) if (padding)
@ -1021,12 +1125,25 @@ static int p54_start(struct ieee80211_hw *dev)
return -ENOMEM; return -ENOMEM;
} }
if (!priv->cached_stats) {
priv->cached_stats = kzalloc(sizeof(struct p54_statistics) +
priv->tx_hdr_len + sizeof(struct p54_control_hdr),
GFP_KERNEL);
if (!priv->cached_stats) {
kfree(priv->cached_vdcf);
priv->cached_vdcf = NULL;
return -ENOMEM;
}
}
err = priv->open(dev); err = priv->open(dev);
if (!err) if (!err)
priv->mode = IEEE80211_IF_TYPE_MNTR; priv->mode = IEEE80211_IF_TYPE_MNTR;
p54_init_vdcf(dev); p54_init_vdcf(dev);
mod_timer(&priv->stats_timer, jiffies + HZ);
return err; return err;
} }
@ -1034,9 +1151,12 @@ static void p54_stop(struct ieee80211_hw *dev)
{ {
struct p54_common *priv = dev->priv; struct p54_common *priv = dev->priv;
struct sk_buff *skb; struct sk_buff *skb;
del_timer(&priv->stats_timer);
while ((skb = skb_dequeue(&priv->tx_queue))) while ((skb = skb_dequeue(&priv->tx_queue)))
kfree_skb(skb); kfree_skb(skb);
priv->stop(dev); priv->stop(dev);
priv->tsf_high32 = priv->tsf_low32 = 0;
priv->mode = IEEE80211_IF_TYPE_INVALID; priv->mode = IEEE80211_IF_TYPE_INVALID;
} }
@ -1091,6 +1211,7 @@ static int p54_config(struct ieee80211_hw *dev, struct ieee80211_conf *conf)
mutex_lock(&priv->conf_mutex); mutex_lock(&priv->conf_mutex);
priv->rx_antenna = (conf->antenna_sel_rx == 0) ? priv->rx_antenna = (conf->antenna_sel_rx == 0) ?
2 : conf->antenna_sel_tx - 1; 2 : conf->antenna_sel_tx - 1;
priv->output_power = conf->power_level << 2;
ret = p54_set_freq(dev, cpu_to_le16(conf->channel->center_freq)); ret = p54_set_freq(dev, cpu_to_le16(conf->channel->center_freq));
p54_set_vdcf(dev); p54_set_vdcf(dev);
mutex_unlock(&priv->conf_mutex); mutex_unlock(&priv->conf_mutex);
@ -1118,13 +1239,26 @@ static void p54_configure_filter(struct ieee80211_hw *dev,
{ {
struct p54_common *priv = dev->priv; struct p54_common *priv = dev->priv;
*total_flags &= FIF_BCN_PRBRESP_PROMISC; *total_flags &= FIF_BCN_PRBRESP_PROMISC |
FIF_PROMISC_IN_BSS |
FIF_FCSFAIL;
priv->filter_flags = *total_flags;
if (changed_flags & FIF_BCN_PRBRESP_PROMISC) { if (changed_flags & FIF_BCN_PRBRESP_PROMISC) {
if (*total_flags & FIF_BCN_PRBRESP_PROMISC) if (*total_flags & FIF_BCN_PRBRESP_PROMISC)
p54_set_filter(dev, 0, NULL); p54_set_filter(dev, priv->filter_type, NULL);
else else
p54_set_filter(dev, 0, priv->bssid); p54_set_filter(dev, priv->filter_type, priv->bssid);
}
if (changed_flags & FIF_PROMISC_IN_BSS) {
if (*total_flags & FIF_PROMISC_IN_BSS)
p54_set_filter(dev, priv->filter_type |
cpu_to_le16(0x8), NULL);
else
p54_set_filter(dev, priv->filter_type &
~cpu_to_le16(0x8), priv->bssid);
} }
} }
@ -1148,10 +1282,67 @@ static int p54_conf_tx(struct ieee80211_hw *dev, u16 queue,
return 0; return 0;
} }
static int p54_init_xbow_synth(struct ieee80211_hw *dev)
{
struct p54_common *priv = dev->priv;
struct p54_control_hdr *hdr;
struct p54_tx_control_xbow_synth *xbow;
hdr = kzalloc(sizeof(*hdr) + sizeof(*xbow) +
priv->tx_hdr_len, GFP_KERNEL);
if (!hdr)
return -ENOMEM;
hdr = (void *)hdr + priv->tx_hdr_len;
hdr->magic1 = cpu_to_le16(0x8001);
hdr->len = cpu_to_le16(sizeof(*xbow));
hdr->type = cpu_to_le16(P54_CONTROL_TYPE_XBOW_SYNTH_CFG);
p54_assign_address(dev, NULL, hdr, sizeof(*hdr) + sizeof(*xbow));
xbow = (struct p54_tx_control_xbow_synth *) hdr->data;
xbow->magic1 = cpu_to_le16(0x1);
xbow->magic2 = cpu_to_le16(0x2);
xbow->freq = cpu_to_le16(5390);
priv->tx(dev, hdr, sizeof(*hdr) + sizeof(*xbow), 1);
return 0;
}
static void p54_statistics_timer(unsigned long data)
{
struct ieee80211_hw *dev = (struct ieee80211_hw *) data;
struct p54_common *priv = dev->priv;
struct p54_control_hdr *hdr;
struct p54_statistics *stats;
BUG_ON(!priv->cached_stats);
hdr = (void *)priv->cached_stats + priv->tx_hdr_len;
hdr->magic1 = cpu_to_le16(0x8000);
hdr->len = cpu_to_le16(sizeof(*stats));
hdr->type = cpu_to_le16(P54_CONTROL_TYPE_STAT_READBACK);
p54_assign_address(dev, NULL, hdr, sizeof(*hdr) + sizeof(*stats));
priv->tx(dev, hdr, sizeof(*hdr) + sizeof(*stats), 0);
}
static int p54_get_stats(struct ieee80211_hw *dev, static int p54_get_stats(struct ieee80211_hw *dev,
struct ieee80211_low_level_stats *stats) struct ieee80211_low_level_stats *stats)
{ {
/* TODO */ struct p54_common *priv = dev->priv;
del_timer(&priv->stats_timer);
p54_statistics_timer((unsigned long)dev);
if (!wait_for_completion_interruptible_timeout(&priv->stats_comp, HZ)) {
printk(KERN_ERR "%s: device does not respond!\n",
wiphy_name(dev->wiphy));
return -EBUSY;
}
memcpy(stats, &priv->stats, sizeof(*stats));
return 0; return 0;
} }
@ -1193,12 +1384,12 @@ struct ieee80211_hw *p54_init_common(size_t priv_data_len)
skb_queue_head_init(&priv->tx_queue); skb_queue_head_init(&priv->tx_queue);
dev->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | /* not sure */ dev->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | /* not sure */
IEEE80211_HW_RX_INCLUDES_FCS | IEEE80211_HW_RX_INCLUDES_FCS |
IEEE80211_HW_SIGNAL_UNSPEC; IEEE80211_HW_SIGNAL_DBM |
IEEE80211_HW_NOISE_DBM;
dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
dev->channel_change_time = 1000; /* TODO: find actual value */ dev->channel_change_time = 1000; /* TODO: find actual value */
dev->max_signal = 127;
priv->tx_stats[0].limit = 1; priv->tx_stats[0].limit = 1;
priv->tx_stats[1].limit = 1; priv->tx_stats[1].limit = 1;
@ -1206,11 +1397,15 @@ struct ieee80211_hw *p54_init_common(size_t priv_data_len)
priv->tx_stats[3].limit = 1; priv->tx_stats[3].limit = 1;
priv->tx_stats[4].limit = 5; priv->tx_stats[4].limit = 5;
dev->queues = 1; dev->queues = 1;
priv->noise = -94;
dev->extra_tx_headroom = sizeof(struct p54_control_hdr) + 4 + dev->extra_tx_headroom = sizeof(struct p54_control_hdr) + 4 +
sizeof(struct p54_tx_control_allocdata); sizeof(struct p54_tx_control_allocdata);
mutex_init(&priv->conf_mutex); mutex_init(&priv->conf_mutex);
init_completion(&priv->eeprom_comp); init_completion(&priv->eeprom_comp);
init_completion(&priv->stats_comp);
setup_timer(&priv->stats_timer, p54_statistics_timer,
(unsigned long)dev);
return dev; return dev;
} }
@ -1219,6 +1414,7 @@ EXPORT_SYMBOL_GPL(p54_init_common);
void p54_free_common(struct ieee80211_hw *dev) void p54_free_common(struct ieee80211_hw *dev)
{ {
struct p54_common *priv = dev->priv; struct p54_common *priv = dev->priv;
kfree(priv->cached_stats);
kfree(priv->iq_autocal); kfree(priv->iq_autocal);
kfree(priv->output_limit); kfree(priv->output_limit);
kfree(priv->curve_data); kfree(priv->curve_data);

View File

@ -185,7 +185,8 @@ struct p54_rx_hdr {
u8 rssi; u8 rssi;
u8 quality; u8 quality;
u16 unknown2; u16 unknown2;
__le64 timestamp; __le32 tsf32;
__le32 unalloc0;
u8 align[0]; u8 align[0];
} __attribute__ ((packed)); } __attribute__ ((packed));
@ -300,4 +301,24 @@ struct p54_tx_control_vdcf {
__le16 frameburst; __le16 frameburst;
} __attribute__ ((packed)); } __attribute__ ((packed));
struct p54_statistics {
__le32 rx_success;
__le32 rx_bad_fcs;
__le32 rx_abort;
__le32 rx_abort_phy;
__le32 rts_success;
__le32 rts_fail;
__le32 tsf32;
__le32 airtime;
__le32 noise;
__le32 unkn[10]; /* CCE / CCA / RADAR */
} __attribute__ ((packed));
struct p54_tx_control_xbow_synth {
__le16 magic1;
__le16 magic2;
__le16 freq;
u32 padding[5];
} __attribute__ ((packed));
#endif /* P54COMMON_H */ #endif /* P54COMMON_H */

View File

@ -40,11 +40,15 @@ config RT2X00_LIB_CRYPTO
config RT2X00_LIB_RFKILL config RT2X00_LIB_RFKILL
boolean boolean
depends on RT2X00_LIB depends on RT2X00_LIB
select RFKILL depends on RFKILL
default y
config RT2X00_LIB_LEDS config RT2X00_LIB_LEDS
boolean boolean
depends on RT2X00_LIB && NEW_LEDS depends on RT2X00_LIB
depends on NEW_LEDS
depends on LEDS_CLASS
default y
config RT2400PCI config RT2400PCI
tristate "Ralink rt2400 (PCI/PCMCIA) support" tristate "Ralink rt2400 (PCI/PCMCIA) support"
@ -57,23 +61,6 @@ config RT2400PCI
When compiled as a module, this driver will be called "rt2400pci.ko". When compiled as a module, this driver will be called "rt2400pci.ko".
config RT2400PCI_RFKILL
bool "Ralink rt2400 rfkill support"
depends on RT2400PCI
select RT2X00_LIB_RFKILL
---help---
This adds support for integrated rt2400 hardware that features a
hardware button to control the radio state.
This feature depends on the RF switch subsystem rfkill.
config RT2400PCI_LEDS
bool "Ralink rt2400 leds support"
depends on RT2400PCI && NEW_LEDS
select LEDS_CLASS
select RT2X00_LIB_LEDS
---help---
This adds support for led triggers provided my mac80211.
config RT2500PCI config RT2500PCI
tristate "Ralink rt2500 (PCI/PCMCIA) support" tristate "Ralink rt2500 (PCI/PCMCIA) support"
depends on PCI depends on PCI
@ -85,23 +72,6 @@ config RT2500PCI
When compiled as a module, this driver will be called "rt2500pci.ko". When compiled as a module, this driver will be called "rt2500pci.ko".
config RT2500PCI_RFKILL
bool "Ralink rt2500 rfkill support"
depends on RT2500PCI
select RT2X00_LIB_RFKILL
---help---
This adds support for integrated rt2500 hardware that features a
hardware button to control the radio state.
This feature depends on the RF switch subsystem rfkill.
config RT2500PCI_LEDS
bool "Ralink rt2500 leds support"
depends on RT2500PCI && NEW_LEDS
select LEDS_CLASS
select RT2X00_LIB_LEDS
---help---
This adds support for led triggers provided my mac80211.
config RT61PCI config RT61PCI
tristate "Ralink rt2501/rt61 (PCI/PCMCIA) support" tristate "Ralink rt2501/rt61 (PCI/PCMCIA) support"
depends on PCI depends on PCI
@ -116,23 +86,6 @@ config RT61PCI
When compiled as a module, this driver will be called "rt61pci.ko". When compiled as a module, this driver will be called "rt61pci.ko".
config RT61PCI_RFKILL
bool "Ralink rt2501/rt61 rfkill support"
depends on RT61PCI
select RT2X00_LIB_RFKILL
---help---
This adds support for integrated rt61 hardware that features a
hardware button to control the radio state.
This feature depends on the RF switch subsystem rfkill.
config RT61PCI_LEDS
bool "Ralink rt2501/rt61 leds support"
depends on RT61PCI && NEW_LEDS
select LEDS_CLASS
select RT2X00_LIB_LEDS
---help---
This adds support for led triggers provided my mac80211.
config RT2500USB config RT2500USB
tristate "Ralink rt2500 (USB) support" tristate "Ralink rt2500 (USB) support"
depends on USB depends on USB
@ -143,14 +96,6 @@ config RT2500USB
When compiled as a module, this driver will be called "rt2500usb.ko". When compiled as a module, this driver will be called "rt2500usb.ko".
config RT2500USB_LEDS
bool "Ralink rt2500 leds support"
depends on RT2500USB && NEW_LEDS
select LEDS_CLASS
select RT2X00_LIB_LEDS
---help---
This adds support for led triggers provided my mac80211.
config RT73USB config RT73USB
tristate "Ralink rt2501/rt73 (USB) support" tristate "Ralink rt2501/rt73 (USB) support"
depends on USB depends on USB
@ -164,14 +109,6 @@ config RT73USB
When compiled as a module, this driver will be called "rt73usb.ko". When compiled as a module, this driver will be called "rt73usb.ko".
config RT73USB_LEDS
bool "Ralink rt2501/rt73 leds support"
depends on RT73USB && NEW_LEDS
select LEDS_CLASS
select RT2X00_LIB_LEDS
---help---
This adds support for led triggers provided my mac80211.
config RT2X00_LIB_DEBUGFS config RT2X00_LIB_DEBUGFS
bool "Ralink debugfs support" bool "Ralink debugfs support"
depends on RT2X00_LIB && MAC80211_DEBUGFS depends on RT2X00_LIB && MAC80211_DEBUGFS

View File

@ -231,7 +231,7 @@ static const struct rt2x00debug rt2400pci_rt2x00debug = {
}; };
#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ #endif /* CONFIG_RT2X00_LIB_DEBUGFS */
#ifdef CONFIG_RT2400PCI_RFKILL #ifdef CONFIG_RT2X00_LIB_RFKILL
static int rt2400pci_rfkill_poll(struct rt2x00_dev *rt2x00dev) static int rt2400pci_rfkill_poll(struct rt2x00_dev *rt2x00dev)
{ {
u32 reg; u32 reg;
@ -241,9 +241,9 @@ static int rt2400pci_rfkill_poll(struct rt2x00_dev *rt2x00dev)
} }
#else #else
#define rt2400pci_rfkill_poll NULL #define rt2400pci_rfkill_poll NULL
#endif /* CONFIG_RT2400PCI_RFKILL */ #endif /* CONFIG_RT2X00_LIB_RFKILL */
#ifdef CONFIG_RT2400PCI_LEDS #ifdef CONFIG_RT2X00_LIB_LEDS
static void rt2400pci_brightness_set(struct led_classdev *led_cdev, static void rt2400pci_brightness_set(struct led_classdev *led_cdev,
enum led_brightness brightness) enum led_brightness brightness)
{ {
@ -288,7 +288,7 @@ static void rt2400pci_init_led(struct rt2x00_dev *rt2x00dev,
led->led_dev.blink_set = rt2400pci_blink_set; led->led_dev.blink_set = rt2400pci_blink_set;
led->flags = LED_INITIALIZED; led->flags = LED_INITIALIZED;
} }
#endif /* CONFIG_RT2400PCI_LEDS */ #endif /* CONFIG_RT2X00_LIB_LEDS */
/* /*
* Configuration handlers. * Configuration handlers.
@ -1374,22 +1374,22 @@ static int rt2400pci_init_eeprom(struct rt2x00_dev *rt2x00dev)
/* /*
* Store led mode, for correct led behaviour. * Store led mode, for correct led behaviour.
*/ */
#ifdef CONFIG_RT2400PCI_LEDS #ifdef CONFIG_RT2X00_LIB_LEDS
value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_LED_MODE); value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_LED_MODE);
rt2400pci_init_led(rt2x00dev, &rt2x00dev->led_radio, LED_TYPE_RADIO); rt2400pci_init_led(rt2x00dev, &rt2x00dev->led_radio, LED_TYPE_RADIO);
if (value == LED_MODE_TXRX_ACTIVITY) if (value == LED_MODE_TXRX_ACTIVITY)
rt2400pci_init_led(rt2x00dev, &rt2x00dev->led_qual, rt2400pci_init_led(rt2x00dev, &rt2x00dev->led_qual,
LED_TYPE_ACTIVITY); LED_TYPE_ACTIVITY);
#endif /* CONFIG_RT2400PCI_LEDS */ #endif /* CONFIG_RT2X00_LIB_LEDS */
/* /*
* Detect if this device has an hardware controlled radio. * Detect if this device has an hardware controlled radio.
*/ */
#ifdef CONFIG_RT2400PCI_RFKILL #ifdef CONFIG_RT2X00_LIB_RFKILL
if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_HARDWARE_RADIO)) if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_HARDWARE_RADIO))
__set_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags); __set_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags);
#endif /* CONFIG_RT2400PCI_RFKILL */ #endif /* CONFIG_RT2X00_LIB_RFKILL */
/* /*
* Check if the BBP tuning should be enabled. * Check if the BBP tuning should be enabled.

View File

@ -231,7 +231,7 @@ static const struct rt2x00debug rt2500pci_rt2x00debug = {
}; };
#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ #endif /* CONFIG_RT2X00_LIB_DEBUGFS */
#ifdef CONFIG_RT2500PCI_RFKILL #ifdef CONFIG_RT2X00_LIB_RFKILL
static int rt2500pci_rfkill_poll(struct rt2x00_dev *rt2x00dev) static int rt2500pci_rfkill_poll(struct rt2x00_dev *rt2x00dev)
{ {
u32 reg; u32 reg;
@ -241,9 +241,9 @@ static int rt2500pci_rfkill_poll(struct rt2x00_dev *rt2x00dev)
} }
#else #else
#define rt2500pci_rfkill_poll NULL #define rt2500pci_rfkill_poll NULL
#endif /* CONFIG_RT2500PCI_RFKILL */ #endif /* CONFIG_RT2X00_LIB_RFKILL */
#ifdef CONFIG_RT2500PCI_LEDS #ifdef CONFIG_RT2X00_LIB_LEDS
static void rt2500pci_brightness_set(struct led_classdev *led_cdev, static void rt2500pci_brightness_set(struct led_classdev *led_cdev,
enum led_brightness brightness) enum led_brightness brightness)
{ {
@ -288,7 +288,7 @@ static void rt2500pci_init_led(struct rt2x00_dev *rt2x00dev,
led->led_dev.blink_set = rt2500pci_blink_set; led->led_dev.blink_set = rt2500pci_blink_set;
led->flags = LED_INITIALIZED; led->flags = LED_INITIALIZED;
} }
#endif /* CONFIG_RT2500PCI_LEDS */ #endif /* CONFIG_RT2X00_LIB_LEDS */
/* /*
* Configuration handlers. * Configuration handlers.
@ -1533,22 +1533,22 @@ static int rt2500pci_init_eeprom(struct rt2x00_dev *rt2x00dev)
/* /*
* Store led mode, for correct led behaviour. * Store led mode, for correct led behaviour.
*/ */
#ifdef CONFIG_RT2500PCI_LEDS #ifdef CONFIG_RT2X00_LIB_LEDS
value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_LED_MODE); value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_LED_MODE);
rt2500pci_init_led(rt2x00dev, &rt2x00dev->led_radio, LED_TYPE_RADIO); rt2500pci_init_led(rt2x00dev, &rt2x00dev->led_radio, LED_TYPE_RADIO);
if (value == LED_MODE_TXRX_ACTIVITY) if (value == LED_MODE_TXRX_ACTIVITY)
rt2500pci_init_led(rt2x00dev, &rt2x00dev->led_qual, rt2500pci_init_led(rt2x00dev, &rt2x00dev->led_qual,
LED_TYPE_ACTIVITY); LED_TYPE_ACTIVITY);
#endif /* CONFIG_RT2500PCI_LEDS */ #endif /* CONFIG_RT2X00_LIB_LEDS */
/* /*
* Detect if this device has an hardware controlled radio. * Detect if this device has an hardware controlled radio.
*/ */
#ifdef CONFIG_RT2500PCI_RFKILL #ifdef CONFIG_RT2X00_LIB_RFKILL
if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_HARDWARE_RADIO)) if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_HARDWARE_RADIO))
__set_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags); __set_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags);
#endif /* CONFIG_RT2500PCI_RFKILL */ #endif /* CONFIG_RT2X00_LIB_RFKILL */
/* /*
* Check if the BBP tuning should be enabled. * Check if the BBP tuning should be enabled.

View File

@ -288,7 +288,7 @@ static const struct rt2x00debug rt2500usb_rt2x00debug = {
}; };
#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ #endif /* CONFIG_RT2X00_LIB_DEBUGFS */
#ifdef CONFIG_RT2500USB_LEDS #ifdef CONFIG_RT2X00_LIB_LEDS
static void rt2500usb_brightness_set(struct led_classdev *led_cdev, static void rt2500usb_brightness_set(struct led_classdev *led_cdev,
enum led_brightness brightness) enum led_brightness brightness)
{ {
@ -333,7 +333,7 @@ static void rt2500usb_init_led(struct rt2x00_dev *rt2x00dev,
led->led_dev.blink_set = rt2500usb_blink_set; led->led_dev.blink_set = rt2500usb_blink_set;
led->flags = LED_INITIALIZED; led->flags = LED_INITIALIZED;
} }
#endif /* CONFIG_RT2500USB_LEDS */ #endif /* CONFIG_RT2X00_LIB_LEDS */
/* /*
* Configuration handlers. * Configuration handlers.
@ -1133,7 +1133,6 @@ static void rt2500usb_write_beacon(struct queue_entry *entry)
int pipe = usb_sndbulkpipe(usb_dev, 1); int pipe = usb_sndbulkpipe(usb_dev, 1);
int length; int length;
u16 reg; u16 reg;
u32 word, len;
/* /*
* Add the descriptor in front of the skb. * Add the descriptor in front of the skb.
@ -1142,17 +1141,6 @@ static void rt2500usb_write_beacon(struct queue_entry *entry)
memcpy(entry->skb->data, skbdesc->desc, skbdesc->desc_len); memcpy(entry->skb->data, skbdesc->desc, skbdesc->desc_len);
skbdesc->desc = entry->skb->data; skbdesc->desc = entry->skb->data;
/*
* Adjust the beacon databyte count. The current number is
* calculated before this function gets called, but falsely
* assumes that the descriptor was already present in the SKB.
*/
rt2x00_desc_read(skbdesc->desc, 0, &word);
len = rt2x00_get_field32(word, TXD_W0_DATABYTE_COUNT);
len += skbdesc->desc_len;
rt2x00_set_field32(&word, TXD_W0_DATABYTE_COUNT, len);
rt2x00_desc_write(skbdesc->desc, 0, word);
/* /*
* Disable beaconing while we are reloading the beacon data, * Disable beaconing while we are reloading the beacon data,
* otherwise we might be sending out invalid data. * otherwise we might be sending out invalid data.
@ -1485,14 +1473,14 @@ static int rt2500usb_init_eeprom(struct rt2x00_dev *rt2x00dev)
/* /*
* Store led mode, for correct led behaviour. * Store led mode, for correct led behaviour.
*/ */
#ifdef CONFIG_RT2500USB_LEDS #ifdef CONFIG_RT2X00_LIB_LEDS
value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_LED_MODE); value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_LED_MODE);
rt2500usb_init_led(rt2x00dev, &rt2x00dev->led_radio, LED_TYPE_RADIO); rt2500usb_init_led(rt2x00dev, &rt2x00dev->led_radio, LED_TYPE_RADIO);
if (value == LED_MODE_TXRX_ACTIVITY) if (value == LED_MODE_TXRX_ACTIVITY)
rt2500usb_init_led(rt2x00dev, &rt2x00dev->led_qual, rt2500usb_init_led(rt2x00dev, &rt2x00dev->led_qual,
LED_TYPE_ACTIVITY); LED_TYPE_ACTIVITY);
#endif /* CONFIG_RT2500USB_LEDS */ #endif /* CONFIG_RT2X00_LIB_LEDS */
/* /*
* Check if the BBP tuning should be disabled. * Check if the BBP tuning should be disabled.

View File

@ -45,16 +45,15 @@ static int rt2x00rfkill_toggle_radio(void *data, enum rfkill_state state)
return 0; return 0;
if (state == RFKILL_STATE_UNBLOCKED) { if (state == RFKILL_STATE_UNBLOCKED) {
INFO(rt2x00dev, "Hardware button pressed, enabling radio.\n"); INFO(rt2x00dev, "RFKILL event: enabling radio.\n");
clear_bit(DEVICE_STATE_DISABLED_RADIO_HW, &rt2x00dev->flags); clear_bit(DEVICE_STATE_DISABLED_RADIO_HW, &rt2x00dev->flags);
retval = rt2x00lib_enable_radio(rt2x00dev); retval = rt2x00lib_enable_radio(rt2x00dev);
} else if (state == RFKILL_STATE_SOFT_BLOCKED) { } else if (state == RFKILL_STATE_SOFT_BLOCKED) {
INFO(rt2x00dev, "Hardware button pressed, disabling radio.\n"); INFO(rt2x00dev, "RFKILL event: disabling radio.\n");
set_bit(DEVICE_STATE_DISABLED_RADIO_HW, &rt2x00dev->flags); set_bit(DEVICE_STATE_DISABLED_RADIO_HW, &rt2x00dev->flags);
rt2x00lib_disable_radio(rt2x00dev); rt2x00lib_disable_radio(rt2x00dev);
} else { } else {
WARNING(rt2x00dev, "Received unexpected rfkill state %d.\n", WARNING(rt2x00dev, "RFKILL event: unknown state %d.\n", state);
state);
} }
return retval; return retval;
@ -64,7 +63,12 @@ static int rt2x00rfkill_get_state(void *data, enum rfkill_state *state)
{ {
struct rt2x00_dev *rt2x00dev = data; struct rt2x00_dev *rt2x00dev = data;
*state = rt2x00dev->rfkill->state; /*
* rfkill_poll reports 1 when the key has been pressed and the
* radio should be blocked.
*/
*state = rt2x00dev->ops->lib->rfkill_poll(rt2x00dev) ?
RFKILL_STATE_SOFT_BLOCKED : RFKILL_STATE_UNBLOCKED;
return 0; return 0;
} }
@ -73,19 +77,18 @@ static void rt2x00rfkill_poll(struct work_struct *work)
{ {
struct rt2x00_dev *rt2x00dev = struct rt2x00_dev *rt2x00dev =
container_of(work, struct rt2x00_dev, rfkill_work.work); container_of(work, struct rt2x00_dev, rfkill_work.work);
int state; enum rfkill_state state;
if (!test_bit(RFKILL_STATE_REGISTERED, &rt2x00dev->rfkill_state)) if (!test_bit(RFKILL_STATE_REGISTERED, &rt2x00dev->rfkill_state) ||
!test_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags))
return; return;
/* /*
* rfkill_poll reports 1 when the key has been pressed and the * Poll latest state and report it to rfkill who should sort
* radio should be blocked. * out if the state should be toggled or not.
*/ */
state = !rt2x00dev->ops->lib->rfkill_poll(rt2x00dev) ? if (!rt2x00rfkill_get_state(rt2x00dev, &state))
RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; rfkill_force_state(rt2x00dev->rfkill, state);
rfkill_force_state(rt2x00dev->rfkill, state);
queue_delayed_work(rt2x00dev->hw->workqueue, queue_delayed_work(rt2x00dev->hw->workqueue,
&rt2x00dev->rfkill_work, RFKILL_POLL_INTERVAL); &rt2x00dev->rfkill_work, RFKILL_POLL_INTERVAL);
@ -93,8 +96,8 @@ static void rt2x00rfkill_poll(struct work_struct *work)
void rt2x00rfkill_register(struct rt2x00_dev *rt2x00dev) void rt2x00rfkill_register(struct rt2x00_dev *rt2x00dev)
{ {
if (!test_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags) || if (!test_bit(RFKILL_STATE_ALLOCATED, &rt2x00dev->rfkill_state) ||
!test_bit(RFKILL_STATE_ALLOCATED, &rt2x00dev->rfkill_state)) test_bit(RFKILL_STATE_REGISTERED, &rt2x00dev->rfkill_state))
return; return;
if (rfkill_register(rt2x00dev->rfkill)) { if (rfkill_register(rt2x00dev->rfkill)) {
@ -114,7 +117,7 @@ void rt2x00rfkill_register(struct rt2x00_dev *rt2x00dev)
void rt2x00rfkill_unregister(struct rt2x00_dev *rt2x00dev) void rt2x00rfkill_unregister(struct rt2x00_dev *rt2x00dev)
{ {
if (!test_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags) || if (!test_bit(RFKILL_STATE_ALLOCATED, &rt2x00dev->rfkill_state) ||
!test_bit(RFKILL_STATE_REGISTERED, &rt2x00dev->rfkill_state)) !test_bit(RFKILL_STATE_REGISTERED, &rt2x00dev->rfkill_state))
return; return;
@ -127,21 +130,25 @@ void rt2x00rfkill_unregister(struct rt2x00_dev *rt2x00dev)
void rt2x00rfkill_allocate(struct rt2x00_dev *rt2x00dev) void rt2x00rfkill_allocate(struct rt2x00_dev *rt2x00dev)
{ {
if (!test_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags)) struct device *dev = wiphy_dev(rt2x00dev->hw->wiphy);
if (test_bit(RFKILL_STATE_ALLOCATED, &rt2x00dev->rfkill_state))
return; return;
rt2x00dev->rfkill = rt2x00dev->rfkill = rfkill_allocate(dev, RFKILL_TYPE_WLAN);
rfkill_allocate(wiphy_dev(rt2x00dev->hw->wiphy), RFKILL_TYPE_WLAN);
if (!rt2x00dev->rfkill) { if (!rt2x00dev->rfkill) {
ERROR(rt2x00dev, "Failed to allocate rfkill handler.\n"); ERROR(rt2x00dev, "Failed to allocate rfkill handler.\n");
return; return;
} }
__set_bit(RFKILL_STATE_ALLOCATED, &rt2x00dev->rfkill_state);
rt2x00dev->rfkill->name = rt2x00dev->ops->name; rt2x00dev->rfkill->name = rt2x00dev->ops->name;
rt2x00dev->rfkill->data = rt2x00dev; rt2x00dev->rfkill->data = rt2x00dev;
rt2x00dev->rfkill->state = -1; rt2x00dev->rfkill->state = -1;
rt2x00dev->rfkill->toggle_radio = rt2x00rfkill_toggle_radio; rt2x00dev->rfkill->toggle_radio = rt2x00rfkill_toggle_radio;
rt2x00dev->rfkill->get_state = rt2x00rfkill_get_state; if (test_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags))
rt2x00dev->rfkill->get_state = rt2x00rfkill_get_state;
INIT_DELAYED_WORK(&rt2x00dev->rfkill_work, rt2x00rfkill_poll); INIT_DELAYED_WORK(&rt2x00dev->rfkill_work, rt2x00rfkill_poll);
@ -150,8 +157,7 @@ void rt2x00rfkill_allocate(struct rt2x00_dev *rt2x00dev)
void rt2x00rfkill_free(struct rt2x00_dev *rt2x00dev) void rt2x00rfkill_free(struct rt2x00_dev *rt2x00dev)
{ {
if (!test_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags) || if (!test_bit(RFKILL_STATE_ALLOCATED, &rt2x00dev->flags))
!test_bit(RFKILL_STATE_ALLOCATED, &rt2x00dev->rfkill_state))
return; return;
cancel_delayed_work_sync(&rt2x00dev->rfkill_work); cancel_delayed_work_sync(&rt2x00dev->rfkill_work);

View File

@ -163,7 +163,7 @@ rf_write:
rt2x00_rf_write(rt2x00dev, word, value); rt2x00_rf_write(rt2x00dev, word, value);
} }
#ifdef CONFIG_RT61PCI_LEDS #ifdef CONFIG_RT2X00_LIB_LEDS
/* /*
* This function is only called from rt61pci_led_brightness() * This function is only called from rt61pci_led_brightness()
* make gcc happy by placing this function inside the * make gcc happy by placing this function inside the
@ -195,7 +195,7 @@ static void rt61pci_mcu_request(struct rt2x00_dev *rt2x00dev,
rt2x00_set_field32(&reg, HOST_CMD_CSR_INTERRUPT_MCU, 1); rt2x00_set_field32(&reg, HOST_CMD_CSR_INTERRUPT_MCU, 1);
rt2x00pci_register_write(rt2x00dev, HOST_CMD_CSR, reg); rt2x00pci_register_write(rt2x00dev, HOST_CMD_CSR, reg);
} }
#endif /* CONFIG_RT61PCI_LEDS */ #endif /* CONFIG_RT2X00_LIB_LEDS */
static void rt61pci_eepromregister_read(struct eeprom_93cx6 *eeprom) static void rt61pci_eepromregister_read(struct eeprom_93cx6 *eeprom)
{ {
@ -271,7 +271,7 @@ static const struct rt2x00debug rt61pci_rt2x00debug = {
}; };
#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ #endif /* CONFIG_RT2X00_LIB_DEBUGFS */
#ifdef CONFIG_RT61PCI_RFKILL #ifdef CONFIG_RT2X00_LIB_RFKILL
static int rt61pci_rfkill_poll(struct rt2x00_dev *rt2x00dev) static int rt61pci_rfkill_poll(struct rt2x00_dev *rt2x00dev)
{ {
u32 reg; u32 reg;
@ -281,9 +281,9 @@ static int rt61pci_rfkill_poll(struct rt2x00_dev *rt2x00dev)
} }
#else #else
#define rt61pci_rfkill_poll NULL #define rt61pci_rfkill_poll NULL
#endif /* CONFIG_RT61PCI_RFKILL */ #endif /* CONFIG_RT2X00_LIB_RFKILL */
#ifdef CONFIG_RT61PCI_LEDS #ifdef CONFIG_RT2X00_LIB_LEDS
static void rt61pci_brightness_set(struct led_classdev *led_cdev, static void rt61pci_brightness_set(struct led_classdev *led_cdev,
enum led_brightness brightness) enum led_brightness brightness)
{ {
@ -348,7 +348,7 @@ static void rt61pci_init_led(struct rt2x00_dev *rt2x00dev,
led->led_dev.blink_set = rt61pci_blink_set; led->led_dev.blink_set = rt61pci_blink_set;
led->flags = LED_INITIALIZED; led->flags = LED_INITIALIZED;
} }
#endif /* CONFIG_RT61PCI_LEDS */ #endif /* CONFIG_RT2X00_LIB_LEDS */
/* /*
* Configuration handlers. * Configuration handlers.
@ -2313,10 +2313,10 @@ static int rt61pci_init_eeprom(struct rt2x00_dev *rt2x00dev)
/* /*
* Detect if this device has an hardware controlled radio. * Detect if this device has an hardware controlled radio.
*/ */
#ifdef CONFIG_RT61PCI_RFKILL #ifdef CONFIG_RT2X00_LIB_RFKILL
if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_HARDWARE_RADIO)) if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_HARDWARE_RADIO))
__set_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags); __set_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags);
#endif /* CONFIG_RT61PCI_RFKILL */ #endif /* CONFIG_RT2X00_LIB_RFKILL */
/* /*
* Read frequency offset and RF programming sequence. * Read frequency offset and RF programming sequence.
@ -2374,7 +2374,7 @@ static int rt61pci_init_eeprom(struct rt2x00_dev *rt2x00dev)
* If the eeprom value is invalid, * If the eeprom value is invalid,
* switch to default led mode. * switch to default led mode.
*/ */
#ifdef CONFIG_RT61PCI_LEDS #ifdef CONFIG_RT2X00_LIB_LEDS
rt2x00_eeprom_read(rt2x00dev, EEPROM_LED, &eeprom); rt2x00_eeprom_read(rt2x00dev, EEPROM_LED, &eeprom);
value = rt2x00_get_field16(eeprom, EEPROM_LED_LED_MODE); value = rt2x00_get_field16(eeprom, EEPROM_LED_LED_MODE);
@ -2408,7 +2408,7 @@ static int rt61pci_init_eeprom(struct rt2x00_dev *rt2x00dev)
rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_READY_A, rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_READY_A,
rt2x00_get_field16(eeprom, rt2x00_get_field16(eeprom,
EEPROM_LED_POLARITY_RDY_A)); EEPROM_LED_POLARITY_RDY_A));
#endif /* CONFIG_RT61PCI_LEDS */ #endif /* CONFIG_RT2X00_LIB_LEDS */
return 0; return 0;
} }

View File

@ -292,7 +292,7 @@ static const struct rt2x00debug rt73usb_rt2x00debug = {
}; };
#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ #endif /* CONFIG_RT2X00_LIB_DEBUGFS */
#ifdef CONFIG_RT73USB_LEDS #ifdef CONFIG_RT2X00_LIB_LEDS
static void rt73usb_brightness_set(struct led_classdev *led_cdev, static void rt73usb_brightness_set(struct led_classdev *led_cdev,
enum led_brightness brightness) enum led_brightness brightness)
{ {
@ -359,7 +359,7 @@ static void rt73usb_init_led(struct rt2x00_dev *rt2x00dev,
led->led_dev.blink_set = rt73usb_blink_set; led->led_dev.blink_set = rt73usb_blink_set;
led->flags = LED_INITIALIZED; led->flags = LED_INITIALIZED;
} }
#endif /* CONFIG_RT73USB_LEDS */ #endif /* CONFIG_RT2X00_LIB_LEDS */
/* /*
* Configuration handlers. * Configuration handlers.
@ -1572,7 +1572,6 @@ static void rt73usb_write_beacon(struct queue_entry *entry)
struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb);
unsigned int beacon_base; unsigned int beacon_base;
u32 reg; u32 reg;
u32 word, len;
/* /*
* Add the descriptor in front of the skb. * Add the descriptor in front of the skb.
@ -1581,17 +1580,6 @@ static void rt73usb_write_beacon(struct queue_entry *entry)
memcpy(entry->skb->data, skbdesc->desc, skbdesc->desc_len); memcpy(entry->skb->data, skbdesc->desc, skbdesc->desc_len);
skbdesc->desc = entry->skb->data; skbdesc->desc = entry->skb->data;
/*
* Adjust the beacon databyte count. The current number is
* calculated before this function gets called, but falsely
* assumes that the descriptor was already present in the SKB.
*/
rt2x00_desc_read(skbdesc->desc, 0, &word);
len = rt2x00_get_field32(word, TXD_W0_DATABYTE_COUNT);
len += skbdesc->desc_len;
rt2x00_set_field32(&word, TXD_W0_DATABYTE_COUNT, len);
rt2x00_desc_write(skbdesc->desc, 0, word);
/* /*
* Disable beaconing while we are reloading the beacon data, * Disable beaconing while we are reloading the beacon data,
* otherwise we might be sending out invalid data. * otherwise we might be sending out invalid data.
@ -1944,7 +1932,7 @@ static int rt73usb_init_eeprom(struct rt2x00_dev *rt2x00dev)
/* /*
* Store led settings, for correct led behaviour. * Store led settings, for correct led behaviour.
*/ */
#ifdef CONFIG_RT73USB_LEDS #ifdef CONFIG_RT2X00_LIB_LEDS
rt2x00_eeprom_read(rt2x00dev, EEPROM_LED, &eeprom); rt2x00_eeprom_read(rt2x00dev, EEPROM_LED, &eeprom);
rt73usb_init_led(rt2x00dev, &rt2x00dev->led_radio, LED_TYPE_RADIO); rt73usb_init_led(rt2x00dev, &rt2x00dev->led_radio, LED_TYPE_RADIO);
@ -1977,7 +1965,7 @@ static int rt73usb_init_eeprom(struct rt2x00_dev *rt2x00dev)
rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_READY_A, rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_READY_A,
rt2x00_get_field16(eeprom, rt2x00_get_field16(eeprom,
EEPROM_LED_POLARITY_RDY_A)); EEPROM_LED_POLARITY_RDY_A));
#endif /* CONFIG_RT73USB_LEDS */ #endif /* CONFIG_RT2X00_LIB_LEDS */
return 0; return 0;
} }

View File

@ -643,6 +643,9 @@ struct ieee80211_mgmt {
} u; } u;
} __attribute__ ((packed)); } __attribute__ ((packed));
/* mgmt header + 1 byte category code */
#define IEEE80211_MIN_ACTION_SIZE offsetof(struct ieee80211_mgmt, u.action.u)
/* Control frames */ /* Control frames */
struct ieee80211_rts { struct ieee80211_rts {
@ -708,7 +711,7 @@ struct ieee80211_ht_addt_info {
/* 802.11n HT capabilities masks */ /* 802.11n HT capabilities masks */
#define IEEE80211_HT_CAP_SUP_WIDTH 0x0002 #define IEEE80211_HT_CAP_SUP_WIDTH 0x0002
#define IEEE80211_HT_CAP_MIMO_PS 0x000C #define IEEE80211_HT_CAP_SM_PS 0x000C
#define IEEE80211_HT_CAP_GRN_FLD 0x0010 #define IEEE80211_HT_CAP_GRN_FLD 0x0010
#define IEEE80211_HT_CAP_SGI_20 0x0020 #define IEEE80211_HT_CAP_SGI_20 0x0020
#define IEEE80211_HT_CAP_SGI_40 0x0040 #define IEEE80211_HT_CAP_SGI_40 0x0040
@ -737,11 +740,26 @@ struct ieee80211_ht_addt_info {
#define IEEE80211_HT_IE_NON_GF_STA_PRSNT 0x0004 #define IEEE80211_HT_IE_NON_GF_STA_PRSNT 0x0004
#define IEEE80211_HT_IE_NON_HT_STA_PRSNT 0x0010 #define IEEE80211_HT_IE_NON_HT_STA_PRSNT 0x0010
/* MIMO Power Save Modes */ /* block-ack parameters */
#define WLAN_HT_CAP_MIMO_PS_STATIC 0 #define IEEE80211_ADDBA_PARAM_POLICY_MASK 0x0002
#define WLAN_HT_CAP_MIMO_PS_DYNAMIC 1 #define IEEE80211_ADDBA_PARAM_TID_MASK 0x003C
#define WLAN_HT_CAP_MIMO_PS_INVALID 2 #define IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK 0xFFA0
#define WLAN_HT_CAP_MIMO_PS_DISABLED 3 #define IEEE80211_DELBA_PARAM_TID_MASK 0xF000
#define IEEE80211_DELBA_PARAM_INITIATOR_MASK 0x0800
/*
* A-PMDU buffer sizes
* According to IEEE802.11n spec size varies from 8K to 64K (in powers of 2)
*/
#define IEEE80211_MIN_AMPDU_BUF 0x8
#define IEEE80211_MAX_AMPDU_BUF 0x40
/* Spatial Multiplexing Power Save Modes */
#define WLAN_HT_CAP_SM_PS_STATIC 0
#define WLAN_HT_CAP_SM_PS_DYNAMIC 1
#define WLAN_HT_CAP_SM_PS_INVALID 2
#define WLAN_HT_CAP_SM_PS_DISABLED 3
/* Authentication algorithms */ /* Authentication algorithms */
#define WLAN_AUTH_OPEN 0 #define WLAN_AUTH_OPEN 0

View File

@ -1142,7 +1142,7 @@ enum ieee80211_ampdu_mlme_action {
* of assocaited station or AP. * of assocaited station or AP.
* *
* @conf_tx: Configure TX queue parameters (EDCF (aifs, cw_min, cw_max), * @conf_tx: Configure TX queue parameters (EDCF (aifs, cw_min, cw_max),
* bursting) for a hardware TX queue. Must be atomic. * bursting) for a hardware TX queue.
* *
* @get_tx_stats: Get statistics of the current TX queue status. This is used * @get_tx_stats: Get statistics of the current TX queue status. This is used
* to get number of currently queued packets (queue length), maximum queue * to get number of currently queued packets (queue length), maximum queue

View File

@ -7,6 +7,8 @@ mac80211-y := \
sta_info.o \ sta_info.o \
wep.o \ wep.o \
wpa.o \ wpa.o \
scan.o \
ht.o \
mlme.o \ mlme.o \
iface.o \ iface.o \
rate.o \ rate.o \
@ -15,6 +17,7 @@ mac80211-y := \
aes_ccm.o \ aes_ccm.o \
cfg.o \ cfg.o \
rx.o \ rx.o \
spectmgmt.o \
tx.o \ tx.o \
key.o \ key.o \
util.o \ util.o \

992
net/mac80211/ht.c Normal file
View File

@ -0,0 +1,992 @@
/*
* HT handling
*
* Copyright 2003, Jouni Malinen <jkmaline@cc.hut.fi>
* Copyright 2002-2005, Instant802 Networks, Inc.
* Copyright 2005-2006, Devicescape Software, Inc.
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
* Copyright 2007, Michael Wu <flamingice@sourmilk.net>
* Copyright 2007-2008, Intel Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/ieee80211.h>
#include <net/wireless.h>
#include <net/mac80211.h>
#include "ieee80211_i.h"
#include "sta_info.h"
#include "wme.h"
int ieee80211_ht_cap_ie_to_ht_info(struct ieee80211_ht_cap *ht_cap_ie,
struct ieee80211_ht_info *ht_info)
{
if (ht_info == NULL)
return -EINVAL;
memset(ht_info, 0, sizeof(*ht_info));
if (ht_cap_ie) {
u8 ampdu_info = ht_cap_ie->ampdu_params_info;
ht_info->ht_supported = 1;
ht_info->cap = le16_to_cpu(ht_cap_ie->cap_info);
ht_info->ampdu_factor =
ampdu_info & IEEE80211_HT_CAP_AMPDU_FACTOR;
ht_info->ampdu_density =
(ampdu_info & IEEE80211_HT_CAP_AMPDU_DENSITY) >> 2;
memcpy(ht_info->supp_mcs_set, ht_cap_ie->supp_mcs_set, 16);
} else
ht_info->ht_supported = 0;
return 0;
}
int ieee80211_ht_addt_info_ie_to_ht_bss_info(
struct ieee80211_ht_addt_info *ht_add_info_ie,
struct ieee80211_ht_bss_info *bss_info)
{
if (bss_info == NULL)
return -EINVAL;
memset(bss_info, 0, sizeof(*bss_info));
if (ht_add_info_ie) {
u16 op_mode;
op_mode = le16_to_cpu(ht_add_info_ie->operation_mode);
bss_info->primary_channel = ht_add_info_ie->control_chan;
bss_info->bss_cap = ht_add_info_ie->ht_param;
bss_info->bss_op_mode = (u8)(op_mode & 0xff);
}
return 0;
}
static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata,
const u8 *da, u16 tid,
u8 dialog_token, u16 start_seq_num,
u16 agg_size, u16 timeout)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_if_sta *ifsta = &sdata->u.sta;
struct sk_buff *skb;
struct ieee80211_mgmt *mgmt;
u16 capab;
skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom);
if (!skb) {
printk(KERN_ERR "%s: failed to allocate buffer "
"for addba request frame\n", sdata->dev->name);
return;
}
skb_reserve(skb, local->hw.extra_tx_headroom);
mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
memset(mgmt, 0, 24);
memcpy(mgmt->da, da, ETH_ALEN);
memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
if (sdata->vif.type == IEEE80211_IF_TYPE_AP)
memcpy(mgmt->bssid, sdata->dev->dev_addr, ETH_ALEN);
else
memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_ACTION);
skb_put(skb, 1 + sizeof(mgmt->u.action.u.addba_req));
mgmt->u.action.category = WLAN_CATEGORY_BACK;
mgmt->u.action.u.addba_req.action_code = WLAN_ACTION_ADDBA_REQ;
mgmt->u.action.u.addba_req.dialog_token = dialog_token;
capab = (u16)(1 << 1); /* bit 1 aggregation policy */
capab |= (u16)(tid << 2); /* bit 5:2 TID number */
capab |= (u16)(agg_size << 6); /* bit 15:6 max size of aggergation */
mgmt->u.action.u.addba_req.capab = cpu_to_le16(capab);
mgmt->u.action.u.addba_req.timeout = cpu_to_le16(timeout);
mgmt->u.action.u.addba_req.start_seq_num =
cpu_to_le16(start_seq_num << 4);
ieee80211_tx_skb(sdata, skb, 0);
}
static void ieee80211_send_addba_resp(struct ieee80211_sub_if_data *sdata, u8 *da, u16 tid,
u8 dialog_token, u16 status, u16 policy,
u16 buf_size, u16 timeout)
{
struct ieee80211_if_sta *ifsta = &sdata->u.sta;
struct ieee80211_local *local = sdata->local;
struct sk_buff *skb;
struct ieee80211_mgmt *mgmt;
u16 capab;
skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom);
if (!skb) {
printk(KERN_DEBUG "%s: failed to allocate buffer "
"for addba resp frame\n", sdata->dev->name);
return;
}
skb_reserve(skb, local->hw.extra_tx_headroom);
mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
memset(mgmt, 0, 24);
memcpy(mgmt->da, da, ETH_ALEN);
memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
if (sdata->vif.type == IEEE80211_IF_TYPE_AP)
memcpy(mgmt->bssid, sdata->dev->dev_addr, ETH_ALEN);
else
memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_ACTION);
skb_put(skb, 1 + sizeof(mgmt->u.action.u.addba_resp));
mgmt->u.action.category = WLAN_CATEGORY_BACK;
mgmt->u.action.u.addba_resp.action_code = WLAN_ACTION_ADDBA_RESP;
mgmt->u.action.u.addba_resp.dialog_token = dialog_token;
capab = (u16)(policy << 1); /* bit 1 aggregation policy */
capab |= (u16)(tid << 2); /* bit 5:2 TID number */
capab |= (u16)(buf_size << 6); /* bit 15:6 max size of aggregation */
mgmt->u.action.u.addba_resp.capab = cpu_to_le16(capab);
mgmt->u.action.u.addba_resp.timeout = cpu_to_le16(timeout);
mgmt->u.action.u.addba_resp.status = cpu_to_le16(status);
ieee80211_tx_skb(sdata, skb, 0);
}
static void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata,
const u8 *da, u16 tid,
u16 initiator, u16 reason_code)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_if_sta *ifsta = &sdata->u.sta;
struct sk_buff *skb;
struct ieee80211_mgmt *mgmt;
u16 params;
skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom);
if (!skb) {
printk(KERN_ERR "%s: failed to allocate buffer "
"for delba frame\n", sdata->dev->name);
return;
}
skb_reserve(skb, local->hw.extra_tx_headroom);
mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
memset(mgmt, 0, 24);
memcpy(mgmt->da, da, ETH_ALEN);
memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
if (sdata->vif.type == IEEE80211_IF_TYPE_AP)
memcpy(mgmt->bssid, sdata->dev->dev_addr, ETH_ALEN);
else
memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_ACTION);
skb_put(skb, 1 + sizeof(mgmt->u.action.u.delba));
mgmt->u.action.category = WLAN_CATEGORY_BACK;
mgmt->u.action.u.delba.action_code = WLAN_ACTION_DELBA;
params = (u16)(initiator << 11); /* bit 11 initiator */
params |= (u16)(tid << 12); /* bit 15:12 TID number */
mgmt->u.action.u.delba.params = cpu_to_le16(params);
mgmt->u.action.u.delba.reason_code = cpu_to_le16(reason_code);
ieee80211_tx_skb(sdata, skb, 0);
}
void ieee80211_send_bar(struct ieee80211_sub_if_data *sdata, u8 *ra, u16 tid, u16 ssn)
{
struct ieee80211_local *local = sdata->local;
struct sk_buff *skb;
struct ieee80211_bar *bar;
u16 bar_control = 0;
skb = dev_alloc_skb(sizeof(*bar) + local->hw.extra_tx_headroom);
if (!skb) {
printk(KERN_ERR "%s: failed to allocate buffer for "
"bar frame\n", sdata->dev->name);
return;
}
skb_reserve(skb, local->hw.extra_tx_headroom);
bar = (struct ieee80211_bar *)skb_put(skb, sizeof(*bar));
memset(bar, 0, sizeof(*bar));
bar->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL |
IEEE80211_STYPE_BACK_REQ);
memcpy(bar->ra, ra, ETH_ALEN);
memcpy(bar->ta, sdata->dev->dev_addr, ETH_ALEN);
bar_control |= (u16)IEEE80211_BAR_CTRL_ACK_POLICY_NORMAL;
bar_control |= (u16)IEEE80211_BAR_CTRL_CBMTID_COMPRESSED_BA;
bar_control |= (u16)(tid << 12);
bar->control = cpu_to_le16(bar_control);
bar->start_seq_num = cpu_to_le16(ssn);
ieee80211_tx_skb(sdata, skb, 0);
}
void ieee80211_sta_stop_rx_ba_session(struct ieee80211_sub_if_data *sdata, u8 *ra, u16 tid,
u16 initiator, u16 reason)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_hw *hw = &local->hw;
struct sta_info *sta;
int ret, i;
DECLARE_MAC_BUF(mac);
rcu_read_lock();
sta = sta_info_get(local, ra);
if (!sta) {
rcu_read_unlock();
return;
}
/* check if TID is in operational state */
spin_lock_bh(&sta->lock);
if (sta->ampdu_mlme.tid_state_rx[tid]
!= HT_AGG_STATE_OPERATIONAL) {
spin_unlock_bh(&sta->lock);
rcu_read_unlock();
return;
}
sta->ampdu_mlme.tid_state_rx[tid] =
HT_AGG_STATE_REQ_STOP_BA_MSK |
(initiator << HT_AGG_STATE_INITIATOR_SHIFT);
spin_unlock_bh(&sta->lock);
/* stop HW Rx aggregation. ampdu_action existence
* already verified in session init so we add the BUG_ON */
BUG_ON(!local->ops->ampdu_action);
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "Rx BA session stop requested for %s tid %u\n",
print_mac(mac, ra), tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
ret = local->ops->ampdu_action(hw, IEEE80211_AMPDU_RX_STOP,
ra, tid, NULL);
if (ret)
printk(KERN_DEBUG "HW problem - can not stop rx "
"aggregation for tid %d\n", tid);
/* shutdown timer has not expired */
if (initiator != WLAN_BACK_TIMER)
del_timer_sync(&sta->ampdu_mlme.tid_rx[tid]->session_timer);
/* check if this is a self generated aggregation halt */
if (initiator == WLAN_BACK_RECIPIENT || initiator == WLAN_BACK_TIMER)
ieee80211_send_delba(sdata, ra, tid, 0, reason);
/* free the reordering buffer */
for (i = 0; i < sta->ampdu_mlme.tid_rx[tid]->buf_size; i++) {
if (sta->ampdu_mlme.tid_rx[tid]->reorder_buf[i]) {
/* release the reordered frames */
dev_kfree_skb(sta->ampdu_mlme.tid_rx[tid]->reorder_buf[i]);
sta->ampdu_mlme.tid_rx[tid]->stored_mpdu_num--;
sta->ampdu_mlme.tid_rx[tid]->reorder_buf[i] = NULL;
}
}
/* free resources */
kfree(sta->ampdu_mlme.tid_rx[tid]->reorder_buf);
kfree(sta->ampdu_mlme.tid_rx[tid]);
sta->ampdu_mlme.tid_rx[tid] = NULL;
sta->ampdu_mlme.tid_state_rx[tid] = HT_AGG_STATE_IDLE;
rcu_read_unlock();
}
/*
* After sending add Block Ack request we activated a timer until
* add Block Ack response will arrive from the recipient.
* If this timer expires sta_addba_resp_timer_expired will be executed.
*/
static void sta_addba_resp_timer_expired(unsigned long data)
{
/* not an elegant detour, but there is no choice as the timer passes
* only one argument, and both sta_info and TID are needed, so init
* flow in sta_info_create gives the TID as data, while the timer_to_id
* array gives the sta through container_of */
u16 tid = *(u8 *)data;
struct sta_info *temp_sta = container_of((void *)data,
struct sta_info, timer_to_tid[tid]);
struct ieee80211_local *local = temp_sta->local;
struct ieee80211_hw *hw = &local->hw;
struct sta_info *sta;
u8 *state;
rcu_read_lock();
sta = sta_info_get(local, temp_sta->addr);
if (!sta) {
rcu_read_unlock();
return;
}
state = &sta->ampdu_mlme.tid_state_tx[tid];
/* check if the TID waits for addBA response */
spin_lock_bh(&sta->lock);
if (!(*state & HT_ADDBA_REQUESTED_MSK)) {
spin_unlock_bh(&sta->lock);
*state = HT_AGG_STATE_IDLE;
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "timer expired on tid %d but we are not "
"expecting addBA response there", tid);
#endif
goto timer_expired_exit;
}
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "addBA response timer expired on tid %d\n", tid);
#endif
/* go through the state check in stop_BA_session */
*state = HT_AGG_STATE_OPERATIONAL;
spin_unlock_bh(&sta->lock);
ieee80211_stop_tx_ba_session(hw, temp_sta->addr, tid,
WLAN_BACK_INITIATOR);
timer_expired_exit:
rcu_read_unlock();
}
void ieee80211_sta_tear_down_BA_sessions(struct ieee80211_sub_if_data *sdata, u8 *addr)
{
struct ieee80211_local *local = sdata->local;
int i;
for (i = 0; i < STA_TID_NUM; i++) {
ieee80211_stop_tx_ba_session(&local->hw, addr, i,
WLAN_BACK_INITIATOR);
ieee80211_sta_stop_rx_ba_session(sdata, addr, i,
WLAN_BACK_RECIPIENT,
WLAN_REASON_QSTA_LEAVE_QBSS);
}
}
int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
{
struct ieee80211_local *local = hw_to_local(hw);
struct sta_info *sta;
struct ieee80211_sub_if_data *sdata;
u16 start_seq_num;
u8 *state;
int ret;
DECLARE_MAC_BUF(mac);
if (tid >= STA_TID_NUM)
return -EINVAL;
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "Open BA session requested for %s tid %u\n",
print_mac(mac, ra), tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
rcu_read_lock();
sta = sta_info_get(local, ra);
if (!sta) {
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "Could not find the station\n");
#endif
ret = -ENOENT;
goto exit;
}
spin_lock_bh(&sta->lock);
/* we have tried too many times, receiver does not want A-MPDU */
if (sta->ampdu_mlme.addba_req_num[tid] > HT_AGG_MAX_RETRIES) {
ret = -EBUSY;
goto err_unlock_sta;
}
state = &sta->ampdu_mlme.tid_state_tx[tid];
/* check if the TID is not in aggregation flow already */
if (*state != HT_AGG_STATE_IDLE) {
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "BA request denied - session is not "
"idle on tid %u\n", tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
ret = -EAGAIN;
goto err_unlock_sta;
}
/* prepare A-MPDU MLME for Tx aggregation */
sta->ampdu_mlme.tid_tx[tid] =
kmalloc(sizeof(struct tid_ampdu_tx), GFP_ATOMIC);
if (!sta->ampdu_mlme.tid_tx[tid]) {
#ifdef CONFIG_MAC80211_HT_DEBUG
if (net_ratelimit())
printk(KERN_ERR "allocate tx mlme to tid %d failed\n",
tid);
#endif
ret = -ENOMEM;
goto err_unlock_sta;
}
/* Tx timer */
sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer.function =
sta_addba_resp_timer_expired;
sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer.data =
(unsigned long)&sta->timer_to_tid[tid];
init_timer(&sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer);
/* create a new queue for this aggregation */
ret = ieee80211_ht_agg_queue_add(local, sta, tid);
/* case no queue is available to aggregation
* don't switch to aggregation */
if (ret) {
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "BA request denied - queue unavailable for"
" tid %d\n", tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
goto err_unlock_queue;
}
sdata = sta->sdata;
/* Ok, the Addba frame hasn't been sent yet, but if the driver calls the
* call back right away, it must see that the flow has begun */
*state |= HT_ADDBA_REQUESTED_MSK;
/* This is slightly racy because the queue isn't stopped */
start_seq_num = sta->tid_seq[tid];
if (local->ops->ampdu_action)
ret = local->ops->ampdu_action(hw, IEEE80211_AMPDU_TX_START,
ra, tid, &start_seq_num);
if (ret) {
/* No need to requeue the packets in the agg queue, since we
* held the tx lock: no packet could be enqueued to the newly
* allocated queue */
ieee80211_ht_agg_queue_remove(local, sta, tid, 0);
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "BA request denied - HW unavailable for"
" tid %d\n", tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
*state = HT_AGG_STATE_IDLE;
goto err_unlock_queue;
}
/* Will put all the packets in the new SW queue */
ieee80211_requeue(local, ieee802_1d_to_ac[tid]);
spin_unlock_bh(&sta->lock);
/* send an addBA request */
sta->ampdu_mlme.dialog_token_allocator++;
sta->ampdu_mlme.tid_tx[tid]->dialog_token =
sta->ampdu_mlme.dialog_token_allocator;
sta->ampdu_mlme.tid_tx[tid]->ssn = start_seq_num;
ieee80211_send_addba_request(sta->sdata, ra, tid,
sta->ampdu_mlme.tid_tx[tid]->dialog_token,
sta->ampdu_mlme.tid_tx[tid]->ssn,
0x40, 5000);
/* activate the timer for the recipient's addBA response */
sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer.expires =
jiffies + ADDBA_RESP_INTERVAL;
add_timer(&sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer);
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "activated addBA response timer on tid %d\n", tid);
#endif
goto exit;
err_unlock_queue:
kfree(sta->ampdu_mlme.tid_tx[tid]);
sta->ampdu_mlme.tid_tx[tid] = NULL;
ret = -EBUSY;
err_unlock_sta:
spin_unlock_bh(&sta->lock);
exit:
rcu_read_unlock();
return ret;
}
EXPORT_SYMBOL(ieee80211_start_tx_ba_session);
int ieee80211_stop_tx_ba_session(struct ieee80211_hw *hw,
u8 *ra, u16 tid,
enum ieee80211_back_parties initiator)
{
struct ieee80211_local *local = hw_to_local(hw);
struct sta_info *sta;
u8 *state;
int ret = 0;
DECLARE_MAC_BUF(mac);
if (tid >= STA_TID_NUM)
return -EINVAL;
rcu_read_lock();
sta = sta_info_get(local, ra);
if (!sta) {
rcu_read_unlock();
return -ENOENT;
}
/* check if the TID is in aggregation */
state = &sta->ampdu_mlme.tid_state_tx[tid];
spin_lock_bh(&sta->lock);
if (*state != HT_AGG_STATE_OPERATIONAL) {
ret = -ENOENT;
goto stop_BA_exit;
}
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "Tx BA session stop requested for %s tid %u\n",
print_mac(mac, ra), tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
ieee80211_stop_queue(hw, sta->tid_to_tx_q[tid]);
*state = HT_AGG_STATE_REQ_STOP_BA_MSK |
(initiator << HT_AGG_STATE_INITIATOR_SHIFT);
if (local->ops->ampdu_action)
ret = local->ops->ampdu_action(hw, IEEE80211_AMPDU_TX_STOP,
ra, tid, NULL);
/* case HW denied going back to legacy */
if (ret) {
WARN_ON(ret != -EBUSY);
*state = HT_AGG_STATE_OPERATIONAL;
ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]);
goto stop_BA_exit;
}
stop_BA_exit:
spin_unlock_bh(&sta->lock);
rcu_read_unlock();
return ret;
}
EXPORT_SYMBOL(ieee80211_stop_tx_ba_session);
void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid)
{
struct ieee80211_local *local = hw_to_local(hw);
struct sta_info *sta;
u8 *state;
DECLARE_MAC_BUF(mac);
if (tid >= STA_TID_NUM) {
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "Bad TID value: tid = %d (>= %d)\n",
tid, STA_TID_NUM);
#endif
return;
}
rcu_read_lock();
sta = sta_info_get(local, ra);
if (!sta) {
rcu_read_unlock();
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "Could not find station: %s\n",
print_mac(mac, ra));
#endif
return;
}
state = &sta->ampdu_mlme.tid_state_tx[tid];
spin_lock_bh(&sta->lock);
if (!(*state & HT_ADDBA_REQUESTED_MSK)) {
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "addBA was not requested yet, state is %d\n",
*state);
#endif
spin_unlock_bh(&sta->lock);
rcu_read_unlock();
return;
}
WARN_ON_ONCE(*state & HT_ADDBA_DRV_READY_MSK);
*state |= HT_ADDBA_DRV_READY_MSK;
if (*state == HT_AGG_STATE_OPERATIONAL) {
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "Aggregation is on for tid %d \n", tid);
#endif
ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]);
}
spin_unlock_bh(&sta->lock);
rcu_read_unlock();
}
EXPORT_SYMBOL(ieee80211_start_tx_ba_cb);
void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid)
{
struct ieee80211_local *local = hw_to_local(hw);
struct sta_info *sta;
u8 *state;
int agg_queue;
DECLARE_MAC_BUF(mac);
if (tid >= STA_TID_NUM) {
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "Bad TID value: tid = %d (>= %d)\n",
tid, STA_TID_NUM);
#endif
return;
}
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "Stopping Tx BA session for %s tid %d\n",
print_mac(mac, ra), tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
rcu_read_lock();
sta = sta_info_get(local, ra);
if (!sta) {
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "Could not find station: %s\n",
print_mac(mac, ra));
#endif
rcu_read_unlock();
return;
}
state = &sta->ampdu_mlme.tid_state_tx[tid];
/* NOTE: no need to use sta->lock in this state check, as
* ieee80211_stop_tx_ba_session will let only one stop call to
* pass through per sta/tid
*/
if ((*state & HT_AGG_STATE_REQ_STOP_BA_MSK) == 0) {
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "unexpected callback to A-MPDU stop\n");
#endif
rcu_read_unlock();
return;
}
if (*state & HT_AGG_STATE_INITIATOR_MSK)
ieee80211_send_delba(sta->sdata, ra, tid,
WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE);
agg_queue = sta->tid_to_tx_q[tid];
ieee80211_ht_agg_queue_remove(local, sta, tid, 1);
/* We just requeued the all the frames that were in the
* removed queue, and since we might miss a softirq we do
* netif_schedule_queue. ieee80211_wake_queue is not used
* here as this queue is not necessarily stopped
*/
netif_schedule_queue(netdev_get_tx_queue(local->mdev, agg_queue));
spin_lock_bh(&sta->lock);
*state = HT_AGG_STATE_IDLE;
sta->ampdu_mlme.addba_req_num[tid] = 0;
kfree(sta->ampdu_mlme.tid_tx[tid]);
sta->ampdu_mlme.tid_tx[tid] = NULL;
spin_unlock_bh(&sta->lock);
rcu_read_unlock();
}
EXPORT_SYMBOL(ieee80211_stop_tx_ba_cb);
void ieee80211_start_tx_ba_cb_irqsafe(struct ieee80211_hw *hw,
const u8 *ra, u16 tid)
{
struct ieee80211_local *local = hw_to_local(hw);
struct ieee80211_ra_tid *ra_tid;
struct sk_buff *skb = dev_alloc_skb(0);
if (unlikely(!skb)) {
#ifdef CONFIG_MAC80211_HT_DEBUG
if (net_ratelimit())
printk(KERN_WARNING "%s: Not enough memory, "
"dropping start BA session", skb->dev->name);
#endif
return;
}
ra_tid = (struct ieee80211_ra_tid *) &skb->cb;
memcpy(&ra_tid->ra, ra, ETH_ALEN);
ra_tid->tid = tid;
skb->pkt_type = IEEE80211_ADDBA_MSG;
skb_queue_tail(&local->skb_queue, skb);
tasklet_schedule(&local->tasklet);
}
EXPORT_SYMBOL(ieee80211_start_tx_ba_cb_irqsafe);
void ieee80211_stop_tx_ba_cb_irqsafe(struct ieee80211_hw *hw,
const u8 *ra, u16 tid)
{
struct ieee80211_local *local = hw_to_local(hw);
struct ieee80211_ra_tid *ra_tid;
struct sk_buff *skb = dev_alloc_skb(0);
if (unlikely(!skb)) {
#ifdef CONFIG_MAC80211_HT_DEBUG
if (net_ratelimit())
printk(KERN_WARNING "%s: Not enough memory, "
"dropping stop BA session", skb->dev->name);
#endif
return;
}
ra_tid = (struct ieee80211_ra_tid *) &skb->cb;
memcpy(&ra_tid->ra, ra, ETH_ALEN);
ra_tid->tid = tid;
skb->pkt_type = IEEE80211_DELBA_MSG;
skb_queue_tail(&local->skb_queue, skb);
tasklet_schedule(&local->tasklet);
}
EXPORT_SYMBOL(ieee80211_stop_tx_ba_cb_irqsafe);
/*
* After accepting the AddBA Request we activated a timer,
* resetting it after each frame that arrives from the originator.
* if this timer expires ieee80211_sta_stop_rx_ba_session will be executed.
*/
static void sta_rx_agg_session_timer_expired(unsigned long data)
{
/* not an elegant detour, but there is no choice as the timer passes
* only one argument, and various sta_info are needed here, so init
* flow in sta_info_create gives the TID as data, while the timer_to_id
* array gives the sta through container_of */
u8 *ptid = (u8 *)data;
u8 *timer_to_id = ptid - *ptid;
struct sta_info *sta = container_of(timer_to_id, struct sta_info,
timer_to_tid[0]);
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "rx session timer expired on tid %d\n", (u16)*ptid);
#endif
ieee80211_sta_stop_rx_ba_session(sta->sdata, sta->addr,
(u16)*ptid, WLAN_BACK_TIMER,
WLAN_REASON_QSTA_TIMEOUT);
}
void ieee80211_process_addba_request(struct ieee80211_local *local,
struct sta_info *sta,
struct ieee80211_mgmt *mgmt,
size_t len)
{
struct ieee80211_hw *hw = &local->hw;
struct ieee80211_conf *conf = &hw->conf;
struct tid_ampdu_rx *tid_agg_rx;
u16 capab, tid, timeout, ba_policy, buf_size, start_seq_num, status;
u8 dialog_token;
int ret = -EOPNOTSUPP;
DECLARE_MAC_BUF(mac);
/* extract session parameters from addba request frame */
dialog_token = mgmt->u.action.u.addba_req.dialog_token;
timeout = le16_to_cpu(mgmt->u.action.u.addba_req.timeout);
start_seq_num =
le16_to_cpu(mgmt->u.action.u.addba_req.start_seq_num) >> 4;
capab = le16_to_cpu(mgmt->u.action.u.addba_req.capab);
ba_policy = (capab & IEEE80211_ADDBA_PARAM_POLICY_MASK) >> 1;
tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2;
buf_size = (capab & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> 6;
status = WLAN_STATUS_REQUEST_DECLINED;
/* sanity check for incoming parameters:
* check if configuration can support the BA policy
* and if buffer size does not exceeds max value */
if (((ba_policy != 1)
&& (!(conf->ht_conf.cap & IEEE80211_HT_CAP_DELAY_BA)))
|| (buf_size > IEEE80211_MAX_AMPDU_BUF)) {
status = WLAN_STATUS_INVALID_QOS_PARAM;
#ifdef CONFIG_MAC80211_HT_DEBUG
if (net_ratelimit())
printk(KERN_DEBUG "AddBA Req with bad params from "
"%s on tid %u. policy %d, buffer size %d\n",
print_mac(mac, mgmt->sa), tid, ba_policy,
buf_size);
#endif /* CONFIG_MAC80211_HT_DEBUG */
goto end_no_lock;
}
/* determine default buffer size */
if (buf_size == 0) {
struct ieee80211_supported_band *sband;
sband = local->hw.wiphy->bands[conf->channel->band];
buf_size = IEEE80211_MIN_AMPDU_BUF;
buf_size = buf_size << sband->ht_info.ampdu_factor;
}
/* examine state machine */
spin_lock_bh(&sta->lock);
if (sta->ampdu_mlme.tid_state_rx[tid] != HT_AGG_STATE_IDLE) {
#ifdef CONFIG_MAC80211_HT_DEBUG
if (net_ratelimit())
printk(KERN_DEBUG "unexpected AddBA Req from "
"%s on tid %u\n",
print_mac(mac, mgmt->sa), tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
goto end;
}
/* prepare A-MPDU MLME for Rx aggregation */
sta->ampdu_mlme.tid_rx[tid] =
kmalloc(sizeof(struct tid_ampdu_rx), GFP_ATOMIC);
if (!sta->ampdu_mlme.tid_rx[tid]) {
#ifdef CONFIG_MAC80211_HT_DEBUG
if (net_ratelimit())
printk(KERN_ERR "allocate rx mlme to tid %d failed\n",
tid);
#endif
goto end;
}
/* rx timer */
sta->ampdu_mlme.tid_rx[tid]->session_timer.function =
sta_rx_agg_session_timer_expired;
sta->ampdu_mlme.tid_rx[tid]->session_timer.data =
(unsigned long)&sta->timer_to_tid[tid];
init_timer(&sta->ampdu_mlme.tid_rx[tid]->session_timer);
tid_agg_rx = sta->ampdu_mlme.tid_rx[tid];
/* prepare reordering buffer */
tid_agg_rx->reorder_buf =
kmalloc(buf_size * sizeof(struct sk_buff *), GFP_ATOMIC);
if (!tid_agg_rx->reorder_buf) {
#ifdef CONFIG_MAC80211_HT_DEBUG
if (net_ratelimit())
printk(KERN_ERR "can not allocate reordering buffer "
"to tid %d\n", tid);
#endif
kfree(sta->ampdu_mlme.tid_rx[tid]);
goto end;
}
memset(tid_agg_rx->reorder_buf, 0,
buf_size * sizeof(struct sk_buff *));
if (local->ops->ampdu_action)
ret = local->ops->ampdu_action(hw, IEEE80211_AMPDU_RX_START,
sta->addr, tid, &start_seq_num);
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "Rx A-MPDU request on tid %d result %d\n", tid, ret);
#endif /* CONFIG_MAC80211_HT_DEBUG */
if (ret) {
kfree(tid_agg_rx->reorder_buf);
kfree(tid_agg_rx);
sta->ampdu_mlme.tid_rx[tid] = NULL;
goto end;
}
/* change state and send addba resp */
sta->ampdu_mlme.tid_state_rx[tid] = HT_AGG_STATE_OPERATIONAL;
tid_agg_rx->dialog_token = dialog_token;
tid_agg_rx->ssn = start_seq_num;
tid_agg_rx->head_seq_num = start_seq_num;
tid_agg_rx->buf_size = buf_size;
tid_agg_rx->timeout = timeout;
tid_agg_rx->stored_mpdu_num = 0;
status = WLAN_STATUS_SUCCESS;
end:
spin_unlock_bh(&sta->lock);
end_no_lock:
ieee80211_send_addba_resp(sta->sdata, sta->addr, tid,
dialog_token, status, 1, buf_size, timeout);
}
void ieee80211_process_addba_resp(struct ieee80211_local *local,
struct sta_info *sta,
struct ieee80211_mgmt *mgmt,
size_t len)
{
struct ieee80211_hw *hw = &local->hw;
u16 capab;
u16 tid;
u8 *state;
capab = le16_to_cpu(mgmt->u.action.u.addba_resp.capab);
tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2;
state = &sta->ampdu_mlme.tid_state_tx[tid];
spin_lock_bh(&sta->lock);
if (!(*state & HT_ADDBA_REQUESTED_MSK)) {
spin_unlock_bh(&sta->lock);
return;
}
if (mgmt->u.action.u.addba_resp.dialog_token !=
sta->ampdu_mlme.tid_tx[tid]->dialog_token) {
spin_unlock_bh(&sta->lock);
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "wrong addBA response token, tid %d\n", tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
return;
}
del_timer_sync(&sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer);
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "switched off addBA timer for tid %d \n", tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
if (le16_to_cpu(mgmt->u.action.u.addba_resp.status)
== WLAN_STATUS_SUCCESS) {
*state |= HT_ADDBA_RECEIVED_MSK;
sta->ampdu_mlme.addba_req_num[tid] = 0;
if (*state == HT_AGG_STATE_OPERATIONAL)
ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]);
spin_unlock_bh(&sta->lock);
} else {
sta->ampdu_mlme.addba_req_num[tid]++;
/* this will allow the state check in stop_BA_session */
*state = HT_AGG_STATE_OPERATIONAL;
spin_unlock_bh(&sta->lock);
ieee80211_stop_tx_ba_session(hw, sta->addr, tid,
WLAN_BACK_INITIATOR);
}
}
void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta,
struct ieee80211_mgmt *mgmt, size_t len)
{
struct ieee80211_local *local = sdata->local;
u16 tid, params;
u16 initiator;
DECLARE_MAC_BUF(mac);
params = le16_to_cpu(mgmt->u.action.u.delba.params);
tid = (params & IEEE80211_DELBA_PARAM_TID_MASK) >> 12;
initiator = (params & IEEE80211_DELBA_PARAM_INITIATOR_MASK) >> 11;
#ifdef CONFIG_MAC80211_HT_DEBUG
if (net_ratelimit())
printk(KERN_DEBUG "delba from %s (%s) tid %d reason code %d\n",
print_mac(mac, mgmt->sa),
initiator ? "initiator" : "recipient", tid,
mgmt->u.action.u.delba.reason_code);
#endif /* CONFIG_MAC80211_HT_DEBUG */
if (initiator == WLAN_BACK_INITIATOR)
ieee80211_sta_stop_rx_ba_session(sdata, sta->addr, tid,
WLAN_BACK_INITIATOR, 0);
else { /* WLAN_BACK_RECIPIENT */
spin_lock_bh(&sta->lock);
sta->ampdu_mlme.tid_state_tx[tid] =
HT_AGG_STATE_OPERATIONAL;
spin_unlock_bh(&sta->lock);
ieee80211_stop_tx_ba_session(&local->hw, sta->addr, tid,
WLAN_BACK_RECIPIENT);
}
}

View File

@ -53,6 +53,12 @@ struct ieee80211_local;
* increased memory use (about 2 kB of RAM per entry). */ * increased memory use (about 2 kB of RAM per entry). */
#define IEEE80211_FRAGMENT_MAX 4 #define IEEE80211_FRAGMENT_MAX 4
/*
* Time after which we ignore scan results and no longer report/use
* them in any way.
*/
#define IEEE80211_SCAN_RESULT_EXPIRE (10 * HZ)
struct ieee80211_fragment_entry { struct ieee80211_fragment_entry {
unsigned long first_frag_time; unsigned long first_frag_time;
unsigned int seq; unsigned int seq;
@ -636,7 +642,7 @@ struct ieee80211_local {
enum { SCAN_SET_CHANNEL, SCAN_SEND_PROBE } scan_state; enum { SCAN_SET_CHANNEL, SCAN_SEND_PROBE } scan_state;
unsigned long last_scan_completed; unsigned long last_scan_completed;
struct delayed_work scan_work; struct delayed_work scan_work;
struct net_device *scan_dev; struct ieee80211_sub_if_data *scan_sdata;
struct ieee80211_channel *oper_channel, *scan_channel; struct ieee80211_channel *oper_channel, *scan_channel;
u8 scan_ssid[IEEE80211_MAX_SSID_LEN]; u8 scan_ssid[IEEE80211_MAX_SSID_LEN];
size_t scan_ssid_len; size_t scan_ssid_len;
@ -903,29 +909,31 @@ int ieee80211_sta_disassociate(struct ieee80211_sub_if_data *sdata, u16 reason);
void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata, void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
u32 changed); u32 changed);
u32 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata); u32 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata);
int ieee80211_ht_cap_ie_to_ht_info(struct ieee80211_ht_cap *ht_cap_ie,
struct ieee80211_ht_info *ht_info);
int ieee80211_ht_addt_info_ie_to_ht_bss_info(
struct ieee80211_ht_addt_info *ht_add_info_ie,
struct ieee80211_ht_bss_info *bss_info);
void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata, const u8 *da,
u16 tid, u8 dialog_token, u16 start_seq_num,
u16 agg_size, u16 timeout);
void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata, const u8 *da, u16 tid,
u16 initiator, u16 reason_code);
void ieee80211_send_bar(struct ieee80211_sub_if_data *sdata, u8 *ra, u16 tid, u16 ssn);
void ieee80211_sta_stop_rx_ba_session(struct ieee80211_sub_if_data *sdata, u8 *da,
u16 tid, u16 initiator, u16 reason);
void sta_addba_resp_timer_expired(unsigned long data);
void ieee80211_sta_tear_down_BA_sessions(struct ieee80211_sub_if_data *sdata, u8 *addr);
u64 ieee80211_sta_get_rates(struct ieee80211_local *local, u64 ieee80211_sta_get_rates(struct ieee80211_local *local,
struct ieee802_11_elems *elems, struct ieee802_11_elems *elems,
enum ieee80211_band band); enum ieee80211_band band);
void ieee80211_sta_tx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
int encrypt); u8 *ssid, size_t ssid_len);
void ieee802_11_parse_elems(u8 *start, size_t len, void ieee802_11_parse_elems(u8 *start, size_t len,
struct ieee802_11_elems *elems); struct ieee802_11_elems *elems);
void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local);
int ieee80211_sta_start_scan(struct ieee80211_sub_if_data *scan_sdata,
u8 *ssid, size_t ssid_len);
struct ieee80211_sta_bss *
ieee80211_bss_info_update(struct ieee80211_local *local,
struct ieee80211_rx_status *rx_status,
struct ieee80211_mgmt *mgmt,
size_t len,
struct ieee802_11_elems *elems,
int freq, bool beacon);
struct ieee80211_sta_bss *
ieee80211_rx_bss_add(struct ieee80211_local *local, u8 *bssid, int freq,
u8 *ssid, u8 ssid_len);
struct ieee80211_sta_bss *
ieee80211_rx_bss_get(struct ieee80211_local *local, u8 *bssid, int freq,
u8 *ssid, u8 ssid_len);
void ieee80211_rx_bss_put(struct ieee80211_local *local,
struct ieee80211_sta_bss *bss);
#ifdef CONFIG_MAC80211_MESH #ifdef CONFIG_MAC80211_MESH
void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata); void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata);
@ -951,6 +959,34 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev);
int ieee80211_monitor_start_xmit(struct sk_buff *skb, struct net_device *dev); int ieee80211_monitor_start_xmit(struct sk_buff *skb, struct net_device *dev);
int ieee80211_subif_start_xmit(struct sk_buff *skb, struct net_device *dev); int ieee80211_subif_start_xmit(struct sk_buff *skb, struct net_device *dev);
/* HT */
int ieee80211_ht_cap_ie_to_ht_info(struct ieee80211_ht_cap *ht_cap_ie,
struct ieee80211_ht_info *ht_info);
int ieee80211_ht_addt_info_ie_to_ht_bss_info(
struct ieee80211_ht_addt_info *ht_add_info_ie,
struct ieee80211_ht_bss_info *bss_info);
void ieee80211_send_bar(struct ieee80211_sub_if_data *sdata, u8 *ra, u16 tid, u16 ssn);
void ieee80211_sta_stop_rx_ba_session(struct ieee80211_sub_if_data *sdata, u8 *da,
u16 tid, u16 initiator, u16 reason);
void ieee80211_sta_tear_down_BA_sessions(struct ieee80211_sub_if_data *sdata, u8 *addr);
void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta,
struct ieee80211_mgmt *mgmt, size_t len);
void ieee80211_process_addba_resp(struct ieee80211_local *local,
struct sta_info *sta,
struct ieee80211_mgmt *mgmt,
size_t len);
void ieee80211_process_addba_request(struct ieee80211_local *local,
struct sta_info *sta,
struct ieee80211_mgmt *mgmt,
size_t len);
/* Spectrum management */
void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt,
size_t len);
/* utility functions/constants */ /* utility functions/constants */
extern void *mac80211_wiphy_privid; /* for wiphy privid */ extern void *mac80211_wiphy_privid; /* for wiphy privid */
extern const unsigned char rfc1042_header[6]; extern const unsigned char rfc1042_header[6];
@ -961,6 +997,9 @@ int ieee80211_frame_duration(struct ieee80211_local *local, size_t len,
int rate, int erp, int short_preamble); int rate, int erp, int short_preamble);
void mac80211_ev_michael_mic_failure(struct ieee80211_sub_if_data *sdata, int keyidx, void mac80211_ev_michael_mic_failure(struct ieee80211_sub_if_data *sdata, int keyidx,
struct ieee80211_hdr *hdr); struct ieee80211_hdr *hdr);
void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata);
void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
int encrypt);
#ifdef CONFIG_MAC80211_NOINLINE #ifdef CONFIG_MAC80211_NOINLINE
#define debug_noinline noinline #define debug_noinline noinline

View File

@ -31,11 +31,11 @@ static void ieee80211_teardown_sdata(struct net_device *dev)
int flushed; int flushed;
int i; int i;
ieee80211_debugfs_remove_netdev(sdata);
/* free extra data */ /* free extra data */
ieee80211_free_keys(sdata); ieee80211_free_keys(sdata);
ieee80211_debugfs_remove_netdev(sdata);
for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++) for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++)
__skb_queue_purge(&sdata->fragments[i].skb_list); __skb_queue_purge(&sdata->fragments[i].skb_list);
sdata->fragment_next = 0; sdata->fragment_next = 0;

View File

@ -399,8 +399,15 @@ static int ieee80211_open(struct net_device *dev)
atomic_inc(&local->iff_promiscs); atomic_inc(&local->iff_promiscs);
local->open_count++; local->open_count++;
if (need_hw_reconfig) if (need_hw_reconfig) {
ieee80211_hw_config(local); ieee80211_hw_config(local);
/*
* set default queue parameters so drivers don't
* need to initialise the hardware if the hardware
* doesn't start up with sane defaults
*/
ieee80211_set_wmm_default(sdata);
}
/* /*
* ieee80211_sta_work is disabled while network interface * ieee80211_sta_work is disabled while network interface
@ -551,7 +558,7 @@ static int ieee80211_stop(struct net_device *dev)
synchronize_rcu(); synchronize_rcu();
skb_queue_purge(&sdata->u.sta.skb_queue); skb_queue_purge(&sdata->u.sta.skb_queue);
if (local->scan_dev == sdata->dev) { if (local->scan_sdata == sdata) {
if (!local->ops->hw_scan) { if (!local->ops->hw_scan) {
local->sta_sw_scanning = 0; local->sta_sw_scanning = 0;
cancel_delayed_work(&local->scan_work); cancel_delayed_work(&local->scan_work);
@ -593,379 +600,6 @@ static int ieee80211_stop(struct net_device *dev)
return 0; return 0;
} }
int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
{
struct ieee80211_local *local = hw_to_local(hw);
struct sta_info *sta;
struct ieee80211_sub_if_data *sdata;
u16 start_seq_num;
u8 *state;
int ret;
DECLARE_MAC_BUF(mac);
if (tid >= STA_TID_NUM)
return -EINVAL;
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "Open BA session requested for %s tid %u\n",
print_mac(mac, ra), tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
rcu_read_lock();
sta = sta_info_get(local, ra);
if (!sta) {
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "Could not find the station\n");
#endif
ret = -ENOENT;
goto exit;
}
spin_lock_bh(&sta->lock);
/* we have tried too many times, receiver does not want A-MPDU */
if (sta->ampdu_mlme.addba_req_num[tid] > HT_AGG_MAX_RETRIES) {
ret = -EBUSY;
goto err_unlock_sta;
}
state = &sta->ampdu_mlme.tid_state_tx[tid];
/* check if the TID is not in aggregation flow already */
if (*state != HT_AGG_STATE_IDLE) {
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "BA request denied - session is not "
"idle on tid %u\n", tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
ret = -EAGAIN;
goto err_unlock_sta;
}
/* prepare A-MPDU MLME for Tx aggregation */
sta->ampdu_mlme.tid_tx[tid] =
kmalloc(sizeof(struct tid_ampdu_tx), GFP_ATOMIC);
if (!sta->ampdu_mlme.tid_tx[tid]) {
#ifdef CONFIG_MAC80211_HT_DEBUG
if (net_ratelimit())
printk(KERN_ERR "allocate tx mlme to tid %d failed\n",
tid);
#endif
ret = -ENOMEM;
goto err_unlock_sta;
}
/* Tx timer */
sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer.function =
sta_addba_resp_timer_expired;
sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer.data =
(unsigned long)&sta->timer_to_tid[tid];
init_timer(&sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer);
/* create a new queue for this aggregation */
ret = ieee80211_ht_agg_queue_add(local, sta, tid);
/* case no queue is available to aggregation
* don't switch to aggregation */
if (ret) {
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "BA request denied - queue unavailable for"
" tid %d\n", tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
goto err_unlock_queue;
}
sdata = sta->sdata;
/* Ok, the Addba frame hasn't been sent yet, but if the driver calls the
* call back right away, it must see that the flow has begun */
*state |= HT_ADDBA_REQUESTED_MSK;
/* This is slightly racy because the queue isn't stopped */
start_seq_num = sta->tid_seq[tid];
if (local->ops->ampdu_action)
ret = local->ops->ampdu_action(hw, IEEE80211_AMPDU_TX_START,
ra, tid, &start_seq_num);
if (ret) {
/* No need to requeue the packets in the agg queue, since we
* held the tx lock: no packet could be enqueued to the newly
* allocated queue */
ieee80211_ht_agg_queue_remove(local, sta, tid, 0);
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "BA request denied - HW unavailable for"
" tid %d\n", tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
*state = HT_AGG_STATE_IDLE;
goto err_unlock_queue;
}
/* Will put all the packets in the new SW queue */
ieee80211_requeue(local, ieee802_1d_to_ac[tid]);
spin_unlock_bh(&sta->lock);
/* send an addBA request */
sta->ampdu_mlme.dialog_token_allocator++;
sta->ampdu_mlme.tid_tx[tid]->dialog_token =
sta->ampdu_mlme.dialog_token_allocator;
sta->ampdu_mlme.tid_tx[tid]->ssn = start_seq_num;
ieee80211_send_addba_request(sta->sdata, ra, tid,
sta->ampdu_mlme.tid_tx[tid]->dialog_token,
sta->ampdu_mlme.tid_tx[tid]->ssn,
0x40, 5000);
/* activate the timer for the recipient's addBA response */
sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer.expires =
jiffies + ADDBA_RESP_INTERVAL;
add_timer(&sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer);
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "activated addBA response timer on tid %d\n", tid);
#endif
goto exit;
err_unlock_queue:
kfree(sta->ampdu_mlme.tid_tx[tid]);
sta->ampdu_mlme.tid_tx[tid] = NULL;
ret = -EBUSY;
err_unlock_sta:
spin_unlock_bh(&sta->lock);
exit:
rcu_read_unlock();
return ret;
}
EXPORT_SYMBOL(ieee80211_start_tx_ba_session);
int ieee80211_stop_tx_ba_session(struct ieee80211_hw *hw,
u8 *ra, u16 tid,
enum ieee80211_back_parties initiator)
{
struct ieee80211_local *local = hw_to_local(hw);
struct sta_info *sta;
u8 *state;
int ret = 0;
DECLARE_MAC_BUF(mac);
if (tid >= STA_TID_NUM)
return -EINVAL;
rcu_read_lock();
sta = sta_info_get(local, ra);
if (!sta) {
rcu_read_unlock();
return -ENOENT;
}
/* check if the TID is in aggregation */
state = &sta->ampdu_mlme.tid_state_tx[tid];
spin_lock_bh(&sta->lock);
if (*state != HT_AGG_STATE_OPERATIONAL) {
ret = -ENOENT;
goto stop_BA_exit;
}
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "Tx BA session stop requested for %s tid %u\n",
print_mac(mac, ra), tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
ieee80211_stop_queue(hw, sta->tid_to_tx_q[tid]);
*state = HT_AGG_STATE_REQ_STOP_BA_MSK |
(initiator << HT_AGG_STATE_INITIATOR_SHIFT);
if (local->ops->ampdu_action)
ret = local->ops->ampdu_action(hw, IEEE80211_AMPDU_TX_STOP,
ra, tid, NULL);
/* case HW denied going back to legacy */
if (ret) {
WARN_ON(ret != -EBUSY);
*state = HT_AGG_STATE_OPERATIONAL;
ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]);
goto stop_BA_exit;
}
stop_BA_exit:
spin_unlock_bh(&sta->lock);
rcu_read_unlock();
return ret;
}
EXPORT_SYMBOL(ieee80211_stop_tx_ba_session);
void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid)
{
struct ieee80211_local *local = hw_to_local(hw);
struct sta_info *sta;
u8 *state;
DECLARE_MAC_BUF(mac);
if (tid >= STA_TID_NUM) {
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "Bad TID value: tid = %d (>= %d)\n",
tid, STA_TID_NUM);
#endif
return;
}
rcu_read_lock();
sta = sta_info_get(local, ra);
if (!sta) {
rcu_read_unlock();
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "Could not find station: %s\n",
print_mac(mac, ra));
#endif
return;
}
state = &sta->ampdu_mlme.tid_state_tx[tid];
spin_lock_bh(&sta->lock);
if (!(*state & HT_ADDBA_REQUESTED_MSK)) {
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "addBA was not requested yet, state is %d\n",
*state);
#endif
spin_unlock_bh(&sta->lock);
rcu_read_unlock();
return;
}
WARN_ON_ONCE(*state & HT_ADDBA_DRV_READY_MSK);
*state |= HT_ADDBA_DRV_READY_MSK;
if (*state == HT_AGG_STATE_OPERATIONAL) {
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "Aggregation is on for tid %d \n", tid);
#endif
ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]);
}
spin_unlock_bh(&sta->lock);
rcu_read_unlock();
}
EXPORT_SYMBOL(ieee80211_start_tx_ba_cb);
void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid)
{
struct ieee80211_local *local = hw_to_local(hw);
struct sta_info *sta;
u8 *state;
int agg_queue;
DECLARE_MAC_BUF(mac);
if (tid >= STA_TID_NUM) {
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "Bad TID value: tid = %d (>= %d)\n",
tid, STA_TID_NUM);
#endif
return;
}
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "Stopping Tx BA session for %s tid %d\n",
print_mac(mac, ra), tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
rcu_read_lock();
sta = sta_info_get(local, ra);
if (!sta) {
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "Could not find station: %s\n",
print_mac(mac, ra));
#endif
rcu_read_unlock();
return;
}
state = &sta->ampdu_mlme.tid_state_tx[tid];
/* NOTE: no need to use sta->lock in this state check, as
* ieee80211_stop_tx_ba_session will let only one stop call to
* pass through per sta/tid
*/
if ((*state & HT_AGG_STATE_REQ_STOP_BA_MSK) == 0) {
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "unexpected callback to A-MPDU stop\n");
#endif
rcu_read_unlock();
return;
}
if (*state & HT_AGG_STATE_INITIATOR_MSK)
ieee80211_send_delba(sta->sdata, ra, tid,
WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE);
agg_queue = sta->tid_to_tx_q[tid];
ieee80211_ht_agg_queue_remove(local, sta, tid, 1);
/* We just requeued the all the frames that were in the
* removed queue, and since we might miss a softirq we do
* netif_schedule_queue. ieee80211_wake_queue is not used
* here as this queue is not necessarily stopped
*/
netif_schedule_queue(netdev_get_tx_queue(local->mdev, agg_queue));
spin_lock_bh(&sta->lock);
*state = HT_AGG_STATE_IDLE;
sta->ampdu_mlme.addba_req_num[tid] = 0;
kfree(sta->ampdu_mlme.tid_tx[tid]);
sta->ampdu_mlme.tid_tx[tid] = NULL;
spin_unlock_bh(&sta->lock);
rcu_read_unlock();
}
EXPORT_SYMBOL(ieee80211_stop_tx_ba_cb);
void ieee80211_start_tx_ba_cb_irqsafe(struct ieee80211_hw *hw,
const u8 *ra, u16 tid)
{
struct ieee80211_local *local = hw_to_local(hw);
struct ieee80211_ra_tid *ra_tid;
struct sk_buff *skb = dev_alloc_skb(0);
if (unlikely(!skb)) {
#ifdef CONFIG_MAC80211_HT_DEBUG
if (net_ratelimit())
printk(KERN_WARNING "%s: Not enough memory, "
"dropping start BA session", skb->dev->name);
#endif
return;
}
ra_tid = (struct ieee80211_ra_tid *) &skb->cb;
memcpy(&ra_tid->ra, ra, ETH_ALEN);
ra_tid->tid = tid;
skb->pkt_type = IEEE80211_ADDBA_MSG;
skb_queue_tail(&local->skb_queue, skb);
tasklet_schedule(&local->tasklet);
}
EXPORT_SYMBOL(ieee80211_start_tx_ba_cb_irqsafe);
void ieee80211_stop_tx_ba_cb_irqsafe(struct ieee80211_hw *hw,
const u8 *ra, u16 tid)
{
struct ieee80211_local *local = hw_to_local(hw);
struct ieee80211_ra_tid *ra_tid;
struct sk_buff *skb = dev_alloc_skb(0);
if (unlikely(!skb)) {
#ifdef CONFIG_MAC80211_HT_DEBUG
if (net_ratelimit())
printk(KERN_WARNING "%s: Not enough memory, "
"dropping stop BA session", skb->dev->name);
#endif
return;
}
ra_tid = (struct ieee80211_ra_tid *) &skb->cb;
memcpy(&ra_tid->ra, ra, ETH_ALEN);
ra_tid->tid = tid;
skb->pkt_type = IEEE80211_DELBA_MSG;
skb_queue_tail(&local->skb_queue, skb);
tasklet_schedule(&local->tasklet);
}
EXPORT_SYMBOL(ieee80211_stop_tx_ba_cb_irqsafe);
static void ieee80211_set_multicast_list(struct net_device *dev) static void ieee80211_set_multicast_list(struct net_device *dev)
{ {
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
@ -1140,8 +774,8 @@ u32 ieee80211_handle_ht(struct ieee80211_local *local, int enable_ht,
ht_conf.ht_supported = 1; ht_conf.ht_supported = 1;
ht_conf.cap = req_ht_cap->cap & sband->ht_info.cap; ht_conf.cap = req_ht_cap->cap & sband->ht_info.cap;
ht_conf.cap &= ~(IEEE80211_HT_CAP_MIMO_PS); ht_conf.cap &= ~(IEEE80211_HT_CAP_SM_PS);
ht_conf.cap |= sband->ht_info.cap & IEEE80211_HT_CAP_MIMO_PS; ht_conf.cap |= sband->ht_info.cap & IEEE80211_HT_CAP_SM_PS;
ht_bss_conf.primary_channel = req_bss_cap->primary_channel; ht_bss_conf.primary_channel = req_bss_cap->primary_channel;
ht_bss_conf.bss_cap = req_bss_cap->bss_cap; ht_bss_conf.bss_cap = req_bss_cap->bss_cap;
ht_bss_conf.bss_op_mode = req_bss_cap->bss_op_mode; ht_bss_conf.bss_op_mode = req_bss_cap->bss_op_mode;

View File

@ -149,7 +149,7 @@ static int mesh_path_sel_frame_tx(enum mpath_frame_type action, u8 flags,
pos += ETH_ALEN; pos += ETH_ALEN;
memcpy(pos, &dst_dsn, 4); memcpy(pos, &dst_dsn, 4);
ieee80211_sta_tx(sdata, skb, 0); ieee80211_tx_skb(sdata, skb, 0);
return 0; return 0;
} }
@ -198,7 +198,7 @@ int mesh_path_error_tx(u8 *dst, __le32 dst_dsn, u8 *ra,
pos += ETH_ALEN; pos += ETH_ALEN;
memcpy(pos, &dst_dsn, 4); memcpy(pos, &dst_dsn, 4);
ieee80211_sta_tx(sdata, skb, 0); ieee80211_tx_skb(sdata, skb, 0);
return 0; return 0;
} }
@ -581,6 +581,10 @@ void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata,
size_t baselen; size_t baselen;
u32 last_hop_metric; u32 last_hop_metric;
/* need action_code */
if (len < IEEE80211_MIN_ACTION_SIZE + 1)
return;
baselen = (u8 *) mgmt->u.action.u.mesh_action.variable - (u8 *) mgmt; baselen = (u8 *) mgmt->u.action.u.mesh_action.variable - (u8 *) mgmt;
ieee802_11_parse_elems(mgmt->u.action.u.mesh_action.variable, ieee802_11_parse_elems(mgmt->u.action.u.mesh_action.variable,
len - baselen, &elems); len - baselen, &elems);

View File

@ -217,7 +217,7 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
memcpy(pos, &reason, 2); memcpy(pos, &reason, 2);
} }
ieee80211_sta_tx(sdata, skb, 0); ieee80211_tx_skb(sdata, skb, 0);
return 0; return 0;
} }
@ -421,6 +421,10 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
DECLARE_MAC_BUF(mac); DECLARE_MAC_BUF(mac);
#endif #endif
/* need action_code, aux */
if (len < IEEE80211_MIN_ACTION_SIZE + 3)
return;
if (is_multicast_ether_addr(mgmt->da)) { if (is_multicast_ether_addr(mgmt->da)) {
mpl_dbg("Mesh plink: ignore frame from multicast address"); mpl_dbg("Mesh plink: ignore frame from multicast address");
return; return;

File diff suppressed because it is too large Load Diff

View File

@ -1511,22 +1511,95 @@ ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx)
} }
static ieee80211_rx_result debug_noinline static ieee80211_rx_result debug_noinline
ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx) ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
{ {
struct ieee80211_sub_if_data *sdata; struct ieee80211_local *local = rx->local;
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) rx->skb->data;
int len = rx->skb->len;
if (!ieee80211_is_action(mgmt->frame_control))
return RX_CONTINUE;
if (!rx->sta)
return RX_DROP_MONITOR;
if (!(rx->flags & IEEE80211_RX_RA_MATCH)) if (!(rx->flags & IEEE80211_RX_RA_MATCH))
return RX_DROP_MONITOR; return RX_DROP_MONITOR;
sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev); /* all categories we currently handle have action_code */
if ((sdata->vif.type == IEEE80211_IF_TYPE_STA || if (len < IEEE80211_MIN_ACTION_SIZE + 1)
sdata->vif.type == IEEE80211_IF_TYPE_IBSS ||
sdata->vif.type == IEEE80211_IF_TYPE_MESH_POINT) &&
!(sdata->flags & IEEE80211_SDATA_USERSPACE_MLME))
ieee80211_sta_rx_mgmt(sdata, rx->skb, rx->status);
else
return RX_DROP_MONITOR; return RX_DROP_MONITOR;
/*
* FIXME: revisit this, I'm sure we should handle most
* of these frames in other modes as well!
*/
if (sdata->vif.type != IEEE80211_IF_TYPE_STA &&
sdata->vif.type != IEEE80211_IF_TYPE_IBSS)
return RX_DROP_MONITOR;
switch (mgmt->u.action.category) {
case WLAN_CATEGORY_BACK:
switch (mgmt->u.action.u.addba_req.action_code) {
case WLAN_ACTION_ADDBA_REQ:
if (len < (IEEE80211_MIN_ACTION_SIZE +
sizeof(mgmt->u.action.u.addba_req)))
return RX_DROP_MONITOR;
ieee80211_process_addba_request(local, rx->sta, mgmt, len);
break;
case WLAN_ACTION_ADDBA_RESP:
if (len < (IEEE80211_MIN_ACTION_SIZE +
sizeof(mgmt->u.action.u.addba_resp)))
return RX_DROP_MONITOR;
ieee80211_process_addba_resp(local, rx->sta, mgmt, len);
break;
case WLAN_ACTION_DELBA:
if (len < (IEEE80211_MIN_ACTION_SIZE +
sizeof(mgmt->u.action.u.delba)))
return RX_DROP_MONITOR;
ieee80211_process_delba(sdata, rx->sta, mgmt, len);
break;
}
break;
case WLAN_CATEGORY_SPECTRUM_MGMT:
if (local->hw.conf.channel->band != IEEE80211_BAND_5GHZ)
return RX_DROP_MONITOR;
switch (mgmt->u.action.u.measurement.action_code) {
case WLAN_ACTION_SPCT_MSR_REQ:
if (len < (IEEE80211_MIN_ACTION_SIZE +
sizeof(mgmt->u.action.u.measurement)))
return RX_DROP_MONITOR;
ieee80211_process_measurement_req(sdata, mgmt, len);
break;
}
break;
default:
return RX_CONTINUE;
}
rx->sta->rx_packets++;
dev_kfree_skb(rx->skb);
return RX_QUEUED;
}
static ieee80211_rx_result debug_noinline
ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
if (!(rx->flags & IEEE80211_RX_RA_MATCH))
return RX_DROP_MONITOR;
if (sdata->vif.type != IEEE80211_IF_TYPE_STA &&
sdata->vif.type != IEEE80211_IF_TYPE_IBSS &&
sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT)
return RX_DROP_MONITOR;
if (sdata->flags & IEEE80211_SDATA_USERSPACE_MLME)
return RX_DROP_MONITOR;
ieee80211_sta_rx_mgmt(sdata, rx->skb, rx->status);
return RX_QUEUED; return RX_QUEUED;
} }
@ -1689,6 +1762,7 @@ static void ieee80211_invoke_rx_handlers(struct ieee80211_sub_if_data *sdata,
CALL_RXH(ieee80211_rx_h_mesh_fwding); CALL_RXH(ieee80211_rx_h_mesh_fwding);
CALL_RXH(ieee80211_rx_h_data) CALL_RXH(ieee80211_rx_h_data)
CALL_RXH(ieee80211_rx_h_ctrl) CALL_RXH(ieee80211_rx_h_ctrl)
CALL_RXH(ieee80211_rx_h_action)
CALL_RXH(ieee80211_rx_h_mgmt) CALL_RXH(ieee80211_rx_h_mgmt)
#undef CALL_RXH #undef CALL_RXH

933
net/mac80211/scan.c Normal file
View File

@ -0,0 +1,933 @@
/*
* Scanning implementation
*
* Copyright 2003, Jouni Malinen <jkmaline@cc.hut.fi>
* Copyright 2004, Instant802 Networks, Inc.
* Copyright 2005, Devicescape Software, Inc.
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
* Copyright 2007, Michael Wu <flamingice@sourmilk.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
/* TODO:
* order BSS list by RSSI(?) ("quality of AP")
* scan result table filtering (by capability (privacy, IBSS/BSS, WPA/RSN IE,
* SSID)
*/
#include <linux/wireless.h>
#include <linux/if_arp.h>
#include <net/mac80211.h>
#include <net/iw_handler.h>
#include "ieee80211_i.h"
#include "mesh.h"
#define IEEE80211_PROBE_DELAY (HZ / 33)
#define IEEE80211_CHANNEL_TIME (HZ / 33)
#define IEEE80211_PASSIVE_CHANNEL_TIME (HZ / 5)
void ieee80211_rx_bss_list_init(struct ieee80211_local *local)
{
spin_lock_init(&local->sta_bss_lock);
INIT_LIST_HEAD(&local->sta_bss_list);
}
void ieee80211_rx_bss_list_deinit(struct ieee80211_local *local)
{
struct ieee80211_sta_bss *bss, *tmp;
list_for_each_entry_safe(bss, tmp, &local->sta_bss_list, list)
ieee80211_rx_bss_put(local, bss);
}
struct ieee80211_sta_bss *
ieee80211_rx_bss_get(struct ieee80211_local *local, u8 *bssid, int freq,
u8 *ssid, u8 ssid_len)
{
struct ieee80211_sta_bss *bss;
spin_lock_bh(&local->sta_bss_lock);
bss = local->sta_bss_hash[STA_HASH(bssid)];
while (bss) {
if (!bss_mesh_cfg(bss) &&
!memcmp(bss->bssid, bssid, ETH_ALEN) &&
bss->freq == freq &&
bss->ssid_len == ssid_len &&
(ssid_len == 0 || !memcmp(bss->ssid, ssid, ssid_len))) {
atomic_inc(&bss->users);
break;
}
bss = bss->hnext;
}
spin_unlock_bh(&local->sta_bss_lock);
return bss;
}
/* Caller must hold local->sta_bss_lock */
static void __ieee80211_rx_bss_hash_add(struct ieee80211_local *local,
struct ieee80211_sta_bss *bss)
{
u8 hash_idx;
if (bss_mesh_cfg(bss))
hash_idx = mesh_id_hash(bss_mesh_id(bss),
bss_mesh_id_len(bss));
else
hash_idx = STA_HASH(bss->bssid);
bss->hnext = local->sta_bss_hash[hash_idx];
local->sta_bss_hash[hash_idx] = bss;
}
/* Caller must hold local->sta_bss_lock */
static void __ieee80211_rx_bss_hash_del(struct ieee80211_local *local,
struct ieee80211_sta_bss *bss)
{
struct ieee80211_sta_bss *b, *prev = NULL;
b = local->sta_bss_hash[STA_HASH(bss->bssid)];
while (b) {
if (b == bss) {
if (!prev)
local->sta_bss_hash[STA_HASH(bss->bssid)] =
bss->hnext;
else
prev->hnext = bss->hnext;
break;
}
prev = b;
b = b->hnext;
}
}
struct ieee80211_sta_bss *
ieee80211_rx_bss_add(struct ieee80211_local *local, u8 *bssid, int freq,
u8 *ssid, u8 ssid_len)
{
struct ieee80211_sta_bss *bss;
bss = kzalloc(sizeof(*bss), GFP_ATOMIC);
if (!bss)
return NULL;
atomic_set(&bss->users, 2);
memcpy(bss->bssid, bssid, ETH_ALEN);
bss->freq = freq;
if (ssid && ssid_len <= IEEE80211_MAX_SSID_LEN) {
memcpy(bss->ssid, ssid, ssid_len);
bss->ssid_len = ssid_len;
}
spin_lock_bh(&local->sta_bss_lock);
/* TODO: order by RSSI? */
list_add_tail(&bss->list, &local->sta_bss_list);
__ieee80211_rx_bss_hash_add(local, bss);
spin_unlock_bh(&local->sta_bss_lock);
return bss;
}
#ifdef CONFIG_MAC80211_MESH
static struct ieee80211_sta_bss *
ieee80211_rx_mesh_bss_get(struct ieee80211_local *local, u8 *mesh_id, int mesh_id_len,
u8 *mesh_cfg, int freq)
{
struct ieee80211_sta_bss *bss;
spin_lock_bh(&local->sta_bss_lock);
bss = local->sta_bss_hash[mesh_id_hash(mesh_id, mesh_id_len)];
while (bss) {
if (bss_mesh_cfg(bss) &&
!memcmp(bss_mesh_cfg(bss), mesh_cfg, MESH_CFG_CMP_LEN) &&
bss->freq == freq &&
mesh_id_len == bss->mesh_id_len &&
(mesh_id_len == 0 || !memcmp(bss->mesh_id, mesh_id,
mesh_id_len))) {
atomic_inc(&bss->users);
break;
}
bss = bss->hnext;
}
spin_unlock_bh(&local->sta_bss_lock);
return bss;
}
static struct ieee80211_sta_bss *
ieee80211_rx_mesh_bss_add(struct ieee80211_local *local, u8 *mesh_id, int mesh_id_len,
u8 *mesh_cfg, int mesh_config_len, int freq)
{
struct ieee80211_sta_bss *bss;
if (mesh_config_len != MESH_CFG_LEN)
return NULL;
bss = kzalloc(sizeof(*bss), GFP_ATOMIC);
if (!bss)
return NULL;
bss->mesh_cfg = kmalloc(MESH_CFG_CMP_LEN, GFP_ATOMIC);
if (!bss->mesh_cfg) {
kfree(bss);
return NULL;
}
if (mesh_id_len && mesh_id_len <= IEEE80211_MAX_MESH_ID_LEN) {
bss->mesh_id = kmalloc(mesh_id_len, GFP_ATOMIC);
if (!bss->mesh_id) {
kfree(bss->mesh_cfg);
kfree(bss);
return NULL;
}
memcpy(bss->mesh_id, mesh_id, mesh_id_len);
}
atomic_set(&bss->users, 2);
memcpy(bss->mesh_cfg, mesh_cfg, MESH_CFG_CMP_LEN);
bss->mesh_id_len = mesh_id_len;
bss->freq = freq;
spin_lock_bh(&local->sta_bss_lock);
/* TODO: order by RSSI? */
list_add_tail(&bss->list, &local->sta_bss_list);
__ieee80211_rx_bss_hash_add(local, bss);
spin_unlock_bh(&local->sta_bss_lock);
return bss;
}
#endif
static void ieee80211_rx_bss_free(struct ieee80211_sta_bss *bss)
{
kfree(bss->ies);
kfree(bss_mesh_id(bss));
kfree(bss_mesh_cfg(bss));
kfree(bss);
}
void ieee80211_rx_bss_put(struct ieee80211_local *local,
struct ieee80211_sta_bss *bss)
{
local_bh_disable();
if (!atomic_dec_and_lock(&bss->users, &local->sta_bss_lock)) {
local_bh_enable();
return;
}
__ieee80211_rx_bss_hash_del(local, bss);
list_del(&bss->list);
spin_unlock_bh(&local->sta_bss_lock);
ieee80211_rx_bss_free(bss);
}
struct ieee80211_sta_bss *
ieee80211_bss_info_update(struct ieee80211_local *local,
struct ieee80211_rx_status *rx_status,
struct ieee80211_mgmt *mgmt,
size_t len,
struct ieee802_11_elems *elems,
int freq, bool beacon)
{
struct ieee80211_sta_bss *bss;
int clen;
#ifdef CONFIG_MAC80211_MESH
if (elems->mesh_config)
bss = ieee80211_rx_mesh_bss_get(local, elems->mesh_id,
elems->mesh_id_len, elems->mesh_config, freq);
else
#endif
bss = ieee80211_rx_bss_get(local, mgmt->bssid, freq,
elems->ssid, elems->ssid_len);
if (!bss) {
#ifdef CONFIG_MAC80211_MESH
if (elems->mesh_config)
bss = ieee80211_rx_mesh_bss_add(local, elems->mesh_id,
elems->mesh_id_len, elems->mesh_config,
elems->mesh_config_len, freq);
else
#endif
bss = ieee80211_rx_bss_add(local, mgmt->bssid, freq,
elems->ssid, elems->ssid_len);
if (!bss)
return NULL;
} else {
#if 0
/* TODO: order by RSSI? */
spin_lock_bh(&local->sta_bss_lock);
list_move_tail(&bss->list, &local->sta_bss_list);
spin_unlock_bh(&local->sta_bss_lock);
#endif
}
/* save the ERP value so that it is available at association time */
if (elems->erp_info && elems->erp_info_len >= 1) {
bss->erp_value = elems->erp_info[0];
bss->has_erp_value = 1;
}
bss->beacon_int = le16_to_cpu(mgmt->u.beacon.beacon_int);
bss->capability = le16_to_cpu(mgmt->u.beacon.capab_info);
if (elems->tim) {
struct ieee80211_tim_ie *tim_ie =
(struct ieee80211_tim_ie *)elems->tim;
bss->dtim_period = tim_ie->dtim_period;
}
/* set default value for buggy APs */
if (!elems->tim || bss->dtim_period == 0)
bss->dtim_period = 1;
bss->supp_rates_len = 0;
if (elems->supp_rates) {
clen = IEEE80211_MAX_SUPP_RATES - bss->supp_rates_len;
if (clen > elems->supp_rates_len)
clen = elems->supp_rates_len;
memcpy(&bss->supp_rates[bss->supp_rates_len], elems->supp_rates,
clen);
bss->supp_rates_len += clen;
}
if (elems->ext_supp_rates) {
clen = IEEE80211_MAX_SUPP_RATES - bss->supp_rates_len;
if (clen > elems->ext_supp_rates_len)
clen = elems->ext_supp_rates_len;
memcpy(&bss->supp_rates[bss->supp_rates_len],
elems->ext_supp_rates, clen);
bss->supp_rates_len += clen;
}
bss->band = rx_status->band;
bss->timestamp = le64_to_cpu(mgmt->u.beacon.timestamp);
bss->last_update = jiffies;
bss->signal = rx_status->signal;
bss->noise = rx_status->noise;
bss->qual = rx_status->qual;
bss->wmm_used = elems->wmm_param || elems->wmm_info;
if (!beacon)
bss->last_probe_resp = jiffies;
/*
* For probe responses, or if we don't have any information yet,
* use the IEs from the beacon.
*/
if (!bss->ies || !beacon) {
if (bss->ies == NULL || bss->ies_len < elems->total_len) {
kfree(bss->ies);
bss->ies = kmalloc(elems->total_len, GFP_ATOMIC);
}
if (bss->ies) {
memcpy(bss->ies, elems->ie_start, elems->total_len);
bss->ies_len = elems->total_len;
} else
bss->ies_len = 0;
}
return bss;
}
ieee80211_rx_result
ieee80211_sta_rx_scan(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
struct ieee80211_rx_status *rx_status)
{
struct ieee80211_mgmt *mgmt;
struct ieee80211_sta_bss *bss;
u8 *elements;
struct ieee80211_channel *channel;
size_t baselen;
int freq;
__le16 fc;
bool presp, beacon = false;
struct ieee802_11_elems elems;
if (skb->len < 2)
return RX_DROP_UNUSABLE;
mgmt = (struct ieee80211_mgmt *) skb->data;
fc = mgmt->frame_control;
if (ieee80211_is_ctl(fc))
return RX_CONTINUE;
if (skb->len < 24)
return RX_DROP_MONITOR;
presp = ieee80211_is_probe_resp(fc);
if (presp) {
/* ignore ProbeResp to foreign address */
if (memcmp(mgmt->da, sdata->dev->dev_addr, ETH_ALEN))
return RX_DROP_MONITOR;
presp = true;
elements = mgmt->u.probe_resp.variable;
baselen = offsetof(struct ieee80211_mgmt, u.probe_resp.variable);
} else {
beacon = ieee80211_is_beacon(fc);
baselen = offsetof(struct ieee80211_mgmt, u.beacon.variable);
elements = mgmt->u.beacon.variable;
}
if (!presp && !beacon)
return RX_CONTINUE;
if (baselen > skb->len)
return RX_DROP_MONITOR;
ieee802_11_parse_elems(elements, skb->len - baselen, &elems);
if (elems.ds_params && elems.ds_params_len == 1)
freq = ieee80211_channel_to_frequency(elems.ds_params[0]);
else
freq = rx_status->freq;
channel = ieee80211_get_channel(sdata->local->hw.wiphy, freq);
if (!channel || channel->flags & IEEE80211_CHAN_DISABLED)
return RX_DROP_MONITOR;
bss = ieee80211_bss_info_update(sdata->local, rx_status,
mgmt, skb->len, &elems,
freq, beacon);
ieee80211_rx_bss_put(sdata->local, bss);
dev_kfree_skb(skb);
return RX_QUEUED;
}
static void ieee80211_send_nullfunc(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
int powersave)
{
struct sk_buff *skb;
struct ieee80211_hdr *nullfunc;
__le16 fc;
skb = dev_alloc_skb(local->hw.extra_tx_headroom + 24);
if (!skb) {
printk(KERN_DEBUG "%s: failed to allocate buffer for nullfunc "
"frame\n", sdata->dev->name);
return;
}
skb_reserve(skb, local->hw.extra_tx_headroom);
nullfunc = (struct ieee80211_hdr *) skb_put(skb, 24);
memset(nullfunc, 0, 24);
fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC |
IEEE80211_FCTL_TODS);
if (powersave)
fc |= cpu_to_le16(IEEE80211_FCTL_PM);
nullfunc->frame_control = fc;
memcpy(nullfunc->addr1, sdata->u.sta.bssid, ETH_ALEN);
memcpy(nullfunc->addr2, sdata->dev->dev_addr, ETH_ALEN);
memcpy(nullfunc->addr3, sdata->u.sta.bssid, ETH_ALEN);
ieee80211_tx_skb(sdata, skb, 0);
}
static void ieee80211_restart_sta_timer(struct ieee80211_sub_if_data *sdata)
{
if (sdata->vif.type == IEEE80211_IF_TYPE_STA ||
ieee80211_vif_is_mesh(&sdata->vif))
ieee80211_sta_timer((unsigned long)sdata);
}
void ieee80211_scan_completed(struct ieee80211_hw *hw)
{
struct ieee80211_local *local = hw_to_local(hw);
struct ieee80211_sub_if_data *sdata;
union iwreq_data wrqu;
local->last_scan_completed = jiffies;
memset(&wrqu, 0, sizeof(wrqu));
wireless_send_event(local->scan_sdata->dev, SIOCGIWSCAN, &wrqu, NULL);
if (local->sta_hw_scanning) {
local->sta_hw_scanning = 0;
if (ieee80211_hw_config(local))
printk(KERN_DEBUG "%s: failed to restore operational "
"channel after scan\n", wiphy_name(local->hw.wiphy));
/* Restart STA timer for HW scan case */
rcu_read_lock();
list_for_each_entry_rcu(sdata, &local->interfaces, list)
ieee80211_restart_sta_timer(sdata);
rcu_read_unlock();
goto done;
}
local->sta_sw_scanning = 0;
if (ieee80211_hw_config(local))
printk(KERN_DEBUG "%s: failed to restore operational "
"channel after scan\n", wiphy_name(local->hw.wiphy));
netif_tx_lock_bh(local->mdev);
netif_addr_lock(local->mdev);
local->filter_flags &= ~FIF_BCN_PRBRESP_PROMISC;
local->ops->configure_filter(local_to_hw(local),
FIF_BCN_PRBRESP_PROMISC,
&local->filter_flags,
local->mdev->mc_count,
local->mdev->mc_list);
netif_addr_unlock(local->mdev);
netif_tx_unlock_bh(local->mdev);
rcu_read_lock();
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
/* Tell AP we're back */
if (sdata->vif.type == IEEE80211_IF_TYPE_STA) {
if (sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED) {
ieee80211_send_nullfunc(local, sdata, 0);
netif_tx_wake_all_queues(sdata->dev);
}
} else
netif_tx_wake_all_queues(sdata->dev);
ieee80211_restart_sta_timer(sdata);
}
rcu_read_unlock();
done:
ieee80211_mlme_notify_scan_completed(local);
}
EXPORT_SYMBOL(ieee80211_scan_completed);
void ieee80211_sta_scan_work(struct work_struct *work)
{
struct ieee80211_local *local =
container_of(work, struct ieee80211_local, scan_work.work);
struct ieee80211_sub_if_data *sdata = local->scan_sdata;
struct ieee80211_supported_band *sband;
struct ieee80211_channel *chan;
int skip;
unsigned long next_delay = 0;
if (!local->sta_sw_scanning)
return;
switch (local->scan_state) {
case SCAN_SET_CHANNEL:
/*
* Get current scan band. scan_band may be IEEE80211_NUM_BANDS
* after we successfully scanned the last channel of the last
* band (and the last band is supported by the hw)
*/
if (local->scan_band < IEEE80211_NUM_BANDS)
sband = local->hw.wiphy->bands[local->scan_band];
else
sband = NULL;
/*
* If we are at an unsupported band and have more bands
* left to scan, advance to the next supported one.
*/
while (!sband && local->scan_band < IEEE80211_NUM_BANDS - 1) {
local->scan_band++;
sband = local->hw.wiphy->bands[local->scan_band];
local->scan_channel_idx = 0;
}
/* if no more bands/channels left, complete scan */
if (!sband || local->scan_channel_idx >= sband->n_channels) {
ieee80211_scan_completed(local_to_hw(local));
return;
}
skip = 0;
chan = &sband->channels[local->scan_channel_idx];
if (chan->flags & IEEE80211_CHAN_DISABLED ||
(sdata->vif.type == IEEE80211_IF_TYPE_IBSS &&
chan->flags & IEEE80211_CHAN_NO_IBSS))
skip = 1;
if (!skip) {
local->scan_channel = chan;
if (ieee80211_hw_config(local)) {
printk(KERN_DEBUG "%s: failed to set freq to "
"%d MHz for scan\n", wiphy_name(local->hw.wiphy),
chan->center_freq);
skip = 1;
}
}
/* advance state machine to next channel/band */
local->scan_channel_idx++;
if (local->scan_channel_idx >= sband->n_channels) {
/*
* scan_band may end up == IEEE80211_NUM_BANDS, but
* we'll catch that case above and complete the scan
* if that is the case.
*/
local->scan_band++;
local->scan_channel_idx = 0;
}
if (skip)
break;
next_delay = IEEE80211_PROBE_DELAY +
usecs_to_jiffies(local->hw.channel_change_time);
local->scan_state = SCAN_SEND_PROBE;
break;
case SCAN_SEND_PROBE:
next_delay = IEEE80211_PASSIVE_CHANNEL_TIME;
local->scan_state = SCAN_SET_CHANNEL;
if (local->scan_channel->flags & IEEE80211_CHAN_PASSIVE_SCAN)
break;
ieee80211_send_probe_req(sdata, NULL, local->scan_ssid,
local->scan_ssid_len);
next_delay = IEEE80211_CHANNEL_TIME;
break;
}
if (local->sta_sw_scanning)
queue_delayed_work(local->hw.workqueue, &local->scan_work,
next_delay);
}
int ieee80211_sta_start_scan(struct ieee80211_sub_if_data *scan_sdata,
u8 *ssid, size_t ssid_len)
{
struct ieee80211_local *local = scan_sdata->local;
struct ieee80211_sub_if_data *sdata;
if (ssid_len > IEEE80211_MAX_SSID_LEN)
return -EINVAL;
/* MLME-SCAN.request (page 118) page 144 (11.1.3.1)
* BSSType: INFRASTRUCTURE, INDEPENDENT, ANY_BSS
* BSSID: MACAddress
* SSID
* ScanType: ACTIVE, PASSIVE
* ProbeDelay: delay (in microseconds) to be used prior to transmitting
* a Probe frame during active scanning
* ChannelList
* MinChannelTime (>= ProbeDelay), in TU
* MaxChannelTime: (>= MinChannelTime), in TU
*/
/* MLME-SCAN.confirm
* BSSDescriptionSet
* ResultCode: SUCCESS, INVALID_PARAMETERS
*/
if (local->sta_sw_scanning || local->sta_hw_scanning) {
if (local->scan_sdata == scan_sdata)
return 0;
return -EBUSY;
}
if (local->ops->hw_scan) {
int rc = local->ops->hw_scan(local_to_hw(local),
ssid, ssid_len);
if (!rc) {
local->sta_hw_scanning = 1;
local->scan_sdata = scan_sdata;
}
return rc;
}
local->sta_sw_scanning = 1;
rcu_read_lock();
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
if (sdata->vif.type == IEEE80211_IF_TYPE_STA) {
if (sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED) {
netif_tx_stop_all_queues(sdata->dev);
ieee80211_send_nullfunc(local, sdata, 1);
}
} else
netif_tx_stop_all_queues(sdata->dev);
}
rcu_read_unlock();
if (ssid) {
local->scan_ssid_len = ssid_len;
memcpy(local->scan_ssid, ssid, ssid_len);
} else
local->scan_ssid_len = 0;
local->scan_state = SCAN_SET_CHANNEL;
local->scan_channel_idx = 0;
local->scan_band = IEEE80211_BAND_2GHZ;
local->scan_sdata = scan_sdata;
netif_addr_lock_bh(local->mdev);
local->filter_flags |= FIF_BCN_PRBRESP_PROMISC;
local->ops->configure_filter(local_to_hw(local),
FIF_BCN_PRBRESP_PROMISC,
&local->filter_flags,
local->mdev->mc_count,
local->mdev->mc_list);
netif_addr_unlock_bh(local->mdev);
/* TODO: start scan as soon as all nullfunc frames are ACKed */
queue_delayed_work(local->hw.workqueue, &local->scan_work,
IEEE80211_CHANNEL_TIME);
return 0;
}
int ieee80211_sta_req_scan(struct ieee80211_sub_if_data *sdata, u8 *ssid, size_t ssid_len)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_if_sta *ifsta;
if (sdata->vif.type != IEEE80211_IF_TYPE_STA)
return ieee80211_sta_start_scan(sdata, ssid, ssid_len);
/*
* STA has a state machine that might need to defer scanning
* while it's trying to associate/authenticate, therefore we
* queue it up to the state machine in that case.
*/
if (local->sta_sw_scanning || local->sta_hw_scanning) {
if (local->scan_sdata == sdata)
return 0;
return -EBUSY;
}
ifsta = &sdata->u.sta;
ifsta->scan_ssid_len = ssid_len;
if (ssid_len)
memcpy(ifsta->scan_ssid, ssid, ssid_len);
set_bit(IEEE80211_STA_REQ_SCAN, &ifsta->request);
queue_work(local->hw.workqueue, &ifsta->work);
return 0;
}
static void ieee80211_sta_add_scan_ies(struct iw_request_info *info,
struct ieee80211_sta_bss *bss,
char **current_ev, char *end_buf)
{
u8 *pos, *end, *next;
struct iw_event iwe;
if (bss == NULL || bss->ies == NULL)
return;
/*
* If needed, fragment the IEs buffer (at IE boundaries) into short
* enough fragments to fit into IW_GENERIC_IE_MAX octet messages.
*/
pos = bss->ies;
end = pos + bss->ies_len;
while (end - pos > IW_GENERIC_IE_MAX) {
next = pos + 2 + pos[1];
while (next + 2 + next[1] - pos < IW_GENERIC_IE_MAX)
next = next + 2 + next[1];
memset(&iwe, 0, sizeof(iwe));
iwe.cmd = IWEVGENIE;
iwe.u.data.length = next - pos;
*current_ev = iwe_stream_add_point(info, *current_ev,
end_buf, &iwe, pos);
pos = next;
}
if (end > pos) {
memset(&iwe, 0, sizeof(iwe));
iwe.cmd = IWEVGENIE;
iwe.u.data.length = end - pos;
*current_ev = iwe_stream_add_point(info, *current_ev,
end_buf, &iwe, pos);
}
}
static char *
ieee80211_sta_scan_result(struct ieee80211_local *local,
struct iw_request_info *info,
struct ieee80211_sta_bss *bss,
char *current_ev, char *end_buf)
{
struct iw_event iwe;
char *buf;
if (time_after(jiffies,
bss->last_update + IEEE80211_SCAN_RESULT_EXPIRE))
return current_ev;
memset(&iwe, 0, sizeof(iwe));
iwe.cmd = SIOCGIWAP;
iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
memcpy(iwe.u.ap_addr.sa_data, bss->bssid, ETH_ALEN);
current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe,
IW_EV_ADDR_LEN);
memset(&iwe, 0, sizeof(iwe));
iwe.cmd = SIOCGIWESSID;
if (bss_mesh_cfg(bss)) {
iwe.u.data.length = bss_mesh_id_len(bss);
iwe.u.data.flags = 1;
current_ev = iwe_stream_add_point(info, current_ev, end_buf,
&iwe, bss_mesh_id(bss));
} else {
iwe.u.data.length = bss->ssid_len;
iwe.u.data.flags = 1;
current_ev = iwe_stream_add_point(info, current_ev, end_buf,
&iwe, bss->ssid);
}
if (bss->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)
|| bss_mesh_cfg(bss)) {
memset(&iwe, 0, sizeof(iwe));
iwe.cmd = SIOCGIWMODE;
if (bss_mesh_cfg(bss))
iwe.u.mode = IW_MODE_MESH;
else if (bss->capability & WLAN_CAPABILITY_ESS)
iwe.u.mode = IW_MODE_MASTER;
else
iwe.u.mode = IW_MODE_ADHOC;
current_ev = iwe_stream_add_event(info, current_ev, end_buf,
&iwe, IW_EV_UINT_LEN);
}
memset(&iwe, 0, sizeof(iwe));
iwe.cmd = SIOCGIWFREQ;
iwe.u.freq.m = ieee80211_frequency_to_channel(bss->freq);
iwe.u.freq.e = 0;
current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe,
IW_EV_FREQ_LEN);
memset(&iwe, 0, sizeof(iwe));
iwe.cmd = SIOCGIWFREQ;
iwe.u.freq.m = bss->freq;
iwe.u.freq.e = 6;
current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe,
IW_EV_FREQ_LEN);
memset(&iwe, 0, sizeof(iwe));
iwe.cmd = IWEVQUAL;
iwe.u.qual.qual = bss->qual;
iwe.u.qual.level = bss->signal;
iwe.u.qual.noise = bss->noise;
iwe.u.qual.updated = local->wstats_flags;
current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe,
IW_EV_QUAL_LEN);
memset(&iwe, 0, sizeof(iwe));
iwe.cmd = SIOCGIWENCODE;
if (bss->capability & WLAN_CAPABILITY_PRIVACY)
iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
else
iwe.u.data.flags = IW_ENCODE_DISABLED;
iwe.u.data.length = 0;
current_ev = iwe_stream_add_point(info, current_ev, end_buf,
&iwe, "");
ieee80211_sta_add_scan_ies(info, bss, &current_ev, end_buf);
if (bss->supp_rates_len > 0) {
/* display all supported rates in readable format */
char *p = current_ev + iwe_stream_lcp_len(info);
int i;
memset(&iwe, 0, sizeof(iwe));
iwe.cmd = SIOCGIWRATE;
/* Those two flags are ignored... */
iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
for (i = 0; i < bss->supp_rates_len; i++) {
iwe.u.bitrate.value = ((bss->supp_rates[i] &
0x7f) * 500000);
p = iwe_stream_add_value(info, current_ev, p,
end_buf, &iwe, IW_EV_PARAM_LEN);
}
current_ev = p;
}
buf = kmalloc(30, GFP_ATOMIC);
if (buf) {
memset(&iwe, 0, sizeof(iwe));
iwe.cmd = IWEVCUSTOM;
sprintf(buf, "tsf=%016llx", (unsigned long long)(bss->timestamp));
iwe.u.data.length = strlen(buf);
current_ev = iwe_stream_add_point(info, current_ev, end_buf,
&iwe, buf);
memset(&iwe, 0, sizeof(iwe));
iwe.cmd = IWEVCUSTOM;
sprintf(buf, " Last beacon: %dms ago",
jiffies_to_msecs(jiffies - bss->last_update));
iwe.u.data.length = strlen(buf);
current_ev = iwe_stream_add_point(info, current_ev,
end_buf, &iwe, buf);
kfree(buf);
}
if (bss_mesh_cfg(bss)) {
u8 *cfg = bss_mesh_cfg(bss);
buf = kmalloc(50, GFP_ATOMIC);
if (buf) {
memset(&iwe, 0, sizeof(iwe));
iwe.cmd = IWEVCUSTOM;
sprintf(buf, "Mesh network (version %d)", cfg[0]);
iwe.u.data.length = strlen(buf);
current_ev = iwe_stream_add_point(info, current_ev,
end_buf,
&iwe, buf);
sprintf(buf, "Path Selection Protocol ID: "
"0x%02X%02X%02X%02X", cfg[1], cfg[2], cfg[3],
cfg[4]);
iwe.u.data.length = strlen(buf);
current_ev = iwe_stream_add_point(info, current_ev,
end_buf,
&iwe, buf);
sprintf(buf, "Path Selection Metric ID: "
"0x%02X%02X%02X%02X", cfg[5], cfg[6], cfg[7],
cfg[8]);
iwe.u.data.length = strlen(buf);
current_ev = iwe_stream_add_point(info, current_ev,
end_buf,
&iwe, buf);
sprintf(buf, "Congestion Control Mode ID: "
"0x%02X%02X%02X%02X", cfg[9], cfg[10],
cfg[11], cfg[12]);
iwe.u.data.length = strlen(buf);
current_ev = iwe_stream_add_point(info, current_ev,
end_buf,
&iwe, buf);
sprintf(buf, "Channel Precedence: "
"0x%02X%02X%02X%02X", cfg[13], cfg[14],
cfg[15], cfg[16]);
iwe.u.data.length = strlen(buf);
current_ev = iwe_stream_add_point(info, current_ev,
end_buf,
&iwe, buf);
kfree(buf);
}
}
return current_ev;
}
int ieee80211_sta_scan_results(struct ieee80211_local *local,
struct iw_request_info *info,
char *buf, size_t len)
{
char *current_ev = buf;
char *end_buf = buf + len;
struct ieee80211_sta_bss *bss;
spin_lock_bh(&local->sta_bss_lock);
list_for_each_entry(bss, &local->sta_bss_list, list) {
if (buf + len - current_ev <= IW_EV_ADDR_LEN) {
spin_unlock_bh(&local->sta_bss_lock);
return -E2BIG;
}
current_ev = ieee80211_sta_scan_result(local, info, bss,
current_ev, end_buf);
}
spin_unlock_bh(&local->sta_bss_lock);
return current_ev - buf;
}

86
net/mac80211/spectmgmt.c Normal file
View File

@ -0,0 +1,86 @@
/*
* spectrum management
*
* Copyright 2003, Jouni Malinen <jkmaline@cc.hut.fi>
* Copyright 2002-2005, Instant802 Networks, Inc.
* Copyright 2005-2006, Devicescape Software, Inc.
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
* Copyright 2007, Michael Wu <flamingice@sourmilk.net>
* Copyright 2007-2008, Intel Corporation
* Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/ieee80211.h>
#include <net/wireless.h>
#include <net/mac80211.h>
#include "ieee80211_i.h"
#include "sta_info.h"
#include "wme.h"
static void ieee80211_send_refuse_measurement_request(struct ieee80211_sub_if_data *sdata,
struct ieee80211_msrment_ie *request_ie,
const u8 *da, const u8 *bssid,
u8 dialog_token)
{
struct ieee80211_local *local = sdata->local;
struct sk_buff *skb;
struct ieee80211_mgmt *msr_report;
skb = dev_alloc_skb(sizeof(*msr_report) + local->hw.extra_tx_headroom +
sizeof(struct ieee80211_msrment_ie));
if (!skb) {
printk(KERN_ERR "%s: failed to allocate buffer for "
"measurement report frame\n", sdata->dev->name);
return;
}
skb_reserve(skb, local->hw.extra_tx_headroom);
msr_report = (struct ieee80211_mgmt *)skb_put(skb, 24);
memset(msr_report, 0, 24);
memcpy(msr_report->da, da, ETH_ALEN);
memcpy(msr_report->sa, sdata->dev->dev_addr, ETH_ALEN);
memcpy(msr_report->bssid, bssid, ETH_ALEN);
msr_report->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_ACTION);
skb_put(skb, 1 + sizeof(msr_report->u.action.u.measurement));
msr_report->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT;
msr_report->u.action.u.measurement.action_code =
WLAN_ACTION_SPCT_MSR_RPRT;
msr_report->u.action.u.measurement.dialog_token = dialog_token;
msr_report->u.action.u.measurement.element_id = WLAN_EID_MEASURE_REPORT;
msr_report->u.action.u.measurement.length =
sizeof(struct ieee80211_msrment_ie);
memset(&msr_report->u.action.u.measurement.msr_elem, 0,
sizeof(struct ieee80211_msrment_ie));
msr_report->u.action.u.measurement.msr_elem.token = request_ie->token;
msr_report->u.action.u.measurement.msr_elem.mode |=
IEEE80211_SPCT_MSR_RPRT_MODE_REFUSED;
msr_report->u.action.u.measurement.msr_elem.type = request_ie->type;
ieee80211_tx_skb(sdata, skb, 0);
}
void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt,
size_t len)
{
/*
* Ignoring measurement request is spec violation.
* Mandatory measurements must be reported optional
* measurements might be refused or reported incapable
* For now just refuse
* TODO: Answer basic measurement as unmeasured
*/
ieee80211_send_refuse_measurement_request(sdata,
&mgmt->u.action.u.measurement.msr_elem,
mgmt->sa, mgmt->bssid,
mgmt->u.action.u.measurement.dialog_token);
}

View File

@ -428,3 +428,187 @@ void ieee80211_iterate_active_interfaces_atomic(
rcu_read_unlock(); rcu_read_unlock();
} }
EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_atomic); EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_atomic);
void ieee802_11_parse_elems(u8 *start, size_t len,
struct ieee802_11_elems *elems)
{
size_t left = len;
u8 *pos = start;
memset(elems, 0, sizeof(*elems));
elems->ie_start = start;
elems->total_len = len;
while (left >= 2) {
u8 id, elen;
id = *pos++;
elen = *pos++;
left -= 2;
if (elen > left)
return;
switch (id) {
case WLAN_EID_SSID:
elems->ssid = pos;
elems->ssid_len = elen;
break;
case WLAN_EID_SUPP_RATES:
elems->supp_rates = pos;
elems->supp_rates_len = elen;
break;
case WLAN_EID_FH_PARAMS:
elems->fh_params = pos;
elems->fh_params_len = elen;
break;
case WLAN_EID_DS_PARAMS:
elems->ds_params = pos;
elems->ds_params_len = elen;
break;
case WLAN_EID_CF_PARAMS:
elems->cf_params = pos;
elems->cf_params_len = elen;
break;
case WLAN_EID_TIM:
elems->tim = pos;
elems->tim_len = elen;
break;
case WLAN_EID_IBSS_PARAMS:
elems->ibss_params = pos;
elems->ibss_params_len = elen;
break;
case WLAN_EID_CHALLENGE:
elems->challenge = pos;
elems->challenge_len = elen;
break;
case WLAN_EID_WPA:
if (elen >= 4 && pos[0] == 0x00 && pos[1] == 0x50 &&
pos[2] == 0xf2) {
/* Microsoft OUI (00:50:F2) */
if (pos[3] == 1) {
/* OUI Type 1 - WPA IE */
elems->wpa = pos;
elems->wpa_len = elen;
} else if (elen >= 5 && pos[3] == 2) {
if (pos[4] == 0) {
elems->wmm_info = pos;
elems->wmm_info_len = elen;
} else if (pos[4] == 1) {
elems->wmm_param = pos;
elems->wmm_param_len = elen;
}
}
}
break;
case WLAN_EID_RSN:
elems->rsn = pos;
elems->rsn_len = elen;
break;
case WLAN_EID_ERP_INFO:
elems->erp_info = pos;
elems->erp_info_len = elen;
break;
case WLAN_EID_EXT_SUPP_RATES:
elems->ext_supp_rates = pos;
elems->ext_supp_rates_len = elen;
break;
case WLAN_EID_HT_CAPABILITY:
elems->ht_cap_elem = pos;
elems->ht_cap_elem_len = elen;
break;
case WLAN_EID_HT_EXTRA_INFO:
elems->ht_info_elem = pos;
elems->ht_info_elem_len = elen;
break;
case WLAN_EID_MESH_ID:
elems->mesh_id = pos;
elems->mesh_id_len = elen;
break;
case WLAN_EID_MESH_CONFIG:
elems->mesh_config = pos;
elems->mesh_config_len = elen;
break;
case WLAN_EID_PEER_LINK:
elems->peer_link = pos;
elems->peer_link_len = elen;
break;
case WLAN_EID_PREQ:
elems->preq = pos;
elems->preq_len = elen;
break;
case WLAN_EID_PREP:
elems->prep = pos;
elems->prep_len = elen;
break;
case WLAN_EID_PERR:
elems->perr = pos;
elems->perr_len = elen;
break;
case WLAN_EID_CHANNEL_SWITCH:
elems->ch_switch_elem = pos;
elems->ch_switch_elem_len = elen;
break;
case WLAN_EID_QUIET:
if (!elems->quiet_elem) {
elems->quiet_elem = pos;
elems->quiet_elem_len = elen;
}
elems->num_of_quiet_elem++;
break;
case WLAN_EID_COUNTRY:
elems->country_elem = pos;
elems->country_elem_len = elen;
break;
case WLAN_EID_PWR_CONSTRAINT:
elems->pwr_constr_elem = pos;
elems->pwr_constr_elem_len = elen;
break;
default:
break;
}
left -= elen;
pos += elen;
}
}
void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_tx_queue_params qparam;
int i;
if (!local->ops->conf_tx)
return;
memset(&qparam, 0, sizeof(qparam));
qparam.aifs = 2;
if (local->hw.conf.channel->band == IEEE80211_BAND_2GHZ &&
!(sdata->flags & IEEE80211_SDATA_OPERATING_GMODE))
qparam.cw_min = 31;
else
qparam.cw_min = 15;
qparam.cw_max = 1023;
qparam.txop = 0;
for (i = 0; i < local_to_hw(local)->queues; i++)
local->ops->conf_tx(local_to_hw(local), i, &qparam);
}
void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
int encrypt)
{
skb->dev = sdata->local->mdev;
skb_set_mac_header(skb, 0);
skb_set_network_header(skb, 0);
skb_set_transport_header(skb, 0);
skb->iif = sdata->dev->ifindex;
skb->do_not_encrypt = !encrypt;
dev_queue_xmit(skb);
}