Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * SPI testing utility (using spidev driver)
0004  *
0005  * Copyright (c) 2007  MontaVista Software, Inc.
0006  * Copyright (c) 2007  Anton Vorontsov <avorontsov@ru.mvista.com>
0007  *
0008  * Cross-compile with cross-gcc -I/path/to/cross-kernel/include
0009  */
0010 
0011 #include <stdint.h>
0012 #include <unistd.h>
0013 #include <stdio.h>
0014 #include <stdlib.h>
0015 #include <string.h>
0016 #include <errno.h>
0017 #include <getopt.h>
0018 #include <fcntl.h>
0019 #include <time.h>
0020 #include <sys/ioctl.h>
0021 #include <linux/ioctl.h>
0022 #include <sys/stat.h>
0023 #include <linux/types.h>
0024 #include <linux/spi/spidev.h>
0025 
0026 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
0027 
0028 static void pabort(const char *s)
0029 {
0030     if (errno != 0)
0031         perror(s);
0032     else
0033         printf("%s\n", s);
0034 
0035     abort();
0036 }
0037 
0038 static const char *device = "/dev/spidev1.1";
0039 static uint32_t mode;
0040 static uint8_t bits = 8;
0041 static char *input_file;
0042 static char *output_file;
0043 static uint32_t speed = 500000;
0044 static uint16_t delay;
0045 static int verbose;
0046 static int transfer_size;
0047 static int iterations;
0048 static int interval = 5; /* interval in seconds for showing transfer rate */
0049 
0050 static uint8_t default_tx[] = {
0051     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0052     0x40, 0x00, 0x00, 0x00, 0x00, 0x95,
0053     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0054     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0055     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0056     0xF0, 0x0D,
0057 };
0058 
0059 static uint8_t default_rx[ARRAY_SIZE(default_tx)] = {0, };
0060 static char *input_tx;
0061 
0062 static void hex_dump(const void *src, size_t length, size_t line_size,
0063              char *prefix)
0064 {
0065     int i = 0;
0066     const unsigned char *address = src;
0067     const unsigned char *line = address;
0068     unsigned char c;
0069 
0070     printf("%s | ", prefix);
0071     while (length-- > 0) {
0072         printf("%02X ", *address++);
0073         if (!(++i % line_size) || (length == 0 && i % line_size)) {
0074             if (length == 0) {
0075                 while (i++ % line_size)
0076                     printf("__ ");
0077             }
0078             printf(" |");
0079             while (line < address) {
0080                 c = *line++;
0081                 printf("%c", (c < 32 || c > 126) ? '.' : c);
0082             }
0083             printf("|\n");
0084             if (length > 0)
0085                 printf("%s | ", prefix);
0086         }
0087     }
0088 }
0089 
0090 /*
0091  *  Unescape - process hexadecimal escape character
0092  *      converts shell input "\x23" -> 0x23
0093  */
0094 static int unescape(char *_dst, char *_src, size_t len)
0095 {
0096     int ret = 0;
0097     int match;
0098     char *src = _src;
0099     char *dst = _dst;
0100     unsigned int ch;
0101 
0102     while (*src) {
0103         if (*src == '\\' && *(src+1) == 'x') {
0104             match = sscanf(src + 2, "%2x", &ch);
0105             if (!match)
0106                 pabort("malformed input string");
0107 
0108             src += 4;
0109             *dst++ = (unsigned char)ch;
0110         } else {
0111             *dst++ = *src++;
0112         }
0113         ret++;
0114     }
0115     return ret;
0116 }
0117 
0118 static void transfer(int fd, uint8_t const *tx, uint8_t const *rx, size_t len)
0119 {
0120     int ret;
0121     int out_fd;
0122     struct spi_ioc_transfer tr = {
0123         .tx_buf = (unsigned long)tx,
0124         .rx_buf = (unsigned long)rx,
0125         .len = len,
0126         .delay_usecs = delay,
0127         .speed_hz = speed,
0128         .bits_per_word = bits,
0129     };
0130 
0131     if (mode & SPI_TX_OCTAL)
0132         tr.tx_nbits = 8;
0133     else if (mode & SPI_TX_QUAD)
0134         tr.tx_nbits = 4;
0135     else if (mode & SPI_TX_DUAL)
0136         tr.tx_nbits = 2;
0137     if (mode & SPI_RX_OCTAL)
0138         tr.rx_nbits = 8;
0139     else if (mode & SPI_RX_QUAD)
0140         tr.rx_nbits = 4;
0141     else if (mode & SPI_RX_DUAL)
0142         tr.rx_nbits = 2;
0143     if (!(mode & SPI_LOOP)) {
0144         if (mode & (SPI_TX_OCTAL | SPI_TX_QUAD | SPI_TX_DUAL))
0145             tr.rx_buf = 0;
0146         else if (mode & (SPI_RX_OCTAL | SPI_RX_QUAD | SPI_RX_DUAL))
0147             tr.tx_buf = 0;
0148     }
0149 
0150     ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
0151     if (ret < 1)
0152         pabort("can't send spi message");
0153 
0154     if (verbose)
0155         hex_dump(tx, len, 32, "TX");
0156 
0157     if (output_file) {
0158         out_fd = open(output_file, O_WRONLY | O_CREAT | O_TRUNC, 0666);
0159         if (out_fd < 0)
0160             pabort("could not open output file");
0161 
0162         ret = write(out_fd, rx, len);
0163         if (ret != len)
0164             pabort("not all bytes written to output file");
0165 
0166         close(out_fd);
0167     }
0168 
0169     if (verbose)
0170         hex_dump(rx, len, 32, "RX");
0171 }
0172 
0173 static void print_usage(const char *prog)
0174 {
0175     printf("Usage: %s [-DsbdlHOLC3vpNR24SI]\n", prog);
0176     puts("  -D --device   device to use (default /dev/spidev1.1)\n"
0177          "  -s --speed    max speed (Hz)\n"
0178          "  -d --delay    delay (usec)\n"
0179          "  -b --bpw      bits per word\n"
0180          "  -i --input    input data from a file (e.g. \"test.bin\")\n"
0181          "  -o --output   output data to a file (e.g. \"results.bin\")\n"
0182          "  -l --loop     loopback\n"
0183          "  -H --cpha     clock phase\n"
0184          "  -O --cpol     clock polarity\n"
0185          "  -L --lsb      least significant bit first\n"
0186          "  -C --cs-high  chip select active high\n"
0187          "  -3 --3wire    SI/SO signals shared\n"
0188          "  -v --verbose  Verbose (show tx buffer)\n"
0189          "  -p            Send data (e.g. \"1234\\xde\\xad\")\n"
0190          "  -N --no-cs    no chip select\n"
0191          "  -R --ready    slave pulls low to pause\n"
0192          "  -2 --dual     dual transfer\n"
0193          "  -4 --quad     quad transfer\n"
0194          "  -8 --octal    octal transfer\n"
0195          "  -S --size     transfer size\n"
0196          "  -I --iter     iterations\n");
0197     exit(1);
0198 }
0199 
0200 static void parse_opts(int argc, char *argv[])
0201 {
0202     while (1) {
0203         static const struct option lopts[] = {
0204             { "device",  1, 0, 'D' },
0205             { "speed",   1, 0, 's' },
0206             { "delay",   1, 0, 'd' },
0207             { "bpw",     1, 0, 'b' },
0208             { "input",   1, 0, 'i' },
0209             { "output",  1, 0, 'o' },
0210             { "loop",    0, 0, 'l' },
0211             { "cpha",    0, 0, 'H' },
0212             { "cpol",    0, 0, 'O' },
0213             { "lsb",     0, 0, 'L' },
0214             { "cs-high", 0, 0, 'C' },
0215             { "3wire",   0, 0, '3' },
0216             { "no-cs",   0, 0, 'N' },
0217             { "ready",   0, 0, 'R' },
0218             { "dual",    0, 0, '2' },
0219             { "verbose", 0, 0, 'v' },
0220             { "quad",    0, 0, '4' },
0221             { "octal",   0, 0, '8' },
0222             { "size",    1, 0, 'S' },
0223             { "iter",    1, 0, 'I' },
0224             { NULL, 0, 0, 0 },
0225         };
0226         int c;
0227 
0228         c = getopt_long(argc, argv, "D:s:d:b:i:o:lHOLC3NR248p:vS:I:",
0229                 lopts, NULL);
0230 
0231         if (c == -1)
0232             break;
0233 
0234         switch (c) {
0235         case 'D':
0236             device = optarg;
0237             break;
0238         case 's':
0239             speed = atoi(optarg);
0240             break;
0241         case 'd':
0242             delay = atoi(optarg);
0243             break;
0244         case 'b':
0245             bits = atoi(optarg);
0246             break;
0247         case 'i':
0248             input_file = optarg;
0249             break;
0250         case 'o':
0251             output_file = optarg;
0252             break;
0253         case 'l':
0254             mode |= SPI_LOOP;
0255             break;
0256         case 'H':
0257             mode |= SPI_CPHA;
0258             break;
0259         case 'O':
0260             mode |= SPI_CPOL;
0261             break;
0262         case 'L':
0263             mode |= SPI_LSB_FIRST;
0264             break;
0265         case 'C':
0266             mode |= SPI_CS_HIGH;
0267             break;
0268         case '3':
0269             mode |= SPI_3WIRE;
0270             break;
0271         case 'N':
0272             mode |= SPI_NO_CS;
0273             break;
0274         case 'v':
0275             verbose = 1;
0276             break;
0277         case 'R':
0278             mode |= SPI_READY;
0279             break;
0280         case 'p':
0281             input_tx = optarg;
0282             break;
0283         case '2':
0284             mode |= SPI_TX_DUAL;
0285             break;
0286         case '4':
0287             mode |= SPI_TX_QUAD;
0288             break;
0289         case '8':
0290             mode |= SPI_TX_OCTAL;
0291             break;
0292         case 'S':
0293             transfer_size = atoi(optarg);
0294             break;
0295         case 'I':
0296             iterations = atoi(optarg);
0297             break;
0298         default:
0299             print_usage(argv[0]);
0300         }
0301     }
0302     if (mode & SPI_LOOP) {
0303         if (mode & SPI_TX_DUAL)
0304             mode |= SPI_RX_DUAL;
0305         if (mode & SPI_TX_QUAD)
0306             mode |= SPI_RX_QUAD;
0307         if (mode & SPI_TX_OCTAL)
0308             mode |= SPI_RX_OCTAL;
0309     }
0310 }
0311 
0312 static void transfer_escaped_string(int fd, char *str)
0313 {
0314     size_t size = strlen(str);
0315     uint8_t *tx;
0316     uint8_t *rx;
0317 
0318     tx = malloc(size);
0319     if (!tx)
0320         pabort("can't allocate tx buffer");
0321 
0322     rx = malloc(size);
0323     if (!rx)
0324         pabort("can't allocate rx buffer");
0325 
0326     size = unescape((char *)tx, str, size);
0327     transfer(fd, tx, rx, size);
0328     free(rx);
0329     free(tx);
0330 }
0331 
0332 static void transfer_file(int fd, char *filename)
0333 {
0334     ssize_t bytes;
0335     struct stat sb;
0336     int tx_fd;
0337     uint8_t *tx;
0338     uint8_t *rx;
0339 
0340     if (stat(filename, &sb) == -1)
0341         pabort("can't stat input file");
0342 
0343     tx_fd = open(filename, O_RDONLY);
0344     if (tx_fd < 0)
0345         pabort("can't open input file");
0346 
0347     tx = malloc(sb.st_size);
0348     if (!tx)
0349         pabort("can't allocate tx buffer");
0350 
0351     rx = malloc(sb.st_size);
0352     if (!rx)
0353         pabort("can't allocate rx buffer");
0354 
0355     bytes = read(tx_fd, tx, sb.st_size);
0356     if (bytes != sb.st_size)
0357         pabort("failed to read input file");
0358 
0359     transfer(fd, tx, rx, sb.st_size);
0360     free(rx);
0361     free(tx);
0362     close(tx_fd);
0363 }
0364 
0365 static uint64_t _read_count;
0366 static uint64_t _write_count;
0367 
0368 static void show_transfer_rate(void)
0369 {
0370     static uint64_t prev_read_count, prev_write_count;
0371     double rx_rate, tx_rate;
0372 
0373     rx_rate = ((_read_count - prev_read_count) * 8) / (interval*1000.0);
0374     tx_rate = ((_write_count - prev_write_count) * 8) / (interval*1000.0);
0375 
0376     printf("rate: tx %.1fkbps, rx %.1fkbps\n", rx_rate, tx_rate);
0377 
0378     prev_read_count = _read_count;
0379     prev_write_count = _write_count;
0380 }
0381 
0382 static void transfer_buf(int fd, int len)
0383 {
0384     uint8_t *tx;
0385     uint8_t *rx;
0386     int i;
0387 
0388     tx = malloc(len);
0389     if (!tx)
0390         pabort("can't allocate tx buffer");
0391     for (i = 0; i < len; i++)
0392         tx[i] = random();
0393 
0394     rx = malloc(len);
0395     if (!rx)
0396         pabort("can't allocate rx buffer");
0397 
0398     transfer(fd, tx, rx, len);
0399 
0400     _write_count += len;
0401     _read_count += len;
0402 
0403     if (mode & SPI_LOOP) {
0404         if (memcmp(tx, rx, len)) {
0405             fprintf(stderr, "transfer error !\n");
0406             hex_dump(tx, len, 32, "TX");
0407             hex_dump(rx, len, 32, "RX");
0408             exit(1);
0409         }
0410     }
0411 
0412     free(rx);
0413     free(tx);
0414 }
0415 
0416 int main(int argc, char *argv[])
0417 {
0418     int ret = 0;
0419     int fd;
0420     uint32_t request;
0421 
0422     parse_opts(argc, argv);
0423 
0424     if (input_tx && input_file)
0425         pabort("only one of -p and --input may be selected");
0426 
0427     fd = open(device, O_RDWR);
0428     if (fd < 0)
0429         pabort("can't open device");
0430 
0431     /*
0432      * spi mode
0433      */
0434     /* WR is make a request to assign 'mode' */
0435     request = mode;
0436     ret = ioctl(fd, SPI_IOC_WR_MODE32, &mode);
0437     if (ret == -1)
0438         pabort("can't set spi mode");
0439 
0440     /* RD is read what mode the device actually is in */
0441     ret = ioctl(fd, SPI_IOC_RD_MODE32, &mode);
0442     if (ret == -1)
0443         pabort("can't get spi mode");
0444     /* Drivers can reject some mode bits without returning an error.
0445      * Read the current value to identify what mode it is in, and if it
0446      * differs from the requested mode, warn the user.
0447      */
0448     if (request != mode)
0449         printf("WARNING device does not support requested mode 0x%x\n",
0450             request);
0451 
0452     /*
0453      * bits per word
0454      */
0455     ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
0456     if (ret == -1)
0457         pabort("can't set bits per word");
0458 
0459     ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
0460     if (ret == -1)
0461         pabort("can't get bits per word");
0462 
0463     /*
0464      * max speed hz
0465      */
0466     ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
0467     if (ret == -1)
0468         pabort("can't set max speed hz");
0469 
0470     ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
0471     if (ret == -1)
0472         pabort("can't get max speed hz");
0473 
0474     printf("spi mode: 0x%x\n", mode);
0475     printf("bits per word: %u\n", bits);
0476     printf("max speed: %u Hz (%u kHz)\n", speed, speed/1000);
0477 
0478     if (input_tx)
0479         transfer_escaped_string(fd, input_tx);
0480     else if (input_file)
0481         transfer_file(fd, input_file);
0482     else if (transfer_size) {
0483         struct timespec last_stat;
0484 
0485         clock_gettime(CLOCK_MONOTONIC, &last_stat);
0486 
0487         while (iterations-- > 0) {
0488             struct timespec current;
0489 
0490             transfer_buf(fd, transfer_size);
0491 
0492             clock_gettime(CLOCK_MONOTONIC, &current);
0493             if (current.tv_sec - last_stat.tv_sec > interval) {
0494                 show_transfer_rate();
0495                 last_stat = current;
0496             }
0497         }
0498         printf("total: tx %.1fKB, rx %.1fKB\n",
0499                _write_count/1024.0, _read_count/1024.0);
0500     } else
0501         transfer(fd, default_tx, default_rx, sizeof(default_tx));
0502 
0503     close(fd);
0504 
0505     return ret;
0506 }