feat: (devfs) a new filesystem for device exposure.
[lunaix-os.git] / lunaix-os / kernel / device / devfs.c
diff --git a/lunaix-os/kernel/device/devfs.c b/lunaix-os/kernel/device/devfs.c
new file mode 100644 (file)
index 0000000..73abac7
--- /dev/null
@@ -0,0 +1,147 @@
+#include <lunaix/device.h>
+#include <lunaix/fs.h>
+#include <lunaix/fs/devfs.h>
+#include <lunaix/spike.h>
+
+extern struct v_inode_ops devfs_inode_ops;
+extern struct v_file_ops devfs_file_ops;
+
+int
+devfs_read(struct v_inode* inode, void* buffer, size_t len, size_t fpos)
+{
+    assert(inode->data);
+
+    struct device* dev = (struct device*)inode->data;
+
+    if (!dev->read) {
+        return ENOTSUP;
+    }
+
+    return dev->read(dev, buffer, fpos, len);
+}
+
+int
+devfs_write(struct v_inode* inode, void* buffer, size_t len, size_t fpos)
+{
+    assert(inode->data);
+
+    struct device* dev = (struct device*)inode->data;
+
+    if (!dev->write) {
+        return ENOTSUP;
+    }
+
+    return dev->write(dev, buffer, fpos, len);
+}
+
+int
+devfs_get_itype(struct device* dev)
+{
+    int itype = VFS_IFFILE;
+    int dev_if = dev->dev_type & DEV_MSKIF;
+    if (dev_if == DEV_IFCAT) {
+        itype = VFS_IFDIR;
+    } else if (dev_if == DEV_IFVOL) {
+        itype |= VFS_IFVOLDEV;
+    } else {
+        itype |= VFS_IFSEQDEV;
+    }
+    return itype;
+}
+
+int
+devfs_mknod(struct v_dnode* dnode, struct device* dev)
+{
+    assert(dev);
+
+    struct v_inode* devnod = vfs_i_find(dnode->super_block, dev->dev_id);
+    if (!devnod) {
+        if ((devnod = vfs_i_alloc(dnode->super_block))) {
+            devnod->id = dev->dev_id;
+            devnod->data = dev;
+            devnod->itype = devfs_get_itype(dev);
+
+            vfs_i_addhash(devnod);
+        } else {
+            return ENOMEM;
+        }
+    }
+
+    vfs_assign_inode(dnode, devnod);
+    return 0;
+}
+
+int
+devfs_dirlookup(struct v_inode* this, struct v_dnode* dnode)
+{
+    struct device* dev = device_getbyname(this->data, &dnode->name);
+    if (!dev) {
+        return ENOENT;
+    }
+    return devfs_mknod(dnode, dev);
+}
+
+int
+devfs_readdir(struct v_file* file, struct dir_context* dctx)
+{
+    struct device* dev = device_getbyoffset(file->inode->data, dctx->index);
+    if (!dev) {
+        return 0;
+    }
+    dctx->read_complete_callback(
+      dctx, dev->name.value, dev->name.len, devfs_get_itype(dev));
+    return 1;
+}
+
+void
+devfs_init_inode(struct v_superblock* vsb, struct v_inode* inode)
+{
+    inode->ops = &devfs_inode_ops;
+    inode->default_fops = &devfs_file_ops;
+}
+
+int
+devfs_mount(struct v_superblock* vsb, struct v_dnode* mount_point)
+{
+    vsb->ops.init_inode = devfs_init_inode;
+
+    struct v_inode* rootnod = vfs_i_alloc(vsb);
+
+    if (!rootnod) {
+        return ENOMEM;
+    }
+
+    rootnod->id = -1;
+    rootnod->itype = VFS_IFDIR;
+
+    vfs_i_addhash(rootnod);
+    vfs_assign_inode(mount_point, rootnod);
+
+    return 0;
+}
+
+int
+devfs_unmount(struct v_superblock* vsb)
+{
+    return 0;
+}
+
+void
+devfs_init()
+{
+    struct filesystem* fs = fsm_new_fs("devfs", 5);
+    fsm_register(fs);
+    fs->mount = devfs_mount;
+    fs->unmount = devfs_unmount;
+}
+
+struct v_inode_ops devfs_inode_ops = { .dir_lookup = devfs_dirlookup,
+                                       .open = default_inode_open,
+                                       .mkdir = default_inode_mkdir,
+                                       .rmdir = default_inode_rmdir };
+
+struct v_file_ops devfs_file_ops = { .close = default_file_close,
+                                     .read = devfs_read,
+                                     .write = devfs_write,
+                                     .seek = default_file_seek,
+                                     .readdir = devfs_readdir };
\ No newline at end of file