Commit 10d626f4 authored by David S. Miller's avatar David S. Miller
parents 62d83681 bb1cafb8
...@@ -32,9 +32,29 @@ ...@@ -32,9 +32,29 @@
#include <net/nl802154.h> #include <net/nl802154.h>
#include <net/wpan-phy.h> #include <net/wpan-phy.h>
struct wpan_phy *net_to_phy(struct net_device *dev) struct fakehard_priv {
struct wpan_phy *phy;
};
static struct wpan_phy *fake_to_phy(const struct net_device *dev)
{ {
return container_of(dev->dev.parent, struct wpan_phy, dev); struct fakehard_priv *priv = netdev_priv(dev);
return priv->phy;
}
/**
* fake_get_phy - Return a phy corresponding to this device.
* @dev: The network device for which to return the wan-phy object
*
* This function returns a wpan-phy object corresponding to the passed
* network device. Reference counter for wpan-phy object is incremented,
* so when the wpan-phy isn't necessary, you should drop the reference
* via @wpan_phy_put() call.
*/
static struct wpan_phy *fake_get_phy(const struct net_device *dev)
{
struct wpan_phy *phy = fake_to_phy(dev);
return to_phy(get_device(&phy->dev));
} }
/** /**
...@@ -43,7 +63,7 @@ struct wpan_phy *net_to_phy(struct net_device *dev) ...@@ -43,7 +63,7 @@ struct wpan_phy *net_to_phy(struct net_device *dev)
* *
* Return the ID of the PAN from the PIB. * Return the ID of the PAN from the PIB.
*/ */
static u16 fake_get_pan_id(struct net_device *dev) static u16 fake_get_pan_id(const struct net_device *dev)
{ {
BUG_ON(dev->type != ARPHRD_IEEE802154); BUG_ON(dev->type != ARPHRD_IEEE802154);
...@@ -58,7 +78,7 @@ static u16 fake_get_pan_id(struct net_device *dev) ...@@ -58,7 +78,7 @@ static u16 fake_get_pan_id(struct net_device *dev)
* device. If the device has not yet had a short address assigned * device. If the device has not yet had a short address assigned
* then this should return 0xFFFF to indicate a lack of association. * then this should return 0xFFFF to indicate a lack of association.
*/ */
static u16 fake_get_short_addr(struct net_device *dev) static u16 fake_get_short_addr(const struct net_device *dev)
{ {
BUG_ON(dev->type != ARPHRD_IEEE802154); BUG_ON(dev->type != ARPHRD_IEEE802154);
...@@ -78,7 +98,7 @@ static u16 fake_get_short_addr(struct net_device *dev) ...@@ -78,7 +98,7 @@ static u16 fake_get_short_addr(struct net_device *dev)
* Note: This is in section 7.2.1.2 of the IEEE 802.15.4-2006 * Note: This is in section 7.2.1.2 of the IEEE 802.15.4-2006
* document. * document.
*/ */
static u8 fake_get_dsn(struct net_device *dev) static u8 fake_get_dsn(const struct net_device *dev)
{ {
BUG_ON(dev->type != ARPHRD_IEEE802154); BUG_ON(dev->type != ARPHRD_IEEE802154);
...@@ -98,7 +118,7 @@ static u8 fake_get_dsn(struct net_device *dev) ...@@ -98,7 +118,7 @@ static u8 fake_get_dsn(struct net_device *dev)
* Note: This is in section 7.2.1.2 of the IEEE 802.15.4-2006 * Note: This is in section 7.2.1.2 of the IEEE 802.15.4-2006
* document. * document.
*/ */
static u8 fake_get_bsn(struct net_device *dev) static u8 fake_get_bsn(const struct net_device *dev)
{ {
BUG_ON(dev->type != ARPHRD_IEEE802154); BUG_ON(dev->type != ARPHRD_IEEE802154);
...@@ -121,7 +141,7 @@ static u8 fake_get_bsn(struct net_device *dev) ...@@ -121,7 +141,7 @@ static u8 fake_get_bsn(struct net_device *dev)
static int fake_assoc_req(struct net_device *dev, static int fake_assoc_req(struct net_device *dev,
struct ieee802154_addr *addr, u8 channel, u8 page, u8 cap) struct ieee802154_addr *addr, u8 channel, u8 page, u8 cap)
{ {
struct wpan_phy *phy = net_to_phy(dev); struct wpan_phy *phy = fake_to_phy(dev);
mutex_lock(&phy->pib_lock); mutex_lock(&phy->pib_lock);
phy->current_channel = channel; phy->current_channel = channel;
...@@ -196,7 +216,7 @@ static int fake_start_req(struct net_device *dev, struct ieee802154_addr *addr, ...@@ -196,7 +216,7 @@ static int fake_start_req(struct net_device *dev, struct ieee802154_addr *addr,
u8 bcn_ord, u8 sf_ord, u8 pan_coord, u8 blx, u8 bcn_ord, u8 sf_ord, u8 pan_coord, u8 blx,
u8 coord_realign) u8 coord_realign)
{ {
struct wpan_phy *phy = net_to_phy(dev); struct wpan_phy *phy = fake_to_phy(dev);
mutex_lock(&phy->pib_lock); mutex_lock(&phy->pib_lock);
phy->current_channel = channel; phy->current_channel = channel;
...@@ -239,6 +259,8 @@ static struct ieee802154_mlme_ops fake_mlme = { ...@@ -239,6 +259,8 @@ static struct ieee802154_mlme_ops fake_mlme = {
.start_req = fake_start_req, .start_req = fake_start_req,
.scan_req = fake_scan_req, .scan_req = fake_scan_req,
.get_phy = fake_get_phy,
.get_pan_id = fake_get_pan_id, .get_pan_id = fake_get_pan_id,
.get_short_addr = fake_get_short_addr, .get_short_addr = fake_get_short_addr,
.get_dsn = fake_get_dsn, .get_dsn = fake_get_dsn,
...@@ -313,7 +335,7 @@ static const struct net_device_ops fake_ops = { ...@@ -313,7 +335,7 @@ static const struct net_device_ops fake_ops = {
static void ieee802154_fake_destruct(struct net_device *dev) static void ieee802154_fake_destruct(struct net_device *dev)
{ {
struct wpan_phy *phy = net_to_phy(dev); struct wpan_phy *phy = fake_to_phy(dev);
wpan_phy_unregister(phy); wpan_phy_unregister(phy);
free_netdev(dev); free_netdev(dev);
...@@ -338,13 +360,14 @@ static void ieee802154_fake_setup(struct net_device *dev) ...@@ -338,13 +360,14 @@ static void ieee802154_fake_setup(struct net_device *dev)
static int __devinit ieee802154fake_probe(struct platform_device *pdev) static int __devinit ieee802154fake_probe(struct platform_device *pdev)
{ {
struct net_device *dev; struct net_device *dev;
struct fakehard_priv *priv;
struct wpan_phy *phy = wpan_phy_alloc(0); struct wpan_phy *phy = wpan_phy_alloc(0);
int err; int err;
if (!phy) if (!phy)
return -ENOMEM; return -ENOMEM;
dev = alloc_netdev(0, "hardwpan%d", ieee802154_fake_setup); dev = alloc_netdev(sizeof(struct fakehard_priv), "hardwpan%d", ieee802154_fake_setup);
if (!dev) { if (!dev) {
wpan_phy_free(phy); wpan_phy_free(phy);
return -ENOMEM; return -ENOMEM;
...@@ -356,12 +379,23 @@ static int __devinit ieee802154fake_probe(struct platform_device *pdev) ...@@ -356,12 +379,23 @@ static int __devinit ieee802154fake_probe(struct platform_device *pdev)
dev->addr_len); dev->addr_len);
memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len); memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len);
phy->channels_supported = (1 << 27) - 1; /*
* For now we'd like to emulate 2.4 GHz-only device,
* both O-QPSK and CSS
*/
/* 2.4 GHz O-QPSK 802.15.4-2003 */
phy->channels_supported[0] |= 0x7FFF800;
/* 2.4 GHz CSS 802.15.4a-2007 */
phy->channels_supported[3] |= 0x3fff;
phy->transmit_power = 0xbf; phy->transmit_power = 0xbf;
dev->netdev_ops = &fake_ops; dev->netdev_ops = &fake_ops;
dev->ml_priv = &fake_mlme; dev->ml_priv = &fake_mlme;
priv = netdev_priv(dev);
priv->phy = phy;
/* /*
* If the name is a format string the caller wants us to do a * If the name is a format string the caller wants us to do a
* name allocation. * name allocation.
...@@ -372,11 +406,12 @@ static int __devinit ieee802154fake_probe(struct platform_device *pdev) ...@@ -372,11 +406,12 @@ static int __devinit ieee802154fake_probe(struct platform_device *pdev)
goto out; goto out;
} }
wpan_phy_set_dev(phy, &pdev->dev);
SET_NETDEV_DEV(dev, &phy->dev); SET_NETDEV_DEV(dev, &phy->dev);
platform_set_drvdata(pdev, dev); platform_set_drvdata(pdev, dev);
err = wpan_phy_register(&pdev->dev, phy); err = wpan_phy_register(phy);
if (err) if (err)
goto out; goto out;
......
...@@ -65,6 +65,9 @@ enum { ...@@ -65,6 +65,9 @@ enum {
IEEE802154_ATTR_SEC, IEEE802154_ATTR_SEC,
IEEE802154_ATTR_PAGE, IEEE802154_ATTR_PAGE,
IEEE802154_ATTR_CHANNEL_PAGE_LIST,
IEEE802154_ATTR_PHY_NAME,
__IEEE802154_ATTR_MAX, __IEEE802154_ATTR_MAX,
}; };
...@@ -114,6 +117,9 @@ enum { ...@@ -114,6 +117,9 @@ enum {
IEEE802154_RX_ENABLE_CONF, /* Not supported yet */ IEEE802154_RX_ENABLE_CONF, /* Not supported yet */
IEEE802154_LIST_IFACE, IEEE802154_LIST_IFACE,
IEEE802154_LIST_PHY,
IEEE802154_ADD_IFACE,
IEEE802154_DEL_IFACE,
__IEEE802154_CMD_MAX, __IEEE802154_CMD_MAX,
}; };
......
...@@ -74,8 +74,12 @@ static inline int mac_cb_type(struct sk_buff *skb) ...@@ -74,8 +74,12 @@ static inline int mac_cb_type(struct sk_buff *skb)
#define IEEE802154_MAC_SCAN_PASSIVE 2 #define IEEE802154_MAC_SCAN_PASSIVE 2
#define IEEE802154_MAC_SCAN_ORPHAN 3 #define IEEE802154_MAC_SCAN_ORPHAN 3
struct wpan_phy;
/* /*
* This should be located at net_device->ml_priv * This should be located at net_device->ml_priv
*
* get_phy should increment the reference counting on returned phy.
* Use wpan_wpy_put to put that reference.
*/ */
struct ieee802154_mlme_ops { struct ieee802154_mlme_ops {
int (*assoc_req)(struct net_device *dev, int (*assoc_req)(struct net_device *dev,
...@@ -94,18 +98,20 @@ struct ieee802154_mlme_ops { ...@@ -94,18 +98,20 @@ struct ieee802154_mlme_ops {
int (*scan_req)(struct net_device *dev, int (*scan_req)(struct net_device *dev,
u8 type, u32 channels, u8 page, u8 duration); u8 type, u32 channels, u8 page, u8 duration);
struct wpan_phy *(*get_phy)(const struct net_device *dev);
/* /*
* FIXME: these should become the part of PIB/MIB interface. * FIXME: these should become the part of PIB/MIB interface.
* However we still don't have IB interface of any kind * However we still don't have IB interface of any kind
*/ */
u16 (*get_pan_id)(struct net_device *dev); u16 (*get_pan_id)(const struct net_device *dev);
u16 (*get_short_addr)(struct net_device *dev); u16 (*get_short_addr)(const struct net_device *dev);
u8 (*get_dsn)(struct net_device *dev); u8 (*get_dsn)(const struct net_device *dev);
u8 (*get_bsn)(struct net_device *dev); u8 (*get_bsn)(const struct net_device *dev);
}; };
static inline struct ieee802154_mlme_ops *ieee802154_mlme_ops( static inline struct ieee802154_mlme_ops *ieee802154_mlme_ops(
struct net_device *dev) const struct net_device *dev)
{ {
return dev->ml_priv; return dev->ml_priv;
} }
......
...@@ -34,20 +34,32 @@ struct wpan_phy { ...@@ -34,20 +34,32 @@ struct wpan_phy {
*/ */
u8 current_channel; u8 current_channel;
u8 current_page; u8 current_page;
u32 channels_supported; u32 channels_supported[32];
u8 transmit_power; u8 transmit_power;
u8 cca_mode; u8 cca_mode;
struct device dev; struct device dev;
int idx; 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))); char priv[0] __attribute__((__aligned__(NETDEV_ALIGN)));
}; };
#define to_phy(_dev) container_of(_dev, struct wpan_phy, dev)
struct wpan_phy *wpan_phy_alloc(size_t priv_size); struct wpan_phy *wpan_phy_alloc(size_t priv_size);
int wpan_phy_register(struct device *parent, struct wpan_phy *phy); static inline void wpan_phy_set_dev(struct wpan_phy *phy, struct device *dev)
{
phy->dev.parent = dev;
}
int wpan_phy_register(struct wpan_phy *phy);
void wpan_phy_unregister(struct wpan_phy *phy); void wpan_phy_unregister(struct wpan_phy *phy);
void wpan_phy_free(struct wpan_phy *phy); void wpan_phy_free(struct wpan_phy *phy);
/* Same semantics as for class_for_each_device */
int wpan_phy_for_each(int (*fn)(struct wpan_phy *phy, void *data), void *data);
static inline void *wpan_phy_priv(struct wpan_phy *phy) static inline void *wpan_phy_priv(struct wpan_phy *phy)
{ {
...@@ -56,6 +68,12 @@ static inline void *wpan_phy_priv(struct wpan_phy *phy) ...@@ -56,6 +68,12 @@ static inline void *wpan_phy_priv(struct wpan_phy *phy)
} }
struct wpan_phy *wpan_phy_find(const char *str); struct wpan_phy *wpan_phy_find(const char *str);
static inline void wpan_phy_put(struct wpan_phy *phy)
{
put_device(&phy->dev);
}
static inline const char *wpan_phy_name(struct wpan_phy *phy) static inline const char *wpan_phy_name(struct wpan_phy *phy)
{ {
return dev_name(&phy->dev); return dev_name(&phy->dev);
......
obj-$(CONFIG_IEEE802154) += nl802154.o af_802154.o wpan-class.o obj-$(CONFIG_IEEE802154) += ieee802154.o af_802154.o
nl802154-y := netlink.o nl_policy.o ieee802154-y := netlink.o nl-mac.o nl-phy.o nl_policy.o wpan-class.o
af_802154-y := af_ieee802154.o raw.o dgram.o af_802154-y := af_ieee802154.o raw.o dgram.o
ccflags-y += -Wall -DDEBUG ccflags-y += -Wall -DDEBUG
/*
* Copyright (C) 2007, 2008, 2009 Siemens AG
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#ifndef IEEE_802154_LOCAL_H
#define IEEE_802154_LOCAL_H
int __init ieee802154_nl_init(void);
void __exit ieee802154_nl_exit(void);
#define IEEE802154_OP(_cmd, _func) \
{ \
.cmd = _cmd, \
.policy = ieee802154_policy, \
.doit = _func, \
.dumpit = NULL, \
.flags = GENL_ADMIN_PERM, \
}
#define IEEE802154_DUMP(_cmd, _func, _dump) \
{ \
.cmd = _cmd, \
.policy = ieee802154_policy, \
.doit = _func, \
.dumpit = _dump, \
}
struct genl_info;
struct sk_buff *ieee802154_nl_create(int flags, u8 req);
int ieee802154_nl_mcast(struct sk_buff *msg, unsigned int group);
struct sk_buff *ieee802154_nl_new_reply(struct genl_info *info,
int flags, u8 req);
int ieee802154_nl_reply(struct sk_buff *msg, struct genl_info *info);
extern struct genl_family nl802154_family;
int nl802154_mac_register(void);
int nl802154_phy_register(void);
#endif
This diff is collapsed.
This diff is collapsed.
/*
* Netlink inteface for IEEE 802.15.4 stack
*
* Copyright 2007, 2008 Siemens AG
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Written by:
* Sergey Lapin <slapin@ossfans.org>
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
* Maxim Osipov <maxim.osipov@siemens.com>
*/
#include <linux/kernel.h>
#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"
static int ieee802154_nl_fill_phy(struct sk_buff *msg, u32 pid,
u32 seq, int flags, struct wpan_phy *phy)
{
void *hdr;
int i, pages = 0;
uint32_t *buf = kzalloc(32 * sizeof(uint32_t), GFP_KERNEL);
pr_debug("%s\n", __func__);
if (!buf)
goto out;
hdr = genlmsg_put(msg, 0, seq, &nl802154_family, flags,
IEEE802154_LIST_PHY);
if (!hdr)
goto out;
mutex_lock(&phy->pib_lock);
NLA_PUT_STRING(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy));
NLA_PUT_U8(msg, IEEE802154_ATTR_PAGE, phy->current_page);
NLA_PUT_U8(msg, IEEE802154_ATTR_CHANNEL, phy->current_channel);
for (i = 0; i < 32; i++) {
if (phy->channels_supported[i])
buf[pages++] = phy->channels_supported[i] | (i << 27);
}
if (pages)
NLA_PUT(msg, IEEE802154_ATTR_CHANNEL_PAGE_LIST,
pages * sizeof(uint32_t), buf);
mutex_unlock(&phy->pib_lock);
return genlmsg_end(msg, hdr);
nla_put_failure:
mutex_unlock(&phy->pib_lock);
genlmsg_cancel(msg, hdr);
out:
kfree(buf);
return -EMSGSIZE;
}
static int ieee802154_list_phy(struct sk_buff *skb,
struct genl_info *info)
{
/* Request for interface name, index, type, IEEE address,
PAN Id, short address */
struct sk_buff *msg;
struct wpan_phy *phy;
const char *name;
int rc = -ENOBUFS;
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 */
phy = wpan_phy_find(name);
if (!phy)
return -ENODEV;
msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
if (!msg)
goto out_dev;
rc = ieee802154_nl_fill_phy(msg, info->snd_pid, info->snd_seq,
0, phy);
if (rc < 0)
goto out_free;
wpan_phy_put(phy);
return genlmsg_reply(msg, info);
out_free:
nlmsg_free(msg);
out_dev:
wpan_phy_put(phy);
return rc;
}
struct dump_phy_data {
struct sk_buff *skb;
struct netlink_callback *cb;
int idx, s_idx;
};
static int ieee802154_dump_phy_iter(struct wpan_phy *phy, void *_data)
{
int rc;
struct dump_phy_data *data = _data;
pr_debug("%s\n", __func__);
if (data->idx++ < data->s_idx)
return 0;
rc = ieee802154_nl_fill_phy(data->skb,
NETLINK_CB(data->cb->skb).pid,
data->cb->nlh->nlmsg_seq,
NLM_F_MULTI,
phy);
if (rc < 0) {
data->idx--;
return rc;
}
return 0;
}
static int ieee802154_dump_phy(struct sk_buff *skb,
struct netlink_callback *cb)
{
struct dump_phy_data data = {
.cb = cb,
.skb = skb,
.s_idx = cb->args[0],
.idx = 0,
};
pr_debug("%s\n", __func__);
wpan_phy_for_each(ieee802154_dump_phy_iter, &data);
cb->args[0] = data.idx;
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),
};
/*
* No need to unregister as family unregistration will do it.
*/
int nl802154_phy_register(void)
{
int i;
int rc;
for (i = 0; i < ARRAY_SIZE(ieee802154_phy_ops); i++) {
rc = genl_register_ops(&nl802154_family,
&ieee802154_phy_ops[i]);
if (rc)
return rc;
}
return 0;
}
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
const struct nla_policy ieee802154_policy[IEEE802154_ATTR_MAX + 1] = { const struct nla_policy ieee802154_policy[IEEE802154_ATTR_MAX + 1] = {
[IEEE802154_ATTR_DEV_NAME] = { .type = NLA_STRING, }, [IEEE802154_ATTR_DEV_NAME] = { .type = NLA_STRING, },
[IEEE802154_ATTR_DEV_INDEX] = { .type = NLA_U32, }, [IEEE802154_ATTR_DEV_INDEX] = { .type = NLA_U32, },
[IEEE802154_ATTR_PHY_NAME] = { .type = NLA_STRING, },
[IEEE802154_ATTR_STATUS] = { .type = NLA_U8, }, [IEEE802154_ATTR_STATUS] = { .type = NLA_U8, },
[IEEE802154_ATTR_SHORT_ADDR] = { .type = NLA_U16, }, [IEEE802154_ATTR_SHORT_ADDR] = { .type = NLA_U16, },
...@@ -50,5 +51,6 @@ const struct nla_policy ieee802154_policy[IEEE802154_ATTR_MAX + 1] = { ...@@ -50,5 +51,6 @@ const struct nla_policy ieee802154_policy[IEEE802154_ATTR_MAX + 1] = {
[IEEE802154_ATTR_CHANNELS] = { .type = NLA_U32, }, [IEEE802154_ATTR_CHANNELS] = { .type = NLA_U32, },
[IEEE802154_ATTR_DURATION] = { .type = NLA_U8, }, [IEEE802154_ATTR_DURATION] = { .type = NLA_U8, },
[IEEE802154_ATTR_ED_LIST] = { .len = 27 }, [IEEE802154_ATTR_ED_LIST] = { .len = 27 },
[IEEE802154_ATTR_CHANNEL_PAGE_LIST] = { .len = 32 * 4, },
}; };
...@@ -22,6 +22,8 @@ ...@@ -22,6 +22,8 @@
#include <net/wpan-phy.h> #include <net/wpan-phy.h>
#include "ieee802154.h"
#define MASTER_SHOW_COMPLEX(name, format_string, args...) \ #define MASTER_SHOW_COMPLEX(name, format_string, args...) \
static ssize_t name ## _show(struct device *dev, \ static ssize_t name ## _show(struct device *dev, \
struct device_attribute *attr, char *buf) \ struct device_attribute *attr, char *buf) \
...@@ -30,7 +32,7 @@ static ssize_t name ## _show(struct device *dev, \ ...@@ -30,7 +32,7 @@ static ssize_t name ## _show(struct device *dev, \
int ret; \ int ret; \
\ \
mutex_lock(&phy->pib_lock); \ mutex_lock(&phy->pib_lock); \
ret = sprintf(buf, format_string "\n", args); \ ret = snprintf(buf, PAGE_SIZE, format_string "\n", args); \
mutex_unlock(&phy->pib_lock); \ mutex_unlock(&phy->pib_lock); \
return ret; \ return ret; \
} }
...@@ -40,12 +42,30 @@ static ssize_t name ## _show(struct device *dev, \ ...@@ -40,12 +42,30 @@ static ssize_t name ## _show(struct device *dev, \
MASTER_SHOW(current_channel, "%d"); MASTER_SHOW(current_channel, "%d");
MASTER_SHOW(current_page, "%d"); MASTER_SHOW(current_page, "%d");
MASTER_SHOW(channels_supported, "%#x");
MASTER_SHOW_COMPLEX(transmit_power, "%d +- %d dB", MASTER_SHOW_COMPLEX(transmit_power, "%d +- %d dB",
((signed char) (phy->transmit_power << 2)) >> 2, ((signed char) (phy->transmit_power << 2)) >> 2,
(phy->transmit_power >> 6) ? (phy->transmit_power >> 6) * 3 : 1 ); (phy->transmit_power >> 6) ? (phy->transmit_power >> 6) * 3 : 1 );
MASTER_SHOW(cca_mode, "%d"); MASTER_SHOW(cca_mode, "%d");
static ssize_t channels_supported_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct wpan_phy *phy = container_of(dev, struct wpan_phy, dev);
int ret;
int i, len = 0;
mutex_lock(&phy->pib_lock);
for (i = 0; i < 32; i++) {
ret = snprintf(buf + len, PAGE_SIZE - len,
"%#09x\n", phy->channels_supported[i]);
if (ret < 0)
break;
len += ret;
}
mutex_unlock(&phy->pib_lock);
return len;
}
static struct device_attribute pmib_attrs[] = { static struct device_attribute pmib_attrs[] = {
__ATTR_RO(current_channel), __ATTR_RO(current_channel),
__ATTR_RO(current_page), __ATTR_RO(current_page),
...@@ -91,6 +111,31 @@ struct wpan_phy *wpan_phy_find(const char *str) ...@@ -91,6 +111,31 @@ struct wpan_phy *wpan_phy_find(const char *str)
} }
EXPORT_SYMBOL(wpan_phy_find); EXPORT_SYMBOL(wpan_phy_find);
struct wpan_phy_iter_data {
int (*fn)(struct wpan_phy *phy, void *data);
void *data;
};
static int wpan_phy_iter(struct device *dev, void *_data)
{
struct wpan_phy_iter_data *wpid = _data;
struct wpan_phy *phy = container_of(dev, struct wpan_phy, dev);
return wpid->fn(phy, wpid->data);
}
int wpan_phy_for_each(int (*fn)(struct wpan_phy *phy, void *data),
void *data)
{
struct wpan_phy_iter_data wpid = {
.fn = fn,
.data = data,
};
return class_for_each_device(&wpan_phy_class, NULL,
&wpid, wpan_phy_iter);
}
EXPORT_SYMBOL(wpan_phy_for_each);
static int wpan_phy_idx_valid(int idx) static int wpan_phy_idx_valid(int idx)
{ {
return idx >= 0; return idx >= 0;
...@@ -118,14 +163,15 @@ struct wpan_phy *wpan_phy_alloc(size_t priv_size) ...@@ -118,14 +163,15 @@ struct wpan_phy *wpan_phy_alloc(size_t priv_size)
phy->dev.class = &wpan_phy_class; phy->dev.class = &wpan_phy_class;
phy->current_channel = -1; /* not initialised */
phy->current_page = 0; /* for compatibility */
return phy; return phy;
} }
EXPORT_SYMBOL(wpan_phy_alloc); EXPORT_SYMBOL(wpan_phy_alloc);
int wpan_phy_register(struct device *parent, struct wpan_phy *phy) int wpan_phy_register(struct wpan_phy *phy)
{ {
phy->dev.parent = parent;
return device_add(&phy->dev); return device_add(&phy->dev);
} }
EXPORT_SYMBOL(wpan_phy_register); EXPORT_SYMBOL(wpan_phy_register);
...@@ -144,16 +190,31 @@ EXPORT_SYMBOL(wpan_phy_free); ...@@ -144,16 +190,31 @@ EXPORT_SYMBOL(wpan_phy_free);
static int __init wpan_phy_class_init(void) static int __init wpan_phy_class_init(void)
{ {
return class_register(&wpan_phy_class); int rc;
rc = class_register(&wpan_phy_class);
if (rc)
goto err;
rc = ieee802154_nl_init();
if (rc)
goto err_nl;
return 0;
err_nl:
class_unregister(&wpan_phy_class);
err:
return rc;
} }
subsys_initcall(wpan_phy_class_init); module_init(wpan_phy_class_init);
static void __exit wpan_phy_class_exit(void) static void __exit wpan_phy_class_exit(void)
{ {
ieee802154_nl_exit();
class_unregister(&wpan_phy_class); class_unregister(&wpan_phy_class);
} }
module_exit(wpan_phy_class_exit); module_exit(wpan_phy_class_exit);
MODULE_DESCRIPTION("IEEE 802.15.4 device class");
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("IEEE 802.15.4 configuration interface");
MODULE_AUTHOR("Dmitry Eremin-Solenikov");
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