xref: /kernel/linux/linux-6.6/tools/spi/spidev_test.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * SPI testing utility (using spidev driver)
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2007  MontaVista Software, Inc.
662306a36Sopenharmony_ci * Copyright (c) 2007  Anton Vorontsov <avorontsov@ru.mvista.com>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Cross-compile with cross-gcc -I/path/to/cross-kernel/include
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <stdint.h>
1262306a36Sopenharmony_ci#include <unistd.h>
1362306a36Sopenharmony_ci#include <stdio.h>
1462306a36Sopenharmony_ci#include <stdlib.h>
1562306a36Sopenharmony_ci#include <string.h>
1662306a36Sopenharmony_ci#include <errno.h>
1762306a36Sopenharmony_ci#include <getopt.h>
1862306a36Sopenharmony_ci#include <fcntl.h>
1962306a36Sopenharmony_ci#include <time.h>
2062306a36Sopenharmony_ci#include <sys/ioctl.h>
2162306a36Sopenharmony_ci#include <linux/ioctl.h>
2262306a36Sopenharmony_ci#include <sys/stat.h>
2362306a36Sopenharmony_ci#include <linux/types.h>
2462306a36Sopenharmony_ci#include <linux/spi/spidev.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic void pabort(const char *s)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	if (errno != 0)
3162306a36Sopenharmony_ci		perror(s);
3262306a36Sopenharmony_ci	else
3362306a36Sopenharmony_ci		printf("%s\n", s);
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	abort();
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic const char *device = "/dev/spidev1.1";
3962306a36Sopenharmony_cistatic uint32_t mode;
4062306a36Sopenharmony_cistatic uint8_t bits = 8;
4162306a36Sopenharmony_cistatic char *input_file;
4262306a36Sopenharmony_cistatic char *output_file;
4362306a36Sopenharmony_cistatic uint32_t speed = 500000;
4462306a36Sopenharmony_cistatic uint16_t delay;
4562306a36Sopenharmony_cistatic int verbose;
4662306a36Sopenharmony_cistatic int transfer_size;
4762306a36Sopenharmony_cistatic int iterations;
4862306a36Sopenharmony_cistatic int interval = 5; /* interval in seconds for showing transfer rate */
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic uint8_t default_tx[] = {
5162306a36Sopenharmony_ci	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
5262306a36Sopenharmony_ci	0x40, 0x00, 0x00, 0x00, 0x00, 0x95,
5362306a36Sopenharmony_ci	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
5462306a36Sopenharmony_ci	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
5562306a36Sopenharmony_ci	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
5662306a36Sopenharmony_ci	0xF0, 0x0D,
5762306a36Sopenharmony_ci};
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic uint8_t default_rx[ARRAY_SIZE(default_tx)] = {0, };
6062306a36Sopenharmony_cistatic char *input_tx;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic void hex_dump(const void *src, size_t length, size_t line_size,
6362306a36Sopenharmony_ci		     char *prefix)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	int i = 0;
6662306a36Sopenharmony_ci	const unsigned char *address = src;
6762306a36Sopenharmony_ci	const unsigned char *line = address;
6862306a36Sopenharmony_ci	unsigned char c;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	printf("%s | ", prefix);
7162306a36Sopenharmony_ci	while (length-- > 0) {
7262306a36Sopenharmony_ci		printf("%02X ", *address++);
7362306a36Sopenharmony_ci		if (!(++i % line_size) || (length == 0 && i % line_size)) {
7462306a36Sopenharmony_ci			if (length == 0) {
7562306a36Sopenharmony_ci				while (i++ % line_size)
7662306a36Sopenharmony_ci					printf("__ ");
7762306a36Sopenharmony_ci			}
7862306a36Sopenharmony_ci			printf(" |");
7962306a36Sopenharmony_ci			while (line < address) {
8062306a36Sopenharmony_ci				c = *line++;
8162306a36Sopenharmony_ci				printf("%c", (c < 32 || c > 126) ? '.' : c);
8262306a36Sopenharmony_ci			}
8362306a36Sopenharmony_ci			printf("|\n");
8462306a36Sopenharmony_ci			if (length > 0)
8562306a36Sopenharmony_ci				printf("%s | ", prefix);
8662306a36Sopenharmony_ci		}
8762306a36Sopenharmony_ci	}
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci/*
9162306a36Sopenharmony_ci *  Unescape - process hexadecimal escape character
9262306a36Sopenharmony_ci *      converts shell input "\x23" -> 0x23
9362306a36Sopenharmony_ci */
9462306a36Sopenharmony_cistatic int unescape(char *_dst, char *_src, size_t len)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	int ret = 0;
9762306a36Sopenharmony_ci	int match;
9862306a36Sopenharmony_ci	char *src = _src;
9962306a36Sopenharmony_ci	char *dst = _dst;
10062306a36Sopenharmony_ci	unsigned int ch;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	while (*src) {
10362306a36Sopenharmony_ci		if (*src == '\\' && *(src+1) == 'x') {
10462306a36Sopenharmony_ci			match = sscanf(src + 2, "%2x", &ch);
10562306a36Sopenharmony_ci			if (!match)
10662306a36Sopenharmony_ci				pabort("malformed input string");
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci			src += 4;
10962306a36Sopenharmony_ci			*dst++ = (unsigned char)ch;
11062306a36Sopenharmony_ci		} else {
11162306a36Sopenharmony_ci			*dst++ = *src++;
11262306a36Sopenharmony_ci		}
11362306a36Sopenharmony_ci		ret++;
11462306a36Sopenharmony_ci	}
11562306a36Sopenharmony_ci	return ret;
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic void transfer(int fd, uint8_t const *tx, uint8_t const *rx, size_t len)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	int ret;
12162306a36Sopenharmony_ci	int out_fd;
12262306a36Sopenharmony_ci	struct spi_ioc_transfer tr = {
12362306a36Sopenharmony_ci		.tx_buf = (unsigned long)tx,
12462306a36Sopenharmony_ci		.rx_buf = (unsigned long)rx,
12562306a36Sopenharmony_ci		.len = len,
12662306a36Sopenharmony_ci		.delay_usecs = delay,
12762306a36Sopenharmony_ci		.speed_hz = speed,
12862306a36Sopenharmony_ci		.bits_per_word = bits,
12962306a36Sopenharmony_ci	};
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	if (mode & SPI_TX_OCTAL)
13262306a36Sopenharmony_ci		tr.tx_nbits = 8;
13362306a36Sopenharmony_ci	else if (mode & SPI_TX_QUAD)
13462306a36Sopenharmony_ci		tr.tx_nbits = 4;
13562306a36Sopenharmony_ci	else if (mode & SPI_TX_DUAL)
13662306a36Sopenharmony_ci		tr.tx_nbits = 2;
13762306a36Sopenharmony_ci	if (mode & SPI_RX_OCTAL)
13862306a36Sopenharmony_ci		tr.rx_nbits = 8;
13962306a36Sopenharmony_ci	else if (mode & SPI_RX_QUAD)
14062306a36Sopenharmony_ci		tr.rx_nbits = 4;
14162306a36Sopenharmony_ci	else if (mode & SPI_RX_DUAL)
14262306a36Sopenharmony_ci		tr.rx_nbits = 2;
14362306a36Sopenharmony_ci	if (!(mode & SPI_LOOP)) {
14462306a36Sopenharmony_ci		if (mode & (SPI_TX_OCTAL | SPI_TX_QUAD | SPI_TX_DUAL))
14562306a36Sopenharmony_ci			tr.rx_buf = 0;
14662306a36Sopenharmony_ci		else if (mode & (SPI_RX_OCTAL | SPI_RX_QUAD | SPI_RX_DUAL))
14762306a36Sopenharmony_ci			tr.tx_buf = 0;
14862306a36Sopenharmony_ci	}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
15162306a36Sopenharmony_ci	if (ret < 1)
15262306a36Sopenharmony_ci		pabort("can't send spi message");
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	if (verbose)
15562306a36Sopenharmony_ci		hex_dump(tx, len, 32, "TX");
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	if (output_file) {
15862306a36Sopenharmony_ci		out_fd = open(output_file, O_WRONLY | O_CREAT | O_TRUNC, 0666);
15962306a36Sopenharmony_ci		if (out_fd < 0)
16062306a36Sopenharmony_ci			pabort("could not open output file");
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci		ret = write(out_fd, rx, len);
16362306a36Sopenharmony_ci		if (ret != len)
16462306a36Sopenharmony_ci			pabort("not all bytes written to output file");
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci		close(out_fd);
16762306a36Sopenharmony_ci	}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	if (verbose)
17062306a36Sopenharmony_ci		hex_dump(rx, len, 32, "RX");
17162306a36Sopenharmony_ci}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_cistatic void print_usage(const char *prog)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	printf("Usage: %s [-2348CDFHILMNORSZbdilopsv]\n", prog);
17662306a36Sopenharmony_ci	puts("general device settings:\n"
17762306a36Sopenharmony_ci		 "  -D --device         device to use (default /dev/spidev1.1)\n"
17862306a36Sopenharmony_ci		 "  -s --speed          max speed (Hz)\n"
17962306a36Sopenharmony_ci		 "  -d --delay          delay (usec)\n"
18062306a36Sopenharmony_ci		 "  -l --loop           loopback\n"
18162306a36Sopenharmony_ci		 "spi mode:\n"
18262306a36Sopenharmony_ci		 "  -H --cpha           clock phase\n"
18362306a36Sopenharmony_ci		 "  -O --cpol           clock polarity\n"
18462306a36Sopenharmony_ci		 "  -F --rx-cpha-flip   flip CPHA on Rx only xfer\n"
18562306a36Sopenharmony_ci		 "number of wires for transmission:\n"
18662306a36Sopenharmony_ci		 "  -2 --dual           dual transfer\n"
18762306a36Sopenharmony_ci		 "  -4 --quad           quad transfer\n"
18862306a36Sopenharmony_ci		 "  -8 --octal          octal transfer\n"
18962306a36Sopenharmony_ci		 "  -3 --3wire          SI/SO signals shared\n"
19062306a36Sopenharmony_ci		 "  -Z --3wire-hiz      high impedance turnaround\n"
19162306a36Sopenharmony_ci		 "data:\n"
19262306a36Sopenharmony_ci		 "  -i --input          input data from a file (e.g. \"test.bin\")\n"
19362306a36Sopenharmony_ci		 "  -o --output         output data to a file (e.g. \"results.bin\")\n"
19462306a36Sopenharmony_ci		 "  -p                  Send data (e.g. \"1234\\xde\\xad\")\n"
19562306a36Sopenharmony_ci		 "  -S --size           transfer size\n"
19662306a36Sopenharmony_ci		 "  -I --iter           iterations\n"
19762306a36Sopenharmony_ci		 "additional parameters:\n"
19862306a36Sopenharmony_ci		 "  -b --bpw            bits per word\n"
19962306a36Sopenharmony_ci		 "  -L --lsb            least significant bit first\n"
20062306a36Sopenharmony_ci		 "  -C --cs-high        chip select active high\n"
20162306a36Sopenharmony_ci		 "  -N --no-cs          no chip select\n"
20262306a36Sopenharmony_ci		 "  -R --ready          slave pulls low to pause\n"
20362306a36Sopenharmony_ci		 "  -M --mosi-idle-low  leave mosi line low when idle\n"
20462306a36Sopenharmony_ci		 "misc:\n"
20562306a36Sopenharmony_ci		 "  -v --verbose        Verbose (show tx buffer)\n");
20662306a36Sopenharmony_ci	exit(1);
20762306a36Sopenharmony_ci}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_cistatic void parse_opts(int argc, char *argv[])
21062306a36Sopenharmony_ci{
21162306a36Sopenharmony_ci	while (1) {
21262306a36Sopenharmony_ci		static const struct option lopts[] = {
21362306a36Sopenharmony_ci			{ "device",        1, 0, 'D' },
21462306a36Sopenharmony_ci			{ "speed",         1, 0, 's' },
21562306a36Sopenharmony_ci			{ "delay",         1, 0, 'd' },
21662306a36Sopenharmony_ci			{ "loop",          0, 0, 'l' },
21762306a36Sopenharmony_ci			{ "cpha",          0, 0, 'H' },
21862306a36Sopenharmony_ci			{ "cpol",          0, 0, 'O' },
21962306a36Sopenharmony_ci			{ "rx-cpha-flip",  0, 0, 'F' },
22062306a36Sopenharmony_ci			{ "dual",          0, 0, '2' },
22162306a36Sopenharmony_ci			{ "quad",          0, 0, '4' },
22262306a36Sopenharmony_ci			{ "octal",         0, 0, '8' },
22362306a36Sopenharmony_ci			{ "3wire",         0, 0, '3' },
22462306a36Sopenharmony_ci			{ "3wire-hiz",     0, 0, 'Z' },
22562306a36Sopenharmony_ci			{ "input",         1, 0, 'i' },
22662306a36Sopenharmony_ci			{ "output",        1, 0, 'o' },
22762306a36Sopenharmony_ci			{ "size",          1, 0, 'S' },
22862306a36Sopenharmony_ci			{ "iter",          1, 0, 'I' },
22962306a36Sopenharmony_ci			{ "bpw",           1, 0, 'b' },
23062306a36Sopenharmony_ci			{ "lsb",           0, 0, 'L' },
23162306a36Sopenharmony_ci			{ "cs-high",       0, 0, 'C' },
23262306a36Sopenharmony_ci			{ "no-cs",         0, 0, 'N' },
23362306a36Sopenharmony_ci			{ "ready",         0, 0, 'R' },
23462306a36Sopenharmony_ci			{ "mosi-idle-low", 0, 0, 'M' },
23562306a36Sopenharmony_ci			{ "verbose",       0, 0, 'v' },
23662306a36Sopenharmony_ci			{ NULL, 0, 0, 0 },
23762306a36Sopenharmony_ci		};
23862306a36Sopenharmony_ci		int c;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci		c = getopt_long(argc, argv, "D:s:d:b:i:o:lHOLC3ZFMNR248p:vS:I:",
24162306a36Sopenharmony_ci				lopts, NULL);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci		if (c == -1)
24462306a36Sopenharmony_ci			break;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci		switch (c) {
24762306a36Sopenharmony_ci		case 'D':
24862306a36Sopenharmony_ci			device = optarg;
24962306a36Sopenharmony_ci			break;
25062306a36Sopenharmony_ci		case 's':
25162306a36Sopenharmony_ci			speed = atoi(optarg);
25262306a36Sopenharmony_ci			break;
25362306a36Sopenharmony_ci		case 'd':
25462306a36Sopenharmony_ci			delay = atoi(optarg);
25562306a36Sopenharmony_ci			break;
25662306a36Sopenharmony_ci		case 'b':
25762306a36Sopenharmony_ci			bits = atoi(optarg);
25862306a36Sopenharmony_ci			break;
25962306a36Sopenharmony_ci		case 'i':
26062306a36Sopenharmony_ci			input_file = optarg;
26162306a36Sopenharmony_ci			break;
26262306a36Sopenharmony_ci		case 'o':
26362306a36Sopenharmony_ci			output_file = optarg;
26462306a36Sopenharmony_ci			break;
26562306a36Sopenharmony_ci		case 'l':
26662306a36Sopenharmony_ci			mode |= SPI_LOOP;
26762306a36Sopenharmony_ci			break;
26862306a36Sopenharmony_ci		case 'H':
26962306a36Sopenharmony_ci			mode |= SPI_CPHA;
27062306a36Sopenharmony_ci			break;
27162306a36Sopenharmony_ci		case 'O':
27262306a36Sopenharmony_ci			mode |= SPI_CPOL;
27362306a36Sopenharmony_ci			break;
27462306a36Sopenharmony_ci		case 'L':
27562306a36Sopenharmony_ci			mode |= SPI_LSB_FIRST;
27662306a36Sopenharmony_ci			break;
27762306a36Sopenharmony_ci		case 'C':
27862306a36Sopenharmony_ci			mode |= SPI_CS_HIGH;
27962306a36Sopenharmony_ci			break;
28062306a36Sopenharmony_ci		case '3':
28162306a36Sopenharmony_ci			mode |= SPI_3WIRE;
28262306a36Sopenharmony_ci			break;
28362306a36Sopenharmony_ci		case 'Z':
28462306a36Sopenharmony_ci			mode |= SPI_3WIRE_HIZ;
28562306a36Sopenharmony_ci			break;
28662306a36Sopenharmony_ci		case 'F':
28762306a36Sopenharmony_ci			mode |= SPI_RX_CPHA_FLIP;
28862306a36Sopenharmony_ci			break;
28962306a36Sopenharmony_ci		case 'M':
29062306a36Sopenharmony_ci			mode |= SPI_MOSI_IDLE_LOW;
29162306a36Sopenharmony_ci			break;
29262306a36Sopenharmony_ci		case 'N':
29362306a36Sopenharmony_ci			mode |= SPI_NO_CS;
29462306a36Sopenharmony_ci			break;
29562306a36Sopenharmony_ci		case 'v':
29662306a36Sopenharmony_ci			verbose = 1;
29762306a36Sopenharmony_ci			break;
29862306a36Sopenharmony_ci		case 'R':
29962306a36Sopenharmony_ci			mode |= SPI_READY;
30062306a36Sopenharmony_ci			break;
30162306a36Sopenharmony_ci		case 'p':
30262306a36Sopenharmony_ci			input_tx = optarg;
30362306a36Sopenharmony_ci			break;
30462306a36Sopenharmony_ci		case '2':
30562306a36Sopenharmony_ci			mode |= SPI_TX_DUAL;
30662306a36Sopenharmony_ci			break;
30762306a36Sopenharmony_ci		case '4':
30862306a36Sopenharmony_ci			mode |= SPI_TX_QUAD;
30962306a36Sopenharmony_ci			break;
31062306a36Sopenharmony_ci		case '8':
31162306a36Sopenharmony_ci			mode |= SPI_TX_OCTAL;
31262306a36Sopenharmony_ci			break;
31362306a36Sopenharmony_ci		case 'S':
31462306a36Sopenharmony_ci			transfer_size = atoi(optarg);
31562306a36Sopenharmony_ci			break;
31662306a36Sopenharmony_ci		case 'I':
31762306a36Sopenharmony_ci			iterations = atoi(optarg);
31862306a36Sopenharmony_ci			break;
31962306a36Sopenharmony_ci		default:
32062306a36Sopenharmony_ci			print_usage(argv[0]);
32162306a36Sopenharmony_ci		}
32262306a36Sopenharmony_ci	}
32362306a36Sopenharmony_ci	if (mode & SPI_LOOP) {
32462306a36Sopenharmony_ci		if (mode & SPI_TX_DUAL)
32562306a36Sopenharmony_ci			mode |= SPI_RX_DUAL;
32662306a36Sopenharmony_ci		if (mode & SPI_TX_QUAD)
32762306a36Sopenharmony_ci			mode |= SPI_RX_QUAD;
32862306a36Sopenharmony_ci		if (mode & SPI_TX_OCTAL)
32962306a36Sopenharmony_ci			mode |= SPI_RX_OCTAL;
33062306a36Sopenharmony_ci	}
33162306a36Sopenharmony_ci}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_cistatic void transfer_escaped_string(int fd, char *str)
33462306a36Sopenharmony_ci{
33562306a36Sopenharmony_ci	size_t size = strlen(str);
33662306a36Sopenharmony_ci	uint8_t *tx;
33762306a36Sopenharmony_ci	uint8_t *rx;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	tx = malloc(size);
34062306a36Sopenharmony_ci	if (!tx)
34162306a36Sopenharmony_ci		pabort("can't allocate tx buffer");
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	rx = malloc(size);
34462306a36Sopenharmony_ci	if (!rx)
34562306a36Sopenharmony_ci		pabort("can't allocate rx buffer");
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	size = unescape((char *)tx, str, size);
34862306a36Sopenharmony_ci	transfer(fd, tx, rx, size);
34962306a36Sopenharmony_ci	free(rx);
35062306a36Sopenharmony_ci	free(tx);
35162306a36Sopenharmony_ci}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_cistatic void transfer_file(int fd, char *filename)
35462306a36Sopenharmony_ci{
35562306a36Sopenharmony_ci	ssize_t bytes;
35662306a36Sopenharmony_ci	struct stat sb;
35762306a36Sopenharmony_ci	int tx_fd;
35862306a36Sopenharmony_ci	uint8_t *tx;
35962306a36Sopenharmony_ci	uint8_t *rx;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	if (stat(filename, &sb) == -1)
36262306a36Sopenharmony_ci		pabort("can't stat input file");
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	tx_fd = open(filename, O_RDONLY);
36562306a36Sopenharmony_ci	if (tx_fd < 0)
36662306a36Sopenharmony_ci		pabort("can't open input file");
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	tx = malloc(sb.st_size);
36962306a36Sopenharmony_ci	if (!tx)
37062306a36Sopenharmony_ci		pabort("can't allocate tx buffer");
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	rx = malloc(sb.st_size);
37362306a36Sopenharmony_ci	if (!rx)
37462306a36Sopenharmony_ci		pabort("can't allocate rx buffer");
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	bytes = read(tx_fd, tx, sb.st_size);
37762306a36Sopenharmony_ci	if (bytes != sb.st_size)
37862306a36Sopenharmony_ci		pabort("failed to read input file");
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	transfer(fd, tx, rx, sb.st_size);
38162306a36Sopenharmony_ci	free(rx);
38262306a36Sopenharmony_ci	free(tx);
38362306a36Sopenharmony_ci	close(tx_fd);
38462306a36Sopenharmony_ci}
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_cistatic uint64_t _read_count;
38762306a36Sopenharmony_cistatic uint64_t _write_count;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_cistatic void show_transfer_rate(void)
39062306a36Sopenharmony_ci{
39162306a36Sopenharmony_ci	static uint64_t prev_read_count, prev_write_count;
39262306a36Sopenharmony_ci	double rx_rate, tx_rate;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	rx_rate = ((_read_count - prev_read_count) * 8) / (interval*1000.0);
39562306a36Sopenharmony_ci	tx_rate = ((_write_count - prev_write_count) * 8) / (interval*1000.0);
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	printf("rate: tx %.1fkbps, rx %.1fkbps\n", rx_rate, tx_rate);
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	prev_read_count = _read_count;
40062306a36Sopenharmony_ci	prev_write_count = _write_count;
40162306a36Sopenharmony_ci}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_cistatic void transfer_buf(int fd, int len)
40462306a36Sopenharmony_ci{
40562306a36Sopenharmony_ci	uint8_t *tx;
40662306a36Sopenharmony_ci	uint8_t *rx;
40762306a36Sopenharmony_ci	int i;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	tx = malloc(len);
41062306a36Sopenharmony_ci	if (!tx)
41162306a36Sopenharmony_ci		pabort("can't allocate tx buffer");
41262306a36Sopenharmony_ci	for (i = 0; i < len; i++)
41362306a36Sopenharmony_ci		tx[i] = random();
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	rx = malloc(len);
41662306a36Sopenharmony_ci	if (!rx)
41762306a36Sopenharmony_ci		pabort("can't allocate rx buffer");
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	transfer(fd, tx, rx, len);
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	_write_count += len;
42262306a36Sopenharmony_ci	_read_count += len;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	if (mode & SPI_LOOP) {
42562306a36Sopenharmony_ci		if (memcmp(tx, rx, len)) {
42662306a36Sopenharmony_ci			fprintf(stderr, "transfer error !\n");
42762306a36Sopenharmony_ci			hex_dump(tx, len, 32, "TX");
42862306a36Sopenharmony_ci			hex_dump(rx, len, 32, "RX");
42962306a36Sopenharmony_ci			exit(1);
43062306a36Sopenharmony_ci		}
43162306a36Sopenharmony_ci	}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	free(rx);
43462306a36Sopenharmony_ci	free(tx);
43562306a36Sopenharmony_ci}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ciint main(int argc, char *argv[])
43862306a36Sopenharmony_ci{
43962306a36Sopenharmony_ci	int ret = 0;
44062306a36Sopenharmony_ci	int fd;
44162306a36Sopenharmony_ci	uint32_t request;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	parse_opts(argc, argv);
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	if (input_tx && input_file)
44662306a36Sopenharmony_ci		pabort("only one of -p and --input may be selected");
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	fd = open(device, O_RDWR);
44962306a36Sopenharmony_ci	if (fd < 0)
45062306a36Sopenharmony_ci		pabort("can't open device");
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	/*
45362306a36Sopenharmony_ci	 * spi mode
45462306a36Sopenharmony_ci	 */
45562306a36Sopenharmony_ci	/* WR is make a request to assign 'mode' */
45662306a36Sopenharmony_ci	request = mode;
45762306a36Sopenharmony_ci	ret = ioctl(fd, SPI_IOC_WR_MODE32, &mode);
45862306a36Sopenharmony_ci	if (ret == -1)
45962306a36Sopenharmony_ci		pabort("can't set spi mode");
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	/* RD is read what mode the device actually is in */
46262306a36Sopenharmony_ci	ret = ioctl(fd, SPI_IOC_RD_MODE32, &mode);
46362306a36Sopenharmony_ci	if (ret == -1)
46462306a36Sopenharmony_ci		pabort("can't get spi mode");
46562306a36Sopenharmony_ci	/* Drivers can reject some mode bits without returning an error.
46662306a36Sopenharmony_ci	 * Read the current value to identify what mode it is in, and if it
46762306a36Sopenharmony_ci	 * differs from the requested mode, warn the user.
46862306a36Sopenharmony_ci	 */
46962306a36Sopenharmony_ci	if (request != mode)
47062306a36Sopenharmony_ci		printf("WARNING device does not support requested mode 0x%x\n",
47162306a36Sopenharmony_ci			request);
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	/*
47462306a36Sopenharmony_ci	 * bits per word
47562306a36Sopenharmony_ci	 */
47662306a36Sopenharmony_ci	ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
47762306a36Sopenharmony_ci	if (ret == -1)
47862306a36Sopenharmony_ci		pabort("can't set bits per word");
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
48162306a36Sopenharmony_ci	if (ret == -1)
48262306a36Sopenharmony_ci		pabort("can't get bits per word");
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	/*
48562306a36Sopenharmony_ci	 * max speed hz
48662306a36Sopenharmony_ci	 */
48762306a36Sopenharmony_ci	ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
48862306a36Sopenharmony_ci	if (ret == -1)
48962306a36Sopenharmony_ci		pabort("can't set max speed hz");
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
49262306a36Sopenharmony_ci	if (ret == -1)
49362306a36Sopenharmony_ci		pabort("can't get max speed hz");
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	printf("spi mode: 0x%x\n", mode);
49662306a36Sopenharmony_ci	printf("bits per word: %u\n", bits);
49762306a36Sopenharmony_ci	printf("max speed: %u Hz (%u kHz)\n", speed, speed/1000);
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	if (input_tx)
50062306a36Sopenharmony_ci		transfer_escaped_string(fd, input_tx);
50162306a36Sopenharmony_ci	else if (input_file)
50262306a36Sopenharmony_ci		transfer_file(fd, input_file);
50362306a36Sopenharmony_ci	else if (transfer_size) {
50462306a36Sopenharmony_ci		struct timespec last_stat;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci		clock_gettime(CLOCK_MONOTONIC, &last_stat);
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci		while (iterations-- > 0) {
50962306a36Sopenharmony_ci			struct timespec current;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci			transfer_buf(fd, transfer_size);
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci			clock_gettime(CLOCK_MONOTONIC, &current);
51462306a36Sopenharmony_ci			if (current.tv_sec - last_stat.tv_sec > interval) {
51562306a36Sopenharmony_ci				show_transfer_rate();
51662306a36Sopenharmony_ci				last_stat = current;
51762306a36Sopenharmony_ci			}
51862306a36Sopenharmony_ci		}
51962306a36Sopenharmony_ci		printf("total: tx %.1fKB, rx %.1fKB\n",
52062306a36Sopenharmony_ci		       _write_count/1024.0, _read_count/1024.0);
52162306a36Sopenharmony_ci	} else
52262306a36Sopenharmony_ci		transfer(fd, default_tx, default_rx, sizeof(default_tx));
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	close(fd);
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	return ret;
52762306a36Sopenharmony_ci}
528