mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 22:21:40 +00:00
ieee802154: add support for creation/removal of logic interfaces
Add support for two more NL802154 commands: ADD_IFACE and DEL_IFACE, thus allowing creation and removal of logic WPAN interfaces on the top of wpan-phy. Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
This commit is contained in:
parent
0a868b26c8
commit
bb1cafb8fc
@ -118,6 +118,8 @@ enum {
|
||||
|
||||
IEEE802154_LIST_IFACE,
|
||||
IEEE802154_LIST_PHY,
|
||||
IEEE802154_ADD_IFACE,
|
||||
IEEE802154_DEL_IFACE,
|
||||
|
||||
__IEEE802154_CMD_MAX,
|
||||
};
|
||||
|
@ -41,6 +41,10 @@ struct wpan_phy {
|
||||
struct device dev;
|
||||
int idx;
|
||||
|
||||
struct net_device *(*add_iface)(struct wpan_phy *phy,
|
||||
const char *name);
|
||||
void (*del_iface)(struct wpan_phy *phy, struct net_device *dev);
|
||||
|
||||
char priv[0] __attribute__((__aligned__(NETDEV_ALIGN)));
|
||||
};
|
||||
|
||||
|
@ -26,6 +26,9 @@
|
||||
#include <net/netlink.h>
|
||||
#include <net/genetlink.h>
|
||||
#include <net/wpan-phy.h>
|
||||
#include <net/af_ieee802154.h>
|
||||
#include <net/ieee802154_netdev.h>
|
||||
#include <net/rtnetlink.h> /* for rtnl_{un,}lock */
|
||||
#include <linux/nl802154.h>
|
||||
|
||||
#include "ieee802154.h"
|
||||
@ -164,9 +167,162 @@ static int ieee802154_dump_phy(struct sk_buff *skb,
|
||||
return skb->len;
|
||||
}
|
||||
|
||||
static int ieee802154_add_iface(struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
{
|
||||
struct sk_buff *msg;
|
||||
struct wpan_phy *phy;
|
||||
const char *name;
|
||||
const char *devname;
|
||||
int rc = -ENOBUFS;
|
||||
struct net_device *dev;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
|
||||
if (!info->attrs[IEEE802154_ATTR_PHY_NAME])
|
||||
return -EINVAL;
|
||||
|
||||
name = nla_data(info->attrs[IEEE802154_ATTR_PHY_NAME]);
|
||||
if (name[nla_len(info->attrs[IEEE802154_ATTR_PHY_NAME]) - 1] != '\0')
|
||||
return -EINVAL; /* phy name should be null-terminated */
|
||||
|
||||
if (info->attrs[IEEE802154_ATTR_DEV_NAME]) {
|
||||
devname = nla_data(info->attrs[IEEE802154_ATTR_DEV_NAME]);
|
||||
if (devname[nla_len(info->attrs[IEEE802154_ATTR_DEV_NAME]) - 1]
|
||||
!= '\0')
|
||||
return -EINVAL; /* phy name should be null-terminated */
|
||||
} else {
|
||||
devname = "wpan%d";
|
||||
}
|
||||
|
||||
if (strlen(devname) >= IFNAMSIZ)
|
||||
return -ENAMETOOLONG;
|
||||
|
||||
phy = wpan_phy_find(name);
|
||||
if (!phy)
|
||||
return -ENODEV;
|
||||
|
||||
msg = ieee802154_nl_new_reply(info, 0, IEEE802154_ADD_IFACE);
|
||||
if (!msg)
|
||||
goto out_dev;
|
||||
|
||||
if (!phy->add_iface) {
|
||||
rc = -EINVAL;
|
||||
goto nla_put_failure;
|
||||
}
|
||||
|
||||
dev = phy->add_iface(phy, devname);
|
||||
if (IS_ERR(dev)) {
|
||||
rc = PTR_ERR(dev);
|
||||
goto nla_put_failure;
|
||||
}
|
||||
|
||||
NLA_PUT_STRING(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy));
|
||||
NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name);
|
||||
|
||||
dev_put(dev);
|
||||
|
||||
wpan_phy_put(phy);
|
||||
|
||||
return ieee802154_nl_reply(msg, info);
|
||||
|
||||
nla_put_failure:
|
||||
nlmsg_free(msg);
|
||||
out_dev:
|
||||
wpan_phy_put(phy);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int ieee802154_del_iface(struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
{
|
||||
struct sk_buff *msg;
|
||||
struct wpan_phy *phy;
|
||||
const char *name;
|
||||
int rc;
|
||||
struct net_device *dev;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
|
||||
if (!info->attrs[IEEE802154_ATTR_DEV_NAME])
|
||||
return -EINVAL;
|
||||
|
||||
name = nla_data(info->attrs[IEEE802154_ATTR_DEV_NAME]);
|
||||
if (name[nla_len(info->attrs[IEEE802154_ATTR_DEV_NAME]) - 1] != '\0')
|
||||
return -EINVAL; /* name should be null-terminated */
|
||||
|
||||
dev = dev_get_by_name(genl_info_net(info), name);
|
||||
if (!dev)
|
||||
return -ENODEV;
|
||||
|
||||
phy = ieee802154_mlme_ops(dev)->get_phy(dev);
|
||||
BUG_ON(!phy);
|
||||
|
||||
rc = -EINVAL;
|
||||
/* phy name is optional, but should be checked if it's given */
|
||||
if (info->attrs[IEEE802154_ATTR_PHY_NAME]) {
|
||||
struct wpan_phy *phy2;
|
||||
|
||||
const char *pname =
|
||||
nla_data(info->attrs[IEEE802154_ATTR_PHY_NAME]);
|
||||
if (pname[nla_len(info->attrs[IEEE802154_ATTR_PHY_NAME]) - 1]
|
||||
!= '\0')
|
||||
/* name should be null-terminated */
|
||||
goto out_dev;
|
||||
|
||||
phy2 = wpan_phy_find(pname);
|
||||
if (!phy2)
|
||||
goto out_dev;
|
||||
|
||||
if (phy != phy2) {
|
||||
wpan_phy_put(phy2);
|
||||
goto out_dev;
|
||||
}
|
||||
}
|
||||
|
||||
rc = -ENOBUFS;
|
||||
|
||||
msg = ieee802154_nl_new_reply(info, 0, IEEE802154_DEL_IFACE);
|
||||
if (!msg)
|
||||
goto out_dev;
|
||||
|
||||
if (!phy->del_iface) {
|
||||
rc = -EINVAL;
|
||||
goto nla_put_failure;
|
||||
}
|
||||
|
||||
rtnl_lock();
|
||||
phy->del_iface(phy, dev);
|
||||
|
||||
/* We don't have device anymore */
|
||||
dev_put(dev);
|
||||
dev = NULL;
|
||||
|
||||
rtnl_unlock();
|
||||
|
||||
|
||||
NLA_PUT_STRING(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy));
|
||||
NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, name);
|
||||
|
||||
wpan_phy_put(phy);
|
||||
|
||||
return ieee802154_nl_reply(msg, info);
|
||||
|
||||
nla_put_failure:
|
||||
nlmsg_free(msg);
|
||||
out_dev:
|
||||
wpan_phy_put(phy);
|
||||
if (dev)
|
||||
dev_put(dev);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static struct genl_ops ieee802154_phy_ops[] = {
|
||||
IEEE802154_DUMP(IEEE802154_LIST_PHY, ieee802154_list_phy,
|
||||
ieee802154_dump_phy),
|
||||
IEEE802154_OP(IEEE802154_ADD_IFACE, ieee802154_add_iface),
|
||||
IEEE802154_OP(IEEE802154_DEL_IFACE, ieee802154_del_iface),
|
||||
};
|
||||
|
||||
/*
|
||||
|
Loading…
Reference in New Issue
Block a user