/* * Copyright (C) 2017 Inria * * 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_rn2xx3 * @{ * * @file * @brief Driver implementation for the RN2483/RN2903 devices * * @author Alexandre Abadie * * @} */ #include #include #include "assert.h" #include "ztimer.h" #include "fmt.h" #include "kernel_defines.h" #include "rn2xx3_params.h" #include "rn2xx3.h" #include "rn2xx3_internal.h" #define ENABLE_DEBUG 0 /* Warning: to correctly display the debug message from sleep timer callback,, add CFLAGS+=-DTHREAD_STACKSIZE_IDLE=THREAD_STACKSIZE_DEFAULT to the build command. */ #include "debug.h" /** * @brief Delay when resetting the device, 10ms */ #define RESET_DELAY (10UL) /* * Interrupt callbacks */ static void _rx_cb(void *arg, uint8_t c) { rn2xx3_t *dev = arg; netdev_t *netdev = &dev->netdev; /* Avoid overflow of module response buffer */ if (dev->resp_size >= RN2XX3_MAX_BUF) { return; } /* The device closes the response with \r\n when done */ if (c == '\r') { return; } else if (c == '\n') { /* response end delimiter */ dev->resp_buf[dev->resp_size++] = '\0'; if (dev->int_state == RN2XX3_INT_STATE_MAC_RX_MESSAGE) { /* RX state: closing RX buffer */ dev->rx_buf[(dev->rx_size + 1) / 2] = 0; netdev_trigger_event_isr(netdev); } else if (dev->int_state == RN2XX3_INT_STATE_MAC_TX) { /* still in TX state: transmission complete but no data received */ netdev_trigger_event_isr(netdev); } dev->resp_size = 0; dev->rx_size = 0; dev->resp_done = 1; mutex_unlock(&(dev->resp_lock)); } else { switch (dev->int_state) { /* A successful mac TX command expects 2 kinds of replies: * - mac_tx_ok: transmission done, no data received * - mac_rx : transmission done, some data * received on port . */ case RN2XX3_INT_STATE_MAC_TX: /* some data are available */ dev->resp_buf[dev->resp_size++] = c; /* if module response ends with 'rx ' after 8 received chars, the module starts receiving data */ if (dev->resp_size == 8 && dev->resp_buf[4] == 'r' && dev->resp_buf[5] == 'x' && dev->resp_buf[6] == ' ') { /* next received chars correspond to the port number */ dev->int_state = RN2XX3_INT_STATE_MAC_RX_PORT; } break; case RN2XX3_INT_STATE_MAC_RX_PORT: dev->resp_buf[dev->resp_size++] = c; if (c == ' ') { dev->int_state = RN2XX3_INT_STATE_MAC_RX_MESSAGE; } break; case RN2XX3_INT_STATE_MAC_RX_MESSAGE: /* read and convert RX data (received in hex chars) */ if (c == ' ') { dev->resp_buf[dev->resp_size++] = c; break; } dev->rx_tmp_buf[dev->rx_size % 2] = c; /* We convert pairs of hex character to bytes on the fly to save space in memory */ if (dev->rx_size != 0 && dev->rx_size % 2) { rn2xx3_hex_to_bytes(dev->rx_tmp_buf, &dev->rx_buf[(dev->rx_size - 1) / 2]); } dev->rx_size++; break; default: dev->resp_buf[dev->resp_size++] = c; break; } } } static void _sleep_timer_cb(void *arg) { DEBUG("[rn2xx3] exit sleep\n"); rn2xx3_t *dev = arg; dev->int_state = RN2XX3_INT_STATE_IDLE; } /* * Driver's "public" functions */ void rn2xx3_setup(rn2xx3_t *dev, const rn2xx3_params_t *params) { assert(dev && (params->uart < UART_NUMOF)); /* initialize device parameters */ dev->p = *params; /* initialize pins and perform hardware reset */ if (gpio_is_valid(dev->p.pin_reset)) { gpio_init(dev->p.pin_reset, GPIO_OUT); gpio_set(dev->p.pin_reset); } /* UART is initialized later, since interrupts cannot be handled yet */ } int rn2xx3_init(rn2xx3_t *dev) { rn2xx3_set_internal_state(dev, RN2XX3_INT_STATE_RESET); /* initialize buffers and locks*/ dev->resp_size = 0; dev->cmd_buf[0] = 0; /* initialize UART and GPIO pins */ if (uart_init(dev->p.uart, dev->p.baudrate, _rx_cb, dev) != UART_OK) { DEBUG("[rn2xx3] init: error initializing UART\n"); return -ENXIO; } /* if reset pin is connected, do a hardware reset */ if (gpio_is_valid(dev->p.pin_reset)) { gpio_clear(dev->p.pin_reset); ztimer_sleep(ZTIMER_MSEC, RESET_DELAY); gpio_set(dev->p.pin_reset); } dev->sleep_timer.callback = _sleep_timer_cb; dev->sleep_timer.arg = dev; rn2xx3_sys_set_sleep_duration(dev, CONFIG_RN2XX3_DEFAULT_SLEEP); /* sending empty command to clear uart buffer */ if (rn2xx3_write_cmd(dev) == RN2XX3_TIMEOUT) { DEBUG("[rn2xx3] init: initialization failed\n"); return RN2XX3_TIMEOUT; } if (rn2xx3_mac_init(dev) != RN2XX3_OK) { DEBUG("[rn2xx3] mac: initialization failed\n"); return RN2XX3_ERR_MAC_INIT; } DEBUG("[rn2xx3] init: initialization successful\n"); return RN2XX3_OK; } int rn2xx3_sys_reset(rn2xx3_t *dev) { size_t p = snprintf(dev->cmd_buf, sizeof(dev->cmd_buf) - 1, "sys reset"); dev->cmd_buf[p] = 0; int ret = rn2xx3_write_cmd(dev); if (ret == RN2XX3_TIMEOUT || ret == RN2XX3_ERR_SLEEP_MODE) { DEBUG("[rn2xx3] reset: failed\n"); return ret; } return RN2XX3_OK; } int rn2xx3_sys_factory_reset(rn2xx3_t *dev) { size_t p = snprintf(dev->cmd_buf, sizeof(dev->cmd_buf) - 1, "sys factoryRESET"); dev->cmd_buf[p] = 0; int ret = rn2xx3_write_cmd(dev); if (ret == RN2XX3_TIMEOUT || ret == RN2XX3_ERR_SLEEP_MODE) { DEBUG("[rn2xx3] factory reset: failed\n"); return ret; } return RN2XX3_OK; } int rn2xx3_sys_sleep(rn2xx3_t *dev) { size_t p = snprintf(dev->cmd_buf, sizeof(dev->cmd_buf) - 1, "sys sleep %" PRIu32 "", dev->sleep); dev->cmd_buf[p] = 0; if (dev->int_state == RN2XX3_INT_STATE_SLEEP) { DEBUG("[rn2xx3] sleep: device already in sleep mode\n"); return RN2XX3_ERR_SLEEP_MODE; } /* always succeeds */ rn2xx3_write_cmd_no_wait(dev); /* Wait a little to check if the device could go to sleep. No answer means it worked. */ ztimer_sleep(ZTIMER_MSEC, 1); DEBUG("[rn2xx3] RESP: %s\n", dev->resp_buf); if (rn2xx3_process_response(dev) == RN2XX3_ERR_INVALID_PARAM) { DEBUG("[rn2xx3] sleep: cannot put module in sleep mode\n"); return RN2XX3_ERR_INVALID_PARAM; } rn2xx3_set_internal_state(dev, RN2XX3_INT_STATE_SLEEP); ztimer_set(ZTIMER_MSEC, &dev->sleep_timer, dev->sleep); return RN2XX3_OK; } int rn2xx3_mac_init(rn2xx3_t *dev) { rn2xx3_mac_set_dr(dev, CONFIG_LORAMAC_DEFAULT_DR); rn2xx3_mac_set_tx_power(dev, CONFIG_LORAMAC_DEFAULT_TX_POWER); rn2xx3_mac_set_tx_port(dev, CONFIG_LORAMAC_DEFAULT_TX_PORT); rn2xx3_mac_set_tx_mode(dev, CONFIG_LORAMAC_DEFAULT_TX_MODE); rn2xx3_mac_set_adr(dev, IS_ACTIVE(CONFIG_LORAMAC_DEFAULT_ADR)); rn2xx3_mac_set_retx(dev, CONFIG_LORAMAC_DEFAULT_RETX); rn2xx3_mac_set_linkchk_interval(dev, CONFIG_LORAMAC_DEFAULT_LINKCHK); rn2xx3_mac_set_rx1_delay(dev, CONFIG_LORAMAC_DEFAULT_RX1_DELAY); rn2xx3_mac_set_ar(dev, IS_ACTIVE(CONFIG_RN2XX3_DEFAULT_AR)); rn2xx3_mac_set_rx2_dr(dev, CONFIG_LORAMAC_DEFAULT_RX2_DR); rn2xx3_mac_set_rx2_freq(dev, CONFIG_LORAMAC_DEFAULT_RX2_FREQ); return RN2XX3_OK; } int rn2xx3_mac_tx(rn2xx3_t *dev, uint8_t *payload, uint8_t payload_len) { if (dev->int_state == RN2XX3_INT_STATE_SLEEP) { DEBUG("[rn2xx3] ABORT: device is in sleep mode\n"); return RN2XX3_ERR_SLEEP_MODE; } rn2xx3_mac_tx_start(dev); for (unsigned i = 0; i < payload_len; ++i) { rn2xx3_cmd_append(dev, &payload[i], 1); } int ret = rn2xx3_mac_tx_finalize(dev); if (ret != RN2XX3_OK) { rn2xx3_set_internal_state(dev, RN2XX3_INT_STATE_IDLE); DEBUG("[rn2xx3] TX command failed\n"); return ret; } ret = rn2xx3_wait_reply(dev, RN2XX3_REPLY_DELAY_TIMEOUT); rn2xx3_set_internal_state(dev, RN2XX3_INT_STATE_IDLE); return ret; } int rn2xx3_mac_join_network(rn2xx3_t *dev, loramac_join_mode_t mode) { size_t p = snprintf(dev->cmd_buf, sizeof(dev->cmd_buf) - 1, "mac join %s", (mode == LORAMAC_JOIN_OTAA) ? "otaa" : "abp"); dev->cmd_buf[p] = 0; int ret = rn2xx3_write_cmd(dev); if (ret != RN2XX3_OK) { DEBUG("[rn2xx3] join procedure command failed\n"); return ret; } rn2xx3_set_internal_state(dev, RN2XX3_INT_STATE_MAC_JOIN); ret = rn2xx3_wait_reply(dev, CONFIG_LORAMAC_DEFAULT_JOIN_DELAY1 + \ CONFIG_LORAMAC_DEFAULT_JOIN_DELAY2); rn2xx3_set_internal_state(dev, RN2XX3_INT_STATE_IDLE); return ret; } int rn2xx3_mac_save(rn2xx3_t *dev) { size_t p = snprintf(dev->cmd_buf, sizeof(dev->cmd_buf) - 1, "mac save"); dev->cmd_buf[p] = 0; return rn2xx3_write_cmd(dev); }