/* * Copyright (c) 2006-2021, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2018-02-11 Bernard Ignore O_CREAT flag in open. */ #include #include #include #include #include #include #include #include #include #include #include static int dfs_devfs_open(struct dfs_file *file) { int ret = RT_EOK; RT_ASSERT(file != RT_NULL); RT_ASSERT(file->vnode->ref_count > 0); if (file->vnode->ref_count > 1) { if (file->vnode->type == FT_DIRECTORY && !(file->flags & O_DIRECTORY)) { return -ENOENT; } file->fpos = 0; } if (!S_ISDIR(file->vnode->mode)) { rt_device_t device = RT_NULL; struct dfs_dentry *de = file->dentry; char *device_name = rt_malloc(DFS_PATH_MAX); if (!device_name) { return -ENOMEM; } /* skip `/dev` */ rt_snprintf(device_name, DFS_PATH_MAX, "%s%s", de->mnt->fullpath + sizeof("/dev") - 1, de->pathname); device = rt_device_find(device_name + 1); if (device) { file->vnode->data = device; #ifdef RT_USING_POSIX_DEVIO if (device->fops && device->fops->open) { ret = device->fops->open(file); if (ret == RT_EOK || ret == -RT_ENOSYS) { ret = RT_EOK; } } else if (device->ops && file->vnode->ref_count == 1) #else if (device->ops && file->vnode->ref_count == 1) #endif /* RT_USING_POSIX_DEVIO */ { ret = rt_device_open(device, RT_DEVICE_OFLAG_RDWR); if (ret == RT_EOK || ret == -RT_ENOSYS) { ret = RT_EOK; } } } } return ret; } static int dfs_devfs_close(struct dfs_file *file) { int ret = RT_EOK; rt_device_t device; RT_ASSERT(file != RT_NULL); RT_ASSERT(file->vnode->ref_count > 0); if (file->vnode && file->vnode->data) { /* get device handler */ device = (rt_device_t)file->vnode->data; #ifdef RT_USING_POSIX_DEVIO if (device->fops && device->fops->close) { ret = device->fops->close(file); } else if (file->vnode->ref_count == 1) #else if (device->ops && file->vnode->ref_count == 1) #endif /* RT_USING_POSIX_DEVIO */ { /* close device handler */ ret = rt_device_close(device); } } return ret; } static ssize_t dfs_devfs_read(struct dfs_file *file, void *buf, size_t count, off_t *pos) { ssize_t ret = -RT_EIO; rt_device_t device; RT_ASSERT(file != RT_NULL); if (file->vnode && file->vnode->data) { /* get device handler */ device = (rt_device_t)file->vnode->data; #ifdef RT_USING_POSIX_DEVIO if (device->fops && device->fops->read) { ret = device->fops->read(file, buf, count, pos); } else #else if (device->ops) #endif /* RT_USING_POSIX_DEVIO */ { /* read device data */ ret = rt_device_read(device, *pos, buf, count); *pos += ret; } } return ret; } static ssize_t dfs_devfs_write(struct dfs_file *file, const void *buf, size_t count, off_t *pos) { ssize_t ret = -RT_EIO; rt_device_t device; RT_ASSERT(file != RT_NULL); if(file->vnode->data) { /* get device handler */ device = (rt_device_t)file->vnode->data; if ((file->dentry->pathname[0] == '/') && (file->dentry->pathname[1] == '\0')) return -RT_ENOSYS; #ifdef RT_USING_POSIX_DEVIO if (device->fops && device->fops->write) { ret = device->fops->write(file, buf, count, pos); } else #else if (device->ops) #endif /* RT_USING_POSIX_DEVIO */ { /* read device data */ ret = rt_device_write(device, *pos, buf, count); *pos += ret; } } return ret; } static int dfs_devfs_ioctl(struct dfs_file *file, int cmd, void *args) { int ret = RT_EOK; rt_device_t device; RT_ASSERT(file != RT_NULL); if (file->vnode && file->vnode->data) { /* get device handler */ device = (rt_device_t)file->vnode->data; if ((file->dentry->pathname[0] == '/') && (file->dentry->pathname[1] == '\0')) return -RT_ENOSYS; #ifdef RT_USING_POSIX_DEVIO if (device->fops && device->fops->ioctl) { ret = device->fops->ioctl(file, cmd, args); } else #endif /* RT_USING_POSIX_DEVIO */ { ret = rt_device_control(device, cmd, args); } } return ret; } static int dfs_devfs_getdents(struct dfs_file *file, struct dirent *dirp, uint32_t count) { int ret = -RT_ENOSYS; RT_ASSERT(file != RT_NULL); return ret; } static int dfs_devfs_poll(struct dfs_file *file, struct rt_pollreq *req) { int mask = 0; rt_device_t device; RT_ASSERT(file != RT_NULL); if (file->vnode && file->vnode->data) { /* get device handler */ device = (rt_device_t)file->vnode->data; #ifdef RT_USING_POSIX_DEVIO if (device->fops && device->fops->poll) { mask = device->fops->poll(file, req); } #endif /* RT_USING_POSIX_DEVIO */ } return mask; } static int dfs_devfs_flush(struct dfs_file *file) { int ret = RT_EOK; rt_device_t device; RT_ASSERT(file != RT_NULL); if (file->vnode && file->vnode->data) { /* get device handler */ device = (rt_device_t)file->vnode->data; #ifdef RT_USING_POSIX_DEVIO if (device->fops && device->fops->flush) { ret = device->fops->flush(file); } #endif /* RT_USING_POSIX_DEVIO */ } return ret; } static off_t dfs_devfs_lseek(struct dfs_file *file, off_t offset, int wherece) { off_t ret = 0; rt_device_t device; RT_ASSERT(file != RT_NULL); if (file->vnode && file->vnode->data) { /* get device handler */ device = (rt_device_t)file->vnode->data; #ifdef RT_USING_POSIX_DEVIO if (device->fops && device->fops->lseek) { ret = device->fops->lseek(file, offset, wherece); } #endif /* RT_USING_POSIX_DEVIO */ } return ret; } static int dfs_devfs_truncate(struct dfs_file *file, off_t offset) { int ret = RT_EOK; rt_device_t device; RT_ASSERT(file != RT_NULL); if (file->vnode && file->vnode->data) { /* get device handler */ device = (rt_device_t)file->vnode->data; #ifdef RT_USING_POSIX_DEVIO if (device->fops && device->fops->truncate) { ret = device->fops->truncate(file, offset); } #endif /* RT_USING_POSIX_DEVIO */ } return ret; } static int dfs_devfs_mmap(struct dfs_file *file, struct lwp_avl_struct *mmap) { int ret = RT_EOK; rt_device_t device; RT_ASSERT(file != RT_NULL); if (file->vnode && file->vnode->data) { /* get device handler */ device = (rt_device_t)file->vnode->data; #ifdef RT_USING_POSIX_DEVIO if (device->fops && device->fops->mmap) { ret = device->fops->mmap(file, mmap); } #endif /* RT_USING_POSIX_DEVIO */ } return ret; } static int dfs_devfs_lock(struct dfs_file *file, struct file_lock *flock) { int ret = RT_EOK; rt_device_t device; RT_ASSERT(file != RT_NULL); if (file->vnode && file->vnode->data) { /* get device handler */ device = (rt_device_t)file->vnode->data; #ifdef RT_USING_POSIX_DEVIO if (device->fops && device->fops->lock) { ret = device->fops->lock(file, flock); } #endif /* RT_USING_POSIX_DEVIO */ } return ret; } static int dfs_devfs_flock(struct dfs_file *file, int operation, struct file_lock *flock) { int ret = RT_EOK; rt_device_t device; RT_ASSERT(file != RT_NULL); if (file->vnode && file->vnode->data) { /* get device handler */ device = (rt_device_t)file->vnode->data; #ifdef RT_USING_POSIX_DEVIO if (device->fops && device->fops->flock) { ret = device->fops->flock(file, operation, flock); } #endif /* RT_USING_POSIX_DEVIO */ } return ret; } static const struct dfs_file_ops _dev_fops = { .open = dfs_devfs_open, .close = dfs_devfs_close, .read = dfs_devfs_read, .write = dfs_devfs_write, .ioctl = dfs_devfs_ioctl, .getdents = dfs_devfs_getdents, .poll = dfs_devfs_poll, .flush = dfs_devfs_flush, .lseek = dfs_devfs_lseek, .truncate = dfs_devfs_truncate, .mmap = dfs_devfs_mmap, .lock = dfs_devfs_lock, .flock = dfs_devfs_flock, }; const struct dfs_file_ops *dfs_devfs_fops(void) { return &_dev_fops; } mode_t dfs_devfs_device_to_mode(struct rt_device *device) { mode_t mode = 0; switch (device->type) { case RT_Device_Class_Char: mode = S_IFCHR | 0777; break; case RT_Device_Class_Block: mode = S_IFBLK | 0777; break; case RT_Device_Class_Pipe: mode = S_IFIFO | 0777; break; default: mode = S_IFCHR | 0777; break; } return mode; } static void dfs_devfs_mkdir(const char *fullpath, mode_t mode) { int len = rt_strlen(fullpath); char *path = (char *)rt_malloc(len + 1); if (path) { int index = len - 1; rt_strcpy(path, fullpath); if (path[index] == '/') { path[index] = '\0'; index --; } while (path[index] != '/' && index >= 0) { index --; } path[index] = '\0'; if (index > 0 && access(path, 0) != 0) { int i = 0; if (path[i] == '/') { i ++; } while (index > i) { if (path[i] == '/') { path[i] = '\0'; mkdir(path, mode); path[i] = '/'; } i ++; } mkdir(path, mode); } } } void dfs_devfs_device_add(rt_device_t device) { int fd; char path[512]; if (device) { rt_snprintf(path, 512, "/dev/%s", device->parent.name); if (access(path, 0) != 0) { mode_t mode = dfs_devfs_device_to_mode(device); dfs_devfs_mkdir(path, mode); fd = open(path, O_RDWR | O_CREAT, mode); if (fd >= 0) { close(fd); } } } } int dfs_devfs_update(void) { int count = rt_object_get_length(RT_Object_Class_Device); if (count > 0) { rt_device_t *devices = rt_malloc(count * sizeof(rt_device_t)); if (devices) { rt_object_get_pointers(RT_Object_Class_Device, (rt_object_t *)devices, count); for (int index = 0; index < count; index ++) { dfs_devfs_device_add(devices[index]); } rt_free(devices); } } return count; }