Skip to content

Commit

Permalink
nrf_rpc: add reliable mode for uart transport
Browse files Browse the repository at this point in the history
Commit adds acknowledgement functionality for
serial communication over uart.
It is configurable and can be disabled.

Signed-off-by: Aleksandr Khromykh <[email protected]>
  • Loading branch information
alxelax authored and nordicjm committed Oct 10, 2024
1 parent 6d5f856 commit 319c2ad
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 17 deletions.
1 change: 1 addition & 0 deletions samples/nrf_rpc/protocols_serialization/client/prj.conf
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ CONFIG_NRF_RPC_ZCBOR_BACKUPS=1
CONFIG_NRF_RPC_THREAD_STACK_SIZE=4096
CONFIG_NRF_RPC_GROUP_INIT_WAIT_TIME=-1
CONFIG_NRF_RPC_UART_TRANSPORT=y
CONFIG_NRF_RPC_UART_RELIABLE=y

CONFIG_UART_LINE_CTRL=y
CONFIG_UART_INTERRUPT_DRIVEN=y
Expand Down
1 change: 1 addition & 0 deletions samples/nrf_rpc/protocols_serialization/server/prj.conf
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ CONFIG_NRF_RPC_ZCBOR_BACKUPS=1
CONFIG_NRF_RPC_THREAD_STACK_SIZE=4096
CONFIG_NRF_RPC_GROUP_INIT_WAIT_TIME=-1
CONFIG_NRF_RPC_UART_TRANSPORT=y
CONFIG_NRF_RPC_UART_RELIABLE=y

CONFIG_UART_LINE_CTRL=y
CONFIG_UART_INTERRUPT_DRIVEN=y
23 changes: 23 additions & 0 deletions subsys/nrf_rpc/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,29 @@ config NRF_RPC_UART_RX_THREAD_STACK_SIZE
thread is responsible for consuming data received over the UART, and
passing decoded nRF RPC packets to the nRF RPC core.

config NRF_RPC_UART_RELIABLE
bool "UART reliability"
help
Enables acknowledgment functionality for each frame sent over UART.

if NRF_RPC_UART_RELIABLE

config NRF_RPC_UART_ACK_WAITING_TIME
int "Time window to receive acknowledgment"
default 50
help
Defines time in milliseconds during which the device waits
for acknowledgment for sent data.

config NRF_RPC_UART_TX_ATTEMPTS
int "Number of transmitting attempts"
default 3
help
Number of transmitting attempts, after which sender gives up if
acknowledgment has not been received yet.

endif # NRF_RPC_UART_RELIABLE

endmenu # "nRF RPC over UART configuration"

config NRF_RPC_CBOR
Expand Down
112 changes: 95 additions & 17 deletions subsys/nrf_rpc/nrf_rpc_uart.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,55 @@ struct nrf_rpc_uart {
uint8_t rx_packet[CONFIG_NRF_RPC_UART_MAX_PACKET_SIZE];
size_t rx_packet_len;

/* Ack waiting semaphore */
struct k_sem ack_sem;
uint16_t ack_payload;
struct k_mutex ack_tx_lock;

/* TX lock */
struct k_mutex tx_lock;
};

#define CRC_SIZE sizeof(uint16_t)

static int hdlc_decode_byte(struct nrf_rpc_uart *uart_tr, uint8_t byte)
static void send_byte(const struct device *dev, uint8_t byte);

static void ack_rx(struct nrf_rpc_uart *uart_tr)
{
if (!IS_ENABLED(CONFIG_NRF_RPC_UART_RELIABLE)) {
return;
}

if (uart_tr->rx_packet_len == CRC_SIZE) {
uart_tr->ack_payload = sys_get_le16(uart_tr->rx_packet);
k_sem_give(&uart_tr->ack_sem);
k_yield();
}
}

static void ack_tx(struct nrf_rpc_uart *uart_tr, uint16_t ack_pld)
{
uint8_t ack[2];

if (!IS_ENABLED(CONFIG_NRF_RPC_UART_RELIABLE)) {
return;
}

sys_put_le16(ack_pld, ack);
k_mutex_lock(&uart_tr->ack_tx_lock, K_FOREVER);
LOG_HEXDUMP_DBG(ack, sizeof(ack), "Tx ack");

uart_poll_out(uart_tr->uart, HDLC_CHAR_DELIMITER);

send_byte(uart_tr->uart, ack[0]);
send_byte(uart_tr->uart, ack[1]);

uart_poll_out(uart_tr->uart, HDLC_CHAR_DELIMITER);

k_mutex_unlock(&uart_tr->ack_tx_lock);
}

static void hdlc_decode_byte(struct nrf_rpc_uart *uart_tr, uint8_t byte)
{
/* TODO: handle frame buffer overflow */

Expand All @@ -79,12 +121,11 @@ static int hdlc_decode_byte(struct nrf_rpc_uart *uart_tr, uint8_t byte)
if (uart_tr->rx_packet_len > CRC_SIZE) {
uart_tr->rx_packet_len -= CRC_SIZE;
} else {
ack_rx(uart_tr);
uart_tr->rx_packet_len = 0;
uart_tr->hdlc_state = HDLC_STATE_UNSYNC;
}
}

return 0;
}

static void work_handler(struct k_work *work)
Expand All @@ -101,9 +142,7 @@ static void work_handler(struct k_work *work)
len = ring_buf_get_claim(&uart_tr->rx_ringbuf, &data,
CONFIG_NRF_RPC_UART_MAX_PACKET_SIZE);
for (size_t i = 0; i < len; i++) {
if (hdlc_decode_byte(uart_tr, data[i]) != 0) {
continue;
}
hdlc_decode_byte(uart_tr, data[i]);

if (uart_tr->hdlc_state != HDLC_STATE_FRAME_FOUND) {
continue;
Expand All @@ -113,13 +152,15 @@ static void work_handler(struct k_work *work)
crc_calculated =
crc16_ccitt(0xffff, uart_tr->rx_packet, uart_tr->rx_packet_len);
if (crc_calculated != crc_received) {
LOG_ERR("Invalid CRC, calculated %x received %x", crc_calculated,
LOG_ERR("Invalid CRC, calculated %4x received %4x", crc_calculated,
crc_received);
uart_tr->rx_packet_len = 0;
uart_tr->hdlc_state = HDLC_STATE_UNSYNC;
return;
}

ack_tx(uart_tr, crc_received);

uart_tr->receive_callback(transport, uart_tr->rx_packet,
uart_tr->rx_packet_len, uart_tr->receive_ctx);
uart_tr->rx_packet_len = 0;
Expand Down Expand Up @@ -210,6 +251,11 @@ static int init(const struct nrf_rpc_tr *transport, nrf_rpc_tr_receive_handler_t

k_mutex_init(&uart_tr->tx_lock);

if (IS_ENABLED(CONFIG_NRF_RPC_UART_RELIABLE)) {
k_mutex_init(&uart_tr->ack_tx_lock);
k_sem_init(&uart_tr->ack_sem, 0, 1);
}

k_work_queue_init(&uart_tr->rx_workq);
k_work_queue_start(&uart_tr->rx_workq, uart_tr->rx_workq_stack,
K_THREAD_STACK_SIZEOF(uart_tr->rx_workq_stack), K_PRIO_PREEMPT(0), NULL);
Expand Down Expand Up @@ -237,29 +283,61 @@ static void send_byte(const struct device *dev, uint8_t byte)
static int send(const struct nrf_rpc_tr *transport, const uint8_t *data, size_t length)
{
uint8_t crc[2];
uint16_t crc_val;
bool acked = true;
struct nrf_rpc_uart *uart_tr = transport->ctx;

k_mutex_lock(&uart_tr->tx_lock, K_FOREVER);

LOG_HEXDUMP_DBG(data, length, "Sending frame");

k_mutex_lock(&uart_tr->tx_lock, K_FOREVER);
#if CONFIG_NRF_RPC_UART_RELIABLE
int attempts = 0;

uart_poll_out(uart_tr->uart, HDLC_CHAR_DELIMITER);
acked = false;

for (size_t i = 0; i < length; i++) {
send_byte(uart_tr->uart, data[i]);
}
do {
attempts++;
k_mutex_lock(&uart_tr->ack_tx_lock, K_FOREVER);
k_sem_reset(&uart_tr->ack_sem);
#endif /* CONFIG_NRF_RPC_UART_RELIABLE */

sys_put_le16(crc16_ccitt(0xffff, data, length), crc);
send_byte(uart_tr->uart, crc[0]);
send_byte(uart_tr->uart, crc[1]);
uart_poll_out(uart_tr->uart, HDLC_CHAR_DELIMITER);

uart_poll_out(uart_tr->uart, HDLC_CHAR_DELIMITER);
for (size_t i = 0; i < length; i++) {
send_byte(uart_tr->uart, data[i]);
}

crc_val = crc16_ccitt(0xffff, data, length);
sys_put_le16(crc_val, crc);
send_byte(uart_tr->uart, crc[0]);
send_byte(uart_tr->uart, crc[1]);

uart_poll_out(uart_tr->uart, HDLC_CHAR_DELIMITER);

#if CONFIG_NRF_RPC_UART_RELIABLE
k_mutex_unlock(&uart_tr->ack_tx_lock);
if (k_sem_take(&uart_tr->ack_sem, K_MSEC(CONFIG_NRF_RPC_UART_ACK_WAITING_TIME)) ==
0) {
acked = uart_tr->ack_payload == crc_val;
if (!acked) {
LOG_WRN("Wrong ack. Expected: %#4x. Received: %#4x.", crc_val,
uart_tr->ack_payload);
} else {
LOG_DBG("Acked successfully.");
}
} else {
LOG_WRN("Ack timeout expired.");
}

} while (!acked && attempts < CONFIG_NRF_RPC_UART_TX_ATTEMPTS);
#endif /* CONFIG_NRF_RPC_UART_RELIABLE */

k_free((void *)data);

k_mutex_unlock(&uart_tr->tx_lock);

return 0;
return acked ? 0 : -EPROTO;
}

static void *tx_buf_alloc(const struct nrf_rpc_tr *transport, size_t *size)
Expand Down

0 comments on commit 319c2ad

Please sign in to comment.