Some of available devices are just dump radios implementing IEEE 802.15.4
PHY layer. This commit adds a common library that acts like an intermediate
layer between our socket family and drivers for those dumb devices.

Currently this is data-only part (no commands, no beacons). Control
interfaces will follow up shortly.

Note this implementaion is neither certified, nor feature complete!

Signed-off-by: Dmitry Eremin-Solenikov <[email protected]>
Signed-off-by: Sergey Lapin <[email protected]>
---
 MAINTAINERS               |    1 +
 include/linux/mac802154.h |   35 ++
 include/net/mac802154.h   |  106 ++++++
 net/Kconfig               |    1 +
 net/Makefile              |    1 +
 net/mac802154/Kconfig     |   17 +
 net/mac802154/Makefile    |    4 +
 net/mac802154/dev.c       |  924 +++++++++++++++++++++++++++++++++++++++++++++
 net/mac802154/mac802154.h |   85 +++++
 net/mac802154/mac_cmd.c   |   99 +++++
 net/mac802154/mdev.c      |  295 +++++++++++++++
 net/mac802154/mib.h       |   32 ++
 net/mac802154/rx.c        |   98 +++++
 13 files changed, 1698 insertions(+), 0 deletions(-)
 create mode 100644 include/linux/mac802154.h
 create mode 100644 include/net/mac802154.h
 create mode 100644 net/mac802154/Kconfig
 create mode 100644 net/mac802154/Makefile
 create mode 100644 net/mac802154/dev.c
 create mode 100644 net/mac802154/mac802154.h
 create mode 100644 net/mac802154/mac_cmd.c
 create mode 100644 net/mac802154/mdev.c
 create mode 100644 net/mac802154/mib.h
 create mode 100644 net/mac802154/rx.c

diff --git a/MAINTAINERS b/MAINTAINERS
index d6befb2..5bdb64e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2529,6 +2529,7 @@ W:        http://apps.sourceforge.net/trac/linux-zigbee
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/lowpan/lowpan.git
 S:     Maintained
 F:     net/ieee802154/
+F:     net/mac802154/
 F:     drivers/ieee802154/
 
 INTEGRITY MEASUREMENT ARCHITECTURE (IMA)
diff --git a/include/linux/mac802154.h b/include/linux/mac802154.h
new file mode 100644
index 0000000..e95e38d
--- /dev/null
+++ b/include/linux/mac802154.h
@@ -0,0 +1,35 @@
+/*
+ * 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 LINUX_MAC802154_H
+#define LINUX_MAC802154_H
+
+enum {
+       IFLA_WPAN_UNSPEC,
+       IFLA_WPAN_CHANNEL,
+       IFLA_WPAN_PAN_ID,
+       IFLA_WPAN_SHORT_ADDR,
+       IFLA_WPAN_COORD_SHORT_ADDR,
+       IFLA_WPAN_COORD_EXT_ADDR,
+       __IFLA_WPAN_MAX,
+};
+
+#define IFLA_WPAN_MAX  (__IFLA_WPAN_MAX - 1)
+
+#endif
+
diff --git a/include/net/mac802154.h b/include/net/mac802154.h
new file mode 100644
index 0000000..db76799
--- /dev/null
+++ b/include/net/mac802154.h
@@ -0,0 +1,106 @@
+/*
+ * IEEE802.15.4-2003 specification
+ *
+ * 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.
+ *
+ * Written by:
+ */
+#ifndef NET_MAC802154_H
+#define NET_MAC802154_H
+
+struct ieee802154_dev {
+       int     extra_tx_headroom; /* headroom to reserve for tx skb */
+       void    *priv;          /* driver-specific data */
+       u32     channel_mask;
+       u8      current_channel;
+       u32     flags; /* Flags for device to set */
+       struct device *parent;
+};
+
+/* Checksum is in hardware and is omitted from packet */
+/**
+ * enum ieee802154_hw_flags - hardware flags
+ *
+ * These flags are used to indicate hardware capabilities to
+ * the stack. Generally, flags here should have their meaning
+ * done in a way that the simplest hardware doesn't need setting
+ * any particular flags. There are some exceptions to this rule,
+ * however, so you are advised to review these flags carefully.
+ *
+ * @IEEE802154_HW_OMIT_CKSUM:
+ *     Indicates that receiver omits FCS and transmitter will add
+ *     FCS on it's own.
+ *
+ * @IEEE802154_HW_AACK:
+ *     Indicates that receiver will autorespond with ACK frames.
+ */
+enum ieee802154_hw_flags {
+       IEEE802154_HW_OMIT_CKSUM                        = 1 << 0,
+       IEEE802154_HW_AACK                              = 1 << 1,
+};
+
+struct sk_buff;
+
+/**
+ * struct ieee802154_ops - callbacks from mac802154 to the driver
+ *
+ * This structure contains various callbacks that the driver may
+ * handle or, in some cases, must handle, for example to transmit
+ * a frame.
+ *
+ * @start: Handler that 802.15.4 module calls for device initialisation.
+ *     This function is called before the first interface is attached.
+ *
+ * @stop: Handler that 802.15.4 module calls for device cleanup
+ *     This function is called after the last interface is removed.
+ *
+ * @tx: Handler that 802.15.4 module calls for each transmitted frame.
+ *      skb cntains the buffer starting from the IEEE 802.15.4 header.
+ *      The low-level driver should send the frame based on available
+ *      configuration.
+ *      This function should return zero or negative errno.
+ *
+ * @ed: Handler that 802.15.4 module calls for Energy Detection.
+ *      This function should place the value for detected energy
+ *      (usually device-dependant) in the level pointer and return
+ *      either zero or negative errno.
+ *
+ * @set_channel: Set radio for listening on specific channel.
+ *      Set the device for listening on specified channel.
+ *      Returns either zero, or negative errno.
+ */
+struct ieee802154_ops {
+       struct module   *owner;
+       int             (*start)(struct ieee802154_dev *dev);
+       void            (*stop)(struct ieee802154_dev *dev);
+       int             (*xmit)(struct ieee802154_dev *dev,
+                                               struct sk_buff *skb);
+       int             (*ed)(struct ieee802154_dev *dev, u8 *level);
+       int             (*set_channel)(struct ieee802154_dev *dev,
+                                               int channel);
+};
+
+struct ieee802154_dev *ieee802154_alloc_device(size_t priv_size,
+                                               struct ieee802154_ops *ops);
+int ieee802154_register_device(struct ieee802154_dev *dev);
+void ieee802154_unregister_device(struct ieee802154_dev *dev);
+void ieee802154_free_device(struct ieee802154_dev *dev);
+
+void ieee802154_rx(struct ieee802154_dev *dev, struct sk_buff *skb, u8 lqi);
+void ieee802154_rx_irqsafe(struct ieee802154_dev *dev, struct sk_buff *skb,
+               u8 lqi);
+#endif
+
diff --git a/net/Kconfig b/net/Kconfig
index 7051b97..b42d325 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -180,6 +180,7 @@ source "net/econet/Kconfig"
 source "net/wanrouter/Kconfig"
 source "net/phonet/Kconfig"
 source "net/ieee802154/Kconfig"
+source "net/mac802154/Kconfig"
 source "net/sched/Kconfig"
 source "net/dcb/Kconfig"
 
diff --git a/net/Makefile b/net/Makefile
index ba324ae..81115f6 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -61,6 +61,7 @@ ifneq ($(CONFIG_DCB),)
 obj-y                          += dcb/
 endif
 obj-y                          += ieee802154/
+obj-y                          += mac802154/
 
 ifeq ($(CONFIG_NET),y)
 obj-$(CONFIG_SYSCTL)           += sysctl_net.o
diff --git a/net/mac802154/Kconfig b/net/mac802154/Kconfig
new file mode 100644
index 0000000..2dd7f99
--- /dev/null
+++ b/net/mac802154/Kconfig
@@ -0,0 +1,17 @@
+config MAC802154
+       tristate "Generic IEEE 802.15.4 Soft Networking Stack (mac802154)"
+       depends on IEEE802154 && EXPERIMENTAL
+       select CRC_CCITT
+       ---help---
+         This option enables the hardware independent IEEE 802.15.4
+         networking stack for SoftMAC devices (the ones implementing
+         only PHY level of IEEE 802.15.4 standard).
+
+         Note: this implementation is neither certified, nor feature
+         complete! We do not garantee that it is compatible w/ other
+         implementations, etc.
+
+         If you plan to use HardMAC IEEE 802.15.4 devices, you can
+          say N here. Alternatievly you can say M to compile it as
+         module.
+
diff --git a/net/mac802154/Makefile b/net/mac802154/Makefile
new file mode 100644
index 0000000..07b7821
--- /dev/null
+++ b/net/mac802154/Makefile
@@ -0,0 +1,4 @@
+obj-$(CONFIG_MAC802154) +=     mac802154.o
+mac802154-objs         := rx.o mdev.o dev.o mac_cmd.o
+
+EXTRA_CFLAGS += -Wall -DDEBUG
diff --git a/net/mac802154/dev.c b/net/mac802154/dev.c
new file mode 100644
index 0000000..b40f6fd
--- /dev/null
+++ b/net/mac802154/dev.c
@@ -0,0 +1,924 @@
+/*
+ * 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 <[email protected]>
+ * Maxim Gorbachyov <[email protected]>
+ */
+
+#include <linux/net.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/mac802154.h>
+#include <net/rtnetlink.h>
+
+#include <net/af_ieee802154.h>
+#include <net/mac802154.h>
+#include <net/ieee802154_netdev.h>
+#include <net/ieee802154.h>
+
+#include "mac802154.h"
+#include "mib.h"
+
+static int ieee802154_net_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       struct ieee802154_sub_if_data *priv;
+       priv = netdev_priv(dev);
+
+       if (!(priv->hw->hw.flags & IEEE802154_HW_OMIT_CKSUM)) {
+               u16 crc = crc_ccitt(0, skb->data, skb->len);
+               u8 *data = skb_put(skb, 2);
+               data[0] = crc & 0xff;
+               data[1] = crc >> 8;
+       }
+
+       read_lock(&priv->mib_lock);
+       phy_cb(skb)->chan = priv->chan;
+       read_unlock(&priv->mib_lock);
+
+       skb->iif = dev->ifindex;
+       skb->dev = priv->hw->netdev;
+       dev->stats.tx_packets++;
+       dev->stats.tx_bytes += skb->len;
+
+       dev->trans_start = jiffies;
+       dev_queue_xmit(skb);
+
+       return 0;
+}
+
+static int ieee802154_slave_open(struct net_device *dev)
+{
+       struct ieee802154_sub_if_data *priv = netdev_priv(dev);
+       int res = 0;
+
+       if (priv->hw->open_count++ == 0) {
+               res = dev_open(priv->hw->netdev);
+               WARN_ON(res);
+               if (res)
+                       goto err;
+       }
+
+       netif_start_queue(dev);
+       return 0;
+err:
+       priv->hw->open_count--;
+
+       return res;
+}
+
+static int ieee802154_slave_close(struct net_device *dev)
+{
+       struct ieee802154_sub_if_data *priv = netdev_priv(dev);
+
+       netif_stop_queue(dev);
+
+       if ((--priv->hw->open_count) == 0) {
+               if (netif_running(priv->hw->netdev))
+                       dev_close(priv->hw->netdev);
+       }
+
+       return 0;
+}
+
+
+static int ieee802154_slave_ioctl(struct net_device *dev, struct ifreq *ifr,
+               int cmd)
+{
+       struct ieee802154_sub_if_data *priv = netdev_priv(dev);
+       struct sockaddr_ieee802154 *sa =
+               (struct sockaddr_ieee802154 *)&ifr->ifr_addr;
+       int err = -ENOIOCTLCMD;
+
+       read_lock(&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;
+       }
+       read_unlock(&priv->mib_lock);
+       return err;
+}
+
+static int ieee802154_slave_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);
+       return 0;
+}
+
+static void ieee802154_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 ieee802154_header_create(struct sk_buff *skb,
+                          struct net_device *dev,
+                          unsigned short type, const void *_daddr,
+                          const void *_saddr, unsigned len)
+{
+       u8 head[24] = {};
+       int pos = 0;
+
+       u16 fc;
+       const struct ieee802154_addr *saddr = _saddr;
+       const struct ieee802154_addr *daddr = _daddr;
+       struct ieee802154_addr dev_addr;
+       struct ieee802154_sub_if_data *priv = netdev_priv(dev);
+
+       fc = mac_cb_type(skb);
+       if (mac_cb_is_ackreq(skb))
+               fc |= IEEE802154_FC_ACK_REQ;
+
+       pos = 2;
+
+       head[pos++] = mac_cb(skb)->seq; /* DSN/BSN */
+
+       if (!daddr)
+               return -EINVAL;
+
+       if (!saddr) {
+               read_lock(&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;
+
+               read_unlock(&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 {
+                       ieee802154_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 {
+                       ieee802154_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 ieee802154_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;
+               memcpy(addr->hwaddr, hdr, IEEE802154_ADDR_LEN);
+               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("malformed packet\n");
+       return 0;
+}
+
+static struct header_ops ieee802154_header_ops = {
+       .create         = ieee802154_header_create,
+       .parse          = ieee802154_header_parse,
+};
+
+static const struct net_device_ops ieee802154_slave_ops = {
+       .ndo_open               = ieee802154_slave_open,
+       .ndo_stop               = ieee802154_slave_close,
+       .ndo_start_xmit         = ieee802154_net_xmit,
+       .ndo_do_ioctl           = ieee802154_slave_ioctl,
+       .ndo_set_mac_address    = ieee802154_slave_mac_addr,
+};
+
+static void ieee802154_netdev_setup(struct net_device *dev)
+{
+       dev->addr_len           = IEEE802154_ADDR_LEN;
+       memset(dev->broadcast, 0xff, IEEE802154_ADDR_LEN);
+       dev->features           = NETIF_F_NO_CSUM;
+       dev->hard_header_len    = 2 + 1 + 20 + 14;
+       dev->header_ops         = &ieee802154_header_ops;
+       dev->needed_tailroom    = 2; /* FCS */
+       dev->mtu                = 127;
+       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         = &ieee802154_slave_ops;
+       dev->ml_priv            = &mac802154_mlme;
+}
+
+/*
+ * This is for hw unregistration only, as it doesn't do RCU locking
+ */
+void ieee802154_drop_slaves(struct ieee802154_dev *hw)
+{
+       struct ieee802154_priv *priv = ieee802154_to_priv(hw);
+       struct ieee802154_sub_if_data *sdata, *next;
+
+       ASSERT_RTNL();
+
+       list_for_each_entry_safe(sdata, next, &priv->slaves, list) {
+               mutex_lock(&sdata->hw->slaves_mtx);
+               list_del(&sdata->list);
+               mutex_unlock(&sdata->hw->slaves_mtx);
+
+               dev_put(sdata->hw->netdev);
+
+               unregister_netdevice(sdata->dev);
+       }
+}
+
+static int ieee802154_netdev_validate(struct nlattr *tb[],
+               struct nlattr *data[])
+{
+       if (tb[IFLA_ADDRESS])
+               if (nla_len(tb[IFLA_ADDRESS]) != IEEE802154_ADDR_LEN)
+                       return -EINVAL;
+
+       if (tb[IFLA_BROADCAST])
+               return -EINVAL;
+
+       return 0;
+}
+
+static int ieee802154_netdev_newlink(struct net_device *dev,
+                                          struct nlattr *tb[],
+                                          struct nlattr *data[])
+{
+       struct net_device *mdev;
+       struct ieee802154_sub_if_data *priv;
+       struct ieee802154_priv *ipriv;
+       int err;
+
+       if (!tb[IFLA_LINK])
+               return -EOPNOTSUPP;
+
+       mdev = __dev_get_by_index(dev_net(dev), nla_get_u32(tb[IFLA_LINK]));
+       if (!mdev)
+               return -ENODEV;
+
+       if (mdev->type != ARPHRD_IEEE802154_PHY)
+               return -EINVAL;
+
+       ipriv = netdev_priv(mdev);
+
+       priv = netdev_priv(dev);
+       priv->dev = dev;
+       priv->hw = ipriv;
+
+       rwlock_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;
+
+       dev_hold(ipriv->netdev);
+
+       dev->needed_headroom = ipriv->hw.extra_tx_headroom;
+
+       SET_NETDEV_DEV(dev, &ipriv->netdev->dev);
+
+       err = register_netdevice(dev);
+       if (err < 0)
+               return err;
+
+       mutex_lock(&ipriv->slaves_mtx);
+       list_add_tail_rcu(&priv->list, &ipriv->slaves);
+       mutex_unlock(&ipriv->slaves_mtx);
+
+       return 0;
+}
+
+static void ieee802154_netdev_dellink(struct net_device *dev)
+{
+       struct ieee802154_sub_if_data *sdata;
+       ASSERT_RTNL();
+
+       BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+       sdata = netdev_priv(dev);
+
+       mutex_lock(&sdata->hw->slaves_mtx);
+       list_del_rcu(&sdata->list);
+       mutex_unlock(&sdata->hw->slaves_mtx);
+
+       dev_put(sdata->hw->netdev);
+
+       synchronize_rcu();
+       unregister_netdevice(sdata->dev);
+}
+
+static size_t ieee802154_netdev_get_size(const struct net_device *dev)
+{
+       return  nla_total_size(2) +     /* IFLA_WPAN_CHANNEL */
+               nla_total_size(2) +     /* IFLA_WPAN_PAN_ID */
+               nla_total_size(2) +     /* IFLA_WPAN_SHORT_ADDR */
+               nla_total_size(2) +     /* IFLA_WPAN_COORD_SHORT_ADDR */
+               nla_total_size(8);      /* IFLA_WPAN_COORD_EXT_ADDR */
+}
+
+static int ieee802154_netdev_fill_info(struct sk_buff *skb,
+                                       const struct net_device *dev)
+{
+       struct ieee802154_sub_if_data *priv = netdev_priv(dev);
+
+       read_lock(&priv->mib_lock);
+
+       NLA_PUT_U16(skb, IFLA_WPAN_CHANNEL, priv->chan);
+       NLA_PUT_U16(skb, IFLA_WPAN_PAN_ID, priv->pan_id);
+       NLA_PUT_U16(skb, IFLA_WPAN_SHORT_ADDR, priv->short_addr);
+       /* TODO: IFLA_WPAN_COORD_SHORT_ADDR */
+       /* TODO: IFLA_WPAN_COORD_EXT_ADDR */
+
+       read_unlock(&priv->mib_lock);
+
+       return 0;
+
+nla_put_failure:
+       read_unlock(&priv->mib_lock);
+       return -EMSGSIZE;
+}
+
+static struct rtnl_link_ops wpan_link_ops __read_mostly = {
+       .kind           = "wpan",
+       .priv_size      = sizeof(struct ieee802154_sub_if_data),
+       .setup          = ieee802154_netdev_setup,
+       .validate       = ieee802154_netdev_validate,
+       .newlink        = ieee802154_netdev_newlink,
+       .dellink        = ieee802154_netdev_dellink,
+       .get_size       = ieee802154_netdev_get_size,
+       .fill_info      = ieee802154_netdev_fill_info,
+};
+
+static int ieee802154_process_beacon(struct net_device *dev,
+               struct sk_buff *skb)
+{
+       pr_warning("ieee802154: beacon frames are not yet supported\n");
+       kfree_skb(skb);
+       return NET_RX_DROP;
+}
+
+static int ieee802154_process_ack(struct net_device *dev, struct sk_buff *skb)
+{
+       pr_debug("got ACK for SEQ=%d\n", mac_cb(skb)->seq);
+
+       kfree_skb(skb);
+       return NET_RX_SUCCESS;
+}
+
+static int ieee802154_process_data(struct net_device *dev, struct sk_buff *skb)
+{
+       return netif_rx(skb);
+}
+
+static int ieee802154_subif_frame(struct ieee802154_sub_if_data *sdata,
+               struct sk_buff *skb)
+{
+       pr_debug("%s Getting packet via slave interface %s\n",
+                               __func__, sdata->dev->name);
+
+       read_lock(&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 == sdata->short_addr)
+                       skb->pkt_type = PACKET_HOST;
+               else if (mac_cb(skb)->da.short_addr ==
+                                       IEEE802154_ADDR_BROADCAST)
+                       skb->pkt_type = PACKET_BROADCAST;
+               else
+                       skb->pkt_type = PACKET_OTHERHOST;
+               break;
+       }
+
+       read_unlock(&sdata->mib_lock);
+
+       skb->dev = sdata->dev;
+
+       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_BEACON:
+               return ieee802154_process_beacon(sdata->dev, skb);
+       case IEEE802154_FC_TYPE_ACK:
+               return ieee802154_process_ack(sdata->dev, skb);
+       case IEEE802154_FC_TYPE_MAC_CMD:
+               return ieee802154_process_cmd(sdata->dev, skb);
+       case IEEE802154_FC_TYPE_DATA:
+               return ieee802154_process_data(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 u8 fetch_skb_u8(struct sk_buff *skb)
+{
+       u8 ret;
+
+       BUG_ON(skb->len < 1);
+
+       ret = skb->data[0];
+       skb_pull(skb, 1);
+
+       return ret;
+}
+
+static u16 fetch_skb_u16(struct sk_buff *skb)
+{
+       u16 ret;
+
+       BUG_ON(skb->len < 2);
+
+       ret = skb->data[0] + (skb->data[1] * 256);
+       skb_pull(skb, 2);
+       return ret;
+}
+
+static void fetch_skb_u64(struct sk_buff *skb, void *data)
+{
+       BUG_ON(skb->len < IEEE802154_ADDR_LEN);
+
+       memcpy(data, skb->data, IEEE802154_ADDR_LEN);
+       skb_pull(skb, IEEE802154_ADDR_LEN);
+}
+
+#define IEEE802154_FETCH_U8(skb, var)          \
+       do {                                    \
+               if (skb->len < 1)               \
+                       goto exit_error;        \
+               var = fetch_skb_u8(skb);        \
+       } while (0)
+
+#define IEEE802154_FETCH_U16(skb, var)         \
+       do {                                    \
+               if (skb->len < 2)               \
+                       goto exit_error;        \
+               var = fetch_skb_u16(skb);       \
+       } while (0)
+
+#define IEEE802154_FETCH_U64(skb, var)                 \
+       do {                                            \
+               if (skb->len < IEEE802154_ADDR_LEN)     \
+                       goto exit_error;                \
+               fetch_skb_u64(skb, &var);               \
+       } while (0)
+
+static int parse_frame_start(struct sk_buff *skb)
+{
+       u8 *head = skb->data;
+       u16 fc;
+
+       if (skb->len < 3) {
+               pr_debug("frame size %d bytes is too short\n", skb->len);
+               return -EINVAL;
+       }
+
+       IEEE802154_FETCH_U16(skb, fc);
+       IEEE802154_FETCH_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");
+               return -EINVAL;
+       }
+
+       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)
+                       return -EINVAL;
+       }
+
+       if (mac_cb(skb)->da.addr_type != IEEE802154_ADDR_NONE) {
+               IEEE802154_FETCH_U16(skb, mac_cb(skb)->da.pan_id);
+
+               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) {
+                       IEEE802154_FETCH_U16(skb, mac_cb(skb)->da.short_addr);
+                       pr_debug("%s(): dst SHORT address %04x\n",
+                                       __func__, mac_cb(skb)->da.short_addr);
+
+               } else {
+                       IEEE802154_FETCH_U64(skb, mac_cb(skb)->da.hwaddr);
+                       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 */
+                       IEEE802154_FETCH_U16(skb, mac_cb(skb)->sa.pan_id);
+                       pr_debug("%s(): src IEEE802154_FC_INTRA_PAN\n",
+                                       __func__);
+               }
+
+               if (mac_cb(skb)->sa.addr_type == IEEE802154_ADDR_SHORT) {
+                       IEEE802154_FETCH_U16(skb, mac_cb(skb)->sa.short_addr);
+                       pr_debug("%s(): src IEEE802154_ADDR_SHORT\n",
+                                       __func__);
+               } else {
+                       IEEE802154_FETCH_U64(skb, mac_cb(skb)->sa.hwaddr);
+                       pr_debug("%s(): src hardware addr\n", __func__);
+               }
+       }
+
+       return 0;
+
+exit_error:
+       return -EINVAL;
+}
+
+void ieee802154_subif_rx(struct ieee802154_dev *hw, struct sk_buff *skb)
+{
+       struct ieee802154_priv *priv = ieee802154_to_priv(hw);
+       struct ieee802154_sub_if_data *sdata, *prev = NULL;
+       int ret;
+
+       BUILD_BUG_ON(sizeof(struct ieee802154_mac_cb) > sizeof(skb->cb));
+       pr_debug("%s()\n", __func__);
+
+       if (!(priv->hw.flags & IEEE802154_HW_OMIT_CKSUM)) {
+               u16 crc;
+
+               if (skb->len < 2) {
+                       pr_debug("%s(): Got invalid frame\n", __func__);
+                       goto out;
+               }
+               crc = crc_ccitt(0, skb->data, skb->len);
+               if (crc) {
+                       pr_debug("%s(): CRC mismatch\n", __func__);
+                       goto out;
+               }
+               skb_trim(skb, skb->len - 2); /* CRC */
+       }
+
+       ret = parse_frame_start(skb); /* 3 bytes pulled after this */
+       if (ret) {
+               pr_debug("%s(): Got invalid frame\n", __func__);
+               goto out;
+       }
+
+       pr_debug("%s() frame %d\n", __func__, mac_cb_type(skb));
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(sdata, &priv->slaves, list)
+       {
+               if (prev) {
+                       struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
+                       if (skb2)
+                               ieee802154_subif_frame(prev, skb2);
+               }
+
+               prev = sdata;
+       }
+
+       if (prev) {
+               ieee802154_subif_frame(prev, skb);
+               skb = NULL;
+       }
+
+       rcu_read_unlock();
+
+out:
+       dev_kfree_skb(skb);
+       return;
+}
+
+u16 ieee802154_dev_get_pan_id(struct net_device *dev)
+{
+       struct ieee802154_sub_if_data *priv = netdev_priv(dev);
+       u16 ret;
+
+       BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+       read_lock(&priv->mib_lock);
+       ret = priv->pan_id;
+       read_unlock(&priv->mib_lock);
+
+       return ret;
+}
+
+u16 ieee802154_dev_get_short_addr(struct net_device *dev)
+{
+       struct ieee802154_sub_if_data *priv = netdev_priv(dev);
+       u16 ret;
+
+       BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+       read_lock(&priv->mib_lock);
+       ret = priv->short_addr;
+       read_unlock(&priv->mib_lock);
+
+       return ret;
+}
+
+void ieee802154_dev_set_pan_id(struct net_device *dev, u16 val)
+{
+       struct ieee802154_sub_if_data *priv = netdev_priv(dev);
+
+       BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+       write_lock(&priv->mib_lock);
+       priv->pan_id = val;
+       write_unlock(&priv->mib_lock);
+}
+void ieee802154_dev_set_short_addr(struct net_device *dev, u16 val)
+{
+       struct ieee802154_sub_if_data *priv = netdev_priv(dev);
+
+       BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+       write_lock(&priv->mib_lock);
+       priv->short_addr = val;
+       write_unlock(&priv->mib_lock);
+}
+void ieee802154_dev_set_channel(struct net_device *dev, u8 val)
+{
+       struct ieee802154_sub_if_data *priv = netdev_priv(dev);
+
+       BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+       write_lock(&priv->mib_lock);
+       priv->chan = val;
+       write_unlock(&priv->mib_lock);
+}
+
+u8 ieee802154_dev_get_dsn(struct net_device *dev)
+{
+       struct ieee802154_sub_if_data *priv = netdev_priv(dev);
+       u16 ret;
+
+       BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+       write_lock(&priv->mib_lock);
+       ret = priv->dsn++;
+       write_unlock(&priv->mib_lock);
+
+       return ret;
+}
+
+u8 ieee802154_dev_get_bsn(struct net_device *dev)
+{
+       struct ieee802154_sub_if_data *priv = netdev_priv(dev);
+       u16 ret;
+
+       BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+       write_lock(&priv->mib_lock);
+       ret = priv->bsn++;
+       write_unlock(&priv->mib_lock);
+
+       return ret;
+}
+
+static int __init ieee802154_dev_init(void)
+{
+       return rtnl_link_register(&wpan_link_ops);
+}
+module_init(ieee802154_dev_init);
+
+static void __exit ieee802154_dev_exit(void)
+{
+       rtnl_link_unregister(&wpan_link_ops);
+}
+module_exit(ieee802154_dev_exit);
+
+MODULE_ALIAS_RTNL_LINK("wpan");
+
diff --git a/net/mac802154/mac802154.h b/net/mac802154/mac802154.h
new file mode 100644
index 0000000..f4e8d29
--- /dev/null
+++ b/net/mac802154/mac802154.h
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ *
+ * Written by:
+ * Pavel Smolenskiy <[email protected]>
+ * Maxim Gorbachyov <[email protected]>
+ * Dmitry Eremin-Solenikov <[email protected]>
+ */
+#ifndef MAC802154_H
+#define MAC802154_H
+
+#include <linux/spinlock.h>
+struct ieee802154_priv {
+       struct ieee802154_dev   hw;
+       struct ieee802154_ops   *ops;
+
+       struct net_device *netdev; /* mwpanX device */
+       int open_count;
+       /* As in mac80211 slaves list is modified:
+        * 1) under the RTNL
+        * 2) protected by slaves_mtx;
+        * 3) in an RCU manner
+        *
+        * So atomic readers can use any of this protection methods
+        */
+       struct list_head        slaves;
+       struct mutex            slaves_mtx;
+       /* This one is used for scanning and other
+        * jobs not to be interfered with serial driver */
+       struct workqueue_struct *dev_workqueue;
+};
+
+#define ieee802154_to_priv(_hw)        container_of(_hw, struct 
ieee802154_priv, hw)
+
+struct ieee802154_sub_if_data {
+       struct list_head list; /* the ieee802154_priv->slaves list */
+
+       struct ieee802154_priv *hw;
+       struct net_device *dev;
+
+       rwlock_t mib_lock;
+
+       u16 pan_id;
+       u16 short_addr;
+
+       u8 chan;
+
+       /* MAC BSN field */
+       u8 bsn;
+       /* MAC BSN field */
+       u8 dsn;
+};
+
+void ieee802154_drop_slaves(struct ieee802154_dev *hw);
+
+void ieee802154_subif_rx(struct ieee802154_dev *hw, struct sk_buff *skb);
+
+struct ieee802154_phy_cb {
+       u8 lqi;
+       u8 chan;
+};
+
+static inline struct ieee802154_phy_cb *phy_cb(struct sk_buff *skb)
+{
+       return (struct ieee802154_phy_cb *)skb->cb;
+}
+
+extern struct ieee802154_mlme_ops mac802154_mlme;
+
+int ieee802154_process_cmd(struct net_device *dev, struct sk_buff *skb);
+
+#endif
diff --git a/net/mac802154/mac_cmd.c b/net/mac802154/mac_cmd.c
new file mode 100644
index 0000000..8941628
--- /dev/null
+++ b/net/mac802154/mac_cmd.c
@@ -0,0 +1,99 @@
+/*
+ * MAC commands 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 <[email protected]>
+ * Dmitry Eremin-Solenikov <[email protected]>
+ */
+
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <net/af_ieee802154.h>
+#include <net/mac802154.h>
+#include <net/ieee802154.h>
+#include <net/ieee802154_netdev.h>
+#include <net/nl802154.h>
+
+#include "mac802154.h"
+#include "mib.h"
+
+int ieee802154_process_cmd(struct net_device *dev, struct sk_buff *skb)
+{
+       pr_warning("ieee802154: command frames are not yet supported\n");
+       kfree_skb(skb);
+       return NET_RX_DROP;
+}
+
+
+static int ieee802154_mlme_assoc_req(struct net_device *dev,
+               struct ieee802154_addr *addr, u8 channel, u8 cap)
+{
+       /* We simply emulate it here */
+       return ieee802154_nl_assoc_confirm(dev,
+                       ieee802154_dev_get_short_addr(dev),
+                       IEEE802154_SUCCESS);
+}
+
+static int ieee802154_mlme_assoc_resp(struct net_device *dev,
+               struct ieee802154_addr *addr, u16 short_addr, u8 status)
+{
+       return 0;
+}
+
+static int ieee802154_mlme_disassoc_req(struct net_device *dev,
+               struct ieee802154_addr *addr, u8 reason)
+{
+       return ieee802154_nl_disassoc_confirm(dev, IEEE802154_SUCCESS);
+}
+
+static int ieee802154_mlme_start_req(struct net_device *dev,
+                               struct ieee802154_addr *addr,
+                               u8 channel,
+                               u8 bcn_ord, u8 sf_ord, u8 pan_coord, u8 blx,
+                               u8 coord_realign)
+{
+       /* We don't emulate beacons here at all, so START should fail */
+       ieee802154_nl_start_confirm(dev, IEEE802154_INVALID_PARAMETER);
+       return 0;
+}
+
+static int ieee802154_mlme_scan_req(struct net_device *dev, u8 type,
+               u32 channels,
+               u8 duration)
+{
+       u8 edl[27] = {};
+       return ieee802154_nl_scan_confirm(dev, IEEE802154_SUCCESS, type,
+                       channels,
+                       type == IEEE802154_MAC_SCAN_ED ? edl : NULL);
+}
+
+
+struct ieee802154_mlme_ops mac802154_mlme = {
+       .assoc_req = ieee802154_mlme_assoc_req,
+       .assoc_resp = ieee802154_mlme_assoc_resp,
+       .disassoc_req = ieee802154_mlme_disassoc_req,
+       .start_req = ieee802154_mlme_start_req,
+       .scan_req = ieee802154_mlme_scan_req,
+
+       .get_pan_id = ieee802154_dev_get_pan_id,
+       .get_short_addr = ieee802154_dev_get_short_addr,
+       .get_dsn = ieee802154_dev_get_dsn,
+       .get_bsn = ieee802154_dev_get_bsn,
+};
+
diff --git a/net/mac802154/mdev.c b/net/mac802154/mdev.c
new file mode 100644
index 0000000..191e942
--- /dev/null
+++ b/net/mac802154/mdev.c
@@ -0,0 +1,295 @@
+/*
+ * 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 <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <net/route.h>
+
+#include <net/af_ieee802154.h>
+#include <net/mac802154.h>
+
+#include "mac802154.h"
+
+struct xmit_work {
+       struct sk_buff *skb;
+       struct work_struct work;
+       struct ieee802154_priv *priv;
+};
+
+static void ieee802154_xmit_worker(struct work_struct *work)
+{
+       struct xmit_work *xw = container_of(work, struct xmit_work, work);
+       int res;
+
+       if (xw->priv->hw.current_channel != phy_cb(xw->skb)->chan) {
+               res = xw->priv->ops->set_channel(&xw->priv->hw,
+                               phy_cb(xw->skb)->chan);
+               if (res) {
+                       pr_debug("set_channel failed\n");
+                       goto out;
+               }
+       }
+
+       res = xw->priv->ops->xmit(&xw->priv->hw, xw->skb);
+
+out:
+       /* FIXME: result processing and/or requeue!!! */
+       dev_kfree_skb(xw->skb);
+
+       kfree(xw);
+}
+
+static int ieee802154_master_hard_start_xmit(struct sk_buff *skb,
+               struct net_device *dev)
+{
+       struct ieee802154_priv *priv = netdev_priv(dev);
+       struct xmit_work *work;
+
+       if (skb_cow_head(skb, priv->hw.extra_tx_headroom)) {
+               dev_kfree_skb(skb);
+               return NETDEV_TX_OK;
+       }
+
+       work = kzalloc(sizeof(struct xmit_work), GFP_ATOMIC);
+       if (!work)
+               return NETDEV_TX_BUSY;
+
+       INIT_WORK(&work->work, ieee802154_xmit_worker);
+       work->skb = skb;
+       work->priv = priv;
+
+       queue_work(priv->dev_workqueue, &work->work);
+
+       return NETDEV_TX_OK;
+}
+
+static int ieee802154_master_open(struct net_device *dev)
+{
+       int rc;
+       struct ieee802154_priv *priv = netdev_priv(dev);
+
+       if (!priv) {
+               pr_debug("%s:%s: unable to get master private data\n",
+                               __FILE__, __func__);
+               return -ENODEV;
+       }
+
+       if (!priv->open_count)
+               return -EOPNOTSUPP;
+
+       rc = priv->ops->start(&priv->hw);
+
+       if (!rc)
+               netif_tx_start_all_queues(dev);
+
+       return rc;
+}
+
+static int ieee802154_master_close(struct net_device *dev)
+{
+       struct ieee802154_priv *priv = netdev_priv(dev);
+       struct ieee802154_sub_if_data *sdata;
+
+       ASSERT_RTNL();
+
+       /* We are under RTNL, so it's fine to do this */
+       list_for_each_entry(sdata, &priv->slaves, list)
+               if (netif_running(sdata->dev))
+                       dev_close(sdata->dev);
+
+       priv->ops->stop(&priv->hw);
+
+       return 0;
+}
+
+static ssize_t ieee802154_netdev_show(const struct device *dev,
+                  struct device_attribute *attr, char *buf,
+                  ssize_t (*format)(const struct net_device *, char *))
+{
+       struct net_device *netdev = to_net_dev(dev);
+       ssize_t ret = -EINVAL;
+
+       if (netdev->reg_state <= NETREG_REGISTERED)
+               ret = (*format)(netdev, buf);
+
+       return ret;
+}
+#define MASTER_SHOW(field, format_string)                              \
+static ssize_t format_##field(const struct net_device *dev, char *buf) \
+{                                                                      \
+       struct ieee802154_priv *priv = netdev_priv(dev);                \
+       return sprintf(buf, format_string, priv->hw.field);             \
+}                                                                      \
+static ssize_t show_##field(struct device *dev,                                
\
+                           struct device_attribute *attr, char *buf)   \
+{                                                                      \
+       return ieee802154_netdev_show(dev, attr, buf, format_##field);  \
+}                                                                      \
+static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL)
+
+static const char fmt_long_hex[] = "%#lx\n";
+static const char fmt_hex[] = "%#x\n";
+static const char fmt_dec[] = "%d\n";
+
+MASTER_SHOW(current_channel, fmt_dec);
+MASTER_SHOW(channel_mask, fmt_hex);
+
+static struct attribute *pmib_attrs[] = {
+       &dev_attr_current_channel.attr,
+       &dev_attr_channel_mask.attr,
+       NULL
+};
+
+static struct attribute_group pmib_group = {
+       .name  = "pib",
+       .attrs  = pmib_attrs,
+};
+
+static const struct net_device_ops ieee802154_master_ops = {
+       .ndo_open               = ieee802154_master_open,
+       .ndo_stop               = ieee802154_master_close,
+       .ndo_start_xmit         = ieee802154_master_hard_start_xmit,
+};
+
+static void ieee802154_netdev_setup_master(struct net_device *dev)
+{
+       dev->addr_len           = 0;
+       dev->features           = NETIF_F_NO_CSUM;
+       dev->hard_header_len    = 0;
+       dev->mtu                = 127;
+       dev->tx_queue_len       = 0;
+       dev->type               = ARPHRD_IEEE802154_PHY;
+       dev->flags              = IFF_NOARP | IFF_BROADCAST;
+       dev->watchdog_timeo     = 0;
+
+       dev->netdev_ops = &ieee802154_master_ops;
+}
+
+struct ieee802154_dev *ieee802154_alloc_device(size_t priv_size,
+               struct ieee802154_ops *ops)
+{
+       struct net_device *dev;
+       struct ieee802154_priv *priv;
+
+       dev = alloc_netdev(ALIGN(sizeof(*priv), NETDEV_ALIGN) + priv_size,
+                       "mwpan%d", ieee802154_netdev_setup_master);
+       if (!dev) {
+               printk(KERN_ERR
+                       "Failure to initialize master IEEE802154 device\n");
+               return NULL;
+       }
+       priv = netdev_priv(dev);
+       priv->netdev = dev;
+       priv->hw.priv = (char *)priv + ALIGN(sizeof(*priv), NETDEV_ALIGN);
+
+       BUG_ON(!dev);
+       BUG_ON(!ops);
+       BUG_ON(!ops->xmit);
+       BUG_ON(!ops->ed);
+       BUG_ON(!ops->start);
+       BUG_ON(!ops->stop);
+
+       if (!try_module_get(ops->owner)) {
+               free_netdev(dev);
+               return NULL;
+       }
+
+       priv->ops = ops;
+
+       INIT_LIST_HEAD(&priv->slaves);
+       mutex_init(&priv->slaves_mtx);
+
+       return &priv->hw;
+}
+EXPORT_SYMBOL(ieee802154_alloc_device);
+
+void ieee802154_free_device(struct ieee802154_dev *hw)
+{
+       struct ieee802154_priv *priv = ieee802154_to_priv(hw);
+
+       BUG_ON(!list_empty(&priv->slaves));
+       BUG_ON(!priv->netdev);
+
+       module_put(priv->ops->owner);
+
+       free_netdev(priv->netdev);
+}
+EXPORT_SYMBOL(ieee802154_free_device);
+
+int ieee802154_register_device(struct ieee802154_dev *dev)
+{
+       struct ieee802154_priv *priv = ieee802154_to_priv(dev);
+       struct net_device *ndev = priv->netdev;
+
+       int rc;
+
+       rtnl_lock();
+       if (strchr(ndev->name, '%')) {
+               rc = dev_alloc_name(ndev, ndev->name);
+               if (rc < 0)
+                       goto out_unlock;
+       }
+
+       priv->dev_workqueue =
+               create_singlethread_workqueue(ndev->name);
+       if (!priv->dev_workqueue) {
+               rc = -ENOMEM;
+               goto out_unlock;
+       }
+
+       ndev->needed_headroom = priv->hw.extra_tx_headroom;
+       SET_NETDEV_DEV(ndev, priv->hw.parent);
+
+       ndev->sysfs_groups[1] = &pmib_group;
+
+       rc = register_netdevice(ndev);
+       if (rc < 0)
+               goto out_wq;
+
+       rtnl_unlock();
+
+       return 0;
+
+out_wq:
+       destroy_workqueue(priv->dev_workqueue);
+out_unlock:
+       rtnl_unlock();
+       return rc;
+}
+EXPORT_SYMBOL(ieee802154_register_device);
+
+void ieee802154_unregister_device(struct ieee802154_dev *dev)
+{
+       struct ieee802154_priv *priv = ieee802154_to_priv(dev);
+
+       flush_workqueue(priv->dev_workqueue);
+       destroy_workqueue(priv->dev_workqueue);
+
+       rtnl_lock();
+
+       ieee802154_drop_slaves(dev);
+       unregister_netdevice(priv->netdev);
+
+       rtnl_unlock();
+}
+EXPORT_SYMBOL(ieee802154_unregister_device);
+
+MODULE_DESCRIPTION("IEEE 802.15.4 implementation");
+MODULE_LICENSE("GPL v2");
+
diff --git a/net/mac802154/mib.h b/net/mac802154/mib.h
new file mode 100644
index 0000000..57bf03f
--- /dev/null
+++ b/net/mac802154/mib.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright 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.
+ *
+ */
+
+#ifndef MIB802154_H
+#define MIB802154_H
+
+/* FIXME: should be dropped in favour of generic MIB API */
+u8 ieee802154_dev_get_dsn(struct net_device *dev);
+u8 ieee802154_dev_get_bsn(struct net_device *dev);
+u16 ieee802154_dev_get_pan_id(struct net_device *dev);
+u16 ieee802154_dev_get_short_addr(struct net_device *dev);
+void ieee802154_dev_set_pan_id(struct net_device *dev, u16 val);
+void ieee802154_dev_set_short_addr(struct net_device *dev, u16 val);
+void ieee802154_dev_set_channel(struct net_device *dev, u8 chan);
+
+
+#endif
diff --git a/net/mac802154/rx.c b/net/mac802154/rx.c
new file mode 100644
index 0000000..81a269e
--- /dev/null
+++ b/net/mac802154/rx.c
@@ -0,0 +1,98 @@
+/*
+ * 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.
+ *
+ * Written by:
+ * Pavel Smolenskiy <[email protected]>
+ * Maxim Gorbachyov <[email protected]>
+ * Dmitry Eremin-Solenikov <[email protected]>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/workqueue.h>
+#include <linux/netdevice.h>
+
+#include <net/mac802154.h>
+
+#include "mac802154.h"
+
+static void __ieee802154_rx_prepare(struct ieee802154_dev *dev,
+               struct sk_buff *skb, u8 lqi)
+{
+       struct ieee802154_priv *priv = ieee802154_to_priv(dev);
+
+       BUG_ON(!skb);
+
+       phy_cb(skb)->lqi = lqi;
+
+       skb->dev = priv->netdev;
+
+       skb->iif = skb->dev->ifindex;
+
+       skb->protocol = htons(ETH_P_IEEE802154);
+
+       skb_reset_mac_header(skb);
+}
+
+void ieee802154_rx(struct ieee802154_dev *dev, struct sk_buff *skb, u8 lqi)
+{
+       struct sk_buff *skb2;
+
+       __ieee802154_rx_prepare(dev, skb, lqi);
+
+       skb2 = skb_clone(skb, GFP_KERNEL);
+       netif_rx(skb2);
+
+       ieee802154_subif_rx(dev, skb);
+}
+EXPORT_SYMBOL(ieee802154_rx);
+
+struct rx_work {
+       struct sk_buff *skb;
+       struct work_struct work;
+       struct ieee802154_dev *dev;
+};
+
+static void ieee802154_rx_worker(struct work_struct *work)
+{
+       struct rx_work *rw = container_of(work, struct rx_work, work);
+       struct sk_buff *skb = rw->skb;
+
+       struct sk_buff *skb2 = skb_clone(skb, GFP_KERNEL);
+       netif_rx(skb2);
+
+       ieee802154_subif_rx(rw->dev, skb);
+       kfree(rw);
+}
+
+void ieee802154_rx_irqsafe(struct ieee802154_dev *dev,
+               struct sk_buff *skb, u8 lqi)
+{
+       struct ieee802154_priv *priv = ieee802154_to_priv(dev);
+       struct rx_work *work = kzalloc(sizeof(struct rx_work), GFP_ATOMIC);
+
+       if (!work)
+               return;
+
+       __ieee802154_rx_prepare(dev, skb, lqi);
+
+       INIT_WORK(&work->work, ieee802154_rx_worker);
+       work->skb = skb;
+       work->dev = dev;
+
+       queue_work(priv->dev_workqueue, &work->work);
+}
+EXPORT_SYMBOL(ieee802154_rx_irqsafe);
-- 
1.6.3.3


------------------------------------------------------------------------------
Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day 
trial. Simplify your report design, integration and deployment - and focus on 
what you do best, core application coding. Discover what's new with 
Crystal Reports now.  http://p.sf.net/sfu/bobj-july
_______________________________________________
Linux-zigbee-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/linux-zigbee-devel

Reply via email to