1 /*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <interface/spi/spi.h>
18 #include <lib/spi/common/utils.h>
19 #include <lib/spi/srv/common/common.h>
20 #include <lib/spi/srv/tipc/tipc.h>
21 #include <lib/tipc/tipc_srv.h>
22 #include <stdlib.h>
23 #include <sys/mman.h>
24 #include <uapi/err.h>
25 #include <uapi/mm.h>
26
27 #define TLOG_TAG "spi-srv-tipc"
28 #include <trusty_log.h>
29
30 /**
31 * chan_ctx - per-connection SPI data
32 * @shm: state of memory region shared with SPI server
33 * @shm_handle: handle to shared memory region
34 * @cs: tracks CS state of the underlying SPI device
35 * true - asserted, false - deasserted
36 */
37 struct chan_ctx {
38 struct mem_buf shm;
39 handle_t shm_handle;
40 bool cs;
41 };
42
shm_is_mapped(struct chan_ctx * ctx)43 static inline bool shm_is_mapped(struct chan_ctx* ctx) {
44 return ctx->shm.buf && ctx->shm_handle != INVALID_IPC_HANDLE;
45 }
46
shm_unmap(struct chan_ctx * ctx)47 static inline void shm_unmap(struct chan_ctx* ctx) {
48 if (shm_is_mapped(ctx)) {
49 munmap(ctx->shm.buf, ctx->shm.capacity);
50 mb_destroy(&ctx->shm);
51 close(ctx->shm_handle);
52 ctx->shm_handle = INVALID_IPC_HANDLE;
53 }
54 }
55
56 union spi_msg_req_args {
57 struct spi_shm_map_req shm;
58 struct spi_batch_req batch;
59 };
60
get_spi_msg_size(struct spi_msg_req * req)61 static size_t get_spi_msg_size(struct spi_msg_req* req) {
62 size_t msg_size = sizeof(struct spi_msg_req);
63 switch (req->cmd & SPI_CMD_OP_MASK) {
64 case SPI_CMD_MSG_OP_SHM_MAP:
65 msg_size += sizeof(struct spi_shm_map_req);
66 break;
67
68 case SPI_CMD_MSG_OP_BATCH_EXEC:
69 msg_size += sizeof(struct spi_batch_req);
70 break;
71 }
72 return msg_size;
73 }
74
recv_msg(handle_t chan,struct spi_msg_req * req,union spi_msg_req_args * args,handle_t * h)75 static int recv_msg(handle_t chan,
76 struct spi_msg_req* req,
77 union spi_msg_req_args* args,
78 handle_t* h) {
79 int rc;
80 struct ipc_msg_info msg_inf;
81 size_t num_handles = h ? 1 : 0;
82
83 rc = get_msg(chan, &msg_inf);
84 if (rc != NO_ERROR) {
85 TLOGE("failed (%d) to get_msg()\n", rc);
86 return rc;
87 }
88
89 struct iovec iovs[2] = {
90 {
91 .iov_base = req,
92 .iov_len = sizeof(*req),
93 },
94 {
95 .iov_base = args,
96 .iov_len = sizeof(*args),
97 },
98 };
99 struct ipc_msg msg = {
100 .iov = iovs,
101 .num_iov = countof(iovs),
102 .handles = h,
103 .num_handles = num_handles,
104 };
105 rc = read_msg(chan, msg_inf.id, 0, &msg);
106 if (rc != (int)get_spi_msg_size(req)) {
107 TLOGE("failed (%d) to read_msg()\n", rc);
108 put_msg(chan, msg_inf.id);
109 return rc;
110 }
111
112 put_msg(chan, msg_inf.id);
113 return NO_ERROR;
114 }
115
handle_msg_shm_map_req(handle_t chan,struct chan_ctx * ctx,struct spi_shm_map_req * shm_req,handle_t shm_handle)116 static int handle_msg_shm_map_req(handle_t chan,
117 struct chan_ctx* ctx,
118 struct spi_shm_map_req* shm_req,
119 handle_t shm_handle) {
120 int rc = NO_ERROR;
121 void* shm_base;
122
123 shm_unmap(ctx);
124
125 shm_base = mmap(0, shm_req->len, MMAP_FLAG_PROT_READ | MMAP_FLAG_PROT_WRITE,
126 0, shm_handle, 0);
127 if (shm_base == MAP_FAILED) {
128 TLOGE("failed to map shared memory\n");
129 rc = ERR_GENERIC;
130 goto err_mmap;
131 }
132
133 struct spi_msg_resp resp = {
134 .status = translate_lk_err(rc)
135 };
136
137 rc = tipc_send1(chan, &resp, sizeof(resp));
138 if (rc < 0 || (size_t)rc != sizeof(resp)) {
139 TLOGE("failed (%d) to send SPI response\n", rc);
140 if (rc >= 0) {
141 rc = ERR_BAD_LEN;
142 }
143 goto err_resp;
144 }
145
146 mb_init(&ctx->shm, shm_base, shm_req->len, SPI_CMD_SHM_ALIGN);
147 ctx->shm_handle = shm_handle;
148 return NO_ERROR;
149
150 err_resp:
151 munmap(shm_base, shm_req->len);
152 err_mmap:
153 return rc;
154 }
155
handle_msg_batch_req(handle_t chan,struct spi_dev_ctx * spi,struct chan_ctx * ctx,struct spi_batch_req * batch_req)156 static int handle_msg_batch_req(handle_t chan,
157 struct spi_dev_ctx* spi,
158 struct chan_ctx* ctx,
159 struct spi_batch_req* batch_req) {
160 int rc;
161 struct spi_batch_state state;
162
163 if (!shm_is_mapped(ctx)) {
164 return ERR_BAD_STATE;
165 }
166
167 state.cs = ctx->cs;
168 state.num_cmds = 0;
169 rc = spi_srv_handle_batch(spi, &ctx->shm, batch_req, &state);
170 if (rc == NO_ERROR) {
171 ctx->cs = state.cs;
172 }
173
174 struct spi_msg_resp resp = {
175 .cmd = SPI_CMD_MSG_OP_BATCH_EXEC | SPI_CMD_RESP_BIT,
176 .status = translate_lk_err(rc)
177 };
178
179 struct spi_batch_resp batch_resp = {
180 .len = mb_curr_pos(&ctx->shm),
181 .failed = (rc != NO_ERROR) ? (uint32_t)state.num_cmds : 0
182 };
183
184 rc = tipc_send2(chan, &resp, sizeof(resp), &batch_resp, sizeof(batch_resp));
185 if (rc < 0 || (size_t)rc != sizeof(resp) + sizeof(batch_resp)) {
186 TLOGE("failed (%d) to send batch response\n", rc);
187 if (rc >= 0) {
188 rc = ERR_BAD_LEN;
189 }
190 return rc;
191 }
192
193 return NO_ERROR;
194 }
195
on_connect(const struct tipc_port * port,handle_t chan,const struct uuid * peer,void ** ctx_p)196 static int on_connect(const struct tipc_port* port,
197 handle_t chan,
198 const struct uuid* peer,
199 void** ctx_p) {
200 struct chan_ctx* ctx = calloc(1, sizeof(struct chan_ctx));
201 if (!ctx) {
202 TLOGE("failed to allocate channel context\n");
203 return ERR_NO_MEMORY;
204 }
205
206 ctx->shm_handle = INVALID_IPC_HANDLE;
207
208 *ctx_p = ctx;
209 return NO_ERROR;
210 }
211
on_message(const struct tipc_port * port,handle_t chan,void * chan_ctx)212 static int on_message(const struct tipc_port* port,
213 handle_t chan,
214 void* chan_ctx) {
215 int rc;
216 struct spi_msg_req req;
217 union spi_msg_req_args args;
218 struct spi_dev_ctx* spi = (struct spi_dev_ctx*)port->priv;
219 struct chan_ctx* ctx = (struct chan_ctx*)chan_ctx;
220 handle_t h = INVALID_IPC_HANDLE;
221
222 rc = recv_msg(chan, &req, &args, &h);
223 if (rc != NO_ERROR) {
224 TLOGE("failed (%d) to receive SPI message, closing connection\n", rc);
225 return rc;
226 }
227
228 switch (req.cmd & SPI_CMD_OP_MASK) {
229 case SPI_CMD_MSG_OP_SHM_MAP:
230 rc = handle_msg_shm_map_req(chan, ctx, &args.shm, h);
231 break;
232
233 case SPI_CMD_MSG_OP_BATCH_EXEC:
234 rc = handle_msg_batch_req(chan, spi, ctx, &args.batch);
235 break;
236
237 default:
238 TLOGE("cmd 0x%x: unknown command\n", req.cmd);
239 rc = ERR_CMD_UNKNOWN;
240 }
241
242 if (rc != NO_ERROR) {
243 TLOGE("failed (%d) to handle SPI message, closing connection\n", rc);
244 return rc;
245 }
246
247 return NO_ERROR;
248 }
249
on_disconnect(const struct tipc_port * port,handle_t chan,void * _ctx)250 static void on_disconnect(const struct tipc_port* port,
251 handle_t chan,
252 void* _ctx) {
253 int rc;
254 struct spi_dev_ctx* spi = (struct spi_dev_ctx*)port->priv;
255 struct chan_ctx* ctx = (struct chan_ctx*)_ctx;
256 struct spi_shm_hdr hdr;
257 struct mem_buf mb;
258 struct spi_batch_req req;
259 struct spi_batch_state state;
260
261 /* make sure CS is deasserted */
262 if (!ctx->cs) {
263 return;
264 }
265
266 /* Construct a batch with a single deassert command to recover CS state */
267 hdr.cmd = SPI_CMD_SHM_OP_CS_DEASSERT;
268
269 /* Deassert commands are header only */
270 mb_init(&mb, &hdr, sizeof(hdr), SPI_CMD_SHM_ALIGN);
271 mb_resize(&mb, sizeof(hdr));
272
273 req.len = sizeof(hdr);
274 req.num_cmds = 1;
275
276 state.cs = true;
277 state.num_cmds = 0;
278
279 rc = spi_srv_handle_batch(spi, &mb, &req, &state);
280 /* CS state will be out of sync. This is an unrecoverable error. */
281 assert(rc == NO_ERROR);
282
283 ctx->cs = false;
284 }
285
on_channel_cleanup(void * _ctx)286 static void on_channel_cleanup(void* _ctx) {
287 struct chan_ctx* ctx = (struct chan_ctx*)_ctx;
288 assert(!ctx->cs);
289 shm_unmap(ctx);
290 free(ctx);
291 }
292
293 static const struct tipc_srv_ops spi_dev_ops = {
294 .on_connect = on_connect,
295 .on_message = on_message,
296 .on_disconnect = on_disconnect,
297 .on_channel_cleanup = on_channel_cleanup,
298 };
299
add_spi_service(struct tipc_hset * hset,const struct tipc_port * ports,size_t num_ports)300 int add_spi_service(struct tipc_hset* hset,
301 const struct tipc_port* ports,
302 size_t num_ports) {
303 int rc;
304
305 for (size_t i = 0; i < num_ports; i++) {
306 if (!ports[i].priv) {
307 return ERR_INVALID_ARGS;
308 }
309
310 rc = tipc_add_service(hset, &ports[i], 1, 1, &spi_dev_ops);
311 if (rc != NO_ERROR) {
312 return rc;
313 }
314 }
315
316 return NO_ERROR;
317 }
318