Commit 1ea4ff3e authored by Johannes Berg's avatar Johannes Berg

cfg80211: support reloading regulatory database

If the regulatory database is loaded, and then updated, it may
be necessary to reload it. Add an nl80211 command to do this.

Note that this just reloads the database, it doesn't re-apply
the rules from it immediately.
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 007f6c5e
...@@ -990,6 +990,8 @@ ...@@ -990,6 +990,8 @@
* &NL80211_CMD_CONNECT or &NL80211_CMD_ROAM. If the 4 way handshake failed * &NL80211_CMD_CONNECT or &NL80211_CMD_ROAM. If the 4 way handshake failed
* &NL80211_CMD_DISCONNECT should be indicated instead. * &NL80211_CMD_DISCONNECT should be indicated instead.
* *
* @NL80211_CMD_RELOAD_REGDB: Request that the regdb firmware file is reloaded.
*
* @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
*/ */
...@@ -1194,6 +1196,8 @@ enum nl80211_commands { ...@@ -1194,6 +1196,8 @@ enum nl80211_commands {
NL80211_CMD_PORT_AUTHORIZED, NL80211_CMD_PORT_AUTHORIZED,
NL80211_CMD_RELOAD_REGDB,
/* add new commands above here */ /* add new commands above here */
/* used to define NL80211_CMD_MAX below */ /* used to define NL80211_CMD_MAX below */
......
...@@ -5678,6 +5678,11 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) ...@@ -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, static int nl80211_get_mesh_config(struct sk_buff *skb,
struct genl_info *info) struct genl_info *info)
{ {
...@@ -12708,6 +12713,12 @@ static const struct genl_ops nl80211_ops[] = { ...@@ -12708,6 +12713,12 @@ static const struct genl_ops nl80211_ops[] = {
.policy = nl80211_policy, .policy = nl80211_policy,
.flags = GENL_ADMIN_PERM, .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, .cmd = NL80211_CMD_GET_MESH_CONFIG,
.doit = nl80211_get_mesh_config, .doit = nl80211_get_mesh_config,
......
...@@ -781,6 +781,8 @@ static int query_regdb(const char *alpha2) ...@@ -781,6 +781,8 @@ static int query_regdb(const char *alpha2)
const struct fwdb_header *hdr = regdb; const struct fwdb_header *hdr = regdb;
const struct fwdb_country *country; const struct fwdb_country *country;
ASSERT_RTNL();
if (IS_ERR(regdb)) if (IS_ERR(regdb))
return PTR_ERR(regdb); return PTR_ERR(regdb);
...@@ -796,41 +798,47 @@ static int query_regdb(const char *alpha2) ...@@ -796,41 +798,47 @@ static int query_regdb(const char *alpha2)
static void regdb_fw_cb(const struct firmware *fw, void *context) static void regdb_fw_cb(const struct firmware *fw, void *context)
{ {
int set_error = 0;
bool restore = true;
void *db; void *db;
if (!fw) { if (!fw) {
pr_info("failed to load regulatory.db\n"); pr_info("failed to load regulatory.db\n");
regdb = ERR_PTR(-ENODATA); set_error = -ENODATA;
goto restore; } else if (!valid_regdb(fw->data, fw->size)) {
}
if (!valid_regdb(fw->data, fw->size)) {
pr_info("loaded regulatory.db is malformed\n"); pr_info("loaded regulatory.db is malformed\n");
release_firmware(fw); set_error = -EINVAL;
regdb = ERR_PTR(-EINVAL);
goto restore;
} }
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); db = kmemdup(fw->data, fw->size, GFP_KERNEL);
release_firmware(fw); if (db) {
if (!db)
goto restore;
regdb = db; regdb = db;
restore = context && query_regdb(context);
} else {
restore = true;
}
}
if (query_regdb(context)) if (restore)
goto restore;
goto free;
restore:
rtnl_lock();
restore_regulatory_settings(true); restore_regulatory_settings(true);
rtnl_unlock(); rtnl_unlock();
free:
kfree(context); kfree(context);
release_firmware(fw);
} }
static int query_regdb_file(const char *alpha2) static int query_regdb_file(const char *alpha2)
{ {
ASSERT_RTNL();
if (regdb) if (regdb)
return query_regdb(alpha2); return query_regdb(alpha2);
...@@ -843,6 +851,38 @@ static int query_regdb_file(const char *alpha2) ...@@ -843,6 +851,38 @@ static int query_regdb_file(const char *alpha2)
(void *)alpha2, regdb_fw_cb); (void *)alpha2, regdb_fw_cb);
} }
int reg_reload_regdb(void)
{
const struct firmware *fw;
void *db;
int err;
err = request_firmware(&fw, "regulatory.db", &reg_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) static bool reg_query_database(struct regulatory_request *request)
{ {
/* query internal regulatory database (if it exists) */ /* query internal regulatory database (if it exists) */
......
...@@ -179,4 +179,10 @@ void regulatory_propagate_dfs_state(struct wiphy *wiphy, ...@@ -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 * @wiphy2 - wiphy it's dfs_region to be checked against that of wiphy1
*/ */
bool reg_dfs_domain_same(struct wiphy *wiphy1, struct wiphy *wiphy2); 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 */ #endif /* __NET_WIRELESS_REG_H */
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