#define FS_BLOCKDEVICE 0x04
#define FS_SYMLINK 0x05
+/* poll() event flags — shared between kernel VFS and syscall layer */
+#define VFS_POLL_IN 0x0001
+#define VFS_POLL_OUT 0x0004
+#define VFS_POLL_ERR 0x0008
+#define VFS_POLL_HUP 0x0010
+
typedef struct fs_node {
char name[128];
uint32_t flags;
int (*readdir)(struct fs_node* node, uint32_t* inout_index, void* buf, uint32_t buf_len);
int (*ioctl)(struct fs_node* node, uint32_t cmd, void* arg);
uintptr_t (*mmap)(struct fs_node* node, uintptr_t addr, uint32_t length, uint32_t prot, uint32_t offset);
+ int (*poll)(struct fs_node* node, int events);
// Directory mutation operations (called on the parent directory node)
int (*create)(struct fs_node* dir, const char* name, uint32_t flags, struct fs_node** out);
return count;
}
+static int kbd_dev_poll(fs_node_t* node, int events) {
+ (void)node;
+ int revents = 0;
+ if (events & VFS_POLL_IN) {
+ uintptr_t flags = spin_lock_irqsave(&scan_lock);
+ if (scan_head != scan_tail) revents |= VFS_POLL_IN;
+ spin_unlock_irqrestore(&scan_lock, flags);
+ }
+ if (events & VFS_POLL_OUT) revents |= VFS_POLL_OUT;
+ return revents;
+}
+
static fs_node_t g_dev_kbd_node;
void keyboard_init(void) {
g_dev_kbd_node.flags = FS_CHARDEVICE;
g_dev_kbd_node.inode = 21;
g_dev_kbd_node.read = &kbd_dev_read;
+ g_dev_kbd_node.poll = &kbd_dev_poll;
devfs_register_device(&g_dev_kbd_node);
}
return size;
}
+static int dev_null_poll(fs_node_t* node, int events) {
+ (void)node;
+ int revents = 0;
+ if (events & VFS_POLL_IN) revents |= VFS_POLL_IN | VFS_POLL_HUP;
+ if (events & VFS_POLL_OUT) revents |= VFS_POLL_OUT;
+ return revents;
+}
+
+static int dev_always_ready_poll(fs_node_t* node, int events) {
+ (void)node;
+ int revents = 0;
+ if (events & VFS_POLL_IN) revents |= VFS_POLL_IN;
+ if (events & VFS_POLL_OUT) revents |= VFS_POLL_OUT;
+ return revents;
+}
+
static struct fs_node* devfs_finddir_impl(struct fs_node* node, const char* name) {
(void)node;
if (!name || name[0] == 0) return 0;
g_dev_null.length = 0;
g_dev_null.read = &dev_null_read;
g_dev_null.write = &dev_null_write;
+ g_dev_null.poll = &dev_null_poll;
g_dev_null.open = 0;
g_dev_null.close = 0;
g_dev_null.finddir = 0;
g_dev_zero.inode = 7;
g_dev_zero.read = &dev_zero_read;
g_dev_zero.write = &dev_zero_write;
+ g_dev_zero.poll = &dev_always_ready_poll;
memset(&g_dev_random, 0, sizeof(g_dev_random));
strcpy(g_dev_random.name, "random");
g_dev_random.inode = 8;
g_dev_random.read = &dev_random_read;
g_dev_random.write = &dev_random_write;
+ g_dev_random.poll = &dev_always_ready_poll;
memset(&g_dev_urandom, 0, sizeof(g_dev_urandom));
strcpy(g_dev_urandom.name, "urandom");
g_dev_urandom.inode = 9;
g_dev_urandom.read = &dev_random_read;
g_dev_urandom.write = &dev_random_write;
+ g_dev_urandom.poll = &dev_always_ready_poll;
}
fs_node_t* devfs_create_root(void) {
static uint32_t pty_slave_write_fn(fs_node_t* node, uint32_t offset, uint32_t size, const uint8_t* buffer);
static int pty_slave_ioctl_fn(fs_node_t* node, uint32_t cmd, void* arg);
+static int pty_master_poll_fn(fs_node_t* node, int events) {
+ int idx = pty_ino_to_idx(node->inode);
+ if (idx < 0) return 0;
+ int revents = 0;
+ if ((events & VFS_POLL_IN) && pty_master_can_read_idx(idx)) revents |= VFS_POLL_IN;
+ if ((events & VFS_POLL_OUT) && pty_master_can_write_idx(idx)) revents |= VFS_POLL_OUT;
+ return revents;
+}
+
+static int pty_slave_poll_fn(fs_node_t* node, int events) {
+ int idx = pty_ino_to_idx(node->inode);
+ if (idx < 0) return 0;
+ int revents = 0;
+ if ((events & VFS_POLL_IN) && pty_slave_can_read_idx(idx)) revents |= VFS_POLL_IN;
+ if ((events & VFS_POLL_OUT) && pty_slave_can_write_idx(idx)) revents |= VFS_POLL_OUT;
+ return revents;
+}
+
static void pty_init_pair(int idx) {
struct pty_pair* p = &g_ptys[idx];
memset(p, 0, sizeof(*p));
p->master_node.inode = PTY_MASTER_INO_BASE + (uint32_t)idx;
p->master_node.read = &pty_master_read_fn;
p->master_node.write = &pty_master_write_fn;
+ p->master_node.poll = &pty_master_poll_fn;
memset(&p->slave_node, 0, sizeof(p->slave_node));
name[0] = '0' + (char)idx;
p->slave_node.read = &pty_slave_read_fn;
p->slave_node.write = &pty_slave_write_fn;
p->slave_node.ioctl = &pty_slave_ioctl_fn;
+ p->slave_node.poll = &pty_slave_poll_fn;
}
/* --- DevFS pts directory callbacks --- */
g_dev_ptmx_node.inode = PTY_MASTER_INO_BASE;
g_dev_ptmx_node.read = &pty_ptmx_read_fn;
g_dev_ptmx_node.write = &pty_ptmx_write_fn;
+ g_dev_ptmx_node.poll = &pty_master_poll_fn;
devfs_register_device(&g_dev_ptmx_node);
/* Register /dev/pts directory */
if (!kfds) return -EINVAL;
if (nfds > 64U) return -EINVAL;
- // timeout semantics (minimal):
- // - timeout == 0 : non-blocking
- // - timeout < 0 : block forever
- // - timeout > 0 : treated as "ticks" (best-effort)
extern uint32_t get_tick_count(void);
uint32_t start_tick = get_tick_count();
struct file* f = fd_get(fd);
if (!f || !f->node) {
kfds[i].revents |= POLLERR;
+ ready++;
continue;
}
fs_node_t* n = f->node;
- // Pipes (identified by node name prefix).
- if (n->name[0] == 'p' && n->name[1] == 'i' && n->name[2] == 'p' && n->name[3] == 'e' && n->name[4] == ':') {
- struct pipe_node* pn = (struct pipe_node*)n;
- struct pipe_state* ps = pn->ps;
- if (!ps) {
- kfds[i].revents |= POLLERR;
- } else if (pn->is_read_end) {
- if ((kfds[i].events & POLLIN) && (ps->count > 0 || ps->writers == 0)) {
- kfds[i].revents |= POLLIN;
- if (ps->writers == 0) kfds[i].revents |= POLLHUP;
- }
- } else {
- if (ps->readers == 0) {
- if (kfds[i].events & POLLOUT) kfds[i].revents |= POLLERR;
- } else {
- uint32_t free = ps->cap - ps->count;
- if ((kfds[i].events & POLLOUT) && free > 0) {
- kfds[i].revents |= POLLOUT;
- }
- }
- }
- } else if (n->flags == FS_CHARDEVICE) {
- // devfs devices: inode 2=/dev/null, 3=/dev/tty
- if (n->inode == 2) {
- if (kfds[i].events & POLLIN) kfds[i].revents |= POLLIN | POLLHUP;
- if (kfds[i].events & POLLOUT) kfds[i].revents |= POLLOUT;
- } else if (n->inode == 3) {
- if ((kfds[i].events & POLLIN) && tty_can_read()) kfds[i].revents |= POLLIN;
- if ((kfds[i].events & POLLOUT) && tty_can_write()) kfds[i].revents |= POLLOUT;
- } else if (pty_is_master_ino(n->inode)) {
- int pi = pty_ino_to_idx(n->inode);
- if ((kfds[i].events & POLLIN) && pty_master_can_read_idx(pi)) kfds[i].revents |= POLLIN;
- if ((kfds[i].events & POLLOUT) && pty_master_can_write_idx(pi)) kfds[i].revents |= POLLOUT;
- } else if (pty_is_slave_ino(n->inode)) {
- int pi = pty_ino_to_idx(n->inode);
- if ((kfds[i].events & POLLIN) && pty_slave_can_read_idx(pi)) kfds[i].revents |= POLLIN;
- if ((kfds[i].events & POLLOUT) && pty_slave_can_write_idx(pi)) kfds[i].revents |= POLLOUT;
- }
+ if (n->poll) {
+ int vfs_events = 0;
+ if (kfds[i].events & POLLIN) vfs_events |= VFS_POLL_IN;
+ if (kfds[i].events & POLLOUT) vfs_events |= VFS_POLL_OUT;
+
+ int vfs_rev = n->poll(n, vfs_events);
+
+ if (vfs_rev & VFS_POLL_IN) kfds[i].revents |= POLLIN;
+ if (vfs_rev & VFS_POLL_OUT) kfds[i].revents |= POLLOUT;
+ if (vfs_rev & VFS_POLL_ERR) kfds[i].revents |= POLLERR;
+ if (vfs_rev & VFS_POLL_HUP) kfds[i].revents |= POLLHUP;
} else {
- // Regular files are always readable/writable (best-effort).
- if (kfds[i].events & POLLIN) kfds[i].revents |= POLLIN;
+ if (kfds[i].events & POLLIN) kfds[i].revents |= POLLIN;
if (kfds[i].events & POLLOUT) kfds[i].revents |= POLLOUT;
}
}
}
+static int pipe_poll(fs_node_t* n, int events) {
+ struct pipe_node* pn = (struct pipe_node*)n;
+ if (!pn || !pn->ps) return VFS_POLL_ERR;
+ struct pipe_state* ps = pn->ps;
+ int revents = 0;
+ if (pn->is_read_end) {
+ if ((events & VFS_POLL_IN) && (ps->count > 0 || ps->writers == 0)) {
+ revents |= VFS_POLL_IN;
+ if (ps->writers == 0) revents |= VFS_POLL_HUP;
+ }
+ } else {
+ if (ps->readers == 0) {
+ if (events & VFS_POLL_OUT) revents |= VFS_POLL_ERR;
+ } else {
+ uint32_t free = ps->cap - ps->count;
+ if ((events & VFS_POLL_OUT) && free > 0) revents |= VFS_POLL_OUT;
+ }
+ }
+ return revents;
+}
+
static int pipe_node_create(struct pipe_state* ps, int is_read_end, fs_node_t** out_node) {
if (!ps || !out_node) return -EINVAL;
struct pipe_node* pn = (struct pipe_node*)kmalloc(sizeof(*pn));
pn->node.open = NULL;
pn->node.finddir = NULL;
pn->node.close = pipe_close;
+ pn->node.poll = pipe_poll;
if (pn->is_read_end) {
strcpy(pn->node.name, "pipe:r");
pn->node.read = pipe_read;
if (!f || !f->node) return -EBADF;
int nonblock = (f->flags & O_NONBLOCK) ? 1 : 0;
- if (nonblock) {
- // Non-blocking pipes: if empty but writers exist, return -EAGAIN.
- if (f->node->name[0] == 'p' && f->node->name[1] == 'i' && f->node->name[2] == 'p' && f->node->name[3] == 'e' && f->node->name[4] == ':') {
- struct pipe_node* pn = (struct pipe_node*)f->node;
- struct pipe_state* ps = pn ? pn->ps : 0;
- if (pn && ps && pn->is_read_end && ps->count == 0 && ps->writers != 0) {
- return -EAGAIN;
- }
- }
-
- // Non-blocking char devices (tty/pty) need special handling, since devfs read blocks.
- if (f->node->flags == FS_CHARDEVICE) {
- if (f->node->inode == 3) {
- if (!tty_can_read()) return -EAGAIN;
- } else if (pty_is_master_ino(f->node->inode)) {
- if (!pty_master_can_read_idx(pty_ino_to_idx(f->node->inode))) return -EAGAIN;
- } else if (pty_is_slave_ino(f->node->inode)) {
- if (!pty_slave_can_read_idx(pty_ino_to_idx(f->node->inode))) return -EAGAIN;
- }
- }
+ if (nonblock && f->node->poll) {
+ int rev = f->node->poll(f->node, VFS_POLL_IN);
+ if (!(rev & (VFS_POLL_IN | VFS_POLL_ERR | VFS_POLL_HUP)))
+ return -EAGAIN;
}
if (f->node->flags == FS_CHARDEVICE) {
if (!f || !f->node) return -EBADF;
int nonblock = (f->flags & O_NONBLOCK) ? 1 : 0;
- if (nonblock) {
- // Non-blocking pipe write: if full but readers exist, return -EAGAIN.
- if (f->node->name[0] == 'p' && f->node->name[1] == 'i' && f->node->name[2] == 'p' && f->node->name[3] == 'e' && f->node->name[4] == ':') {
- struct pipe_node* pn = (struct pipe_node*)f->node;
- struct pipe_state* ps = pn ? pn->ps : 0;
- if (pn && ps && !pn->is_read_end) {
- if (ps->readers != 0 && (ps->cap - ps->count) == 0) {
- return -EAGAIN;
- }
- }
- }
+ if (nonblock && f->node->poll) {
+ int rev = f->node->poll(f->node, VFS_POLL_OUT);
+ if (!(rev & (VFS_POLL_OUT | VFS_POLL_ERR)))
+ return -EAGAIN;
}
if (!f->node->write) return -ESPIPE;
if (((f->node->flags & FS_FILE) == 0) && f->node->flags != FS_CHARDEVICE) return -ESPIPE;
return tty_ioctl(cmd, arg);
}
+static int tty_devfs_poll(fs_node_t* node, int events) {
+ (void)node;
+ int revents = 0;
+ if ((events & VFS_POLL_IN) && tty_can_read()) revents |= VFS_POLL_IN;
+ if ((events & VFS_POLL_OUT) && tty_can_write()) revents |= VFS_POLL_OUT;
+ return revents;
+}
+
void tty_init(void) {
spinlock_init(&tty_lock);
line_len = 0;
g_dev_console_node.read = &tty_devfs_read;
g_dev_console_node.write = &tty_devfs_write;
g_dev_console_node.ioctl = &tty_devfs_ioctl;
+ g_dev_console_node.poll = &tty_devfs_poll;
devfs_register_device(&g_dev_console_node);
/* Register /dev/tty */
g_dev_tty_node.read = &tty_devfs_read;
g_dev_tty_node.write = &tty_devfs_write;
g_dev_tty_node.ioctl = &tty_devfs_ioctl;
+ g_dev_tty_node.poll = &tty_devfs_poll;
devfs_register_device(&g_dev_tty_node);
}