521 lines
11 KiB
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;
|
|
}
|