diff --git a/include/libhexagonrpc/fastrpc.h b/include/libhexagonrpc/fastrpc.h index 69f0d17..e581a91 100644 --- a/include/libhexagonrpc/fastrpc.h +++ b/include/libhexagonrpc/fastrpc.h @@ -23,6 +23,7 @@ #define FASTRPC_H #include +#include #include #include @@ -41,6 +42,8 @@ #define REMOTE_SCALARS_INBUFS(sc) (((sc) >> 16) & 0xff) #define REMOTE_SCALARS_OUTBUFS(sc) (((sc) >> 8) & 0xff) +#define HEXAGONRPC_DELIMITER 0xFFFFFFFF + struct fastrpc_context { int fd; uint32_t handle; @@ -54,6 +57,13 @@ struct fastrpc_function_def_interp2 { uint8_t out_bufs; }; +struct hrpc_method_def_interp3 { + uint32_t msg_id; + bool has_prim_out; + size_t n_args; + const uint32_t *args; +}; + struct fastrpc_context *fastrpc_create_context(int fd, uint32_t handle); static inline void fastrpc_destroy_context(struct fastrpc_context *ctx) @@ -70,4 +80,14 @@ int fastrpc2(const struct fastrpc_function_def_interp2 *def, int fastrpc(const struct fastrpc_function_def_interp2 *def, const struct fastrpc_context *ctx, ...); +int vhexagonrpc2(const struct hrpc_method_def_interp3 *def, + int fd, uint32_t handle, va_list list); +int vhexagonrpc(const struct hrpc_method_def_interp3 *def, + const struct fastrpc_context *ctx, va_list list); +int hexagonrpc2(const struct hrpc_method_def_interp3 *def, + int fd, uint32_t handle, ...); +int hexagonrpc(const struct hrpc_method_def_interp3 *def, + const struct fastrpc_context *ctx, ...); + + #endif diff --git a/include/libhexagonrpc/interface.h b/include/libhexagonrpc/interface.h index 0b6700f..c7a0af2 100644 --- a/include/libhexagonrpc/interface.h +++ b/include/libhexagonrpc/interface.h @@ -36,6 +36,12 @@ outnums, outbufs) \ extern const struct fastrpc_function_def_interp2 name##_def; +#define HEXAGONRPC_DEFINE_REMOTE_METHOD3_EMPTY(mid, name) \ + extern const struct hrpc_method_def_interp3 name##_def; + +#define HEXAGONRPC_DEFINE_REMOTE_METHOD3(mid, name, has_oprim, ...) \ + HEXAGONRPC_DEFINE_REMOTE_METHOD3_EMPTY(mid, name) + #else /* HEXAGONRPC_BUILD_METHOD_DEFINITIONS */ #define HEXAGONRPC_DEFINE_REMOTE_METHOD(mid, name, \ @@ -49,6 +55,23 @@ .out_bufs = outbufs, \ }; +#define HEXAGONRPC_DEFINE_REMOTE_METHOD3_EMPTY(mid, name) \ + const struct hrpc_method_def_interp3 name##_def = { \ + .msg_id = mid, \ + .has_prim_out = false, \ + .n_args = 0, \ + .args = NULL, \ + }; + +#define HEXAGONRPC_DEFINE_REMOTE_METHOD3(mid, name, has_oprim, ...) \ + static const uint32_t name##_args[] = { __VA_ARGS__ }; \ + const struct hrpc_method_def_interp3 name##_def = { \ + .msg_id = mid, \ + .has_prim_out = has_oprim, \ + .n_args = sizeof(name##_args) / sizeof(*name##_args), \ + .args = name##_args, \ + }; + #endif /* HEXAGONRPC_BUILD_METHOD_DEFINITIONS */ #endif /* LIBHEXAGONRPC_INTERFACE_H */ diff --git a/libhexagonrpc/context.c b/libhexagonrpc/context.c index c3fa204..646ab4f 100644 --- a/libhexagonrpc/context.c +++ b/libhexagonrpc/context.c @@ -54,3 +54,21 @@ int fastrpc(const struct fastrpc_function_def_interp2 *def, return ret; } +int vhexagonrpc(const struct hrpc_method_def_interp3 *def, + const struct fastrpc_context *ctx, va_list arg_list) +{ + return vhexagonrpc2(def, ctx->fd, ctx->handle, arg_list); +} + +int hexagonrpc(const struct hrpc_method_def_interp3 *def, + const struct fastrpc_context *ctx, ...) +{ + va_list arg_list; + int ret; + + va_start(arg_list, ctx); + ret = vhexagonrpc(def, ctx, arg_list); + va_end(arg_list); + + return ret; +} diff --git a/libhexagonrpc/fastrpc.c b/libhexagonrpc/fastrpc.c index cee9323..420543a 100644 --- a/libhexagonrpc/fastrpc.c +++ b/libhexagonrpc/fastrpc.c @@ -250,3 +250,221 @@ int fastrpc2(const struct fastrpc_function_def_interp2 *def, return ret; } + +/* + * This populates relevant arguments with information necessary to receive + * output from the remote processor. + * + * First, it allocates a new first output buffer to contain the returned 32-bit + * integers. This output buffer must be freed after use. + * + * With a peek at the output arguments, it populates the fastrpc_invoke_args + * struct to give information about the provided buffers to the kernel, and + * adds an entry to the first input buffer to tell the remote processor the + * size of the function-level output buffer. + */ +static void prepare_outbufs3(const struct hrpc_method_def_interp3 *def, + struct fastrpc_invoke_args *args, + size_t delim, + uint32_t *n_prim_in, uint8_t *n_outbufs, + uint32_t *prim, + va_list peek) +{ + uint32_t n_prim_out = 0; + size_t i; + + if (def->has_prim_out) + *n_outbufs = 1; + else + *n_outbufs = 0; + + for (i = delim; i < def->n_args; i++) { + if (def->args[i]) { + prim[*n_prim_in] = va_arg(peek, uint32_t); + + args[*n_outbufs].length = prim[*n_prim_in] * def->args[i]; + args[*n_outbufs].ptr = (__u64) va_arg(peek, void *); + args[*n_outbufs].fd = -1; + + (*n_prim_in)++; + (*n_outbufs)++; + } else { + va_arg(peek, uint32_t *); + n_prim_out++; + } + } + + if (def->has_prim_out) { + args[0].length = sizeof(uint32_t) * n_prim_out; + args[0].ptr = (__u64) &prim[*n_prim_in]; + args[0].fd = -1; + } +} + +static void return_prim_out(const struct hrpc_method_def_interp3 *def, + size_t delim, + const uint32_t *prim_out, + va_list list) +{ + uint32_t *ptr; + size_t i, j = 0; + + for (i = delim; i < def->n_args; i++) { + if (def->args[i]) { + va_arg(list, uint32_t); + va_arg(list, void *); + } else { + ptr = va_arg(list, uint32_t *); + *ptr = prim_out[j]; + j++; + } + } +} + +/* + * This is the main function to invoke a FastRPC procedure call. The first + * parameter specifies how to populate the ioctl-level buffers. The second and + * third parameters specify where to send the invocation to. The fourth is the + * list of arguments that the procedure call should interact with. + * + * A good example for this would be the adsp_listener_next2 call: + * + * static inline void adsp_listener_next2(int fd, uint32_t handle, + * uint32_t prev_ctx, + * uint32_t prev_result, + * uint32_t nested_outbufs_len, + * void *nested_outbufs, + * uint32_t *ctx, + * uint32_t *nested_handle, + * uint32_t *nested_sc, + * uint32_t *nested_inbufs_len, + * uint32_t nested_inbufs_size, + * void *nested_inbufs) + * { + * uint32_t args[] = { 0, 0, 1, HEXAGONRPC_DELIMITER, 0, 0, 0, 1 }; + * struct hrpc_method_def_interp3 def = { + * .mid = 4, + * .has_out_prim = true, + * .n_args = 8, + * .args = args, + * }; + * return vhexagonrpc2(def, fd, handle, + * prev_ctx, + * prev_result, + * nested_outbufs_len, + * nested_outbufs, + * ctx, + * nested_handle, + * nested_sc, + * nested_inbufs_len, + * nested_inbufs_size, + * nested_inbufs); + * } + */ +int vhexagonrpc2(const struct hrpc_method_def_interp3 *def, + int fd, uint32_t handle, va_list list) +{ + va_list peek; + struct fastrpc_invoke invoke; + struct fastrpc_invoke_args *args; + uint32_t *prim; + uint32_t n_prim_in = 0; + uint32_t msg_id; + uint8_t n_inbufs = 1, n_outbufs; + size_t i; + int ret = -1; + + /* + * If there are only input buffers specified in the method definition, + * we need an extra primary input argument for their sizes. + */ + args = malloc(sizeof(struct fastrpc_invoke_args) * (def->n_args + 1)); + if (args == NULL) + return -1; + + /* + * The input and output buffers need a size in the primary input buffer + * so all arguments except the optional delimiter contribute to the + * primary buffers. + */ + prim = malloc(sizeof(uint32_t) * (def->n_args + 1)); + if (prim == NULL) + goto err_free_args; + + /* + * The apps_std interface demonstrates that there is support + * for more than 32 methods for a single interface. Support + * extended method IDs that go beyond 5 bits and the ID + * reserved for this purpose. + */ + if (def->msg_id >= 31) { + prim[n_prim_in] = def->msg_id; + n_prim_in++; + msg_id = 31; + } else { + msg_id = def->msg_id; + } + + for (i = 0; i < def->n_args; i++) { + if (def->args[i] == HEXAGONRPC_DELIMITER) { + i++; + break; + } + + prim[n_prim_in] = va_arg(list, uint32_t); + + if (def->args[i]) { + args[n_inbufs].length = prim[n_prim_in] * def->args[i]; + args[n_inbufs].ptr = (__u64) va_arg(list, const void *); + args[n_inbufs].fd = -1; + n_inbufs++; + } + + n_prim_in++; + } + + va_copy(peek, list); + prepare_outbufs3(def, &args[n_inbufs], i, &n_prim_in, &n_outbufs, prim, peek); + va_end(peek); + + args[0].length = sizeof(uint32_t) * n_prim_in; + args[0].ptr = (__u64) prim; + args[0].fd = -1; + + // Pass the primary input buffer if not empty, otherwise skip it. + if (n_prim_in != 0) { + invoke.args = (__u64) args; + } else { + invoke.args = (__u64) &args[1]; + n_inbufs--; + } + + invoke.handle = handle; + invoke.sc = REMOTE_SCALARS_MAKE(msg_id, n_inbufs, n_outbufs); + + ret = ioctl(fd, FASTRPC_IOCTL_INVOKE, (__u64) &invoke); + if (ret == -1) + goto err_free_prim; + + return_prim_out(def, i, &prim[n_prim_in], list); + +err_free_prim: + free(prim); +err_free_args: + free(args); + + return ret; +} + +int hexagonrpc2(const struct hrpc_method_def_interp3 *def, + int fd, uint32_t handle, ...) +{ + va_list list; + int ret; + + va_start(list, handle); + ret = vhexagonrpc2(def, fd, handle, list); + va_end(list); + + return ret; +}