text: // Copyright 2019 The Fuchsia Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include <ctype.h>
  #include <errno.h>
  #include <fcntl.h>
  #include <lib/fdio/fdio.h>
  #include <lib/fdio/unsafe.h>
  #include <lib/spi/spi.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <unistd.h>
  #include <zircon/status.h>
  
  void usage(char* prog) {
      fprintf(stderr, "usage:\n");
      fprintf(stderr, "    %s DEVICE r LENGTH\n", prog);
      fprintf(stderr, "    %s DEVICE w BYTES ...\n", prog);
      fprintf(stderr, "    %s DEVICE x BYTES ...\n", prog);
  }
  
  void convert_args(char** argv, size_t length, uint8_t* buffer) {
      for (size_t i = 0; i < length; i++) {
          buffer[i] = static_cast<uint8_t>(strtoul(argv[i], nullptr, 0));
      }
  }
  
  void print_buffer(uint8_t* buffer, size_t length) {
      char ascii[16];
      char* a = ascii;
      size_t i;
  
      for (i = 0; i < length; i++) {
          if (i % 16 == 0) {
              printf("%04zx: ", i);
              a = ascii;
          }
  
          printf("%02x ", buffer[i]);
          if (isprint(buffer[i])) {
              *a++ = buffer[i];
          } else {
              *a++ = '.';
          }
  
          if (i % 16 == 15) {
              printf("|%.16s|\n", ascii);
          }
          else if (i % 8 == 7) {
              printf(" ");
          }
      }
  
      int rem = i % 16;
      if (rem != 0) {
          int spaces = (16 - rem) * 3;
          if (rem < 8) {
              spaces++;
          }
          printf("%*s|%.*s|\n", spaces, "", rem, ascii);
      }
  }
  
  int main(int argc, char** argv) {
      if (argc < 4) {
          usage(argv[0]);
          return -1;
      }
  
      int fd = open(argv[1], O_RDWR);
      if (fd < 0) {
          fprintf(stderr, "%s: %s\n", argv[1], strerror(errno));
          usage(argv[0]);
          return -2;
      }
  
      fdio_t* io = fdio_unsafe_fd_to_io(fd);
      if (io == nullptr) {
          fprintf(stderr, "%s: fdio conversion failed\n", argv[1]);
          close(fd);
          return -3;
      }
  
      zx_status_t status;
  
      switch (argv[2][0]) {
          case 'r': {
              size_t length = strtoull(argv[3], nullptr, 0);
              uint8_t buffer[length];
              status = spi_receive(fdio_unsafe_borrow_channel(io), buffer, length);
              print_buffer(buffer, length);
              break;
          }
          case 'w': {
              size_t length = argc - 3;
              uint8_t buffer[length];
              convert_args(&argv[3], length, buffer);
              status = spi_transmit(fdio_unsafe_borrow_channel(io), buffer, length);
              break;
          }
          case 'x': {
              size_t length = argc - 3;
              uint8_t send[length];
              uint8_t recv[length];
              convert_args(&argv[3], length, send);
              status = spi_exchange(fdio_unsafe_borrow_channel(io), send, recv, length);
              print_buffer(recv, length);
              break;
          }
          default:
              fprintf(stderr, "%c: unrecognized command\n", argv[2][0]);
              usage(argv[0]);
              status = -4;
      }
  
      fdio_unsafe_release(io);
      close(fd);
      return status;
  }
  