Fix: interrupt preemption
This commit is contained in:
@@ -29,6 +29,8 @@ size_t (*uart_putb)(const uint8_t *bytes, size_t len) = uart_putb_sync;
|
||||
size_t (*uart_getb)(uint8_t *bytes, size_t len) = uart_getb_sync;
|
||||
int is_uart_inited = false;
|
||||
|
||||
int is_uart_in_interrupt_queue = false;
|
||||
|
||||
ringbuffer_t *uart_readbuf;
|
||||
ringbuffer_t *uart_writebuf;
|
||||
|
||||
@@ -83,37 +85,47 @@ void uart_init(void)
|
||||
is_uart_inited = true;
|
||||
}
|
||||
|
||||
void uart_transmit_interrupt_handler()
|
||||
static inline
|
||||
void _enable_uart_interrupt()
|
||||
{
|
||||
_uart_enable_receive_interrupt(true);
|
||||
if (uart_writebuf->size > 0)
|
||||
_uart_write_data(ringbuffer_bump(uart_writebuf));
|
||||
_uart_enable_transmit_interrupt(true);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
ringbuffer_t *buf;
|
||||
uint8_t val;
|
||||
} uart_interrupt_callback_payload_t;
|
||||
|
||||
static inline
|
||||
void uart_receive_interrupt_callback(uint64_t param)
|
||||
void _uart_transmit_interrupt_callback(uint64_t)
|
||||
{
|
||||
uart_interrupt_callback_payload_t *payload = (void *)param;
|
||||
ringbuffer_push(payload->buf, payload->val);
|
||||
kfree(payload);
|
||||
_uart_write_data(ringbuffer_bump(uart_writebuf));
|
||||
|
||||
is_uart_in_interrupt_queue = false;
|
||||
_enable_uart_interrupt();
|
||||
}
|
||||
|
||||
void uart_receive_interrupt_handler()
|
||||
static inline
|
||||
void _uart_transmit_interrupt_handler()
|
||||
{
|
||||
if (uart_writebuf->size > 0) {
|
||||
is_uart_in_interrupt_queue = true;
|
||||
add_interrupt_task(11, _uart_transmit_interrupt_callback, 0x0);
|
||||
}
|
||||
}
|
||||
|
||||
static inline
|
||||
void _uart_receive_interrupt_callback(uint64_t)
|
||||
{
|
||||
ringbuffer_push(uart_readbuf, _uart_read_data());
|
||||
|
||||
is_uart_in_interrupt_queue = false;
|
||||
_enable_uart_interrupt();
|
||||
}
|
||||
|
||||
static inline
|
||||
void _uart_receive_interrupt_handler()
|
||||
{
|
||||
if (uart_readbuf->size < uart_readbuf->cap) {
|
||||
uint8_t b = _uart_read_data();
|
||||
|
||||
uart_interrupt_callback_payload_t *param = kmalloc(
|
||||
sizeof(uart_interrupt_callback_payload_t));
|
||||
*param = (uart_interrupt_callback_payload_t){
|
||||
.buf = uart_readbuf,
|
||||
.val = b,
|
||||
};
|
||||
add_interrupt_task(10, uart_receive_interrupt_callback, (uint64_t)param);
|
||||
is_uart_in_interrupt_queue = true;
|
||||
add_interrupt_task(12, _uart_receive_interrupt_callback, 0x0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,14 +133,11 @@ void uart_irq_handler(void)
|
||||
{
|
||||
_uart_enable_receive_interrupt(false);
|
||||
_uart_enable_transmit_interrupt(false);
|
||||
|
||||
if (_uart_receive_interrupt())
|
||||
uart_receive_interrupt_handler();
|
||||
_uart_receive_interrupt_handler();
|
||||
if (_uart_transmit_interrupt())
|
||||
uart_transmit_interrupt_handler();
|
||||
|
||||
_uart_enable_receive_interrupt(true);
|
||||
if (uart_writebuf->size > 0)
|
||||
_uart_enable_transmit_interrupt(true);
|
||||
_uart_transmit_interrupt_handler();
|
||||
}
|
||||
|
||||
size_t uart_putb_async(const uint8_t *bytes, size_t len)
|
||||
@@ -137,8 +146,8 @@ size_t uart_putb_async(const uint8_t *bytes, size_t len)
|
||||
for (; sentlen < len; ++bytes, ++sentlen)
|
||||
ringbuffer_push(uart_writebuf, *bytes);
|
||||
|
||||
if (uart_writebuf->size > 0)
|
||||
_uart_enable_transmit_interrupt(true);
|
||||
if (!is_uart_in_interrupt_queue && uart_writebuf)
|
||||
_enable_uart_interrupt();
|
||||
|
||||
return sentlen;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user