* mac80211 configuration hooks for cfg80211
*
* Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2013-2014 Intel Mobile Communications GmbH
*
* This file is GPLv2 as found in COPYING.
*/
if (old)
return -EALREADY;
- /* TODO: make hostapd tell us what it wants */
- sdata->smps_mode = IEEE80211_SMPS_OFF;
+ switch (params->smps_mode) {
+ case NL80211_SMPS_OFF:
+ sdata->smps_mode = IEEE80211_SMPS_OFF;
+ break;
+ case NL80211_SMPS_STATIC:
+ sdata->smps_mode = IEEE80211_SMPS_STATIC;
+ break;
+ case NL80211_SMPS_DYNAMIC:
+ sdata->smps_mode = IEEE80211_SMPS_DYNAMIC;
+ break;
+ default:
+ return -EINVAL;
+ }
sdata->needed_rx_chains = sdata->local->rx_chains;
mutex_lock(&local->mtx);
clear_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE);
}
- if (mask & BIT(NL80211_STA_FLAG_WME)) {
- if (set & BIT(NL80211_STA_FLAG_WME)) {
- set_sta_flag(sta, WLAN_STA_WME);
- sta->sta.wme = true;
- } else {
- clear_sta_flag(sta, WLAN_STA_WME);
- sta->sta.wme = false;
- }
- }
+ if (mask & BIT(NL80211_STA_FLAG_WME))
+ sta->sta.wme = set & BIT(NL80211_STA_FLAG_WME);
if (mask & BIT(NL80211_STA_FLAG_MFP)) {
if (set & BIT(NL80211_STA_FLAG_MFP))
}
static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev,
- const u8 *mac)
+ struct station_del_parameters *params)
{
struct ieee80211_sub_if_data *sdata;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- if (mac)
- return sta_info_destroy_addr_bss(sdata, mac);
+ if (params->mac)
+ return sta_info_destroy_addr_bss(sdata, params->mac);
sta_info_flush(sdata);
return 0;
return 0;
}
+static void mpp_set_pinfo(struct mesh_path *mpath, u8 *mpp,
+ struct mpath_info *pinfo)
+{
+ memset(pinfo, 0, sizeof(*pinfo));
+ memcpy(mpp, mpath->mpp, ETH_ALEN);
+
+ pinfo->generation = mpp_paths_generation;
+}
+
+static int ieee80211_get_mpp(struct wiphy *wiphy, struct net_device *dev,
+ u8 *dst, u8 *mpp, struct mpath_info *pinfo)
+
+{
+ struct ieee80211_sub_if_data *sdata;
+ struct mesh_path *mpath;
+
+ sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+ rcu_read_lock();
+ mpath = mpp_path_lookup(sdata, dst);
+ if (!mpath) {
+ rcu_read_unlock();
+ return -ENOENT;
+ }
+ memcpy(dst, mpath->dst, ETH_ALEN);
+ mpp_set_pinfo(mpath, mpp, pinfo);
+ rcu_read_unlock();
+ return 0;
+}
+
+static int ieee80211_dump_mpp(struct wiphy *wiphy, struct net_device *dev,
+ int idx, u8 *dst, u8 *mpp,
+ struct mpath_info *pinfo)
+{
+ struct ieee80211_sub_if_data *sdata;
+ struct mesh_path *mpath;
+
+ sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+ rcu_read_lock();
+ mpath = mpp_path_lookup_by_idx(sdata, idx);
+ if (!mpath) {
+ rcu_read_unlock();
+ return -ENOENT;
+ }
+ memcpy(dst, mpath->dst, ETH_ALEN);
+ mpp_set_pinfo(mpath, mpp, pinfo);
+ rcu_read_unlock();
+ return 0;
+}
+
static int ieee80211_get_mesh_config(struct wiphy *wiphy,
struct net_device *dev,
struct mesh_config *conf)
return err;
}
- if (changed & WIPHY_PARAM_COVERAGE_CLASS) {
- err = drv_set_coverage_class(local, wiphy->coverage_class);
+ if ((changed & WIPHY_PARAM_COVERAGE_CLASS) ||
+ (changed & WIPHY_PARAM_DYN_ACK)) {
+ s16 coverage_class;
+
+ coverage_class = changed & WIPHY_PARAM_COVERAGE_CLASS ?
+ wiphy->coverage_class : -1;
+ err = drv_set_coverage_class(local, coverage_class);
if (err)
return err;
return 0;
}
+static bool ieee80211_coalesce_started_roc(struct ieee80211_local *local,
+ struct ieee80211_roc_work *new_roc,
+ struct ieee80211_roc_work *cur_roc)
+{
+ unsigned long j = jiffies;
+ unsigned long cur_roc_end = cur_roc->hw_start_time +
+ msecs_to_jiffies(cur_roc->duration);
+ struct ieee80211_roc_work *next_roc;
+ int new_dur;
+
+ if (WARN_ON(!cur_roc->started || !cur_roc->hw_begun))
+ return false;
+
+ if (time_after(j + IEEE80211_ROC_MIN_LEFT, cur_roc_end))
+ return false;
+
+ ieee80211_handle_roc_started(new_roc);
+
+ new_dur = new_roc->duration - jiffies_to_msecs(cur_roc_end - j);
+
+ /* cur_roc is long enough - add new_roc to the dependents list. */
+ if (new_dur <= 0) {
+ list_add_tail(&new_roc->list, &cur_roc->dependents);
+ return true;
+ }
+
+ new_roc->duration = new_dur;
+
+ /*
+ * if cur_roc was already coalesced before, we might
+ * want to extend the next roc instead of adding
+ * a new one.
+ */
+ next_roc = list_entry(cur_roc->list.next,
+ struct ieee80211_roc_work, list);
+ if (&next_roc->list != &local->roc_list &&
+ next_roc->chan == new_roc->chan &&
+ next_roc->sdata == new_roc->sdata &&
+ !WARN_ON(next_roc->started)) {
+ list_add_tail(&new_roc->list, &next_roc->dependents);
+ next_roc->duration = max(next_roc->duration,
+ new_roc->duration);
+ next_roc->type = max(next_roc->type, new_roc->type);
+ return true;
+ }
+
+ /* add right after cur_roc */
+ list_add(&new_roc->list, &cur_roc->list);
+
+ return true;
+}
+
static int ieee80211_start_roc_work(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
struct ieee80211_channel *channel,
/* If it has already started, it's more difficult ... */
if (local->ops->remain_on_channel) {
- unsigned long j = jiffies;
-
/*
* In the offloaded ROC case, if it hasn't begun, add
* this new one to the dependent list to be handled
break;
}
- if (time_before(j + IEEE80211_ROC_MIN_LEFT,
- tmp->hw_start_time +
- msecs_to_jiffies(tmp->duration))) {
- int new_dur;
-
- ieee80211_handle_roc_started(roc);
-
- new_dur = roc->duration -
- jiffies_to_msecs(tmp->hw_start_time +
- msecs_to_jiffies(
- tmp->duration) -
- j);
-
- if (new_dur > 0) {
- /* add right after tmp */
- list_add(&roc->list, &tmp->list);
- } else {
- list_add_tail(&roc->list,
- &tmp->dependents);
- }
+ if (ieee80211_coalesce_started_roc(local, roc, tmp))
queued = true;
- }
} else if (del_timer_sync(&tmp->work.timer)) {
unsigned long new_end;
if (sdata->reserved_ready)
return 0;
- err = ieee80211_vif_use_reserved_context(sdata);
- if (err)
- return err;
-
- return 0;
+ return ieee80211_vif_use_reserved_context(sdata);
}
if (!cfg80211_chandef_identical(&sdata->vif.bss_conf.chandef,
return err;
ieee80211_bss_info_change_notify(sdata, changed);
- cfg80211_ch_switch_notify(sdata->dev, &sdata->csa_chandef);
if (sdata->csa_block_tx) {
ieee80211_wake_vif_queues(local, sdata,
sdata->csa_block_tx = false;
}
+ err = drv_post_channel_switch(sdata);
+ if (err)
+ return err;
+
+ cfg80211_ch_switch_notify(sdata->dev, &sdata->csa_chandef);
+
return 0;
}
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = sdata->local;
+ struct ieee80211_channel_switch ch_switch;
struct ieee80211_chanctx_conf *conf;
struct ieee80211_chanctx *chanctx;
int err, changed = 0;
goto out;
}
+ err = drv_pre_channel_switch(sdata, &ch_switch);
+ if (err)
+ goto out;
+
err = ieee80211_vif_reserve_chanctx(sdata, ¶ms->chandef,
chanctx->mode,
params->radar_required);
goto out;
}
+ ch_switch.timestamp = 0;
+ ch_switch.device_timestamp = 0;
+ ch_switch.block_tx = params->block_tx;
+ ch_switch.chandef = params->chandef;
+ ch_switch.count = params->count;
+
err = ieee80211_set_csa_beacon(sdata, params, &changed);
if (err) {
ieee80211_vif_unreserve_chanctx(sdata);
band = chanctx_conf->def.chan->band;
sta = sta_info_get_bss(sdata, peer);
if (sta) {
- qos = test_sta_flag(sta, WLAN_STA_WME);
+ qos = sta->sta.wme;
} else {
rcu_read_unlock();
return -ENOLINK;
.change_mpath = ieee80211_change_mpath,
.get_mpath = ieee80211_get_mpath,
.dump_mpath = ieee80211_dump_mpath,
+ .get_mpp = ieee80211_get_mpp,
+ .dump_mpp = ieee80211_dump_mpp,
.update_mesh_config = ieee80211_update_mesh_config,
.get_mesh_config = ieee80211_get_mesh_config,
.join_mesh = ieee80211_join_mesh,