| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412 |
- /*
- * Copyright (c) 2006-2023, RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date Author Notes
- * 2023-02-25 GuEe-GUI the first version
- */
- #include <rtthread.h>
- #include <rtdevice.h>
- #define DBG_TAG "rpmsg.char"
- #define DBG_LVL DBG_INFO
- #include <rtdbg.h>
- struct rpmsg_char_ctrl;
- struct rpmsg_char
- {
- struct rt_device parent;
- rt_list_t list;
- struct rpmsg_char_ctrl *rchc;
- struct rt_rpmsg_endpoint *ept;
- rt_bool_t is_overwrite;
- struct rt_ringbuffer msg_ring;
- rt_uint8_t msg_pool[RT_RPMSG_CHAR_MSG_SIZE_MAX * RT_RPMSG_CHAR_MSG_MAX];
- };
- struct rpmsg_char_ctrl
- {
- struct rt_device parent;
- struct rt_rpmsg_device *rdev;
- rt_list_t ept_nodes;
- rt_list_t del_ept_nodes;
- struct rt_spinlock lock;
- struct rt_work del_ept_work;
- };
- #define raw_to_rpmsg_char(raw) rt_container_of(raw, struct rpmsg_char, parent)
- #define raw_to_rpmsg_char_ctrl(raw) rt_container_of(raw, struct rpmsg_char_ctrl, parent)
- static struct rt_dm_ida rpmsg_ept_ida = RT_DM_IDA_INIT(RPMSG_EPT);
- static struct rt_dm_ida rpmsg_char_ida = RT_DM_IDA_INIT(RPMSG_CHAR);
- static rt_err_t rpmsg_char_open(rt_device_t dev, rt_uint16_t oflag)
- {
- rt_ubase_t level;
- rt_err_t err = RT_EOK;
- struct rpmsg_char_ctrl *rchc;
- struct rpmsg_char *this_rch = raw_to_rpmsg_char(dev), *rch, *rch_next;
- rchc = this_rch->rchc;
- level = rt_spin_lock_irqsave(&rchc->lock);
- rt_list_for_each_entry_safe(rch, rch_next, &rchc->del_ept_nodes, list)
- {
- if (rch == this_rch)
- {
- /* It's been cleaned. Don't open it. */
- err = -RT_EIO;
- break;
- }
- }
- rt_spin_unlock_irqrestore(&rchc->lock, level);
- return err;
- }
- static rt_ssize_t rpmsg_char_read(rt_device_t dev,
- rt_off_t pos, void *buffer, rt_size_t size)
- {
- struct rpmsg_char *rch = raw_to_rpmsg_char(dev);
- return rt_ringbuffer_get(&rch->msg_ring, buffer, size);
- }
- static rt_ssize_t rpmsg_char_write(rt_device_t dev,
- rt_off_t pos, const void *buffer, rt_size_t size)
- {
- struct rpmsg_char *rch = raw_to_rpmsg_char(dev);
- return rt_rpmsg_send(rch->ept, buffer, size) ? : size;
- }
- static rt_err_t rpmsg_char_control(rt_device_t dev, int cmd, void *args)
- {
- struct rpmsg_char *rch = raw_to_rpmsg_char(dev);
- if (cmd == RT_DEVICE_CTRL_RPMSG_DESTROY_EPT)
- {
- if (dev->ref_count == 1)
- {
- rt_ubase_t level;
- level = rt_spin_lock_irqsave(&rch->rchc->lock);
- rt_list_remove(&rch->list);
- rt_list_insert_before(&rch->rchc->del_ept_nodes, &rch->list);
- rt_spin_unlock_irqrestore(&rch->rchc->lock, level);
- rt_work_submit(&rch->rchc->del_ept_work,
- RT_SCHED_PRIV(rt_thread_self()).remaining_tick);
- }
- return RT_EOK;
- }
- if (cmd == RT_DEVICE_CTRL_RPMSG_DATA_OVERWRITE)
- {
- rch->is_overwrite = !!args;
- return RT_EOK;
- }
- return -RT_EINVAL;
- }
- #ifdef RT_USING_DEVICE_OPS
- const static struct rt_device_ops rpmsg_char_ops =
- {
- .open = rpmsg_char_open,
- .read = rpmsg_char_read,
- .write = rpmsg_char_write,
- .control = rpmsg_char_control,
- };
- #endif
- static rt_err_t rpmsg_char_rx_callback(struct rt_rpmsg_device *rdev,
- rt_uint32_t src, void *data, rt_size_t len)
- {
- rt_size_t res_size;
- struct rpmsg_char *rch;
- struct rt_rpmsg_endpoint *ept;
- struct rt_rpmsg_endpoint_info info;
- RT_ASSERT(len <= RT_RPMSG_CHAR_MSG_SIZE_MAX);
- info.src = RT_RPMSG_ADDR_ANY;
- info.dst = src;
- info.name[0] = '\0';
- ept = rt_rpmsg_find_endpoint(rdev, &info);
- if (ept)
- {
- rch = ept->priv;
- if (rch->is_overwrite)
- {
- res_size = rt_ringbuffer_put_force(&rch->msg_ring, data, len);
- }
- else
- {
- res_size = rt_ringbuffer_put(&rch->msg_ring, data, len);
- }
- }
- else
- {
- return -RT_EINVAL;
- }
- return res_size ? RT_EOK : -RT_ENOMEM;
- }
- static void rpmsg_char_ctrl_del_ept_work(struct rt_work *work, void *work_data)
- {
- rt_ubase_t level;
- rt_size_t clean_count = 0;
- struct rpmsg_char *rch, *rch_next;
- struct rpmsg_char_ctrl *rchc = work_data;
- level = rt_spin_lock_irqsave(&rchc->lock);
- rt_list_for_each_entry_safe(rch, rch_next, &rchc->del_ept_nodes, list)
- {
- if (rch->parent.open_flag == RT_DEVICE_OFLAG_CLOSE)
- {
- rt_list_remove(&rch->list);
- rt_spin_unlock_irqrestore(&rchc->lock, level);
- rt_rpmsg_destroy_endpoint(rchc->rdev, rch->ept);
- rt_dm_ida_free(&rpmsg_ept_ida, rch->parent.device_id);
- rt_device_unregister(&rch->parent);
- rt_free(rch);
- level = rt_spin_lock_irqsave(&rchc->lock);
- ++clean_count;
- }
- }
- rt_spin_unlock_irqrestore(&rchc->lock, level);
- if (!clean_count)
- {
- /* Try again */
- rt_work_submit(&rchc->del_ept_work, RT_TICK_PER_SECOND);
- }
- }
- static rt_err_t rpmsg_char_ctrl_control(rt_device_t dev, int cmd, void *args)
- {
- struct rpmsg_char_ctrl *rchc = raw_to_rpmsg_char_ctrl(dev);
- if (cmd == RT_DEVICE_CTRL_RPMSG_CREATE_EPT && args)
- {
- int device_id;
- rt_ubase_t level;
- struct rpmsg_char *rch;
- struct rt_rpmsg_endpoint *ept;
- struct rt_rpmsg_endpoint_info *info = args;
- if (!info->name[0])
- {
- rt_strncpy(info->name, "rpmsg-raw", RT_RPMSG_NAME_SIZE);
- }
- ept = rt_rpmsg_create_endpoint(rchc->rdev, info, &rpmsg_char_rx_callback);
- if (rt_is_err(ept))
- {
- return rt_ptr_err(ept);
- }
- rch = rt_calloc(1, sizeof(*rch));
- if (!rch)
- {
- rt_rpmsg_destroy_endpoint(rchc->rdev, ept);
- return -RT_ENOMEM;
- }
- if ((device_id = rt_dm_ida_alloc(&rpmsg_ept_ida)) < 0)
- {
- rt_free(rch);
- rt_rpmsg_destroy_endpoint(rchc->rdev, ept);
- return -RT_EFULL;
- }
- ept->priv = rch;
- rch->ept = ept;
- rch->rchc = rchc;
- rch->parent.type = RT_Device_Class_Char;
- #ifdef RT_USING_DEVICE_OPS
- rch->parent.ops = &rpmsg_char_ops;
- #else
- rch->parent.read = rpmsg_char_read;
- rch->parent.write = rpmsg_char_write;
- rch->parent.control = rpmsg_char_control;
- #endif
- rch->parent.master_id = rpmsg_ept_ida.master_id;
- rch->parent.device_id = device_id;
- rt_ringbuffer_init(&rch->msg_ring, rch->msg_pool, sizeof(rch->msg_pool));
- rt_dm_dev_set_name(&rch->parent, "rpmsg_%ux%u", ept->info.src, ept->info.dst);
- rt_list_init(&rch->list);
- level = rt_spin_lock_irqsave(&rchc->lock);
- rt_list_insert_before(&rchc->ept_nodes, &rch->list);
- rt_spin_unlock_irqrestore(&rchc->lock, level);
- rt_device_register(&rch->parent, rt_dm_dev_get_name(&rch->parent),
- RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_REMOVABLE);
- return RT_EOK;
- }
- return -RT_EINVAL;
- }
- #ifdef RT_USING_DEVICE_OPS
- const static struct rt_device_ops rpmsg_char_ctrl_ops =
- {
- .control = rpmsg_char_ctrl_control,
- };
- #endif
- static rt_err_t rpmsg_char_probe(struct rt_rpmsg_device *rdev)
- {
- rt_err_t err;
- int device_id;
- struct rpmsg_char_ctrl *rchc = rt_calloc(1, sizeof(*rchc));
- if (!rchc)
- {
- return -RT_ENOMEM;
- }
- if ((device_id = rt_dm_ida_alloc(&rpmsg_char_ida)) < 0)
- {
- err = -RT_EFULL;
- goto _free_dev;
- }
- rchc->rdev = rdev;
- rdev->parent.user_data = rchc;
- rt_list_init(&rchc->ept_nodes);
- rt_list_init(&rchc->del_ept_nodes);
- rt_spin_lock_init(&rchc->lock);
- rt_work_init(&rchc->del_ept_work, rpmsg_char_ctrl_del_ept_work, rchc);
- rt_dm_dev_set_name(&rchc->parent, "rpmsg_char%u", device_id);
- rchc->parent.type = RT_Device_Class_Char;
- #ifdef RT_USING_DEVICE_OPS
- rchc->parent.ops = &rpmsg_char_ctrl_ops;
- #else
- rchc->parent.control = rpmsg_char_ctrl_control;
- #endif
- rchc->parent.master_id = rpmsg_char_ida.master_id;
- rchc->parent.device_id = device_id;
- if ((err = rt_device_register(&rchc->parent, rt_dm_dev_get_name(&rchc->parent),
- RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_REMOVABLE)))
- {
- goto _fail;
- }
- return RT_EOK;
- _fail:
- rt_dm_ida_free(&rpmsg_char_ida, device_id);
- _free_dev:
- rt_free(rchc);
- return err;
- }
- static rt_err_t rpmsg_char_remove(struct rt_rpmsg_device *rdev)
- {
- rt_ubase_t level;
- struct rpmsg_char *rch, *rch_next;
- struct rpmsg_char_ctrl *rchc = rdev->parent.user_data;
- rt_work_cancel(&rchc->del_ept_work);
- level = rt_spin_lock_irqsave(&rchc->lock);
- rt_list_for_each_entry_safe(rch, rch_next, &rchc->ept_nodes, list)
- {
- rt_list_remove(&rch->list);
- rt_spin_unlock_irqrestore(&rchc->lock, level);
- rt_rpmsg_destroy_endpoint(rchc->rdev, rch->ept);
- rt_dm_ida_free(&rpmsg_ept_ida, rch->parent.device_id);
- rt_device_unregister(&rch->parent);
- rt_free(rch);
- level = rt_spin_lock_irqsave(&rchc->lock);
- }
- rt_list_for_each_entry_safe(rch, rch_next, &rchc->del_ept_nodes, list)
- {
- rt_list_remove(&rch->list);
- rt_spin_unlock_irqrestore(&rchc->lock, level);
- rt_rpmsg_destroy_endpoint(rchc->rdev, rch->ept);
- rt_dm_ida_free(&rpmsg_ept_ida, rch->parent.device_id);
- rt_device_unregister(&rch->parent);
- rt_free(rch);
- level = rt_spin_lock_irqsave(&rchc->lock);
- }
- rt_spin_unlock_irqrestore(&rchc->lock, level);
- rt_dm_ida_free(&rpmsg_char_ida, rchc->parent.device_id);
- rt_device_unregister(&rchc->parent);
- rt_free(rchc);
- return RT_EOK;
- }
- static struct rt_rpmsg_device_id rpmsg_char_ids[] =
- {
- { .name = "rpmsg-raw" },
- { .name = "rpmsg-char" },
- { /* sentinel */ }
- };
- static struct rt_rpmsg_driver rpmsg_char_driver =
- {
- .parent.parent =
- {
- .name = "rpmsg-char",
- },
- .ids = rpmsg_char_ids,
- .probe = rpmsg_char_probe,
- .remove = rpmsg_char_remove,
- };
- RT_RPMSG_DRIVER_EXPORT(rpmsg_char_driver);
|