#include #include #include #include #include #include #include #include /************************************************************************* * 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; }