c860a6e5f1
Before this commit, each rpmsgdev server only manages one file no matter how many times the client opened. It can't work fine with some complex drivers, such as input driver (keyboard, touchscreen and button), because input driver will alloc a new private open structure for every open operation to make sure all the applications don't miss data when input driver is used by multiple applications. This commit solves this problem by making the files in server and client be one-to-one. Signed-off-by: wangbowen6 <wangbowen6@xiaomi.com>
1081 lines
30 KiB
C
1081 lines
30 KiB
C
/****************************************************************************
|
|
* drivers/misc/rpmsgdev.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 <sys/types.h>
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <fcntl.h>
|
|
#include <poll.h>
|
|
#include <limits.h>
|
|
#include <debug.h>
|
|
|
|
#include <nuttx/kmalloc.h>
|
|
#include <nuttx/fs/fs.h>
|
|
#include <nuttx/fs/ioctl.h>
|
|
#include <nuttx/video/fb.h>
|
|
#include <nuttx/mutex.h>
|
|
#include <nuttx/rptun/openamp.h>
|
|
|
|
#include "rpmsgdev.h"
|
|
|
|
/****************************************************************************
|
|
* Pre-processor Definitions
|
|
****************************************************************************/
|
|
|
|
#ifndef rpmsgdeverr
|
|
# define rpmsgdeverr(fmt, ...)
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Private Types
|
|
****************************************************************************/
|
|
|
|
struct rpmsgdev_priv_s
|
|
{
|
|
uint64_t filep; /* store server file pointer */
|
|
};
|
|
|
|
struct rpmsgdev_s
|
|
{
|
|
struct rpmsg_endpoint ept; /* Rpmsg endpoint */
|
|
FAR const char *remotecpu; /* The server cpu name */
|
|
FAR const char *remotepath; /* The device path in the server cpu */
|
|
sem_t wait; /* Wait sem, used for preventing any
|
|
* opreation until the connection
|
|
* between two cpu established.
|
|
*/
|
|
};
|
|
|
|
/* Rpmsg device cookie used to handle the response from the remote cpu */
|
|
|
|
struct rpmsgdev_cookie_s
|
|
{
|
|
sem_t sem; /* Semaphore used fo rpmsg */
|
|
int result; /* The return value of the remote call */
|
|
FAR void *data; /* The return data buffer of the remote call */
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Function Prototypes
|
|
****************************************************************************/
|
|
|
|
/* The file operation functions */
|
|
|
|
static int rpmsgdev_open(FAR struct file *filep);
|
|
static int rpmsgdev_close(FAR struct file *filep);
|
|
static ssize_t rpmsgdev_read(FAR struct file *filep, FAR char *buffer,
|
|
size_t buflen);
|
|
static ssize_t rpmsgdev_write(FAR struct file *filep, FAR const char *buffer,
|
|
size_t buflen);
|
|
static off_t rpmsgdev_seek(FAR struct file *filep, off_t offset,
|
|
int whence);
|
|
static size_t rpmsgdev_ioctl_arglen(int cmd);
|
|
static int rpmsgdev_ioctl(FAR struct file *filep, int cmd,
|
|
unsigned long arg);
|
|
static int rpmsgdev_poll(FAR struct file *filep, FAR struct pollfd *fds,
|
|
bool setup);
|
|
|
|
/* Functions for sending data to the remote cpu */
|
|
|
|
static int rpmsgdev_send_recv(FAR struct rpmsgdev_s *priv,
|
|
uint32_t command, bool copy,
|
|
FAR struct rpmsgdev_header_s *msg,
|
|
int len, FAR void *data);
|
|
static FAR void *rpmsgdev_get_tx_payload_buffer(FAR struct rpmsgdev_s *priv,
|
|
FAR uint32_t *len);
|
|
|
|
/* Functions handle the responses from the remote cpu */
|
|
|
|
static int rpmsgdev_default_handler(FAR struct rpmsg_endpoint *ept,
|
|
FAR void *data, size_t len,
|
|
uint32_t src, FAR void *priv);
|
|
static int rpmsgdev_read_handler(FAR struct rpmsg_endpoint *ept,
|
|
FAR void *data, size_t len,
|
|
uint32_t src, FAR void *priv);
|
|
static int rpmsgdev_ioctl_handler(FAR struct rpmsg_endpoint *ept,
|
|
FAR void *data, size_t len,
|
|
uint32_t src, FAR void *priv);
|
|
static int rpmsgdev_notify_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 void rpmsgdev_device_created(struct rpmsg_device *rdev,
|
|
FAR void *priv_);
|
|
static void rpmsgdev_device_destroy(struct rpmsg_device *rdev,
|
|
FAR void *priv_);
|
|
static int rpmsgdev_ept_cb(FAR struct rpmsg_endpoint *ept,
|
|
FAR void *data, size_t len, uint32_t src,
|
|
FAR void *priv);
|
|
static void rpmsgdev_ns_bound(struct rpmsg_endpoint *ept);
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
/* Rpmsg device response handler table */
|
|
|
|
static const rpmsg_ept_cb g_rpmsgdev_handler[] =
|
|
{
|
|
[RPMSGDEV_OPEN] = rpmsgdev_default_handler,
|
|
[RPMSGDEV_CLOSE] = rpmsgdev_default_handler,
|
|
[RPMSGDEV_READ] = rpmsgdev_read_handler,
|
|
[RPMSGDEV_WRITE] = rpmsgdev_default_handler,
|
|
[RPMSGDEV_LSEEK] = rpmsgdev_default_handler,
|
|
[RPMSGDEV_IOCTL] = rpmsgdev_ioctl_handler,
|
|
[RPMSGDEV_POLL] = rpmsgdev_default_handler,
|
|
[RPMSGDEV_NOTIFY] = rpmsgdev_notify_handler,
|
|
};
|
|
|
|
/* File operations */
|
|
|
|
const struct file_operations g_rpmsgdev_ops =
|
|
{
|
|
rpmsgdev_open, /* open */
|
|
rpmsgdev_close, /* close */
|
|
rpmsgdev_read, /* read */
|
|
rpmsgdev_write, /* write */
|
|
rpmsgdev_seek, /* seek */
|
|
rpmsgdev_ioctl, /* ioctl */
|
|
rpmsgdev_poll /* poll */
|
|
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
|
|
, NULL /* unlink */
|
|
#endif
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: rpmsgdev_open
|
|
*
|
|
* Description:
|
|
* Rpmsg-device open operation
|
|
*
|
|
* Parameters:
|
|
* filep - the file instance
|
|
*
|
|
* Returned Values:
|
|
* OK on success; A negated errno value is returned on any failure.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int rpmsgdev_open(FAR struct file *filep)
|
|
{
|
|
FAR struct rpmsgdev_s *dev;
|
|
FAR struct rpmsgdev_priv_s *priv;
|
|
struct rpmsgdev_open_s msg;
|
|
int ret;
|
|
|
|
/* Sanity checks */
|
|
|
|
DEBUGASSERT(filep->f_inode != NULL);
|
|
|
|
/* Get the mountpoint inode reference from the file structure and the
|
|
* mountpoint private data from the inode structure
|
|
*/
|
|
|
|
dev = filep->f_inode->i_private;
|
|
DEBUGASSERT(dev != NULL);
|
|
|
|
priv = (FAR struct rpmsgdev_priv_s *)kmm_zalloc(sizeof(*priv));
|
|
if (priv == NULL)
|
|
{
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/* Try to open the device in the remote cpu */
|
|
|
|
msg.flags = filep->f_oflags;
|
|
ret = rpmsgdev_send_recv(dev, RPMSGDEV_OPEN, true, &msg.header,
|
|
sizeof(msg), NULL);
|
|
if (ret < 0)
|
|
{
|
|
rpmsgdeverr("open failed, ret=%d\n", ret);
|
|
kmm_free(priv);
|
|
return ret;
|
|
}
|
|
|
|
priv->filep = msg.filep;
|
|
|
|
/* Attach the private date to the struct file instance */
|
|
|
|
filep->f_priv = priv;
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: rpmsgdev_close
|
|
*
|
|
* Description:
|
|
* Rpmsg-device close operation
|
|
*
|
|
* Parameters:
|
|
* filep - the file instance
|
|
*
|
|
* Returned Values:
|
|
* OK on success; A negated errno value is returned on any failure.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int rpmsgdev_close(FAR struct file *filep)
|
|
{
|
|
FAR struct rpmsgdev_s *dev;
|
|
FAR struct rpmsgdev_priv_s *priv;
|
|
struct rpmsgdev_close_s msg;
|
|
int ret;
|
|
|
|
/* Sanity checks */
|
|
|
|
DEBUGASSERT(filep->f_inode != NULL);
|
|
|
|
/* Recover our private data from the struct file instance */
|
|
|
|
dev = filep->f_inode->i_private;
|
|
priv = filep->f_priv;
|
|
DEBUGASSERT(dev != NULL && priv != NULL);
|
|
|
|
/* Try to close the device in the remote cpu */
|
|
|
|
msg.filep = priv->filep;
|
|
ret = rpmsgdev_send_recv(dev, RPMSGDEV_CLOSE, true, &msg.header,
|
|
sizeof(msg), NULL);
|
|
if (ret < 0)
|
|
{
|
|
rpmsgdeverr("close failed, ret=%d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
filep->f_priv = NULL;
|
|
kmm_free(priv);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: rpmsgdev_read
|
|
*
|
|
* Description:
|
|
* Rpmsg-device read operation
|
|
*
|
|
* Parameters:
|
|
* filep - the file instance
|
|
* buffer - the read buffer pointer
|
|
* buflen - the read buffer length
|
|
*
|
|
* Returned Values:
|
|
* The positive non-zero number of bytes read on success, 0 on if an
|
|
* end-of-file condition, or a negated errno value on any failure.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static ssize_t rpmsgdev_read(FAR struct file *filep, FAR char *buffer,
|
|
size_t buflen)
|
|
{
|
|
FAR struct rpmsgdev_s *dev;
|
|
FAR struct rpmsgdev_priv_s *priv;
|
|
struct rpmsgdev_read_s msg;
|
|
struct iovec read;
|
|
ssize_t ret;
|
|
|
|
if (buffer == NULL)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Sanity checks */
|
|
|
|
DEBUGASSERT(filep->f_inode != NULL);
|
|
|
|
/* Recover our private data from the struct file instance */
|
|
|
|
dev = filep->f_inode->i_private;
|
|
priv = filep->f_priv;
|
|
DEBUGASSERT(dev != NULL && priv != NULL);
|
|
|
|
/* Call the host to perform the read */
|
|
|
|
read.iov_base = buffer;
|
|
read.iov_len = 0;
|
|
|
|
msg.filep = priv->filep;
|
|
msg.count = buflen;
|
|
|
|
ret = rpmsgdev_send_recv(dev, RPMSGDEV_READ, true, &msg.header,
|
|
sizeof(msg) - 1, &read);
|
|
|
|
return read.iov_len ? read.iov_len : ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: rpmsgdev_write
|
|
*
|
|
* Description:
|
|
* Rpmsg-device write operation
|
|
*
|
|
* Parameters:
|
|
* filep - the file instance
|
|
* buffer - the write buffer pointer
|
|
* buflen - the write buffer length
|
|
*
|
|
* Returned Values:
|
|
* On success, the number of bytes written are returned (zero indicates
|
|
* nothing was written). On any failure, a negated errno value is returned
|
|
* (see comments withwrite() for a description of the appropriate errno
|
|
* values).
|
|
*
|
|
****************************************************************************/
|
|
|
|
static ssize_t rpmsgdev_write(FAR struct file *filep, const char *buffer,
|
|
size_t buflen)
|
|
{
|
|
FAR struct rpmsgdev_s *dev;
|
|
FAR struct rpmsgdev_priv_s *priv;
|
|
FAR struct rpmsgdev_write_s *msg;
|
|
struct rpmsgdev_cookie_s cookie;
|
|
uint32_t space;
|
|
size_t written = 0;
|
|
int ret;
|
|
|
|
if (buffer == NULL)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
|
|
DEBUGASSERT(filep->f_inode != NULL);
|
|
|
|
/* Recover our private data from the struct file instance */
|
|
|
|
dev = filep->f_inode->i_private;
|
|
priv = filep->f_priv;
|
|
DEBUGASSERT(dev != NULL && priv != NULL);
|
|
|
|
/* Perform the rpmsg write */
|
|
|
|
memset(&cookie, 0, sizeof(cookie));
|
|
nxsem_init(&cookie.sem, 0, 0);
|
|
nxsem_set_protocol(&cookie.sem, SEM_PRIO_NONE);
|
|
|
|
while (written < buflen)
|
|
{
|
|
msg = rpmsgdev_get_tx_payload_buffer(dev, &space);
|
|
if (msg == NULL)
|
|
{
|
|
ret = -ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
space -= sizeof(*msg) - 1;
|
|
if (space >= buflen - written)
|
|
{
|
|
/* Send complete, set cookie is valid, need ack */
|
|
|
|
space = buflen - written;
|
|
msg->header.cookie = (uintptr_t)&cookie;
|
|
}
|
|
else
|
|
{
|
|
/* Not send complete, set cookie invalid, do not need ack */
|
|
|
|
msg->header.cookie = 0;
|
|
}
|
|
|
|
msg->header.command = RPMSGDEV_WRITE;
|
|
msg->header.result = -ENXIO;
|
|
msg->filep = priv->filep;
|
|
msg->count = space;
|
|
memcpy(msg->buf, buffer + written, space);
|
|
|
|
ret = rpmsg_send_nocopy(&dev->ept, msg, sizeof(*msg) - 1 + space);
|
|
if (ret < 0)
|
|
{
|
|
goto out;
|
|
}
|
|
|
|
written += space;
|
|
}
|
|
|
|
ret = rpmsg_wait(&dev->ept, &cookie.sem);
|
|
if (ret < 0)
|
|
{
|
|
goto out;
|
|
}
|
|
|
|
ret = cookie.result;
|
|
|
|
out:
|
|
nxsem_destroy(&cookie.sem);
|
|
return ret < 0 ? ret : buflen;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: rpmsgdev_seek
|
|
*
|
|
* Description:
|
|
* Rpmsg-device seek operation
|
|
*
|
|
* Parameters:
|
|
* file File structure instance
|
|
* offset Defines the offset to position to
|
|
* whence Defines how to use offset
|
|
*
|
|
* Returned Values:
|
|
* The resulting offset on success. A negated errno value is returned on
|
|
* any failure (see lseek comments).
|
|
*
|
|
****************************************************************************/
|
|
|
|
static off_t rpmsgdev_seek(FAR struct file *filep, off_t offset, int whence)
|
|
{
|
|
FAR struct rpmsgdev_s *dev;
|
|
FAR struct rpmsgdev_priv_s *priv;
|
|
struct rpmsgdev_lseek_s msg;
|
|
|
|
/* Sanity checks */
|
|
|
|
DEBUGASSERT(filep->f_inode != NULL);
|
|
|
|
/* Recover our private data from the struct file instance */
|
|
|
|
dev = filep->f_inode->i_private;
|
|
priv = filep->f_priv;
|
|
DEBUGASSERT(dev != NULL && priv != NULL);
|
|
|
|
/* Call our internal routine to perform the seek */
|
|
|
|
msg.filep = priv->filep;
|
|
msg.offset = offset;
|
|
msg.whence = whence;
|
|
|
|
return rpmsgdev_send_recv(dev, RPMSGDEV_LSEEK, true, &msg.header,
|
|
sizeof(msg), NULL);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: rpmsgdev_ioctl_arglen
|
|
*
|
|
* Description:
|
|
* Get rpmsg device ioctl argument length according to the command
|
|
*
|
|
* Parameters:
|
|
* cmd - the ioctl command
|
|
*
|
|
* Returned Values:
|
|
* 0 - ioctl command not support
|
|
* positive - the argument length
|
|
*
|
|
****************************************************************************/
|
|
|
|
static size_t rpmsgdev_ioctl_arglen(int cmd)
|
|
{
|
|
switch (cmd)
|
|
{
|
|
case FIONBIO:
|
|
case FIONWRITE:
|
|
case FIONREAD:
|
|
case FIONSPACE:
|
|
case FBIOSET_POWER:
|
|
case FBIOGET_POWER:
|
|
return sizeof(int);
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: rpmsgdev_ioctl
|
|
*
|
|
* Description:
|
|
* Rpmsg-device ioctl operation
|
|
*
|
|
* Parameters:
|
|
* filep - the file instance
|
|
* cmd - the ioctl command
|
|
* arg - the ioctl arguments
|
|
*
|
|
* Returned Values:
|
|
* OK on success; A negated errno value is returned on any failure.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int rpmsgdev_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
|
|
{
|
|
FAR struct rpmsgdev_s *dev;
|
|
FAR struct rpmsgdev_priv_s *priv;
|
|
FAR struct rpmsgdev_ioctl_s *msg;
|
|
uint32_t space;
|
|
size_t arglen;
|
|
size_t msglen;
|
|
|
|
/* Sanity checks */
|
|
|
|
DEBUGASSERT(filep->f_inode != NULL);
|
|
|
|
/* Recover our private data from the struct file instance */
|
|
|
|
dev = filep->f_inode->i_private;
|
|
priv = filep->f_priv;
|
|
DEBUGASSERT(dev != NULL && priv != NULL);
|
|
|
|
/* Call our internal routine to perform the ioctl */
|
|
|
|
arglen = rpmsgdev_ioctl_arglen(cmd);
|
|
msglen = sizeof(*msg) + arglen - 1;
|
|
|
|
msg = rpmsgdev_get_tx_payload_buffer(dev, &space);
|
|
if (msg == NULL)
|
|
{
|
|
return -ENOMEM;
|
|
}
|
|
|
|
msg->filep = priv->filep;
|
|
msg->request = cmd;
|
|
msg->arg = arg;
|
|
msg->arglen = arglen;
|
|
|
|
if (arglen > 0)
|
|
{
|
|
memcpy(msg->buf, (FAR void *)(uintptr_t)arg, arglen);
|
|
}
|
|
|
|
return rpmsgdev_send_recv(dev, RPMSGDEV_IOCTL, false, &msg->header,
|
|
msglen, arglen > 0 ? (FAR void *)arg : NULL);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: rpmsgdev_poll
|
|
*
|
|
* Description:
|
|
* Rpmsg-device poll operation
|
|
*
|
|
* Parameters:
|
|
* filep - the file instance
|
|
* fds - The structure describing the events to be monitored.
|
|
* setup - true: Setup up the poll; false: Teardown the poll
|
|
*
|
|
* Returned Values:
|
|
* OK on success; A negated errno value is returned on any failure.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int rpmsgdev_poll(FAR struct file *filep, FAR struct pollfd *fds,
|
|
bool setup)
|
|
{
|
|
FAR struct rpmsgdev_s *dev;
|
|
FAR struct rpmsgdev_priv_s *priv;
|
|
struct rpmsgdev_poll_s msg;
|
|
|
|
/* Sanity checks */
|
|
|
|
DEBUGASSERT(filep->f_inode != NULL);
|
|
|
|
/* Recover our private data from the struct file instance */
|
|
|
|
dev = filep->f_inode->i_private;
|
|
priv = filep->f_priv;
|
|
DEBUGASSERT(dev != NULL && priv != NULL);
|
|
|
|
/* Setup or teardown the poll */
|
|
|
|
msg.filep = priv->filep;
|
|
msg.events = fds->events;
|
|
msg.setup = setup;
|
|
msg.fds = (uint64_t)(uintptr_t)fds;
|
|
|
|
return rpmsgdev_send_recv(dev, RPMSGDEV_POLL, true, &msg.header,
|
|
sizeof(msg), NULL);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: rpmsgdev_get_tx_payload_buffer
|
|
*
|
|
* Description:
|
|
* Get the rpmsg device tx payload, the buffer is from the rpmsg
|
|
* share memory that can be accessed by local and remote cpu.
|
|
*
|
|
* Parameters:
|
|
* priv - The rpmsg-device handle
|
|
* len - The got memroy size
|
|
*
|
|
* Returned Values:
|
|
* NULL - failure
|
|
* not NULL - success
|
|
*
|
|
****************************************************************************/
|
|
|
|
static FAR void *rpmsgdev_get_tx_payload_buffer(FAR struct rpmsgdev_s *priv,
|
|
FAR uint32_t *len)
|
|
{
|
|
int sval;
|
|
|
|
nxsem_get_value(&priv->wait, &sval);
|
|
if (sval <= 0)
|
|
{
|
|
rpmsg_wait(&priv->ept, &priv->wait);
|
|
rpmsg_post(&priv->ept, &priv->wait);
|
|
}
|
|
|
|
return rpmsg_get_tx_payload_buffer(&priv->ept, len, true);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: rpmsgdev_send_recv
|
|
*
|
|
* Description:
|
|
* Send and receive the rpmsg data.
|
|
*
|
|
* Parameters:
|
|
* priv - rpmsg device handle
|
|
* command - the command, RPMSGDEV_OPEN, RPMSGDEV_CLOSE, RPMSGDEV_READ,
|
|
* RPMSGDEV_WRITE, RPMSGDEV_IOCTL
|
|
* copy - true, send a message across to the remote processor, and the
|
|
* tx buffer will be alloced inside function rpmsg_send()
|
|
* false, send a message in tx buffer reserved by
|
|
* rpmsg_get_tx_payload_buffer() across to the remote
|
|
* processor.
|
|
* msg - the message header
|
|
* len - length of the payload
|
|
* data - the data
|
|
*
|
|
* Returned Values:
|
|
* OK on success; A negated errno value is returned on any failure.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int rpmsgdev_send_recv(FAR struct rpmsgdev_s *priv,
|
|
uint32_t command, bool copy,
|
|
FAR struct rpmsgdev_header_s *msg,
|
|
int len, FAR void *data)
|
|
{
|
|
struct rpmsgdev_cookie_s cookie;
|
|
int ret;
|
|
|
|
memset(&cookie, 0, sizeof(cookie));
|
|
nxsem_init(&cookie.sem, 0, 0);
|
|
nxsem_set_protocol(&cookie.sem, SEM_PRIO_NONE);
|
|
|
|
if (data != NULL)
|
|
{
|
|
cookie.data = data;
|
|
}
|
|
else if (copy)
|
|
{
|
|
cookie.data = msg;
|
|
}
|
|
|
|
msg->command = command;
|
|
msg->result = -ENXIO;
|
|
msg->cookie = (uintptr_t)&cookie;
|
|
|
|
if (copy)
|
|
{
|
|
ret = rpmsg_send(&priv->ept, msg, len);
|
|
}
|
|
else
|
|
{
|
|
ret = rpmsg_send_nocopy(&priv->ept, msg, len);
|
|
}
|
|
|
|
if (ret < 0)
|
|
{
|
|
goto fail;
|
|
}
|
|
|
|
ret = rpmsg_wait(&priv->ept, &cookie.sem);
|
|
if (ret >= 0)
|
|
{
|
|
ret = cookie.result;
|
|
}
|
|
|
|
fail:
|
|
nxsem_destroy(&cookie.sem);
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: rpmsgdev_default_handler
|
|
*
|
|
* Description:
|
|
* Default rpmsg-device response handler, this function will be called to
|
|
* process the return message of rpmsgdev_open(), rpmsgdev_close() and
|
|
* rpmsgdev_write().
|
|
*
|
|
* Parameters:
|
|
* ept - The rpmsg endpoint
|
|
* data - The return message
|
|
* len - The return message length
|
|
* src - unknow
|
|
* priv - unknow
|
|
*
|
|
* Returned Values:
|
|
* Always OK
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int rpmsgdev_default_handler(FAR struct rpmsg_endpoint *ept,
|
|
FAR void *data, size_t len,
|
|
uint32_t src, FAR void *priv)
|
|
{
|
|
FAR struct rpmsgdev_header_s *header = data;
|
|
FAR struct rpmsgdev_cookie_s *cookie =
|
|
(FAR struct rpmsgdev_cookie_s *)(uintptr_t)header->cookie;
|
|
|
|
cookie->result = header->result;
|
|
if (cookie->result >= 0 && cookie->data)
|
|
{
|
|
memcpy(cookie->data, data, len);
|
|
}
|
|
|
|
rpmsg_post(ept, &cookie->sem);
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: rpmsgdev_read_handler
|
|
*
|
|
* Description:
|
|
* Rpmsg-device read response handler, this function will be called to
|
|
* process the return message of rpmsgdev_read().
|
|
*
|
|
* Parameters:
|
|
* ept - The rpmsg endpoint
|
|
* data - The return message
|
|
* len - The return message length
|
|
* src - unknow
|
|
* priv - unknow
|
|
*
|
|
* Returned Values:
|
|
* Always OK
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int rpmsgdev_read_handler(FAR struct rpmsg_endpoint *ept,
|
|
FAR void *data, size_t len,
|
|
uint32_t src, FAR void *priv)
|
|
{
|
|
FAR struct rpmsgdev_header_s *header = data;
|
|
FAR struct rpmsgdev_cookie_s *cookie =
|
|
(FAR struct rpmsgdev_cookie_s *)(uintptr_t)header->cookie;
|
|
FAR struct rpmsgdev_read_s *rsp = data;
|
|
FAR struct iovec *read = cookie->data;
|
|
|
|
cookie->result = header->result;
|
|
if (cookie->result > 0)
|
|
{
|
|
memcpy(read->iov_base + read->iov_len, rsp->buf, cookie->result);
|
|
read->iov_len += cookie->result;
|
|
}
|
|
|
|
if (cookie->result <= 0 || read->iov_len >= rsp->count)
|
|
{
|
|
rpmsg_post(ept, &cookie->sem);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: rpmsgdev_ioctl_handler
|
|
*
|
|
* Description:
|
|
* Rpmsg-device ioctl response handler, this function will be called to
|
|
* process the return message of rpmsgdev_ioctl().
|
|
*
|
|
* Parameters:
|
|
* ept - The rpmsg endpoint
|
|
* data - The return message
|
|
* len - The return message length
|
|
* src - unknow
|
|
* priv - unknow
|
|
*
|
|
* Returned Values:
|
|
* Always OK
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int rpmsgdev_ioctl_handler(FAR struct rpmsg_endpoint *ept,
|
|
FAR void *data, size_t len,
|
|
uint32_t src, FAR void *priv)
|
|
{
|
|
FAR struct rpmsgdev_header_s *header = data;
|
|
FAR struct rpmsgdev_cookie_s *cookie =
|
|
(FAR struct rpmsgdev_cookie_s *)(uintptr_t)header->cookie;
|
|
FAR struct rpmsgdev_ioctl_s *rsp = data;
|
|
|
|
if (cookie->result >= 0 && rsp->arglen > 0)
|
|
{
|
|
memcpy(cookie->data, (FAR void *)(uintptr_t)rsp->buf, rsp->arglen);
|
|
}
|
|
|
|
rpmsg_post(ept, &cookie->sem);
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: rpmsgdev_notify_handler
|
|
*
|
|
* Description:
|
|
* Rpmsg-device poll notify handler.
|
|
*
|
|
* Parameters:
|
|
* ept - The rpmsg endpoint
|
|
* data - The return message
|
|
* len - The return message length
|
|
* src - unknow
|
|
* priv - unknow
|
|
*
|
|
* Returned Values:
|
|
* Always OK
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int rpmsgdev_notify_handler(FAR struct rpmsg_endpoint *ept,
|
|
FAR void *data, size_t len,
|
|
uint32_t src, FAR void *priv)
|
|
{
|
|
FAR struct rpmsgdev_notify_s *rsp = data;
|
|
FAR struct pollfd *fds;
|
|
|
|
fds = (FAR struct pollfd *)(uintptr_t)rsp->fds;
|
|
poll_notify(&fds, 1, rsp->revents);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: rpmsgdev_ns_bound
|
|
*
|
|
* Description:
|
|
* Rpmsg device end point service bound callback function , called when
|
|
* remote end point address is received.
|
|
*
|
|
* Parameters:
|
|
* ept - The rpmsg-device end point
|
|
*
|
|
* Returned Values:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void rpmsgdev_ns_bound(FAR struct rpmsg_endpoint *ept)
|
|
{
|
|
FAR struct rpmsgdev_s *priv = ept->priv;
|
|
|
|
rpmsg_post(&priv->ept, &priv->wait);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: rpmsgdev_device_created
|
|
*
|
|
* Description:
|
|
* Rpmsg device create function, this function will be called by rptun to
|
|
* create a rpmsg-device end point.
|
|
*
|
|
* Parameters:
|
|
* rdev - The rpmsg-device end point
|
|
* priv_ - Rpmsg-device handle
|
|
*
|
|
* Returned Values:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void rpmsgdev_device_created(FAR struct rpmsg_device *rdev,
|
|
FAR void *priv_)
|
|
{
|
|
FAR struct rpmsgdev_s *priv = priv_;
|
|
char buf[RPMSG_NAME_SIZE];
|
|
|
|
if (strcmp(priv->remotecpu, rpmsg_get_cpuname(rdev)) == 0)
|
|
{
|
|
priv->ept.priv = priv;
|
|
priv->ept.ns_bound_cb = rpmsgdev_ns_bound;
|
|
snprintf(buf, sizeof(buf), "%s%s", RPMSGDEV_NAME_PREFIX,
|
|
priv->remotepath);
|
|
rpmsg_create_ept(&priv->ept, rdev, buf,
|
|
RPMSG_ADDR_ANY, RPMSG_ADDR_ANY,
|
|
rpmsgdev_ept_cb, NULL);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: rpmsgdev_device_destroy
|
|
*
|
|
* Description:
|
|
* Rpmsg device destroy function, this function will be called by rptun to
|
|
* destroy rpmsg-device end point.
|
|
*
|
|
* Parameters:
|
|
* rdev - The rpmsg-device end point
|
|
* priv_ - Rpmsg-device handle
|
|
*
|
|
* Returned Values:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void rpmsgdev_device_destroy(FAR struct rpmsg_device *rdev,
|
|
FAR void *priv_)
|
|
{
|
|
FAR struct rpmsgdev_s *priv = priv_;
|
|
|
|
if (strcmp(priv->remotecpu, rpmsg_get_cpuname(rdev)) == 0)
|
|
{
|
|
rpmsg_destroy_ept(&priv->ept);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: rpmsgdev_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 rpmsgdev_ept_cb(FAR struct rpmsg_endpoint *ept,
|
|
FAR void *data, size_t len, uint32_t src,
|
|
FAR void *priv)
|
|
{
|
|
FAR struct rpmsgdev_header_s *header = data;
|
|
uint32_t command = header->command;
|
|
|
|
if (command < ARRAY_SIZE(g_rpmsgdev_handler))
|
|
{
|
|
return g_rpmsgdev_handler[command](ept, data, len, src, priv);
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: rpmsgdev_register
|
|
*
|
|
* Description:
|
|
* Rpmsg-device client initialize function, the client cpu should call
|
|
* this function in the board initialize process.
|
|
*
|
|
* Parameters:
|
|
* remotecpu - the server cpu name
|
|
* remotepath - the device you want to access in the remote cpu
|
|
* localpath - the device path in local cpu, if NULL, the localpath is
|
|
* same as the remotepath, provide this argument to supoort
|
|
* custom device path
|
|
*
|
|
* Returned Values:
|
|
* OK on success; A negated errno value is returned on any failure.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int rpmsgdev_register(FAR const char *remotecpu, FAR const char *remotepath,
|
|
FAR const char *localpath)
|
|
{
|
|
FAR struct rpmsgdev_s *dev;
|
|
int ret;
|
|
|
|
/* Arguments check */
|
|
|
|
if (remotecpu == NULL || remotepath == NULL)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
|
|
dev = kmm_zalloc(sizeof(*dev));
|
|
if (dev == NULL)
|
|
{
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/* Initialize the rpmsg device */
|
|
|
|
dev->remotecpu = remotecpu;
|
|
dev->remotepath = remotepath;
|
|
|
|
nxsem_init(&dev->wait, 0, 0);
|
|
nxsem_set_protocol(&dev->wait, SEM_PRIO_NONE);
|
|
|
|
/* Register the rpmsg callback */
|
|
|
|
ret = rpmsg_register_callback(dev,
|
|
rpmsgdev_device_created,
|
|
rpmsgdev_device_destroy,
|
|
NULL,
|
|
NULL);
|
|
if (ret < 0)
|
|
{
|
|
rpmsgdeverr("register callback failed, ret=%d\n", ret);
|
|
goto fail;
|
|
}
|
|
|
|
/* Register driver, using the remotepath if localpath is NULL */
|
|
|
|
if (localpath == NULL)
|
|
{
|
|
localpath = remotepath;
|
|
}
|
|
|
|
ret = register_driver(localpath, &g_rpmsgdev_ops, 0666, dev);
|
|
if (ret < 0)
|
|
{
|
|
rpmsgdeverr("register driver failed, ret=%d\n", ret);
|
|
goto fail_with_rpmsg;
|
|
}
|
|
|
|
return OK;
|
|
|
|
fail_with_rpmsg:
|
|
rpmsg_unregister_callback(dev,
|
|
rpmsgdev_device_created,
|
|
rpmsgdev_device_destroy,
|
|
NULL,
|
|
NULL);
|
|
|
|
fail:
|
|
nxsem_destroy(&dev->wait);
|
|
kmm_free(dev);
|
|
|
|
return ret;
|
|
}
|