mac80211-hwsim: Add HWSIM_CMD_GET_RADIO command

HWSIM_CMD_GET_RADIO returns information about a specific radio id or
all of them in response to a dump. Create the netlink skb or use the
one provided by the dump functionality. Use the existing attribute
appending function to fill in the same attributes when creating a
new hwsim radio.

Save alpha2 and struct ieee80211_regdomain in the hwsim data or else
they will be lost in the depths of regulatory infrastructure.

Signed-off-by: Patrik Flykt <patrik.flykt@linux.intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Patrik Flykt 2014-11-12 16:42:40 +02:00 committed by Johannes Berg
parent c449981c47
commit 93d638d49c
2 changed files with 125 additions and 1 deletions

View File

@ -415,6 +415,8 @@ struct mac80211_hwsim_data {
bool destroy_on_close;
struct work_struct destroy_work;
u32 portid;
char alpha2[2];
const struct ieee80211_regdomain *regd;
struct ieee80211_channel *tmp_chan;
struct delayed_work roc_done;
@ -2420,6 +2422,7 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
if (param->reg_strict)
hw->wiphy->regulatory_flags |= REGULATORY_STRICT_REG;
if (param->regd) {
data->regd = param->regd;
hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
wiphy_apply_custom_regulatory(hw->wiphy, param->regd);
/* give the regulatory workqueue a chance to run */
@ -2438,8 +2441,11 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
wiphy_debug(hw->wiphy, "hwaddr %pM registered\n", hw->wiphy->perm_addr);
if (param->reg_alpha2)
if (param->reg_alpha2) {
data->alpha2[0] = param->reg_alpha2[0];
data->alpha2[1] = param->reg_alpha2[1];
regulatory_hint(hw->wiphy, param->reg_alpha2);
}
data->debugfs = debugfs_create_dir("hwsim", hw->wiphy->debugfsdir);
debugfs_create_file("ps", 0666, data->debugfs, data, &hwsim_fops_ps);
@ -2518,6 +2524,44 @@ static void mac80211_hwsim_del_radio(struct mac80211_hwsim_data *data,
ieee80211_free_hw(data->hw);
}
static int mac80211_hwsim_get_radio(struct sk_buff *skb,
struct mac80211_hwsim_data *data,
u32 portid, u32 seq,
struct netlink_callback *cb, int flags)
{
void *hdr;
struct hwsim_new_radio_params param = { };
int res = -EMSGSIZE;
hdr = genlmsg_put(skb, portid, seq, &hwsim_genl_family, flags,
HWSIM_CMD_GET_RADIO);
if (!hdr)
return -EMSGSIZE;
if (cb)
genl_dump_check_consistent(cb, hdr, &hwsim_genl_family);
param.reg_alpha2 = data->alpha2;
param.reg_strict = !!(data->hw->wiphy->regulatory_flags &
REGULATORY_STRICT_REG);
param.p2p_device = !!(data->hw->wiphy->interface_modes &
BIT(NL80211_IFTYPE_P2P_DEVICE));
param.use_chanctx = data->use_chanctx;
param.regd = data->regd;
param.channels = data->channels;
param.hwname = wiphy_name(data->hw->wiphy);
res = append_radio_msg(skb, data->idx, &param);
if (res < 0)
goto out_err;
return genlmsg_end(skb, hdr);
out_err:
genlmsg_cancel(skb, hdr);
return res;
}
static void mac80211_hwsim_free(void)
{
struct mac80211_hwsim_data *data;
@ -2823,6 +2867,77 @@ static int hwsim_del_radio_nl(struct sk_buff *msg, struct genl_info *info)
return -ENODEV;
}
static int hwsim_get_radio_nl(struct sk_buff *msg, struct genl_info *info)
{
struct mac80211_hwsim_data *data;
struct sk_buff *skb;
int idx, res = -ENODEV;
if (!info->attrs[HWSIM_ATTR_RADIO_ID])
return -EINVAL;
idx = nla_get_u32(info->attrs[HWSIM_ATTR_RADIO_ID]);
spin_lock_bh(&hwsim_radio_lock);
list_for_each_entry(data, &hwsim_radios, list) {
if (data->idx != idx)
continue;
skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!skb) {
res = -ENOMEM;
goto out_err;
}
res = mac80211_hwsim_get_radio(skb, data, info->snd_portid,
info->snd_seq, NULL, 0);
if (res < 0) {
nlmsg_free(skb);
goto out_err;
}
genlmsg_reply(skb, info);
break;
}
out_err:
spin_unlock_bh(&hwsim_radio_lock);
return res;
}
static int hwsim_dump_radio_nl(struct sk_buff *skb,
struct netlink_callback *cb)
{
int idx = cb->args[0];
struct mac80211_hwsim_data *data = NULL;
int res;
spin_lock_bh(&hwsim_radio_lock);
if (idx == hwsim_radio_idx)
goto done;
list_for_each_entry(data, &hwsim_radios, list) {
if (data->idx < idx)
continue;
res = mac80211_hwsim_get_radio(skb, data,
NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq, cb,
NLM_F_MULTI);
if (res < 0)
break;
idx = data->idx + 1;
}
cb->args[0] = idx;
done:
spin_unlock_bh(&hwsim_radio_lock);
return skb->len;
}
/* Generic Netlink operations array */
static const struct genl_ops hwsim_ops[] = {
{
@ -2853,6 +2968,12 @@ static const struct genl_ops hwsim_ops[] = {
.doit = hwsim_del_radio_nl,
.flags = GENL_ADMIN_PERM,
},
{
.cmd = HWSIM_CMD_GET_RADIO,
.policy = hwsim_genl_policy,
.doit = hwsim_get_radio_nl,
.dumpit = hwsim_dump_radio_nl,
},
};
static void destroy_radio(struct work_struct *work)

View File

@ -69,6 +69,8 @@ enum hwsim_tx_control_flags {
* returns the radio ID (>= 0) or negative on errors, if successful
* then multicast the result
* @HWSIM_CMD_DEL_RADIO: destroy a radio, reply is multicasted
* @HWSIM_CMD_GET_RADIO: fetch information about existing radios, uses:
* %HWSIM_ATTR_RADIO_ID
* @__HWSIM_CMD_MAX: enum limit
*/
enum {
@ -78,6 +80,7 @@ enum {
HWSIM_CMD_TX_INFO_FRAME,
HWSIM_CMD_NEW_RADIO,
HWSIM_CMD_DEL_RADIO,
HWSIM_CMD_GET_RADIO,
__HWSIM_CMD_MAX,
};
#define HWSIM_CMD_MAX (_HWSIM_CMD_MAX - 1)