/**************************************************************************** * drivers/rptun/rptun.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "rptun.h" /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ #ifndef ALIGN_UP # define ALIGN_UP(s, a) (((s) + (a) - 1) & ~((a) - 1)) #endif #define RPTUNIOC_NONE 0 /**************************************************************************** * Private Types ****************************************************************************/ struct rptun_priv_s { FAR struct rptun_dev_s *dev; struct remoteproc rproc; struct rpmsg_virtio_device rvdev; struct rpmsg_virtio_shm_pool pool[2]; struct metal_list bind; rmutex_t lock; struct metal_list node; sem_t semtx; unsigned long cmd; #ifdef CONFIG_RPTUN_WORKQUEUE struct work_s work; #else sem_t semrx; pid_t tid; #endif #ifdef CONFIG_RPTUN_PM bool stay; #endif #ifdef CONFIG_RPTUN_PING struct rpmsg_endpoint ping; #endif }; struct rptun_bind_s { char name[RPMSG_NAME_SIZE]; uint32_t dest; struct metal_list node; }; struct rptun_cb_s { FAR void *priv; rpmsg_dev_cb_t device_created; rpmsg_dev_cb_t device_destroy; rpmsg_match_cb_t ns_match; rpmsg_bind_cb_t ns_bind; struct metal_list node; }; struct rptun_store_s { struct file file; FAR char *buf; }; /**************************************************************************** * Private Function Prototypes ****************************************************************************/ static FAR struct remoteproc *rptun_init(FAR struct remoteproc *rproc, FAR const struct remoteproc_ops *ops, FAR void *arg); static void rptun_remove(FAR struct remoteproc *rproc); static int rptun_config(struct remoteproc *rproc, void *data); static int rptun_start(FAR struct remoteproc *rproc); static int rptun_stop(FAR struct remoteproc *rproc); static int rptun_notify(FAR struct remoteproc *rproc, uint32_t id); static FAR struct remoteproc_mem * rptun_get_mem(FAR struct remoteproc *rproc, FAR const char *name, metal_phys_addr_t pa, metal_phys_addr_t da, FAR void *va, size_t size, FAR struct remoteproc_mem *buf); static int rptun_notify_wait(FAR struct remoteproc *rproc, uint32_t id); static void rptun_ns_bind(FAR struct rpmsg_device *rdev, FAR const char *name, uint32_t dest); static int rptun_dev_start(FAR struct remoteproc *rproc); static int rptun_dev_stop(FAR struct remoteproc *rproc); static int rptun_dev_ioctl(FAR struct file *filep, int cmd, unsigned long arg); #ifdef CONFIG_RPTUN_LOADER static int rptun_store_open(FAR void *store_, FAR const char *path, FAR const void **img_data); static void rptun_store_close(FAR void *store_); static int rptun_store_load(FAR void *store_, size_t offset, size_t size, FAR const void **data, metal_phys_addr_t pa, FAR struct metal_io_region *io, char is_blocking); #endif static metal_phys_addr_t rptun_pa_to_da(FAR struct rptun_dev_s *dev, metal_phys_addr_t pa); static metal_phys_addr_t rptun_da_to_pa(FAR struct rptun_dev_s *dev, metal_phys_addr_t da); /**************************************************************************** * Private Data ****************************************************************************/ static const struct remoteproc_ops g_rptun_ops = { .init = rptun_init, .remove = rptun_remove, .config = rptun_config, .start = rptun_start, .stop = rptun_stop, .notify = rptun_notify, .get_mem = rptun_get_mem, .notify_wait = rptun_notify_wait, }; static const struct file_operations g_rptun_devops = { NULL, /* open */ NULL, /* close */ NULL, /* read */ NULL, /* write */ NULL, /* seek */ rptun_dev_ioctl, /* ioctl */ }; #ifdef CONFIG_RPTUN_LOADER static const struct image_store_ops g_rptun_storeops = { .open = rptun_store_open, .close = rptun_store_close, .load = rptun_store_load, .features = SUPPORT_SEEK, }; #endif static METAL_DECLARE_LIST(g_rptun_cb); static METAL_DECLARE_LIST(g_rptun_priv); static rmutex_t g_rptun_lockcb = NXRMUTEX_INITIALIZER; /**************************************************************************** * Private Functions ****************************************************************************/ #ifdef CONFIG_RPTUN_PM static inline void rptun_pm_action(FAR struct rptun_priv_s *priv, bool stay) { irqstate_t flags; flags = enter_critical_section(); if (stay && !priv->stay) { pm_stay(PM_IDLE_DOMAIN, PM_IDLE); priv->stay = true; } if (!stay && priv->stay && !rptun_buffer_nused(&priv->rvdev, false)) { pm_relax(PM_IDLE_DOMAIN, PM_IDLE); priv->stay = false; } leave_critical_section(flags); } #else # define rptun_pm_action(priv, stay) #endif static void rptun_worker(FAR void *arg) { FAR struct rptun_priv_s *priv = arg; switch (priv->cmd) { case RPTUNIOC_START: if (priv->rproc.state == RPROC_OFFLINE) { rptun_dev_start(&priv->rproc); } break; case RPTUNIOC_STOP: if (priv->rproc.state != RPROC_OFFLINE) { rptun_dev_stop(&priv->rproc); } break; } priv->cmd = RPTUNIOC_NONE; remoteproc_get_notification(&priv->rproc, RPTUN_NOTIFY_ALL); } #ifdef CONFIG_RPTUN_WORKQUEUE static void rptun_wakeup_rx(FAR struct rptun_priv_s *priv) { work_queue(HPWORK, &priv->work, rptun_worker, priv, 0); } static void rptun_in_recursive(int tid, FAR void *arg) { *((FAR bool *)arg) = (nxsched_gettid() == tid); } static bool rptun_is_recursive(FAR struct rptun_priv_s *priv) { bool in = false; work_foreach(HPWORK, rptun_in_recursive, &in); return in; } #else static int rptun_thread(int argc, FAR char *argv[]) { FAR struct rptun_priv_s *priv; priv = (FAR struct rptun_priv_s *)((uintptr_t)strtoul(argv[2], NULL, 16)); priv->tid = nxsched_gettid(); while (1) { nxsem_wait_uninterruptible(&priv->semrx); rptun_worker(priv); } return 0; } static void rptun_wakeup_rx(FAR struct rptun_priv_s *priv) { int semcount; nxsem_get_value(&priv->semrx, &semcount); if (semcount < 1) { nxsem_post(&priv->semrx); } } static bool rptun_is_recursive(FAR struct rptun_priv_s *priv) { return nxsched_gettid() == priv->tid; } #endif static void rptun_wakeup_tx(FAR struct rptun_priv_s *priv) { int semcount; nxsem_get_value(&priv->semtx, &semcount); while (semcount++ < 1) { nxsem_post(&priv->semtx); } } static int rptun_callback(FAR void *arg, uint32_t vqid) { FAR struct rptun_priv_s *priv = arg; FAR struct rpmsg_virtio_device *rvdev = &priv->rvdev; FAR struct virtio_device *vdev = rvdev->vdev; FAR struct virtqueue *svq = rvdev->svq; FAR struct virtqueue *rvq = rvdev->rvq; if (vqid == RPTUN_NOTIFY_ALL || vqid == vdev->vrings_info[rvq->vq_queue_index].notifyid) { if (rptun_buffer_nused(&priv->rvdev, true)) { rptun_wakeup_rx(priv); } } if (vqid == RPTUN_NOTIFY_ALL || vqid == vdev->vrings_info[svq->vq_queue_index].notifyid) { rptun_wakeup_tx(priv); rptun_pm_action(priv, false); } return OK; } static FAR struct remoteproc *rptun_init(FAR struct remoteproc *rproc, FAR const struct remoteproc_ops *ops, FAR void *arg) { rproc->ops = ops; rproc->priv = arg; return rproc; } static void rptun_remove(FAR struct remoteproc *rproc) { rproc->priv = NULL; } static int rptun_config(struct remoteproc *rproc, void *data) { struct rptun_priv_s *priv = rproc->priv; if (RPTUN_IS_MASTER(priv->dev)) { return RPTUN_CONFIG(priv->dev, data); } return 0; } static int rptun_start(FAR struct remoteproc *rproc) { FAR struct rptun_priv_s *priv = rproc->priv; if (RPTUN_IS_MASTER(priv->dev)) { return RPTUN_START(priv->dev); } return 0; } static int rptun_stop(FAR struct remoteproc *rproc) { FAR struct rptun_priv_s *priv = rproc->priv; if (RPTUN_IS_MASTER(priv->dev)) { return RPTUN_STOP(priv->dev); } return 0; } static int rptun_notify(FAR struct remoteproc *rproc, uint32_t id) { FAR struct rptun_priv_s *priv = rproc->priv; FAR struct rpmsg_virtio_device *rvdev = &priv->rvdev; FAR struct virtqueue *vq = rvdev->svq; if (rvdev->vdev && vq && rvdev->vdev->vrings_info[vq->vq_queue_index].notifyid == id) { rptun_pm_action(priv, true); } RPTUN_NOTIFY(priv->dev, id); return 0; } static FAR struct remoteproc_mem * rptun_get_mem(FAR struct remoteproc *rproc, FAR const char *name, metal_phys_addr_t pa, metal_phys_addr_t da, FAR void *va, size_t size, FAR struct remoteproc_mem *buf) { FAR struct rptun_priv_s *priv = rproc->priv; metal_list_init(&buf->node); strlcpy(buf->name, name ? name : "", RPROC_MAX_NAME_LEN); buf->io = metal_io_get_region(); buf->size = size; if (pa != METAL_BAD_PHYS) { buf->pa = pa; buf->da = rptun_pa_to_da(priv->dev, pa); } else if (da != METAL_BAD_PHYS) { buf->pa = rptun_da_to_pa(priv->dev, da); buf->da = da; } else { buf->pa = metal_io_virt_to_phys(buf->io, va); buf->da = rptun_pa_to_da(priv->dev, buf->pa); } if (buf->pa == METAL_BAD_PHYS || buf->da == METAL_BAD_PHYS) { return NULL; } return buf; } static int rptun_notify_wait(FAR struct remoteproc *rproc, uint32_t id) { FAR struct rptun_priv_s *priv = rproc->priv; if (!rptun_is_recursive(priv)) { return -EAGAIN; } /* Wait to wakeup */ nxsem_wait(&priv->semtx); rptun_worker(priv); return 0; } static void *rptun_get_priv_by_rdev(FAR struct rpmsg_device *rdev) { struct rpmsg_virtio_device *rvdev; struct virtio_device *vdev; struct remoteproc_virtio *rpvdev; struct remoteproc *rproc; if (!rdev) { return NULL; } rvdev = metal_container_of(rdev, struct rpmsg_virtio_device, rdev); vdev = rvdev->vdev; if (!vdev) { return NULL; } rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev); rproc = rpvdev->priv; if (!rproc) { return NULL; } return rproc->priv; } static void rptun_ns_bind(FAR struct rpmsg_device *rdev, FAR const char *name, uint32_t dest) { FAR struct rptun_priv_s *priv = rptun_get_priv_by_rdev(rdev); FAR struct rptun_bind_s *bind; FAR struct metal_list *node; nxrmutex_lock(&g_rptun_lockcb); metal_list_for_each(&g_rptun_cb, node) { FAR struct rptun_cb_s *cb; cb = metal_container_of(node, struct rptun_cb_s, node); if (cb->ns_match && cb->ns_match(rdev, cb->priv, name, dest)) { rpmsg_bind_cb_t ns_bind = cb->ns_bind; FAR void *cb_priv = cb->priv; nxrmutex_unlock(&g_rptun_lockcb); DEBUGASSERT(ns_bind != NULL); ns_bind(rdev, cb_priv, name, dest); return; } } nxrmutex_unlock(&g_rptun_lockcb); bind = kmm_malloc(sizeof(struct rptun_bind_s)); if (bind == NULL) { return; } bind->dest = dest; strlcpy(bind->name, name, RPMSG_NAME_SIZE); nxrmutex_lock(&priv->lock); metal_list_add_tail(&priv->bind, &bind->node); nxrmutex_unlock(&priv->lock); } static void rptun_ns_unbind(FAR struct rpmsg_device *rdev, FAR const char *name, uint32_t dest) { FAR struct rptun_priv_s *priv = rptun_get_priv_by_rdev(rdev); FAR struct metal_list *node; nxrmutex_lock(&priv->lock); metal_list_for_each(&priv->bind, node) { struct rptun_bind_s *bind; bind = metal_container_of(node, struct rptun_bind_s, node); if (bind->dest == dest && !strncmp(bind->name, name, RPMSG_NAME_SIZE)) { metal_list_del(node); kmm_free(bind); break; } } nxrmutex_unlock(&priv->lock); } static int rptun_dev_start(FAR struct remoteproc *rproc) { FAR struct rptun_priv_s *priv = rproc->priv; FAR struct virtio_device *vdev; FAR struct rptun_rsc_s *rsc; FAR struct metal_list *node; FAR struct rptun_cb_s *cb; unsigned int role = RPMSG_REMOTE; int ret; ret = remoteproc_config(rproc, NULL); if (ret) { return ret; } #ifdef CONFIG_RPTUN_LOADER if (RPTUN_GET_FIRMWARE(priv->dev)) { struct rptun_store_s store = { 0 }; ret = remoteproc_load(rproc, RPTUN_GET_FIRMWARE(priv->dev), &store, &g_rptun_storeops, NULL); if (ret) { return ret; } rsc = rproc->rsc_table; } else #endif { rsc = RPTUN_GET_RESOURCE(priv->dev); if (!rsc) { return -EINVAL; } ret = remoteproc_set_rsc_table(rproc, (struct resource_table *)rsc, sizeof(struct rptun_rsc_s)); if (ret) { return ret; } } /* Update resource table on MASTER side */ if (RPTUN_IS_MASTER(priv->dev)) { uint32_t tbsz; uint32_t v0sz; uint32_t v1sz; uint32_t shbufsz; metal_phys_addr_t da0; metal_phys_addr_t da1; uint32_t align0; uint32_t align1; FAR void *va0; FAR void *va1; FAR void *shbuf; FAR struct metal_io_region *io; metal_phys_addr_t pa0; metal_phys_addr_t pa1; align0 = rsc->rpmsg_vring0.align; align1 = rsc->rpmsg_vring1.align; v0sz = ALIGN_UP(vring_size(rsc->rpmsg_vring0.num, align0), align0); v1sz = ALIGN_UP(vring_size(rsc->rpmsg_vring1.num, align1), align1); if (rsc->rpmsg_vring0.da == 0 || rsc->rpmsg_vring1.da == 0) { tbsz = ALIGN_UP(sizeof(struct rptun_rsc_s), MAX(align0, align1)); va0 = (FAR char *)rsc + tbsz; va1 = (FAR char *)rsc + tbsz + v0sz; io = metal_io_get_region(); pa0 = metal_io_virt_to_phys(io, va0); pa1 = metal_io_virt_to_phys(io, va1); da0 = da1 = METAL_BAD_PHYS; remoteproc_mmap(rproc, &pa0, &da0, v0sz, 0, NULL); remoteproc_mmap(rproc, &pa1, &da1, v1sz, 0, NULL); rsc->rpmsg_vring0.da = da0; rsc->rpmsg_vring1.da = da1; shbuf = (FAR char *)rsc + tbsz + v0sz + v1sz; shbufsz = rsc->config.r2h_buf_size * rsc->rpmsg_vring0.num + rsc->config.h2r_buf_size * rsc->rpmsg_vring1.num; rpmsg_virtio_init_shm_pool(priv->pool, shbuf, shbufsz); } else { da0 = rsc->rpmsg_vring0.da; shbuf = (FAR char *)remoteproc_mmap(rproc, NULL, &da0, v0sz, 0, NULL) + v0sz; shbufsz = rsc->config.r2h_buf_size * rsc->rpmsg_vring0.num; rpmsg_virtio_init_shm_pool(&priv->pool[0], shbuf, shbufsz); da1 = rsc->rpmsg_vring1.da; shbuf = (FAR char *)remoteproc_mmap(rproc, NULL, &da1, v1sz, 0, NULL) + v1sz; shbufsz = rsc->config.h2r_buf_size * rsc->rpmsg_vring1.num; rpmsg_virtio_init_shm_pool(&priv->pool[1], shbuf, shbufsz); } role = RPMSG_HOST; } /* Remote proc create */ vdev = remoteproc_create_virtio(rproc, 0, role, NULL); if (!vdev) { return -ENOMEM; } if (priv->pool[1].base) { struct rpmsg_virtio_config config = { RPMSG_BUFFER_SIZE, RPMSG_BUFFER_SIZE, true, }; ret = rpmsg_init_vdev_with_config(&priv->rvdev, vdev, rptun_ns_bind, metal_io_get_region(), priv->pool, &config); } else { ret = rpmsg_init_vdev(&priv->rvdev, vdev, rptun_ns_bind, metal_io_get_region(), priv->pool); } if (ret) { remoteproc_remove_virtio(rproc, vdev); return ret; } priv->rvdev.rdev.ns_unbind_cb = rptun_ns_unbind; /* Remote proc start */ ret = remoteproc_start(rproc); if (ret) { remoteproc_remove_virtio(rproc, vdev); return ret; } /* Register callback to mbox for receiving remote message */ RPTUN_REGISTER_CALLBACK(priv->dev, rptun_callback, priv); /* Broadcast device_created to all registers */ nxrmutex_lock(&g_rptun_lockcb); metal_list_for_each(&g_rptun_cb, node) { cb = metal_container_of(node, struct rptun_cb_s, node); if (cb->device_created) { cb->device_created(&priv->rvdev.rdev, cb->priv); } } nxrmutex_unlock(&g_rptun_lockcb); virtqueue_enable_cb(priv->rvdev.svq); #ifdef CONFIG_RPTUN_PING rptun_ping_init(&priv->rvdev, &priv->ping); #endif return 0; } static int rptun_dev_stop(FAR struct remoteproc *rproc) { FAR struct rptun_priv_s *priv = rproc->priv; FAR struct metal_list *node; FAR struct rptun_cb_s *cb; #ifdef CONFIG_RPTUN_PING rptun_ping_deinit(&priv->ping); #endif /* Unregister callback from mbox */ RPTUN_UNREGISTER_CALLBACK(priv->dev); /* Remove priv from list */ nxrmutex_lock(&g_rptun_lockcb); /* Broadcast device_destroy to all registers */ metal_list_for_each(&g_rptun_cb, node) { cb = metal_container_of(node, struct rptun_cb_s, node); if (cb->device_destroy) { cb->device_destroy(&priv->rvdev.rdev, cb->priv); } } nxrmutex_unlock(&g_rptun_lockcb); /* Remote proc stop and shutdown */ remoteproc_shutdown(rproc); /* Remote proc remove */ rpmsg_deinit_vdev(&priv->rvdev); remoteproc_remove_virtio(rproc, priv->rvdev.vdev); return 0; } static int rptun_do_ioctl(FAR struct rptun_priv_s *priv, int cmd, unsigned long arg) { int ret = OK; switch (cmd) { case RPTUNIOC_START: case RPTUNIOC_STOP: priv->cmd = cmd; rptun_wakeup_rx(priv); break; case RPTUNIOC_RESET: RPTUN_RESET(priv->dev, arg); break; case RPTUNIOC_PANIC: RPTUN_PANIC(priv->dev); break; case RPTUNIOC_DUMP: rptun_dump(&priv->rvdev); break; #ifdef CONFIG_RPTUN_PING case RPTUNIOC_PING: rptun_ping(&priv->ping, (FAR const struct rptun_ping_s *)arg); break; #endif default: ret = -ENOTTY; break; } return ret; } static int rptun_dev_ioctl(FAR struct file *filep, int cmd, unsigned long arg) { FAR struct inode *inode = filep->f_inode; return rptun_do_ioctl(inode->i_private, cmd, arg); } #ifdef CONFIG_RPTUN_LOADER static int rptun_store_open(FAR void *store_, FAR const char *path, FAR const void **img_data) { FAR struct rptun_store_s *store = store_; int len = 0x100; int ret; ret = file_open(&store->file, path, O_RDONLY); if (ret < 0) { return ret; } store->buf = kmm_malloc(len); if (!store->buf) { file_close(&store->file); return -ENOMEM; } *img_data = store->buf; return file_read(&store->file, store->buf, len); } static void rptun_store_close(FAR void *store_) { FAR struct rptun_store_s *store = store_; kmm_free(store->buf); file_close(&store->file); } static int rptun_store_load(FAR void *store_, size_t offset, size_t size, FAR const void **data, metal_phys_addr_t pa, FAR struct metal_io_region *io, char is_blocking) { FAR struct rptun_store_s *store = store_; FAR char *tmp; if (pa == METAL_BAD_PHYS) { tmp = kmm_realloc(store->buf, size); if (!tmp) { return -ENOMEM; } store->buf = tmp; *data = tmp; } else { tmp = metal_io_phys_to_virt(io, pa); if (!tmp) { return -EINVAL; } } file_seek(&store->file, offset, SEEK_SET); return file_read(&store->file, tmp, size); } #endif static metal_phys_addr_t rptun_pa_to_da(FAR struct rptun_dev_s *dev, metal_phys_addr_t pa) { FAR const struct rptun_addrenv_s *addrenv; uint32_t i; addrenv = RPTUN_GET_ADDRENV(dev); if (!addrenv) { return pa; } for (i = 0; addrenv[i].size; i++) { if (pa - addrenv[i].pa < addrenv[i].size) { return addrenv[i].da + (pa - addrenv[i].pa); } } return pa; } static metal_phys_addr_t rptun_da_to_pa(FAR struct rptun_dev_s *dev, metal_phys_addr_t da) { FAR const struct rptun_addrenv_s *addrenv; uint32_t i; addrenv = RPTUN_GET_ADDRENV(dev); if (!addrenv) { return da; } for (i = 0; addrenv[i].size; i++) { if (da - addrenv[i].da < addrenv[i].size) { return addrenv[i].pa + (da - addrenv[i].da); } } return da; } static int rptun_ioctl_foreach(FAR const char *cpuname, int cmd, unsigned long value) { FAR struct metal_list *node; int ret = OK; metal_list_for_each(&g_rptun_priv, node) { FAR struct rptun_priv_s *priv; priv = metal_container_of(node, struct rptun_priv_s, node); if (!cpuname || !strcmp(RPTUN_GET_CPUNAME(priv->dev), cpuname)) { ret = rptun_do_ioctl(priv, cmd, value); if (ret < 0) break; } } return ret; } /**************************************************************************** * Public Functions ****************************************************************************/ int rpmsg_wait(FAR struct rpmsg_endpoint *ept, FAR sem_t *sem) { FAR struct rptun_priv_s *priv; int ret; if (!ept || !sem) { return -EINVAL; } priv = rptun_get_priv_by_rdev(ept->rdev); if (!priv || !rptun_is_recursive(priv)) { return nxsem_wait_uninterruptible(sem); } while (1) { ret = nxsem_trywait(sem); if (ret >= 0) { break; } nxsem_wait(&priv->semtx); rptun_worker(priv); } return ret; } int rpmsg_post(FAR struct rpmsg_endpoint *ept, FAR sem_t *sem) { FAR struct rptun_priv_s *priv; int semcount; int ret; if (!ept || !sem) { return -EINVAL; } nxsem_get_value(sem, &semcount); ret = nxsem_post(sem); priv = rptun_get_priv_by_rdev(ept->rdev); if (priv && semcount >= 0) { rptun_wakeup_tx(priv); } return ret; } FAR const char *rpmsg_get_cpuname(FAR struct rpmsg_device *rdev) { FAR struct rptun_priv_s *priv = rptun_get_priv_by_rdev(rdev); return RPTUN_GET_CPUNAME(priv->dev); } int rpmsg_register_callback(FAR void *priv_, rpmsg_dev_cb_t device_created, rpmsg_dev_cb_t device_destroy, rpmsg_match_cb_t ns_match, rpmsg_bind_cb_t ns_bind) { FAR struct metal_list *node; FAR struct metal_list *bnode; FAR struct rptun_cb_s *cb; cb = kmm_zalloc(sizeof(struct rptun_cb_s)); if (!cb) { return -ENOMEM; } cb->priv = priv_; cb->device_created = device_created; cb->device_destroy = device_destroy; cb->ns_match = ns_match; cb->ns_bind = ns_bind; nxrmutex_lock(&g_rptun_lockcb); metal_list_for_each(&g_rptun_priv, node) { FAR struct rptun_priv_s *priv; priv = metal_container_of(node, struct rptun_priv_s, node); if (priv->rproc.state != RPROC_RUNNING) { continue; } if (device_created) { device_created(&priv->rvdev.rdev, priv_); } if (ns_bind == NULL) { continue; } DEBUGASSERT(ns_match != NULL); again: nxrmutex_lock(&priv->lock); metal_list_for_each(&priv->bind, bnode) { FAR struct rptun_bind_s *bind; bind = metal_container_of(bnode, struct rptun_bind_s, node); if (ns_match(&priv->rvdev.rdev, priv_, bind->name, bind->dest)) { metal_list_del(bnode); nxrmutex_unlock(&priv->lock); ns_bind(&priv->rvdev.rdev, priv_, bind->name, bind->dest); kmm_free(bind); goto again; } } nxrmutex_unlock(&priv->lock); } metal_list_add_tail(&g_rptun_cb, &cb->node); nxrmutex_unlock(&g_rptun_lockcb); return 0; } void rpmsg_unregister_callback(FAR void *priv_, rpmsg_dev_cb_t device_created, rpmsg_dev_cb_t device_destroy, rpmsg_match_cb_t ns_match, rpmsg_bind_cb_t ns_bind) { FAR struct metal_list *node; FAR struct metal_list *pnode; nxrmutex_lock(&g_rptun_lockcb); metal_list_for_each(&g_rptun_cb, node) { FAR struct rptun_cb_s *cb = NULL; cb = metal_container_of(node, struct rptun_cb_s, node); if (cb->priv == priv_ && cb->device_created == device_created && cb->device_destroy == device_destroy && cb->ns_match == ns_match && cb->ns_bind == ns_bind) { metal_list_del(&cb->node); kmm_free(cb); break; } } if (device_destroy) { metal_list_for_each(&g_rptun_priv, pnode) { struct rptun_priv_s *priv; priv = metal_container_of(pnode, struct rptun_priv_s, node); if (priv->rproc.state == RPROC_RUNNING) { device_destroy(&priv->rvdev.rdev, priv_); } } } nxrmutex_unlock(&g_rptun_lockcb); } int rptun_initialize(FAR struct rptun_dev_s *dev) { struct metal_init_params params = METAL_INIT_DEFAULTS; FAR struct rptun_priv_s *priv; static bool onceinit; #ifndef CONFIG_RPTUN_WORKQUEUE FAR char *argv[3]; char arg1[19]; #endif char name[32]; int ret; if (!onceinit) { ret = metal_init(¶ms); if (ret < 0) { return ret; } onceinit = true; } priv = kmm_zalloc(sizeof(struct rptun_priv_s)); if (priv == NULL) { ret = -ENOMEM; goto err_mem; } priv->dev = dev; remoteproc_init(&priv->rproc, &g_rptun_ops, priv); metal_list_init(&priv->bind); nxrmutex_init(&priv->lock); snprintf(name, sizeof(name), "/dev/rptun/%s", RPTUN_GET_CPUNAME(dev)); ret = register_driver(name, &g_rptun_devops, 0222, priv); if (ret < 0) { goto err_driver; } nxsem_init(&priv->semtx, 0, 0); #ifdef CONFIG_RPTUN_WORKQUEUE if (RPTUN_IS_AUTOSTART(dev)) { priv->cmd = RPTUNIOC_START; work_queue(HPWORK, &priv->work, rptun_worker, priv, 0); } #else if (RPTUN_IS_AUTOSTART(dev)) { priv->cmd = RPTUNIOC_START; nxsem_init(&priv->semrx, 0, 1); } else { nxsem_init(&priv->semrx, 0, 0); } snprintf(arg1, sizeof(arg1), "%p", priv); argv[0] = (void *)RPTUN_GET_CPUNAME(dev); argv[1] = arg1; argv[2] = NULL; ret = kthread_create("rptun", CONFIG_RPTUN_PRIORITY, CONFIG_RPTUN_STACKSIZE, rptun_thread, argv); if (ret < 0) { unregister_driver(name); nxsem_destroy(&priv->semtx); nxsem_destroy(&priv->semrx); goto err_driver; } #endif /* Add priv to list */ nxrmutex_lock(&g_rptun_lockcb); metal_list_add_tail(&g_rptun_priv, &priv->node); nxrmutex_unlock(&g_rptun_lockcb); return OK; err_driver: nxrmutex_destroy(&priv->lock); kmm_free(priv); err_mem: metal_finish(); return ret; } int rptun_boot(FAR const char *cpuname) { return rptun_ioctl_foreach(cpuname, RPTUNIOC_START, 0); } int rptun_reset(FAR const char *cpuname, int value) { return rptun_ioctl_foreach(cpuname, RPTUNIOC_RESET, value); } int rptun_panic(FAR const char *cpuname) { return rptun_ioctl_foreach(cpuname, RPTUNIOC_PANIC, 0); } int rptun_buffer_nused(FAR struct rpmsg_virtio_device *rvdev, bool rx) { FAR struct virtqueue *vq = rx ? rvdev->rvq : rvdev->svq; uint16_t nused = vq->vq_ring.avail->idx - vq->vq_ring.used->idx; if ((rpmsg_virtio_get_role(rvdev) == RPMSG_HOST) ^ rx) { return nused; } else { return vq->vq_nentries - nused; } } void rptun_dump_all(void) { rptun_ioctl_foreach(NULL, RPTUNIOC_DUMP, 0); }