Merge branch 'for-john' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next

This commit is contained in:
John W. Linville
2013-08-09 15:08:10 -04:00
19 changed files with 772 additions and 34 deletions

View File

@@ -862,8 +862,8 @@ static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata,
return 0;
}
static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
struct cfg80211_beacon_data *params)
int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
struct cfg80211_beacon_data *params)
{
struct beacon_data *new, *old;
int new_head_len, new_tail_len;
@@ -1026,6 +1026,12 @@ static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev,
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
/* don't allow changing the beacon while CSA is in place - offset
* of channel switch counter may change
*/
if (sdata->vif.csa_active)
return -EBUSY;
old = rtnl_dereference(sdata->u.ap.beacon);
if (!old)
return -ENOENT;
@@ -1050,6 +1056,10 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
return -ENOENT;
old_probe_resp = rtnl_dereference(sdata->u.ap.probe_resp);
/* abort any running channel switch */
sdata->vif.csa_active = false;
cancel_work_sync(&sdata->csa_finalize_work);
/* turn off carrier for this interface and dependent VLANs */
list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
netif_carrier_off(vlan->dev);
@@ -2777,6 +2787,178 @@ static int ieee80211_start_radar_detection(struct wiphy *wiphy,
return 0;
}
static struct cfg80211_beacon_data *
cfg80211_beacon_dup(struct cfg80211_beacon_data *beacon)
{
struct cfg80211_beacon_data *new_beacon;
u8 *pos;
int len;
len = beacon->head_len + beacon->tail_len + beacon->beacon_ies_len +
beacon->proberesp_ies_len + beacon->assocresp_ies_len +
beacon->probe_resp_len;
new_beacon = kzalloc(sizeof(*new_beacon) + len, GFP_KERNEL);
if (!new_beacon)
return NULL;
pos = (u8 *)(new_beacon + 1);
if (beacon->head_len) {
new_beacon->head_len = beacon->head_len;
new_beacon->head = pos;
memcpy(pos, beacon->head, beacon->head_len);
pos += beacon->head_len;
}
if (beacon->tail_len) {
new_beacon->tail_len = beacon->tail_len;
new_beacon->tail = pos;
memcpy(pos, beacon->tail, beacon->tail_len);
pos += beacon->tail_len;
}
if (beacon->beacon_ies_len) {
new_beacon->beacon_ies_len = beacon->beacon_ies_len;
new_beacon->beacon_ies = pos;
memcpy(pos, beacon->beacon_ies, beacon->beacon_ies_len);
pos += beacon->beacon_ies_len;
}
if (beacon->proberesp_ies_len) {
new_beacon->proberesp_ies_len = beacon->proberesp_ies_len;
new_beacon->proberesp_ies = pos;
memcpy(pos, beacon->proberesp_ies, beacon->proberesp_ies_len);
pos += beacon->proberesp_ies_len;
}
if (beacon->assocresp_ies_len) {
new_beacon->assocresp_ies_len = beacon->assocresp_ies_len;
new_beacon->assocresp_ies = pos;
memcpy(pos, beacon->assocresp_ies, beacon->assocresp_ies_len);
pos += beacon->assocresp_ies_len;
}
if (beacon->probe_resp_len) {
new_beacon->probe_resp_len = beacon->probe_resp_len;
beacon->probe_resp = pos;
memcpy(pos, beacon->probe_resp, beacon->probe_resp_len);
pos += beacon->probe_resp_len;
}
return new_beacon;
}
void ieee80211_csa_finalize_work(struct work_struct *work)
{
struct ieee80211_sub_if_data *sdata =
container_of(work, struct ieee80211_sub_if_data,
csa_finalize_work);
struct ieee80211_local *local = sdata->local;
int err, changed;
if (!ieee80211_sdata_running(sdata))
return;
if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP))
return;
sdata->radar_required = sdata->csa_radar_required;
err = ieee80211_vif_change_channel(sdata, &local->csa_chandef,
&changed);
if (WARN_ON(err < 0))
return;
err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon);
if (err < 0)
return;
changed |= err;
kfree(sdata->u.ap.next_beacon);
sdata->u.ap.next_beacon = NULL;
sdata->vif.csa_active = false;
ieee80211_wake_queues_by_reason(&sdata->local->hw,
IEEE80211_MAX_QUEUE_MAP,
IEEE80211_QUEUE_STOP_REASON_CSA);
ieee80211_bss_info_change_notify(sdata, changed);
cfg80211_ch_switch_notify(sdata->dev, &local->csa_chandef);
}
static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_csa_settings *params)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = sdata->local;
struct ieee80211_chanctx_conf *chanctx_conf;
struct ieee80211_chanctx *chanctx;
int err, num_chanctx;
if (!list_empty(&local->roc_list) || local->scanning)
return -EBUSY;
if (sdata->wdev.cac_started)
return -EBUSY;
if (cfg80211_chandef_identical(&params->chandef,
&sdata->vif.bss_conf.chandef))
return -EINVAL;
rcu_read_lock();
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
if (!chanctx_conf) {
rcu_read_unlock();
return -EBUSY;
}
/* don't handle for multi-VIF cases */
chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
if (chanctx->refcount > 1) {
rcu_read_unlock();
return -EBUSY;
}
num_chanctx = 0;
list_for_each_entry_rcu(chanctx, &local->chanctx_list, list)
num_chanctx++;
rcu_read_unlock();
if (num_chanctx > 1)
return -EBUSY;
/* don't allow another channel switch if one is already active. */
if (sdata->vif.csa_active)
return -EBUSY;
/* only handle AP for now. */
switch (sdata->vif.type) {
case NL80211_IFTYPE_AP:
break;
default:
return -EOPNOTSUPP;
}
sdata->u.ap.next_beacon = cfg80211_beacon_dup(&params->beacon_after);
if (!sdata->u.ap.next_beacon)
return -ENOMEM;
sdata->csa_counter_offset_beacon = params->counter_offset_beacon;
sdata->csa_counter_offset_presp = params->counter_offset_presp;
sdata->csa_radar_required = params->radar_required;
if (params->block_tx)
ieee80211_stop_queues_by_reason(&local->hw,
IEEE80211_MAX_QUEUE_MAP,
IEEE80211_QUEUE_STOP_REASON_CSA);
err = ieee80211_assign_beacon(sdata, &params->beacon_csa);
if (err < 0)
return err;
local->csa_chandef = params->chandef;
sdata->vif.csa_active = true;
ieee80211_bss_info_change_notify(sdata, err);
drv_channel_switch_beacon(sdata, &params->chandef);
return 0;
}
static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
struct ieee80211_channel *chan, bool offchan,
unsigned int wait, const u8 *buf, size_t len,
@@ -3494,4 +3676,5 @@ struct cfg80211_ops mac80211_config_ops = {
.get_et_strings = ieee80211_get_et_strings,
.get_channel = ieee80211_cfg_get_channel,
.start_radar_detection = ieee80211_start_radar_detection,
.channel_switch = ieee80211_channel_switch,
};