add idl4k kernel firmware version 1.13.0.105

This commit is contained in:
Jaroslav Kysela
2015-03-26 17:22:37 +01:00
parent 5194d2792e
commit e9070cdc77
31064 changed files with 12769984 additions and 0 deletions

View File

@@ -0,0 +1,12 @@
config IEEE802154
tristate "IEEE Std 802.15.4 Low-Rate Wireless Personal Area Networks support (EXPERIMENTAL)"
depends on EXPERIMENTAL
---help---
IEEE Std 802.15.4 defines a low data rate, low power and low
complexity short range wireless personal area networks. It was
designed to organise networks of sensors, switches, etc automation
devices. Maximum allowed data rate is 250 kb/s and typical personal
operating space around 10m.
Say Y here to compile LR-WPAN support into the kernel or say M to
compile it as modules.

View File

@@ -0,0 +1,5 @@
obj-$(CONFIG_IEEE802154) += nl802154.o af_802154.o wpan-class.o
nl802154-y := netlink.o nl_policy.o
af_802154-y := af_ieee802154.o raw.o dgram.o
ccflags-y += -Wall -DDEBUG

View File

@@ -0,0 +1,36 @@
/*
* Internal interfaces for ieee 802.15.4 address family.
*
* Copyright 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.
*
* Written by:
* Sergey Lapin <slapin@ossfans.org>
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
*/
#ifndef AF802154_H
#define AF802154_H
struct sk_buff;
struct net_devce;
extern struct proto ieee802154_raw_prot;
extern struct proto ieee802154_dgram_prot;
void ieee802154_raw_deliver(struct net_device *dev, struct sk_buff *skb);
int ieee802154_dgram_deliver(struct net_device *dev, struct sk_buff *skb);
struct net_device *ieee802154_get_dev(struct net *net,
struct ieee802154_addr *addr);
#endif

View File

@@ -0,0 +1,366 @@
/*
* IEEE802154.4 socket interface
*
* 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>
* Maxim Gorbachyov <maxim.gorbachev@siemens.com>
*/
#include <linux/net.h>
#include <linux/capability.h>
#include <linux/module.h>
#include <linux/if_arp.h>
#include <linux/if.h>
#include <linux/termios.h> /* For TIOCOUTQ/INQ */
#include <linux/list.h>
#include <net/datalink.h>
#include <net/psnap.h>
#include <net/sock.h>
#include <net/tcp_states.h>
#include <net/route.h>
#include <net/af_ieee802154.h>
#include <net/ieee802154_netdev.h>
#include "af802154.h"
/*
* Utility function for families
*/
struct net_device *ieee802154_get_dev(struct net *net,
struct ieee802154_addr *addr)
{
struct net_device *dev = NULL;
struct net_device *tmp;
u16 pan_id, short_addr;
switch (addr->addr_type) {
case IEEE802154_ADDR_LONG:
rtnl_lock();
dev = dev_getbyhwaddr(net, ARPHRD_IEEE802154, addr->hwaddr);
if (dev)
dev_hold(dev);
rtnl_unlock();
break;
case IEEE802154_ADDR_SHORT:
if (addr->pan_id == 0xffff ||
addr->short_addr == IEEE802154_ADDR_UNDEF ||
addr->short_addr == 0xffff)
break;
rtnl_lock();
for_each_netdev(net, tmp) {
if (tmp->type != ARPHRD_IEEE802154)
continue;
pan_id = ieee802154_mlme_ops(tmp)->get_pan_id(tmp);
short_addr =
ieee802154_mlme_ops(tmp)->get_short_addr(tmp);
if (pan_id == addr->pan_id &&
short_addr == addr->short_addr) {
dev = tmp;
dev_hold(dev);
break;
}
}
rtnl_unlock();
break;
default:
pr_warning("Unsupported ieee802154 address type: %d\n",
addr->addr_type);
break;
}
return dev;
}
static int ieee802154_sock_release(struct socket *sock)
{
struct sock *sk = sock->sk;
if (sk) {
sock->sk = NULL;
sk->sk_prot->close(sk, 0);
}
return 0;
}
static int ieee802154_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
struct msghdr *msg, size_t len)
{
struct sock *sk = sock->sk;
return sk->sk_prot->sendmsg(iocb, sk, msg, len);
}
static int ieee802154_sock_bind(struct socket *sock, struct sockaddr *uaddr,
int addr_len)
{
struct sock *sk = sock->sk;
if (sk->sk_prot->bind)
return sk->sk_prot->bind(sk, uaddr, addr_len);
return sock_no_bind(sock, uaddr, addr_len);
}
static int ieee802154_sock_connect(struct socket *sock, struct sockaddr *uaddr,
int addr_len, int flags)
{
struct sock *sk = sock->sk;
if (uaddr->sa_family == AF_UNSPEC)
return sk->sk_prot->disconnect(sk, flags);
return sk->sk_prot->connect(sk, uaddr, addr_len);
}
static int ieee802154_dev_ioctl(struct sock *sk, struct ifreq __user *arg,
unsigned int cmd)
{
struct ifreq ifr;
int ret = -ENOIOCTLCMD;
struct net_device *dev;
if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
return -EFAULT;
ifr.ifr_name[IFNAMSIZ-1] = 0;
dev_load(sock_net(sk), ifr.ifr_name);
dev = dev_get_by_name(sock_net(sk), ifr.ifr_name);
if (dev->type == ARPHRD_IEEE802154 && dev->netdev_ops->ndo_do_ioctl)
ret = dev->netdev_ops->ndo_do_ioctl(dev, &ifr, cmd);
if (!ret && copy_to_user(arg, &ifr, sizeof(struct ifreq)))
ret = -EFAULT;
dev_put(dev);
return ret;
}
static int ieee802154_sock_ioctl(struct socket *sock, unsigned int cmd,
unsigned long arg)
{
struct sock *sk = sock->sk;
switch (cmd) {
case SIOCGSTAMP:
return sock_get_timestamp(sk, (struct timeval __user *)arg);
case SIOCGSTAMPNS:
return sock_get_timestampns(sk, (struct timespec __user *)arg);
case SIOCGIFADDR:
case SIOCSIFADDR:
return ieee802154_dev_ioctl(sk, (struct ifreq __user *)arg,
cmd);
default:
if (!sk->sk_prot->ioctl)
return -ENOIOCTLCMD;
return sk->sk_prot->ioctl(sk, cmd, arg);
}
}
static const struct proto_ops ieee802154_raw_ops = {
.family = PF_IEEE802154,
.owner = THIS_MODULE,
.release = ieee802154_sock_release,
.bind = ieee802154_sock_bind,
.connect = ieee802154_sock_connect,
.socketpair = sock_no_socketpair,
.accept = sock_no_accept,
.getname = sock_no_getname,
.poll = datagram_poll,
.ioctl = ieee802154_sock_ioctl,
.listen = sock_no_listen,
.shutdown = sock_no_shutdown,
.setsockopt = sock_common_setsockopt,
.getsockopt = sock_common_getsockopt,
.sendmsg = ieee802154_sock_sendmsg,
.recvmsg = sock_common_recvmsg,
.mmap = sock_no_mmap,
.sendpage = sock_no_sendpage,
#ifdef CONFIG_COMPAT
.compat_setsockopt = compat_sock_common_setsockopt,
.compat_getsockopt = compat_sock_common_getsockopt,
#endif
};
static const struct proto_ops ieee802154_dgram_ops = {
.family = PF_IEEE802154,
.owner = THIS_MODULE,
.release = ieee802154_sock_release,
.bind = ieee802154_sock_bind,
.connect = ieee802154_sock_connect,
.socketpair = sock_no_socketpair,
.accept = sock_no_accept,
.getname = sock_no_getname,
.poll = datagram_poll,
.ioctl = ieee802154_sock_ioctl,
.listen = sock_no_listen,
.shutdown = sock_no_shutdown,
.setsockopt = sock_common_setsockopt,
.getsockopt = sock_common_getsockopt,
.sendmsg = ieee802154_sock_sendmsg,
.recvmsg = sock_common_recvmsg,
.mmap = sock_no_mmap,
.sendpage = sock_no_sendpage,
#ifdef CONFIG_COMPAT
.compat_setsockopt = compat_sock_common_setsockopt,
.compat_getsockopt = compat_sock_common_getsockopt,
#endif
};
/*
* Create a socket. Initialise the socket, blank the addresses
* set the state.
*/
static int ieee802154_create(struct net *net, struct socket *sock,
int protocol)
{
struct sock *sk;
int rc;
struct proto *proto;
const struct proto_ops *ops;
if (net != &init_net)
return -EAFNOSUPPORT;
switch (sock->type) {
case SOCK_RAW:
proto = &ieee802154_raw_prot;
ops = &ieee802154_raw_ops;
break;
case SOCK_DGRAM:
proto = &ieee802154_dgram_prot;
ops = &ieee802154_dgram_ops;
break;
default:
rc = -ESOCKTNOSUPPORT;
goto out;
}
rc = -ENOMEM;
sk = sk_alloc(net, PF_IEEE802154, GFP_KERNEL, proto);
if (!sk)
goto out;
rc = 0;
sock->ops = ops;
sock_init_data(sock, sk);
/* FIXME: sk->sk_destruct */
sk->sk_family = PF_IEEE802154;
/* Checksums on by default */
sock_set_flag(sk, SOCK_ZAPPED);
if (sk->sk_prot->hash)
sk->sk_prot->hash(sk);
if (sk->sk_prot->init) {
rc = sk->sk_prot->init(sk);
if (rc)
sk_common_release(sk);
}
out:
return rc;
}
static struct net_proto_family ieee802154_family_ops = {
.family = PF_IEEE802154,
.create = ieee802154_create,
.owner = THIS_MODULE,
};
static int ieee802154_rcv(struct sk_buff *skb, struct net_device *dev,
struct packet_type *pt, struct net_device *orig_dev)
{
if (!netif_running(dev))
return -ENODEV;
pr_debug("got frame, type %d, dev %p\n", dev->type, dev);
#ifdef DEBUG
print_hex_dump_bytes("ieee802154_rcv ", DUMP_PREFIX_NONE, skb->data, skb->len);
#endif
if (!net_eq(dev_net(dev), &init_net))
goto drop;
ieee802154_raw_deliver(dev, skb);
if (dev->type != ARPHRD_IEEE802154)
goto drop;
if (skb->pkt_type != PACKET_OTHERHOST)
return ieee802154_dgram_deliver(dev, skb);
drop:
kfree_skb(skb);
return NET_RX_DROP;
}
static struct packet_type ieee802154_packet_type = {
.type = __constant_htons(ETH_P_IEEE802154),
.func = ieee802154_rcv,
};
static int __init af_ieee802154_init(void)
{
int rc = -EINVAL;
rc = proto_register(&ieee802154_raw_prot, 1);
if (rc)
goto out;
rc = proto_register(&ieee802154_dgram_prot, 1);
if (rc)
goto err_dgram;
/* Tell SOCKET that we are alive */
rc = sock_register(&ieee802154_family_ops);
if (rc)
goto err_sock;
dev_add_pack(&ieee802154_packet_type);
rc = 0;
goto out;
err_sock:
proto_unregister(&ieee802154_dgram_prot);
err_dgram:
proto_unregister(&ieee802154_raw_prot);
out:
return rc;
}
static void __exit af_ieee802154_remove(void)
{
dev_remove_pack(&ieee802154_packet_type);
sock_unregister(PF_IEEE802154);
proto_unregister(&ieee802154_dgram_prot);
proto_unregister(&ieee802154_raw_prot);
}
module_init(af_ieee802154_init);
module_exit(af_ieee802154_remove);
MODULE_LICENSE("GPL");
MODULE_ALIAS_NETPROTO(PF_IEEE802154);

View File

@@ -0,0 +1,461 @@
/*
* ZigBee socket interface
*
* 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>
*/
#include <linux/net.h>
#include <linux/module.h>
#include <linux/if_arp.h>
#include <linux/list.h>
#include <net/sock.h>
#include <net/af_ieee802154.h>
#include <net/ieee802154.h>
#include <net/ieee802154_netdev.h>
#include <asm/ioctls.h>
#include "af802154.h"
static HLIST_HEAD(dgram_head);
static DEFINE_RWLOCK(dgram_lock);
struct dgram_sock {
struct sock sk;
struct ieee802154_addr src_addr;
struct ieee802154_addr dst_addr;
unsigned bound:1;
unsigned want_ack:1;
};
static inline struct dgram_sock *dgram_sk(const struct sock *sk)
{
return container_of(sk, struct dgram_sock, sk);
}
static void dgram_hash(struct sock *sk)
{
write_lock_bh(&dgram_lock);
sk_add_node(sk, &dgram_head);
sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
write_unlock_bh(&dgram_lock);
}
static void dgram_unhash(struct sock *sk)
{
write_lock_bh(&dgram_lock);
if (sk_del_node_init(sk))
sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
write_unlock_bh(&dgram_lock);
}
static int dgram_init(struct sock *sk)
{
struct dgram_sock *ro = dgram_sk(sk);
ro->dst_addr.addr_type = IEEE802154_ADDR_LONG;
ro->dst_addr.pan_id = 0xffff;
ro->want_ack = 1;
memset(&ro->dst_addr.hwaddr, 0xff, sizeof(ro->dst_addr.hwaddr));
return 0;
}
static void dgram_close(struct sock *sk, long timeout)
{
sk_common_release(sk);
}
static int dgram_bind(struct sock *sk, struct sockaddr *uaddr, int len)
{
struct sockaddr_ieee802154 *addr = (struct sockaddr_ieee802154 *)uaddr;
struct dgram_sock *ro = dgram_sk(sk);
int err = -EINVAL;
struct net_device *dev;
lock_sock(sk);
ro->bound = 0;
if (len < sizeof(*addr))
goto out;
if (addr->family != AF_IEEE802154)
goto out;
dev = ieee802154_get_dev(sock_net(sk), &addr->addr);
if (!dev) {
err = -ENODEV;
goto out;
}
if (dev->type != ARPHRD_IEEE802154) {
err = -ENODEV;
goto out_put;
}
memcpy(&ro->src_addr, &addr->addr, sizeof(struct ieee802154_addr));
ro->bound = 1;
err = 0;
out_put:
dev_put(dev);
out:
release_sock(sk);
return err;
}
static int dgram_ioctl(struct sock *sk, int cmd, unsigned long arg)
{
switch (cmd) {
case SIOCOUTQ:
{
int amount = sk_wmem_alloc_get(sk);
return put_user(amount, (int __user *)arg);
}
case SIOCINQ:
{
struct sk_buff *skb;
unsigned long amount;
amount = 0;
spin_lock_bh(&sk->sk_receive_queue.lock);
skb = skb_peek(&sk->sk_receive_queue);
if (skb != NULL) {
/*
* We will only return the amount
* of this packet since that is all
* that will be read.
*/
/* FIXME: parse the header for more correct value */
amount = skb->len - (3+8+8);
}
spin_unlock_bh(&sk->sk_receive_queue.lock);
return put_user(amount, (int __user *)arg);
}
}
return -ENOIOCTLCMD;
}
/* FIXME: autobind */
static int dgram_connect(struct sock *sk, struct sockaddr *uaddr,
int len)
{
struct sockaddr_ieee802154 *addr = (struct sockaddr_ieee802154 *)uaddr;
struct dgram_sock *ro = dgram_sk(sk);
int err = 0;
if (len < sizeof(*addr))
return -EINVAL;
if (addr->family != AF_IEEE802154)
return -EINVAL;
lock_sock(sk);
if (!ro->bound) {
err = -ENETUNREACH;
goto out;
}
memcpy(&ro->dst_addr, &addr->addr, sizeof(struct ieee802154_addr));
out:
release_sock(sk);
return err;
}
static int dgram_disconnect(struct sock *sk, int flags)
{
struct dgram_sock *ro = dgram_sk(sk);
lock_sock(sk);
ro->dst_addr.addr_type = IEEE802154_ADDR_LONG;
memset(&ro->dst_addr.hwaddr, 0xff, sizeof(ro->dst_addr.hwaddr));
release_sock(sk);
return 0;
}
static int dgram_sendmsg(struct kiocb *iocb, struct sock *sk,
struct msghdr *msg, size_t size)
{
struct net_device *dev;
unsigned mtu;
struct sk_buff *skb;
struct dgram_sock *ro = dgram_sk(sk);
int err;
if (msg->msg_flags & MSG_OOB) {
pr_debug("msg->msg_flags = 0x%x\n", msg->msg_flags);
return -EOPNOTSUPP;
}
if (!ro->bound)
dev = dev_getfirstbyhwtype(sock_net(sk), ARPHRD_IEEE802154);
else
dev = ieee802154_get_dev(sock_net(sk), &ro->src_addr);
if (!dev) {
pr_debug("no dev\n");
err = -ENXIO;
goto out;
}
mtu = dev->mtu;
pr_debug("name = %s, mtu = %u\n", dev->name, mtu);
skb = sock_alloc_send_skb(sk, LL_ALLOCATED_SPACE(dev) + size,
msg->msg_flags & MSG_DONTWAIT,
&err);
if (!skb)
goto out_dev;
skb_reserve(skb, LL_RESERVED_SPACE(dev));
skb_reset_network_header(skb);
mac_cb(skb)->flags = IEEE802154_FC_TYPE_DATA;
if (ro->want_ack)
mac_cb(skb)->flags |= MAC_CB_FLAG_ACKREQ;
mac_cb(skb)->seq = ieee802154_mlme_ops(dev)->get_dsn(dev);
err = dev_hard_header(skb, dev, ETH_P_IEEE802154, &ro->dst_addr,
ro->bound ? &ro->src_addr : NULL, size);
if (err < 0)
goto out_skb;
skb_reset_mac_header(skb);
err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);
if (err < 0)
goto out_skb;
if (size > mtu) {
pr_debug("size = %Zu, mtu = %u\n", size, mtu);
err = -EINVAL;
goto out_skb;
}
skb->dev = dev;
skb->sk = sk;
skb->protocol = htons(ETH_P_IEEE802154);
dev_put(dev);
err = dev_queue_xmit(skb);
if (err > 0)
err = net_xmit_errno(err);
return err ?: size;
out_skb:
kfree_skb(skb);
out_dev:
dev_put(dev);
out:
return err;
}
static int dgram_recvmsg(struct kiocb *iocb, struct sock *sk,
struct msghdr *msg, size_t len, int noblock, int flags,
int *addr_len)
{
size_t copied = 0;
int err = -EOPNOTSUPP;
struct sk_buff *skb;
skb = skb_recv_datagram(sk, flags, noblock, &err);
if (!skb)
goto out;
copied = skb->len;
if (len < copied) {
msg->msg_flags |= MSG_TRUNC;
copied = len;
}
/* FIXME: skip headers if necessary ?! */
err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
if (err)
goto done;
sock_recv_timestamp(msg, sk, skb);
if (flags & MSG_TRUNC)
copied = skb->len;
done:
skb_free_datagram(sk, skb);
out:
if (err)
return err;
return copied;
}
static int dgram_rcv_skb(struct sock *sk, struct sk_buff *skb)
{
if (sock_queue_rcv_skb(sk, skb) < 0) {
atomic_inc(&sk->sk_drops);
kfree_skb(skb);
return NET_RX_DROP;
}
return NET_RX_SUCCESS;
}
static inline int ieee802154_match_sock(u8 *hw_addr, u16 pan_id,
u16 short_addr, struct dgram_sock *ro)
{
if (!ro->bound)
return 1;
if (ro->src_addr.addr_type == IEEE802154_ADDR_LONG &&
!memcmp(ro->src_addr.hwaddr, hw_addr, IEEE802154_ADDR_LEN))
return 1;
if (ro->src_addr.addr_type == IEEE802154_ADDR_SHORT &&
pan_id == ro->src_addr.pan_id &&
short_addr == ro->src_addr.short_addr)
return 1;
return 0;
}
int ieee802154_dgram_deliver(struct net_device *dev, struct sk_buff *skb)
{
struct sock *sk, *prev = NULL;
struct hlist_node *node;
int ret = NET_RX_SUCCESS;
u16 pan_id, short_addr;
/* Data frame processing */
BUG_ON(dev->type != ARPHRD_IEEE802154);
pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev);
short_addr = ieee802154_mlme_ops(dev)->get_short_addr(dev);
read_lock(&dgram_lock);
sk_for_each(sk, node, &dgram_head) {
if (ieee802154_match_sock(dev->dev_addr, pan_id, short_addr,
dgram_sk(sk))) {
if (prev) {
struct sk_buff *clone;
clone = skb_clone(skb, GFP_ATOMIC);
if (clone)
dgram_rcv_skb(prev, clone);
}
prev = sk;
}
}
if (prev)
dgram_rcv_skb(prev, skb);
else {
kfree_skb(skb);
ret = NET_RX_DROP;
}
read_unlock(&dgram_lock);
return ret;
}
static int dgram_getsockopt(struct sock *sk, int level, int optname,
char __user *optval, int __user *optlen)
{
struct dgram_sock *ro = dgram_sk(sk);
int val, len;
if (level != SOL_IEEE802154)
return -EOPNOTSUPP;
if (get_user(len, optlen))
return -EFAULT;
len = min_t(unsigned int, len, sizeof(int));
switch (optname) {
case WPAN_WANTACK:
val = ro->want_ack;
break;
default:
return -ENOPROTOOPT;
}
if (put_user(len, optlen))
return -EFAULT;
if (copy_to_user(optval, &val, len))
return -EFAULT;
return 0;
}
static int dgram_setsockopt(struct sock *sk, int level, int optname,
char __user *optval, unsigned int optlen)
{
struct dgram_sock *ro = dgram_sk(sk);
int val;
int err = 0;
if (optlen < sizeof(int))
return -EINVAL;
if (get_user(val, (int __user *)optval))
return -EFAULT;
lock_sock(sk);
switch (optname) {
case WPAN_WANTACK:
ro->want_ack = !!val;
break;
default:
err = -ENOPROTOOPT;
break;
}
release_sock(sk);
return err;
}
struct proto ieee802154_dgram_prot = {
.name = "IEEE-802.15.4-MAC",
.owner = THIS_MODULE,
.obj_size = sizeof(struct dgram_sock),
.init = dgram_init,
.close = dgram_close,
.bind = dgram_bind,
.sendmsg = dgram_sendmsg,
.recvmsg = dgram_recvmsg,
.hash = dgram_hash,
.unhash = dgram_unhash,
.connect = dgram_connect,
.disconnect = dgram_disconnect,
.ioctl = dgram_ioctl,
.getsockopt = dgram_getsockopt,
.setsockopt = dgram_setsockopt,
};

View File

@@ -0,0 +1,689 @@
/*
* 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 <linux/if_arp.h>
#include <linux/netdevice.h>
#include <net/netlink.h>
#include <net/genetlink.h>
#include <net/sock.h>
#include <linux/nl802154.h>
#include <net/af_ieee802154.h>
#include <net/nl802154.h>
#include <net/ieee802154.h>
#include <net/ieee802154_netdev.h>
static unsigned int ieee802154_seq_num;
static DEFINE_SPINLOCK(ieee802154_seq_lock);
static struct genl_family ieee802154_coordinator_family = {
.id = GENL_ID_GENERATE,
.hdrsize = 0,
.name = IEEE802154_NL_NAME,
.version = 1,
.maxattr = IEEE802154_ATTR_MAX,
};
static struct genl_multicast_group ieee802154_coord_mcgrp = {
.name = IEEE802154_MCAST_COORD_NAME,
};
static struct genl_multicast_group ieee802154_beacon_mcgrp = {
.name = IEEE802154_MCAST_BEACON_NAME,
};
/* Requests to userspace */
static struct sk_buff *ieee802154_nl_create(int flags, u8 req)
{
void *hdr;
struct sk_buff *msg = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
unsigned long f;
if (!msg)
return NULL;
spin_lock_irqsave(&ieee802154_seq_lock, f);
hdr = genlmsg_put(msg, 0, ieee802154_seq_num++,
&ieee802154_coordinator_family, flags, req);
spin_unlock_irqrestore(&ieee802154_seq_lock, f);
if (!hdr) {
nlmsg_free(msg);
return NULL;
}
return msg;
}
static int ieee802154_nl_finish(struct sk_buff *msg)
{
/* XXX: nlh is right at the start of msg */
void *hdr = genlmsg_data(NLMSG_DATA(msg->data));
if (genlmsg_end(msg, hdr) < 0)
goto out;
return genlmsg_multicast(msg, 0, ieee802154_coord_mcgrp.id,
GFP_ATOMIC);
out:
nlmsg_free(msg);
return -ENOBUFS;
}
int ieee802154_nl_assoc_indic(struct net_device *dev,
struct ieee802154_addr *addr, u8 cap)
{
struct sk_buff *msg;
pr_debug("%s\n", __func__);
if (addr->addr_type != IEEE802154_ADDR_LONG) {
pr_err("%s: received non-long source address!\n", __func__);
return -EINVAL;
}
msg = ieee802154_nl_create(0, IEEE802154_ASSOCIATE_INDIC);
if (!msg)
return -ENOBUFS;
NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name);
NLA_PUT_U32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex);
NLA_PUT(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
dev->dev_addr);
NLA_PUT(msg, IEEE802154_ATTR_SRC_HW_ADDR, IEEE802154_ADDR_LEN,
addr->hwaddr);
NLA_PUT_U8(msg, IEEE802154_ATTR_CAPABILITY, cap);
return ieee802154_nl_finish(msg);
nla_put_failure:
nlmsg_free(msg);
return -ENOBUFS;
}
EXPORT_SYMBOL(ieee802154_nl_assoc_indic);
int ieee802154_nl_assoc_confirm(struct net_device *dev, u16 short_addr,
u8 status)
{
struct sk_buff *msg;
pr_debug("%s\n", __func__);
msg = ieee802154_nl_create(0, IEEE802154_ASSOCIATE_CONF);
if (!msg)
return -ENOBUFS;
NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name);
NLA_PUT_U32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex);
NLA_PUT(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
dev->dev_addr);
NLA_PUT_U16(msg, IEEE802154_ATTR_SHORT_ADDR, short_addr);
NLA_PUT_U8(msg, IEEE802154_ATTR_STATUS, status);
return ieee802154_nl_finish(msg);
nla_put_failure:
nlmsg_free(msg);
return -ENOBUFS;
}
EXPORT_SYMBOL(ieee802154_nl_assoc_confirm);
int ieee802154_nl_disassoc_indic(struct net_device *dev,
struct ieee802154_addr *addr, u8 reason)
{
struct sk_buff *msg;
pr_debug("%s\n", __func__);
msg = ieee802154_nl_create(0, IEEE802154_DISASSOCIATE_INDIC);
if (!msg)
return -ENOBUFS;
NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name);
NLA_PUT_U32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex);
NLA_PUT(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
dev->dev_addr);
if (addr->addr_type == IEEE802154_ADDR_LONG)
NLA_PUT(msg, IEEE802154_ATTR_SRC_HW_ADDR, IEEE802154_ADDR_LEN,
addr->hwaddr);
else
NLA_PUT_U16(msg, IEEE802154_ATTR_SRC_SHORT_ADDR,
addr->short_addr);
NLA_PUT_U8(msg, IEEE802154_ATTR_REASON, reason);
return ieee802154_nl_finish(msg);
nla_put_failure:
nlmsg_free(msg);
return -ENOBUFS;
}
EXPORT_SYMBOL(ieee802154_nl_disassoc_indic);
int ieee802154_nl_disassoc_confirm(struct net_device *dev, u8 status)
{
struct sk_buff *msg;
pr_debug("%s\n", __func__);
msg = ieee802154_nl_create(0, IEEE802154_DISASSOCIATE_CONF);
if (!msg)
return -ENOBUFS;
NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name);
NLA_PUT_U32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex);
NLA_PUT(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
dev->dev_addr);
NLA_PUT_U8(msg, IEEE802154_ATTR_STATUS, status);
return ieee802154_nl_finish(msg);
nla_put_failure:
nlmsg_free(msg);
return -ENOBUFS;
}
EXPORT_SYMBOL(ieee802154_nl_disassoc_confirm);
int ieee802154_nl_beacon_indic(struct net_device *dev,
u16 panid, u16 coord_addr)
{
struct sk_buff *msg;
pr_debug("%s\n", __func__);
msg = ieee802154_nl_create(0, IEEE802154_BEACON_NOTIFY_INDIC);
if (!msg)
return -ENOBUFS;
NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name);
NLA_PUT_U32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex);
NLA_PUT(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
dev->dev_addr);
NLA_PUT_U16(msg, IEEE802154_ATTR_COORD_SHORT_ADDR, coord_addr);
NLA_PUT_U16(msg, IEEE802154_ATTR_COORD_PAN_ID, panid);
return ieee802154_nl_finish(msg);
nla_put_failure:
nlmsg_free(msg);
return -ENOBUFS;
}
EXPORT_SYMBOL(ieee802154_nl_beacon_indic);
int ieee802154_nl_scan_confirm(struct net_device *dev,
u8 status, u8 scan_type, u32 unscanned, u8 page,
u8 *edl/* , struct list_head *pan_desc_list */)
{
struct sk_buff *msg;
pr_debug("%s\n", __func__);
msg = ieee802154_nl_create(0, IEEE802154_SCAN_CONF);
if (!msg)
return -ENOBUFS;
NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name);
NLA_PUT_U32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex);
NLA_PUT(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
dev->dev_addr);
NLA_PUT_U8(msg, IEEE802154_ATTR_STATUS, status);
NLA_PUT_U8(msg, IEEE802154_ATTR_SCAN_TYPE, scan_type);
NLA_PUT_U32(msg, IEEE802154_ATTR_CHANNELS, unscanned);
NLA_PUT_U8(msg, IEEE802154_ATTR_PAGE, page);
if (edl)
NLA_PUT(msg, IEEE802154_ATTR_ED_LIST, 27, edl);
return ieee802154_nl_finish(msg);
nla_put_failure:
nlmsg_free(msg);
return -ENOBUFS;
}
EXPORT_SYMBOL(ieee802154_nl_scan_confirm);
int ieee802154_nl_start_confirm(struct net_device *dev, u8 status)
{
struct sk_buff *msg;
pr_debug("%s\n", __func__);
msg = ieee802154_nl_create(0, IEEE802154_START_CONF);
if (!msg)
return -ENOBUFS;
NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name);
NLA_PUT_U32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex);
NLA_PUT(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
dev->dev_addr);
NLA_PUT_U8(msg, IEEE802154_ATTR_STATUS, status);
return ieee802154_nl_finish(msg);
nla_put_failure:
nlmsg_free(msg);
return -ENOBUFS;
}
EXPORT_SYMBOL(ieee802154_nl_start_confirm);
static int ieee802154_nl_fill_iface(struct sk_buff *msg, u32 pid,
u32 seq, int flags, struct net_device *dev)
{
void *hdr;
pr_debug("%s\n", __func__);
hdr = genlmsg_put(msg, 0, seq, &ieee802154_coordinator_family, flags,
IEEE802154_LIST_IFACE);
if (!hdr)
goto out;
NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name);
NLA_PUT_U32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex);
NLA_PUT(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
dev->dev_addr);
NLA_PUT_U16(msg, IEEE802154_ATTR_SHORT_ADDR,
ieee802154_mlme_ops(dev)->get_short_addr(dev));
NLA_PUT_U16(msg, IEEE802154_ATTR_PAN_ID,
ieee802154_mlme_ops(dev)->get_pan_id(dev));
return genlmsg_end(msg, hdr);
nla_put_failure:
genlmsg_cancel(msg, hdr);
out:
return -EMSGSIZE;
}
/* Requests from userspace */
static struct net_device *ieee802154_nl_get_dev(struct genl_info *info)
{
struct net_device *dev;
if (info->attrs[IEEE802154_ATTR_DEV_NAME]) {
char name[IFNAMSIZ + 1];
nla_strlcpy(name, info->attrs[IEEE802154_ATTR_DEV_NAME],
sizeof(name));
dev = dev_get_by_name(&init_net, name);
} else if (info->attrs[IEEE802154_ATTR_DEV_INDEX])
dev = dev_get_by_index(&init_net,
nla_get_u32(info->attrs[IEEE802154_ATTR_DEV_INDEX]));
else
return NULL;
if (!dev)
return NULL;
if (dev->type != ARPHRD_IEEE802154) {
dev_put(dev);
return NULL;
}
return dev;
}
static int ieee802154_associate_req(struct sk_buff *skb,
struct genl_info *info)
{
struct net_device *dev;
struct ieee802154_addr addr;
u8 page;
int ret = -EINVAL;
if (!info->attrs[IEEE802154_ATTR_CHANNEL] ||
!info->attrs[IEEE802154_ATTR_COORD_PAN_ID] ||
(!info->attrs[IEEE802154_ATTR_COORD_HW_ADDR] &&
!info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]) ||
!info->attrs[IEEE802154_ATTR_CAPABILITY])
return -EINVAL;
dev = ieee802154_nl_get_dev(info);
if (!dev)
return -ENODEV;
if (info->attrs[IEEE802154_ATTR_COORD_HW_ADDR]) {
addr.addr_type = IEEE802154_ADDR_LONG;
nla_memcpy(addr.hwaddr,
info->attrs[IEEE802154_ATTR_COORD_HW_ADDR],
IEEE802154_ADDR_LEN);
} else {
addr.addr_type = IEEE802154_ADDR_SHORT;
addr.short_addr = nla_get_u16(
info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]);
}
addr.pan_id = nla_get_u16(info->attrs[IEEE802154_ATTR_COORD_PAN_ID]);
if (info->attrs[IEEE802154_ATTR_PAGE])
page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]);
else
page = 0;
ret = ieee802154_mlme_ops(dev)->assoc_req(dev, &addr,
nla_get_u8(info->attrs[IEEE802154_ATTR_CHANNEL]),
page,
nla_get_u8(info->attrs[IEEE802154_ATTR_CAPABILITY]));
dev_put(dev);
return ret;
}
static int ieee802154_associate_resp(struct sk_buff *skb,
struct genl_info *info)
{
struct net_device *dev;
struct ieee802154_addr addr;
int ret = -EINVAL;
if (!info->attrs[IEEE802154_ATTR_STATUS] ||
!info->attrs[IEEE802154_ATTR_DEST_HW_ADDR] ||
!info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR])
return -EINVAL;
dev = ieee802154_nl_get_dev(info);
if (!dev)
return -ENODEV;
addr.addr_type = IEEE802154_ADDR_LONG;
nla_memcpy(addr.hwaddr, info->attrs[IEEE802154_ATTR_DEST_HW_ADDR],
IEEE802154_ADDR_LEN);
addr.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev);
ret = ieee802154_mlme_ops(dev)->assoc_resp(dev, &addr,
nla_get_u16(info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]),
nla_get_u8(info->attrs[IEEE802154_ATTR_STATUS]));
dev_put(dev);
return ret;
}
static int ieee802154_disassociate_req(struct sk_buff *skb,
struct genl_info *info)
{
struct net_device *dev;
struct ieee802154_addr addr;
int ret = -EINVAL;
if ((!info->attrs[IEEE802154_ATTR_DEST_HW_ADDR] &&
!info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]) ||
!info->attrs[IEEE802154_ATTR_REASON])
return -EINVAL;
dev = ieee802154_nl_get_dev(info);
if (!dev)
return -ENODEV;
if (info->attrs[IEEE802154_ATTR_DEST_HW_ADDR]) {
addr.addr_type = IEEE802154_ADDR_LONG;
nla_memcpy(addr.hwaddr,
info->attrs[IEEE802154_ATTR_DEST_HW_ADDR],
IEEE802154_ADDR_LEN);
} else {
addr.addr_type = IEEE802154_ADDR_SHORT;
addr.short_addr = nla_get_u16(
info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]);
}
addr.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev);
ret = ieee802154_mlme_ops(dev)->disassoc_req(dev, &addr,
nla_get_u8(info->attrs[IEEE802154_ATTR_REASON]));
dev_put(dev);
return ret;
}
/*
* PANid, channel, beacon_order = 15, superframe_order = 15,
* PAN_coordinator, battery_life_extension = 0,
* coord_realignment = 0, security_enable = 0
*/
static int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info)
{
struct net_device *dev;
struct ieee802154_addr addr;
u8 channel, bcn_ord, sf_ord;
u8 page;
int pan_coord, blx, coord_realign;
int ret;
if (!info->attrs[IEEE802154_ATTR_COORD_PAN_ID] ||
!info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR] ||
!info->attrs[IEEE802154_ATTR_CHANNEL] ||
!info->attrs[IEEE802154_ATTR_BCN_ORD] ||
!info->attrs[IEEE802154_ATTR_SF_ORD] ||
!info->attrs[IEEE802154_ATTR_PAN_COORD] ||
!info->attrs[IEEE802154_ATTR_BAT_EXT] ||
!info->attrs[IEEE802154_ATTR_COORD_REALIGN]
)
return -EINVAL;
dev = ieee802154_nl_get_dev(info);
if (!dev)
return -ENODEV;
addr.addr_type = IEEE802154_ADDR_SHORT;
addr.short_addr = nla_get_u16(
info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]);
addr.pan_id = nla_get_u16(info->attrs[IEEE802154_ATTR_COORD_PAN_ID]);
channel = nla_get_u8(info->attrs[IEEE802154_ATTR_CHANNEL]);
bcn_ord = nla_get_u8(info->attrs[IEEE802154_ATTR_BCN_ORD]);
sf_ord = nla_get_u8(info->attrs[IEEE802154_ATTR_SF_ORD]);
pan_coord = nla_get_u8(info->attrs[IEEE802154_ATTR_PAN_COORD]);
blx = nla_get_u8(info->attrs[IEEE802154_ATTR_BAT_EXT]);
coord_realign = nla_get_u8(info->attrs[IEEE802154_ATTR_COORD_REALIGN]);
if (info->attrs[IEEE802154_ATTR_PAGE])
page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]);
else
page = 0;
if (addr.short_addr == IEEE802154_ADDR_BROADCAST) {
ieee802154_nl_start_confirm(dev, IEEE802154_NO_SHORT_ADDRESS);
dev_put(dev);
return -EINVAL;
}
ret = ieee802154_mlme_ops(dev)->start_req(dev, &addr, channel, page,
bcn_ord, sf_ord, pan_coord, blx, coord_realign);
dev_put(dev);
return ret;
}
static int ieee802154_scan_req(struct sk_buff *skb, struct genl_info *info)
{
struct net_device *dev;
int ret;
u8 type;
u32 channels;
u8 duration;
u8 page;
if (!info->attrs[IEEE802154_ATTR_SCAN_TYPE] ||
!info->attrs[IEEE802154_ATTR_CHANNELS] ||
!info->attrs[IEEE802154_ATTR_DURATION])
return -EINVAL;
dev = ieee802154_nl_get_dev(info);
if (!dev)
return -ENODEV;
type = nla_get_u8(info->attrs[IEEE802154_ATTR_SCAN_TYPE]);
channels = nla_get_u32(info->attrs[IEEE802154_ATTR_CHANNELS]);
duration = nla_get_u8(info->attrs[IEEE802154_ATTR_DURATION]);
if (info->attrs[IEEE802154_ATTR_PAGE])
page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]);
else
page = 0;
ret = ieee802154_mlme_ops(dev)->scan_req(dev, type, channels, page,
duration);
dev_put(dev);
return ret;
}
static int ieee802154_list_iface(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 net_device *dev = NULL;
int rc = -ENOBUFS;
pr_debug("%s\n", __func__);
dev = ieee802154_nl_get_dev(info);
if (!dev)
return -ENODEV;
msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
if (!msg)
goto out_dev;
rc = ieee802154_nl_fill_iface(msg, info->snd_pid, info->snd_seq,
0, dev);
if (rc < 0)
goto out_free;
dev_put(dev);
return genlmsg_unicast(&init_net, msg, info->snd_pid);
out_free:
nlmsg_free(msg);
out_dev:
dev_put(dev);
return rc;
}
static int ieee802154_dump_iface(struct sk_buff *skb,
struct netlink_callback *cb)
{
struct net *net = sock_net(skb->sk);
struct net_device *dev;
int idx;
int s_idx = cb->args[0];
pr_debug("%s\n", __func__);
idx = 0;
for_each_netdev(net, dev) {
if (idx < s_idx || (dev->type != ARPHRD_IEEE802154))
goto cont;
if (ieee802154_nl_fill_iface(skb, NETLINK_CB(cb->skb).pid,
cb->nlh->nlmsg_seq, NLM_F_MULTI, dev) < 0)
break;
cont:
idx++;
}
cb->args[0] = idx;
return skb->len;
}
#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, \
}
static struct genl_ops ieee802154_coordinator_ops[] = {
IEEE802154_OP(IEEE802154_ASSOCIATE_REQ, ieee802154_associate_req),
IEEE802154_OP(IEEE802154_ASSOCIATE_RESP, ieee802154_associate_resp),
IEEE802154_OP(IEEE802154_DISASSOCIATE_REQ, ieee802154_disassociate_req),
IEEE802154_OP(IEEE802154_SCAN_REQ, ieee802154_scan_req),
IEEE802154_OP(IEEE802154_START_REQ, ieee802154_start_req),
IEEE802154_DUMP(IEEE802154_LIST_IFACE, ieee802154_list_iface,
ieee802154_dump_iface),
};
static int __init ieee802154_nl_init(void)
{
int rc;
int i;
rc = genl_register_family(&ieee802154_coordinator_family);
if (rc)
goto fail;
rc = genl_register_mc_group(&ieee802154_coordinator_family,
&ieee802154_coord_mcgrp);
if (rc)
goto fail;
rc = genl_register_mc_group(&ieee802154_coordinator_family,
&ieee802154_beacon_mcgrp);
if (rc)
goto fail;
for (i = 0; i < ARRAY_SIZE(ieee802154_coordinator_ops); i++) {
rc = genl_register_ops(&ieee802154_coordinator_family,
&ieee802154_coordinator_ops[i]);
if (rc)
goto fail;
}
return 0;
fail:
genl_unregister_family(&ieee802154_coordinator_family);
return rc;
}
module_init(ieee802154_nl_init);
static void __exit ieee802154_nl_exit(void)
{
genl_unregister_family(&ieee802154_coordinator_family);
}
module_exit(ieee802154_nl_exit);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("ieee 802.15.4 configuration interface");

View File

@@ -0,0 +1,54 @@
/*
* nl802154.h
*
* Copyright (C) 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.
*
*/
#include <linux/kernel.h>
#include <net/netlink.h>
#include <linux/nl802154.h>
#define NLA_HW_ADDR NLA_U64
const struct nla_policy ieee802154_policy[IEEE802154_ATTR_MAX + 1] = {
[IEEE802154_ATTR_DEV_NAME] = { .type = NLA_STRING, },
[IEEE802154_ATTR_DEV_INDEX] = { .type = NLA_U32, },
[IEEE802154_ATTR_STATUS] = { .type = NLA_U8, },
[IEEE802154_ATTR_SHORT_ADDR] = { .type = NLA_U16, },
[IEEE802154_ATTR_HW_ADDR] = { .type = NLA_HW_ADDR, },
[IEEE802154_ATTR_PAN_ID] = { .type = NLA_U16, },
[IEEE802154_ATTR_CHANNEL] = { .type = NLA_U8, },
[IEEE802154_ATTR_PAGE] = { .type = NLA_U8, },
[IEEE802154_ATTR_COORD_SHORT_ADDR] = { .type = NLA_U16, },
[IEEE802154_ATTR_COORD_HW_ADDR] = { .type = NLA_HW_ADDR, },
[IEEE802154_ATTR_COORD_PAN_ID] = { .type = NLA_U16, },
[IEEE802154_ATTR_SRC_SHORT_ADDR] = { .type = NLA_U16, },
[IEEE802154_ATTR_SRC_HW_ADDR] = { .type = NLA_HW_ADDR, },
[IEEE802154_ATTR_SRC_PAN_ID] = { .type = NLA_U16, },
[IEEE802154_ATTR_DEST_SHORT_ADDR] = { .type = NLA_U16, },
[IEEE802154_ATTR_DEST_HW_ADDR] = { .type = NLA_HW_ADDR, },
[IEEE802154_ATTR_DEST_PAN_ID] = { .type = NLA_U16, },
[IEEE802154_ATTR_CAPABILITY] = { .type = NLA_U8, },
[IEEE802154_ATTR_REASON] = { .type = NLA_U8, },
[IEEE802154_ATTR_SCAN_TYPE] = { .type = NLA_U8, },
[IEEE802154_ATTR_CHANNELS] = { .type = NLA_U32, },
[IEEE802154_ATTR_DURATION] = { .type = NLA_U8, },
[IEEE802154_ATTR_ED_LIST] = { .len = 27 },
};

267
kernel/net/ieee802154/raw.c Normal file
View File

@@ -0,0 +1,267 @@
/*
* Raw IEEE 802.15.4 sockets
*
* 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>
*/
#include <linux/net.h>
#include <linux/module.h>
#include <linux/if_arp.h>
#include <linux/list.h>
#include <net/sock.h>
#include <net/af_ieee802154.h>
#include "af802154.h"
static HLIST_HEAD(raw_head);
static DEFINE_RWLOCK(raw_lock);
static void raw_hash(struct sock *sk)
{
write_lock_bh(&raw_lock);
sk_add_node(sk, &raw_head);
sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
write_unlock_bh(&raw_lock);
}
static void raw_unhash(struct sock *sk)
{
write_lock_bh(&raw_lock);
if (sk_del_node_init(sk))
sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
write_unlock_bh(&raw_lock);
}
static void raw_close(struct sock *sk, long timeout)
{
sk_common_release(sk);
}
static int raw_bind(struct sock *sk, struct sockaddr *uaddr, int len)
{
struct sockaddr_ieee802154 *addr = (struct sockaddr_ieee802154 *)uaddr;
int err = 0;
struct net_device *dev = NULL;
if (len < sizeof(*addr))
return -EINVAL;
if (addr->family != AF_IEEE802154)
return -EINVAL;
lock_sock(sk);
dev = ieee802154_get_dev(sock_net(sk), &addr->addr);
if (!dev) {
err = -ENODEV;
goto out;
}
if (dev->type != ARPHRD_IEEE802154) {
err = -ENODEV;
goto out_put;
}
sk->sk_bound_dev_if = dev->ifindex;
sk_dst_reset(sk);
out_put:
dev_put(dev);
out:
release_sock(sk);
return err;
}
static int raw_connect(struct sock *sk, struct sockaddr *uaddr,
int addr_len)
{
return -ENOTSUPP;
}
static int raw_disconnect(struct sock *sk, int flags)
{
return 0;
}
static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
size_t size)
{
struct net_device *dev;
unsigned mtu;
struct sk_buff *skb;
int err;
if (msg->msg_flags & MSG_OOB) {
pr_debug("msg->msg_flags = 0x%x\n", msg->msg_flags);
return -EOPNOTSUPP;
}
lock_sock(sk);
if (!sk->sk_bound_dev_if)
dev = dev_getfirstbyhwtype(sock_net(sk), ARPHRD_IEEE802154);
else
dev = dev_get_by_index(sock_net(sk), sk->sk_bound_dev_if);
release_sock(sk);
if (!dev) {
pr_debug("no dev\n");
err = -ENXIO;
goto out;
}
mtu = dev->mtu;
pr_debug("name = %s, mtu = %u\n", dev->name, mtu);
if (size > mtu) {
pr_debug("size = %Zu, mtu = %u\n", size, mtu);
err = -EINVAL;
goto out_dev;
}
skb = sock_alloc_send_skb(sk, LL_ALLOCATED_SPACE(dev) + size,
msg->msg_flags & MSG_DONTWAIT, &err);
if (!skb)
goto out_dev;
skb_reserve(skb, LL_RESERVED_SPACE(dev));
skb_reset_mac_header(skb);
skb_reset_network_header(skb);
err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);
if (err < 0)
goto out_skb;
skb->dev = dev;
skb->sk = sk;
skb->protocol = htons(ETH_P_IEEE802154);
dev_put(dev);
err = dev_queue_xmit(skb);
if (err > 0)
err = net_xmit_errno(err);
return err ?: size;
out_skb:
kfree_skb(skb);
out_dev:
dev_put(dev);
out:
return err;
}
static int raw_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
size_t len, int noblock, int flags, int *addr_len)
{
size_t copied = 0;
int err = -EOPNOTSUPP;
struct sk_buff *skb;
skb = skb_recv_datagram(sk, flags, noblock, &err);
if (!skb)
goto out;
copied = skb->len;
if (len < copied) {
msg->msg_flags |= MSG_TRUNC;
copied = len;
}
err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
if (err)
goto done;
sock_recv_timestamp(msg, sk, skb);
if (flags & MSG_TRUNC)
copied = skb->len;
done:
skb_free_datagram(sk, skb);
out:
if (err)
return err;
return copied;
}
static int raw_rcv_skb(struct sock *sk, struct sk_buff *skb)
{
if (sock_queue_rcv_skb(sk, skb) < 0) {
atomic_inc(&sk->sk_drops);
kfree_skb(skb);
return NET_RX_DROP;
}
return NET_RX_SUCCESS;
}
void ieee802154_raw_deliver(struct net_device *dev, struct sk_buff *skb)
{
struct sock *sk;
struct hlist_node *node;
read_lock(&raw_lock);
sk_for_each(sk, node, &raw_head) {
bh_lock_sock(sk);
if (!sk->sk_bound_dev_if ||
sk->sk_bound_dev_if == dev->ifindex) {
struct sk_buff *clone;
clone = skb_clone(skb, GFP_ATOMIC);
if (clone)
raw_rcv_skb(sk, clone);
}
bh_unlock_sock(sk);
}
read_unlock(&raw_lock);
}
static int raw_getsockopt(struct sock *sk, int level, int optname,
char __user *optval, int __user *optlen)
{
return -EOPNOTSUPP;
}
static int raw_setsockopt(struct sock *sk, int level, int optname,
char __user *optval, unsigned int optlen)
{
return -EOPNOTSUPP;
}
struct proto ieee802154_raw_prot = {
.name = "IEEE-802.15.4-RAW",
.owner = THIS_MODULE,
.obj_size = sizeof(struct sock),
.close = raw_close,
.bind = raw_bind,
.sendmsg = raw_sendmsg,
.recvmsg = raw_recvmsg,
.hash = raw_hash,
.unhash = raw_unhash,
.connect = raw_connect,
.disconnect = raw_disconnect,
.getsockopt = raw_getsockopt,
.setsockopt = raw_setsockopt,
};

View File

@@ -0,0 +1,159 @@
/*
* 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.
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <net/wpan-phy.h>
#define MASTER_SHOW_COMPLEX(name, format_string, args...) \
static ssize_t name ## _show(struct device *dev, \
struct device_attribute *attr, char *buf) \
{ \
struct wpan_phy *phy = container_of(dev, struct wpan_phy, dev); \
int ret; \
\
mutex_lock(&phy->pib_lock); \
ret = sprintf(buf, format_string "\n", args); \
mutex_unlock(&phy->pib_lock); \
return ret; \
}
#define MASTER_SHOW(field, format_string) \
MASTER_SHOW_COMPLEX(field, format_string, phy->field)
MASTER_SHOW(current_channel, "%d");
MASTER_SHOW(current_page, "%d");
MASTER_SHOW(channels_supported, "%#x");
MASTER_SHOW_COMPLEX(transmit_power, "%d +- %d dB",
((signed char) (phy->transmit_power << 2)) >> 2,
(phy->transmit_power >> 6) ? (phy->transmit_power >> 6) * 3 : 1 );
MASTER_SHOW(cca_mode, "%d");
static struct device_attribute pmib_attrs[] = {
__ATTR_RO(current_channel),
__ATTR_RO(current_page),
__ATTR_RO(channels_supported),
__ATTR_RO(transmit_power),
__ATTR_RO(cca_mode),
{},
};
static void wpan_phy_release(struct device *d)
{
struct wpan_phy *phy = container_of(d, struct wpan_phy, dev);
kfree(phy);
}
static struct class wpan_phy_class = {
.name = "ieee802154",
.dev_release = wpan_phy_release,
.dev_attrs = pmib_attrs,
};
static DEFINE_MUTEX(wpan_phy_mutex);
static int wpan_phy_idx;
static int wpan_phy_match(struct device *dev, void *data)
{
return !strcmp(dev_name(dev), (const char *)data);
}
struct wpan_phy *wpan_phy_find(const char *str)
{
struct device *dev;
if (WARN_ON(!str))
return NULL;
dev = class_find_device(&wpan_phy_class, NULL,
(void *)str, wpan_phy_match);
if (!dev)
return NULL;
return container_of(dev, struct wpan_phy, dev);
}
EXPORT_SYMBOL(wpan_phy_find);
static int wpan_phy_idx_valid(int idx)
{
return idx >= 0;
}
struct wpan_phy *wpan_phy_alloc(size_t priv_size)
{
struct wpan_phy *phy = kzalloc(sizeof(*phy) + priv_size,
GFP_KERNEL);
mutex_lock(&wpan_phy_mutex);
phy->idx = wpan_phy_idx++;
if (unlikely(!wpan_phy_idx_valid(phy->idx))) {
wpan_phy_idx--;
mutex_unlock(&wpan_phy_mutex);
kfree(phy);
return NULL;
}
mutex_unlock(&wpan_phy_mutex);
mutex_init(&phy->pib_lock);
device_initialize(&phy->dev);
dev_set_name(&phy->dev, "wpan-phy%d", phy->idx);
phy->dev.class = &wpan_phy_class;
return phy;
}
EXPORT_SYMBOL(wpan_phy_alloc);
int wpan_phy_register(struct device *parent, struct wpan_phy *phy)
{
phy->dev.parent = parent;
return device_add(&phy->dev);
}
EXPORT_SYMBOL(wpan_phy_register);
void wpan_phy_unregister(struct wpan_phy *phy)
{
device_del(&phy->dev);
}
EXPORT_SYMBOL(wpan_phy_unregister);
void wpan_phy_free(struct wpan_phy *phy)
{
put_device(&phy->dev);
}
EXPORT_SYMBOL(wpan_phy_free);
static int __init wpan_phy_class_init(void)
{
return class_register(&wpan_phy_class);
}
subsys_initcall(wpan_phy_class_init);
static void __exit wpan_phy_class_exit(void)
{
class_unregister(&wpan_phy_class);
}
module_exit(wpan_phy_class_exit);
MODULE_DESCRIPTION("IEEE 802.15.4 device class");
MODULE_LICENSE("GPL v2");