From 5dfdaf58d61f06a458529430c24b1191ea4d1a27 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 19 Dec 2007 02:03:33 +0100 Subject: 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 Signed-off-by: John W. Linville --- net/mac80211/cfg.c | 156 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) (limited to 'net/mac80211/cfg.c') diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index d02d9ef6b1e..5a4c6edd934 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include "ieee80211_i.h" #include "cfg.h" @@ -294,6 +295,158 @@ static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev, 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 = { .add_virtual_intf = ieee80211_add_iface, .del_virtual_intf = ieee80211_del_iface, @@ -302,5 +455,8 @@ struct cfg80211_ops mac80211_config_ops = { .del_key = ieee80211_del_key, .get_key = ieee80211_get_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, }; -- cgit v1.2.3