mirror of
https://github.com/JannisHeydemann/BoredOS.git
synced 2026-05-30 02:16:58 +00:00
FEATURE: add Bsh + userspace terminal, remove legacy cmd/cli utils
This commit is contained in:
30
src/sys/cmd.h
Normal file
30
src/sys/cmd.h
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||
#ifndef CMD_H
|
||||
#define CMD_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void cmd_init(void);
|
||||
void cmd_reset(void);
|
||||
|
||||
void cmd_write(const char *str);
|
||||
void cmd_write_len(const char *str, size_t len);
|
||||
void cmd_putchar(char c);
|
||||
void cmd_write_int(int n);
|
||||
void cmd_write_hex(uint64_t n);
|
||||
int cmd_get_cursor_col(void);
|
||||
void cmd_screen_clear(void);
|
||||
|
||||
void cmd_increment_msg_count(void);
|
||||
void cmd_reset_msg_count(void);
|
||||
|
||||
uint32_t cmd_get_config_value(const char *key);
|
||||
void cmd_set_current_color(uint32_t color);
|
||||
void cmd_set_raw_mode(bool enabled);
|
||||
void cmd_process_finished(void);
|
||||
|
||||
#endif
|
||||
76
src/sys/cmd_stub.c
Normal file
76
src/sys/cmd_stub.c
Normal file
@@ -0,0 +1,76 @@
|
||||
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||
#include "cmd.h"
|
||||
#include "core/kutils.h"
|
||||
|
||||
extern void serial_write(const char *str);
|
||||
|
||||
static void serial_write_char(char c) {
|
||||
char buf[2] = { c, 0 };
|
||||
serial_write(buf);
|
||||
}
|
||||
|
||||
void cmd_init(void) {
|
||||
}
|
||||
|
||||
void cmd_reset(void) {
|
||||
}
|
||||
|
||||
void cmd_write(const char *str) {
|
||||
if (!str) return;
|
||||
serial_write(str);
|
||||
}
|
||||
|
||||
void cmd_write_len(const char *str, size_t len) {
|
||||
if (!str || len == 0) return;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
serial_write_char(str[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void cmd_putchar(char c) {
|
||||
serial_write_char(c);
|
||||
}
|
||||
|
||||
void cmd_write_int(int n) {
|
||||
char buf[32];
|
||||
k_itoa(n, buf);
|
||||
cmd_write(buf);
|
||||
}
|
||||
|
||||
void cmd_write_hex(uint64_t n) {
|
||||
char buf[17];
|
||||
k_itoa_hex(n, buf);
|
||||
cmd_write("0x");
|
||||
cmd_write(buf);
|
||||
}
|
||||
|
||||
int cmd_get_cursor_col(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cmd_screen_clear(void) {
|
||||
}
|
||||
|
||||
void cmd_increment_msg_count(void) {
|
||||
}
|
||||
|
||||
void cmd_reset_msg_count(void) {
|
||||
}
|
||||
|
||||
uint32_t cmd_get_config_value(const char *key) {
|
||||
(void)key;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cmd_set_current_color(uint32_t color) {
|
||||
(void)color;
|
||||
}
|
||||
|
||||
void cmd_set_raw_mode(bool enabled) {
|
||||
(void)enabled;
|
||||
}
|
||||
|
||||
void cmd_process_finished(void) {
|
||||
}
|
||||
@@ -27,23 +27,24 @@ static uint64_t free_pml4_later[MAX_CPUS_SCHED] = {0};
|
||||
static spinlock_t runqueue_lock = SPINLOCK_INIT;
|
||||
static uint32_t next_cpu_assign = 1;
|
||||
|
||||
static void process_cleanup_inner(process_t *proc);
|
||||
|
||||
void process_init(void) {
|
||||
for (int i = 0; i < MAX_PROCESSES; i++) {
|
||||
processes[i].pid = 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
// Current kernel execution is PID 0
|
||||
process_t *kernel_proc = &processes[0];
|
||||
kernel_proc->pid = next_pid++;
|
||||
kernel_proc->is_user = false;
|
||||
kernel_proc->is_idle = true;
|
||||
kernel_proc->tty_id = -1;
|
||||
kernel_proc->kill_pending = false;
|
||||
|
||||
// We don't have its RSP or PML4 yet, but it's already running.
|
||||
// The timer interrupt will naturally capture its context on the first tick!
|
||||
|
||||
kernel_proc->pml4_phys = paging_get_pml4_phys();
|
||||
kernel_proc->kernel_stack = 0;
|
||||
|
||||
// Initialize FPU/SSE state for kernel (first interrupt will capture it on stack)
|
||||
kernel_proc->fpu_initialized = true;
|
||||
|
||||
for (int i = 0; i < MAX_PROCESS_FDS; i++) kernel_proc->fds[i] = NULL;
|
||||
@@ -78,11 +79,14 @@ process_t* process_create(void (*entry_point)(void), bool is_user) {
|
||||
|
||||
new_proc->pid = next_pid++;
|
||||
new_proc->is_user = is_user;
|
||||
new_proc->tty_id = -1;
|
||||
new_proc->kill_pending = false;
|
||||
|
||||
process_t *parent = process_get_current();
|
||||
if (parent) {
|
||||
extern void mem_memcpy(void *dest, const void *src, size_t len);
|
||||
mem_memcpy(new_proc->cwd, parent->cwd, 1024);
|
||||
new_proc->tty_id = parent->tty_id;
|
||||
} else {
|
||||
mem_memset(new_proc->cwd, 0, 1024);
|
||||
new_proc->cwd[0] = '/';
|
||||
@@ -207,6 +211,18 @@ process_t* process_create_elf(const char* filepath, const char* args_str) {
|
||||
new_proc->heap_start = 0x20000000; // 512MB mark
|
||||
new_proc->heap_end = 0x20000000;
|
||||
new_proc->is_terminal_proc = false;
|
||||
new_proc->tty_id = -1;
|
||||
new_proc->kill_pending = false;
|
||||
|
||||
process_t *parent = process_get_current();
|
||||
if (parent) {
|
||||
extern void mem_memcpy(void *dest, const void *src, size_t len);
|
||||
mem_memcpy(new_proc->cwd, parent->cwd, 1024);
|
||||
} else {
|
||||
extern void mem_memset(void *dest, int val, size_t len);
|
||||
mem_memset(new_proc->cwd, 0, 1024);
|
||||
new_proc->cwd[0] = '/';
|
||||
}
|
||||
|
||||
// 2. Load ELF executable
|
||||
size_t elf_load_size = 0;
|
||||
@@ -426,6 +442,69 @@ uint64_t process_schedule(uint64_t current_rsp) {
|
||||
// Save context
|
||||
cur->rsp = current_rsp;
|
||||
|
||||
if (cur->kill_pending && cur->pid != 0xFFFFFFFF && cur->pid != 0) {
|
||||
process_cleanup_inner(cur);
|
||||
|
||||
process_t *prev = cur;
|
||||
while (prev->next != cur) {
|
||||
prev = prev->next;
|
||||
}
|
||||
|
||||
if (prev != cur) {
|
||||
prev->next = cur->next;
|
||||
|
||||
process_t *next_proc = cur->next;
|
||||
while (next_proc != cur) {
|
||||
if (next_proc->cpu_affinity == my_cpu && next_proc->pid != 0xFFFFFFFF && !next_proc->kill_pending) break;
|
||||
next_proc = next_proc->next;
|
||||
}
|
||||
|
||||
if (next_proc == cur || next_proc->cpu_affinity != my_cpu) {
|
||||
for (int i = 0; i < MAX_PROCESSES; i++) {
|
||||
if (processes[i].pid == 0 || (processes[i].cpu_affinity == my_cpu && processes[i].is_user == false)) {
|
||||
next_proc = &processes[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
current_process[my_cpu] = next_proc;
|
||||
|
||||
cur->pid = 0xFFFFFFFF;
|
||||
cur->cpu_affinity = 0xFFFFFFFF;
|
||||
cur->ui_window = NULL;
|
||||
cur->is_terminal_proc = false;
|
||||
cur->kill_pending = false;
|
||||
|
||||
free_kernel_stack_later[my_cpu] = cur->kernel_stack_alloc;
|
||||
cur->kernel_stack_alloc = NULL;
|
||||
free_pml4_later[my_cpu] = cur->pml4_phys;
|
||||
cur->pml4_phys = 0;
|
||||
|
||||
if (current_process[my_cpu]->is_user && current_process[my_cpu]->kernel_stack) {
|
||||
tss_set_stack_cpu(my_cpu, current_process[my_cpu]->kernel_stack);
|
||||
cpu_state_t *cpu_state = smp_get_cpu(my_cpu);
|
||||
if (cpu_state) {
|
||||
cpu_state->kernel_syscall_stack = current_process[my_cpu]->kernel_stack;
|
||||
}
|
||||
}
|
||||
|
||||
paging_switch_directory(current_process[my_cpu]->pml4_phys);
|
||||
|
||||
current_process[my_cpu]->ticks++;
|
||||
uint64_t next_rsp = current_process[my_cpu]->rsp;
|
||||
|
||||
spinlock_release_irqrestore(&runqueue_lock, rflags);
|
||||
if (cleanup_stack) kfree(cleanup_stack);
|
||||
if (cleanup_pml4) {
|
||||
extern void paging_destroy_user_pml4_phys(uint64_t pml4_phys);
|
||||
paging_destroy_user_pml4_phys(cleanup_pml4);
|
||||
}
|
||||
|
||||
return next_rsp;
|
||||
}
|
||||
}
|
||||
|
||||
// Switch to next ready process assigned to this CPU
|
||||
extern uint32_t wm_get_ticks(void);
|
||||
uint32_t now = wm_get_ticks();
|
||||
@@ -435,7 +514,7 @@ uint64_t process_schedule(uint64_t current_rsp) {
|
||||
|
||||
while (next_proc != start) {
|
||||
// Only consider processes assigned to our CPU and not terminated
|
||||
if (next_proc->cpu_affinity == my_cpu && next_proc->pid != 0xFFFFFFFF) {
|
||||
if (next_proc->cpu_affinity == my_cpu && next_proc->pid != 0xFFFFFFFF && !next_proc->kill_pending) {
|
||||
if (next_proc->pid == 0 || next_proc->sleep_until == 0 || next_proc->sleep_until <= now) {
|
||||
break;
|
||||
}
|
||||
@@ -497,12 +576,20 @@ process_t* process_get_by_pid(uint32_t pid) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void process_kill_by_tty(int tty_id) {
|
||||
if (tty_id < 0) return;
|
||||
for (int i = 0; i < MAX_PROCESSES; i++) {
|
||||
if (processes[i].pid != 0xFFFFFFFF && processes[i].pid != 0 && processes[i].tty_id == tty_id) {
|
||||
process_terminate(&processes[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void process_cleanup_inner(process_t *proc) {
|
||||
if (!proc || proc->pid == 0xFFFFFFFF) return;
|
||||
|
||||
// 1. Cleanup side effects
|
||||
extern Window win_cmd;
|
||||
if (proc->ui_window && (proc->ui_window != &win_cmd)) {
|
||||
if (proc->ui_window) {
|
||||
wm_remove_window((Window *)proc->ui_window);
|
||||
proc->ui_window = NULL;
|
||||
}
|
||||
@@ -529,6 +616,14 @@ static void process_cleanup_inner(process_t *proc) {
|
||||
void process_terminate(process_t *to_delete) {
|
||||
if (!to_delete || to_delete->pid == 0xFFFFFFFF || to_delete->pid == 0) return;
|
||||
|
||||
uint32_t cpu_count = smp_cpu_count();
|
||||
for (uint32_t c = 0; c < cpu_count && c < MAX_CPUS_SCHED; c++) {
|
||||
if (current_process[c] == to_delete) {
|
||||
to_delete->kill_pending = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t rflags = spinlock_acquire_irqsave(&runqueue_lock);
|
||||
|
||||
process_cleanup_inner(to_delete);
|
||||
@@ -549,7 +644,6 @@ void process_terminate(process_t *to_delete) {
|
||||
prev->next = to_delete->next;
|
||||
|
||||
// Update per-CPU current_process if this was the current on any CPU
|
||||
uint32_t cpu_count = smp_cpu_count();
|
||||
for (uint32_t c = 0; c < cpu_count && c < MAX_CPUS_SCHED; c++) {
|
||||
if (current_process[c] == to_delete) {
|
||||
process_t *np = to_delete->next;
|
||||
@@ -571,6 +665,7 @@ void process_terminate(process_t *to_delete) {
|
||||
// Mark slot as free
|
||||
to_delete->pid = 0xFFFFFFFF;
|
||||
to_delete->cpu_affinity = 0xFFFFFFFF;
|
||||
to_delete->kill_pending = false;
|
||||
|
||||
if (to_delete->user_stack_alloc) kfree(to_delete->user_stack_alloc);
|
||||
// Defer kernel stack until we switch away from it
|
||||
@@ -639,6 +734,7 @@ uint64_t process_terminate_current(void) {
|
||||
to_delete->cpu_affinity = 0xFFFFFFFF;
|
||||
to_delete->ui_window = NULL;
|
||||
to_delete->is_terminal_proc = false;
|
||||
to_delete->kill_pending = false;
|
||||
|
||||
// 4. Load context for the NEXT process
|
||||
if (current_process[my_cpu]->is_user && current_process[my_cpu]->kernel_stack) {
|
||||
|
||||
@@ -43,6 +43,8 @@ typedef struct process {
|
||||
void *user_stack_alloc;
|
||||
|
||||
bool is_terminal_proc;
|
||||
int tty_id;
|
||||
bool kill_pending;
|
||||
|
||||
struct process *next;
|
||||
|
||||
@@ -75,6 +77,7 @@ uint64_t process_schedule(uint64_t current_rsp);
|
||||
uint64_t process_terminate_current(void);
|
||||
void process_terminate(process_t *proc);
|
||||
process_t* process_get_by_pid(uint32_t pid);
|
||||
void process_kill_by_tty(int tty_id);
|
||||
|
||||
// SMP: IPI handler for AP scheduling
|
||||
uint64_t sched_ipi_handler(registers_t *regs);
|
||||
|
||||
@@ -19,11 +19,16 @@
|
||||
#include "network.h"
|
||||
#include "icmp.h"
|
||||
#include "cmd.h"
|
||||
#include "tty.h"
|
||||
#include "font_manager.h"
|
||||
#include "graphics.h"
|
||||
|
||||
extern bool ps2_ctrl_pressed;
|
||||
|
||||
#define SPAWN_FLAG_TERMINAL 0x1
|
||||
#define SPAWN_FLAG_INHERIT_TTY 0x2
|
||||
#define SPAWN_FLAG_TTY_ID 0x4
|
||||
|
||||
// Read MSR
|
||||
static inline uint64_t rdmsr(uint32_t msr) {
|
||||
uint32_t low, high;
|
||||
@@ -180,6 +185,12 @@ static void user_window_resize(Window *win, int w, int h) {
|
||||
extern void mem_memset(void *dest, int val, size_t len);
|
||||
mem_memset(win->pixels, 0, w * h * sizeof(uint32_t));
|
||||
}
|
||||
|
||||
process_t *proc = process_get_by_ui_window(win);
|
||||
if (proc) {
|
||||
gui_event_t ev = { .type = GUI_EVENT_RESIZE, .arg1 = w, .arg2 = h };
|
||||
process_push_gui_event(proc, &ev);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -197,9 +208,21 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
|
||||
if (syscall_num == 1) { // SYS_WRITE
|
||||
extern void cmd_write_len(const char *str, size_t len);
|
||||
process_t *proc = process_get_current();
|
||||
if (!proc || !proc->is_user || proc->is_terminal_proc) {
|
||||
cmd_write_len((const char*)arg2, (size_t)arg3);
|
||||
const char *buf = (const char*)arg2;
|
||||
size_t len = (size_t)arg3;
|
||||
if (!proc || !proc->is_user) {
|
||||
cmd_write_len(buf, len);
|
||||
return len;
|
||||
}
|
||||
if (proc->is_terminal_proc) {
|
||||
if (proc->tty_id >= 0) {
|
||||
tty_write_output(proc->tty_id, buf, len);
|
||||
return len;
|
||||
}
|
||||
cmd_write_len(buf, len);
|
||||
return len;
|
||||
}
|
||||
return len;
|
||||
} else if (syscall_num == 3) { // SYS_GUI
|
||||
int cmd = (int)arg1;
|
||||
process_t *proc = process_get_current();
|
||||
@@ -1254,6 +1277,35 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
|
||||
return cmd_get_config_value(key);
|
||||
} else if (cmd == 29) { // SYSTEM_CMD_SET_TEXT_COLOR
|
||||
uint32_t color = (uint32_t)arg2;
|
||||
if (proc->is_terminal_proc && proc->tty_id >= 0) {
|
||||
char seq[32];
|
||||
int pos = 0;
|
||||
int r = (color >> 16) & 0xFF;
|
||||
int g = (color >> 8) & 0xFF;
|
||||
int b = color & 0xFF;
|
||||
|
||||
seq[pos++] = 0x1B;
|
||||
seq[pos++] = '[';
|
||||
seq[pos++] = '3';
|
||||
seq[pos++] = '8';
|
||||
seq[pos++] = ';';
|
||||
seq[pos++] = '2';
|
||||
seq[pos++] = ';';
|
||||
|
||||
char num[8];
|
||||
k_itoa(r, num);
|
||||
for (int i = 0; num[i] && pos < (int)sizeof(seq) - 1; i++) seq[pos++] = num[i];
|
||||
seq[pos++] = ';';
|
||||
k_itoa(g, num);
|
||||
for (int i = 0; num[i] && pos < (int)sizeof(seq) - 1; i++) seq[pos++] = num[i];
|
||||
seq[pos++] = ';';
|
||||
k_itoa(b, num);
|
||||
for (int i = 0; num[i] && pos < (int)sizeof(seq) - 1; i++) seq[pos++] = num[i];
|
||||
seq[pos++] = 'm';
|
||||
|
||||
tty_write_output(proc->tty_id, seq, (size_t)pos);
|
||||
return 0;
|
||||
}
|
||||
cmd_set_current_color(color);
|
||||
return 0;
|
||||
} else if (cmd == 31) { // SYSTEM_CMD_SET_WALLPAPER_PATH
|
||||
@@ -1374,6 +1426,85 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
|
||||
}
|
||||
return -1;
|
||||
return -1;
|
||||
} else if (cmd == 60) { // SYSTEM_CMD_TTY_CREATE
|
||||
return tty_create();
|
||||
} else if (cmd == 61) { // SYSTEM_CMD_TTY_READ_OUT
|
||||
int tty_id = (int)arg2;
|
||||
char *buf = (char *)arg3;
|
||||
size_t len = (size_t)arg4;
|
||||
if (!buf || len == 0) return 0;
|
||||
return tty_read_output(tty_id, buf, len);
|
||||
} else if (cmd == 62) { // SYSTEM_CMD_TTY_WRITE_IN
|
||||
int tty_id = (int)arg2;
|
||||
const char *buf = (const char *)arg3;
|
||||
size_t len = (size_t)arg4;
|
||||
if (!buf || len == 0) return 0;
|
||||
return tty_write_input(tty_id, buf, len);
|
||||
} else if (cmd == 63) { // SYSTEM_CMD_TTY_READ_IN
|
||||
char *buf = (char *)arg2;
|
||||
size_t len = (size_t)arg3;
|
||||
if (!buf || len == 0) return 0;
|
||||
if (proc->tty_id < 0) return 0;
|
||||
return tty_read_input(proc->tty_id, buf, len);
|
||||
} else if (cmd == 65) { // SYSTEM_CMD_TTY_SET_FG
|
||||
int tty_id = (int)arg2;
|
||||
int pid = (int)arg3;
|
||||
return tty_set_foreground(tty_id, pid);
|
||||
} else if (cmd == 66) { // SYSTEM_CMD_TTY_GET_FG
|
||||
int tty_id = (int)arg2;
|
||||
return tty_get_foreground(tty_id);
|
||||
} else if (cmd == 67) { // SYSTEM_CMD_TTY_KILL_FG
|
||||
int tty_id = (int)arg2;
|
||||
int pid = tty_get_foreground(tty_id);
|
||||
if (pid <= 0) return 0;
|
||||
process_t *target = process_get_by_pid((uint32_t)pid);
|
||||
if (target) process_terminate(target);
|
||||
tty_set_foreground(tty_id, 0);
|
||||
return 0;
|
||||
} else if (cmd == 68) {
|
||||
int tty_id = (int)arg2;
|
||||
process_kill_by_tty(tty_id);
|
||||
tty_set_foreground(tty_id, 0);
|
||||
return 0;
|
||||
} else if (cmd == 69) {
|
||||
int tty_id = (int)arg2;
|
||||
return tty_destroy(tty_id);
|
||||
} else if (cmd == 64) {
|
||||
const char *user_path = (const char *)arg2;
|
||||
const char *user_args = (const char *)arg3;
|
||||
uint64_t flags = arg4;
|
||||
int tty_id = (int)arg5;
|
||||
|
||||
if (!user_path) return -1;
|
||||
|
||||
char path_buf[256];
|
||||
int pi = 0;
|
||||
while (pi < 255 && user_path[pi]) {
|
||||
path_buf[pi] = user_path[pi];
|
||||
pi++;
|
||||
}
|
||||
path_buf[pi] = 0;
|
||||
|
||||
char args_buf[512];
|
||||
const char *args_ptr = NULL;
|
||||
if (user_args) {
|
||||
int ai = 0;
|
||||
while (ai < 511 && user_args[ai]) {
|
||||
args_buf[ai] = user_args[ai];
|
||||
ai++;
|
||||
}
|
||||
args_buf[ai] = 0;
|
||||
args_ptr = args_buf;
|
||||
}
|
||||
|
||||
process_t *child = process_create_elf(path_buf, args_ptr);
|
||||
if (!child) return -1;
|
||||
|
||||
if (flags & SPAWN_FLAG_TERMINAL) child->is_terminal_proc = true;
|
||||
if (flags & SPAWN_FLAG_TTY_ID) child->tty_id = tty_id;
|
||||
else if (flags & SPAWN_FLAG_INHERIT_TTY) child->tty_id = proc->tty_id;
|
||||
|
||||
return (uint64_t)child->pid;
|
||||
} else if (cmd == SYSTEM_CMD_PARALLEL_RUN) {
|
||||
void (*user_fn)(void*) = (void (*)(void*))arg2;
|
||||
void **args = (void **)arg3;
|
||||
|
||||
158
src/sys/tty.c
Normal file
158
src/sys/tty.c
Normal file
@@ -0,0 +1,158 @@
|
||||
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||
#include "tty.h"
|
||||
#include "spinlock.h"
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define TTY_MAX 8
|
||||
#define TTY_OUT_SIZE 16384
|
||||
#define TTY_IN_SIZE 4096
|
||||
|
||||
typedef struct {
|
||||
bool used;
|
||||
int id;
|
||||
char out_buf[TTY_OUT_SIZE];
|
||||
uint32_t out_head;
|
||||
uint32_t out_tail;
|
||||
char in_buf[TTY_IN_SIZE];
|
||||
uint32_t in_head;
|
||||
uint32_t in_tail;
|
||||
int fg_pid;
|
||||
spinlock_t lock;
|
||||
} tty_t;
|
||||
|
||||
static tty_t ttys[TTY_MAX] = {0};
|
||||
|
||||
extern void mem_memset(void *dest, int val, size_t len);
|
||||
|
||||
static tty_t *tty_get(int tty_id) {
|
||||
if (tty_id < 0 || tty_id >= TTY_MAX) return NULL;
|
||||
if (!ttys[tty_id].used) return NULL;
|
||||
return &ttys[tty_id];
|
||||
}
|
||||
|
||||
int tty_create(void) {
|
||||
for (int i = 0; i < TTY_MAX; i++) {
|
||||
if (!ttys[i].used) {
|
||||
ttys[i].used = true;
|
||||
ttys[i].id = i;
|
||||
ttys[i].out_head = 0;
|
||||
ttys[i].out_tail = 0;
|
||||
ttys[i].in_head = 0;
|
||||
ttys[i].in_tail = 0;
|
||||
ttys[i].fg_pid = -1;
|
||||
ttys[i].lock = SPINLOCK_INIT;
|
||||
mem_memset(ttys[i].out_buf, 0, sizeof(ttys[i].out_buf));
|
||||
mem_memset(ttys[i].in_buf, 0, sizeof(ttys[i].in_buf));
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int tty_destroy(int tty_id) {
|
||||
if (tty_id < 0 || tty_id >= TTY_MAX) return -1;
|
||||
tty_t *tty = &ttys[tty_id];
|
||||
|
||||
uint64_t rflags = spinlock_acquire_irqsave(&tty->lock);
|
||||
if (!tty->used) {
|
||||
spinlock_release_irqrestore(&tty->lock, rflags);
|
||||
return -1;
|
||||
}
|
||||
|
||||
tty->used = false;
|
||||
tty->id = -1;
|
||||
tty->out_head = 0;
|
||||
tty->out_tail = 0;
|
||||
tty->in_head = 0;
|
||||
tty->in_tail = 0;
|
||||
tty->fg_pid = -1;
|
||||
mem_memset(tty->out_buf, 0, sizeof(tty->out_buf));
|
||||
mem_memset(tty->in_buf, 0, sizeof(tty->in_buf));
|
||||
|
||||
spinlock_release_irqrestore(&tty->lock, rflags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tty_write_ring(char *buf, uint32_t size, uint32_t *head, uint32_t *tail, const char *data, size_t len) {
|
||||
int written = 0;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
uint32_t next = (*head + 1) % size;
|
||||
if (next == *tail) break;
|
||||
buf[*head] = data[i];
|
||||
*head = next;
|
||||
written++;
|
||||
}
|
||||
return written;
|
||||
}
|
||||
|
||||
static int tty_read_ring(char *buf, uint32_t size, uint32_t *head, uint32_t *tail, char *out, size_t max_len) {
|
||||
int read = 0;
|
||||
while (*tail != *head && (size_t)read < max_len) {
|
||||
out[read++] = buf[*tail];
|
||||
*tail = (*tail + 1) % size;
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
int tty_write_output(int tty_id, const char *data, size_t len) {
|
||||
tty_t *tty = tty_get(tty_id);
|
||||
if (!tty || !data || len == 0) return 0;
|
||||
|
||||
uint64_t rflags = spinlock_acquire_irqsave(&tty->lock);
|
||||
int written = tty_write_ring(tty->out_buf, TTY_OUT_SIZE, &tty->out_head, &tty->out_tail, data, len);
|
||||
spinlock_release_irqrestore(&tty->lock, rflags);
|
||||
return written;
|
||||
}
|
||||
|
||||
int tty_read_output(int tty_id, char *buf, size_t max_len) {
|
||||
tty_t *tty = tty_get(tty_id);
|
||||
if (!tty || !buf || max_len == 0) return 0;
|
||||
|
||||
uint64_t rflags = spinlock_acquire_irqsave(&tty->lock);
|
||||
int read = tty_read_ring(tty->out_buf, TTY_OUT_SIZE, &tty->out_head, &tty->out_tail, buf, max_len);
|
||||
spinlock_release_irqrestore(&tty->lock, rflags);
|
||||
return read;
|
||||
}
|
||||
|
||||
int tty_write_input(int tty_id, const char *data, size_t len) {
|
||||
tty_t *tty = tty_get(tty_id);
|
||||
if (!tty || !data || len == 0) return 0;
|
||||
|
||||
uint64_t rflags = spinlock_acquire_irqsave(&tty->lock);
|
||||
int written = tty_write_ring(tty->in_buf, TTY_IN_SIZE, &tty->in_head, &tty->in_tail, data, len);
|
||||
spinlock_release_irqrestore(&tty->lock, rflags);
|
||||
return written;
|
||||
}
|
||||
|
||||
int tty_read_input(int tty_id, char *buf, size_t max_len) {
|
||||
tty_t *tty = tty_get(tty_id);
|
||||
if (!tty || !buf || max_len == 0) return 0;
|
||||
|
||||
uint64_t rflags = spinlock_acquire_irqsave(&tty->lock);
|
||||
int read = tty_read_ring(tty->in_buf, TTY_IN_SIZE, &tty->in_head, &tty->in_tail, buf, max_len);
|
||||
spinlock_release_irqrestore(&tty->lock, rflags);
|
||||
return read;
|
||||
}
|
||||
|
||||
int tty_set_foreground(int tty_id, int pid) {
|
||||
tty_t *tty = tty_get(tty_id);
|
||||
if (!tty) return -1;
|
||||
|
||||
uint64_t rflags = spinlock_acquire_irqsave(&tty->lock);
|
||||
tty->fg_pid = pid;
|
||||
spinlock_release_irqrestore(&tty->lock, rflags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tty_get_foreground(int tty_id) {
|
||||
tty_t *tty = tty_get(tty_id);
|
||||
if (!tty) return -1;
|
||||
|
||||
uint64_t rflags = spinlock_acquire_irqsave(&tty->lock);
|
||||
int pid = tty->fg_pid;
|
||||
spinlock_release_irqrestore(&tty->lock, rflags);
|
||||
return pid;
|
||||
}
|
||||
18
src/sys/tty.h
Normal file
18
src/sys/tty.h
Normal file
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||
#ifndef TTY_H
|
||||
#define TTY_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
int tty_create(void);
|
||||
int tty_destroy(int tty_id);
|
||||
int tty_write_output(int tty_id, const char *data, size_t len);
|
||||
int tty_read_output(int tty_id, char *buf, size_t max_len);
|
||||
int tty_write_input(int tty_id, const char *data, size_t len);
|
||||
int tty_read_input(int tty_id, char *buf, size_t max_len);
|
||||
int tty_set_foreground(int tty_id, int pid);
|
||||
int tty_get_foreground(int tty_id);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user