mirror of
https://github.com/torvalds/linux.git
synced 2024-11-02 02:01:29 +00:00
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next-2.6
This commit is contained in:
commit
c655705037
@ -224,7 +224,7 @@ static void setup_ht_cap(struct ieee80211_ht_info *ht_info)
|
||||
|
||||
ht_info->ht_supported = 1;
|
||||
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_DSSSCCK40;
|
||||
|
||||
|
@ -585,8 +585,6 @@ enum {
|
||||
struct b43_qos_params {
|
||||
/* The QOS parameters */
|
||||
struct ieee80211_tx_queue_params p;
|
||||
/* Does this need to get uploaded to hardware? */
|
||||
bool need_hw_update;
|
||||
};
|
||||
|
||||
struct b43_wldev;
|
||||
@ -648,11 +646,8 @@ struct b43_wl {
|
||||
bool beacon_templates_virgin; /* Never wrote the templates? */
|
||||
struct work_struct beacon_update_trigger;
|
||||
|
||||
/* The current QOS parameters for the 4 queues.
|
||||
* This is protected by the irq_lock. */
|
||||
/* The current QOS parameters for the 4 queues. */
|
||||
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.
|
||||
* This is scheduled when we determine that the actual TX output
|
||||
|
@ -3059,36 +3059,31 @@ static void b43_qos_params_upload(struct b43_wldev *dev,
|
||||
}
|
||||
}
|
||||
|
||||
/* Update the QOS parameters in hardware. */
|
||||
static void b43_qos_update(struct b43_wldev *dev)
|
||||
/* Mapping of mac80211 queue numbers to b43 QoS SHM offsets. */
|
||||
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_qos_params *params;
|
||||
unsigned long flags;
|
||||
unsigned int i;
|
||||
|
||||
/* Mapping of mac80211 queues to b43 SHM offsets. */
|
||||
static const u16 qos_shm_offsets[] = {
|
||||
[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));
|
||||
BUILD_BUG_ON(ARRAY_SIZE(b43_qos_shm_offsets) !=
|
||||
ARRAY_SIZE(wl->qos_params));
|
||||
|
||||
b43_mac_suspend(dev);
|
||||
spin_lock_irqsave(&wl->irq_lock, flags);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(wl->qos_params); i++) {
|
||||
params = &(wl->qos_params[i]);
|
||||
if (params->need_hw_update) {
|
||||
b43_qos_params_upload(dev, &(params->p),
|
||||
qos_shm_offsets[i]);
|
||||
params->need_hw_update = 0;
|
||||
}
|
||||
b43_qos_params_upload(dev, &(params->p),
|
||||
b43_qos_shm_offsets[i]);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&wl->irq_lock, flags);
|
||||
b43_mac_enable(dev);
|
||||
}
|
||||
|
||||
@ -3097,25 +3092,50 @@ static void b43_qos_clear(struct b43_wl *wl)
|
||||
struct b43_qos_params *params;
|
||||
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++) {
|
||||
params = &(wl->qos_params[i]);
|
||||
|
||||
memset(&(params->p), 0, sizeof(params->p));
|
||||
params->p.aifs = -1;
|
||||
params->need_hw_update = 1;
|
||||
switch (b43_qos_shm_offsets[i]) {
|
||||
case B43_QOS_VOICE:
|
||||
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 */
|
||||
static void b43_qos_init(struct b43_wldev *dev)
|
||||
{
|
||||
struct b43_wl *wl = dev->wl;
|
||||
unsigned int i;
|
||||
|
||||
/* Upload the current QOS parameters. */
|
||||
for (i = 0; i < ARRAY_SIZE(wl->qos_params); i++)
|
||||
wl->qos_params[i].need_hw_update = 1;
|
||||
b43_qos_update(dev);
|
||||
b43_qos_upload_all(dev);
|
||||
|
||||
/* Enable QOS support. */
|
||||
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);
|
||||
}
|
||||
|
||||
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,
|
||||
const struct ieee80211_tx_queue_params *params)
|
||||
{
|
||||
struct b43_wl *wl = hw_to_b43_wl(hw);
|
||||
unsigned long flags;
|
||||
struct b43_wldev *dev;
|
||||
unsigned int queue = (unsigned int)_queue;
|
||||
struct b43_qos_params *p;
|
||||
int err = -ENODEV;
|
||||
|
||||
if (queue >= ARRAY_SIZE(wl->qos_params)) {
|
||||
/* 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. */
|
||||
return 0;
|
||||
}
|
||||
BUILD_BUG_ON(ARRAY_SIZE(b43_qos_shm_offsets) !=
|
||||
ARRAY_SIZE(wl->qos_params));
|
||||
|
||||
spin_lock_irqsave(&wl->irq_lock, flags);
|
||||
p = &(wl->qos_params[queue]);
|
||||
memcpy(&(p->p), params, sizeof(p->p));
|
||||
p->need_hw_update = 1;
|
||||
spin_unlock_irqrestore(&wl->irq_lock, flags);
|
||||
mutex_lock(&wl->mutex);
|
||||
dev = wl->current_dev;
|
||||
if (unlikely(!dev || (b43_status(dev) < B43_STAT_INITIALIZED)))
|
||||
goto out_unlock;
|
||||
|
||||
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,
|
||||
@ -4186,7 +4203,6 @@ static void b43_op_stop(struct ieee80211_hw *hw)
|
||||
struct b43_wldev *dev = wl->current_dev;
|
||||
|
||||
b43_rfkill_exit(dev);
|
||||
cancel_work_sync(&(wl->qos_update_work));
|
||||
cancel_work_sync(&(wl->beacon_update_trigger));
|
||||
|
||||
mutex_lock(&wl->mutex);
|
||||
@ -4585,7 +4601,6 @@ static int b43_wireless_init(struct ssb_device *dev)
|
||||
spin_lock_init(&wl->shm_lock);
|
||||
mutex_init(&wl->mutex);
|
||||
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->txpower_adjust_work, b43_phy_txpower_adjust_work);
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
b43_phy_write(dev, B43_PHY_NRSSILT_CTRL, offset);
|
||||
mmiowb();
|
||||
b43_phy_write(dev, B43_PHY_NRSSILT_DATA, (u16) val);
|
||||
}
|
||||
|
||||
|
@ -595,12 +595,14 @@ static void b43legacy_phy_initb5(struct b43legacy_wldev *dev)
|
||||
0x0035) & 0xFFC0) | 0x0064);
|
||||
b43legacy_phy_write(dev, 0x005D, (b43legacy_phy_read(dev,
|
||||
0x005D) & 0xFF80) | 0x000A);
|
||||
b43legacy_phy_write(dev, 0x5B, 0x0000);
|
||||
b43legacy_phy_write(dev, 0x5C, 0x0000);
|
||||
}
|
||||
|
||||
if (dev->bad_frames_preempt)
|
||||
b43legacy_phy_write(dev, B43legacy_PHY_RADIO_BITFIELD,
|
||||
b43legacy_phy_read(dev,
|
||||
B43legacy_PHY_RADIO_BITFIELD) | (1 << 11));
|
||||
B43legacy_PHY_RADIO_BITFIELD) | (1 << 12));
|
||||
|
||||
if (phy->analog == 1) {
|
||||
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);
|
||||
}
|
||||
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, 0x005B, 0x007B);
|
||||
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, 0x0038, 0x0668);
|
||||
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,
|
||||
0x005D) & 0xFF80) | 0x0003);
|
||||
if (phy->radio_rev <= 2)
|
||||
@ -1010,7 +1012,7 @@ static void b43legacy_phy_initg(struct b43legacy_wldev *dev)
|
||||
b43legacy_phy_initb5(dev);
|
||||
else
|
||||
b43legacy_phy_initb6(dev);
|
||||
if (phy->rev >= 2 || phy->gmode)
|
||||
if (phy->rev >= 2 && phy->gmode)
|
||||
b43legacy_phy_inita(dev);
|
||||
|
||||
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, 0x0015, 0x00C0);
|
||||
}
|
||||
if (phy->rev >= 2 || phy->gmode) {
|
||||
if (phy->gmode) {
|
||||
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, 0x04C3, 0x8006);
|
||||
if (tmp == 5)
|
||||
b43legacy_phy_write(dev, 0x04CC,
|
||||
(b43legacy_phy_read(dev,
|
||||
0x04CC) & 0x00FF) |
|
||||
0x1F00);
|
||||
b43legacy_phy_write(dev, 0x04CC,
|
||||
(b43legacy_phy_read(dev,
|
||||
0x04CC) & 0x00FF) |
|
||||
0x1F00);
|
||||
}
|
||||
b43legacy_phy_write(dev, 0x047E, 0x0078);
|
||||
if (phy->rev >= 2)
|
||||
b43legacy_phy_write(dev, 0x047E, 0x0078);
|
||||
}
|
||||
if (phy->radio_rev == 8) {
|
||||
b43legacy_phy_write(dev, 0x0801, b43legacy_phy_read(dev, 0x0801)
|
||||
@ -1078,7 +1084,7 @@ static void b43legacy_phy_initg(struct b43legacy_wldev *dev)
|
||||
else
|
||||
b43legacy_phy_write(dev, 0x002F, 0x0202);
|
||||
}
|
||||
if (phy->gmode || phy->rev >= 2) {
|
||||
if (phy->gmode) {
|
||||
b43legacy_phy_lo_adjust(dev, 0);
|
||||
b43legacy_phy_write(dev, 0x080F, 0x8078);
|
||||
}
|
||||
|
@ -624,7 +624,7 @@ void b43legacy_handle_hwtxstatus(struct b43legacy_wldev *dev,
|
||||
tmp = hw->count;
|
||||
status.frame_count = (tmp >> 4);
|
||||
status.rts_count = (tmp & 0x0F);
|
||||
tmp = hw->flags;
|
||||
tmp = hw->flags << 1;
|
||||
status.supp_reason = ((tmp & 0x1C) >> 2);
|
||||
status.pm_indicated = !!(tmp & 0x80);
|
||||
status.intermediate = !!(tmp & 0x40);
|
||||
|
@ -681,19 +681,7 @@ static void iwl3945_rx_reply_rx(struct iwl3945_priv *priv,
|
||||
priv->last_rx_noise = rx_status.noise;
|
||||
}
|
||||
|
||||
if (priv->iw_mode == IEEE80211_IF_TYPE_MNTR) {
|
||||
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;
|
||||
}
|
||||
iwl3945_pass_packet_to_mac80211(priv, rxb, &rx_status);
|
||||
}
|
||||
|
||||
int iwl3945_hw_txq_attach_buf_to_tfd(struct iwl3945_priv *priv, void *ptr,
|
||||
|
@ -1607,8 +1607,8 @@ static int iwl4965_send_rxon_assoc(struct iwl_priv *priv)
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int iwl4965_hw_channel_switch(struct iwl_priv *priv, u16 channel)
|
||||
#ifdef IEEE80211_CONF_CHANNEL_SWITCH
|
||||
static int iwl4965_hw_channel_switch(struct iwl_priv *priv, u16 channel)
|
||||
{
|
||||
int rc;
|
||||
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);
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int iwl4965_shared_mem_rx_idx(struct iwl_priv *priv)
|
||||
{
|
||||
|
@ -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),
|
||||
* OR in "1", and increment "success" if this
|
||||
* frame was successful. */
|
||||
window->data <<= 1;;
|
||||
window->data <<= 1;
|
||||
if (successes > 0) {
|
||||
window->success_counter++;
|
||||
window->data |= 0x1;
|
||||
@ -1128,6 +1128,7 @@ static s32 rs_get_best_rate(struct iwl_priv *priv,
|
||||
|
||||
/* Higher rate not available, use the original */
|
||||
} else {
|
||||
new_rate = rate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1153,8 +1154,8 @@ static int rs_switch_to_mimo2(struct iwl_priv *priv,
|
||||
!sta->ht_info.ht_supported)
|
||||
return -1;
|
||||
|
||||
if (((sta->ht_info.cap & IEEE80211_HT_CAP_MIMO_PS) >> 2)
|
||||
== IWL_MIMO_PS_STATIC)
|
||||
if (((sta->ht_info.cap & IEEE80211_HT_CAP_SM_PS) >> 2)
|
||||
== WLAN_HT_CAP_SM_PS_STATIC)
|
||||
return -1;
|
||||
|
||||
/* 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));
|
||||
u8 start_action = tbl->action;
|
||||
u8 valid_tx_ant = priv->hw_params.valid_tx_ant;
|
||||
u8 tx_chains_num = priv->hw_params.tx_chains_num;
|
||||
int ret = 0;
|
||||
|
||||
for (; ;) {
|
||||
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");
|
||||
|
||||
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 */
|
||||
if (window->success_ratio >= IWL_RS_GOOD_RATIO)
|
||||
break;
|
||||
@ -1299,7 +1308,7 @@ static int rs_move_legacy_other(struct iwl_priv *priv,
|
||||
|
||||
if (rs_toggle_antenna(valid_tx_ant,
|
||||
&search_tbl->current_rate, search_tbl)) {
|
||||
lq_sta->search_better_tbl = 1;
|
||||
rs_set_expected_tpt_table(lq_sta, search_tbl);
|
||||
goto out;
|
||||
}
|
||||
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,
|
||||
search_tbl, index);
|
||||
if (!ret) {
|
||||
lq_sta->search_better_tbl = 1;
|
||||
lq_sta->action_counter = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
/* Set up search table to try MIMO */
|
||||
memcpy(search_tbl, tbl, sz);
|
||||
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,
|
||||
search_tbl, index);
|
||||
if (!ret) {
|
||||
lq_sta->search_better_tbl = 1;
|
||||
lq_sta->action_counter = 0;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
}
|
||||
tbl->action++;
|
||||
if (tbl->action > IWL_LEGACY_SWITCH_MIMO2)
|
||||
tbl->action = IWL_LEGACY_SWITCH_ANTENNA;
|
||||
if (tbl->action > IWL_LEGACY_SWITCH_MIMO2_BC)
|
||||
tbl->action = IWL_LEGACY_SWITCH_ANTENNA1;
|
||||
|
||||
if (tbl->action == start_action)
|
||||
break;
|
||||
|
||||
}
|
||||
search_tbl->lq_type = LQ_NONE;
|
||||
return 0;
|
||||
|
||||
out:
|
||||
out:
|
||||
lq_sta->search_better_tbl = 1;
|
||||
tbl->action++;
|
||||
if (tbl->action > IWL_LEGACY_SWITCH_MIMO2)
|
||||
tbl->action = IWL_LEGACY_SWITCH_ANTENNA;
|
||||
if (tbl->action > IWL_LEGACY_SWITCH_MIMO2_BC)
|
||||
tbl->action = IWL_LEGACY_SWITCH_ANTENNA1;
|
||||
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));
|
||||
u8 start_action = tbl->action;
|
||||
u8 valid_tx_ant = priv->hw_params.valid_tx_ant;
|
||||
u8 tx_chains_num = priv->hw_params.tx_chains_num;
|
||||
int ret;
|
||||
|
||||
for (;;) {
|
||||
lq_sta->action_counter++;
|
||||
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");
|
||||
|
||||
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)
|
||||
break;
|
||||
|
||||
memcpy(search_tbl, tbl, sz);
|
||||
if (rs_toggle_antenna(valid_tx_ant,
|
||||
&search_tbl->current_rate, search_tbl)) {
|
||||
lq_sta->search_better_tbl = 1;
|
||||
&search_tbl->current_rate, search_tbl))
|
||||
goto out;
|
||||
}
|
||||
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");
|
||||
memcpy(search_tbl, tbl, sz);
|
||||
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,
|
||||
search_tbl, index);
|
||||
if (!ret) {
|
||||
lq_sta->search_better_tbl = 1;
|
||||
if (!ret)
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case IWL_SISO_SWITCH_GI:
|
||||
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, index, is_green);
|
||||
lq_sta->search_better_tbl = 1;
|
||||
goto out;
|
||||
}
|
||||
tbl->action++;
|
||||
if (tbl->action > IWL_SISO_SWITCH_GI)
|
||||
tbl->action = IWL_SISO_SWITCH_ANTENNA;
|
||||
tbl->action = IWL_SISO_SWITCH_ANTENNA1;
|
||||
|
||||
if (tbl->action == start_action)
|
||||
break;
|
||||
}
|
||||
search_tbl->lq_type = LQ_NONE;
|
||||
return 0;
|
||||
|
||||
out:
|
||||
lq_sta->search_better_tbl = 1;
|
||||
tbl->action++;
|
||||
if (tbl->action > IWL_SISO_SWITCH_GI)
|
||||
tbl->action = IWL_SISO_SWITCH_ANTENNA;
|
||||
tbl->action = IWL_SISO_SWITCH_ANTENNA1;
|
||||
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 *search_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) -
|
||||
(sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
|
||||
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;
|
||||
|
||||
for (;;) {
|
||||
lq_sta->action_counter++;
|
||||
switch (tbl->action) {
|
||||
case IWL_MIMO_SWITCH_ANTENNA_A:
|
||||
case IWL_MIMO_SWITCH_ANTENNA_B:
|
||||
case IWL_MIMO2_SWITCH_ANTENNA1:
|
||||
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");
|
||||
|
||||
/* Set up new search table for SISO */
|
||||
memcpy(search_tbl, tbl, sz);
|
||||
|
||||
/*FIXME:RS:need to check ant validity + C*/
|
||||
if (tbl->action == IWL_MIMO_SWITCH_ANTENNA_A)
|
||||
if (tbl->action == IWL_MIMO2_SWITCH_SISO_A)
|
||||
search_tbl->ant_type = ANT_A;
|
||||
else
|
||||
else if (tbl->action == IWL_MIMO2_SWITCH_SISO_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,
|
||||
search_tbl, index);
|
||||
if (!ret) {
|
||||
lq_sta->search_better_tbl = 1;
|
||||
if (!ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case IWL_MIMO_SWITCH_GI:
|
||||
case IWL_MIMO2_SWITCH_GI:
|
||||
if (!tbl->is_fat &&
|
||||
!(priv->current_ht_config.sgf &
|
||||
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, index, is_green);
|
||||
lq_sta->search_better_tbl = 1;
|
||||
goto out;
|
||||
|
||||
}
|
||||
tbl->action++;
|
||||
if (tbl->action > IWL_MIMO_SWITCH_GI)
|
||||
tbl->action = IWL_MIMO_SWITCH_ANTENNA_A;
|
||||
if (tbl->action > IWL_MIMO2_SWITCH_GI)
|
||||
tbl->action = IWL_MIMO2_SWITCH_ANTENNA1;
|
||||
|
||||
if (tbl->action == start_action)
|
||||
break;
|
||||
}
|
||||
|
||||
search_tbl->lq_type = LQ_NONE;
|
||||
return 0;
|
||||
out:
|
||||
lq_sta->search_better_tbl = 1;
|
||||
tbl->action++;
|
||||
if (tbl->action > IWL_MIMO_SWITCH_GI)
|
||||
tbl->action = IWL_MIMO_SWITCH_ANTENNA_A;
|
||||
if (tbl->action > IWL_MIMO2_SWITCH_GI)
|
||||
tbl->action = IWL_MIMO2_SWITCH_ANTENNA1;
|
||||
return 0;
|
||||
|
||||
}
|
||||
@ -1748,19 +1807,13 @@ static void rs_rate_scale_perform(struct iwl_priv *priv,
|
||||
rs_stay_in_table(lq_sta);
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Else we have enough samples; calculate estimate of
|
||||
* actual average throughput */
|
||||
} else {
|
||||
/*FIXME:RS remove this else if we don't get this error*/
|
||||
if (window->average_tpt != ((window->success_ratio *
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
BUG_ON(window->average_tpt != ((window->success_ratio *
|
||||
tbl->expected_tpt[index] + 64) / 128));
|
||||
|
||||
/* If we are searching for better modulation mode, check success. */
|
||||
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. */
|
||||
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",
|
||||
window->success_ratio,
|
||||
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++)
|
||||
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
|
||||
* the lowest or the highest rate.. Could consider using RSSI from
|
||||
* previous packets? Need to have IEEE 802.1X auth succeed immediately
|
||||
|
@ -206,21 +206,28 @@ enum {
|
||||
#define IWL_RATE_DECREASE_TH 1920 /* 15% */
|
||||
|
||||
/* possible actions when in legacy mode */
|
||||
#define IWL_LEGACY_SWITCH_ANTENNA 0
|
||||
#define IWL_LEGACY_SWITCH_SISO 1
|
||||
#define IWL_LEGACY_SWITCH_MIMO2 2
|
||||
#define IWL_LEGACY_SWITCH_ANTENNA1 0
|
||||
#define IWL_LEGACY_SWITCH_ANTENNA2 1
|
||||
#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 */
|
||||
#define IWL_SISO_SWITCH_ANTENNA 0
|
||||
#define IWL_SISO_SWITCH_MIMO2 1
|
||||
#define IWL_SISO_SWITCH_GI 2
|
||||
#define IWL_SISO_SWITCH_ANTENNA1 0
|
||||
#define IWL_SISO_SWITCH_ANTENNA2 1
|
||||
#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 */
|
||||
#define IWL_MIMO_SWITCH_ANTENNA_A 0
|
||||
#define IWL_MIMO_SWITCH_ANTENNA_B 1
|
||||
#define IWL_MIMO_SWITCH_GI 2
|
||||
|
||||
/*FIXME:RS:separate MIMO2/3 transitions*/
|
||||
#define IWL_MIMO2_SWITCH_ANTENNA1 0
|
||||
#define IWL_MIMO2_SWITCH_ANTENNA2 1
|
||||
#define IWL_MIMO2_SWITCH_SISO_A 2
|
||||
#define IWL_MIMO2_SWITCH_SISO_B 3
|
||||
#define IWL_MIMO2_SWITCH_SISO_C 4
|
||||
#define IWL_MIMO2_SWITCH_GI 5
|
||||
|
||||
/*FIXME:RS:add posible acctions for MIMO3*/
|
||||
|
||||
|
@ -485,7 +485,7 @@ static u8 iwl4965_rate_get_lowest_plcp(struct iwl_priv *priv)
|
||||
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_tx_beacon_cmd *tx_beacon_cmd;
|
||||
@ -564,8 +564,6 @@ static void iwl4965_ht_conf(struct iwl_priv *priv,
|
||||
if (!iwl_conf->is_ht)
|
||||
return;
|
||||
|
||||
priv->ps_mode = (u8)((ht_conf->cap & IEEE80211_HT_CAP_MIMO_PS) >> 2);
|
||||
|
||||
if (ht_conf->cap & IEEE80211_HT_CAP_SGI_20)
|
||||
iwl_conf->sgf |= HT_SHORT_GI_20MHZ;
|
||||
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->sm_ps = (u8)((ht_conf->cap & IEEE80211_HT_CAP_SM_PS) >> 2);
|
||||
|
||||
memcpy(iwl_conf->supp_mcs_set, ht_conf->supp_mcs_set, 16);
|
||||
|
||||
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);
|
||||
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 */
|
||||
iwl_chain_noise_reset(priv);
|
||||
|
@ -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",
|
||||
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 */
|
||||
average_noise[0] = ((data->chain_noise_a)/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,
|
||||
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);
|
||||
|
||||
|
@ -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_SGI_20;
|
||||
ht_info->cap |= (u16)(IEEE80211_HT_CAP_MIMO_PS &
|
||||
(IWL_MIMO_PS_NONE << 2));
|
||||
ht_info->cap |= (u16)(IEEE80211_HT_CAP_SM_PS &
|
||||
(WLAN_HT_CAP_SM_PS_DISABLED << 2));
|
||||
|
||||
max_bit_rate = MAX_BIT_RATE_20_MHZ;
|
||||
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);
|
||||
|
||||
/* # 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;
|
||||
else
|
||||
return 3;
|
||||
@ -720,17 +721,18 @@ static int iwl_get_idle_rx_chain_count(struct iwl_priv *priv, int active_cnt)
|
||||
int idle_cnt;
|
||||
bool is_cam = !test_bit(STATUS_POWER_PMI, &priv->status);
|
||||
/* # Rx chains when idling and maybe trying to save power */
|
||||
switch (priv->ps_mode) {
|
||||
case IWL_MIMO_PS_STATIC:
|
||||
case IWL_MIMO_PS_DYNAMIC:
|
||||
switch (priv->current_ht_config.sm_ps) {
|
||||
case WLAN_HT_CAP_SM_PS_STATIC:
|
||||
case WLAN_HT_CAP_SM_PS_DYNAMIC:
|
||||
idle_cnt = (is_cam) ? 2 : 1;
|
||||
break;
|
||||
case IWL_MIMO_PS_NONE:
|
||||
case WLAN_HT_CAP_SM_PS_DISABLED:
|
||||
idle_cnt = (is_cam) ? active_cnt : 1;
|
||||
break;
|
||||
case IWL_MIMO_PS_INVALID:
|
||||
case WLAN_HT_CAP_SM_PS_INVALID:
|
||||
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);
|
||||
idle_cnt = -1;
|
||||
break;
|
||||
@ -738,6 +740,17 @@ static int iwl_get_idle_rx_chain_count(struct iwl_priv *priv, int active_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
|
||||
*
|
||||
@ -748,25 +761,35 @@ void iwl_set_rxon_chain(struct iwl_priv *priv)
|
||||
{
|
||||
bool is_single = is_single_rx_stream(priv);
|
||||
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;
|
||||
|
||||
/* Tell uCode which antennas are actually connected.
|
||||
* Before first association, we assume all antennas are connected.
|
||||
* Just after first association, iwl_chain_noise_calibration()
|
||||
* 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? */
|
||||
active_rx_cnt = iwl_get_active_rx_chain_count(priv);
|
||||
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)
|
||||
idle_rx_cnt = priv->hw_params.rx_chains_num;
|
||||
/* correct rx chain count according hw settings
|
||||
* 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 |= idle_rx_cnt << RXON_RX_CHAIN_CNT_POS;
|
||||
@ -778,7 +801,7 @@ void iwl_set_rxon_chain(struct iwl_priv *priv)
|
||||
else
|
||||
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,
|
||||
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->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 */
|
||||
iwl_set_rxon_chain(priv);
|
||||
@ -1135,7 +1158,6 @@ int iwl_verify_ucode(struct iwl_priv *priv)
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_verify_ucode);
|
||||
|
||||
|
||||
static const char *desc_lookup(int i)
|
||||
{
|
||||
switch (i) {
|
||||
@ -1216,9 +1238,9 @@ EXPORT_SYMBOL(iwl_dump_nic_error_log);
|
||||
/**
|
||||
* 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 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)
|
||||
{
|
||||
|
@ -184,7 +184,6 @@ struct iwl_cfg {
|
||||
struct ieee80211_hw *iwl_alloc_all(struct iwl_cfg *cfg,
|
||||
struct ieee80211_ops *hw_ops);
|
||||
void iwl_hw_detect(struct iwl_priv *priv);
|
||||
|
||||
void iwl_clear_stations_table(struct iwl_priv *priv);
|
||||
void iwl_reset_qos(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_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);
|
||||
/* FIXME: remove when TX is moved to iwl core */
|
||||
int iwl_rx_queue_restock(struct iwl_priv *priv);
|
||||
int iwl_rx_queue_space(const struct iwl_rx_queue *q);
|
||||
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_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);
|
||||
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_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);
|
||||
@ -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 subsystem is not compiled.
|
||||
****************************************************/
|
||||
void iwl_rf_kill(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);
|
||||
|
||||
@ -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);
|
||||
int iwl_scan_cancel(struct iwl_priv *priv);
|
||||
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);
|
||||
void iwl_setup_rx_scan_handlers(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
|
||||
******************************************************/
|
||||
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_event_log(struct iwl_priv *priv);
|
||||
|
||||
|
@ -406,6 +406,7 @@ struct iwl_ht_info {
|
||||
/* self configuration data */
|
||||
u8 is_ht;
|
||||
u8 supported_chan_width;
|
||||
u8 sm_ps;
|
||||
u8 is_green_field;
|
||||
u8 sgf; /* HT_SHORT_GI_* short guard interval */
|
||||
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_DATA(x) (IWL_RX_HDR(x)->payload)
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* Functions implemented in iwl-base.c which are forward declared here
|
||||
* for use by iwl-*.c
|
||||
* Functions implemented in core module which are forward declared here
|
||||
* for use by iwl-[4-5].c
|
||||
*
|
||||
*****************************************************************************/
|
||||
struct iwl_addsta_cmd;
|
||||
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)
|
||||
* NOTE: The implementation of these functions are not hardware specific
|
||||
* which is why they are in the core module files.
|
||||
*
|
||||
* Naming convention --
|
||||
* iwl4965_ <-- Its part of iwlwifi (should be changed to iwl4965_)
|
||||
* iwl4965_hw_ <-- Hardware specific (implemented in iwl-XXXX.c by all HW)
|
||||
* iwl_ <-- Is part of iwlwifi
|
||||
* iwlXXXX_ <-- Hardware specific (implemented in iwl-XXXX.c for XXXX)
|
||||
* iwl4965_bg_ <-- Called from work queue context
|
||||
* 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 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);
|
||||
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_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_HT_ONLY 1
|
||||
#define IWL_OPERATION_MODE_MIXED 2
|
||||
@ -703,8 +680,9 @@ enum iwl4965_false_alarm_state {
|
||||
|
||||
enum iwl4965_chain_noise_state {
|
||||
IWL_CHAIN_NOISE_ALIVE = 0, /* must be 0 */
|
||||
IWL_CHAIN_NOISE_ACCUMULATE = 1,
|
||||
IWL_CHAIN_NOISE_CALIBRATED = 2,
|
||||
IWL_CHAIN_NOISE_ACCUMULATE,
|
||||
IWL_CHAIN_NOISE_CALIBRATED,
|
||||
IWL_CHAIN_NOISE_DONE,
|
||||
};
|
||||
|
||||
enum iwl4965_calib_enabled_state {
|
||||
@ -762,17 +740,18 @@ struct iwl_sensitivity_data {
|
||||
|
||||
/* Chain noise (differential Rx gain) calib data */
|
||||
struct iwl_chain_noise_data {
|
||||
u8 state;
|
||||
u16 beacon_count;
|
||||
u32 active_chains;
|
||||
u32 chain_noise_a;
|
||||
u32 chain_noise_b;
|
||||
u32 chain_noise_c;
|
||||
u32 chain_signal_a;
|
||||
u32 chain_signal_b;
|
||||
u32 chain_signal_c;
|
||||
u16 beacon_count;
|
||||
u8 disconn_array[NUM_RX_CHAINS];
|
||||
u8 delta_gain_code[NUM_RX_CHAINS];
|
||||
u8 radio_write;
|
||||
u8 state;
|
||||
};
|
||||
|
||||
#define EEPROM_SEM_TIMEOUT 10 /* milliseconds */
|
||||
@ -995,7 +974,6 @@ struct iwl_priv {
|
||||
* hardware */
|
||||
u16 assoc_id;
|
||||
u16 assoc_capability;
|
||||
u8 ps_mode;
|
||||
|
||||
struct iwl_qos_info qos_data;
|
||||
|
||||
|
@ -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);
|
||||
int ret = 0;
|
||||
u16 uninitialized_var(final_mode);
|
||||
|
||||
/* If on battery, set to 3,
|
||||
* if plugged into AC power, set to CAM ("continuously aware mode"),
|
||||
* else user level */
|
||||
/* Don't update the RX chain when chain noise calibration is running */
|
||||
if (priv->chain_noise_data.state != IWL_CHAIN_NOISE_DONE &&
|
||||
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) {
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
if (final_mode != IWL_POWER_MODE_CAM)
|
||||
@ -359,35 +368,26 @@ EXPORT_SYMBOL(iwl_power_enable_management);
|
||||
/* set user_power_setting */
|
||||
int iwl_power_set_user_mode(struct iwl_priv *priv, u16 mode)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (mode > IWL_POWER_LIMIT)
|
||||
return -EINVAL;
|
||||
|
||||
priv->power_data.user_power_setting = mode;
|
||||
|
||||
ret = iwl_power_update_mode(priv, 0);
|
||||
|
||||
return ret;
|
||||
return iwl_power_update_mode(priv, 0);
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_power_set_user_mode);
|
||||
|
||||
|
||||
/* set system_power_setting. This should be set by over all
|
||||
* PM application.
|
||||
*/
|
||||
int iwl_power_set_system_mode(struct iwl_priv *priv, u16 mode)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (mode > IWL_POWER_LIMIT)
|
||||
return -EINVAL;
|
||||
|
||||
priv->power_data.system_power_setting = mode;
|
||||
|
||||
ret = iwl_power_update_mode(priv, 0);
|
||||
|
||||
return ret;
|
||||
return iwl_power_update_mode(priv, 0);
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_power_set_system_mode);
|
||||
|
||||
|
@ -72,7 +72,7 @@ struct iwl_power_mgr {
|
||||
/* final power level that used to calculate final power command */
|
||||
u8 power_mode;
|
||||
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 is_battery_active; /* DC/AC power */
|
||||
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_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_enable_management(struct iwl_priv *priv);
|
||||
int iwl_power_set_user_mode(struct iwl_priv *priv, u16 mode);
|
||||
|
@ -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];
|
||||
const char *s = essid;
|
||||
@ -111,7 +111,6 @@ const char *iwl_escape_essid(const char *essid, u8 essid_len)
|
||||
*d = '\0';
|
||||
return escaped;
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_escape_essid);
|
||||
|
||||
/**
|
||||
* iwl_scan_cancel - Cancel any currently executing HW scan
|
||||
|
@ -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)
|
||||
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 &= ~(STA_FLG_RTS_MIMO_PROT_MSK | STA_FLG_MIMO_DIS_MSK);
|
||||
|
||||
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;
|
||||
break;
|
||||
case WLAN_HT_CAP_MIMO_PS_DYNAMIC:
|
||||
case WLAN_HT_CAP_SM_PS_DYNAMIC:
|
||||
sta_flags |= STA_FLG_RTS_MIMO_PROT_MSK;
|
||||
break;
|
||||
case WLAN_HT_CAP_MIMO_PS_DISABLED:
|
||||
case WLAN_HT_CAP_SM_PS_DISABLED:
|
||||
break;
|
||||
default:
|
||||
IWL_WARNING("Invalid MIMO PS mode %d\n", mimo_ps_mode);
|
||||
|
@ -63,7 +63,7 @@ static const u16 default_tid_to_tx_fifo[] = {
|
||||
* Does NOT advance any TFD circular buffer read/write indexes
|
||||
* 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 = &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;
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_hw_txq_free_tfd);
|
||||
|
||||
|
||||
int iwl_hw_txq_attach_buf_to_tfd(struct iwl_priv *priv, void *ptr,
|
||||
static int iwl_hw_txq_attach_buf_to_tfd(struct iwl_priv *priv, void *ptr,
|
||||
dma_addr_t addr, u16 len)
|
||||
{
|
||||
int index, is_odd;
|
||||
@ -151,7 +149,6 @@ int iwl_hw_txq_attach_buf_to_tfd(struct iwl_priv *priv, void *ptr,
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_hw_txq_attach_buf_to_tfd);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
|
||||
/**
|
||||
* iwl_txq_ctx_reset - Reset TX queue context
|
||||
* Destroys all DMA structures and initialise them again
|
||||
@ -545,6 +541,7 @@ int iwl_txq_ctx_reset(struct iwl_priv *priv)
|
||||
error_kw:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* iwl_txq_ctx_stop - Stop all Tx DMA channels, free Tx queue memory
|
||||
*/
|
||||
|
@ -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,
|
||||
uint16_t command, struct cmd_header *in_cmd, int in_cmd_size,
|
||||
int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *),
|
||||
|
@ -26,6 +26,12 @@ int __lbs_cmd(struct lbs_private *priv, uint16_t command,
|
||||
int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *),
|
||||
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,
|
||||
struct cmd_header *resp);
|
||||
|
||||
|
@ -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_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 */
|
||||
|
||||
/* Station firmware use TxPD status field to report final Tx transmit
|
||||
|
@ -83,6 +83,7 @@
|
||||
#define CMD_802_11_INACTIVITY_TIMEOUT 0x0067
|
||||
#define CMD_802_11_SLEEP_PERIOD 0x0068
|
||||
#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_SUBSCRIBE_EVENT 0x0075
|
||||
#define CMD_802_11_RATE_ADAPT_RATESET 0x0076
|
||||
|
@ -607,14 +607,28 @@ struct cmd_ds_802_11_eeprom_access {
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct cmd_ds_802_11_tpc_cfg {
|
||||
struct cmd_header hdr;
|
||||
|
||||
__le16 action;
|
||||
u8 enable;
|
||||
s8 P0;
|
||||
s8 P1;
|
||||
s8 P2;
|
||||
u8 usesnr;
|
||||
uint8_t enable;
|
||||
int8_t P0;
|
||||
int8_t P1;
|
||||
int8_t P2;
|
||||
uint8_t usesnr;
|
||||
} __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 {
|
||||
__le16 action;
|
||||
__le16 numled;
|
||||
|
@ -1205,7 +1205,13 @@ void lbs_remove_card(struct lbs_private *priv)
|
||||
cancel_delayed_work_sync(&priv->scan_work);
|
||||
cancel_delayed_work_sync(&priv->assoc_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);
|
||||
lbs_deb_main("done destroying worker thread\n");
|
||||
|
||||
if (priv->psmode == LBS802_11POWERMODEMAX_PSP) {
|
||||
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);
|
||||
}
|
||||
|
||||
/* Flush pending command nodes */
|
||||
/* Delete the timeout of the currently processing command */
|
||||
del_timer_sync(&priv->command_timer);
|
||||
|
||||
/* Flush pending command nodes */
|
||||
spin_lock_irqsave(&priv->driver_lock, flags);
|
||||
lbs_deb_main("clearing pending commands\n");
|
||||
list_for_each_entry(cmdnode, &priv->cmdpendingq, list) {
|
||||
cmdnode->result = -ENOENT;
|
||||
cmdnode->cmdwaitqwoken = 1;
|
||||
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);
|
||||
|
||||
unregister_netdev(dev);
|
||||
|
@ -1820,7 +1820,21 @@ static int lbs_set_txpow(struct net_device *dev, struct iw_request_info *info,
|
||||
}
|
||||
|
||||
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;
|
||||
} else {
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* Validate requested power level against firmware allowed levels */
|
||||
/* Validate requested power level against firmware allowed
|
||||
* levels */
|
||||
if (priv->txpower_min && (dbm < priv->txpower_min)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
@ -1840,6 +1855,18 @@ static int lbs_set_txpow(struct net_device *dev, struct iw_request_info *info,
|
||||
ret = -EINVAL;
|
||||
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 */
|
||||
|
@ -80,6 +80,7 @@ struct p54_common {
|
||||
struct pda_channel_output_limit *output_limit;
|
||||
unsigned int output_limit_len;
|
||||
struct pda_pa_curve_data *curve_data;
|
||||
unsigned int filter_flags;
|
||||
u16 rxhw;
|
||||
u8 version;
|
||||
u8 rx_antenna;
|
||||
@ -87,7 +88,15 @@ struct p54_common {
|
||||
void *cached_vdcf;
|
||||
unsigned int fw_var;
|
||||
unsigned int fw_interface;
|
||||
unsigned int output_power;
|
||||
u32 tsf_low32;
|
||||
u32 tsf_high32;
|
||||
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;
|
||||
struct completion eeprom_comp;
|
||||
};
|
||||
|
@ -27,7 +27,7 @@ MODULE_DESCRIPTION("Softmac Prism54 common code");
|
||||
MODULE_LICENSE("GPL");
|
||||
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 = 20, .hw_value = 1, .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, },
|
||||
};
|
||||
|
||||
static struct ieee80211_channel p54_channels[] = {
|
||||
static struct ieee80211_channel p54_bgchannels[] = {
|
||||
{ .center_freq = 2412, .hw_value = 1, },
|
||||
{ .center_freq = 2417, .hw_value = 2, },
|
||||
{ .center_freq = 2422, .hw_value = 3, },
|
||||
@ -60,10 +60,66 @@ static struct ieee80211_channel p54_channels[] = {
|
||||
};
|
||||
|
||||
static struct ieee80211_supported_band band_2GHz = {
|
||||
.channels = p54_channels,
|
||||
.n_channels = ARRAY_SIZE(p54_channels),
|
||||
.bitrates = p54_rates,
|
||||
.n_bitrates = ARRAY_SIZE(p54_rates),
|
||||
.channels = p54_bgchannels,
|
||||
.n_channels = ARRAY_SIZE(p54_bgchannels),
|
||||
.bitrates = p54_bgrates,
|
||||
.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)
|
||||
@ -252,6 +308,7 @@ static int p54_convert_rev1(struct ieee80211_hw *dev,
|
||||
|
||||
const char* p54_rf_chips[] = { "NULL", "Indigo?", "Duette",
|
||||
"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)
|
||||
{
|
||||
@ -371,20 +428,20 @@ int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len)
|
||||
}
|
||||
|
||||
switch (priv->rxhw) {
|
||||
case 4: /* XBow */
|
||||
case 1: /* Indigo? */
|
||||
case 2: /* Duette */
|
||||
/* TODO: 5GHz initialization goes here */
|
||||
|
||||
case 3: /* Frisbee */
|
||||
case 5: /* Longbow */
|
||||
dev->wiphy->bands[IEEE80211_BAND_2GHZ] = &band_2GHz;
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "%s: unsupported RF-Chip\n",
|
||||
wiphy_name(dev->wiphy));
|
||||
err = -EINVAL;
|
||||
goto err;
|
||||
case 4: /* XBow */
|
||||
p54_init_xbow_synth(dev);
|
||||
case 1: /* Indigo? */
|
||||
case 2: /* Duette */
|
||||
dev->wiphy->bands[IEEE80211_BAND_5GHZ] = &band_5GHz;
|
||||
case 3: /* Frisbee */
|
||||
case 5: /* Longbow */
|
||||
dev->wiphy->bands[IEEE80211_BAND_2GHZ] = &band_2GHz;
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "%s: unsupported RF-Chip\n",
|
||||
wiphy_name(dev->wiphy));
|
||||
err = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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)
|
||||
{
|
||||
struct p54_common *priv = dev->priv;
|
||||
struct p54_rx_hdr *hdr = (struct p54_rx_hdr *) skb->data;
|
||||
struct ieee80211_rx_status rx_status = {0};
|
||||
u16 freq = le16_to_cpu(hdr->freq);
|
||||
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? */
|
||||
rx_status.qual = (100 * hdr->rssi) / 127;
|
||||
rx_status.rate_idx = hdr->rate & 0xf;
|
||||
rx_status.freq = freq;
|
||||
rx_status.band = IEEE80211_BAND_2GHZ;
|
||||
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;
|
||||
|
||||
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.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));
|
||||
ieee80211_tx_status_irqsafe(dev, entry);
|
||||
goto out;
|
||||
@ -542,6 +622,27 @@ static void p54_rx_eeprom_readback(struct ieee80211_hw *dev,
|
||||
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)
|
||||
{
|
||||
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;
|
||||
case P54_CONTROL_TYPE_BBP:
|
||||
break;
|
||||
case P54_CONTROL_TYPE_STAT_READBACK:
|
||||
p54_rx_stats(dev, skb);
|
||||
break;
|
||||
case P54_CONTROL_TYPE_EEPROM_READBACK:
|
||||
p54_rx_eeprom_readback(dev, skb);
|
||||
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->tx_antenna = (info->antenna_sel_tx == 0) ?
|
||||
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) ?
|
||||
0 : cts_rate;
|
||||
if (padding)
|
||||
@ -1021,12 +1125,25 @@ static int p54_start(struct ieee80211_hw *dev)
|
||||
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);
|
||||
if (!err)
|
||||
priv->mode = IEEE80211_IF_TYPE_MNTR;
|
||||
|
||||
p54_init_vdcf(dev);
|
||||
|
||||
mod_timer(&priv->stats_timer, jiffies + HZ);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -1034,9 +1151,12 @@ static void p54_stop(struct ieee80211_hw *dev)
|
||||
{
|
||||
struct p54_common *priv = dev->priv;
|
||||
struct sk_buff *skb;
|
||||
|
||||
del_timer(&priv->stats_timer);
|
||||
while ((skb = skb_dequeue(&priv->tx_queue)))
|
||||
kfree_skb(skb);
|
||||
priv->stop(dev);
|
||||
priv->tsf_high32 = priv->tsf_low32 = 0;
|
||||
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);
|
||||
priv->rx_antenna = (conf->antenna_sel_rx == 0) ?
|
||||
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));
|
||||
p54_set_vdcf(dev);
|
||||
mutex_unlock(&priv->conf_mutex);
|
||||
@ -1118,13 +1239,26 @@ static void p54_configure_filter(struct ieee80211_hw *dev,
|
||||
{
|
||||
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 (*total_flags & FIF_BCN_PRBRESP_PROMISC)
|
||||
p54_set_filter(dev, 0, NULL);
|
||||
p54_set_filter(dev, priv->filter_type, NULL);
|
||||
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;
|
||||
}
|
||||
|
||||
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,
|
||||
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;
|
||||
}
|
||||
|
||||
@ -1193,12 +1384,12 @@ struct ieee80211_hw *p54_init_common(size_t priv_data_len)
|
||||
skb_queue_head_init(&priv->tx_queue);
|
||||
dev->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | /* not sure */
|
||||
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->channel_change_time = 1000; /* TODO: find actual value */
|
||||
dev->max_signal = 127;
|
||||
|
||||
priv->tx_stats[0].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[4].limit = 5;
|
||||
dev->queues = 1;
|
||||
priv->noise = -94;
|
||||
dev->extra_tx_headroom = sizeof(struct p54_control_hdr) + 4 +
|
||||
sizeof(struct p54_tx_control_allocdata);
|
||||
|
||||
mutex_init(&priv->conf_mutex);
|
||||
init_completion(&priv->eeprom_comp);
|
||||
init_completion(&priv->stats_comp);
|
||||
setup_timer(&priv->stats_timer, p54_statistics_timer,
|
||||
(unsigned long)dev);
|
||||
|
||||
return dev;
|
||||
}
|
||||
@ -1219,6 +1414,7 @@ EXPORT_SYMBOL_GPL(p54_init_common);
|
||||
void p54_free_common(struct ieee80211_hw *dev)
|
||||
{
|
||||
struct p54_common *priv = dev->priv;
|
||||
kfree(priv->cached_stats);
|
||||
kfree(priv->iq_autocal);
|
||||
kfree(priv->output_limit);
|
||||
kfree(priv->curve_data);
|
||||
|
@ -185,7 +185,8 @@ struct p54_rx_hdr {
|
||||
u8 rssi;
|
||||
u8 quality;
|
||||
u16 unknown2;
|
||||
__le64 timestamp;
|
||||
__le32 tsf32;
|
||||
__le32 unalloc0;
|
||||
u8 align[0];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
@ -300,4 +301,24 @@ struct p54_tx_control_vdcf {
|
||||
__le16 frameburst;
|
||||
} __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 */
|
||||
|
@ -40,11 +40,15 @@ config RT2X00_LIB_CRYPTO
|
||||
config RT2X00_LIB_RFKILL
|
||||
boolean
|
||||
depends on RT2X00_LIB
|
||||
select RFKILL
|
||||
depends on RFKILL
|
||||
default y
|
||||
|
||||
config RT2X00_LIB_LEDS
|
||||
boolean
|
||||
depends on RT2X00_LIB && NEW_LEDS
|
||||
depends on RT2X00_LIB
|
||||
depends on NEW_LEDS
|
||||
depends on LEDS_CLASS
|
||||
default y
|
||||
|
||||
config RT2400PCI
|
||||
tristate "Ralink rt2400 (PCI/PCMCIA) support"
|
||||
@ -57,23 +61,6 @@ config RT2400PCI
|
||||
|
||||
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
|
||||
tristate "Ralink rt2500 (PCI/PCMCIA) support"
|
||||
depends on PCI
|
||||
@ -85,23 +72,6 @@ config RT2500PCI
|
||||
|
||||
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
|
||||
tristate "Ralink rt2501/rt61 (PCI/PCMCIA) support"
|
||||
depends on PCI
|
||||
@ -116,23 +86,6 @@ config RT61PCI
|
||||
|
||||
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
|
||||
tristate "Ralink rt2500 (USB) support"
|
||||
depends on USB
|
||||
@ -143,14 +96,6 @@ config RT2500USB
|
||||
|
||||
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
|
||||
tristate "Ralink rt2501/rt73 (USB) support"
|
||||
depends on USB
|
||||
@ -164,14 +109,6 @@ config RT73USB
|
||||
|
||||
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
|
||||
bool "Ralink debugfs support"
|
||||
depends on RT2X00_LIB && MAC80211_DEBUGFS
|
||||
|
@ -231,7 +231,7 @@ static const struct rt2x00debug rt2400pci_rt2x00debug = {
|
||||
};
|
||||
#endif /* CONFIG_RT2X00_LIB_DEBUGFS */
|
||||
|
||||
#ifdef CONFIG_RT2400PCI_RFKILL
|
||||
#ifdef CONFIG_RT2X00_LIB_RFKILL
|
||||
static int rt2400pci_rfkill_poll(struct rt2x00_dev *rt2x00dev)
|
||||
{
|
||||
u32 reg;
|
||||
@ -241,9 +241,9 @@ static int rt2400pci_rfkill_poll(struct rt2x00_dev *rt2x00dev)
|
||||
}
|
||||
#else
|
||||
#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,
|
||||
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->flags = LED_INITIALIZED;
|
||||
}
|
||||
#endif /* CONFIG_RT2400PCI_LEDS */
|
||||
#endif /* CONFIG_RT2X00_LIB_LEDS */
|
||||
|
||||
/*
|
||||
* Configuration handlers.
|
||||
@ -1374,22 +1374,22 @@ static int rt2400pci_init_eeprom(struct rt2x00_dev *rt2x00dev)
|
||||
/*
|
||||
* 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);
|
||||
|
||||
rt2400pci_init_led(rt2x00dev, &rt2x00dev->led_radio, LED_TYPE_RADIO);
|
||||
if (value == LED_MODE_TXRX_ACTIVITY)
|
||||
rt2400pci_init_led(rt2x00dev, &rt2x00dev->led_qual,
|
||||
LED_TYPE_ACTIVITY);
|
||||
#endif /* CONFIG_RT2400PCI_LEDS */
|
||||
#endif /* CONFIG_RT2X00_LIB_LEDS */
|
||||
|
||||
/*
|
||||
* 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))
|
||||
__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.
|
||||
|
@ -231,7 +231,7 @@ static const struct rt2x00debug rt2500pci_rt2x00debug = {
|
||||
};
|
||||
#endif /* CONFIG_RT2X00_LIB_DEBUGFS */
|
||||
|
||||
#ifdef CONFIG_RT2500PCI_RFKILL
|
||||
#ifdef CONFIG_RT2X00_LIB_RFKILL
|
||||
static int rt2500pci_rfkill_poll(struct rt2x00_dev *rt2x00dev)
|
||||
{
|
||||
u32 reg;
|
||||
@ -241,9 +241,9 @@ static int rt2500pci_rfkill_poll(struct rt2x00_dev *rt2x00dev)
|
||||
}
|
||||
#else
|
||||
#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,
|
||||
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->flags = LED_INITIALIZED;
|
||||
}
|
||||
#endif /* CONFIG_RT2500PCI_LEDS */
|
||||
#endif /* CONFIG_RT2X00_LIB_LEDS */
|
||||
|
||||
/*
|
||||
* Configuration handlers.
|
||||
@ -1533,22 +1533,22 @@ static int rt2500pci_init_eeprom(struct rt2x00_dev *rt2x00dev)
|
||||
/*
|
||||
* 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);
|
||||
|
||||
rt2500pci_init_led(rt2x00dev, &rt2x00dev->led_radio, LED_TYPE_RADIO);
|
||||
if (value == LED_MODE_TXRX_ACTIVITY)
|
||||
rt2500pci_init_led(rt2x00dev, &rt2x00dev->led_qual,
|
||||
LED_TYPE_ACTIVITY);
|
||||
#endif /* CONFIG_RT2500PCI_LEDS */
|
||||
#endif /* CONFIG_RT2X00_LIB_LEDS */
|
||||
|
||||
/*
|
||||
* 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))
|
||||
__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.
|
||||
|
@ -288,7 +288,7 @@ static const struct rt2x00debug rt2500usb_rt2x00debug = {
|
||||
};
|
||||
#endif /* CONFIG_RT2X00_LIB_DEBUGFS */
|
||||
|
||||
#ifdef CONFIG_RT2500USB_LEDS
|
||||
#ifdef CONFIG_RT2X00_LIB_LEDS
|
||||
static void rt2500usb_brightness_set(struct led_classdev *led_cdev,
|
||||
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->flags = LED_INITIALIZED;
|
||||
}
|
||||
#endif /* CONFIG_RT2500USB_LEDS */
|
||||
#endif /* CONFIG_RT2X00_LIB_LEDS */
|
||||
|
||||
/*
|
||||
* Configuration handlers.
|
||||
@ -1133,7 +1133,6 @@ static void rt2500usb_write_beacon(struct queue_entry *entry)
|
||||
int pipe = usb_sndbulkpipe(usb_dev, 1);
|
||||
int length;
|
||||
u16 reg;
|
||||
u32 word, len;
|
||||
|
||||
/*
|
||||
* 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);
|
||||
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,
|
||||
* 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.
|
||||
*/
|
||||
#ifdef CONFIG_RT2500USB_LEDS
|
||||
#ifdef CONFIG_RT2X00_LIB_LEDS
|
||||
value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_LED_MODE);
|
||||
|
||||
rt2500usb_init_led(rt2x00dev, &rt2x00dev->led_radio, LED_TYPE_RADIO);
|
||||
if (value == LED_MODE_TXRX_ACTIVITY)
|
||||
rt2500usb_init_led(rt2x00dev, &rt2x00dev->led_qual,
|
||||
LED_TYPE_ACTIVITY);
|
||||
#endif /* CONFIG_RT2500USB_LEDS */
|
||||
#endif /* CONFIG_RT2X00_LIB_LEDS */
|
||||
|
||||
/*
|
||||
* Check if the BBP tuning should be disabled.
|
||||
|
@ -45,16 +45,15 @@ static int rt2x00rfkill_toggle_radio(void *data, enum rfkill_state state)
|
||||
return 0;
|
||||
|
||||
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);
|
||||
retval = rt2x00lib_enable_radio(rt2x00dev);
|
||||
} 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);
|
||||
rt2x00lib_disable_radio(rt2x00dev);
|
||||
} else {
|
||||
WARNING(rt2x00dev, "Received unexpected rfkill state %d.\n",
|
||||
state);
|
||||
WARNING(rt2x00dev, "RFKILL event: unknown state %d.\n", state);
|
||||
}
|
||||
|
||||
return retval;
|
||||
@ -64,7 +63,12 @@ static int rt2x00rfkill_get_state(void *data, enum rfkill_state *state)
|
||||
{
|
||||
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;
|
||||
}
|
||||
@ -73,19 +77,18 @@ static void rt2x00rfkill_poll(struct work_struct *work)
|
||||
{
|
||||
struct rt2x00_dev *rt2x00dev =
|
||||
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;
|
||||
|
||||
/*
|
||||
* rfkill_poll reports 1 when the key has been pressed and the
|
||||
* radio should be blocked.
|
||||
* Poll latest state and report it to rfkill who should sort
|
||||
* out if the state should be toggled or not.
|
||||
*/
|
||||
state = !rt2x00dev->ops->lib->rfkill_poll(rt2x00dev) ?
|
||||
RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED;
|
||||
|
||||
rfkill_force_state(rt2x00dev->rfkill, state);
|
||||
if (!rt2x00rfkill_get_state(rt2x00dev, &state))
|
||||
rfkill_force_state(rt2x00dev->rfkill, state);
|
||||
|
||||
queue_delayed_work(rt2x00dev->hw->workqueue,
|
||||
&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)
|
||||
{
|
||||
if (!test_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags) ||
|
||||
!test_bit(RFKILL_STATE_ALLOCATED, &rt2x00dev->rfkill_state))
|
||||
if (!test_bit(RFKILL_STATE_ALLOCATED, &rt2x00dev->rfkill_state) ||
|
||||
test_bit(RFKILL_STATE_REGISTERED, &rt2x00dev->rfkill_state))
|
||||
return;
|
||||
|
||||
if (rfkill_register(rt2x00dev->rfkill)) {
|
||||
@ -114,7 +117,7 @@ void rt2x00rfkill_register(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))
|
||||
return;
|
||||
|
||||
@ -127,21 +130,25 @@ void rt2x00rfkill_unregister(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;
|
||||
|
||||
rt2x00dev->rfkill =
|
||||
rfkill_allocate(wiphy_dev(rt2x00dev->hw->wiphy), RFKILL_TYPE_WLAN);
|
||||
rt2x00dev->rfkill = rfkill_allocate(dev, RFKILL_TYPE_WLAN);
|
||||
if (!rt2x00dev->rfkill) {
|
||||
ERROR(rt2x00dev, "Failed to allocate rfkill handler.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
__set_bit(RFKILL_STATE_ALLOCATED, &rt2x00dev->rfkill_state);
|
||||
|
||||
rt2x00dev->rfkill->name = rt2x00dev->ops->name;
|
||||
rt2x00dev->rfkill->data = rt2x00dev;
|
||||
rt2x00dev->rfkill->state = -1;
|
||||
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);
|
||||
|
||||
@ -150,8 +157,7 @@ void rt2x00rfkill_allocate(struct rt2x00_dev *rt2x00dev)
|
||||
|
||||
void rt2x00rfkill_free(struct rt2x00_dev *rt2x00dev)
|
||||
{
|
||||
if (!test_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags) ||
|
||||
!test_bit(RFKILL_STATE_ALLOCATED, &rt2x00dev->rfkill_state))
|
||||
if (!test_bit(RFKILL_STATE_ALLOCATED, &rt2x00dev->flags))
|
||||
return;
|
||||
|
||||
cancel_delayed_work_sync(&rt2x00dev->rfkill_work);
|
||||
|
@ -163,7 +163,7 @@ rf_write:
|
||||
rt2x00_rf_write(rt2x00dev, word, value);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_RT61PCI_LEDS
|
||||
#ifdef CONFIG_RT2X00_LIB_LEDS
|
||||
/*
|
||||
* This function is only called from rt61pci_led_brightness()
|
||||
* 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(®, HOST_CMD_CSR_INTERRUPT_MCU, 1);
|
||||
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)
|
||||
{
|
||||
@ -271,7 +271,7 @@ static const struct rt2x00debug rt61pci_rt2x00debug = {
|
||||
};
|
||||
#endif /* CONFIG_RT2X00_LIB_DEBUGFS */
|
||||
|
||||
#ifdef CONFIG_RT61PCI_RFKILL
|
||||
#ifdef CONFIG_RT2X00_LIB_RFKILL
|
||||
static int rt61pci_rfkill_poll(struct rt2x00_dev *rt2x00dev)
|
||||
{
|
||||
u32 reg;
|
||||
@ -281,9 +281,9 @@ static int rt61pci_rfkill_poll(struct rt2x00_dev *rt2x00dev)
|
||||
}
|
||||
#else
|
||||
#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,
|
||||
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->flags = LED_INITIALIZED;
|
||||
}
|
||||
#endif /* CONFIG_RT61PCI_LEDS */
|
||||
#endif /* CONFIG_RT2X00_LIB_LEDS */
|
||||
|
||||
/*
|
||||
* Configuration handlers.
|
||||
@ -2313,10 +2313,10 @@ static int rt61pci_init_eeprom(struct rt2x00_dev *rt2x00dev)
|
||||
/*
|
||||
* 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))
|
||||
__set_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags);
|
||||
#endif /* CONFIG_RT61PCI_RFKILL */
|
||||
#endif /* CONFIG_RT2X00_LIB_RFKILL */
|
||||
|
||||
/*
|
||||
* 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,
|
||||
* switch to default led mode.
|
||||
*/
|
||||
#ifdef CONFIG_RT61PCI_LEDS
|
||||
#ifdef CONFIG_RT2X00_LIB_LEDS
|
||||
rt2x00_eeprom_read(rt2x00dev, EEPROM_LED, &eeprom);
|
||||
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_get_field16(eeprom,
|
||||
EEPROM_LED_POLARITY_RDY_A));
|
||||
#endif /* CONFIG_RT61PCI_LEDS */
|
||||
#endif /* CONFIG_RT2X00_LIB_LEDS */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -292,7 +292,7 @@ static const struct rt2x00debug rt73usb_rt2x00debug = {
|
||||
};
|
||||
#endif /* CONFIG_RT2X00_LIB_DEBUGFS */
|
||||
|
||||
#ifdef CONFIG_RT73USB_LEDS
|
||||
#ifdef CONFIG_RT2X00_LIB_LEDS
|
||||
static void rt73usb_brightness_set(struct led_classdev *led_cdev,
|
||||
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->flags = LED_INITIALIZED;
|
||||
}
|
||||
#endif /* CONFIG_RT73USB_LEDS */
|
||||
#endif /* CONFIG_RT2X00_LIB_LEDS */
|
||||
|
||||
/*
|
||||
* 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);
|
||||
unsigned int beacon_base;
|
||||
u32 reg;
|
||||
u32 word, len;
|
||||
|
||||
/*
|
||||
* 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);
|
||||
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,
|
||||
* 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.
|
||||
*/
|
||||
#ifdef CONFIG_RT73USB_LEDS
|
||||
#ifdef CONFIG_RT2X00_LIB_LEDS
|
||||
rt2x00_eeprom_read(rt2x00dev, EEPROM_LED, &eeprom);
|
||||
|
||||
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_get_field16(eeprom,
|
||||
EEPROM_LED_POLARITY_RDY_A));
|
||||
#endif /* CONFIG_RT73USB_LEDS */
|
||||
#endif /* CONFIG_RT2X00_LIB_LEDS */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -643,6 +643,9 @@ struct ieee80211_mgmt {
|
||||
} u;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* mgmt header + 1 byte category code */
|
||||
#define IEEE80211_MIN_ACTION_SIZE offsetof(struct ieee80211_mgmt, u.action.u)
|
||||
|
||||
|
||||
/* Control frames */
|
||||
struct ieee80211_rts {
|
||||
@ -708,7 +711,7 @@ struct ieee80211_ht_addt_info {
|
||||
|
||||
/* 802.11n HT capabilities masks */
|
||||
#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_SGI_20 0x0020
|
||||
#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_HT_STA_PRSNT 0x0010
|
||||
|
||||
/* MIMO Power Save Modes */
|
||||
#define WLAN_HT_CAP_MIMO_PS_STATIC 0
|
||||
#define WLAN_HT_CAP_MIMO_PS_DYNAMIC 1
|
||||
#define WLAN_HT_CAP_MIMO_PS_INVALID 2
|
||||
#define WLAN_HT_CAP_MIMO_PS_DISABLED 3
|
||||
/* block-ack parameters */
|
||||
#define IEEE80211_ADDBA_PARAM_POLICY_MASK 0x0002
|
||||
#define IEEE80211_ADDBA_PARAM_TID_MASK 0x003C
|
||||
#define IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK 0xFFA0
|
||||
#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 */
|
||||
#define WLAN_AUTH_OPEN 0
|
||||
|
@ -1142,7 +1142,7 @@ enum ieee80211_ampdu_mlme_action {
|
||||
* of assocaited station or AP.
|
||||
*
|
||||
* @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
|
||||
* to get number of currently queued packets (queue length), maximum queue
|
||||
|
@ -7,6 +7,8 @@ mac80211-y := \
|
||||
sta_info.o \
|
||||
wep.o \
|
||||
wpa.o \
|
||||
scan.o \
|
||||
ht.o \
|
||||
mlme.o \
|
||||
iface.o \
|
||||
rate.o \
|
||||
@ -15,6 +17,7 @@ mac80211-y := \
|
||||
aes_ccm.o \
|
||||
cfg.o \
|
||||
rx.o \
|
||||
spectmgmt.o \
|
||||
tx.o \
|
||||
key.o \
|
||||
util.o \
|
||||
|
992
net/mac80211/ht.c
Normal file
992
net/mac80211/ht.c
Normal 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);
|
||||
}
|
||||
}
|
@ -53,6 +53,12 @@ struct ieee80211_local;
|
||||
* increased memory use (about 2 kB of RAM per entry). */
|
||||
#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 {
|
||||
unsigned long first_frag_time;
|
||||
unsigned int seq;
|
||||
@ -636,7 +642,7 @@ struct ieee80211_local {
|
||||
enum { SCAN_SET_CHANNEL, SCAN_SEND_PROBE } scan_state;
|
||||
unsigned long last_scan_completed;
|
||||
struct delayed_work scan_work;
|
||||
struct net_device *scan_dev;
|
||||
struct ieee80211_sub_if_data *scan_sdata;
|
||||
struct ieee80211_channel *oper_channel, *scan_channel;
|
||||
u8 scan_ssid[IEEE80211_MAX_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,
|
||||
u32 changed);
|
||||
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,
|
||||
struct ieee802_11_elems *elems,
|
||||
enum ieee80211_band band);
|
||||
void ieee80211_sta_tx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
|
||||
int encrypt);
|
||||
void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
|
||||
u8 *ssid, size_t ssid_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
|
||||
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_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 */
|
||||
extern void *mac80211_wiphy_privid; /* for wiphy privid */
|
||||
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);
|
||||
void mac80211_ev_michael_mic_failure(struct ieee80211_sub_if_data *sdata, int keyidx,
|
||||
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
|
||||
#define debug_noinline noinline
|
||||
|
@ -31,11 +31,11 @@ static void ieee80211_teardown_sdata(struct net_device *dev)
|
||||
int flushed;
|
||||
int i;
|
||||
|
||||
ieee80211_debugfs_remove_netdev(sdata);
|
||||
|
||||
/* free extra data */
|
||||
ieee80211_free_keys(sdata);
|
||||
|
||||
ieee80211_debugfs_remove_netdev(sdata);
|
||||
|
||||
for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++)
|
||||
__skb_queue_purge(&sdata->fragments[i].skb_list);
|
||||
sdata->fragment_next = 0;
|
||||
|
@ -399,8 +399,15 @@ static int ieee80211_open(struct net_device *dev)
|
||||
atomic_inc(&local->iff_promiscs);
|
||||
|
||||
local->open_count++;
|
||||
if (need_hw_reconfig)
|
||||
if (need_hw_reconfig) {
|
||||
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
|
||||
@ -551,7 +558,7 @@ static int ieee80211_stop(struct net_device *dev)
|
||||
synchronize_rcu();
|
||||
skb_queue_purge(&sdata->u.sta.skb_queue);
|
||||
|
||||
if (local->scan_dev == sdata->dev) {
|
||||
if (local->scan_sdata == sdata) {
|
||||
if (!local->ops->hw_scan) {
|
||||
local->sta_sw_scanning = 0;
|
||||
cancel_delayed_work(&local->scan_work);
|
||||
@ -593,379 +600,6 @@ static int ieee80211_stop(struct net_device *dev)
|
||||
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)
|
||||
{
|
||||
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.cap = req_ht_cap->cap & sband->ht_info.cap;
|
||||
ht_conf.cap &= ~(IEEE80211_HT_CAP_MIMO_PS);
|
||||
ht_conf.cap |= sband->ht_info.cap & IEEE80211_HT_CAP_MIMO_PS;
|
||||
ht_conf.cap &= ~(IEEE80211_HT_CAP_SM_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.bss_cap = req_bss_cap->bss_cap;
|
||||
ht_bss_conf.bss_op_mode = req_bss_cap->bss_op_mode;
|
||||
|
@ -149,7 +149,7 @@ static int mesh_path_sel_frame_tx(enum mpath_frame_type action, u8 flags,
|
||||
pos += ETH_ALEN;
|
||||
memcpy(pos, &dst_dsn, 4);
|
||||
|
||||
ieee80211_sta_tx(sdata, skb, 0);
|
||||
ieee80211_tx_skb(sdata, skb, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -198,7 +198,7 @@ int mesh_path_error_tx(u8 *dst, __le32 dst_dsn, u8 *ra,
|
||||
pos += ETH_ALEN;
|
||||
memcpy(pos, &dst_dsn, 4);
|
||||
|
||||
ieee80211_sta_tx(sdata, skb, 0);
|
||||
ieee80211_tx_skb(sdata, skb, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -581,6 +581,10 @@ void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata,
|
||||
size_t baselen;
|
||||
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;
|
||||
ieee802_11_parse_elems(mgmt->u.action.u.mesh_action.variable,
|
||||
len - baselen, &elems);
|
||||
|
@ -217,7 +217,7 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
|
||||
memcpy(pos, &reason, 2);
|
||||
}
|
||||
|
||||
ieee80211_sta_tx(sdata, skb, 0);
|
||||
ieee80211_tx_skb(sdata, skb, 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);
|
||||
#endif
|
||||
|
||||
/* need action_code, aux */
|
||||
if (len < IEEE80211_MIN_ACTION_SIZE + 3)
|
||||
return;
|
||||
|
||||
if (is_multicast_ether_addr(mgmt->da)) {
|
||||
mpl_dbg("Mesh plink: ignore frame from multicast address");
|
||||
return;
|
||||
|
3131
net/mac80211/mlme.c
3131
net/mac80211/mlme.c
File diff suppressed because it is too large
Load Diff
@ -1511,22 +1511,95 @@ ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx)
|
||||
}
|
||||
|
||||
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))
|
||||
return RX_DROP_MONITOR;
|
||||
|
||||
sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
|
||||
if ((sdata->vif.type == IEEE80211_IF_TYPE_STA ||
|
||||
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
|
||||
/* all categories we currently handle have action_code */
|
||||
if (len < IEEE80211_MIN_ACTION_SIZE + 1)
|
||||
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;
|
||||
}
|
||||
|
||||
@ -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_data)
|
||||
CALL_RXH(ieee80211_rx_h_ctrl)
|
||||
CALL_RXH(ieee80211_rx_h_action)
|
||||
CALL_RXH(ieee80211_rx_h_mgmt)
|
||||
|
||||
#undef CALL_RXH
|
||||
|
933
net/mac80211/scan.c
Normal file
933
net/mac80211/scan.c
Normal 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, ¤t_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
86
net/mac80211/spectmgmt.c
Normal 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);
|
||||
}
|
@ -428,3 +428,187 @@ void ieee80211_iterate_active_interfaces_atomic(
|
||||
rcu_read_unlock();
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user