SchOS/kernel/dev/kdev.c
Nexoniarz 5d4d318ebf udev works: /sys device tree, uevent netlink, datagram sockets with per-message credentials
Kernel devices (input, mem, tty, graphics, block) get /sys/devices/virtual
entries with dev/uevent/subsystem, /sys/class and /sys/dev links, input
capabilities, and writable uevent files that announce on the netlink socket.
eudev now runs, processes a full trigger and settles.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EVRhxi9zpdW2uDUN4FVcao
2026-10-26 11:39:13 +01:00

240 lines
6.2 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
//
// See include/dev/kdev.h.
#include "core/types.h"
#include "core/defs.h"
#include "core/param.h"
#include "core/spinlock.h"
#include "core/sleeplock.h"
#include "fs/fs.h"
#include "fs/blkdev.h"
#include "dev/kdev.h"
#include "dev/pty.h"
#include "net/socket.h"
static const char *const subsys[] = {
"input", "mem", "tty", "graphics", "block",
};
#define NSUBSYS ((int)(sizeof subsys / sizeof subsys[0]))
#define NO_PARENT (-1)
// Majors and minors are the ones /dev reports, devsw indices and all -- the
// devnodes table in kernel/fs/devfs.c. udev compares what it reads here with
// what stat() says about the node, so these have to be the same numbers.
static const struct kdev table[] = {
{ "input", "input0", "", 0, 0, 0, NO_PARENT, KD_KEYBOARD },
{ "input", "event0", "input/event0", 7, 0, 0, 0, KD_NONE },
{ "input", "input1", "", 0, 0, 0, NO_PARENT, KD_MOUSE },
{ "input", "event1", "input/event1", 7, 1, 0, 2, KD_NONE },
{ "mem", "null", "null", 1, 2, 0, NO_PARENT, KD_NONE },
{ "mem", "zero", "zero", 1, 5, 0, NO_PARENT, KD_NONE },
{ "mem", "full", "full", 1, 7, 0, NO_PARENT, KD_NONE },
{ "mem", "random", "random", 1, 8, 0, NO_PARENT, KD_NONE },
{ "mem", "urandom", "urandom", 1, 9, 0, NO_PARENT, KD_NONE },
{ "tty", "console", "console", 1, 1, 0, NO_PARENT, KD_NONE },
{ "tty", "tty", "tty", 1, 10, 0, NO_PARENT, KD_NONE },
{ "tty", "tty0", "tty0", 1, 0, 0, NO_PARENT, KD_NONE },
{ "tty", "tty1", "tty1", 1, 11, 0, NO_PARENT, KD_NONE },
{ "tty", "tty2", "tty2", 1, 12, 0, NO_PARENT, KD_NONE },
{ "tty", "tty3", "tty3", 1, 13, 0, NO_PARENT, KD_NONE },
{ "tty", "tty4", "tty4", 1, 14, 0, NO_PARENT, KD_NONE },
{ "tty", "tty5", "tty5", 1, 15, 0, NO_PARENT, KD_NONE },
{ "tty", "tty6", "tty6", 1, 16, 0, NO_PARENT, KD_NONE },
{ "tty", "tty7", "tty7", 1, 17, 0, NO_PARENT, KD_NONE },
{ "tty", "ptmx", "ptmx", PTMX_MAJOR, 0, 0, NO_PARENT, KD_NONE },
{ "graphics", "fb0", "fb0", 2, 0, 0, NO_PARENT, KD_NONE },
};
#define NTABLE ((int)(sizeof table / sizeof table[0]))
int kdev_nsubsys(void) { return NSUBSYS; }
const char *kdev_subsys(int s) { return s >= 0 && s < NSUBSYS ? subsys[s] : 0; }
int
kdev_nth(int n)
{
if (n < NTABLE)
return n < 0 ? -1 : n;
n -= NTABLE;
for (int k = 0, b; (b = blkdev_nth(k)) >= 0; k++)
if (n-- == 0)
return KDEV_BLK0 + b;
return -1;
}
static void
copy(char *d, const char *s, int cap)
{
int i = 0;
while (s[i] && i < cap - 1) { d[i] = s[i]; i++; }
d[i] = 0;
}
int
kdev_get(int i, struct kdev *k)
{
if (i >= 0 && i < NTABLE) {
*k = table[i];
return 0;
}
if (i >= KDEV_BLK0 && blkdev_present((uint)(i - KDEV_BLK0))) {
memset(k, 0, sizeof(*k));
copy(k->subsys, "block", sizeof k->subsys);
copy(k->name, blkdev_name((uint)(i - KDEV_BLK0)), sizeof k->name);
copy(k->devname, k->name, sizeof k->devname);
k->major = 8;
k->minor = (short)(i - KDEV_BLK0);
k->block = 1;
k->parent = NO_PARENT;
return 0;
}
return -1;
}
void
kdev_numbers(const struct kdev *k, int *maj, int *min)
{
if (k->block) {
*maj = k->major;
*min = k->minor;
return;
}
dev_stat_numbers(k->major, k->minor, maj, min);
}
int
kdev_path(int i, char *buf, int cap)
{
struct kdev k;
char tmp[160];
int n = 0;
if (kdev_get(i, &k) < 0)
return -1;
// The top ancestor names the subsystem directory; below it, one directory
// per level.
int chain[4], depth = 0;
for (int c = i; c >= 0 && depth < 4; ) {
struct kdev ck;
if (kdev_get(c, &ck) < 0)
break;
chain[depth++] = c;
c = ck.parent;
}
copy(tmp, "devices/virtual/", sizeof tmp);
n = (int)strlen(tmp);
{
struct kdev top;
kdev_get(chain[depth - 1], &top);
copy(tmp + n, top.subsys, (int)sizeof tmp - n);
n += (int)strlen(tmp + n);
}
for (int d = depth - 1; d >= 0 && n < (int)sizeof tmp - 2; d--) {
struct kdev ck;
kdev_get(chain[d], &ck);
tmp[n++] = '/';
copy(tmp + n, ck.name, (int)sizeof tmp - n);
n += (int)strlen(tmp + n);
}
copy(buf, tmp, cap);
return (int)strlen(buf);
}
static uint kseq;
static int
put(char *b, int cap, int n, const char *s)
{
while (*s && n < cap - 1)
b[n++] = *s++;
return n;
}
static int
putnum(char *b, int cap, int n, uint v)
{
char t[12];
int k = 0;
do { t[k++] = '0' + v % 10; v /= 10; } while (v);
while (k && n < cap - 1)
b[n++] = t[--k];
return n;
}
// KEY=value pairs, NUL separated, as the kernel sends them; `nl` swaps the
// NULs for newlines, which is what the uevent file shows.
static int
vars(int i, char *buf, int cap, const char *action, int nl)
{
struct kdev k;
char path[160];
int n = 0;
char sep = nl ? '\n' : 0;
if (kdev_get(i, &k) < 0 || kdev_path(i, path, sizeof path) < 0)
return -1;
if (action) {
n = put(buf, cap, n, "ACTION=");
n = put(buf, cap, n, action);
buf[n++] = sep;
}
n = put(buf, cap, n, "DEVPATH=/");
n = put(buf, cap, n, path);
buf[n++] = sep;
n = put(buf, cap, n, "SUBSYSTEM=");
n = put(buf, cap, n, k.subsys);
buf[n++] = sep;
if (action) {
n = put(buf, cap, n, "SEQNUM=");
n = putnum(buf, cap, n, ++kseq);
buf[n++] = sep;
}
if (k.devname[0]) {
int maj, min;
kdev_numbers(&k, &maj, &min);
n = put(buf, cap, n, "MAJOR=");
n = putnum(buf, cap, n, (uint)maj);
buf[n++] = sep;
n = put(buf, cap, n, "MINOR=");
n = putnum(buf, cap, n, (uint)min);
buf[n++] = sep;
n = put(buf, cap, n, "DEVNAME=");
n = put(buf, cap, n, k.devname);
buf[n++] = sep;
}
if (k.block) {
n = put(buf, cap, n, "DEVTYPE=disk");
buf[n++] = sep;
}
return n;
}
int
kdev_uevent_text(int i, char *buf, int cap)
{
return vars(i, buf, cap, 0, 1);
}
void
kdev_uevent(int i, const char *action)
{
char msg[512];
char path[160];
int n = 0;
if (kdev_path(i, path, sizeof path) < 0)
return;
n = put(msg, sizeof msg, n, action);
msg[n++] = '@';
n = put(msg, sizeof msg, n, "/");
n = put(msg, sizeof msg, n, path);
msg[n++] = 0;
int v = vars(i, msg + n, (int)sizeof msg - n, action, 0);
if (v < 0)
return;
uevent_send(UEVENT_GROUP_KERNEL, msg, n + v, 0);
}