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
This commit is contained in:
parent
a9f8cb7e0f
commit
5d4d318ebf
10 changed files with 813 additions and 37 deletions
|
|
@ -274,6 +274,25 @@ static int
|
|||
devfs_read(struct inode *ip, int u, uint64 d, uint64 o, uint n)
|
||||
{
|
||||
int x = extra_of(ip->inum);
|
||||
// mkdir lays down the "." and ".." of a v1-style directory by reading and
|
||||
// rewriting its first two entries (build_dir_tree). A directory here has
|
||||
// none stored, so the read gives zeros and the write is swallowed.
|
||||
if (x >= 0 && ip->type == T_DIR) {
|
||||
char z[64];
|
||||
memset(z, 0, sizeof z);
|
||||
for (uint done = 0; done < n; ) {
|
||||
uint c = n - done < sizeof z ? n - done : (uint)sizeof z;
|
||||
if (u) {
|
||||
if (copy_to_user(d + done, z, c) < 0)
|
||||
return -EFAULT;
|
||||
} else {
|
||||
memmove((char *)d + done, z, c);
|
||||
}
|
||||
done += c;
|
||||
}
|
||||
(void)o;
|
||||
return (int)n;
|
||||
}
|
||||
if (x < 0 || ip->type != T_SYMLINK)
|
||||
return -1;
|
||||
extras_init();
|
||||
|
|
@ -300,6 +319,8 @@ static int
|
|||
devfs_write(struct inode *ip, int u, uint64 s, uint64 o, uint n)
|
||||
{
|
||||
int x = extra_of(ip->inum);
|
||||
if (x >= 0 && ip->type == T_DIR)
|
||||
return (int)n; // see devfs_read
|
||||
if (x < 0 || ip->type != T_SYMLINK || o != 0 || n >= EXTRA_TARGET)
|
||||
return -1;
|
||||
extras_init();
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@
|
|||
#include "core/hwinfo.h"
|
||||
#include "gfx/gfx.h"
|
||||
#include "dev/acpi.h"
|
||||
#include "dev/kdev.h"
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Inode numbers: kind, index, file -- the whole identity, since iget gives
|
||||
|
|
@ -59,6 +60,16 @@
|
|||
#define K_DMIID 6ULL // file a DMI_* field + 1
|
||||
#define K_DMITAB 7ULL // file 1 the entry point, 2 the table
|
||||
#define K_PSU 8ULL // idx an acpi_psu_get() index; file a psufiles[] index
|
||||
#define K_KDEV 9ULL // idx a kdev index; file 0 the directory, KF_* its files
|
||||
#define K_KSUBV 10ULL // idx a kdev subsystem: /sys/devices/virtual/<subsys>
|
||||
#define K_KCLASS 11ULL // idx a kdev subsystem: /sys/class/<subsys>
|
||||
#define K_KCLINK 12ULL // idx a kdev: the symlink in /sys/class/<subsys>
|
||||
#define K_KDEVD 13ULL // idx 0 char, 1 block: /sys/dev/char, /sys/dev/block
|
||||
#define K_KDEVL 14ULL // idx a kdev: the "MAJ:MIN" symlink in /sys/dev/*
|
||||
|
||||
// Files in a kdev directory (K_KDEV).
|
||||
enum { KF_DIR, KF_DEV, KF_UEVENT, KF_SUBSYSTEM, KF_NAME, KF_PROPERTIES,
|
||||
KF_CAPS, KF_EV, KF_KEY, KF_REL, KF_ABS, KF_N };
|
||||
|
||||
#define INO(k, idx, f) (((uint64)(k) << 40) | ((uint64)(idx) << 8) | (uint64)(f))
|
||||
#define I_KIND(i) ((int)((i) >> 40))
|
||||
|
|
@ -68,6 +79,7 @@
|
|||
enum {
|
||||
D_ROOT, D_BUS, D_PCI, D_PCIDEV, D_USB, D_USBDEV, D_FW, D_ACPI, D_ACPITAB,
|
||||
D_DMI, D_DMITAB, D_MEMMAP, D_EFI, D_CLASS, D_CLASSDMI, D_DMIID, D_PSU,
|
||||
D_DEVICES, D_VIRTUAL, D_DEVT,
|
||||
D_NDIRS
|
||||
};
|
||||
|
||||
|
|
@ -89,6 +101,9 @@ static const struct { const char *name; int parent; } dirs[D_NDIRS] = {
|
|||
[D_CLASSDMI] = { "dmi", D_CLASS },
|
||||
[D_DMIID] = { "id", D_CLASSDMI },
|
||||
[D_PSU] = { "power_supply", D_CLASS },
|
||||
[D_DEVICES] = { "devices", D_ROOT },
|
||||
[D_VIRTUAL] = { "virtual", D_DEVICES },
|
||||
[D_DEVT] = { "dev", D_ROOT },
|
||||
};
|
||||
|
||||
#define SYSFS_ROOT INO(K_DIR, D_ROOT, 0)
|
||||
|
|
@ -387,6 +402,96 @@ gen_psu(struct out *o, int idx, int f)
|
|||
}
|
||||
}
|
||||
|
||||
// ---- kernel-implemented devices (include/dev/kdev.h) ----
|
||||
|
||||
static int
|
||||
kd_subsys_of(const struct kdev *k)
|
||||
{
|
||||
for (int s = 0; s < kdev_nsubsys(); s++) {
|
||||
const char *n = kdev_subsys(s), *m = k->subsys;
|
||||
while (*n && *n == *m) { n++; m++; }
|
||||
if (*n == *m)
|
||||
return s;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void
|
||||
kd_devt(const struct kdev *k, char *name)
|
||||
{
|
||||
struct out o = { name, 0, 30 };
|
||||
int maj, min;
|
||||
kdev_numbers(k, &maj, &min);
|
||||
putu(&o, (uint64)maj);
|
||||
putc_(&o, ':');
|
||||
putu(&o, (uint64)min);
|
||||
name[o.n] = 0;
|
||||
}
|
||||
|
||||
// Does file f exist in kdev k's directory?
|
||||
static int
|
||||
kd_has(const struct kdev *k, int f)
|
||||
{
|
||||
switch (f) {
|
||||
case KF_DEV: return k->devname[0] != 0;
|
||||
case KF_UEVENT:
|
||||
case KF_SUBSYSTEM: return 1;
|
||||
case KF_NAME: case KF_PROPERTIES: case KF_CAPS:
|
||||
return k->cap != KD_NONE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
kd_children(uint64 dino, child_fn cb, void *ctx)
|
||||
{
|
||||
int kind = I_KIND(dino), idx = I_IDX(dino);
|
||||
char name[32];
|
||||
struct kdev k;
|
||||
|
||||
if (kind == K_KSUBV) {
|
||||
for (int n = 0, i; (i = kdev_nth(n)) >= 0; n++)
|
||||
if (kdev_get(i, &k) == 0 && k.parent < 0 && kd_subsys_of(&k) == idx &&
|
||||
cb(ctx, INO(K_KDEV, i, 0), 1, k.name))
|
||||
return 1;
|
||||
} else if (kind == K_KCLASS) {
|
||||
for (int n = 0, i; (i = kdev_nth(n)) >= 0; n++)
|
||||
if (kdev_get(i, &k) == 0 && kd_subsys_of(&k) == idx &&
|
||||
cb(ctx, INO(K_KCLINK, i, 0), 2, k.name))
|
||||
return 1;
|
||||
} else if (kind == K_KDEVD) {
|
||||
for (int n = 0, i; (i = kdev_nth(n)) >= 0; n++)
|
||||
if (kdev_get(i, &k) == 0 && k.devname[0] && k.block == idx) {
|
||||
kd_devt(&k, name);
|
||||
if (cb(ctx, INO(K_KDEVL, i, 0), 2, name))
|
||||
return 1;
|
||||
}
|
||||
} else if (kind == K_KDEV && I_FILE(dino) == KF_DIR) {
|
||||
if (kdev_get(idx, &k) < 0)
|
||||
return 0;
|
||||
for (int f = KF_DEV; f <= KF_CAPS; f++)
|
||||
if (kd_has(&k, f)) {
|
||||
static const char *const names[] = { 0, "dev", "uevent", "subsystem",
|
||||
"name", "properties", "capabilities" };
|
||||
if (cb(ctx, INO(K_KDEV, idx, f),
|
||||
f == KF_CAPS ? 1 : f == KF_SUBSYSTEM ? 2 : 0, names[f]))
|
||||
return 1;
|
||||
}
|
||||
for (int n = 0, i; (i = kdev_nth(n)) >= 0; n++) {
|
||||
struct kdev c;
|
||||
if (kdev_get(i, &c) == 0 && c.parent == idx &&
|
||||
cb(ctx, INO(K_KDEV, i, 0), 1, c.name))
|
||||
return 1;
|
||||
}
|
||||
} else if (kind == K_KDEV && I_FILE(dino) == KF_CAPS) {
|
||||
static const char *const names[] = { "ev", "key", "rel", "abs" };
|
||||
for (int f = KF_EV; f <= KF_ABS; f++)
|
||||
if (cb(ctx, INO(K_KDEV, idx, f), 0, names[f - KF_EV]))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
children(uint64 dino, child_fn cb, void *ctx)
|
||||
{
|
||||
|
|
@ -403,6 +508,21 @@ children(uint64 dino, child_fn cb, void *ctx)
|
|||
return 1;
|
||||
}
|
||||
switch (idx) {
|
||||
case D_VIRTUAL:
|
||||
for (int s = 0; s < kdev_nsubsys(); s++)
|
||||
if (cb(ctx, INO(K_KSUBV, s, 0), 1, kdev_subsys(s)))
|
||||
return 1;
|
||||
break;
|
||||
case D_DEVT:
|
||||
if (cb(ctx, INO(K_KDEVD, 0, 0), 1, "char") ||
|
||||
cb(ctx, INO(K_KDEVD, 1, 0), 1, "block"))
|
||||
return 1;
|
||||
break;
|
||||
case D_CLASS:
|
||||
for (int s = 0; s < kdev_nsubsys(); s++)
|
||||
if (cb(ctx, INO(K_KCLASS, s, 0), 1, kdev_subsys(s)))
|
||||
return 1;
|
||||
break;
|
||||
case D_PCIDEV:
|
||||
for (int i = 0; i < pci_count(); i++) {
|
||||
struct out o = { name, 0, sizeof name - 1 };
|
||||
|
|
@ -486,6 +606,9 @@ children(uint64 dino, child_fn cb, void *ctx)
|
|||
for (int f = 1; f < NPSUFILES; f++)
|
||||
if (psu_has(&b, f) && cb(ctx, INO(K_PSU, idx, f), 0, psufiles[f]))
|
||||
return 1;
|
||||
} else if (kind == K_KSUBV || kind == K_KCLASS || kind == K_KDEVD ||
|
||||
kind == K_KDEV) {
|
||||
return kd_children(dino, cb, ctx);
|
||||
} else if (kind == K_MEMMAP && I_FILE(dino) == 0) {
|
||||
for (int f = 1; f < 4; f++)
|
||||
if (cb(ctx, INO(K_MEMMAP, idx, f), 0, memfiles[f]))
|
||||
|
|
@ -506,6 +629,19 @@ parent_of(uint64 dino)
|
|||
case K_USB: return INO(K_DIR, D_USBDEV, 0);
|
||||
case K_MEMMAP: return INO(K_DIR, D_MEMMAP, 0);
|
||||
case K_PSU: return INO(K_DIR, D_PSU, 0);
|
||||
case K_KSUBV: return INO(K_DIR, D_VIRTUAL, 0);
|
||||
case K_KCLASS: return INO(K_DIR, D_CLASS, 0);
|
||||
case K_KDEVD: return INO(K_DIR, D_DEVT, 0);
|
||||
case K_KDEV: {
|
||||
struct kdev k;
|
||||
if (I_FILE(dino) == KF_CAPS)
|
||||
return INO(K_KDEV, I_IDX(dino), 0);
|
||||
if (kdev_get(I_IDX(dino), &k) < 0)
|
||||
return SYSFS_ROOT;
|
||||
if (k.parent >= 0)
|
||||
return INO(K_KDEV, k.parent, 0);
|
||||
return INO(K_KSUBV, kd_subsys_of(&k), 0);
|
||||
}
|
||||
}
|
||||
return SYSFS_ROOT;
|
||||
}
|
||||
|
|
@ -687,6 +823,67 @@ text(uint64 ino, char *buf, int cap)
|
|||
putc_(&o, '\n');
|
||||
return o.n;
|
||||
}
|
||||
case K_KDEV: {
|
||||
struct kdev k;
|
||||
char path[160];
|
||||
if (kdev_get(idx, &k) < 0 || f < KF_DEV || f >= KF_N || f == KF_CAPS ||
|
||||
(f != KF_UEVENT && f != KF_SUBSYSTEM && !kd_has(&k, f) &&
|
||||
!(f >= KF_EV && k.cap != KD_NONE)))
|
||||
return -1;
|
||||
switch (f) {
|
||||
case KF_DEV:
|
||||
kd_devt(&k, buf);
|
||||
o.n = (int)strlen(buf);
|
||||
putc_(&o, '\n');
|
||||
return o.n;
|
||||
case KF_UEVENT:
|
||||
return kdev_uevent_text(idx, buf, cap);
|
||||
case KF_SUBSYSTEM: {
|
||||
// ../ once for each directory between here and /sys, then class/<s>.
|
||||
if (kdev_path(idx, path, sizeof path) < 0)
|
||||
return -1;
|
||||
for (const char *p = path; ; p++) {
|
||||
if (*p == '/' || *p == 0)
|
||||
puts_(&o, "../");
|
||||
if (*p == 0)
|
||||
break;
|
||||
}
|
||||
puts_(&o, "class/");
|
||||
puts_(&o, k.subsys);
|
||||
return o.n;
|
||||
}
|
||||
case KF_NAME:
|
||||
puts_(&o, k.cap == KD_KEYBOARD ? "SchOS keyboard\n" : "SchOS mouse\n");
|
||||
return o.n;
|
||||
case KF_PROPERTIES:
|
||||
puts_(&o, "0\n");
|
||||
return o.n;
|
||||
case KF_EV:
|
||||
puts_(&o, k.cap == KD_KEYBOARD ? "120013\n" : "17\n");
|
||||
return o.n;
|
||||
case KF_KEY:
|
||||
puts_(&o, k.cap == KD_KEYBOARD
|
||||
? "ffffffffffffffff ffffffffffffffff ffffffffffffffff fffffffffffffffe\n"
|
||||
: "70000 0 0 0 0\n");
|
||||
return o.n;
|
||||
case KF_REL:
|
||||
puts_(&o, k.cap == KD_MOUSE ? "903\n" : "0\n");
|
||||
return o.n;
|
||||
case KF_ABS:
|
||||
puts_(&o, "0\n");
|
||||
return o.n;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
case K_KCLINK:
|
||||
case K_KDEVL: {
|
||||
char path[160];
|
||||
if (kdev_path(idx, path, sizeof path) < 0)
|
||||
return -1;
|
||||
puts_(&o, "../../");
|
||||
puts_(&o, path);
|
||||
return o.n;
|
||||
}
|
||||
case K_DMIID: {
|
||||
int n = dmi_field(f - 1, buf, cap - 1);
|
||||
if (n < 0)
|
||||
|
|
@ -722,10 +919,30 @@ exists_dir(uint64 ino)
|
|||
}
|
||||
case K_PSU:
|
||||
return I_IDX(ino) < acpi_psu_count();
|
||||
case K_KSUBV:
|
||||
case K_KCLASS:
|
||||
return I_IDX(ino) < kdev_nsubsys();
|
||||
case K_KDEVD:
|
||||
return I_IDX(ino) < 2;
|
||||
case K_KDEV: {
|
||||
struct kdev k;
|
||||
if (kdev_get(I_IDX(ino), &k) < 0)
|
||||
return 0;
|
||||
return I_FILE(ino) == KF_DIR || (I_FILE(ino) == KF_CAPS && k.cap != KD_NONE);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Symlinks: the class and dev/ entries, and a device's own `subsystem`.
|
||||
static int
|
||||
is_link(uint64 ino)
|
||||
{
|
||||
int k = I_KIND(ino);
|
||||
return k == K_KCLINK || k == K_KDEVL ||
|
||||
(k == K_KDEV && I_FILE(ino) == KF_SUBSYSTEM);
|
||||
}
|
||||
|
||||
static void
|
||||
sysfs_iload(struct inode *ip)
|
||||
{
|
||||
|
|
@ -740,13 +957,24 @@ sysfs_iload(struct inode *ip)
|
|||
ip->size = 0;
|
||||
ip->nlink = 1;
|
||||
|
||||
int subdir = I_KIND(ino) == K_PCI &&
|
||||
(I_FILE(ino) == PCI_GRAPHICS || I_FILE(ino) == PCI_FB0);
|
||||
int subdir = (I_KIND(ino) == K_PCI &&
|
||||
(I_FILE(ino) == PCI_GRAPHICS || I_FILE(ino) == PCI_FB0)) ||
|
||||
(I_KIND(ino) == K_KDEV && I_FILE(ino) == KF_CAPS);
|
||||
if ((I_FILE(ino) == 0 || subdir) && exists_dir(ino)) {
|
||||
ip->type = T_DIR;
|
||||
ip->nlink = 2;
|
||||
return;
|
||||
}
|
||||
if (is_link(ino)) {
|
||||
char lb[200];
|
||||
int ln = text(ino, lb, sizeof lb);
|
||||
if (ln >= 0) {
|
||||
ip->type = T_SYMLINK;
|
||||
ip->size = (uint64)ln; // readlink(2) trusts this length
|
||||
ip->mode = 0777;
|
||||
return;
|
||||
}
|
||||
}
|
||||
int n = binary(ino, 0, 0);
|
||||
if (n >= 0) {
|
||||
ip->type = T_FILE;
|
||||
|
|
@ -761,7 +989,10 @@ 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)
|
||||
if ((I_KIND(ino) == K_PCI && (I_FILE(ino) == PCI_ENABLE ||
|
||||
I_FILE(ino) == 10 /* uevent */)) ||
|
||||
(I_KIND(ino) == K_USB && I_FILE(ino) == NUSBFILES - 1) ||
|
||||
(I_KIND(ino) == K_KDEV && I_FILE(ino) == KF_UEVENT))
|
||||
ip->rwmode = 3;
|
||||
return;
|
||||
}
|
||||
|
|
@ -822,9 +1053,31 @@ sysfs_read(struct inode *ip, int user_dst, uint64 dst, uint64 off, uint n)
|
|||
static int
|
||||
sysfs_write(struct inode *ip, int u, uint64 s, uint64 o, uint n)
|
||||
{
|
||||
(void)u; (void)s; (void)o;
|
||||
(void)o;
|
||||
if (I_KIND(ip->inum) == K_PCI && I_FILE(ip->inum) == PCI_ENABLE)
|
||||
return (int)n;
|
||||
// `echo add > uevent`: udevadm trigger asks the kernel to say again what it
|
||||
// said when the device appeared. Only the kernel's own devices are announced;
|
||||
// a PCI or USB device's uevent accepts the write and says nothing, because
|
||||
// their /sys paths are not the /devices tree an event's DEVPATH must name.
|
||||
if (I_KIND(ip->inum) == K_PCI || I_KIND(ip->inum) == K_USB)
|
||||
return (int)n;
|
||||
if (I_KIND(ip->inum) == K_KDEV && I_FILE(ip->inum) == KF_UEVENT) {
|
||||
char act[16];
|
||||
uint k = n < sizeof(act) - 1 ? n : (uint)sizeof(act) - 1;
|
||||
if (u) {
|
||||
if (copy_from_user(act, s, k) < 0)
|
||||
return -EFAULT;
|
||||
} else {
|
||||
memmove(act, (void *)s, k);
|
||||
}
|
||||
uint e = 0;
|
||||
while (e < k && act[e] != '\n' && act[e] != ' ')
|
||||
e++;
|
||||
act[e] = 0;
|
||||
kdev_uevent(I_IDX(ip->inum), e ? act : "change");
|
||||
return (int)n;
|
||||
}
|
||||
return -EROFS;
|
||||
}
|
||||
|
||||
|
|
@ -896,7 +1149,7 @@ emit_cb(void *ctx, uint64 ino, int isdir, const char *name)
|
|||
de->d_ino = ino;
|
||||
de->d_off = l->used + rec;
|
||||
de->d_reclen = (ushort)rec;
|
||||
de->d_type = isdir ? 4 : 8;
|
||||
de->d_type = isdir == 2 ? 10 : isdir ? 4 : 8;
|
||||
memmove(de->d_name, name, nlen);
|
||||
memset(de->d_name + nlen, 0, rec - (int)sizeof(*de) - nlen);
|
||||
l->used += rec;
|
||||
|
|
|
|||
Loading…
Reference in a new issue