1 /*
2  * Copyright (c) 2006-2021, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2018-02-11     Bernard      Ignore O_CREAT flag in open.
9  */
10 #include <rthw.h>
11 #include <rtthread.h>
12 #include <rtdevice.h>
13 
14 #include <dfs.h>
15 #include <dfs_fs.h>
16 #include <dfs_file.h>
17 
18 #include "devfs.h"
19 
20 struct device_dirent
21 {
22     rt_device_t *devices;
23     rt_uint16_t read_index;
24     rt_uint16_t device_count;
25 };
26 
dfs_device_fs_mount(struct dfs_filesystem * fs,unsigned long rwflag,const void * data)27 int dfs_device_fs_mount(struct dfs_filesystem *fs, unsigned long rwflag, const void *data)
28 {
29     return RT_EOK;
30 }
31 
dfs_device_fs_statfs(struct dfs_filesystem * fs,struct statfs * buf)32 int dfs_device_fs_statfs(struct dfs_filesystem *fs, struct statfs *buf)
33 {
34     buf->f_bsize  = 512;
35     buf->f_blocks = 2048 * 64; // 64M
36     buf->f_bfree  = buf->f_blocks;
37     buf->f_bavail = buf->f_bfree;
38 
39     return RT_EOK;
40 }
41 
dfs_device_fs_ioctl(struct dfs_file * file,int cmd,void * args)42 int dfs_device_fs_ioctl(struct dfs_file *file, int cmd, void *args)
43 {
44     rt_err_t result;
45     rt_device_t dev_id;
46 
47     RT_ASSERT(file != RT_NULL);
48 
49     /* get device handler */
50     dev_id = (rt_device_t)file->vnode->data;
51     RT_ASSERT(dev_id != RT_NULL);
52 
53     if ((file->vnode->path[0] == '/') && (file->vnode->path[1] == '\0'))
54         return -RT_ENOSYS;
55 
56     /* close device handler */
57     result = rt_device_control(dev_id, cmd, args);
58     if (result == RT_EOK)
59         return RT_EOK;
60 
61     return result;
62 }
63 
dfs_device_fs_read(struct dfs_file * file,void * buf,size_t count)64 ssize_t dfs_device_fs_read(struct dfs_file *file, void *buf, size_t count)
65 {
66     int result;
67     rt_device_t dev_id;
68 
69     RT_ASSERT(file != RT_NULL);
70 
71     /* get device handler */
72     dev_id = (rt_device_t)file->vnode->data;
73     RT_ASSERT(dev_id != RT_NULL);
74 
75     if ((file->vnode->path[0] == '/') && (file->vnode->path[1] == '\0'))
76         return -RT_ENOSYS;
77 
78     /* read device data */
79     result = rt_device_read(dev_id, file->pos, buf, count);
80     file->pos += result;
81 
82     return result;
83 }
84 
dfs_device_fs_write(struct dfs_file * file,const void * buf,size_t count)85 ssize_t dfs_device_fs_write(struct dfs_file *file, const void *buf, size_t count)
86 {
87     int result;
88     rt_device_t dev_id;
89 
90     RT_ASSERT(file != RT_NULL);
91 
92     /* get device handler */
93     dev_id = (rt_device_t)file->vnode->data;
94     RT_ASSERT(dev_id != RT_NULL);
95 
96     if ((file->vnode->path[0] == '/') && (file->vnode->path[1] == '\0'))
97         return -RT_ENOSYS;
98 
99     /* read device data */
100     result = rt_device_write(dev_id, file->pos, buf, count);
101     file->pos += result;
102 
103     return result;
104 }
105 
dfs_device_fs_close(struct dfs_file * file)106 int dfs_device_fs_close(struct dfs_file *file)
107 {
108     rt_err_t result;
109     rt_device_t dev_id;
110 
111     RT_ASSERT(file != RT_NULL);
112     RT_ASSERT(file->vnode->ref_count > 0);
113 
114     if (file->vnode->ref_count > 1)
115     {
116         return 0;
117     }
118 
119     if (file->vnode->type == FT_DIRECTORY && (file->vnode->path[0] == '/') && (file->vnode->path[1] == '\0'))
120     {
121         struct device_dirent *root_dirent;
122 
123         root_dirent = (struct device_dirent *)file->vnode->data;
124         RT_ASSERT(root_dirent != RT_NULL);
125 
126         /* release dirent */
127         rt_free(root_dirent);
128         return RT_EOK;
129     }
130 
131     /* get device handler */
132     dev_id = (rt_device_t)file->vnode->data;
133     RT_ASSERT(dev_id != RT_NULL);
134 
135     /* close device handler */
136     result = rt_device_close(dev_id);
137     if (result == RT_EOK)
138     {
139         file->vnode->data = RT_NULL;
140 
141         return RT_EOK;
142     }
143 
144     return -EIO;
145 }
146 
dfs_device_fs_open(struct dfs_file * file)147 int dfs_device_fs_open(struct dfs_file *file)
148 {
149     rt_err_t result;
150     rt_device_t device;
151 
152     RT_ASSERT(file->vnode->ref_count > 0);
153     if (file->vnode->ref_count > 1)
154     {
155         file->pos = 0;
156         return 0;
157     }
158     /* open root directory */
159     if ((file->vnode->path[0] == '/') && (file->vnode->path[1] == '\0') &&
160         (file->flags & O_DIRECTORY))
161     {
162         struct rt_object *object;
163         struct rt_list_node *node;
164         struct rt_object_information *information;
165         struct device_dirent *root_dirent;
166         rt_uint32_t count = 0;
167 
168         /* lock scheduler */
169         rt_enter_critical();
170 
171         /* traverse device object */
172         information = rt_object_get_information(RT_Object_Class_Device);
173         RT_ASSERT(information != RT_NULL);
174         for (node = information->object_list.next; node != &(information->object_list); node = node->next)
175         {
176             count ++;
177         }
178         rt_exit_critical();
179 
180         root_dirent = (struct device_dirent *)rt_malloc(sizeof(struct device_dirent) +
181                       count * sizeof(rt_device_t));
182         if (root_dirent != RT_NULL)
183         {
184             /* lock scheduler */
185             rt_enter_critical();
186 
187             root_dirent->devices = (rt_device_t *)(root_dirent + 1);
188             root_dirent->read_index = 0;
189             root_dirent->device_count = count;
190             count = 0;
191             /* get all device node */
192             for (node = information->object_list.next; node != &(information->object_list); node = node->next)
193             {
194                 /* avoid memory write through */
195                 if (count == root_dirent->device_count)
196                 {
197                     rt_kprintf("warning: There are newly added devices that are not displayed!");
198                     break;
199                 }
200                 object = rt_list_entry(node, struct rt_object, list);
201                 root_dirent->devices[count] = (rt_device_t)object;
202                 count ++;
203             }
204             rt_exit_critical();
205         }
206 
207         /* set data */
208         file->vnode->data = root_dirent;
209 
210         return RT_EOK;
211     }
212 #ifdef RT_USING_DEV_BUS
213     else if (file->flags & O_CREAT)
214     {
215         if (!(file->flags & O_DIRECTORY))
216         {
217             return -ENOSYS;
218         }
219         /* regester bus device */
220         if (rt_device_bus_create(&file->vnode->path[1], 0) == RT_NULL)
221         {
222             return -EEXIST;
223         }
224     }
225 #endif
226 
227     device = rt_device_find(&file->vnode->path[1]);
228     if (device == RT_NULL)
229     {
230         return -ENODEV;
231     }
232 
233 #ifdef RT_USING_POSIX_DEVIO
234     if (device->fops)
235     {
236         /* use device fops */
237         file->vnode->fops = device->fops;
238         file->vnode->data = (void *)device;
239 
240         /* use fops */
241         if (file->vnode->fops->open)
242         {
243             result = file->vnode->fops->open(file);
244             if (result == RT_EOK || result == -RT_ENOSYS)
245             {
246                 file->vnode->type = FT_DEVICE;
247                 return 0;
248             }
249         }
250     }
251     else
252 #endif /* RT_USING_POSIX_DEVIO */
253     {
254         result = rt_device_open(device, RT_DEVICE_OFLAG_RDWR);
255         if (result == RT_EOK || result == -RT_ENOSYS)
256         {
257             file->vnode->data = device;
258             file->vnode->type = FT_DEVICE;
259             return RT_EOK;
260         }
261     }
262 
263     file->vnode->data = RT_NULL;
264     /* open device failed. */
265     return -EIO;
266 }
267 
dfs_device_fs_unlink(struct dfs_filesystem * fs,const char * path)268 int dfs_device_fs_unlink(struct dfs_filesystem *fs, const char *path)
269 {
270 #ifdef RT_USING_DEV_BUS
271     rt_device_t dev_id;
272 
273     dev_id = rt_device_find(&path[1]);
274     if (dev_id == RT_NULL)
275     {
276         return -1;
277     }
278     if (dev_id->type != RT_Device_Class_Bus)
279     {
280         return -1;
281     }
282     rt_device_bus_destroy(dev_id);
283 #endif
284     return RT_EOK;
285 }
286 
dfs_device_fs_stat(struct dfs_filesystem * fs,const char * path,struct stat * st)287 int dfs_device_fs_stat(struct dfs_filesystem *fs, const char *path, struct stat *st)
288 {
289     st->st_dev = (dev_t)((size_t)dfs_filesystem_lookup(fs->path));
290     /* stat root directory */
291     if ((path[0] == '/') && (path[1] == '\0'))
292     {
293         st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH |
294                       S_IWUSR | S_IWGRP | S_IWOTH;
295         st->st_mode &= ~S_IFREG;
296         st->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
297 
298         st->st_size  = 0;
299         st->st_mtime = 0;
300 
301         return RT_EOK;
302     }
303     else
304     {
305         rt_device_t dev_id;
306 
307         dev_id = rt_device_find(&path[1]);
308         if (dev_id != RT_NULL)
309         {
310             st->st_mode = S_IRUSR | S_IRGRP | S_IROTH |
311                           S_IWUSR | S_IWGRP | S_IWOTH;
312 
313             if (dev_id->type == RT_Device_Class_Char)
314                 st->st_mode |= S_IFCHR;
315             else if (dev_id->type == RT_Device_Class_Block)
316                 st->st_mode |= S_IFBLK;
317             else if (dev_id->type == RT_Device_Class_Pipe)
318                 st->st_mode |= S_IFIFO;
319             else if (dev_id->type == RT_Device_Class_Bus)
320                 st->st_mode |= S_IFDIR;
321             else
322                 st->st_mode |= S_IFREG;
323 
324             st->st_size  = 0;
325             st->st_mtime = 0;
326 
327             return RT_EOK;
328         }
329     }
330 
331     return -ENOENT;
332 }
333 
dfs_device_fs_getdents(struct dfs_file * file,struct dirent * dirp,uint32_t count)334 int dfs_device_fs_getdents(struct dfs_file *file, struct dirent *dirp, uint32_t count)
335 {
336     rt_uint32_t index;
337     rt_object_t object;
338     struct dirent *d;
339     struct device_dirent *root_dirent;
340 
341     root_dirent = (struct device_dirent *)file->vnode->data;
342     RT_ASSERT(root_dirent != RT_NULL);
343 
344     /* make integer count */
345     count = (count / sizeof(struct dirent));
346     if (count == 0)
347         return -EINVAL;
348 
349     for (index = 0; index < count && index + root_dirent->read_index < root_dirent->device_count;
350         index ++)
351     {
352         object = (rt_object_t)root_dirent->devices[root_dirent->read_index + index];
353 
354         d = dirp + index;
355         if ((((rt_device_t)object)->type) == RT_Device_Class_Bus)
356         {
357             d->d_type = DT_DIR;
358         }
359         else
360         {
361             d->d_type = DT_REG;
362         }
363         d->d_namlen = RT_NAME_MAX;
364         d->d_reclen = (rt_uint16_t)sizeof(struct dirent);
365         rt_strncpy(d->d_name, object->name, RT_NAME_MAX);
366     }
367 
368     root_dirent->read_index += index;
369 
370     return index * sizeof(struct dirent);
371 }
372 
dfs_device_fs_poll(struct dfs_file * fd,struct rt_pollreq * req)373 static int dfs_device_fs_poll(struct dfs_file *fd, struct rt_pollreq *req)
374 {
375     int mask = 0;
376 
377     return mask;
378 }
379 
380 static const struct dfs_file_ops _device_fops =
381 {
382     dfs_device_fs_open,
383     dfs_device_fs_close,
384     dfs_device_fs_ioctl,
385     dfs_device_fs_read,
386     dfs_device_fs_write,
387     RT_NULL,                    /* flush */
388     RT_NULL,                    /* lseek */
389     dfs_device_fs_getdents,
390     dfs_device_fs_poll,
391 };
392 
393 static const struct dfs_filesystem_ops _device_fs =
394 {
395     "devfs",
396     DFS_FS_FLAG_DEFAULT,
397     &_device_fops,
398     dfs_device_fs_mount,
399     RT_NULL, /*unmount*/
400     RT_NULL, /*mkfs*/
401     dfs_device_fs_statfs,
402     dfs_device_fs_unlink,
403     dfs_device_fs_stat,
404     RT_NULL, /*rename*/
405 };
406 
devfs_init(void)407 int devfs_init(void)
408 {
409     /* register device file system */
410     dfs_register(&_device_fs);
411 
412     return 0;
413 }
414