859 lines
21 KiB
C
859 lines
21 KiB
C
/*
|
|
* @copyright (c) 2023-2024, MR Development Team
|
|
*
|
|
* @license SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* @date 2023-10-20 MacRsh First version
|
|
*/
|
|
|
|
#include "include/mr_api.h"
|
|
|
|
#define MR_ROOT_DEV_NAME "dev"
|
|
|
|
static struct mr_dev root_dev =
|
|
{
|
|
MR_MAGIC_NUMBER,
|
|
MR_ROOT_DEV_NAME,
|
|
Mr_Dev_Type_Root,
|
|
MR_NULL,
|
|
{&root_dev.list, &root_dev.list},
|
|
{&root_dev.clist, &root_dev.clist}
|
|
};
|
|
|
|
MR_INLINE int dev_is_root(struct mr_dev *dev)
|
|
{
|
|
return (int)dev->type == Mr_Dev_Type_Root;
|
|
}
|
|
|
|
MR_INLINE struct mr_dev *dev_find_child(struct mr_dev *parent, const char *name)
|
|
{
|
|
/* Find the child device */
|
|
for (struct mr_list *list = parent->clist.next; list != &parent->clist; list = list->next)
|
|
{
|
|
struct mr_dev *dev = (struct mr_dev *)MR_CONTAINER_OF(list, struct mr_dev, list);
|
|
if (strncmp(name, dev->name, MR_CFG_DEV_NAME_MAX) == 0)
|
|
{
|
|
return dev;
|
|
}
|
|
}
|
|
return MR_NULL;
|
|
}
|
|
|
|
MR_INLINE int dev_register_child(struct mr_dev *parent, struct mr_dev *child, const char *name)
|
|
{
|
|
/* Check whether the device with the same name exists */
|
|
if (dev_find_child(parent, name) != MR_NULL)
|
|
{
|
|
return MR_EEXIST;
|
|
}
|
|
|
|
/* Insert the device into the child list */
|
|
child->magic = MR_MAGIC_NUMBER;
|
|
strncpy(child->name, name, MR_CFG_DEV_NAME_MAX);
|
|
child->parent = parent;
|
|
mr_list_insert_before(&parent->clist, &child->list);
|
|
return MR_EOK;
|
|
}
|
|
|
|
MR_INLINE const char *dev_clear_path(const char *path)
|
|
{
|
|
/* Skip the leading '/' */
|
|
if (*path == '/')
|
|
{
|
|
path++;
|
|
}
|
|
|
|
/* Skip the leading 'dev/'(root path) */
|
|
if (strncmp(path, MR_ROOT_DEV_NAME"/", sizeof(MR_ROOT_DEV_NAME"/") - 1) == 0)
|
|
{
|
|
path += sizeof(MR_ROOT_DEV_NAME"/") - 1;
|
|
}
|
|
return path;
|
|
}
|
|
|
|
MR_INLINE int dev_register_by_path(struct mr_dev *parent, struct mr_dev *dev, const char *path)
|
|
{
|
|
char child_name[MR_CFG_DEV_NAME_MAX + 1] = {0};
|
|
|
|
/* Clear the path */
|
|
path = dev_clear_path(path);
|
|
|
|
/* Check whether the child path exists */
|
|
const char *child_path = strchr(path, '/');
|
|
if (child_path != MR_NULL)
|
|
{
|
|
/* Get the child name */
|
|
size_t len = (child_path - path) > MR_CFG_DEV_NAME_MAX ? MR_CFG_DEV_NAME_MAX : (child_path - path);
|
|
strncpy(child_name, path, len);
|
|
child_name[len] = '\0';
|
|
|
|
/* Find the child */
|
|
struct mr_dev *child = dev_find_child(parent, child_name);
|
|
if (child == MR_NULL)
|
|
{
|
|
return MR_ENOTFOUND;
|
|
}
|
|
|
|
/* Continue iterating */
|
|
return dev_register_by_path(child, dev, child_path);
|
|
} else
|
|
{
|
|
/* Register the child device to the parent */
|
|
return dev_register_child(parent, dev, path);
|
|
}
|
|
}
|
|
|
|
MR_INLINE struct mr_dev *dev_find_by_path(struct mr_dev *parent, const char *path)
|
|
{
|
|
char child_name[MR_CFG_DEV_NAME_MAX + 1] = {0};
|
|
|
|
/* Clear the path */
|
|
path = dev_clear_path(path);
|
|
|
|
/* Check whether the child path exists */
|
|
const char *child_path = strchr(path, '/');
|
|
if (child_path != MR_NULL)
|
|
{
|
|
/* Get the child name */
|
|
size_t len = (child_path - path) > MR_CFG_DEV_NAME_MAX ? MR_CFG_DEV_NAME_MAX : (child_path - path);
|
|
strncpy(child_name, path, len);
|
|
child_name[len] = '\0';
|
|
|
|
/* Find the child */
|
|
struct mr_dev *child = dev_find_child(parent, child_name);
|
|
if (child == MR_NULL)
|
|
{
|
|
return MR_NULL;
|
|
}
|
|
|
|
/* Continue iterating */
|
|
return dev_find_by_path(child, child_path);
|
|
} else
|
|
{
|
|
/* Find the child */
|
|
return dev_find_child(parent, path);
|
|
}
|
|
}
|
|
|
|
#ifdef MR_USING_RDWR_CTL
|
|
MR_INLINE int dev_lock_take(struct mr_dev *dev, int take, int set)
|
|
{
|
|
/* Continue iterating until reach the root device */
|
|
if (dev_is_root(dev->parent) != MR_TRUE)
|
|
{
|
|
int ret = dev_lock_take(dev->parent, take, set);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
/* Check whether the device is taken */
|
|
if (dev->lflags & take)
|
|
{
|
|
return MR_EBUSY;
|
|
}
|
|
|
|
/* Take the device */
|
|
MR_BIT_SET(dev->lflags, set);
|
|
return MR_EOK;
|
|
}
|
|
|
|
MR_INLINE void dev_lock_release(struct mr_dev *dev, int release)
|
|
{
|
|
/* Continue iterating until reach the root device */
|
|
if (dev_is_root(dev->parent) != MR_TRUE)
|
|
{
|
|
dev_lock_release(dev->parent, release);
|
|
}
|
|
|
|
/* Release the device */
|
|
MR_BIT_CLR(dev->lflags, release);
|
|
}
|
|
#endif /* MR_USING_RDWR_CTL */
|
|
|
|
MR_INLINE int dev_get_path(struct mr_dev *dev, char *buf, size_t bufsz)
|
|
{
|
|
int ret = 0;
|
|
|
|
/* Continue to get the path of the parent device */
|
|
if (dev->parent != MR_NULL)
|
|
{
|
|
ret = dev_get_path(dev->parent, buf, bufsz);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
/* Check whether the buffer is enough */
|
|
if ((bufsz - ret) <= (strlen(dev->name) + 1))
|
|
{
|
|
return ret;
|
|
}
|
|
ret += snprintf(buf + ret, bufsz - ret, "/%s", dev->name);
|
|
return ret;
|
|
}
|
|
|
|
MR_INLINE int dev_register(struct mr_dev *dev, const char *path)
|
|
{
|
|
/* Disable interrupt */
|
|
mr_interrupt_disable();
|
|
|
|
/* Register the device to the root device */
|
|
int ret = dev_register_by_path(&root_dev, dev, path);
|
|
|
|
/* Enable interrupt */
|
|
mr_interrupt_enable();
|
|
return ret;
|
|
}
|
|
|
|
MR_INLINE struct mr_dev *dev_find(const char *path)
|
|
{
|
|
/* Find the device from the root device */
|
|
return dev_find_by_path(&root_dev, path);
|
|
}
|
|
|
|
MR_INLINE int dev_open(struct mr_dev *dev, int oflags)
|
|
{
|
|
#ifdef MR_USING_RDWR_CTL
|
|
if (MR_BIT_IS_SET(dev->sflags, oflags) != MR_ENABLE)
|
|
{
|
|
return MR_ENOTSUP;
|
|
}
|
|
#endif /* MR_USING_RDWR_CTL */
|
|
|
|
/* Check whether the device is opened */
|
|
if (dev->ref_count == 0)
|
|
{
|
|
/* Continue iterating until reach the root device */
|
|
if (dev_is_root(dev->parent) != MR_TRUE)
|
|
{
|
|
int ret = dev_open(dev->parent, oflags);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
/* Open the device */
|
|
if (dev->ops->open != MR_NULL)
|
|
{
|
|
int ret = dev->ops->open(dev);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
#ifdef MR_USING_RDWR_CTL
|
|
else if (MR_BIT_IS_SET(dev->sflags, MR_SFLAG_ONLY) == MR_ENABLE)
|
|
{
|
|
return MR_EBUSY;
|
|
}
|
|
#endif /* MR_USING_RDWR_CTL */
|
|
|
|
/* Increase the reference count */
|
|
dev->ref_count++;
|
|
return MR_EOK;
|
|
}
|
|
|
|
MR_INLINE int dev_close(struct mr_dev *dev)
|
|
{
|
|
/* Decrease the reference count */
|
|
dev->ref_count--;
|
|
|
|
/* Check whether the device needs to be closed */
|
|
if (dev->ref_count == 0)
|
|
{
|
|
/* Continue iterating until reach the root device */
|
|
if (dev_is_root(dev->parent) != MR_TRUE)
|
|
{
|
|
int ret = dev_close(dev->parent);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
/* Close the device */
|
|
if (dev->ops->close != MR_NULL)
|
|
{
|
|
return dev->ops->close(dev);
|
|
}
|
|
}
|
|
return MR_EOK;
|
|
}
|
|
|
|
MR_INLINE ssize_t dev_read(struct mr_dev *dev, int off, void *buf, size_t size, int async)
|
|
{
|
|
#ifdef MR_USING_RDWR_CTL
|
|
do
|
|
{
|
|
/* Disable interrupt */
|
|
mr_interrupt_disable();
|
|
int ret = dev_lock_take(dev, (MR_LFLAG_RD | MR_LFLAG_SLEEP), MR_LFLAG_RD);
|
|
if (ret < 0)
|
|
{
|
|
/* Enable interrupt */
|
|
mr_interrupt_enable();
|
|
return ret;
|
|
}
|
|
/* Enable interrupt */
|
|
mr_interrupt_enable();
|
|
} while (0);
|
|
#endif /* MR_USING_RDWR_CTL */
|
|
|
|
/* Read buffer from the device */
|
|
ssize_t ret = dev->ops->read(dev, off, buf, size, async);
|
|
|
|
#ifdef MR_USING_RDWR_CTL
|
|
dev_lock_release(dev, MR_LFLAG_RD);
|
|
#endif /* MR_USING_RDWR_CTL */
|
|
return ret;
|
|
}
|
|
|
|
MR_INLINE ssize_t dev_write(struct mr_dev *dev, int offset, const void *buf, size_t size, int async)
|
|
{
|
|
#ifdef MR_USING_RDWR_CTL
|
|
do
|
|
{
|
|
/* Disable interrupt */
|
|
mr_interrupt_disable();
|
|
int ret = dev_lock_take(dev,
|
|
(MR_LFLAG_WR | MR_LFLAG_SLEEP | (async == MR_SYNC ? MR_LFLAG_NONBLOCK : 0)),
|
|
MR_LFLAG_WR);
|
|
if (ret < 0)
|
|
{
|
|
/* Enable interrupt */
|
|
mr_interrupt_enable();
|
|
return ret;
|
|
}
|
|
/* Enable interrupt */
|
|
mr_interrupt_enable();
|
|
} while (0);
|
|
#endif /* MR_USING_RDWR_CTL */
|
|
|
|
/* Write buffer to the device */
|
|
ssize_t ret = dev->ops->write(dev, offset, buf, size, async);
|
|
|
|
#ifdef MR_USING_RDWR_CTL
|
|
dev_lock_release(dev, MR_LFLAG_WR);
|
|
if ((async == MR_ASYNC) && (ret > 0))
|
|
{
|
|
/* Disable interrupt */
|
|
mr_interrupt_disable();
|
|
dev_lock_take(dev, 0, MR_LFLAG_NONBLOCK);
|
|
/* Enable interrupt */
|
|
mr_interrupt_enable();
|
|
}
|
|
#endif /* MR_USING_RDWR_CTL */
|
|
return ret;
|
|
}
|
|
|
|
MR_INLINE int dev_ioctl(struct mr_dev *dev, int desc, int off, int cmd, void *args)
|
|
{
|
|
/* Check whether the device has an ioctl function */
|
|
if (dev->ops->ioctl == MR_NULL)
|
|
{
|
|
return MR_ENOTSUP;
|
|
}
|
|
|
|
switch (cmd)
|
|
{
|
|
case MR_CTL_SET_RD_CALL:
|
|
{
|
|
dev->rd_call.desc = desc;
|
|
dev->rd_call.call = (int (*)(int desc, void *args))args;
|
|
return sizeof(dev->rd_call);
|
|
}
|
|
case MR_CTL_SET_WR_CALL:
|
|
{
|
|
dev->wr_call.desc = desc;
|
|
dev->wr_call.call = (int (*)(int desc, void *args))args;
|
|
return sizeof(dev->wr_call);
|
|
}
|
|
case MR_CTL_GET_SFLAGS:
|
|
{
|
|
if (args != MR_NULL)
|
|
{
|
|
int *sflags = (int *)args;
|
|
|
|
*sflags = dev->sflags;
|
|
return sizeof(sflags);
|
|
}
|
|
return MR_EINVAL;
|
|
}
|
|
case MR_CTL_GET_PATH:
|
|
{
|
|
if (args != MR_NULL)
|
|
{
|
|
struct
|
|
{
|
|
char *buf;
|
|
size_t bufsz;
|
|
} *path = args;
|
|
|
|
return dev_get_path(dev, path->buf, path->bufsz);
|
|
}
|
|
return MR_EINVAL;
|
|
}
|
|
case MR_CTL_GET_NAME:
|
|
{
|
|
if (args != MR_NULL)
|
|
{
|
|
char **name = (char **)args;
|
|
|
|
*name = dev->name;
|
|
return sizeof(name);
|
|
}
|
|
return MR_EINVAL;
|
|
}
|
|
case MR_CTL_GET_RD_CALL:
|
|
{
|
|
if (args != MR_NULL)
|
|
{
|
|
*(int (**)(int desc, void *args))args = dev->rd_call.call;
|
|
return sizeof(dev->rd_call.call);
|
|
}
|
|
return MR_EINVAL;
|
|
}
|
|
case MR_CTL_GET_WR_CALL:
|
|
{
|
|
if (args != MR_NULL)
|
|
{
|
|
*(int (**)(int desc, void *args))args = dev->wr_call.call;
|
|
return sizeof(dev->wr_call.call);
|
|
}
|
|
return MR_EINVAL;
|
|
}
|
|
default:
|
|
{
|
|
#ifdef MR_USING_RDWR_CTL
|
|
do
|
|
{
|
|
/* Disable interrupt */
|
|
mr_interrupt_disable();
|
|
int ret = dev_lock_take(dev, (MR_LFLAG_RDWR | MR_LFLAG_SLEEP | MR_LFLAG_NONBLOCK), MR_LFLAG_RDWR);
|
|
if (ret < 0)
|
|
{
|
|
/* Enable interrupt */
|
|
mr_interrupt_enable();
|
|
return ret;
|
|
}
|
|
/* Enable interrupt */
|
|
mr_interrupt_enable();
|
|
} while (0);
|
|
#endif /* MR_USING_RDWR_CTL */
|
|
|
|
/* I/O control to the device */
|
|
int ret = dev->ops->ioctl(dev, off, cmd, args);
|
|
|
|
#ifdef MR_USING_RDWR_CTL
|
|
dev_lock_release(dev, MR_LFLAG_RDWR);
|
|
#endif /* MR_USING_RDWR_CTL */
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief This function register a device.
|
|
*
|
|
* @param dev The device.
|
|
* @param path The path of the device.
|
|
* @param type The type of the device.
|
|
* @param sflags The support flags of the device.
|
|
* @param ops The operations of the device.
|
|
* @param drv The driver of the device.
|
|
*
|
|
* @return MR_EOK on success, otherwise an error code.
|
|
*/
|
|
int mr_dev_register(struct mr_dev *dev,
|
|
const char *path,
|
|
int type,
|
|
int sflags,
|
|
struct mr_dev_ops *ops,
|
|
struct mr_drv *drv)
|
|
{
|
|
static struct mr_dev_ops null_ops = {MR_NULL};
|
|
|
|
MR_ASSERT(dev != MR_NULL);
|
|
MR_ASSERT(dev->magic != MR_MAGIC_NUMBER);
|
|
MR_ASSERT(path != MR_NULL);
|
|
MR_ASSERT(type != Mr_Dev_Type_Root);
|
|
MR_ASSERT((ops != MR_NULL) || (sflags == MR_SFLAG_NONRDWR));
|
|
MR_ASSERT((ops->read != MR_NULL) || (MR_BIT_IS_SET(sflags, MR_SFLAG_RDONLY) == MR_DISABLE));
|
|
MR_ASSERT((ops->write != MR_NULL) || (MR_BIT_IS_SET(sflags, MR_SFLAG_WRONLY) == MR_DISABLE));
|
|
MR_ASSERT((drv != MR_NULL) || (sflags & MR_SFLAG_NONDRV));
|
|
MR_ASSERT((drv == MR_NULL) || (drv->type == type));
|
|
|
|
/* Initialize the fields */
|
|
dev->magic = 0;
|
|
memset(dev->name, '\0', MR_CFG_DEV_NAME_MAX);
|
|
dev->parent = MR_NULL;
|
|
mr_list_init(&dev->list);
|
|
mr_list_init(&dev->clist);
|
|
dev->type = type;
|
|
dev->ref_count = 0;
|
|
#ifdef MR_USING_RDWR_CTL
|
|
dev->sflags = sflags;
|
|
dev->lflags = 0;
|
|
#endif /* MR_USING_RDWR_CTL */
|
|
dev->rd_call.desc = -1;
|
|
dev->rd_call.call = MR_NULL;
|
|
dev->wr_call.desc = -1;
|
|
dev->wr_call.call = MR_NULL;
|
|
dev->ops = (ops != MR_NULL) ? ops : &null_ops;
|
|
dev->drv = drv;
|
|
|
|
/* Register the device */
|
|
return dev_register(dev, path);
|
|
}
|
|
|
|
/**
|
|
* @brief This function handle device interrupt.
|
|
*
|
|
* @param dev The device to be handle.
|
|
* @param event The event to be handle.
|
|
* @param args The arguments of the event.
|
|
*/
|
|
int mr_dev_isr(struct mr_dev *dev, int event, void *args)
|
|
{
|
|
MR_ASSERT(dev != MR_NULL);
|
|
|
|
/* Check whether the device is opened */
|
|
if (dev->ref_count == 0)
|
|
{
|
|
return MR_EINVAL;
|
|
}
|
|
|
|
/* Check whether the device has ISR */
|
|
if (dev->ops->isr == MR_NULL)
|
|
{
|
|
return MR_ENOTSUP;
|
|
}
|
|
|
|
/* Call the device ISR */
|
|
ssize_t ret = dev->ops->isr(dev, event, args);
|
|
if (ret < 0)
|
|
{
|
|
return (int)ret;
|
|
}
|
|
|
|
/* Handle the event */
|
|
switch (event & MR_ISR_MASK)
|
|
{
|
|
case MR_ISR_RD:
|
|
{
|
|
if (dev->rd_call.call != MR_NULL)
|
|
{
|
|
return dev->rd_call.call(dev->rd_call.desc, &ret);
|
|
}
|
|
return MR_EOK;
|
|
}
|
|
|
|
case MR_ISR_WR:
|
|
{
|
|
#ifdef MR_USING_RDWR_CTL
|
|
dev_lock_release(dev, MR_LFLAG_NONBLOCK);
|
|
#endif /* MR_USING_RDWR_CTL */
|
|
if (dev->wr_call.call != MR_NULL)
|
|
{
|
|
return dev->wr_call.call(dev->wr_call.desc, &ret);
|
|
}
|
|
return MR_EOK;
|
|
}
|
|
|
|
default:
|
|
{
|
|
return MR_ENOTSUP;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Device descriptor structure.
|
|
*/
|
|
static struct mr_desc
|
|
{
|
|
struct mr_dev *dev; /* Device */
|
|
int oflags; /* Open flags */
|
|
int offset; /* Offset */
|
|
#ifndef MR_CFG_DESC_MAX
|
|
#define MR_CFG_DESC_MAX (32)
|
|
#endif /* MR_CFG_DESC_MAX */
|
|
} desc_map[MR_CFG_DESC_MAX] = {0};
|
|
|
|
#define DESC_OF(desc) (desc_map[(desc)])
|
|
#define DESC_IS_VALID(desc) (((desc) >= 0 && (desc) < MR_CFG_DESC_MAX) && ((DESC_OF(desc).dev) != MR_NULL))
|
|
|
|
#ifdef MR_USING_DESC_CHECK
|
|
#define MR_DESC_CHECK(desc) if (DESC_IS_VALID(desc) == MR_FALSE) { return MR_EINVAL; }
|
|
#else
|
|
#define MR_DESC_CHECK(desc)
|
|
#endif /* MR_USING_DESC_CHECK */
|
|
|
|
static int desc_allocate(const char *path)
|
|
{
|
|
int desc = -1;
|
|
|
|
/* Find a free descriptor */
|
|
for (size_t i = 0; i < MR_CFG_DESC_MAX; i++)
|
|
{
|
|
if (DESC_OF(i).dev == MR_NULL)
|
|
{
|
|
desc = (int)i;
|
|
break;
|
|
}
|
|
}
|
|
if (desc < 0)
|
|
{
|
|
return MR_ENOMEM;
|
|
}
|
|
|
|
/* Find the device */
|
|
struct mr_dev *dev = dev_find(path);
|
|
if (dev == MR_NULL)
|
|
{
|
|
return MR_ENOTFOUND;
|
|
}
|
|
|
|
/* Initialize the fields */
|
|
DESC_OF(desc).dev = dev;
|
|
DESC_OF(desc).offset = -1;
|
|
DESC_OF(desc).oflags = MR_OFLAG_CLOSED;
|
|
return desc;
|
|
}
|
|
|
|
static void desc_free(int desc)
|
|
{
|
|
if (desc >= 0 && desc < MR_CFG_DESC_MAX)
|
|
{
|
|
DESC_OF(desc).dev = MR_NULL;
|
|
DESC_OF(desc).oflags = MR_OFLAG_CLOSED;
|
|
DESC_OF(desc).offset = -1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief This function open a device.
|
|
*
|
|
* @param path The path of the device.
|
|
* @param oflags The open flags of the device.
|
|
*
|
|
* @return The descriptor of the device, otherwise an error code.
|
|
*/
|
|
int mr_dev_open(const char *path, int oflags)
|
|
{
|
|
MR_ASSERT(path != MR_NULL);
|
|
MR_ASSERT(oflags != MR_OFLAG_CLOSED);
|
|
|
|
/* Allocate a descriptor */
|
|
int desc = desc_allocate(path);
|
|
if (desc < 0)
|
|
{
|
|
return desc;
|
|
}
|
|
|
|
/* Open the device */
|
|
int ret = dev_open(DESC_OF(desc).dev, oflags);
|
|
if (ret < 0)
|
|
{
|
|
desc_free(desc);
|
|
return ret;
|
|
}
|
|
|
|
/* Initialize the open flags */
|
|
DESC_OF(desc).oflags = oflags;
|
|
return desc;
|
|
}
|
|
|
|
/**
|
|
* @brief This function close a device.
|
|
*
|
|
* @param desc The descriptor of the device.
|
|
*
|
|
* @return MR_EOK on success, otherwise an error code.
|
|
*/
|
|
int mr_dev_close(int desc)
|
|
{
|
|
MR_DESC_CHECK(desc);
|
|
|
|
/* Close the device */
|
|
int ret = dev_close(DESC_OF(desc).dev);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* Free the descriptor */
|
|
desc_free(desc);
|
|
return MR_EOK;
|
|
}
|
|
|
|
/**
|
|
* @brief This function read a device.
|
|
*
|
|
* @param desc The descriptor of the device.
|
|
* @param buf The buf buffer to be read.
|
|
* @param size The size of read.
|
|
*
|
|
* @return The size of the actual read, otherwise an error code.
|
|
*/
|
|
ssize_t mr_dev_read(int desc, void *buf, size_t size)
|
|
{
|
|
MR_ASSERT((buf != MR_NULL) || (size == 0));
|
|
MR_DESC_CHECK(desc);
|
|
|
|
#ifdef MR_USING_RDWR_CTL
|
|
if (MR_BIT_IS_SET(DESC_OF(desc).oflags, MR_OFLAG_RDONLY) == MR_DISABLE)
|
|
{
|
|
return MR_ENOTSUP;
|
|
}
|
|
#endif /* MR_USING_RDWR_CTL */
|
|
|
|
/* Read buffer from the device */
|
|
return dev_read(DESC_OF(desc).dev,
|
|
DESC_OF(desc).offset,
|
|
buf,
|
|
size,
|
|
(MR_BIT_IS_SET(DESC_OF(desc).oflags, MR_OFLAG_NONBLOCK)));
|
|
}
|
|
|
|
/**
|
|
* @brief This function write a device.
|
|
*
|
|
* @param desc The descriptor of the device.
|
|
* @param buf The buf buffer to be written.
|
|
* @param size The size of write.
|
|
*
|
|
* @return The size of the actual write, otherwise an error code.
|
|
*/
|
|
ssize_t mr_dev_write(int desc, const void *buf, size_t size)
|
|
{
|
|
MR_ASSERT((buf != MR_NULL) || (size == 0));
|
|
MR_DESC_CHECK(desc);
|
|
|
|
#ifdef MR_USING_RDWR_CTL
|
|
if (MR_BIT_IS_SET(DESC_OF(desc).oflags, MR_OFLAG_WRONLY) == MR_DISABLE)
|
|
{
|
|
return MR_ENOTSUP;
|
|
}
|
|
#endif /* MR_USING_RDWR_CTL */
|
|
|
|
/* Write buffer to the device */
|
|
return dev_write(DESC_OF(desc).dev,
|
|
DESC_OF(desc).offset,
|
|
buf,
|
|
size,
|
|
(MR_BIT_IS_SET(DESC_OF(desc).oflags, MR_OFLAG_NONBLOCK)));
|
|
}
|
|
|
|
/**
|
|
* @brief This function ioctl a device.
|
|
*
|
|
* @param desc The descriptor of the device.
|
|
* @param cmd The command of the device.
|
|
* @param args The arguments of the device.
|
|
*
|
|
* @return The arguments of the device, otherwise an error code.
|
|
*/
|
|
int mr_dev_ioctl(int desc, int cmd, void *args)
|
|
{
|
|
MR_DESC_CHECK(desc);
|
|
|
|
switch (cmd)
|
|
{
|
|
case MR_CTL_SET_OFFSET:
|
|
{
|
|
if (args != MR_NULL)
|
|
{
|
|
int offset = *(int *)args;
|
|
|
|
DESC_OF(desc).offset = offset;
|
|
return sizeof(offset);
|
|
}
|
|
return MR_EINVAL;
|
|
}
|
|
case MR_CTL_GET_OFFSET:
|
|
{
|
|
if (args != MR_NULL)
|
|
{
|
|
int *offset = (int *)args;
|
|
|
|
*offset = DESC_OF(desc).offset;
|
|
return sizeof(offset);
|
|
}
|
|
return MR_EINVAL;
|
|
}
|
|
case MR_CTL_GET_OFLAGS:
|
|
{
|
|
if (args != MR_NULL)
|
|
{
|
|
int *oflags = (int *)args;
|
|
|
|
*oflags = DESC_OF(desc).oflags;
|
|
}
|
|
return MR_EINVAL;
|
|
}
|
|
default:
|
|
{
|
|
/* I/O control to the device */
|
|
return dev_ioctl(DESC_OF(desc).dev, desc, DESC_OF(desc).offset, cmd, args);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief This function check if the descriptor is valid.
|
|
*
|
|
* @param desc The descriptor of the device.
|
|
*
|
|
* @return MR_TRUE if the descriptor is valid, otherwise MR_FALSE.
|
|
*/
|
|
int mr_dev_is_valid(int desc)
|
|
{
|
|
return DESC_IS_VALID(desc);
|
|
}
|
|
|
|
#ifdef MR_USING_MSH
|
|
#include "include/components/mr_msh.h"
|
|
|
|
static void msh_list_tree(struct mr_dev *parent, int level)
|
|
{
|
|
if (level == 0)
|
|
{
|
|
mr_msh_printf("|-- %-*s", MR_CFG_DEV_NAME_MAX, parent->name);
|
|
} else
|
|
{
|
|
mr_msh_printf("%*s|-- %-*s", level, " ", MR_CFG_DEV_NAME_MAX, parent->name);
|
|
}
|
|
for (size_t i = 0; i < MR_CFG_DESC_MAX; i++)
|
|
{
|
|
if (desc_map[i].dev == parent)
|
|
{
|
|
mr_msh_printf(" [%d]", i);
|
|
}
|
|
}
|
|
mr_msh_printf("\r\n");
|
|
for (struct mr_list *child = parent->clist.next; child != &parent->clist; child = child->next)
|
|
{
|
|
struct mr_dev *dev = MR_CONTAINER_OF(child, struct mr_dev, list);
|
|
msh_list_tree(dev, level + 4);
|
|
}
|
|
}
|
|
|
|
static int msh_cmd_dlist(int argc, void *argv)
|
|
{
|
|
msh_list_tree(&root_dev, 0);
|
|
return MR_EOK;
|
|
}
|
|
|
|
/**
|
|
* @brief Exports device MSH commands.
|
|
*/
|
|
MR_MSH_CMD_EXPORT(dlist, msh_cmd_dlist, "list all devices.");
|
|
|
|
#endif /* MR_USING_MSH */
|