mirror of
https://github.com/justinian/jsix.git
synced 2025-12-10 08:24:32 -08:00
Now used by the 6s shell to enable backspace, return, etc. No cursor movement yet or anything complex.
194 lines
4.7 KiB
C++
194 lines
4.7 KiB
C++
// 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/channel.hh>
|
|
#include <j6/condition.hh>
|
|
#include <j6/errors.h>
|
|
#include <j6/flags.h>
|
|
#include <j6/memutils.h>
|
|
#include <j6/mutex.hh>
|
|
#include <j6/syscalls.h>
|
|
#include <j6/syslog.hh>
|
|
#include <util/spinlock.h>
|
|
#include <util/bip_buffer.h>
|
|
#include <util/new.h>
|
|
|
|
namespace j6 {
|
|
|
|
struct channel_memory_area
|
|
{
|
|
j6::mutex mutex;
|
|
j6::condition waiting;
|
|
util::bip_buffer buf;
|
|
};
|
|
|
|
|
|
static bool
|
|
check_channel_size(size_t s)
|
|
{
|
|
if (s < arch::frame_size || (s & (s - 1)) != 0) {
|
|
syslog(j6::logs::ipc, j6::log_level::error, "Bad channel size: %lx", s);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
static uintptr_t
|
|
create_channel_vma(j6_handle_t &vma, size_t size)
|
|
{
|
|
uintptr_t addr = 0;
|
|
j6_status_t result = j6_vma_create_map(&vma, size, &addr, j6_vm_flag_write);
|
|
if (result != j6_status_ok) {
|
|
syslog(j6::logs::ipc, j6::log_level::error, "Failed to create channel VMA. Error: %lx", result);
|
|
return 0;
|
|
}
|
|
syslog(j6::logs::ipc, j6::log_level::verbose, "Created channel VMA at 0x%lx size 0x%lx", addr, size);
|
|
return addr;
|
|
}
|
|
|
|
|
|
static void
|
|
init_channel_memory_area(channel_memory_area *area, size_t size)
|
|
{
|
|
new (&area->mutex) j6::mutex;
|
|
new (&area->waiting) j6::condition;
|
|
new (&area->buf) util::bip_buffer {size - sizeof(*area)};
|
|
}
|
|
|
|
|
|
channel *
|
|
channel::create(size_t tx_size, size_t rx_size)
|
|
{
|
|
if (!rx_size)
|
|
rx_size = tx_size;
|
|
|
|
if (!check_channel_size(tx_size) || !check_channel_size(rx_size))
|
|
return nullptr;
|
|
|
|
j6_status_t result;
|
|
j6_handle_t tx_vma = j6_handle_invalid;
|
|
j6_handle_t rx_vma = j6_handle_invalid;
|
|
|
|
uintptr_t tx_addr = create_channel_vma(tx_vma, tx_size);
|
|
if (!tx_addr)
|
|
return nullptr;
|
|
|
|
uintptr_t rx_addr = create_channel_vma(rx_vma, rx_size);
|
|
if (!rx_addr) {
|
|
j6_vma_unmap(tx_vma, 0);
|
|
j6_handle_close(tx_vma);
|
|
return nullptr;
|
|
}
|
|
|
|
channel_memory_area *tx = reinterpret_cast<channel_memory_area*>(tx_addr);
|
|
channel_memory_area *rx = reinterpret_cast<channel_memory_area*>(rx_addr);
|
|
init_channel_memory_area(tx, tx_size);
|
|
init_channel_memory_area(rx, rx_size);
|
|
|
|
j6::syslog(logs::ipc, log_level::info, "Created new channel with handles {%x, %x}", tx_vma, rx_vma);
|
|
|
|
return new channel {{tx_vma, rx_vma}, *tx, *rx};
|
|
}
|
|
|
|
channel *
|
|
channel::open(const channel_def &def)
|
|
{
|
|
j6_status_t result;
|
|
|
|
uintptr_t tx_addr = 0;
|
|
result = j6_vma_map(def.tx, 0, &tx_addr, 0);
|
|
if (result != j6_status_ok) {
|
|
syslog(j6::logs::ipc, j6::log_level::error, "Failed to map channel VMA. Error: %lx", result);
|
|
return nullptr;
|
|
}
|
|
|
|
uintptr_t rx_addr = 0;
|
|
result = j6_vma_map(def.rx, 0, &rx_addr, 0);
|
|
if (result != j6_status_ok) {
|
|
j6_vma_unmap(def.tx, 0);
|
|
j6_handle_close(def.tx);
|
|
syslog(j6::logs::ipc, j6::log_level::error, "Failed to map channel VMA. Error: %lx", result);
|
|
return nullptr;
|
|
}
|
|
|
|
channel_memory_area *tx = reinterpret_cast<channel_memory_area*>(tx_addr);
|
|
channel_memory_area *rx = reinterpret_cast<channel_memory_area*>(rx_addr);
|
|
|
|
j6::syslog(logs::ipc, log_level::info, "Opening existing channel with handles {%x:0x%lx, %x:0x%lx}", def.tx, tx, def.rx, rx);
|
|
return new channel { def, *tx, *rx };
|
|
}
|
|
|
|
channel::channel(
|
|
const channel_def &def,
|
|
channel_memory_area &tx,
|
|
channel_memory_area &rx) :
|
|
m_def {def},
|
|
m_tx {tx},
|
|
m_rx {rx}
|
|
{
|
|
}
|
|
|
|
|
|
size_t
|
|
channel::reserve(size_t size, uint8_t **area, bool block)
|
|
{
|
|
if (size > m_tx.buf.buffer_size())
|
|
return j6_err_insufficient;
|
|
|
|
j6::scoped_lock lock {m_tx.mutex};
|
|
while (m_tx.buf.write_available() < size) {
|
|
if (!block) return 0;
|
|
lock.release();
|
|
m_tx.waiting.wait();
|
|
lock.acquire();
|
|
}
|
|
|
|
return m_tx.buf.reserve(size, area);
|
|
}
|
|
|
|
void
|
|
channel::commit(size_t size)
|
|
{
|
|
j6::scoped_lock lock {m_tx.mutex};
|
|
m_tx.buf.commit(size);
|
|
if (!size) return;
|
|
|
|
m_tx.waiting.wake();
|
|
j6::syslog(logs::ipc, log_level::spam,
|
|
"Sending %d bytes to channel on {%x}", size, m_def.tx);
|
|
}
|
|
|
|
size_t
|
|
channel::get_block(uint8_t const **area, bool block) const
|
|
{
|
|
j6::scoped_lock lock {m_rx.mutex};
|
|
while (!m_rx.buf.size()) {
|
|
if (!block) return 0;
|
|
|
|
lock.release();
|
|
m_rx.waiting.wait();
|
|
lock.acquire();
|
|
}
|
|
|
|
return m_rx.buf.get_block(area);
|
|
}
|
|
|
|
void
|
|
channel::consume(size_t size)
|
|
{
|
|
j6::scoped_lock lock {m_tx.mutex};
|
|
m_rx.buf.consume(size);
|
|
if (!size) return;
|
|
|
|
m_rx.waiting.wake();
|
|
j6::syslog(logs::ipc, log_level::spam,
|
|
"Read %d bytes from channel on {%x}", size, m_def.rx);
|
|
}
|
|
|
|
} // namespace j6
|
|
|
|
#endif // __j6kernel
|