diff --git a/README.md b/README.md
index f38510f..5a20b66 100644
--- a/README.md
+++ b/README.md
@@ -53,6 +53,7 @@ died.
The following sources were used as reference:
- [https://android.googlesource.com/platform/system/chre](https://android.googlesource.com/platform/system/chre)
+ - [https://android.googlesource.com/platform/external/fastrpc](https://android.googlesource.com/platform/external/fastrpc)
- [https://github.com/calebccff/sensordaemon-re](https://github.com/calebccff/sensordaemon-re)
- [https://gitlab.freedesktop.org/mobile-broadband/libqmi/-/merge\_requests/346](https://gitlab.freedesktop.org/mobile-broadband/libqmi/-/merge_requests/346)
- [https://git.linaro.org/landing-teams/working/qualcomm/fastrpc.git](https://git.linaro.org/landing-teams/working/qualcomm/fastrpc.git)
diff --git a/fastrpc/fastrpc-ctx.c b/fastrpc/fastrpc-ctx.c
new file mode 100644
index 0000000..97d8cbe
--- /dev/null
+++ b/fastrpc/fastrpc-ctx.c
@@ -0,0 +1,54 @@
+/*
+ * FastRPC API Replacement - context-based interface
+ *
+ * Copyright (C) 2022 Richard Acayan
+ *
+ * This file is part of sensh.
+ *
+ * Sensh is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "fastrpc.h"
+
+struct fastrpc_context *fastrpc_create_context(int fd, uint32_t handle)
+{
+ struct fastrpc_context *ctx;
+
+ ctx = malloc(sizeof(*ctx));
+ if (ctx == NULL)
+ return NULL;
+
+ ctx->fd = fd;
+ ctx->handle = handle;
+}
+
+int vfastrpc(const struct fastrpc_function_def_interp1 *def,
+ const struct fastrpc_context *ctx, va_list arg_list)
+{
+ return vfastrpc2(def, ctx->fd, ctx->handle, arg_list);
+}
+
+int fastrpc(const struct fastrpc_function_def_interp1 *def,
+ const struct fastrpc_context *ctx, ...)
+{
+ va_list arg_list;
+ int ret;
+
+ va_start(arg_list, ctx);
+ ret = vfastrpc(def, ctx, arg_list);
+ va_end(arg_list);
+
+ return ret;
+}
+
diff --git a/fastrpc/fastrpc.c b/fastrpc/fastrpc.c
new file mode 100644
index 0000000..14c36bf
--- /dev/null
+++ b/fastrpc/fastrpc.c
@@ -0,0 +1,229 @@
+/*
+ * FastRPC API Replacement
+ *
+ * Copyright (C) 2022 Richard Acayan
+ *
+ * This file is part of sensh.
+ *
+ * Sensh is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include "fastrpc.h"
+
+/*
+ * This returns the amount of ioctl-level input buffers needed to pass a given
+ * amount of 32-bit numbers and variable-size function-level buffers passed to
+ * fastrpc().
+ *
+ * This doesn't just return the number of buffers because fastrpc() generates
+ * the first ioctl-level buffer by itself. It should only provide it if needed,
+ * otherwise the remote processor will get an unexpected argument.
+ *
+ * So, this inline function returns the number of function-level buffers, added
+ * to the amount of first buffers (1), if any.
+ */
+static inline uint8_t io_side_count(uint8_t nums, uint8_t bufs)
+{
+ return bufs + ((nums || bufs) && 1);
+}
+
+static void allocate_first_inbuf(const struct fastrpc_function_def_interp1 *def,
+ struct fastrpc_invoke_args *arg,
+ uint32_t **inbuf)
+{
+ arg->length = sizeof(uint32_t) * (def->in_nums
+ + def->in_bufs
+ + def->out_bufs);
+ arg->ptr = (__u64) (*inbuf = malloc(arg->length));
+ arg->fd = -1;
+}
+
+static void allocate_first_outbuf(const struct fastrpc_function_def_interp1 *def,
+ struct fastrpc_invoke_args *arg,
+ uint32_t **outbuf)
+{
+ arg->length = sizeof(uint32_t) * (def->out_nums + def->out_bufs);
+ arg->ptr = (__u64) (*outbuf = malloc(arg->length));
+ arg->fd = -1;
+}
+
+/*
+ * This populates relevant inputs (in general) 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 and returned function-level buffer sizes. 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 buffer to the kernel, and adds an entry
+ * to the first input buffer to tell the remote processor how large the
+ * function-level output buffer can be.
+ *
+ * This is the only part of the argument processing that can be split off into
+ * a different function because it is operating on a copy of the va_list.
+ * Calling va_arg() on a va_list after the return of a function that already
+ * used it causes undefined behavior.
+ */
+static void prepare_outbufs(const struct fastrpc_function_def_interp1 *def,
+ struct fastrpc_invoke_args *args,
+ uint8_t out_count,
+ uint32_t *inbuf,
+ uint32_t **outbuf,
+ va_list peek)
+{
+ int i;
+
+ allocate_first_outbuf(def, args, outbuf);
+
+ for (i = 0; i < def->out_nums; i++)
+ va_arg(peek, uint32_t *);
+
+ for (i = 0; i < def->out_bufs; i++) {
+ inbuf[i] = args[i + 1].length = va_arg(peek, uint32_t);
+ va_arg(peek, uint32_t *);
+ args[i + 1].ptr = (__u64) va_arg(peek, void *);
+ args[i + 1].fd = -1;
+ }
+}
+
+/*
+ * 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.
+ *
+ * The argument list has, in order:
+ * - a (uint32_t val) for each input number
+ * - a (uint32_t len, void *buf) for each input buffer
+ * - a (uint32_t *val) for each output number
+ * - a (uint32_t max_size, uint32_t *len, void *buf) for each output buffer
+ *
+ * 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_size,
+ * uint32_t *nested_inbufs_len,
+ * void *nested_inbufs)
+ * {
+ * struct fastrpc_function_def_interp1 def = {
+ * .in_nums = 2,
+ * .in_bufs = 1,
+ * .out_nums = 3,
+ * .out_bufs = 1,
+ * .mid = 4,
+ * };
+ * return fastrpc(def, fd, handle,
+ * prev_ctx,
+ * prev_result,
+ * nested_outbufs_len,
+ * nested_outbufs,
+ * ctx,
+ * nested_handle,
+ * nested_sc,
+ * nested_inbufs_size,
+ * nested_inbufs_len,
+ * nested_inbufs);
+ * }
+ */
+int vfastrpc2(const struct fastrpc_function_def_interp1 *def,
+ int fd, uint32_t handle, va_list arg_list)
+{
+ va_list peek;
+ struct fastrpc_invoke invoke;
+ struct fastrpc_invoke_args *args;
+ uint32_t *inbuf;
+ uint32_t *outbuf;
+ uint8_t in_count;
+ uint8_t out_count;
+ uint8_t i;
+ int ret;
+
+ /*
+ * Calculate the amount of needed buffers, accounting for the need for
+ * the maximum size of the output buffer.
+ */
+ in_count = io_side_count(def->in_nums + def->out_bufs, def->in_bufs);
+ out_count = io_side_count(def->out_nums, def->out_bufs);
+
+ invoke.handle = handle;
+ invoke.sc = REMOTE_SCALARS_MAKE(def->msg_id, in_count, out_count);
+ invoke.args = (__u64) (args = malloc(sizeof(*args) * (in_count + out_count)));
+
+ allocate_first_inbuf(def, args, &inbuf);
+
+ for (i = 0; i < def->in_nums; i++)
+ inbuf[i] = va_arg(arg_list, uint32_t);
+
+ for (i = 1; i < in_count; i++) {
+ args[i].length = inbuf[def->in_nums + i]
+ = va_arg(arg_list, uint32_t);
+ args[i].ptr = (__u64) va_arg(arg_list, void *);
+ args[i].fd = -1;
+ }
+
+ va_copy(peek, arg_list);
+ prepare_outbufs(def,
+ &args[in_count],
+ out_count,
+ &inbuf[def->in_nums + def->in_bufs],
+ &outbuf,
+ peek);
+ va_end(peek);
+
+ ret = ioctl(fd, FASTRPC_IOCTL_INVOKE, (__u64) &invoke);
+
+ for (i = 0; i < def->out_nums; i++)
+ *va_arg(arg_list, uint32_t *) = outbuf[i];
+
+ for (i = 1; i < def->out_bufs; i++) {
+ va_arg(arg_list, uint32_t);
+ *va_arg(arg_list, uint32_t *) = outbuf[def->out_nums + i];
+ va_arg(arg_list, void *);
+ }
+
+ free(args);
+ free(inbuf);
+ free(outbuf);
+
+ return ret;
+}
+
+int fastrpc2(const struct fastrpc_function_def_interp1 *def,
+ int fd, uint32_t handle, ...)
+{
+ va_list arg_list;
+ int ret;
+
+ va_start(arg_list, handle);
+ ret = vfastrpc2(def, fd, handle, arg_list);
+ va_end(arg_list);
+
+ return ret;
+}
diff --git a/fastrpc/fastrpc.h b/fastrpc/fastrpc.h
new file mode 100644
index 0000000..5fc3e00
--- /dev/null
+++ b/fastrpc/fastrpc.h
@@ -0,0 +1,69 @@
+/*
+ * FastRPC API Replacement - header files
+ *
+ * Copyright (C) 2022 Richard Acayan
+ *
+ * This file is part of sensh.
+ *
+ * Sensh is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef FASTRPC_H
+#define FASTRPC_H
+
+#include
+#include
+#include
+
+// See fastrpc.git/src/remotectl_stub.c
+#define REMOTE_SCALARS_MAKEX(nAttr,nMethod,nIn,nOut,noIn,noOut) \
+ ((((uint32_t) (nAttr) & 0x7) << 29) | \
+ (((uint32_t) (nMethod) & 0x1f) << 24) | \
+ (((uint32_t) (nIn) & 0xff) << 16) | \
+ (((uint32_t) (nOut) & 0xff) << 8) | \
+ (((uint32_t) (noIn) & 0x0f) << 4) | \
+ ((uint32_t) (noOut) & 0x0f))
+
+#define REMOTE_SCALARS_MAKE(nMethod,nIn,nOut) REMOTE_SCALARS_MAKEX(0,nMethod,nIn,nOut,0,0)
+
+struct fastrpc_context {
+ int fd;
+ uint32_t handle;
+};
+
+struct fastrpc_function_def_interp1 {
+ uint32_t msg_id;
+ uint8_t in_nums;
+ uint8_t in_bufs;
+ uint8_t out_nums;
+ uint8_t out_bufs;
+};
+
+struct fastrpc_context *fastrpc_create_context(int fd, uint32_t handle);
+
+static inline void fastrpc_destroy_context(struct fastrpc_context *ctx)
+{
+ free(ctx);
+}
+
+int vfastrpc2(const struct fastrpc_function_def_interp1 *def,
+ int fd, uint32_t handle, va_list arg_list);
+int vfastrpc(const struct fastrpc_function_def_interp1 *def,
+ const struct fastrpc_context *ctx, va_list arg_list);
+int fastrpc2(const struct fastrpc_function_def_interp1 *def,
+ int fd, uint32_t handle, ...);
+int fastrpc(const struct fastrpc_function_def_interp1 *def,
+ const struct fastrpc_context *ctx, ...);
+
+#endif
diff --git a/fastrpc/fastrpc_remotectl.h b/fastrpc/fastrpc_remotectl.h
new file mode 100644
index 0000000..6103f96
--- /dev/null
+++ b/fastrpc/fastrpc_remotectl.h
@@ -0,0 +1,42 @@
+/*
+ * Remote processor control interface
+ *
+ * Copyright (C) 2022 Richard Acayan
+ *
+ * This file is part of sensh.
+ *
+ * Sensh is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef FASTRPC_REMOTECTL_H
+#define FASTRPC_REMOTECTL_H
+
+#include "fastrpc.h"
+
+#define REMOTECTL_HANDLE 0
+
+#define DEFINE_REMOTE_PROCEDURE(mid, name, innums, inbufs, outnums, outbufs) \
+ struct fastrpc_function_def_interp1 name##_def = { \
+ .msg_id = mid, \
+ .in_nums = innums, \
+ .in_bufs = inbufs, \
+ .out_nums = outnums, \
+ .out_bufs = outbufs, \
+ }
+
+DEFINE_REMOTE_PROCEDURE(0, remotectl_open, 0, 1, 1, 1);
+
+#undef DEFINE_REMOTE_PROCEDURE
+
+#endif
diff --git a/fastrpc/rpcd.c b/fastrpc/rpcd.c
new file mode 100644
index 0000000..2b75657
--- /dev/null
+++ b/fastrpc/rpcd.c
@@ -0,0 +1,62 @@
+/*
+ * FastRPC Example
+ *
+ * Copyright (C) 2022 Richard Acayan
+ *
+ * This file is part of sensh.
+ *
+ * Sensh is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+#include
+#include
+
+#include "fastrpc.h"
+#include "fastrpc_remotectl.h"
+
+static int remotectl_open(int fd, char *name, struct fastrpc_context **ctx, void (*err_cb)(const char *err, size_t err_len))
+{
+ uint32_t handle;
+ uint32_t err_len;
+ char err[256];
+ int ret;
+
+ ret = fastrpc2(&remotectl_open_def, fd, REMOTECTL_HANDLE,
+ strlen(name) + 1, name,
+ &handle,
+ 256, &err_len, err);
+ if (ret) {
+ err_cb(err, err_len);
+ return ret;
+ }
+
+ *ctx = fastrpc_create_context(fd, handle);
+
+ return 0;
+}
+
+static void remotectl_err(const char *err, size_t err_len)
+{
+ write(STDERR_FILENO, err, err_len);
+}
+
+int main()
+{
+ struct fastrpc_context *ctx;
+
+ remotectl_open(-1, "adsp_listener", &ctx, remotectl_err);
+ printf("{ .fd = %u, .handle = %u, }\n", ctx->fd, ctx->handle);
+ free(ctx);
+}