/*
 * Copyright (C) 2014 INRIA
 * 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     driver_xbee
 * @{
 *
 * @file
 * @brief       High-level driver implementation for the XBee S1 802.15.4 modem
 *
 * @author      Kévin Roussel <kevin.roussel@inria.fr>
 * @author      Hauke Petersen <hauke.petersen@fu-berlin.de>
 *
 * @}
 */

#include <string.h>

#include "xbee.h"
#include "xtimer.h"
#include "msg.h"
#include "net/eui64.h"
#include "net/ieee802154.h"
#include "periph/cpuid.h"

#define ENABLE_DEBUG    (0)
#include "debug.h"

/**
 * @brief   Internal driver event type when RX is finished
 */
#define ISR_EVENT_RX_DONE           (0x0001)

/**
 * @brief   Delay when entering command mode, must be > 1s
 */
#define ENTER_CMD_MODE_DELAY        (1100U * 1000U)
/**
 * @brief   Delay when resetting the device, 10ms
 */
#define RESET_DELAY                 (10U * 1000U)

/**
 * @brief   Timeout for receiving AT command response
 */
#define RESP_TIMEOUT_USEC           (SEC_IN_USEC)

/**
 * @brief   Start delimiter in API frame mode
 */
#define API_START_DELIMITER         (0x7e)

/**
 * @brief   Command IDs when communicating in API frame mode
 * @{
 */
#define API_ID_MODEM_STATUS         (0x8a)  /**< modem status frame */
#define API_ID_AT                   (0x08)  /**< AT command request frame */
#define API_ID_AT_QUEUE             (0x09)  /**< queued AT command frame */
#define API_ID_AT_RESP              (0x88)  /**< AT command response frame */
#define API_ID_TX_LONG_ADDR         (0x00)  /**< TX frame (long address) */
#define API_ID_TX_SHORT_ADDR        (0x01)  /**< TX frame (short address) */
#define API_ID_TX_RESP              (0x89)  /**< TX response frame */
#define API_ID_RX_LONG_ADDR         (0x80)  /**< RX frame (long address) */
#define API_ID_RX_SHORT_ADDR        (0x81)  /**< RX frame (short address) */
/** @} */

/**
 * @brief   Internal option flags (to be expanded if needed)
 * @{
 */
#define OPT_DIS_AUTO_ACK            (0x01)  /**< disable sending of auto ACKs */
/** @} */

/**
 * @brief   Data-structure describing AT command response frames
 */
typedef struct {
    uint8_t status;         /**< AT command response status, 0 for success */
    uint8_t data[8];        /**< returned data from the AT command */
    uint8_t data_len;       /**< number ob bytes written to @p data */
} resp_t;


/*
 * Driver's internal utility functions
 */
static uint8_t _cksum(uint8_t *buf, size_t size)
{
    uint8_t res = 0xff;
    for (size_t i = 3; i < size; i++) {
        res -= buf[i];
    }
    return res;
}

static void _at_cmd(xbee_t *dev, const char *cmd)
{
    DEBUG("xbee: AT_CMD: %s\n", cmd);

    uart_write(dev->uart, (uint8_t *)cmd, strlen(cmd));
}

static void isr_resp_timeout(void *arg)
{
    xbee_t *dev = (xbee_t *)arg;

    if (mutex_trylock(&(dev->resp_lock)) == 0) {
        dev->int_state = XBEE_INT_STATE_IDLE;
    }

    mutex_unlock(&(dev->resp_lock));
}

static void _api_at_cmd(xbee_t *dev, uint8_t *cmd, uint8_t size, resp_t *resp)
{
    DEBUG("xbee: AT_CMD: %s\n", cmd);

    /* acquire TX lock */
    mutex_lock(&(dev->tx_lock));
    /* construct API frame */
    dev->tx_buf[0] = API_START_DELIMITER;
    dev->tx_buf[1] = (size + 2) >> 8;
    dev->tx_buf[2] = (size + 2) & 0xff;
    dev->tx_buf[3] = API_ID_AT;
    dev->tx_buf[4] = 1;             /* use fixed frame id */
    memcpy(dev->tx_buf + 5, cmd, size);
    dev->tx_buf[size + 5] = _cksum(dev->tx_buf, size + 5);

    /* reset the response data counter */
    dev->resp_count = 0;
    /* start send data */
    uart_write(dev->uart, dev->tx_buf, size + 6);

    xtimer_ticks64_t sent_time = xtimer_now64();

    xtimer_t resp_timer;

    resp_timer.callback = isr_resp_timeout;
    resp_timer.arg = dev;

    xtimer_set(&resp_timer, RESP_TIMEOUT_USEC);

    /* wait for results */
    while ((dev->resp_limit != dev->resp_count) &&
           (xtimer_less(
                xtimer_diff32_64(xtimer_now64(), sent_time),
                xtimer_ticks_from_usec(RESP_TIMEOUT_USEC)))) {
        mutex_lock(&(dev->resp_lock));
    }

    xtimer_remove(&resp_timer);

    if (dev->resp_limit != dev->resp_count) {
        DEBUG("xbee: response timeout\n");
        resp->status = 255;
        mutex_unlock(&(dev->tx_lock));

        return;
    }

    /* populate response data structure */
    resp->status = dev->resp_buf[3];
    resp->data_len = dev->resp_limit - 5;
    if (resp->data_len > 0) {
        memcpy(resp->data, &(dev->resp_buf[4]), resp->data_len);
    }
    mutex_unlock(&(dev->tx_lock));
}

/*
 * Interrupt callbacks
 */
static void _rx_cb(void *arg, uint8_t c)
{
    xbee_t *dev = (xbee_t *)arg;
    msg_t msg;

    switch (dev->int_state) {
        case XBEE_INT_STATE_IDLE:
            /* check for beginning of new data frame */
            if (c == API_START_DELIMITER) {
                dev->int_state = XBEE_INT_STATE_SIZE1;
            }
            break;
        case XBEE_INT_STATE_SIZE1:
            dev->int_size = ((uint16_t)c) << 8;
            dev->int_state = XBEE_INT_STATE_SIZE2;
            break;
        case XBEE_INT_STATE_SIZE2:
            dev->int_size += c;
            dev->int_state = XBEE_INT_STATE_TYPE;
            break;
        case XBEE_INT_STATE_TYPE:
            if (c == API_ID_RX_SHORT_ADDR || c == API_ID_RX_LONG_ADDR) {
                /* in case old data was not processed, ignore incoming data */
                if (dev->rx_count != 0) {
                    dev->int_state = XBEE_INT_STATE_IDLE;
                    return;
                }
                dev->rx_limit = dev->int_size + 1;
                dev->rx_buf[dev->rx_count++] = c;
                dev->int_state = XBEE_INT_STATE_RX;
            }
            else if (c == API_ID_AT_RESP) {
                dev->resp_limit = dev->int_size;
                dev->int_state = XBEE_INT_STATE_RESP;
            }
            else {
                dev->int_state = XBEE_INT_STATE_IDLE;
            }
            break;
        case XBEE_INT_STATE_RESP:
            dev->resp_buf[dev->resp_count++] = c;
            if (dev->resp_count == dev->resp_limit) {
                /* here we ignore the checksum to prevent deadlocks */
                mutex_unlock(&(dev->resp_lock));
                dev->int_state = XBEE_INT_STATE_IDLE;
            }
            break;
        case XBEE_INT_STATE_RX:
            dev->rx_buf[dev->rx_count++] = c;
            if (dev->rx_count == dev->rx_limit) {
                /* packet is complete */
                msg.type = GNRC_NETDEV_MSG_TYPE_EVENT;
                msg.content.value = ISR_EVENT_RX_DONE;
                msg_send_int(&msg, dev->mac_pid);
                dev->int_state = XBEE_INT_STATE_IDLE;
            }
            break;
        default:
            /* this should never be the case */
            break;
    }
}

/*
 * Getter and setter functions
 */

static int _get_addr_short(xbee_t *dev, uint8_t *val, size_t len)
{
    uint8_t cmd[2];
    resp_t resp;

    if (len < 2) {
        return -EOVERFLOW;
    }

    cmd[0] = 'M';
    cmd[1] = 'Y';
    _api_at_cmd(dev, cmd, 2, &resp);
    if (resp.status == 0) {
        memcpy(val, resp.data, IEEE802154_SHORT_ADDRESS_LEN);
        return IEEE802154_SHORT_ADDRESS_LEN;
    }
    return -ECANCELED;
}

static int _get_addr_long(xbee_t *dev, uint8_t *val, size_t len)
{
    uint8_t cmd[2];
    resp_t resp;

    if (len < IEEE802154_LONG_ADDRESS_LEN) {
        return -EOVERFLOW;
    }

    /* read 4 high byte - AT command: SH*/
    cmd[0] = 'S';
    cmd[1] = 'H';
    _api_at_cmd(dev, cmd, 2, &resp);
    if (resp.status == 0) {
        memcpy(val, resp.data, 4);
    }
    else {
        return -ECANCELED;
    }
    /* read next 4 byte - AT command: SL */
    cmd[1] = 'L';
    _api_at_cmd(dev, cmd, 2, &resp);
    if (resp.status == 0) {
        memcpy(val + 4, resp.data, 4);
        return IEEE802154_LONG_ADDRESS_LEN;
    }
    return -ECANCELED;
}

static int _set_short_addr(xbee_t *dev, uint8_t *address)
{
    uint8_t cmd[4];
    resp_t resp;

    cmd[0] = 'M';
    cmd[1] = 'Y';
    cmd[2] = address[0];
    cmd[3] = address[1];
    _api_at_cmd(dev, cmd, 4, &resp);

    return resp.status;
}

static int _set_addr(xbee_t *dev, uint8_t *val, size_t len)
{
    uint8_t addr[2];

    /* device only supports setting the short address */
    if (len != 2) {
        return -ENOTSUP;
    }

    addr[0] = val[0];
    addr[1] = val[1];

#ifdef MODULE_SIXLOWPAN
    /* https://tools.ietf.org/html/rfc4944#section-12 requires the first bit
     * to 0 for unicast addresses */
    addr[1] &= 0x7F;
#endif

    if (dev->addr_flags & XBEE_ADDR_FLAGS_LONG ||
          _set_short_addr(dev, addr) == 0) {

        memcpy(dev->addr_short, addr, 2);

        return 2;
    }

    return -ECANCELED;
}

static int _set_addr_len(xbee_t *dev, uint16_t *val, size_t len)
{
    if (len != sizeof(uint16_t)) {
        return -EOVERFLOW;
    }

    switch (*val) {
        case IEEE802154_LONG_ADDRESS_LEN:
            dev->addr_flags |= XBEE_ADDR_FLAGS_LONG;

            /* disable short address */
            uint8_t disabled_addr[] = { 0xFF, 0xFF };

            _set_short_addr(dev, disabled_addr);

            break;
        case IEEE802154_SHORT_ADDRESS_LEN:
            dev->addr_flags &= ~XBEE_ADDR_FLAGS_LONG;

            /* restore short address */
            _set_short_addr(dev, dev->addr_short);

            break;
        default:
            return -EINVAL;
    }

    return sizeof(uint16_t);
}

static int _get_channel(xbee_t *dev, uint8_t *val, size_t max)
{
    uint8_t cmd[2];
    resp_t resp;

    if (max < 2) {
        return -EOVERFLOW;
    }
    cmd[0] = 'C';
    cmd[1] = 'H';
    _api_at_cmd(dev, cmd, 2, &resp);
    if (resp.status == 0) {
        val[0] = resp.data[0];
        val[1] = 0;
        return 2;
    }
    return -ECANCELED;
}

static int _set_channel(xbee_t *dev, uint8_t *val, size_t len)
{
    uint8_t cmd[3];
    resp_t resp;

    if (len != 2 || val[1] != 0) {
        return -EINVAL;
    }
    cmd[0] = 'C';
    cmd[1] = 'H';
    cmd[2] = val[0];
    _api_at_cmd(dev, cmd, 3, &resp);
    if (resp.status == 0) {
        return 2;
    }
    return -EINVAL;
}

static int _get_panid(xbee_t *dev, uint8_t *val, size_t max)
{
    uint8_t cmd[2];
    resp_t resp;

    if (max < 2) {
        return -EOVERFLOW;
    }
    cmd[0] = 'I';
    cmd[1] = 'D';
    _api_at_cmd(dev, cmd, 2, &resp);
    if (resp.status == 0) {
        val[0] = resp.data[1];
        val[1] = resp.data[0];
        return 2;
    }
    return -ECANCELED;
}

static int _set_panid(xbee_t *dev, uint8_t *val, size_t len)
{
    uint8_t cmd[4];
    resp_t resp;

    if (len != 2) {
        return -EINVAL;
    }
    cmd[0] = 'I';
    cmd[1] = 'D';
    cmd[2] = val[1];
    cmd[3] = val[0];
    _api_at_cmd(dev, cmd, 4, &resp);
    if (resp.status == 0) {
        return 2;
    }
    return -EINVAL;
}

static int _get_proto(xbee_t *dev, uint8_t *val, size_t max)
{
    if (max < sizeof(gnrc_nettype_t)) {
        return -EOVERFLOW;
    }
    memcpy(val, &(dev->proto), sizeof(gnrc_nettype_t));
    return sizeof(gnrc_nettype_t);
}

static int _set_proto(xbee_t *dev, uint8_t *val, size_t len)
{
    if (len != sizeof(gnrc_nettype_t)) {
        return -EINVAL;
    }
    memcpy(&(dev->proto), val, sizeof(gnrc_nettype_t));
    return sizeof(gnrc_nettype_t);
}

#ifdef MODULE_XBEE_ENCRYPTION
static int _set_encryption(xbee_t *dev, uint8_t *val)
{
    uint8_t cmd[3];
    resp_t resp;
    /* get the current state of Encryption */
    cmd[0] = 'E';
    cmd[1] = 'E';
    _api_at_cmd(dev, cmd, 2, &resp);

    /* Prevent writing the same value in EE. */
    if (val[0] != resp.data[0] ){
        cmd[0] = 'E';
        cmd[1] = 'E';
        cmd[2] = val[0];
        _api_at_cmd(dev, cmd, 3, &resp);
    }
    if (resp.status == 0) {
        return 2;
    }
    return -ECANCELED;
}

static int _set_encryption_key(xbee_t *dev, uint8_t *val, size_t len)
{
        uint8_t cmd[18];
        resp_t resp;
        if (len != 16) { /* the AES key is 128bit, 16 byte */
            return  -EINVAL;
        }
        cmd[0] = 'K';
        cmd[1] = 'Y';

       for (int i = 0; i < 16; i++) { /* Append the key to the KY API AT command */
           cmd[i + 2] = val[i];
       }
        _api_at_cmd(dev, cmd, 18, &resp);
        if (resp.status == 0) {
            return 2;
        }
        return -ECANCELED;
}
#endif

/*
 * Driver's "public" functions
 */
int xbee_init(xbee_t *dev, const xbee_params_t *params)
{
    uint8_t tmp[2];

    /* check device and bus parameters */
    if (dev == NULL) {
        return -ENODEV;
    }
    if (params->uart >= UART_NUMOF) {
        return -ENXIO;
    }
    /* set device driver */
    dev->driver = &xbee_driver;
    /* set peripherals to use */
    dev->uart = params->uart;
    dev->reset_pin = params->reset_pin;
    dev->sleep_pin = params->sleep_pin;
    /* set default options */
    dev->addr_flags = 0;
    dev->proto = XBEE_DEFAULT_PROTOCOL;
    dev->options = 0;
    /* initialize buffers and locks*/
    mutex_init(&(dev->tx_lock));
    mutex_init(&(dev->resp_lock));
    dev->resp_limit = 1;    /* needs to be greater then 0 initially */
    dev->rx_count = 0;
    /* initialize UART and GPIO pins */
    if (uart_init(params->uart, params->baudrate, _rx_cb, dev) != UART_OK) {
        DEBUG("xbee: Error initializing UART\n");
        return -ENXIO;
    }
    if (params->reset_pin != GPIO_UNDEF) {
        if (gpio_init(params->reset_pin, GPIO_OUT) < 0) {
            DEBUG("xbee: Error initializing RESET pin\n");
            return -ENXIO;
        }
        gpio_set(params->reset_pin);
    }
    if (params->sleep_pin != GPIO_UNDEF) {
        if (gpio_init(params->sleep_pin, GPIO_OUT) < 0) {
            DEBUG("xbee: Error initializing SLEEP pin\n");
            return -ENXIO;
        }
        gpio_clear(params->sleep_pin);
    }
    /* if reset pin is connected, do a hardware reset */
    if (params->reset_pin != GPIO_UNDEF) {
        gpio_clear(params->reset_pin);
        xtimer_usleep(RESET_DELAY);
        gpio_set(params->reset_pin);
    }
    /* put the XBee device into command mode */
    xtimer_usleep(ENTER_CMD_MODE_DELAY);
    _at_cmd(dev, "+++");
    xtimer_usleep(ENTER_CMD_MODE_DELAY);
    /* disable non IEEE802.15.4 extensions */
    _at_cmd(dev, "ATMM2\r");
    /* put XBee module in "API mode without escaped characters" */
    _at_cmd(dev, "ATAP1\r");
    /* apply AT commands */
    _at_cmd(dev, "ATAC\r");
    /* exit command mode */
    _at_cmd(dev, "ATCN\r");

    /* load long address (we can not set it, its read only for Xbee devices) */
    if (_get_addr_long(dev, dev->addr_long.uint8, IEEE802154_LONG_ADDRESS_LEN) < 0) {
        DEBUG("xbee: Error getting address\n");
        return -EIO;
    }
    if (_set_addr(dev, &((dev->addr_long).uint8[6]), IEEE802154_SHORT_ADDRESS_LEN) < 0) {
        DEBUG("xbee: Error setting short address\n");
        return -EIO;
    }
    /* set default channel */
    tmp[1] = 0;
    tmp[0] = XBEE_DEFAULT_CHANNEL;
    if (_set_channel(dev, tmp, 2) < 0) {
        DEBUG("xbee: Error setting channel\n");
        return -EIO;
    }
    /* set default PAN ID */
    tmp[1] = (uint8_t)(XBEE_DEFAULT_PANID >> 8);
    tmp[0] = (uint8_t)(XBEE_DEFAULT_PANID & 0xff);
    if (_set_panid(dev, tmp, 2) < 0) {
        DEBUG("xbee: Error setting PAN ID\n");
        return -EIO;
    }

    DEBUG("xbee: Initialization successful\n");
    return 0;
}

static inline bool _is_broadcast(gnrc_netif_hdr_t *hdr)
{
    /* IEEE 802.15.4 does not support multicast so we need to check both flags */
    return (bool)(hdr->flags & (GNRC_NETIF_HDR_FLAGS_BROADCAST |
                                GNRC_NETIF_HDR_FLAGS_MULTICAST));
}

static int _send(gnrc_netdev_t *netdev, gnrc_pktsnip_t *pkt)
{
    xbee_t *dev = (xbee_t *)netdev;
    size_t size;
    size_t pos;
    gnrc_netif_hdr_t *hdr;
    gnrc_pktsnip_t *payload;

    /* check device descriptor and packet */
    if (pkt == NULL) {
        return -ENOMSG;
    }
    if (dev == NULL) {
        gnrc_pktbuf_release(pkt);
        return -ENODEV;
    }

    /* figure out the size of the payload to send */
    size = gnrc_pkt_len(pkt->next);
    if (size > XBEE_MAX_PAYLOAD_LENGTH) {
        DEBUG("xbee: Error sending data, payload length exceeds limit\n");
        gnrc_pktbuf_release(pkt);
        return -EOVERFLOW;
    }
    /* get netif header check address length and flags */
    hdr = (gnrc_netif_hdr_t *)pkt->data;
    if (!((hdr->dst_l2addr_len == IEEE802154_SHORT_ADDRESS_LEN) ||
          (hdr->dst_l2addr_len == IEEE802154_LONG_ADDRESS_LEN) ||
          _is_broadcast(hdr))) {
        gnrc_pktbuf_release(pkt);
        return -ENOMSG;
    }

    /* acquire TX lock */
    mutex_lock(&(dev->tx_lock));
    /* put together the API frame */
    dev->tx_buf[0] = API_START_DELIMITER;
    dev->tx_buf[4] = 0;         /* set to zero to disable response frame */
    /* set size, API id and address field depending on dst address length  */
    if (_is_broadcast(hdr)) {
        DEBUG("xbee: sending broadcast");
        dev->tx_buf[1] = (uint8_t)((size + 5) >> 8);
        dev->tx_buf[2] = (uint8_t)(size + 5);
        dev->tx_buf[3] = API_ID_TX_SHORT_ADDR;
        dev->tx_buf[5] = 0xff;
        dev->tx_buf[6] = 0xff;
        pos = 7;
    }
    else if (hdr->dst_l2addr_len == IEEE802154_SHORT_ADDRESS_LEN) {
        uint8_t *destination = gnrc_netif_hdr_get_dst_addr(hdr);

        DEBUG("xbee: sending unicast to %02x:%02x",
              (uint8_t) destination[0], (uint8_t) destination[1]);

        dev->tx_buf[1] = (uint8_t)((size + 5) >> 8);
        dev->tx_buf[2] = (uint8_t)(size + 5);
        dev->tx_buf[3] = API_ID_TX_SHORT_ADDR;
        memcpy(dev->tx_buf + 5, destination, IEEE802154_SHORT_ADDRESS_LEN);
        pos = 7;
    }
    else {
        uint8_t *destination = gnrc_netif_hdr_get_dst_addr(hdr);

        DEBUG("xbee: sending unicast to %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
              (uint8_t) destination[0], (uint8_t) destination[1],
              (uint8_t) destination[2], (uint8_t) destination[3],
              (uint8_t) destination[4], (uint8_t) destination[5],
              (uint8_t) destination[6], (uint8_t) destination[7]);

        dev->tx_buf[1] = (uint8_t)((size + 11) >> 8);
        dev->tx_buf[2] = (uint8_t)(size + 11);
        dev->tx_buf[3] = API_ID_TX_LONG_ADDR;
        memcpy(dev->tx_buf + 5, destination, IEEE802154_LONG_ADDRESS_LEN);
        pos = 13;
    }
    /* set options */
    DEBUG(", option: %02x", dev->options);
    dev->tx_buf[pos++] = dev->options;
    /* copy payload */
    DEBUG(", payload:");
    payload = pkt->next;
    while (payload) {
#if ENABLE_DEBUG
        for (size_t i = 0; i < payload->size; i++) {
            DEBUG(" %02x", ((uint8_t *) payload->data)[i]);
        }
#endif
        memcpy(&(dev->tx_buf[pos]), payload->data, payload->size);
        pos += payload->size;
        payload = payload->next;
    }
    /* set checksum */
    dev->tx_buf[pos] = _cksum(dev->tx_buf, pos);
    /* start transmission */
    uart_write(dev->uart, dev->tx_buf, pos + 1);
    /* release data */
    gnrc_pktbuf_release(pkt);
    /* release TX lock */
    mutex_unlock(&(dev->tx_lock));
    /* return number of payload byte */
    DEBUG("\n");
    return (int)size;
}

static int _add_cb(gnrc_netdev_t *dev, gnrc_netdev_event_cb_t cb)
{
    if (dev == NULL) {
        return -ENODEV;
    }
    if (dev->event_cb != NULL) {
        return -ENOBUFS;
    }
    dev->event_cb = cb;
    return 0;
}

static int _rem_cb(gnrc_netdev_t *dev, gnrc_netdev_event_cb_t cb)
{
    if (dev == NULL) {
        return -ENODEV;
    }
    if (dev->event_cb != cb) {
        return -ENOENT;
    }
    dev->event_cb = NULL;
    return 0;
}

static int _get(gnrc_netdev_t *netdev, netopt_t opt, void *value, size_t max_len)
{
    xbee_t *dev = (xbee_t *)netdev;
    if (dev == NULL) {
        return -ENODEV;
    }

    switch (opt) {
        case NETOPT_ADDRESS:
            return _get_addr_short(dev, (uint8_t *)value, max_len);
        case NETOPT_ADDRESS_LONG:
            return _get_addr_long(dev, (uint8_t *)value, max_len);
        case NETOPT_ADDR_LEN:
        case NETOPT_SRC_LEN:
            if (max_len < sizeof(uint16_t)) {
                return -EOVERFLOW;
            }
            if (dev->addr_flags & XBEE_ADDR_FLAGS_LONG) {
                *((uint16_t *)value) = IEEE802154_LONG_ADDRESS_LEN;
            }
            else {
                *((uint16_t *)value) = IEEE802154_SHORT_ADDRESS_LEN;
            }
            return sizeof(uint16_t);
        case NETOPT_IPV6_IID:
            if (max_len < sizeof(eui64_t)) {
                return -EOVERFLOW;
            }
            if (dev->addr_flags & XBEE_ADDR_FLAGS_LONG) {
                ieee802154_get_iid(value, (uint8_t *)&dev->addr_long, IEEE802154_LONG_ADDRESS_LEN);
            }
            else {
                ieee802154_get_iid(value, (uint8_t *)&dev->addr_short, IEEE802154_SHORT_ADDRESS_LEN);
            }

            return sizeof(eui64_t);
        case NETOPT_CHANNEL:
            return _get_channel(dev, (uint8_t *)value, max_len);
        case NETOPT_MAX_PACKET_SIZE:
            if (max_len < sizeof(uint16_t)) {
                return -EOVERFLOW;
            }
            *((uint16_t *)value) = XBEE_MAX_PAYLOAD_LENGTH;
            return sizeof(uint16_t);
        case NETOPT_NID:
            return _get_panid(dev, (uint8_t *)value, max_len);
        case NETOPT_PROTO:
            return _get_proto(dev, (uint8_t *)value, max_len);
        default:
            return -ENOTSUP;
    }
}

static int _set(gnrc_netdev_t *netdev, netopt_t opt, void *value, size_t value_len)
{
    xbee_t *dev = (xbee_t *)netdev;
    if (dev == NULL) {
        return -ENODEV;
    }

    switch (opt) {
        case NETOPT_ADDRESS:
            return _set_addr(dev, (uint8_t *)value, value_len);
        case NETOPT_ADDR_LEN:
        case NETOPT_SRC_LEN:
            return _set_addr_len(dev, value, value_len);
        case NETOPT_CHANNEL:
            return _set_channel(dev, (uint8_t *)value, value_len);
        case NETOPT_NID:
            return _set_panid(dev, (uint8_t *)value, value_len);
        case NETOPT_PROTO:
            return _set_proto(dev, (uint8_t *)value, value_len);
#ifdef MODULE_XBEE_ENCRYPTION
        case NETOPT_ENCRYPTION:
            return _set_encryption(dev, (uint8_t *)value);
        case NETOPT_ENCRYPTION_KEY:
            return _set_encryption_key(dev, (uint8_t *)value, value_len);
#endif
        default:
            return -ENOTSUP;
    }
}

static void _isr_event(gnrc_netdev_t *netdev, uint32_t event_type)
{
    xbee_t *dev = (xbee_t *)netdev;
    gnrc_pktsnip_t *pkt_head;
    gnrc_pktsnip_t *pkt;
    gnrc_netif_hdr_t *hdr;
    size_t pos;
    size_t addr_len;
    uint8_t cksum = 0;

    /* check device */
    if (dev == NULL) {
        return;
    }
    /* check rx callback and event type */
    if (event_type != ISR_EVENT_RX_DONE || dev->event_cb == NULL) {
        dev->rx_count = 0;
        return;
    }

    /* read address length */
    if (dev->rx_buf[0] == API_ID_RX_SHORT_ADDR) {
        addr_len = IEEE802154_SHORT_ADDRESS_LEN;
    }
    else {
        addr_len = IEEE802154_LONG_ADDRESS_LEN;
    }

#ifdef XBEE_DENIED_ADDRESSES
    if (addr_len == 8) {
        uint8_t denied_addresses[] = XBEE_DENIED_ADDRESSES;
        for (size_t i = 0; i < sizeof(denied_addresses) / 8; i++) {
            if (memcmp(&(dev->rx_buf[1]),
                       &denied_addresses[i * 8],
                       addr_len) == 0) {
                dev->rx_count = 0;

                DEBUG("xbee: dropping denied packet\n");

                return;
            }
        }
    }
#endif

    /* check checksum for correctness */
    for (int i = 0; i < dev->rx_limit; i++) {
        cksum += dev->rx_buf[i];
    }
    if (cksum != 0xff) {
        DEBUG("xbee: Received packet with incorrect checksum, dropping it\n");
        dev->rx_count = 0;
        return;
    }

    /* allocate and fill interface header */
    pkt_head = gnrc_pktbuf_add(NULL, NULL,
                               sizeof(gnrc_netif_hdr_t) + (2 * addr_len),
                               GNRC_NETTYPE_NETIF);
    if (pkt_head == NULL) {
        DEBUG("xbee: Error allocating netif header in packet buffer on RX\n");
        dev->rx_count = 0;
        return;
    }
    hdr = (gnrc_netif_hdr_t *)pkt_head->data;
    hdr->src_l2addr_len = (uint8_t)addr_len;
    hdr->dst_l2addr_len = (uint8_t)addr_len;
    hdr->if_pid = dev->mac_pid;
    hdr->rssi = dev->rx_buf[1 + addr_len]; /* API ID + source address */
    hdr->lqi = 0;
    gnrc_netif_hdr_set_src_addr(hdr, &(dev->rx_buf[1]), addr_len);
    if (addr_len == 2) {
        gnrc_netif_hdr_set_dst_addr(hdr, dev->addr_short, IEEE802154_SHORT_ADDRESS_LEN);
    }
    else {
        gnrc_netif_hdr_set_dst_addr(hdr, dev->addr_long.uint8, IEEE802154_LONG_ADDRESS_LEN);
    }
    pos = 3 + addr_len;
    /* allocate and copy payload */
    pkt = gnrc_pktbuf_add(pkt_head, &(dev->rx_buf[pos]), dev->rx_limit - pos - 1,
                          dev->proto);
    if (pkt == NULL) {
        DEBUG("xbee: Error allocating payload in packet buffer on RX\n");
        gnrc_pktbuf_release(pkt_head);
        dev->rx_count = 0;
        return;
    }

#if ENABLE_DEBUG
    DEBUG("xbee: received packet from");
    for (size_t i = 0; i < addr_len; i++) {
        DEBUG(" %02x", (uint8_t) gnrc_netif_hdr_get_src_addr(hdr)[i]);
    }

    DEBUG(", RSSI: -%d dBm", hdr->rssi);
    DEBUG(", options: %02x", (uint8_t) dev->rx_buf[1 + addr_len + 1]); /* API ID + source address + RSSI */
    DEBUG(", payload:");

    for (size_t i = 0; i < pkt->size; i++) {
        DEBUG(" %02x", ((uint8_t *) pkt->data)[i]);
    }
    DEBUG("\n");
#endif

    /* pass on the received packet */
    dev->event_cb(NETDEV_EVENT_RX_COMPLETE, pkt);
    /* reset RX byte counter to enable receiving of the next packet */
    dev->rx_count = 0;
}

/*
 * The drivers netdev interface
 */
const gnrc_netdev_driver_t xbee_driver = {
    .send_data = _send,
    .add_event_callback = _add_cb,
    .rem_event_callback = _rem_cb,
    .get = _get,
    .set = _set,
    .isr_event = _isr_event,
};