This is an automated email from the ASF dual-hosted git repository. xiaoxiang781216 pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit ed6f81a1be766ed52290a2b1ce0d63b70d13f094 Author: zhangbo56 <[email protected]> AuthorDate: Mon Nov 2 19:38:18 2026 +0800 drivers/rpmsg: add rpmsg wakelock Add an rpmsg service that lets a CPU hold a PM wakelock on its peer: RPMSGIOC_ACQUIRE_WAKELOCK and RPMSGIOC_RELEASE_WAKELOCK on an rpmsg device send the wakelock name to the remote side, which creates a wakelock of that name on first acquire, stays it, and releases and frees it once its count drops back to zero. Signed-off-by: zhangbo56 <[email protected]> Signed-off-by: Bowen Wang <[email protected]> --- drivers/rpmsg/CMakeLists.txt | 4 + drivers/rpmsg/Kconfig | 9 + drivers/rpmsg/Make.defs | 4 + drivers/rpmsg/rpmsg.c | 14 ++ drivers/rpmsg/rpmsg.h | 3 + drivers/rpmsg/rpmsg_wakelock.c | 418 +++++++++++++++++++++++++++++++++++++++++ drivers/rpmsg/rpmsg_wakelock.h | 52 +++++ include/nuttx/rpmsg/rpmsg.h | 2 + 8 files changed, 506 insertions(+) diff --git a/drivers/rpmsg/CMakeLists.txt b/drivers/rpmsg/CMakeLists.txt index 253e6f0f751..9c6b0418366 100644 --- a/drivers/rpmsg/CMakeLists.txt +++ b/drivers/rpmsg/CMakeLists.txt @@ -73,6 +73,10 @@ if(CONFIG_RPMSG) list(APPEND SRCS rpmsg_test.c) endif() + if(CONFIG_RPMSG_WAKELOCK) + list(APPEND SRCS rpmsg_wakelock.c) + endif() + target_include_directories(drivers PRIVATE ${NUTTX_DIR}/openamp/open-amp/lib) target_sources(drivers PRIVATE ${SRCS}) endif() diff --git a/drivers/rpmsg/Kconfig b/drivers/rpmsg/Kconfig index c299759780a..aa504879a0d 100644 --- a/drivers/rpmsg/Kconfig +++ b/drivers/rpmsg/Kconfig @@ -51,6 +51,15 @@ config RPMSG_TEST bool "rpmsg test support" default n +config RPMSG_WAKELOCK + bool "rpmsg wakelock support" + default n + depends on PM + ---help--- + Let a CPU hold a PM wakelock on the remote CPU through the + RPMSGIOC_ACQUIRE_WAKELOCK and RPMSGIOC_RELEASE_WAKELOCK ioctls. + Both sides of the link must enable it. + endif # RPMSG config RPMSG_ROUTER diff --git a/drivers/rpmsg/Make.defs b/drivers/rpmsg/Make.defs index fcf1e21265a..5c991e2380b 100644 --- a/drivers/rpmsg/Make.defs +++ b/drivers/rpmsg/Make.defs @@ -42,6 +42,10 @@ ifeq ($(CONFIG_RPMSG_TEST),y) CSRCS += rpmsg_test.c endif +ifeq ($(CONFIG_RPMSG_WAKELOCK),y) +CSRCS += rpmsg_wakelock.c +endif + ifeq ($(CONFIG_RPMSG_ROUTER),y) CSRCS += rpmsg_router_hub.c rpmsg_router_edge.c endif diff --git a/drivers/rpmsg/rpmsg.c b/drivers/rpmsg/rpmsg.c index ef9ec51a614..9dd33013370 100644 --- a/drivers/rpmsg/rpmsg.c +++ b/drivers/rpmsg/rpmsg.c @@ -38,6 +38,7 @@ #include "rpmsg_procfs.h" #include "rpmsg_router.h" #include "rpmsg_test.h" +#include "rpmsg_wakelock.h" /**************************************************************************** * Private Types @@ -145,6 +146,12 @@ static int rpmsg_dev_ioctl_(FAR struct rpmsg_s *rpmsg, int cmd, *(FAR bool *)arg = rpmsg_is_running(rpmsg_get_rdev_by_rpmsg(rpmsg)); ret = OK; break; +#ifdef CONFIG_RPMSG_WAKELOCK + case RPMSGIOC_ACQUIRE_WAKELOCK: + case RPMSGIOC_RELEASE_WAKELOCK: + ret = rpmsg_wakelock(&rpmsg->wakelock, cmd, arg); + break; +#endif default: if (rpmsg->ops->ioctl) { @@ -462,6 +469,9 @@ void rpmsg_device_created(FAR struct rpmsg_s *rpmsg) #ifdef CONFIG_RPMSG_TEST rpmsg_test_init(rdev, &rpmsg->test); #endif +#ifdef CONFIG_RPMSG_WAKELOCK + rpmsg_wakelock_init(rdev, &rpmsg->wakelock); +#endif } void rpmsg_device_destroy(FAR struct rpmsg_s *rpmsg) @@ -479,6 +489,10 @@ void rpmsg_device_destroy(FAR struct rpmsg_s *rpmsg) rpmsg_test_deinit(&rpmsg->test); #endif +#ifdef CONFIG_RPMSG_WAKELOCK + rpmsg_wakelock_deinit(&rpmsg->wakelock); +#endif + nxrmutex_lock(&rpmsg->lock); metal_list_for_each_safe(&rpmsg->bind, tmp, node) { diff --git a/drivers/rpmsg/rpmsg.h b/drivers/rpmsg/rpmsg.h index acfc26c0676..06b1948b486 100644 --- a/drivers/rpmsg/rpmsg.h +++ b/drivers/rpmsg/rpmsg.h @@ -49,6 +49,9 @@ struct rpmsg_s #endif #ifdef CONFIG_RPMSG_TEST struct rpmsg_endpoint test; +#endif +#ifdef CONFIG_RPMSG_WAKELOCK + struct rpmsg_endpoint wakelock; #endif atomic_t signals; }; diff --git a/drivers/rpmsg/rpmsg_wakelock.c b/drivers/rpmsg/rpmsg_wakelock.c new file mode 100644 index 00000000000..61c1db8c415 --- /dev/null +++ b/drivers/rpmsg/rpmsg_wakelock.c @@ -0,0 +1,418 @@ +/**************************************************************************** + * drivers/rpmsg/rpmsg_wakelock.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 <debug.h> + +#include <inttypes.h> +#include <sys/param.h> +#include <string.h> + +#include <nuttx/list.h> +#include <nuttx/kmalloc.h> +#include <nuttx/mutex.h> +#include <nuttx/power/pm.h> +#include <nuttx/rpmsg/rpmsg.h> + +#include "rpmsg_wakelock.h" + +/**************************************************************************** + * Pre-processor definitions + ****************************************************************************/ + +#define RPMSG_WAKELOCK_NAME "rpmsg-wakelock" + +#define RPMSG_WAKELOCK_ACQUIRE_REQ 1 +#define RPMSG_WAKELOCK_RELEASE_REQ 2 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +begin_packed_struct struct rpmsg_wakelock_msg_s +{ + uint32_t cmd; + uint32_t len; + int32_t err; + uint8_t data[1]; +} end_packed_struct; + +struct rpmsg_wakelock_node_s +{ + struct pm_wakelock_s wakelock; /* wakelock */ + struct list_node node; /* list node */ +}; + +struct rpmsg_wakelock_s +{ + FAR struct rpmsg_endpoint *ept; /* Rpmsg endpoint */ + mutex_t lock; /* List lock */ + struct list_node list; /* List node */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Functions handle the responses from the remote cpu */ + +static int rpmsg_wakelock_acquire_req_handler(FAR struct rpmsg_endpoint *ept, + FAR void *data, size_t len, + uint32_t src, FAR void *priv); + +static int rpmsg_wakelock_release_req_handler(FAR struct rpmsg_endpoint *ept, + FAR void *data, size_t len, + uint32_t src, FAR void *priv); + +/* Functions for creating communication with remote cpu */ + +static int rpmsg_wakelock_ept_cb(FAR struct rpmsg_endpoint *ept, + FAR void *data, size_t len, uint32_t src, + FAR void *priv); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const rpmsg_ept_cb g_rpmsg_wakelock_handler[] = +{ + [RPMSG_WAKELOCK_ACQUIRE_REQ] = rpmsg_wakelock_acquire_req_handler, + [RPMSG_WAKELOCK_RELEASE_REQ] = rpmsg_wakelock_release_req_handler, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int rpmsg_wakelock_control(FAR struct rpmsg_wakelock_s *priv, + FAR const char *name, bool acquire) +{ + FAR struct rpmsg_wakelock_node_s *tmp; + FAR struct rpmsg_wakelock_node_s *wl = NULL; + + nxmutex_lock(&priv->lock); + + list_for_every_entry(&priv->list, tmp, + struct rpmsg_wakelock_node_s, node) + { + if (strcmp(tmp->wakelock.name, name) == 0) + { + wl = tmp; + break; + } + } + + if (acquire) + { + if (wl == NULL) + { + wl = kmm_zalloc(sizeof(*wl)); + if (wl == NULL) + { + rpmsgerr("rpmsg_wakelock_ctl_wakelock: kmm_zalloc failed\n"); + nxmutex_unlock(&priv->lock); + return -ENOMEM; + } + + rpmsginfo("wakelock created: %s\n", name); + pm_wakelock_init(&wl->wakelock, name, PM_IDLE_DOMAIN, PM_NORMAL); + list_add_tail(&priv->list, &wl->node); + } + + rpmsginfo("acquire wake lock: %s\n", name); + pm_wakelock_stay(&wl->wakelock); + } + else + { + if (wl != NULL) + { + rpmsginfo("release wake lock: %s\n", name); + pm_wakelock_relax(&wl->wakelock); + if (pm_wakelock_staycount(&wl->wakelock) == 0) + { + rpmsginfo("wakelock destroyed: %s\n", name); + list_delete(&wl->node); + pm_wakelock_uninit(&wl->wakelock); + kmm_free(wl); + } + } + } + + nxmutex_unlock(&priv->lock); + return 0; +} + +/**************************************************************************** + * Name: rpmsg_wakelock_acquire_req_handler + * + * Description: + * rpmsg-wakelock acquire req handler. + * + * Parameters: + * ept - The rpmsg endpoint + * data - The return message + * len - The return message length + * + * Returned Values: + * Always OK + * + ****************************************************************************/ + +static int rpmsg_wakelock_acquire_req_handler(FAR struct rpmsg_endpoint *ept, + FAR void *data, size_t len, + uint32_t src, FAR void *priv_) +{ + FAR struct rpmsg_wakelock_s *priv = priv_; + FAR struct rpmsg_wakelock_msg_s *msg = data; + FAR const char *name = (FAR const char *)msg->data; + + msg->err = rpmsg_wakelock_control(priv, name, true); + return 0; +} + +/**************************************************************************** + * Name: rpmsg_wakelock_release_req_handler + * + * Description: + * rpmsg-wakelock release req handler. + * + * Parameters: + * ept - The rpmsg endpoint + * data - The return message + * len - The return message length + * + * Returned Values: + * Always OK + * + ****************************************************************************/ + +static int rpmsg_wakelock_release_req_handler(FAR struct rpmsg_endpoint *ept, + FAR void *data, size_t len, + uint32_t src, FAR void *priv_) +{ + FAR struct rpmsg_wakelock_s *priv = ept->priv; + FAR struct rpmsg_wakelock_msg_s *msg = data; + FAR const char *name = (FAR const char *)msg->data; + + msg->err = rpmsg_wakelock_control(priv, name, false); + return 0; +} + +/**************************************************************************** + * Name: rpmsg_wakelock_ept_cb + * + * Description: + * Rpmsg device end point callback function, this function will be called + * when receive the remote cpu message. + * + * Parameters: + * ept - The rpmsg-device end point + * data - The received data + * len - The received data length + * src - The source address of the message + * priv - Private data of the endpoint + * + * Returned Values: + * OK on success; A negated errno value is returned on any failure. + * + ****************************************************************************/ + +static int rpmsg_wakelock_ept_cb(FAR struct rpmsg_endpoint *ept, + FAR void *data, size_t len, uint32_t src, + FAR void *priv) +{ + FAR struct rpmsg_wakelock_msg_s *msg = data; + uint32_t command; + + /* The name must end with a NUL inside the received message */ + + if (len < sizeof(*msg) || + memchr(msg->data, '\0', len - offsetof(struct rpmsg_wakelock_msg_s, + data)) == NULL) + { + rpmsgerr("rpmsg_wakelock_ept_cb: bad message, len %zu\n", len); + return -EINVAL; + } + + command = msg->cmd; + if (command < nitems(g_rpmsg_wakelock_handler) && + g_rpmsg_wakelock_handler[command] != NULL) + { + return g_rpmsg_wakelock_handler[command](ept, data, len, src, priv); + } + + rpmsgerr("rpmsg_wakelock_ept_cb: invalid command: %" PRIu32 "\n", + command); + return -EINVAL; +} + +/**************************************************************************** + * Name: rpmsg_wakelock + * + * Description: + * rpmsg-wakelock ioctl operation + * + * Parameters: + * ept - The rpmsg endpoint + * cmd - the ioctl command + * arg - the ioctl argument + * + * Returned Values: + * OK on success; A negated errno value is returned on any failure. + * + ****************************************************************************/ + +int rpmsg_wakelock(FAR struct rpmsg_endpoint *ept, + int cmd, unsigned long arg) +{ + FAR struct rpmsg_wakelock_s *priv = ept->priv; + FAR const char *str = (FAR const char *)arg; + FAR struct rpmsg_wakelock_msg_s *msg; + uint32_t msglen; + int ret; + + DEBUGASSERT(priv != NULL); + + /* pm_wakelock_init() keeps a shorter name only, which the peer could + * not match on release. + */ + + if (strlen(str) >= sizeof(((FAR struct pm_wakelock_s *)0)->name)) + { + return -ENAMETOOLONG; + } + + msg = rpmsg_get_tx_payload_buffer(ept, &msglen, true); + if (msg == NULL) + { + return -ENOMEM; + } + + msglen = MIN(msglen, sizeof(*msg) + strlen(str)); + + if (cmd == RPMSGIOC_ACQUIRE_WAKELOCK) + { + msg->cmd = RPMSG_WAKELOCK_ACQUIRE_REQ; + } + else if (cmd == RPMSGIOC_RELEASE_WAKELOCK) + { + msg->cmd = RPMSG_WAKELOCK_RELEASE_REQ; + } + else + { + rpmsgerr("rpmsg_wakelock_ioctl: invalid command: %d\n", cmd); + rpmsg_release_tx_buffer(ept, msg); + return -EINVAL; + } + + memcpy(msg->data, str, msglen - sizeof(*msg) + 1); + + msg->len = msglen; + msg->err = 0; + + ret = rpmsg_send_nocopy(priv->ept, msg, msglen); + if (ret < 0) + { + rpmsgerr("rpmsg_wakelock_ioctl: rpmsg_send_nocopy failed: %d\n", ret); + rpmsg_release_tx_buffer(priv->ept, msg); + return ret; + } + + return OK; +} + +/**************************************************************************** + * Name: rpmsg_wakelock_init + * + * Description: + * Using remote wakelock to wake/sleep CPU + * + * Parameters: + * rdev - rpmsg device + * ept - rpmsg endpoint + * + * Returned Values: + * OK on success; A negated errno value is returned on any failure. + ****************************************************************************/ + +int rpmsg_wakelock_init(FAR struct rpmsg_device *rdev, + FAR struct rpmsg_endpoint *ept) +{ + FAR struct rpmsg_wakelock_s *priv; + + priv = kmm_zalloc(sizeof(*priv)); + if (priv == NULL) + { + rpmsgerr("rpmsg_wakelock_initialize: kmm_zalloc failed\n"); + return -ENOMEM; + } + + /* Initialize rpmsg device */ + + nxmutex_init(&priv->lock); + list_initialize(&priv->list); + + priv->ept = ept; + ept->priv = priv; + + return rpmsg_create_ept(ept, rdev, RPMSG_WAKELOCK_NAME, + RPMSG_ADDR_ANY, RPMSG_ADDR_ANY, + rpmsg_wakelock_ept_cb, NULL); +} + +/**************************************************************************** + * Name: rpmsg_wakelock_deinit + * + * Description: + * rpmsg wakelock deinit + * + * Parameters: + * ept - rpmsg endpoint + * + * Returned Values: + * None + ****************************************************************************/ + +void rpmsg_wakelock_deinit(FAR struct rpmsg_endpoint *ept) +{ + FAR struct rpmsg_wakelock_s *priv = ept->priv; + FAR struct rpmsg_wakelock_node_s *wl; + FAR struct rpmsg_wakelock_node_s *tmp; + + rpmsg_destroy_ept(ept); + + /* Drop what a peer that went away still holds, or this CPU never + * sleeps again. + */ + + list_for_every_entry_safe(&priv->list, wl, tmp, + struct rpmsg_wakelock_node_s, node) + { + list_delete(&wl->node); + pm_wakelock_uninit(&wl->wakelock); + kmm_free(wl); + } + + nxmutex_destroy(&priv->lock); + kmm_free(priv); +} diff --git a/drivers/rpmsg/rpmsg_wakelock.h b/drivers/rpmsg/rpmsg_wakelock.h new file mode 100644 index 00000000000..d1b313c9ab9 --- /dev/null +++ b/drivers/rpmsg/rpmsg_wakelock.h @@ -0,0 +1,52 @@ +/**************************************************************************** + * drivers/rpmsg/rpmsg_wakelock.h + * + * 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. + * + ****************************************************************************/ + +#ifndef __DRIVERS_RPMSG_WAKELOCK_H +#define __DRIVERS_RPMSG_WAKELOCK_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/compiler.h> + +#include <stdint.h> + +/**************************************************************************** + * Internal function prototypes + ****************************************************************************/ + +/**************************************************************************** + * Internal data + ****************************************************************************/ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +int rpmsg_wakelock(FAR struct rpmsg_endpoint *ept, + int cmd, unsigned long arg); + +int rpmsg_wakelock_init(FAR struct rpmsg_device *rdev, + FAR struct rpmsg_endpoint *ept); + +void rpmsg_wakelock_deinit(FAR struct rpmsg_endpoint *ept); + +#endif /* __DRIVERS_RPMSG_WAKELOCK_H */ diff --git a/include/nuttx/rpmsg/rpmsg.h b/include/nuttx/rpmsg/rpmsg.h index b93d3f0fc66..92c9a2e615b 100644 --- a/include/nuttx/rpmsg/rpmsg.h +++ b/include/nuttx/rpmsg/rpmsg.h @@ -46,6 +46,8 @@ #define RPMSGIOC_PING _RPMSGIOC(3) #define RPMSGIOC_TEST _RPMSGIOC(4) #define RPMSGIOC_RUNNING _RPMSGIOC(5) +#define RPMSGIOC_ACQUIRE_WAKELOCK _RPMSGIOC(6) +#define RPMSGIOC_RELEASE_WAKELOCK _RPMSGIOC(7) #define RPMSG_SIGNAL_RUNNING TIOCM_CD
