1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * SPI testing utility (using spidev driver) 4 * 5 * Copyright (c) 2007 MontaVista Software, Inc. 6 * Copyright (c) 2007 Anton Vorontsov <avorontsov@ru.mvista.com> 7 */ 8 9 #include <stdint.h> 10 #include <unistd.h> 11 #include <stdio.h> 12 #include <stdlib.h> 13 #include <string.h> 14 #include <ctype.h> 15 #include <errno.h> 16 #include <getopt.h> 17 #include <fcntl.h> 18 #include <time.h> 19 #include <sys/ioctl.h> 20 #include <linux/ioctl.h> 21 #include <sys/stat.h> 22 #include <linux/types.h> 23 #include <linux/spi/spidev.h> 24 #include <linux/kernel.h> 25 26 static void pabort(const char *s) 27 { 28 if (errno != 0) 29 perror(s); 30 else 31 printf("%s\n", s); 32 33 abort(); 34 } 35 36 static const char *device = "/dev/spidev1.1"; 37 static uint32_t mode; 38 static uint8_t bits = 8; 39 static char *input_file; 40 static char *output_file; 41 static uint32_t speed = 500000; 42 static uint16_t delay; 43 static uint16_t word_delay; 44 static int verbose; 45 static int transfer_size = -1; 46 static int transfers = 1; 47 static int iterations; 48 static int interval = 5; /* interval in seconds for showing transfer rate */ 49 static int compare; 50 static int nonzero; 51 static int do_tx = 1, do_rx = 1; 52 static int random_input; 53 static int predictable; 54 static int input_choices; 55 56 static uint8_t default_tx[] = { 57 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 58 0x40, 0x00, 0x00, 0x00, 0x00, 0x95, 59 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 60 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 61 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 62 0xF0, 0x0D, 63 }; 64 65 static uint8_t default_rx[ARRAY_SIZE(default_tx)] = {0, }; 66 static char *input_tx; 67 68 static void hex_dump(const void *src, size_t length, size_t line_size, 69 char *prefix) 70 { 71 int i = 0; 72 const unsigned char *address = src; 73 const unsigned char *line = address; 74 unsigned char c; 75 76 printf("%s | ", prefix); 77 78 if (length == 0) 79 printf("__ ||\n"); 80 81 while (length-- > 0) { 82 printf("%02X ", *address++); 83 if (!(++i % line_size) || (length == 0 && i % line_size)) { 84 if (length == 0) { 85 while (i++ % line_size) 86 printf("__ "); 87 } 88 printf(" |"); 89 while (line < address) { 90 c = *line++; 91 printf("%c", (c < 32 || c > 126) ? '.' : c); 92 } 93 printf("|\n"); 94 if (length > 0) 95 printf("%s | ", prefix); 96 } 97 } 98 } 99 100 /* 101 * Unescape - process hexadecimal escape character 102 * converts shell input "\x23" -> 0x23 103 */ 104 static int unescape(char *src, char *dst, size_t size) 105 { 106 char *out = dst; 107 unsigned int ch; 108 109 while (*src && size--) { 110 if (src[0] == '\\' && src[1] != '\0') { 111 src++; 112 113 if (src[0] == 'x' && 114 isxdigit((unsigned char)src[1]) && 115 isxdigit((unsigned char)src[2]) && 116 sscanf(&src[1], "%2x", &ch)) { 117 *out++ = (unsigned char)ch; 118 src += 3; 119 continue; 120 } 121 122 *out++ = '\\'; 123 if (!size--) 124 break; 125 } 126 *out++ = *src++; 127 } 128 return out - dst; 129 } 130 131 static void transfer(int fd, uint8_t const * const tx, uint8_t const * const rx, size_t len) 132 { 133 int ret; 134 int out_fd; 135 size_t bytes_per_word = DIV_ROUND_UP(bits, 8); 136 int effective_transfers; 137 struct spi_ioc_transfer *tr; 138 size_t len_per_transfer; 139 const uint8_t *tx_buf = tx; 140 const uint8_t *rx_buf = rx; 141 size_t rem; 142 143 if (len) { 144 effective_transfers = min_t(size_t, transfers, DIV_ROUND_UP(len, bytes_per_word)); 145 len_per_transfer = roundup(DIV_ROUND_UP(len, effective_transfers), bytes_per_word); 146 } else { 147 /* zero-length transfers may be handled by the driver, deliver as ordered */ 148 effective_transfers = transfers; 149 len_per_transfer = 0; 150 } 151 152 if (SPI_MSGSIZE(effective_transfers) == 0) 153 pabort("too many transfers, SPI_IOC_MESSAGE size limit exceeded"); 154 155 tr = calloc(effective_transfers, sizeof(*tr)); 156 if (!tr) 157 pabort("can't allocate transfer array"); 158 159 rem = len; 160 161 for (int i = 0; i < effective_transfers; i++) { 162 size_t n = min(rem, len_per_transfer); 163 164 tr[i] = (struct spi_ioc_transfer) { 165 .tx_buf = (unsigned long)tx_buf, 166 .rx_buf = (unsigned long)rx_buf, 167 .len = n, 168 .delay_usecs = delay, 169 .word_delay_usecs = word_delay, 170 .speed_hz = speed, 171 .bits_per_word = bits, 172 }; 173 174 if (tx_buf) 175 tx_buf += n; 176 177 if (rx_buf) 178 rx_buf += n; 179 180 rem -= n; 181 182 if (mode & SPI_TX_OCTAL) 183 tr[i].tx_nbits = 8; 184 else if (mode & SPI_TX_QUAD) 185 tr[i].tx_nbits = 4; 186 else if (mode & SPI_TX_DUAL) 187 tr[i].tx_nbits = 2; 188 if (mode & SPI_RX_OCTAL) 189 tr[i].rx_nbits = 8; 190 else if (mode & SPI_RX_QUAD) 191 tr[i].rx_nbits = 4; 192 else if (mode & SPI_RX_DUAL) 193 tr[i].rx_nbits = 2; 194 if (!(mode & SPI_LOOP)) { 195 if (mode & (SPI_TX_OCTAL | SPI_TX_QUAD | SPI_TX_DUAL)) 196 tr[i].rx_buf = 0; 197 else if (mode & (SPI_RX_OCTAL | SPI_RX_QUAD | SPI_RX_DUAL)) 198 tr[i].tx_buf = 0; 199 } 200 } 201 202 ret = ioctl(fd, SPI_IOC_MESSAGE(effective_transfers), tr); 203 free(tr); 204 if (ret < 0) { 205 const size_t dump_len = min_t(size_t, len, 256); 206 int saved_errno = errno; 207 208 if (tx) { 209 hex_dump(tx, dump_len, 32, "TX"); 210 if (len > dump_len) 211 printf("... (%zu more bytes)\n", len - dump_len); 212 } 213 errno = saved_errno; 214 pabort("can't send spi message"); 215 } 216 217 if (verbose && tx) 218 hex_dump(tx, len, 32, "TX"); 219 220 if (output_file) { 221 out_fd = open(output_file, O_WRONLY | O_CREAT | O_TRUNC, 0666); 222 if (out_fd < 0) 223 pabort("could not open output file"); 224 225 ret = write(out_fd, rx, len); 226 if (ret != len) 227 pabort("not all bytes written to output file"); 228 229 close(out_fd); 230 } 231 232 if (verbose && rx) 233 hex_dump(rx, len, 32, "RX"); 234 235 if (tx && rx && (compare || mode & SPI_LOOP)) { 236 if (memcmp(tx, rx, len)) { 237 fprintf(stderr, "transfer error !\n"); 238 hex_dump(tx, len, 32, "TX"); 239 hex_dump(rx, len, 32, "RX"); 240 exit(1); 241 } 242 } 243 } 244 245 static void print_usage(const char *prog) 246 { 247 printf("Usage: %s [-2348CDFHILMNOPRSTZbcdiloprstvwz]\n", prog); 248 puts("general device settings:\n" 249 " -D --device device to use (default /dev/spidev1.1)\n" 250 " -s --speed max speed (Hz)\n" 251 " -d --delay delay (usec)\n" 252 " -w --word-delay word delay (usec)\n" 253 " -l --loop loopback\n" 254 " -c --compare compare RX'ed and TX'ed data\n" 255 " -t --no-tx don't send data\n" 256 " -r --no-rx don't receive data\n" 257 "spi mode:\n" 258 " -H --cpha clock phase\n" 259 " -O --cpol clock polarity\n" 260 " -F --rx-cpha-flip flip CPHA on Rx only xfer\n" 261 "number of wires for transmission:\n" 262 " -2 --dual dual transfer\n" 263 " -4 --quad quad transfer\n" 264 " -8 --octal octal transfer\n" 265 " -3 --3wire SI/SO signals shared\n" 266 " -Z --3wire-hiz high impedance turnaround\n" 267 "data:\n" 268 " -i --input input data from a file (e.g. \"test.bin\")\n" 269 " -o --output output data to a file (e.g. \"results.bin\")\n" 270 " -p send data (e.g. \"1234\\xde\\xad\")\n" 271 " -z --nonzero don't send 0x00 or 0xff bytes\n" 272 " -P --predictable transfer the given number of sequential bytes\n" 273 " -S --size transfer the given number of random bytes\n" 274 " -I --iter iterations\n" 275 "additional parameters:\n" 276 " -b --bpw bits per word\n" 277 " -L --lsb least significant bit first\n" 278 " -C --cs-high chip select active high\n" 279 " -N --no-cs no chip select\n" 280 " -R --ready slave pulls low to pause\n" 281 " -M --mosi-idle-low leave mosi line low when idle\n" 282 " -T --transfers number of transfers\n" 283 "misc:\n" 284 " -v --verbose Verbose (show tx buffer)\n"); 285 exit(1); 286 } 287 288 static void parse_opts(int argc, char *argv[]) 289 { 290 while (1) { 291 static const struct option lopts[] = { 292 { "device", 1, 0, 'D' }, 293 { "speed", 1, 0, 's' }, 294 { "delay", 1, 0, 'd' }, 295 { "word-delay", 1, 0, 'w' }, 296 { "loop", 0, 0, 'l' }, 297 { "compare", 0, 0, 'c' }, 298 { "no-tx", 0, 0, 't' }, 299 { "no-rx", 0, 0, 'r' }, 300 { "cpha", 0, 0, 'H' }, 301 { "cpol", 0, 0, 'O' }, 302 { "rx-cpha-flip", 0, 0, 'F' }, 303 { "dual", 0, 0, '2' }, 304 { "quad", 0, 0, '4' }, 305 { "octal", 0, 0, '8' }, 306 { "3wire", 0, 0, '3' }, 307 { "3wire-hiz", 0, 0, 'Z' }, 308 { "input", 1, 0, 'i' }, 309 { "output", 1, 0, 'o' }, 310 { "size", 1, 0, 'S' }, 311 { "nonzero", 0, 0, 'z' }, 312 { "iter", 1, 0, 'I' }, 313 { "bpw", 1, 0, 'b' }, 314 { "lsb", 0, 0, 'L' }, 315 { "cs-high", 0, 0, 'C' }, 316 { "no-cs", 0, 0, 'N' }, 317 { "ready", 0, 0, 'R' }, 318 { "mosi-idle-low", 0, 0, 'M' }, 319 { "predictable", 1, 0, 'P' }, 320 { "transfers", 1, 0, 'T' }, 321 { "verbose", 0, 0, 'v' }, 322 { NULL, 0, 0, 0 }, 323 }; 324 int c; 325 326 c = getopt_long(argc, argv, "D:s:d:w:b:i:o:lctrHOLC3ZFMNR248p:P:T:vS:zI:", 327 lopts, NULL); 328 329 if (c == -1) 330 break; 331 332 switch (c) { 333 case 'D': 334 device = optarg; 335 break; 336 case 's': 337 speed = atoi(optarg); 338 break; 339 case 'd': 340 delay = atoi(optarg); 341 break; 342 case 'w': 343 word_delay = atoi(optarg); 344 break; 345 case 'b': 346 bits = atoi(optarg); 347 break; 348 case 'i': 349 input_file = optarg; 350 input_choices++; 351 break; 352 case 'o': 353 output_file = optarg; 354 break; 355 case 'l': 356 mode |= SPI_LOOP; 357 break; 358 case 'c': 359 compare = 1; 360 break; 361 case 't': 362 do_tx = 0; 363 break; 364 case 'r': 365 do_rx = 0; 366 break; 367 case 'H': 368 mode |= SPI_CPHA; 369 break; 370 case 'O': 371 mode |= SPI_CPOL; 372 break; 373 case 'L': 374 mode |= SPI_LSB_FIRST; 375 break; 376 case 'C': 377 mode |= SPI_CS_HIGH; 378 break; 379 case '3': 380 mode |= SPI_3WIRE; 381 break; 382 case 'Z': 383 mode |= SPI_3WIRE_HIZ; 384 break; 385 case 'F': 386 mode |= SPI_RX_CPHA_FLIP; 387 break; 388 case 'M': 389 mode |= SPI_MOSI_IDLE_LOW; 390 break; 391 case 'T': 392 transfers = atoi(optarg); 393 break; 394 case 'N': 395 mode |= SPI_NO_CS; 396 break; 397 case 'v': 398 verbose = 1; 399 break; 400 case 'R': 401 mode |= SPI_READY; 402 break; 403 case 'p': 404 input_tx = optarg; 405 input_choices++; 406 break; 407 case 'P': 408 transfer_size = atoi(optarg); 409 predictable = 1; 410 input_choices++; 411 break; 412 case '2': 413 mode |= SPI_TX_DUAL; 414 break; 415 case '4': 416 mode |= SPI_TX_QUAD; 417 break; 418 case '8': 419 mode |= SPI_TX_OCTAL; 420 break; 421 case 'S': 422 transfer_size = atoi(optarg); 423 random_input = 1; 424 input_choices++; 425 break; 426 case 'z': 427 nonzero = 1; 428 break; 429 case 'I': 430 iterations = atoi(optarg); 431 break; 432 default: 433 print_usage(argv[0]); 434 } 435 } 436 if (mode & SPI_LOOP) { 437 if (mode & SPI_TX_DUAL) 438 mode |= SPI_RX_DUAL; 439 if (mode & SPI_TX_QUAD) 440 mode |= SPI_RX_QUAD; 441 if (mode & SPI_TX_OCTAL) 442 mode |= SPI_RX_OCTAL; 443 } 444 } 445 446 static void transfer_escaped_string(int fd, char *str) 447 { 448 size_t size = strlen(str); 449 uint8_t *tx; 450 uint8_t *rx = NULL; 451 452 tx = malloc(size); 453 if (!tx) 454 pabort("can't allocate tx buffer"); 455 456 if (do_rx) { 457 rx = malloc(size); 458 if (!rx) 459 pabort("can't allocate rx buffer"); 460 } 461 462 size = unescape(str, (char *)tx, size); 463 transfer(fd, tx, rx, size); 464 free(rx); 465 free(tx); 466 } 467 468 static void transfer_file(int fd, char *filename) 469 { 470 ssize_t bytes; 471 struct stat sb; 472 int tx_fd; 473 uint8_t *tx; 474 uint8_t *rx = NULL; 475 476 if (stat(filename, &sb) == -1) 477 pabort("can't stat input file"); 478 479 tx_fd = open(filename, O_RDONLY); 480 if (tx_fd < 0) 481 pabort("can't open input file"); 482 483 tx = malloc(sb.st_size); 484 if (!tx) 485 pabort("can't allocate tx buffer"); 486 487 488 if (do_rx) { 489 rx = malloc(sb.st_size); 490 if (!rx) 491 pabort("can't allocate rx buffer"); 492 } 493 494 bytes = read(tx_fd, tx, sb.st_size); 495 if (bytes != sb.st_size) 496 pabort("failed to read input file"); 497 498 transfer(fd, tx, rx, sb.st_size); 499 free(rx); 500 free(tx); 501 close(tx_fd); 502 } 503 504 static uint64_t _read_count; 505 static uint64_t _write_count; 506 507 static void show_transfer_rate(void) 508 { 509 static uint64_t prev_read_count, prev_write_count; 510 double rx_rate, tx_rate; 511 512 rx_rate = ((_read_count - prev_read_count) * 8) / (interval*1000.0); 513 tx_rate = ((_write_count - prev_write_count) * 8) / (interval*1000.0); 514 515 printf("rate: tx %.1fkbps, rx %.1fkbps\n", rx_rate, tx_rate); 516 517 prev_read_count = _read_count; 518 prev_write_count = _write_count; 519 } 520 521 static void transfer_buf(int fd, int len) 522 { 523 uint8_t *tx = NULL; 524 uint8_t *rx = NULL; 525 int i; 526 527 if (do_tx) { 528 tx = malloc(len); 529 if (!tx) 530 pabort("can't allocate tx buffer"); 531 for (i = 0; i < len; i++) { 532 if (predictable) { 533 int v = i - iterations; 534 535 if (nonzero) 536 v = ((v % 254) + 254) % 254 + 1; 537 tx[i] = (char)v; 538 } else { 539 do { 540 tx[i] = random(); 541 } while (nonzero && (tx[i] == 0x0 || tx[i] == 0xff)); 542 } 543 } 544 } 545 546 if (do_rx) { 547 rx = malloc(len); 548 if (!rx) 549 pabort("can't allocate rx buffer"); 550 } 551 552 transfer(fd, tx, rx, len); 553 if (do_tx) 554 _write_count += len; 555 if (do_rx) 556 _read_count += len; 557 558 free(rx); 559 free(tx); 560 } 561 562 int main(int argc, char *argv[]) 563 { 564 int ret = 0; 565 int fd; 566 uint32_t request; 567 568 parse_opts(argc, argv); 569 570 if (input_choices > 1) 571 pabort("at most one of -S (--size), -p, -i (--input), -P (--predictable) may be selected, " 572 "and each may be specified only once"); 573 574 if ((random_input || predictable) && transfer_size < 0) 575 pabort("a size argument is mandatory for -S (--size) and -P (--predictable)"); 576 577 if (iterations && transfer_size < 0) 578 pabort("-I (--iter) is only implemented for -S (--size) and -P (--predictable)"); 579 580 if (nonzero && transfer_size < 0) 581 pabort("-z (--nonzero) is only implemented for -S (--size) and -P (--predictable)"); 582 583 if (compare && (!do_tx || !do_rx)) 584 pabort("-c (--compare) conflicts with -t (--no-tx) or -r (--no-rx)"); 585 586 if (!do_rx && output_file) 587 pabort("-r (--no-rx) conflicts with -o (--output)"); 588 589 if (!do_tx && transfer_size < 0) 590 pabort("-t (--no-tx) is only implemented for -S (--size) and -P (--predictable)"); 591 592 if (compare && mode & (SPI_TX_OCTAL | SPI_TX_QUAD | SPI_TX_DUAL)) 593 pabort("-c (--compare) conflicts with -2 (--dual), -4 (--quad) or -8 (--octal)"); 594 595 if (transfers < 1) 596 pabort("-T (--transfers) must be 1 or above"); 597 598 if (bits < 1) 599 pabort("-b (--bpw) must be 1 or above"); 600 601 fd = open(device, O_RDWR); 602 if (fd < 0) 603 pabort("can't open device"); 604 605 /* 606 * spi mode 607 */ 608 /* WR is make a request to assign 'mode' */ 609 request = mode; 610 ret = ioctl(fd, SPI_IOC_WR_MODE32, &mode); 611 if (ret == -1) 612 pabort("can't set spi mode"); 613 614 /* RD is read what mode the device actually is in */ 615 ret = ioctl(fd, SPI_IOC_RD_MODE32, &mode); 616 if (ret == -1) 617 pabort("can't get spi mode"); 618 /* Drivers can reject some mode bits without returning an error. 619 * Read the current value to identify what mode it is in, and if it 620 * differs from the requested mode, warn the user. 621 */ 622 if (request != mode) 623 printf("WARNING device does not support requested mode 0x%x\n", 624 request); 625 626 /* 627 * bits per word 628 */ 629 ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits); 630 if (ret == -1) 631 pabort("can't set bits per word"); 632 633 ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits); 634 if (ret == -1) 635 pabort("can't get bits per word"); 636 637 /* 638 * max speed hz 639 */ 640 ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed); 641 if (ret == -1) 642 pabort("can't set max speed hz"); 643 644 ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed); 645 if (ret == -1) 646 pabort("can't get max speed hz"); 647 648 printf("spi mode: 0x%x\n", mode); 649 printf("bits per word: %u\n", bits); 650 printf("max speed: %u Hz (%u kHz)\n", speed, speed/1000); 651 652 if (input_tx) 653 transfer_escaped_string(fd, input_tx); 654 else if (input_file) 655 transfer_file(fd, input_file); 656 else if (transfer_size >= 0) { 657 struct timespec last_stat; 658 659 clock_gettime(CLOCK_MONOTONIC, &last_stat); 660 661 while (iterations-- > 0) { 662 struct timespec current; 663 664 transfer_buf(fd, transfer_size); 665 666 clock_gettime(CLOCK_MONOTONIC, ¤t); 667 if (current.tv_sec - last_stat.tv_sec > interval) { 668 show_transfer_rate(); 669 last_stat = current; 670 } 671 } 672 printf("total: tx %.1fKB, rx %.1fKB\n", 673 _write_count/1024.0, _read_count/1024.0); 674 } else 675 transfer(fd, default_tx, do_rx ? default_rx : NULL, sizeof(default_tx)); 676 677 close(fd); 678 679 return ret; 680 } 681