Base support for wpan device. By wpan device it means any IEEE 802.15.4 compatible transciever. This code attaches wpan device to networking stack.
Signed-off-by: Alexander Smirnov <alex.bluesman.smir...@gmail.com> --- include/linux/nl802154.h | 6 + include/net/wpan-phy.h | 2 + net/mac802154/Makefile | 2 +- net/mac802154/ieee802154_dev.c | 5 +- net/mac802154/mac802154.h | 17 ++ net/mac802154/rx.c | 1 + net/mac802154/wpan.c | 613 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 644 insertions(+), 2 deletions(-) create mode 100644 net/mac802154/wpan.c diff --git a/include/linux/nl802154.h b/include/linux/nl802154.h index d2b5ae2..2c3b1cf 100644 --- a/include/linux/nl802154.h +++ b/include/linux/nl802154.h @@ -124,4 +124,10 @@ enum { #define IEEE802154_CMD_MAX (__IEEE802154_CMD_MAX - 1) +enum { + IEEE802154_DEV_WPAN, + + __IEEE802154_DEV_MAX, +}; + #endif diff --git a/include/net/wpan-phy.h b/include/net/wpan-phy.h index 0c2c931..469e0e6 100644 --- a/include/net/wpan-phy.h +++ b/include/net/wpan-phy.h @@ -24,6 +24,8 @@ #include <linux/netdevice.h> #include <linux/mutex.h> +#define IEEE802154_WPAN_NUM_PAGES 32 + struct wpan_phy { struct mutex pib_lock; diff --git a/net/mac802154/Makefile b/net/mac802154/Makefile index 50e2569..082eee7 100644 --- a/net/mac802154/Makefile +++ b/net/mac802154/Makefile @@ -1,2 +1,2 @@ obj-$(CONFIG_MAC802154) += mac802154.o -mac802154-objs := ieee802154_dev.o rx.o tx.o mib.o mac_cmd.o +mac802154-objs := ieee802154_dev.o rx.o tx.o mib.o mac_cmd.o wpan.o diff --git a/net/mac802154/ieee802154_dev.c b/net/mac802154/ieee802154_dev.c index 8fe1a9b..89731be 100644 --- a/net/mac802154/ieee802154_dev.c +++ b/net/mac802154/ieee802154_dev.c @@ -130,8 +130,11 @@ static struct net_device *mac802154_add_iface(struct wpan_phy *phy, struct net_device *dev; int err = -ENOMEM; - /* No devices is currently supported */ switch (type) { + case IEEE802154_DEV_WPAN: + dev = alloc_netdev(sizeof(struct mac802154_sub_if_data), + name, mac802154_wpan_setup); + break; default: dev = NULL; err = -EINVAL; diff --git a/net/mac802154/mac802154.h b/net/mac802154/mac802154.h index 752ae6a..43ef561 100644 --- a/net/mac802154/mac802154.h +++ b/net/mac802154/mac802154.h @@ -23,6 +23,12 @@ #ifndef MAC802154_H #define MAC802154_H +/* + * FC SeqNum DPAN DA SA Sec + * 2 + 1 + 2 + 8 + 8 + 14 = 37 + */ +#define MAC802154_MAX_MHR_SIZE 37 + struct mac802154_priv { struct ieee802154_dev hw; struct ieee802154_ops *ops; @@ -75,4 +81,15 @@ struct mac802154_sub_if_data { u8 dsn; }; +extern struct ieee802154_mlme_ops mac802154_mlme_wpan; + +void mac802154_wpan_setup(struct net_device *dev); + +void mac802154_wpans_rx(struct mac802154_priv *priv, struct sk_buff *skb); +netdev_tx_t mac802154_tx(struct mac802154_priv *priv, struct sk_buff *skb, + u8 page, u8 chan); +int mac802154_slave_open(struct net_device *dev); +int mac802154_slave_close(struct net_device *dev); +int mac802154_process_cmd(struct net_device *dev, struct sk_buff *skb); + #endif /* MAC802154_H */ diff --git a/net/mac802154/rx.c b/net/mac802154/rx.c index 1642c48..21af50a 100644 --- a/net/mac802154/rx.c +++ b/net/mac802154/rx.c @@ -59,6 +59,7 @@ mac802154_subif_rx(struct ieee802154_dev *hw, struct sk_buff *skb, u8 lqi) } skb_trim(skb, skb->len - 2); /* CRC */ } + mac802154_wpans_rx(priv, skb); out: dev_kfree_skb(skb); diff --git a/net/mac802154/wpan.c b/net/mac802154/wpan.c new file mode 100644 index 0000000..b8648bc --- /dev/null +++ b/net/mac802154/wpan.c @@ -0,0 +1,613 @@ +/* + * 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: + * Dmitry Eremin-Solenikov <dbarysh...@gmail.com> + * Sergey Lapin <sla...@ossfans.org> + * Maxim Gorbachyov <maxim.gorbac...@siemens.com> + * Alexander Smirnov <alex.bluesman.smir...@gmail.com> + */ + +#include <linux/netdevice.h> +#include <linux/capability.h> +#include <linux/module.h> +#include <linux/if_arp.h> +#include <linux/rculist.h> +#include <linux/random.h> +#include <linux/crc-ccitt.h> +#include <linux/nl802154.h> +#include <linux/interrupt.h> + +#include <net/rtnetlink.h> +#include <net/af_ieee802154.h> +#include <net/mac802154.h> +#include <net/ieee802154_netdev.h> +#include <net/ieee802154.h> +#include <net/wpan-phy.h> + +#include "mac802154.h" +#include "mib.h" + +static netdev_tx_t mac802154_wpan_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct mac802154_sub_if_data *priv; + u8 chan, page; + + priv = netdev_priv(dev); + + spin_lock_bh(&priv->mib_lock); + chan = priv->chan; + page = priv->page; + spin_unlock_bh(&priv->mib_lock); + + if (chan == (u8)-1 || chan >= 27 || page >= IEEE802154_WPAN_NUM_PAGES) + return NETDEV_TX_OK; + + skb->skb_iif = dev->ifindex; + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; + + return mac802154_tx(priv->hw, skb, page, chan); +} + +static int mac802154_wpan_ioctl(struct net_device *dev, struct ifreq *ifr, + int cmd) +{ + struct mac802154_sub_if_data *priv = netdev_priv(dev); + struct sockaddr_ieee802154 *sa = + (struct sockaddr_ieee802154 *)&ifr->ifr_addr; + int err = -ENOIOCTLCMD; + + spin_lock_bh(&priv->mib_lock); + + switch (cmd) { + case SIOCGIFADDR: + if (priv->pan_id == IEEE802154_PANID_BROADCAST || + priv->short_addr == IEEE802154_ADDR_BROADCAST) { + err = -EADDRNOTAVAIL; + break; + } + + sa->family = AF_IEEE802154; + sa->addr.addr_type = IEEE802154_ADDR_SHORT; + sa->addr.pan_id = priv->pan_id; + sa->addr.short_addr = priv->short_addr; + + err = 0; + break; + case SIOCSIFADDR: + dev_warn(&dev->dev, + "Using DEBUGing ioctl SIOCSIFADDR isn't recommened!\n"); + if (sa->family != AF_IEEE802154 || + sa->addr.addr_type != IEEE802154_ADDR_SHORT || + sa->addr.pan_id == IEEE802154_PANID_BROADCAST || + sa->addr.short_addr == IEEE802154_ADDR_BROADCAST || + sa->addr.short_addr == IEEE802154_ADDR_UNDEF) { + err = -EINVAL; + break; + } + + priv->pan_id = sa->addr.pan_id; + priv->short_addr = sa->addr.short_addr; + err = 0; + break; + } + spin_unlock_bh(&priv->mib_lock); + return err; +} + +static int mac802154_wpan_mac_addr(struct net_device *dev, void *p) +{ + struct sockaddr *addr = p; + + if (netif_running(dev)) + return -EBUSY; + + /* FIXME: validate addr */ + memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); + mac802154_dev_set_ieee_addr(dev); + + return 0; +} + +static inline void mac802154_haddr_copy_swap(u8 *dest, const u8 *src) +{ + int i; + for (i = 0; i < IEEE802154_ADDR_LEN; i++) + dest[IEEE802154_ADDR_LEN - i - 1] = src[i]; +} + +static int mac802154_header_create(struct sk_buff *skb, + struct net_device *dev, + unsigned short type, const void *_daddr, + const void *_saddr, unsigned len) +{ + u8 head[MAC802154_MAX_MHR_SIZE] = {}; + int pos = 2; + + u16 fc; + const struct ieee802154_addr *saddr = _saddr; + const struct ieee802154_addr *daddr = _daddr; + struct ieee802154_addr dev_addr; + struct mac802154_sub_if_data *priv = netdev_priv(dev); + + fc = mac_cb_type(skb); + if (mac_cb_is_ackreq(skb)) + fc |= IEEE802154_FC_ACK_REQ; + + head[pos++] = mac_cb(skb)->seq; /* DSN/BSN */ + + if (!daddr) + return -EINVAL; + + if (!saddr) { + spin_lock_bh(&priv->mib_lock); + if (priv->short_addr == IEEE802154_ADDR_BROADCAST || + priv->short_addr == IEEE802154_ADDR_UNDEF || + priv->pan_id == IEEE802154_PANID_BROADCAST) { + dev_addr.addr_type = IEEE802154_ADDR_LONG; + memcpy(dev_addr.hwaddr, dev->dev_addr, + IEEE802154_ADDR_LEN); + } else { + dev_addr.addr_type = IEEE802154_ADDR_SHORT; + dev_addr.short_addr = priv->short_addr; + } + + dev_addr.pan_id = priv->pan_id; + saddr = &dev_addr; + + spin_unlock_bh(&priv->mib_lock); + } + + if (daddr->addr_type != IEEE802154_ADDR_NONE) { + fc |= (daddr->addr_type << IEEE802154_FC_DAMODE_SHIFT); + + head[pos++] = daddr->pan_id & 0xff; + head[pos++] = daddr->pan_id >> 8; + + if (daddr->addr_type == IEEE802154_ADDR_SHORT) { + head[pos++] = daddr->short_addr & 0xff; + head[pos++] = daddr->short_addr >> 8; + } else { + mac802154_haddr_copy_swap(head + pos, daddr->hwaddr); + pos += IEEE802154_ADDR_LEN; + } + } + + if (saddr->addr_type != IEEE802154_ADDR_NONE) { + fc |= (saddr->addr_type << IEEE802154_FC_SAMODE_SHIFT); + + if ((saddr->pan_id == daddr->pan_id) && + (saddr->pan_id != IEEE802154_PANID_BROADCAST)) + /* PANID compression/ intra PAN */ + fc |= IEEE802154_FC_INTRA_PAN; + else { + head[pos++] = saddr->pan_id & 0xff; + head[pos++] = saddr->pan_id >> 8; + } + + if (saddr->addr_type == IEEE802154_ADDR_SHORT) { + head[pos++] = saddr->short_addr & 0xff; + head[pos++] = saddr->short_addr >> 8; + } else { + mac802154_haddr_copy_swap(head + pos, saddr->hwaddr); + pos += IEEE802154_ADDR_LEN; + } + } + + head[0] = fc; + head[1] = fc >> 8; + + memcpy(skb_push(skb, pos), head, pos); + + return pos; +} + +static int mac802154_header_parse(const struct sk_buff *skb, + unsigned char *haddr) +{ + const u8 *hdr = skb_mac_header(skb), *tail = skb_tail_pointer(skb); + struct ieee802154_addr *addr = (struct ieee802154_addr *)haddr; + u16 fc; + int da_type; + + if (hdr + 3 > tail) + goto malformed; + + fc = hdr[0] | (hdr[1] << 8); + + hdr += 3; + + da_type = IEEE802154_FC_DAMODE(fc); + addr->addr_type = IEEE802154_FC_SAMODE(fc); + + switch (da_type) { + case IEEE802154_ADDR_NONE: + if (fc & IEEE802154_FC_INTRA_PAN) + goto malformed; + break; + + case IEEE802154_ADDR_LONG: + if (hdr + 2 > tail) + goto malformed; + if (fc & IEEE802154_FC_INTRA_PAN) { + addr->pan_id = hdr[0] | (hdr[1] << 8); + hdr += 2; + } + + if (hdr + IEEE802154_ADDR_LEN > tail) + goto malformed; + hdr += IEEE802154_ADDR_LEN; + break; + + case IEEE802154_ADDR_SHORT: + if (hdr + 2 > tail) + goto malformed; + if (fc & IEEE802154_FC_INTRA_PAN) { + addr->pan_id = hdr[0] | (hdr[1] << 8); + hdr += 2; + } + + if (hdr + 2 > tail) + goto malformed; + hdr += 2; + break; + + default: + goto malformed; + + } + + switch (addr->addr_type) { + case IEEE802154_ADDR_NONE: + break; + + case IEEE802154_ADDR_LONG: + if (hdr + 2 > tail) + goto malformed; + if (!(fc & IEEE802154_FC_INTRA_PAN)) { + addr->pan_id = hdr[0] | (hdr[1] << 8); + hdr += 2; + } + + if (hdr + IEEE802154_ADDR_LEN > tail) + goto malformed; + mac802154_haddr_copy_swap(addr->hwaddr, hdr); + hdr += IEEE802154_ADDR_LEN; + break; + + case IEEE802154_ADDR_SHORT: + if (hdr + 2 > tail) + goto malformed; + if (!(fc & IEEE802154_FC_INTRA_PAN)) { + addr->pan_id = hdr[0] | (hdr[1] << 8); + hdr += 2; + } + + if (hdr + 2 > tail) + goto malformed; + addr->short_addr = hdr[0] | (hdr[1] << 8); + hdr += 2; + break; + + default: + goto malformed; + + } + + return sizeof(struct ieee802154_addr); + +malformed: + pr_debug("(%s): malformed packet\n", __func__); + return 0; +} + +static struct header_ops mac802154_header_ops = { + .create = mac802154_header_create, + .parse = mac802154_header_parse, +}; + +static const struct net_device_ops mac802154_wpan_ops = { + .ndo_open = mac802154_slave_open, + .ndo_stop = mac802154_slave_close, + .ndo_start_xmit = mac802154_wpan_xmit, + .ndo_do_ioctl = mac802154_wpan_ioctl, + .ndo_set_mac_address = mac802154_wpan_mac_addr, +}; + +void mac802154_wpan_setup(struct net_device *dev) +{ + struct mac802154_sub_if_data *priv; + + dev->addr_len = IEEE802154_ADDR_LEN; + memset(dev->broadcast, 0xff, IEEE802154_ADDR_LEN); + dev->hard_header_len = MAC802154_MAX_MHR_SIZE; + dev->header_ops = &mac802154_header_ops; + dev->needed_tailroom = 2; /* FCS */ + dev->mtu = IEEE802154_MTU; + dev->tx_queue_len = 10; + dev->type = ARPHRD_IEEE802154; + dev->flags = IFF_NOARP | IFF_BROADCAST; + dev->watchdog_timeo = 0; + + dev->destructor = free_netdev; + dev->netdev_ops = &mac802154_wpan_ops; + dev->ml_priv = &mac802154_mlme_wpan.wpan_ops; + + priv = netdev_priv(dev); + priv->type = IEEE802154_DEV_WPAN; + + priv->chan = -1; /* not initialized */ + priv->page = 0; /* for compat */ + + spin_lock_init(&priv->mib_lock); + + get_random_bytes(&priv->bsn, 1); + get_random_bytes(&priv->dsn, 1); + + priv->pan_id = IEEE802154_PANID_BROADCAST; + priv->short_addr = IEEE802154_ADDR_BROADCAST; +} + +static int mac802154_process_data(struct net_device *dev, struct sk_buff *skb) +{ + if (in_interrupt()) + return netif_rx(skb); + else + return netif_rx_ni(skb); +} + +static int mac802154_subif_frame(struct mac802154_sub_if_data *sdata, + struct sk_buff *skb) +{ + pr_debug("(%s): getting packet via slave interface %s\n", + __func__, sdata->dev->name); + + spin_lock_bh(&sdata->mib_lock); + + switch (mac_cb(skb)->da.addr_type) { + case IEEE802154_ADDR_NONE: + if (mac_cb(skb)->sa.addr_type != IEEE802154_ADDR_NONE) + /* FIXME: check if we are PAN coordinator :) */ + skb->pkt_type = PACKET_OTHERHOST; + else + /* ACK comes with both addresses empty */ + skb->pkt_type = PACKET_HOST; + break; + case IEEE802154_ADDR_LONG: + if (mac_cb(skb)->da.pan_id != sdata->pan_id && + mac_cb(skb)->da.pan_id != IEEE802154_PANID_BROADCAST) + skb->pkt_type = PACKET_OTHERHOST; + else if (!memcmp(mac_cb(skb)->da.hwaddr, sdata->dev->dev_addr, + IEEE802154_ADDR_LEN)) + skb->pkt_type = PACKET_HOST; + else + skb->pkt_type = PACKET_OTHERHOST; + break; + case IEEE802154_ADDR_SHORT: + if (mac_cb(skb)->da.pan_id != sdata->pan_id && + mac_cb(skb)->da.pan_id != IEEE802154_PANID_BROADCAST) + skb->pkt_type = PACKET_OTHERHOST; + else if (mac_cb(skb)->da.short_addr == + IEEE802154_ADDR_BROADCAST) + skb->pkt_type = PACKET_BROADCAST; + else if (mac_cb(skb)->da.short_addr == sdata->short_addr) + skb->pkt_type = PACKET_HOST; + else + skb->pkt_type = PACKET_OTHERHOST; + break; + } + + spin_unlock_bh(&sdata->mib_lock); + + skb->dev = sdata->dev; + + sdata->dev->stats.rx_packets++; + sdata->dev->stats.rx_bytes += skb->len; + + if (skb->pkt_type == PACKET_HOST && mac_cb_is_ackreq(skb) && + !(sdata->hw->hw.flags & IEEE802154_HW_AACK)) + dev_warn(&sdata->dev->dev, + "ACK requested, however AACK not supported.\n"); + + switch (mac_cb_type(skb)) { + case IEEE802154_FC_TYPE_DATA: + return mac802154_process_data(sdata->dev, skb); + case IEEE802154_FC_TYPE_BEACON: + case IEEE802154_FC_TYPE_ACK: + case IEEE802154_FC_TYPE_MAC_CMD: + return mac802154_process_cmd(sdata->dev, skb); + default: + pr_warning("ieee802154: bad frame received (type = %d)\n", + mac_cb_type(skb)); + kfree_skb(skb); + return NET_RX_DROP; + } +} + +static int mac802154_fetch_skb_u8(struct sk_buff *skb, u8 *dest) +{ + if (!pskb_may_pull(skb, 1)) + return -EINVAL; + + *dest = skb->data[0]; + skb_pull(skb, 1); + + return 0; +} + +static int mac802154_fetch_skb_u16(struct sk_buff *skb, u16 *dest) +{ + if (!pskb_may_pull(skb, 2)) + return -EINVAL; + + *dest = skb->data[0] + (skb->data[1] << 8); + skb_pull(skb, 2); + + return 0; +} + +static int mac802154_fetch_skb_u64(struct sk_buff *skb, u8 *dest) +{ + int i; + + if (!pskb_may_pull(skb, IEEE802154_ADDR_LEN)) + return -EINVAL; + + for (i = 0; i < IEEE802154_ADDR_LEN; i++) + dest[IEEE802154_ADDR_LEN - i - 1] = skb->data[i]; + skb_pull(skb, IEEE802154_ADDR_LEN); + + return 0; +} + +static int parse_frame_start(struct sk_buff *skb) +{ + u8 *head = skb->data; + u16 fc; + + if (!pskb_may_pull(skb, 3)) { + pr_debug("(%s): frame size %d bytes is too short\n", + __func__, skb->len); + goto exit_error; + } + + mac802154_fetch_skb_u16(skb, &fc); + mac802154_fetch_skb_u8(skb, &mac_cb(skb)->seq); + + pr_debug("(%s): %04x dsn%02x\n", __func__, fc, head[2]); + + mac_cb(skb)->flags = IEEE802154_FC_TYPE(fc); + + if (fc & IEEE802154_FC_ACK_REQ) { + pr_debug("(%s): ACKNOWLEDGE required\n", __func__); + mac_cb(skb)->flags |= MAC_CB_FLAG_ACKREQ; + } + + if (fc & IEEE802154_FC_SECEN) + mac_cb(skb)->flags |= MAC_CB_FLAG_SECEN; + + if (fc & IEEE802154_FC_INTRA_PAN) + mac_cb(skb)->flags |= MAC_CB_FLAG_INTRAPAN; + + /* TODO */ + if (mac_cb_is_secen(skb)) { + pr_info("security support is not implemented\n"); + goto exit_error; + } + + mac_cb(skb)->sa.addr_type = IEEE802154_FC_SAMODE(fc); + if (mac_cb(skb)->sa.addr_type == IEEE802154_ADDR_NONE) + pr_debug("(%s): src addr_type is NONE\n", __func__); + + mac_cb(skb)->da.addr_type = IEEE802154_FC_DAMODE(fc); + if (mac_cb(skb)->da.addr_type == IEEE802154_ADDR_NONE) + pr_debug("(%s): dst addr_type is NONE\n", __func__); + + if (IEEE802154_FC_TYPE(fc) == IEEE802154_FC_TYPE_ACK) { + /* ACK can only have NONE-type addresses */ + if (mac_cb(skb)->sa.addr_type != IEEE802154_ADDR_NONE || + mac_cb(skb)->da.addr_type != IEEE802154_ADDR_NONE) + goto exit_error; + } + + if (mac_cb(skb)->da.addr_type != IEEE802154_ADDR_NONE) { + if (mac802154_fetch_skb_u16(skb, &mac_cb(skb)->da.pan_id)) + goto exit_error; + + if (mac_cb_is_intrapan(skb)) { /* ! panid compress */ + pr_debug("(%s): src IEEE802154_FC_INTRA_PAN\n", + __func__); + mac_cb(skb)->sa.pan_id = mac_cb(skb)->da.pan_id; + pr_debug("(%s): src PAN address %04x\n", + __func__, mac_cb(skb)->sa.pan_id); + } + + pr_debug("(%s): dst PAN address %04x\n", + __func__, mac_cb(skb)->da.pan_id); + + if (mac_cb(skb)->da.addr_type == IEEE802154_ADDR_SHORT) { + if (mac802154_fetch_skb_u16(skb, + &mac_cb(skb)->da.short_addr)) + goto exit_error; + pr_debug("(%s): dst SHORT address %04x\n", + __func__, mac_cb(skb)->da.short_addr); + + } else { + if (mac802154_fetch_skb_u64(skb, + mac_cb(skb)->da.hwaddr)) + goto exit_error; + pr_debug("(%s): dst hardware addr\n", __func__); + } + } + + if (mac_cb(skb)->sa.addr_type != IEEE802154_ADDR_NONE) { + pr_debug("(%s): got src non-NONE address\n", __func__); + if (!(mac_cb_is_intrapan(skb))) { /* ! panid compress */ + if (mac802154_fetch_skb_u16(skb, + &mac_cb(skb)->sa.pan_id)) + goto exit_error; + pr_debug("(%s): src IEEE802154_FC_INTRA_PAN\n", + __func__); + } + + if (mac_cb(skb)->sa.addr_type == IEEE802154_ADDR_SHORT) { + if (mac802154_fetch_skb_u16(skb, + &mac_cb(skb)->sa.short_addr)) + goto exit_error; + pr_debug("(%s): src IEEE802154_ADDR_SHORT\n", + __func__); + } else { + if (mac802154_fetch_skb_u64(skb, + mac_cb(skb)->sa.hwaddr)) + goto exit_error; + pr_debug("(%s): src hardware addr\n", __func__); + } + } + + return 0; + +exit_error: + return -EINVAL; +} + +void mac802154_wpans_rx(struct mac802154_priv *priv, struct sk_buff *skb) +{ + int ret; + struct mac802154_sub_if_data *sdata; + struct sk_buff *skb2; + + ret = parse_frame_start(skb); /* 3 bytes pulled after this */ + if (ret) { + pr_debug("(%s): Got invalid frame\n", __func__); + return; + } + + pr_debug("(%s): frame %d\n", __func__, mac_cb_type(skb)); + + rcu_read_lock(); + list_for_each_entry_rcu(sdata, &priv->slaves, list) + { + if (sdata->type != IEEE802154_DEV_WPAN) + continue; + + skb2 = skb_clone(skb, GFP_ATOMIC); + if (skb2) + mac802154_subif_frame(sdata, skb2); + } + + rcu_read_unlock(); +} -- 1.7.2.3 ------------------------------------------------------------------------------ All the data continuously generated in your IT infrastructure contains a definitive record of customers, application performance, security threats, fraudulent activity, and more. Splunk takes this data and makes sense of it. IT sense. And common sense. http://p.sf.net/sfu/splunk-novd2d _______________________________________________ Linux-zigbee-devel mailing list Linux-zigbee-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/linux-zigbee-devel