• 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 <assert.h>
18  #include <lib/spi/client/spi.h>
19  #include <lib/tipc/tipc.h>
20  #include <lk/compiler.h>
21  #include <lk/macros.h>
22  #include <stdbool.h>
23  #include <stdlib.h>
24  #include <string.h>
25  #include <sys/auxv.h>
26  #include <trusty/memref.h>
27  #include <uapi/err.h>
28  #include <uapi/mm.h>
29  
30  #define TLOG_TAG "spi-client"
31  #include <trusty_log.h>
32  
33  #define PAGE_SIZE getauxval(AT_PAGESZ)
34  
35  /**
36   * Size of the largest SPI request argument structure. Needs to be updated if we
37   * add larger SPI arguments.
38   */
39  #define SPI_CMD_SHM_ARGS_MAX_SIZE sizeof(struct spi_xfer_args)
40  
send_shm(struct spi_dev * dev,struct spi_msg_req * req,struct spi_shm_map_req * shm_req,handle_t memref)41  static int send_shm(struct spi_dev* dev,
42                      struct spi_msg_req* req,
43                      struct spi_shm_map_req* shm_req,
44                      handle_t memref) {
45      int rc;
46      struct iovec iovs[2] = {
47              {
48                      .iov_base = req,
49                      .iov_len = sizeof(*req),
50              },
51              {
52                      .iov_base = shm_req,
53                      .iov_len = sizeof(*shm_req),
54              },
55      };
56      struct ipc_msg msg = {
57              .iov = iovs,
58              .num_iov = countof(iovs),
59              .handles = &memref,
60              .num_handles = 1,
61      };
62      rc = send_msg(dev->h, &msg);
63      if (rc < 0) {
64          TLOGE("failed (%d) to send memref\n", rc);
65          return rc;
66      }
67      return NO_ERROR;
68  }
69  
handle_shm_resp(handle_t chan)70  static int handle_shm_resp(handle_t chan) {
71      int rc;
72      struct uevent evt;
73      struct spi_msg_resp resp;
74  
75      rc = wait(chan, &evt, INFINITE_TIME);
76      if (rc != NO_ERROR) {
77          TLOGE("failed (%d) to wait for reply\n", rc);
78          return rc;
79      }
80  
81      rc = tipc_recv1(chan, sizeof(resp), &resp, sizeof(resp));
82      if (rc < 0 || (size_t)rc != sizeof(resp)) {
83          TLOGE("failed (%d) to read reply\n", rc);
84          if (rc >= 0) {
85              rc = ERR_BAD_LEN;
86          }
87          return rc;
88      }
89  
90      return translate_srv_err(resp.status);
91  }
92  
shm_map(struct spi_dev * dev,void * shm_base,size_t shm_size)93  static int shm_map(struct spi_dev* dev, void* shm_base, size_t shm_size) {
94      int rc;
95      struct spi_msg_req req;
96      struct spi_shm_map_req shm_req;
97  
98      /* create memref to send to SPI server */
99      rc = memref_create(shm_base, shm_size,
100                         MMAP_FLAG_PROT_READ | MMAP_FLAG_PROT_WRITE);
101      if (rc < 0) {
102          TLOGE("failed (%d) to create memref\n", rc);
103          goto err_memref_create;
104      }
105      handle_t memref = (handle_t)rc;
106  
107      /* send memref to SPI server */
108      req.cmd = SPI_CMD_MSG_OP_SHM_MAP;
109      shm_req.len = shm_size;
110      rc = send_shm(dev, &req, &shm_req, memref);
111      if (rc < 0) {
112          TLOGE("failed (%d) to send memref\n", rc);
113          goto err_send_msg;
114      }
115  
116      /* handle SPI server's response */
117      rc = handle_shm_resp(dev->h);
118      if (rc != NO_ERROR) {
119          TLOGE("failed (%d) to handle shared memory map response\n", rc);
120          goto err_resp;
121      }
122  
123      close(memref);
124      return NO_ERROR;
125  
126  err_resp:
127  err_send_msg:
128      close(memref);
129  err_memref_create:
130      return rc;
131  }
132  
get_shm_size(size_t max_num_cmds,size_t max_total_payload)133  static inline size_t get_shm_size(size_t max_num_cmds,
134                                    size_t max_total_payload) {
135      /* account for space taken up by alignment requirements */
136      size_t max_total_align = max_num_cmds * (SPI_CMD_SHM_ALIGN - 1);
137      size_t cmd_size = round_up(sizeof(struct spi_shm_hdr), SPI_CMD_SHM_ALIGN) +
138                        round_up(SPI_CMD_SHM_ARGS_MAX_SIZE, SPI_CMD_SHM_ALIGN);
139      size_t shm_size =
140              max_num_cmds * cmd_size + max_total_payload + max_total_align;
141  
142      return round_up(shm_size, PAGE_SIZE);
143  }
144  
spi_dev_open(struct spi_dev * dev,const char * name,size_t max_num_cmds,size_t max_total_payload)145  int spi_dev_open(struct spi_dev* dev,
146                   const char* name,
147                   size_t max_num_cmds,
148                   size_t max_total_payload) {
149      int rc;
150      void* shm_base;
151      size_t shm_size;
152  
153      if (!dev || !name || max_num_cmds == 0) {
154          return ERR_INVALID_ARGS;
155      }
156  
157      /* connect to SPI service */
158      rc = tipc_connect(&dev->h, name);
159      if (rc != NO_ERROR) {
160          TLOGE("failed (%d) to connect to service \"%s\"\n", rc, name);
161          goto err_connect;
162      }
163  
164      /* allocate shared memory */
165      shm_size = get_shm_size(max_num_cmds, max_total_payload);
166      shm_base = memalign(PAGE_SIZE, shm_size);
167      if (!shm_base) {
168          TLOGE("failed to allocate shared memory, base: %p, size: %zu\n",
169                shm_base, shm_size);
170          rc = ERR_NO_MEMORY;
171          goto err_shm_alloc;
172      }
173  
174      /* establish shared memory with SPI server*/
175      rc = shm_map(dev, shm_base, shm_size);
176      if (rc != NO_ERROR) {
177          TLOGE("failed (%d) to send shared memory\n", rc);
178          goto err_shm_send;
179      }
180  
181      mb_init(&dev->shm, shm_base, shm_size, SPI_CMD_SHM_ALIGN);
182      mb_resize(&dev->shm, shm_size);
183      dev->max_num_cmds = max_num_cmds;
184      dev->max_total_payload = max_total_payload;
185      spi_clear_cmds(dev);
186      return NO_ERROR;
187  
188  err_shm_send:
189      /*
190       * There is no way to free() shared memory safely once SPI server receives
191       * the memref. At this point in the program, we don't know if shm_map() has
192       * successfully sent the shared memory or not. So we leak the memory in case
193       * it was already shared.
194       * TODO: It may be possible to avoid memory leaks using other ways of
195       * allocating shared memory.
196       */
197  err_shm_alloc:
198      close(dev->h);
199      dev->h = INVALID_IPC_HANDLE;
200  err_connect:
201      return rc;
202  }
203  
is_initialized(struct spi_dev * dev)204  static inline bool is_initialized(struct spi_dev* dev) {
205      return dev && dev->h != INVALID_IPC_HANDLE;
206  }
207  
spi_clear_cmds(struct spi_dev * dev)208  void spi_clear_cmds(struct spi_dev* dev) {
209      assert(is_initialized(dev));
210      mb_rewind_pos(&dev->shm);
211      dev->num_cmds = 0;
212      dev->total_payload = 0;
213      dev->config_err = false;
214  }
215  
send_batch_req(struct spi_dev * dev)216  static int send_batch_req(struct spi_dev* dev) {
217      struct spi_msg_req req = {
218              .cmd = SPI_CMD_MSG_OP_BATCH_EXEC,
219      };
220      struct spi_batch_req batch_req = {
221              .len = mb_curr_pos(&dev->shm),
222              .num_cmds = dev->num_cmds,
223      };
224      int rc = tipc_send2(dev->h, &req, sizeof(req), &batch_req,
225                          sizeof(batch_req));
226      if (rc < 0 || (size_t)rc != sizeof(req) + sizeof(batch_req)) {
227          TLOGE("failed (%d) to send SPI batch request\n", rc);
228          if (rc >= 0) {
229              rc = ERR_BAD_LEN;
230          }
231          return rc;
232      }
233      return NO_ERROR;
234  }
235  
validate_batch_resp(struct spi_batch_resp * batch_resp,struct mem_buf * shm,size_t * failed)236  static int validate_batch_resp(struct spi_batch_resp* batch_resp,
237                                 struct mem_buf* shm,
238                                 size_t* failed) {
239      int rc = NO_ERROR;
240      struct spi_shm_hdr* shm_hdr;
241      uint32_t shm_hdr_cmd;
242      uint32_t shm_hdr_status;
243      struct spi_xfer_args* xfer_resp;
244      uint32_t xfer_resp_len;
245  
246      /*
247       * length of the response in shared memory must be equal to that of the
248       * request
249       */
250      if (batch_resp->len != mb_curr_pos(shm)) {
251          return ERR_BAD_STATE;
252      }
253  
254      mb_rewind_pos(shm);
255  
256      while (mb_curr_pos(shm) < batch_resp->len) {
257          shm_hdr = mb_advance_pos(shm, sizeof(*shm_hdr));
258          shm_hdr_cmd = READ_ONCE(shm_hdr->cmd);
259          shm_hdr_status = READ_ONCE(shm_hdr->status);
260  
261          if (!(shm_hdr_cmd & SPI_CMD_RESP_BIT)) {
262              TLOGE("invalid response 0x%08x\n", shm_hdr_cmd);
263              return ERR_BAD_STATE;
264          }
265          rc = translate_srv_err(shm_hdr_status);
266          if (rc != NO_ERROR) {
267              return rc;
268          }
269  
270          switch (shm_hdr_cmd & SPI_CMD_OP_MASK) {
271          case SPI_CMD_SHM_OP_XFER:
272              /* skip xfer_resp and payload */
273              xfer_resp = mb_advance_pos(shm, sizeof(*xfer_resp));
274              xfer_resp_len = READ_ONCE(xfer_resp->len);
275              mb_advance_pos(shm, xfer_resp_len);
276              break;
277          case SPI_CMD_SHM_OP_CS_ASSERT:
278          case SPI_CMD_SHM_OP_CS_DEASSERT:
279              break;
280          case SPI_CMD_SHM_OP_SET_CLK:
281              /* skip spi_clk_args */
282              mb_advance_pos(shm, sizeof(struct spi_clk_args));
283              break;
284          case SPI_CMD_SHM_OP_DELAY:
285              /* skip spi_delay_args */
286              mb_advance_pos(shm, sizeof(struct spi_delay_args));
287              break;
288          default:
289              TLOGE("cmd 0x%x: unknown command\n", shm_hdr_cmd);
290              return ERR_CMD_UNKNOWN;
291          }
292          (*failed)++;
293      }
294  
295      return NO_ERROR;
296  }
297  
handle_batch_resp(struct spi_dev * dev,size_t * failed)298  static int handle_batch_resp(struct spi_dev* dev, size_t* failed) {
299      int rc;
300      struct uevent evt;
301      struct spi_msg_resp resp;
302      struct spi_batch_resp batch_resp;
303  
304      rc = wait(dev->h, &evt, INFINITE_TIME);
305      if (rc != NO_ERROR) {
306          TLOGE("failed (%d) to wait for batch response\n", rc);
307          return rc;
308      }
309  
310      rc = tipc_recv2(dev->h, sizeof(resp) + sizeof(batch_resp), &resp,
311                      sizeof(resp), &batch_resp, sizeof(batch_resp));
312      if (rc < 0 || (size_t)rc != sizeof(resp) + sizeof(batch_resp)) {
313          TLOGE("failed (%d) to receive batch response\n", rc);
314          if (rc >= 0) {
315              rc = ERR_BAD_LEN;
316          }
317          return rc;
318      }
319  
320      rc = translate_srv_err(resp.status);
321      if (rc != NO_ERROR) {
322          TLOGE("batch request encountered an error\n");
323          *failed = batch_resp.failed;
324          return rc;
325      }
326  
327      return validate_batch_resp(&batch_resp, &dev->shm, failed);
328  }
329  
spi_exec_cmds(struct spi_dev * dev,size_t * failed)330  int spi_exec_cmds(struct spi_dev* dev, size_t* failed) {
331      int rc;
332      size_t fake_failed;
333  
334      if (!is_initialized(dev)) {
335          return ERR_INVALID_ARGS;
336      }
337  
338      if (!failed) {
339          failed = &fake_failed;
340      }
341      *failed = 0;
342  
343      if (dev->config_err) {
344          rc = ERR_BAD_STATE;
345          *failed = dev->num_cmds;
346          goto out;
347      }
348  
349      rc = send_batch_req(dev);
350      if (rc != NO_ERROR) {
351          goto out;
352      }
353  
354      rc = handle_batch_resp(dev, failed);
355  
356  out:
357      /* reset SPI requests */
358      spi_clear_cmds(dev);
359      return rc;
360  }
361  
spi_add_cmd(struct spi_dev * dev,uint32_t cmd,void ** args,size_t args_len,void ** payload,size_t payload_len)362  static int spi_add_cmd(struct spi_dev* dev,
363                         uint32_t cmd,
364                         void** args,
365                         size_t args_len,
366                         void** payload,
367                         size_t payload_len) {
368      int rc;
369      struct spi_shm_hdr* shm_hdr;
370  
371      assert(args || !args_len);
372      assert(payload || !payload_len);
373      assert(args_len <= SPI_CMD_SHM_ARGS_MAX_SIZE);
374  
375      if (!is_initialized(dev)) {
376          rc = ERR_BAD_HANDLE;
377          goto err_init;
378      }
379      if (dev->config_err) {
380          rc = ERR_BAD_STATE;
381          goto err_config;
382      }
383      if (dev->num_cmds >= dev->max_num_cmds) {
384          rc = ERR_OUT_OF_RANGE;
385          goto err_range;
386      }
387  
388      shm_hdr = mb_advance_pos(&dev->shm, sizeof(*shm_hdr));
389      if (!shm_hdr) {
390          rc = ERR_TOO_BIG;
391          goto err_shm_hdr;
392      }
393      WRITE_ONCE(shm_hdr->cmd, cmd);
394      WRITE_ONCE(shm_hdr->status, 0);
395  
396      if (args) {
397          *args = mb_advance_pos(&dev->shm, args_len);
398          if (!*args) {
399              rc = ERR_TOO_BIG;
400              goto err_args;
401          }
402      }
403      if (payload) {
404          assert(dev->total_payload <= dev->max_total_payload);
405          if (payload_len > dev->max_total_payload - dev->total_payload) {
406              rc = ERR_TOO_BIG;
407              goto err_payload;
408          }
409          dev->total_payload += payload_len;
410  
411          *payload = mb_advance_pos(&dev->shm, payload_len);
412          assert(*payload);
413      }
414  
415      dev->num_cmds++;
416      return NO_ERROR;
417  
418  err_payload:
419      *args = NULL;
420  err_args:
421  err_shm_hdr:
422  err_range:
423      dev->config_err = true;
424  err_config:
425  err_init:
426      return rc;
427  }
428  
spi_add_data_xfer_cmd(struct spi_dev * dev,void ** tx,void ** rx,size_t len)429  int spi_add_data_xfer_cmd(struct spi_dev* dev,
430                            void** tx,
431                            void** rx,
432                            size_t len) {
433      int rc;
434      struct spi_xfer_args* args;
435      uint32_t flags;
436      void* payload;
437  
438      rc = spi_add_cmd(dev, SPI_CMD_SHM_OP_XFER, (void**)&args, sizeof(*args),
439                       &payload, len);
440      if (rc != NO_ERROR) {
441          return rc;
442      }
443  
444      flags = (tx ? SPI_XFER_FLAGS_TX : 0) | (rx ? SPI_XFER_FLAGS_RX : 0);
445      WRITE_ONCE(args->len, len);
446      WRITE_ONCE(args->flags, flags);
447  
448      if (tx) {
449          *tx = payload;
450      }
451      if (rx) {
452          *rx = payload;
453      }
454  
455      return NO_ERROR;
456  }
457  
spi_add_cs_assert_cmd(struct spi_dev * dev)458  int spi_add_cs_assert_cmd(struct spi_dev* dev) {
459      return spi_add_cmd(dev, SPI_CMD_SHM_OP_CS_ASSERT, NULL, 0, NULL, 0);
460  }
461  
spi_add_cs_deassert_cmd(struct spi_dev * dev)462  int spi_add_cs_deassert_cmd(struct spi_dev* dev) {
463      return spi_add_cmd(dev, SPI_CMD_SHM_OP_CS_DEASSERT, NULL, 0, NULL, 0);
464  }
465  
spi_add_set_clk_cmd(struct spi_dev * dev,uint64_t clk_hz_in,uint64_t ** clk_hz_out)466  int spi_add_set_clk_cmd(struct spi_dev* dev,
467                          uint64_t clk_hz_in,
468                          uint64_t** clk_hz_out) {
469      int rc;
470      struct spi_clk_args* args;
471  
472      rc = spi_add_cmd(dev, SPI_CMD_SHM_OP_SET_CLK, (void**)&args, sizeof(*args),
473                       NULL, 0);
474      if (rc != NO_ERROR) {
475          return rc;
476      }
477  
478      WRITE_ONCE(args->clk_hz, clk_hz_in);
479  
480      if (clk_hz_out) {
481          *clk_hz_out = &args->clk_hz;
482      }
483  
484      return NO_ERROR;
485  }
486  
spi_add_delay_cmd(struct spi_dev * dev,uint64_t delay_ns)487  int spi_add_delay_cmd(struct spi_dev* dev, uint64_t delay_ns) {
488      int rc;
489      struct spi_delay_args* args;
490  
491      rc = spi_add_cmd(dev, SPI_CMD_SHM_OP_DELAY, (void**)&args, sizeof(*args),
492                       NULL, 0);
493      if (rc != NO_ERROR) {
494          return rc;
495      }
496  
497      WRITE_ONCE(args->delay_ns, delay_ns);
498  
499      return NO_ERROR;
500  }
501