Commit 73d54c9e authored by Luis R. Rodriguez's avatar Luis R. Rodriguez Committed by John W. Linville

cfg80211: add regulatory netlink multicast group

This allows us to send to userspace "regulatory" events.
For now we just send an event when we change regulatory domains.
We also notify userspace when devices are using their own custom
world roaming regulatory domains.
Signed-off-by: default avatarLuis R. Rodriguez <lrodriguez@atheros.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 7db90f4a
...@@ -150,6 +150,17 @@ ...@@ -150,6 +150,17 @@
* @NL80211_CMD_SCAN_ABORTED: scan was aborted, for unspecified reasons, * @NL80211_CMD_SCAN_ABORTED: scan was aborted, for unspecified reasons,
* partial scan results may be available * partial scan results may be available
* *
* @NL80211_CMD_REG_CHANGE: indicates to userspace the regulatory domain
* has been changed and provides details of the request information
* that caused the change such as who initiated the regulatory request
* (%NL80211_ATTR_REG_INITIATOR), the wiphy_idx
* (%NL80211_ATTR_REG_ALPHA2) on which the request was made from if
* the initiator was %NL80211_REGDOM_SET_BY_COUNTRY_IE or
* %NL80211_REGDOM_SET_BY_DRIVER, the type of regulatory domain
* set (%NL80211_ATTR_REG_TYPE), if the type of regulatory domain is
* %NL80211_REG_TYPE_COUNTRY the alpha2 to which we have moved on
* to (%NL80211_ATTR_REG_ALPHA2).
*
* @NL80211_CMD_MAX: highest used command number * @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use * @__NL80211_CMD_AFTER_LAST: internal use
*/ */
...@@ -204,6 +215,8 @@ enum nl80211_commands { ...@@ -204,6 +215,8 @@ enum nl80211_commands {
NL80211_CMD_NEW_SCAN_RESULTS, NL80211_CMD_NEW_SCAN_RESULTS,
NL80211_CMD_SCAN_ABORTED, NL80211_CMD_SCAN_ABORTED,
NL80211_CMD_REG_CHANGE,
/* add new commands above here */ /* add new commands above here */
/* used to define NL80211_CMD_MAX below */ /* used to define NL80211_CMD_MAX below */
...@@ -218,6 +231,8 @@ enum nl80211_commands { ...@@ -218,6 +231,8 @@ enum nl80211_commands {
#define NL80211_CMD_SET_BSS NL80211_CMD_SET_BSS #define NL80211_CMD_SET_BSS NL80211_CMD_SET_BSS
#define NL80211_CMD_SET_MGMT_EXTRA_IE NL80211_CMD_SET_MGMT_EXTRA_IE #define NL80211_CMD_SET_MGMT_EXTRA_IE NL80211_CMD_SET_MGMT_EXTRA_IE
#define NL80211_CMD_REG_CHANGE NL80211_CMD_REG_CHANGE
/** /**
* enum nl80211_attrs - nl80211 netlink attributes * enum nl80211_attrs - nl80211 netlink attributes
* *
...@@ -329,6 +344,11 @@ enum nl80211_commands { ...@@ -329,6 +344,11 @@ enum nl80211_commands {
* messages carried the same generation number) * messages carried the same generation number)
* @NL80211_ATTR_BSS: scan result BSS * @NL80211_ATTR_BSS: scan result BSS
* *
* @NL80211_ATTR_REG_INITIATOR: indicates who requested the regulatory domain
* currently in effect. This could be any of the %NL80211_REGDOM_SET_BY_*
* @NL80211_ATTR_REG_TYPE: indicates the type of the regulatory domain currently
* set. This can be one of the nl80211_reg_type (%NL80211_REGDOM_TYPE_*)
*
* @NL80211_ATTR_MAX: highest attribute number currently defined * @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use * @__NL80211_ATTR_AFTER_LAST: internal use
*/ */
...@@ -403,6 +423,9 @@ enum nl80211_attrs { ...@@ -403,6 +423,9 @@ enum nl80211_attrs {
NL80211_ATTR_SCAN_GENERATION, NL80211_ATTR_SCAN_GENERATION,
NL80211_ATTR_BSS, NL80211_ATTR_BSS,
NL80211_ATTR_REG_INITIATOR,
NL80211_ATTR_REG_TYPE,
/* add attributes here, update the policy in nl80211.c */ /* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST, __NL80211_ATTR_AFTER_LAST,
...@@ -420,6 +443,8 @@ enum nl80211_attrs { ...@@ -420,6 +443,8 @@ enum nl80211_attrs {
#define NL80211_ATTR_WIPHY_CHANNEL_TYPE NL80211_ATTR_WIPHY_CHANNEL_TYPE #define NL80211_ATTR_WIPHY_CHANNEL_TYPE NL80211_ATTR_WIPHY_CHANNEL_TYPE
#define NL80211_ATTR_MGMT_SUBTYPE NL80211_ATTR_MGMT_SUBTYPE #define NL80211_ATTR_MGMT_SUBTYPE NL80211_ATTR_MGMT_SUBTYPE
#define NL80211_ATTR_IE NL80211_ATTR_IE #define NL80211_ATTR_IE NL80211_ATTR_IE
#define NL80211_ATTR_REG_INITIATOR NL80211_ATTR_REG_INITIATOR
#define NL80211_ATTR_REG_TYPE NL80211_ATTR_REG_TYPE
#define NL80211_MAX_SUPP_RATES 32 #define NL80211_MAX_SUPP_RATES 32
#define NL80211_MAX_SUPP_REG_RULES 32 #define NL80211_MAX_SUPP_REG_RULES 32
...@@ -691,6 +716,29 @@ enum nl80211_reg_initiator { ...@@ -691,6 +716,29 @@ enum nl80211_reg_initiator {
NL80211_REGDOM_SET_BY_COUNTRY_IE, NL80211_REGDOM_SET_BY_COUNTRY_IE,
}; };
/**
* enum nl80211_reg_type - specifies the type of regulatory domain
* @NL80211_REGDOM_TYPE_COUNTRY: the regulatory domain set is one that pertains
* to a specific country. When this is set you can count on the
* ISO / IEC 3166 alpha2 country code being valid.
* @NL80211_REGDOM_TYPE_WORLD: the regulatory set domain is the world regulatory
* domain.
* @NL80211_REGDOM_TYPE_CUSTOM_WORLD: the regulatory domain set is a custom
* driver specific world regulatory domain. These do not apply system-wide
* and are only applicable to the individual devices which have requested
* them to be applied.
* @NL80211_REGDOM_TYPE_INTERSECTION: the regulatory domain set is the product
* of an intersection between two regulatory domains -- the previously
* set regulatory domain on the system and the last accepted regulatory
* domain request to be processed.
*/
enum nl80211_reg_type {
NL80211_REGDOM_TYPE_COUNTRY,
NL80211_REGDOM_TYPE_WORLD,
NL80211_REGDOM_TYPE_CUSTOM_WORLD,
NL80211_REGDOM_TYPE_INTERSECTION,
};
/** /**
* enum nl80211_reg_rule_attr - regulatory rule attributes * enum nl80211_reg_rule_attr - regulatory rule attributes
* @NL80211_ATTR_REG_RULE_FLAGS: a set of flags which specify additional * @NL80211_ATTR_REG_RULE_FLAGS: a set of flags which specify additional
......
...@@ -365,6 +365,17 @@ int wiphy_register(struct wiphy *wiphy) ...@@ -365,6 +365,17 @@ int wiphy_register(struct wiphy *wiphy)
if (IS_ERR(drv->wiphy.debugfsdir)) if (IS_ERR(drv->wiphy.debugfsdir))
drv->wiphy.debugfsdir = NULL; drv->wiphy.debugfsdir = NULL;
if (wiphy->custom_regulatory) {
struct regulatory_request request;
request.wiphy_idx = get_wiphy_idx(wiphy);
request.initiator = NL80211_REGDOM_SET_BY_DRIVER;
request.alpha2[0] = '9';
request.alpha2[1] = '9';
nl80211_send_reg_change_event(&request);
}
res = 0; res = 0;
out_unlock: out_unlock:
mutex_unlock(&cfg80211_mutex); mutex_unlock(&cfg80211_mutex);
......
...@@ -2739,6 +2739,9 @@ static struct genl_multicast_group nl80211_config_mcgrp = { ...@@ -2739,6 +2739,9 @@ static struct genl_multicast_group nl80211_config_mcgrp = {
static struct genl_multicast_group nl80211_scan_mcgrp = { static struct genl_multicast_group nl80211_scan_mcgrp = {
.name = "scan", .name = "scan",
}; };
static struct genl_multicast_group nl80211_regulatory_mcgrp = {
.name = "regulatory",
};
/* notification functions */ /* notification functions */
...@@ -2818,6 +2821,61 @@ void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev, ...@@ -2818,6 +2821,61 @@ void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
genlmsg_multicast(msg, 0, nl80211_scan_mcgrp.id, GFP_KERNEL); genlmsg_multicast(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.
*/
void nl80211_send_reg_change_event(struct regulatory_request *request)
{
struct sk_buff *msg;
void *hdr;
msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
if (!msg)
return;
hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_CHANGE);
if (!hdr) {
nlmsg_free(msg);
return;
}
/* Userspace can always count this one always being set */
NLA_PUT_U8(msg, NL80211_ATTR_REG_INITIATOR, request->initiator);
if (request->alpha2[0] == '0' && request->alpha2[1] == '0')
NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
NL80211_REGDOM_TYPE_WORLD);
else if (request->alpha2[0] == '9' && request->alpha2[1] == '9')
NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
NL80211_REGDOM_TYPE_CUSTOM_WORLD);
else if ((request->alpha2[0] == '9' && request->alpha2[1] == '8') ||
request->intersect)
NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
NL80211_REGDOM_TYPE_INTERSECTION);
else {
NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
NL80211_REGDOM_TYPE_COUNTRY);
NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, request->alpha2);
}
if (wiphy_idx_valid(request->wiphy_idx))
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx);
if (genlmsg_end(msg, hdr) < 0) {
nlmsg_free(msg);
return;
}
genlmsg_multicast(msg, 0, nl80211_regulatory_mcgrp.id, GFP_KERNEL);
return;
nla_put_failure:
genlmsg_cancel(msg, hdr);
nlmsg_free(msg);
}
/* initialisation/exit functions */ /* initialisation/exit functions */
int nl80211_init(void) int nl80211_init(void)
...@@ -2842,6 +2900,10 @@ int nl80211_init(void) ...@@ -2842,6 +2900,10 @@ int nl80211_init(void)
if (err) if (err)
goto err_out; goto err_out;
err = genl_register_mc_group(&nl80211_fam, &nl80211_regulatory_mcgrp);
if (err)
goto err_out;
return 0; return 0;
err_out: err_out:
genl_unregister_family(&nl80211_fam); genl_unregister_family(&nl80211_fam);
......
...@@ -11,6 +11,7 @@ extern void nl80211_send_scan_done(struct cfg80211_registered_device *rdev, ...@@ -11,6 +11,7 @@ extern void nl80211_send_scan_done(struct cfg80211_registered_device *rdev,
struct net_device *netdev); struct net_device *netdev);
extern void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev, extern void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
struct net_device *netdev); struct net_device *netdev);
extern void nl80211_send_reg_change_event(struct regulatory_request *request);
#else #else
static inline int nl80211_init(void) static inline int nl80211_init(void)
{ {
...@@ -31,6 +32,10 @@ static inline void nl80211_send_scan_aborted( ...@@ -31,6 +32,10 @@ static inline void nl80211_send_scan_aborted(
struct cfg80211_registered_device *rdev, struct cfg80211_registered_device *rdev,
struct net_device *netdev) struct net_device *netdev)
{} {}
static inline void
nl80211_send_reg_change_event(struct regulatory_request *request)
{
}
#endif /* CONFIG_NL80211 */ #endif /* CONFIG_NL80211 */
#endif /* __NET_WIRELESS_NL80211_H */ #endif /* __NET_WIRELESS_NL80211_H */
...@@ -41,6 +41,7 @@ ...@@ -41,6 +41,7 @@
#include <net/cfg80211.h> #include <net/cfg80211.h>
#include "core.h" #include "core.h"
#include "reg.h" #include "reg.h"
#include "nl80211.h"
/* Receipt of information from last regulatory request */ /* Receipt of information from last regulatory request */
static struct regulatory_request *last_request; static struct regulatory_request *last_request;
...@@ -1403,8 +1404,16 @@ static int __regulatory_hint(struct wiphy *wiphy, ...@@ -1403,8 +1404,16 @@ static int __regulatory_hint(struct wiphy *wiphy,
pending_request = NULL; pending_request = NULL;
/* When r == REG_INTERSECT we do need to call CRDA */ /* When r == REG_INTERSECT we do need to call CRDA */
if (r < 0) if (r < 0) {
/*
* Since CRDA will not be called in this case as we already
* have applied the requested regulatory domain before we just
* inform userspace we have processed the request
*/
if (r == -EALREADY)
nl80211_send_reg_change_event(last_request);
return r; return r;
}
/* /*
* Note: When CONFIG_WIRELESS_OLD_REGULATORY is enabled * Note: When CONFIG_WIRELESS_OLD_REGULATORY is enabled
...@@ -2084,6 +2093,8 @@ int set_regdom(const struct ieee80211_regdomain *rd) ...@@ -2084,6 +2093,8 @@ int set_regdom(const struct ieee80211_regdomain *rd)
print_regdomain(cfg80211_regdomain); print_regdomain(cfg80211_regdomain);
nl80211_send_reg_change_event(last_request);
return r; return r;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment