SchOS/include/gmmos/gmmos.h
Nexoniarz f6c4b6377f GMMOS: USB keyboards on xHCI
The third micro driver: xhci.c with everything but finding a keyboard and
reading it taken out. It does not take over the live driver's state --
the kernel may have died inside that driver -- so each controller is
halted, reset and enumerated from nothing. Of the live driver it uses
the register mapping and the scratchpad pages the controller was lent
(DCBAA entry 0, read before the reset).

Root ports, and USB 2 hubs up to two deep (power, reset, route string,
transaction translator for a slow device behind a high-speed hub). A
boot-protocol keyboard interface gets Set Configuration, Set Protocol
boot, Set Idle, one interrupt IN endpoint and a Normal TRB, re-queued
after every report. No interrupts, no hot-plug, nothing that is not a
keyboard or a hub. A static 256 KB pool instead of the DMA allocator.

Checked under QEMU with the PS/2 controller switched off (i8042=off): a
USB keyboard on a root port, and one behind a hub, both type into the
GMMOS terminal. QEMU's hub is full speed, so the transaction-translator
path is not exercised.

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

70 lines
2.8 KiB
C

#pragma once
#include "core/types.h"
// GMMOS -- Guru Meditation Micro OS. What runs after a panic.
//
// The PSOGM used to be the last thing the kernel did: a report, blinking
// keyboard lights, and a machine that needed its power button. GMMOS keeps the
// report and goes on living under it. The real kernel is dead and stays dead --
// every other processor is halted (see trap()), and nothing of it is called
// again that takes a lock, sleeps, allocates or waits for an interrupt. What
// GMMOS has instead is its own: the framebuffer, a clock read from the TSC, and
// keyboards it polls itself through micro drivers.
//
// A micro driver is a cut-down copy of a real one that does only what a
// keyboard needs. The real drivers run on interrupts, threads and locks, all of
// which belong to the kernel that just died.
//
// See kernel/gmmos/gmmos.c.
void gmmos_run(char *reason, addr_t *pcs, int npcs) __attribute__((noreturn));
// One keyboard GMMOS can read by polling.
struct gmmos_kbd {
const char *name;
int (*probe)(void); // nonzero if it is there and usable
// The next key, or 0 when there is none: a Unicode codepoint, or one of the
// KBD_* codes from include/dev/kbd.h for keys that are not characters.
int (*poll)(void);
void (*leds)(int on); // optional: all three lights on or off
};
extern const struct gmmos_kbd gmmos_ps2;
extern const struct gmmos_kbd gmmos_i2c;
extern const struct gmmos_kbd gmmos_xhci;
// A USB or I2C keyboard report in the boot layout -- modifiers, a reserved
// byte, six usages -- turned into keys. A report is a state, not an event, so
// a key is a usage that was not in the previous one. kernel/gmmos/hidkeys.c.
struct gmmos_hidkeys {
uchar prev[8];
int q[16];
int qh, qt;
};
void gmmos_hid_report(struct gmmos_hidkeys *k, const uchar *boot);
int gmmos_hid_next(struct gmmos_hidkeys *k);
// I2C keyboards, as kernel/acpi/i2c_hid.c found them at boot. GMMOS cannot run
// ACPI to find them again, so the live driver hands over what it learnt: the
// DesignWare controller, the device's address, its input report size, the
// interrupt pin when there is one, and the parsed report descriptor.
struct gmmos_i2c_kbd {
volatile uint32 *regs;
int txdepth, rxdepth;
int addr;
uint32 maxin;
volatile uint32 *pin; // AMD GPIO pin register, or 0: poll blindly
int active_low;
void *hid;
};
int i2c_hid_keyboards(struct gmmos_i2c_kbd *out, int max);
// The xHCI controllers kernel/drivers/bus/xhci/xhci.c brought up: their
// registers, mapped. The micro driver resets each and enumerates again.
int xhci_gmmos_controllers(volatile uchar **caps, int max);
// From kernel/core/panic.c: draw the report again, and where its closing
// lines are, which GMMOS writes its status and its keys into.
void psogm_repaint(void);
void psogm_footer(int *x, int *y, int *step);