344 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			344 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #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;
 | |
| }
 | 
