diff options
Diffstat (limited to 'net/wireless')
-rw-r--r-- | net/wireless/nl80211.c | 11 | ||||
-rw-r--r-- | net/wireless/reg.c | 80 | ||||
-rw-r--r-- | net/wireless/reg.h | 6 |
3 files changed, 77 insertions, 20 deletions
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 5129342151e6..67a03f2885a4 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -5678,6 +5678,11 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) } } +static int nl80211_reload_regdb(struct sk_buff *skb, struct genl_info *info) +{ + return reg_reload_regdb(); +} + static int nl80211_get_mesh_config(struct sk_buff *skb, struct genl_info *info) { @@ -12709,6 +12714,12 @@ static const struct genl_ops nl80211_ops[] = { .flags = GENL_ADMIN_PERM, }, { + .cmd = NL80211_CMD_RELOAD_REGDB, + .doit = nl80211_reload_regdb, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, + { .cmd = NL80211_CMD_GET_MESH_CONFIG, .doit = nl80211_get_mesh_config, .policy = nl80211_policy, diff --git a/net/wireless/reg.c b/net/wireless/reg.c index e9aeb05aaf3e..180addda52af 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -781,6 +781,8 @@ static int query_regdb(const char *alpha2) const struct fwdb_header *hdr = regdb; const struct fwdb_country *country; + ASSERT_RTNL(); + if (IS_ERR(regdb)) return PTR_ERR(regdb); @@ -796,41 +798,47 @@ static int query_regdb(const char *alpha2) static void regdb_fw_cb(const struct firmware *fw, void *context) { + int set_error = 0; + bool restore = true; void *db; if (!fw) { pr_info("failed to load regulatory.db\n"); - regdb = ERR_PTR(-ENODATA); - goto restore; - } - - if (!valid_regdb(fw->data, fw->size)) { + set_error = -ENODATA; + } else if (!valid_regdb(fw->data, fw->size)) { pr_info("loaded regulatory.db is malformed\n"); - release_firmware(fw); - regdb = ERR_PTR(-EINVAL); - goto restore; + set_error = -EINVAL; } - db = kmemdup(fw->data, fw->size, GFP_KERNEL); - release_firmware(fw); + rtnl_lock(); + if (WARN_ON(regdb && !IS_ERR(regdb))) { + /* just restore and free new db */ + } else if (set_error) { + regdb = ERR_PTR(set_error); + } else if (fw) { + db = kmemdup(fw->data, fw->size, GFP_KERNEL); + if (db) { + regdb = db; + restore = context && query_regdb(context); + } else { + restore = true; + } + } - if (!db) - goto restore; - regdb = db; + if (restore) + restore_regulatory_settings(true); - if (query_regdb(context)) - goto restore; - goto free; - restore: - rtnl_lock(); - restore_regulatory_settings(true); rtnl_unlock(); - free: + kfree(context); + + release_firmware(fw); } static int query_regdb_file(const char *alpha2) { + ASSERT_RTNL(); + if (regdb) return query_regdb(alpha2); @@ -843,6 +851,38 @@ static int query_regdb_file(const char *alpha2) (void *)alpha2, regdb_fw_cb); } +int reg_reload_regdb(void) +{ + const struct firmware *fw; + void *db; + int err; + + err = request_firmware(&fw, "regulatory.db", ®_pdev->dev); + if (err) + return err; + + if (!valid_regdb(fw->data, fw->size)) { + err = -ENODATA; + goto out; + } + + db = kmemdup(fw->data, fw->size, GFP_KERNEL); + if (!db) { + err = -ENOMEM; + goto out; + } + + rtnl_lock(); + if (!IS_ERR_OR_NULL(regdb)) + kfree(regdb); + regdb = db; + rtnl_unlock(); + + out: + release_firmware(fw); + return err; +} + static bool reg_query_database(struct regulatory_request *request) { /* query internal regulatory database (if it exists) */ diff --git a/net/wireless/reg.h b/net/wireless/reg.h index ca7fedf2e7a1..9529c522611a 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h @@ -179,4 +179,10 @@ void regulatory_propagate_dfs_state(struct wiphy *wiphy, * @wiphy2 - wiphy it's dfs_region to be checked against that of wiphy1 */ bool reg_dfs_domain_same(struct wiphy *wiphy1, struct wiphy *wiphy2); + +/** + * reg_reload_regdb - reload the regulatory.db firmware file + */ +int reg_reload_regdb(void); + #endif /* __NET_WIRELESS_REG_H */ |