Landfall

Change of pkg/devices/lib/spi/src/gpio.cc

236:779f8f6a9587
pkg/devices/lib/spi/src/gpio.cc cpm-library-improvements
     1.1 --- a/pkg/devices/lib/spi/src/gpio.cc	Mon Nov 13 01:20:09 2023 +0100
     1.2 +++ b/pkg/devices/lib/spi/src/gpio.cc	Tue Nov 14 00:02:42 2023 +0100
     1.3 @@ -19,7 +19,9 @@
     1.4   * Boston, MA  02110-1301, USA
     1.5   */
     1.6  
     1.7 +#include <l4/devices/byteorder.h>
     1.8  #include <l4/devices/spi-gpio.h>
     1.9 +#include <stdlib.h> /* NOTE: required by calloc/free */
    1.10  #include <time.h>
    1.11  
    1.12  
    1.13 @@ -58,18 +60,19 @@
    1.14  
    1.15  uint32_t Spi_gpio::send(uint32_t bytes, const uint8_t data[])
    1.16  {
    1.17 -  return send_dc(bytes, data, NULL);
    1.18 +  return send_dc(bytes, data, NULL, 8, false);
    1.19  }
    1.20  
    1.21 -/* Send a byte sequence with accompanying data/command indicators. */
    1.22 +/* Send a byte sequence with accompanying data/command indicators. Bytes are
    1.23 +   grouped into character units, with each unit holding a value with the given
    1.24 +   byte ordering. */
    1.25  
    1.26  uint32_t Spi_gpio::send_dc(uint32_t bytes, const uint8_t data[],
    1.27 -                           const int dc[])
    1.28 +                           const int dc[], uint8_t char_size, bool big_endian)
    1.29  {
    1.30    struct timespec ts;
    1.31 -  uint8_t mask;
    1.32 -  int bit;
    1.33 -  uint32_t byte;
    1.34 +  uint32_t offset, char_unit;
    1.35 +  uint8_t char_unit_size = ((char_size ? char_size - 1 : 0) / 8) + 1;
    1.36  
    1.37    if (_frequency)
    1.38    {
    1.39 @@ -83,27 +86,34 @@
    1.40    _clock_device->set(_clock_pin, 1);
    1.41    _data_device->set(_data_pin, 0);
    1.42  
    1.43 +  if ((_control_device != NULL) && (_control_pin >= 0) && (dc != NULL))
    1.44 +    _control_device->set(_control_pin, dc[0] ? 1 : 0);
    1.45 +
    1.46    /* Enter the transmission state. */
    1.47  
    1.48    _enable_device->set(_enable_pin, 0);
    1.49  
    1.50    /* Clock data using the clock and data outputs. */
    1.51  
    1.52 -  for (byte = 0; byte < bytes; byte++)
    1.53 +  for (offset = 0, char_unit = 0; offset < bytes; offset += char_unit_size, char_unit++)
    1.54    {
    1.55 -    mask = 0x80;
    1.56 +    uint32_t mask, value = get_stored_value(&data[offset], char_unit_size, big_endian);
    1.57 +
    1.58 +    /* Set the control level, if appropriate. */
    1.59  
    1.60 -    for (bit = 0; bit < 8; bit++)
    1.61 +    if ((_control_device != NULL) && (_control_pin >= 0) && (dc != NULL))
    1.62 +      _control_device->set(_control_pin, dc[char_unit] ? 1 : 0);
    1.63 +
    1.64 +    mask = 1 << (char_size - 1);
    1.65 +
    1.66 +    for (uint8_t bit = 0; bit < char_size; bit++)
    1.67      {
    1.68        /* NOTE: Data presented on falling clock level and sampled on rising clock
    1.69                 level. This is SPI mode 3, or 0 given that the enable level is
    1.70                 driven low immediately before the first bit is presented. */
    1.71  
    1.72        _clock_device->set(_clock_pin, 0);
    1.73 -      _data_device->set(_data_pin, data[byte] & mask ? 1 : 0);
    1.74 -
    1.75 -      if ((_control_device != NULL) && (_control_pin >= 0) && (dc != NULL))
    1.76 -        _control_device->set(_control_pin, dc[byte] ? 1 : 0);
    1.77 +      _data_device->set(_data_pin, value & mask ? 1 : 0);
    1.78  
    1.79        if (_frequency)
    1.80          nanosleep(&ts, NULL);
    1.81 @@ -122,45 +132,44 @@
    1.82    return bytes;
    1.83  }
    1.84  
    1.85 -/* Send a number of units, each holding a value in a big endian arrangement
    1.86 -   limited to the given character size, with any bit immediately above the
    1.87 -   character portion of the unit indicating the data/command level. */
    1.88 +/* Send a number of units, each holding a value limited to the given character
    1.89 +   size, with any bit immediately above the character portion of the unit
    1.90 +   indicating the data/command level. */
    1.91  
    1.92  uint32_t Spi_gpio::send_units(uint32_t bytes, const uint8_t data[],
    1.93 -                              uint8_t unit_size, uint8_t char_size)
    1.94 +                              uint8_t unit_size, uint8_t char_size,
    1.95 +                              bool big_endian)
    1.96  {
    1.97 -  /* Obtain the data/command values for each unit. */
    1.98 +  uint32_t char_units = bytes / unit_size;
    1.99 +  uint8_t char_unit_size = ((char_size ? char_size - 1 : 0) / 8) + 1;
   1.100  
   1.101 -  uint32_t chars = bytes / unit_size;
   1.102 -  int dc[chars];
   1.103 -
   1.104 +  /* Obtain the data/command values for each unit. */
   1.105    /* Obtain the actual character bytes to be sent. */
   1.106  
   1.107 -  int char_unit_size = ((char_size ? char_size - 1 : 0) / 8) + 1;
   1.108 -  uint8_t char_data[char_unit_size * chars];
   1.109 +  /* NOTE: Work around stack allocation limitations in L4Re. We would want to
   1.110 +           use a static allocation as follows:
   1.111 +
   1.112 +  int dc[char_units];
   1.113 +  uint8_t char_data[char_unit_size * char_units];
   1.114 +  */
   1.115 +
   1.116 +  int *dc = (int *) calloc(char_units, sizeof(int));
   1.117 +  uint8_t *char_data = (uint8_t *) calloc(char_units, char_unit_size);
   1.118  
   1.119    /* Traverse the byte sequence, extracting data/command bits for each unit. */
   1.120  
   1.121 -  for (uint32_t offset = 0, unit = 0, char_offset = 0; unit < chars;
   1.122 -       offset += unit_size, unit++, char_offset += char_unit_size)
   1.123 +  for (uint32_t offset = 0, char_unit = 0, char_offset = 0; char_unit < char_units;
   1.124 +       offset += unit_size, char_unit++, char_offset += char_unit_size)
   1.125    {
   1.126      /* Obtain the unit value. */
   1.127  
   1.128 -    uint32_t value = 0;
   1.129 -
   1.130 -    for (uint8_t byte = 0; byte < unit_size; byte++)
   1.131 -      value = (value << 8) | data[offset + byte];
   1.132 +    uint32_t value = get_stored_value(&data[offset], unit_size, big_endian);
   1.133  
   1.134      /* The unit size must be greater than the character size for data/command
   1.135         bits to be present. */
   1.136  
   1.137 -    if (unit_size * 8 <= char_size)
   1.138 -      dc[unit] = 0;
   1.139 -
   1.140 -    /* Extract the data/command level. */
   1.141 -
   1.142 -    else
   1.143 -      dc[unit] = value & (1 << char_size) ? 1 : 0;
   1.144 +    if (unit_size * 8 > char_size)
   1.145 +      dc[char_unit] = value & (1 << char_size) ? 1 : 0;
   1.146  
   1.147      /* Obtain the actual character data from the input data, discarding the most
   1.148         significant bytes beyond the character and masking out any remaining
   1.149 @@ -168,14 +177,38 @@
   1.150  
   1.151      value = value & ((1 << char_size) - 1);
   1.152  
   1.153 -    for (uint8_t byte = char_unit_size; byte != 0; byte--)
   1.154 -    {
   1.155 -      char_data[char_offset + byte - 1] = value & 0xff;
   1.156 -      value >>= 8;
   1.157 -    }
   1.158 +    set_stored_value(value, &char_data[char_offset], char_unit_size, big_endian);
   1.159    }
   1.160  
   1.161 -  return send_dc(chars, char_data, dc);
   1.162 +  /* Discard the data/command values if they will not be used. */
   1.163 +
   1.164 +  uint32_t result = send_dc(char_units * char_unit_size, char_data,
   1.165 +                            (unit_size * 8 <= char_size) ? NULL : dc,
   1.166 +                            char_size, big_endian);
   1.167 +
   1.168 +  /* NOTE: Working around allocation limitations with dc... */
   1.169 +
   1.170 +  free(dc);
   1.171 +  free(char_data);
   1.172 +  return result;
   1.173 +}
   1.174 +
   1.175 +/* Perform a transfer, ignoring any DMA-specific parameters. */
   1.176 +
   1.177 +uint32_t Spi_gpio::transfer(l4_addr_t vaddr,
   1.178 +                            l4re_dma_space_dma_addr_t paddr,
   1.179 +                            uint32_t count, uint8_t unit_size,
   1.180 +                            uint8_t char_size,
   1.181 +                            l4_addr_t desc_vaddr,
   1.182 +                            l4re_dma_space_dma_addr_t desc_paddr)
   1.183 +{
   1.184 +  (void) paddr; (void) desc_vaddr; (void) desc_paddr;
   1.185 +
   1.186 +  /* Assume little endian data is being transferred, being the native data
   1.187 +     representation. */
   1.188 +
   1.189 +  return send_units(count, (const uint8_t *) vaddr, unit_size, char_size,
   1.190 +                    false);
   1.191  }
   1.192  
   1.193  
   1.194 @@ -201,13 +234,34 @@
   1.195  }
   1.196  
   1.197  uint32_t spi_gpio_send_dc(void *channel, uint32_t bytes, const uint8_t data[],
   1.198 -                          const int dc[])
   1.199 +                          const int dc[], uint8_t char_size, int big_endian)
   1.200  {
   1.201 -  return static_cast<Spi_gpio *>(channel)->send_dc(bytes, data, dc);
   1.202 +  return static_cast<Spi_gpio *>(channel)->send_dc(bytes, data, dc, char_size,
   1.203 +    big_endian);
   1.204  }
   1.205  
   1.206  uint32_t spi_gpio_send_units(void *channel, uint32_t bytes, const uint8_t data[],
   1.207 -                             uint8_t unit_size, uint8_t char_size)
   1.208 +                             uint8_t unit_size, uint8_t char_size, int big_endian)
   1.209  {
   1.210 -  return static_cast<Spi_gpio *>(channel)->send_units(bytes, data, unit_size, char_size);
   1.211 +  return static_cast<Spi_gpio *>(channel)->send_units(bytes, data, unit_size,
   1.212 +    char_size, big_endian);
   1.213  }
   1.214 +
   1.215 +uint32_t spi_gpio_transfer(void *channel, l4_addr_t vaddr,
   1.216 +                           l4re_dma_space_dma_addr_t paddr,
   1.217 +                           uint32_t count, uint8_t unit_size,
   1.218 +                           uint8_t char_size)
   1.219 +{ 
   1.220 +  return static_cast<Spi_gpio *>(channel)->transfer(vaddr, paddr, count,
   1.221 +    unit_size, char_size);
   1.222 +}
   1.223 +
   1.224 +uint32_t spi_gpio_transfer_descriptor(void *channel, l4_addr_t vaddr,
   1.225 +                                      l4re_dma_space_dma_addr_t paddr,
   1.226 +                                      uint32_t count, uint8_t unit_size,
   1.227 +                                      uint8_t char_size, l4_addr_t desc_vaddr,
   1.228 +                                      l4re_dma_space_dma_addr_t desc_paddr)
   1.229 +{
   1.230 +  return static_cast<Spi_gpio *>(channel)->transfer(vaddr, paddr, count,
   1.231 +    unit_size, char_size, desc_vaddr, desc_paddr);    
   1.232 +}