/ src / piolib / piolib.c
piolib.c
  1  // SPDX-License-Identifier: GPL-2.0
  2  /*
  3   * Copyright (c) 2023-24 Raspberry Pi Ltd.
  4   * All rights reserved.
  5   */
  6  #include <errno.h>
  7  #include <fcntl.h>
  8  #include <pthread.h>
  9  #include <stdio.h>
 10  #include <stdlib.h>
 11  #include <string.h>
 12  #include <sys/stat.h>
 13  #include <sys/types.h>
 14  #include <time.h>
 15  #include <unistd.h>
 16  
 17  #include "piolib.h"
 18  #include "piolib_priv.h"
 19  
 20  #define PIO_MAX_INSTANCES 4
 21  
 22  extern PIO_CHIP_T *__start_piochips;
 23  extern PIO_CHIP_T *__stop_piochips;
 24  
 25  static __thread PIO __pio;
 26  
 27  static PIO pio_instances[PIO_MAX_INSTANCES];
 28  static uint num_instances;
 29  static pthread_mutex_t pio_handle_lock;
 30  
 31  void pio_select(PIO pio) { __pio = pio; }
 32  
 33  PIO pio_get_current(void) {
 34      PIO pio = __pio;
 35      check_pio_param(pio);
 36      return pio;
 37  }
 38  
 39  int pio_get_index(PIO pio) {
 40      int i;
 41      for (i = 0; i < PIO_MAX_INSTANCES; i++) {
 42          if (pio == pio_instances[i])
 43              return i;
 44      }
 45      return -1;
 46  }
 47  
 48  int pio_init(void) {
 49      static bool initialised;
 50      const PIO_CHIP_T *const *p;
 51      uint i = 0;
 52      int err;
 53  
 54      if (initialised)
 55          return 0;
 56      num_instances = 0;
 57      p = &__start_piochips;
 58      while (p < &__stop_piochips && num_instances < PIO_MAX_INSTANCES) {
 59          PIO_CHIP_T *chip = *p;
 60          PIO pio = chip->create_instance(chip, i);
 61          if (pio && !PIO_IS_ERR(pio)) {
 62              pio_instances[num_instances++] = pio;
 63              i++;
 64          } else {
 65              p++;
 66              i = 0;
 67          }
 68      }
 69  
 70      err = pthread_mutex_init(&pio_handle_lock, NULL);
 71      if (err)
 72          return err;
 73  
 74      initialised = true;
 75      return 0;
 76  }
 77  
 78  PIO pio_open(uint idx) {
 79      PIO pio = NULL;
 80      int err;
 81  
 82      err = pio_init();
 83      if (err)
 84          return PIO_ERR(err);
 85  
 86      if (idx >= num_instances)
 87          return PIO_ERR(-EINVAL);
 88  
 89      pthread_mutex_lock(&pio_handle_lock);
 90  
 91      pio = pio_instances[idx];
 92      if (pio) {
 93          if (pio->in_use)
 94              err = -EBUSY;
 95          else
 96              pio->in_use = 1;
 97      }
 98  
 99      pthread_mutex_unlock(&pio_handle_lock);
100  
101      if (err)
102          return PIO_ERR(err);
103  
104      err = pio->chip->open_instance(pio);
105      if (err) {
106          pio->in_use = 0;
107          return PIO_ERR(err);
108      }
109  
110      pio_select(pio);
111  
112      return pio;
113  }
114  
115  PIO pio_open_by_name(const char *name) {
116      int err = -ENOENT;
117      uint i;
118  
119      err = pio_init();
120      if (err)
121          return PIO_ERR(err);
122  
123      for (i = 0; i < num_instances; i++) {
124          PIO p = pio_instances[i];
125          if (!strcmp(name, p->chip->name))
126              break;
127      }
128  
129      if (i == num_instances)
130          return PIO_ERR(-ENOENT);
131  
132      return pio_open(i);
133  }
134  
135  PIO pio_open_helper(uint idx) {
136      PIO pio = pio_instances[idx];
137      if (!pio || !pio->in_use) {
138          pio = pio_open(idx);
139          if (PIO_IS_ERR(pio)) {
140              printf("* Failed to open PIO device %d (error %d)\n", idx,
141                     PIO_ERR_VAL(pio));
142              exit(1);
143          }
144      }
145      return pio;
146  }
147  
148  void pio_close(PIO pio) {
149      pio->chip->close_instance(pio);
150      pthread_mutex_lock(&pio_handle_lock);
151      pio->in_use = 0;
152      pthread_mutex_unlock(&pio_handle_lock);
153  }
154  
155  void pio_panic(const char *msg) {
156      fprintf(stderr, "PANIC: %s\n", msg);
157      exit(1);
158  }
159  
160  void sleep_us(uint64_t us) {
161      const struct timespec tv = {.tv_sec = (us / 1000000),
162                                  .tv_nsec = 1000ull * (us % 1000000)};
163      nanosleep(&tv, NULL);
164  }