input, fb, vt, sysfs: the kernel calls Xorg makes

Xorg on a framebuffer -- the fbdev video driver, the evdev input driver,
libpciaccess -- asks the kernel a fixed set of questions before it draws
anything, and gives up on the first wrong answer. These are those
answers.

evdev: EVIOCGVERSION, GID, GNAME, GPHYS, GUNIQ, GPROP, GBIT, GKEY, GABS,
GREP and the accepted setters. Xorg and libinput decide what a device
*is* from the capability bitmaps; /dev/input/eventN answered only
EVIOCGRAB, which reads as no device at all. Each device's bitmaps are
its defaults plus whatever it has reported, so a USB tablet makes the
pointer absolute.

fbdev: FBIOPUT_VSCREENINFO succeeds for the mode already set, as on
Linux -- the fbdev driver sets it every time it starts -- and still
refuses any other. FBIOPAN_DISPLAY to the origin; FBIOBLANK.

The console: KDSETMODE(KD_GRAPHICS) stops it drawing on a terminal,
KDSKBMODE(K_OFF) stops keys reaching its tty, and VT_PROCESS now does
the switch handshake: leaving the terminal sends relsig and waits for
VT_RELDISP, arriving sends acqsig. A terminal whose owner has died goes
back to the kernel whole, so a crashed X cannot leave the console dead.

/sys: boot_vga, a writable enable, and graphics/fb0 under the display
device, which is how the fbdev driver pairs a card with /dev/fb0.

ports/linux-headers: <linux/*.h> into musl's prefix, from sabotage-
linux's kernel-headers 4.19.88-2. Its checksum is recorded from the
first download; upstream publishes none.

cmd/xready makes each call and checks the answer, including the switch
handshake on tty2 and an owner that dies holding it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
This commit is contained in:
Nexoniarz 2026-10-24 12:06:04 +02:00
commit b1323e193a
14 changed files with 860 additions and 32 deletions

View file

@ -489,6 +489,8 @@ consoleintr(int (*getc)(void))
// Input goes to whichever terminal is on screen. That is what makes a
// terminal a terminal: the keyboard follows the eyes.
struct vt *v = vt_current();
if (!vt_takes_keys(v))
continue; // K_OFF: its owner reads /dev/input
// Input mapping, before anything looks at the character. ISTRIP is about
// seven-bit lines and applies to characters only -- masking a keycode

View file

@ -189,14 +189,47 @@ fb_ioctl(int request, addr_t arg)
return 0;
}
// The mode came from the bootloader's VBE call and we have no way to
// change it. Saying so is more useful than accepting the request and
// leaving the caller to discover, by drawing, that nothing happened.
case FBIOPUT_VSCREENINFO:
return -EINVAL;
// The mode came from the loader and there is no way to change it. A
// request for a *different* one is refused, which is more useful than
// accepting it and leaving the caller to discover by drawing that nothing
// happened. A request for the mode already set succeeds and is answered
// with it, as on Linux: Xorg's fbdev driver sets the mode it was just told
// about, every time it starts, and gives up if that fails.
case FBIOPUT_VSCREENINFO: {
struct fb_var_screeninfo *v =
(struct fb_var_screeninfo *)user_ptr(arg, sizeof(*v));
if (!v)
return -EFAULT;
if (v->xres != (unsigned int)w || v->yres != (unsigned int)h ||
(v->bits_per_pixel && v->bits_per_pixel != (unsigned int)gfx_fb_bpp()) ||
(v->xres_virtual && v->xres_virtual != (unsigned int)w) ||
(v->yres_virtual && v->yres_virtual != (unsigned int)h))
return -EINVAL;
unsigned int act = v->activate;
memset(v, 0, sizeof(*v));
v->xres = v->xres_virtual = (unsigned int)w;
v->yres = v->yres_virtual = (unsigned int)h;
v->bits_per_pixel = gfx_fb_bpp();
v->activate = act;
pixel_format(v, v->bits_per_pixel);
return 0;
}
// One screen-sized buffer, so the only pan there is is to the origin.
case FBIOPAN_DISPLAY: {
struct fb_var_screeninfo *v =
(struct fb_var_screeninfo *)user_ptr(arg, sizeof(*v));
if (!v)
return -EFAULT;
return (v->xoffset || v->yoffset) ? -EINVAL : 0;
}
// Blanking is a screen saver's request, and saying no makes Xorg log a
// warning every time one starts. There is no power control behind the
// framebuffer, so unblanking is all that can honestly succeed -- and
// blanking does too, as Linux's own drivers without the hook do.
case FBIOBLANK:
return -EINVAL;
return 0;
}
return -ENOTTY;

View file

@ -19,6 +19,8 @@
#include "fs/fs.h"
#include "fs/file.h"
#include "dev/input.h"
#include "core/uaccess.h"
#include "gfx/gfx.h"
#include "uapi/errno.h"
#define NEVENT 256 // per device; ~10 seconds of frantic typing
@ -46,6 +48,17 @@ struct input_dev {
// every key pressed since boot, and the next program to open the device is
// handed the lot as though the user had just typed it.
int opens;
// What the device has said, for the EVIOCG* queries. `caps` is every type
// and code it can produce: its defaults, set in inputinit(), and anything
// it has actually reported since -- a USB tablet makes the mouse device
// absolute the first time it moves. `keys` is which keys and buttons are
// down right now, and `abs` where each absolute axis last was. Tracked
// whether or not anybody has the device open; a program that opens it with
// a key held needs to know.
uchar caps[EV_MAX + 1][(KEY_MAX + 1) / 8];
uchar keys[(KEY_MAX + 1) / 8];
int32 abs[2];
};
static struct input_dev idev[NINPUTDEV];
@ -61,6 +74,21 @@ inputinit(void)
for(i = 0; i < NINPUTDEV; i++)
initlock(&idev[i].lock, "input");
// Every key a PS/2 or USB keyboard here can produce is below 0x80, but a
// keyboard advertises the whole first page on Linux too, and both Xorg's
// evdev and libinput call a device a keyboard by finding letter keys in it.
struct input_dev *k = &idev[INPUT_KBD], *m = &idev[INPUT_MOUSE];
for(i = 1; i < 0x100; i++)
k->caps[EV_KEY][i / 8] |= 1 << (i % 8);
k->caps[0][0] = (1 << EV_SYN) | (1 << EV_KEY);
k->caps[0][EV_REP / 8] |= 1 << (EV_REP % 8);
m->caps[0][0] = (1 << EV_SYN) | (1 << EV_KEY) | (1 << EV_REL);
for(i = BTN_LEFT; i <= BTN_MIDDLE; i++)
m->caps[EV_KEY][i / 8] |= 1 << (i % 8);
m->caps[EV_REL][0] = (1 << REL_X) | (1 << REL_Y);
m->caps[EV_REL][REL_WHEEL / 8] |= 1 << (REL_WHEEL % 8);
// Producers run from interrupt handlers and can fire before this point in
// boot. Until the locks exist, an event has nowhere safe to go.
__sync_synchronize();
@ -79,6 +107,19 @@ input_report(int dev, int type, int code, int value)
return;
d = &idev[dev];
if(type > 0 && type <= EV_MAX && code >= 0 && code <= KEY_MAX){
d->caps[0][type / 8] |= 1 << (type % 8);
d->caps[type][code / 8] |= 1 << (code % 8);
if(type == EV_KEY){
if(value)
d->keys[code / 8] |= 1 << (code % 8);
else
d->keys[code / 8] &= ~(1 << (code % 8));
}
if(type == EV_ABS && code < 2)
d->abs[code] = value;
}
// Nobody is listening, so there is nowhere for this to go. Linux does the
// same: an evdev device with no clients does not accumulate.
if(d->opens == 0)
@ -213,18 +254,146 @@ inputclose(struct inode *ip)
}
}
// ioctl on /dev/input/eventN. Only EVIOCGRAB so far; the EVIOCG* query
// requests (name, id, capability bitmaps) can arrive when something needs
// them.
// Copy `n` bytes of `src` out for a variable-length query: at most the
// caller's buffer, and the return value is how much went, as Linux's is.
static int
evioc_out(addr_t arg, uint cap, const void *src, uint n)
{
if(n > cap)
n = cap;
void *dst = user_ptr(arg, n ? n : 1);
if(!dst)
return -EFAULT;
memmove(dst, src, n);
return (int)n;
}
static int
evioc_str(addr_t arg, uint cap, const char *s)
{
return evioc_out(arg, cap, s, strlen(s) + 1);
}
// ioctl on /dev/input/eventN. See the EVIOC* block in include/dev/input.h.
int
input_ioctl(struct inode *ip, int request, addr_t arg)
{
int on;
int dev = ip->minor;
uint r = (uint)request;
if(request == EVIOCGRAB){
if(dev < 0 || dev >= NINPUTDEV)
return -ENODEV;
struct input_dev *d = &idev[dev];
switch(r){
case EVIOCGRAB:
// Linux passes the flag *by value* in the argument, not by pointer.
on = (int)arg;
return input_grab(ip->minor, on);
return input_grab(dev, (int)arg);
case EVIOCREVOKE:
// A session manager taking the device back from a client on a VT
// switch. With one reader per device, dropping the grab is the part
// that has an effect.
return input_grab(dev, 0);
case EVIOCGVERSION: {
int *v = user_ptr(arg, sizeof *v);
if(!v)
return -EFAULT;
*v = 0x010001;
return 0;
}
case EVIOCGID: {
struct input_id *id = user_ptr(arg, sizeof *id);
if(!id)
return -EFAULT;
id->bustype = BUS_I8042;
id->vendor = 0x0001;
id->product = dev == INPUT_KBD ? 0x0001 : 0x0002;
id->version = 0x0100;
return 0;
}
case EVIOCGREP: {
uint *rep = user_ptr(arg, 2 * sizeof *rep);
if(!rep)
return -EFAULT;
rep[0] = 250; // delay, ms
rep[1] = 33; // period, ms
return 0;
}
case EVIOCSREP:
case EVIOCSCLOCKID:
// Repeat is the reader's business here -- the kernel queues no repeat
// events -- and the clock is the only one there is. Accepted, so that a
// program setting either at startup carries on.
return 0;
}
if(EVIOC_TYPE(r) != 'E' || EVIOC_DIR(r) != 2)
return -ENOTTY;
uint nr = EVIOC_NR(r), cap = EVIOC_SIZE(r);
int got;
if(nr >= EVIOCNR_GABS && nr < EVIOCNR_GABS + ABS_MAX + 1){
struct input_absinfo *a = user_ptr(arg, sizeof *a);
uint axis = nr - EVIOCNR_GABS;
if(!a)
return -EFAULT;
memset(a, 0, sizeof *a);
// Absolute positions arrive already in pixels; see mouse_set_abs().
if(axis < 2){
a->value = d->abs[axis];
a->maximum = (axis == 0 ? (int)gfx_width() : (int)gfx_height()) - 1;
if(a->maximum < 1)
a->maximum = axis == 0 ? 799 : 599;
}
return 0;
}
acquire(&d->lock);
if(nr >= EVIOCNR_GBIT && nr <= EVIOCNR_GBIT + EV_MAX){
uint type = nr - EVIOCNR_GBIT;
// EVIOCGBIT(0) is the bitmap of event types, which is one byte's worth
// of interest in a field Linux sizes for EV_MAX.
uint len = type == 0 ? (EV_MAX + 1) / 8 :
type == EV_KEY ? (KEY_MAX + 1) / 8 :
type == EV_REL ? (REL_MAX + 1) / 8 :
type == EV_ABS ? (ABS_MAX + 1) / 8 : 8;
uchar buf[(KEY_MAX + 1) / 8];
memmove(buf, d->caps[type], len);
release(&d->lock);
return evioc_out(arg, cap, buf, len);
}
if(nr == EVIOCNR_GKEY){
uchar buf[(KEY_MAX + 1) / 8];
memmove(buf, d->keys, sizeof buf);
release(&d->lock);
return evioc_out(arg, cap, buf, sizeof buf);
}
release(&d->lock);
switch(nr){
case EVIOCNR_GNAME:
return evioc_str(arg, cap, dev == INPUT_KBD ? "SchOS keyboard"
: "SchOS pointer");
case EVIOCNR_GPHYS:
return evioc_str(arg, cap, dev == INPUT_KBD ? "isa0060/serio0/input0"
: "isa0060/serio1/input0");
case EVIOCNR_GUNIQ:
return evioc_str(arg, cap, "");
case EVIOCNR_GPROP: {
uchar prop[4] = { 0 };
if(dev == INPUT_MOUSE)
prop[0] = 1 << INPUT_PROP_POINTER;
return evioc_out(arg, cap, prop, sizeof prop);
}
case EVIOCNR_GLED:
case EVIOCNR_GSND:
case EVIOCNR_GSW: {
// None of these exist here: all off.
uchar zero[8] = { 0 };
got = evioc_out(arg, cap, zero, sizeof zero);
return got;
}
}
return -ENOTTY;
}

View file

@ -20,6 +20,9 @@
#include "gfx/theme.h"
#include "gfx/gfx.h"
#include "dev/vt.h"
#include "arch/mmu.h"
#include "core/proc.h"
#include "uapi/errno.h"
int vt_active = 0;
@ -54,10 +57,13 @@ cell_at(struct vt *v, int col, int row)
return &v->cells[row * v->cols + col];
}
// On screen, and the console's to draw. A terminal in KD_GRAPHICS belongs to
// a display server: its grid still takes writes -- a kernel message lands in
// it -- but nothing is drawn, or the text would be painted over the desktop.
static int
is_active(struct vt *v)
{
return vt_ready && v->index == vt_active;
return vt_ready && v->index == vt_active && v->kdmode != KD_GRAPHICS;
}
// Paint one cell from the grid. The single place that turns stored state into
@ -164,6 +170,8 @@ vt_init(void)
// pgid 1, which is init's, so the first shell on each terminal already
// sees itself as the foreground job rather than stopping with SIGTTIN.
v->fg_pgrp = 1;
v->leaving_to = -1;
v->kbmode = K_UNICODE;
termios_defaults(&v->tio);
for (int r = 0; r < rows; r++)
blank_row(v, r);
@ -826,6 +834,58 @@ vt_feed_1(struct vt *v, int c)
// Switching
// ---------------------------------------------------------------------------
// A terminal whose VT_PROCESS owner is gone goes back to the kernel, whole:
// switching, drawing and the keyboard. Otherwise a display server that
// crashed would leave the screen frozen on its last frame, the keyboard
// deaf and the chord ignored, with no way back short of a reset.
static int
owner_alive(struct vt *v)
{
if (v->mode != VT_PROCESS)
return 0;
if (v->owner && proc_exists(v->owner))
return 1;
v->mode = VT_AUTO;
v->owner = 0;
v->leaving_to = -1;
v->kbmode = K_UNICODE;
if (v->kdmode == KD_GRAPHICS) {
v->kdmode = KD_TEXT;
if (v->index == vt_active) {
vt_repaint(v);
gfx_flush();
}
}
return 0;
}
static void
switch_now(int i)
{
struct vt *to = &vts[i];
vt_active = i;
// Repaint from the grid. Nothing was saved on the way out because nothing
// needed to be: the outgoing terminal's grid has been current all along,
// which is the whole reason the grid exists. A terminal in graphics mode is
// not repainted -- its owner is told, and draws its own.
if (to->kdmode != KD_GRAPHICS) {
vt_repaint(to);
gfx_flush();
}
if (owner_alive(to) && to->acqsig)
sigsend(to->owner, to->acqsig);
}
// Switch terminals.
//
// Away from one in VT_PROCESS, the switch is a request: its owner is sent
// relsig and the switch waits for VT_RELDISP. That is how a display server
// learns it is about to lose the screen -- it stops drawing, and only then
// says yes. Switching without asking left it drawing straight over the text
// console it had been switched away from. Back to such a terminal, the
// switch happens and the owner is sent acqsig, to redraw.
int
vt_switch(int i)
{
@ -836,16 +896,67 @@ vt_switch(int i)
if (i == vt_active)
return 0;
vt_active = i;
// Repaint from the grid. Nothing was saved on the way out because nothing
// needed to be: the outgoing terminal's grid has been current all along,
// which is the whole reason the grid exists.
vt_repaint(&vts[i]);
gfx_flush();
struct vt *from = &vts[vt_active];
if (owner_alive(from) && from->relsig) {
from->leaving_to = i;
if (sigsend(from->owner, from->relsig) == 0)
return 0;
owner_alive(from); // died in between; fall through
}
switch_now(i);
return 0;
}
int
vt_reldisp(struct vt *v, int arg)
{
if (arg == VT_ACKACQ)
return 0;
if (v->mode != VT_PROCESS || v->leaving_to < 0)
return -EINVAL;
int to = v->leaving_to;
v->leaving_to = -1;
if (arg && v->index == vt_active)
switch_now(to);
return 0;
}
void
vt_set_process(struct vt *v, int mode, int relsig, int acqsig)
{
v->mode = mode;
v->leaving_to = -1;
if (mode == VT_PROCESS) {
v->owner = myproc() ? myproc()->pid : 0;
v->relsig = relsig > 0 && relsig < 32 ? relsig : 0;
v->acqsig = acqsig > 0 && acqsig < 32 ? acqsig : 0;
} else {
v->owner = v->relsig = v->acqsig = 0;
}
}
void
vt_set_kdmode(struct vt *v, int mode)
{
int was = v->kdmode;
v->kdmode = mode;
// Back to text on the terminal on screen: the grid has been kept current
// underneath the whole time, so putting it back is a repaint.
if (was == KD_GRAPHICS && mode == KD_TEXT && v->index == vt_active) {
vt_repaint(v);
gfx_flush();
}
}
int
vt_takes_keys(struct vt *v)
{
if (v->kbmode == K_XLATE || v->kbmode == K_UNICODE)
return 1;
// A raw or K_OFF terminal whose owner died gets its keyboard back.
return v->mode == VT_PROCESS && !owner_alive(v);
}
int
vt_index(struct vt *v)
{
@ -870,7 +981,7 @@ vt_index(struct vt *v)
int
vt_kernel_switches(void)
{
return vt_current()->mode != VT_PROCESS;
return !owner_alive(vt_current());
}
// Step to the previous or next terminal that is actually in use.

View file

@ -4,7 +4,8 @@
//
// /sys/bus/pci/devices/0000:00:1f.2/ vendor device class revision irq
// subsystem_vendor subsystem_device
// resource config uevent
// resource config uevent boot_vga
// enable graphics/fb0/
// /sys/bus/usb/devices/1-3.2/ idVendor idProduct bcdDevice busnum
// devnum speed bDevice* uevent
// /sys/firmware/acpi/tables/DSDT the tables, byte for byte
@ -40,6 +41,7 @@
#include "dev/pci.h"
#include "dev/usb.h"
#include "core/hwinfo.h"
#include "gfx/gfx.h"
// ---------------------------------------------------------------------------
// Inode numbers: kind, index, file -- the whole identity, since iget gives
@ -87,11 +89,43 @@ static const struct { const char *name; int parent; } dirs[D_NDIRS] = {
static const char *const pcifiles[] = {
0, "vendor", "device", "class", "revision", "subsystem_vendor",
"subsystem_device", "irq", "resource", "config", "uevent",
"subsystem_device", "irq", "resource", "config", "uevent", "boot_vga",
"enable",
};
#define PCI_CONFIG 9
#define PCI_ENABLE 12
#define NPCIFILES ((int)(sizeof pcifiles / sizeof pcifiles[0]))
// Two directories under the display device only: graphics/ and graphics/fb0/.
// Xorg's fbdev driver finds the framebuffer that belongs to a PCI device by
// looking for exactly that path, and libpciaccess picks the primary card by
// boot_vga. Empty, because the path existing is the whole answer.
#define PCI_GRAPHICS 13
#define PCI_FB0 14
// The display device: the one whose memory the framebuffer is in, which is
// what boot_vga means on Linux -- the card firmware brought up and drew on.
// Failing that, the first display controller. -1 when there is neither.
static int
boot_vga_idx(void)
{
uint64 fb = gfx_fb_phys();
int first = -1;
for (int i = 0; i < pci_count(); i++) {
struct pci_dev *d = pci_get(i);
if (d->class != 0x03)
continue;
if (first < 0)
first = i;
for (int b = 0; fb && b < 6; b++)
if (!d->bar[b].is_io && d->bar[b].base && fb >= d->bar[b].base &&
fb < d->bar[b].base + d->bar[b].size)
return i;
}
return first;
}
static const char *const usbfiles[] = {
0, "idVendor", "idProduct", "bcdDevice", "busnum", "devnum", "speed",
"bDeviceClass", "bDeviceSubClass", "bDeviceProtocol", "bNumConfigurations",
@ -276,6 +310,12 @@ children(uint64 dino, child_fn cb, void *ctx)
for (int f = 1; f < NPCIFILES; f++)
if (cb(ctx, INO(K_PCI, idx, f), 0, pcifiles[f]))
return 1;
if (idx == boot_vga_idx() && gfx_fb_phys() &&
cb(ctx, INO(K_PCI, idx, PCI_GRAPHICS), 1, "graphics"))
return 1;
} else if (kind == K_PCI && I_FILE(dino) == PCI_GRAPHICS) {
if (cb(ctx, INO(K_PCI, idx, PCI_FB0), 1, "fb0"))
return 1;
} else if (kind == K_USB && I_FILE(dino) == 0) {
for (int f = 1; f < NUSBFILES; f++)
if (cb(ctx, INO(K_USB, idx, f), 0, usbfiles[f]))
@ -293,7 +333,10 @@ parent_of(uint64 dino)
{
switch (I_KIND(dino)) {
case K_DIR: return INO(K_DIR, dirs[I_IDX(dino)].parent, 0);
case K_PCI: return INO(K_DIR, D_PCIDEV, 0);
case K_PCI:
if (I_FILE(dino) == PCI_GRAPHICS) return INO(K_PCI, I_IDX(dino), 0);
if (I_FILE(dino) == PCI_FB0) return INO(K_PCI, I_IDX(dino), PCI_GRAPHICS);
return INO(K_DIR, D_PCIDEV, 0);
case K_USB: return INO(K_DIR, D_USBDEV, 0);
case K_MEMMAP: return INO(K_DIR, D_MEMMAP, 0);
}
@ -363,6 +406,13 @@ gen_pci(struct out *o, struct pci_dev *d, int f)
case 5: puts_(o, "0x"); putx(o, svid, 4, 0); break;
case 6: puts_(o, "0x"); putx(o, sdid, 4, 0); break;
case 7: putu(o, d->irq == 0xFF ? 0 : d->irq); break;
case 11: putc_(o, d == pci_get(boot_vga_idx()) ? '1' : '0'); break;
case PCI_ENABLE: {
// Whether memory or I/O decoding is on: what "enabled" amounts to.
uint32 cmd = pci_read32(d->bus, d->slot, d->func, 0x04);
putc_(o, (cmd & 3) ? '1' : '0');
break;
}
case 8:
// start, end, flags per BAR, then the ROM, as Linux prints them.
for (int b = 0; b < 7; b++) {
@ -487,6 +537,8 @@ exists_dir(uint64 ino)
case K_DIR:
return I_IDX(ino) < D_NDIRS && (I_IDX(ino) != D_EFI || boot_is_efi());
case K_PCI:
if (I_FILE(ino) == PCI_GRAPHICS || I_FILE(ino) == PCI_FB0)
return I_IDX(ino) == boot_vga_idx() && gfx_fb_phys() != 0;
return I_IDX(ino) < pci_count();
case K_USB: {
struct usb_info u;
@ -514,7 +566,9 @@ sysfs_iload(struct inode *ip)
ip->size = 0;
ip->nlink = 1;
if (I_FILE(ino) == 0 && exists_dir(ino)) {
int subdir = I_KIND(ino) == K_PCI &&
(I_FILE(ino) == PCI_GRAPHICS || I_FILE(ino) == PCI_FB0);
if ((I_FILE(ino) == 0 || subdir) && exists_dir(ino)) {
ip->type = T_DIR;
ip->nlink = 2;
return;
@ -533,6 +587,8 @@ sysfs_iload(struct inode *ip)
kmfree(buf);
if (n >= 0) {
ip->type = T_FILE;
if (I_KIND(ino) == K_PCI && I_FILE(ino) == PCI_ENABLE)
ip->rwmode = 3;
return;
}
}
@ -585,9 +641,18 @@ sysfs_read(struct inode *ip, int user_dst, uint64 dst, uint64 off, uint n)
return r;
}
// Only `enable`, which libpciaccess writes before it touches a device. The
// kernel's drivers already turned decoding on for every device they use, and
// one nobody drives does not need it -- so the write is accepted and changes
// nothing, which is what it would have done on Linux for a device already on.
static int
sysfs_write(struct inode *ip, int u, uint64 s, uint64 o, uint n)
{ (void)ip; (void)u; (void)s; (void)o; (void)n; return -EROFS; }
{
(void)u; (void)s; (void)o;
if (I_KIND(ip->inum) == K_PCI && I_FILE(ip->inum) == PCI_ENABLE)
return (int)n;
return -EROFS;
}
struct find { const char *name; uint64 ino; };

View file

@ -1544,7 +1544,8 @@ addr_t sys_ioctl(void)
struct vt_mode *m = (struct vt_mode*)user_ptr(argp, sizeof(*m));
if (!m) return -EFAULT;
if (m->mode != VT_AUTO && m->mode != VT_PROCESS) return -EINVAL;
vt_for_minor(f->ip->minor)->mode = m->mode;
vt_set_process(vt_for_minor(f->ip->minor), m->mode, m->relsig,
m->acqsig);
return 0;
}
// Which chord the kernel switches on. SchOS's own ioctl -- see the long
@ -1601,10 +1602,60 @@ addr_t sys_ioctl(void)
struct vt_mode *m = (struct vt_mode*)user_ptr(argp, sizeof(*m));
if (!m) return -EFAULT;
memset(m, 0, sizeof(*m));
m->mode = (char)vt_for_minor(f->ip->minor)->mode;
struct vt *v = vt_for_minor(f->ip->minor);
m->mode = (char)v->mode;
m->relsig = (short)v->relsig;
m->acqsig = (short)v->acqsig;
return 0;
}
// The display server's half of a switch; see vt_switch(). By value.
if (request == VT_RELDISP)
return vt_reldisp(vt_for_minor(f->ip->minor), (int)argp);
// Terminals are a fixed set here and are never freed.
if (request == VT_DISALLOCATE)
return 0;
if (request == KDSETMODE) {
if (argp != KD_TEXT && argp != KD_GRAPHICS) return -EINVAL;
vt_set_kdmode(vt_for_minor(f->ip->minor), (int)argp);
return 0;
}
if (request == KDGETMODE) {
int *out = (int*)user_ptr(argp, sizeof(int));
if (!out) return -EFAULT;
*out = vt_for_minor(f->ip->minor)->kdmode;
return 0;
}
if (request == KDSKBMODE) {
if (argp > K_OFF) return -EINVAL;
vt_for_minor(f->ip->minor)->kbmode = (int)argp;
return 0;
}
if (request == KDGKBMODE) {
int *out = (int*)user_ptr(argp, sizeof(int));
if (!out) return -EFAULT;
*out = vt_for_minor(f->ip->minor)->kbmode;
return 0;
}
if (request == KDGKBTYPE) {
char *out = (char*)user_ptr(argp, 1);
if (!out) return -EFAULT;
*out = KB_101;
return 0;
}
// LEDs and the bell: nothing to light and no speaker on this path.
if (request == KDGETLED || request == KDGKBLED) {
char *out = (char*)user_ptr(argp, 1);
if (!out) return -EFAULT;
*out = 0;
return 0;
}
if (request == KDSETLED || request == KDSKBLED ||
request == KIOCSOUND || request == KDMKTONE)
return 0;
return 0;
}