From 6793970198645d94f39328ab465f736eb9cce8b4 Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Thu, 12 Apr 2018 18:04:00 -0400 Subject: serial_irq: Add new generic/serial_irq.c code Extract out common code from avr/serial.c, sam3x8e/serial.c, and stm32f1/serial.c into a new generic/serial_irq.c file. Signed-off-by: Kevin O'Connor --- src/generic/serial_irq.c | 120 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 src/generic/serial_irq.c (limited to 'src/generic/serial_irq.c') diff --git a/src/generic/serial_irq.c b/src/generic/serial_irq.c new file mode 100644 index 00000000..79eaad7f --- /dev/null +++ b/src/generic/serial_irq.c @@ -0,0 +1,120 @@ +// Generic interrupt based serial uart helper code +// +// Copyright (C) 2016-2018 Kevin O'Connor +// +// This file may be distributed under the terms of the GNU GPLv3 license. + +#include // memmove +#include "autoconf.h" // CONFIG_SERIAL_BAUD +#include "board/io.h" // readb +#include "board/irq.h" // irq_save +#include "board/misc.h" // console_sendf +#include "board/pgm.h" // READP +#include "command.h" // DECL_CONSTANT +#include "sched.h" // sched_wake_tasks +#include "serial_irq.h" // serial_enable_tx_irq + +static char receive_buf[192]; +static uint8_t receive_pos; +static char transmit_buf[96]; +static uint8_t transmit_pos, transmit_max; + +DECL_CONSTANT(SERIAL_BAUD, CONFIG_SERIAL_BAUD); + +// Rx interrupt - store read data +void +serial_rx_byte(uint_fast8_t data) +{ + if (data == MESSAGE_SYNC) + sched_wake_tasks(); + if (receive_pos >= sizeof(receive_buf)) + // Serial overflow - ignore it as crc error will force retransmit + return; + receive_buf[receive_pos++] = data; +} + +// Tx interrupt - get next byte to transmit +int +serial_get_tx_byte(uint8_t *pdata) +{ + if (transmit_pos >= transmit_max) + return -1; + *pdata = transmit_buf[transmit_pos++]; + return 0; +} + +// Remove from the receive buffer the given number of bytes +static void +console_pop_input(uint_fast8_t len) +{ + uint_fast8_t copied = 0; + for (;;) { + uint_fast8_t rpos = readb(&receive_pos); + uint_fast8_t needcopy = rpos - len; + if (needcopy) { + memmove(&receive_buf[copied], &receive_buf[copied + len] + , needcopy - copied); + copied = needcopy; + sched_wake_tasks(); + } + irqstatus_t flag = irq_save(); + if (rpos != readb(&receive_pos)) { + // Raced with irq handler - retry + irq_restore(flag); + continue; + } + receive_pos = needcopy; + irq_restore(flag); + break; + } +} + +// Process any incoming commands +void +console_task(void) +{ + uint_fast8_t rpos = readb(&receive_pos); + uint8_t pop_count; + int8_t ret = command_find_block(receive_buf, rpos, &pop_count); + if (ret > 0) + command_dispatch(receive_buf, pop_count); + if (ret) + console_pop_input(pop_count); +} +DECL_TASK(console_task); + +// Encode and transmit a "response" message +void +console_sendf(const struct command_encoder *ce, va_list args) +{ + // Verify space for message + uint_fast8_t tpos = readb(&transmit_pos), tmax = readb(&transmit_max); + if (tpos >= tmax) { + tpos = tmax = 0; + writeb(&transmit_max, 0); + writeb(&transmit_pos, 0); + } + uint_fast8_t max_size = READP(ce->max_size); + if (tmax + max_size > sizeof(transmit_buf)) { + if (tmax + max_size - tpos > sizeof(transmit_buf)) + // Not enough space for message + return; + // Disable TX irq and move buffer + writeb(&transmit_max, 0); + tpos = readb(&transmit_pos); + tmax -= tpos; + memmove(&transmit_buf[0], &transmit_buf[tpos], tmax); + writeb(&transmit_pos, 0); + writeb(&transmit_max, tmax); + serial_enable_tx_irq(); + } + + // Generate message + char *buf = &transmit_buf[tmax]; + uint8_t msglen = command_encodef(buf, ce, args); + command_add_frame(buf, msglen); + + // Start message transmit + writeb(&transmit_max, tmax + msglen); + serial_enable_tx_irq(); +} -- cgit v1.2.3-70-g09d2