• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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