| #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; |
| } |