rumpk/hal/uart_input.zig

98 lines
3.3 KiB
Zig

// SPDX-License-Identifier: LCL-1.0
// Copyright (c) 2026 Markus Maiwald
// Stewardship: Self Sovereign Society Foundation
//! Rumpk Layer 0: UART Input Logic (Kernel Only)
//!
//! Separated from uart.zig to avoid polluting userland stubs with kernel dependencies.
const std = @import("std");
const builtin = @import("builtin");
const uart = @import("uart.zig");
// Input Ring Buffer (256 bytes, power of 2 for fast masking)
const INPUT_BUFFER_SIZE = 256;
var input_buffer: [INPUT_BUFFER_SIZE]u8 = undefined;
var input_head = std.atomic.Value(u32).init(0); // Write position
var input_tail = std.atomic.Value(u32).init(0); // Read position
pub fn poll_input() void {
// Only Kernel uses this
const Kernel = struct {
extern fn ion_push_stdin(ptr: [*]const u8, len: usize) void;
extern fn kprint(s: [*]const u8) void;
};
switch (builtin.cpu.arch) {
.riscv64 => {
const thr: *volatile u8 = @ptrFromInt(uart.NS16550A_BASE + uart.NS16550A_THR);
const lsr: *volatile u8 = @ptrFromInt(uart.NS16550A_BASE + uart.NS16550A_LSR);
// Read all available bytes from UART FIFO (Limit 128 to prevent stall)
var loop_limit: usize = 0;
while ((lsr.* & 0x01) != 0 and loop_limit < 128) { // Data Ready
loop_limit += 1;
const byte = thr.*;
const byte_arr = [1]u8{byte};
// DEBUG: Trace hardware read
Kernel.kprint("[HW Read]\n");
// Forward to Kernel Input Channel
Kernel.ion_push_stdin(&byte_arr, 1);
// Add to ring buffer if not full
const head_val = input_head.load(.monotonic);
const tail_val = input_tail.load(.monotonic);
const next_head = (head_val + 1) % INPUT_BUFFER_SIZE;
if (next_head != tail_val) {
input_buffer[head_val] = byte;
input_head.store(next_head, .monotonic);
}
}
},
.aarch64 => {
const dr: *volatile u32 = @ptrFromInt(uart.PL011_BASE + uart.PL011_DR);
const fr: *volatile u32 = @ptrFromInt(uart.PL011_BASE + uart.PL011_FR);
while ((fr.* & (1 << 4)) == 0) { // RXFE (Receive FIFO Empty) is bit 4
const byte: u8 = @truncate(dr.*);
const byte_arr = [1]u8{byte};
Kernel.ion_push_stdin(&byte_arr, 1);
const head_val = input_head.load(.monotonic);
const tail_val = input_tail.load(.monotonic);
const next_head = (head_val + 1) % INPUT_BUFFER_SIZE;
if (next_head != tail_val) {
input_buffer[head_val] = byte;
input_head.store(next_head, .monotonic);
}
}
},
else => {},
}
}
export fn uart_poll_input() void {
poll_input();
}
pub fn read_byte() ?u8 {
// First, poll UART to refill buffer
poll_input();
// Then read from buffer
const head_val = input_head.load(.monotonic);
const tail_val = input_tail.load(.monotonic);
if (tail_val != head_val) {
const byte = input_buffer[tail_val];
input_tail.store((tail_val + 1) % INPUT_BUFFER_SIZE, .monotonic);
return byte;
}
return null;
}