Viewing: devfs.c
📄 devfs.c (Read Only) ⬅ To go back
#include "devfs.h"

#include "errno.h"
#include "utils.h"

extern uint32_t get_tick_count(void);

struct devfs_root {
    fs_node_t vfs;
};

static struct devfs_root g_dev_root;
static fs_node_t g_dev_null;
static fs_node_t g_dev_zero;
static fs_node_t g_dev_random;
static fs_node_t g_dev_urandom;
static uint32_t g_devfs_inited = 0;

static struct fs_node* devfs_finddir_impl(struct fs_node* node, const char* name);
static int devfs_readdir_impl(struct fs_node* node, uint32_t* inout_index, void* buf, uint32_t buf_len);
static uint32_t dev_null_read(fs_node_t* node, uint32_t offset, uint32_t size, uint8_t* buffer);
static uint32_t dev_null_write(fs_node_t* node, uint32_t offset, uint32_t size, const uint8_t* buffer);
static uint32_t dev_zero_read(fs_node_t* node, uint32_t offset, uint32_t size, uint8_t* buffer);
static uint32_t dev_zero_write(fs_node_t* node, uint32_t offset, uint32_t size, const uint8_t* buffer);
static uint32_t dev_random_read(fs_node_t* node, uint32_t offset, uint32_t size, uint8_t* buffer);
static uint32_t dev_random_write(fs_node_t* node, uint32_t offset, uint32_t size, const uint8_t* buffer);
static int dev_null_poll(fs_node_t* node, int events);
static int dev_always_ready_poll(fs_node_t* node, int events);

static const struct file_operations devfs_dir_ops = {0};

static const struct inode_operations devfs_dir_iops = {
    .lookup  = devfs_finddir_impl,
    .readdir = devfs_readdir_impl,
};

static const struct file_operations dev_null_ops = {
    .read  = dev_null_read,
    .write = dev_null_write,
    .poll  = dev_null_poll,
};

static const struct file_operations dev_zero_ops = {
    .read  = dev_zero_read,
    .write = dev_zero_write,
    .poll  = dev_always_ready_poll,
};

static const struct file_operations dev_random_ops = {
    .read  = dev_random_read,
    .write = dev_random_write,
    .poll  = dev_always_ready_poll,
};

/* --- Device registry --- */
static fs_node_t* g_registered[DEVFS_MAX_DEVICES];
static int g_registered_count = 0;

int devfs_register_device(fs_node_t *node) {
    if (!node) return -1;
    if (g_registered_count >= DEVFS_MAX_DEVICES) return -1;
    g_registered[g_registered_count++] = node;
    return 0;
}

static uint32_t prng_state = 0x12345678;

static uint32_t dev_null_read(fs_node_t* node, uint32_t offset, uint32_t size, uint8_t* buffer) {
    (void)node;
    (void)offset;
    (void)size;
    (void)buffer;
    return 0;
}

static uint32_t dev_null_write(fs_node_t* node, uint32_t offset, uint32_t size, const uint8_t* buffer) {
    (void)node;
    (void)offset;
    (void)buffer;
    return size;
}

static uint32_t dev_zero_read(fs_node_t* node, uint32_t offset, uint32_t size, uint8_t* buffer) {
    (void)node;
    (void)offset;
    if (buffer && size > 0)
        memset(buffer, 0, size);
    return size;
}

static uint32_t dev_zero_write(fs_node_t* node, uint32_t offset, uint32_t size, const uint8_t* buffer) {
    (void)node;
    (void)offset;
    (void)buffer;
    return size;
}

static uint32_t prng_next(void) {
    uint32_t s = prng_state;
    s ^= s << 13;
    s ^= s >> 17;
    s ^= s << 5;
    prng_state = s;
    return s;
}

static uint32_t dev_random_read(fs_node_t* node, uint32_t offset, uint32_t size, uint8_t* buffer) {
    (void)node;
    (void)offset;
    if (!buffer || size == 0) return 0;
    prng_state ^= get_tick_count();
    for (uint32_t i = 0; i < size; i++) {
        if ((i & 3) == 0) {
            uint32_t r = prng_next();
            buffer[i] = (uint8_t)(r & 0xFF);
        } else {
            buffer[i] = (uint8_t)((prng_next() >> ((i & 3) * 8)) & 0xFF);
        }
    }
    return size;
}

static uint32_t dev_random_write(fs_node_t* node, uint32_t offset, uint32_t size, const uint8_t* buffer) {
    (void)node;
    (void)offset;
    if (buffer && size >= 4) {
        uint32_t seed = 0;
        memcpy(&seed, buffer, 4);
        prng_state ^= seed;
    }
    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;

    if (strcmp(name, "null") == 0) return &g_dev_null;
    if (strcmp(name, "zero") == 0) return &g_dev_zero;
    if (strcmp(name, "random") == 0) return &g_dev_random;
    if (strcmp(name, "urandom") == 0) return &g_dev_urandom;

    for (int i = 0; i < g_registered_count; i++) {
        if (strcmp(g_registered[i]->name, name) == 0)
            return g_registered[i];
    }
    return 0;
}

static int devfs_readdir_impl(struct fs_node* node, uint32_t* inout_index, void* buf, uint32_t buf_len) {
    (void)node;
    if (!inout_index || !buf) return -1;
    if (buf_len < sizeof(struct vfs_dirent)) return -1;

    /* Built-in devices (owned by devfs) */
    static const struct { const char* name; uint32_t ino; uint8_t type; } builtins[] = {
        { "null",    2,  FS_CHARDEVICE },
        { "zero",    7,  FS_CHARDEVICE },
        { "random",  8,  FS_CHARDEVICE },
        { "urandom", 9,  FS_CHARDEVICE },
    };
    enum { NBUILTINS = 4 };

    uint32_t total = (uint32_t)(NBUILTINS + g_registered_count);
    uint32_t idx = *inout_index;
    uint32_t cap = buf_len / (uint32_t)sizeof(struct vfs_dirent);
    struct vfs_dirent* ents = (struct vfs_dirent*)buf;
    uint32_t written = 0;

    while (written < cap) {
        struct vfs_dirent e;
        memset(&e, 0, sizeof(e));

        if (idx == 0) {
            e.d_ino = 1; e.d_type = FS_DIRECTORY; strcpy(e.d_name, ".");
        } else if (idx == 1) {
            e.d_ino = 1; e.d_type = FS_DIRECTORY; strcpy(e.d_name, "..");
        } else {
            uint32_t di = idx - 2;
            if (di >= total) break;
            if (di < NBUILTINS) {
                e.d_ino = builtins[di].ino;
                e.d_type = builtins[di].type;
                strcpy(e.d_name, builtins[di].name);
            } else {
                fs_node_t* rn = g_registered[di - NBUILTINS];
                e.d_ino = rn->inode;
                e.d_type = (uint8_t)rn->flags;
                strcpy(e.d_name, rn->name);
            }
        }

        e.d_reclen = (uint16_t)sizeof(e);
        ents[written] = e;
        written++;
        idx++;
    }

    *inout_index = idx;
    return (int)(written * (uint32_t)sizeof(struct vfs_dirent));
}

static void devfs_init_once(void) {
    if (g_devfs_inited) return;
    g_devfs_inited = 1;

    memset(&g_dev_root, 0, sizeof(g_dev_root));
    strcpy(g_dev_root.vfs.name, "dev");
    g_dev_root.vfs.flags = FS_DIRECTORY;
    g_dev_root.vfs.inode = 1;
    g_dev_root.vfs.length = 0;
    g_dev_root.vfs.f_ops = &devfs_dir_ops;
    g_dev_root.vfs.i_ops = &devfs_dir_iops;

    memset(&g_dev_null, 0, sizeof(g_dev_null));
    strcpy(g_dev_null.name, "null");
    g_dev_null.flags = FS_CHARDEVICE;
    g_dev_null.inode = 2;
    g_dev_null.f_ops = &dev_null_ops;

    memset(&g_dev_zero, 0, sizeof(g_dev_zero));
    strcpy(g_dev_zero.name, "zero");
    g_dev_zero.flags = FS_CHARDEVICE;
    g_dev_zero.inode = 7;
    g_dev_zero.f_ops = &dev_zero_ops;

    memset(&g_dev_random, 0, sizeof(g_dev_random));
    strcpy(g_dev_random.name, "random");
    g_dev_random.flags = FS_CHARDEVICE;
    g_dev_random.inode = 8;
    g_dev_random.f_ops = &dev_random_ops;

    memset(&g_dev_urandom, 0, sizeof(g_dev_urandom));
    strcpy(g_dev_urandom.name, "urandom");
    g_dev_urandom.flags = FS_CHARDEVICE;
    g_dev_urandom.inode = 9;
    g_dev_urandom.f_ops = &dev_random_ops;
}

fs_node_t* devfs_create_root(void) {
    devfs_init_once();
    return &g_dev_root.vfs;
}