/* * Copyright (C) 2016 Fundación Inria Chile * * 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_sx127x * @{ * @file * @brief Netdev adaptation for the sx127x driver * * @author Eugene P. * @author José Ignacio Alamos * @} */ #include #include #include #include "net/netopt.h" #include "net/netdev.h" #include "net/lora.h" #include "sx127x_registers.h" #include "sx127x_internal.h" #include "sx127x_netdev.h" #include "sx127x.h" #define ENABLE_DEBUG (0) #include "debug.h" /* Internal helper functions */ static int _set_state(sx127x_t *dev, netopt_state_t state); static int _get_state(sx127x_t *dev, void *val); void _on_dio0_irq(void *arg); void _on_dio1_irq(void *arg); void _on_dio2_irq(void *arg); void _on_dio3_irq(void *arg); static int _send(netdev_t *netdev, const iolist_t *iolist) { sx127x_t *dev = (sx127x_t*) netdev; if (sx127x_get_state(dev) == SX127X_RF_TX_RUNNING) { DEBUG("[sx127x] Cannot send packet: radio already in transmitting " "state.\n"); return -ENOTSUP; } uint8_t size = iolist_size(iolist); switch (dev->settings.modem) { case SX127X_MODEM_FSK: /* todo */ break; case SX127X_MODEM_LORA: /* Initializes the payload size */ sx127x_set_payload_length(dev, size); /* Full buffer used for Tx */ sx127x_reg_write(dev, SX127X_REG_LR_FIFOTXBASEADDR, 0x00); sx127x_reg_write(dev, SX127X_REG_LR_FIFOADDRPTR, 0x00); /* FIFO operations can not take place in Sleep mode * So wake up the chip */ if (sx127x_get_op_mode(dev) == SX127X_RF_OPMODE_SLEEP) { sx127x_set_standby(dev); xtimer_usleep(SX127X_RADIO_WAKEUP_TIME); /* wait for chip wake up */ } /* Write payload buffer */ for (const iolist_t *iol = iolist; iol; iol = iol->iol_next) { sx127x_write_fifo(dev, iol->iol_base, iol->iol_len); } break; default: puts("sx127x_netdev, Unsupported modem"); break; } /* Enable TXDONE interrupt */ sx127x_reg_write(dev, SX127X_REG_LR_IRQFLAGSMASK, SX127X_RF_LORA_IRQFLAGS_RXTIMEOUT | SX127X_RF_LORA_IRQFLAGS_RXDONE | SX127X_RF_LORA_IRQFLAGS_PAYLOADCRCERROR | SX127X_RF_LORA_IRQFLAGS_VALIDHEADER | /* SX127X_RF_LORA_IRQFLAGS_TXDONE | */ SX127X_RF_LORA_IRQFLAGS_CADDONE | SX127X_RF_LORA_IRQFLAGS_FHSSCHANGEDCHANNEL | SX127X_RF_LORA_IRQFLAGS_CADDETECTED); /* Set TXDONE interrupt to the DIO0 line */ sx127x_reg_write(dev, SX127X_REG_DIOMAPPING1, (sx127x_reg_read(dev, SX127X_REG_DIOMAPPING1) & SX127X_RF_LORA_DIOMAPPING1_DIO0_MASK) | SX127X_RF_LORA_DIOMAPPING1_DIO0_01); /* Start TX timeout timer */ xtimer_set(&dev->_internal.tx_timeout_timer, dev->settings.lora.tx_timeout); /* Put chip into transfer mode */ sx127x_set_state(dev, SX127X_RF_TX_RUNNING); sx127x_set_op_mode(dev, SX127X_RF_OPMODE_TRANSMITTER); return 0; } static int _recv(netdev_t *netdev, void *buf, size_t len, void *info) { sx127x_t *dev = (sx127x_t*) netdev; volatile uint8_t irq_flags = 0; uint8_t size = 0; switch (dev->settings.modem) { case SX127X_MODEM_FSK: /* todo */ break; case SX127X_MODEM_LORA: /* Clear IRQ */ sx127x_reg_write(dev, SX127X_REG_LR_IRQFLAGS, SX127X_RF_LORA_IRQFLAGS_RXDONE); irq_flags = sx127x_reg_read(dev, SX127X_REG_LR_IRQFLAGS); if ( (irq_flags & SX127X_RF_LORA_IRQFLAGS_PAYLOADCRCERROR_MASK) == SX127X_RF_LORA_IRQFLAGS_PAYLOADCRCERROR) { /* Clear IRQ */ sx127x_reg_write(dev, SX127X_REG_LR_IRQFLAGS, SX127X_RF_LORA_IRQFLAGS_PAYLOADCRCERROR); if (!(dev->settings.lora.flags & SX127X_RX_CONTINUOUS_FLAG)) { sx127x_set_state(dev, SX127X_RF_IDLE); } xtimer_remove(&dev->_internal.rx_timeout_timer); netdev->event_callback(netdev, NETDEV_EVENT_CRC_ERROR); return -EBADMSG; } netdev_sx127x_lora_packet_info_t *packet_info = info; if (packet_info) { /* there is no LQI for LoRa */ packet_info->lqi = 0; uint8_t snr_value = sx127x_reg_read(dev, SX127X_REG_LR_PKTSNRVALUE); if (snr_value & 0x80) { /* The SNR is negative */ /* Invert and divide by 4 */ packet_info->snr = -1 * ((~snr_value + 1) & 0xFF) >> 2; } else { /* Divide by 4 */ packet_info->snr = (snr_value & 0xFF) >> 2; } int16_t rssi = sx127x_reg_read(dev, SX127X_REG_LR_PKTRSSIVALUE); if (packet_info->snr < 0) { #if defined(MODULE_SX1272) packet_info->rssi = SX127X_RSSI_OFFSET + rssi + (rssi >> 4) + packet_info->snr; #else /* MODULE_SX1276 */ if (dev->settings.channel > SX127X_RF_MID_BAND_THRESH) { packet_info->rssi = SX127X_RSSI_OFFSET_HF + rssi + (rssi >> 4) + packet_info->snr; } else { packet_info->rssi = SX127X_RSSI_OFFSET_LF + rssi + (rssi >> 4) + packet_info->snr; } #endif } else { #if defined(MODULE_SX1272) packet_info->rssi = SX127X_RSSI_OFFSET + rssi + (rssi >> 4); #else /* MODULE_SX1276 */ if (dev->settings.channel > SX127X_RF_MID_BAND_THRESH) { packet_info->rssi = SX127X_RSSI_OFFSET_HF + rssi + (rssi >> 4); } else { packet_info->rssi = SX127X_RSSI_OFFSET_LF + rssi + (rssi >> 4); } #endif } } size = sx127x_reg_read(dev, SX127X_REG_LR_RXNBBYTES); if (buf == NULL) { return size; } if (size > len) { return -ENOBUFS; } if (!(dev->settings.lora.flags & SX127X_RX_CONTINUOUS_FLAG)) { sx127x_set_state(dev, SX127X_RF_IDLE); } xtimer_remove(&dev->_internal.rx_timeout_timer); /* Read the last packet from FIFO */ uint8_t last_rx_addr = sx127x_reg_read(dev, SX127X_REG_LR_FIFORXCURRENTADDR); sx127x_reg_write(dev, SX127X_REG_LR_FIFOADDRPTR, last_rx_addr); sx127x_read_fifo(dev, (uint8_t*)buf, size); break; default: break; } return size; } static int _init(netdev_t *netdev) { sx127x_t *sx127x = (sx127x_t*) netdev; sx127x->irq = 0; sx127x_radio_settings_t settings; settings.channel = SX127X_CHANNEL_DEFAULT; settings.modem = SX127X_MODEM_DEFAULT; settings.state = SX127X_RF_IDLE; sx127x->settings = settings; /* Launch initialization of driver and device */ DEBUG("[sx127x] netdev: initializing driver...\n"); if (sx127x_init(sx127x) != SX127X_INIT_OK) { DEBUG("[sx127x] netdev: initialization failed\n"); return -1; } sx127x_init_radio_settings(sx127x); /* Put chip into sleep */ sx127x_set_sleep(sx127x); DEBUG("[sx127x] netdev: initialization done\n"); return 0; } static void _isr(netdev_t *netdev) { sx127x_t *dev = (sx127x_t *) netdev; uint8_t irq = dev->irq; dev->irq = 0; switch (irq) { case SX127X_IRQ_DIO0: _on_dio0_irq(dev); break; case SX127X_IRQ_DIO1: _on_dio1_irq(dev); break; case SX127X_IRQ_DIO2: _on_dio2_irq(dev); break; case SX127X_IRQ_DIO3: _on_dio3_irq(dev); break; default: break; } } static int _get(netdev_t *netdev, netopt_t opt, void *val, size_t max_len) { (void) max_len; /* unused when compiled without debug, assert empty */ sx127x_t *dev = (sx127x_t*) netdev; if (dev == NULL) { return -ENODEV; } switch(opt) { case NETOPT_STATE: assert(max_len >= sizeof(netopt_state_t)); return _get_state(dev, val); case NETOPT_DEVICE_TYPE: assert(max_len >= sizeof(uint16_t)); *((uint16_t*) val) = NETDEV_TYPE_LORA; return sizeof(uint16_t); case NETOPT_CHANNEL_FREQUENCY: assert(max_len >= sizeof(uint32_t)); *((uint32_t*) val) = sx127x_get_channel(dev); return sizeof(uint32_t); case NETOPT_BANDWIDTH: assert(max_len >= sizeof(uint8_t)); *((uint8_t*) val) = sx127x_get_bandwidth(dev); return sizeof(uint8_t); case NETOPT_SPREADING_FACTOR: assert(max_len >= sizeof(uint8_t)); *((uint8_t*) val) = sx127x_get_spreading_factor(dev); return sizeof(uint8_t); case NETOPT_CODING_RATE: assert(max_len >= sizeof(uint8_t)); *((uint8_t*) val) = sx127x_get_coding_rate(dev); return sizeof(uint8_t); case NETOPT_MAX_PACKET_SIZE: assert(max_len >= sizeof(uint8_t)); *((uint8_t*) val) = sx127x_get_max_payload_len(dev); return sizeof(uint8_t); case NETOPT_INTEGRITY_CHECK: assert(max_len >= sizeof(netopt_enable_t)); *((netopt_enable_t*) val) = sx127x_get_crc(dev) ? NETOPT_ENABLE : NETOPT_DISABLE; return sizeof(netopt_enable_t); case NETOPT_CHANNEL_HOP: assert(max_len >= sizeof(netopt_enable_t)); *((netopt_enable_t*) val) = (dev->settings.lora.flags & SX127X_CHANNEL_HOPPING_FLAG) ? NETOPT_ENABLE : NETOPT_DISABLE; return sizeof(netopt_enable_t); case NETOPT_CHANNEL_HOP_PERIOD: assert(max_len >= sizeof(uint8_t)); *((uint8_t*) val) = sx127x_get_hop_period(dev); return sizeof(uint8_t); case NETOPT_SINGLE_RECEIVE: assert(max_len >= sizeof(uint8_t)); *((netopt_enable_t*) val) = sx127x_get_rx_single(dev) ? NETOPT_ENABLE : NETOPT_DISABLE; return sizeof(netopt_enable_t); case NETOPT_TX_POWER: assert(max_len >= sizeof(int16_t)); *((int16_t*) val) = (int16_t)sx127x_get_tx_power(dev); return sizeof(int16_t); case NETOPT_IQ_INVERT: assert(max_len >= sizeof(uint8_t)); *((netopt_enable_t*) val) = sx127x_get_iq_invert(dev) ? NETOPT_ENABLE : NETOPT_DISABLE; return sizeof(netopt_enable_t); default: break; } return -ENOTSUP; } static int _set(netdev_t *netdev, netopt_t opt, const void *val, size_t len) { (void)len; /* unused when compiled without debug, assert empty */ sx127x_t *dev = (sx127x_t*) netdev; int res = -ENOTSUP; if (dev == NULL) { return -ENODEV; } switch(opt) { case NETOPT_STATE: assert(len <= sizeof(netopt_state_t)); return _set_state(dev, *((const netopt_state_t*) val)); case NETOPT_DEVICE_TYPE: assert(len <= sizeof(uint16_t)); /* Only LoRa modem is supported for the moment */ if (*(const uint16_t*) val == NETDEV_TYPE_LORA) { sx127x_set_modem(dev, SX127X_MODEM_LORA); return sizeof(uint16_t); } else { return -EINVAL; } case NETOPT_CHANNEL_FREQUENCY: assert(len <= sizeof(uint32_t)); sx127x_set_channel(dev, *((const uint32_t*) val)); return sizeof(uint32_t); case NETOPT_BANDWIDTH: assert(len <= sizeof(uint8_t)); uint8_t bw = *((const uint8_t *)val); if (bw > LORA_BW_500_KHZ) { res = -EINVAL; break; } sx127x_set_bandwidth(dev, bw); return sizeof(uint8_t); case NETOPT_SPREADING_FACTOR: assert(len <= sizeof(uint8_t)); uint8_t sf = *((const uint8_t *)val); if ((sf < LORA_SF6) || (sf > LORA_SF12)) { res = -EINVAL; break; } sx127x_set_spreading_factor(dev, sf); return sizeof(uint8_t); case NETOPT_CODING_RATE: assert(len <= sizeof(uint8_t)); uint8_t cr = *((const uint8_t *)val); if ((cr < LORA_CR_4_5) || (cr > LORA_CR_4_8)) { res = -EINVAL; break; } sx127x_set_coding_rate(dev, cr); return sizeof(uint8_t); case NETOPT_MAX_PACKET_SIZE: assert(len <= sizeof(uint8_t)); sx127x_set_max_payload_len(dev, *((const uint8_t*) val)); return sizeof(uint8_t); case NETOPT_INTEGRITY_CHECK: assert(len <= sizeof(netopt_enable_t)); sx127x_set_crc(dev, *((const netopt_enable_t*) val) ? true : false); return sizeof(netopt_enable_t); case NETOPT_CHANNEL_HOP: assert(len <= sizeof(netopt_enable_t)); sx127x_set_freq_hop(dev, *((const netopt_enable_t*) val) ? true : false); return sizeof(netopt_enable_t); case NETOPT_CHANNEL_HOP_PERIOD: assert(len <= sizeof(uint8_t)); sx127x_set_hop_period(dev, *((const uint8_t*) val)); return sizeof(uint8_t); case NETOPT_SINGLE_RECEIVE: assert(len <= sizeof(netopt_enable_t)); sx127x_set_rx_single(dev, *((const netopt_enable_t*) val) ? true : false); return sizeof(netopt_enable_t); case NETOPT_RX_TIMEOUT: assert(len <= sizeof(uint32_t)); sx127x_set_rx_timeout(dev, *((const uint32_t*) val)); return sizeof(uint32_t); case NETOPT_TX_TIMEOUT: assert(len <= sizeof(uint32_t)); sx127x_set_tx_timeout(dev, *((const uint32_t*) val)); return sizeof(uint32_t); case NETOPT_TX_POWER: assert(len <= sizeof(int16_t)); int16_t power = *((const int16_t *)val); if ((power < INT8_MIN) || (power > INT8_MAX)) { res = -EINVAL; break; } sx127x_set_tx_power(dev, (int8_t)power); return sizeof(int16_t); case NETOPT_FIXED_HEADER: assert(len <= sizeof(netopt_enable_t)); sx127x_set_fixed_header_len_mode(dev, *((const netopt_enable_t*) val) ? true : false); return sizeof(netopt_enable_t); case NETOPT_PREAMBLE_LENGTH: assert(len <= sizeof(uint16_t)); sx127x_set_preamble_length(dev, *((const uint16_t*) val)); return sizeof(uint16_t); case NETOPT_IQ_INVERT: assert(len <= sizeof(netopt_enable_t)); sx127x_set_iq_invert(dev, *((const netopt_enable_t*) val) ? true : false); return sizeof(bool); default: break; } return res; } static int _set_state(sx127x_t *dev, netopt_state_t state) { switch (state) { case NETOPT_STATE_SLEEP: sx127x_set_sleep(dev); break; case NETOPT_STATE_STANDBY: sx127x_set_standby(dev); break; case NETOPT_STATE_IDLE: /* set permanent listening */ sx127x_set_rx_timeout(dev, 0); sx127x_set_rx(dev); break; case NETOPT_STATE_RX: sx127x_set_rx(dev); break; case NETOPT_STATE_TX: sx127x_set_tx(dev); break; case NETOPT_STATE_RESET: sx127x_reset(dev); break; default: return -ENOTSUP; } return sizeof(netopt_state_t); } static int _get_state(sx127x_t *dev, void *val) { uint8_t op_mode; op_mode = sx127x_get_op_mode(dev); netopt_state_t state = NETOPT_STATE_OFF; switch(op_mode) { case SX127X_RF_OPMODE_SLEEP: state = NETOPT_STATE_SLEEP; break; case SX127X_RF_OPMODE_STANDBY: state = NETOPT_STATE_STANDBY; break; case SX127X_RF_OPMODE_TRANSMITTER: state = NETOPT_STATE_TX; break; case SX127X_RF_OPMODE_RECEIVER: case SX127X_RF_LORA_OPMODE_RECEIVER_SINGLE: state = NETOPT_STATE_IDLE; break; default: break; } memcpy(val, &state, sizeof(netopt_state_t)); return sizeof(netopt_state_t); } void _on_dio0_irq(void *arg) { sx127x_t *dev = (sx127x_t *) arg; netdev_t *netdev = (netdev_t*) &dev->netdev; switch (dev->settings.state) { case SX127X_RF_RX_RUNNING: netdev->event_callback(netdev, NETDEV_EVENT_RX_COMPLETE); break; case SX127X_RF_TX_RUNNING: xtimer_remove(&dev->_internal.tx_timeout_timer); switch (dev->settings.modem) { case SX127X_MODEM_LORA: /* Clear IRQ */ sx127x_reg_write(dev, SX127X_REG_LR_IRQFLAGS, SX127X_RF_LORA_IRQFLAGS_TXDONE); /* Intentional fall-through */ case SX127X_MODEM_FSK: default: sx127x_set_state(dev, SX127X_RF_IDLE); netdev->event_callback(netdev, NETDEV_EVENT_TX_COMPLETE); break; } break; case SX127X_RF_IDLE: DEBUG("[sx127x] netdev: sx127x_on_dio0: IDLE state\n"); break; default: DEBUG("[sx127x] netdev: sx127x_on_dio0: unknown state [%d]\n", dev->settings.state); break; } } void _on_dio1_irq(void *arg) { /* Get interrupt context */ sx127x_t *dev = (sx127x_t *) arg; netdev_t *netdev = (netdev_t*) &dev->netdev; switch (dev->settings.state) { case SX127X_RF_RX_RUNNING: switch (dev->settings.modem) { case SX127X_MODEM_FSK: /* todo */ break; case SX127X_MODEM_LORA: xtimer_remove(&dev->_internal.rx_timeout_timer); /* Clear Irq */ sx127x_reg_write(dev, SX127X_REG_LR_IRQFLAGS, SX127X_RF_LORA_IRQFLAGS_RXTIMEOUT); sx127x_set_state(dev, SX127X_RF_IDLE); netdev->event_callback(netdev, NETDEV_EVENT_RX_TIMEOUT); break; default: break; } break; case SX127X_RF_TX_RUNNING: switch (dev->settings.modem) { case SX127X_MODEM_FSK: /* todo */ break; case SX127X_MODEM_LORA: break; default: break; } break; default: puts("[sx127x] netdev: sx127x_on_dio1: unknown state"); break; } } void _on_dio2_irq(void *arg) { /* Get interrupt context */ sx127x_t *dev = (sx127x_t *) arg; netdev_t *netdev = (netdev_t*) dev; switch (dev->settings.state) { case SX127X_RF_RX_RUNNING: switch (dev->settings.modem) { case SX127X_MODEM_FSK: /* todo */ break; case SX127X_MODEM_LORA: if (dev->settings.lora.flags & SX127X_CHANNEL_HOPPING_FLAG) { /* Clear IRQ */ sx127x_reg_write(dev, SX127X_REG_LR_IRQFLAGS, SX127X_RF_LORA_IRQFLAGS_FHSSCHANGEDCHANNEL); dev->_internal.last_channel = (sx127x_reg_read(dev, SX127X_REG_LR_HOPCHANNEL) & SX127X_RF_LORA_HOPCHANNEL_CHANNEL_MASK); netdev->event_callback(netdev, NETDEV_EVENT_FHSS_CHANGE_CHANNEL); } break; default: break; } break; case SX127X_RF_TX_RUNNING: switch (dev->settings.modem) { case SX127X_MODEM_FSK: break; case SX127X_MODEM_LORA: if (dev->settings.lora.flags & SX127X_CHANNEL_HOPPING_FLAG) { /* Clear IRQ */ sx127x_reg_write(dev, SX127X_REG_LR_IRQFLAGS, SX127X_RF_LORA_IRQFLAGS_FHSSCHANGEDCHANNEL); dev->_internal.last_channel = (sx127x_reg_read(dev, SX127X_REG_LR_HOPCHANNEL) & SX127X_RF_LORA_HOPCHANNEL_CHANNEL_MASK); netdev->event_callback(netdev, NETDEV_EVENT_FHSS_CHANGE_CHANNEL); } break; default: break; } break; default: puts("[sx127x] netdev: sx127x_on_dio2: unknown state"); break; } } void _on_dio3_irq(void *arg) { /* Get interrupt context */ sx127x_t *dev = (sx127x_t *) arg; netdev_t *netdev = (netdev_t *) dev; switch (dev->settings.modem) { case SX127X_MODEM_FSK: break; case SX127X_MODEM_LORA: /* Clear IRQ */ sx127x_reg_write(dev, SX127X_REG_LR_IRQFLAGS, SX127X_RF_LORA_IRQFLAGS_CADDETECTED | SX127X_RF_LORA_IRQFLAGS_CADDONE); /* Send event message */ dev->_internal.is_last_cad_success = ((sx127x_reg_read(dev, SX127X_REG_LR_IRQFLAGS) & SX127X_RF_LORA_IRQFLAGS_CADDETECTED) == SX127X_RF_LORA_IRQFLAGS_CADDETECTED); netdev->event_callback(netdev, NETDEV_EVENT_CAD_DONE); break; default: puts("[sx127x] netdev: sx127x_on_dio3: unknown modem"); break; } } const netdev_driver_t sx127x_driver = { .send = _send, .recv = _recv, .init = _init, .isr = _isr, .get = _get, .set = _set, };