rt-thread/components/dfs/dfs_v2/filesystems/devfs/devfs.c

521 lines
11 KiB
C

/*
* 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 <rthw.h>
#include <rtdbg.h>
#include <rtdevice.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/unistd.h>
#include <dfs.h>
#include <dfs_fs.h>
#include <dfs_file.h>
#include <dfs_dentry.h>
#include <dfs_mnt.h>
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;
}