Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next-2.6 into for-davem

Conflicts:
	drivers/net/wireless/iwlwifi/iwl-agn-tx.c
	net/mac80211/sta_info.h
This commit is contained in:
John W. Linville
2011-05-16 14:55:42 -04:00
213 changed files with 19165 additions and 2154 deletions

View File

@@ -370,7 +370,7 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
spin_lock_init(&rdev->bss_lock);
INIT_LIST_HEAD(&rdev->bss_list);
INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done);
INIT_WORK(&rdev->sched_scan_results_wk, __cfg80211_sched_scan_results);
#ifdef CONFIG_CFG80211_WEXT
rdev->wiphy.wext = &cfg80211_wext_handler;
#endif
@@ -416,6 +416,67 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
}
EXPORT_SYMBOL(wiphy_new);
static int wiphy_verify_combinations(struct wiphy *wiphy)
{
const struct ieee80211_iface_combination *c;
int i, j;
/* If we have combinations enforce them */
if (wiphy->n_iface_combinations)
wiphy->flags |= WIPHY_FLAG_ENFORCE_COMBINATIONS;
for (i = 0; i < wiphy->n_iface_combinations; i++) {
u32 cnt = 0;
u16 all_iftypes = 0;
c = &wiphy->iface_combinations[i];
/* Combinations with just one interface aren't real */
if (WARN_ON(c->max_interfaces < 2))
return -EINVAL;
/* Need at least one channel */
if (WARN_ON(!c->num_different_channels))
return -EINVAL;
if (WARN_ON(!c->n_limits))
return -EINVAL;
for (j = 0; j < c->n_limits; j++) {
u16 types = c->limits[j].types;
/*
* interface types shouldn't overlap, this is
* used in cfg80211_can_change_interface()
*/
if (WARN_ON(types & all_iftypes))
return -EINVAL;
all_iftypes |= types;
if (WARN_ON(!c->limits[j].max))
return -EINVAL;
/* Shouldn't list software iftypes in combinations! */
if (WARN_ON(wiphy->software_iftypes & types))
return -EINVAL;
cnt += c->limits[j].max;
/*
* Don't advertise an unsupported type
* in a combination.
*/
if (WARN_ON((wiphy->interface_modes & types) != types))
return -EINVAL;
}
/* You can't even choose that many! */
if (WARN_ON(cnt < c->max_interfaces))
return -EINVAL;
}
return 0;
}
int wiphy_register(struct wiphy *wiphy)
{
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
@@ -444,6 +505,10 @@ int wiphy_register(struct wiphy *wiphy)
if (WARN_ON(ifmodes != wiphy->interface_modes))
wiphy->interface_modes = ifmodes;
res = wiphy_verify_combinations(wiphy);
if (res)
return res;
/* sanity check supported bands/channels */
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
sband = wiphy->bands[band];
@@ -493,6 +558,13 @@ int wiphy_register(struct wiphy *wiphy)
return -EINVAL;
}
if (rdev->wiphy.wowlan.n_patterns) {
if (WARN_ON(!rdev->wiphy.wowlan.pattern_min_len ||
rdev->wiphy.wowlan.pattern_min_len >
rdev->wiphy.wowlan.pattern_max_len))
return -EINVAL;
}
/* check and set up bitrates */
ieee80211_set_bitrate_flags(wiphy);
@@ -631,6 +703,7 @@ void cfg80211_dev_free(struct cfg80211_registered_device *rdev)
mutex_destroy(&rdev->devlist_mtx);
list_for_each_entry_safe(scan, tmp, &rdev->bss_list, list)
cfg80211_put_bss(&scan->pub);
cfg80211_rdev_free_wowlan(rdev);
kfree(rdev);
}
@@ -664,6 +737,11 @@ static void wdev_cleanup_work(struct work_struct *work)
___cfg80211_scan_done(rdev, true);
}
if (WARN_ON(rdev->sched_scan_req &&
rdev->sched_scan_req->dev == wdev->netdev)) {
__cfg80211_stop_sched_scan(rdev, false);
}
cfg80211_unlock_rdev(rdev);
mutex_lock(&rdev->devlist_mtx);
@@ -685,6 +763,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb,
struct net_device *dev = ndev;
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct cfg80211_registered_device *rdev;
int ret;
if (!wdev)
return NOTIFY_DONE;
@@ -751,6 +830,10 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb,
break;
case NL80211_IFTYPE_P2P_CLIENT:
case NL80211_IFTYPE_STATION:
cfg80211_lock_rdev(rdev);
__cfg80211_stop_sched_scan(rdev, false);
cfg80211_unlock_rdev(rdev);
wdev_lock(wdev);
#ifdef CONFIG_CFG80211_WEXT
kfree(wdev->wext.ie);
@@ -769,6 +852,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb,
default:
break;
}
wdev->beacon_interval = 0;
break;
case NETDEV_DOWN:
dev_hold(dev);
@@ -875,6 +959,9 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb,
return notifier_from_errno(-EOPNOTSUPP);
if (rfkill_blocked(rdev->rfkill))
return notifier_from_errno(-ERFKILL);
ret = cfg80211_can_add_interface(rdev, wdev->iftype);
if (ret)
return notifier_from_errno(ret);
break;
}

View File

@@ -60,8 +60,10 @@ struct cfg80211_registered_device {
struct rb_root bss_tree;
u32 bss_generation;
struct cfg80211_scan_request *scan_req; /* protected by RTNL */
struct cfg80211_sched_scan_request *sched_scan_req;
unsigned long suspend_at;
struct work_struct scan_done_wk;
struct work_struct sched_scan_results_wk;
#ifdef CONFIG_NL80211_TESTMODE
struct genl_info *testmode_info;
@@ -70,6 +72,8 @@ struct cfg80211_registered_device {
struct work_struct conn_work;
struct work_struct event_work;
struct cfg80211_wowlan *wowlan;
/* must be last because of the way we do wiphy_priv(),
* and it should at least be aligned to NETDEV_ALIGN */
struct wiphy wiphy __attribute__((__aligned__(NETDEV_ALIGN)));
@@ -89,6 +93,18 @@ bool wiphy_idx_valid(int wiphy_idx)
return wiphy_idx >= 0;
}
static inline void
cfg80211_rdev_free_wowlan(struct cfg80211_registered_device *rdev)
{
int i;
if (!rdev->wowlan)
return;
for (i = 0; i < rdev->wowlan->n_patterns; i++)
kfree(rdev->wowlan->patterns[i].mask);
kfree(rdev->wowlan->patterns);
kfree(rdev->wowlan);
}
extern struct workqueue_struct *cfg80211_wq;
extern struct mutex cfg80211_mutex;
@@ -397,12 +413,26 @@ void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len);
void cfg80211_sme_disassoc(struct net_device *dev, int idx);
void __cfg80211_scan_done(struct work_struct *wk);
void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak);
void __cfg80211_sched_scan_results(struct work_struct *wk);
int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,
bool driver_initiated);
void cfg80211_upload_connect_keys(struct wireless_dev *wdev);
int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
struct net_device *dev, enum nl80211_iftype ntype,
u32 *flags, struct vif_params *params);
void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev);
int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
enum nl80211_iftype iftype);
static inline int
cfg80211_can_add_interface(struct cfg80211_registered_device *rdev,
enum nl80211_iftype iftype)
{
return cfg80211_can_change_interface(rdev, NULL, iftype);
}
struct ieee80211_channel *
rdev_freq_to_chan(struct cfg80211_registered_device *rdev,
int freq, enum nl80211_channel_type channel_type);
@@ -412,6 +442,9 @@ int cfg80211_set_freq(struct cfg80211_registered_device *rdev,
u16 cfg80211_calculate_bitrate(struct rate_info *rate);
int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev,
u32 beacon_int);
#ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS
#define CFG80211_DEV_WARN_ON(cond) WARN_ON(cond)
#else

View File

@@ -96,13 +96,12 @@ static int lib80211_wep_build_iv(struct sk_buff *skb, int hdr_len,
u8 *key, int keylen, void *priv)
{
struct lib80211_wep_data *wep = priv;
u32 klen, len;
u32 klen;
u8 *pos;
if (skb_headroom(skb) < 4 || skb->len < hdr_len)
return -1;
len = skb->len - hdr_len;
pos = skb_push(skb, 4);
memmove(pos, pos + 4, hdr_len);
pos += hdr_len;

View File

@@ -963,6 +963,16 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
if (memcmp(mgmt->bssid, dev->dev_addr, ETH_ALEN))
err = -EINVAL;
break;
case NL80211_IFTYPE_MESH_POINT:
if (memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN)) {
err = -EINVAL;
break;
}
/*
* check for mesh DA must be done by driver as
* cfg80211 doesn't track the stations
*/
break;
default:
err = -EOPNOTSUPP;
break;

View File

@@ -173,6 +173,9 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
[NL80211_ATTR_MCAST_RATE] = { .type = NLA_U32 },
[NL80211_ATTR_OFFCHANNEL_TX_OK] = { .type = NLA_FLAG },
[NL80211_ATTR_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED },
[NL80211_ATTR_WOWLAN_TRIGGERS] = { .type = NLA_NESTED },
[NL80211_ATTR_STA_PLINK_STATE] = { .type = NLA_U8 },
[NL80211_ATTR_SCHED_SCAN_INTERVAL] = { .type = NLA_U32 },
};
/* policy for the key attributes */
@@ -194,6 +197,15 @@ nl80211_key_default_policy[NUM_NL80211_KEY_DEFAULT_TYPES] = {
[NL80211_KEY_DEFAULT_TYPE_MULTICAST] = { .type = NLA_FLAG },
};
/* policy for WoWLAN attributes */
static const struct nla_policy
nl80211_wowlan_policy[NUM_NL80211_WOWLAN_TRIG] = {
[NL80211_WOWLAN_TRIG_ANY] = { .type = NLA_FLAG },
[NL80211_WOWLAN_TRIG_DISCONNECT] = { .type = NLA_FLAG },
[NL80211_WOWLAN_TRIG_MAGIC_PKT] = { .type = NLA_FLAG },
[NL80211_WOWLAN_TRIG_PKT_PATTERN] = { .type = NLA_NESTED },
};
/* ifidx get helper */
static int nl80211_get_ifidx(struct netlink_callback *cb)
{
@@ -534,6 +546,7 @@ static int nl80211_key_allowed(struct wireless_dev *wdev)
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_AP_VLAN:
case NL80211_IFTYPE_P2P_GO:
case NL80211_IFTYPE_MESH_POINT:
break;
case NL80211_IFTYPE_ADHOC:
if (!wdev->current_bss)
@@ -551,6 +564,88 @@ static int nl80211_key_allowed(struct wireless_dev *wdev)
return 0;
}
static int nl80211_put_iftypes(struct sk_buff *msg, u32 attr, u16 ifmodes)
{
struct nlattr *nl_modes = nla_nest_start(msg, attr);
int i;
if (!nl_modes)
goto nla_put_failure;
i = 0;
while (ifmodes) {
if (ifmodes & 1)
NLA_PUT_FLAG(msg, i);
ifmodes >>= 1;
i++;
}
nla_nest_end(msg, nl_modes);
return 0;
nla_put_failure:
return -ENOBUFS;
}
static int nl80211_put_iface_combinations(struct wiphy *wiphy,
struct sk_buff *msg)
{
struct nlattr *nl_combis;
int i, j;
nl_combis = nla_nest_start(msg,
NL80211_ATTR_INTERFACE_COMBINATIONS);
if (!nl_combis)
goto nla_put_failure;
for (i = 0; i < wiphy->n_iface_combinations; i++) {
const struct ieee80211_iface_combination *c;
struct nlattr *nl_combi, *nl_limits;
c = &wiphy->iface_combinations[i];
nl_combi = nla_nest_start(msg, i + 1);
if (!nl_combi)
goto nla_put_failure;
nl_limits = nla_nest_start(msg, NL80211_IFACE_COMB_LIMITS);
if (!nl_limits)
goto nla_put_failure;
for (j = 0; j < c->n_limits; j++) {
struct nlattr *nl_limit;
nl_limit = nla_nest_start(msg, j + 1);
if (!nl_limit)
goto nla_put_failure;
NLA_PUT_U32(msg, NL80211_IFACE_LIMIT_MAX,
c->limits[j].max);
if (nl80211_put_iftypes(msg, NL80211_IFACE_LIMIT_TYPES,
c->limits[j].types))
goto nla_put_failure;
nla_nest_end(msg, nl_limit);
}
nla_nest_end(msg, nl_limits);
if (c->beacon_int_infra_match)
NLA_PUT_FLAG(msg,
NL80211_IFACE_COMB_STA_AP_BI_MATCH);
NLA_PUT_U32(msg, NL80211_IFACE_COMB_NUM_CHANNELS,
c->num_different_channels);
NLA_PUT_U32(msg, NL80211_IFACE_COMB_MAXNUM,
c->max_interfaces);
nla_nest_end(msg, nl_combi);
}
nla_nest_end(msg, nl_combis);
return 0;
nla_put_failure:
return -ENOBUFS;
}
static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
struct cfg80211_registered_device *dev)
{
@@ -558,13 +653,11 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
struct nlattr *nl_bands, *nl_band;
struct nlattr *nl_freqs, *nl_freq;
struct nlattr *nl_rates, *nl_rate;
struct nlattr *nl_modes;
struct nlattr *nl_cmds;
enum ieee80211_band band;
struct ieee80211_channel *chan;
struct ieee80211_rate *rate;
int i;
u16 ifmodes = dev->wiphy.interface_modes;
const struct ieee80211_txrx_stypes *mgmt_stypes =
dev->wiphy.mgmt_stypes;
@@ -624,20 +717,10 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
}
}
nl_modes = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_IFTYPES);
if (!nl_modes)
if (nl80211_put_iftypes(msg, NL80211_ATTR_SUPPORTED_IFTYPES,
dev->wiphy.interface_modes))
goto nla_put_failure;
i = 0;
while (ifmodes) {
if (ifmodes & 1)
NLA_PUT_FLAG(msg, i);
ifmodes >>= 1;
i++;
}
nla_nest_end(msg, nl_modes);
nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS);
if (!nl_bands)
goto nla_put_failure;
@@ -749,6 +832,8 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
}
CMD(set_channel, SET_CHANNEL);
CMD(set_wds_peer, SET_WDS_PEER);
if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN)
CMD(sched_scan_start, START_SCHED_SCAN);
#undef CMD
@@ -821,6 +906,42 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
nla_nest_end(msg, nl_ifs);
}
if (dev->wiphy.wowlan.flags || dev->wiphy.wowlan.n_patterns) {
struct nlattr *nl_wowlan;
nl_wowlan = nla_nest_start(msg,
NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED);
if (!nl_wowlan)
goto nla_put_failure;
if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_ANY)
NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_ANY);
if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_DISCONNECT)
NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT);
if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_MAGIC_PKT)
NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT);
if (dev->wiphy.wowlan.n_patterns) {
struct nl80211_wowlan_pattern_support pat = {
.max_patterns = dev->wiphy.wowlan.n_patterns,
.min_pattern_len =
dev->wiphy.wowlan.pattern_min_len,
.max_pattern_len =
dev->wiphy.wowlan.pattern_max_len,
};
NLA_PUT(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN,
sizeof(pat), &pat);
}
nla_nest_end(msg, nl_wowlan);
}
if (nl80211_put_iftypes(msg, NL80211_ATTR_SOFTWARE_IFTYPES,
dev->wiphy.software_iftypes))
goto nla_put_failure;
if (nl80211_put_iface_combinations(&dev->wiphy, msg))
goto nla_put_failure;
return genlmsg_end(msg, hdr);
nla_put_failure:
@@ -1682,14 +1803,6 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
if (err)
goto out;
if (!(rdev->wiphy.flags &
WIPHY_FLAG_SUPPORTS_SEPARATE_DEFAULT_KEYS)) {
if (!key.def_uni || !key.def_multi) {
err = -EOPNOTSUPP;
goto out;
}
}
err = rdev->ops->set_default_key(&rdev->wiphy, dev, key.idx,
key.def_uni, key.def_multi);
@@ -1840,8 +1953,9 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info)
struct beacon_parameters *info);
struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct net_device *dev = info->user_ptr[1];
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct beacon_parameters params;
int haveinfo = 0;
int haveinfo = 0, err;
if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_BEACON_TAIL]))
return -EINVAL;
@@ -1850,6 +1964,8 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info)
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
return -EOPNOTSUPP;
memset(&params, 0, sizeof(params));
switch (info->genlhdr->cmd) {
case NL80211_CMD_NEW_BEACON:
/* these are required for NEW_BEACON */
@@ -1858,6 +1974,15 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info)
!info->attrs[NL80211_ATTR_BEACON_HEAD])
return -EINVAL;
params.interval =
nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
params.dtim_period =
nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]);
err = cfg80211_validate_beacon_int(rdev, params.interval);
if (err)
return err;
call = rdev->ops->add_beacon;
break;
case NL80211_CMD_SET_BEACON:
@@ -1871,20 +1996,6 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info)
if (!call)
return -EOPNOTSUPP;
memset(&params, 0, sizeof(params));
if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
params.interval =
nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
haveinfo = 1;
}
if (info->attrs[NL80211_ATTR_DTIM_PERIOD]) {
params.dtim_period =
nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]);
haveinfo = 1;
}
if (info->attrs[NL80211_ATTR_BEACON_HEAD]) {
params.head = nla_data(info->attrs[NL80211_ATTR_BEACON_HEAD]);
params.head_len =
@@ -1902,13 +2013,18 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info)
if (!haveinfo)
return -EINVAL;
return call(&rdev->wiphy, dev, &params);
err = call(&rdev->wiphy, dev, &params);
if (!err && params.interval)
wdev->beacon_interval = params.interval;
return err;
}
static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct net_device *dev = info->user_ptr[1];
struct wireless_dev *wdev = dev->ieee80211_ptr;
int err;
if (!rdev->ops->del_beacon)
return -EOPNOTSUPP;
@@ -1917,7 +2033,10 @@ static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info)
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
return -EOPNOTSUPP;
return rdev->ops->del_beacon(&rdev->wiphy, dev);
err = rdev->ops->del_beacon(&rdev->wiphy, dev);
if (!err)
wdev->beacon_interval = 0;
return err;
}
static const struct nla_policy sta_flags_policy[NL80211_STA_FLAG_MAX + 1] = {
@@ -2216,6 +2335,7 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
memset(&params, 0, sizeof(params));
params.listen_interval = -1;
params.plink_state = -1;
if (info->attrs[NL80211_ATTR_STA_AID])
return -EINVAL;
@@ -2247,6 +2367,10 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
params.plink_action =
nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
if (info->attrs[NL80211_ATTR_STA_PLINK_STATE])
params.plink_state =
nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_STATE]);
err = get_vlan(info, rdev, &params.vlan);
if (err)
goto out;
@@ -2286,10 +2410,9 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
err = -EINVAL;
if (params.listen_interval >= 0)
err = -EINVAL;
if (params.supported_rates)
err = -EINVAL;
if (params.sta_flags_mask &
~(BIT(NL80211_STA_FLAG_AUTHENTICATED) |
BIT(NL80211_STA_FLAG_MFP) |
BIT(NL80211_STA_FLAG_AUTHORIZED)))
err = -EINVAL;
break;
@@ -2840,6 +2963,7 @@ static const struct nla_policy
[NL80211_MESH_SETUP_USERSPACE_AUTH] = { .type = NLA_FLAG },
[NL80211_MESH_SETUP_IE] = { .type = NLA_BINARY,
.len = IEEE80211_MAX_DATA_LEN },
[NL80211_MESH_SETUP_USERSPACE_AMPE] = { .type = NLA_FLAG },
};
static int nl80211_parse_mesh_config(struct genl_info *info,
@@ -2949,7 +3073,8 @@ static int nl80211_parse_mesh_setup(struct genl_info *info,
setup->ie = nla_data(ieattr);
setup->ie_len = nla_len(ieattr);
}
setup->is_secure = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AUTH]);
setup->is_authenticated = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AUTH]);
setup->is_secure = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AMPE]);
return 0;
}
@@ -3318,6 +3443,188 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
return err;
}
static int nl80211_start_sched_scan(struct sk_buff *skb,
struct genl_info *info)
{
struct cfg80211_sched_scan_request *request;
struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct net_device *dev = info->user_ptr[1];
struct cfg80211_ssid *ssid;
struct ieee80211_channel *channel;
struct nlattr *attr;
struct wiphy *wiphy;
int err, tmp, n_ssids = 0, n_channels, i;
u32 interval;
enum ieee80211_band band;
size_t ie_len;
if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
!rdev->ops->sched_scan_start)
return -EOPNOTSUPP;
if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
return -EINVAL;
if (rdev->sched_scan_req)
return -EINPROGRESS;
if (!info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL])
return -EINVAL;
interval = nla_get_u32(info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]);
if (interval == 0)
return -EINVAL;
wiphy = &rdev->wiphy;
if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
n_channels = validate_scan_freqs(
info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
if (!n_channels)
return -EINVAL;
} else {
n_channels = 0;
for (band = 0; band < IEEE80211_NUM_BANDS; band++)
if (wiphy->bands[band])
n_channels += wiphy->bands[band]->n_channels;
}
if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS],
tmp)
n_ssids++;
if (n_ssids > wiphy->max_scan_ssids)
return -EINVAL;
if (info->attrs[NL80211_ATTR_IE])
ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
else
ie_len = 0;
if (ie_len > wiphy->max_scan_ie_len)
return -EINVAL;
request = kzalloc(sizeof(*request)
+ sizeof(*ssid) * n_ssids
+ sizeof(channel) * n_channels
+ ie_len, GFP_KERNEL);
if (!request)
return -ENOMEM;
if (n_ssids)
request->ssids = (void *)&request->channels[n_channels];
request->n_ssids = n_ssids;
if (ie_len) {
if (request->ssids)
request->ie = (void *)(request->ssids + n_ssids);
else
request->ie = (void *)(request->channels + n_channels);
}
i = 0;
if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
/* user specified, bail out if channel not found */
nla_for_each_nested(attr,
info->attrs[NL80211_ATTR_SCAN_FREQUENCIES],
tmp) {
struct ieee80211_channel *chan;
chan = ieee80211_get_channel(wiphy, nla_get_u32(attr));
if (!chan) {
err = -EINVAL;
goto out_free;
}
/* ignore disabled channels */
if (chan->flags & IEEE80211_CHAN_DISABLED)
continue;
request->channels[i] = chan;
i++;
}
} else {
/* all channels */
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
int j;
if (!wiphy->bands[band])
continue;
for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
struct ieee80211_channel *chan;
chan = &wiphy->bands[band]->channels[j];
if (chan->flags & IEEE80211_CHAN_DISABLED)
continue;
request->channels[i] = chan;
i++;
}
}
}
if (!i) {
err = -EINVAL;
goto out_free;
}
request->n_channels = i;
i = 0;
if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS],
tmp) {
if (request->ssids[i].ssid_len >
IEEE80211_MAX_SSID_LEN) {
err = -EINVAL;
goto out_free;
}
memcpy(request->ssids[i].ssid, nla_data(attr),
nla_len(attr));
request->ssids[i].ssid_len = nla_len(attr);
i++;
}
}
if (info->attrs[NL80211_ATTR_IE]) {
request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
memcpy((void *)request->ie,
nla_data(info->attrs[NL80211_ATTR_IE]),
request->ie_len);
}
request->dev = dev;
request->wiphy = &rdev->wiphy;
request->interval = interval;
err = rdev->ops->sched_scan_start(&rdev->wiphy, dev, request);
if (!err) {
rdev->sched_scan_req = request;
nl80211_send_sched_scan(rdev, dev,
NL80211_CMD_START_SCHED_SCAN);
goto out;
}
out_free:
kfree(request);
out:
return err;
}
static int nl80211_stop_sched_scan(struct sk_buff *skb,
struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
!rdev->ops->sched_scan_stop)
return -EOPNOTSUPP;
return __cfg80211_stop_sched_scan(rdev, false);
}
static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags,
struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
@@ -4816,6 +5123,194 @@ static int nl80211_leave_mesh(struct sk_buff *skb, struct genl_info *info)
return cfg80211_leave_mesh(rdev, dev);
}
static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct sk_buff *msg;
void *hdr;
if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns)
return -EOPNOTSUPP;
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!msg)
return -ENOMEM;
hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
NL80211_CMD_GET_WOWLAN);
if (!hdr)
goto nla_put_failure;
if (rdev->wowlan) {
struct nlattr *nl_wowlan;
nl_wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS);
if (!nl_wowlan)
goto nla_put_failure;
if (rdev->wowlan->any)
NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_ANY);
if (rdev->wowlan->disconnect)
NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT);
if (rdev->wowlan->magic_pkt)
NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT);
if (rdev->wowlan->n_patterns) {
struct nlattr *nl_pats, *nl_pat;
int i, pat_len;
nl_pats = nla_nest_start(msg,
NL80211_WOWLAN_TRIG_PKT_PATTERN);
if (!nl_pats)
goto nla_put_failure;
for (i = 0; i < rdev->wowlan->n_patterns; i++) {
nl_pat = nla_nest_start(msg, i + 1);
if (!nl_pat)
goto nla_put_failure;
pat_len = rdev->wowlan->patterns[i].pattern_len;
NLA_PUT(msg, NL80211_WOWLAN_PKTPAT_MASK,
DIV_ROUND_UP(pat_len, 8),
rdev->wowlan->patterns[i].mask);
NLA_PUT(msg, NL80211_WOWLAN_PKTPAT_PATTERN,
pat_len,
rdev->wowlan->patterns[i].pattern);
nla_nest_end(msg, nl_pat);
}
nla_nest_end(msg, nl_pats);
}
nla_nest_end(msg, nl_wowlan);
}
genlmsg_end(msg, hdr);
return genlmsg_reply(msg, info);
nla_put_failure:
nlmsg_free(msg);
return -ENOBUFS;
}
static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct nlattr *tb[NUM_NL80211_WOWLAN_TRIG];
struct cfg80211_wowlan no_triggers = {};
struct cfg80211_wowlan new_triggers = {};
struct wiphy_wowlan_support *wowlan = &rdev->wiphy.wowlan;
int err, i;
if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns)
return -EOPNOTSUPP;
if (!info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS])
goto no_triggers;
err = nla_parse(tb, MAX_NL80211_WOWLAN_TRIG,
nla_data(info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]),
nla_len(info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]),
nl80211_wowlan_policy);
if (err)
return err;
if (tb[NL80211_WOWLAN_TRIG_ANY]) {
if (!(wowlan->flags & WIPHY_WOWLAN_ANY))
return -EINVAL;
new_triggers.any = true;
}
if (tb[NL80211_WOWLAN_TRIG_DISCONNECT]) {
if (!(wowlan->flags & WIPHY_WOWLAN_DISCONNECT))
return -EINVAL;
new_triggers.disconnect = true;
}
if (tb[NL80211_WOWLAN_TRIG_MAGIC_PKT]) {
if (!(wowlan->flags & WIPHY_WOWLAN_MAGIC_PKT))
return -EINVAL;
new_triggers.magic_pkt = true;
}
if (tb[NL80211_WOWLAN_TRIG_PKT_PATTERN]) {
struct nlattr *pat;
int n_patterns = 0;
int rem, pat_len, mask_len;
struct nlattr *pat_tb[NUM_NL80211_WOWLAN_PKTPAT];
nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
rem)
n_patterns++;
if (n_patterns > wowlan->n_patterns)
return -EINVAL;
new_triggers.patterns = kcalloc(n_patterns,
sizeof(new_triggers.patterns[0]),
GFP_KERNEL);
if (!new_triggers.patterns)
return -ENOMEM;
new_triggers.n_patterns = n_patterns;
i = 0;
nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
rem) {
nla_parse(pat_tb, MAX_NL80211_WOWLAN_PKTPAT,
nla_data(pat), nla_len(pat), NULL);
err = -EINVAL;
if (!pat_tb[NL80211_WOWLAN_PKTPAT_MASK] ||
!pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN])
goto error;
pat_len = nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]);
mask_len = DIV_ROUND_UP(pat_len, 8);
if (nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]) !=
mask_len)
goto error;
if (pat_len > wowlan->pattern_max_len ||
pat_len < wowlan->pattern_min_len)
goto error;
new_triggers.patterns[i].mask =
kmalloc(mask_len + pat_len, GFP_KERNEL);
if (!new_triggers.patterns[i].mask) {
err = -ENOMEM;
goto error;
}
new_triggers.patterns[i].pattern =
new_triggers.patterns[i].mask + mask_len;
memcpy(new_triggers.patterns[i].mask,
nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]),
mask_len);
new_triggers.patterns[i].pattern_len = pat_len;
memcpy(new_triggers.patterns[i].pattern,
nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]),
pat_len);
i++;
}
}
if (memcmp(&new_triggers, &no_triggers, sizeof(new_triggers))) {
struct cfg80211_wowlan *ntrig;
ntrig = kmemdup(&new_triggers, sizeof(new_triggers),
GFP_KERNEL);
if (!ntrig) {
err = -ENOMEM;
goto error;
}
cfg80211_rdev_free_wowlan(rdev);
rdev->wowlan = ntrig;
} else {
no_triggers:
cfg80211_rdev_free_wowlan(rdev);
rdev->wowlan = NULL;
}
return 0;
error:
for (i = 0; i < new_triggers.n_patterns; i++)
kfree(new_triggers.patterns[i].mask);
kfree(new_triggers.patterns);
return err;
}
#define NL80211_FLAG_NEED_WIPHY 0x01
#define NL80211_FLAG_NEED_NETDEV 0x02
#define NL80211_FLAG_NEED_RTNL 0x04
@@ -5099,6 +5594,22 @@ static struct genl_ops nl80211_ops[] = {
.policy = nl80211_policy,
.dumpit = nl80211_dump_scan,
},
{
.cmd = NL80211_CMD_START_SCHED_SCAN,
.doit = nl80211_start_sched_scan,
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
NL80211_FLAG_NEED_RTNL,
},
{
.cmd = NL80211_CMD_STOP_SCHED_SCAN,
.doit = nl80211_stop_sched_scan,
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
NL80211_FLAG_NEED_RTNL,
},
{
.cmd = NL80211_CMD_AUTHENTICATE,
.doit = nl80211_authenticate,
@@ -5314,6 +5825,22 @@ static struct genl_ops nl80211_ops[] = {
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
NL80211_FLAG_NEED_RTNL,
},
{
.cmd = NL80211_CMD_GET_WOWLAN,
.doit = nl80211_get_wowlan,
.policy = nl80211_policy,
/* can be retrieved by unprivileged users */
.internal_flags = NL80211_FLAG_NEED_WIPHY |
NL80211_FLAG_NEED_RTNL,
},
{
.cmd = NL80211_CMD_SET_WOWLAN,
.doit = nl80211_set_wowlan,
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
.internal_flags = NL80211_FLAG_NEED_WIPHY |
NL80211_FLAG_NEED_RTNL,
},
};
static struct genl_multicast_group nl80211_mlme_mcgrp = {
@@ -5409,6 +5936,28 @@ static int nl80211_send_scan_msg(struct sk_buff *msg,
return -EMSGSIZE;
}
static int
nl80211_send_sched_scan_msg(struct sk_buff *msg,
struct cfg80211_registered_device *rdev,
struct net_device *netdev,
u32 pid, u32 seq, int flags, u32 cmd)
{
void *hdr;
hdr = nl80211hdr_put(msg, pid, seq, flags, cmd);
if (!hdr)
return -1;
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
return genlmsg_end(msg, hdr);
nla_put_failure:
genlmsg_cancel(msg, hdr);
return -EMSGSIZE;
}
void nl80211_send_scan_start(struct cfg80211_registered_device *rdev,
struct net_device *netdev)
{
@@ -5466,6 +6015,43 @@ void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
nl80211_scan_mcgrp.id, GFP_KERNEL);
}
void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev,
struct net_device *netdev)
{
struct sk_buff *msg;
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!msg)
return;
if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0,
NL80211_CMD_SCHED_SCAN_RESULTS) < 0) {
nlmsg_free(msg);
return;
}
genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
nl80211_scan_mcgrp.id, GFP_KERNEL);
}
void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev,
struct net_device *netdev, u32 cmd)
{
struct sk_buff *msg;
msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
if (!msg)
return;
if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0, cmd) < 0) {
nlmsg_free(msg);
return;
}
genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
nl80211_scan_mcgrp.id, GFP_KERNEL);
}
/*
* This can happen on global regulatory changes or device specific settings
* based on custom world regulatory domains.

View File

@@ -12,6 +12,10 @@ void nl80211_send_scan_done(struct cfg80211_registered_device *rdev,
struct net_device *netdev);
void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
struct net_device *netdev);
void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev,
struct net_device *netdev, u32 cmd);
void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev,
struct net_device *netdev);
void nl80211_send_reg_change_event(struct regulatory_request *request);
void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev,
struct net_device *netdev,

View File

@@ -672,11 +672,9 @@ static int freq_reg_info_regd(struct wiphy *wiphy,
for (i = 0; i < regd->n_reg_rules; i++) {
const struct ieee80211_reg_rule *rr;
const struct ieee80211_freq_range *fr = NULL;
const struct ieee80211_power_rule *pr = NULL;
rr = &regd->reg_rules[i];
fr = &rr->freq_range;
pr = &rr->power_rule;
/*
* We only need to know if one frequency rule was

View File

@@ -93,6 +93,69 @@ void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted)
}
EXPORT_SYMBOL(cfg80211_scan_done);
void __cfg80211_sched_scan_results(struct work_struct *wk)
{
struct cfg80211_registered_device *rdev;
rdev = container_of(wk, struct cfg80211_registered_device,
sched_scan_results_wk);
cfg80211_lock_rdev(rdev);
/* we don't have sched_scan_req anymore if the scan is stopping */
if (rdev->sched_scan_req)
nl80211_send_sched_scan_results(rdev,
rdev->sched_scan_req->dev);
cfg80211_unlock_rdev(rdev);
}
void cfg80211_sched_scan_results(struct wiphy *wiphy)
{
/* ignore if we're not scanning */
if (wiphy_to_dev(wiphy)->sched_scan_req)
queue_work(cfg80211_wq,
&wiphy_to_dev(wiphy)->sched_scan_results_wk);
}
EXPORT_SYMBOL(cfg80211_sched_scan_results);
void cfg80211_sched_scan_stopped(struct wiphy *wiphy)
{
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
cfg80211_lock_rdev(rdev);
__cfg80211_stop_sched_scan(rdev, true);
cfg80211_unlock_rdev(rdev);
}
EXPORT_SYMBOL(cfg80211_sched_scan_stopped);
int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,
bool driver_initiated)
{
int err;
struct net_device *dev;
ASSERT_RDEV_LOCK(rdev);
if (!rdev->sched_scan_req)
return 0;
dev = rdev->sched_scan_req->dev;
if (!driver_initiated) {
err = rdev->ops->sched_scan_stop(&rdev->wiphy, dev);
if (err)
return err;
}
nl80211_send_sched_scan(rdev, dev, NL80211_CMD_SCHED_SCAN_STOPPED);
kfree(rdev->sched_scan_req);
rdev->sched_scan_req = NULL;
return err;
}
static void bss_release(struct kref *ref)
{
struct cfg80211_internal_bss *bss;
@@ -210,7 +273,7 @@ static bool is_mesh(struct cfg80211_bss *a,
{
const u8 *ie;
if (!is_zero_ether_addr(a->bssid))
if (!WLAN_CAPABILITY_IS_MBSS(a->capability))
return false;
ie = cfg80211_find_ie(WLAN_EID_MESH_ID,
@@ -248,11 +311,7 @@ static int cmp_bss(struct cfg80211_bss *a,
if (a->channel != b->channel)
return b->channel->center_freq - a->channel->center_freq;
r = memcmp(a->bssid, b->bssid, ETH_ALEN);
if (r)
return r;
if (is_zero_ether_addr(a->bssid)) {
if (WLAN_CAPABILITY_IS_MBSS(a->capability | b->capability)) {
r = cmp_ies(WLAN_EID_MESH_ID,
a->information_elements,
a->len_information_elements,
@@ -267,6 +326,10 @@ static int cmp_bss(struct cfg80211_bss *a,
b->len_information_elements);
}
r = memcmp(a->bssid, b->bssid, ETH_ALEN);
if (r)
return r;
return cmp_ies(WLAN_EID_SSID,
a->information_elements,
a->len_information_elements,
@@ -407,7 +470,7 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
res->ts = jiffies;
if (is_zero_ether_addr(res->pub.bssid)) {
if (WLAN_CAPABILITY_IS_MBSS(res->pub.capability)) {
/* must be mesh, verify */
meshid = cfg80211_find_ie(WLAN_EID_MESH_ID,
res->pub.information_elements,

View File

@@ -93,7 +93,7 @@ static int wiphy_suspend(struct device *dev, pm_message_t state)
if (rdev->ops->suspend) {
rtnl_lock();
ret = rdev->ops->suspend(&rdev->wiphy);
ret = rdev->ops->suspend(&rdev->wiphy, rdev->wowlan);
rtnl_unlock();
}

View File

@@ -544,7 +544,8 @@ EXPORT_SYMBOL(ieee80211_data_from_8023);
void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list,
const u8 *addr, enum nl80211_iftype iftype,
const unsigned int extra_headroom)
const unsigned int extra_headroom,
bool has_80211_header)
{
struct sk_buff *frame = NULL;
u16 ethertype;
@@ -553,14 +554,18 @@ void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list,
int remaining, err;
u8 dst[ETH_ALEN], src[ETH_ALEN];
err = ieee80211_data_to_8023(skb, addr, iftype);
if (err)
goto out;
if (has_80211_header) {
err = ieee80211_data_to_8023(skb, addr, iftype);
if (err)
goto out;
/* skip the wrapping header */
eth = (struct ethhdr *) skb_pull(skb, sizeof(struct ethhdr));
if (!eth)
goto out;
/* skip the wrapping header */
eth = (struct ethhdr *) skb_pull(skb, sizeof(struct ethhdr));
if (!eth)
goto out;
} else {
eth = (struct ethhdr *) skb->data;
}
while (skb != frame) {
u8 padding;
@@ -803,6 +808,11 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
return -EBUSY;
if (ntype != otype) {
err = cfg80211_can_change_interface(rdev, dev->ieee80211_ptr,
ntype);
if (err)
return err;
dev->ieee80211_ptr->use_4addr = false;
dev->ieee80211_ptr->mesh_id_up_len = 0;
@@ -896,3 +906,103 @@ u16 cfg80211_calculate_bitrate(struct rate_info *rate)
/* do NOT round down here */
return (bitrate + 50000) / 100000;
}
int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev,
u32 beacon_int)
{
struct wireless_dev *wdev;
int res = 0;
if (!beacon_int)
return -EINVAL;
mutex_lock(&rdev->devlist_mtx);
list_for_each_entry(wdev, &rdev->netdev_list, list) {
if (!wdev->beacon_interval)
continue;
if (wdev->beacon_interval != beacon_int) {
res = -EINVAL;
break;
}
}
mutex_unlock(&rdev->devlist_mtx);
return res;
}
int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
enum nl80211_iftype iftype)
{
struct wireless_dev *wdev_iter;
int num[NUM_NL80211_IFTYPES];
int total = 1;
int i, j;
ASSERT_RTNL();
/* Always allow software iftypes */
if (rdev->wiphy.software_iftypes & BIT(iftype))
return 0;
/*
* Drivers will gradually all set this flag, until all
* have it we only enforce for those that set it.
*/
if (!(rdev->wiphy.flags & WIPHY_FLAG_ENFORCE_COMBINATIONS))
return 0;
memset(num, 0, sizeof(num));
num[iftype] = 1;
mutex_lock(&rdev->devlist_mtx);
list_for_each_entry(wdev_iter, &rdev->netdev_list, list) {
if (wdev_iter == wdev)
continue;
if (!netif_running(wdev_iter->netdev))
continue;
if (rdev->wiphy.software_iftypes & BIT(wdev_iter->iftype))
continue;
num[wdev_iter->iftype]++;
total++;
}
mutex_unlock(&rdev->devlist_mtx);
for (i = 0; i < rdev->wiphy.n_iface_combinations; i++) {
const struct ieee80211_iface_combination *c;
struct ieee80211_iface_limit *limits;
c = &rdev->wiphy.iface_combinations[i];
limits = kmemdup(c->limits, sizeof(limits[0]) * c->n_limits,
GFP_KERNEL);
if (!limits)
return -ENOMEM;
if (total > c->max_interfaces)
goto cont;
for (iftype = 0; iftype < NUM_NL80211_IFTYPES; iftype++) {
if (rdev->wiphy.software_iftypes & BIT(iftype))
continue;
for (j = 0; j < c->n_limits; j++) {
if (!(limits[j].types & iftype))
continue;
if (limits[j].max < num[iftype])
goto cont;
limits[j].max -= num[iftype];
}
}
/* yay, it fits */
kfree(limits);
return 0;
cont:
kfree(limits);
}
return -EBUSY;
}