* 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 <sys/param.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;
struct list_node node;
};
struct rpmsg_wakelock_s
{
FAR struct rpmsg_endpoint *ept;
mutex_t lock;
struct list_node list;
};
* Private Function Prototypes
****************************************************************************/
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);
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 - unknow
* priv - unknow
*
* 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 = msg->cmd;
if (command < nitems(g_rpmsg_wakelock_handler))
{
return g_rpmsg_wakelock_handler[command](ept, data, len, src, priv);
}
rpmsgerr("rpmsg_wakelock_ept_cb: invalid command: %d\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);
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;
}
* 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;
}
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;
rpmsg_destroy_ept(ept);
nxmutex_destroy(&priv->lock);
kmm_free(priv);
}