0ead147841
The default mode for Rpmsgdev is to read/write data as long as possible for caller, this mode does not apply to tun devices, tun devices can read and write only one complete ip packet at a time. Signed-off-by: zhanghongyu <zhanghongyu@xiaomi.com>
1233 lines
34 KiB
C
1233 lines
34 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 <nuttx/drivers/rpmsgdev.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 */
|
|
bool nonblock; /* true: open with O_NONBLOCK */
|
|
};
|
|
|
|
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.
|
|
*/
|
|
uint32_t flags; /* Read and write special handle flags */
|
|
};
|
|
|
|
/* 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 void rpmsgdev_wait_cb(FAR struct pollfd *fds);
|
|
static int rpmsgdev_wait(FAR struct file *filep, pollevent_t events);
|
|
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 ssize_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_READ_NOFRAG] = 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 */
|
|
NULL, /* mmap */
|
|
NULL, /* truncate */
|
|
rpmsgdev_poll /* poll */
|
|
};
|
|
|
|
/****************************************************************************
|
|
* 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, open with O_NONBLOCK
|
|
* by default to avoid the server rptun thread blocked in read/write
|
|
* operations.
|
|
*/
|
|
|
|
msg.flags = filep->f_oflags | O_NONBLOCK;
|
|
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;
|
|
priv->nonblock = (filep->f_oflags & O_NONBLOCK) != 0;
|
|
|
|
/* 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_wait_cb
|
|
*
|
|
* Description:
|
|
* Rpmsg-device read/write operation wait callback function
|
|
*
|
|
* Parameters:
|
|
* fds - The structure describing the events to be monitored.
|
|
*
|
|
* Returned Values:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void rpmsgdev_wait_cb(FAR struct pollfd *fds)
|
|
{
|
|
int semcount = 0;
|
|
FAR sem_t *pollsem = (FAR sem_t *)fds->arg;
|
|
|
|
nxsem_get_value(pollsem, &semcount);
|
|
if (semcount < 1)
|
|
{
|
|
nxsem_post(pollsem);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: rpmsgdev_wait
|
|
*
|
|
* Description:
|
|
* Rpmsg-device read/write operation wait function, this function will be
|
|
* called in the rpmsgdev_read()/rpmsgdev_write() when the open flags is
|
|
* not NONBLOCKED to avoid the server rptun thread blocked in file_read()
|
|
* or file_write(). By calling this function before sending the READ or
|
|
* WRITE command to server, a simulated blocked read/write operation is
|
|
* achived.
|
|
*
|
|
* Parameters:
|
|
* filep - the file instance
|
|
* events - the events to be monitored
|
|
*
|
|
* Returned Values:
|
|
* OK on success; A negated errno value is returned on any failure.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int rpmsgdev_wait(FAR struct file *filep, pollevent_t events)
|
|
{
|
|
int ret;
|
|
sem_t sem;
|
|
struct pollfd fds;
|
|
|
|
nxsem_init(&sem, 0, 0);
|
|
|
|
fds.events = events;
|
|
fds.arg = &sem;
|
|
fds.cb = rpmsgdev_wait_cb;
|
|
events |= POLLERR | POLLHUP;
|
|
|
|
while (1)
|
|
{
|
|
ret = rpmsgdev_poll(filep, &fds, true);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
ret = nxsem_wait(&sem);
|
|
rpmsgdev_poll(filep, &fds, false);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
if ((fds.revents & events) != 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
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;
|
|
uint32_t command;
|
|
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);
|
|
|
|
/* If the open flags is not nonblock, should poll the device for
|
|
* read ready first to avoid the server rptun thread blocked in
|
|
* device read operation.
|
|
*/
|
|
|
|
if (priv->nonblock == false)
|
|
{
|
|
ret = rpmsgdev_wait(filep, POLLIN);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
/* Call the host to perform the read */
|
|
|
|
read.iov_base = buffer;
|
|
read.iov_len = 0;
|
|
|
|
msg.filep = priv->filep;
|
|
msg.count = buflen;
|
|
command = dev->flags & RPMSGDEV_NOFRAG_READ ?
|
|
RPMSGDEV_READ_NOFRAG : RPMSGDEV_READ;
|
|
|
|
ret = rpmsgdev_send_recv(dev, command, 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);
|
|
|
|
/* If the open flags is not nonblock, should poll the device for
|
|
* write ready first to avoid the server rptun thread blocked in
|
|
* device write operation.
|
|
*/
|
|
|
|
if (priv->nonblock == false)
|
|
{
|
|
ret = rpmsgdev_wait(filep, POLLOUT);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
/* Perform the rpmsg write */
|
|
|
|
memset(&cookie, 0, sizeof(cookie));
|
|
nxsem_init(&cookie.sem, 0, 0);
|
|
|
|
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 if ((dev->flags & RPMSGDEV_NOFRAG_WRITE) != 0)
|
|
{
|
|
rpmsg_release_tx_buffer(&dev->ept, msg);
|
|
ret = -EMSGSIZE;
|
|
goto out;
|
|
}
|
|
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;
|
|
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);
|
|
|
|
/* Call our internal routine to perform the seek */
|
|
|
|
msg.filep = priv->filep;
|
|
msg.offset = offset;
|
|
msg.whence = whence;
|
|
|
|
ret = rpmsgdev_send_recv(dev, RPMSGDEV_LSEEK, true, &msg.header,
|
|
sizeof(msg), NULL);
|
|
if (ret >= 0)
|
|
{
|
|
filep->f_pos = msg.offset;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* 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 ssize_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 -ENOTTY;
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* 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;
|
|
ssize_t arglen;
|
|
size_t msglen;
|
|
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);
|
|
|
|
/* Call our internal routine to perform the ioctl */
|
|
|
|
arglen = rpmsgdev_ioctl_arglen(cmd);
|
|
if (arglen < 0)
|
|
{
|
|
return arglen;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
ret = rpmsgdev_send_recv(dev, RPMSGDEV_IOCTL, false, &msg->header,
|
|
msglen, arglen > 0 ? (FAR void *)arg : NULL);
|
|
if (cmd == FIONBIO && ret >= 0)
|
|
{
|
|
int *nonblock = (FAR int *)(uintptr_t)arg;
|
|
priv->nonblock = *nonblock;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* 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);
|
|
|
|
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 (header->command == RPMSGDEV_READ_NOFRAG ||
|
|
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;
|
|
|
|
cookie->result = header->result;
|
|
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 < nitems(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
|
|
* flags - RPMSGDEV_NOFRAG_READ and RPMSGDEV_NOFRAG_WRITE can be set
|
|
* to indicates that the read and write data of the device
|
|
* cannot be split or aggregated
|
|
*
|
|
* 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, uint32_t flags)
|
|
{
|
|
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;
|
|
dev->flags = flags;
|
|
|
|
nxsem_init(&dev->wait, 0, 0);
|
|
|
|
/* 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;
|
|
}
|