/* * @copyright (c) 2023-2024, MR Development Team * * @license SPDX-License-Identifier: Apache-2.0 * * @date 2023-10-20 MacRsh First version * @date 2024-04-07 MacRsh Reconstructed */ #include "../mr-library/include/mr_api.h" #include #undef MR_LOG_TAG #define MR_LOG_TAG "device" #define _MAGIC_NUMBER (0xdeadbeef) /**< Magic number */ #define _LOCK_RD_MASK (0xffff0000) /**< Read lock mask */ #define _LOCK_WR_MASK (0x0000ffff) /**< Write lock mask */ #define _LOCK_ALL_MASK (0xffffffff) /**< Read/write lock mask */ /** * @brief Device event complete structure. */ struct _device_event { struct mr_list list; /**< Event list */ int descriptor; /**< Event descriptor */ uint32_t event; /**< Event */ void (*callback)(int descriptor, uint32_t event, void *args); /**< Callback function */ }; static struct mr_device _root_device = { .magic = _MAGIC_NUMBER, .name = "/", .list = MR_LIST_INIT(&_root_device.list), .clist = MR_LIST_INIT(&_root_device.clist), .parent = NULL}; #ifndef MR_CFG_DESCRIPTOR_MAX #define MR_CFG_DESCRIPTOR_MAX (128) #endif /* MR_CFG_DESCRIPTOR_MAX */ static struct mr_descriptor _descriptor_map[MR_CFG_DESCRIPTOR_MAX] = {0}; MR_INLINE struct mr_device *_device_find_from(const char *name, struct mr_device *parent) { /* Deal with special names */ if (strcmp(name, "..") == 0) { return parent->parent; } else if (strcmp(name, ".") == 0) { return parent; } /* Find the device with the specified name */ for (struct mr_list *list = parent->clist.next; list != &parent->clist; list = list->next) { struct mr_device *iter = MR_CONTAINER_OF(list, struct mr_device, list); if (strcmp(name, iter->name) == 0) { return iter; } } return NULL; } MR_INLINE struct mr_device *_device_next_find(const char **path, struct mr_device *parent) { /* Skip the leading '/' */ if ((*path)[0] == '/') { (*path)++; } /* Find the next path */ const char *next_path = strchr((*path), '/'); if (next_path != NULL) { char name[sizeof(parent->name) + 1]; size_t len = MR_BOUND(next_path - (*path), 0, sizeof(name)); strncpy(name, *path, len); name[len] = '\0'; *path = next_path + 1; /* Find the next device */ return _device_find_from(name, parent); } else { /* Without next */ return NULL; } } static int _device_register_iter(struct mr_device *device, const char *path, struct mr_device *parent) { struct mr_device *next_parent = _device_next_find(&path, parent); if (next_parent != NULL) { return _device_register_iter(device, path, next_parent); } else { /* Check if the device exists */ if (_device_find_from(path, parent) != NULL) { return MR_EEXIST; } /* Register the device */ device->magic = _MAGIC_NUMBER; strncpy(device->name, path, sizeof(device->name)); mr_list_insert_before(&parent->clist, &device->list); device->parent = parent; return MR_EOK; } } static struct mr_device *_device_find_iter(const char *path, struct mr_device *parent) { struct mr_device *next_parent = _device_next_find(&path, parent); if (next_parent != NULL) { return _device_find_iter(path, next_parent); } else { return _device_find_from(path, parent); } } MR_INLINE struct mr_device *_device_find(const char *path) { struct mr_device *device; /* Critical section enter */ mr_critical_enter(); /* Find the device */ device = _device_find_iter(path, &_root_device); /* Critical section exit */ mr_critical_exit(); return device; } MR_INLINE bool _device_flags_is_valid(struct mr_device *device, uint32_t flags) { return MR_BIT_IS_SET(device->flags, flags); } MR_INLINE bool _descriptor_is_valid(int descriptor) { return (descriptor >= 0) && (descriptor < MR_ARRAY_NUM(_descriptor_map)) && (_descriptor_map[descriptor].device != NULL); } MR_INLINE bool _descriptor_flags_is_valid(int descriptor, uint32_t flags) { return MR_BIT_IS_SET(_descriptor_map[descriptor].flags, flags); } MR_INLINE int _device_take(struct mr_device *device, int descriptor, uint32_t mask) { uint32_t lock; int ret; /* If the device is not FDX, the read/write must be locked */ mask = (device->fdx == true) ? mask : _LOCK_ALL_MASK; /* Calculate the lock mask, since the descriptor can be 0, need to add 1 */ lock = (((descriptor + 1) << 16) | (descriptor + 1)) & mask; /* Critical section enter */ mr_critical_enter(); if (_descriptor_is_valid(descriptor) == false) { ret = MR_EINVAL; } else if ((device->lock & mask) == 0) { MR_BIT_SET(device->lock, lock); ret = MR_EOK; } else { ret = MR_EBUSY; } /* Critical section exit */ mr_critical_exit(); return ret; } MR_INLINE void _device_release(struct mr_device *device, uint32_t mask) { /* If the device is not FDX, the read/write must be locked */ mask = (device->fdx == true) ? mask : _LOCK_ALL_MASK; /* Release the device lock */ MR_BIT_CLR(device->lock, mask); } MR_INLINE int _device_event_create(struct mr_device *device, int descriptor, struct mr_device_event *event) { struct _device_event *_event; int ret; /* Critical section enter */ mr_critical_enter(); /* Check if the event exists */ for (struct mr_list *list = device->event_list.next; list != &device->event_list; list = list->next) { _event = MR_CONTAINER_OF(list, struct _device_event, list); if ((_event->descriptor == descriptor) && (_event->event == event->event)) { ret = MR_EEXIST; goto _exit; } } /* Create the new event */ _event = mr_malloc(sizeof(struct _device_event)); if (_event == NULL) { ret = MR_ENOMEM; goto _exit; } /* Add the event */ mr_list_init(&_event->list); _event->descriptor = descriptor; _event->event = event->event; _event->callback = event->callback; mr_list_insert_before(&device->event_list, &_event->list); ret = MR_EOK; _exit: /* Critical section exit */ mr_critical_exit(); return ret; } MR_INLINE int _device_event_destroy(struct mr_device *device, int descriptor, struct mr_device_event *event) { int ret; /* Critical section enter */ mr_critical_enter(); /* Find the event */ for (struct mr_list *list = device->event_list.next; list != &device->event_list; list = list->next) { struct _device_event *_event = MR_CONTAINER_OF(list, struct _device_event, list); if ((_event->descriptor == descriptor) && (_event->event == event->event)) { /* Destroy the event */ mr_list_remove(&_event->list); mr_free(_event); ret = MR_EOK; goto _exit; } } /* Not found the event */ ret = MR_ENOENT; _exit: /* Critical section exit */ mr_critical_exit(); return ret; } MR_INLINE void _device_event_destroy_all(struct mr_device *device, int descriptor) { /* Critical section enter */ mr_critical_enter(); /* Destroy all events for the specified descriptor */ for (struct mr_list *list = device->event_list.next; list != &device->event_list; list = list->next) { struct _device_event *_event = MR_CONTAINER_OF(list, struct _device_event, list); if (_event->descriptor == descriptor) { /* Destroy the event */ list = list->prev; mr_list_remove(&_event->list); mr_free(_event); } } /* Critical section exit */ mr_critical_exit(); } MR_INLINE void _device_event_handler(struct mr_device *device, uint32_t event, void *args) { for (struct mr_list *list = device->event_list.next; list != &device->event_list; list = list->next) { struct _device_event *_event = MR_CONTAINER_OF(list, struct _device_event, list); /* Call the callback if the event matches */ if (_event->event == event) { _event->callback(_event->descriptor, _event->event, args); } } } MR_INLINE int _descriptor_allocate(struct mr_device *device, uint32_t flags) { int descriptor = -1; /* Check if the flags are valid */ if (_device_flags_is_valid(device, flags) == false) { return MR_EPERM; } /* Critical section enter */ mr_critical_enter(); /* Try to allocate the descriptor */ for (int i = 0; i < MR_ARRAY_NUM(_descriptor_map); i++) { if (_descriptor_map[i].device == NULL) { _descriptor_map[i].device = device; _descriptor_map[i].flags = flags; _descriptor_map[i].pos = -1; descriptor = i; break; } } /* Critical section exit */ mr_critical_exit(); /* Return the descriptor or error */ return (descriptor >= 0) ? descriptor : MR_ENOMEM; } MR_INLINE void _descriptor_free(int descriptor) { /* Critical section enter */ mr_critical_enter(); /* Free the descriptor */ _descriptor_map[descriptor].device = NULL; /* Critical section exit */ mr_critical_exit(); } static int _device_register(struct mr_device *device, const char *path) { int ret; /* Critical section enter */ mr_critical_enter(); ret = _device_register_iter(device, path, &_root_device); /* Critical section exit */ mr_critical_exit(); return ret; } static int _device_unregister(struct mr_device *device) { int ret; /* Critical section enter */ mr_critical_enter(); /* Unregister the device only when there are no more references to it */ if (device->ref_count == 0) { device->magic = 0; mr_list_remove(&device->list); device->parent = NULL; ret = MR_EOK; } else { ret = MR_EBUSY; } /* Critical section exit */ mr_critical_exit(); return ret; } static int _device_isr(struct mr_device *device, uint32_t event, void *args) { uint32_t mask; int ret; /* Critical section enter */ mr_critical_enter(); /* If the device has no references, cannot handle the ISR */ if (device->ref_count == 0) { ret = MR_EPERM; goto _exit; } /* Call the device ISR */ if (device->ops->isr != NULL) { ret = device->ops->isr(device, event, args); if (ret < 0) { goto _exit; } } /* Release the device based on event */ mask = (event & MR_EVENT_RD_COMPLETE) ? _LOCK_RD_MASK : 0; mask |= (event & MR_EVENT_WR_COMPLETE) ? _LOCK_WR_MASK : 0; _device_release(device, mask); /* Call the event handler */ _device_event_handler(device, event, args); ret = MR_EOK; _exit: /* Critical section exit */ mr_critical_exit(); return ret; } static int _device_open(const char *path, uint32_t flags) { struct mr_device *device; int descriptor; int ret; /* Find the device */ device = _device_find(path); if (device == NULL) { return MR_ENOENT; } /* Allocate a descriptor */ descriptor = _descriptor_allocate(device, flags); if (descriptor < 0) { return descriptor; } /* Take the device */ ret = _device_take(device, descriptor, _LOCK_ALL_MASK); if (ret < 0) { _descriptor_free(descriptor); return ret; } /* Device will only be opened the first time it is accessed */ if ((device->ref_count == 0) && (device->ops->open != NULL)) { ret = device->ops->open(device); if (ret < 0) { _descriptor_free(descriptor); goto _exit; } } /* Increment the reference count */ device->ref_count++; /* Return the descriptor */ ret = descriptor; _exit: /* Release the device */ _device_release(device, _LOCK_ALL_MASK); return ret; } static int _device_close(int descriptor) { struct mr_device *device; int ret; /* Find the device */ device = _descriptor_map[descriptor].device; if (device == NULL) { return MR_EINVAL; } /* Take the device */ ret = _device_take(device, descriptor, _LOCK_ALL_MASK); if (ret < 0) { return ret; } /* Device will only be closed the last time it is accessed */ if ((device->ref_count == 1) && (device->ops->close != NULL)) { ret = device->ops->close(device); if (ret < 0) { goto _exit; } } /* Decrement the reference count */ device->ref_count--; /* Destroy events associated with the descriptor */ _device_event_destroy_all(device, descriptor); /* Free the descriptor */ _descriptor_free(descriptor); _exit: /* Release the device */ _device_release(device, _LOCK_ALL_MASK); return ret; } static ssize_t _device_read(int descriptor, void *buf, size_t count) { struct mr_device *device; ssize_t ret; int pos; /* Find the device */ device = _descriptor_map[descriptor].device; if (device == NULL) { return MR_EINVAL; } /* Take the device */ ret = _device_take(device, descriptor, _LOCK_RD_MASK); if (ret < 0) { return ret; } /* Get the position */ pos = _descriptor_map[descriptor].pos; /* Async or sync read */ if (_descriptor_flags_is_valid(descriptor, MR_FLAG_RDONLY_ASYNC) == true) { /* Async read */ ret = device->ops->read_async(device, pos, buf, count); if (ret > 0) { /* If the operation is successful, the device will not be released * until the operation is finished */ return ret; } } else if (_descriptor_flags_is_valid(descriptor, MR_FLAG_RDONLY) == true) { /* Sync read */ ret = device->ops->read(device, pos, buf, count); } else { ret = MR_EACCES; } /* Release the device */ _device_release(device, _LOCK_RD_MASK); return ret; } static ssize_t _device_write(int descriptor, const void *buf, size_t count) { struct mr_device *device; ssize_t ret; int pos; /* Find the device */ device = _descriptor_map[descriptor].device; if (device == NULL) { return MR_EINVAL; } /* Take the device */ ret = _device_take(device, descriptor, _LOCK_WR_MASK); if (ret < 0) { return ret; } /* Get the position */ pos = _descriptor_map[descriptor].pos; /* Async or sync write */ if (_descriptor_flags_is_valid(descriptor, MR_FLAG_WRONLY_ASYNC) == true) { /* Async write */ ret = device->ops->write_async(device, pos, buf, count); if (ret > 0) { /* If the operation is successful, the device will not be released * until the operation is finished */ return ret; } } else if (_descriptor_flags_is_valid(descriptor, MR_FLAG_WRONLY) == true) { /* Sync write */ ret = device->ops->write(device, pos, buf, count); } else { ret = MR_EACCES; } /* Release the device */ _device_release(device, _LOCK_WR_MASK); return ret; } static int _device_ioctl(int descriptor, int cmd, void *args) { struct mr_device *device; int pos, ret; /* Find the device */ device = _descriptor_map[descriptor].device; if (device == NULL) { return MR_EINVAL; } /* Take the device */ ret = _device_take(device, descriptor, _LOCK_ALL_MASK); if (ret < 0) { return ret; } /* Get the position */ pos = _descriptor_map[descriptor].pos; /* Control the device */ switch (cmd) { case MR_CTRL_SET(MR_CMD_POS): { /* Check the argument */ if (args == NULL) { ret = MR_EINVAL; goto _exit; } /* Set the position */ _descriptor_map[descriptor].pos = *(int *)args; ret = MR_EOK; break; } case MR_CTRL_GET(MR_CMD_POS): { /* Check the argument */ if (args == NULL) { ret = MR_EINVAL; goto _exit; } /* Get the position */ *(int *)args = pos; ret = MR_EOK; break; } case MR_CTRL_NEW(MR_CMD_EVENT): { /* Check the argument */ if (args == NULL) { ret = MR_EINVAL; goto _exit; } /* Create the event */ ret = _device_event_create(device, descriptor,args); break; } case MR_CTRL_DEL(MR_CMD_EVENT): { /* Check the argument */ if (args == NULL) { ret = MR_EINVAL; goto _exit; } /* Destroy the event */ ret = _device_event_destroy(device, descriptor, args); break; } default: { /* CMD is defined by the device */ ret = device->ops->ioctl(device, pos, cmd, args); break; } } _exit: /* Release the device */ _device_release(device, _LOCK_ALL_MASK); return ret; } /** * @brief This function gets the descriptor map. * * @param descriptor_map The descriptor map. * * @return The count of the descriptor map. */ size_t _mr_descriptor_map_get(struct mr_descriptor **descriptor_map) { MR_ASSERT(descriptor_map != NULL); *descriptor_map = _descriptor_map; return sizeof(_descriptor_map) / sizeof(struct mr_descriptor); } /** * @brief This function register a device. * * @param device The device. * @param path The path of the device. * @param type The type of the device. * @param ops The operations of the device. * @param driver The driver of the device. * * @return The error code. */ int mr_device_register(struct mr_device *device, const char *path, uint32_t type, struct mr_device_ops *ops, const void *driver) { MR_ASSERT((device != NULL) && (device->magic != _MAGIC_NUMBER)); MR_ASSERT(path != NULL); static struct mr_device_ops null_ops = {NULL}; int ret; /* Set default ops if not specified */ ops = (ops == NULL) ? &null_ops : ops; /* Initialize the device */ mr_list_init(&device->list); mr_list_init(&device->clist); device->parent = NULL; device->type = type & (~MR_DEVICE_TYPE_FDX); device->fdx = MR_BIT_IS_SET(type, MR_DEVICE_TYPE_FDX); device->flags = (ops->read != NULL ? MR_FLAG_RDONLY : 0) | (ops->write != NULL ? MR_FLAG_WRONLY : 0) | (ops->read_async != NULL ? MR_FLAG_RDONLY_ASYNC : 0) | (ops->write_async != NULL ? MR_FLAG_WRONLY_ASYNC : 0); device->ref_count = 0; device->lock = 0; device->ops = ops; device->driver = driver; mr_list_init(&device->event_list); /* Register the device */ ret = _device_register(device, path); if (ret < 0) { goto _exit; } /* Return error code */ return ret; _exit: MR_LOG_E("Register '%s' failed: %s.\r\n", path, mr_strerror(ret)); return ret; } /** * @brief This function unregister a device. * * @param device The device. * * @return The error code. */ int mr_device_unregister(struct mr_device *device) { MR_ASSERT((device != NULL) && (device->magic == _MAGIC_NUMBER)); int ret; /* Unregister the device */ ret = _device_unregister(device); if (ret < 0) { goto _exit; } /* Return error code */ return ret; _exit: MR_LOG_E("Unregister '%s' failed: %s.\r\n", device->name, mr_strerror(ret)); return ret; } /** * @brief This function ISR of a device. * * @param device The device. * @param event The event of the ISR. * @param args The arguments. * * @return The error code. */ int mr_device_isr(struct mr_device *device, uint32_t event, void *args) { MR_ASSERT((device != NULL) && (device->magic == _MAGIC_NUMBER)); /* Call the device ISR */ return _device_isr(device, event, args); } /** * @brief This function open a device. * * @param device The device. * @param path The path of the device. * @param flags The flags of the device. * * @return The descriptor of the device on success, otherwise an error code. */ int mr_device_open(const char *path, uint32_t flags) { MR_ASSERT(path != NULL); MR_ASSERT(flags != 0); int ret; /* Open the device */ ret = _device_open(path, flags); if (ret < 0) { goto _exit; } /* Return the descriptor */ return ret; _exit: MR_LOG_E("Open '%s' failed: %s.\r\n", path, mr_strerror(ret)); return ret; } /** * @brief This function close a device. * * @param descriptor The descriptor of the device. * * @return The error code. */ int mr_device_close(int descriptor) { int ret; /* Check if the descriptor is valid */ if (_descriptor_is_valid(descriptor) == false) { return MR_EINVAL; } /* Close the device */ ret = _device_close(descriptor); if (ret < 0) { goto _exit; } /* Return error code */ return ret; _exit: MR_LOG_E("Close '%d' failed: %s.\r\n", descriptor, mr_strerror(ret)); return ret; } /** * @brief This function read data from a device. * * @param descriptor The descriptor of the device. * @param buf The buffer to be read. * @param count The count of read. * * @return The size of the actual read, otherwise an error code. * * @note If the async operation is successful, the device will not be released * until the operation is complete. */ ssize_t mr_device_read(int descriptor, void *buf, size_t count) { ssize_t ret; /* Check if the descriptor is valid */ if (_descriptor_is_valid(descriptor) == false) { return MR_EINVAL; } /* Read the device */ ret = _device_read(descriptor, buf, count); if (ret < 0) { goto _exit; } /* Return actual read size */ return ret; _exit: MR_LOG_E("Read '%d' failed: %s.\r\n", descriptor, mr_strerror((int)ret)); return ret; } /** * @brief This function write data to a device. * * @param descriptor The descriptor of the device. * @param buf The buffer to be written. * @param count The count of write. * * @return The size of the actual write, otherwise an error code. * * @note If the async operation is successful, the device will not be released * until the operation is complete. */ ssize_t mr_device_write(int descriptor, const void *buf, size_t count) { ssize_t ret; /* Check if the descriptor is valid */ if (_descriptor_is_valid(descriptor) == false) { return MR_EINVAL; } /* Write the device */ ret = _device_write(descriptor, buf, count); if (ret < 0) { goto _exit; } /* Return actual write size */ return ret; _exit: MR_LOG_E("Write '%d' failed: %s.\r\n", descriptor, mr_strerror((int)ret)); return ret; } /** * @brief This function ioctl a device. * * @param descriptor The descriptor of the device. * @param cmd The command of the ioctl. * @param args The arguments of the ioctl. * * @return The size of the actual ioctl, otherwise an error code. */ int mr_device_ioctl(int descriptor, int cmd, void *args) { int ret; /* Check if the descriptor is valid */ if (_descriptor_is_valid(descriptor) == false) { return MR_EINVAL; } /* Ioctl the device */ ret = _device_ioctl(descriptor, cmd, args); if (ret < 0) { goto _exit; } /* Return actual ioctl size */ return ret; _exit: MR_LOG_E("Ioctl '%d' failed: %s.\r\n", descriptor, mr_strerror(ret)); return ret; }