M7350v1_en_gpl

This commit is contained in:
T
2024-09-09 08:52:07 +00:00
commit f9cc65cfda
65988 changed files with 26357421 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,235 @@
/*
* Copyright 2011, Siemens AG
* written by Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
*/
/*
* Based on patches from Jon Smirl <jonsmirl@gmail.com>
* Copyright (c) 2011 Jon Smirl <jonsmirl@gmail.com>
*
* 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.
*/
/* Jon's code is based on 6lowpan implementation for Contiki which is:
* Copyright (c) 2008, Swedish Institute of Computer Science.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef __6LOWPAN_H__
#define __6LOWPAN_H__
/* need to know address length to manipulate with it */
#define IEEE802154_ALEN 8
#define UIP_802154_SHORTADDR_LEN 2 /* compressed ipv6 address length */
#define UIP_IPH_LEN 40 /* ipv6 fixed header size */
#define UIP_PROTO_UDP 17 /* ipv6 next header value for UDP */
#define UIP_FRAGH_LEN 8 /* ipv6 fragment header size */
/*
* ipv6 address based on mac
* second bit-flip (Universe/Local) is done according RFC2464
*/
#define is_addr_mac_addr_based(a, m) \
((((a)->s6_addr[8]) == (((m)[0]) ^ 0x02)) && \
(((a)->s6_addr[9]) == (m)[1]) && \
(((a)->s6_addr[10]) == (m)[2]) && \
(((a)->s6_addr[11]) == (m)[3]) && \
(((a)->s6_addr[12]) == (m)[4]) && \
(((a)->s6_addr[13]) == (m)[5]) && \
(((a)->s6_addr[14]) == (m)[6]) && \
(((a)->s6_addr[15]) == (m)[7]))
/* ipv6 address is unspecified */
#define is_addr_unspecified(a) \
((((a)->s6_addr32[0]) == 0) && \
(((a)->s6_addr32[1]) == 0) && \
(((a)->s6_addr32[2]) == 0) && \
(((a)->s6_addr32[3]) == 0))
/* compare ipv6 addresses prefixes */
#define ipaddr_prefixcmp(addr1, addr2, length) \
(memcmp(addr1, addr2, length >> 3) == 0)
/* local link, i.e. FE80::/10 */
#define is_addr_link_local(a) (((a)->s6_addr16[0]) == 0x80FE)
/*
* check whether we can compress the IID to 16 bits,
* it's possible for unicast adresses with first 49 bits are zero only.
*/
#define lowpan_is_iid_16_bit_compressable(a) \
((((a)->s6_addr16[4]) == 0) && \
(((a)->s6_addr16[5]) == 0) && \
(((a)->s6_addr16[6]) == 0) && \
((((a)->s6_addr[14]) & 0x80) == 0))
/* multicast address */
#define is_addr_mcast(a) (((a)->s6_addr[0]) == 0xFF)
/* check whether the 112-bit gid of the multicast address is mappable to: */
/* 9 bits, for FF02::1 (all nodes) and FF02::2 (all routers) addresses only. */
#define lowpan_is_mcast_addr_compressable(a) \
((((a)->s6_addr16[1]) == 0) && \
(((a)->s6_addr16[2]) == 0) && \
(((a)->s6_addr16[3]) == 0) && \
(((a)->s6_addr16[4]) == 0) && \
(((a)->s6_addr16[5]) == 0) && \
(((a)->s6_addr16[6]) == 0) && \
(((a)->s6_addr[14]) == 0) && \
((((a)->s6_addr[15]) == 1) || (((a)->s6_addr[15]) == 2)))
/* 48 bits, FFXX::00XX:XXXX:XXXX */
#define lowpan_is_mcast_addr_compressable48(a) \
((((a)->s6_addr16[1]) == 0) && \
(((a)->s6_addr16[2]) == 0) && \
(((a)->s6_addr16[3]) == 0) && \
(((a)->s6_addr16[4]) == 0) && \
(((a)->s6_addr[10]) == 0))
/* 32 bits, FFXX::00XX:XXXX */
#define lowpan_is_mcast_addr_compressable32(a) \
((((a)->s6_addr16[1]) == 0) && \
(((a)->s6_addr16[2]) == 0) && \
(((a)->s6_addr16[3]) == 0) && \
(((a)->s6_addr16[4]) == 0) && \
(((a)->s6_addr16[5]) == 0) && \
(((a)->s6_addr[12]) == 0))
/* 8 bits, FF02::00XX */
#define lowpan_is_mcast_addr_compressable8(a) \
((((a)->s6_addr[1]) == 2) && \
(((a)->s6_addr16[1]) == 0) && \
(((a)->s6_addr16[2]) == 0) && \
(((a)->s6_addr16[3]) == 0) && \
(((a)->s6_addr16[4]) == 0) && \
(((a)->s6_addr16[5]) == 0) && \
(((a)->s6_addr16[6]) == 0) && \
(((a)->s6_addr[14]) == 0))
#define lowpan_is_addr_broadcast(a) \
((((a)[0]) == 0xFF) && \
(((a)[1]) == 0xFF) && \
(((a)[2]) == 0xFF) && \
(((a)[3]) == 0xFF) && \
(((a)[4]) == 0xFF) && \
(((a)[5]) == 0xFF) && \
(((a)[6]) == 0xFF) && \
(((a)[7]) == 0xFF))
#define LOWPAN_DISPATCH_IPV6 0x41 /* 01000001 = 65 */
#define LOWPAN_DISPATCH_HC1 0x42 /* 01000010 = 66 */
#define LOWPAN_DISPATCH_IPHC 0x60 /* 011xxxxx = ... */
#define LOWPAN_DISPATCH_FRAG1 0xc0 /* 11000xxx */
#define LOWPAN_DISPATCH_FRAGN 0xe0 /* 11100xxx */
#define LOWPAN_DISPATCH_MASK 0xf8 /* 11111000 */
#define LOWPAN_FRAG_TIMEOUT (HZ * 60) /* time-out 60 sec */
#define LOWPAN_FRAG1_HEAD_SIZE 0x4
#define LOWPAN_FRAGN_HEAD_SIZE 0x5
/*
* According IEEE802.15.4 standard:
* - MTU is 127 octets
* - maximum MHR size is 37 octets
* - MFR size is 2 octets
*
* so minimal payload size that we may guarantee is:
* MTU - MHR - MFR = 88 octets
*/
#define LOWPAN_FRAG_SIZE 88
/*
* Values of fields within the IPHC encoding first byte
* (C stands for compressed and I for inline)
*/
#define LOWPAN_IPHC_TF 0x18
#define LOWPAN_IPHC_FL_C 0x10
#define LOWPAN_IPHC_TC_C 0x08
#define LOWPAN_IPHC_NH_C 0x04
#define LOWPAN_IPHC_TTL_1 0x01
#define LOWPAN_IPHC_TTL_64 0x02
#define LOWPAN_IPHC_TTL_255 0x03
#define LOWPAN_IPHC_TTL_I 0x00
/* Values of fields within the IPHC encoding second byte */
#define LOWPAN_IPHC_CID 0x80
#define LOWPAN_IPHC_SAC 0x40
#define LOWPAN_IPHC_SAM_00 0x00
#define LOWPAN_IPHC_SAM_01 0x10
#define LOWPAN_IPHC_SAM_10 0x20
#define LOWPAN_IPHC_SAM 0x30
#define LOWPAN_IPHC_SAM_BIT 4
#define LOWPAN_IPHC_M 0x08
#define LOWPAN_IPHC_DAC 0x04
#define LOWPAN_IPHC_DAM_00 0x00
#define LOWPAN_IPHC_DAM_01 0x01
#define LOWPAN_IPHC_DAM_10 0x02
#define LOWPAN_IPHC_DAM_11 0x03
#define LOWPAN_IPHC_DAM_BIT 0
/*
* LOWPAN_UDP encoding (works together with IPHC)
*/
#define LOWPAN_NHC_UDP_MASK 0xF8
#define LOWPAN_NHC_UDP_ID 0xF0
#define LOWPAN_NHC_UDP_CHECKSUMC 0x04
#define LOWPAN_NHC_UDP_CHECKSUMI 0x00
#define LOWPAN_NHC_UDP_4BIT_PORT 0xF0B0
#define LOWPAN_NHC_UDP_4BIT_MASK 0xFFF0
#define LOWPAN_NHC_UDP_8BIT_PORT 0xF000
#define LOWPAN_NHC_UDP_8BIT_MASK 0xFF00
/* values for port compression, _with checksum_ ie bit 5 set to 0 */
#define LOWPAN_NHC_UDP_CS_P_00 0xF0 /* all inline */
#define LOWPAN_NHC_UDP_CS_P_01 0xF1 /* source 16bit inline,
dest = 0xF0 + 8 bit inline */
#define LOWPAN_NHC_UDP_CS_P_10 0xF2 /* source = 0xF0 + 8bit inline,
dest = 16 bit inline */
#define LOWPAN_NHC_UDP_CS_P_11 0xF3 /* source & dest = 0xF0B + 4bit inline */
#endif /* __6LOWPAN_H__ */

View File

@@ -0,0 +1,18 @@
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.
config IEEE802154_6LOWPAN
tristate "6lowpan support over IEEE 802.15.4"
depends on IEEE802154 && IPV6
---help---
IPv6 compression over IEEE 802.15.4.

View File

@@ -0,0 +1,5 @@
obj-$(CONFIG_IEEE802154) += ieee802154.o af_802154.o
obj-$(CONFIG_IEEE802154_6LOWPAN) += 6lowpan.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

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,373 @@
/*
* 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 <linux/slab.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:
rcu_read_lock();
dev = dev_getbyhwaddr_rcu(net, ARPHRD_IEEE802154, addr->hwaddr);
if (dev)
dev_hold(dev);
rcu_read_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 (addr_len < sizeof(uaddr->sa_family))
return -EINVAL;
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)
return -ENODEV;
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, int kern)
{
struct sock *sk;
int rc;
struct proto *proto;
const struct proto_ops *ops;
if (!net_eq(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 const 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))
goto drop;
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,464 @@
/*
* IEEE 802.15.4 dgram 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 <linux/slab.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 hlen, tlen;
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);
hlen = LL_RESERVED_SPACE(dev);
tlen = dev->needed_tailroom;
skb = sock_alloc_send_skb(sk, hlen + tlen + size,
msg->msg_flags & MSG_DONTWAIT,
&err);
if (!skb)
goto out_dev;
skb_reserve(skb, hlen);
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_ts_and_drops(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) {
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,53 @@
/*
* 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

View File

@@ -0,0 +1,139 @@
/*
* 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/gfp.h>
#include <net/genetlink.h>
#include <linux/nl802154.h>
#include "ieee802154.h"
static unsigned int ieee802154_seq_num;
static DEFINE_SPINLOCK(ieee802154_seq_lock);
struct genl_family nl802154_family = {
.id = GENL_ID_GENERATE,
.hdrsize = 0,
.name = IEEE802154_NL_NAME,
.version = 1,
.maxattr = IEEE802154_ATTR_MAX,
};
/* Requests to userspace */
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++,
&nl802154_family, flags, req);
spin_unlock_irqrestore(&ieee802154_seq_lock, f);
if (!hdr) {
nlmsg_free(msg);
return NULL;
}
return msg;
}
int ieee802154_nl_mcast(struct sk_buff *msg, unsigned int group)
{
/* 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, group, GFP_ATOMIC);
out:
nlmsg_free(msg);
return -ENOBUFS;
}
struct sk_buff *ieee802154_nl_new_reply(struct genl_info *info,
int flags, u8 req)
{
void *hdr;
struct sk_buff *msg = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
if (!msg)
return NULL;
hdr = genlmsg_put_reply(msg, info,
&nl802154_family, flags, req);
if (!hdr) {
nlmsg_free(msg);
return NULL;
}
return msg;
}
int ieee802154_nl_reply(struct sk_buff *msg, struct genl_info *info)
{
/* 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_reply(msg, info);
out:
nlmsg_free(msg);
return -ENOBUFS;
}
int __init ieee802154_nl_init(void)
{
int rc;
rc = genl_register_family(&nl802154_family);
if (rc)
goto fail;
rc = nl802154_mac_register();
if (rc)
goto fail;
rc = nl802154_phy_register();
if (rc)
goto fail;
return 0;
fail:
genl_unregister_family(&nl802154_family);
return rc;
}
void __exit ieee802154_nl_exit(void)
{
genl_unregister_family(&nl802154_family);
}

View File

@@ -0,0 +1,619 @@
/*
* 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/gfp.h>
#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 <linux/export.h>
#include <net/af_ieee802154.h>
#include <net/nl802154.h>
#include <net/ieee802154.h>
#include <net/ieee802154_netdev.h>
#include <net/wpan-phy.h>
#include "ieee802154.h"
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,
};
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_mcast(msg, ieee802154_coord_mcgrp.id);
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_mcast(msg, ieee802154_coord_mcgrp.id);
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_mcast(msg, ieee802154_coord_mcgrp.id);
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_mcast(msg, ieee802154_coord_mcgrp.id);
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_mcast(msg, ieee802154_coord_mcgrp.id);
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_mcast(msg, ieee802154_coord_mcgrp.id);
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_mcast(msg, ieee802154_coord_mcgrp.id);
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;
struct wpan_phy *phy;
pr_debug("%s\n", __func__);
hdr = genlmsg_put(msg, 0, seq, &nl802154_family, flags,
IEEE802154_LIST_IFACE);
if (!hdr)
goto out;
phy = ieee802154_mlme_ops(dev)->get_phy(dev);
BUG_ON(!phy);
NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name);
NLA_PUT_STRING(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy));
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));
wpan_phy_put(phy);
return genlmsg_end(msg, hdr);
nla_put_failure:
wpan_phy_put(phy);
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_reply(msg, info);
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;
}
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),
};
/*
* No need to unregister as family unregistration will do it.
*/
int nl802154_mac_register(void)
{
int i;
int rc;
rc = genl_register_mc_group(&nl802154_family,
&ieee802154_coord_mcgrp);
if (rc)
return rc;
rc = genl_register_mc_group(&nl802154_family,
&ieee802154_beacon_mcgrp);
if (rc)
return rc;
for (i = 0; i < ARRAY_SIZE(ieee802154_coordinator_ops); i++) {
rc = genl_register_ops(&nl802154_family,
&ieee802154_coordinator_ops[i]);
if (rc)
return rc;
}
return 0;
}

View File

@@ -0,0 +1,377 @@
/*
* 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/slab.h>
#include <linux/if_arp.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)
return -EMSGSIZE;
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);
kfree(buf);
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;
}
if (info->attrs[IEEE802154_ATTR_HW_ADDR] &&
nla_len(info->attrs[IEEE802154_ATTR_HW_ADDR]) !=
IEEE802154_ADDR_LEN) {
rc = -EINVAL;
goto nla_put_failure;
}
dev = phy->add_iface(phy, devname);
if (IS_ERR(dev)) {
rc = PTR_ERR(dev);
goto nla_put_failure;
}
if (info->attrs[IEEE802154_ATTR_HW_ADDR]) {
struct sockaddr addr;
addr.sa_family = ARPHRD_IEEE802154;
nla_memcpy(&addr.sa_data, info->attrs[IEEE802154_ATTR_HW_ADDR],
IEEE802154_ADDR_LEN);
/*
* strangely enough, some callbacks (inetdev_event) from
* dev_set_mac_address require RTNL_LOCK
*/
rtnl_lock();
rc = dev_set_mac_address(dev, &addr);
rtnl_unlock();
if (rc)
goto dev_unregister;
}
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);
dev_unregister:
rtnl_lock(); /* del_iface must be called with RTNL lock */
phy->del_iface(phy, dev);
dev_put(dev);
rtnl_unlock();
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;
}

View File

@@ -0,0 +1,56 @@
/*
* 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_PHY_NAME] = { .type = NLA_STRING, },
[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 },
[IEEE802154_ATTR_CHANNEL_PAGE_LIST] = { .len = 32 * 4, },
};

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

@@ -0,0 +1,270 @@
/*
* 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 <linux/slab.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 hlen, tlen;
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;
}
hlen = LL_RESERVED_SPACE(dev);
tlen = dev->needed_tailroom;
skb = sock_alloc_send_skb(sk, hlen + tlen + size,
msg->msg_flags & MSG_DONTWAIT, &err);
if (!skb)
goto out_dev;
skb_reserve(skb, hlen);
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_ts_and_drops(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) {
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,226 @@
/*
* 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/slab.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <net/wpan-phy.h>
#include "ieee802154.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 = snprintf(buf, PAGE_SIZE, 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_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 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[] = {
__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);
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)
{
return idx >= 0;
}
struct wpan_phy *wpan_phy_alloc(size_t priv_size)
{
struct wpan_phy *phy = kzalloc(sizeof(*phy) + priv_size,
GFP_KERNEL);
if (!phy)
goto out;
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);
goto out;
}
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;
phy->current_channel = -1; /* not initialised */
phy->current_page = 0; /* for compatibility */
return phy;
out:
return NULL;
}
EXPORT_SYMBOL(wpan_phy_alloc);
int wpan_phy_register(struct wpan_phy *phy)
{
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)
{
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);
static void __exit wpan_phy_class_exit(void)
{
ieee802154_nl_exit();
class_unregister(&wpan_phy_class);
}
module_exit(wpan_phy_class_exit);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("IEEE 802.15.4 configuration interface");
MODULE_AUTHOR("Dmitry Eremin-Solenikov");