diff --git a/Makefile.dep b/Makefile.dep
index c2133a365fca034ac26a1c2fa1444333fbbb399c..549493a12d32ab68001219d8eaf71eba68b09554 100644
--- a/Makefile.dep
+++ b/Makefile.dep
@@ -201,3 +201,7 @@ ifneq (,$(filter oonf_common,$(USEMODULE)))
   USEPKG += oonf_api
   USEMODULE += socket_base
 endif
+
+ifneq (,$(filter ng_at86rf2%,$(USEMODULE)))
+  USEMODULE += ng_at86rf2xx
+endif
diff --git a/Makefile.pseudomodules b/Makefile.pseudomodules
index 3536c4ab5e15d80a8b60e4f4edb1abe8838d0876..e3f51876d9539374d8565e9b2c5eb1396089dfe4 100644
--- a/Makefile.pseudomodules
+++ b/Makefile.pseudomodules
@@ -3,3 +3,7 @@ PSEUDOMODULES += transport_layer
 PSEUDOMODULES += ng_ipv6_router
 PSEUDOMODULES += pktqueue
 PSEUDOMODULES += ng_netbase
+
+# include variants of the AT86RF2xx drivers as pseudo modules
+PSEUDOMODULES += ng_at86rf23%
+PSEUDOMODULES += ng_at86rf21%
diff --git a/drivers/Makefile.include b/drivers/Makefile.include
index 1075ca8593626bd3a1a38886518aa8ebbf38cc55..08f9fccf56aeac134aebb405c328ff95e779ea03 100644
--- a/drivers/Makefile.include
+++ b/drivers/Makefile.include
@@ -13,6 +13,9 @@ endif
 ifneq (,$(filter at86rf231,$(USEMODULE)))
     USEMODULE_INCLUDES += $(RIOTBASE)/drivers/at86rf231/include
 endif
+ifneq (,$(filter ng_at86rf2xx,$(USEMODULE)))
+    USEMODULE_INCLUDES += $(RIOTBASE)/drivers/ng_at86rf2xx/include
+endif
 ifneq (,$(filter isl29020,$(USEMODULE)))
     USEMODULE_INCLUDES += $(RIOTBASE)/drivers/isl29020/include
 endif
diff --git a/drivers/include/ng_at86rf2xx.h b/drivers/include/ng_at86rf2xx.h
new file mode 100644
index 0000000000000000000000000000000000000000..b62e5edfa8807ffead90208d9d8b16fa18c29cf9
--- /dev/null
+++ b/drivers/include/ng_at86rf2xx.h
@@ -0,0 +1,366 @@
+/*
+ * Copyright (C) 2015 Freie Universität Berlin
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser General
+ * Public License v2.1. See the file LICENSE in the top level directory for more
+ * details.
+ */
+
+/**
+ * @defgroup    drivers_ng_at86rf2xx AT86RF2xx based drivers
+ * @ingroup     drivers
+ *
+ * This module contains drivers for radio devices in Atmel's AT86RF2xx series.
+ * The driver is aimed to work with all devices of this series.
+ *
+ * @{
+ *
+ * @file
+ * @brief       Interface definition for AT86RF2xx based drivers
+ *
+ * @author      Thomas Eichinger <thomas.eichinger@fu-berlin.de>
+ * @author      Hauke Petersen <hauke.petersen@fu-berlin.de>
+ * @author      Kaspar Schleiser <kaspar@schleiser.de>
+ */
+
+#ifndef NG_AT86RF2XX_H_
+#define NG_AT86RF2XX_H_
+
+#include <stdint.h>
+
+#include "board.h"
+#include "periph/spi.h"
+#include "periph/gpio.h"
+#include "net/ng_netdev.h"
+#include "ng_at86rf2xx.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief   Maximum possible packet size in byte
+ */
+#define NG_AT86RF2XX_MAX_PKT_LENGTH     (127)
+
+/**
+ * @brief   Default addresses used if the CPUID module is not present
+ * @{
+ */
+#define NG_AT86RF2XX_DEFAULT_ADDR_SHORT (0x0230)
+#define NG_AT86RF2XX_DEFAULT_ADDR_LONG  (0x1222334455667788)
+/** @} */
+
+/**
+  * @brief   Channel configuration
+  * @{
+  */
+#ifdef MODULE_NG_AT86RF212B
+/* the AT86RF212B has a sub-1GHz radio */
+#define NG_AT86RF2XX_MIN_CHANNEL        (0)
+#define NG_AT86RF2XX_MAX_CHANNEL        (10)
+#define NG_AT86RF2XX_DEFAULT_CHANNEL    (5)
+#else
+#define NG_AT86RF2XX_MIN_CHANNEL        (11U)
+#define NG_AT86RF2XX_MAX_CHANNEL        (26U)
+#define NG_AT86RF2XX_DEFAULT_CHANNEL    (17U)
+#endif
+/** @} */
+
+/**
+ * @brief   Default PAN ID
+ *
+ * TODO: Read some global network stack specific configuration value
+ */
+#define NG_AT86RF2XX_DEFAULT_PANID      (0x0023)
+
+/**
+ * @brief   Default TX power (0dBm)
+ */
+#define NG_AT86RF2XX_DEFAULT_TXPOWER    (0U)
+
+/**
+ * @brief   Flags for device internal states (see datasheet)
+ * @{
+ */
+#define NG_AT86RF2XX_STATE_TRX_OFF      (0x08)  /**< idle */
+#define NG_AT86RF2XX_STATE_PLL_ON       (0x09)  /**< ready to transmit */
+#define NG_AT86RF2XX_STATE_SLEEP        (0x0f)  /**< sleep mode */
+#define NG_AT86RF2XX_STATE_BUSY_RX_AACK (0x11)  /**< busy receiving data */
+#define NG_AT86RF2XX_STATE_BUSY_TX_ARET (0x12)  /**< busy transmitting data */
+#define NG_AT86RF2XX_STATE_RX_AACK_ON   (0x16)  /**< wait for incoming data */
+#define NG_AT86RF2XX_STATE_TX_ARET_ON   (0x19)  /**< ready for sending data */
+#define NG_AT86RF2XX_STATE_IN_PROGRESS  (0x1f)  /**< ongoing state conversion */
+/** @} */
+
+/**
+ * @brief   Internal device option flags
+ * @{
+ */
+#define NG_AT86RF2XX_OPT_AUTOACK        (0x0001)    /**< auto ACKs active */
+#define NG_AT86RF2XX_OPT_CSMA           (0x0002)    /**< CSMA active */
+#define NG_AT86RF2XX_OPT_PROMISCUOUS    (0x0004)    /**< promiscuous mode
+                                                     *   active */
+#define NG_AT86RF2XX_OPT_PRELOADING     (0x0008)    /**< preloading enabled */
+#define NG_AT86RF2XX_OPT_TELL_TX_START  (0x0010)    /**< notify MAC layer on TX
+                                                     *   start */
+#define NG_AT86RF2XX_OPT_TELL_TX_END    (0x0020)    /**< notify MAC layer on TX
+                                                     *   finished */
+#define NG_AT86RF2XX_OPT_TELL_RX_START  (0x0040)    /**< notify MAC layer on RX
+                                                     *   start */
+#define NG_AT86RF2XX_OPT_TELL_RX_END    (0x0080)    /**< notify MAC layer on RX
+                                                     *   finished */
+#define NG_AT86RF2XX_OPT_RAWDUMP        (0x0100)    /**< pass RAW frame data to
+                                                     *   upper layer */
+#define NG_AT86RF2XX_OPT_SRC_ADDR_LONG  (0x0200)    /**< send data using long
+                                                     *   source address */
+#define NG_AT86RF2XX_OPT_USE_SRC_PAN    (0x0400)    /**< do not compress source
+                                                     *   PAN ID */
+/** @} */
+
+/**
+ * @brief   Device descriptor for AT86RF2XX radio devices
+ */
+typedef struct {
+    /* netdev fields */
+    const ng_netdev_driver_t *driver;   /**< pointer to the devices interface */
+    ng_netdev_event_cb_t event_cb;      /**< netdev event callback */
+    kernel_pid_t mac_pid;               /**< the driver's thread's PID */
+    /* device specific fields */
+    spi_t spi;                          /**< used SPI device */
+    gpio_t cs_pin;                      /**< chip select pin */
+    gpio_t sleep_pin;                   /**< sleep pin */
+    gpio_t reset_pin;                   /**< reset pin */
+    gpio_t int_pin;                     /**< external interrupt pin */
+    ng_nettype_t proto;                 /**< protocol the radio expects */
+    uint8_t state;                      /**< current state of the radio */
+    uint8_t seq_nr;                     /**< sequence number to use next */
+    uint8_t frame_len;                  /**< length of the current TX frame */
+    uint16_t pan;                       /**< currently used PAN ID */
+    uint8_t addr_short[2];              /**< the radio's short address */
+    uint8_t addr_long[8];               /**< the radio's long address */
+    uint16_t options;                   /**< state of used options */
+    uint8_t idle_state;                 /**< state to return to after sending */
+} ng_at86rf2xx_t;
+
+
+/**
+ * @brief   Initialize a given AT86RF2xx device
+ *
+ * @param[out] dev          device descriptor
+ * @param[in] spi           SPI bus the device is connected to
+ * @param[in] spi_speed     SPI speed to use
+ * @param[in] cs_pin        GPIO pin connected to chip select
+ * @param[in] int_pin       GPIO pin connected to the interrupt pin
+ * @param[in] sleep_pin     GPIO pin connected to the sleep pin
+ * @param[in] reset_pin     GPIO pin connected to the reset pin
+ *
+ * @return                  0 on success
+ * @return                  <0 on error
+ */
+int ng_at86rf2xx_init(ng_at86rf2xx_t *dev, spi_t spi, spi_speed_t spi_speed,
+                      gpio_t cs_pin, gpio_t int_pin,
+                      gpio_t sleep_pin, gpio_t reset_pin);
+
+/**
+ * @brief   Trigger a hardware reset and configure radio with default values
+ *
+ * @param[in] dev           device to reset
+ */
+void ng_at86rf2xx_reset(ng_at86rf2xx_t *dev);
+
+/**
+ * @brief   Trigger a clear channel assessment
+ *
+ * @param[in] dev           device to use
+ *
+ * @return                  true if channel is clear
+ * @return                  false if channel is busy
+ */
+bool ng_at86rf2xx_cca(ng_at86rf2xx_t *dev);
+
+/**
+ * @brief   Get the short address of the given device
+ *
+ * @param[in] dev           device to read from
+ *
+ * @return                  the currently set (2-byte) short address
+ */
+uint16_t ng_at86rf2xx_get_addr_short(ng_at86rf2xx_t *dev);
+
+/**
+ * @brief   Set the short address of the given device
+ *
+ * @param[in] dev           device to write to
+ * @param[in] addr          (2-byte) short address to set
+ */
+void ng_at86rf2xx_set_addr_short(ng_at86rf2xx_t *dev, uint16_t addr);
+
+/**
+ * @brief   Get the configured long address of the given device
+ *
+ * @param[in] dev           device to read from
+ *
+ * @return                  the currently set (8-byte) long address
+ */
+uint64_t ng_at86rf2xx_get_addr_long(ng_at86rf2xx_t *dev);
+
+/**
+ * @brief   Set the long address of the given device
+ *
+ * @param[in] dev           device to write to
+ * @param[in] addr          (8-byte) long address to set
+ */
+void ng_at86rf2xx_set_addr_long(ng_at86rf2xx_t *dev, uint64_t addr);
+
+/**
+ * @brief   Get the configured channel of the given device
+ *
+ * @param[in] dev           device to read from
+ *
+ * @return                  the currently set channel
+ */
+uint8_t ng_at86rf2xx_get_chan(ng_at86rf2xx_t *dev);
+
+/**
+ * @brief   Set the channel of the given device
+ *
+ * @param[in] dev           device to write to
+ * @param[in] chan          channel to set
+ */
+void ng_at86rf2xx_set_chan(ng_at86rf2xx_t *dev, uint8_t chan);
+
+/**
+ * @brief   Get the configured PAN ID of the given device
+ *
+ * @param[in] dev           device to read from
+ *
+ * @return                  the currently set PAN ID
+ */
+uint16_t ng_at86rf2xx_get_pan(ng_at86rf2xx_t *dev);
+
+/**
+ * @brief   Set the PAN ID of the given device
+ *
+ * @param[in] dev           device to write to
+ * @param[in] pan           PAN ID to set
+ */
+void ng_at86rf2xx_set_pan(ng_at86rf2xx_t *dev, uint16_t pan);
+
+/**
+ * @brief   Get the configured transmission power of the given device [in dBm]
+ *
+ * @param[in] dev           device to read from
+ *
+ * @return                  configured transmission power in dBm
+ */
+int16_t ng_at86rf2xx_get_txpower(ng_at86rf2xx_t *dev);
+
+/**
+ * @brief   Set the transmission power of the given device [in dBm]
+ *
+ * If the device does not support the exact dBm value given, it will set a value
+ * as close as possible to the given value. If the given value is larger or
+ * lower then the maximal or minimal possible value, the min or max value is
+ * set, respectively.
+ *
+ * @param[in] dev           device to write to
+ * @param[in] txpower       transmission power in dBm
+ */
+void ng_at86rf2xx_set_txpower(ng_at86rf2xx_t *dev, int16_t txpower);
+
+/**
+ * @brief   Enable or disable driver specific options
+ *
+ * @param[in] dev           device to set/clear option flag for
+ * @param[in] option        option to enable/disable
+ * @param[in] state         true for enable, false for disable
+ */
+void ng_at86rf2xx_set_option(ng_at86rf2xx_t *dev, uint16_t option, bool state);
+
+/**
+ * @brief   Get the given devices current internal state
+ *
+ * @param[in] dev           device to get state of
+ * @return                  the current state of the given device
+ */
+uint8_t ng_at86rf2xx_get_state(ng_at86rf2xx_t *dev);
+
+/**
+ * @brief   Set the state of the given device (trigger a state change)
+ *
+ * @param[in] dev           device to change state of
+ * @param[in] state         the targeted new state
+ */
+void ng_at86rf2xx_set_state(ng_at86rf2xx_t *dev, uint8_t state);
+
+/**
+ * @brief   Convenience function for simply sending data
+ *
+ * @note This function ignores the PRELOADING option
+ *
+ * @param[in] dev           device to use for sending
+ * @param[in] data          data to send (must include IEEE802.15.4 header)
+ * @param[in] len           length of @p data
+ *
+ * @return                  number of bytes that were actually send
+ * @return                  0 on error
+ */
+size_t ng_at86rf2xx_send(ng_at86rf2xx_t *dev, uint8_t *data, size_t len);
+
+/**
+ * @brief   Prepare for sending of data
+ *
+ * This function puts the given device into the TX state, so no receiving of
+ * data is possible after it was called.
+ *
+ * @param[in] dev            device to prepare for sending
+ */
+void ng_at86rf2xx_tx_prepare(ng_at86rf2xx_t *dev);
+
+/**
+ * @brief   Load chunks of data into the transmit buffer of the given device
+ *
+ * @param[in] dev           device to write data to
+ * @param[in] data          buffer containing the data to load
+ * @param[in] len           number of bytes in @p buffer
+ * @param[in] offset        offset used when writing data to internal buffer
+ *
+ * @return                  offset + number of bytes written
+ */
+size_t ng_at86rf2xx_tx_load(ng_at86rf2xx_t *dev, uint8_t *data, size_t len,
+                            size_t offset);
+
+/**
+ * @brief   Trigger sending of data previously loaded into transmit buffer
+ *
+ * @param[in] dev           device to trigger
+ */
+void ng_at86rf2xx_tx_exec(ng_at86rf2xx_t *dev);
+
+/**
+ * @brief   Read the length of a received packet
+ *
+ * @param dev               device to read from
+ *
+ * @return                  overall length of a received packet in byte
+ */
+size_t ng_at86rf2xx_rx_len(ng_at86rf2xx_t *dev);
+
+/**
+ * @brief   Read a chunk of data from the receive buffer of the given device
+ *
+ * @param[in]  dev          device to read from
+ * @param[out] data         buffer to write data to
+ * @param[in]  len          number of bytes to read from device
+ * @param[in]  offset       offset in the receive buffer
+ */
+void ng_at86rf2xx_rx_read(ng_at86rf2xx_t *dev, uint8_t *data, size_t len,
+                          size_t offset);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NG_AT86RF2XX_H_ */
+/** @} */
diff --git a/drivers/ng_at86rf2xx/Makefile b/drivers/ng_at86rf2xx/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..48422e909a47d7cd428d10fa73825060ccc8d8c2
--- /dev/null
+++ b/drivers/ng_at86rf2xx/Makefile
@@ -0,0 +1 @@
+include $(RIOTBASE)/Makefile.base
diff --git a/drivers/ng_at86rf2xx/include/ng_at86rf2xx_internal.h b/drivers/ng_at86rf2xx/include/ng_at86rf2xx_internal.h
new file mode 100644
index 0000000000000000000000000000000000000000..c5e8ca424cfb2ffdcf52f1c5323db3382d026965
--- /dev/null
+++ b/drivers/ng_at86rf2xx/include/ng_at86rf2xx_internal.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2013 Alaeddine Weslati <alaeddine.weslati@inria.fr>
+ * Copyright (C) 2015 Freie Universität Berlin
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser General
+ * Public License v2.1. See the file LICENSE in the top level directory for more
+ * details.
+ */
+
+/**
+ * @ingroup     drivers_ng_at86rf2xx
+ * @{
+ *
+ * @file
+ * @brief       Internal interfaces for AT86RF2xx drivers
+ *
+ * @author      Alaeddine Weslati <alaeddine.weslati@inria.fr>
+ * @author      Thomas Eichinger <thomas.eichinger@fu-berlin.de>
+ * @author      Hauke Petersen <hauke.petersen@fu-berlin.de>
+ */
+
+#ifndef NG_AT86RF2XX_INTERNAL_H_
+#define NG_AT86RF2XX_INTERNAL_H_
+
+#include <stdint.h>
+
+#include "ng_at86rf2xx.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief   Read from a register at address `addr` from device `dev`.
+ *
+ * @param[in] dev       device to read from
+ * @param[in] addr      address of the register to read
+ *
+ * @return              the value of the specified register
+ */
+uint8_t ng_at86rf2xx_reg_read(const ng_at86rf2xx_t *dev, const uint8_t addr);
+
+/**
+ * @brief   Write to a register at address `addr` from device `dev`.
+ *
+ * @param[in] dev       device to write to
+ * @param[in] addr      address of the register to write
+ * @param[in] value     value to write to the given register
+ */
+void ng_at86rf2xx_reg_write(const ng_at86rf2xx_t *dev, const uint8_t addr,
+                            const uint8_t value);
+
+/**
+ * @brief   Read a chunk of data from the SRAM of the given device
+ *
+ * @param[in]  dev      device to read from
+ * @param[in]  offset   starting address to read from [valid 0x00-0x7f]
+ * @param[out] data     buffer to read data into
+ * @param[in]  len      number of bytes to read from SRAM
+ */
+void ng_at86rf2xx_sram_read(const ng_at86rf2xx_t *dev,
+                            const uint8_t offset,
+                            uint8_t *data,
+                            const size_t len);
+
+/**
+ * @brief   Write a chunk of data into the SRAM of the given device
+ *
+ * @param[in] dev       device to write to
+ * @param[in] offset    address in the SRAM to write to [valid 0x00-0x7f]
+ * @param[in] data      data to copy into SRAM
+ * @param[in] len       number of bytes to write to SRAM
+ */
+void ng_at86rf2xx_sram_write(const ng_at86rf2xx_t *dev,
+                             const uint8_t offset,
+                             const uint8_t *data,
+                             const size_t len);
+
+/**
+ * @brief   Read the internal frame buffer of the given device
+ *
+ * Reading the frame buffer returns some extra bytes that are not accessible
+ * through reading the RAM directly.
+ *
+ * @param[in]  dev      device to read from
+ * @param[out] data     buffer to copy the data to
+ * @param[in]  len      number of bytes to read from the frame buffer
+ */
+void ng_at86rf2xx_fb_read(const ng_at86rf2xx_t *dev,
+                          uint8_t *data, const size_t len);
+
+/**
+ * @brief   Convenience function for reading the status of the given device
+ *
+ * @param[in] dev       device to read the status from
+ *
+ * @return              internal status of the given device
+ */
+uint8_t ng_at86rf2xx_get_status(const ng_at86rf2xx_t *dev);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NG_AT86RF2XX_INTERNAL_H_ */
+/** @} */
diff --git a/drivers/ng_at86rf2xx/include/ng_at86rf2xx_netdev.h b/drivers/ng_at86rf2xx/include/ng_at86rf2xx_netdev.h
new file mode 100644
index 0000000000000000000000000000000000000000..2085fb3e6e3c24850fbb9e8f8d642d17582a0a7c
--- /dev/null
+++ b/drivers/ng_at86rf2xx/include/ng_at86rf2xx_netdev.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2015 Freie Universität Berlin
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @ingroup     drivers_ng_at86rf2xx
+ * @{
+ *
+ * @file
+ * @brief       Netdev interface to AT86RF2xx drivers
+ *
+ * @author      Hauke Petersen <hauke.petersen@fu-berlin.de>
+ */
+
+#ifndef NG_AT86RF2XX_NETDEV_H_
+#define NG_AT86RF2XX_NETDEV_H_
+
+#include "net/ng_netdev.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief   Reference to the netdev device driver struct
+ */
+extern const ng_netdev_driver_t ng_at86rf2xx_driver;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NG_AT86RF2XX_NETDEV_H_ */
+/** @} */
diff --git a/drivers/ng_at86rf2xx/include/ng_at86rf2xx_registers.h b/drivers/ng_at86rf2xx/include/ng_at86rf2xx_registers.h
new file mode 100644
index 0000000000000000000000000000000000000000..35c7429cedb9e69bbd9b7ade255e79a09600f4b5
--- /dev/null
+++ b/drivers/ng_at86rf2xx/include/ng_at86rf2xx_registers.h
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2013 Alaeddine Weslati <alaeddine.weslati@inria.fr>
+ * Copyright (C) 2015 Freie Universität Berlin
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @ingroup     drivers_ng_at86rf2xx
+ * @{
+ *
+ * @file
+ * @brief       Register and command definitions for AT86RF2xx devices
+ *
+ * @author      Alaeddine Weslati <alaeddine.weslati@inria.fr>
+ * @author      Thomas Eichinger <thomas.eichinger@fu-berlin.de>
+ * @author      Hauke Petersen <hauke.petersen@fu-berlin.de>
+ * @author      Kaspar Schleiser <kaspar@schleiser.de>
+ */
+
+#ifndef NG_AT86RF2XX_REGISTERS_H_
+#define NG_AT86RF2XX_REGISTERS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief   Constant part numbers of the AT86RF2xx device family
+ * @{
+ */
+#define NG_AT86RF212B_PARTNUM       (0x07)
+#define NG_AT86RF231_PARTNUM        (0x03)
+#define NG_AT86RF232_PARTNUM        (0x0a)
+#define NG_AT86RF233_PARTNUM        (0x0b)
+/** @} */
+
+/**
+ * @brief   Assign the part number for the device we are building the driver for
+ * @{
+ */
+#ifdef MODULE_NG_AT86RF212B
+#define NG_AT86RF2XX_PARTNUM        NG_AT86RF212B_PARTNUM
+#elif MODULE_NG_AT86RF232
+#define NG_AT86RF2XX_PARTNUM        NG_AT86RF232_PARTNUM
+#elif MODULE_NG_AT86RF233
+#define NG_AT86RF2XX_PARTNUM        NG_AT86RF233_PARTNUM
+#else /* MODULE_NG_AT86RF231 as default device */
+#define NG_AT86RF2XX_PARTNUM        NG_AT86RF231_PARTNUM
+#endif
+/** @} */
+
+/**
+ * @brief   SPI access specifiers
+ * @{
+ */
+#define NG_AT86RF2XX_ACCESS_REG                                 (0x80)
+#define NG_AT86RF2XX_ACCESS_FB                                  (0x20)
+#define NG_AT86RF2XX_ACCESS_SRAM                                (0x00)
+#define NG_AT86RF2XX_ACCESS_READ                                (0x00)
+#define NG_AT86RF2XX_ACCESS_WRITE                               (0x40)
+/** @} */
+
+/**
+ * @brief   Register addresses
+ * @{
+ */
+#define NG_AT86RF2XX_REG__TRX_STATUS                            (0x01)
+#define NG_AT86RF2XX_REG__TRX_STATE                             (0x02)
+#define NG_AT86RF2XX_REG__TRX_CTRL_0                            (0x03)
+#define NG_AT86RF2XX_REG__TRX_CTRL_1                            (0x04)
+#define NG_AT86RF2XX_REG__PHY_TX_PWR                            (0x05)
+#define NG_AT86RF2XX_REG__PHY_RSSI                              (0x06)
+#define NG_AT86RF2XX_REG__PHY_ED_LEVEL                          (0x07)
+#define NG_AT86RF2XX_REG__PHY_CC_CCA                            (0x08)
+#define NG_AT86RF2XX_REG__CCA_THRES                             (0x09)
+#define NG_AT86RF2XX_REG__RX_CTRL                               (0x0A)
+#define NG_AT86RF2XX_REG__SFD_VALUE                             (0x0B)
+#define NG_AT86RF2XX_REG__TRX_CTRL_2                            (0x0C)
+#define NG_AT86RF2XX_REG__ANT_DIV                               (0x0D)
+#define NG_AT86RF2XX_REG__IRQ_MASK                              (0x0E)
+#define NG_AT86RF2XX_REG__IRQ_STATUS                            (0x0F)
+#define NG_AT86RF2XX_REG__VREG_CTRL                             (0x10)
+#define NG_AT86RF2XX_REG__BATMON                                (0x11)
+#define NG_AT86RF2XX_REG__XOSC_CTRL                             (0x12)
+#define NG_AT86RF2XX_REG__CC_CTRL_1                             (0x14)
+#define NG_AT86RF2XX_REG__RX_SYN                                (0x15)
+#define NG_AT86RF2XX_REG__XAH_CTRL_1                            (0x17)
+#define NG_AT86RF2XX_REG__FTN_CTRL                              (0x18)
+#define NG_AT86RF2XX_REG__PLL_CF                                (0x1A)
+#define NG_AT86RF2XX_REG__PLL_DCU                               (0x1B)
+#define NG_AT86RF2XX_REG__PART_NUM                              (0x1C)
+#define NG_AT86RF2XX_REG__VERSION_NUM                           (0x1D)
+#define NG_AT86RF2XX_REG__MAN_ID_0                              (0x1E)
+#define NG_AT86RF2XX_REG__MAN_ID_1                              (0x1F)
+#define NG_AT86RF2XX_REG__SHORT_ADDR_0                          (0x20)
+#define NG_AT86RF2XX_REG__SHORT_ADDR_1                          (0x21)
+#define NG_AT86RF2XX_REG__PAN_ID_0                              (0x22)
+#define NG_AT86RF2XX_REG__PAN_ID_1                              (0x23)
+#define NG_AT86RF2XX_REG__IEEE_ADDR_0                           (0x24)
+#define NG_AT86RF2XX_REG__IEEE_ADDR_1                           (0x25)
+#define NG_AT86RF2XX_REG__IEEE_ADDR_2                           (0x26)
+#define NG_AT86RF2XX_REG__IEEE_ADDR_3                           (0x27)
+#define NG_AT86RF2XX_REG__IEEE_ADDR_4                           (0x28)
+#define NG_AT86RF2XX_REG__IEEE_ADDR_5                           (0x29)
+#define NG_AT86RF2XX_REG__IEEE_ADDR_6                           (0x2A)
+#define NG_AT86RF2XX_REG__IEEE_ADDR_7                           (0x2B)
+#define NG_AT86RF2XX_REG__XAH_CTRL_0                            (0x2C)
+#define NG_AT86RF2XX_REG__CSMA_SEED_0                           (0x2D)
+#define NG_AT86RF2XX_REG__CSMA_SEED_1                           (0x2E)
+#define NG_AT86RF2XX_REG__CSMA_BE                               (0x2F)
+#define NG_AT86RF2XX_REG__TST_CTRL_DIGI                         (0x36)
+/** @} */
+
+/**
+ * @brief   Bitfield definitions for the TRX_CTRL_0 register
+ * @{
+ */
+#define NG_AT86RF2XX_TRX_CTRL_0_MASK__PAD_IO                    (0xC0)
+#define NG_AT86RF2XX_TRX_CTRL_0_MASK__PAD_IO_CLKM               (0x30)
+#define NG_AT86RF2XX_TRX_CTRL_0_MASK__CLKM_SHA_SEL              (0x08)
+#define NG_AT86RF2XX_TRX_CTRL_0_MASK__CLKM_CTRL                 (0x07)
+
+#define NG_AT86RF2XX_TRX_CTRL_0_DEFAULT__PAD_IO                 (0x00)
+#define NG_AT86RF2XX_TRX_CTRL_0_DEFAULT__PAD_IO_CLKM            (0x10)
+#define NG_AT86RF2XX_TRX_CTRL_0_DEFAULT__CLKM_SHA_SEL           (0x08)
+#define NG_AT86RF2XX_TRX_CTRL_0_DEFAULT__CLKM_CTRL              (0x01)
+
+#define NG_AT86RF2XX_TRX_CTRL_0_CLKM_CTRL__OFF                  (0x00)
+#define NG_AT86RF2XX_TRX_CTRL_0_CLKM_CTRL__1MHz                 (0x01)
+#define NG_AT86RF2XX_TRX_CTRL_0_CLKM_CTRL__2MHz                 (0x02)
+#define NG_AT86RF2XX_TRX_CTRL_0_CLKM_CTRL__4MHz                 (0x03)
+#define NG_AT86RF2XX_TRX_CTRL_0_CLKM_CTRL__8MHz                 (0x04)
+#define NG_AT86RF2XX_TRX_CTRL_0_CLKM_CTRL__16MHz                (0x05)
+#define NG_AT86RF2XX_TRX_CTRL_0_CLKM_CTRL__250kHz               (0x06)
+#define NG_AT86RF2XX_TRX_CTRL_0_CLKM_CTRL__62_5kHz              (0x07)
+/** @} */
+
+/**
+ * @brief   Bitfield definitions for the TRX_CTRL_1 register
+ * @{
+ */
+#define NG_AT86RF2XX_TRX_CTRL_1_MASK__PA_EXT_EN                 (0x80)
+#define NG_AT86RF2XX_TRX_CTRL_1_MASK__IRQ_2_EXT_EN              (0x40)
+#define NG_AT86RF2XX_TRX_CTRL_1_MASK__TX_AUTO_CRC_ON            (0x20)
+#define NG_AT86RF2XX_TRX_CTRL_1_MASK__RX_BL_CTRL                (0x10)
+#define NG_AT86RF2XX_TRX_CTRL_1_MASK__SPI_CMD_MODE              (0x0C)
+#define NG_AT86RF2XX_TRX_CTRL_1_MASK__IRQ_MASK_MODE             (0x02)
+#define NG_AT86RF2XX_TRX_CTRL_1_MASK__IRQ_POLARITY              (0x01)
+/** @} */
+
+/**
+ * @brief   Bitfield definitions for the TXR_CTRL_2 register
+ * @{
+ */
+#define NG_AT86RF2XX_TRX_CTRL_2_MASK__RX_SAFE_MODE              (0x80)
+#define NG_AT86RF2XX_TRX_CTRL_2_MASK__SUB_MODE                  (0x4)
+#define NG_AT86RF2XX_TRX_CTRL_2_MASK__OQPSK_DATA_RATE           (0x03)
+#define NG_AT86RF2XX_TRX_CTRL_2_MASK__OQPSK_SCRAM_EN            (0x20)
+/** @} */
+
+/**
+ * @brief   Bitfield definitions for the IRQ_STATUS register
+ * @{
+ */
+#define NG_AT86RF2XX_IRQ_STATUS_MASK__BAT_LOW                   (0x80)
+#define NG_AT86RF2XX_IRQ_STATUS_MASK__TRX_UR                    (0x40)
+#define NG_AT86RF2XX_IRQ_STATUS_MASK__AMI                       (0x20)
+#define NG_AT86RF2XX_IRQ_STATUS_MASK__CCA_ED_DONE               (0x10)
+#define NG_AT86RF2XX_IRQ_STATUS_MASK__TRX_END                   (0x08)
+#define NG_AT86RF2XX_IRQ_STATUS_MASK__RX_START                  (0x04)
+#define NG_AT86RF2XX_IRQ_STATUS_MASK__PLL_UNLOCK                (0x02)
+#define NG_AT86RF2XX_IRQ_STATUS_MASK__PLL_LOCK                  (0x01)
+/** @} */
+
+/**
+ * @brief   Bitfield definitions for the TRX_STATUS register
+ * @{
+ */
+#define NG_AT86RF2XX_TRX_STATUS_MASK__CCA_DONE                  (0x80)
+#define NG_AT86RF2XX_TRX_STATUS_MASK__CCA_STATUS                (0x40)
+#define NG_AT86RF2XX_TRX_STATUS_MASK__TRX_STATUS                (0x1F)
+
+#define NG_AT86RF2XX_TRX_STATUS__P_ON                           (0x00)
+#define NG_AT86RF2XX_TRX_STATUS__BUSY_RX                        (0x01)
+#define NG_AT86RF2XX_TRX_STATUS__BUSY_TX                        (0x02)
+#define NG_AT86RF2XX_TRX_STATUS__RX_ON                          (0x06)
+#define NG_AT86RF2XX_TRX_STATUS__TRX_OFF                        (0x08)
+#define NG_AT86RF2XX_TRX_STATUS__PLL_ON                         (0x09)
+#define NG_AT86RF2XX_TRX_STATUS__SLEEP                          (0x0F)
+#define NG_AT86RF2XX_TRX_STATUS__BUSY_RX_AACK                   (0x11)
+#define NG_AT86RF2XX_TRX_STATUS__BUSY_TX_ARET                   (0x12)
+#define NG_AT86RF2XX_TRX_STATUS__RX_AACK_ON                     (0x16)
+#define NG_AT86RF2XX_TRX_STATUS__TX_ARET_ON                     (0x19)
+#define NG_AT86RF2XX_TRX_STATUS__RX_ON_NOCLK                    (0x1C)
+#define NG_AT86RF2XX_TRX_STATUS__RX_AACK_ON_NOCLK               (0x1D)
+#define NG_AT86RF2XX_TRX_STATUS__BUSY_RX_AACK_NOCLK             (0x1E)
+#define NG_AT86RF2XX_TRX_STATUS__STATE_TRANSITION_IN_PROGRESS   (0x1F)
+/** @} */
+
+/**
+ * @brief   Bitfield definitions for the TRX_STATE register
+ * @{
+ */
+#define NG_AT86RF2XX_TRX_STATE_MASK__TRAC                       (0xe0)
+
+#define NG_AT86RF2XX_TRX_STATE__NOP                             (0x00)
+#define NG_AT86RF2XX_TRX_STATE__TX_START                        (0x02)
+#define NG_AT86RF2XX_TRX_STATE__FORCE_TRX_OFF                   (0x03)
+#define NG_AT86RF2XX_TRX_STATE__FORCE_PLL_ON                    (0x04)
+#define NG_AT86RF2XX_TRX_STATE__RX_ON                           (0x06)
+#define NG_AT86RF2XX_TRX_STATE__TRX_OFF                         (0x08)
+#define NG_AT86RF2XX_TRX_STATE__PLL_ON                          (0x09)
+#define NG_AT86RF2XX_TRX_STATE__RX_AACK_ON                      (0x16)
+#define NG_AT86RF2XX_TRX_STATE__TX_ARET_ON                      (0x19)
+#define NG_AT86RF2XX_TRX_STATE__TRAC_SUCCESS                    (0x00)
+#define NG_AT86RF2XX_TRX_STATE__TRAC_SUCCESS_DATA_PENDING       (0x20)
+#define NG_AT86RF2XX_TRX_STATE__TRAC_SUCCESS_WAIT_FOR_ACK       (0x40)
+#define NG_AT86RF2XX_TRX_STATE__TRAC_CHANNEL_ACCESS_FAILURE     (0x60)
+#define NG_AT86RF2XX_TRX_STATE__TRAC_NO_ACK                     (0xa0)
+#define NG_AT86RF2XX_TRX_STATE__TRAC_INVALID                    (0xe0)
+/** @} */
+
+/**
+ * @brief   Bitfield definitions for the PHY_CCA register
+ * @{
+ */
+#define NG_AT86RF2XX_PHY_CC_CCA_MASK__CCA_REQUEST               (0x80)
+#define NG_AT86RF2XX_PHY_CC_CCA_MASK__CCA_MODE                  (0x60)
+#define NG_AT86RF2XX_PHY_CC_CCA_MASK__CHANNEL                   (0x1F)
+
+#define NG_AT86RF2XX_PHY_CC_CCA_DEFAULT__CCA_MODE               (0x20)
+/** @} */
+
+/**
+ * @brief   Bitfield definitions for the PHY_TX_PWR register
+ * @{
+ */
+#define NG_AT86RF2XX_PHY_TX_PWR_MASK__PA_BUF_LT                 (0xC0)
+#define NG_AT86RF2XX_PHY_TX_PWR_MASK__PA_LT                     (0x30)
+#define NG_AT86RF2XX_PHY_TX_PWR_MASK__TX_PWR                    (0x0F)
+#define NG_AT86RF2XX_PHY_TX_PWR_DEFAULT__PA_BUF_LT              (0xC0)
+#define NG_AT86RF2XX_PHY_TX_PWR_DEFAULT__PA_LT                  (0x00)
+#define NG_AT86RF2XX_PHY_TX_PWR_DEFAULT__TX_PWR                 (0x00)
+#define NG_AT86RF2XX_PHY_TX_PWR_TX_PWR_VALUE__3dBm              (0x00)
+#define NG_AT86RF2XX_PHY_TX_PWR_TX_PWR_VALUE__2_8dBm            (0x01)
+#define NG_AT86RF2XX_PHY_TX_PWR_TX_PWR_VALUE__2_3dBm            (0x02)
+#define NG_AT86RF2XX_PHY_TX_PWR_TX_PWR_VALUE__1_8dBm            (0x03)
+#define NG_AT86RF2XX_PHY_TX_PWR_TX_PWR_VALUE__1_3dBm            (0x04)
+#define NG_AT86RF2XX_PHY_TX_PWR_TX_PWR_VALUE__0_7dBm            (0x05)
+#define NG_AT86RF2XX_PHY_TX_PWR_TX_PWR_VALUE__0dBm              (0x06)
+#define NG_AT86RF2XX_PHY_TX_PWR_TX_PWR_VALUE__m1dBm             (0x07)
+#define NG_AT86RF2XX_PHY_TX_PWR_TX_PWR_VALUE__m2dBm             (0x08)
+#define NG_AT86RF2XX_PHY_TX_PWR_TX_PWR_VALUE__m3dBm             (0x09)
+#define NG_AT86RF2XX_PHY_TX_PWR_TX_PWR_VALUE__m4dBm             (0x0A)
+#define NG_AT86RF2XX_PHY_TX_PWR_TX_PWR_VALUE__m5dBm             (0x0B)
+#define NG_AT86RF2XX_PHY_TX_PWR_TX_PWR_VALUE__m7dBm             (0x0C)
+#define NG_AT86RF2XX_PHY_TX_PWR_TX_PWR_VALUE__m9dBm             (0x0D)
+#define NG_AT86RF2XX_PHY_TX_PWR_TX_PWR_VALUE__m12dBm            (0x0E)
+#define NG_AT86RF2XX_PHY_TX_PWR_TX_PWR_VALUE__m17dBm            (0x0F)
+/** @} */
+
+/**
+ * @brief   Bitfield definitions for the PHY_RSSI register
+ * @{
+ */
+#define NG_AT86RF2XX_PHY_RSSI_MASK__RX_CRC_VALID                (0x80)
+#define NG_AT86RF2XX_PHY_RSSI_MASK__RND_VALUE                   (0x60)
+#define NG_AT86RF2XX_PHY_RSSI_MASK__RSSI                        (0x1F)
+/** @} */
+
+/**
+ * @brief   Bitfield definitions for the XOSC_CTRL register
+ * @{
+ */
+#define NG_AT86RF2XX_XOSC_CTRL__XTAL_MODE_CRYSTAL               (0xF0)
+#define NG_AT86RF2XX_XOSC_CTRL__XTAL_MODE_EXTERNAL              (0xF0)
+/** @} */
+
+/**
+ * @brief   Timing values
+ * @{
+ */
+#define NG_AT86RF2XX_TIMING__VCC_TO_P_ON                        (330)
+#define NG_AT86RF2XX_TIMING__SLEEP_TO_TRX_OFF                   (380)
+#define NG_AT86RF2XX_TIMING__TRX_OFF_TO_PLL_ON                  (110)
+#define NG_AT86RF2XX_TIMING__TRX_OFF_TO_RX_ON                   (110)
+#define NG_AT86RF2XX_TIMING__PLL_ON_TO_BUSY_TX                  (16)
+#define NG_AT86RF2XX_TIMING__RESET                              (100)
+#define NG_AT86RF2XX_TIMING__RESET_TO_TRX_OFF                   (37)
+/** @} */
+
+/**
+ * @brief   Bitfield definitions for the XAH_CTRL_1 register
+ * @{
+ */
+#define NG_AT86RF2XX_XAH_CTRL_1__AACK_FLTR_RES_FT               (0x20)
+#define NG_AT86RF2XX_XAH_CTRL_1__AACK_UPLD_RES_FT               (0x10)
+#define NG_AT86RF2XX_XAH_CTRL_1__AACK_ACK_TIME                  (0x04)
+#define NG_AT86RF2XX_XAH_CTRL_1__AACK_PROM_MODE                 (0x02)
+/** @} */
+
+/**
+ * @brief   Bitfield definitions for the CSMA_SEED_1 register
+ * @{
+ */
+#define NG_AT86RF2XX_CSMA_SEED_1__AACK_SET_PD                   (0x20)
+#define NG_AT86RF2XX_CSMA_SEED_1__AACK_DIS_ACK                  (0x10)
+#define NG_AT86RF2XX_CSMA_SEED_1__AACK_I_AM_COORD               (0x08)
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NG_AT86RF2XX_REGISTERS_H_ */
+/** @} */
diff --git a/drivers/ng_at86rf2xx/ng_at86rf2xx.c b/drivers/ng_at86rf2xx/ng_at86rf2xx.c
new file mode 100644
index 0000000000000000000000000000000000000000..7707fb69c0c7100d45cb075f661ce0b860133e72
--- /dev/null
+++ b/drivers/ng_at86rf2xx/ng_at86rf2xx.c
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2013 Alaeddine Weslati <alaeddine.weslati@inria.fr>
+ * Copyright (C) 2015 Freie Universität Berlin
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @ingroup     drivers_ng_at86rf2xx
+ * @{
+ *
+ * @file
+ * @brief       Implementation of public functions for AT86RF2xx drivers
+ *
+ * @author      Alaeddine Weslati <alaeddine.weslati@inria.fr>
+ * @author      Thomas Eichinger <thomas.eichinger@fu-berlin.de>
+ * @author      Hauke Petersen <hauke.petersen@fu-berlin.de>
+ * @author      Kaspar Schleiser <kaspar@schleiser.de>
+ *
+ * @}
+ */
+
+#include "hwtimer.h"
+#include "periph/cpuid.h"
+#include "net/ng_ieee802154.h"
+#include "net/ng_netbase.h"
+#include "ng_at86rf2xx_registers.h"
+#include "ng_at86rf2xx_internal.h"
+#include "ng_at86rf2xx_netdev.h"
+
+#define ENABLE_DEBUG (0)
+#include "debug.h"
+
+
+#define RESET_DELAY             (0U)        /* must be > 625ns */
+
+
+static void _irq_handler(void *arg)
+{
+    msg_t msg;
+    ng_at86rf2xx_t *dev = (ng_at86rf2xx_t *) arg;
+
+    /* tell driver thread about the interrupt */
+    msg.type = NG_NETDEV_MSG_TYPE_EVENT;
+    msg_send(&msg, dev->mac_pid);
+}
+
+int ng_at86rf2xx_init(ng_at86rf2xx_t *dev, spi_t spi, spi_speed_t spi_speed,
+                      gpio_t cs_pin, gpio_t int_pin,
+                      gpio_t sleep_pin, gpio_t reset_pin)
+{
+    dev->driver = &ng_at86rf2xx_driver;
+
+    /* initialize device descriptor */
+    dev->spi = spi;
+    dev->cs_pin = cs_pin;
+    dev->int_pin = int_pin;
+    dev->sleep_pin = sleep_pin;
+    dev->reset_pin = reset_pin;
+
+    /* initialise SPI */
+    spi_init_master(dev->spi, SPI_CONF_FIRST_RISING, spi_speed);
+    /* initialise GPIOs */
+    gpio_init_out(dev->cs_pin, GPIO_NOPULL);
+    gpio_set(dev->cs_pin);
+    gpio_init_out(dev->sleep_pin, GPIO_NOPULL);
+    gpio_clear(dev->sleep_pin);
+    gpio_init_out(dev->reset_pin, GPIO_NOPULL);
+    gpio_set(dev->reset_pin);
+    gpio_init_int(dev->int_pin, GPIO_NOPULL, GPIO_RISING, _irq_handler, dev);
+
+    /* test if the SPI is set up correctly and the device is responding */
+    if (ng_at86rf2xx_reg_read(dev, NG_AT86RF2XX_REG__PART_NUM) !=
+        NG_AT86RF2XX_PARTNUM) {
+        DEBUG("[ng_at86rf2xx] error: unable to read correct part number\n");
+        return -1;
+    }
+
+    /* reset device to default values and put it into RX state */
+    ng_at86rf2xx_reset(dev);
+    return 0;
+}
+
+void ng_at86rf2xx_reset(ng_at86rf2xx_t *dev)
+{
+    uint8_t tmp;
+#if CPUID_ID_LEN
+    uint8_t cpuid[CPUID_ID_LEN];
+    uint16_t addr_short;
+    uint64_t addr_long;
+#endif
+
+    /* trigger hardware reset */
+    gpio_clear(dev->reset_pin);
+    hwtimer_wait(HWTIMER_TICKS(RESET_DELAY));
+    gpio_set(dev->reset_pin);
+    /* reset options and sequence number */
+    dev->seq_nr = 0;
+    dev->options = 0;
+    /* set short and long address */
+#if CPUID_ID_LEN
+    cpuid_get(cpuid);
+#if CPUID < 8
+    /* in case CPUID_ID_LEN < 8, fill missing bytes with zeros */
+    for (int i = CPUID_ID_LEN; i < 8; i++) {
+        cpuid[i] = 0;
+    }
+#else
+    for (int i = 8; i < CPUID_ID_LEN; i++) {
+        cpuid[i & 0x07] ^= cpuid[i];
+    }
+#endif
+    /* make sure we mark the address as non-multicast and not globally unique */
+    cpuid[0] &= ~(0x01);
+    cpuid[0] |= 0x02;
+    /* copy and set long address */
+    memcpy(&addr_long, cpuid, 8);
+    ng_at86rf2xx_set_addr_long(dev, addr_long);
+    /* now compress the long addr to form the short address */
+    for (int i = 2; i < 8; i++) {
+        cpuid[i & 0x01] ^= cpuid[i];
+    }
+    memcpy(&addr_short, cpuid, 2);
+    ng_at86rf2xx_set_addr_short(dev, addr_short);
+#else
+    ng_at86rf2xx_set_addr_long(dev, NG_AT86RF2XX_DEFAULT_ADDR_LONG);
+    ng_at86rf2xx_set_addr_short(dev, NG_AT86RF2XX_DEFAULT_ADDR_SHORT);
+#endif
+    /* set default PAN id */
+    ng_at86rf2xx_set_pan(dev, NG_AT86RF2XX_DEFAULT_PANID);
+    /* set default channel */
+    ng_at86rf2xx_set_chan(dev, NG_AT86RF2XX_DEFAULT_CHANNEL);
+    /* set default TX power */
+    ng_at86rf2xx_set_txpower(dev, NG_AT86RF2XX_DEFAULT_TXPOWER);
+    /* set default options */
+    ng_at86rf2xx_set_option(dev, NG_AT86RF2XX_OPT_AUTOACK, true);
+    ng_at86rf2xx_set_option(dev, NG_AT86RF2XX_OPT_CSMA, true);
+    ng_at86rf2xx_set_option(dev, NG_AT86RF2XX_OPT_TELL_RX_END, true);
+    /* set default protocol */
+#ifdef MODULE_NG_SIXLOWPAN
+    dev->proto = NG_NETTYPE_SIXLOWPAN;
+#else
+    dev->proto = NG_NETTYPE_UNDEF;
+#endif
+    /* enable safe mode (protect RX FIFO until reading data starts) */
+    tmp = NG_AT86RF2XX_TRX_CTRL_2_MASK__RX_SAFE_MODE;
+#ifdef MODULE_NG_AT86RF212
+    /* settings used by Linux 4.0rc at86rf212b driver */
+    tmp |= (NG_AT86RF2XX_TRX_CTRL_2_MASK__SUB_MODE
+            | NG_AT86RF2XX_TRX_CTRL_2_MASK__OQPSK_SCRAM_EN);
+#endif
+    ng_at86rf2xx_reg_write(dev, NG_AT86RF2XX_REG__TRX_CTRL_2, tmp);
+    /* enable interrupts */
+    ng_at86rf2xx_reg_write(dev, NG_AT86RF2XX_REG__IRQ_MASK,
+                           (NG_AT86RF2XX_IRQ_STATUS_MASK__RX_START |
+                            NG_AT86RF2XX_IRQ_STATUS_MASK__TRX_END));
+    /* go into RX state */
+    ng_at86rf2xx_set_state(dev, NG_AT86RF2XX_STATE_RX_AACK_ON);
+
+    DEBUG("ng_at86rf2xx_reset(): reset complete.\n");
+}
+
+bool ng_at86rf2xx_cca(ng_at86rf2xx_t *dev)
+{
+    uint8_t tmp;
+    uint8_t status;
+
+    /* trigger CCA measurment */
+    tmp = ng_at86rf2xx_reg_read(dev, NG_AT86RF2XX_REG__PHY_CC_CCA);
+    tmp &= NG_AT86RF2XX_PHY_CC_CCA_MASK__CCA_REQUEST;
+    ng_at86rf2xx_reg_write(dev, NG_AT86RF2XX_REG__PHY_CC_CCA, tmp);
+    /* wait for result to be ready */
+    do {
+        status = ng_at86rf2xx_reg_read(dev, NG_AT86RF2XX_REG__TRX_STATUS);
+    } while (!(status & NG_AT86RF2XX_TRX_STATUS_MASK__CCA_DONE));
+    /* return according to measurement */
+    if (status & NG_AT86RF2XX_TRX_STATUS_MASK__CCA_STATUS) {
+        return true;
+    }
+    else {
+        return false;
+    }
+}
+
+size_t ng_at86rf2xx_send(ng_at86rf2xx_t *dev, uint8_t *data, size_t len)
+{
+    /* check data length */
+    if (len > NG_AT86RF2XX_MAX_PKT_LENGTH) {
+        DEBUG("[ng_at86rf2xx] Error: data to send exceeds max packet size\n");
+        return 0;
+    }
+    ng_at86rf2xx_tx_prepare(dev);
+    ng_at86rf2xx_tx_load(dev, data, len, 0);
+    ng_at86rf2xx_tx_exec(dev);
+    return len;
+}
+
+void ng_at86rf2xx_tx_prepare(ng_at86rf2xx_t *dev)
+{
+    uint8_t state;
+
+    /* make sure ongoing transmissions are finished */
+    do {
+        state = ng_at86rf2xx_get_state(dev);
+    }
+    while (state == NG_AT86RF2XX_STATE_BUSY_RX_AACK);
+    dev->idle_state = state;
+    ng_at86rf2xx_set_state(dev, NG_AT86RF2XX_STATE_TX_ARET_ON);
+    dev->frame_len = NG_IEEE802154_FCS_LEN;
+}
+
+size_t ng_at86rf2xx_tx_load(ng_at86rf2xx_t *dev, uint8_t *data,
+                            size_t len, size_t offset)
+{
+    dev->frame_len += (uint8_t)len;
+    ng_at86rf2xx_sram_write(dev, offset + 1, data, len);
+    return offset + len;
+}
+
+void ng_at86rf2xx_tx_exec(ng_at86rf2xx_t *dev)
+{
+    /* write frame length field in FIFO */
+    ng_at86rf2xx_sram_write(dev, 0, &(dev->frame_len), 1);
+    /* trigger sending of pre-loaded frame */
+    ng_at86rf2xx_reg_write(dev, NG_AT86RF2XX_REG__TRX_STATE,
+                           NG_AT86RF2XX_TRX_STATE__TX_START);
+    if (dev->event_cb && (dev->options & NG_AT86RF2XX_OPT_TELL_TX_START)) {
+        dev->event_cb(NETDEV_EVENT_TX_STARTED, NULL);
+    }
+}
+
+size_t ng_at86rf2xx_rx_len(ng_at86rf2xx_t *dev)
+{
+    uint8_t res;
+    ng_at86rf2xx_fb_read(dev, &res, 1);
+    return (size_t)(res - 2);           /* extract the PHR and LQI field */
+}
+
+void ng_at86rf2xx_rx_read(ng_at86rf2xx_t *dev, uint8_t *data, size_t len,
+                          size_t offset)
+{
+    /* when reading from SRAM, the different chips from the AT86RF2xx family
+     * behave differently: the AT86F233, the AT86RF232 and the ATRF86212B return
+     * frame length field (PHR) at position 0 and the first data byte at
+     * position 1.
+     * The AT86RF231 does not return the PHR field and return
+     * the first data byte at position 0.
+     */
+#ifndef MODULE_NG_AT86RF231
+    ng_at86rf2xx_sram_read(dev, offset + 1, data, len);
+#else
+    ng_at86rf2xx_sram_read(dev, offset, data, len);
+#endif
+}
diff --git a/drivers/ng_at86rf2xx/ng_at86rf2xx_getset.c b/drivers/ng_at86rf2xx/ng_at86rf2xx_getset.c
new file mode 100644
index 0000000000000000000000000000000000000000..44e37b40af04e5f6f5b029158e035bbd7f64bf0a
--- /dev/null
+++ b/drivers/ng_at86rf2xx/ng_at86rf2xx_getset.c
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2015 Freie Universität Berlin
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @ingroup     drivers_ng_at86rf2xx
+ * @{
+ *
+ * @file
+ * @brief       Getter and setter functions for the AT86RF2xx drivers
+ *
+ * @author      Thomas Eichinger <thomas.eichinger@fu-berlin.de>
+ * @author      Hauke Petersen <hauke.petersen@fu-berlin.de>
+ *
+ * @}
+ */
+
+#include "ng_at86rf2xx.h"
+#include "ng_at86rf2xx_internal.h"
+#include "ng_at86rf2xx_registers.h"
+#include "periph/spi.h"
+
+#define ENABLE_DEBUG (1)
+#include "debug.h"
+
+
+static const int16_t tx_pow_to_dbm[] = {3, 3, 2, 2, 1, 1, 0,
+                                        -1, -2, -3, -4, -5, -7, -9, -12, -17};
+
+static const uint8_t dbm_to_tx_pow[] = {0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e,
+                                        0x0e, 0x0d, 0x0d, 0x0c, 0x0c, 0x0b,
+                                        0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06,
+                                        0x05, 0x03, 0x00};
+
+uint16_t ng_at86rf2xx_get_addr_short(ng_at86rf2xx_t *dev)
+{
+    return (dev->addr_short[0] << 8) | dev->addr_short[1];
+}
+
+void ng_at86rf2xx_set_addr_short(ng_at86rf2xx_t *dev, uint16_t addr)
+{
+    dev->addr_short[0] = addr >> 8;
+    dev->addr_short[1] = addr & 0xff;
+    ng_at86rf2xx_reg_write(dev, NG_AT86RF2XX_REG__SHORT_ADDR_0,
+                           dev->addr_short[0]);
+    ng_at86rf2xx_reg_write(dev, NG_AT86RF2XX_REG__SHORT_ADDR_1,
+                           dev->addr_short[1]);
+}
+
+uint64_t ng_at86rf2xx_get_addr_long(ng_at86rf2xx_t *dev)
+{
+    uint64_t addr;
+    uint8_t *ap = (uint8_t *)(&addr);
+    for (int i = 0; i < 8; i++) {
+        ap[i] = dev->addr_long[7 - i];
+    }
+    return addr;
+}
+
+void ng_at86rf2xx_set_addr_long(ng_at86rf2xx_t *dev, uint64_t addr)
+{
+    for (int i = 0; i < 8; i++) {
+        dev->addr_long[i] = (addr >> ((7 - i) * 8));
+        ng_at86rf2xx_reg_write(dev, (NG_AT86RF2XX_REG__IEEE_ADDR_0 + i),
+                               dev->addr_long[i]);
+    }
+}
+
+uint8_t ng_at86rf2xx_get_chan(ng_at86rf2xx_t *dev)
+{
+    uint8_t res = ng_at86rf2xx_reg_read(dev, NG_AT86RF2XX_REG__PHY_CC_CCA);
+    return (res & NG_AT86RF2XX_PHY_CC_CCA_MASK__CHANNEL);
+}
+
+void ng_at86rf2xx_set_chan(ng_at86rf2xx_t *dev, uint8_t channel)
+{
+    uint8_t tmp;
+
+    if (channel < NG_AT86RF2XX_MIN_CHANNEL
+        || channel > NG_AT86RF2XX_MAX_CHANNEL) {
+        return;
+    }
+    tmp = ng_at86rf2xx_reg_read(dev, NG_AT86RF2XX_REG__PHY_CC_CCA);
+    tmp &= ~(NG_AT86RF2XX_PHY_CC_CCA_MASK__CHANNEL);
+    tmp |= (channel & NG_AT86RF2XX_PHY_CC_CCA_MASK__CHANNEL);
+    ng_at86rf2xx_reg_write(dev, NG_AT86RF2XX_REG__PHY_CC_CCA, tmp);
+}
+
+uint16_t ng_at86rf2xx_get_pan(ng_at86rf2xx_t *dev)
+{
+    return dev->pan;
+}
+
+void ng_at86rf2xx_set_pan(ng_at86rf2xx_t *dev, uint16_t pan)
+{
+    dev->pan = pan;
+    DEBUG("pan0: %u, pan1: %u\n", (uint8_t)pan, pan >> 8);
+    ng_at86rf2xx_reg_write(dev, NG_AT86RF2XX_REG__PAN_ID_0, (uint8_t)pan);
+    ng_at86rf2xx_reg_write(dev, NG_AT86RF2XX_REG__PAN_ID_1, (pan >> 8));
+}
+
+int16_t ng_at86rf2xx_get_txpower(ng_at86rf2xx_t *dev)
+{
+    uint8_t txpower = ng_at86rf2xx_reg_read(dev, NG_AT86RF2XX_REG__PHY_TX_PWR)
+                & NG_AT86RF2XX_PHY_TX_PWR_MASK__TX_PWR;
+    return tx_pow_to_dbm[txpower];
+}
+
+void ng_at86rf2xx_set_txpower(ng_at86rf2xx_t *dev, int16_t txpower)
+{
+    txpower += 17;
+    if (txpower < 0) {
+        txpower = 0;
+    } else if (txpower > 20) {
+        txpower = 20;
+    }
+    ng_at86rf2xx_reg_write(dev, NG_AT86RF2XX_REG__PHY_TX_PWR,
+                           dbm_to_tx_pow[txpower]);
+}
+
+void ng_at86rf2xx_set_option(ng_at86rf2xx_t *dev, uint16_t option, bool state)
+{
+    uint8_t tmp;
+
+    DEBUG("set option %i to %i\n", option, state);
+
+    /* set option field */
+    if (state) {
+        dev->options |= option;
+        /* trigger option specific actions */
+        switch (option) {
+            case NG_AT86RF2XX_OPT_CSMA:
+                DEBUG("[ng_at86rf2xx] opt: enabling CSMA mode\n");
+                /* TODO: en/disable csma */
+                break;
+            case NG_AT86RF2XX_OPT_PROMISCUOUS:
+                DEBUG("[ng_at86rf2xx] opt: enabling PROMISCUOUS mode\n");
+                /* disable auto ACKs in promiscuous mode */
+                tmp = ng_at86rf2xx_reg_read(dev, NG_AT86RF2XX_REG__CSMA_SEED_1);
+                tmp |= NG_AT86RF2XX_CSMA_SEED_1__AACK_DIS_ACK;
+                ng_at86rf2xx_reg_write(dev, NG_AT86RF2XX_REG__CSMA_SEED_1, tmp);
+                /* enable promiscuous mode */
+                tmp = ng_at86rf2xx_reg_read(dev, NG_AT86RF2XX_REG__XAH_CTRL_1);
+                tmp |= NG_AT86RF2XX_XAH_CTRL_1__AACK_PROM_MODE;
+                ng_at86rf2xx_reg_write(dev, NG_AT86RF2XX_REG__XAH_CTRL_1, tmp);
+                break;
+            case NG_AT86RF2XX_OPT_AUTOACK:
+                DEBUG("[ng_at86rf2xx] opt: enabling auto ACKs\n");
+                tmp = ng_at86rf2xx_reg_read(dev, NG_AT86RF2XX_REG__CSMA_SEED_1);
+                tmp &= ~(NG_AT86RF2XX_CSMA_SEED_1__AACK_DIS_ACK);
+                ng_at86rf2xx_reg_write(dev, NG_AT86RF2XX_REG__CSMA_SEED_1, tmp);
+                break;
+            default:
+                /* do nothing */
+                break;
+        }
+    }
+    else {
+        dev->options &= ~(option);
+        /* trigger option specific actions */
+        switch (option) {
+            case NG_AT86RF2XX_OPT_CSMA:
+                /* TODO: en/disable csma */
+                break;
+            case NG_AT86RF2XX_OPT_PROMISCUOUS:
+                /* disable promiscuous mode */
+                tmp = ng_at86rf2xx_reg_read(dev, NG_AT86RF2XX_REG__XAH_CTRL_1);
+                tmp &= ~(NG_AT86RF2XX_XAH_CTRL_1__AACK_PROM_MODE);
+                ng_at86rf2xx_reg_write(dev, NG_AT86RF2XX_REG__XAH_CTRL_1, tmp);
+                /* re-enable AUTOACK only if the option is set */
+                if (dev->options & NG_AT86RF2XX_OPT_AUTOACK) {
+                    tmp = ng_at86rf2xx_reg_read(dev,
+                                                NG_AT86RF2XX_REG__CSMA_SEED_1);
+                    tmp &= ~(NG_AT86RF2XX_CSMA_SEED_1__AACK_DIS_ACK);
+                    ng_at86rf2xx_reg_write(dev, NG_AT86RF2XX_REG__CSMA_SEED_1,
+                                           tmp);
+                }
+                break;
+            case NG_AT86RF2XX_OPT_AUTOACK:
+                tmp = ng_at86rf2xx_reg_read(dev, NG_AT86RF2XX_REG__CSMA_SEED_1);
+                tmp |= NG_AT86RF2XX_CSMA_SEED_1__AACK_DIS_ACK;
+                ng_at86rf2xx_reg_write(dev, NG_AT86RF2XX_REG__CSMA_SEED_1, tmp);
+                break;
+            default:
+                /* do nothing */
+                break;
+        }
+    }
+}
+
+uint8_t ng_at86rf2xx_get_state(ng_at86rf2xx_t *dev)
+{
+    uint8_t status = ng_at86rf2xx_get_status(dev);
+    return (status & 0x1f);
+}
+
+static inline void _set_state(ng_at86rf2xx_t *dev, uint8_t state)
+{
+    ng_at86rf2xx_reg_write(dev, NG_AT86RF2XX_REG__TRX_STATE, state);
+    while (ng_at86rf2xx_get_state(dev) != state);
+}
+
+void ng_at86rf2xx_set_state(ng_at86rf2xx_t *dev, uint8_t state)
+{
+    uint8_t old_state = ng_at86rf2xx_get_state(dev);
+
+    if (state == old_state) {
+        return;
+    }
+    /* make sure there is no ongoing transmission */
+    while (old_state == NG_AT86RF2XX_STATE_BUSY_RX_AACK ||
+           old_state == NG_AT86RF2XX_STATE_BUSY_TX_ARET) {
+        old_state = ng_at86rf2xx_get_state(dev);
+    }
+    /* check if we need to wake up from sleep mode */
+    if (old_state == NG_AT86RF2XX_STATE_SLEEP) {
+        DEBUG("at86rf2xx: waking up from sleep mode\n");
+        gpio_clear(dev->sleep_pin);
+        while (ng_at86rf2xx_get_state(dev) != NG_AT86RF2XX_STATE_TRX_OFF);
+    }
+    /* go to neutral TRX_OFF state */
+    _set_state(dev, NG_AT86RF2XX_STATE_TRX_OFF);
+    if (state == NG_AT86RF2XX_STATE_RX_AACK_ON ||
+        state == NG_AT86RF2XX_STATE_TX_ARET_ON) {
+        _set_state(dev, state);
+    } else if (state == NG_AT86RF2XX_STATE_SLEEP) {
+        gpio_set(dev->sleep_pin);
+        while (ng_at86rf2xx_get_state(dev) != NG_AT86RF2XX_STATE_SLEEP);
+    }
+}
diff --git a/drivers/ng_at86rf2xx/ng_at86rf2xx_internal.c b/drivers/ng_at86rf2xx/ng_at86rf2xx_internal.c
new file mode 100644
index 0000000000000000000000000000000000000000..95511009a8638c30b827e85edec24d1b781ddcfd
--- /dev/null
+++ b/drivers/ng_at86rf2xx/ng_at86rf2xx_internal.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2013 Alaeddine Weslati <alaeddine.weslati@inria.fr>
+ * Copyright (C) 2015 Freie Universität Berlin
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @ingroup     drivers_ng_at86rf2xx
+ * @{
+ *
+ * @file
+ * @brief       Implementation of driver internal functions
+ *
+ * @author      Alaeddine Weslati <alaeddine.weslati@inria.fr>
+ * @author      Thomas Eichinger <thomas.eichinger@fu-berlin.de>
+ * @author      Joakim Gebart <joakim.gebart@eistec.se>
+ * @author      Hauke Petersen <hauke.petersen@fu-berlin.de>
+ *
+ * @}
+ */
+
+#include "periph/spi.h"
+#include "periph/gpio.h"
+#include "ng_at86rf2xx_internal.h"
+#include "ng_at86rf2xx_registers.h"
+
+void ng_at86rf2xx_reg_write(const ng_at86rf2xx_t *dev,
+                            const uint8_t addr,
+                            const uint8_t value)
+{
+    spi_acquire(dev->spi);
+    gpio_clear(dev->cs_pin);
+    spi_transfer_reg(dev->spi,
+                     NG_AT86RF2XX_ACCESS_REG | NG_AT86RF2XX_ACCESS_WRITE | addr,
+                     value, 0);
+    gpio_set(dev->cs_pin);
+    spi_release(dev->spi);
+}
+
+uint8_t ng_at86rf2xx_reg_read(const ng_at86rf2xx_t *dev, const uint8_t addr)
+{
+    char value;
+
+    spi_acquire(dev->spi);
+    gpio_clear(dev->cs_pin);
+    spi_transfer_reg(dev->spi,
+                     NG_AT86RF2XX_ACCESS_REG | NG_AT86RF2XX_ACCESS_READ | addr,
+                     0, &value);
+    gpio_set(dev->cs_pin);
+    spi_release(dev->spi);
+
+    return (uint8_t)value;
+}
+
+void ng_at86rf2xx_sram_read(const ng_at86rf2xx_t *dev,
+                            const uint8_t offset,
+                            uint8_t *data,
+                            const size_t len)
+{
+    spi_acquire(dev->spi);
+    gpio_clear(dev->cs_pin);
+    spi_transfer_reg(dev->spi,
+                     NG_AT86RF2XX_ACCESS_SRAM | NG_AT86RF2XX_ACCESS_READ,
+                     (char)offset, NULL);
+    spi_transfer_bytes(dev->spi, NULL, (char*)data, len);
+    gpio_set(dev->cs_pin);
+    spi_release(dev->spi);
+}
+
+void ng_at86rf2xx_sram_write(const ng_at86rf2xx_t *dev,
+                             const uint8_t offset,
+                             const uint8_t *data,
+                             const size_t len)
+{
+    spi_acquire(dev->spi);
+    gpio_clear(dev->cs_pin);
+    spi_transfer_reg(dev->spi,
+                     NG_AT86RF2XX_ACCESS_SRAM | NG_AT86RF2XX_ACCESS_WRITE,
+                     (char)offset, NULL);
+    spi_transfer_bytes(dev->spi, (char*)data, NULL, len);
+    gpio_set(dev->cs_pin);
+    spi_release(dev->spi);
+}
+
+void ng_at86rf2xx_fb_read(const ng_at86rf2xx_t *dev,
+                          uint8_t *data,
+                          const size_t len)
+{
+    spi_acquire(dev->spi);
+    gpio_clear(dev->cs_pin);
+    spi_transfer_byte(dev->spi,
+                      NG_AT86RF2XX_ACCESS_FB | NG_AT86RF2XX_ACCESS_READ,
+                      NULL);
+    spi_transfer_bytes(dev->spi, NULL, (char *)data, len);
+    gpio_set(dev->cs_pin);
+    spi_release(dev->spi);
+}
+
+uint8_t ng_at86rf2xx_get_status(const ng_at86rf2xx_t *dev)
+{
+    return (ng_at86rf2xx_reg_read(dev, NG_AT86RF2XX_REG__TRX_STATUS)
+            & NG_AT86RF2XX_TRX_STATUS_MASK__TRX_STATUS);
+}
diff --git a/drivers/ng_at86rf2xx/ng_at86rf2xx_netdev.c b/drivers/ng_at86rf2xx/ng_at86rf2xx_netdev.c
new file mode 100644
index 0000000000000000000000000000000000000000..30c75194a50e9e83b53499f9e41180ebdf63e0d7
--- /dev/null
+++ b/drivers/ng_at86rf2xx/ng_at86rf2xx_netdev.c
@@ -0,0 +1,676 @@
+/*
+ * Copyright (C) 2015 Freie Universität Berlin
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @ingroup     drivers_ng_at86rf2xx
+ * @{
+ *
+ * @file
+ * @brief       Netdev adaption for the AT86RF2xx drivers
+ *
+ * @author      Thomas Eichinger <thomas.eichinger@fu-berlin.de>
+ * @author      Hauke Petersen <hauke.petersen@fu-berlin.de>
+ *
+ * @}
+ */
+
+#include "net/ng_ieee802154.h"
+#include "net/ng_netbase.h"
+#include "ng_at86rf2xx.h"
+#include "ng_at86rf2xx_netdev.h"
+#include "ng_at86rf2xx_internal.h"
+#include "ng_at86rf2xx_registers.h"
+
+#define ENABLE_DEBUG (0)
+#include "debug.h"
+
+/* TODO: generalize and move to ng_ieee802154 */
+static size_t _make_data_frame_hdr(ng_at86rf2xx_t *dev, uint8_t *buf,
+                                   ng_netif_hdr_t *hdr)
+{
+    int pos = 0;
+
+    /* we are building a data frame here */
+    buf[0] = NG_IEEE802154_FCF_TYPE_DATA;
+    buf[1] = 0x88;      /* use short src and dst addresses as starting point */
+
+    /* if AUTOACK is enabled, then we also expect ACKs for this packet */
+    if (dev->options & NG_AT86RF2XX_OPT_AUTOACK) {
+        buf[0] |= NG_IEEE802154_FCF_ACK_REQ;
+    }
+
+    /* fill in destination PAN ID */
+    pos = 3;
+    buf[pos++] = (uint8_t)((dev->pan) & 0xff);
+    buf[pos++] = (uint8_t)((dev->pan) >> 8);
+
+    /* fill in destination address */
+    if (hdr->flags &
+        (NG_NETIF_HDR_FLAGS_BROADCAST | NG_NETIF_HDR_FLAGS_MULTICAST)) {
+        buf[pos++] = 0xff;
+        buf[pos++] = 0xff;
+    }
+    else if (hdr->dst_l2addr_len == 2) {
+        uint8_t *dst_addr = ng_netif_hdr_get_dst_addr(hdr);
+        buf[pos++] = dst_addr[1];
+        buf[pos++] = dst_addr[0];
+    }
+    else if (hdr->dst_l2addr_len == 8) {
+        buf[1] |= 0x04;
+        uint8_t *dst_addr = ng_netif_hdr_get_dst_addr(hdr);
+        for (int i = 7;  i >= 0; i--) {
+            buf[pos++] = dst_addr[i];
+        }
+    }
+    else {
+        /* unsupported address length */
+        return 0;
+    }
+
+    /* fill in source PAN ID (if applicable */
+    if (dev->options & NG_AT86RF2XX_OPT_USE_SRC_PAN) {
+        buf[pos++] = (uint8_t)((dev->pan) & 0xff);
+        buf[pos++] = (uint8_t)((dev->pan) >> 8);
+    } else {
+        buf[0] |= NG_IEEE802154_FCF_PAN_COMP;
+    }
+
+    /* fill in source address */
+    if (dev->options & NG_AT86RF2XX_OPT_SRC_ADDR_LONG) {
+        buf[1] |= 0x40;
+        memcpy(&(buf[pos]), dev->addr_long, 8);
+        pos += 8;
+    }
+    else {
+        buf[pos++] = dev->addr_short[0];
+        buf[pos++] = dev->addr_short[1];
+    }
+
+    /* set sequence number */
+    buf[2] = dev->seq_nr++;
+    /* return actual header length */
+    return pos;
+}
+
+/* TODO: generalize and move to ng_ieee802154 */
+/* TODO: include security header implications */
+static size_t _get_frame_hdr_len(uint8_t *mhr)
+{
+    uint8_t tmp;
+    size_t len = 3;
+
+    /* figure out address sizes */
+    tmp = (mhr[1] & NG_IEEE802154_FCF_DST_ADDR_MASK);
+    if (tmp == NG_IEEE802154_FCF_DST_ADDR_SHORT) {
+        len += 4;
+    }
+    else if (tmp == NG_IEEE802154_FCF_DST_ADDR_LONG) {
+        len += 10;
+    }
+    else if (tmp != NG_IEEE802154_FCF_DST_ADDR_VOID) {
+        return 0;
+    }
+    tmp = (mhr[1] & NG_IEEE802154_FCF_SRC_ADDR_MASK);
+    if (tmp == NG_IEEE802154_FCF_SRC_ADDR_VOID) {
+        return len;
+    }
+    else {
+        if (!(mhr[0] & NG_IEEE802154_FCF_PAN_COMP)) {
+            len += 2;
+        }
+        if (tmp == NG_IEEE802154_FCF_SRC_ADDR_SHORT) {
+            return (len + 2);
+        }
+        else if (tmp == NG_IEEE802154_FCF_SRC_ADDR_LONG) {
+            return (len + 8);
+        }
+    }
+    return 0;
+}
+
+/* TODO: generalize and move to ng_ieee802154 */
+static ng_pktsnip_t *_make_netif_hdr(uint8_t *mhr)
+{
+    uint8_t tmp;
+    uint8_t *addr;
+    uint8_t src_len, dst_len;
+    ng_pktsnip_t *snip;
+    ng_netif_hdr_t *hdr;
+
+    /* figure out address sizes */
+    tmp = mhr[1] & NG_IEEE802154_FCF_SRC_ADDR_MASK;
+    if (tmp == NG_IEEE802154_FCF_SRC_ADDR_SHORT) {
+        src_len = 2;
+    }
+    else if (tmp == NG_IEEE802154_FCF_SRC_ADDR_LONG) {
+        src_len = 8;
+    }
+    else if (tmp == 0) {
+        src_len = 0;
+    }
+    else {
+        return NULL;
+    }
+    tmp = mhr[1] & NG_IEEE802154_FCF_DST_ADDR_MASK;
+    if (tmp == NG_IEEE802154_FCF_DST_ADDR_SHORT) {
+        dst_len = 2;
+    }
+    else if (tmp == NG_IEEE802154_FCF_DST_ADDR_LONG) {
+        dst_len = 8;
+    }
+    else if (tmp == 0) {
+        dst_len = 0;
+    }
+    else {
+        return NULL;
+    }
+    /* allocate space for header */
+    snip = ng_pktbuf_add(NULL, NULL, sizeof(ng_netif_hdr_t) + src_len + dst_len,
+                         NG_NETTYPE_NETIF);
+    if (snip == NULL) {
+        return NULL;
+    }
+    /* fill header */
+    hdr = (ng_netif_hdr_t *)snip->data;
+    ng_netif_hdr_init(hdr, src_len, dst_len);
+    if (dst_len > 0) {
+        tmp = 5 + dst_len;
+        addr = ng_netif_hdr_get_dst_addr(hdr);
+        for (int i = 0; i < dst_len; i++) {
+            addr[i] = mhr[5 + (dst_len - i) - 1];
+        }
+    }
+    else {
+        tmp = 3;
+    }
+    if (!(mhr[0] & NG_IEEE802154_FCF_PAN_COMP)) {
+        tmp += 2;
+    }
+    if (src_len > 0) {
+        addr = ng_netif_hdr_get_src_addr(hdr);
+        for (int i = 0; i < src_len; i++) {
+            addr[i] = mhr[tmp + (src_len - i) - 1];
+        }
+    }
+    return snip;
+}
+
+
+static int _send(ng_netdev_t *netdev, ng_pktsnip_t *pkt)
+{
+    ng_at86rf2xx_t *dev = (ng_at86rf2xx_t *)netdev;
+    ng_pktsnip_t *snip;
+    uint8_t mhr[NG_IEEE802154_MAX_HDR_LEN];
+    size_t len;
+
+    if (pkt == NULL) {
+        return -ENOMSG;
+    }
+    if (dev == NULL) {
+        ng_pktbuf_release(pkt);
+        return -ENODEV;
+    }
+
+    /* create 802.15.4 header */
+    len = _make_data_frame_hdr(dev, mhr, (ng_netif_hdr_t *)pkt->data);
+    if (len == 0) {
+        DEBUG("[ng_at86rf2xx] error: unable to create 802.15.4 header\n");
+        ng_pktbuf_release(pkt);
+        return -ENOMSG;
+    }
+    /* check if packet (header + payload + FCS) fits into FIFO */
+    snip = pkt->next;
+    if ((ng_pkt_len(snip) + len + 2) > NG_AT86RF2XX_MAX_PKT_LENGTH) {
+        DEBUG("[ng_at86rf2xx] error: packet too large to be send\n");
+        ng_pktbuf_release(pkt);
+        return -EOVERFLOW;
+    }
+
+    ng_at86rf2xx_tx_prepare(dev);
+    /* put header into FIFO */
+    len = ng_at86rf2xx_tx_load(dev, mhr, len, 0);
+    /* load packet data into FIFO */
+    while (snip) {
+        len += ng_at86rf2xx_tx_load(dev, snip->data, snip->size, len);
+        snip = snip->next;
+    }
+    /* send data out directly if pre-loading id disabled */
+    if (!(dev->options & NG_AT86RF2XX_OPT_PRELOADING)) {
+        ng_at86rf2xx_tx_exec(dev);
+    }
+    /* release packet */
+    ng_pktbuf_release(pkt);
+    /* return the number of bytes that were actually send out */
+    return (int)len;
+}
+
+static void _receive_data(ng_at86rf2xx_t *dev)
+{
+    uint8_t mhr[NG_IEEE802154_MAX_HDR_LEN];
+    size_t pkt_len, hdr_len;
+    ng_pktsnip_t *hdr, *payload = NULL;
+    ng_netif_hdr_t *netif;
+
+    /* get the size of the received packet (unlocks frame buffer protection) */
+    pkt_len = ng_at86rf2xx_rx_len(dev);
+
+    /* abort here already if no event callback is registered */
+    if (!dev->event_cb) {
+        return;
+    }
+
+    /* in raw mode, just read the binary dump into the packet buffer */
+    if (dev->options & NG_AT86RF2XX_OPT_RAWDUMP) {
+        payload = ng_pktbuf_add(NULL, NULL, pkt_len, NG_NETTYPE_UNDEF);
+        if (payload == NULL ) {
+            DEBUG("[ng_at86rf2xx] error: unable to allocate RAW data\n");
+            return;
+        }
+        ng_at86rf2xx_rx_read(dev, payload->data, pkt_len, 0);
+        dev->event_cb(NETDEV_EVENT_RX_COMPLETE, payload);
+        return;
+    }
+
+    /* get FCF field and compute 802.15.4 header length */
+    ng_at86rf2xx_rx_read(dev, mhr, 2, 0);
+    hdr_len = _get_frame_hdr_len(mhr);
+    if (hdr_len == 0) {
+        DEBUG("[ng_at86rf2xx] error: unable parse incoming frame header\n");
+        return;
+    }
+    /* read the rest of the header and parse the netif header from it */
+    ng_at86rf2xx_rx_read(dev, &(mhr[2]), hdr_len - 2, 2);
+    hdr = _make_netif_hdr(mhr);
+    if (hdr == NULL) {
+        DEBUG("[ng_at86rf2xx] error: unable to allocate netif header\n");
+        return;
+    }
+    /* fill missing fields in netif header */
+    netif = (ng_netif_hdr_t *)hdr->data;
+    netif->if_pid = dev->mac_pid;
+    ng_at86rf2xx_rx_read(dev, &(netif->lqi), 1, pkt_len);
+    netif->rssi = ng_at86rf2xx_reg_read(dev, NG_AT86RF2XX_REG__PHY_ED_LEVEL);
+
+    /* allocate payload */
+    payload = ng_pktbuf_add(hdr, NULL, (pkt_len - hdr_len), dev->proto);
+    if (payload == NULL) {
+        DEBUG("[ng_at86rf2xx] error: unable to allocate incoming payload\n");
+        ng_pktbuf_release(hdr);
+        return;
+    }
+    /* copy payload */
+    ng_at86rf2xx_rx_read(dev, payload->data, payload->size, hdr_len);
+    /* finish up and send data to upper layers */
+    dev->event_cb(NETDEV_EVENT_RX_COMPLETE, payload);
+}
+
+static int _set_state(ng_at86rf2xx_t *dev, ng_netconf_state_t state)
+{
+    switch (state) {
+        case NETCONF_STATE_SLEEP:
+            ng_at86rf2xx_set_state(dev, NG_AT86RF2XX_STATE_TRX_OFF);
+            break;
+        case NETCONF_STATE_IDLE:
+            ng_at86rf2xx_set_state(dev, NG_AT86RF2XX_STATE_RX_AACK_ON);
+            break;
+        case NETCONF_STATE_TX:
+            if (dev->options & NG_AT86RF2XX_OPT_PRELOADING) {
+                ng_at86rf2xx_tx_exec(dev);
+            }
+            break;
+        case NETCONF_STATE_RESET:
+            ng_at86rf2xx_reset(dev);
+            break;
+        default:
+            return -ENOTSUP;
+    }
+    return sizeof(ng_netconf_state_t);
+}
+
+ng_netconf_state_t _get_state(ng_at86rf2xx_t *dev)
+{
+    switch (ng_at86rf2xx_get_state(dev)) {
+        case NG_AT86RF2XX_STATE_SLEEP:
+            return NETCONF_STATE_SLEEP;
+        case NG_AT86RF2XX_STATE_BUSY_RX_AACK:
+            return NETCONF_STATE_RX;
+        case NG_AT86RF2XX_STATE_BUSY_TX_ARET:
+        case NG_AT86RF2XX_STATE_TX_ARET_ON:
+            return NETCONF_STATE_TX;
+        case NG_AT86RF2XX_STATE_RX_AACK_ON:
+        default:
+            return NETCONF_STATE_IDLE;
+    }
+}
+
+static int _get(ng_netdev_t *device, ng_netconf_opt_t opt,
+                void *val, size_t max_len)
+{
+    if (device == NULL) {
+        return -ENODEV;
+    }
+    ng_at86rf2xx_t *dev = (ng_at86rf2xx_t *) device;
+
+    switch (opt) {
+
+        case NETCONF_OPT_ADDRESS:
+            if (max_len < sizeof(uint16_t)) {
+                return -EOVERFLOW;
+            }
+            *((uint16_t *)val) = ng_at86rf2xx_get_addr_short(dev);
+            return sizeof(uint16_t);
+
+        case NETCONF_OPT_ADDRESS_LONG:
+            if (max_len < sizeof(uint64_t)) {
+                return -EOVERFLOW;
+            }
+            *((uint64_t *)val) = ng_at86rf2xx_get_addr_long(dev);
+            return sizeof(uint64_t);
+
+        case NETCONF_OPT_ADDR_LEN:
+            if (max_len < sizeof(uint16_t)) {
+                return -EOVERFLOW;
+            }
+            *((uint16_t *)val) = 2;
+            return sizeof(uint16_t);
+
+        case NETCONF_OPT_SRC_LEN:
+            if (max_len < sizeof(uint16_t)) {
+                return -EOVERFLOW;
+            }
+            if (dev->options & NG_AT86RF2XX_OPT_SRC_ADDR_LONG) {
+                *((uint16_t *)val) = 8;
+            }
+            else {
+                *((uint16_t *)val) = 2;
+            }
+            return sizeof(uint16_t);
+
+        case NETCONF_OPT_NID:
+            if (max_len < sizeof(uint16_t)) {
+                return -EOVERFLOW;
+            }
+            *((uint16_t *)val) = dev->pan;
+            return sizeof(uint16_t);
+
+        case NETCONF_OPT_CHANNEL:
+            if (max_len < sizeof(uint16_t)) {
+                return -EOVERFLOW;
+            }
+            ((uint8_t *)val)[1] = 0;
+            ((uint8_t *)val)[0] = ng_at86rf2xx_get_chan(dev);
+            return sizeof(uint16_t);
+
+        case NETCONF_OPT_TX_POWER:
+            if (max_len < sizeof(int16_t)) {
+                return -EOVERFLOW;
+            }
+            *((uint16_t *)val) = ng_at86rf2xx_get_txpower(dev);
+            return sizeof(uint16_t);
+
+        case NETCONF_OPT_MAX_PACKET_SIZE:
+            if (max_len < sizeof(int16_t)) {
+                return -EOVERFLOW;
+            }
+            *((uint16_t *)val) = NG_AT86RF2XX_MAX_PKT_LENGTH;
+            return sizeof(uint16_t);
+
+        case NETCONF_OPT_STATE:
+            if (max_len < sizeof(ng_netconf_state_t)) {
+                return -EOVERFLOW;
+            }
+            *((ng_netconf_state_t*)val) = _get_state(dev);
+            break;
+
+        case NETCONF_OPT_PRELOADING:
+            if (max_len < sizeof(ng_netconf_enable_t)) {
+                return -EOVERFLOW;
+            }
+            if (dev->options & NG_AT86RF2XX_OPT_PRELOADING) {
+                *((ng_netconf_enable_t *)val) = NETCONF_ENABLE;
+            }
+            else {
+                *((ng_netconf_enable_t *)val) = NETCONF_DISABLE;
+            }
+            return sizeof(ng_netconf_enable_t);
+
+        case NETCONF_OPT_AUTOACK:
+            if (max_len < sizeof(ng_netconf_enable_t)) {
+                return -EOVERFLOW;
+            }
+            if (dev->options & NG_AT86RF2XX_OPT_AUTOACK) {
+                *((ng_netconf_enable_t *)val) = NETCONF_ENABLE;
+            }
+            else {
+                *((ng_netconf_enable_t *)val) = NETCONF_DISABLE;
+            }
+            return sizeof(ng_netconf_enable_t);
+
+        case NETCONF_OPT_PROMISCUOUSMODE:
+            if (max_len < sizeof(ng_netconf_enable_t)) {
+                return -EOVERFLOW;
+            }
+            if (dev->options & NG_AT86RF2XX_OPT_PROMISCUOUS) {
+                *((ng_netconf_enable_t *)val) = NETCONF_ENABLE;
+            }
+            else {
+                *((ng_netconf_enable_t *)val) = NETCONF_DISABLE;
+            }
+            return sizeof(ng_netconf_enable_t);
+
+        case NETCONF_OPT_RAWMODE:
+            if (max_len < sizeof(ng_netconf_enable_t)) {
+                return -EOVERFLOW;
+            }
+            if (dev->options & NG_AT86RF2XX_OPT_RAWDUMP) {
+                *((ng_netconf_enable_t *)val) = NETCONF_ENABLE;
+            }
+            else {
+                *((ng_netconf_enable_t *)val) = NETCONF_DISABLE;
+            }
+            return sizeof(ng_netconf_enable_t);
+
+        case NETCONF_OPT_IS_CHANNEL_CLR:
+            if (max_len < sizeof(ng_netconf_enable_t)) {
+                return -EOVERFLOW;
+            }
+            if (ng_at86rf2xx_cca(dev)) {
+                *((ng_netconf_enable_t *)val) = NETCONF_ENABLE;
+            }
+            else {
+                *((ng_netconf_enable_t *)val) = NETCONF_DISABLE;
+            }
+            return sizeof(ng_netconf_enable_t);
+
+        default:
+            return -ENOTSUP;
+    }
+
+    return 0;
+}
+
+static int _set(ng_netdev_t *device, ng_netconf_opt_t opt,
+                void *val, size_t len)
+{
+    ng_at86rf2xx_t *dev = (ng_at86rf2xx_t *) device;
+
+    if (dev == NULL) {
+        return -ENODEV;
+    }
+
+    switch (opt) {
+        case NETCONF_OPT_ADDRESS:
+            if (len > sizeof(uint16_t)) {
+                return -EOVERFLOW;
+            }
+            ng_at86rf2xx_set_addr_short(dev, *((uint16_t*)val));
+            return sizeof(uint16_t);
+
+        case NETCONF_OPT_ADDRESS_LONG:
+            if (len > sizeof(uint64_t)) {
+                return -EOVERFLOW;
+            }
+            ng_at86rf2xx_set_addr_long(dev, *((uint64_t*)val));
+            return sizeof(uint64_t);
+
+        case NETCONF_OPT_SRC_LEN:
+            if (len > sizeof(uint16_t)) {
+                return -EOVERFLOW;
+            }
+            if (*((uint16_t *)val) == 2) {
+                ng_at86rf2xx_set_option(dev, NG_AT86RF2XX_OPT_SRC_ADDR_LONG,
+                                        false);
+            }
+            else if (*((uint16_t *)val) == 8) {
+                ng_at86rf2xx_set_option(dev, NG_AT86RF2XX_OPT_SRC_ADDR_LONG,
+                                        true);
+            }
+            else {
+                return -ENOTSUP;
+            }
+            return sizeof(uint16_t);
+
+        case NETCONF_OPT_NID:
+            if (len > sizeof(uint16_t)) {
+                return -EOVERFLOW;
+            }
+            ng_at86rf2xx_set_pan(dev, *((uint16_t *)val));
+            return sizeof(uint16_t);
+
+        case NETCONF_OPT_CHANNEL:
+            if (len != sizeof(uint16_t)) {
+                return -EINVAL;
+            }
+            uint8_t chan = ((uint8_t *)val)[0];
+            if (chan < NG_AT86RF2XX_MIN_CHANNEL ||
+                chan > NG_AT86RF2XX_MAX_CHANNEL) {
+                return -ENOTSUP;
+            }
+            ng_at86rf2xx_set_chan(dev, chan);
+            return sizeof(uint16_t);
+
+        case NETCONF_OPT_TX_POWER:
+            if (len > sizeof(int16_t)) {
+                return -EOVERFLOW;
+            }
+            ng_at86rf2xx_set_txpower(dev, *((int16_t *)val));
+            return sizeof(uint16_t);
+
+        case NETCONF_OPT_STATE:
+            if (len > sizeof(ng_netconf_state_t)) {
+                return -EOVERFLOW;
+            }
+            return _set_state(dev, *((ng_netconf_state_t *)val));
+
+        case NETCONF_OPT_AUTOACK:
+            if (len > sizeof(ng_netconf_enable_t)) {
+                return -EOVERFLOW;
+            }
+            ng_at86rf2xx_set_option(dev, NG_AT86RF2XX_OPT_AUTOACK,
+                                    ((bool *)val)[0]);
+            return sizeof(ng_netconf_enable_t);
+
+        case NETCONF_OPT_PRELOADING:
+            if (len > sizeof(ng_netconf_enable_t)) {
+                return -EOVERFLOW;
+            }
+            ng_at86rf2xx_set_option(dev, NG_AT86RF2XX_OPT_PRELOADING,
+                                    ((bool *)val)[0]);
+            return sizeof(ng_netconf_enable_t);
+
+        case NETCONF_OPT_PROMISCUOUSMODE:
+            if (len > sizeof(ng_netconf_enable_t)) {
+                return -EOVERFLOW;
+            }
+            ng_at86rf2xx_set_option(dev, NG_AT86RF2XX_OPT_PROMISCUOUS,
+                                    ((bool *)val)[0]);
+            return sizeof(ng_netconf_enable_t);
+
+        case NETCONF_OPT_RAWMODE:
+            if (len > sizeof(ng_netconf_enable_t)) {
+                return -EOVERFLOW;
+            }
+            ng_at86rf2xx_set_option(dev, NG_AT86RF2XX_OPT_RAWDUMP,
+                                    ((bool *)val)[0]);
+            return sizeof(ng_netconf_enable_t);
+
+        default:
+            return -ENOTSUP;
+    }
+
+    return 0;
+}
+
+static int _add_event_cb(ng_netdev_t *dev, ng_netdev_event_cb_t cb)
+{
+    if (dev == NULL) {
+        return -ENODEV;
+    }
+    if (dev->event_cb) {
+        return -ENOBUFS;
+    }
+
+    dev->event_cb = cb;
+    return 0;
+}
+
+static int _rem_event_cb(ng_netdev_t *dev, ng_netdev_event_cb_t cb)
+{
+    if (dev == NULL) {
+        return -ENODEV;
+    }
+    if (dev->event_cb != cb) {
+        return -ENOENT;
+    }
+
+    dev->event_cb = NULL;
+    return 0;
+}
+
+static void _isr_event(ng_netdev_t *device, uint32_t event_type)
+{
+    ng_at86rf2xx_t *dev = (ng_at86rf2xx_t *) device;
+    uint8_t irq_mask;
+    uint8_t state;
+
+    /* read (consume) device status */
+    irq_mask = ng_at86rf2xx_reg_read(dev, NG_AT86RF2XX_REG__IRQ_STATUS);
+    state = ng_at86rf2xx_get_state(dev);
+
+    if (irq_mask & NG_AT86RF2XX_IRQ_STATUS_MASK__RX_START) {
+        if (dev->event_cb && (dev->options & NG_AT86RF2XX_OPT_TELL_RX_START)) {
+            dev->event_cb(NETDEV_EVENT_RX_STARTED, NULL);
+        }
+        DEBUG("[ng_at86rf2xx] EVT - RX_START\n");
+    }
+    if (irq_mask & NG_AT86RF2XX_IRQ_STATUS_MASK__TRX_END) {
+        if (state == NG_AT86RF2XX_STATE_RX_AACK_ON || state == NG_AT86RF2XX_STATE_BUSY_RX_AACK) {
+            DEBUG("[ng_at86rf2xx] EVT - RX_END\n");
+            if (!(dev->options & NG_AT86RF2XX_OPT_TELL_RX_END)) {
+                return;
+            }
+            _receive_data(dev);
+        }
+        else if (state == NG_AT86RF2XX_STATE_TX_ARET_ON) {
+            if (dev->event_cb && (dev->options & NG_AT86RF2XX_OPT_TELL_TX_END)) {
+                dev->event_cb(NETDEV_EVENT_TX_COMPLETE, NULL);
+            }
+            DEBUG("[ng_at86rf2xx] EVT - TX_END\n");
+            ng_at86rf2xx_set_state(dev, dev->idle_state);
+        }
+    }
+}
+
+const ng_netdev_driver_t ng_at86rf2xx_driver = {
+    .send_data = _send,
+    .add_event_callback = _add_event_cb,
+    .rem_event_callback = _rem_event_cb,
+    .get = _get,
+    .set = _set,
+    .isr_event = _isr_event,
+};