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
 

Reply via email to