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 <rtdbg.h>
12 #include <rtdevice.h>
13 
14 #include <fcntl.h>
15 #include <errno.h>
16 #include <sys/unistd.h>
17 
18 #include <dfs.h>
19 #include <dfs_fs.h>
20 #include <dfs_file.h>
21 #include <dfs_dentry.h>
22 #include <dfs_mnt.h>
23 
dfs_devfs_open(struct dfs_file * file)24 static int dfs_devfs_open(struct dfs_file *file)
25 {
26     int ret = RT_EOK;
27 
28     RT_ASSERT(file != RT_NULL);
29     RT_ASSERT(file->vnode->ref_count > 0);
30 
31     if (file->vnode->ref_count > 1)
32     {
33         if (file->vnode->type == FT_DIRECTORY
34             && !(file->flags & O_DIRECTORY))
35         {
36             return -ENOENT;
37         }
38         file->fpos = 0;
39     }
40 
41     if (!S_ISDIR(file->vnode->mode))
42     {
43         rt_device_t device = RT_NULL;
44         struct dfs_dentry *de = file->dentry;
45         char *device_name = rt_malloc(DFS_PATH_MAX);
46 
47         if (!device_name)
48         {
49             return -ENOMEM;
50         }
51 
52         /* skip `/dev` */
53         rt_snprintf(device_name, DFS_PATH_MAX, "%s%s", de->mnt->fullpath + sizeof("/dev") - 1, de->pathname);
54 
55         device = rt_device_find(device_name + 1);
56         if (device)
57         {
58             file->vnode->data = device;
59 #ifdef RT_USING_POSIX_DEVIO
60             if (device->fops && device->fops->open)
61             {
62                 ret = device->fops->open(file);
63                 if (ret == RT_EOK || ret == -RT_ENOSYS)
64                 {
65                     ret = RT_EOK;
66                 }
67             }
68             else if (device->ops && file->vnode->ref_count == 1)
69 #else
70             if (device->ops && file->vnode->ref_count == 1)
71 #endif /* RT_USING_POSIX_DEVIO */
72             {
73                 ret = rt_device_open(device, RT_DEVICE_OFLAG_RDWR);
74                 if (ret == RT_EOK || ret == -RT_ENOSYS)
75                 {
76                     ret = RT_EOK;
77                 }
78             }
79         }
80         rt_free(device_name);
81     }
82 
83     return ret;
84 }
85 
dfs_devfs_close(struct dfs_file * file)86 static int dfs_devfs_close(struct dfs_file *file)
87 {
88     int ret = RT_EOK;
89     rt_device_t device;
90 
91     RT_ASSERT(file != RT_NULL);
92     RT_ASSERT(file->vnode->ref_count > 0);
93 
94     if (file->vnode && file->vnode->data)
95     {
96         /* get device handler */
97         device = (rt_device_t)file->vnode->data;
98 
99 #ifdef RT_USING_POSIX_DEVIO
100         if (device->fops && device->fops->close)
101         {
102             ret = device->fops->close(file);
103         }
104         else if (file->vnode->ref_count == 1)
105 #else
106         if (device->ops && file->vnode->ref_count == 1)
107 #endif /* RT_USING_POSIX_DEVIO */
108         {
109             /* close device handler */
110             ret = rt_device_close(device);
111         }
112     }
113 
114     return ret;
115 }
116 
_get_unit_shift(rt_device_t device)117 static rt_ubase_t _get_unit_shift(rt_device_t device)
118 {
119     rt_ubase_t shift = 0;
120 
121     /**
122      * transfer unit size from POSIX RW(in bytes) to rt_device_R/W
123      * (block size for blk device, otherwise in bytes).
124      */
125     if (device->type == RT_Device_Class_Block)
126     {
127         struct rt_device_blk_geometry geometry = {0};
128 
129         /* default to 512 */
130         shift = 9;
131         if (!rt_device_control(device, RT_DEVICE_CTRL_BLK_GETGEOME, &geometry))
132         {
133             shift = __rt_ffs(geometry.block_size) - 1;
134         }
135     }
136 
137     return shift;
138 }
139 
dfs_devfs_read(struct dfs_file * file,void * buf,size_t count,off_t * pos)140 static ssize_t dfs_devfs_read(struct dfs_file *file, void *buf, size_t count, off_t *pos)
141 {
142     ssize_t ret = -RT_EIO;
143     rt_device_t device;
144 
145     RT_ASSERT(file != RT_NULL);
146 
147     if (file->vnode && file->vnode->data)
148     {
149         /* get device handler */
150         device = (rt_device_t)file->vnode->data;
151 
152 #ifdef RT_USING_POSIX_DEVIO
153         if (device->fops && device->fops->read)
154         {
155             ret = device->fops->read(file, buf, count, pos);
156         }
157         else
158 #else
159         if (device->ops)
160 #endif /* RT_USING_POSIX_DEVIO */
161         {
162             rt_ubase_t shift = _get_unit_shift(device);
163 
164             ret = rt_device_read(device, *pos, buf, count >> shift);
165             if (ret > 0)
166             {
167                 ret <<= shift;
168                 *pos += ret;
169             }
170         }
171     }
172 
173     return ret;
174 }
175 
dfs_devfs_write(struct dfs_file * file,const void * buf,size_t count,off_t * pos)176 static ssize_t dfs_devfs_write(struct dfs_file *file, const void *buf, size_t count, off_t *pos)
177 {
178     ssize_t ret = -RT_EIO;
179     rt_device_t device;
180 
181     RT_ASSERT(file != RT_NULL);
182 
183     if(file->vnode->data)
184     {
185         /* get device handler */
186         device = (rt_device_t)file->vnode->data;
187 
188         if ((file->dentry->pathname[0] == '/') && (file->dentry->pathname[1] == '\0'))
189             return -RT_ENOSYS;
190 
191 #ifdef RT_USING_POSIX_DEVIO
192         if (device->fops && device->fops->write)
193         {
194             ret = device->fops->write(file, buf, count, pos);
195         }
196         else
197 #else
198         if (device->ops)
199 #endif /* RT_USING_POSIX_DEVIO */
200         {
201             rt_ubase_t shift = _get_unit_shift(device);
202 
203             /* read device data */
204             ret = rt_device_write(device, *pos, buf, count >> shift);
205             if (ret > 0)
206             {
207                 ret <<= shift;
208                 *pos += ret;
209             }
210         }
211     }
212 
213     return ret;
214 }
215 
dfs_devfs_ioctl(struct dfs_file * file,int cmd,void * args)216 static int dfs_devfs_ioctl(struct dfs_file *file, int cmd, void *args)
217 {
218     int ret = RT_EOK;
219     rt_device_t device;
220 
221     RT_ASSERT(file != RT_NULL);
222 
223     if (file->vnode && file->vnode->data)
224     {
225         /* get device handler */
226         device = (rt_device_t)file->vnode->data;
227 
228         if ((file->dentry->pathname[0] == '/') && (file->dentry->pathname[1] == '\0'))
229             return -RT_ENOSYS;
230 
231 #ifdef RT_USING_POSIX_DEVIO
232         if (device->fops && device->fops->ioctl)
233         {
234             ret = device->fops->ioctl(file, cmd, args);
235         }
236         else
237 #endif /* RT_USING_POSIX_DEVIO */
238         {
239             ret = rt_device_control(device, cmd, args);
240         }
241     }
242 
243     return ret;
244 }
245 
dfs_devfs_getdents(struct dfs_file * file,struct dirent * dirp,uint32_t count)246 static int dfs_devfs_getdents(struct dfs_file *file, struct dirent *dirp, uint32_t count)
247 {
248     int ret = -RT_ENOSYS;
249 
250     RT_ASSERT(file != RT_NULL);
251 
252     return ret;
253 }
254 
dfs_devfs_poll(struct dfs_file * file,struct rt_pollreq * req)255 static int dfs_devfs_poll(struct dfs_file *file, struct rt_pollreq *req)
256 {
257     int mask = 0;
258     rt_device_t device;
259 
260     RT_ASSERT(file != RT_NULL);
261 
262     if (file->vnode && file->vnode->data)
263     {
264         /* get device handler */
265         device = (rt_device_t)file->vnode->data;
266 
267 #ifdef RT_USING_POSIX_DEVIO
268         if (device->fops && device->fops->poll)
269         {
270             mask = device->fops->poll(file, req);
271         }
272 #endif /* RT_USING_POSIX_DEVIO */
273     }
274 
275     return mask;
276 }
277 
dfs_devfs_flush(struct dfs_file * file)278 static int dfs_devfs_flush(struct dfs_file *file)
279 {
280     int ret = RT_EOK;
281     rt_device_t device;
282 
283     RT_ASSERT(file != RT_NULL);
284 
285     if (file->vnode && file->vnode->data)
286     {
287         /* get device handler */
288         device = (rt_device_t)file->vnode->data;
289 
290 #ifdef RT_USING_POSIX_DEVIO
291         if (device->fops && device->fops->flush)
292         {
293             ret = device->fops->flush(file);
294         }
295 #endif /* RT_USING_POSIX_DEVIO */
296     }
297 
298     return ret;
299 }
300 
dfs_devfs_lseek(struct dfs_file * file,off_t offset,int wherece)301 static off_t dfs_devfs_lseek(struct dfs_file *file, off_t offset, int wherece)
302 {
303     off_t ret = -EPERM;
304     rt_device_t device;
305 
306     RT_ASSERT(file != RT_NULL);
307 
308     if (file->vnode && file->vnode->data)
309     {
310         /* get device handler */
311         device = (rt_device_t)file->vnode->data;
312 
313 #ifdef RT_USING_POSIX_DEVIO
314         if (device->fops && device->fops->lseek)
315         {
316             ret = device->fops->lseek(file, offset, wherece);
317         }
318 #endif /* RT_USING_POSIX_DEVIO */
319     }
320 
321     return ret;
322 }
323 
dfs_devfs_truncate(struct dfs_file * file,off_t offset)324 static int dfs_devfs_truncate(struct dfs_file *file, off_t offset)
325 {
326     int ret = RT_EOK;
327     rt_device_t device;
328 
329     RT_ASSERT(file != RT_NULL);
330 
331     if (file->vnode && file->vnode->data)
332     {
333         /* get device handler */
334         device = (rt_device_t)file->vnode->data;
335 
336 #ifdef RT_USING_POSIX_DEVIO
337         if (device->fops && device->fops->truncate)
338         {
339             ret = device->fops->truncate(file, offset);
340         }
341 #endif /* RT_USING_POSIX_DEVIO */
342     }
343 
344     return ret;
345 }
346 
dfs_devfs_mmap(struct dfs_file * file,struct lwp_avl_struct * mmap)347 static int dfs_devfs_mmap(struct dfs_file *file, struct lwp_avl_struct *mmap)
348 {
349     int ret = RT_EOK;
350     rt_device_t device;
351 
352     RT_ASSERT(file != RT_NULL);
353 
354     if (file->vnode && file->vnode->data)
355     {
356         /* get device handler */
357         device = (rt_device_t)file->vnode->data;
358 
359 #ifdef RT_USING_POSIX_DEVIO
360         if (device->fops && device->fops->mmap)
361         {
362             ret = device->fops->mmap(file, mmap);
363         }
364 #endif /* RT_USING_POSIX_DEVIO */
365     }
366 
367     return ret;
368 }
369 
dfs_devfs_lock(struct dfs_file * file,struct file_lock * flock)370 static int dfs_devfs_lock(struct dfs_file *file, struct file_lock *flock)
371 {
372     int ret = RT_EOK;
373     rt_device_t device;
374 
375     RT_ASSERT(file != RT_NULL);
376 
377     if (file->vnode && file->vnode->data)
378     {
379         /* get device handler */
380         device = (rt_device_t)file->vnode->data;
381 
382 #ifdef RT_USING_POSIX_DEVIO
383         if (device->fops && device->fops->lock)
384         {
385             ret = device->fops->lock(file, flock);
386         }
387 #endif /* RT_USING_POSIX_DEVIO */
388     }
389 
390     return ret;
391 }
392 
dfs_devfs_flock(struct dfs_file * file,int operation,struct file_lock * flock)393 static int dfs_devfs_flock(struct dfs_file *file, int operation, struct file_lock *flock)
394 {
395     int ret = RT_EOK;
396     rt_device_t device;
397 
398     RT_ASSERT(file != RT_NULL);
399 
400     if (file->vnode && file->vnode->data)
401     {
402         /* get device handler */
403         device = (rt_device_t)file->vnode->data;
404 
405 #ifdef RT_USING_POSIX_DEVIO
406         if (device->fops && device->fops->flock)
407         {
408             ret = device->fops->flock(file, operation, flock);
409         }
410 #endif /* RT_USING_POSIX_DEVIO */
411     }
412 
413     return ret;
414 }
415 
416 static const struct dfs_file_ops _dev_fops =
417 {
418     .open = dfs_devfs_open,
419     .close = dfs_devfs_close,
420     .read = dfs_devfs_read,
421     .write = dfs_devfs_write,
422     .ioctl = dfs_devfs_ioctl,
423     .getdents = dfs_devfs_getdents,
424     .poll = dfs_devfs_poll,
425 
426     .flush = dfs_devfs_flush,
427     .lseek = dfs_devfs_lseek,
428     .truncate = dfs_devfs_truncate,
429     .mmap = dfs_devfs_mmap,
430     .lock = dfs_devfs_lock,
431     .flock = dfs_devfs_flock,
432 };
433 
dfs_devfs_fops(void)434 const struct dfs_file_ops *dfs_devfs_fops(void)
435 {
436     return &_dev_fops;
437 }
438 
dfs_devfs_device_to_mode(struct rt_device * device)439 mode_t dfs_devfs_device_to_mode(struct rt_device *device)
440 {
441     mode_t mode = 0;
442 
443     switch (device->type)
444     {
445     case RT_Device_Class_Char:
446         mode = S_IFCHR | 0666;
447         break;
448     case RT_Device_Class_Block:
449         mode = S_IFBLK | 0666;
450         break;
451     case RT_Device_Class_Pipe:
452         mode = S_IFIFO | 0666;
453         break;
454     default:
455         mode = S_IFCHR | 0666;
456         break;
457     }
458 
459     return mode;
460 }
461 
dfs_devfs_mkdir(const char * fullpath,mode_t mode)462 static void dfs_devfs_mkdir(const char *fullpath, mode_t mode)
463 {
464     int len = rt_strlen(fullpath);
465     char *path = (char *)rt_malloc(len + 1);
466 
467     if (path)
468     {
469         int index = len - 1;
470 
471         rt_strcpy(path, fullpath);
472 
473         if (path[index] == '/')
474         {
475             path[index] = '\0';
476             index --;
477         }
478 
479         while (path[index] != '/' && index >= 0)
480         {
481             index --;
482         }
483 
484         path[index] = '\0';
485 
486         if (index > 0 && access(path, 0) != 0)
487         {
488             int i = 0;
489 
490             if (path[i] == '/')
491             {
492                 i ++;
493             }
494 
495             while (index > i)
496             {
497                 if (path[i] == '/')
498                 {
499                     path[i] = '\0';
500                     mkdir(path, mode);
501                     path[i] = '/';
502                 }
503 
504                 i ++;
505             }
506 
507             mkdir(path, mode);
508         }
509     }
510 }
511 
dfs_devfs_device_add(rt_device_t device)512 void dfs_devfs_device_add(rt_device_t device)
513 {
514     int fd;
515     char path[512];
516 
517     if (device)
518     {
519         rt_snprintf(path, 512, "/dev/%s", device->parent.name);
520 
521         if (access(path, 0) != 0)
522         {
523             mode_t mode = dfs_devfs_device_to_mode(device);
524 
525             dfs_devfs_mkdir(path, mode);
526 
527             fd = open(path, O_RDWR | O_CREAT, mode);
528             if (fd >= 0)
529             {
530                 close(fd);
531             }
532         }
533     }
534 }
535 
dfs_devfs_update(void)536 int dfs_devfs_update(void)
537 {
538     int count = rt_object_get_length(RT_Object_Class_Device);
539     if (count > 0)
540     {
541         rt_device_t *devices = rt_malloc(count * sizeof(rt_device_t));
542         if (devices)
543         {
544             rt_object_get_pointers(RT_Object_Class_Device, (rt_object_t *)devices, count);
545 
546             for (int index = 0; index < count; index ++)
547             {
548                 dfs_devfs_device_add(devices[index]);
549             }
550             rt_free(devices);
551         }
552     }
553 
554     return count;
555 }
556