diff --git a/drivers/include/xbee.h b/drivers/include/xbee.h new file mode 100644 index 0000000000..ae791792ee --- /dev/null +++ b/drivers/include/xbee.h @@ -0,0 +1,163 @@ +/* + * 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. + */ + +/** + * @defgroup drivers_xbee XBee driver + * @ingroup drivers + * @brief High-level driver for the XBee S1 802.15.4 modem + * @{ + * + * @file + * @brief High-level driver for the XBee S1 802.15.4 modem + * + * @author Kévin Roussel + * @author Hauke Petersen + */ + +#ifndef XBEE_H_ +#define XBEE_H_ + +#include + +#include "kernel.h" +#include "mutex.h" +#include "periph/uart.h" +#include "periph/gpio.h" +#include "net/ng_netbase.h" + +#ifdef __cplusplus + extern "C" { +#endif + +/** + * @brief Maximum payload length that can be send + */ +#define XBEE_MAX_PAYLOAD_LENGTH (100U) + +/** + * @brief Maximum packet length, including XBee API frame overhead + */ +#define XBEE_MAX_PKT_LENGTH (115U) + +/** + * @brief Maximum length of a command response + */ +#define XBEE_MAX_RESP_LENGTH (16U) + +/** + * @brief Default protocol for data that is coming in + */ +#ifdef MODULE_NG_SIXLOWPAN +#define XBEE_DEFAULT_PROTOCOL (NG_NETTYPE_SIXLOWPAN) +#else +#define XBEE_DEFAULT_PROTOCOL (NG_NETTYPE_UNDEF) +#endif + +/** + * @brief Default short address used after initialization + */ +#define XBEE_DEFAULT_SHORT_ADDR (0x0230) + +/** + * @brief Default PAN ID used after initialization + */ +#define XBEE_DEFAULT_PANID (0x0001) + +/** + * @brief Default channel used after initialization + */ +#define XBEE_DEFAULT_CHANNEL (11U) + +/** + * @brief States of the internal FSM for handling incoming UART frames + * + * Incoming data frames on the UART interfaces are handled using a finite state + * machine (FSM) in the UARTs RX interrupt handler. The FSM is needed to extract + * frame specific data as the frame size, frame type, and checksums. + */ +typedef enum { + XBEE_INT_STATE_IDLE, /**< waiting for the beginning of a new frame */ + XBEE_INT_STATE_SIZE1, /**< waiting for the first byte (MSB) of the + * frame size field */ + XBEE_INT_STATE_SIZE2, /**< waiting for the second byte (LSB) of the + * frame size field */ + XBEE_INT_STATE_TYPE, /**< waiting for the frame type field */ + XBEE_INT_STATE_RESP, /**< handling incoming data for AT command + * responses */ + XBEE_INT_STATE_RX, /**< handling incoming data when receiving radio + * packets */ +} xbee_rx_state_t; + +/** + * @brief XBee device descriptor + */ +typedef struct { + /* netdev fields */ + ng_netdev_driver_t const *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 driver specific fields */ + uart_t uart; /**< UART interfaced used */ + gpio_t reset_pin; /**< GPIO pin connected to RESET */ + gpio_t sleep_pin; /**< GPIO pin connected to SLEEP */ + ng_nettype_t proto; /**< protocol the interface speaks */ + uint8_t options; /**< options field */ + uint8_t addr_short[2]; /**< onw 802.15.4 short address */ + uint8_t addr_long[8]; /**< own 802.15.4 long address */ + /* general variables for the UART RX state machine */ + xbee_rx_state_t int_state; /**< current state if the UART RX FSM */ + uint16_t int_size; /**< temporary space for parsing the + * frame size */ + /* values for the UART TX state machine */ + mutex_t tx_lock; /**< mutex to allow only one + * transmission at a time */ + uint8_t tx_buf[XBEE_MAX_PKT_LENGTH];/**< transmit data buffer */ + uint16_t tx_count; /**< counter for ongoing transmission */ + uint16_t tx_limit; /**< size of TX frame transferred */ + /* buffer and synchronization for command responses */ + mutex_t resp_lock; /**< mutex for waiting for AT command + * response frames */ + uint8_t resp_buf[XBEE_MAX_RESP_LENGTH]; /**< AT response data buffer */ + uint16_t resp_count; /**< counter for ongoing transmission */ + uint16_t resp_limit; /**< size RESP frame in transferred */ + /* buffer and synchronization for incoming network packets */ + uint8_t rx_buf[XBEE_MAX_PKT_LENGTH];/**< receiving data buffer */ + uint16_t rx_count; /**< counter for ongoing transmission */ + uint16_t rx_limit; /**< size RX frame transferred */ +} xbee_t; + +/** + * @brief Reference to the XBee driver interface + */ +extern const ng_netdev_driver_t xbee_driver; + +/** + * @brief Initialize the given Xbee device + * + * @param[out] dev Xbee device to initialize + * @param[in] uart UART interfaced the device is connected to + * @param[in] baudrate baudrate to use + * @param[in] sleep_pin GPIO pin that is connected to the SLEEP pin, set to + * GPIO_NUMOF if not used + * @param[in] status_pin GPIO pin that is connected to the STATUS pin, set to + * GPIO_NUMOF if not used + * + * @return 0 on success + * @return -ENODEV on invalid device descriptor + * @return -ENXIO on invalid UART or GPIO pins + */ +int xbee_init(xbee_t *dev, uart_t uart, uint32_t baudrate, + gpio_t sleep_pin, gpio_t status_pin); + +#ifdef __cplusplus +} +#endif + +#endif /* XBEE_H_ */ +/** @} */ diff --git a/drivers/xbee/Makefile b/drivers/xbee/Makefile new file mode 100644 index 0000000000..768bf8702a --- /dev/null +++ b/drivers/xbee/Makefile @@ -0,0 +1,3 @@ +MODULE = xbee + +include $(RIOTBASE)/Makefile.base diff --git a/drivers/xbee/xbee.c b/drivers/xbee/xbee.c new file mode 100644 index 0000000000..0aca6e9d76 --- /dev/null +++ b/drivers/xbee/xbee.c @@ -0,0 +1,716 @@ +/* + * 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 + * @author Hauke Petersen + * + * @} + */ + +#include + +#include "xbee.h" +#include "mutex.h" +#include "hwtimer.h" +#include "msg.h" +#include "periph/uart.h" +#include "periph/gpio.h" +#include "periph/cpuid.h" +#include "net/ng_netbase.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 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 (int 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", cmd); + + for (int i = 0; cmd[i] != '\0'; i++) { + uart_write_blocking(dev->uart, cmd[i]); + } +} + +static void _api_at_cmd(xbee_t *dev, uint8_t *cmd, uint8_t size, resp_t *resp) +{ + /* 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); + + /* prepare UART for sending out the data and receiving the response */ + dev->tx_limit = size + 6; + dev->tx_count = 0; + dev->resp_count = 0; + /* start send data */ + uart_tx_begin(dev->uart); + + /* wait for results */ + while (dev->resp_limit != dev->resp_count) { + mutex_lock(&(dev->resp_lock)); + } + + /* 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); + } +} + +/* + * Interrupt callbacks + */ +int _tx_cb(void *arg) +{ + xbee_t *dev = (xbee_t *)arg; + if (dev->tx_count < dev->tx_limit) { + /* more data to send */ + char c = (char)(dev->tx_buf[dev->tx_count++]); + uart_write(dev->uart, c); + return 1; + } + /* release TX lock */ + mutex_unlock(&(dev->tx_lock)); + return 0; +} + +void _rx_cb(void *arg, char 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 += (uint8_t)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++] = (uint8_t)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++] = (uint8_t)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++] = (uint8_t)c; + if (dev->rx_count == dev->rx_limit) { + /* packet is complete */ + msg.type = NG_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 + */ + +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, 2); + return 2; + } + return -ECANCELED; +} + +int _get_addr_long(xbee_t *dev, uint8_t *val, size_t len) +{ + uint8_t cmd[2]; + resp_t resp; + + if (len < 8) { + 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 8; + } + return -ECANCELED; +} + +int _set_addr(xbee_t *dev, uint8_t *val, size_t len) +{ + uint8_t cmd[4]; + resp_t resp; + + /* device only supports setting the short address */ + if (len != 2) { + return -ENOTSUP; + } + cmd[0] = 'M'; + cmd[1] = 'Y'; + cmd[2] = val[0]; + cmd[3] = val[1]; + _api_at_cmd(dev, cmd, 4, &resp); + if (resp.status == 0) { + memcpy(dev->addr_short, val, 2); + return 2; + } + return -ECANCELED; +} + +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; +} + +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; +} + +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; +} + +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; +} + +int _get_proto(xbee_t *dev, uint8_t *val, size_t max) +{ + if (max < sizeof(ng_nettype_t)) { + return -EOVERFLOW; + } + memcpy(val, &(dev->proto), sizeof(ng_nettype_t)); + return sizeof(ng_nettype_t); +} + +int _set_proto(xbee_t *dev, uint8_t *val, size_t len) +{ + if (len != sizeof(ng_nettype_t)) { + return -EINVAL; + } + memcpy(&(dev->proto), val, sizeof(ng_nettype_t)); + return sizeof(ng_nettype_t); +} + +/* + * Driver's "public" functions + */ +int xbee_init(xbee_t *dev, uart_t uart, uint32_t baudrate, + gpio_t reset_pin, gpio_t sleep_pin) +{ + uint8_t tmp[2]; + + /* check device and bus parameters */ + if (dev == NULL) { + return -ENODEV; + } + if (uart >= UART_NUMOF) { + return -ENXIO; + } + /* set device driver */ + dev->driver = &xbee_driver; + /* set peripherals to use */ + dev->uart = uart; + dev->reset_pin = reset_pin; + dev->sleep_pin = sleep_pin; + /* set default options */ + 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(uart, baudrate, _rx_cb, _tx_cb, dev) < 0) { + DEBUG("xbee: Error initializing UART\n"); + return -ENXIO; + } + if (reset_pin < GPIO_NUMOF) { + if (gpio_init_out(reset_pin, GPIO_NOPULL) < 0) { + DEBUG("xbee: Error initializing RESET pin\n"); + return -ENXIO; + } + gpio_set(reset_pin); + } + if (sleep_pin < GPIO_NUMOF) { + if (gpio_init_out(sleep_pin, GPIO_NOPULL) < 0) { + DEBUG("xbee: Error initializing SLEEP pin\n"); + return -ENXIO; + } + gpio_clear(sleep_pin); + } + /* if reset pin is connected, do a hardware reset */ + if (reset_pin < GPIO_NUMOF) { + gpio_clear(reset_pin); + hwtimer_wait(HWTIMER_TICKS(RESET_DELAY)); + gpio_set(reset_pin); + } + /* put the XBee device into command mode */ + hwtimer_wait(HWTIMER_TICKS(ENTER_CMD_MODE_DELAY)); + _at_cmd(dev, "+++"); + hwtimer_wait(HWTIMER_TICKS(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"); + + /* set default short address, use CPU ID if available */ +#if CPUID_ID_LEN + /* get CPU ID */ + uint8_t id[CPUID_ID_LEN]; + cpuid_get(id); + /* compress to 2 byte */ + memset(dev->addr_short, 0, 2); + int i; + for (i = 0; i < (CPUID_ID_LEN / 2); i++) { + dev->addr_short[0] ^= id[i]; + } + for (; i < CPUID_ID_LEN; i++) { + dev->addr_short[1] ^= id[i]; + } +#else + dev->addr_short[0] = (uint8_t)(XBEE_DEFAULT_SHORT_ADDR >> 8); + dev->addr_short[1] = (uint8_t)(XBEE_DEFAULT_SHORT_ADDR); +#endif + _set_addr(dev, dev->addr_short, 2); + /* load long address (we can not set it, its read only for Xbee devices) */ + _get_addr_long(dev, dev->addr_long, 8); + /* set default channel */ + tmp[1] = 0; + tmp[0] = XBEE_DEFAULT_CHANNEL; + _set_channel(dev, tmp, 2); + /* set default PAN ID */ + tmp[1] = (uint8_t)(XBEE_DEFAULT_PANID >> 8); + tmp[0] = (uint8_t)(XBEE_DEFAULT_PANID & 0xff); + _set_panid(dev, tmp, 2); + + DEBUG("xbee: Initialization successful\n"); + return 0; +} + +int _send(ng_netdev_t *netdev, ng_pktsnip_t *pkt) +{ + xbee_t *dev = (xbee_t *)netdev; + size_t size; + size_t pos; + ng_netif_hdr_t *hdr; + ng_pktsnip_t *payload; + + /* check device descriptor and packet */ + if (pkt == NULL) { + return -ENOMSG; + } + if (dev == NULL) { + ng_pktbuf_release(pkt); + return -ENODEV; + } + + /* figure out the size of the payload to send */ + size = ng_pkt_len(pkt->next); + if (size > XBEE_MAX_PAYLOAD_LENGTH) { + DEBUG("xbee: Error sending data, payload length exceeds limit\n"); + ng_pktbuf_release(pkt); + return -EOVERFLOW; + } + /* get netif header check address length */ + hdr = (ng_netif_hdr_t *)pkt->data; + if (!(hdr->dst_l2addr_len == 2 || hdr->dst_l2addr_len == 8)) { + ng_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 (hdr->dst_l2addr_len == 2) { + 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, ng_netif_hdr_get_dst_addr(hdr), 2); + pos = 7; + } else { + 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 + 11, ng_netif_hdr_get_dst_addr(hdr), 8); + pos = 13; + } + /* set options */ + dev->tx_buf[pos++] = dev->options; + /* copy payload */ + payload = pkt->next; + while (payload) { + 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); + /* prepare transmission */ + dev->tx_limit = (uint16_t)pos + 1; + dev->tx_count = 0; + /* start transmission */ + uart_tx_begin(dev->uart); + /* release data */ + ng_pktbuf_release(pkt); + /* return number of payload byte */ + return (int)size; +} + +int _add_cb(ng_netdev_t *dev, ng_netdev_event_cb_t cb) +{ + if (dev == NULL) { + return -ENODEV; + } + if (dev->event_cb != NULL) { + return -ENOBUFS; + } + dev->event_cb = cb; + return 0; +} + +int _rem_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; +} + +int _get(ng_netdev_t *netdev, ng_netconf_opt_t opt, void *value, size_t max_len) +{ + xbee_t *dev = (xbee_t *)netdev; + if (dev == NULL) { + return -ENODEV; + } + + switch (opt) { + case NETCONF_OPT_ADDRESS: + return _get_addr_short(dev, (uint8_t *)value, max_len); + case NETCONF_OPT_ADDRESS_LONG: + return _get_addr_long(dev, (uint8_t *)value, max_len); + case NETCONF_OPT_CHANNEL: + return _get_channel(dev, (uint8_t *)value, max_len); + case NETCONF_OPT_NID: + return _get_panid(dev, (uint8_t *)value, max_len); + case NETCONF_OPT_PROTO: + return _get_proto(dev, (uint8_t *)value, max_len); + default: + return -ENOTSUP; + } +} + +int _set(ng_netdev_t *netdev, ng_netconf_opt_t opt, void *value, size_t value_len) +{ + xbee_t *dev = (xbee_t *)netdev; + if (dev == NULL) { + return -ENODEV; + } + + switch (opt) { + case NETCONF_OPT_ADDRESS: + return _set_addr(dev, (uint8_t *)value, value_len); + case NETCONF_OPT_CHANNEL: + return _set_channel(dev, (uint8_t *)value, value_len); + case NETCONF_OPT_NID: + return _set_panid(dev, (uint8_t *)value, value_len); + case NETCONF_OPT_PROTO: + return _set_proto(dev, (uint8_t *)value, value_len); + default: + return -ENOTSUP; + } +} + +void _isr_event(ng_netdev_t *netdev, uint32_t event_type) +{ + xbee_t *dev = (xbee_t *)netdev; + ng_pktsnip_t *pkt_head; + ng_pktsnip_t *pkt; + ng_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 = 2; + } + else { + addr_len = 8; + } + + /* 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 = ng_pktbuf_add(NULL, NULL, + sizeof(ng_netif_hdr_t) + (2 * addr_len), + NG_NETTYPE_UNDEF); + if (pkt_head == NULL) { + DEBUG("xbee: Error allocating netif header in packet buffer on RX\n"); + dev->rx_count = 0; + return; + } + hdr = (ng_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[2 + addr_len]; + hdr->lqi = 0; + ng_netif_hdr_set_src_addr(hdr, &(dev->rx_buf[1]), addr_len); + if (addr_len == 2) { + ng_netif_hdr_set_dst_addr(hdr, dev->addr_short, 2); + } + else { + ng_netif_hdr_set_dst_addr(hdr, dev->addr_long, 8); + } + pos = 3 + addr_len; + /* allocate and copy payload */ + pkt = ng_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"); + ng_pktbuf_release(pkt_head); + dev->rx_count = 0; + return; + } + + /* 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 ng_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, +};