feat: extend keyboard
This commit is contained in:
1 parent
ed74e6df9b
commit
c069cdcbe6
1 file changed
+154
-2
+154
-2
@@ -4,8 +4,160 @@
|
|||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
void keyboardInit(void);
|
void keyboardInit(void);
|
||||||
|
|
||||||
|
/* The real PS/2 IRQ1 handler (isr.c): reads the scancode off the hardware
|
||||||
|
port and hands it to keyboardHandleScancode below. */
|
||||||
void keyboardHandler(void);
|
void keyboardHandler(void);
|
||||||
bool keyboardHasChar(void);
|
|
||||||
char keyboardReadChar(void);
|
/*
|
||||||
|
* Per-reader console input queues: a fixed table of ring-buffer-backed byte
|
||||||
|
* queues (ring.h), each independently allocated and each fed a copy of
|
||||||
|
* every decoded keystroke by keyboardHandleScancode -- like several
|
||||||
|
* terminals plugged into one keyboard, every open reader sees every key.
|
||||||
|
* This replaces an earlier design with exactly one global buffer shared by
|
||||||
|
* every consumer (the kernel shell and every ring-3 process's fd 0 alike),
|
||||||
|
* which had two real problems: concurrent readers raced directly on the
|
||||||
|
* same head/tail indices with no locking, and its blocking wait
|
||||||
|
* (keyboardReadChar's raw "sti; hlt" loop) was invisible to the scheduler --
|
||||||
|
* a thread parked there could never be woken by schedWakeChannel, never
|
||||||
|
* charged CFS-lite vruntime while "running" it, and never recovered by
|
||||||
|
* threadKill the way every other blocking read/write in this kernel can be
|
||||||
|
* (pipe.h's pipeReadBlocking/pipeWriteBlocking, vfs.h's fdFlagBlocking
|
||||||
|
* pipe/fifo ends). Every reader here uses the exact same
|
||||||
|
* schedBlockCurrent/schedWakeChannel wait-channel contract as pipe.c, wired
|
||||||
|
* into vfs.c's fdConsole read path (vfsBlockingChannel) so syscall.c's
|
||||||
|
* sysRead restart-syscall trick works on fd 0 exactly like it already does
|
||||||
|
* on a pipe end.
|
||||||
|
*/
|
||||||
|
/* Bumped from 48 (this pool's very first, never-since-revisited size, set
|
||||||
|
when sched.h's maxThreads was still 32): vfs.c's vfsFdInit grabs one
|
||||||
|
reader automatically for EVERY thread ever created (kernel or user alike,
|
||||||
|
whether it ever reads fd 0 or not, its own doc comment already says so)
|
||||||
|
at CREATION time, which happens sequentially and entirely inside kmain()
|
||||||
|
for every boot-time thread, well before schedStart() ever runs -- so the
|
||||||
|
first 48 creation-order calls always succeeded and every one after them
|
||||||
|
always silently got consoleReaderInvalid, deterministically, regardless
|
||||||
|
of scheduling. shell.c's shellRun makes a SEPARATE, later, explicit
|
||||||
|
keyboardOpenReader() call once the shell thread actually gets a
|
||||||
|
timeslice (its own real input queue, distinct from its automatic-but-
|
||||||
|
unused fd 0 slot) -- that call can only succeed if some earlier thread
|
||||||
|
has ALREADY died and been reaped (reclaimOpenFds, sched.c) by then,
|
||||||
|
freeing a slot back up. maxThreads grew from 32 to 168 across dozens of
|
||||||
|
falas' own headroom bumps (sched.c's own history comment) while this
|
||||||
|
completely separate pool never grew to match, so by FALA 33 kmain.c was
|
||||||
|
creating far more than 48 threads before schedStart() ever ran --
|
||||||
|
shellRun's own call was surviving purely on the timing-dependent hope
|
||||||
|
that at least one of the first 48 automatic grabs belonged to an
|
||||||
|
already-exited thread by the time it ran, not on any real guarantee.
|
||||||
|
Adding one more automatically-grabbing kernel thread (hereDriverThread,
|
||||||
|
kmain.c) tipped this from "usually still works" to "shell has no input"
|
||||||
|
on a clean run -- caught live by this fala's own gate, not a review.
|
||||||
|
Sized to maxThreads (168) plus the same +8 headroom margin sched.c's own
|
||||||
|
bumps use, which is enough for every one of maxThreads' automatic
|
||||||
|
grabs to always succeed deterministically PLUS shellRun's own extra
|
||||||
|
explicit call on top, eliminating the race entirely rather than
|
||||||
|
continuing to depend on exit timing. Bumped again (176 -> 184) in lockstep
|
||||||
|
with sched.c's own maxThreads bump (168 -> 176, hcat/hsink/
|
||||||
|
hereStringPipelineDriverThread) -- the same fixed maxThreads+8 relationship,
|
||||||
|
not re-derived from scratch each time. Bumped again (184 -> 192) in
|
||||||
|
lockstep with sched.c's own maxThreads bump (176 -> 184, pipecat/
|
||||||
|
threeStagePipelineDriverThread), same relationship. Bumped again
|
||||||
|
(192 -> 200) in lockstep with sched.c's own maxThreads bump (184 -> 192,
|
||||||
|
cwdtest/cwdtarget), same relationship. Bumped again (200 -> 208) in
|
||||||
|
lockstep with sched.c's own maxThreads bump (192 -> 200, envprint/
|
||||||
|
envDriverThread), same relationship. Bumped again (208 -> 216) in
|
||||||
|
lockstep with sched.c's own maxThreads bump (200 -> 208, nicetest/
|
||||||
|
nicetarget), same relationship. Bumped again (216 -> 224) in lockstep
|
||||||
|
with sched.c's own maxThreads bump (208 -> 216, date), same
|
||||||
|
relationship. Bumped again (224 -> 232) in lockstep with sched.c's own
|
||||||
|
maxThreads bump (216 -> 224, timetest), same relationship. Bumped again
|
||||||
|
(232 -> 240) in lockstep with sched.c's own maxThreads bump (224 -> 232,
|
||||||
|
lsDriverThread), same relationship. Bumped again (240 -> 248) in
|
||||||
|
lockstep with sched.c's own maxThreads bump (232 -> 240,
|
||||||
|
andOrDriverThread), same relationship. Bumped again (248 -> 256) in
|
||||||
|
lockstep with sched.c's own maxThreads bump (240 -> 248, rmtest), same
|
||||||
|
relationship. Bumped again (256 -> 264) in lockstep with sched.c's own
|
||||||
|
maxThreads bump (248 -> 256, permtest plus its one transient runtime-
|
||||||
|
spawned child), same relationship. Bumped again (264 -> 272) in lockstep
|
||||||
|
with sched.c's own maxThreads bump (256 -> 264, dirpermtest), same
|
||||||
|
relationship. Bumped again (272 -> 280) in lockstep with sched.c's own
|
||||||
|
maxThreads bump (264 -> 272, grouptest plus its two transient runtime-
|
||||||
|
spawned children), same relationship. Bumped again (280 -> 288) in
|
||||||
|
lockstep with sched.c's own maxThreads bump (272 -> 280, pidfdtest plus
|
||||||
|
its one transient runtime-spawned killvictim), same relationship.
|
||||||
|
Bumped again (288 -> 296) in lockstep with sched.c's own maxThreads bump
|
||||||
|
(280 -> 288, mmaptest plus its one transient runtime-spawned
|
||||||
|
mmapprotvictim), same relationship. */
|
||||||
|
#define maxConsoleReaders 296
|
||||||
|
#define consoleReaderInvalid (-1)
|
||||||
|
|
||||||
|
/* Allocates a fresh, empty reader queue and returns its handle, or
|
||||||
|
consoleReaderInvalid if the table is full. Every thread gets one for its
|
||||||
|
own fd 0 at creation time (vfs.c's vfsFdInit) -- one keystroke delivered
|
||||||
|
by keyboardHandleScancode reaches every thread's queue independently, so
|
||||||
|
two unrelated processes both reading fd 0 each see their own copy rather
|
||||||
|
than racing for the same byte. */
|
||||||
|
int keyboardOpenReader(void);
|
||||||
|
|
||||||
|
/* Releases a reader's slot for reuse. No-op on an invalid handle. */
|
||||||
|
void keyboardCloseReader(int handle);
|
||||||
|
|
||||||
|
/* Non-blocking: pops up to length queued bytes into buffer, in the order
|
||||||
|
they were typed. Returns the number of bytes actually copied (which may
|
||||||
|
be less than length, or 0 only when length itself was 0), or -1 for an
|
||||||
|
invalid/closed handle or when the queue is currently empty (a live
|
||||||
|
keyboard has no end-of-file the way a pipe's write end closing does --
|
||||||
|
"nothing typed yet" and "try again later" are the same thing). */
|
||||||
|
int64 keyboardReaderRead(int handle, void* buffer, uint64 length);
|
||||||
|
|
||||||
|
/* Blocks (parks the calling thread and halts) until at least one byte is
|
||||||
|
available, then behaves exactly like keyboardReaderRead. Only valid from
|
||||||
|
plain kernel-thread context (see shell.c) -- calling this from inside a
|
||||||
|
syscall's interrupt-gate handler would freeze the whole machine, not just
|
||||||
|
the caller, the same hazard pipeReadBlocking's doc comment (pipe.h)
|
||||||
|
describes; a syscall handler must instead use vfsBlockingChannel (vfs.h)
|
||||||
|
with the restart-syscall pattern. Returns -1 immediately, without
|
||||||
|
blocking, for an invalid handle. */
|
||||||
|
int64 keyboardReaderReadBlocking(int handle, void* buffer, uint64 length);
|
||||||
|
|
||||||
|
/* The address schedBlockCurrent/schedWakeChannel use for this reader's wait
|
||||||
|
queue -- mirrors pipe.h's pipeChannel exactly, including returning
|
||||||
|
nullptr for an invalid handle so a syscall handler can tell "do not
|
||||||
|
block, this fd cannot ever produce more data" apart from "wait and
|
||||||
|
retry". */
|
||||||
|
const void* keyboardReaderChannel(int handle);
|
||||||
|
|
||||||
|
/* Non-consuming readiness peek for poll.h's vfsPollReady (vfs.c): true iff
|
||||||
|
keyboardReaderRead would return actual bytes right now, without popping
|
||||||
|
any of them off the ring. False for an invalid handle. */
|
||||||
|
bool keyboardReaderHasData(int handle);
|
||||||
|
|
||||||
|
/* The scancode-decoding half of keyboardHandler, split out so tests (and
|
||||||
|
the live boot demo in kmain.c, which has no automated way to drive a real
|
||||||
|
PS/2 device under headless QEMU) can inject a keystroke exactly the way
|
||||||
|
real hardware would, without needing to fake port I/O. Handles shift and
|
||||||
|
(now) left-Ctrl make/break codes, intercepts the real tty INTR/SUSP
|
||||||
|
combination (Ctrl-C/Ctrl-Z) as job-control signals to the foreground
|
||||||
|
process group instead of ordinary data (see the file comment in
|
||||||
|
keyboard.c), and otherwise broadcasts the decoded character to every open
|
||||||
|
reader queue, waking anyone blocked on one via schedWakeChannel. */
|
||||||
|
void keyboardHandleScancode(uint8 scancode);
|
||||||
|
|
||||||
|
/* Real tcsetpgrp(3): names the process group (sched.h's Thread.pgid) that
|
||||||
|
OWNS "the terminal" from this point on -- the only pgid keyboardHandleScancode's
|
||||||
|
Ctrl-C/Ctrl-Z interception (below) ever delivers sigInt/sigTstp to.
|
||||||
|
0 (the default, before anything ever calls this -- see keyboardInit) can
|
||||||
|
never match any live user thread's own pgid (every boot-spawned process
|
||||||
|
becomes its own new group leader, sched.c's initialPgid, so the lowest
|
||||||
|
possible real pgid is 1; only the permanent idle thread, which never
|
||||||
|
reads a keyboard, is ever pgid 0), so "nobody has claimed the foreground
|
||||||
|
yet" and "deliver to no one" are naturally the same state, with no extra
|
||||||
|
sentinel needed. */
|
||||||
|
void keyboardSetForegroundPgid(uint32 pgid);
|
||||||
|
|
||||||
|
/* The pgid keyboardSetForegroundPgid last recorded (0 if never called since
|
||||||
|
keyboardInit) -- real tcgetpgrp(3)'s counterpart, exposed to userspace
|
||||||
|
through sysGetFgPgrp/userGetForegroundPgid. */
|
||||||
|
uint32 keyboardForegroundPgid(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
Reference in new issue
Block a user