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:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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(¶ms, 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(¶ms, 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, ¶ms);
|
||||
err = call(&rdev->wiphy, dev, ¶ms);
|
||||
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(¶ms, 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, ¶ms.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.
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 = ®d->reg_rules[i];
|
||||
fr = &rr->freq_range;
|
||||
pr = &rr->power_rule;
|
||||
|
||||
/*
|
||||
* We only need to know if one frequency rule was
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user