/* * 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 drivers_xbee * @{ * * @file * @brief High-level driver implementation for the XBee S1 802.15.4 modem * * @author Kévin Roussel * @author Hauke Petersen * * @} */ #include #include #include "xbee.h" #include "assert.h" #include "xtimer.h" #include "net/eui64.h" #include "net/netdev.h" #include "net/ieee802154.h" #ifdef MODULE_GNRC #include "net/gnrc.h" #endif #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 (1100UL * US_PER_MS) /** * @brief Delay when resetting the device, 10ms */ #define RESET_DELAY (10UL * US_PER_MS) /** * @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((netdev_t *)dev, NETDEV_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, const 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, const 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, const 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, const 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, const 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, const 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, const 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(netdev_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(netdev_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(netdev_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(netdev_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, NETDEV_EVENT_RX_COMPLETE); } } } static int xbee_get(netdev_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); #ifdef MODULE_GNRC case NETOPT_PROTO: if (max_len != sizeof(gnrc_nettype_t)) { return -EOVERFLOW; } *((gnrc_nettype_t *)value) = XBEE_DEFAULT_PROTOCOL; return sizeof(gnrc_nettype_t); #endif default: return -ENOTSUP; } } static int xbee_set(netdev_t *ndev, netopt_t opt, const void *value, size_t len) { xbee_t *dev = (xbee_t *)ndev; assert(dev); switch (opt) { case NETOPT_ADDRESS: return _set_addr(dev, value, len); case NETOPT_ADDR_LEN: case NETOPT_SRC_LEN: return _set_addr_len(dev, value, len); case NETOPT_CHANNEL: return _set_channel(dev, value, len); case NETOPT_NID: return _set_panid(dev, value, len); #ifdef MODULE_XBEE_ENCRYPTION case NETOPT_ENCRYPTION: return _set_encryption(dev, value); case NETOPT_ENCRYPTION_KEY: return _set_encryption_key(dev, value, len); #endif default: return -ENOTSUP; } } /* * The drivers netdev interface */ const netdev_driver_t xbee_driver = { .send = xbee_send, .recv = xbee_recv, .init = xbee_init, .isr = xbee_isr, .get = xbee_get, .set = xbee_set, };