diff --git a/apps/examples/CMakeLists.txt b/apps/examples/CMakeLists.txt index fa72d9e..e75473e 100644 --- a/apps/examples/CMakeLists.txt +++ b/apps/examples/CMakeLists.txt @@ -13,5 +13,6 @@ if (WITH_LOAD_FW) endif (WITH_LOAD_FW) if (WITH_PROXY_APPS) add_subdirectory (rpc_demo) +add_subdirectory (linux_rpc_demo) endif (WITH_PROXY_APPS) endif (MACHINE MATCHES ".*microblaze.*") diff --git a/apps/examples/linux_rpc_demo/CMakeLists.txt b/apps/examples/linux_rpc_demo/CMakeLists.txt new file mode 100644 index 0000000..f10cb89 --- /dev/null +++ b/apps/examples/linux_rpc_demo/CMakeLists.txt @@ -0,0 +1,47 @@ +set (_cflags "${CMAKE_C_FLAGS} ${APP_EXTRA_C_FLAGS}") +set (_fw_dir "${APPS_SHARE_DIR}") + +collector_list (_list PROJECT_INC_DIRS) +collector_list (_app_list APP_INC_DIRS) +include_directories (${_list} ${_app_list} ${CMAKE_CURRENT_SOURCE_DIR}) + +collector_list (_list PROJECT_LIB_DIRS) +collector_list (_app_list APP_LIB_DIRS) +link_directories (${_list} ${_app_list}) + +get_property (_linker_opt GLOBAL PROPERTY APP_LINKER_LARGE_TEXT_OPT) +if (NOT _linker_opt) + get_property (_linker_opt GLOBAL PROPERTY APP_LINKER_OPT) +endif (NOT _linker_opt) +collector_list (_deps PROJECT_LIB_DEPS) + +set (OPENAMP_LIB open_amp) + +if (${PROJECT_SYSTEM} STREQUAL "linux") + set (app_list linux_rpc_demod linux_rpc_demo) +endif (${PROJECT_SYSTEM} STREQUAL "linux") + +foreach (_app ${app_list}) + collector_list (_sources APP_COMMON_SOURCES) + list (APPEND _sources "${CMAKE_CURRENT_SOURCE_DIR}/${_app}.c") + + if (WITH_SHARED_LIB) + add_executable (${_app}-shared ${_sources}) + target_link_libraries (${_app}-shared ${OPENAMP_LIB}-shared ${_deps}) + install (TARGETS ${_app}-shared RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + endif (WITH_SHARED_LIB) + + if (WITH_STATIC_LIB) + if (${PROJECT_SYSTEM} STREQUAL "linux") + add_executable (${_app}-static ${_sources}) + target_link_libraries (${_app}-static ${OPENAMP_LIB}-static ${_deps}) + install (TARGETS ${_app}-static RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + else (${PROJECT_SYSTEM}) + add_executable (${_app}.out ${_sources}) + set_source_files_properties(${_sources} PROPERTIES COMPILE_FLAGS "${_cflags}") + + target_link_libraries(${_app}.out -Wl,-Map=${_app}.map -Wl,--gc-sections ${_linker_opt} -Wl,--start-group ${OPENAMP_LIB}-static ${_deps} -Wl,--end-group) + install (TARGETS ${_app}.out RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + endif (${PROJECT_SYSTEM} STREQUAL "linux" ) + endif (WITH_STATIC_LIB) +endforeach(_app) diff --git a/apps/examples/linux_rpc_demo/README.md b/apps/examples/linux_rpc_demo/README.md new file mode 100644 index 0000000..40deb9d --- /dev/null +++ b/apps/examples/linux_rpc_demo/README.md @@ -0,0 +1,58 @@ +# linux_rpc_demo +This readme is about the OpenAMP linux_rpc_demo. + +The linux_rpc_demo is about remote procedure calls between linux master and linux +slave using rpmsg to perform +1. File operations such as open, read, write and close +2. I/O operation such as printf, scanf + +## Compilation + +### Linux Compilation +* Install libsysfs devel and libhugetlbfs devel packages on your Linux. +* build libmetal library: + + ``` + $ mkdir -p build-libmetal + $ cd build-libmetal + $ cmake + $ make VERBOSE=1 DESTDIR= install + ``` + +* build OpenAMP library: + + ``` + $ mkdir -p build-openamp + $ cd build-openamp + $ cmake -DCMAKE_INCLUDE_PATH= \ + -DCMAKE_LIBRARY_PATH= -DWITH_APPS=ON -DWITH_PROXY=ON + $ make VERBOSE=1 DESTDIR=$(pwd) install + ``` + +The OpenAMP library will be generated to `build/usr/local/lib` directory, headers will be +generated to `build/usr/local/include` directory, and the applications executable will be +generated to `build/usr/local/bin` directory. + +* cmake option `-DWITH_APPS=ON` is to build the demonstration applications. +* cmake option `-DWITH_PROXY=ON` to build the linux rpc app. + +## Run the Demo + +### linux_rpc_demo: + +* Start rpc demo server on one console + ``` + $ sudo LD_LIBRARY_PATH=/usr/local/lib:/usr/local/lib \ + build-openamp/usr/local/bin/linux_rpc_demod-shared + ``` + +* Run rpc demo client on another console to perform file and I/O operations on the server + ``` + $ sudo LD_LIBRARY_PATH=/usr/local/lib:/usr/local/lib \ + build-openamp/usr/local/bin/linux_rpc_demo-shared 1 + ``` +Enter the inputs on the master side the same gets printed on the remote side. You will see communication between the master and remote process using rpmsg calls. + +## Note: +`sudo` is required to run the OpenAMP demos between Linux processes, as it doesn't work on +some systems if you are normal users. diff --git a/apps/examples/linux_rpc_demo/linux-rpmsg-rpc-demo.h b/apps/examples/linux_rpc_demo/linux-rpmsg-rpc-demo.h new file mode 100644 index 0000000..3340f9e --- /dev/null +++ b/apps/examples/linux_rpc_demo/linux-rpmsg-rpc-demo.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2021, L&T Technology Services Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef RPMSG_RPC_DEMO_H +#define RPMSG_RPC_DEMO_H + +#define OPEN_ID 0x1UL +#define CLOSE_ID 0x2UL +#define WRITE_ID 0x3UL +#define READ_ID 0x4UL +#define ACK_STATUS_ID 0x5UL +#define TERM_ID 0x6UL +#define INPUT_ID 0x7UL +#define MAX_STRING_LEN 300 +#define MAX_FILE_NAME_LEN 10 + +typedef int (*rpmsg_rpc_poll)(void *arg); + +struct polling { + rpmsg_rpc_poll poll; + void *poll_arg; +}; + +struct rpmsg_rpc_req_open { + char filename[MAX_FILE_NAME_LEN]; + int flags; + int mode; +}; + +struct rpmsg_rpc_req_read { + int fd; + uint32_t buflen; +}; + +struct rpmsg_rpc_req_input { + uint32_t buflen; +}; + +struct rpmsg_rpc_req_write { + int fd; + uint32_t len; + char ptr[MAX_STRING_LEN]; +}; + +struct rpmsg_rpc_req_close { + int fd; +}; + +struct rpmsg_rpc_req_term { + int id; +}; + +struct rpmsg_rpc_resp_open { + int fd; +}; + +struct rpmsg_rpc_resp_read { + int bytes_read; + char buf[MAX_STRING_LEN]; +}; + +struct rpmsg_rpc_resp_input { + int bytes_read; + char buf[MAX_STRING_LEN]; +}; + +struct rpmsg_rpc_resp_write { + int bytes_written; +}; + +struct rpmsg_rpc_resp_close { + int close_ret; +}; + +#endif /* RPMSG_RPC_DEMO_H */ diff --git a/apps/examples/linux_rpc_demo/linux_rpc_demo.c b/apps/examples/linux_rpc_demo/linux_rpc_demo.c new file mode 100644 index 0000000..63d2c78 --- /dev/null +++ b/apps/examples/linux_rpc_demo/linux_rpc_demo.c @@ -0,0 +1,599 @@ +/* + * Copyright (c) 2021, L&T Technology Services Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/*This is a sample demonstration application that showcases usage of proxy + *from the remote core. + *This application is meant to run on the remote CPU running linux. + *This application can print to the master console and perform file I/O through + *rpmsg channels. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "rsc_table.h" +#include "platform_info.h" +#include "linux-rpmsg-rpc-demo.h" + +#define REDEF_O_CREAT 0000100 +#define REDEF_O_EXCL 0000200 +#define REDEF_O_RDONLY 0000000 +#define REDEF_O_WRONLY 0000001 +#define REDEF_O_RDWR 0000002 +#define REDEF_O_APPEND 0002000 +#define REDEF_O_ACCMODE 0000003 + +#define LPRINTF(format, ...) printf(format, ##__VA_ARGS__) +#define LPERROR(format, ...) LPRINTF("ERROR: " format, ##__VA_ARGS__) + +static struct rpmsg_rpc_clt *rpmsg_default_rpc; +static int fd, bytes_written, bytes_read; +static struct polling poll; +static atomic_int wait_resp; + +static void rpmsg_rpc_shutdown(struct rpmsg_rpc_clt *rpc) +{ + (void)rpc; + LPRINTF("RPMSG RPC is shutting down.\r\n"); +} + +void linux_rpmsg_set_default_rpc(struct rpmsg_rpc_clt *rpc) +{ + if (!rpc) + return; + rpmsg_default_rpc = rpc; +} + +/************************************************************************* + * + * FUNCTION + * + * rpmsg_open_cb + * + * DESCRIPTION + * + * Callback function of rpmsg_open + * + *************************************************************************/ +void rpmsg_open_cb(struct rpmsg_rpc_clt *rpc, int status, void *data, + size_t len) +{ + struct rpmsg_rpc_resp_open *resp = + (struct rpmsg_rpc_resp_open *)data; + (void)len; + (void)rpc; + + /* Assign value from return args */ + if (status) + fd = 0; + else + fd = resp->fd; + + /* to clear the flag set in the caller function */ + atomic_flag_clear(&wait_resp); + +} + +/************************************************************************* + * + * FUNCTION + * + * rpmsg_open + * + * DESCRIPTION + * + * Open a file. + * + *************************************************************************/ +int rpmsg_open(const char *filename, int flags, int mode) +{ + struct rpmsg_rpc_clt *rpc = rpmsg_default_rpc; + struct rpmsg_rpc_req_open rpc_open_req; + int filename_len = strlen(filename) + 1; + unsigned int payload_size = sizeof(rpc_open_req); + + if (!filename || payload_size > (int)MAX_BUF_LEN || !rpc) { + return -EINVAL; + } + + /* Construct rpc payload */ + rpc_open_req.flags = flags; + rpc_open_req.mode = mode; + memcpy(rpc_open_req.filename, filename, filename_len); + + /* flag set to wait for response from endpoint callback */ + (void)atomic_flag_test_and_set(&wait_resp); + + /* Trying to send until the endpoint is ready */ + while (rpmsg_rpc_client_send(rpc, OPEN_ID, &rpc_open_req, payload_size) + < 0) { + if (poll.poll) + poll.poll(poll.poll_arg); + } + + /* waiting to get response from endpoint callback */ + while ((atomic_flag_test_and_set(&wait_resp))) { + if (poll.poll) + poll.poll(poll.poll_arg); + } + + return 0; +} + +static char *read_req_buffer; + +/************************************************************************* + * + * FUNCTION + * + * rpmsg_read_cb + * + * DESCRIPTION + * + * Callback function of rpmsg_read + * + *************************************************************************/ +void rpmsg_read_cb(struct rpmsg_rpc_clt *rpc, int status, void *data, + size_t len) +{ + struct rpmsg_rpc_resp_read *resp = + (struct rpmsg_rpc_resp_read *)data; + (void)len; + (void)rpc; + + if (status) { + bytes_read = 0; + goto out; + } + + /* Assign value from return args */ + if (resp->bytes_read > 0) { + memcpy(read_req_buffer, resp->buf, sizeof(resp->buf)); + } + bytes_read = resp->bytes_read; + +out: + /* to clear the flag set in the caller function */ + atomic_flag_clear(&wait_resp); +} + +/************************************************************************* + * + * FUNCTION + * + * rpmsg_read + * + * DESCRIPTION + * + * Read data through RPMsg channel + * + * return the number of data read, à in error case + *************************************************************************/ +int rpmsg_read(int fd, char *buffer, int buflen) +{ + struct rpmsg_rpc_clt *rpc = rpmsg_default_rpc; + struct rpmsg_rpc_req_read rpc_read_req; + unsigned int payload_size = sizeof(rpc_read_req); + int ret; + + if (!rpc || !buffer || buflen == 0) + return -EINVAL; + + /* store buffer address and size for the callback */ + read_req_buffer = buffer; + bytes_read = 0; + + /* Construct rpc payload */ + rpc_read_req.fd = fd; + rpc_read_req.buflen = buflen; + + /* flag set to wait for response from endpoint callback */ + (void)atomic_flag_test_and_set(&wait_resp); + + ret = rpmsg_rpc_client_send(rpc, READ_ID, &rpc_read_req, payload_size); + if (ret < 0) + return 0; + + /* waiting to get response from endpoint callback */ + while ((atomic_flag_test_and_set(&wait_resp))) { + if (poll.poll) + poll.poll(poll.poll_arg); + } + + return bytes_read; +} + +/************************************************************************* + * + * FUNCTION + * + * rpmsg_write_cb + * + * DESCRIPTION + * + * Callback function of rpmsg_write + * + *************************************************************************/ +void rpmsg_write_cb(struct rpmsg_rpc_clt *rpc, int status, void *data, + size_t len) +{ + struct rpmsg_rpc_resp_write *resp = + (struct rpmsg_rpc_resp_write *)data; + (void)len; + (void)rpc; + + if (status) + bytes_written = 0; + else + /* Assign value from return args */ + bytes_written = resp->bytes_written; + + /* to clear the flag set in the caller function */ + atomic_flag_clear(&wait_resp); +} + +/************************************************************************* + * + * FUNCTION + * + * rpmsg_write + * + * DESCRIPTION + * + * Write data through RPMsg channel + * + * return the number of data written, à in error case + *************************************************************************/ +int rpmsg_write(int fd, char *ptr, int len) +{ + struct rpmsg_rpc_clt *rpc = rpmsg_default_rpc; + struct rpmsg_rpc_req_write rpc_write_req; + unsigned int payload_size = sizeof(rpc_write_req); + int ret; + + if (!rpc) + return -EINVAL; + + bytes_written = 0; + + /* Construct rpc payload */ + rpc_write_req.fd = fd; + memcpy(rpc_write_req.ptr, ptr, len + 1); + rpc_write_req.len = len; + + /* flag set to wait for response from endpoint callback */ + (void)atomic_flag_test_and_set(&wait_resp); + + ret = rpmsg_rpc_client_send(rpc, WRITE_ID, &rpc_write_req, + payload_size); + if (ret < 0) + return 0; + + /* waiting to get response from endpoint callback */ + while ((atomic_flag_test_and_set(&wait_resp))) { + if (poll.poll) + poll.poll(poll.poll_arg); + } + + return bytes_written; + +} + +/************************************************************************* + * + * FUNCTION + * + * rpmsg_close_cb + * + * DESCRIPTION + * + * Callback function of rpmsg_close + * + *************************************************************************/ +void rpmsg_close_cb(struct rpmsg_rpc_clt *rpc, int status, void *data, + size_t len) +{ + (void)rpc; + (void)status; + (void)data; + (void)len; + + /* to clear the flag set in the caller function */ + atomic_flag_clear(&wait_resp); +} + +/************************************************************************* + * + * FUNCTION + * + * rpmsg_close + * + * DESCRIPTION + * + * Close a file. + * + *************************************************************************/ +int rpmsg_close(int fd) +{ + struct rpmsg_rpc_clt *rpc = rpmsg_default_rpc; + struct rpmsg_rpc_req_close rpc_close_req; + unsigned int payload_size = sizeof(rpc_close_req); + int ret; + + if (!rpc) + return -EINVAL; + + /* Construct rpc payload */ + rpc_close_req.fd = fd; + + /* flag set to wait for response from endpoint callback */ + (void)atomic_flag_test_and_set(&wait_resp); + + ret = rpmsg_rpc_client_send(rpc, CLOSE_ID, &rpc_close_req, + payload_size); + + /* waiting to get response from endpoint callback */ + while ((atomic_flag_test_and_set(&wait_resp))) { + if (poll.poll) + poll.poll(poll.poll_arg); + } + + return ret; +} + +static char *input_req_buffer; + +/************************************************************************* + * + * FUNCTION + * + * rpmsg_input_cb + * + * DESCRIPTION + * + * Callback function of rpmsg_input + * + *************************************************************************/ +void rpmsg_input_cb(struct rpmsg_rpc_clt *rpc, int status, void *data, + size_t len) +{ + struct rpmsg_rpc_resp_input *resp = + (struct rpmsg_rpc_resp_input *)data; + (void)len; + (void)rpc; + + if (status) { + bytes_read = 0; + goto out; + } + + /* Assign value from return args */ + if (resp->bytes_read > 0) { + memcpy(input_req_buffer, resp->buf, sizeof(resp->buf)); + } + + bytes_read = resp->bytes_read; + +out: + /* to clear the flag set in the caller function */ + atomic_flag_clear(&wait_resp); + +} + +/************************************************************************* + * + * FUNCTION + * + * rpmsg_input + * + * DESCRIPTION + * + * Read input through RPMsg channel + * + * return the number of byte read, 0 in error case + *************************************************************************/ +int rpmsg_input(char *buffer, int buflen) +{ + struct rpmsg_rpc_clt *rpc = rpmsg_default_rpc; + struct rpmsg_rpc_req_input rpc_input_req; + unsigned int payload_size = sizeof(rpc_input_req); + int ret; + + if (!rpc || !buffer || buflen == 0) + return -EINVAL; + + /* store buffer address and size for the callback */ + input_req_buffer = buffer; + bytes_read = 0; + + /* Construct rpc payload */ + rpc_input_req.buflen = buflen; + + /* flag set to wait for response from endpoint callback */ + (void)atomic_flag_test_and_set(&wait_resp); + ret = rpmsg_rpc_client_send(rpc, INPUT_ID, &buflen, payload_size); + if (ret < 0) + return 0; + + /* waiting to get response from endpoint callback */ + while ((atomic_flag_test_and_set(&wait_resp))) { + if (poll.poll) + poll.poll(poll.poll_arg); + } + + return bytes_read; +} + +/* Mapping ID with Callbacks into table */ +static const struct rpmsg_rpc_client_services rpc_table[] = { + {OPEN_ID, &rpmsg_open_cb }, + {READ_ID, &rpmsg_read_cb }, + {WRITE_ID, &rpmsg_write_cb }, + {CLOSE_ID, &rpmsg_close_cb }, + {INPUT_ID, &rpmsg_input_cb } + }; +/*----------------------------------------------------------------------------- + * + * Application specific + * + *----------------------------------------------------------------------------- + */ +int app(struct rpmsg_device *rdev, void *priv) +{ + struct rpmsg_rpc_clt rpc; + struct rpmsg_rpc_req_term rpccall; + + poll.poll = platform_poll; + poll.poll_arg = priv; + char *fname = "remote.file"; + char wbuff[50]; + char rbuff[300]; + char ubuff[50]; + char idata[50]; + char fdata[50]; + int table_len; + int ret; + + /* redirect I/Os */ + LPRINTF("Initializating I/Os redirection...\r\n"); + table_len = (int)sizeof(rpc_table) / sizeof(struct rpmsg_rpc_services); + atomic_init(&wait_resp, 1); + + ret = rpmsg_rpc_client_init(&rpc, rdev, + rpmsg_rpc_shutdown, rpc_table, table_len); + linux_rpmsg_set_default_rpc(&rpc); + + if (ret) { + LPRINTF("Failed to initialize rpmsg rpc\r\n"); + return -1; + } + + printf("\nRemote>Linux Remote Procedure Call (RPC) Demonstration\r\n"); + printf("\nRemote>***************************************************" + "\r\n"); + + printf("\nRemote>Rpmsg based retargetting to proxy initialized..\r\n"); + + /* Remote performing file IO on Master */ + printf("\nRemote>FileIO demo ..\r\n"); + + printf("\nRemote>Creating a file on master and writing to it..\r\n"); + rpmsg_open(fname, REDEF_O_CREAT | REDEF_O_WRONLY | REDEF_O_APPEND, + S_IRUSR | S_IWUSR); + printf("\nRemote>Opened file '%s' with fd = %d\r\n", fname, fd); + sprintf(wbuff, "This is a test string being written to file.."); + rpmsg_write(fd, wbuff, strlen(wbuff)); + printf("\nRemote>Wrote to fd = %d, size = %d, content = %s\r\n", fd, + bytes_written, wbuff); + rpmsg_close(fd); + printf("\nRemote>Closed fd = %d\r\n", fd); + + /* Remote performing file IO on Master */ + printf("\nRemote>Reading a file on master and displaying its " + "contents..\r\n"); + rpmsg_open(fname, REDEF_O_RDONLY, S_IRUSR | S_IWUSR); + printf("\nRemote>Opened file '%s' with fd = %d\r\n", fname, fd); + rpmsg_read(fd, rbuff, sizeof(rbuff)); + *(char *)(&rbuff[0] + bytes_read) = 0; + printf("\nRemote>Read from fd = %d, size = %d, " + "printing contents below .. %s\r\n", fd, bytes_read, rbuff); + rpmsg_close(fd); + printf("\nRemote>Closed fd = %d\r\n", fd); + + while (1) { + /* Remote performing STDIO on Master */ + printf("\nRemote>Remote firmware using scanf and printf .." + "\r\n"); + printf("\nRemote>Scanning user input from master..\r\n"); + printf("\nRemote>Enter name\r\n"); + rpmsg_input(ubuff, sizeof(ubuff)); + ret = bytes_read; + if (ret) { + printf("\nRemote>Enter age\r\n"); + rpmsg_input(idata, sizeof(idata)); + ret = bytes_read; + if (ret) { + printf("\nRemote>Enter value for pi\r\n"); + rpmsg_input(fdata, sizeof(fdata)); + ret = bytes_read; + if (ret) { + printf("\nRemote>User name = '%s'" + "\r\n", ubuff); + printf("\nRemote>User age = '%s'\r\n", + idata); + printf("\nRemote>User entered value of " + "pi = '%s'\r\n", fdata); + } + } + } + if (!ret) { + rpmsg_input(ubuff, sizeof(ubuff)); + printf("Remote> Invalid value. Starting again...."); + } else { + printf("\nRemote>Repeat demo ? (enter yes or no) \r\n"); + scanf("%s", ubuff); + if ((strcmp(ubuff, "no")) && (strcmp(ubuff, "yes"))) { + printf("\nRemote>Invalid option. Starting again" + "....\r\n"); + } else if ((!strcmp(ubuff, "no"))) { + printf("\nRemote>RPC retargetting quitting ..." + "\r\n"); + break; + } + } + } + + printf("\nRemote> Firmware's rpmsg-rpc-channel going down! \r\n"); + rpccall.id = TERM_ID; + (void)rpmsg_rpc_client_send(&rpc, TERM_ID, &rpccall, sizeof(rpccall)); + + LPRINTF("Release remoteproc procedure call\r\n"); + rpmsg_rpc_client_release(&rpc); + return 0; +} + +/*-----------------------------------------------------------------------------* + * Application entry point + *----------------------------------------------------------------------------- + */ + +int main(int argc, char *argv[]) +{ + void *platform; + struct rpmsg_device *rpdev; + int ret; + + LPRINTF("Starting application...\r\n"); + + /* Initialize platform */ + ret = platform_init(argc, argv, &platform); + if (ret) { + LPERROR("Failed to initialize platform.\r\n"); + ret = -1; + } else { + rpdev = platform_create_rpmsg_vdev(platform, 0, + VIRTIO_DEV_SLAVE, + NULL, NULL); + if (!rpdev) { + LPERROR("Failed to create rpmsg virtio device.\r\n"); + ret = -1; + } else { + app(rpdev, platform); + platform_release_rpmsg_vdev(rpdev, platform); + ret = 0; + } + } + + LPRINTF("Stopping application...\r\n"); + platform_cleanup(platform); + + return ret; +} diff --git a/apps/examples/linux_rpc_demo/linux_rpc_demod.c b/apps/examples/linux_rpc_demo/linux_rpc_demod.c new file mode 100644 index 0000000..13772c6 --- /dev/null +++ b/apps/examples/linux_rpc_demo/linux_rpc_demod.c @@ -0,0 +1,316 @@ +/* + * Copyright (c) 2021, L&T Technology Services Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/*This is a sample demonstration application that showcases usage of proxy + *from the remote core. + *This application is meant to run on the remote CPU running linux. + *This application can print to the master console and perform file I/O through + *rpmsg channels. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "platform_info.h" +#include "linux-rpmsg-rpc-demo.h" + +#define REDEF_O_CREAT 100 +#define REDEF_O_EXCL 200 +#define REDEF_O_RDONLY 0 +#define REDEF_O_WRONLY 1 +#define REDEF_O_RDWR 2 +#define REDEF_O_APPEND 2000 +#define REDEF_O_ACCMODE 3 + +#define raw_printf(format, ...) printf(format, ##__VA_ARGS__) +#define LPRINTF(format, ...) raw_printf("Master> " format, ##__VA_ARGS__) +#define LPERROR(format, ...) LPRINTF("ERROR: " format, ##__VA_ARGS__) + +static void *platform; +static struct rpmsg_device *rpdev; +static struct rpmsg_rpc_svr rpcs; +int request_termination; +int ept_deleted; + +void rpmsg_service_server_unbind(struct rpmsg_endpoint *ept) +{ + (void)ept; + rpmsg_destroy_ept(&rpcs.ept); + LPRINTF("Endpoint is destroyed\r\n"); + ept_deleted = 1; +} + +void terminate_rpc_app(void) +{ + LPRINTF("Destroying endpoint.\r\n"); + if (!ept_deleted) + rpmsg_destroy_ept(&rpcs.ept); +} + +void exit_action_handler(int signum) +{ + (void)signum; + terminate_rpc_app(); +} + +void kill_action_handler(int signum) +{ + (void)signum; + LPRINTF("RPC service killed !!\r\n"); + + terminate_rpc_app(); + + if (rpdev) + platform_release_rpmsg_vdev(rpdev, platform); + if (platform) + platform_cleanup(platform); +} + +int rpmsg_handle_open(void *data, struct rpmsg_rpc_svr *rpcs) +{ + void *req_ptr = data + MAX_FUNC_ID_LEN; + struct rpmsg_rpc_req_open *rpc_open_req = req_ptr; + char *buf; + struct rpmsg_rpc_resp_open rpc_open_resp; + int payload_size = sizeof(rpc_open_resp); + struct rpmsg_endpoint *ept = &rpcs->ept; + int fd, ret; + + if (!rpc_open_req || !ept) + return -EINVAL; + buf = rpc_open_req->filename; + + /* Open remote fd */ + fd = open(buf, rpc_open_req->flags, rpc_open_req->mode); + + /* Construct rpc response */ + rpc_open_resp.fd = fd; + + /* Transmit rpc response */ + ret = rpmsg_rpc_server_send(rpcs, OPEN_ID, RPMSG_RPC_OK, &rpc_open_resp, + payload_size); + + return ret > 0 ? 0 : ret; +} + +int rpmsg_handle_close(void *data, struct rpmsg_rpc_svr *rpcs) +{ + void *req_ptr = data + MAX_FUNC_ID_LEN; + struct rpmsg_rpc_req_close *rpc_close_req = req_ptr; + struct rpmsg_rpc_resp_close rpc_close_resp; + struct rpmsg_endpoint *ept = &rpcs->ept; + int payload_size = sizeof(rpc_close_resp); + int ret; + + if (!rpc_close_req || !ept) + return -EINVAL; + + /* Close remote fd */ + ret = close(rpc_close_req->fd); + + /* Construct rpc response */ + rpc_close_resp.close_ret = ret; + + /* Transmit rpc response */ + ret = rpmsg_rpc_server_send(rpcs, CLOSE_ID, RPMSG_RPC_OK, + &rpc_close_resp, payload_size); + + return ret > 0 ? 0 : ret; +} + +int rpmsg_handle_read(void *data, struct rpmsg_rpc_svr *rpcs) +{ + void *req_ptr = data + MAX_FUNC_ID_LEN; + struct rpmsg_rpc_req_read *rpc_read_req = req_ptr; + struct rpmsg_rpc_resp_read rpc_read_resp; + struct rpmsg_endpoint *ept = &rpcs->ept; + unsigned long int bytes_read; + int payload_size = sizeof(rpc_read_resp); + int ret; + + if (!rpc_read_req || !ept) + return -EINVAL; + + if (rpc_read_req->fd == 0) { + bytes_read = MAX_STRING_LEN; + /* Perform read from fd for large size since this is a + * STD/I request + */ + bytes_read = read(rpc_read_req->fd, rpc_read_resp.buf, + bytes_read); + } else { + /* Perform read from fd */ + bytes_read = read(rpc_read_req->fd, rpc_read_resp.buf, + rpc_read_req->buflen); + } + + /* Construct rpc response */ + rpc_read_resp.bytes_read = bytes_read; + + /* Transmit rpc response */ + ret = rpmsg_rpc_server_send(rpcs, READ_ID, RPMSG_RPC_OK, &rpc_read_resp, + payload_size); + + return ret > 0 ? 0 : ret; +} + +int rpmsg_handle_write(void *data, struct rpmsg_rpc_svr *rpcs) +{ + void *req_ptr = data + MAX_FUNC_ID_LEN; + struct rpmsg_rpc_req_write *rpc_write_req = req_ptr; + struct rpmsg_rpc_resp_write rpc_write_resp; + struct rpmsg_endpoint *ept = &rpcs->ept; + int payload_size = sizeof(rpc_write_resp); + int bytes_written; + int ret; + + if (!rpc_write_req || !ept) + return -EINVAL; + + /* Write to remote fd */ + bytes_written = write(rpc_write_req->fd, rpc_write_req->ptr, + rpc_write_req->len); + + /* Construct rpc response */ + rpc_write_resp.bytes_written = bytes_written; + + /* Transmit rpc response */ + ret = rpmsg_rpc_server_send(rpcs, WRITE_ID, RPMSG_RPC_OK, + &rpc_write_resp, payload_size); + + return ret > 0 ? 0 : ret; +} + +int rpmsg_handle_input(void *data, struct rpmsg_rpc_svr *rpcs) +{ + void *req_ptr = data + MAX_FUNC_ID_LEN; + struct rpmsg_rpc_req_input *rpc_input_req = req_ptr; + struct rpmsg_rpc_resp_input rpc_input_resp; + struct rpmsg_endpoint *ept = &rpcs->ept; + int bytes_read; + int payload_size = sizeof(rpc_input_resp); + int ret; + + if (!rpc_input_req || !ept) + return -EINVAL; + + /* Input from remote */ + scanf("%s", rpc_input_resp.buf); + bytes_read = sizeof(rpc_input_resp.buf); + + /* Construct rpc response */ + rpc_input_resp.bytes_read = bytes_read; + + /* Transmit rpc response */ + ret = rpmsg_rpc_server_send(rpcs, INPUT_ID, RPMSG_RPC_OK, + &rpc_input_resp, payload_size); + + return ret > 0 ? 0 : ret; +} + +int rpmsg_handle_term(void *data, struct rpmsg_rpc_svr *rpcs) +{ + void *req_ptr = data + MAX_FUNC_ID_LEN; + struct rpmsg_rpc_req_term *rpc_term_req = req_ptr; + struct rpmsg_endpoint *ept = &rpcs->ept; + + printf("Received termination request at id %d from endpoint %s\r\n", + rpc_term_req->id, ept->name); + request_termination = 1; + + return 0; +} + +/* Service table */ +static struct rpmsg_rpc_services rpc_table[] = { + {OPEN_ID, &rpmsg_handle_open }, + {READ_ID, &rpmsg_handle_read }, + {WRITE_ID, &rpmsg_handle_write }, + {CLOSE_ID, &rpmsg_handle_close }, + {INPUT_ID, &rpmsg_handle_input }, + {TERM_ID, &rpmsg_handle_term } + }; + +/* Application entry point */ +int app(struct rpmsg_device *rdev, void *priv) +{ + int ret = 0; + struct sigaction exit_action; + struct sigaction kill_action; + + /* Initialize signalling infrastructure */ + memset(&exit_action, 0, sizeof(struct sigaction)); + memset(&kill_action, 0, sizeof(struct sigaction)); + exit_action.sa_handler = exit_action_handler; + kill_action.sa_handler = kill_action_handler; + sigaction(SIGTERM, &exit_action, NULL); + sigaction(SIGINT, &exit_action, NULL); + sigaction(SIGKILL, &kill_action, NULL); + sigaction(SIGHUP, &kill_action, NULL); + + /* Initialize RPMSG framework */ + LPRINTF("Try to create rpmsg endpoint.\r\n"); + + ret = rpmsg_rpc_server_init(&rpcs, rdev, rpc_table, + (int)sizeof(rpc_table) / sizeof( + struct rpmsg_rpc_services), + rpmsg_service_server_unbind); + + if (ret) { + LPERROR("Failed to create endpoint.\r\n"); + return -EINVAL; + } + + LPRINTF("Successfully created rpmsg endpoint.\r\n"); + while (1) { + platform_poll(priv); + /* we got a shutdown request, exit */ + if (ept_deleted || request_termination) { + break; + } + } + LPRINTF("\nRPC service exiting !!\r\n"); + + terminate_rpc_app(); + return ret; +} + +int main(int argc, char *argv[]) +{ + int ret; + + LPRINTF("Starting application...\r\n"); + + /* Initialize platform */ + ret = platform_init(argc, argv, &platform); + if (ret) { + LPERROR("Failed to initialize platform.\r\n"); + ret = -1; + } else { + rpdev = platform_create_rpmsg_vdev(platform, 0, + VIRTIO_DEV_MASTER, + NULL, NULL); + if (!rpdev) { + LPERROR("Failed to create rpmsg virtio device.\r\n"); + ret = -1; + } else { + app(rpdev, platform); + platform_release_rpmsg_vdev(rpdev, platform); + ret = 0; + } + } + + LPRINTF("Stopping application...\r\n"); + platform_cleanup(platform); + + return ret; +}