xref: /linux/tools/spi/spidev_test.c (revision 5d7f3375cd574fc83f0cd30839b21455c0e79ae2)
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, &current);
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