1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
/* Copyright (C) 2026 Richard Qin */
/*
* devfs.cc — /dev population and chardev-aware open
*
* Creates /dev as a subdirectory under the ramfs root, then
* populates device nodes. On open(), if the inode is S_IFCHR,
* we look up the cdev by i_rdev and use its fops.
*/
#include <lume/fs.h>
#include <lume/dcache.h>
#include <lume/file.h>
#include <lume/chardev.h>
#include <lume/mount.h>
#include <lume/klog.h>
#include <lume/errno.h>
#include <lume/fs_types.h>
/* ================================================================== */
/* devfs_create_node — create a char device inode under parent */
/* ================================================================== */
static int devfs_create_node(dentry* parent, const char* name,
uint32 major, uint32 minor)
{
inode* dir_ip = parent->d_inode;
if (!dir_ip || !dir_ip->i_op || !dir_ip->i_op->mknod) {
return -EPERM;
}
qstr q{};
q.name = name;
q.len = 0;
for (const char* s = name; *s; s++) {
q.len++;
}
dentry* de = d_alloc(parent, &q);
if (!de) {
return -ENOMEM;
}
int err = dir_ip->i_op->mknod(dir_ip, de, S_IFCHR | 0666,
MKDEV(major, minor));
if (err) {
return err;
}
return 0;
}
/* ================================================================== */
/* devfs_open — chardev-aware open hook */
/* ================================================================== */
/*
* Called from sys_openat when the inode is S_IFCHR.
* Replaces f_op with the chardev's file_operations.
*/
int devfs_chrdev_open(inode* ip, file* fp)
{
if (!S_ISCHR(ip->i_mode)) {
return 0;
}
cdev* cd = cdev_find(ip->i_rdev);
if (!cd || !cd->fops) {
return -ENXIO; /* No such device */
}
fp->f_op = cd->fops;
/* Call the device's own open if it has one */
if (fp->f_op->open) {
return fp->f_op->open(ip, fp);
}
return 0;
}
/* ================================================================== */
/* devfs_init — create /dev and populate device nodes */
/* ================================================================== */
void devfs_init()
{
if (!g_root_dentry || !g_root_dentry->d_inode) {
printk(KERN_ERR "[devfs] no root filesystem!\n");
return;
}
/* Create /dev directory — or reuse if initramfs already created it.
* Linux: devtmpfs similarly handles pre-existing directories. */
inode* root_ip = g_root_dentry->d_inode;
qstr dev_name{};
dev_name.name = "dev";
dev_name.len = 3;
dev_name.hash = full_name_hash("dev", 3);
dentry* dev_de = d_lookup(g_root_dentry, &dev_name);
if (dev_de) {
/* /dev already exists (from initramfs) — reuse it */
printk("[devfs] reusing existing /dev directory\n");
} else {
/* Create /dev from scratch */
if (!root_ip->i_op || !root_ip->i_op->mkdir) {
printk(KERN_ERR "[devfs] root has no mkdir!\n");
return;
}
dev_de = d_alloc(g_root_dentry, &dev_name);
if (!dev_de) {
printk(KERN_ERR "[devfs] failed to alloc /dev dentry\n");
return;
}
int err = root_ip->i_op->mkdir(root_ip, dev_de, S_IFDIR | 0755);
if (err) {
printk(KERN_ERR "[devfs] mkdir /dev failed: %d\n", err);
return;
}
}
/* Populate: /dev/console (5,1) */
devfs_create_node(dev_de, "console", 5, 1);
/* Populate: /dev/null (1,3) */
devfs_create_node(dev_de, "null", 1, 3);
/* Populate: /dev/zero (1,5) */
devfs_create_node(dev_de, "zero", 1, 5);
printk("[devfs] /dev populated (console, null, zero)\n");
}
|