diff --git a/examples/tests/ipc/package_name_client-libtock/Makefile b/examples/tests/ipc/package_name_client-libtock/Makefile new file mode 100644 index 000000000..b9cdd0fa3 --- /dev/null +++ b/examples/tests/ipc/package_name_client-libtock/Makefile @@ -0,0 +1,11 @@ +# Makefile for user application + +# Specify this directory relative to the current application. +TOCK_USERLAND_BASE_DIR = ../../../.. + +# Which files to compile. +C_SRCS := $(wildcard *.c) + +# Include userland master makefile. Contains rules and flags for actually +# building the application. +include $(TOCK_USERLAND_BASE_DIR)/AppMakefile.mk diff --git a/examples/tests/ipc/package_name_client-libtock/README.md b/examples/tests/ipc/package_name_client-libtock/README.md new file mode 100644 index 000000000..83aa565ee --- /dev/null +++ b/examples/tests/ipc/package_name_client-libtock/README.md @@ -0,0 +1,27 @@ +# Package Name Client - libtock + +Discovers an IPC service via TBF Header Package Name using the asynchronous +libtock IPC Registry Package Name driver. + +This is intended to be loaded alongside [package_name_server-libtock](../package_name_server-libtock). When +loaded with the Package Name Server the expected behavior is: + +``` +SERVER: Starting server. +CLIENT: Starting client. +SERVER: IPC Registry Package Name capsule exists. +CLIENT: IPC Registry Package Name capsule exists. +CLIENT: Discovering: "tbf_package_name_service" +CLIENT: Discovery started. +CLIENT: Discovery complete. Service not found. +SERVER: Registering with my package name (see Makefile). +SERVER: Registration started. +SERVER: Registration complete. Succeeded! +CLIENT: Discovering: "tbf_package_name_service" +CLIENT: Discovery started. +CLIENT: Discovery complete. Succeeded! Found IPC ID: 0000000000000001 +``` + +Note that above IPC IDs may not match depending on the order the apps are +loaded and the configuration of your board. + diff --git a/examples/tests/ipc/package_name_client-libtock/main.c b/examples/tests/ipc/package_name_client-libtock/main.c new file mode 100644 index 000000000..aade154a8 --- /dev/null +++ b/examples/tests/ipc/package_name_client-libtock/main.c @@ -0,0 +1,64 @@ +#include +#include +#include + +#include +#include + +// Service name (specified in Makefile of service) +char service_name[] = "tbf_package_name_service"; + +static void ipc_discovery_complete(returncode_t ret, uint64_t ipc_id) { + if (ret == RETURNCODE_SUCCESS) { + uint32_t lower = (uint32_t)ipc_id; + uint32_t upper = (uint32_t)(ipc_id >> 32); + printf("CLIENT: Discovery complete. Succeeded! Found IPC ID: %08X%08X\n", (unsigned int)upper, (unsigned int)lower); + } else if (ret == RETURNCODE_ENODEVICE) { + printf("CLIENT: Discovery complete. Service not found.\n"); + } else { + printf("CLIENT: Discovery complete. Failed with returncode: %d\n", ret); + } +} + +int main(void) { + returncode_t ret = RETURNCODE_SUCCESS; + printf("CLIENT: Starting client.\n"); + + // Check that we can find the driver at all + if (!libtock_ipc_registry_package_name_exists()) { + printf("CLIENT: IPC Registry Package Name capsule does not exist on this board.\n"); + return 1; + } else { + printf("CLIENT: IPC Registry Package Name capsule exists.\n"); + } + + // Delay a bit + libtocksync_alarm_delay_ms(1000); + + // Attempt to discover (this should fail as the server hasn't registered yet) + printf("CLIENT: Discovering: \"%s\"\n", service_name); + ret = libtock_ipc_registry_package_name_discover_service(ipc_discovery_complete, (uint8_t*)service_name, + strlen(service_name)); + if (ret != RETURNCODE_SUCCESS) { + printf("CLIENT: Discovery failed. Returncode: %d\n", ret); + } else { + printf("CLIENT: Discovery started.\n"); + } + + // Delay for a bit + libtocksync_alarm_delay_ms(5000); + + // Re-attempt to discover (this should now succeed) + printf("CLIENT: Discovering: \"%s\"\n", service_name); + ret = libtock_ipc_registry_package_name_discover_service(ipc_discovery_complete, (uint8_t*)service_name, + strlen(service_name)); + if (ret != RETURNCODE_SUCCESS) { + printf("CLIENT: Discovery failed. Returncode: %d\n", ret); + } else { + printf("CLIENT: Discovery started.\n"); + } + + while (1) { + yield(); + } +} diff --git a/examples/tests/ipc/package_name_server-libtock/Makefile b/examples/tests/ipc/package_name_server-libtock/Makefile new file mode 100644 index 000000000..50e2da3ec --- /dev/null +++ b/examples/tests/ipc/package_name_server-libtock/Makefile @@ -0,0 +1,14 @@ +# Makefile for user application + +# Specify this directory relative to the current application. +TOCK_USERLAND_BASE_DIR = ../../../.. + +# Which files to compile. +C_SRCS := $(wildcard *.c) + +# Package name field for TBF headers +PACKAGE_NAME = tbf_package_name_service + +# Include userland master makefile. Contains rules and flags for actually +# building the application. +include $(TOCK_USERLAND_BASE_DIR)/AppMakefile.mk diff --git a/examples/tests/ipc/package_name_server-libtock/README.md b/examples/tests/ipc/package_name_server-libtock/README.md new file mode 100644 index 000000000..115110744 --- /dev/null +++ b/examples/tests/ipc/package_name_server-libtock/README.md @@ -0,0 +1,30 @@ +# Package Name Server - libtock + +Registers as an IPC service via TBF Header Package Name using the asynchronous +libtock IPC Registry Package Name driver. + +The Package Name for this server is specified in the [Makefile](./Makefile) as +`PACKAGE_NAME = tbf_package_name_service`. + +This is intended to be loaded alongside [package_name_client-libtock](../package_name_client-libtock). When +loaded with the Package Name Client the expected behavior is: + +``` +SERVER: Starting server. +CLIENT: Starting client. +SERVER: IPC Registry Package Name capsule exists. +CLIENT: IPC Registry Package Name capsule exists. +CLIENT: Discovering: "tbf_package_name_service" +CLIENT: Discovery started. +CLIENT: Discovery complete. Service not found. +SERVER: Registering with my package name (see Makefile). +SERVER: Registration started. +SERVER: Registration complete. Succeeded! +CLIENT: Discovering: "tbf_package_name_service" +CLIENT: Discovery started. +CLIENT: Discovery complete. Succeeded! Found IPC ID: 0000000000000001 +``` + +Note that above IPC IDs may not match depending on the order the apps are +loaded and the configuration of your board. + diff --git a/examples/tests/ipc/package_name_server-libtock/main.c b/examples/tests/ipc/package_name_server-libtock/main.c new file mode 100644 index 000000000..bd9677028 --- /dev/null +++ b/examples/tests/ipc/package_name_server-libtock/main.c @@ -0,0 +1,41 @@ +#include +#include + +#include +#include + +static void ipc_registration_complete(returncode_t ret) { + if (ret == RETURNCODE_SUCCESS) { + printf("SERVER: Registration complete. Succeeded!\n"); + } else { + printf("SERVER: Registration complete. Failed with returncode: %d\n", ret); + } +} + +int main(void) { + printf("SERVER: Starting server.\n"); + + // Check that we can find the driver at all + if (!libtock_ipc_registry_package_name_exists()) { + printf("SERVER: IPC Registry Package Name capsule does not exist on this board.\n"); + return 1; + } else { + printf("SERVER: IPC Registry Package Name capsule exists.\n"); + } + + // Delay a bit + libtocksync_alarm_delay_ms(5000); + + // Attempt to register + printf("SERVER: Registering with my package name (see Makefile).\n"); + returncode_t ret = libtock_ipc_registry_package_name_register_service(ipc_registration_complete); + if (ret != RETURNCODE_SUCCESS) { + printf("SERVER: Registration failed. Returncode: %d\n", ret); + } else { + printf("SERVER: Registration started.\n"); + } + + while (1) { + yield(); + } +} diff --git a/examples/tests/ipc/relay_request_client-libtock/Makefile b/examples/tests/ipc/relay_request_client-libtock/Makefile new file mode 100644 index 000000000..b9cdd0fa3 --- /dev/null +++ b/examples/tests/ipc/relay_request_client-libtock/Makefile @@ -0,0 +1,11 @@ +# Makefile for user application + +# Specify this directory relative to the current application. +TOCK_USERLAND_BASE_DIR = ../../../.. + +# Which files to compile. +C_SRCS := $(wildcard *.c) + +# Include userland master makefile. Contains rules and flags for actually +# building the application. +include $(TOCK_USERLAND_BASE_DIR)/AppMakefile.mk diff --git a/examples/tests/ipc/relay_request_client-libtock/README.md b/examples/tests/ipc/relay_request_client-libtock/README.md new file mode 100644 index 000000000..9990b614f --- /dev/null +++ b/examples/tests/ipc/relay_request_client-libtock/README.md @@ -0,0 +1,85 @@ +# Relay Request Client - libtock + +Discovers an IPC service via a provided string name using the asynchronous +libtock IPC Registry String Name driver and then periodically sends requests to +it using the asynchronous libtock IPC Relay Request driver. + +This is intended to be loaded alongside [relay_request_server-libtock](../relay_request_server-libtock). When +loaded with the Relay Request Server the expected behavior is: + +``` +SERVER: Starting server. +CLIENT: Starting client. +SERVER: IPC Registry String Name capsule exists. +CLIENT: IPC Registry String Name capsule exists. +SERVER: IPC Relay Request capsule exists. +CLIENT: IPC Relay Request capsule exists. +SERVER: Enabled request waiting callbacks. +CLIENT: Discovering: "Request_Service" +SERVER: Registering with string name: "Request_Service" +CLIENT: Discovery started. +SERVER: Registration started. +CLIENT: Discovery complete. Service not found. +SERVER: Registration complete. Succeeded! +tock$ CLIENT: Discovering: "Request_Service" +CLIENT: Discovery started. +CLIENT: Discovery complete. Succeeded! Found IPC ID: 0000000000000001 +CLIENT: Sending request with data { AA BB CC DD EE FF } +CLIENT: Request initiated. +SERVER: New request waiting from IPC ID: 0000000000000000 +SERVER: Got a request from IPC ID: 0000000000000000 +SERVER: Request data {AA BB CC DD EE FF } +CLIENT: Waiting for response (1 of 10) +CLIENT: Waiting for response (2 of 10) +CLIENT: Waiting for response (3 of 10) +SERVER: Sent response. +CLIENT: Received response. Data { 12 34 56 78 } +CLIENT: Sending request with data { AA BB CC DD EE FF } +... +``` + +Note that above IPC IDs may not match depending on the order the apps are +loaded and the configuration of your board. + +## Fault Handling + +This app is designed to handle faults gracefully. Communication is +intentionally slowed down to provide time to manually trigger faults between +certain actions to see the response. Using the Process Console is the easiest +way to intentionally cause a fault to test this. If your board is configured +with the process Fault Policy `RestartFaultPolicy`, then after one process +restarts discovery will be re-initiated and communication will resume. + +For example, you can fault the server with the command `fault +relay_request_server-libtock`. + +``` +SERVER: Got a request from IPC ID: 0000000000000000 +SERVER: Request data {AA BB CC DD EE FF } +CLIENT: Waiting for response (1 of 10) +CLIENT: Waiting for response (2 of 10) +CLIENT: Waiting for response (3 of 10) +SERVER: Sent response. +CLIENT: Received response. Data { 12 34 56 78 } +$ fault relay_request_server-libtock + +Process relay_request_server-libtock now faulted +SERVER: Starting server. +SERVER: IPC Registry String Name capsule exists. +SERVER: IPC Relay Request capsule exists. +SERVER: Enabled request waiting callbacks. +SERVER: Registering with string name: "Request_Service" +SERVER: Registration started. +SERVER: Registration complete. Succeeded! +CLIENT: Sending request with data { AA BB CC DD EE FF } +CLIENT: Server disappeared? Discover it again. +CLIENT: Discovering: "Request_Service" +CLIENT: Discovery started. +CLIENT: Discovery complete. Succeeded! Found IPC ID: 0000000000000002 +CLIENT: Sending request with data { AA BB CC DD EE FF } +CLIENT: Request initiated. +SERVER: New request waiting from IPC ID: 0000000000000000 +SERVER: Got a request from IPC ID: 0000000000000000 +SERVER: Request data {AA BB CC DD EE FF } +``` + diff --git a/examples/tests/ipc/relay_request_client-libtock/main.c b/examples/tests/ipc/relay_request_client-libtock/main.c new file mode 100644 index 000000000..5293383e0 --- /dev/null +++ b/examples/tests/ipc/relay_request_client-libtock/main.c @@ -0,0 +1,189 @@ +#include +#include +#include + +#include +#include +#include + +// Discovery +static const char* SERVICE_NAME = "Request_Service"; +static uint64_t server_ipc_id = 0; + +// Communication +#define RESPONSE_BUF_LEN 10 +static uint8_t response_buf[RESPONSE_BUF_LEN] = {0}; + +// State Tracking +typedef enum { + START_DISCOVERY, + WAIT_ON_DISCOVERY, + SEND_REQUEST, + WAIT_ON_RESPONSE, + WAIT_ON_CANCEL, + ERROR, +} Client_State_t; +static Client_State_t state = START_DISCOVERY; +#define DELAY_MAX 10 + +// Callback when discovery completes, advances state +static void ipc_discovery_complete(returncode_t ret, uint64_t ipc_id) { + if (ret == RETURNCODE_SUCCESS) { + uint32_t lower = (uint32_t)ipc_id; + uint32_t upper = (uint32_t)(ipc_id >> 32); + printf("CLIENT: Discovery complete. Succeeded! Found IPC ID: %08X%08X\n", (unsigned int)upper, (unsigned int)lower); + server_ipc_id = ipc_id; + state = SEND_REQUEST; + } else if (ret == RETURNCODE_ENODEVICE) { + printf("CLIENT: Discovery complete. Service not found.\n"); + state = START_DISCOVERY; + } else { + printf("CLIENT: Discovery complete. Failed with returncode: %d\n", ret); + state = ERROR; + } +} + +// Handle discovering the service and advancing state +static void discover_ipc_service(void) { + // Attempt to discover + printf("CLIENT: Discovering: \"%s\"\n", SERVICE_NAME); + returncode_t ret = libtock_ipc_registry_string_name_discover_service(ipc_discovery_complete, (uint8_t*)SERVICE_NAME, + strlen(SERVICE_NAME)); + if (ret != RETURNCODE_SUCCESS) { + printf("CLIENT: Discovery failed. Returncode: %d\n", ret); + state = ERROR; + } else { + printf("CLIENT: Discovery started.\n"); + state = WAIT_ON_DISCOVERY; + } +} + +// Callback when a response is received, advances state +static void ipc_response_received(returncode_t ret, size_t len) { + if (ret == RETURNCODE_SUCCESS) { + printf("CLIENT: Received response. Data { "); + for (size_t i = 0; i < len && i < RESPONSE_BUF_LEN; i++) { + printf("%02X ", response_buf[i]); + } + printf("}\n"); + state = SEND_REQUEST; + } else if (ret == RETURNCODE_ESIZE) { + printf("CLIENT: Received partial response. Response buffer was too small. Data { "); + for (size_t i = 0; i < len && i < RESPONSE_BUF_LEN; i++) { + printf("%02X ", response_buf[i]); + } + printf("}\n"); + state = SEND_REQUEST; + } else if (ret == RETURNCODE_ECANCEL) { + printf("CLIENT: Request was canceled. Try again.\n"); + state = SEND_REQUEST; + } else { + printf("CLIENT: Response failed with returncode: %d\n", ret); + state = ERROR; + } +} + +// Handle sending a request and advancing state +static void send_ipc_request(void) { + // Create request data + memset(response_buf, 0, RESPONSE_BUF_LEN); + uint8_t request_buf[6] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}; + printf("CLIENT: Sending request with data { "); + for (size_t i = 0; i < 6; i++) { + printf("%02X ", request_buf[i]); + } + printf("}\n"); + + // Send request + returncode_t ret = libtock_ipc_relay_request_client_send_request(ipc_response_received, server_ipc_id, + request_buf, 6, + response_buf, RESPONSE_BUF_LEN); + if (ret == RETURNCODE_SUCCESS) { + printf("CLIENT: Request initiated.\n"); + state = WAIT_ON_RESPONSE; + } else if (ret == RETURNCODE_ENODEVICE) { + printf("CLIENT: Server disappeared? Discover it again.\n"); + state = START_DISCOVERY; + } else if (ret == RETURNCODE_EUNINSTALLED) { + printf("CLIENT: Server exists, but hasn't enabled requests. Try again?\n"); + state = SEND_REQUEST; + } else { + printf("CLIENT: Request failed. Returncode: %d\n", ret); + state = ERROR; + } +} + +// Handle canceling a request and advancing state +static void cancel_ipc_request(void) { + returncode_t ret = libtock_ipc_relay_request_client_cancel_request(); + if (ret == RETURNCODE_SUCCESS) { + printf("CLIENT: Attempting to cancel request.\n"); + state = WAIT_ON_CANCEL; + } else { + printf("CLIENT: Cancelling request failed. Returncode: %d\n", ret); + state = ERROR; + } +} + +int main(void) { + printf("CLIENT: Starting client.\n"); + + // Check that we can find the driver at all + if (!libtock_ipc_registry_string_name_exists()) { + printf("CLIENT: IPC Registry String Name capsule does not exist on this board.\n"); + return 1; + } else { + printf("CLIENT: IPC Registry String Name capsule exists.\n"); + } + + // Check that we can send messages at all + if (!libtock_ipc_relay_request_exists()) { + printf("CLIENT: IPC Relay Request capsule does not exist on this board.\n"); + return 1; + } else { + printf("CLIENT: IPC Relay Request capsule exists.\n"); + } + + // Communication state machine + uint8_t delay_counter = 0; + while (1) { + if (state == START_DISCOVERY) { + discover_ipc_service(); + delay_counter = 0; + } else if (state == WAIT_ON_DISCOVERY) { + delay_counter++; + if (delay_counter >= DELAY_MAX) { + // This should never occur since discovery doesn't wait on other processes + printf("CLIENT: Timeout waiting on discovery.\n"); + delay_counter = 0; + state = ERROR; + } + } else if (state == SEND_REQUEST) { + send_ipc_request(); + delay_counter = 0; + } else if (state == WAIT_ON_RESPONSE) { + delay_counter++; + printf("CLIENT: Waiting for response (%d of %d)\n", delay_counter, DELAY_MAX); + if (delay_counter >= DELAY_MAX) { + printf("CLIENT: Timeout waiting on response.\n"); + cancel_ipc_request(); + delay_counter = 0; + } + } else if (state == WAIT_ON_CANCEL) { + delay_counter++; + if (delay_counter >= DELAY_MAX) { + // This should never occur since canceling doesn't wait on other processes + printf("CLIENT: Timeout waiting on cancel.\n"); + delay_counter = 0; + state = ERROR; + } + } else { + // error state + printf("CLIENT: Stopping due to error.\n"); + return 1; + } + + // Delay before advancing state machine + libtocksync_alarm_delay_ms(2000); + } +} diff --git a/examples/tests/ipc/relay_request_round_robin-libtocksync/Makefile b/examples/tests/ipc/relay_request_round_robin-libtocksync/Makefile new file mode 100644 index 000000000..55fc843a8 --- /dev/null +++ b/examples/tests/ipc/relay_request_round_robin-libtocksync/Makefile @@ -0,0 +1,35 @@ +# Makefile for user application + +# Specify this directory relative to the current application. +TOCK_USERLAND_BASE_DIR = ../../../.. + +# Which files to compile. +C_SRCS := $(wildcard *.c) + +########## +# Custom targets for this application + +# Keep the all target the default +all: + +# Build three different app versions and upload them +# +# The AVOID_TOOL thing is definitely a hack. Libtock-c has a helpf script in +# `tools/check_override.sh` which ensures that Makefile additions use the +# override keyword when defining variables. These lines are command-line +# arguments though, so they can't and don't need to use override. Having the +# word "override" physically appear on the line avoids detection by the tool. +.PHONY: ROUNDROBIN +ROUNDROBIN: + touch main.c + $(MAKE) program CFLAGS="-DSERVICE=0" PACKAGE_NAME="RoundRobin-Service0" AVOID_TOOL="override" + touch main.c + $(MAKE) program CFLAGS="-DSERVICE=1" PACKAGE_NAME="RoundRobin-Service1" AVOID_TOOL="override" + touch main.c + $(MAKE) program CFLAGS="-DSERVICE=2" PACKAGE_NAME="RoundRobin-Service2" AVOID_TOOL="override" + +########## + +# Include userland master makefile. Contains rules and flags for actually +# building the application. +include $(TOCK_USERLAND_BASE_DIR)/AppMakefile.mk diff --git a/examples/tests/ipc/relay_request_round_robin-libtocksync/README.md b/examples/tests/ipc/relay_request_round_robin-libtocksync/README.md new file mode 100644 index 000000000..ea2ed2807 --- /dev/null +++ b/examples/tests/ipc/relay_request_round_robin-libtocksync/README.md @@ -0,0 +1,44 @@ +# Relay Request Round Robin - libtocksync + +Uses the synchronous libtocksync drivers for string name registration/discovery +(IPC Registry String Name) and requests/responses (IPC Relay Request). + +This is intended to be loaded three separate times with separate SERVICE +configurations. The end result will be three processes connected in round-robin +fashion, Service0 -> Service1 -> Service2 -> (back to Service0). Each receives +from the prior service and sends to the next service. To break the deadlock, +Service2 always sends first while the others always receive first. + +To make this easier to test, a custom Make command has been added to this +application. To build all three apps and load them onto your board use `make +ROUNDROBIN`. When all three are loaded the expected behavior is: + +``` +Service2: Starting. +Service0: Starting. +Service1: Starting. +Service0: IPC Registry String Name capsule exists. +Service1: IPC Registry String Name capsule exists. +Service0: IPC Relay Request capsule exists. +Service1: IPC Relay Request capsule exists. +Service0: IPC String Name registered. +Service1: IPC String Name registered. +Service0: Couldn't find service "Service2" yet. +Service1: IPC String Name discovered service "Service0". IPC ID: 0000000000000000 +Service2: IPC Registry String Name capsule exists. +Service1: Couldn't find service "Service2" yet. +Service2: IPC Relay Request capsule exists. +Service2: IPC String Name registered. +Service2: IPC String Name discovered service "Service1". IPC ID: 0000000000000001 +Service2: IPC String Name discovered service "Service0". IPC ID: 0000000000000000 +tock$ Service0: IPC String Name discovered service "Service2". IPC ID: 0000000000000002 +Service0: IPC String Name discovered service "Service1". IPC ID: 0000000000000001 +Service2: Got response from 0000000000000000! Value is now: 1 +Service1: IPC String Name discovered service "Service2". IPC ID: 0000000000000002 +Service0: Got response from 0000000000000001! Value is now: 2 +Service1: Got response from 0000000000000002! Value is now: 3 +Service2: Got response from 0000000000000000! Value is now: 4 +Service0: Got response from 0000000000000001! Value is now: 5 +... +``` + diff --git a/examples/tests/ipc/relay_request_round_robin-libtocksync/main.c b/examples/tests/ipc/relay_request_round_robin-libtocksync/main.c new file mode 100644 index 000000000..7ba56a6c1 --- /dev/null +++ b/examples/tests/ipc/relay_request_round_robin-libtocksync/main.c @@ -0,0 +1,233 @@ +#include +#include +#include + +#include +#include +#include +#include + + +// Testing setup. Run `make ROUNDROBIN` to automatically build all three +// services and upload them to the board +#ifndef SERVICE +#define SERVICE 0 +#endif + +#if SERVICE == 0 +// First service +static char* SERVICE_NAME = (char*)"Service0"; +static char* PRIOR_NAME = (char*)"Service2"; +static char* NEXT_NAME = (char*)"Service1"; +bool send_first = false; +#elif SERVICE == 1 +// Second service +static char* SERVICE_NAME = (char*)"Service1"; +static char* PRIOR_NAME = (char*)"Service0"; +static char* NEXT_NAME = (char*)"Service2"; +bool send_first = false; +#else +// Third service, note that this one sends first to break the deadlock +static char* SERVICE_NAME = (char*)"Service2"; +static char* PRIOR_NAME = (char*)"Service1"; +static char* NEXT_NAME = (char*)"Service0"; +bool send_first = true; +#endif + + +// Discovery +static uint64_t prior_ipc_id = 0; +static uint64_t next_ipc_id = 0; + +// Communication +static uint8_t value = 0; + +static returncode_t discover_ipc_service(uint8_t* name, size_t name_len, uint64_t* ipc_id) { + while (true) { + returncode_t ret = libtocksync_ipc_registry_string_name_discover_service(name, name_len, ipc_id); + if (ret == RETURNCODE_SUCCESS) { + // Success! Stop searching + uint32_t upper = (uint32_t)((*ipc_id) >> 32); + uint32_t lower = (uint32_t)(*ipc_id); + printf("%s: IPC String Name discovered service \"%s\". IPC ID: %08X%08X\n", SERVICE_NAME, name, + (unsigned int)upper, (unsigned int)lower); + break; + } else if (ret == RETURNCODE_ENODEVICE) { + // Didn't discover it (yet, it may still register later) + printf("%s: Couldn't find service \"%s\" yet.\n", SERVICE_NAME, name); + libtocksync_alarm_delay_ms(1000); // wait before trying again + } else { + // An error occurred + printf("%s: IPC String Name discovery had an error. Returncode: %d\n", SERVICE_NAME, ret); + return ret; + } + } + return RETURNCODE_SUCCESS; +} + +static returncode_t handle_discovery(void) { + // Discover prior service + returncode_t ret = discover_ipc_service((uint8_t*)PRIOR_NAME, strlen(PRIOR_NAME), &prior_ipc_id); + if (ret != RETURNCODE_SUCCESS) return ret; + + // Discover next service + ret = discover_ipc_service((uint8_t*)NEXT_NAME, strlen(NEXT_NAME), &next_ipc_id); + if (ret != RETURNCODE_SUCCESS) return ret; + + return RETURNCODE_SUCCESS; +} + +static returncode_t send_ipc_request(void) { + // Send request to next process + uint8_t request_buf[1] = {0}; + size_t request_len = 1; + uint8_t response_buf[1] = {0}; + size_t response_len = 1; + uint32_t timeout_ms = 1000; + request_buf[0] = value + 1; // increment value + returncode_t ret = libtocksync_ipc_relay_request_client_send_request_with_timeout(next_ipc_id, request_buf, + request_len, response_buf, + &response_len, timeout_ms); + if (ret != RETURNCODE_SUCCESS) { + // Error case + printf("%s: Error sending request. Returncode: %d\n", SERVICE_NAME, ret); + return ret; + } + + // Got proper response! Update value to match + value = response_buf[0]; + uint32_t upper = (uint32_t)(next_ipc_id >> 32); + uint32_t lower = (uint32_t)next_ipc_id; + printf("%s: Got response from %08X%08X! Value is now: %d\n", SERVICE_NAME, (unsigned int)upper, (unsigned int)lower, + value); + return RETURNCODE_SUCCESS; +} + +static returncode_t receive_ipc_request(void) { + // Receive request from prior process + uint8_t retry_count = 0; + uint8_t request_buf[1] = {0}; + size_t request_len = 1; + while (true) { + request_len = 1; + returncode_t ret = libtocksync_ipc_relay_request_server_get_next_request_from(prior_ipc_id, request_buf, + &request_len); + if (ret == RETURNCODE_SUCCESS) { + // Successful request + break; + } else if (ret == RETURNCODE_ENODEVICE) { + // No request right now, try again in a bit + // uint32_t upper = (uint32_t)(prior_ipc_id >> 32); + // uint32_t lower = (uint32_t)prior_ipc_id; + // printf("%s: No request yet from: %08X%08X.\n", SERVICE_NAME, (unsigned int)upper, (unsigned int)lower); + retry_count++; + if (retry_count >= 50) { + // Timeout on waiting for request + return RETURNCODE_ENODEVICE; + } + libtocksync_alarm_delay_ms(100); + continue; + } else { + // An error occurred + printf("%s: Error getting request. Returncode: %d\n", SERVICE_NAME, ret); + return ret; + } + } + + // Got request, send response echoing new value + value = request_buf[0]; + uint8_t response_buf[1] = {value}; + size_t response_len = 1; + returncode_t ret = libtocksync_ipc_relay_request_server_send_response(response_buf, response_len); + if (ret != RETURNCODE_SUCCESS) { + printf("%s: Error sending response. Returncode: %d\n", SERVICE_NAME, ret); + return ret; + } + return RETURNCODE_SUCCESS; +} + +int main(void) { + returncode_t ret = RETURNCODE_SUCCESS; + printf("%s: Starting.\n", SERVICE_NAME); + + // Check that we can find the driver at all + if (!libtock_ipc_registry_string_name_exists()) { + printf("%s: IPC Registry String Name capsule does not exist on this board.\n", SERVICE_NAME); + return 1; + } else { + printf("%s: IPC Registry String Name capsule exists.\n", SERVICE_NAME); + } + + // Check that we can send messages at all + if (!libtock_ipc_relay_request_exists()) { + printf("%s: IPC Relay Request capsule does not exist on this board.\n", SERVICE_NAME); + return 1; + } else { + printf("%s: IPC Relay Request capsule exists.\n", SERVICE_NAME); + } + + // Enable requests. Importantly, do this BEFORE registering so that you can + // immediately receive requests after being discovered + libtocksync_ipc_relay_request_server_enable_requests(); + + // Register service name + ret = libtocksync_ipc_registry_string_name_register_service((uint8_t*)SERVICE_NAME, strlen(SERVICE_NAME)); + if (ret != RETURNCODE_SUCCESS) { + printf("%s: IPC String Name registration failed. Returncode: %d\n", SERVICE_NAME, ret); + return 1; + } else { + printf("%s: IPC String Name registered.\n", SERVICE_NAME); + } + + // Discovery the other two services + ret = handle_discovery(); + if (ret != RETURNCODE_SUCCESS) return 1; + + // Send and receive forever + bool sending = send_first; + while (true) { + if (sending) { + // Sending request to next service + ret = send_ipc_request(); + if (ret == RETURNCODE_SUCCESS) { + // Success, move to next state + sending = !sending; + } else if (ret == RETURNCODE_ENODEVICE) { + // Server doesn't exist anymore. Try discovery again + printf("%s: Server disappeared? Discovering again.\n", SERVICE_NAME); + ret = handle_discovery(); + if (ret != RETURNCODE_SUCCESS) return ret; + } else if (ret == RETURNCODE_ENOACK) { + // Timeout. Stay in same state to just try sending again? + printf("%s: timeout on request. We'll try again...\n", SERVICE_NAME); + } else { + // Error + return 1; + } + } else { + // Get request from prior process and send response + ret = receive_ipc_request(); + if (ret == RETURNCODE_SUCCESS) { + // Success, move to next state + sending = !sending; + } else if (ret == RETURNCODE_EINVAL) { + // Couldn't send response? There was no transaction to respond to. This + // means the client canceled or faulted. It's not an error, so just try + // receiving again. + printf("%s: Client disappeared? Listening for request again.\n", SERVICE_NAME); + } else if (ret == RETURNCODE_ENODEVICE) { + // timed out trying to find a request from the prior service. Do + // discovery again + printf("%s: Never heard from client? Discovering again.\n", SERVICE_NAME); + ret = handle_discovery(); + if (ret != RETURNCODE_SUCCESS) return ret; + } else { + // Error + return 1; + } + } + + // Delay a bit + libtocksync_alarm_delay_ms(1000); + } +} diff --git a/examples/tests/ipc/relay_request_server-libtock/Makefile b/examples/tests/ipc/relay_request_server-libtock/Makefile new file mode 100644 index 000000000..b9cdd0fa3 --- /dev/null +++ b/examples/tests/ipc/relay_request_server-libtock/Makefile @@ -0,0 +1,11 @@ +# Makefile for user application + +# Specify this directory relative to the current application. +TOCK_USERLAND_BASE_DIR = ../../../.. + +# Which files to compile. +C_SRCS := $(wildcard *.c) + +# Include userland master makefile. Contains rules and flags for actually +# building the application. +include $(TOCK_USERLAND_BASE_DIR)/AppMakefile.mk diff --git a/examples/tests/ipc/relay_request_server-libtock/README.md b/examples/tests/ipc/relay_request_server-libtock/README.md new file mode 100644 index 000000000..7aae903a0 --- /dev/null +++ b/examples/tests/ipc/relay_request_server-libtock/README.md @@ -0,0 +1,84 @@ +# Relay Request Server - libtock + +Registers an IPC service via a provided string name using the asynchronous +libtock IPC Registry String Name driver and then continuously listens for +requests and sends responses using the asynchronous libtock IPC Relay Request +driver. + +This is intended to be loaded alongside [relay_request_client-libtock](../relay_request_client-libtock). When +loaded with the Relay Request Client the expected behavior is: + +``` +SERVER: Starting server. +CLIENT: Starting client. +SERVER: IPC Registry String Name capsule exists. +CLIENT: IPC Registry String Name capsule exists. +SERVER: IPC Relay Request capsule exists. +CLIENT: IPC Relay Request capsule exists. +SERVER: Enabled request waiting callbacks. +CLIENT: Discovering: "Request_Service" +SERVER: Registering with string name: "Request_Service" +CLIENT: Discovery started. +SERVER: Registration started. +CLIENT: Discovery complete. Service not found. +SERVER: Registration complete. Succeeded! +tock$ CLIENT: Discovering: "Request_Service" +CLIENT: Discovery started. +CLIENT: Discovery complete. Succeeded! Found IPC ID: 0000000000000001 +CLIENT: Sending request with data { AA BB CC DD EE FF } +CLIENT: Request initiated. +SERVER: New request waiting from IPC ID: 0000000000000000 +SERVER: Got a request from IPC ID: 0000000000000000 +SERVER: Request data {AA BB CC DD EE FF } +CLIENT: Waiting for response (1 of 10) +CLIENT: Waiting for response (2 of 10) +CLIENT: Waiting for response (3 of 10) +SERVER: Sent response. +CLIENT: Received response. Data { 12 34 56 78 } +CLIENT: Sending request with data { AA BB CC DD EE FF } +... +``` + +Note that above IPC IDs may not match depending on the order the apps are +loaded and the configuration of your board. + +## Fault Handling + +This app is designed to handle faults gracefully. Communication is +intentionally slowed down to provide time to manually trigger faults between +certain actions to see the response. Using the Process Console is the easiest +way to intentionally cause a fault to test this. If your board is configured +with the process Fault Policy `RestartFaultPolicy`, then after one process +restarts discovery will be re-initiated and communication will resume. + +For example, you can fault the client with the command `fault +relay_request_client-libtock`. + +``` +SERVER: Got a request from IPC ID: 0000000000000000 +SERVER: Request data {AA BB CC DD EE FF } +CLIENT: Waiting for response (1 of 10) +$ fault relay_request_client-libtock + +Process relay_request_client-libtock now faulted +tock$ CLIENT: Starting client. +CLIENT: IPC Registry String Name capsule exists. +CLIENT: IPC Relay Request capsule exists. +CLIENT: Discovering: "Request_Service" +CLIENT: Discovery started. +CLIENT: Discovery complete. Succeeded! Found IPC ID: 0000000000000001 +CLIENT: Sending request with data { AA BB CC DD EE FF } +CLIENT: Request initiated. +SERVER: New request waiting from IPC ID: 0000000000000002 +SERVER: Error when getting request with returncode: -3 +SERVER: Sending response failed with returncode: -11 +SERVER: Got a request from IPC ID: 0000000000000002 +SERVER: Request data {AA BB CC DD EE FF } +CLIENT: Waiting for response (1 of 10) +CLIENT: Waiting for response (2 of 10) +CLIENT: Waiting for response (3 of 10) +SERVER: Sent response. +CLIENT: Received response. Data { 12 34 56 78 } +``` + + diff --git a/examples/tests/ipc/relay_request_server-libtock/main.c b/examples/tests/ipc/relay_request_server-libtock/main.c new file mode 100644 index 000000000..a0d10b6fa --- /dev/null +++ b/examples/tests/ipc/relay_request_server-libtock/main.c @@ -0,0 +1,159 @@ +#include +#include +#include + +#include +#include +#include + +// Registration +static const char* SERVICE_NAME = "Request_Service"; +static bool service_registered = false; + +// Callback when registration completes +static void ipc_registration_complete(returncode_t ret) { + if (ret == RETURNCODE_SUCCESS) { + printf("SERVER: Registration complete. Succeeded!\n"); + service_registered = true; + } else { + printf("SERVER: Registration complete. Failed with returncode: %d\n", ret); + } +} + +// Handle all existing requests. Returns when no outstanding requests remain. +static void handle_requests(void) { + // loop until all requests are completed + while (true) { + + // Attempt to get a request + uint64_t request_ipc_id = 0; + uint8_t request_buf[10] = {0}; + size_t request_len = 10; + returncode_t ret = libtock_ipc_relay_request_server_get_next_request(&request_ipc_id, request_buf, + &request_len); + + if (ret == RETURNCODE_SUCCESS) { + // Request successfully received + uint32_t lower = (uint32_t)request_ipc_id; + uint32_t upper = (uint32_t)(request_ipc_id >> 32); + printf("SERVER: Got a request from IPC ID: %08X%08X\n", (unsigned int)upper, (unsigned int)lower); + printf("SERVER: Request data {"); + for (size_t i = 0; i < request_len; i++) { + printf("%02X ", request_buf[i]); + } + printf("}\n"); + + // Delay for a while to simulate a server hard at work on a request + libtocksync_alarm_delay_ms(6000); + + // Send response + uint8_t response_buf[4] = {0x12, 0x34, 0x56, 0x78}; + ret = libtock_ipc_relay_request_server_send_response(response_buf, 4); + if (ret != RETURNCODE_SUCCESS) { + // The best course of action is to ignore the failure here. Most + // failures mean that either the client no longer exists, that it + // canceled its request, or that it didn't properly allow buffers. + printf("SERVER: Sending response failed with returncode: %d The server can ignore this.\n", ret); + } else { + printf("SERVER: Sent response.\n"); + } + } else if (ret == RETURNCODE_ESIZE) { + // Request partially received. Request buffer was too small. + uint32_t lower = (uint32_t)request_ipc_id; + uint32_t upper = (uint32_t)(request_ipc_id >> 32); + printf("SERVER: Got a PARTIAL request from IPC ID: %08X%08X\n", (unsigned int)upper, (unsigned int)lower); + printf("SERVER: Request data (truncated) {"); + for (size_t i = 0; i < request_len; i++) { + printf("%02X ", request_buf[i]); + } + printf("}\n"); + + // Send response + uint8_t response_buf[1] = {0xFF}; + ret = libtock_ipc_relay_request_server_send_response(response_buf, 1); + if (ret != RETURNCODE_SUCCESS) { + // The best course of action is to ignore the failure here. Most + // failures mean that either the client no longer exists, that it + // canceled its request, or that it didn't properly allow buffers. + printf("SERVER: Sending response failed with returncode: %d The server can ignore this.\n", ret); + } else { + printf("SERVER: Sent response.\n"); + } + } else if (ret == RETURNCODE_ENODEVICE) { + // No request was waiting. This is not an error. It instead means we + // exhausted all requests. + break; + } else { + // Some other error? This shouldn't happen. + printf("SERVER: Error when getting request with returncode: %d\n", ret); + break; + } + } +} + +// Callback when a request is waiting for us +static void ipc_request_waiting(uint64_t ipc_id) { + uint32_t lower = (uint32_t)ipc_id; + uint32_t upper = (uint32_t)(ipc_id >> 32); + printf("SERVER: New request waiting from IPC ID: %08X%08X\n", (unsigned int)upper, (unsigned int)lower); + + // Handle request(s). Loops until no further requests remain. + handle_requests(); +} + +int main(void) { + returncode_t ret = RETURNCODE_SUCCESS; + printf("SERVER: Starting server.\n"); + + // Check that we can find the driver at all + if (!libtock_ipc_registry_string_name_exists()) { + printf("SERVER: IPC Registry String Name capsule does not exist on this board.\n"); + return 1; + } else { + printf("SERVER: IPC Registry String Name capsule exists.\n"); + } + + // Check that we can send messages at all + if (!libtock_ipc_relay_request_exists()) { + printf("SERVER: IPC Relay Request capsule does not exist on this board.\n"); + return 1; + } else { + printf("SERVER: IPC Relay Request capsule exists.\n"); + } + + // Enable requests and callbacks when a new request is waiting. Importantly, + // do this BEFORE registering as a service, that way you're ready to receive + // requests the moment you are discovered. + ret = libtock_ipc_relay_request_server_enable_requests_with_callback(ipc_request_waiting); + if (ret != RETURNCODE_SUCCESS) { + printf("SERVER: Enabling request waiting callbacks failed. Returncode: %d\n", ret); + return 1; + } else { + printf("SERVER: Enabled request waiting callbacks.\n"); + } + + // Register service + printf("SERVER: Registering with string name: \"%s\"\n", SERVICE_NAME); + ret = libtock_ipc_registry_string_name_register_service(ipc_registration_complete, (uint8_t*)SERVICE_NAME, + strlen(SERVICE_NAME)); + if (ret != RETURNCODE_SUCCESS) { + printf("SERVER: Registration failed. Returncode: %d\n", ret); + return 1; + } else { + printf("SERVER: Registration started.\n"); + } + + // Delay a bit + libtocksync_alarm_delay_ms(2000); + + // Timeout. Registration shouldn't fail + if (!service_registered) { + printf("SERVER: Registration didn't succeed. Giving up.\n"); + return 1; + } + + // Request handling will be done in the callback. Just yield forever + while (true) { + yield(); + } +} diff --git a/examples/tests/ipc/string_name_client-libtock/Makefile b/examples/tests/ipc/string_name_client-libtock/Makefile new file mode 100644 index 000000000..b9cdd0fa3 --- /dev/null +++ b/examples/tests/ipc/string_name_client-libtock/Makefile @@ -0,0 +1,11 @@ +# Makefile for user application + +# Specify this directory relative to the current application. +TOCK_USERLAND_BASE_DIR = ../../../.. + +# Which files to compile. +C_SRCS := $(wildcard *.c) + +# Include userland master makefile. Contains rules and flags for actually +# building the application. +include $(TOCK_USERLAND_BASE_DIR)/AppMakefile.mk diff --git a/examples/tests/ipc/string_name_client-libtock/README.md b/examples/tests/ipc/string_name_client-libtock/README.md new file mode 100644 index 000000000..152b2f26a --- /dev/null +++ b/examples/tests/ipc/string_name_client-libtock/README.md @@ -0,0 +1,28 @@ +# String Name Client - libtock + +Discovers an IPC service via a provided string name using the asynchronous +libtock IPC Registry String Name driver. + +This is intended to be loaded alongside [string_name_server-libtock](../string_name_server-libtock). When +loaded with the String Name Server the expected behavior is: + +``` +Initialization complete. Entering main loop. +SERVER: Starting server. +CLIENT: Starting client. +SERVER: IPC Registry String Name capsule exists. +CLIENT: IPC Registry String Name capsule exists. +CLIENT: Discovering: "Test_Service" +CLIENT: Discovery started. +CLIENT: Discovery complete. Service not found. +SERVER: Registering with string name: "Test_Service" +SERVER: Registration started. +SERVER: Registration complete. Succeeded! +CLIENT: Discovering: "Test_Service" +CLIENT: Discovery started. +CLIENT: Discovery complete. Succeeded! Found IPC ID: 0000000000000001 +``` + +Note that above IPC IDs may not match depending on the order the apps are +loaded and the configuration of your board. + diff --git a/examples/tests/ipc/string_name_client-libtock/main.c b/examples/tests/ipc/string_name_client-libtock/main.c new file mode 100644 index 000000000..269961edb --- /dev/null +++ b/examples/tests/ipc/string_name_client-libtock/main.c @@ -0,0 +1,63 @@ +#include +#include +#include + +#include +#include + +static const char* SERVICE_NAME = "Test_Service"; + +static void ipc_discovery_complete(returncode_t ret, uint64_t ipc_id) { + if (ret == RETURNCODE_SUCCESS) { + uint32_t lower = (uint32_t)ipc_id; + uint32_t upper = (uint32_t)(ipc_id >> 32); + printf("CLIENT: Discovery complete. Succeeded! Found IPC ID: %08X%08X\n", (unsigned int)upper, (unsigned int)lower); + } else if (ret == RETURNCODE_ENODEVICE) { + printf("CLIENT: Discovery complete. Service not found.\n"); + } else { + printf("CLIENT: Discovery complete. Failed with returncode: %d\n", ret); + } +} + +int main(void) { + returncode_t ret = RETURNCODE_SUCCESS; + printf("CLIENT: Starting client.\n"); + + // Check that we can find the driver at all + if (!libtock_ipc_registry_string_name_exists()) { + printf("CLIENT: IPC Registry String Name capsule does not exist on this board.\n"); + return 1; + } else { + printf("CLIENT: IPC Registry String Name capsule exists.\n"); + } + + // Delay a bit + libtocksync_alarm_delay_ms(1000); + + // Attempt to discover (this should fail as the server hasn't registered yet) + printf("CLIENT: Discovering: \"%s\"\n", SERVICE_NAME); + ret = libtock_ipc_registry_string_name_discover_service(ipc_discovery_complete, (uint8_t*)SERVICE_NAME, + strlen(SERVICE_NAME)); + if (ret != RETURNCODE_SUCCESS) { + printf("CLIENT: Discovery failed. Returncode: %d\n", ret); + } else { + printf("CLIENT: Discovery started.\n"); + } + + // Delay for a bit + libtocksync_alarm_delay_ms(5000); + + // Re-attempt to discover (this should now succeed) + printf("CLIENT: Discovering: \"%s\"\n", SERVICE_NAME); + ret = libtock_ipc_registry_string_name_discover_service(ipc_discovery_complete, (uint8_t*)SERVICE_NAME, + strlen(SERVICE_NAME)); + if (ret != RETURNCODE_SUCCESS) { + printf("CLIENT: Discovery failed. Returncode: %d\n", ret); + } else { + printf("CLIENT: Discovery started.\n"); + } + + while (1) { + yield(); + } +} diff --git a/examples/tests/ipc/string_name_server-libtock/Makefile b/examples/tests/ipc/string_name_server-libtock/Makefile new file mode 100644 index 000000000..b9cdd0fa3 --- /dev/null +++ b/examples/tests/ipc/string_name_server-libtock/Makefile @@ -0,0 +1,11 @@ +# Makefile for user application + +# Specify this directory relative to the current application. +TOCK_USERLAND_BASE_DIR = ../../../.. + +# Which files to compile. +C_SRCS := $(wildcard *.c) + +# Include userland master makefile. Contains rules and flags for actually +# building the application. +include $(TOCK_USERLAND_BASE_DIR)/AppMakefile.mk diff --git a/examples/tests/ipc/string_name_server-libtock/README.md b/examples/tests/ipc/string_name_server-libtock/README.md new file mode 100644 index 000000000..c3d6acab8 --- /dev/null +++ b/examples/tests/ipc/string_name_server-libtock/README.md @@ -0,0 +1,28 @@ +# String Name Server - libtock + +Registers an IPC service via a provided string name using the asynchronous +libtock IPC Registry String Name driver. + +This is intended to be loaded alongside [string_name_client-libtock](../string_name_client-libtock). When +loaded with the String Name Client the expected behavior is: + +``` +Initialization complete. Entering main loop. +SERVER: Starting server. +CLIENT: Starting client. +SERVER: IPC Registry String Name capsule exists. +CLIENT: IPC Registry String Name capsule exists. +CLIENT: Discovering: "Test_Service" +CLIENT: Discovery started. +CLIENT: Discovery complete. Service not found. +SERVER: Registering with string name: "Test_Service" +SERVER: Registration started. +SERVER: Registration complete. Succeeded! +CLIENT: Discovering: "Test_Service" +CLIENT: Discovery started. +CLIENT: Discovery complete. Succeeded! Found IPC ID: 0000000000000001 +``` + +Note that above IPC IDs may not match depending on the order the apps are +loaded and the configuration of your board. + diff --git a/examples/tests/ipc/string_name_server-libtock/main.c b/examples/tests/ipc/string_name_server-libtock/main.c new file mode 100644 index 000000000..38ddc9c0f --- /dev/null +++ b/examples/tests/ipc/string_name_server-libtock/main.c @@ -0,0 +1,45 @@ +#include +#include +#include + +#include +#include + +static const char* SERVICE_NAME = "Test_Service"; + +static void ipc_registration_complete(returncode_t ret) { + if (ret == RETURNCODE_SUCCESS) { + printf("SERVER: Registration complete. Succeeded!\n"); + } else { + printf("SERVER: Registration complete. Failed with returncode: %d\n", ret); + } +} + +int main(void) { + printf("SERVER: Starting server.\n"); + + // Check that we can find the driver at all + if (!libtock_ipc_registry_string_name_exists()) { + printf("SERVER: IPC Registry String Name capsule does not exist on this board.\n"); + return 1; + } else { + printf("SERVER: IPC Registry String Name capsule exists.\n"); + } + + // Delay a bit + libtocksync_alarm_delay_ms(5000); + + // Attempt to register + printf("SERVER: Registering with string name: \"%s\"\n", SERVICE_NAME); + returncode_t ret = libtock_ipc_registry_string_name_register_service(ipc_registration_complete, + (uint8_t*)SERVICE_NAME, strlen(SERVICE_NAME)); + if (ret != RETURNCODE_SUCCESS) { + printf("SERVER: Registration failed. Returncode: %d\n", ret); + } else { + printf("SERVER: Registration started.\n"); + } + + while (1) { + yield(); + } +} diff --git a/libtock-sync/Makefile b/libtock-sync/Makefile index 26f64ebbc..bb3317db0 100644 --- a/libtock-sync/Makefile +++ b/libtock-sync/Makefile @@ -15,6 +15,8 @@ $(LIBNAME)_SRCS += $(wildcard $($(LIBNAME)_DIR)/display/*.c) $(LIBNAME)_SRCS += $(wildcard $($(LIBNAME)_DIR)/display/syscalls/*.c) $(LIBNAME)_SRCS += $(wildcard $($(LIBNAME)_DIR)/interface/*.c) $(LIBNAME)_SRCS += $(wildcard $($(LIBNAME)_DIR)/interface/syscalls/*.c) +$(LIBNAME)_SRCS += $(wildcard $($(LIBNAME)_DIR)/ipc/*.c) +$(LIBNAME)_SRCS += $(wildcard $($(LIBNAME)_DIR)/ipc/syscalls/*.c) $(LIBNAME)_SRCS += $(wildcard $($(LIBNAME)_DIR)/kernel/*.c) $(LIBNAME)_SRCS += $(wildcard $($(LIBNAME)_DIR)/kernel/syscalls/*.c) $(LIBNAME)_SRCS += $(wildcard $($(LIBNAME)_DIR)/net/*.c) diff --git a/libtock-sync/ipc/README.md b/libtock-sync/ipc/README.md new file mode 100644 index 000000000..e30e53980 --- /dev/null +++ b/libtock-sync/ipc/README.md @@ -0,0 +1,20 @@ +# Libtock-sync IPC + +This directory holds synchronous drivers for interprocess communication (IPC) support. + +Currently the following drivers exist: + +* **IPC Registry Package Name** + + Provides process registration/discovery via TBF header package names. + +* **IPC Registry String Name** + + Provides process registration/discovery via arbitrary string names. + +* **IPC Relay Request** + + Provides single-copy allow-to-allow requests and responses between processes. + + For client request functionality with a timeout, see [libtock-sync/services/ipc_relay_request_timeout](../services/ipc_relay_request_timeout.h) + diff --git a/libtock-sync/ipc/ipc_registry_package_name.c b/libtock-sync/ipc/ipc_registry_package_name.c new file mode 100644 index 000000000..38610bb86 --- /dev/null +++ b/libtock-sync/ipc/ipc_registry_package_name.c @@ -0,0 +1,34 @@ +#include + +#include "ipc_registry_package_name.h" + +#include "syscalls/ipc_registry_package_name_syscalls.h" + +bool libtocksync_ipc_registry_package_name_exists(void) { + return libtock_ipc_registry_package_name_driver_exists(); +} + +returncode_t libtocksync_ipc_registry_package_name_register_service(void) { + returncode_t err = RETURNCODE_SUCCESS; + err = libtock_ipc_registry_package_name_command_register(); + if (err != RETURNCODE_SUCCESS) return err; + + err = libtocksync_ipc_registry_package_name_yield_wait_for_registration_complete(); + return err; +} + +returncode_t libtocksync_ipc_registry_package_name_discover_service(uint8_t* package_name, size_t package_name_len, + uint64_t* ipc_id) { + returncode_t err = RETURNCODE_SUCCESS; + + err = libtock_ipc_registry_package_name_set_allow_name(package_name, package_name_len); + if (err != RETURNCODE_SUCCESS) return err; + defer { libtock_ipc_registry_package_name_set_allow_name(NULL, 0); + } + + err = libtock_ipc_registry_package_name_command_discover(); + if (err != RETURNCODE_SUCCESS) return err; + + err = libtocksync_ipc_registry_package_name_yield_wait_for_discovery_complete(ipc_id); + return err; +} diff --git a/libtock-sync/ipc/ipc_registry_package_name.h b/libtock-sync/ipc/ipc_registry_package_name.h new file mode 100644 index 000000000..47696fb82 --- /dev/null +++ b/libtock-sync/ipc/ipc_registry_package_name.h @@ -0,0 +1,41 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// Determine if the IPC Registry Package Name capsule exists. +// +// ## Return Value +// +// True if the capsule exists. +bool libtocksync_ipc_registry_package_name_exists(void); + +// Register a new service by package name. +// +// ## Return Value +// +// A returncode indicating whether the service was registered successfully. +returncode_t libtocksync_ipc_registry_package_name_register_service(void); + +// Discovery a service by package name. +// +// ## Arguments +// +// - `package_name`: Array of uint8_t with the package name of the service to be discovered. +// - `package_name_len`: Length of the array. +// - `ipc_id`: Pointer to a location for the discovered IPC identifier to be stored. +// +// ## Return Value +// +// A returncode indicating whether the service was discovered successfully. +returncode_t libtocksync_ipc_registry_package_name_discover_service(uint8_t* package_name, size_t package_name_len, + uint64_t* ipc_id); + + +#ifdef __cplusplus +} +#endif diff --git a/libtock-sync/ipc/ipc_registry_string_name.c b/libtock-sync/ipc/ipc_registry_string_name.c new file mode 100644 index 000000000..911394b8c --- /dev/null +++ b/libtock-sync/ipc/ipc_registry_string_name.c @@ -0,0 +1,61 @@ +#include + +#include + +#include "ipc_registry_string_name.h" + +#include "syscalls/ipc_registry_string_name_syscalls.h" + +#define STRING_BUF_LEN 20 +static uint8_t string_buf[STRING_BUF_LEN] = {0}; + +bool libtocksync_ipc_registry_string_name_exists(void) { + return libtock_ipc_registry_string_name_driver_exists(); +} + +returncode_t libtocksync_ipc_registry_string_name_register_service(uint8_t* string_name, size_t string_name_len) { + // Copy data to local buffer if possible, which is fixed in size and zero-padded + if (string_name_len > STRING_BUF_LEN) { + return RETURNCODE_ESIZE; + } + memset(string_buf, 0, STRING_BUF_LEN); + for (size_t i = 0; i < STRING_BUF_LEN && i < string_name_len; i++) { + string_buf[i] = string_name[i]; + } + + returncode_t err = RETURNCODE_SUCCESS; + err = libtock_ipc_registry_string_name_set_allow_name(string_buf, STRING_BUF_LEN); + if (err != RETURNCODE_SUCCESS) return err; + defer { libtock_ipc_registry_string_name_set_allow_name(NULL, 0); + } + + err = libtock_ipc_registry_string_name_command_register(); + if (err != RETURNCODE_SUCCESS) return err; + + err = libtocksync_ipc_registry_string_name_yield_wait_for_registration_complete(); + return err; +} + +returncode_t libtocksync_ipc_registry_string_name_discover_service(uint8_t* string_name, size_t string_name_len, + uint64_t* ipc_id) { + // Copy data to local buffer if possible, which is fixed in size and zero-padded + if (string_name_len > STRING_BUF_LEN) { + return RETURNCODE_ESIZE; + } + memset(string_buf, 0, STRING_BUF_LEN); + for (size_t i = 0; i < STRING_BUF_LEN && i < string_name_len; i++) { + string_buf[i] = string_name[i]; + } + + returncode_t err = RETURNCODE_SUCCESS; + err = libtock_ipc_registry_string_name_set_allow_name(string_buf, STRING_BUF_LEN); + if (err != RETURNCODE_SUCCESS) return err; + defer { libtock_ipc_registry_string_name_set_allow_name(NULL, 0); + } + + err = libtock_ipc_registry_string_name_command_discover(); + if (err != RETURNCODE_SUCCESS) return err; + + err = libtocksync_ipc_registry_string_name_yield_wait_for_discovery_complete(ipc_id); + return err; +} diff --git a/libtock-sync/ipc/ipc_registry_string_name.h b/libtock-sync/ipc/ipc_registry_string_name.h new file mode 100644 index 000000000..23053a881 --- /dev/null +++ b/libtock-sync/ipc/ipc_registry_string_name.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// Determine if the IPC Registry String Name capsule exists. +// +// ## Return Value +// +// True if the capsule exists. +bool libtocksync_ipc_registry_string_name_exists(void); + +// Register a new service by string name. +// +// ## Arguments +// +// - `string_name`: Array of uint8_t with the string name of the service to be registered. +// - `string_name_len`: Length of the array. +// +// ## Return Value +// +// A returncode indicating whether the service was registered successfully. +returncode_t libtocksync_ipc_registry_string_name_register_service(uint8_t* string_name, size_t string_name_len); + +// Discovery a service by string name. +// +// ## Arguments +// +// - `string_name`: Array of uint8_t with the string name of the service to be discovered. +// - `string_name_len`: Length of the array. +// - `ipc_id`: Pointer to a location for the discovered IPC identifier to be stored. +// +// ## Return Value +// +// A returncode indicating whether the service was discovered successfully. +returncode_t libtocksync_ipc_registry_string_name_discover_service(uint8_t* string_name, size_t string_name_len, + uint64_t* ipc_id); + + +#ifdef __cplusplus +} +#endif diff --git a/libtock-sync/ipc/ipc_relay_request.c b/libtock-sync/ipc/ipc_relay_request.c new file mode 100644 index 000000000..34a0f7c1e --- /dev/null +++ b/libtock-sync/ipc/ipc_relay_request.c @@ -0,0 +1,65 @@ +#include + +#include "ipc_relay_request.h" + +#include "syscalls/ipc_relay_request_syscalls.h" + +bool libtocksync_ipc_relay_request_exists(void) { + return libtock_ipc_relay_request_driver_exists(); +} + +returncode_t libtocksync_ipc_relay_request_client_send_request( + uint64_t ipc_id, + uint8_t* request_buf, size_t request_len, + uint8_t* response_buf, size_t* response_len) { + returncode_t err = RETURNCODE_SUCCESS; + + err = libtock_ipc_relay_request_set_allow_read_buffer(request_buf, request_len); + if (err != RETURNCODE_SUCCESS) return err; + defer { libtock_ipc_relay_request_set_allow_read_buffer(NULL, 0); + } + + err = libtock_ipc_relay_request_set_allow_write_buffer(response_buf, *response_len); + if (err != RETURNCODE_SUCCESS) return err; + defer { libtock_ipc_relay_request_set_allow_write_buffer(NULL, 0); + } + + err = libtock_ipc_relay_request_command_client_send_request(ipc_id); + if (err != RETURNCODE_SUCCESS) return err; + + // we do a yield-waitfor here, so this code will hang forever if the server + // never bothers to respond + err = libtocksync_ipc_relay_request_yield_wait_for_client_response_received(response_len); + if (err != RETURNCODE_SUCCESS) return err; + return RETURNCODE_SUCCESS; +} + +returncode_t libtocksync_ipc_relay_request_server_enable_requests(void) { + // Libtock function is already synchronous + return libtock_ipc_relay_request_server_enable_requests_without_callback(); +} + +returncode_t libtocksync_ipc_relay_request_server_disable_requests(void) { + // Libtock function is already synchronous + return libtock_ipc_relay_request_server_disable_requests(); +} + +returncode_t libtocksync_ipc_relay_request_server_get_next_request( + uint64_t* ipc_id, + uint8_t* request_buf, size_t* request_len) { + // Libtock function is already synchronous + return libtock_ipc_relay_request_server_get_next_request(ipc_id, request_buf, request_len); +} + +returncode_t libtocksync_ipc_relay_request_server_get_next_request_from( + uint64_t ipc_id, + uint8_t* request_buf, size_t* request_len) { + // Libtock function is already synchronous + return libtock_ipc_relay_request_server_get_next_request_from(ipc_id, request_buf, request_len); +} + +returncode_t libtocksync_ipc_relay_request_server_send_response( + uint8_t* response_buf, size_t response_len) { + // Libtock function is already synchronous + return libtock_ipc_relay_request_server_send_response(response_buf, response_len); +} diff --git a/libtock-sync/ipc/ipc_relay_request.h b/libtock-sync/ipc/ipc_relay_request.h new file mode 100644 index 000000000..1c54b5f7d --- /dev/null +++ b/libtock-sync/ipc/ipc_relay_request.h @@ -0,0 +1,120 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// Determine if the IPC Registry String Name capsule exists. +// +// ## Return Value +// +// True if the capsule exists. +bool libtocksync_ipc_relay_request_exists(void); + +// For clients, send a request to a service. +// +// Warning: if the server does not respond, this function will never return. +// While requests can be canceled, this synchronous interface has no ability to +// do so. See for an +// alternative synchronous option with a timeout that is likely preferable. +// +// ## Arguments +// +// `ipc_id`: IPC identifier of service to send request to. +// `request_buf`: Buffer of uint8_t values to send to the service. +// `request_len`: Length of request buffer. +// `response_buf`: Buffer of uint8_t values to hold response from service. +// `response_len`: Pointer to length of response buffer. This will be +// overwritten as the length of the message stored in the response buffer +// (<= original maximum length) +// +// ## Return value +// +// A returncode indicating whether the request was sent and response +// successfully received. +returncode_t libtocksync_ipc_relay_request_client_send_request( + uint64_t ipc_id, + uint8_t* request_buf, size_t request_len, + uint8_t* response_buf, size_t* response_len); + +// For servers, enable requests from clients. +// +// ## Return value +// +// A returncode indicating whether requests have been enabled. +returncode_t libtocksync_ipc_relay_request_server_enable_requests(void); + +// For servers, disable requests from clients. +// +// Note: this will send an error to any outstanding clients. +// +// ## Return value +// +// A returncode indicating whether requests have been canceled. +returncode_t libtocksync_ipc_relay_request_server_disable_requests(void); + +// For servers, get the next waiting request (if any exist). +// +// A request can only be retrieved if no outstanding request is in progress. +// Once retrieved, the request will remain in progress until a response is sent. +// +// ## Arguments +// +// `ipc_id`: Pointer to location to hold the the IPC identifier for the found request. +// `request_buf`: Buffer of uint8_t values to hold request from client. +// `request_len`: Length of request buffer. This will be overwritten as the +// length of the message stored in the request buffer (<= original maximum length) +// +// ## Return value +// +// A returncode indicating whether a request existed and was successfully +// retrieved. If no request was retrieved, `ipc_id` is invalid and +// `request_len` will be set to zero. +returncode_t libtocksync_ipc_relay_request_server_get_next_request( + uint64_t* ipc_id, + uint8_t* request_buf, size_t* request_len); + +// For servers, get the next waiting request from a specified client (if it exists). +// +// A request can only be retrieved if no outstanding request is in progress. +// Once retrieved, the request will remain in progress until a response is sent. +// +// ## Arguments +// +// `ipc_id`: The IPC identifier of the client to get a request from. +// `request_buf`: Buffer of uint8_t values to hold request from client. +// `request_len`: Pointer to length of request buffer. This will be overwritten as the +// length of the message stored in the request buffer (<= original maximum length) +// +// ## Return value +// +// A returncode indicating whether a request existed and was successfully +// retrieved. If no request was retrieved, `ipc_id` is invalid and +// `request_len` will be set to zero. +returncode_t libtocksync_ipc_relay_request_server_get_next_request_from( + uint64_t ipc_id, + uint8_t* request_buf, size_t* request_len); + +// For servers, send a response to the client you got a request from. +// +// The request MUST first be retrieved with one of the `get_next_request` +// functions. The response will go to the client corresponding to that request. +// +// ## Arguments +// +// `response_buf`: Buffer of uint8_t values to send to the client. +// `response_len`: Length of response buffer. +// +// ## Return value +// +// A returncode indicating if the response was successfully sent. +returncode_t libtocksync_ipc_relay_request_server_send_response( + uint8_t* response_buf, size_t response_len); + + +#ifdef __cplusplus +} +#endif diff --git a/libtock-sync/ipc/syscalls/ipc_registry_package_name_syscalls.c b/libtock-sync/ipc/syscalls/ipc_registry_package_name_syscalls.c new file mode 100644 index 000000000..778f08d76 --- /dev/null +++ b/libtock-sync/ipc/syscalls/ipc_registry_package_name_syscalls.c @@ -0,0 +1,16 @@ +#include "ipc_registry_package_name_syscalls.h" + +returncode_t libtocksync_ipc_registry_package_name_yield_wait_for_registration_complete(void) { + yield_waitfor_return_t ywf = yield_wait_for(DRIVER_NUM_IPC_REGISTRY_PACKAGE_NAME, 0); + return tock_status_to_returncode(ywf.data0); +} + +returncode_t libtocksync_ipc_registry_package_name_yield_wait_for_discovery_complete(uint64_t* ipc_id) { + yield_waitfor_return_t ywf = yield_wait_for(DRIVER_NUM_IPC_REGISTRY_PACKAGE_NAME, 1); + + uint32_t lower = ywf.data1; + uint32_t upper = ywf.data2; + *ipc_id = (((uint64_t)upper) << 32) | ((uint64_t)lower); + + return tock_status_to_returncode(ywf.data0); +} diff --git a/libtock-sync/ipc/syscalls/ipc_registry_package_name_syscalls.h b/libtock-sync/ipc/syscalls/ipc_registry_package_name_syscalls.h new file mode 100644 index 000000000..f0316ad87 --- /dev/null +++ b/libtock-sync/ipc/syscalls/ipc_registry_package_name_syscalls.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// Wait for registration to complete +returncode_t libtocksync_ipc_registry_package_name_yield_wait_for_registration_complete(void); + +// Wait for discovery to complete +returncode_t libtocksync_ipc_registry_package_name_yield_wait_for_discovery_complete(uint64_t* ipc_id); + +#ifdef __cplusplus +} +#endif diff --git a/libtock-sync/ipc/syscalls/ipc_registry_string_name_syscalls.c b/libtock-sync/ipc/syscalls/ipc_registry_string_name_syscalls.c new file mode 100644 index 000000000..72ebd29a9 --- /dev/null +++ b/libtock-sync/ipc/syscalls/ipc_registry_string_name_syscalls.c @@ -0,0 +1,19 @@ +#include + +#include "ipc_registry_string_name_syscalls.h" + +returncode_t libtocksync_ipc_registry_string_name_yield_wait_for_registration_complete(void) { + yield_waitfor_return_t ywf = yield_wait_for(DRIVER_NUM_IPC_REGISTRY_STRING_NAME, 0); + + return tock_status_to_returncode(ywf.data0); +} + +returncode_t libtocksync_ipc_registry_string_name_yield_wait_for_discovery_complete(uint64_t* ipc_id) { + yield_waitfor_return_t ywf = yield_wait_for(DRIVER_NUM_IPC_REGISTRY_STRING_NAME, 1); + + uint32_t lower = ywf.data1; + uint32_t upper = ywf.data2; + *ipc_id = (((uint64_t)upper) << 32) | ((uint64_t)lower); + + return tock_status_to_returncode(ywf.data0); +} diff --git a/libtock-sync/ipc/syscalls/ipc_registry_string_name_syscalls.h b/libtock-sync/ipc/syscalls/ipc_registry_string_name_syscalls.h new file mode 100644 index 000000000..4906d856e --- /dev/null +++ b/libtock-sync/ipc/syscalls/ipc_registry_string_name_syscalls.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// Wait for registration to complete +returncode_t libtocksync_ipc_registry_string_name_yield_wait_for_registration_complete(void); + +// Wait for discovery to complete +returncode_t libtocksync_ipc_registry_string_name_yield_wait_for_discovery_complete(uint64_t* ipc_id); + +#ifdef __cplusplus +} +#endif diff --git a/libtock-sync/ipc/syscalls/ipc_relay_request_syscalls.c b/libtock-sync/ipc/syscalls/ipc_relay_request_syscalls.c new file mode 100644 index 000000000..f687408eb --- /dev/null +++ b/libtock-sync/ipc/syscalls/ipc_relay_request_syscalls.c @@ -0,0 +1,17 @@ +#include "ipc_relay_request_syscalls.h" + +returncode_t libtocksync_ipc_relay_request_yield_wait_for_client_response_received(size_t* response_len) { + yield_waitfor_return_t ywf = yield_wait_for(DRIVER_NUM_IPC_RELAY_REQUEST, 0); + *response_len = (size_t)ywf.data1; + return tock_status_to_returncode(ywf.data0); +} + +returncode_t libtocksync_ipc_relay_request_yield_wait_for_server_request_waiting(uint64_t* ipc_id) { + yield_waitfor_return_t ywf = yield_wait_for(DRIVER_NUM_IPC_RELAY_REQUEST, 1); + + uint32_t lower = ywf.data1; + uint32_t upper = ywf.data2; + *ipc_id = (((uint64_t)upper) << 32) | ((uint64_t)lower); + + return tock_status_to_returncode(ywf.data0); +} diff --git a/libtock-sync/ipc/syscalls/ipc_relay_request_syscalls.h b/libtock-sync/ipc/syscalls/ipc_relay_request_syscalls.h new file mode 100644 index 000000000..d76f46b2c --- /dev/null +++ b/libtock-sync/ipc/syscalls/ipc_relay_request_syscalls.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// For clients, wait for a response +returncode_t libtocksync_ipc_relay_request_yield_wait_for_client_response_received(size_t* response_len); + +// For servers, wait for an incoming request +returncode_t libtocksync_ipc_relay_request_yield_wait_for_server_request_waiting(uint64_t* ipc_id); + +#ifdef __cplusplus +} +#endif diff --git a/libtock-sync/services/ipc_relay_request_timeout.c b/libtock-sync/services/ipc_relay_request_timeout.c new file mode 100644 index 000000000..b8f112d38 --- /dev/null +++ b/libtock-sync/services/ipc_relay_request_timeout.c @@ -0,0 +1,106 @@ +#include +#include +#include +#include + +#include "ipc_relay_request_timeout.h" + +// timeout state +static libtock_alarm_t alarm_handle; +static volatile bool cb_fired = false; +static volatile returncode_t cb_result = RETURNCODE_SUCCESS; +static volatile size_t cb_response_len = 0; + +// callback for client, response received +static void client_response_received(int statuscode, int len, + __attribute__ ((unused)) int unused, + __attribute__ ((unused)) void* opaque) { + // got a response. Save results + cb_fired = true; + cb_result = tock_status_to_returncode(statuscode); + cb_response_len = (size_t)((uint32_t)len); +} + +// callback for timer expiring +static void timeout_callback(__attribute__ ((unused)) uint32_t now, + __attribute__ ((unused)) uint32_t scheduled, + __attribute__ ((unused)) void* opaque) { + // timeout occurred, attempt to cancel transaction. This will generate an + // upcall to get us out of the yield anyways + libtock_ipc_relay_request_client_cancel_request(); +} + + +returncode_t libtocksync_ipc_relay_request_client_send_request_with_timeout( + uint64_t ipc_id, + uint8_t* request_buf, size_t request_len, + uint8_t* response_buf, size_t* response_len, + uint32_t timeout_ms) { + returncode_t err = RETURNCODE_SUCCESS; + + err = libtock_ipc_relay_request_set_allow_read_buffer(request_buf, request_len); + if (err != RETURNCODE_SUCCESS) return err; + defer { libtock_ipc_relay_request_set_allow_read_buffer(NULL, 0); + } + + err = libtock_ipc_relay_request_set_allow_write_buffer(response_buf, *response_len); + if (err != RETURNCODE_SUCCESS) return err; + defer { libtock_ipc_relay_request_set_allow_write_buffer(NULL, 0); + } + + if (timeout_ms != 0) { + // timeout case. We need to manually set up callback, yield, and handle + // outcomes + + // setup state + cb_fired = false; + cb_result = RETURNCODE_SUCCESS; + cb_response_len = 0; + + // need to register for response_received upcalls for possible timeout + err = libtock_ipc_relay_request_set_upcall_client_response_received(client_response_received, NULL); + if (err != RETURNCODE_SUCCESS) return err; + defer { libtock_ipc_relay_request_set_upcall_client_response_received(NULL, NULL); + } + + // start a timeout countdown + err = libtock_alarm_in_ms(timeout_ms, timeout_callback, NULL, &alarm_handle); + if (err != RETURNCODE_SUCCESS) return err; + defer { libtock_alarm_ms_cancel(&alarm_handle); + } + + // actually start the request + err = libtock_ipc_relay_request_command_client_send_request(ipc_id); + if (err != RETURNCODE_SUCCESS) return err; + + // we're waiting on one of two possible upcalls her: Alarm or IPC Relay + // Request, so we can't do a yield-waitfor. Instead we register both callbacks + // and yield until one of them occurs + while (!cb_fired) { + yield(); + } + + // handle outcome + if (cb_result == RETURNCODE_ECANCEL) { + // this was a timeout. That's the only way for the request to be canceled + return RETURNCODE_ENOACK; + } else if (cb_result == RETURNCODE_SUCCESS) { + // this was a successful response + *response_len = cb_response_len; + return RETURNCODE_SUCCESS; + } else { + // this was a different error + return err; + } + } else { + // no timeout case, much simpler + + err = libtock_ipc_relay_request_command_client_send_request(ipc_id); + if (err != RETURNCODE_SUCCESS) return err; + + // no timeout, so we can do a yield-waitfor + err = libtocksync_ipc_relay_request_yield_wait_for_client_response_received(response_len); + if (err != RETURNCODE_SUCCESS) return err; + return RETURNCODE_SUCCESS; + } +} diff --git a/libtock-sync/services/ipc_relay_request_timeout.h b/libtock-sync/services/ipc_relay_request_timeout.h new file mode 100644 index 000000000..7d5c0d3c2 --- /dev/null +++ b/libtock-sync/services/ipc_relay_request_timeout.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// For clients, send a request to a service. +// +// This uses the alarm driver to implement a timeout. If a timeout value is +// provided and the given number of milliseconds pass without receiving a +// response, the request will be canceled. If a timeout occurs, the function +// will return the value `RETURNCODE_ENOACK`. +// +// Be aware that this code internally calls `yield()` to wait for a callback +// that could be from either IPC or Alarm. This may also trigger other unrelated +// upcalls if any are outstanding. +// +// ## Arguments +// +// `ipc_id`: IPC identifier of service to send request to. +// `request_buf`: Buffer of uint8_t values to send to the service. +// `request_len`: Length of request buffer. +// `response_buf`: Buffer of uint8_t values to hold response from service. +// `response_len`: Pointer to length of response buffer. This will be +// overwritten as the length of the message stored in the response buffer +// (<= original maximum length) +// `timeout_ms`: Number of milliseconds to wait for a response before canceling +// the request. Waits forever if the timeout is zero. +// +// ## Return value +// +// A returncode indicating whether the request was sent and response +// successfully received. +returncode_t libtocksync_ipc_relay_request_client_send_request_with_timeout( + uint64_t ipc_id, + uint8_t* request_buf, size_t request_len, + uint8_t* response_buf, size_t* response_len, + uint32_t timeout_ms); + + +#ifdef __cplusplus +} +#endif diff --git a/libtock/Makefile b/libtock/Makefile index 978a163ef..754d76686 100644 --- a/libtock/Makefile +++ b/libtock/Makefile @@ -14,6 +14,8 @@ $(LIBNAME)_SRCS += $(wildcard $($(LIBNAME)_DIR)/display/*.c) $(LIBNAME)_SRCS += $(wildcard $($(LIBNAME)_DIR)/display/syscalls/*.c) $(LIBNAME)_SRCS += $(wildcard $($(LIBNAME)_DIR)/interface/*.c) $(LIBNAME)_SRCS += $(wildcard $($(LIBNAME)_DIR)/interface/syscalls/*.c) +$(LIBNAME)_SRCS += $(wildcard $($(LIBNAME)_DIR)/ipc/*.c) +$(LIBNAME)_SRCS += $(wildcard $($(LIBNAME)_DIR)/ipc/syscalls/*.c) $(LIBNAME)_SRCS += $(wildcard $($(LIBNAME)_DIR)/kernel/*.c) $(LIBNAME)_SRCS += $(wildcard $($(LIBNAME)_DIR)/kernel/syscalls/*.c) $(LIBNAME)_SRCS += $(wildcard $($(LIBNAME)_DIR)/net/*.c) diff --git a/libtock/ipc/README.md b/libtock/ipc/README.md new file mode 100644 index 000000000..4cf68d3be --- /dev/null +++ b/libtock/ipc/README.md @@ -0,0 +1,18 @@ +# Libtock IPC + +This directory holds asynchronous drivers for interprocess communication (IPC) support. + +Currently the following drivers exist: + +* **IPC Registry Package Name** + + Provides process registration/discovery via TBF header package names. + +* **IPC Registry String Name** + + Provides process registration/discovery via arbitrary string names. + +* **IPC Relay Request** + + Provides single-copy allow-to-allow requests and responses between processes. + diff --git a/libtock/ipc/ipc_registry_package_name.c b/libtock/ipc/ipc_registry_package_name.c new file mode 100644 index 000000000..08a1b6143 --- /dev/null +++ b/libtock/ipc/ipc_registry_package_name.c @@ -0,0 +1,60 @@ +#include "ipc_registry_package_name.h" + +#include "syscalls/ipc_registry_package_name_syscalls.h" + +// callback for registration complete +static void registration_complete_upcall(int statuscode, + __attribute__ ((unused)) int unused1, + __attribute__ ((unused)) int unused2, void* opaque) { + // Cleanup before performing callback + libtock_ipc_registry_package_name_set_upcall_registration_complete(NULL, NULL); + + libtock_ipc_registry_package_name_registration_complete_callback cb = + (libtock_ipc_registry_package_name_registration_complete_callback)opaque; + cb(tock_status_to_returncode(statuscode)); +} + +// callback for discovery complete +static void discovery_complete_upcall(int statuscode, int lower_val, int upper_val, void* opaque) { + // Cleanup before performing callback + libtock_ipc_registry_package_name_set_allow_name(NULL, 0); + libtock_ipc_registry_package_name_set_upcall_discovery_complete(NULL, NULL); + + libtock_ipc_registry_package_name_discovery_complete_callback cb = + (libtock_ipc_registry_package_name_discovery_complete_callback)opaque; + uint32_t lower = (uint32_t)lower_val; + uint32_t upper = (uint32_t)upper_val; + uint64_t ipc_id = (((uint64_t)upper) << 32) | ((uint64_t)lower); + cb(tock_status_to_returncode(statuscode), ipc_id); +} + +bool libtock_ipc_registry_package_name_exists(void) { + return libtock_ipc_registry_package_name_driver_exists(); +} + +returncode_t libtock_ipc_registry_package_name_register_service( + libtock_ipc_registry_package_name_registration_complete_callback cb) { + returncode_t err = RETURNCODE_SUCCESS; + + err = libtock_ipc_registry_package_name_set_upcall_registration_complete(registration_complete_upcall, cb); + if (err != RETURNCODE_SUCCESS) return err; + + err = libtock_ipc_registry_package_name_command_register(); + return err; +} + +returncode_t libtock_ipc_registry_package_name_discover_service( + libtock_ipc_registry_package_name_discovery_complete_callback cb, + uint8_t* package_name, + size_t package_name_len) { + returncode_t err = RETURNCODE_SUCCESS; + + err = libtock_ipc_registry_package_name_set_allow_name(package_name, package_name_len); + if (err != RETURNCODE_SUCCESS) return err; + + err = libtock_ipc_registry_package_name_set_upcall_discovery_complete(discovery_complete_upcall, cb); + if (err != RETURNCODE_SUCCESS) return err; + + err = libtock_ipc_registry_package_name_command_discover(); + return err; +} diff --git a/libtock/ipc/ipc_registry_package_name.h b/libtock/ipc/ipc_registry_package_name.h new file mode 100644 index 000000000..5fa737702 --- /dev/null +++ b/libtock/ipc/ipc_registry_package_name.h @@ -0,0 +1,58 @@ +#pragma once + +#include "../tock.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// Function signature for registration complete callback. +// +// - `arg1` (`returncode_t`): Status from registration. +typedef void (*libtock_ipc_registry_package_name_registration_complete_callback)(returncode_t); + +// Function signature for discovery complete callback. +// +// - `arg1` (`returncode_t`): Status from discovery. +// - `arg2` (`uint64_t`): IPC identifier for discovered service. +typedef void (*libtock_ipc_registry_package_name_discovery_complete_callback)(returncode_t, uint64_t); + + +// Check if the driver exists. +bool libtock_ipc_registry_package_name_exists(void); + +// Register as a service using its package name. +// +// If successful, the callback will be received when registration has completed. +// +// ## Arguments +// +// `cb`: Callback function triggered when registration is completed. +// +// ## Return value +// +// A returncode indicating whether the registration process started successfully. +returncode_t libtock_ipc_registry_package_name_register_service( + libtock_ipc_registry_package_name_registration_complete_callback cb); + +// Discover a service using a package name. +// +// If successful, the callback will be triggered when discovery has completed. +// +// ## Arguments +// +// `cb`: Callback function triggered when registration is completed. +// `package_name`: Buffer of uint8_t values to use as service name +// `package_name_len`: Length of buffer. +// +// ## Return value +// +// A returncode indicating whether the registration process started successfully. +returncode_t libtock_ipc_registry_package_name_discover_service( + libtock_ipc_registry_package_name_discovery_complete_callback cb, + uint8_t* package_name, + size_t package_name_len); + +#ifdef __cplusplus +} +#endif diff --git a/libtock/ipc/ipc_registry_string_name.c b/libtock/ipc/ipc_registry_string_name.c new file mode 100644 index 000000000..ab2e7c8bf --- /dev/null +++ b/libtock/ipc/ipc_registry_string_name.c @@ -0,0 +1,88 @@ +#include + +#include "ipc_registry_string_name.h" + +#include "syscalls/ipc_registry_string_name_syscalls.h" + +#define STRING_BUF_LEN 20 +static uint8_t string_buf[STRING_BUF_LEN] = {0}; + +// callback for registration complete +static void registration_complete_upcall(int statuscode, + __attribute__ ((unused)) int unused1, + __attribute__ ((unused)) int unused2, void* opaque) { + // Cleanup before performing callback + libtock_ipc_registry_string_name_set_allow_name(NULL, 0); + libtock_ipc_registry_string_name_set_upcall_registration_complete(NULL, NULL); + + libtock_ipc_registry_string_name_registration_complete_callback cb = + (libtock_ipc_registry_string_name_registration_complete_callback)opaque; + cb(tock_status_to_returncode(statuscode)); +} + +// callback for discovery complete +static void discovery_complete_upcall(int statuscode, int lower_val, int upper_val, void* opaque) { + // Cleanup before performing callback + libtock_ipc_registry_string_name_set_allow_name(NULL, 0); + libtock_ipc_registry_string_name_set_upcall_discovery_complete(NULL, NULL); + + libtock_ipc_registry_string_name_discovery_complete_callback cb = + (libtock_ipc_registry_string_name_discovery_complete_callback)opaque; + uint32_t lower = (uint32_t)lower_val; + uint32_t upper = (uint32_t)upper_val; + uint64_t ipc_id = (((uint64_t)upper) << 32) | ((uint64_t)lower); + cb(tock_status_to_returncode(statuscode), ipc_id); +} + +bool libtock_ipc_registry_string_name_exists(void) { + return libtock_ipc_registry_string_name_driver_exists(); +} + +returncode_t libtock_ipc_registry_string_name_register_service( + libtock_ipc_registry_string_name_registration_complete_callback cb, + uint8_t* string_name, + size_t string_name_len) { + // Copy data to local buffer if possible, which is fixed in size and zero-padded + if (string_name_len > STRING_BUF_LEN) { + return RETURNCODE_ESIZE; + } + memset(string_buf, 0, STRING_BUF_LEN); + for (size_t i = 0; i < STRING_BUF_LEN && i < string_name_len; i++) { + string_buf[i] = string_name[i]; + } + + returncode_t err = RETURNCODE_SUCCESS; + err = libtock_ipc_registry_string_name_set_allow_name(string_buf, STRING_BUF_LEN); + if (err != RETURNCODE_SUCCESS) return err; + + err = libtock_ipc_registry_string_name_set_upcall_registration_complete(registration_complete_upcall, cb); + if (err != RETURNCODE_SUCCESS) return err; + + err = libtock_ipc_registry_string_name_command_register(); + return err; +} + +returncode_t libtock_ipc_registry_string_name_discover_service( + libtock_ipc_registry_string_name_discovery_complete_callback cb, + uint8_t* string_name, + size_t string_name_len) { + + // Copy data to local buffer if possible, which is fixed in size and zero-padded + if (string_name_len > STRING_BUF_LEN) { + return RETURNCODE_ESIZE; + } + memset(string_buf, 0, STRING_BUF_LEN); + for (size_t i = 0; i < STRING_BUF_LEN && i < string_name_len; i++) { + string_buf[i] = string_name[i]; + } + + returncode_t err = RETURNCODE_SUCCESS; + err = libtock_ipc_registry_string_name_set_allow_name(string_buf, STRING_BUF_LEN); + if (err != RETURNCODE_SUCCESS) return err; + + err = libtock_ipc_registry_string_name_set_upcall_discovery_complete(discovery_complete_upcall, cb); + if (err != RETURNCODE_SUCCESS) return err; + + err = libtock_ipc_registry_string_name_command_discover(); + return err; +} diff --git a/libtock/ipc/ipc_registry_string_name.h b/libtock/ipc/ipc_registry_string_name.h new file mode 100644 index 000000000..381f540c2 --- /dev/null +++ b/libtock/ipc/ipc_registry_string_name.h @@ -0,0 +1,62 @@ +#pragma once + +#include "../tock.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// Function signature for registration complete callback. +// +// - `arg1` (`returncode_t`): Status from registration. +typedef void (*libtock_ipc_registry_string_name_registration_complete_callback)(returncode_t); + +// Function signature for discovery complete callback. +// +// - `arg1` (`returncode_t`): Status from discovery. +// - `arg2` (`uint64_t`): IPC identifier for discovered service. +typedef void (*libtock_ipc_registry_string_name_discovery_complete_callback)(returncode_t, uint64_t); + + +// Check if the driver exists. +bool libtock_ipc_registry_string_name_exists(void); + +// Register as a service using a string name. +// +// If successful, the callback will be received when registration has completed. +// +// ## Arguments +// +// `cb`: Callback function triggered when registration is completed. +// `string_name`: Buffer of uint8_t values to use as service name +// `string_name_len`: Length of buffer. +// +// ## Return value +// +// A returncode indicating whether the registration process started successfully. +returncode_t libtock_ipc_registry_string_name_register_service( + libtock_ipc_registry_string_name_registration_complete_callback cb, + uint8_t* string_name, + size_t string_name_len); + +// Discover a service using a string name. +// +// If successful, the callback will be triggered when discovery has completed. +// +// ## Arguments +// +// `cb`: Callback function triggered when registration is completed. +// `string_name`: Buffer of uint8_t values to use as service name +// `string_name_len`: Length of buffer. +// +// ## Return value +// +// A returncode indicating whether the registration process started successfully. +returncode_t libtock_ipc_registry_string_name_discover_service( + libtock_ipc_registry_string_name_discovery_complete_callback cb, + uint8_t* string_name, + size_t string_name_len); + +#ifdef __cplusplus +} +#endif diff --git a/libtock/ipc/ipc_relay_request.c b/libtock/ipc/ipc_relay_request.c new file mode 100644 index 000000000..0401f7561 --- /dev/null +++ b/libtock/ipc/ipc_relay_request.c @@ -0,0 +1,144 @@ +#include "ipc_relay_request.h" + +#include "syscalls/ipc_relay_request_syscalls.h" + +// callback for client, response received +static void client_response_received(int statuscode, int len, + __attribute__ ((unused)) int unused, + void* opaque) { + // Cleanup before performing callback + libtock_ipc_relay_request_set_allow_read_buffer(NULL, 0); + libtock_ipc_relay_request_set_allow_write_buffer(NULL, 0); + libtock_ipc_relay_request_set_upcall_client_response_received(NULL, NULL); + + libtock_ipc_relay_request_client_response_received cb = (libtock_ipc_relay_request_client_response_received)opaque; + cb(tock_status_to_returncode(statuscode), (size_t)((uint32_t)len)); +} + +// callback for server, request waiting +static void server_request_waiting(int lower_val, int upper_val, + __attribute__ ((unused)) int unused, + void* opaque) { + libtock_ipc_relay_request_server_request_waiting cb = (libtock_ipc_relay_request_server_request_waiting)opaque; + uint32_t lower = (uint32_t)lower_val; + uint32_t upper = (uint32_t)upper_val; + uint64_t ipc_id = (((uint64_t)upper) << 32) | ((uint64_t)lower); + cb(ipc_id); +} + +bool libtock_ipc_relay_request_exists(void) { + return libtock_ipc_relay_request_driver_exists(); +} + +returncode_t libtock_ipc_relay_request_client_send_request( + libtock_ipc_relay_request_client_response_received cb, + uint64_t ipc_id, + uint8_t* request_buf, size_t request_len, + uint8_t* response_buf, size_t response_len) { + returncode_t err = RETURNCODE_SUCCESS; + + err = libtock_ipc_relay_request_set_allow_read_buffer(request_buf, request_len); + if (err != RETURNCODE_SUCCESS) return err; + + err = libtock_ipc_relay_request_set_allow_write_buffer(response_buf, response_len); + if (err != RETURNCODE_SUCCESS) return err; + + err = libtock_ipc_relay_request_set_upcall_client_response_received(client_response_received, cb); + if (err != RETURNCODE_SUCCESS) return err; + + err = libtock_ipc_relay_request_command_client_send_request(ipc_id); + return err; +} + +returncode_t libtock_ipc_relay_request_client_cancel_request(void) { + return libtock_ipc_relay_request_command_client_cancel_request(); +} + +returncode_t libtock_ipc_relay_request_server_enable_requests_with_callback( + libtock_ipc_relay_request_server_request_waiting cb) { + returncode_t err = RETURNCODE_SUCCESS; + + err = libtock_ipc_relay_request_set_upcall_server_request_waiting(server_request_waiting, cb); + if (err != RETURNCODE_SUCCESS) return err; + + err = libtock_ipc_relay_request_command_server_enable_requests(); + return err; +} + +returncode_t libtock_ipc_relay_request_server_enable_requests_without_callback(void) { + return libtock_ipc_relay_request_command_server_enable_requests(); +} + +returncode_t libtock_ipc_relay_request_server_disable_requests(void) { \ + returncode_t err = RETURNCODE_SUCCESS; + + err = libtock_ipc_relay_request_command_server_disable_requests(); + if (err != RETURNCODE_SUCCESS) return err; + + err = libtock_ipc_relay_request_set_upcall_server_request_waiting(NULL, NULL); + return err; +} + +returncode_t libtock_ipc_relay_request_server_get_next_request( + uint64_t* ipc_id, + uint8_t* request_buf, size_t* request_len) { + returncode_t err = RETURNCODE_SUCCESS; + + err = libtock_ipc_relay_request_set_allow_write_buffer(request_buf, *request_len); + if (err != RETURNCODE_SUCCESS) return err; + + size_t max_request_len = *request_len; + err = libtock_ipc_relay_request_command_server_get_next_request((uint32_t*)request_len, ipc_id); + if (err == RETURNCODE_ESIZE) { + // Request buffer size was too small, up to maximum was copied + *request_len = max_request_len; + } else if (err != RETURNCODE_SUCCESS) { + // Error condition, no data is in the buffer + *request_len = 0; + } + + // Cleanup + libtock_ipc_relay_request_set_allow_write_buffer(NULL, 0); + + return err; +} + +returncode_t libtock_ipc_relay_request_server_get_next_request_from( + uint64_t ipc_id, + uint8_t* request_buf, size_t* request_len) { + returncode_t err = RETURNCODE_SUCCESS; + + err = libtock_ipc_relay_request_set_allow_write_buffer(request_buf, *request_len); + if (err != RETURNCODE_SUCCESS) return err; + + uint64_t ipc_id_copy = ipc_id; + size_t max_request_len = *request_len; + err = libtock_ipc_relay_request_command_server_get_next_request_from((uint32_t*)request_len, &ipc_id_copy); + if (err == RETURNCODE_ESIZE) { + // Request buffer size was too small, up to maximum was copied + *request_len = max_request_len; + } else if (err != RETURNCODE_SUCCESS) { + // Error condition, no data is in the buffer + *request_len = 0; + } + + // Cleanup + libtock_ipc_relay_request_set_allow_write_buffer(NULL, 0); + + return err; +} + +returncode_t libtock_ipc_relay_request_server_send_response( + uint8_t* response_buf, size_t response_len) { + returncode_t err = RETURNCODE_SUCCESS; + + err = libtock_ipc_relay_request_set_allow_read_buffer(response_buf, response_len); + if (err != RETURNCODE_SUCCESS) return err; + + err = libtock_ipc_relay_request_command_server_send_response(); + + // Cleanup + libtock_ipc_relay_request_set_allow_read_buffer(NULL, 0); + + return err; +} diff --git a/libtock/ipc/ipc_relay_request.h b/libtock/ipc/ipc_relay_request.h new file mode 100644 index 000000000..8fa4156b9 --- /dev/null +++ b/libtock/ipc/ipc_relay_request.h @@ -0,0 +1,151 @@ +#pragma once + +#include "../tock.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// Function signature for clients, response received. +// +// - `arg1` (`returncode_t`): Status for response +// - `arg2` (`size_t`): Length of response +typedef void (*libtock_ipc_relay_request_client_response_received)(returncode_t, size_t); + +// Function signature for servers, request waiting. +// +// - `arg1` (`uint64_t`): IPC identifier for waiting request +typedef void (*libtock_ipc_relay_request_server_request_waiting)(uint64_t); + + +// Check if the driver exists. +// +// ## Return value +// +// True if the driver exists. +bool libtock_ipc_relay_request_exists(void); + +// For clients, send a request to a service. +// +// If successful, the callback will be received when the transaction is +// completed. +// +// ## Arguments +// +// `cb`: Callback function triggered when the transaction is completed. +// `ipc_id`: IPC identifier of service to send request to. +// `request_buf`: Buffer of uint8_t values to send to the service. +// `request_len`: Length of request buffer. +// `response_buf`: Buffer of uint8_t values to hold response from service. +// `response_len`: Length of response buffer. +// +// ## Return value +// +// A returncode indicating whether the request has been started successfully. +returncode_t libtock_ipc_relay_request_client_send_request( + libtock_ipc_relay_request_client_response_received cb, + uint64_t ipc_id, + uint8_t* request_buf, size_t request_len, + uint8_t* response_buf, size_t response_len); + +// For clients, cancel an outstanding request. +// +// ## Return value +// +// A returncode indicating whether the request has been canceled successfully. +returncode_t libtock_ipc_relay_request_client_cancel_request(void); + +// For servers, enable requests and callbacks when new requests are waiting for +// this server. +// +// If successful, the callback will be triggered when any future request becomes +// available for this server. +// +// ## Arguments +// +// `cb`: Callback function triggered when a new request is ready +// +// ## Return value +// +// A returncode indicating whether the callback was installed successfully. +returncode_t libtock_ipc_relay_request_server_enable_requests_with_callback( + libtock_ipc_relay_request_server_request_waiting cb); + +// For servers, enable requests without registering for callbacks. +// +// Without callbacks, the server will need to periodically get_next_request to +// see if any requests are waiting for it. +// +// ## Return value +// +// A returncode indicating whether the callback was installed successfully. +returncode_t libtock_ipc_relay_request_server_enable_requests_without_callback(void); + +// For servers, disable request waiting callbacks. +// +// ## Return value +// +// A returncode indicating whether the callback was removed successfully. +returncode_t libtock_ipc_relay_request_server_disable_requests(void); + +// For servers, get the next waiting request (if any exist). +// +// A request can only be retrieved if no outstanding request is in progress. +// Once retrieved, the request will remain in progress until a response is sent. +// +// ## Arguments +// +// `ipc_id`: Pointer to location to hold the the IPC identifier for the found request. +// `request_buf`: Buffer of uint8_t values to hold request from client. +// `request_len`: Length of request buffer. This will be overwritten as the +// length of the message stored in the request buffer (<= original maximum length) +// +// ## Return value +// +// A returncode indicating whether a request existed and was successfully +// retrieved. If no request was retrieved, `ipc_id` is invalid and +// `request_len` will be set to zero. +returncode_t libtock_ipc_relay_request_server_get_next_request( + uint64_t* ipc_id, + uint8_t* request_buf, size_t* request_len); + +// For servers, get the next waiting request from a specified client (if it exists). +// +// A request can only be retrieved if no outstanding request is in progress. +// Once retrieved, the request will remain in progress until a response is sent. +// +// ## Arguments +// +// `ipc_id`: The IPC identifier of the client to get a request from. +// `request_buf`: Buffer of uint8_t values to hold request from client. +// `request_len`: Pointer to length of request buffer. This will be overwritten as the +// length of the message stored in the request buffer (<= original maximum length) +// +// ## Return value +// +// A returncode indicating whether a request existed and was successfully +// retrieved. If no request was retrieved, `request_len` will be set to zero. +returncode_t libtock_ipc_relay_request_server_get_next_request_from( + uint64_t ipc_id, + uint8_t* request_buf, size_t* request_len); + +// For servers, send a response to the client you got a request from. +// +// The request MUST first be retrieved with one of the `get_next_request` +// functions. The response will go to the client corresponding to that request. +// +// ## Arguments +// +// `response_buf`: Buffer of uint8_t values to send to the client. +// `response_len`: Length of response buffer. +// +// ## Return value +// +// A returncode indicating if the response was successfully sent. +returncode_t libtock_ipc_relay_request_server_send_response( + uint8_t* response_buf, size_t response_len); + + +#ifdef __cplusplus +} +#endif diff --git a/libtock/ipc/syscalls/ipc_registry_package_name_syscalls.c b/libtock/ipc/syscalls/ipc_registry_package_name_syscalls.c new file mode 100644 index 000000000..9cde9c01e --- /dev/null +++ b/libtock/ipc/syscalls/ipc_registry_package_name_syscalls.c @@ -0,0 +1,31 @@ +#include "ipc_registry_package_name_syscalls.h" + +bool libtock_ipc_registry_package_name_driver_exists(void) { + return driver_exists(DRIVER_NUM_IPC_REGISTRY_PACKAGE_NAME); +} + +returncode_t libtock_ipc_registry_package_name_set_upcall_registration_complete(subscribe_upcall callback, + void* opaque) { + subscribe_return_t sval = subscribe(DRIVER_NUM_IPC_REGISTRY_PACKAGE_NAME, 0, callback, opaque); + return tock_subscribe_return_to_returncode(sval); +} + +returncode_t libtock_ipc_registry_package_name_set_upcall_discovery_complete(subscribe_upcall callback, void* opaque) { + subscribe_return_t sval = subscribe(DRIVER_NUM_IPC_REGISTRY_PACKAGE_NAME, 1, callback, opaque); + return tock_subscribe_return_to_returncode(sval); +} + +returncode_t libtock_ipc_registry_package_name_set_allow_name(void* data, size_t len) { + allow_ro_return_t aval = allow_readonly(DRIVER_NUM_IPC_REGISTRY_PACKAGE_NAME, 0, data, len); + return tock_allow_ro_return_to_returncode(aval); +} + +returncode_t libtock_ipc_registry_package_name_command_register(void) { + syscall_return_t cval = command(DRIVER_NUM_IPC_REGISTRY_PACKAGE_NAME, 1, 0, 0); + return tock_command_return_novalue_to_returncode(cval); +} + +returncode_t libtock_ipc_registry_package_name_command_discover(void) { + syscall_return_t cval = command(DRIVER_NUM_IPC_REGISTRY_PACKAGE_NAME, 2, 0, 0); + return tock_command_return_novalue_to_returncode(cval); +} diff --git a/libtock/ipc/syscalls/ipc_registry_package_name_syscalls.h b/libtock/ipc/syscalls/ipc_registry_package_name_syscalls.h new file mode 100644 index 000000000..a53e2a4c0 --- /dev/null +++ b/libtock/ipc/syscalls/ipc_registry_package_name_syscalls.h @@ -0,0 +1,32 @@ +#pragma once + +#include "../../tock.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define DRIVER_NUM_IPC_REGISTRY_PACKAGE_NAME 0xA0001 + +// Check if the ipc registry string name driver exists. +bool libtock_ipc_registry_package_name_driver_exists(void); + +// Configure the upcall when registration completes. +returncode_t libtock_ipc_registry_package_name_set_upcall_registration_complete(subscribe_upcall callback, + void* opaque); + +// Configure the upcall when discovery completes. +returncode_t libtock_ipc_registry_package_name_set_upcall_discovery_complete(subscribe_upcall callback, void* opaque); + +// Allow a buffer containing a name for discovery. +returncode_t libtock_ipc_registry_package_name_set_allow_name(void* data, size_t len); + +// Register service. +returncode_t libtock_ipc_registry_package_name_command_register(void); + +// Discover service. +returncode_t libtock_ipc_registry_package_name_command_discover(void); + +#ifdef __cplusplus +} +#endif diff --git a/libtock/ipc/syscalls/ipc_registry_string_name_syscalls.c b/libtock/ipc/syscalls/ipc_registry_string_name_syscalls.c new file mode 100644 index 000000000..d16b37fc4 --- /dev/null +++ b/libtock/ipc/syscalls/ipc_registry_string_name_syscalls.c @@ -0,0 +1,31 @@ +#include "ipc_registry_string_name_syscalls.h" + +bool libtock_ipc_registry_string_name_driver_exists(void) { + return driver_exists(DRIVER_NUM_IPC_REGISTRY_STRING_NAME); +} + +returncode_t libtock_ipc_registry_string_name_set_upcall_registration_complete(subscribe_upcall callback, + void* opaque) { + subscribe_return_t sval = subscribe(DRIVER_NUM_IPC_REGISTRY_STRING_NAME, 0, callback, opaque); + return tock_subscribe_return_to_returncode(sval); +} + +returncode_t libtock_ipc_registry_string_name_set_upcall_discovery_complete(subscribe_upcall callback, void* opaque) { + subscribe_return_t sval = subscribe(DRIVER_NUM_IPC_REGISTRY_STRING_NAME, 1, callback, opaque); + return tock_subscribe_return_to_returncode(sval); +} + +returncode_t libtock_ipc_registry_string_name_set_allow_name(void* data, size_t len) { + allow_ro_return_t aval = allow_readonly(DRIVER_NUM_IPC_REGISTRY_STRING_NAME, 0, data, len); + return tock_allow_ro_return_to_returncode(aval); +} + +returncode_t libtock_ipc_registry_string_name_command_register(void) { + syscall_return_t cval = command(DRIVER_NUM_IPC_REGISTRY_STRING_NAME, 1, 0, 0); + return tock_command_return_novalue_to_returncode(cval); +} + +returncode_t libtock_ipc_registry_string_name_command_discover(void) { + syscall_return_t cval = command(DRIVER_NUM_IPC_REGISTRY_STRING_NAME, 2, 0, 0); + return tock_command_return_novalue_to_returncode(cval); +} diff --git a/libtock/ipc/syscalls/ipc_registry_string_name_syscalls.h b/libtock/ipc/syscalls/ipc_registry_string_name_syscalls.h new file mode 100644 index 000000000..ed3623e3b --- /dev/null +++ b/libtock/ipc/syscalls/ipc_registry_string_name_syscalls.h @@ -0,0 +1,31 @@ +#pragma once + +#include "../../tock.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define DRIVER_NUM_IPC_REGISTRY_STRING_NAME 0xA0000 + +// Check if the ipc registry string name driver exists. +bool libtock_ipc_registry_string_name_driver_exists(void); + +// Configure the upcall when registration completes. +returncode_t libtock_ipc_registry_string_name_set_upcall_registration_complete(subscribe_upcall callback, void* opaque); + +// Configure the upcall when discovery completes. +returncode_t libtock_ipc_registry_string_name_set_upcall_discovery_complete(subscribe_upcall callback, void* opaque); + +// Allow a buffer containing a name for registration or discovery. +returncode_t libtock_ipc_registry_string_name_set_allow_name(void* data, size_t len); + +// Register service. +returncode_t libtock_ipc_registry_string_name_command_register(void); + +// Discover service. +returncode_t libtock_ipc_registry_string_name_command_discover(void); + +#ifdef __cplusplus +} +#endif diff --git a/libtock/ipc/syscalls/ipc_relay_request_syscalls.c b/libtock/ipc/syscalls/ipc_relay_request_syscalls.c new file mode 100644 index 000000000..dca5a5a7e --- /dev/null +++ b/libtock/ipc/syscalls/ipc_relay_request_syscalls.c @@ -0,0 +1,74 @@ +#include "ipc_relay_request_syscalls.h" + +bool libtock_ipc_relay_request_driver_exists(void) { + return driver_exists(DRIVER_NUM_IPC_RELAY_REQUEST); +} + +returncode_t libtock_ipc_relay_request_command_client_send_request(uint64_t ipc_id) { + uint32_t lower = (uint32_t)ipc_id; + uint32_t upper = (uint32_t)(ipc_id >> 32); + syscall_return_t cval = command(DRIVER_NUM_IPC_RELAY_REQUEST, 0x10, lower, upper); + return tock_command_return_novalue_to_returncode(cval); +} + +returncode_t libtock_ipc_relay_request_command_client_cancel_request(void) { + syscall_return_t cval = command(DRIVER_NUM_IPC_RELAY_REQUEST, 0x11, 0, 0); + return tock_command_return_novalue_to_returncode(cval); +} + +returncode_t libtock_ipc_relay_request_command_server_enable_requests(void) { + syscall_return_t cval = command(DRIVER_NUM_IPC_RELAY_REQUEST, 0x20, 0, 0); + return tock_command_return_novalue_to_returncode(cval); +} + +returncode_t libtock_ipc_relay_request_command_server_disable_requests(void) { + syscall_return_t cval = command(DRIVER_NUM_IPC_RELAY_REQUEST, 0x21, 0, 0); + return tock_command_return_novalue_to_returncode(cval); +} + +returncode_t libtock_ipc_relay_request_command_server_get_next_request(uint32_t* len, uint64_t* ipc_id) { + syscall_return_t cval = command(DRIVER_NUM_IPC_RELAY_REQUEST, 0x22, 0, 0); + + // We have two options here: + // * success returns length and ipc_id + // * failure returns just ipc_id, with length either max or irrelevant + // depending on the returncode, which should be handled by higher layers + return tock_command_return_u32_u64_or_u64_to_returncode(cval, len, ipc_id); +} + +returncode_t libtock_ipc_relay_request_command_server_get_next_request_from(uint32_t* len, uint64_t* ipc_id) { + uint32_t lower = (uint32_t)(*ipc_id); + uint32_t upper = (uint32_t)((*ipc_id) >> 32); + syscall_return_t cval = command(DRIVER_NUM_IPC_RELAY_REQUEST, 0x23, lower, upper); + + // We have two options here: + // * success returns length and ipc_id + // * failure returns just ipc_id, with length either max or irrelevant + // depending on the returncode, which should be handled by higher layers + return tock_command_return_u32_u64_or_u64_to_returncode(cval, len, ipc_id); +} + +returncode_t libtock_ipc_relay_request_command_server_send_response(void) { + syscall_return_t cval = command(DRIVER_NUM_IPC_RELAY_REQUEST, 0x24, 0, 0); + return tock_command_return_novalue_to_returncode(cval); +} + +returncode_t libtock_ipc_relay_request_set_upcall_client_response_received(subscribe_upcall callback, void* opaque) { + subscribe_return_t sval = subscribe(DRIVER_NUM_IPC_RELAY_REQUEST, 0, callback, opaque); + return tock_subscribe_return_to_returncode(sval); +} + +returncode_t libtock_ipc_relay_request_set_upcall_server_request_waiting(subscribe_upcall callback, void* opaque) { + subscribe_return_t sval = subscribe(DRIVER_NUM_IPC_RELAY_REQUEST, 1, callback, opaque); + return tock_subscribe_return_to_returncode(sval); +} + +returncode_t libtock_ipc_relay_request_set_allow_read_buffer(void* data, size_t len) { + allow_ro_return_t aval = allow_readonly(DRIVER_NUM_IPC_RELAY_REQUEST, 0, data, len); + return tock_allow_ro_return_to_returncode(aval); +} + +returncode_t libtock_ipc_relay_request_set_allow_write_buffer(void* data, size_t len) { + allow_rw_return_t aval = allow_readwrite(DRIVER_NUM_IPC_RELAY_REQUEST, 0, data, len); + return tock_allow_rw_return_to_returncode(aval); +} diff --git a/libtock/ipc/syscalls/ipc_relay_request_syscalls.h b/libtock/ipc/syscalls/ipc_relay_request_syscalls.h new file mode 100644 index 000000000..c58d0214b --- /dev/null +++ b/libtock/ipc/syscalls/ipc_relay_request_syscalls.h @@ -0,0 +1,39 @@ +#pragma once + +#include "../../tock.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define DRIVER_NUM_IPC_RELAY_REQUEST 0xA0010 + +bool libtock_ipc_relay_request_driver_exists(void); + + +returncode_t libtock_ipc_relay_request_command_client_send_request(uint64_t ipc_id); + +returncode_t libtock_ipc_relay_request_command_client_cancel_request(void); + +returncode_t libtock_ipc_relay_request_command_server_enable_requests(void); + +returncode_t libtock_ipc_relay_request_command_server_disable_requests(void); + +returncode_t libtock_ipc_relay_request_command_server_get_next_request(uint32_t* len, uint64_t* ipc_id); + +returncode_t libtock_ipc_relay_request_command_server_get_next_request_from(uint32_t* len, uint64_t* ipc_id); + +returncode_t libtock_ipc_relay_request_command_server_send_response(void); + +returncode_t libtock_ipc_relay_request_set_upcall_client_response_received(subscribe_upcall callback, void* opaque); + +returncode_t libtock_ipc_relay_request_set_upcall_server_request_waiting(subscribe_upcall callback, void* opaque); + +returncode_t libtock_ipc_relay_request_set_allow_read_buffer(void* data, size_t len); + +returncode_t libtock_ipc_relay_request_set_allow_write_buffer(void* data, size_t len); + + +#ifdef __cplusplus +} +#endif diff --git a/libtock/tock.c b/libtock/tock.c index c74a71906..4dc288b31 100644 --- a/libtock/tock.c +++ b/libtock/tock.c @@ -74,6 +74,44 @@ returncode_t tock_command_return_u32_u32_to_returncode(syscall_return_t command_ } } +returncode_t tock_command_return_u32_u64_or_u64_to_returncode(syscall_return_t command_return, uint32_t* val1, + uint64_t* val2) { + // The success and failure cases here are not symmetric. Two options: + // Success with u32 and u64 + // Failure with u64 + if (command_return.type == TOCK_SYSCALL_SUCCESS_U32_U64) { + // Success case: u32 and u64 + *val1 = command_return.data[0]; +#if defined(__riscv) && __riscv_xlen == 64 + // TRD-RISCV64BIT + *val2 = command_return.data[1]; +#else + // TRD104 + uint32_t lsb = command_return.data[1]; + uint32_t msb = command_return.data[2]; + *val2 = (((uint64_t)msb) << 32) | ((uint64_t)lsb); +#endif + return RETURNCODE_SUCCESS; + } else if (command_return.type == TOCK_SYSCALL_FAILURE_U64) { + // Failure case: error and u64 +#if defined(__riscv) && __riscv_xlen == 64 + // TRD-RISCV64BIT + *val2 = command_return.data[1]; +#else + // TRD104 + uint32_t lsb = command_return.data[1]; + uint32_t msb = command_return.data[2]; + *val2 = (((uint64_t)msb) << 32) | ((uint64_t)lsb); +#endif + return tock_status_to_returncode(command_return.data[0]); + } else { + // The remaining SyscallReturn variants must never happen if using this + // function. We return `EBADRVAL` to signal an unexpected return variant. + return RETURNCODE_EBADRVAL; + } +} + + returncode_t tock_subscribe_return_to_returncode(subscribe_return_t subscribe_return) { // If the subscribe was successful, easily return SUCCESS. if (subscribe_return.success) { diff --git a/libtock/tock.h b/libtock/tock.h index c58db2813..44545b5a3 100644 --- a/libtock/tock.h +++ b/libtock/tock.h @@ -177,6 +177,16 @@ returncode_t tock_command_return_u32_to_returncode(syscall_return_t, uint32_t*); // variants. returncode_t tock_command_return_u32_u32_to_returncode(syscall_return_t, uint32_t*, uint32_t*); +// Convert a `syscall_return_t` that succeeds with one `u32` and one `u64` +// values, and fails with one `u64`. +// +// In the failure case, the `u32` argument will not be modified. +// +// This expects one of two cases `TOCK_SYSCALL_SUCCESS_U32_U64` or +// `TOCK_SYSCALL_FAILURE_U64` to be returned. Do not use with other expected +// SyscallReturn variants. +returncode_t tock_command_return_u32_u64_or_u64_to_returncode(syscall_return_t, uint32_t*, uint64_t*); + // Convert a `syscall_return_t` with a `u64` value to a `returncode_t`. returncode_t tock_command_return_u64_to_returncode(syscall_return_t command_return, uint64_t* val);