mac80211: add beacon configuration via cfg80211
This patch implements the cfg80211 hooks for configuring beaconing on an access point interface in mac80211. While doing so, it fixes a number of races that could badly crash the machine when the beacon is changed while being requested by the driver. Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
parent
51fb61e76d
commit
5dfdaf58d6
|
@ -10,6 +10,7 @@
|
||||||
#include <linux/nl80211.h>
|
#include <linux/nl80211.h>
|
||||||
#include <linux/rtnetlink.h>
|
#include <linux/rtnetlink.h>
|
||||||
#include <net/net_namespace.h>
|
#include <net/net_namespace.h>
|
||||||
|
#include <linux/rcupdate.h>
|
||||||
#include <net/cfg80211.h>
|
#include <net/cfg80211.h>
|
||||||
#include "ieee80211_i.h"
|
#include "ieee80211_i.h"
|
||||||
#include "cfg.h"
|
#include "cfg.h"
|
||||||
|
@ -294,6 +295,158 @@ static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This handles both adding a beacon and setting new beacon info
|
||||||
|
*/
|
||||||
|
static int ieee80211_config_beacon(struct ieee80211_sub_if_data *sdata,
|
||||||
|
struct beacon_parameters *params)
|
||||||
|
{
|
||||||
|
struct beacon_data *new, *old;
|
||||||
|
int new_head_len, new_tail_len;
|
||||||
|
int size;
|
||||||
|
int err = -EINVAL;
|
||||||
|
|
||||||
|
old = sdata->u.ap.beacon;
|
||||||
|
|
||||||
|
/* head must not be zero-length */
|
||||||
|
if (params->head && !params->head_len)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is a kludge. beacon interval should really be part
|
||||||
|
* of the beacon information.
|
||||||
|
*/
|
||||||
|
if (params->interval) {
|
||||||
|
sdata->local->hw.conf.beacon_int = params->interval;
|
||||||
|
if (ieee80211_hw_config(sdata->local))
|
||||||
|
return -EINVAL;
|
||||||
|
/*
|
||||||
|
* We updated some parameter so if below bails out
|
||||||
|
* it's not an error.
|
||||||
|
*/
|
||||||
|
err = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Need to have a beacon head if we don't have one yet */
|
||||||
|
if (!params->head && !old)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
/* sorry, no way to start beaconing without dtim period */
|
||||||
|
if (!params->dtim_period && !old)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
/* new or old head? */
|
||||||
|
if (params->head)
|
||||||
|
new_head_len = params->head_len;
|
||||||
|
else
|
||||||
|
new_head_len = old->head_len;
|
||||||
|
|
||||||
|
/* new or old tail? */
|
||||||
|
if (params->tail || !old)
|
||||||
|
/* params->tail_len will be zero for !params->tail */
|
||||||
|
new_tail_len = params->tail_len;
|
||||||
|
else
|
||||||
|
new_tail_len = old->tail_len;
|
||||||
|
|
||||||
|
size = sizeof(*new) + new_head_len + new_tail_len;
|
||||||
|
|
||||||
|
new = kzalloc(size, GFP_KERNEL);
|
||||||
|
if (!new)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
/* start filling the new info now */
|
||||||
|
|
||||||
|
/* new or old dtim period? */
|
||||||
|
if (params->dtim_period)
|
||||||
|
new->dtim_period = params->dtim_period;
|
||||||
|
else
|
||||||
|
new->dtim_period = old->dtim_period;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* pointers go into the block we allocated,
|
||||||
|
* memory is | beacon_data | head | tail |
|
||||||
|
*/
|
||||||
|
new->head = ((u8 *) new) + sizeof(*new);
|
||||||
|
new->tail = new->head + new_head_len;
|
||||||
|
new->head_len = new_head_len;
|
||||||
|
new->tail_len = new_tail_len;
|
||||||
|
|
||||||
|
/* copy in head */
|
||||||
|
if (params->head)
|
||||||
|
memcpy(new->head, params->head, new_head_len);
|
||||||
|
else
|
||||||
|
memcpy(new->head, old->head, new_head_len);
|
||||||
|
|
||||||
|
/* copy in optional tail */
|
||||||
|
if (params->tail)
|
||||||
|
memcpy(new->tail, params->tail, new_tail_len);
|
||||||
|
else
|
||||||
|
if (old)
|
||||||
|
memcpy(new->tail, old->tail, new_tail_len);
|
||||||
|
|
||||||
|
rcu_assign_pointer(sdata->u.ap.beacon, new);
|
||||||
|
|
||||||
|
synchronize_rcu();
|
||||||
|
|
||||||
|
kfree(old);
|
||||||
|
|
||||||
|
return ieee80211_if_config_beacon(sdata->dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ieee80211_add_beacon(struct wiphy *wiphy, struct net_device *dev,
|
||||||
|
struct beacon_parameters *params)
|
||||||
|
{
|
||||||
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||||
|
struct beacon_data *old;
|
||||||
|
|
||||||
|
if (sdata->vif.type != IEEE80211_IF_TYPE_AP)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
old = sdata->u.ap.beacon;
|
||||||
|
|
||||||
|
if (old)
|
||||||
|
return -EALREADY;
|
||||||
|
|
||||||
|
return ieee80211_config_beacon(sdata, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ieee80211_set_beacon(struct wiphy *wiphy, struct net_device *dev,
|
||||||
|
struct beacon_parameters *params)
|
||||||
|
{
|
||||||
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||||
|
struct beacon_data *old;
|
||||||
|
|
||||||
|
if (sdata->vif.type != IEEE80211_IF_TYPE_AP)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
old = sdata->u.ap.beacon;
|
||||||
|
|
||||||
|
if (!old)
|
||||||
|
return -ENOENT;
|
||||||
|
|
||||||
|
return ieee80211_config_beacon(sdata, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ieee80211_del_beacon(struct wiphy *wiphy, struct net_device *dev)
|
||||||
|
{
|
||||||
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||||
|
struct beacon_data *old;
|
||||||
|
|
||||||
|
if (sdata->vif.type != IEEE80211_IF_TYPE_AP)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
old = sdata->u.ap.beacon;
|
||||||
|
|
||||||
|
if (!old)
|
||||||
|
return -ENOENT;
|
||||||
|
|
||||||
|
rcu_assign_pointer(sdata->u.ap.beacon, NULL);
|
||||||
|
synchronize_rcu();
|
||||||
|
kfree(old);
|
||||||
|
|
||||||
|
return ieee80211_if_config_beacon(dev);
|
||||||
|
}
|
||||||
|
|
||||||
struct cfg80211_ops mac80211_config_ops = {
|
struct cfg80211_ops mac80211_config_ops = {
|
||||||
.add_virtual_intf = ieee80211_add_iface,
|
.add_virtual_intf = ieee80211_add_iface,
|
||||||
.del_virtual_intf = ieee80211_del_iface,
|
.del_virtual_intf = ieee80211_del_iface,
|
||||||
|
@ -302,5 +455,8 @@ struct cfg80211_ops mac80211_config_ops = {
|
||||||
.del_key = ieee80211_del_key,
|
.del_key = ieee80211_del_key,
|
||||||
.get_key = ieee80211_get_key,
|
.get_key = ieee80211_get_key,
|
||||||
.set_default_key = ieee80211_config_default_key,
|
.set_default_key = ieee80211_config_default_key,
|
||||||
|
.add_beacon = ieee80211_add_beacon,
|
||||||
|
.set_beacon = ieee80211_set_beacon,
|
||||||
|
.del_beacon = ieee80211_del_beacon,
|
||||||
.get_station = ieee80211_get_station,
|
.get_station = ieee80211_get_station,
|
||||||
};
|
};
|
||||||
|
|
|
@ -124,7 +124,6 @@ __IEEE80211_IF_FILE(flags);
|
||||||
|
|
||||||
/* AP attributes */
|
/* AP attributes */
|
||||||
IEEE80211_IF_FILE(num_sta_ps, u.ap.num_sta_ps, ATOMIC);
|
IEEE80211_IF_FILE(num_sta_ps, u.ap.num_sta_ps, ATOMIC);
|
||||||
IEEE80211_IF_FILE(dtim_period, u.ap.dtim_period, DEC);
|
|
||||||
IEEE80211_IF_FILE(dtim_count, u.ap.dtim_count, DEC);
|
IEEE80211_IF_FILE(dtim_count, u.ap.dtim_count, DEC);
|
||||||
IEEE80211_IF_FILE(num_beacons, u.ap.num_beacons, DEC);
|
IEEE80211_IF_FILE(num_beacons, u.ap.num_beacons, DEC);
|
||||||
IEEE80211_IF_FILE(force_unicast_rateidx, u.ap.force_unicast_rateidx, DEC);
|
IEEE80211_IF_FILE(force_unicast_rateidx, u.ap.force_unicast_rateidx, DEC);
|
||||||
|
@ -138,26 +137,6 @@ static ssize_t ieee80211_if_fmt_num_buffered_multicast(
|
||||||
}
|
}
|
||||||
__IEEE80211_IF_FILE(num_buffered_multicast);
|
__IEEE80211_IF_FILE(num_buffered_multicast);
|
||||||
|
|
||||||
static ssize_t ieee80211_if_fmt_beacon_head_len(
|
|
||||||
const struct ieee80211_sub_if_data *sdata, char *buf, int buflen)
|
|
||||||
{
|
|
||||||
if (sdata->u.ap.beacon_head)
|
|
||||||
return scnprintf(buf, buflen, "%d\n",
|
|
||||||
sdata->u.ap.beacon_head_len);
|
|
||||||
return scnprintf(buf, buflen, "\n");
|
|
||||||
}
|
|
||||||
__IEEE80211_IF_FILE(beacon_head_len);
|
|
||||||
|
|
||||||
static ssize_t ieee80211_if_fmt_beacon_tail_len(
|
|
||||||
const struct ieee80211_sub_if_data *sdata, char *buf, int buflen)
|
|
||||||
{
|
|
||||||
if (sdata->u.ap.beacon_tail)
|
|
||||||
return scnprintf(buf, buflen, "%d\n",
|
|
||||||
sdata->u.ap.beacon_tail_len);
|
|
||||||
return scnprintf(buf, buflen, "\n");
|
|
||||||
}
|
|
||||||
__IEEE80211_IF_FILE(beacon_tail_len);
|
|
||||||
|
|
||||||
/* WDS attributes */
|
/* WDS attributes */
|
||||||
IEEE80211_IF_FILE(peer, u.wds.remote_addr, MAC);
|
IEEE80211_IF_FILE(peer, u.wds.remote_addr, MAC);
|
||||||
|
|
||||||
|
@ -192,14 +171,11 @@ static void add_ap_files(struct ieee80211_sub_if_data *sdata)
|
||||||
DEBUGFS_ADD(drop_unencrypted, ap);
|
DEBUGFS_ADD(drop_unencrypted, ap);
|
||||||
DEBUGFS_ADD(ieee802_1x_pac, ap);
|
DEBUGFS_ADD(ieee802_1x_pac, ap);
|
||||||
DEBUGFS_ADD(num_sta_ps, ap);
|
DEBUGFS_ADD(num_sta_ps, ap);
|
||||||
DEBUGFS_ADD(dtim_period, ap);
|
|
||||||
DEBUGFS_ADD(dtim_count, ap);
|
DEBUGFS_ADD(dtim_count, ap);
|
||||||
DEBUGFS_ADD(num_beacons, ap);
|
DEBUGFS_ADD(num_beacons, ap);
|
||||||
DEBUGFS_ADD(force_unicast_rateidx, ap);
|
DEBUGFS_ADD(force_unicast_rateidx, ap);
|
||||||
DEBUGFS_ADD(max_ratectrl_rateidx, ap);
|
DEBUGFS_ADD(max_ratectrl_rateidx, ap);
|
||||||
DEBUGFS_ADD(num_buffered_multicast, ap);
|
DEBUGFS_ADD(num_buffered_multicast, ap);
|
||||||
DEBUGFS_ADD(beacon_head_len, ap);
|
|
||||||
DEBUGFS_ADD(beacon_tail_len, ap);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void add_wds_files(struct ieee80211_sub_if_data *sdata)
|
static void add_wds_files(struct ieee80211_sub_if_data *sdata)
|
||||||
|
@ -281,14 +257,11 @@ static void del_ap_files(struct ieee80211_sub_if_data *sdata)
|
||||||
DEBUGFS_DEL(drop_unencrypted, ap);
|
DEBUGFS_DEL(drop_unencrypted, ap);
|
||||||
DEBUGFS_DEL(ieee802_1x_pac, ap);
|
DEBUGFS_DEL(ieee802_1x_pac, ap);
|
||||||
DEBUGFS_DEL(num_sta_ps, ap);
|
DEBUGFS_DEL(num_sta_ps, ap);
|
||||||
DEBUGFS_DEL(dtim_period, ap);
|
|
||||||
DEBUGFS_DEL(dtim_count, ap);
|
DEBUGFS_DEL(dtim_count, ap);
|
||||||
DEBUGFS_DEL(num_beacons, ap);
|
DEBUGFS_DEL(num_beacons, ap);
|
||||||
DEBUGFS_DEL(force_unicast_rateidx, ap);
|
DEBUGFS_DEL(force_unicast_rateidx, ap);
|
||||||
DEBUGFS_DEL(max_ratectrl_rateidx, ap);
|
DEBUGFS_DEL(max_ratectrl_rateidx, ap);
|
||||||
DEBUGFS_DEL(num_buffered_multicast, ap);
|
DEBUGFS_DEL(num_buffered_multicast, ap);
|
||||||
DEBUGFS_DEL(beacon_head_len, ap);
|
|
||||||
DEBUGFS_DEL(beacon_tail_len, ap);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void del_wds_files(struct ieee80211_sub_if_data *sdata)
|
static void del_wds_files(struct ieee80211_sub_if_data *sdata)
|
||||||
|
|
|
@ -321,10 +321,17 @@ static int ieee80211_stop(struct net_device *dev)
|
||||||
|
|
||||||
dev_mc_unsync(local->mdev, dev);
|
dev_mc_unsync(local->mdev, dev);
|
||||||
|
|
||||||
/* down all dependent devices, that is VLANs */
|
/* APs need special treatment */
|
||||||
if (sdata->vif.type == IEEE80211_IF_TYPE_AP) {
|
if (sdata->vif.type == IEEE80211_IF_TYPE_AP) {
|
||||||
struct ieee80211_sub_if_data *vlan, *tmp;
|
struct ieee80211_sub_if_data *vlan, *tmp;
|
||||||
|
struct beacon_data *old_beacon = sdata->u.ap.beacon;
|
||||||
|
|
||||||
|
/* remove beacon */
|
||||||
|
rcu_assign_pointer(sdata->u.ap.beacon, NULL);
|
||||||
|
synchronize_rcu();
|
||||||
|
kfree(old_beacon);
|
||||||
|
|
||||||
|
/* down all dependent devices, that is VLANs */
|
||||||
list_for_each_entry_safe(vlan, tmp, &sdata->u.ap.vlans,
|
list_for_each_entry_safe(vlan, tmp, &sdata->u.ap.vlans,
|
||||||
u.vlan.list)
|
u.vlan.list)
|
||||||
dev_close(vlan->dev);
|
dev_close(vlan->dev);
|
||||||
|
|
|
@ -190,9 +190,14 @@ typedef ieee80211_txrx_result (*ieee80211_tx_handler)
|
||||||
typedef ieee80211_txrx_result (*ieee80211_rx_handler)
|
typedef ieee80211_txrx_result (*ieee80211_rx_handler)
|
||||||
(struct ieee80211_txrx_data *rx);
|
(struct ieee80211_txrx_data *rx);
|
||||||
|
|
||||||
|
struct beacon_data {
|
||||||
|
u8 *head, *tail;
|
||||||
|
int head_len, tail_len;
|
||||||
|
int dtim_period;
|
||||||
|
};
|
||||||
|
|
||||||
struct ieee80211_if_ap {
|
struct ieee80211_if_ap {
|
||||||
u8 *beacon_head, *beacon_tail;
|
struct beacon_data *beacon;
|
||||||
int beacon_head_len, beacon_tail_len;
|
|
||||||
|
|
||||||
struct list_head vlans;
|
struct list_head vlans;
|
||||||
|
|
||||||
|
@ -205,7 +210,7 @@ struct ieee80211_if_ap {
|
||||||
u8 tim[sizeof(unsigned long) * BITS_TO_LONGS(IEEE80211_MAX_AID + 1)];
|
u8 tim[sizeof(unsigned long) * BITS_TO_LONGS(IEEE80211_MAX_AID + 1)];
|
||||||
atomic_t num_sta_ps; /* number of stations in PS mode */
|
atomic_t num_sta_ps; /* number of stations in PS mode */
|
||||||
struct sk_buff_head ps_bc_buf;
|
struct sk_buff_head ps_bc_buf;
|
||||||
int dtim_period, dtim_count;
|
int dtim_count;
|
||||||
int force_unicast_rateidx; /* forced TX rateidx for unicast frames */
|
int force_unicast_rateidx; /* forced TX rateidx for unicast frames */
|
||||||
int max_ratectrl_rateidx; /* max TX rateidx for rate control */
|
int max_ratectrl_rateidx; /* max TX rateidx for rate control */
|
||||||
int num_beacons; /* number of TXed beacon frames for this BSS */
|
int num_beacons; /* number of TXed beacon frames for this BSS */
|
||||||
|
@ -360,14 +365,11 @@ struct ieee80211_sub_if_data {
|
||||||
struct dentry *drop_unencrypted;
|
struct dentry *drop_unencrypted;
|
||||||
struct dentry *ieee802_1x_pac;
|
struct dentry *ieee802_1x_pac;
|
||||||
struct dentry *num_sta_ps;
|
struct dentry *num_sta_ps;
|
||||||
struct dentry *dtim_period;
|
|
||||||
struct dentry *dtim_count;
|
struct dentry *dtim_count;
|
||||||
struct dentry *num_beacons;
|
struct dentry *num_beacons;
|
||||||
struct dentry *force_unicast_rateidx;
|
struct dentry *force_unicast_rateidx;
|
||||||
struct dentry *max_ratectrl_rateidx;
|
struct dentry *max_ratectrl_rateidx;
|
||||||
struct dentry *num_buffered_multicast;
|
struct dentry *num_buffered_multicast;
|
||||||
struct dentry *beacon_head_len;
|
|
||||||
struct dentry *beacon_tail_len;
|
|
||||||
} ap;
|
} ap;
|
||||||
struct {
|
struct {
|
||||||
struct dentry *channel_use;
|
struct dentry *channel_use;
|
||||||
|
|
|
@ -126,7 +126,6 @@ void ieee80211_if_set_type(struct net_device *dev, int type)
|
||||||
sdata->u.vlan.ap = NULL;
|
sdata->u.vlan.ap = NULL;
|
||||||
break;
|
break;
|
||||||
case IEEE80211_IF_TYPE_AP:
|
case IEEE80211_IF_TYPE_AP:
|
||||||
sdata->u.ap.dtim_period = 2;
|
|
||||||
sdata->u.ap.force_unicast_rateidx = -1;
|
sdata->u.ap.force_unicast_rateidx = -1;
|
||||||
sdata->u.ap.max_ratectrl_rateidx = -1;
|
sdata->u.ap.max_ratectrl_rateidx = -1;
|
||||||
skb_queue_head_init(&sdata->u.ap.ps_bc_buf);
|
skb_queue_head_init(&sdata->u.ap.ps_bc_buf);
|
||||||
|
@ -207,8 +206,7 @@ void ieee80211_if_reinit(struct net_device *dev)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
kfree(sdata->u.ap.beacon_head);
|
kfree(sdata->u.ap.beacon);
|
||||||
kfree(sdata->u.ap.beacon_tail);
|
|
||||||
|
|
||||||
while ((skb = skb_dequeue(&sdata->u.ap.ps_bc_buf))) {
|
while ((skb = skb_dequeue(&sdata->u.ap.ps_bc_buf))) {
|
||||||
local->total_ps_buffered--;
|
local->total_ps_buffered--;
|
||||||
|
|
|
@ -1628,7 +1628,8 @@ void ieee80211_tx_pending(unsigned long data)
|
||||||
|
|
||||||
static void ieee80211_beacon_add_tim(struct ieee80211_local *local,
|
static void ieee80211_beacon_add_tim(struct ieee80211_local *local,
|
||||||
struct ieee80211_if_ap *bss,
|
struct ieee80211_if_ap *bss,
|
||||||
struct sk_buff *skb)
|
struct sk_buff *skb,
|
||||||
|
struct beacon_data *beacon)
|
||||||
{
|
{
|
||||||
u8 *pos, *tim;
|
u8 *pos, *tim;
|
||||||
int aid0 = 0;
|
int aid0 = 0;
|
||||||
|
@ -1644,7 +1645,7 @@ static void ieee80211_beacon_add_tim(struct ieee80211_local *local,
|
||||||
IEEE80211_MAX_AID+1);
|
IEEE80211_MAX_AID+1);
|
||||||
|
|
||||||
if (bss->dtim_count == 0)
|
if (bss->dtim_count == 0)
|
||||||
bss->dtim_count = bss->dtim_period - 1;
|
bss->dtim_count = beacon->dtim_period - 1;
|
||||||
else
|
else
|
||||||
bss->dtim_count--;
|
bss->dtim_count--;
|
||||||
|
|
||||||
|
@ -1652,7 +1653,7 @@ static void ieee80211_beacon_add_tim(struct ieee80211_local *local,
|
||||||
*pos++ = WLAN_EID_TIM;
|
*pos++ = WLAN_EID_TIM;
|
||||||
*pos++ = 4;
|
*pos++ = 4;
|
||||||
*pos++ = bss->dtim_count;
|
*pos++ = bss->dtim_count;
|
||||||
*pos++ = bss->dtim_period;
|
*pos++ = beacon->dtim_period;
|
||||||
|
|
||||||
if (bss->dtim_count == 0 && !skb_queue_empty(&bss->ps_bc_buf))
|
if (bss->dtim_count == 0 && !skb_queue_empty(&bss->ps_bc_buf))
|
||||||
aid0 = 1;
|
aid0 = 1;
|
||||||
|
@ -1700,44 +1701,43 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
|
||||||
struct ieee80211_sub_if_data *sdata = NULL;
|
struct ieee80211_sub_if_data *sdata = NULL;
|
||||||
struct ieee80211_if_ap *ap = NULL;
|
struct ieee80211_if_ap *ap = NULL;
|
||||||
struct rate_selection rsel;
|
struct rate_selection rsel;
|
||||||
u8 *b_head, *b_tail;
|
struct beacon_data *beacon;
|
||||||
int bh_len, bt_len;
|
|
||||||
|
rcu_read_lock();
|
||||||
|
|
||||||
sdata = vif_to_sdata(vif);
|
sdata = vif_to_sdata(vif);
|
||||||
bdev = sdata->dev;
|
bdev = sdata->dev;
|
||||||
ap = &sdata->u.ap;
|
ap = &sdata->u.ap;
|
||||||
|
|
||||||
if (!ap || sdata->vif.type != IEEE80211_IF_TYPE_AP ||
|
beacon = rcu_dereference(ap->beacon);
|
||||||
!ap->beacon_head) {
|
|
||||||
|
if (!ap || sdata->vif.type != IEEE80211_IF_TYPE_AP || !beacon) {
|
||||||
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
|
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
|
||||||
if (net_ratelimit())
|
if (net_ratelimit())
|
||||||
printk(KERN_DEBUG "no beacon data avail for %s\n",
|
printk(KERN_DEBUG "no beacon data avail for %s\n",
|
||||||
bdev->name);
|
bdev->name);
|
||||||
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
|
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
|
||||||
return NULL;
|
skb = NULL;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Assume we are generating the normal beacon locally */
|
/* headroom, head length, tail length and maximum TIM length */
|
||||||
b_head = ap->beacon_head;
|
skb = dev_alloc_skb(local->tx_headroom + beacon->head_len +
|
||||||
b_tail = ap->beacon_tail;
|
beacon->tail_len + 256);
|
||||||
bh_len = ap->beacon_head_len;
|
|
||||||
bt_len = ap->beacon_tail_len;
|
|
||||||
|
|
||||||
skb = dev_alloc_skb(local->tx_headroom +
|
|
||||||
bh_len + bt_len + 256 /* maximum TIM len */);
|
|
||||||
if (!skb)
|
if (!skb)
|
||||||
return NULL;
|
goto out;
|
||||||
|
|
||||||
skb_reserve(skb, local->tx_headroom);
|
skb_reserve(skb, local->tx_headroom);
|
||||||
memcpy(skb_put(skb, bh_len), b_head, bh_len);
|
memcpy(skb_put(skb, beacon->head_len), beacon->head,
|
||||||
|
beacon->head_len);
|
||||||
|
|
||||||
ieee80211_include_sequence(sdata, (struct ieee80211_hdr *)skb->data);
|
ieee80211_include_sequence(sdata, (struct ieee80211_hdr *)skb->data);
|
||||||
|
|
||||||
ieee80211_beacon_add_tim(local, ap, skb);
|
ieee80211_beacon_add_tim(local, ap, skb, beacon);
|
||||||
|
|
||||||
if (b_tail) {
|
if (beacon->tail)
|
||||||
memcpy(skb_put(skb, bt_len), b_tail, bt_len);
|
memcpy(skb_put(skb, beacon->tail_len), beacon->tail,
|
||||||
}
|
beacon->tail_len);
|
||||||
|
|
||||||
if (control) {
|
if (control) {
|
||||||
rate_control_get_rate(local->mdev, local->oper_hw_mode, skb,
|
rate_control_get_rate(local->mdev, local->oper_hw_mode, skb,
|
||||||
|
@ -1749,7 +1749,8 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
|
||||||
wiphy_name(local->hw.wiphy));
|
wiphy_name(local->hw.wiphy));
|
||||||
}
|
}
|
||||||
dev_kfree_skb(skb);
|
dev_kfree_skb(skb);
|
||||||
return NULL;
|
skb = NULL;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
control->tx_rate =
|
control->tx_rate =
|
||||||
|
@ -1764,6 +1765,9 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
|
||||||
}
|
}
|
||||||
|
|
||||||
ap->num_beacons++;
|
ap->num_beacons++;
|
||||||
|
|
||||||
|
out:
|
||||||
|
rcu_read_unlock();
|
||||||
return skb;
|
return skb;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(ieee80211_beacon_get);
|
EXPORT_SYMBOL(ieee80211_beacon_get);
|
||||||
|
@ -1815,14 +1819,25 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,
|
||||||
struct net_device *bdev;
|
struct net_device *bdev;
|
||||||
struct ieee80211_sub_if_data *sdata;
|
struct ieee80211_sub_if_data *sdata;
|
||||||
struct ieee80211_if_ap *bss = NULL;
|
struct ieee80211_if_ap *bss = NULL;
|
||||||
|
struct beacon_data *beacon;
|
||||||
|
|
||||||
sdata = vif_to_sdata(vif);
|
sdata = vif_to_sdata(vif);
|
||||||
bdev = sdata->dev;
|
bdev = sdata->dev;
|
||||||
|
|
||||||
if (!bss || sdata->vif.type != IEEE80211_IF_TYPE_AP ||
|
|
||||||
!bss->beacon_head)
|
if (!bss)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
beacon = rcu_dereference(bss->beacon);
|
||||||
|
|
||||||
|
if (sdata->vif.type != IEEE80211_IF_TYPE_AP || !beacon ||
|
||||||
|
!beacon->head) {
|
||||||
|
rcu_read_unlock();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
if (bss->dtim_count != 0)
|
if (bss->dtim_count != 0)
|
||||||
return NULL; /* send buffered bc/mc only after DTIM beacon */
|
return NULL; /* send buffered bc/mc only after DTIM beacon */
|
||||||
memset(control, 0, sizeof(*control));
|
memset(control, 0, sizeof(*control));
|
||||||
|
|
Loading…
Reference in New Issue