SchOS/kernel/dev/kbd.c
Nexoniarz 54b25708c3 Demand paging, the RTL8168H, links that come up late, and Ctrl+Alt+F1..F12
- Anonymous memory can be reserved without being backed: large mappings,
  MAP_NORESERVE and PROT_NONE are recorded and each page appears, zeroed,
  on first touch -- from trap() for the program, from uva2ka() for the
  kernel on its behalf. WebKit reserves 4 GB at start and touches a sliver;
  backing it all failed with ENOMEM and Epiphany aborted.
- r8169: the 8168G generation and later (the laptop's 8168H, XID 541) are
  taken from the firmware's out-of-band mode, get their PLL powered, the
  receive gate opened, and their PHY reached through the OCP window. The
  PHY's power-down bit is cleared; the old code kept it while restarting
  autonegotiation. Untested on the hardware so far.
- The network thread follows each device's link into lwIP. The link was
  read once at attach, 0.02 s into boot; real autonegotiation takes
  seconds, so on hardware the interface stayed down for good.
- Twelve terminals, switched with Ctrl+Alt+F1..F12 by default: the keys X
  uses, so the console and the desktop agree. AltGr+F-key is gone from
  the defaults (chvt -c altgr+F brings it back).
- SO_PASSCRED on Unix sockets, with SCM_CREDENTIALS delivered by recvmsg.
  WebKit sets it on its IPC sockets and aborted when refused.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
2026-10-25 11:27:48 +01:00

205 lines
6.5 KiB
C

#include "core/types.h"
#include "arch/x86.h"
#include "core/defs.h"
#include "dev/kbd.h"
#include "uapi/posix.h"
#include "dev/vt.h" /* NVT -- how many terminals the chords reach */
#include "dev/input.h"
#include "dev/keymap.h"
// The chord, settable at run time through VT_SETCHORD.
//
// Ctrl+Alt+F1..F12 by default, the same keys X switches with. It used to be
// AltGr+F-key, chosen because nothing else claimed it -- but under X the
// server owns the keyboard and only Ctrl+Alt+F-key works, so the console and
// the desktop answered different keys for the same thing.
//
// Ctrl+Alt+minus and Ctrl+Alt+equals step to the previous and next terminal,
// for when you have two and do not care which number they are.
//
// Zero modifiers disables the chord; `chvt N` still works.
uint vt_chord_mods = CTL | ALT;
uint vt_chord_base = SCAN_F1;
// Which terminal an F-key scancode names: F1..F10 are 0x3B..0x44 and F11 and
// F12 sit apart, at 0x57 and 0x58. -1 for anything else.
static int
fkey_index(uint data)
{
if (data >= SCAN_F1 && data < SCAN_F1 + 10) return (int)(data - SCAN_F1);
if (data == 0x57) return 10;
if (data == 0x58) return 11;
return -1;
}
// The terminal a chord key names, from `base`: the F-keys by fkey_index, any
// other base as a run of ten consecutive scancodes -- a digit row is 1..9, 0,
// and the minus and equals after it are already the previous and next keys.
static int
chord_index(uint data, uint base)
{
int i = base == SCAN_F1 ? fkey_index(data)
: (data >= base && data < base + 10 ? (int)(data - base) : -1);
return i < NVT ? i : -1;
}
// Which scancodes are modifiers, and which are locks. Hardware facts rather
// than layout ones -- every layout agrees that 0x2A is a Shift key -- so they
// stay here while the character tables live in keymap.c.
static const uchar shiftcode[256] = {
[0x1D] = CTL,
[0x2A] = SHIFT,
[0x36] = SHIFT,
[0x38] = ALT,
[0x9D] = CTL,
[0xB8] = RALT // right Alt / AltGr -- see RALT in kbd.h
};
static const uchar togglecode[256] = {
[0x3A] = CAPSLOCK,
[0x45] = NUMLOCK,
[0x46] = SCROLLLOCK
};
// A key can produce more than one byte.
//
// consoleintr's contract with getc() is bytes: the serial line hands it raw
// UTF-8 already, and it would have no way to tell an encoded codepoint apart
// from a byte that happened to have the same value. So the keyboard encodes
// here and hands the bytes over one call at a time -- kbdintr loops until
// getc() returns negative, so a queue of at most four drains on its own
// before the next scancode is read.
static uchar pend[4];
static int pend_n, pend_i;
static int
queue_utf8(uint32 cp)
{
pend_i = 0;
if (cp < 0x80) {
pend_n = 0;
return (int)cp; // the common case: no queue at all
}
if (cp < 0x800) {
pend[0] = 0xC0 | (cp >> 6);
pend[1] = 0x80 | (cp & 0x3F);
pend_n = 2;
} else if (cp < 0x10000) {
pend[0] = 0xE0 | (cp >> 12);
pend[1] = 0x80 | ((cp >> 6) & 0x3F);
pend[2] = 0x80 | (cp & 0x3F);
pend_n = 3;
} else {
pend[0] = 0xF0 | (cp >> 18);
pend[1] = 0x80 | ((cp >> 12) & 0x3F);
pend[2] = 0x80 | ((cp >> 6) & 0x3F);
pend[3] = 0x80 | (cp & 0x3F);
pend_n = 4;
}
return pend[pend_i++];
}
int
kbdgetc(void)
{
static uint shift;
uint st, data;
uint32 cp;
if (pend_i < pend_n)
return pend[pend_i++]; // finish the last key first
pend_i = pend_n = 0;
st = inb(KBSTATP);
if((st & KBS_DIB) == 0)
return -1;
if(st & 0x20)
return -1;
data = inb(KBDATAP);
if(data == 0xE0){
shift |= E0ESC;
return 0;
}
// Feed /dev/input/event0 before the byte is turned into a character. This
// has to happen here rather than in a second handler because the byte can
// only be read from the port once -- whoever reads it owes it to everyone
// else. The console path below is untouched: both consumers see every key.
input_ps2_scancode(data, (shift & E0ESC) != 0);
// While something holds an exclusive grab on the keyboard, the console gets
// nothing. The shift/caps state below is still tracked, so releasing the
// grab does not leave the line discipline believing a modifier is held.
if(input_is_grabbed(INPUT_KBD)){
if(data & 0x80){
data = (shift & E0ESC ? data : data & 0x7F);
shift &= ~(shiftcode[data] | E0ESC);
} else {
shift |= shiftcode[data];
shift ^= togglecode[data];
}
return 0;
}
if(data & 0x80){
data = (shift & E0ESC ? data : data & 0x7F);
shift &= ~(shiftcode[data] | E0ESC);
return 0;
} else if(shift & E0ESC){
data |= 0x80;
shift &= ~E0ESC;
}
shift |= shiftcode[data];
shift ^= togglecode[data];
// The switch chord, before the character tables, because there is no
// character to look up: it is a combination that means "show me a different
// terminal". What it means is decided in console.c, which is where the
// terminals are; this only has to name it. See console_vt_hotkey().
//
// The modifier test is an equality, not a mask. `shift & ALT` would fire on
// Ctrl+Alt+1 and on AltGr+1 as well, so a chord aimed at the host -- or a
// Polish keystroke -- would switch terminals behind your back. Exactly the
// modifiers named, and nothing else held.
if (vt_chord_mods != 0 &&
(shift & (CTL | SHIFT | ALT | RALT)) == vt_chord_mods) {
int i = chord_index(data, vt_chord_base);
if (i >= 0)
return KBD_VT_SWITCH + i;
if (data == SCAN_MINUS) return KBD_VT_PREV;
if (data == SCAN_EQUALS) return KBD_VT_NEXT;
}
// And Linux's own: Alt+F-key and Ctrl+Alt+F-key, whatever the chord is set
// to. Under X the terminal is in VT_PROCESS, console_vt_hotkey() lets the
// keys through, and X switches with them itself -- as on Linux.
if (chord_index(data, SCAN_F1) >= 0 &&
((shift & (CTL | SHIFT | ALT | RALT)) == ALT ||
(shift & (CTL | SHIFT | ALT | RALT)) == (CTL | ALT)))
return KBD_VT_SWITCH + chord_index(data, SCAN_F1);
cp = keymap_lookup(data, shift);
if (cp == 0)
return 0;
if (cp >= KBD_SPECIAL)
return (int)cp; // arrows and the rest, not characters
return queue_utf8(cp);
}
void
kbdintr(void)
{
consoleintr(kbdgetc);
}
// The two headers describe the same bits to two different audiences, and a
// mismatch would be silent: a chord set from userspace would simply never
// match, and the only symptom would be a key that does nothing.
_Static_assert(VTC_SHIFT == SHIFT && VTC_CTRL == CTL &&
VTC_ALT == ALT && VTC_RALT == RALT,
"include/uapi/posix.h VTC_* must mirror include/dev/kbd.h modifiers");