nuttx/drivers/misc/rpmsgdev.c
wangbowen6 52a1692cfa rpmsgdev: add rpmsg-device support.
Signed-off-by: wangbowen6 <wangbowen6@xiaomi.com>
2022-08-17 18:33:58 +08:00

1038 lines
28 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 <limits.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_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.
*/
mutex_t excl; /* Exclusive mutex, used to support thread
* safe.
*/
int open_count; /* Device open count */
};
/* 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);
/* 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);
/* 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,
};
/* 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 /* 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;
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);
/* Take the semaphore */
ret = nxmutex_lock(&dev->excl);
if (ret < 0)
{
rpmsgdeverr("semtake error, ret=%d\n", ret);
return ret;
}
/* Check if this is the first time that the driver has been opened. */
if (dev->open_count++ == 0)
{
/* Try to open the file in the host file system */
msg.flags = filep->f_oflags;
ret = rpmsgdev_send_recv(dev, RPMSGDEV_OPEN, true, &msg.header,
sizeof(msg), NULL);
if (ret < 0)
{
rpmsgdeverr("open failed\n");
dev->open_count--;
}
}
nxmutex_unlock(&dev->excl);
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;
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;
DEBUGASSERT(dev != NULL);
/* Take the semaphore */
ret = nxmutex_lock(&dev->excl);
if (ret < 0)
{
return ret;
}
/* There are no more references to the port */
if (--dev->open_count == 0)
{
ret = rpmsgdev_send_recv(dev, RPMSGDEV_CLOSE, true, &msg.header,
sizeof(msg), NULL);
if (ret < 0)
{
dev->open_count++;
}
}
nxmutex_unlock(&dev->excl);
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;
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;
DEBUGASSERT(dev != NULL);
/* Take the semaphore */
ret = nxmutex_lock(&dev->excl);
if (ret < 0)
{
return ret;
}
/* Call the host to perform the read */
read.iov_base = buffer;
read.iov_len = 0;
msg.count = buflen;
ret = rpmsgdev_send_recv(dev, RPMSGDEV_READ, true, &msg.header,
sizeof(msg) - 1, &read);
nxmutex_unlock(&dev->excl);
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_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;
DEBUGASSERT(dev != NULL);
/* Take the semaphore */
ret = nxmutex_lock(&dev->excl);
if (ret < 0)
{
return ret;
}
/* 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->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);
nxmutex_unlock(&dev->excl);
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;
struct rpmsgdev_lseek_s msg;
off_t ret;
/* Sanity checks */
DEBUGASSERT(filep->f_inode != NULL);
/* Recover our private data from the struct file instance */
dev = filep->f_inode->i_private;
DEBUGASSERT(dev != NULL);
/* Take the semaphore */
ret = nxmutex_lock(&dev->excl);
if (ret < 0)
{
return ret;
}
/* Call our internal routine to perform the seek */
msg.offset = offset;
msg.whence = whence;
ret = rpmsgdev_send_recv(dev, RPMSGDEV_LSEEK, true, &msg.header,
sizeof(msg), NULL);
nxmutex_unlock(&dev->excl);
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 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_ioctl_s *msg;
uint32_t space;
size_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;
DEBUGASSERT(dev != NULL);
/* Take the semaphore */
ret = nxmutex_lock(&dev->excl);
if (ret < 0)
{
return ret;
}
/* 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->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);
nxmutex_unlock(&dev->excl);
return ret;
}
/****************************************************************************
* 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_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;
nxmutex_init(&dev->excl);
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:
nxmutex_destroy(&dev->excl);
nxsem_destroy(&dev->wait);
kmm_free(dev);
return ret;
}