You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

813 lines
22 KiB

/*
* Copyright (C) 2014 INRIA
* Copyright (C) 2015-2016 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 <errno.h>
#include "xbee.h"
#include "assert.h"
#include "xtimer.h"
#include "net/eui64.h"
#include "net/netdev2.h"
#include "net/ieee802154.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 (US_PER_SEC)
/**
* @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 */
#define OPT_BCAST_ADDR (0x02) /**< address broadcast */
#define OPT_BCAST_PAN (0x04) /**< PAN broadcast */
/** @} */
/**
* @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(size_t offset, uint8_t *buf, size_t size)
{
uint8_t res = 0xff;
for (size_t i = offset; 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->p.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->cmd_buf[0] = API_START_DELIMITER;
dev->cmd_buf[1] = (size + 2) >> 8;
dev->cmd_buf[2] = (size + 2) & 0xff;
dev->cmd_buf[3] = API_ID_AT;
dev->cmd_buf[4] = 1; /* use fixed frame id */
memcpy(dev->cmd_buf + 5, cmd, size);
dev->cmd_buf[size + 5] = _cksum(3, dev->cmd_buf, size + 5);
/* reset the response data counter */
dev->resp_count = 0;
/* start send data */
uart_write(dev->p.uart, dev->cmd_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] api_at_cmd: 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;
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 */
if (dev->event_callback) {
dev->event_callback((netdev2_t *)dev, NETDEV2_EVENT_ISR);
}
dev->int_state = XBEE_INT_STATE_IDLE;
}
break;
default:
/* this should never be the case */
break;
}
}
/*
* Getter and setter functions
*/
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;
}
#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
*/
void xbee_setup(xbee_t *dev, const xbee_params_t *params)
{
assert(dev && (params->uart < UART_NUMOF));
/* set device driver */
dev->driver = &xbee_driver;
dev->event_callback = NULL;
dev->context = dev;
/* set peripherals to use */
memcpy(&dev->p, params, sizeof(xbee_params_t));
/* initialize pins */
if (dev->p.pin_reset != GPIO_UNDEF) {
gpio_init(dev->p.pin_reset, GPIO_OUT);
gpio_set(dev->p.pin_reset);
}
if (dev->p.pin_sleep != GPIO_UNDEF) {
gpio_init(dev->p.pin_sleep, GPIO_OUT);
gpio_clear(dev->p.pin_sleep);
}
/* we initialize the UART later, since we can not handle interrupts, yet */
}
int xbee_build_hdr(xbee_t *dev, uint8_t *xhdr, size_t payload_len,
void *dst_addr, size_t addr_len)
{
/* make sure payload fits into a packet */
if (payload_len > XBEE_MAX_PAYLOAD_LENGTH) {
return -EOVERFLOW;
}
/* set start delimiter, configure address and set options. Also make sure,
* that the link layer address is of known length */
xhdr[0] = API_START_DELIMITER;
xhdr[4] = dev->tx_fid++;
if (addr_len == IEEE802154_SHORT_ADDRESS_LEN) {
xhdr[3] = API_ID_TX_SHORT_ADDR;
xhdr[7] = dev->options;
}
else if (addr_len == IEEE802154_LONG_ADDRESS_LEN) {
xhdr[3] = API_ID_TX_LONG_ADDR;
xhdr[13] = dev->options;
}
else {
return -ENOMSG;
}
/* finally configure the packet size and copy the actual dst address */
uint16_t size = (uint16_t)(payload_len + addr_len + 3);
xhdr[1] = (uint8_t)(size >> 8);
xhdr[2] = (uint8_t)(size & 0xff);
memcpy(&xhdr[5], dst_addr, addr_len);
return (int)(addr_len + 6);
}
int xbee_parse_hdr(xbee_t *dev, const uint8_t *xhdr, xbee_l2hdr_t *l2hdr)
{
uint8_t alen;
assert(xhdr && l2hdr);
/* get the address length */
if (xhdr[0] == API_ID_RX_SHORT_ADDR) {
alen = IEEE802154_SHORT_ADDRESS_LEN;
}
else if (xhdr[0] == API_ID_RX_LONG_ADDR) {
alen = IEEE802154_LONG_ADDRESS_LEN;
}
else {
return -ENOMSG;
}
/* copy the actual SRC address and the RSSI value */
memcpy(l2hdr->src_addr, &xhdr[1], alen);
l2hdr->rssi = xhdr[1 + alen];
/* copy the destination address */
l2hdr->bcast = (xhdr[2 + alen] & OPT_BCAST_ADDR) ? 1 : 0;
if (l2hdr->bcast) {
memset(l2hdr->dst_addr, 0xff, alen);
}
else {
if (alen == IEEE802154_SHORT_ADDRESS_LEN) {
memcpy(l2hdr->dst_addr, dev->addr_short, alen);
}
else {
memcpy(l2hdr->dst_addr, dev->addr_long.uint8, alen);
}
}
l2hdr->addr_len = alen;
return (int)(alen + 3);
}
int xbee_init(netdev2_t *dev)
{
uint8_t tmp[2];
xbee_t *xbee = (xbee_t *)dev;
/* set default options */
xbee->addr_flags = XBEE_ADDR_FLAGS_LONG;
xbee->options = 0;
/* initialize buffers and locks*/
mutex_init(&(xbee->tx_lock));
mutex_init(&(xbee->resp_lock));
xbee->resp_limit = 1; /* needs to be greater then 0 initially */
xbee->rx_count = 0;
/* initialize UART and GPIO pins */
if (uart_init(xbee->p.uart, xbee->p.br, _rx_cb, xbee) != UART_OK) {
DEBUG("[xbee] init: error initializing UART\n");
return -ENXIO;
}
/* if reset pin is connected, do a hardware reset */
if (xbee->p.pin_reset != GPIO_UNDEF) {
gpio_clear(xbee->p.pin_reset);
xtimer_usleep(RESET_DELAY);
gpio_set(xbee->p.pin_reset);
}
/* put the XBee device into command mode */
xtimer_usleep(ENTER_CMD_MODE_DELAY);
_at_cmd(xbee, "+++");
xtimer_usleep(ENTER_CMD_MODE_DELAY);
/* disable non IEEE802.15.4 extensions */
_at_cmd(xbee, "ATMM2\r");
/* put XBee module in "API mode without escaped characters" */
_at_cmd(xbee, "ATAP1\r");
/* apply AT commands */
_at_cmd(xbee, "ATAC\r");
/* exit command mode */
_at_cmd(xbee, "ATCN\r");
/* load long address (we can not set it, its read only for Xbee devices) */
if (_get_addr_long(xbee, xbee->addr_long.uint8, IEEE802154_LONG_ADDRESS_LEN) < 0) {
DEBUG("[xbee] init: error getting address\n");
return -EIO;
}
if (_set_addr(xbee, &((xbee->addr_long).uint8[6]), IEEE802154_SHORT_ADDRESS_LEN) < 0) {
DEBUG("[xbee] init: error setting short address\n");
return -EIO;
}
/* set default channel */
tmp[1] = 0;
tmp[0] = XBEE_DEFAULT_CHANNEL;
if (_set_channel(xbee, tmp, 2) < 0) {
DEBUG("[xbee] init: 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(xbee, tmp, 2) < 0) {
DEBUG("[xbee] init: error setting PAN ID\n");
return -EIO;
}
DEBUG("[xbee] init: Initialization successful\n");
return 0;
}
static int xbee_send(netdev2_t *dev, const struct iovec *vector, unsigned count)
{
xbee_t *xbee = (xbee_t *)dev;
size_t size;
uint8_t csum;
assert(xbee && vector && (count > 0));
/* calculate the checksum and the packet size */
size = vector[0].iov_len;
csum = _cksum(3, (uint8_t *)vector[0].iov_base, size);
for (unsigned i = 1; i < count; i++) {
size += vector[i].iov_len;
for (size_t p = 0; p < vector[i].iov_len; p++) {
csum -= ((uint8_t *)vector[i].iov_base)[p];
}
}
/* make sure the data fits into a packet */
if (size >= XBEE_MAX_PKT_LENGTH) {
DEBUG("[xbee] send: data to send is too large for TX buffer\n");
return -1;
}
/* send the actual data packet */
DEBUG("[xbee] send: now sending out %i byte\n", (int)size);
mutex_lock(&(xbee->tx_lock));
for (unsigned i = 0; i < count; i++) {
uart_write(xbee->p.uart, vector[i].iov_base, vector[i].iov_len);
}
uart_write(xbee->p.uart, &csum, 1);
mutex_unlock(&(xbee->tx_lock));
/* return number of payload byte */
return (int)size;
}
static int xbee_recv(netdev2_t *dev, void *buf, size_t len, void *info)
{
(void)info;
size_t size;
xbee_t *xbee = (xbee_t *)dev;
assert(xbee);
/* make sure we have new data waiting */
if (xbee->rx_count != xbee->rx_limit) {
DEBUG("[xbee] recv: no data available for reading\n");
return 0;
}
/* data available, so we read it (or it's size) */
size = (size_t)(xbee->rx_limit - 1);
if (buf == NULL) {
if (len > 0) {
DEBUG("[xbee] recv: reading size and dropping: %i\n", size);
xbee->rx_count = 0;
}
else {
DEBUG("[xbee] recv: reading size without dropping: %i\n", size);
}
}
else {
size = (size > len) ? len : size;
DEBUG("[xbee] recv: consuming packet: reading %i byte\n", size);
memcpy(buf, xbee->rx_buf, size);
xbee->rx_count = 0;
}
return (int)size;
}
static void xbee_isr(netdev2_t *netdev)
{
xbee_t *dev = (xbee_t *)netdev;
if (dev->rx_count == dev->rx_limit) {
/* make sure the checksum checks out */
if (_cksum(0, dev->rx_buf, dev->rx_limit) != 0) {
DEBUG("[xbee] isr: invalid RX checksum\n");
dev->rx_count = 0;
}
else {
DEBUG("[xbee] isr: data available, waiting for read\n");
dev->event_callback(netdev, NETDEV2_EVENT_RX_COMPLETE);
}
}
}
static int xbee_get(netdev2_t *ndev, netopt_t opt, void *value, size_t max_len)
{
xbee_t *dev = (xbee_t *)ndev;
assert(dev);
switch (opt) {
case NETOPT_ADDRESS:
assert(max_len >= IEEE802154_SHORT_ADDRESS_LEN);
memcpy(value, dev->addr_short, IEEE802154_SHORT_ADDRESS_LEN);
return IEEE802154_SHORT_ADDRESS_LEN;
case NETOPT_ADDRESS_LONG:
assert(max_len >= IEEE802154_LONG_ADDRESS_LEN);
memcpy(value, dev->addr_long.uint8, IEEE802154_LONG_ADDRESS_LEN);
return IEEE802154_LONG_ADDRESS_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, dev->addr_long.uint8,
IEEE802154_LONG_ADDRESS_LEN);
}
else {
ieee802154_get_iid(value, 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);
default:
return -ENOTSUP;
}
}
static int xbee_set(netdev2_t *ndev, netopt_t opt, void *value, size_t len)
{
xbee_t *dev = (xbee_t *)ndev;
assert(dev);
switch (opt) {
case NETOPT_ADDRESS:
return _set_addr(dev, (uint8_t *)value, len);
case NETOPT_ADDR_LEN:
case NETOPT_SRC_LEN:
return _set_addr_len(dev, value, len);
case NETOPT_CHANNEL:
return _set_channel(dev, (uint8_t *)value, len);
case NETOPT_NID:
return _set_panid(dev, (uint8_t *)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, len);
#endif
default:
return -ENOTSUP;
}
}
/*
* The drivers netdev interface
*/
const netdev2_driver_t xbee_driver = {
.send = xbee_send,
.recv = xbee_recv,
.init = xbee_init,
.isr = xbee_isr,
.get = xbee_get,
.set = xbee_set,
};