mirror of
https://github.com/justinian/jsix.git
synced 2025-12-09 16:04:32 -08:00
[uart] Fix UART driver hangs
The UART driver would constantly hang in unpredictable spots. Turns out it could get into a situation where it was stuck in a loop unable to read more from the receive channel, and/or write to the serial port buffer. Now we use a ring buffer to read as much as possible from the receive channel, write as much as possible to the serial port buffer, and move on without looping.
This commit is contained in:
@@ -125,8 +125,10 @@ channel::receive(void *buffer, size_t *size, bool block)
|
||||
{
|
||||
j6::scoped_lock lock {m_header->mutex};
|
||||
while (!m_header->read_avail()) {
|
||||
if (!block)
|
||||
if (!block) {
|
||||
*size = 0;
|
||||
return j6_status_would_block;
|
||||
}
|
||||
|
||||
lock.release();
|
||||
m_header->read_waiting.wait();
|
||||
|
||||
55
src/libraries/j6/include/j6/ring_buffer.hh
Normal file
55
src/libraries/j6/include/j6/ring_buffer.hh
Normal file
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
/// \file j6/ring_buffer.hh
|
||||
/// Helper class for managing ring buffers in doubly-mapped VMAs
|
||||
|
||||
// The kernel depends on libj6 for some shared code,
|
||||
// but should not include the user-specific code.
|
||||
#ifndef __j6kernel
|
||||
#include <stddef.h>
|
||||
#include <j6/types.h>
|
||||
#include <util/api.h>
|
||||
|
||||
namespace j6 {
|
||||
|
||||
API class ring_buffer
|
||||
{
|
||||
public:
|
||||
ring_buffer(size_t pages);
|
||||
|
||||
inline bool valid() const { return m_data != nullptr; }
|
||||
inline size_t size() const { return 1<<m_bits; }
|
||||
inline size_t used() const { return m_write-m_read; }
|
||||
inline size_t free() const { return size()-used(); }
|
||||
|
||||
inline const char * read_ptr() const { return get(m_read); }
|
||||
inline char * write_ptr() { return get(m_write); }
|
||||
|
||||
inline void commit(size_t &n) {
|
||||
if (n > free()) n = free();
|
||||
m_write += n;
|
||||
}
|
||||
|
||||
inline const char * consume(size_t &n) {
|
||||
if (n > used()) n = used();
|
||||
size_t i = m_read;
|
||||
m_read += n;
|
||||
return get(i);
|
||||
}
|
||||
|
||||
private:
|
||||
inline size_t mask() const { return (1<<m_bits)-1;}
|
||||
inline size_t index(size_t i) const { return i & mask(); }
|
||||
|
||||
inline char * get(size_t i) { return m_data + index(i); }
|
||||
inline const char * get(size_t i) const { return m_data + index(i); }
|
||||
|
||||
size_t m_bits;
|
||||
size_t m_write;
|
||||
size_t m_read;
|
||||
j6_handle_t m_vma;
|
||||
char *m_data;
|
||||
};
|
||||
|
||||
} // namespace j6
|
||||
#endif // __j6kernel
|
||||
|
||||
@@ -13,6 +13,7 @@ j6 = module("j6",
|
||||
"protocol_ids.cpp",
|
||||
"protocols/service_locator.cpp",
|
||||
"protocols/vfs.cpp",
|
||||
"ring_buffer.cpp",
|
||||
"syscalls.s.cog",
|
||||
"sysconf.cpp.cog",
|
||||
"syslog.cpp",
|
||||
@@ -29,6 +30,7 @@ j6 = module("j6",
|
||||
"j6/protocols.h",
|
||||
"j6/protocols/service_locator.h",
|
||||
"j6/protocols/service_locator.hh",
|
||||
"j6/ring_buffer.hh",
|
||||
"j6/syscalls.h.cog",
|
||||
"j6/sysconf.h.cog",
|
||||
"j6/syslog.hh",
|
||||
|
||||
38
src/libraries/j6/ring_buffer.cpp
Normal file
38
src/libraries/j6/ring_buffer.cpp
Normal file
@@ -0,0 +1,38 @@
|
||||
// The kernel depends on libj6 for some shared code,
|
||||
// but should not include the user-specific code.
|
||||
#ifndef __j6kernel
|
||||
|
||||
#include <arch/memory.h>
|
||||
#include <j6/errors.h>
|
||||
#include <j6/flags.h>
|
||||
#include <j6/ring_buffer.hh>
|
||||
#include <j6/syscalls.h>
|
||||
#include <j6/types.h>
|
||||
#include <util/util.h>
|
||||
|
||||
namespace j6 {
|
||||
|
||||
API
|
||||
ring_buffer::ring_buffer(size_t pages) :
|
||||
m_bits {util::log2(pages) + arch::frame_bits},
|
||||
m_write {0},
|
||||
m_read {0},
|
||||
m_vma {j6_handle_invalid},
|
||||
m_data {nullptr}
|
||||
{
|
||||
// Must be a power of 2
|
||||
if (!util::is_pow2(pages))
|
||||
return;
|
||||
|
||||
uintptr_t buffer_addr = 0;
|
||||
size_t size = 1<<m_bits;
|
||||
j6_status_t result = j6_vma_create_map(&m_vma, size, &buffer_addr, j6_vm_flag_ring|j6_vm_flag_write);
|
||||
if (result != j6_status_ok)
|
||||
return;
|
||||
|
||||
m_data = reinterpret_cast<char*>(buffer_addr);
|
||||
}
|
||||
|
||||
} // namespace j6
|
||||
|
||||
#endif // __j6kernel
|
||||
@@ -2,12 +2,14 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <arch/memory.h>
|
||||
#include <j6/cap_flags.h>
|
||||
#include <j6/channel.hh>
|
||||
#include <j6/errors.h>
|
||||
#include <j6/flags.h>
|
||||
#include <j6/init.h>
|
||||
#include <j6/protocols/service_locator.hh>
|
||||
#include <j6/ring_buffer.hh>
|
||||
#include <j6/syscalls.h>
|
||||
#include <j6/sysconf.h>
|
||||
#include <j6/syslog.hh>
|
||||
@@ -64,8 +66,10 @@ main(int argc, const char **argv)
|
||||
serial_port com1 {COM1, in_buf_size, com1_in, out_buf_size, com1_out};
|
||||
serial_port com2 {COM2, in_buf_size, com2_in, out_buf_size, com2_out};
|
||||
|
||||
static constexpr size_t buffer_size = 512;
|
||||
char buffer[buffer_size];
|
||||
static constexpr size_t buffer_pages = 1;
|
||||
j6::ring_buffer buffer {buffer_pages};
|
||||
if (!buffer.valid())
|
||||
return 128;
|
||||
|
||||
j6_handle_t slp = j6_find_init_handle(j6::proto::sl::id);
|
||||
if (slp == j6_handle_invalid)
|
||||
@@ -87,17 +91,13 @@ main(int argc, const char **argv)
|
||||
return 6;
|
||||
|
||||
while (true) {
|
||||
size_t size = buffer_size;
|
||||
while (true) {
|
||||
result = cout->receive(buffer, &size, 0);
|
||||
if (result != j6_status_ok)
|
||||
break;
|
||||
size_t size = buffer.free();
|
||||
cout->receive(buffer.write_ptr(), &size, 0);
|
||||
buffer.commit(size);
|
||||
//j6::syslog(j6::logs::srv, j6::log_level::spam, "uart driver: got %d bytes from channel", size);
|
||||
|
||||
j6::syslog(j6::logs::srv, j6::log_level::spam, "uart driver: got %d bytes from channel", size);
|
||||
com1.write(buffer, size);
|
||||
}
|
||||
if (result != j6_status_would_block)
|
||||
j6::syslog(j6::logs::srv, j6::log_level::error, "uart driver: error %lx receiving from channel", result);
|
||||
size = com1.write(buffer.read_ptr(), buffer.used());
|
||||
buffer.consume(size);
|
||||
|
||||
uint64_t signals = 0;
|
||||
result = j6_event_wait(event, &signals, 500);
|
||||
@@ -110,7 +110,7 @@ main(int argc, const char **argv)
|
||||
j6::syslog(j6::logs::srv, j6::log_level::error, "uart driver: error %lx waiting for irq", result);
|
||||
continue;
|
||||
} else {
|
||||
j6::syslog(j6::logs::srv, j6::log_level::verbose, "uart driver: irq signals: %lx", signals);
|
||||
j6::syslog(j6::logs::srv, j6::log_level::spam, "uart driver: irq signals: %lx", signals);
|
||||
}
|
||||
|
||||
if (signals & (1<<0))
|
||||
@@ -121,5 +121,4 @@ main(int argc, const char **argv)
|
||||
|
||||
j6::syslog(j6::logs::srv, j6::log_level::error, "uart driver somehow got to the end of main");
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user