blob: 9035db2299f6114f76f03fd1bd0870a1d665e623 [file] [log] [blame]
#include <metal/mutex.h>
#include <metal/spinlock.h>
#include <metal/utilities.h>
#include <openamp/open_amp.h>
#include <openamp/rpmsg_retarget.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
/*************************************************************************
* Description
* This files contains rpmsg based redefinitions for C RTL system calls
* such as _open, _read, _write, _close.
*************************************************************************/
static struct rpmsg_rpc_data *rpmsg_default_rpc;
static int rpmsg_rpc_ept_cb(struct rpmsg_endpoint *ept, void *data, size_t len,
uint32_t src, void *priv)
{
struct rpmsg_rpc_syscall *syscall;
(void)priv;
(void)src;
if (data != NULL && ept != NULL) {
syscall = data;
if (syscall->id == TERM_SYSCALL_ID) {
rpmsg_destroy_ept(ept);
} else {
struct rpmsg_rpc_data *rpc;
rpc = metal_container_of(ept,
struct rpmsg_rpc_data,
ept);
metal_spinlock_acquire(&rpc->buflock);
if (rpc->respbuf != NULL && rpc->respbuf_len != 0) {
if (len > rpc->respbuf_len)
len = rpc->respbuf_len;
memcpy(rpc->respbuf, data, len);
}
atomic_flag_clear(&rpc->nacked);
metal_spinlock_release(&rpc->buflock);
}
}
return RPMSG_SUCCESS;
}
static void rpmsg_service_unbind(struct rpmsg_endpoint *ept)
{
struct rpmsg_rpc_data *rpc;
rpc = metal_container_of(ept, struct rpmsg_rpc_data, ept);
rpc->ept_destroyed = 1;
rpmsg_destroy_ept(ept);
atomic_flag_clear(&rpc->nacked);
if (rpc->shutdown_cb)
rpc->shutdown_cb(rpc);
}
int rpmsg_rpc_init(struct rpmsg_rpc_data *rpc,
struct rpmsg_device *rdev,
const char *ept_name, uint32_t ept_addr,
uint32_t ept_raddr,
void *poll_arg, rpmsg_rpc_poll poll,
rpmsg_rpc_shutdown_cb shutdown_cb)
{
int ret;
if (rpc == NULL || rdev == NULL)
return -EINVAL;
metal_spinlock_init(&rpc->buflock);
metal_mutex_init(&rpc->lock);
rpc->shutdown_cb = shutdown_cb;
rpc->poll_arg = poll_arg;
rpc->poll = poll;
rpc->ept_destroyed = 0;
rpc->respbuf = NULL;
rpc->respbuf_len = 0;
atomic_init(&rpc->nacked, 1);
ret = rpmsg_create_ept(&rpc->ept, rdev,
ept_name, ept_addr, ept_raddr,
rpmsg_rpc_ept_cb, rpmsg_service_unbind);
if (ret != 0) {
metal_mutex_release(&rpc->lock);
return -EINVAL;
}
while (!is_rpmsg_ept_ready(&rpc->ept)) {
if (rpc->poll)
rpc->poll(rpc->poll_arg);
}
return 0;
}
void rpmsg_rpc_release(struct rpmsg_rpc_data *rpc)
{
if (rpc == NULL)
return;
if (rpc->ept_destroyed == 0)
rpmsg_destroy_ept(&rpc->ept);
metal_mutex_acquire(&rpc->lock);
metal_spinlock_acquire(&rpc->buflock);
rpc->respbuf = NULL;
rpc->respbuf_len = 0;
metal_spinlock_release(&rpc->buflock);
metal_mutex_release(&rpc->lock);
metal_mutex_deinit(&rpc->lock);
return;
}
int rpmsg_rpc_send(struct rpmsg_rpc_data *rpc,
void *req, size_t len,
void *resp, size_t resp_len)
{
int ret;
if (rpc == NULL)
return -EINVAL;
metal_spinlock_acquire(&rpc->buflock);
rpc->respbuf = resp;
rpc->respbuf_len = resp_len;
metal_spinlock_release(&rpc->buflock);
(void)atomic_flag_test_and_set(&rpc->nacked);
ret = rpmsg_send(&rpc->ept, req, len);
if (ret < 0)
return -EINVAL;
if (!resp)
return ret;
while((atomic_flag_test_and_set(&rpc->nacked))) {
if (rpc->poll)
rpc->poll(rpc->poll_arg);
}
return ret;
}
void rpmsg_set_default_rpc(struct rpmsg_rpc_data *rpc)
{
if (rpc == NULL)
return;
rpmsg_default_rpc = rpc;
}
/*************************************************************************
*
* FUNCTION
*
* _open
*
* DESCRIPTION
*
* Open a file. Minimal implementation
*
*************************************************************************/
#define MAX_BUF_LEN 496UL
int _open(const char *filename, int flags, int mode)
{
struct rpmsg_rpc_data *rpc = rpmsg_default_rpc;
struct rpmsg_rpc_syscall *syscall;
struct rpmsg_rpc_syscall resp;
int filename_len = strlen(filename) + 1;
int payload_size = sizeof(*syscall) + filename_len;
unsigned char tmpbuf[MAX_BUF_LEN];
int ret;
if (filename == NULL || payload_size > (int)MAX_BUF_LEN) {
return -EINVAL;
}
if (rpc == NULL)
return -EINVAL;
/* Construct rpc payload */
syscall = (struct rpmsg_rpc_syscall *)tmpbuf;
syscall->id = OPEN_SYSCALL_ID;
syscall->args.int_field1 = flags;
syscall->args.int_field2 = mode;
syscall->args.data_len = filename_len;
memcpy(tmpbuf + sizeof(*syscall), filename, filename_len);
resp.id = 0;
ret = rpmsg_rpc_send(rpc, tmpbuf, payload_size,
(void *)&resp, sizeof(resp));
if (ret >= 0) {
/* Obtain return args and return to caller */
if (resp.id == OPEN_SYSCALL_ID)
ret = resp.args.int_field1;
else
ret = -EINVAL;
}
return ret;
}
/*************************************************************************
*
* FUNCTION
*
* _read
*
* DESCRIPTION
*
* Low level function to redirect IO to serial.
*
*************************************************************************/
int _read(int fd, char *buffer, int buflen)
{
struct rpmsg_rpc_syscall syscall;
struct rpmsg_rpc_syscall *resp;
struct rpmsg_rpc_data *rpc = rpmsg_default_rpc;
int payload_size = sizeof(syscall);
unsigned char tmpbuf[MAX_BUF_LEN];
int ret;
if (rpc == NULL || buffer == NULL || buflen == 0)
return -EINVAL;
/* Construct rpc payload */
syscall.id = READ_SYSCALL_ID;
syscall.args.int_field1 = fd;
syscall.args.int_field2 = buflen;
syscall.args.data_len = 0; /*not used */
resp = (struct rpmsg_rpc_syscall *)tmpbuf;
resp->id = 0;
ret = rpmsg_rpc_send(rpc, (void *)&syscall, payload_size,
tmpbuf, sizeof(tmpbuf));
/* Obtain return args and return to caller */
if (ret >= 0) {
if (resp->id == READ_SYSCALL_ID) {
if (resp->args.int_field1 > 0) {
int tmplen = resp->args.data_len;
unsigned char *tmpptr = tmpbuf;
tmpptr += sizeof(*resp);
if (tmplen > buflen)
tmplen = buflen;
memcpy(buffer, tmpptr, tmplen);
}
ret = resp->args.int_field1;
} else {
ret = -EINVAL;
}
}
return ret;
}
/*************************************************************************
*
* FUNCTION
*
* _write
*
* DESCRIPTION
*
* Low level function to redirect IO to serial.
*
*************************************************************************/
int _write(int fd, const char *ptr, int len)
{
int ret;
struct rpmsg_rpc_syscall *syscall;
struct rpmsg_rpc_syscall resp;
int payload_size = sizeof(*syscall) + len;
struct rpmsg_rpc_data *rpc = rpmsg_default_rpc;
unsigned char tmpbuf[MAX_BUF_LEN];
unsigned char *tmpptr;
int null_term = 0;
if (rpc == NULL)
return -EINVAL;
if (fd == 1)
null_term = 1;
syscall = (struct rpmsg_rpc_syscall *)tmpbuf;
syscall->id = WRITE_SYSCALL_ID;
syscall->args.int_field1 = fd;
syscall->args.int_field2 = len;
syscall->args.data_len = len + null_term;
tmpptr = tmpbuf + sizeof(*syscall);
memcpy(tmpptr, ptr, len);
if (null_term == 1) {
*(char *)(tmpptr + len + null_term) = 0;
payload_size += 1;
}
resp.id = 0;
ret = rpmsg_rpc_send(rpc, tmpbuf, payload_size,
(void *)&resp, sizeof(resp));
if (ret >= 0) {
if (resp.id == WRITE_SYSCALL_ID)
ret = resp.args.int_field1;
else
ret = -EINVAL;
}
return ret;
}
/*************************************************************************
*
* FUNCTION
*
* _close
*
* DESCRIPTION
*
* Close a file. Minimal implementation
*
*************************************************************************/
int _close(int fd)
{
int ret;
struct rpmsg_rpc_syscall syscall;
struct rpmsg_rpc_syscall resp;
int payload_size = sizeof(syscall);
struct rpmsg_rpc_data *rpc = rpmsg_default_rpc;
if (rpc == NULL)
return -EINVAL;
syscall.id = CLOSE_SYSCALL_ID;
syscall.args.int_field1 = fd;
syscall.args.int_field2 = 0; /*not used */
syscall.args.data_len = 0; /*not used */
resp.id = 0;
ret = rpmsg_rpc_send(rpc, (void*)&syscall, payload_size,
(void*)&resp, sizeof(resp));
if (ret >= 0) {
if (resp.id == CLOSE_SYSCALL_ID)
ret = resp.args.int_field1;
else
ret = -EINVAL;
}
return ret;
}