[lib][spi][srv] Split TIPC functions into a separate library
This these TIPC functions can be built/linked to separately from the
rest of SPI server functionality.
Bug: 118762930
Change-Id: I5577a77ab376263d79c40f8cefce468c2a2d3f1f
diff --git a/lib/spi/srv/common/include/lib/spi/srv/common/common.h b/lib/spi/srv/common/include/lib/spi/srv/common/common.h
new file mode 100644
index 0000000..43a6b62
--- /dev/null
+++ b/lib/spi/srv/common/include/lib/spi/srv/common/common.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <interface/spi/spi.h>
+#include <lib/spi/common/utils.h>
+#include <lk/compiler.h>
+
+__BEGIN_CDECLS
+
+/**
+ * struct spi_dev_ctx - opaque SPI device context structure
+ */
+struct spi_dev_ctx;
+
+/**
+ * spi_batch_state - tracks state associated with SPI batch being processed
+ * @cs: CS state resulting from the SPI batch
+ * @num_cmds: number of commands successfully processed. Also corresponds to the
+ * index of the failed command if an error occurred.
+ */
+struct spi_batch_state {
+ bool cs;
+ size_t num_cmds;
+};
+
+/**
+ * spi_srv_handle_batch() - handle batch of SPI requests
+ * @spi: handle to SPI device
+ * @mb: memory buffer containing the batch of SPI requests
+ * @batch_req: metadata about the batch of SPI requests
+ * @state: keeps track of device state as the batch is being processed
+ *
+ * Return: 0 on success, negative error code otherwise
+ */
+int spi_srv_handle_batch(struct spi_dev_ctx* spi,
+ struct mem_buf* mb,
+ struct spi_batch_req* batch_req,
+ struct spi_batch_state* state);
+
+__END_CDECLS
diff --git a/lib/spi/srv/include/lib/spi/srv/srv.h b/lib/spi/srv/include/lib/spi/srv/srv.h
index 04f1441..18b519b 100644
--- a/lib/spi/srv/include/lib/spi/srv/srv.h
+++ b/lib/spi/srv/include/lib/spi/srv/srv.h
@@ -16,29 +16,5 @@
#pragma once
-#include <lib/tipc/tipc_srv.h>
-#include <lk/compiler.h>
-
-__BEGIN_CDECLS
-
-/**
- * add_spi_service() - Add new SPI service to service set
- * @hset: pointer to handle set to add service to
- * @ports: an array of &struct tipc_port describing ports for this
- * service
- * @num_ports: number of ports in array pointed by @ports
- *
- * Caller must provide a pointer to &struct spi_dev_ctx as port-specific private
- * data in @priv field of &struct tipc_port.
- *
- * This routine can be called multiple times to register multiple services.
- *
- * Each port in @ports may have at most one active connection.
- *
- * Return: 0 on success, negative error code otherwise
- */
-int add_spi_service(struct tipc_hset* hset,
- const struct tipc_port* ports,
- size_t num_ports);
-
-__END_CDECLS
+/* Expose tipc.h functions to clients of lib/spi/srv */
+#include <lib/spi/srv/tipc/tipc.h>
diff --git a/lib/spi/srv/rules.mk b/lib/spi/srv/rules.mk
index 346a66b..7ff178b 100644
--- a/lib/spi/srv/rules.mk
+++ b/lib/spi/srv/rules.mk
@@ -22,6 +22,9 @@
MODULE_DEPS += \
trusty/user/base/interface/spi \
trusty/user/base/lib/spi/common \
- trusty/user/base/lib/tipc \
+ trusty/user/base/lib/spi/srv/tipc \
+
+MODULE_INCLUDES += \
+ trusty/user/base/lib/spi/srv/common/include \
include make/module.mk
diff --git a/lib/spi/srv/srv.c b/lib/spi/srv/srv.c
index 10ff45b..3486d0a 100644
--- a/lib/spi/srv/srv.c
+++ b/lib/spi/srv/srv.c
@@ -16,142 +16,14 @@
#include <interface/spi/spi.h>
#include <lib/spi/common/utils.h>
+#include <lib/spi/srv/common/common.h>
#include <lib/spi/srv/dev.h>
-#include <lib/spi/srv/srv.h>
-#include <lib/tipc/tipc_srv.h>
#include <lk/compiler.h>
-#include <stdlib.h>
-#include <sys/mman.h>
#include <uapi/err.h>
-#include <uapi/mm.h>
#define TLOG_TAG "spi-srv"
#include <trusty_log.h>
-/**
- * chan_ctx - per-connection SPI data
- * @shm: state of memory region shared with SPI server
- * @shm_handle: handle to shared memory region
- * @cs: tracks CS state of the underlying SPI device
- * true - asserted, false - deasserted
- */
-struct chan_ctx {
- struct mem_buf shm;
- handle_t shm_handle;
- bool cs;
-};
-
-static inline bool shm_is_mapped(struct chan_ctx* ctx) {
- return ctx->shm.buf && ctx->shm_handle != INVALID_IPC_HANDLE;
-}
-
-static inline void shm_unmap(struct chan_ctx* ctx) {
- if (shm_is_mapped(ctx)) {
- munmap(ctx->shm.buf, ctx->shm.capacity);
- mb_destroy(&ctx->shm);
- close(ctx->shm_handle);
- ctx->shm_handle = INVALID_IPC_HANDLE;
- }
-}
-
-union spi_msg_req_args {
- struct spi_shm_map_req shm;
- struct spi_batch_req batch;
-};
-
-static size_t get_spi_msg_size(struct spi_msg_req* req) {
- size_t msg_size = sizeof(struct spi_msg_req);
- switch (req->cmd & SPI_CMD_OP_MASK) {
- case SPI_CMD_MSG_OP_SHM_MAP:
- msg_size += sizeof(struct spi_shm_map_req);
- break;
-
- case SPI_CMD_MSG_OP_BATCH_EXEC:
- msg_size += sizeof(struct spi_batch_req);
- break;
- }
- return msg_size;
-}
-
-static int recv_msg(handle_t chan,
- struct spi_msg_req* req,
- union spi_msg_req_args* args,
- handle_t* h) {
- int rc;
- struct ipc_msg_info msg_inf;
- size_t num_handles = h ? 1 : 0;
-
- rc = get_msg(chan, &msg_inf);
- if (rc != NO_ERROR) {
- TLOGE("failed (%d) to get_msg()\n", rc);
- return rc;
- }
-
- struct iovec iovs[2] = {
- {
- .iov_base = req,
- .iov_len = sizeof(*req),
- },
- {
- .iov_base = args,
- .iov_len = sizeof(*args),
- },
- };
- struct ipc_msg msg = {
- .iov = iovs,
- .num_iov = countof(iovs),
- .handles = h,
- .num_handles = num_handles,
- };
- rc = read_msg(chan, msg_inf.id, 0, &msg);
- if (rc != (int)get_spi_msg_size(req)) {
- TLOGE("failed (%d) to read_msg()\n", rc);
- put_msg(chan, msg_inf.id);
- return rc;
- }
-
- put_msg(chan, msg_inf.id);
- return NO_ERROR;
-}
-
-static int handle_msg_shm_map_req(handle_t chan,
- struct chan_ctx* ctx,
- struct spi_shm_map_req* shm_req,
- handle_t shm_handle) {
- int rc = NO_ERROR;
- struct spi_msg_resp resp;
- void* shm_base;
-
- shm_unmap(ctx);
-
- shm_base = mmap(0, shm_req->len, MMAP_FLAG_PROT_READ | MMAP_FLAG_PROT_WRITE,
- 0, shm_handle, 0);
- if (!shm_base) {
- TLOGE("failed to map shared memory\n");
- rc = ERR_GENERIC;
- goto err_mmap;
- }
-
- resp.status = translate_lk_err(rc);
- rc = tipc_send1(chan, &resp, sizeof(resp));
- if (rc < 0 || (size_t)rc != sizeof(resp)) {
- TLOGE("failed (%d) to send SPI response\n", rc);
- if (rc >= 0) {
- rc = ERR_BAD_LEN;
- }
- goto err_resp;
- }
-
- mb_init(&ctx->shm, shm_base, shm_req->len, SPI_CMD_SHM_ALIGN);
- ctx->shm_handle = shm_handle;
- return NO_ERROR;
-
-err_resp:
- munmap(shm_base, shm_req->len);
-err_mmap:
- return rc;
-}
-
static int handle_xfer_args(struct spi_dev_ctx* spi, struct mem_buf* shm) {
int rc;
struct spi_xfer_args* xfer_args;
@@ -206,17 +78,6 @@
return rc;
}
-/**
- * spi_batch_state - tracks state associated with SPI batch being processed
- * @cs: CS state resulting from the SPI batch
- * @num_cmds: number of commands successfully processed. Also corresponds to the
- * index of the failed command if an error occurred.
- */
-struct spi_batch_state {
- bool cs;
- size_t num_cmds;
-};
-
static int unpack_shm(struct spi_dev_ctx* spi,
struct mem_buf* shm,
size_t len,
@@ -289,10 +150,10 @@
return NO_ERROR;
}
-static int handle_shm_batch_req(struct spi_dev_ctx* spi,
- struct mem_buf* shm,
- struct spi_batch_req* batch_req,
- struct spi_batch_state* state) {
+int spi_srv_handle_batch(struct spi_dev_ctx* spi,
+ struct mem_buf* shm,
+ struct spi_batch_req* batch_req,
+ struct spi_batch_state* state) {
int rc = NO_ERROR;
if (batch_req->len > shm->capacity) {
@@ -349,163 +210,3 @@
spi_seq_abort(spi);
return rc;
}
-
-static int handle_msg_batch_req(handle_t chan,
- struct spi_dev_ctx* spi,
- struct chan_ctx* ctx,
- struct spi_batch_req* batch_req) {
- int rc;
- struct spi_msg_resp resp;
- struct spi_batch_resp batch_resp;
- struct spi_batch_state state;
-
- if (!shm_is_mapped(ctx)) {
- return ERR_BAD_STATE;
- }
-
- state.cs = ctx->cs;
- state.num_cmds = 0;
- rc = handle_shm_batch_req(spi, &ctx->shm, batch_req, &state);
- if (rc == NO_ERROR) {
- ctx->cs = state.cs;
- }
-
- resp.cmd = SPI_CMD_MSG_OP_BATCH_EXEC | SPI_CMD_RESP_BIT;
- resp.status = translate_lk_err(rc);
- batch_resp.len = mb_curr_pos(&ctx->shm);
- batch_resp.failed = (rc != NO_ERROR) ? (uint32_t)state.num_cmds : 0;
-
- rc = tipc_send2(chan, &resp, sizeof(resp), &batch_resp, sizeof(batch_resp));
- if (rc < 0 || (size_t)rc != sizeof(resp) + sizeof(batch_resp)) {
- TLOGE("failed (%d) to send batch response\n", rc);
- if (rc >= 0) {
- rc = ERR_BAD_LEN;
- }
- return rc;
- }
-
- return NO_ERROR;
-}
-
-static int on_connect(const struct tipc_port* port,
- handle_t chan,
- const struct uuid* peer,
- void** ctx_p) {
- struct chan_ctx* ctx = calloc(1, sizeof(struct chan_ctx));
- if (!ctx) {
- TLOGE("failed to allocate channel context\n");
- return ERR_NO_MEMORY;
- }
-
- ctx->shm_handle = INVALID_IPC_HANDLE;
-
- *ctx_p = ctx;
- return NO_ERROR;
-}
-
-static int on_message(const struct tipc_port* port,
- handle_t chan,
- void* chan_ctx) {
- int rc;
- struct spi_msg_req req;
- union spi_msg_req_args args;
- struct spi_dev_ctx* spi = (struct spi_dev_ctx*)port->priv;
- struct chan_ctx* ctx = (struct chan_ctx*)chan_ctx;
- handle_t h;
-
- rc = recv_msg(chan, &req, &args, &h);
- if (rc != NO_ERROR) {
- TLOGE("failed (%d) to receive SPI message, closing connection\n", rc);
- return rc;
- }
-
- switch (req.cmd & SPI_CMD_OP_MASK) {
- case SPI_CMD_MSG_OP_SHM_MAP:
- rc = handle_msg_shm_map_req(chan, ctx, &args.shm, h);
- break;
-
- case SPI_CMD_MSG_OP_BATCH_EXEC:
- rc = handle_msg_batch_req(chan, spi, ctx, &args.batch);
- break;
-
- default:
- TLOGE("cmd 0x%x: unknown command\n", req.cmd);
- rc = ERR_CMD_UNKNOWN;
- }
-
- if (rc != NO_ERROR) {
- TLOGE("failed (%d) to handle SPI message, closing connection\n", rc);
- return rc;
- }
-
- return NO_ERROR;
-}
-
-static void on_disconnect(const struct tipc_port* port,
- handle_t chan,
- void* _ctx) {
- int rc;
- struct spi_dev_ctx* spi = (struct spi_dev_ctx*)port->priv;
- struct chan_ctx* ctx = (struct chan_ctx*)_ctx;
- struct spi_shm_hdr hdr;
- struct mem_buf mb;
- struct spi_batch_req req;
- struct spi_batch_state state;
-
- /* make sure CS is deasserted */
- if (!ctx->cs) {
- return;
- }
-
- /* Construct a batch with a single deassert command to recover CS state */
- hdr.cmd = SPI_CMD_SHM_OP_CS_DEASSERT;
-
- /* Deassert commands are header only */
- mb_init(&mb, &hdr, sizeof(hdr), SPI_CMD_SHM_ALIGN);
- mb_resize(&mb, sizeof(hdr));
-
- req.len = sizeof(hdr);
- req.num_cmds = 1;
-
- state.cs = true;
- state.num_cmds = 0;
-
- rc = handle_shm_batch_req(spi, &mb, &req, &state);
- /* CS state will be out of sync. This is an unrecoverable error. */
- assert(rc == NO_ERROR);
-
- ctx->cs = false;
-}
-
-static void on_channel_cleanup(void* _ctx) {
- struct chan_ctx* ctx = (struct chan_ctx*)_ctx;
- assert(!ctx->cs);
- shm_unmap(ctx);
- free(ctx);
-}
-
-static const struct tipc_srv_ops spi_dev_ops = {
- .on_connect = on_connect,
- .on_message = on_message,
- .on_disconnect = on_disconnect,
- .on_channel_cleanup = on_channel_cleanup,
-};
-
-int add_spi_service(struct tipc_hset* hset,
- const struct tipc_port* ports,
- size_t num_ports) {
- int rc;
-
- for (size_t i = 0; i < num_ports; i++) {
- if (!ports[i].priv) {
- return ERR_INVALID_ARGS;
- }
-
- rc = tipc_add_service(hset, &ports[i], 1, 1, &spi_dev_ops);
- if (rc != NO_ERROR) {
- return rc;
- }
- }
-
- return NO_ERROR;
-}
diff --git a/lib/spi/srv/tipc/include/lib/spi/srv/tipc/tipc.h b/lib/spi/srv/tipc/include/lib/spi/srv/tipc/tipc.h
new file mode 100644
index 0000000..04f1441
--- /dev/null
+++ b/lib/spi/srv/tipc/include/lib/spi/srv/tipc/tipc.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <lib/tipc/tipc_srv.h>
+#include <lk/compiler.h>
+
+__BEGIN_CDECLS
+
+/**
+ * add_spi_service() - Add new SPI service to service set
+ * @hset: pointer to handle set to add service to
+ * @ports: an array of &struct tipc_port describing ports for this
+ * service
+ * @num_ports: number of ports in array pointed by @ports
+ *
+ * Caller must provide a pointer to &struct spi_dev_ctx as port-specific private
+ * data in @priv field of &struct tipc_port.
+ *
+ * This routine can be called multiple times to register multiple services.
+ *
+ * Each port in @ports may have at most one active connection.
+ *
+ * Return: 0 on success, negative error code otherwise
+ */
+int add_spi_service(struct tipc_hset* hset,
+ const struct tipc_port* ports,
+ size_t num_ports);
+
+__END_CDECLS
diff --git a/lib/spi/srv/tipc/rules.mk b/lib/spi/srv/tipc/rules.mk
new file mode 100644
index 0000000..544a6c4
--- /dev/null
+++ b/lib/spi/srv/tipc/rules.mk
@@ -0,0 +1,30 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_SRCS += \
+ $(LOCAL_DIR)/tipc.c \
+
+MODULE_DEPS += \
+ trusty/user/base/interface/spi \
+ trusty/user/base/lib/spi/common \
+ trusty/user/base/lib/tipc \
+
+MODULE_INCLUDES += \
+ trusty/user/base/lib/spi/srv/common/include \
+
+include make/module.mk
diff --git a/lib/spi/srv/tipc/tipc.c b/lib/spi/srv/tipc/tipc.c
new file mode 100644
index 0000000..a434f72
--- /dev/null
+++ b/lib/spi/srv/tipc/tipc.c
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <interface/spi/spi.h>
+#include <lib/spi/common/utils.h>
+#include <lib/spi/srv/common/common.h>
+#include <lib/spi/srv/tipc/tipc.h>
+#include <lib/tipc/tipc_srv.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <uapi/err.h>
+#include <uapi/mm.h>
+
+#define TLOG_TAG "spi-srv-tipc"
+#include <trusty_log.h>
+
+/**
+ * chan_ctx - per-connection SPI data
+ * @shm: state of memory region shared with SPI server
+ * @shm_handle: handle to shared memory region
+ * @cs: tracks CS state of the underlying SPI device
+ * true - asserted, false - deasserted
+ */
+struct chan_ctx {
+ struct mem_buf shm;
+ handle_t shm_handle;
+ bool cs;
+};
+
+static inline bool shm_is_mapped(struct chan_ctx* ctx) {
+ return ctx->shm.buf && ctx->shm_handle != INVALID_IPC_HANDLE;
+}
+
+static inline void shm_unmap(struct chan_ctx* ctx) {
+ if (shm_is_mapped(ctx)) {
+ munmap(ctx->shm.buf, ctx->shm.capacity);
+ mb_destroy(&ctx->shm);
+ close(ctx->shm_handle);
+ ctx->shm_handle = INVALID_IPC_HANDLE;
+ }
+}
+
+union spi_msg_req_args {
+ struct spi_shm_map_req shm;
+ struct spi_batch_req batch;
+};
+
+static size_t get_spi_msg_size(struct spi_msg_req* req) {
+ size_t msg_size = sizeof(struct spi_msg_req);
+ switch (req->cmd & SPI_CMD_OP_MASK) {
+ case SPI_CMD_MSG_OP_SHM_MAP:
+ msg_size += sizeof(struct spi_shm_map_req);
+ break;
+
+ case SPI_CMD_MSG_OP_BATCH_EXEC:
+ msg_size += sizeof(struct spi_batch_req);
+ break;
+ }
+ return msg_size;
+}
+
+static int recv_msg(handle_t chan,
+ struct spi_msg_req* req,
+ union spi_msg_req_args* args,
+ handle_t* h) {
+ int rc;
+ struct ipc_msg_info msg_inf;
+ size_t num_handles = h ? 1 : 0;
+
+ rc = get_msg(chan, &msg_inf);
+ if (rc != NO_ERROR) {
+ TLOGE("failed (%d) to get_msg()\n", rc);
+ return rc;
+ }
+
+ struct iovec iovs[2] = {
+ {
+ .iov_base = req,
+ .iov_len = sizeof(*req),
+ },
+ {
+ .iov_base = args,
+ .iov_len = sizeof(*args),
+ },
+ };
+ struct ipc_msg msg = {
+ .iov = iovs,
+ .num_iov = countof(iovs),
+ .handles = h,
+ .num_handles = num_handles,
+ };
+ rc = read_msg(chan, msg_inf.id, 0, &msg);
+ if (rc != (int)get_spi_msg_size(req)) {
+ TLOGE("failed (%d) to read_msg()\n", rc);
+ put_msg(chan, msg_inf.id);
+ return rc;
+ }
+
+ put_msg(chan, msg_inf.id);
+ return NO_ERROR;
+}
+
+static int handle_msg_shm_map_req(handle_t chan,
+ struct chan_ctx* ctx,
+ struct spi_shm_map_req* shm_req,
+ handle_t shm_handle) {
+ int rc = NO_ERROR;
+ struct spi_msg_resp resp;
+ void* shm_base;
+
+ shm_unmap(ctx);
+
+ shm_base = mmap(0, shm_req->len, MMAP_FLAG_PROT_READ | MMAP_FLAG_PROT_WRITE,
+ 0, shm_handle, 0);
+ if (!shm_base) {
+ TLOGE("failed to map shared memory\n");
+ rc = ERR_GENERIC;
+ goto err_mmap;
+ }
+
+ resp.status = translate_lk_err(rc);
+ rc = tipc_send1(chan, &resp, sizeof(resp));
+ if (rc < 0 || (size_t)rc != sizeof(resp)) {
+ TLOGE("failed (%d) to send SPI response\n", rc);
+ if (rc >= 0) {
+ rc = ERR_BAD_LEN;
+ }
+ goto err_resp;
+ }
+
+ mb_init(&ctx->shm, shm_base, shm_req->len, SPI_CMD_SHM_ALIGN);
+ ctx->shm_handle = shm_handle;
+ return NO_ERROR;
+
+err_resp:
+ munmap(shm_base, shm_req->len);
+err_mmap:
+ return rc;
+}
+
+static int handle_msg_batch_req(handle_t chan,
+ struct spi_dev_ctx* spi,
+ struct chan_ctx* ctx,
+ struct spi_batch_req* batch_req) {
+ int rc;
+ struct spi_msg_resp resp;
+ struct spi_batch_resp batch_resp;
+ struct spi_batch_state state;
+
+ if (!shm_is_mapped(ctx)) {
+ return ERR_BAD_STATE;
+ }
+
+ state.cs = ctx->cs;
+ state.num_cmds = 0;
+ rc = spi_srv_handle_batch(spi, &ctx->shm, batch_req, &state);
+ if (rc == NO_ERROR) {
+ ctx->cs = state.cs;
+ }
+
+ resp.cmd = SPI_CMD_MSG_OP_BATCH_EXEC | SPI_CMD_RESP_BIT;
+ resp.status = translate_lk_err(rc);
+ batch_resp.len = mb_curr_pos(&ctx->shm);
+ batch_resp.failed = (rc != NO_ERROR) ? (uint32_t)state.num_cmds : 0;
+
+ rc = tipc_send2(chan, &resp, sizeof(resp), &batch_resp, sizeof(batch_resp));
+ if (rc < 0 || (size_t)rc != sizeof(resp) + sizeof(batch_resp)) {
+ TLOGE("failed (%d) to send batch response\n", rc);
+ if (rc >= 0) {
+ rc = ERR_BAD_LEN;
+ }
+ return rc;
+ }
+
+ return NO_ERROR;
+}
+
+static int on_connect(const struct tipc_port* port,
+ handle_t chan,
+ const struct uuid* peer,
+ void** ctx_p) {
+ struct chan_ctx* ctx = calloc(1, sizeof(struct chan_ctx));
+ if (!ctx) {
+ TLOGE("failed to allocate channel context\n");
+ return ERR_NO_MEMORY;
+ }
+
+ ctx->shm_handle = INVALID_IPC_HANDLE;
+
+ *ctx_p = ctx;
+ return NO_ERROR;
+}
+
+static int on_message(const struct tipc_port* port,
+ handle_t chan,
+ void* chan_ctx) {
+ int rc;
+ struct spi_msg_req req;
+ union spi_msg_req_args args;
+ struct spi_dev_ctx* spi = (struct spi_dev_ctx*)port->priv;
+ struct chan_ctx* ctx = (struct chan_ctx*)chan_ctx;
+ handle_t h;
+
+ rc = recv_msg(chan, &req, &args, &h);
+ if (rc != NO_ERROR) {
+ TLOGE("failed (%d) to receive SPI message, closing connection\n", rc);
+ return rc;
+ }
+
+ switch (req.cmd & SPI_CMD_OP_MASK) {
+ case SPI_CMD_MSG_OP_SHM_MAP:
+ rc = handle_msg_shm_map_req(chan, ctx, &args.shm, h);
+ break;
+
+ case SPI_CMD_MSG_OP_BATCH_EXEC:
+ rc = handle_msg_batch_req(chan, spi, ctx, &args.batch);
+ break;
+
+ default:
+ TLOGE("cmd 0x%x: unknown command\n", req.cmd);
+ rc = ERR_CMD_UNKNOWN;
+ }
+
+ if (rc != NO_ERROR) {
+ TLOGE("failed (%d) to handle SPI message, closing connection\n", rc);
+ return rc;
+ }
+
+ return NO_ERROR;
+}
+
+static void on_disconnect(const struct tipc_port* port,
+ handle_t chan,
+ void* _ctx) {
+ int rc;
+ struct spi_dev_ctx* spi = (struct spi_dev_ctx*)port->priv;
+ struct chan_ctx* ctx = (struct chan_ctx*)_ctx;
+ struct spi_shm_hdr hdr;
+ struct mem_buf mb;
+ struct spi_batch_req req;
+ struct spi_batch_state state;
+
+ /* make sure CS is deasserted */
+ if (!ctx->cs) {
+ return;
+ }
+
+ /* Construct a batch with a single deassert command to recover CS state */
+ hdr.cmd = SPI_CMD_SHM_OP_CS_DEASSERT;
+
+ /* Deassert commands are header only */
+ mb_init(&mb, &hdr, sizeof(hdr), SPI_CMD_SHM_ALIGN);
+ mb_resize(&mb, sizeof(hdr));
+
+ req.len = sizeof(hdr);
+ req.num_cmds = 1;
+
+ state.cs = true;
+ state.num_cmds = 0;
+
+ rc = spi_srv_handle_batch(spi, &mb, &req, &state);
+ /* CS state will be out of sync. This is an unrecoverable error. */
+ assert(rc == NO_ERROR);
+
+ ctx->cs = false;
+}
+
+static void on_channel_cleanup(void* _ctx) {
+ struct chan_ctx* ctx = (struct chan_ctx*)_ctx;
+ assert(!ctx->cs);
+ shm_unmap(ctx);
+ free(ctx);
+}
+
+static const struct tipc_srv_ops spi_dev_ops = {
+ .on_connect = on_connect,
+ .on_message = on_message,
+ .on_disconnect = on_disconnect,
+ .on_channel_cleanup = on_channel_cleanup,
+};
+
+int add_spi_service(struct tipc_hset* hset,
+ const struct tipc_port* ports,
+ size_t num_ports) {
+ int rc;
+
+ for (size_t i = 0; i < num_ports; i++) {
+ if (!ports[i].priv) {
+ return ERR_INVALID_ARGS;
+ }
+
+ rc = tipc_add_service(hset, &ports[i], 1, 1, &spi_dev_ops);
+ if (rc != NO_ERROR) {
+ return rc;
+ }
+ }
+
+ return NO_ERROR;
+}