diff mbox

[1/2] mac802154: add a software MAC 802.15.4 implementation

Message ID 1249913800-10176-2-git-send-email-dbaryshkov@gmail.com (mailing list archive)
State Not Applicable, archived
Headers show

Commit Message

Dmitry Baryshkov Aug. 10, 2009, 2:16 p.m. UTC
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 <dbaryshkov@gmail.com>
Signed-off-by: Sergey Lapin <slapin@ossfans.org>
---
 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

Comments

Johannes Berg Aug. 10, 2009, 2:27 p.m. UTC | #1
On Mon, 2009-08-10 at 18:16 +0400, Dmitry Eremin-Solenikov wrote:
> Some of available devices are just dump radios implementing IEEE 802.15.4

dump -> dumb

> Note this implementaion is neither certified, nor feature complete!

implementation

> + * @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.

That return value is strange.

> +++ b/net/Makefile
> @@ -61,6 +61,7 @@ ifneq ($(CONFIG_DCB),)
>  obj-y				+= dcb/
>  endif
>  obj-y				+= ieee802154/
> +obj-y				+= mac802154/

I think you should use obj-$(CONFIG_MAC802154) as there's no need to
recurse into the dir unless that's selected.

But does it make sense to actually have this little code as a separate
dir/module?

> +	  Note: this implementation is neither certified, nor feature
> +	  complete! We do not garantee that it is compatible w/ other
> +	  implementations, etc.

guarantee

But generally, you don't have to write that anyway due to the license.

> +	  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.

white space gone wrong

> +	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;

heh.

> +static int ieee802154_master_open(struct net_device *dev)
> +{

We've had no end to trouble with the master netdev, I suggest you look
into the current mac80211 code and see if you can get rid of it.

> +struct ieee802154_dev *ieee802154_alloc_device(size_t priv_size,
> +		struct ieee802154_ops *ops)
> +{

> +	if (!try_module_get(ops->owner)) {

That isn't necessary since the module is just calling your function. In
fact, doing this removes the ability to rmmod any module using this
which is bogus.

johannes
Paul E. McKenney Aug. 10, 2009, 5:28 p.m. UTC | #2
On Mon, Aug 10, 2009 at 06:16:39PM +0400, Dmitry Eremin-Solenikov wrote:
> 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!

One question below, otherwise looks plausible.  (I am not entirely sure
which lists are which...)

							Thanx, Paul

> Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
> Signed-off-by: Sergey Lapin <slapin@ossfans.org>
> ---
>  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 <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/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

So this list is different than the RCU-protected one, and readers
always hold locks when traversing it?

I am not familiar with this code, so might be missing something, but
it looks to me like the same list that RCU readers traverse.  If so,
need list_del_rcu() and synchronize_rcu().

> + */
> +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 <pavel.smolenskiy@gmail.com>
> + * Maxim Gorbachyov <maxim.gorbachev@siemens.com>
> + * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
> + */
> +#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 <slapin@ossfans.org>
> + * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
> + */
> +
> +#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 <pavel.smolenskiy@gmail.com>
> + * Maxim Gorbachyov <maxim.gorbachev@siemens.com>
> + * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
> + */
> +
> +#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
> 
> --
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Dmitry Baryshkov Aug. 11, 2009, 8:23 p.m. UTC | #3
On Mon, Aug 10, 2009 at 04:27:46PM +0200, Johannes Berg wrote:
> On Mon, 2009-08-10 at 18:16 +0400, Dmitry Eremin-Solenikov wrote:
> > + * @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.
> 
> That return value is strange.

The idea is that tx function can return info about errors during
transmit (busy channel, no ACK, etc).

> 
> > +++ b/net/Makefile
> > @@ -61,6 +61,7 @@ ifneq ($(CONFIG_DCB),)
> >  obj-y				+= dcb/
> >  endif
> >  obj-y				+= ieee802154/
> > +obj-y				+= mac802154/
> 
> I think you should use obj-$(CONFIG_MAC802154) as there's no need to
> recurse into the dir unless that's selected.

fixed.

> 
> But does it make sense to actually have this little code as a separate
> dir/module?

The submitted patch is only a part of written MAC code. And we still
aren't feature complete. Is there any sence in keeping mac80211
separate? IMHO yes. Same goes for mac802154.

> > +	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;
> 
> heh.

? C&p from mac80211 mostly :)

> 
> > +static int ieee802154_master_open(struct net_device *dev)
> > +{
> 
> We've had no end to trouble with the master netdev, I suggest you look
> into the current mac80211 code and see if you can get rid of it.

What troubles w/ master netdev did you have had? I do still see the
master devices in mac80211. IIUC, it's planned to replace (from
management point of view) the mdev with wiphy instance, isn't it?

> > +struct ieee802154_dev *ieee802154_alloc_device(size_t priv_size,
> > +		struct ieee802154_ops *ops)
> > +{
> 
> > +	if (!try_module_get(ops->owner)) {
> 
> That isn't necessary since the module is just calling your function. In
> fact, doing this removes the ability to rmmod any module using this
> which is bogus.

Hmm. I thought that one may be able to rmmod the module w/ ops while the
device is still hanging in the system. On the other hand, ops are
provided by the driver, so if one rmmods the driver and the device is
still there, it's a bug...
Removing now.
Dmitry Baryshkov Aug. 11, 2009, 8:29 p.m. UTC | #4
On Mon, Aug 10, 2009 at 10:28:43AM -0700, Paul E. McKenney wrote:
> On Mon, Aug 10, 2009 at 06:16:39PM +0400, Dmitry Eremin-Solenikov wrote:
> > 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!
> 
> One question below, otherwise looks plausible.  (I am not entirely sure
> which lists are which...)
> 
> 							Thanx, Paul

[skipped]

> 
> > +
> > +/*
> > + * This is for hw unregistration only, as it doesn't do RCU locking
> 
> So this list is different than the RCU-protected one, and readers
> always hold locks when traversing it?
> 
> I am not familiar with this code, so might be missing something, but
> it looks to me like the same list that RCU readers traverse.  If so,
> need list_del_rcu() and synchronize_rcu().

On the first glance, yes. On the second glance, list_del instead of
list_del_rcu should be safe as there should be now other list traversals
at the same time. I'll think about it however.

> 
> > + */
> > +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);
> > +	}
> > +}
Johannes Berg Aug. 11, 2009, 8:39 p.m. UTC | #5
On Wed, 2009-08-12 at 00:23 +0400, Dmitry Eremin-Solenikov wrote:

> > That return value is strange.
> 
> The idea is that tx function can return info about errors during
> transmit (busy channel, no ACK, etc).

Well, TX functions don't usually wait for anything so busy channel or no
ack wouldn't be possible to return there but more like a TX status, and
I think people are more used to NETDEV_TX_OK etc. return values. But
it's your choice to make, obviously. If you want to return specific
information like that though I would recommend making an enum so you get
at least some type checking (and things like 'return -1' are more
obviously wrong).

> > We've had no end to trouble with the master netdev, I suggest you look
> > into the current mac80211 code and see if you can get rid of it.
> 
> What troubles w/ master netdev did you have had? I do still see the
> master devices in mac80211. IIUC, it's planned to replace (from
> management point of view) the mdev with wiphy instance, isn't it?

Mostly the userspace API was a mess, and you can't actually _do_
anything with the master netdev. It also doesn't see all the frames,
only outgoing. It's very strange.

Yes, you still see it in the current tree, but it's gone in our -next
tree.

The biggest problem is that it really clutters up the userspace API
since you can't do any netdev things with it, it's just a placeholder.

In addition to that, you can't put anything into skb->cb, then push the
frame to the master netdev, and expect things in skb->cb to still be
there when the frame arrives at the master netdev. Not sure you do that
(I hope not because that would be very buggy), but eventually you'll
probably find that you do want that, etc.

IMHO it's just better practise to not use it in situations like this
where it can't be really used as a netdev.

> Hmm. I thought that one may be able to rmmod the module w/ ops while the
> device is still hanging in the system. On the other hand, ops are
> provided by the driver, so if one rmmods the driver and the device is
> still there, it's a bug...

Exactly. You _want_ to be able to rmmod the module and have it
unregister itself.

johannes
Dmitry Baryshkov Aug. 12, 2009, 1:06 p.m. UTC | #6
On Tue, Aug 11, 2009 at 10:39:58PM +0200, Johannes Berg wrote:
> On Wed, 2009-08-12 at 00:23 +0400, Dmitry Eremin-Solenikov wrote:
> 
> > > That return value is strange.
> > 
> > The idea is that tx function can return info about errors during
> > transmit (busy channel, no ACK, etc).
> 
> Well, TX functions don't usually wait for anything so busy channel or no
> ack wouldn't be possible to return there but more like a TX status, and
> I think people are more used to NETDEV_TX_OK etc. return values. But
> it's your choice to make, obviously. If you want to return specific
> information like that though I would recommend making an enum so you get
> at least some type checking (and things like 'return -1' are more
> obviously wrong).

Currently we do all the work from special worker threads, so it's
possible for this callback to sleep. The error isn't yet propagated to
upper layers (there is a huge TODO there), anyway.

> 
> > > We've had no end to trouble with the master netdev, I suggest you look
> > > into the current mac80211 code and see if you can get rid of it.
> > 
> > What troubles w/ master netdev did you have had? I do still see the
> > master devices in mac80211. IIUC, it's planned to replace (from
> > management point of view) the mdev with wiphy instance, isn't it?
> 
> Mostly the userspace API was a mess, and you can't actually _do_
> anything with the master netdev. It also doesn't see all the frames,
> only outgoing. It's very strange.

We were using master netdevices for several purposes:
1) ip link add link mwpanX type wpan, so that we have out-of-box support
   for radio additions. That's really nice thing to have.

2) for SOCK_RAW implementation that can be used to send raw packets
   over-the-air/receive raw packets. I think we can use af_packet for
   this, but I'm still not sure about packet injection.

3) On the RX, we did send a clone of skb to the mdev and clones to slave
   interfaces, thus overcoming your last argument.

> The biggest problem is that it really clutters up the userspace API
> since you can't do any netdev things with it, it's just a placeholder.
> 
> In addition to that, you can't put anything into skb->cb, then push the
> frame to the master netdev, and expect things in skb->cb to still be
> there when the frame arrives at the master netdev. Not sure you do that
> (I hope not because that would be very buggy), but eventually you'll
> probably find that you do want that, etc.

Hmm. It works for us. Could you please tell me more about the problems
with skb->cb ?

> IMHO it's just better practise to not use it in situations like this
> where it can't be really used as a netdev.

We did use it as a netdev, however we can probably live without it.
I'll rethink this interface.
Johannes Berg Aug. 12, 2009, 1:13 p.m. UTC | #7
On Wed, 2009-08-12 at 17:06 +0400, Dmitry Eremin-Solenikov wrote:

> > In addition to that, you can't put anything into skb->cb, then push the
> > frame to the master netdev, and expect things in skb->cb to still be
> > there when the frame arrives at the master netdev. Not sure you do that
> > (I hope not because that would be very buggy), but eventually you'll
> > probably find that you do want that, etc.
> 
> Hmm. It works for us. Could you please tell me more about the problems
> with skb->cb ?

Uh, well, you don't own the skb->cb between dev_queue_xmit() and
ndo_start_xmit(). In fact, your phy_cb data will be overwritten by the
qdisc.

So your code is completely and utterly broken just like mac80211 was for
a long time.

Also, looking into this a bit more, I see no reason to allocate a work
struct every time you get a frame -- better just queue them up and stick
everything else into skb->cb. In fact, you don't need a master netdev
here anyway, just move all code from ieee802154_master_hard_start_xmit()
into ieee802154_net_xmit().

johannes
Dmitry Baryshkov Aug. 12, 2009, 8:46 p.m. UTC | #8
On Wed, Aug 12, 2009 at 03:13:57PM +0200, Johannes Berg wrote:
> On Wed, 2009-08-12 at 17:06 +0400, Dmitry Eremin-Solenikov wrote:
> 
> > > In addition to that, you can't put anything into skb->cb, then push the
> > > frame to the master netdev, and expect things in skb->cb to still be
> > > there when the frame arrives at the master netdev. Not sure you do that
> > > (I hope not because that would be very buggy), but eventually you'll
> > > probably find that you do want that, etc.
> > 
> > Hmm. It works for us. Could you please tell me more about the problems
> > with skb->cb ?
> 
> Uh, well, you don't own the skb->cb between dev_queue_xmit() and
> ndo_start_xmit(). In fact, your phy_cb data will be overwritten by the
> qdisc.

Hmmm. Really weird. Then, if we want to pass data from socket layer to
MAC layer, we should place data in skb->data and not in skb->cb (like
radiotap header)?

> Also, looking into this a bit more, I see no reason to allocate a work
> struct every time you get a frame -- better just queue them up and stick
> everything else into skb->cb. In fact, you don't need a master netdev
> here anyway, just move all code from ieee802154_master_hard_start_xmit()
> into ieee802154_net_xmit().
> 

Nice idea. Thanks a lot!
diff mbox

Patch

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 <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/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 <pavel.smolenskiy@gmail.com>
+ * Maxim Gorbachyov <maxim.gorbachev@siemens.com>
+ * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+ */
+#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 <slapin@ossfans.org>
+ * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+ */
+
+#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 <pavel.smolenskiy@gmail.com>
+ * Maxim Gorbachyov <maxim.gorbachev@siemens.com>
+ * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+ */
+
+#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);