Second Extended Filesystem (ext2) and other improvements (#33)
[lunaix-os.git] / lunaix-os / kernel / device / devfs.c
1 #include <lunaix/device.h>
2 #include <lunaix/fs/api.h>
3 #include <lunaix/fs/devfs.h>
4 #include <lunaix/spike.h>
5
6 #include <usr/lunaix/dirent_defs.h>
7
8 extern struct v_inode_ops devfs_inode_ops;
9 extern struct v_file_ops devfs_file_ops;
10
11 int
12 devfs_read(struct v_inode* inode, void* buffer, size_t len, size_t fpos)
13 {
14     assert(inode->data);
15
16     struct device* dev = resolve_device(inode->data);
17
18     if (!dev || !dev->ops.read) {
19         return ENOTSUP;
20     }
21
22     return dev->ops.read(dev, buffer, fpos, len);
23 }
24
25 int
26 devfs_write(struct v_inode* inode, void* buffer, size_t len, size_t fpos)
27 {
28     assert(inode->data);
29
30     struct device* dev = resolve_device(inode->data);
31
32     if (!dev || !dev->ops.write) {
33         return ENOTSUP;
34     }
35
36     return dev->ops.write(dev, buffer, fpos, len);
37 }
38
39 int
40 devfs_read_page(struct v_inode* inode, void* buffer, size_t fpos)
41 {
42     assert(inode->data);
43
44     struct device* dev = resolve_device(inode->data);
45
46     if (!dev || !dev->ops.read_page) {
47         return ENOTSUP;
48     }
49
50     return dev->ops.read_page(dev, buffer, fpos);
51 }
52
53 int
54 devfs_write_page(struct v_inode* inode, void* buffer, size_t fpos)
55 {
56     assert(inode->data);
57
58     struct device* dev = resolve_device(inode->data);
59
60     if (!dev || !dev->ops.read_page) {
61         return ENOTSUP;
62     }
63
64     return dev->ops.read_page(dev, buffer, fpos);
65 }
66
67 int
68 devfs_get_itype(struct device_meta* dm)
69 {
70     int itype = VFS_IFDEV;
71
72     if (valid_device_subtype_ref(dm, DEV_CAT)) {
73         return VFS_IFDIR;
74     }
75
76     struct device* dev = resolve_device(dm);
77     int dev_if = dev->dev_type & DEV_MSKIF;
78     
79     if (dev_if == DEV_IFVOL) {
80         itype |= VFS_IFVOLDEV;
81     }
82
83     // otherwise, the mapping is considered to be generic seq dev.
84     return itype;
85 }
86
87 int
88 devfs_get_dtype(struct device_meta* dev)
89 {
90     if (valid_device_subtype_ref(dev, DEV_CAT)) {
91         return DT_DIR;
92     }
93     return DT_FILE;
94 }
95
96 int
97 devfs_mknod(struct v_dnode* dnode, struct device_meta* dev)
98 {
99     assert(dev);
100
101     struct v_inode* devnod = vfs_i_find(dnode->super_block, dev->dev_uid);
102     if (!devnod) {
103         if ((devnod = vfs_i_alloc(dnode->super_block))) {
104             devnod->id = dev->dev_uid;
105             devnod->data = dev;
106             devnod->itype = devfs_get_itype(dev);
107
108             vfs_i_addhash(devnod);
109         } else {
110             return ENOMEM;
111         }
112     }
113
114     vfs_assign_inode(dnode, devnod);
115     return 0;
116 }
117
118 int
119 devfs_dirlookup(struct v_inode* this, struct v_dnode* dnode)
120 {
121     void* data = this->data;
122     struct device_meta* rootdev = resolve_device_meta(data);
123
124     if (data && !rootdev) {
125         return ENOTDIR;
126     }
127
128     struct device_meta* dev =
129       device_getbyhname(rootdev, &dnode->name);
130     
131     if (!dev) {
132         return ENOENT;
133     }
134
135     return devfs_mknod(dnode, dev);
136 }
137
138 int
139 devfs_readdir(struct v_file* file, struct dir_context* dctx)
140 {
141     void* data = file->inode->data;
142     struct device_meta* rootdev = resolve_device_meta(data);
143
144     if (data && !rootdev) {
145         return ENOTDIR;
146     }
147
148     if (fsapi_handle_pseudo_dirent(file, dctx)) {
149         return 1;
150     }
151
152     struct device_meta* dev =
153       device_getbyoffset(rootdev, file->f_pos - 2);
154     
155     if (!dev) {
156         return 0;
157     }
158
159     dctx->read_complete_callback(
160       dctx, dev->name.value, dev->name.len, devfs_get_dtype(dev));
161     return 1;
162 }
163
164 void
165 devfs_init_inode(struct v_superblock* vsb, struct v_inode* inode)
166 {
167     inode->ops = &devfs_inode_ops;
168     inode->default_fops = &devfs_file_ops;
169 }
170
171 int
172 devfs_mount(struct v_superblock* vsb, struct v_dnode* mount_point)
173 {
174     vsb->ops.init_inode = devfs_init_inode;
175
176     struct v_inode* rootnod = vfs_i_alloc(vsb);
177
178     if (!rootnod) {
179         return ENOMEM;
180     }
181
182     rootnod->id = -1;
183     rootnod->itype = VFS_IFDIR;
184
185     vfs_i_addhash(rootnod);
186     vfs_assign_inode(mount_point, rootnod);
187
188     return 0;
189 }
190
191 int
192 devfs_unmount(struct v_superblock* vsb)
193 {
194     return 0;
195 }
196
197 void
198 devfs_init()
199 {
200     struct filesystem* fs;
201     fs = fsapi_fs_declare("devfs", FSTYPE_PSEUDO);
202     
203     fsapi_fs_set_mntops(fs, devfs_mount, devfs_unmount);
204     fsapi_fs_finalise(fs);
205 }
206 EXPORT_FILE_SYSTEM(devfs, devfs_init);
207
208 struct v_inode_ops devfs_inode_ops = { .dir_lookup = devfs_dirlookup,
209                                        .open = default_inode_open,
210                                        .mkdir = default_inode_mkdir,
211                                        .rmdir = default_inode_rmdir };
212
213 struct v_file_ops devfs_file_ops = { .close = default_file_close,
214                                      .read = devfs_read,
215                                      .read_page = devfs_read_page,
216                                      .write = devfs_write,
217                                      .write_page = devfs_write_page,
218                                      .seek = default_file_seek,
219                                      .readdir = devfs_readdir };