/* * Copyright (C) 2013 Alaeddine Weslati * * 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_at86rf231 * @{ * * @file * @brief Driver implementation of the AT86RF231 device driver * * @author Alaeddine Weslati * @author Thomas Eichinger * * @} */ #include "at86rf231.h" #include "at86rf231_spi.h" #include "board.h" #include "periph/gpio.h" #include "periph/spi.h" #include "kernel_types.h" #include "transceiver.h" #include "hwtimer.h" #include "config.h" #define ENABLE_DEBUG (0) #include "debug.h" #ifndef AT86RF231_SPI_SPEED #define SPI_SPEED SPI_SPEED_5MHZ #else #define SPI_SPEED AT86RF231_SPI_SPEED #endif #define _MAX_RETRIES (100) static uint16_t radio_pan; static uint8_t radio_channel; static uint16_t radio_address; static uint64_t radio_address_long; netdev_802154_raw_packet_cb_t at86rf231_raw_packet_cb; netdev_rcv_data_cb_t at86rf231_data_packet_cb; /* default source address length for sending in number of byte */ static size_t _default_src_addr_len = 2; uint8_t driver_state; void at86rf231_gpio_spi_interrupts_init(void); void at86rf231_reset(void); #ifdef MODULE_TRANSCEIVER void at86rf231_init(kernel_pid_t tpid) { transceiver_pid = tpid; at86rf231_initialize(NULL); } #endif int at86rf231_initialize(netdev_t *dev) { at86rf231_gpio_spi_interrupts_init(); at86rf231_reset(); at86rf231_on(); /* TODO : * and configure security, power */ #ifdef MODULE_CONFIG at86rf231_set_pan(sysconfig.radio_pan_id); #else at86rf231_set_pan(0x0001); #endif radio_channel = at86rf231_reg_read(AT86RF231_REG__PHY_CC_CCA) & AT86RF231_PHY_CC_CCA_MASK__CHANNEL; radio_address = 0x00FF & (uint16_t)at86rf231_reg_read(AT86RF231_REG__SHORT_ADDR_0); radio_address |= at86rf231_reg_read(AT86RF231_REG__SHORT_ADDR_1) << 8; radio_address_long = 0x00000000000000FF & (uint64_t)at86rf231_reg_read(AT86RF231_REG__IEEE_ADDR_0); radio_address_long |= ((uint64_t)at86rf231_reg_read(AT86RF231_REG__IEEE_ADDR_1)) << 8; radio_address_long |= ((uint64_t)at86rf231_reg_read(AT86RF231_REG__IEEE_ADDR_1)) << 16; radio_address_long |= ((uint64_t)at86rf231_reg_read(AT86RF231_REG__IEEE_ADDR_1)) << 24; radio_address_long |= ((uint64_t)at86rf231_reg_read(AT86RF231_REG__IEEE_ADDR_1)) << 32; radio_address_long |= ((uint64_t)at86rf231_reg_read(AT86RF231_REG__IEEE_ADDR_1)) << 40; radio_address_long |= ((uint64_t)at86rf231_reg_read(AT86RF231_REG__IEEE_ADDR_1)) << 48; radio_address_long |= ((uint64_t)at86rf231_reg_read(AT86RF231_REG__IEEE_ADDR_1)) << 56; return 0; } int at86rf231_on(void) { /* Send a FORCE TRX OFF command */ at86rf231_reg_write(AT86RF231_REG__TRX_STATE, AT86RF231_TRX_STATE__FORCE_TRX_OFF); /* busy wait for TRX_OFF state */ int delay = _MAX_RETRIES; do { if (!--delay) { DEBUG("at86rf231 : ERROR : could not enter TRX_OFF mode\n"); return 0; } } while (at86rf231_get_status() != AT86RF231_TRX_STATUS__TRX_OFF); at86rf231_reg_write(AT86RF231_REG__TRX_STATE, AT86RF231_TRX_STATE__RX_ON); /* change into basic reception mode to initialise CSMA seed by RNG */ delay = _MAX_RETRIES; do { if (!--delay) { DEBUG("at86rf231 : ERROR : could not enter RX_ON mode\n"); return 0; } } while (at86rf231_get_status() != AT86RF231_TRX_STATUS__RX_ON); /* read RNG values into CSMA_SEED_0 */ for (int i=0; i<7; i+=2) { uint8_t tmp = at86rf231_reg_read(AT86RF231_REG__PHY_CC_CCA); tmp = ((tmp&0x60)>>5); at86rf231_reg_write(AT86RF231_REG__CSMA_SEED_0, tmp << i); } /* read CSMA_SEED_1 and write back with RNG value */ uint8_t tmp = at86rf231_reg_read(AT86RF231_REG__CSMA_SEED_1); tmp |= ((at86rf231_reg_read(AT86RF231_REG__PHY_CC_CCA)&0x60)>>5); at86rf231_reg_write(AT86RF231_REG__CSMA_SEED_1, tmp); /* change into reception mode */ at86rf231_switch_to_rx(); return 1; } void at86rf231_off(void) { /* TODO */ } int at86rf231_is_on(void) { return ((at86rf231_get_status() & 0x1f) != 0); } void at86rf231_switch_to_rx(void) { gpio_irq_disable(AT86RF231_INT); /* now change to PLL_ON state for extended operating mode */ at86rf231_reg_write(AT86RF231_REG__TRX_STATE, AT86RF231_TRX_STATE__PLL_ON); do { int max_wait = _MAX_RETRIES; if (!--max_wait) { DEBUG("at86rf231 : ERROR : could not enter PLL_ON mode\n"); break; } } while (at86rf231_get_status() != AT86RF231_TRX_STATUS__PLL_ON); #ifndef MODULE_OPENWSN /* Reset IRQ to TRX END only */ at86rf231_reg_write(AT86RF231_REG__IRQ_MASK, AT86RF231_IRQ_STATUS_MASK__TRX_END); #else /* OpenWSN also needs RX_START IRQ */ at86rf231_reg_write(AT86RF231_REG__IRQ_MASK, ( AT86RF231_IRQ_STATUS_MASK__RX_START | AT86RF231_IRQ_STATUS_MASK__TRX_END)); #endif /* Read IRQ to clear it */ at86rf231_reg_read(AT86RF231_REG__IRQ_STATUS); /* Enable IRQ interrupt */ gpio_irq_enable(AT86RF231_INT); /* Enter RX_AACK_ON state */ at86rf231_reg_write(AT86RF231_REG__TRX_STATE, AT86RF231_TRX_STATE__RX_AACK_ON); do { int max_wait = _MAX_RETRIES; if (!--max_wait) { DEBUG("at86rf231 : ERROR : could not enter RX_AACK_ON mode\n"); break; } } while (at86rf231_get_status() != AT86RF231_TRX_STATUS__RX_AACK_ON); } void at86rf231_rxoverflow_irq(void) { /* TODO */ } #ifndef MODULE_OPENWSN void at86rf231_rx_irq(void) { /* check if we are in sending state */ if (driver_state == AT_DRIVER_STATE_SENDING) { /* Read IRQ to clear it */ at86rf231_reg_read(AT86RF231_REG__IRQ_STATUS); /* tx done, listen again */ at86rf231_switch_to_rx(); /* clear internal state */ driver_state = AT_DRIVER_STATE_DEFAULT; } else { /* handle receive */ at86rf231_rx_handler(); } } #endif int at86rf231_add_raw_recv_callback(netdev_t *dev, netdev_802154_raw_packet_cb_t recv_cb) { (void)dev; if (at86rf231_raw_packet_cb == NULL){ at86rf231_raw_packet_cb = recv_cb; return 0; } return -ENOBUFS; } int at86rf231_rem_raw_recv_callback(netdev_t *dev, netdev_802154_raw_packet_cb_t recv_cb) { (void)dev; at86rf231_raw_packet_cb = NULL; return 0; } int at86rf231_add_data_recv_callback(netdev_t *dev, netdev_rcv_data_cb_t recv_cb) { (void)dev; if (at86rf231_data_packet_cb == NULL){ at86rf231_data_packet_cb = recv_cb; return 0; } return -ENOBUFS; } int at86rf231_rem_data_recv_callback(netdev_t *dev, netdev_rcv_data_cb_t recv_cb) { (void)dev; at86rf231_data_packet_cb = NULL; return 0; } radio_address_t at86rf231_set_address(radio_address_t address) { radio_address = address; uint8_t old_state = at86rf231_get_status() & AT86RF231_TRX_STATUS_MASK__TRX_STATUS; /* Go to state PLL_ON */ at86rf231_reg_write(AT86RF231_REG__TRX_STATE, AT86RF231_TRX_STATE__PLL_ON); /* wait until it is on PLL_ON state */ do { int max_wait = _MAX_RETRIES; if (!--max_wait) { DEBUG("at86rf231 : ERROR : could not enter PLL_ON mode\n"); break; } } while ((at86rf231_get_status() & AT86RF231_TRX_STATUS_MASK__TRX_STATUS) != AT86RF231_TRX_STATUS__PLL_ON); at86rf231_reg_write(AT86RF231_REG__SHORT_ADDR_0, (uint8_t)(0x00FF & radio_address)); at86rf231_reg_write(AT86RF231_REG__SHORT_ADDR_1, (uint8_t)(radio_address >> 8)); /* Go to state old state */ at86rf231_reg_write(AT86RF231_REG__TRX_STATE, old_state); /* wait until it is on old state */ do { int max_wait = _MAX_RETRIES; if (!--max_wait) { DEBUG("at86rf231 : ERROR : could not enter old state %x\n", old_state); break; } } while ((at86rf231_get_status() & AT86RF231_TRX_STATUS_MASK__TRX_STATUS) != old_state); return radio_address; } radio_address_t at86rf231_get_address(void) { return radio_address; } uint64_t at86rf231_set_address_long(uint64_t address) { radio_address_long = address; uint8_t old_state = at86rf231_get_status() & AT86RF231_TRX_STATUS_MASK__TRX_STATUS; /* Go to state PLL_ON */ at86rf231_reg_write(AT86RF231_REG__TRX_STATE, AT86RF231_TRX_STATE__PLL_ON); /* wait until it is on PLL_ON state */ do { int max_wait = _MAX_RETRIES; if (!--max_wait) { DEBUG("at86rf231 : ERROR : could not enter PLL_ON mode\n"); break; } } while ((at86rf231_get_status() & AT86RF231_TRX_STATUS_MASK__TRX_STATUS) != AT86RF231_TRX_STATUS__PLL_ON); at86rf231_reg_write(AT86RF231_REG__IEEE_ADDR_0, (uint8_t)(radio_address_long)); at86rf231_reg_write(AT86RF231_REG__IEEE_ADDR_1, (uint8_t)(radio_address_long >> 8)); at86rf231_reg_write(AT86RF231_REG__IEEE_ADDR_2, (uint8_t)(radio_address_long >> 16)); at86rf231_reg_write(AT86RF231_REG__IEEE_ADDR_3, (uint8_t)(radio_address_long >> 24)); at86rf231_reg_write(AT86RF231_REG__IEEE_ADDR_4, (uint8_t)(radio_address_long >> 32)); at86rf231_reg_write(AT86RF231_REG__IEEE_ADDR_5, (uint8_t)(radio_address_long >> 40)); at86rf231_reg_write(AT86RF231_REG__IEEE_ADDR_6, (uint8_t)(radio_address_long >> 48)); at86rf231_reg_write(AT86RF231_REG__IEEE_ADDR_7, (uint8_t)(radio_address_long >> 56)); /* Go to state old state */ at86rf231_reg_write(AT86RF231_REG__TRX_STATE, old_state); /* wait until it is on old state */ do { int max_wait = _MAX_RETRIES; if (!--max_wait) { DEBUG("at86rf231 : ERROR : could not enter old state %x\n", old_state); break; } } while ((at86rf231_get_status() & AT86RF231_TRX_STATUS_MASK__TRX_STATUS) != old_state); return radio_address_long; } uint64_t at86rf231_get_address_long(void) { return radio_address_long; } uint16_t at86rf231_set_pan(uint16_t pan) { radio_pan = pan; at86rf231_reg_write(AT86RF231_REG__PAN_ID_0, (uint8_t)(0x00FF & radio_pan)); at86rf231_reg_write(AT86RF231_REG__PAN_ID_1, (uint8_t)(radio_pan >> 8)); return radio_pan; } uint16_t at86rf231_get_pan(void) { return radio_pan; } int at86rf231_set_channel(unsigned int channel) { radio_channel = channel; if (channel < AT86RF231_MIN_CHANNEL || channel > AT86RF231_MAX_CHANNEL) { #if DEVELHELP puts("[at86rf231] channel out of range!"); #endif return -1; } at86rf231_reg_write(AT86RF231_REG__PHY_CC_CCA, (radio_channel & AT86RF231_PHY_CC_CCA_MASK__CHANNEL)); return radio_channel; } unsigned int at86rf231_get_channel(void) { return radio_channel; } void at86rf231_set_monitor(int mode) { /* read register */ uint8_t tmp = at86rf231_reg_read(AT86RF231_REG__XAH_CTRL_1); /* set promicuous mode depending on *mode* */ if (mode) { at86rf231_reg_write(AT86RF231_REG__XAH_CTRL_1, (tmp|AT86RF231_XAH_CTRL_1__AACK_PROM_MODE)); } else { at86rf231_reg_write(AT86RF231_REG__XAH_CTRL_1, (tmp&(~AT86RF231_XAH_CTRL_1__AACK_PROM_MODE))); } } int at86rf231_get_monitor(void) { return (at86rf231_reg_read(AT86RF231_REG__XAH_CTRL_1) & AT86RF231_XAH_CTRL_1__AACK_PROM_MODE); } void at86rf231_gpio_spi_interrupts_init(void) { /* SPI init */ spi_acquire(AT86RF231_SPI); spi_init_master(AT86RF231_SPI, SPI_CONF_FIRST_RISING, SPI_SPEED); spi_release(AT86RF231_SPI); /* IRQ0 */ gpio_init_int(AT86RF231_INT, GPIO_NOPULL, GPIO_RISING, (gpio_cb_t)at86rf231_rx_irq, NULL); /* CS */ gpio_init_out(AT86RF231_CS, GPIO_NOPULL); /* SLEEP */ gpio_init_out(AT86RF231_SLEEP, GPIO_NOPULL); /* RESET */ gpio_init_out(AT86RF231_RESET, GPIO_NOPULL); } void at86rf231_reset(void) { /* force reset */ gpio_clear(AT86RF231_RESET); /* put pins to default values */ gpio_set(AT86RF231_CS); gpio_clear(AT86RF231_SLEEP); /* additional waiting to comply to min rst pulse width */ uint8_t volatile delay = 50; /* volatile to ensure it isn't optimized away */ while (--delay); gpio_set(AT86RF231_RESET); } int at86rf231_get_option(netdev_t *dev, netdev_opt_t opt, void *value, size_t *value_len) { /* XXX: first check only for backwards compatibility with transceiver * (see at86rf231_init) remove when adapter for transceiver exists */ if (dev != &at86rf231_netdev) { return -ENODEV; } switch (opt) { case NETDEV_OPT_CHANNEL: if (*value_len < sizeof(unsigned int)) { return -EOVERFLOW; } if (*value_len > sizeof(unsigned int)) { *value_len = sizeof(unsigned int); } *((unsigned int *)value) = at86rf231_get_channel(); break; case NETDEV_OPT_ADDRESS: if (*value_len < sizeof(uint16_t)) { return -EOVERFLOW; } if (*value_len > sizeof(uint16_t)) { *value_len = sizeof(uint16_t); } *((uint16_t *)value) = at86rf231_get_address(); break; case NETDEV_OPT_NID: if (*value_len < sizeof(uint16_t)) { return -EOVERFLOW; } if (*value_len > sizeof(uint16_t)) { *value_len = sizeof(uint16_t); } *((uint16_t *)value) = at86rf231_get_pan(); break; case NETDEV_OPT_ADDRESS_LONG: if (*value_len < sizeof(uint64_t)) { return -EOVERFLOW; } if (*value_len > sizeof(uint64_t)) { *value_len = sizeof(uint64_t); } *((uint64_t *)value) = at86rf231_get_address_long(); break; case NETDEV_OPT_MAX_PACKET_SIZE: if (*value_len == 0) { return -EOVERFLOW; } if (*value_len > sizeof(uint8_t)) { *value_len = sizeof(uint8_t); } *((uint8_t *)value) = AT86RF231_MAX_PKT_LENGTH; break; case NETDEV_OPT_PROTO: if (*value_len < sizeof(netdev_proto_t)) { return -EOVERFLOW; } if (*value_len > sizeof(netdev_proto_t)) { *value_len = sizeof(netdev_proto_t); } *((netdev_type_t *)value) = NETDEV_PROTO_802154; break; case NETDEV_OPT_SRC_LEN: if (*value_len < sizeof(size_t)) { return -EOVERFLOW; } if (*value_len > sizeof(size_t)) { *value_len = sizeof(size_t); } *((size_t *)value) = _default_src_addr_len; default: return -ENOTSUP; } return 0; } static int _type_pun_up_unsigned(void *value_out, size_t desired_len, void *value_in, size_t given_len) { if (given_len > desired_len) { return -EOVERFLOW; } /* XXX this is ugly, but bear with me */ switch (given_len) { case 8: switch (desired_len) { case 8: *((uint64_t *)value_out) = (*((uint64_t *)value_in)); return 0; default: return -EINVAL; } case 4: switch (desired_len) { case 8: *((uint64_t *)value_out) = (uint64_t)(*((uint32_t *)value_in)); return 0; case 4: *((uint32_t *)value_out) = (*((uint32_t *)value_in)); return 0; default: return -EINVAL; } case 2: switch (desired_len) { case 8: *((uint64_t *)value_out) = (uint64_t)(*((uint16_t *)value_in)); return 0; case 4: *((uint32_t *)value_out) = (uint32_t)(*((uint16_t *)value_in)); return 0; case 2: *((uint16_t *)value_out) = (*((uint16_t *)value_in)); return 0; default: return -EINVAL; } case 1: switch (desired_len) { case 8: *((uint64_t *)value_out) = (uint64_t)(*((uint8_t *)value_in)); return 0; case 4: *((uint32_t *)value_out) = (uint32_t)(*((uint8_t *)value_in)); return 0; case 2: *((uint16_t *)value_out) = (uint16_t)(*((uint8_t *)value_in)); return 0; case 1: *((uint8_t *)value_out) = (*((uint8_t *)value_in)); return 0; default: return -EINVAL; } default: return -EINVAL; } } int at86rf231_set_option(netdev_t *dev, netdev_opt_t opt, void *value, size_t value_len) { uint8_t set_value[sizeof(uint64_t)]; int res = 0; /* XXX: first check only for backwards compatibility with transceiver * (see at86rf231_init) remove when adapter for transceiver exists */ if (dev != &at86rf231_netdev) { return -ENODEV; } switch (opt) { case NETDEV_OPT_CHANNEL: if ((res = _type_pun_up_unsigned(set_value, sizeof(unsigned int), value, value_len)) == 0) { unsigned int *v = (unsigned int *)set_value; if (*v > 26) { return -EINVAL; } at86rf231_set_channel(*v); } break; case NETDEV_OPT_ADDRESS: if ((res = _type_pun_up_unsigned(set_value, sizeof(uint16_t), value, value_len)) == 0) { uint16_t *v = (uint16_t *)set_value; if (*v == 0xffff) { /* Do not allow setting to broadcast */ return -EINVAL; } at86rf231_set_address(*v); } break; case NETDEV_OPT_NID: if ((res = _type_pun_up_unsigned(set_value, sizeof(uint16_t), value, value_len)) == 0) { uint16_t *v = (uint16_t *)set_value; if (*v == 0xffff) { /* Do not allow setting to broadcast */ return -EINVAL; } at86rf231_set_pan(*v); } break; case NETDEV_OPT_ADDRESS_LONG: if ((res = _type_pun_up_unsigned(set_value, sizeof(uint64_t), value, value_len)) == 0) { uint64_t *v = (uint64_t *)set_value; /* TODO: error checking? */ at86rf231_set_address_long(*v); } break; case NETDEV_OPT_SRC_LEN: if ((res = _type_pun_up_unsigned(set_value, sizeof(size_t), value, value_len)) == 0) { size_t *v = (size_t *)set_value; if (*v != 2 && *v != 8) { return -EINVAL; } _default_src_addr_len = *v; } break; default: return -ENOTSUP; } return res; } int at86rf231_get_state(netdev_t *dev, netdev_state_t *state) { /* XXX: first check only for backwards compatibility with transceiver * (see at86rf231_init) remove when adapter for transceiver exists */ if (dev != &at86rf231_netdev) { return -ENODEV; } if (!at86rf231_is_on()) { *state = NETDEV_STATE_POWER_OFF; } else if (at86rf231_get_monitor()) { *state = NETDEV_STATE_PROMISCUOUS_MODE; } else { *state = NETDEV_STATE_RX_MODE; } return 0; } int at86rf231_set_state(netdev_t *dev, netdev_state_t state) { /* XXX: first check only for backwards compatibility with transceiver * (see at86rf231_init) remove when adapter for transceiver exists */ if (dev != &at86rf231_netdev) { return -ENODEV; } if (state != NETDEV_STATE_PROMISCUOUS_MODE && at86rf231_get_monitor()) { at86rf231_set_monitor(0); } switch (state) { case NETDEV_STATE_POWER_OFF: at86rf231_off(); break; case NETDEV_STATE_RX_MODE: at86rf231_switch_to_rx(); break; case NETDEV_STATE_PROMISCUOUS_MODE: at86rf231_set_monitor(1); break; default: return -ENOTSUP; } return 0; } int at86rf231_channel_is_clear(netdev_t *dev) { (void)dev; /* channel is checked by hardware automatically before transmission */ return 1; } void at86rf231_event(netdev_t *dev, uint32_t event_type) { (void)dev; (void)event_type; } const netdev_802154_driver_t at86rf231_driver = { .init = at86rf231_initialize, .send_data = netdev_802154_send_data, .add_receive_data_callback = at86rf231_add_data_recv_callback, .rem_receive_data_callback = at86rf231_rem_data_recv_callback, .get_option = at86rf231_get_option, .set_option = at86rf231_set_option, .get_state = at86rf231_get_state, .set_state = at86rf231_set_state, .event = at86rf231_event, .load_tx = at86rf231_load_tx_buf, .transmit = at86rf231_transmit_tx_buf, .send = netdev_802154_send, .add_receive_raw_callback = at86rf231_add_raw_recv_callback, .rem_receive_raw_callback = at86rf231_rem_raw_recv_callback, .channel_is_clear = at86rf231_channel_is_clear, }; netdev_t at86rf231_netdev = { NETDEV_TYPE_802154, (netdev_driver_t *) &at86rf231_driver, NULL };