Skip to content

Commit 30c9a56

Browse files
committed
Adds a way to register devices and device classes.
Signed-off-by: Nathan Ringo <me@remexre.com>
1 parent a96e60b commit 30c9a56

File tree

7 files changed

+261
-3
lines changed

7 files changed

+261
-3
lines changed

doc/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
- [Kernel]()
1111
- [The `print()` and `format()` functions](./kernel/print.md)
1212
- [Init Functions](./kernel/init.md)
13+
- [Devices](./kernel/devices.md)
1314
- [Threads and Harts](./kernel/threads-and-harts.md)
1415
- [Memory management]()
1516
- [Overview](./kernel/mm/overview.md)

doc/kernel/devices.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Devices
2+
3+
Devices that have been detected are represented as a tree of `struct device` in memory.
4+
(This should not be confused with the [Devicetree](https://en.wikipedia.org/wiki/Devicetree) standard, which is one of several ways that ukoOS populates this tree.)

src/kernel/device.c

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2025 ukoOS Contributors
3+
*
4+
* SPDX-License-Identifier: GPL-3.0-or-later
5+
*/
6+
7+
#include <device.h>
8+
#include <init.h>
9+
10+
struct list_head device_classes = LIST_INIT(device_classes);
11+
12+
void register_device_class(struct device_class *device_class) {
13+
assert(device_class->name);
14+
assert(device_class->description);
15+
assert(list_is_empty(&device_class->device_classes));
16+
assert(list_is_empty(&device_class->members));
17+
18+
list_push(&device_classes, &device_class->device_classes);
19+
}
20+
21+
void register_device(struct device *parent, struct device_class *device_class,
22+
struct device *child) {
23+
assert(parent->name);
24+
assert(parent->device_class);
25+
assert(!list_is_empty(&parent->device_class_members));
26+
27+
assert(child->name);
28+
assert(!child->parent);
29+
assert(!child->device_class);
30+
assert(list_is_empty(&child->parent_children));
31+
assert(list_is_empty(&child->children));
32+
assert(list_is_empty(&child->device_class_members));
33+
34+
child->parent = parent;
35+
child->device_class = device_class;
36+
list_push(&parent->children, &child->parent_children);
37+
list_push(&device_class->members, &child->device_class_members);
38+
}
39+
40+
void print_devices(void) {
41+
struct device *device = &root_device;
42+
usize depth = 1;
43+
44+
print("devices {{");
45+
46+
assert(list_is_empty(&device->parent_children));
47+
for (;;) {
48+
assert(device->name);
49+
assert(device->device_class);
50+
assert(device->device_class->name);
51+
print("{indent}{cstr} class={cstr}{cstr}", 2 * depth, device->name,
52+
device->device_class->name,
53+
list_is_empty(&device->children) ? "" : " {");
54+
55+
if (!list_is_empty(&device->children)) {
56+
// Try to go to our first child.
57+
depth++;
58+
device =
59+
container_of(device->children.next, struct device, parent_children);
60+
} else {
61+
// Go up the tree until we find a node with a next sibling, or we've
62+
// returned to the root.
63+
while (device->parent &&
64+
device->parent_children.next == &device->parent->children) {
65+
device = device->parent;
66+
assert(depth > 1);
67+
depth--;
68+
print("{indent}}}", 2 * depth);
69+
}
70+
71+
// If we're at the root, stop traversing.
72+
if (!device->parent) {
73+
assert(depth == 1);
74+
break;
75+
}
76+
77+
// Otherwise, go to our sibling.
78+
device = container_of(device->parent_children.next, struct device,
79+
parent_children);
80+
}
81+
}
82+
83+
print("}}");
84+
}
85+
86+
/**
87+
* The device class of the root device. No other device should have this device
88+
* class.
89+
*/
90+
DEFINE_DEVICE_CLASS(root_device_class, "ukoos-root-device",
91+
"The device class of the root device.");
92+
93+
struct device root_device;
94+
95+
DEFINE_INIT(-10) {
96+
root_device = DEVICE_INIT(root_device, "root-device");
97+
// This can't use register_device() because it doesn't have a parent.
98+
root_device.device_class = &root_device_class;
99+
list_push(&root_device_class.members, &root_device.device_class_members);
100+
}

src/kernel/include.mak

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ kernel-cflags += -fdata-sections -ffunction-sections
2222
kernel-ldflags += -Wl,--gc-sections
2323
kernel-dir = src/kernel
2424
kernel-objs-asm =
25-
kernel-objs-c = devicetree init main panic print random selftest swar_test symbolicate
25+
kernel-objs-c = device devicetree init main panic print random selftest swar_test symbolicate
2626
kernel-objs-c += builtins/bzero builtins/explicit_bzero builtins/memcpy builtins/memcmp builtins/memset builtins/strcmp builtins/strlen
2727
kernel-objs-c += crypto/subtle/rfc7539 crypto/subtle/rfc7693
2828
kernel-objs-c += mm/alloc mm/alloc/block mm/alloc/heap mm/alloc/init mm/alloc/page mm/alloc/segment mm/physical_alloc mm/virtual_alloc

src/kernel/include/device.h

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2025 ukoOS Contributors
3+
*
4+
* SPDX-License-Identifier: GPL-3.0-or-later
5+
*/
6+
7+
#ifndef UKO_OS_KERNEL__DEVICE_H
8+
#define UKO_OS_KERNEL__DEVICE_H 1
9+
10+
#include <list.h>
11+
#include <print.h>
12+
13+
/**
14+
* The kind of device. These should be statically allocated.
15+
*/
16+
struct device_class {
17+
/**
18+
* The name of the device class. This should be a short name suitable as a
19+
* directory name.
20+
*/
21+
const char *name;
22+
23+
/**
24+
* A short description of the device class in prose.
25+
*/
26+
const char *description;
27+
28+
/**
29+
* The `list_head` for this class's entry in the `device_classes` list.
30+
*/
31+
struct list_head device_classes;
32+
33+
/**
34+
* The list of `struct device`s that have this class.
35+
*/
36+
struct list_head members;
37+
};
38+
39+
/**
40+
* Defines a device class and creates an initializer to register it.
41+
*/
42+
#define DEFINE_DEVICE_CLASS(IDENTIFIER, NAME, DESCRIPTION) \
43+
struct device_class IDENTIFIER; \
44+
DEFINE_INIT(-20) { register_device_class(&IDENTIFIER); } \
45+
struct device_class IDENTIFIER = { \
46+
.name = (NAME), \
47+
.description = (DESCRIPTION), \
48+
.device_classes = LIST_INIT((IDENTIFIER).device_classes), \
49+
.members = LIST_INIT((IDENTIFIER).members), \
50+
}
51+
52+
/**
53+
* The list of registered `struct device_class`es.
54+
*/
55+
extern struct list_head device_classes;
56+
57+
/**
58+
* Registers a device class.
59+
*
60+
* - `name` and `description` must be filled out.
61+
* - `device_classes` and `members` must be self-linked.
62+
*/
63+
[[gnu::nonnull(1)]]
64+
void register_device_class(struct device_class *device_class);
65+
66+
/**
67+
* A node in the tree of devices attached to the system.
68+
*/
69+
struct device {
70+
/**
71+
* The name of the device. This must be unique amongst its siblings.
72+
*
73+
* This should be `alloc`-allocated.
74+
*/
75+
char *name;
76+
77+
/**
78+
* The parent of the node. `nullptr` for the root node.
79+
*/
80+
struct device *parent;
81+
82+
/**
83+
* The `list_head` for this device's entry in `parent->children`.
84+
*/
85+
struct list_head parent_children;
86+
87+
/**
88+
* The children of the node.
89+
*/
90+
struct list_head children;
91+
92+
/**
93+
* The kind of device this is.
94+
*/
95+
struct device_class *device_class;
96+
97+
/**
98+
* The `list_head` for this device's entry in `device_class->members`.
99+
*/
100+
struct list_head device_class_members;
101+
};
102+
103+
/**
104+
* Returns an initializer for the `struct device` `DEVICE`.
105+
*
106+
* ### Example
107+
*
108+
* ```c
109+
* struct my_device {
110+
* // ...
111+
* struct device device;
112+
* // ...
113+
* };
114+
*
115+
* struct my_device *my_device = alloc(sizeof(*my_device));
116+
* my_device->device = DEVICE_INIT(my_device->device, "example");
117+
* ```
118+
*/
119+
#define DEVICE_INIT(DEVICE, NAME_FORMAT, ...) \
120+
(struct device) { \
121+
.name = format(NAME_FORMAT __VA_OPT__(, ) __VA_ARGS__), .parent = nullptr, \
122+
.parent_children = LIST_INIT((DEVICE).parent_children), \
123+
.children = LIST_INIT((DEVICE).children), .device_class = nullptr, \
124+
.device_class_members = LIST_INIT((DEVICE).device_class_members), \
125+
}
126+
127+
/**
128+
* The root of the tree of devices.
129+
*/
130+
extern struct device root_device;
131+
132+
/**
133+
* Registers a device in the tree of devices and with its device class.
134+
*
135+
* - `name` must be filled out.
136+
* - `parent` and `device_class` must be `nullptr`.
137+
* - `parent_children`, `children`, and `device_class_members` must be
138+
* self-linked.
139+
*/
140+
[[gnu::nonnull(1, 2, 3)]]
141+
void register_device(struct device *parent, struct device_class *device_class,
142+
struct device *child);
143+
144+
/**
145+
* Prints the tree of devices.
146+
*/
147+
void print_devices(void);
148+
149+
#endif // UKO_OS_KERNEL__DEVICE_H

src/kernel/include/list.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ struct list_head {
6565
* void init_parent(struct parent *p) { p->children = LIST_INIT(p->children); }
6666
* ```
6767
*/
68-
#define LIST_INIT(name) \
69-
(struct list_head) { .prev = &(name), .next = &(name) }
68+
#define LIST_INIT(LIST) \
69+
(struct list_head) { .prev = &(LIST), .next = &(LIST) }
7070

7171
/**
7272
* Returns whether `list` is empty.

src/kernel/main.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
* SPDX-License-Identifier: GPL-3.0-or-later
55
*/
66

7+
#include <device.h>
78
#include <devicetree.h>
89
#include <hart_locals.h>
910
#include <init.h>
@@ -48,6 +49,9 @@ void main(u64 hart_id, paddr devicetree_start, paddr kernel_start,
4849
// Run initializers, which include e.g. setting up device classes and drivers.
4950
run_initializers();
5051

52+
// Print the devices that have been created.
53+
print_devices();
54+
5155
print("Running self-tests...");
5256
run_selftests();
5357

0 commit comments

Comments
 (0)