xref: /openbmc/linux/tools/spi/spidev_test.c (revision b229a7f5)
184a14ae8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
25eca4d84SJoshua Clayton /*
35eca4d84SJoshua Clayton  * SPI testing utility (using spidev driver)
45eca4d84SJoshua Clayton  *
55eca4d84SJoshua Clayton  * Copyright (c) 2007  MontaVista Software, Inc.
65eca4d84SJoshua Clayton  * Copyright (c) 2007  Anton Vorontsov <avorontsov@ru.mvista.com>
75eca4d84SJoshua Clayton  *
85eca4d84SJoshua Clayton  * Cross-compile with cross-gcc -I/path/to/cross-kernel/include
95eca4d84SJoshua Clayton  */
105eca4d84SJoshua Clayton 
115eca4d84SJoshua Clayton #include <stdint.h>
125eca4d84SJoshua Clayton #include <unistd.h>
135eca4d84SJoshua Clayton #include <stdio.h>
145eca4d84SJoshua Clayton #include <stdlib.h>
155eca4d84SJoshua Clayton #include <string.h>
16470a072eSTiezhu Yang #include <errno.h>
175eca4d84SJoshua Clayton #include <getopt.h>
185eca4d84SJoshua Clayton #include <fcntl.h>
199006a7b3SFrode Isaksen #include <time.h>
205eca4d84SJoshua Clayton #include <sys/ioctl.h>
218736f802SBaruch Siach #include <linux/ioctl.h>
227af475a5SJoshua Clayton #include <sys/stat.h>
235eca4d84SJoshua Clayton #include <linux/types.h>
245eca4d84SJoshua Clayton #include <linux/spi/spidev.h>
255eca4d84SJoshua Clayton 
265eca4d84SJoshua Clayton #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
275eca4d84SJoshua Clayton 
pabort(const char * s)285eca4d84SJoshua Clayton static void pabort(const char *s)
295eca4d84SJoshua Clayton {
30470a072eSTiezhu Yang 	if (errno != 0)
315eca4d84SJoshua Clayton 		perror(s);
32470a072eSTiezhu Yang 	else
33470a072eSTiezhu Yang 		printf("%s\n", s);
34470a072eSTiezhu Yang 
355eca4d84SJoshua Clayton 	abort();
365eca4d84SJoshua Clayton }
375eca4d84SJoshua Clayton 
385eca4d84SJoshua Clayton static const char *device = "/dev/spidev1.1";
395eca4d84SJoshua Clayton static uint32_t mode;
405eca4d84SJoshua Clayton static uint8_t bits = 8;
417af475a5SJoshua Clayton static char *input_file;
42983b2788SJoshua Clayton static char *output_file;
435eca4d84SJoshua Clayton static uint32_t speed = 500000;
445eca4d84SJoshua Clayton static uint16_t delay;
455eca4d84SJoshua Clayton static int verbose;
469006a7b3SFrode Isaksen static int transfer_size;
479006a7b3SFrode Isaksen static int iterations;
489006a7b3SFrode Isaksen static int interval = 5; /* interval in seconds for showing transfer rate */
495eca4d84SJoshua Clayton 
50bd207791SQing Zhang static uint8_t default_tx[] = {
515eca4d84SJoshua Clayton 	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
525eca4d84SJoshua Clayton 	0x40, 0x00, 0x00, 0x00, 0x00, 0x95,
535eca4d84SJoshua Clayton 	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
545eca4d84SJoshua Clayton 	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
555eca4d84SJoshua Clayton 	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
565eca4d84SJoshua Clayton 	0xF0, 0x0D,
575eca4d84SJoshua Clayton };
585eca4d84SJoshua Clayton 
59bd207791SQing Zhang static uint8_t default_rx[ARRAY_SIZE(default_tx)] = {0, };
60bd207791SQing Zhang static char *input_tx;
615eca4d84SJoshua Clayton 
hex_dump(const void * src,size_t length,size_t line_size,char * prefix)62b2cfc904SJoshua Clayton static void hex_dump(const void *src, size_t length, size_t line_size,
63b2cfc904SJoshua Clayton 		     char *prefix)
645eca4d84SJoshua Clayton {
655eca4d84SJoshua Clayton 	int i = 0;
665eca4d84SJoshua Clayton 	const unsigned char *address = src;
675eca4d84SJoshua Clayton 	const unsigned char *line = address;
685eca4d84SJoshua Clayton 	unsigned char c;
695eca4d84SJoshua Clayton 
705eca4d84SJoshua Clayton 	printf("%s | ", prefix);
715eca4d84SJoshua Clayton 	while (length-- > 0) {
725eca4d84SJoshua Clayton 		printf("%02X ", *address++);
735eca4d84SJoshua Clayton 		if (!(++i % line_size) || (length == 0 && i % line_size)) {
745eca4d84SJoshua Clayton 			if (length == 0) {
755eca4d84SJoshua Clayton 				while (i++ % line_size)
765eca4d84SJoshua Clayton 					printf("__ ");
775eca4d84SJoshua Clayton 			}
7835386dfdSGeert Uytterhoeven 			printf(" |");
795eca4d84SJoshua Clayton 			while (line < address) {
805eca4d84SJoshua Clayton 				c = *line++;
8135386dfdSGeert Uytterhoeven 				printf("%c", (c < 32 || c > 126) ? '.' : c);
825eca4d84SJoshua Clayton 			}
8335386dfdSGeert Uytterhoeven 			printf("|\n");
845eca4d84SJoshua Clayton 			if (length > 0)
855eca4d84SJoshua Clayton 				printf("%s | ", prefix);
865eca4d84SJoshua Clayton 		}
875eca4d84SJoshua Clayton 	}
885eca4d84SJoshua Clayton }
895eca4d84SJoshua Clayton 
905eca4d84SJoshua Clayton /*
915eca4d84SJoshua Clayton  *  Unescape - process hexadecimal escape character
925eca4d84SJoshua Clayton  *      converts shell input "\x23" -> 0x23
935eca4d84SJoshua Clayton  */
unescape(char * _dst,char * _src,size_t len)945eca4d84SJoshua Clayton static int unescape(char *_dst, char *_src, size_t len)
955eca4d84SJoshua Clayton {
965eca4d84SJoshua Clayton 	int ret = 0;
97a20874f7SJoshua Clayton 	int match;
985eca4d84SJoshua Clayton 	char *src = _src;
995eca4d84SJoshua Clayton 	char *dst = _dst;
1005eca4d84SJoshua Clayton 	unsigned int ch;
1015eca4d84SJoshua Clayton 
1025eca4d84SJoshua Clayton 	while (*src) {
1035eca4d84SJoshua Clayton 		if (*src == '\\' && *(src+1) == 'x') {
104a20874f7SJoshua Clayton 			match = sscanf(src + 2, "%2x", &ch);
105a20874f7SJoshua Clayton 			if (!match)
106a20874f7SJoshua Clayton 				pabort("malformed input string");
107a20874f7SJoshua Clayton 
1085eca4d84SJoshua Clayton 			src += 4;
1095eca4d84SJoshua Clayton 			*dst++ = (unsigned char)ch;
1105eca4d84SJoshua Clayton 		} else {
1115eca4d84SJoshua Clayton 			*dst++ = *src++;
1125eca4d84SJoshua Clayton 		}
1135eca4d84SJoshua Clayton 		ret++;
1145eca4d84SJoshua Clayton 	}
1155eca4d84SJoshua Clayton 	return ret;
1165eca4d84SJoshua Clayton }
1175eca4d84SJoshua Clayton 
transfer(int fd,uint8_t const * tx,uint8_t const * rx,size_t len)1185eca4d84SJoshua Clayton static void transfer(int fd, uint8_t const *tx, uint8_t const *rx, size_t len)
1195eca4d84SJoshua Clayton {
1205eca4d84SJoshua Clayton 	int ret;
121983b2788SJoshua Clayton 	int out_fd;
1225eca4d84SJoshua Clayton 	struct spi_ioc_transfer tr = {
1235eca4d84SJoshua Clayton 		.tx_buf = (unsigned long)tx,
1245eca4d84SJoshua Clayton 		.rx_buf = (unsigned long)rx,
1255eca4d84SJoshua Clayton 		.len = len,
1265eca4d84SJoshua Clayton 		.delay_usecs = delay,
1275eca4d84SJoshua Clayton 		.speed_hz = speed,
1285eca4d84SJoshua Clayton 		.bits_per_word = bits,
1295eca4d84SJoshua Clayton 	};
1305eca4d84SJoshua Clayton 
131896fa735SGeert Uytterhoeven 	if (mode & SPI_TX_OCTAL)
132896fa735SGeert Uytterhoeven 		tr.tx_nbits = 8;
133896fa735SGeert Uytterhoeven 	else if (mode & SPI_TX_QUAD)
1345eca4d84SJoshua Clayton 		tr.tx_nbits = 4;
1355eca4d84SJoshua Clayton 	else if (mode & SPI_TX_DUAL)
1365eca4d84SJoshua Clayton 		tr.tx_nbits = 2;
137896fa735SGeert Uytterhoeven 	if (mode & SPI_RX_OCTAL)
138896fa735SGeert Uytterhoeven 		tr.rx_nbits = 8;
139896fa735SGeert Uytterhoeven 	else if (mode & SPI_RX_QUAD)
1405eca4d84SJoshua Clayton 		tr.rx_nbits = 4;
1415eca4d84SJoshua Clayton 	else if (mode & SPI_RX_DUAL)
1425eca4d84SJoshua Clayton 		tr.rx_nbits = 2;
1435eca4d84SJoshua Clayton 	if (!(mode & SPI_LOOP)) {
144896fa735SGeert Uytterhoeven 		if (mode & (SPI_TX_OCTAL | SPI_TX_QUAD | SPI_TX_DUAL))
1455eca4d84SJoshua Clayton 			tr.rx_buf = 0;
146896fa735SGeert Uytterhoeven 		else if (mode & (SPI_RX_OCTAL | SPI_RX_QUAD | SPI_RX_DUAL))
1475eca4d84SJoshua Clayton 			tr.tx_buf = 0;
1485eca4d84SJoshua Clayton 	}
1495eca4d84SJoshua Clayton 
1505eca4d84SJoshua Clayton 	ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
1515eca4d84SJoshua Clayton 	if (ret < 1)
1525eca4d84SJoshua Clayton 		pabort("can't send spi message");
1535eca4d84SJoshua Clayton 
1545eca4d84SJoshua Clayton 	if (verbose)
1555eca4d84SJoshua Clayton 		hex_dump(tx, len, 32, "TX");
156983b2788SJoshua Clayton 
157983b2788SJoshua Clayton 	if (output_file) {
158983b2788SJoshua Clayton 		out_fd = open(output_file, O_WRONLY | O_CREAT | O_TRUNC, 0666);
159983b2788SJoshua Clayton 		if (out_fd < 0)
160983b2788SJoshua Clayton 			pabort("could not open output file");
161983b2788SJoshua Clayton 
162983b2788SJoshua Clayton 		ret = write(out_fd, rx, len);
163983b2788SJoshua Clayton 		if (ret != len)
164edd3899cSFabio Estevam 			pabort("not all bytes written to output file");
165983b2788SJoshua Clayton 
166983b2788SJoshua Clayton 		close(out_fd);
167983b2788SJoshua Clayton 	}
168983b2788SJoshua Clayton 
1699006a7b3SFrode Isaksen 	if (verbose)
1705eca4d84SJoshua Clayton 		hex_dump(rx, len, 32, "RX");
1715eca4d84SJoshua Clayton }
1725eca4d84SJoshua Clayton 
print_usage(const char * prog)1735eca4d84SJoshua Clayton static void print_usage(const char *prog)
1745eca4d84SJoshua Clayton {
175*b229a7f5SBoerge Struempfel 	printf("Usage: %s [-2348CDFHILMNORSZbdilopsv]\n", prog);
176113f36f2SBoerge Struempfel 	puts("general device settings:\n"
177113f36f2SBoerge Struempfel 		 "  -D --device         device to use (default /dev/spidev1.1)\n"
1785eca4d84SJoshua Clayton 		 "  -s --speed          max speed (Hz)\n"
1795eca4d84SJoshua Clayton 		 "  -d --delay          delay (usec)\n"
1805eca4d84SJoshua Clayton 		 "  -l --loop           loopback\n"
181113f36f2SBoerge Struempfel 		 "spi mode:\n"
1825eca4d84SJoshua Clayton 		 "  -H --cpha           clock phase\n"
1835eca4d84SJoshua Clayton 		 "  -O --cpol           clock polarity\n"
184*b229a7f5SBoerge Struempfel 		 "  -F --rx-cpha-flip   flip CPHA on Rx only xfer\n"
185113f36f2SBoerge Struempfel 		 "number of wires for transmission:\n"
1865eca4d84SJoshua Clayton 		 "  -2 --dual           dual transfer\n"
1879006a7b3SFrode Isaksen 		 "  -4 --quad           quad transfer\n"
188896fa735SGeert Uytterhoeven 		 "  -8 --octal          octal transfer\n"
189113f36f2SBoerge Struempfel 		 "  -3 --3wire          SI/SO signals shared\n"
190*b229a7f5SBoerge Struempfel 		 "  -Z --3wire-hiz      high impedance turnaround\n"
191113f36f2SBoerge Struempfel 		 "data:\n"
192113f36f2SBoerge Struempfel 		 "  -i --input          input data from a file (e.g. \"test.bin\")\n"
193113f36f2SBoerge Struempfel 		 "  -o --output         output data to a file (e.g. \"results.bin\")\n"
194113f36f2SBoerge Struempfel 		 "  -p                  Send data (e.g. \"1234\\xde\\xad\")\n"
1959006a7b3SFrode Isaksen 		 "  -S --size           transfer size\n"
196113f36f2SBoerge Struempfel 		 "  -I --iter           iterations\n"
197113f36f2SBoerge Struempfel 		 "additional parameters:\n"
198113f36f2SBoerge Struempfel 		 "  -b --bpw            bits per word\n"
199113f36f2SBoerge Struempfel 		 "  -L --lsb            least significant bit first\n"
200113f36f2SBoerge Struempfel 		 "  -C --cs-high        chip select active high\n"
201113f36f2SBoerge Struempfel 		 "  -N --no-cs          no chip select\n"
202113f36f2SBoerge Struempfel 		 "  -R --ready          slave pulls low to pause\n"
203*b229a7f5SBoerge Struempfel 		 "  -M --mosi-idle-low  leave mosi line low when idle\n"
204113f36f2SBoerge Struempfel 		 "misc:\n"
205113f36f2SBoerge Struempfel 		 "  -v --verbose        Verbose (show tx buffer)\n");
2065eca4d84SJoshua Clayton 	exit(1);
2075eca4d84SJoshua Clayton }
2085eca4d84SJoshua Clayton 
parse_opts(int argc,char * argv[])2095eca4d84SJoshua Clayton static void parse_opts(int argc, char *argv[])
2105eca4d84SJoshua Clayton {
2115eca4d84SJoshua Clayton 	while (1) {
2125eca4d84SJoshua Clayton 		static const struct option lopts[] = {
2135eca4d84SJoshua Clayton 			{ "device",        1, 0, 'D' },
2145eca4d84SJoshua Clayton 			{ "speed",         1, 0, 's' },
2155eca4d84SJoshua Clayton 			{ "delay",         1, 0, 'd' },
2165eca4d84SJoshua Clayton 			{ "loop",          0, 0, 'l' },
2175eca4d84SJoshua Clayton 			{ "cpha",          0, 0, 'H' },
2185eca4d84SJoshua Clayton 			{ "cpol",          0, 0, 'O' },
219*b229a7f5SBoerge Struempfel 			{ "rx-cpha-flip",  0, 0, 'F' },
2205eca4d84SJoshua Clayton 			{ "dual",          0, 0, '2' },
2215eca4d84SJoshua Clayton 			{ "quad",          0, 0, '4' },
222896fa735SGeert Uytterhoeven 			{ "octal",         0, 0, '8' },
223113f36f2SBoerge Struempfel 			{ "3wire",         0, 0, '3' },
224*b229a7f5SBoerge Struempfel 			{ "3wire-hiz",     0, 0, 'Z' },
225113f36f2SBoerge Struempfel 			{ "input",         1, 0, 'i' },
226113f36f2SBoerge Struempfel 			{ "output",        1, 0, 'o' },
2279006a7b3SFrode Isaksen 			{ "size",          1, 0, 'S' },
2289006a7b3SFrode Isaksen 			{ "iter",          1, 0, 'I' },
229113f36f2SBoerge Struempfel 			{ "bpw",           1, 0, 'b' },
230113f36f2SBoerge Struempfel 			{ "lsb",           0, 0, 'L' },
231113f36f2SBoerge Struempfel 			{ "cs-high",       0, 0, 'C' },
232113f36f2SBoerge Struempfel 			{ "no-cs",         0, 0, 'N' },
233113f36f2SBoerge Struempfel 			{ "ready",         0, 0, 'R' },
234*b229a7f5SBoerge Struempfel 			{ "mosi-idle-low", 0, 0, 'M' },
235113f36f2SBoerge Struempfel 			{ "verbose",       0, 0, 'v' },
2365eca4d84SJoshua Clayton 			{ NULL, 0, 0, 0 },
2375eca4d84SJoshua Clayton 		};
2385eca4d84SJoshua Clayton 		int c;
2395eca4d84SJoshua Clayton 
240*b229a7f5SBoerge Struempfel 		c = getopt_long(argc, argv, "D:s:d:b:i:o:lHOLC3ZFMNR248p:vS:I:",
2417af475a5SJoshua Clayton 				lopts, NULL);
2425eca4d84SJoshua Clayton 
2435eca4d84SJoshua Clayton 		if (c == -1)
2445eca4d84SJoshua Clayton 			break;
2455eca4d84SJoshua Clayton 
2465eca4d84SJoshua Clayton 		switch (c) {
2475eca4d84SJoshua Clayton 		case 'D':
2485eca4d84SJoshua Clayton 			device = optarg;
2495eca4d84SJoshua Clayton 			break;
2505eca4d84SJoshua Clayton 		case 's':
2515eca4d84SJoshua Clayton 			speed = atoi(optarg);
2525eca4d84SJoshua Clayton 			break;
2535eca4d84SJoshua Clayton 		case 'd':
2545eca4d84SJoshua Clayton 			delay = atoi(optarg);
2555eca4d84SJoshua Clayton 			break;
2565eca4d84SJoshua Clayton 		case 'b':
2575eca4d84SJoshua Clayton 			bits = atoi(optarg);
2585eca4d84SJoshua Clayton 			break;
2597af475a5SJoshua Clayton 		case 'i':
2607af475a5SJoshua Clayton 			input_file = optarg;
2617af475a5SJoshua Clayton 			break;
262983b2788SJoshua Clayton 		case 'o':
263983b2788SJoshua Clayton 			output_file = optarg;
264983b2788SJoshua Clayton 			break;
2655eca4d84SJoshua Clayton 		case 'l':
2665eca4d84SJoshua Clayton 			mode |= SPI_LOOP;
2675eca4d84SJoshua Clayton 			break;
2685eca4d84SJoshua Clayton 		case 'H':
2695eca4d84SJoshua Clayton 			mode |= SPI_CPHA;
2705eca4d84SJoshua Clayton 			break;
2715eca4d84SJoshua Clayton 		case 'O':
2725eca4d84SJoshua Clayton 			mode |= SPI_CPOL;
2735eca4d84SJoshua Clayton 			break;
2745eca4d84SJoshua Clayton 		case 'L':
2755eca4d84SJoshua Clayton 			mode |= SPI_LSB_FIRST;
2765eca4d84SJoshua Clayton 			break;
2775eca4d84SJoshua Clayton 		case 'C':
2785eca4d84SJoshua Clayton 			mode |= SPI_CS_HIGH;
2795eca4d84SJoshua Clayton 			break;
2805eca4d84SJoshua Clayton 		case '3':
2815eca4d84SJoshua Clayton 			mode |= SPI_3WIRE;
2825eca4d84SJoshua Clayton 			break;
283*b229a7f5SBoerge Struempfel 		case 'Z':
284*b229a7f5SBoerge Struempfel 			mode |= SPI_3WIRE_HIZ;
285*b229a7f5SBoerge Struempfel 			break;
286*b229a7f5SBoerge Struempfel 		case 'F':
287*b229a7f5SBoerge Struempfel 			mode |= SPI_RX_CPHA_FLIP;
288*b229a7f5SBoerge Struempfel 			break;
289*b229a7f5SBoerge Struempfel 		case 'M':
290*b229a7f5SBoerge Struempfel 			mode |= SPI_MOSI_IDLE_LOW;
291*b229a7f5SBoerge Struempfel 			break;
2925eca4d84SJoshua Clayton 		case 'N':
2935eca4d84SJoshua Clayton 			mode |= SPI_NO_CS;
2945eca4d84SJoshua Clayton 			break;
2955eca4d84SJoshua Clayton 		case 'v':
2965eca4d84SJoshua Clayton 			verbose = 1;
2975eca4d84SJoshua Clayton 			break;
2985eca4d84SJoshua Clayton 		case 'R':
2995eca4d84SJoshua Clayton 			mode |= SPI_READY;
3005eca4d84SJoshua Clayton 			break;
3015eca4d84SJoshua Clayton 		case 'p':
3025eca4d84SJoshua Clayton 			input_tx = optarg;
3035eca4d84SJoshua Clayton 			break;
3045eca4d84SJoshua Clayton 		case '2':
3055eca4d84SJoshua Clayton 			mode |= SPI_TX_DUAL;
3065eca4d84SJoshua Clayton 			break;
3075eca4d84SJoshua Clayton 		case '4':
3085eca4d84SJoshua Clayton 			mode |= SPI_TX_QUAD;
3095eca4d84SJoshua Clayton 			break;
310896fa735SGeert Uytterhoeven 		case '8':
311896fa735SGeert Uytterhoeven 			mode |= SPI_TX_OCTAL;
312896fa735SGeert Uytterhoeven 			break;
3139006a7b3SFrode Isaksen 		case 'S':
3149006a7b3SFrode Isaksen 			transfer_size = atoi(optarg);
3159006a7b3SFrode Isaksen 			break;
3169006a7b3SFrode Isaksen 		case 'I':
3179006a7b3SFrode Isaksen 			iterations = atoi(optarg);
3189006a7b3SFrode Isaksen 			break;
3195eca4d84SJoshua Clayton 		default:
3205eca4d84SJoshua Clayton 			print_usage(argv[0]);
3215eca4d84SJoshua Clayton 		}
3225eca4d84SJoshua Clayton 	}
3235eca4d84SJoshua Clayton 	if (mode & SPI_LOOP) {
3245eca4d84SJoshua Clayton 		if (mode & SPI_TX_DUAL)
3255eca4d84SJoshua Clayton 			mode |= SPI_RX_DUAL;
3265eca4d84SJoshua Clayton 		if (mode & SPI_TX_QUAD)
3275eca4d84SJoshua Clayton 			mode |= SPI_RX_QUAD;
328896fa735SGeert Uytterhoeven 		if (mode & SPI_TX_OCTAL)
329896fa735SGeert Uytterhoeven 			mode |= SPI_RX_OCTAL;
3305eca4d84SJoshua Clayton 	}
3315eca4d84SJoshua Clayton }
3325eca4d84SJoshua Clayton 
transfer_escaped_string(int fd,char * str)3335c437a40SJoshua Clayton static void transfer_escaped_string(int fd, char *str)
3345c437a40SJoshua Clayton {
3350278b34bSGeert Uytterhoeven 	size_t size = strlen(str);
3365c437a40SJoshua Clayton 	uint8_t *tx;
3375c437a40SJoshua Clayton 	uint8_t *rx;
3385c437a40SJoshua Clayton 
3395c437a40SJoshua Clayton 	tx = malloc(size);
3405c437a40SJoshua Clayton 	if (!tx)
3415c437a40SJoshua Clayton 		pabort("can't allocate tx buffer");
3425c437a40SJoshua Clayton 
3435c437a40SJoshua Clayton 	rx = malloc(size);
3445c437a40SJoshua Clayton 	if (!rx)
3455c437a40SJoshua Clayton 		pabort("can't allocate rx buffer");
3465c437a40SJoshua Clayton 
3475c437a40SJoshua Clayton 	size = unescape((char *)tx, str, size);
3485c437a40SJoshua Clayton 	transfer(fd, tx, rx, size);
3495c437a40SJoshua Clayton 	free(rx);
3505c437a40SJoshua Clayton 	free(tx);
3515c437a40SJoshua Clayton }
3525c437a40SJoshua Clayton 
transfer_file(int fd,char * filename)3537af475a5SJoshua Clayton static void transfer_file(int fd, char *filename)
3547af475a5SJoshua Clayton {
3557af475a5SJoshua Clayton 	ssize_t bytes;
3567af475a5SJoshua Clayton 	struct stat sb;
3577af475a5SJoshua Clayton 	int tx_fd;
3587af475a5SJoshua Clayton 	uint8_t *tx;
3597af475a5SJoshua Clayton 	uint8_t *rx;
3607af475a5SJoshua Clayton 
3617af475a5SJoshua Clayton 	if (stat(filename, &sb) == -1)
3627af475a5SJoshua Clayton 		pabort("can't stat input file");
3637af475a5SJoshua Clayton 
3647af475a5SJoshua Clayton 	tx_fd = open(filename, O_RDONLY);
365e634b76cSMichal Vokáč 	if (tx_fd < 0)
3667af475a5SJoshua Clayton 		pabort("can't open input file");
3677af475a5SJoshua Clayton 
3687af475a5SJoshua Clayton 	tx = malloc(sb.st_size);
3697af475a5SJoshua Clayton 	if (!tx)
3707af475a5SJoshua Clayton 		pabort("can't allocate tx buffer");
3717af475a5SJoshua Clayton 
3727af475a5SJoshua Clayton 	rx = malloc(sb.st_size);
3737af475a5SJoshua Clayton 	if (!rx)
3747af475a5SJoshua Clayton 		pabort("can't allocate rx buffer");
3757af475a5SJoshua Clayton 
3767af475a5SJoshua Clayton 	bytes = read(tx_fd, tx, sb.st_size);
3777af475a5SJoshua Clayton 	if (bytes != sb.st_size)
3787af475a5SJoshua Clayton 		pabort("failed to read input file");
3797af475a5SJoshua Clayton 
3807af475a5SJoshua Clayton 	transfer(fd, tx, rx, sb.st_size);
3817af475a5SJoshua Clayton 	free(rx);
3827af475a5SJoshua Clayton 	free(tx);
3837af475a5SJoshua Clayton 	close(tx_fd);
3847af475a5SJoshua Clayton }
3857af475a5SJoshua Clayton 
3869006a7b3SFrode Isaksen static uint64_t _read_count;
3879006a7b3SFrode Isaksen static uint64_t _write_count;
3889006a7b3SFrode Isaksen 
show_transfer_rate(void)3899006a7b3SFrode Isaksen static void show_transfer_rate(void)
3909006a7b3SFrode Isaksen {
3919006a7b3SFrode Isaksen 	static uint64_t prev_read_count, prev_write_count;
3929006a7b3SFrode Isaksen 	double rx_rate, tx_rate;
3939006a7b3SFrode Isaksen 
3949006a7b3SFrode Isaksen 	rx_rate = ((_read_count - prev_read_count) * 8) / (interval*1000.0);
3959006a7b3SFrode Isaksen 	tx_rate = ((_write_count - prev_write_count) * 8) / (interval*1000.0);
3969006a7b3SFrode Isaksen 
3979006a7b3SFrode Isaksen 	printf("rate: tx %.1fkbps, rx %.1fkbps\n", rx_rate, tx_rate);
3989006a7b3SFrode Isaksen 
3999006a7b3SFrode Isaksen 	prev_read_count = _read_count;
4009006a7b3SFrode Isaksen 	prev_write_count = _write_count;
4019006a7b3SFrode Isaksen }
4029006a7b3SFrode Isaksen 
transfer_buf(int fd,int len)4039006a7b3SFrode Isaksen static void transfer_buf(int fd, int len)
4049006a7b3SFrode Isaksen {
4059006a7b3SFrode Isaksen 	uint8_t *tx;
4069006a7b3SFrode Isaksen 	uint8_t *rx;
4079006a7b3SFrode Isaksen 	int i;
4089006a7b3SFrode Isaksen 
4099006a7b3SFrode Isaksen 	tx = malloc(len);
4109006a7b3SFrode Isaksen 	if (!tx)
4119006a7b3SFrode Isaksen 		pabort("can't allocate tx buffer");
4129006a7b3SFrode Isaksen 	for (i = 0; i < len; i++)
4139006a7b3SFrode Isaksen 		tx[i] = random();
4149006a7b3SFrode Isaksen 
4159006a7b3SFrode Isaksen 	rx = malloc(len);
4169006a7b3SFrode Isaksen 	if (!rx)
4179006a7b3SFrode Isaksen 		pabort("can't allocate rx buffer");
4189006a7b3SFrode Isaksen 
4199006a7b3SFrode Isaksen 	transfer(fd, tx, rx, len);
4209006a7b3SFrode Isaksen 
4219006a7b3SFrode Isaksen 	_write_count += len;
4229006a7b3SFrode Isaksen 	_read_count += len;
4239006a7b3SFrode Isaksen 
4249006a7b3SFrode Isaksen 	if (mode & SPI_LOOP) {
4259006a7b3SFrode Isaksen 		if (memcmp(tx, rx, len)) {
4269006a7b3SFrode Isaksen 			fprintf(stderr, "transfer error !\n");
4279006a7b3SFrode Isaksen 			hex_dump(tx, len, 32, "TX");
4289006a7b3SFrode Isaksen 			hex_dump(rx, len, 32, "RX");
4299006a7b3SFrode Isaksen 			exit(1);
4309006a7b3SFrode Isaksen 		}
4319006a7b3SFrode Isaksen 	}
4329006a7b3SFrode Isaksen 
4339006a7b3SFrode Isaksen 	free(rx);
4349006a7b3SFrode Isaksen 	free(tx);
4359006a7b3SFrode Isaksen }
4369006a7b3SFrode Isaksen 
main(int argc,char * argv[])4375eca4d84SJoshua Clayton int main(int argc, char *argv[])
4385eca4d84SJoshua Clayton {
4395eca4d84SJoshua Clayton 	int ret = 0;
4405eca4d84SJoshua Clayton 	int fd;
44141ecad2cSDavid Fries 	uint32_t request;
4425eca4d84SJoshua Clayton 
4435eca4d84SJoshua Clayton 	parse_opts(argc, argv);
4445eca4d84SJoshua Clayton 
4451f3c3632STiezhu Yang 	if (input_tx && input_file)
4461f3c3632STiezhu Yang 		pabort("only one of -p and --input may be selected");
4471f3c3632STiezhu Yang 
4485eca4d84SJoshua Clayton 	fd = open(device, O_RDWR);
4495eca4d84SJoshua Clayton 	if (fd < 0)
4505eca4d84SJoshua Clayton 		pabort("can't open device");
4515eca4d84SJoshua Clayton 
4525eca4d84SJoshua Clayton 	/*
4535eca4d84SJoshua Clayton 	 * spi mode
4545eca4d84SJoshua Clayton 	 */
45541ecad2cSDavid Fries 	/* WR is make a request to assign 'mode' */
45641ecad2cSDavid Fries 	request = mode;
4575eca4d84SJoshua Clayton 	ret = ioctl(fd, SPI_IOC_WR_MODE32, &mode);
4585eca4d84SJoshua Clayton 	if (ret == -1)
4595eca4d84SJoshua Clayton 		pabort("can't set spi mode");
4605eca4d84SJoshua Clayton 
46141ecad2cSDavid Fries 	/* RD is read what mode the device actually is in */
4625eca4d84SJoshua Clayton 	ret = ioctl(fd, SPI_IOC_RD_MODE32, &mode);
4635eca4d84SJoshua Clayton 	if (ret == -1)
4645eca4d84SJoshua Clayton 		pabort("can't get spi mode");
46541ecad2cSDavid Fries 	/* Drivers can reject some mode bits without returning an error.
46641ecad2cSDavid Fries 	 * Read the current value to identify what mode it is in, and if it
46741ecad2cSDavid Fries 	 * differs from the requested mode, warn the user.
46841ecad2cSDavid Fries 	 */
46941ecad2cSDavid Fries 	if (request != mode)
47041ecad2cSDavid Fries 		printf("WARNING device does not support requested mode 0x%x\n",
47141ecad2cSDavid Fries 			request);
4725eca4d84SJoshua Clayton 
4735eca4d84SJoshua Clayton 	/*
4745eca4d84SJoshua Clayton 	 * bits per word
4755eca4d84SJoshua Clayton 	 */
4765eca4d84SJoshua Clayton 	ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
4775eca4d84SJoshua Clayton 	if (ret == -1)
4785eca4d84SJoshua Clayton 		pabort("can't set bits per word");
4795eca4d84SJoshua Clayton 
4805eca4d84SJoshua Clayton 	ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
4815eca4d84SJoshua Clayton 	if (ret == -1)
4825eca4d84SJoshua Clayton 		pabort("can't get bits per word");
4835eca4d84SJoshua Clayton 
4845eca4d84SJoshua Clayton 	/*
4855eca4d84SJoshua Clayton 	 * max speed hz
4865eca4d84SJoshua Clayton 	 */
4875eca4d84SJoshua Clayton 	ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
4885eca4d84SJoshua Clayton 	if (ret == -1)
4895eca4d84SJoshua Clayton 		pabort("can't set max speed hz");
4905eca4d84SJoshua Clayton 
4915eca4d84SJoshua Clayton 	ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
4925eca4d84SJoshua Clayton 	if (ret == -1)
4935eca4d84SJoshua Clayton 		pabort("can't get max speed hz");
4945eca4d84SJoshua Clayton 
4955eca4d84SJoshua Clayton 	printf("spi mode: 0x%x\n", mode);
4969ec8ade8SGeert Uytterhoeven 	printf("bits per word: %u\n", bits);
4979ec8ade8SGeert Uytterhoeven 	printf("max speed: %u Hz (%u kHz)\n", speed, speed/1000);
4985eca4d84SJoshua Clayton 
4995c437a40SJoshua Clayton 	if (input_tx)
5005c437a40SJoshua Clayton 		transfer_escaped_string(fd, input_tx);
5017af475a5SJoshua Clayton 	else if (input_file)
5027af475a5SJoshua Clayton 		transfer_file(fd, input_file);
5039006a7b3SFrode Isaksen 	else if (transfer_size) {
5049006a7b3SFrode Isaksen 		struct timespec last_stat;
5059006a7b3SFrode Isaksen 
5069006a7b3SFrode Isaksen 		clock_gettime(CLOCK_MONOTONIC, &last_stat);
5079006a7b3SFrode Isaksen 
5089006a7b3SFrode Isaksen 		while (iterations-- > 0) {
5099006a7b3SFrode Isaksen 			struct timespec current;
5109006a7b3SFrode Isaksen 
5119006a7b3SFrode Isaksen 			transfer_buf(fd, transfer_size);
5129006a7b3SFrode Isaksen 
5139006a7b3SFrode Isaksen 			clock_gettime(CLOCK_MONOTONIC, &current);
5149006a7b3SFrode Isaksen 			if (current.tv_sec - last_stat.tv_sec > interval) {
5159006a7b3SFrode Isaksen 				show_transfer_rate();
5169006a7b3SFrode Isaksen 				last_stat = current;
5179006a7b3SFrode Isaksen 			}
5189006a7b3SFrode Isaksen 		}
5199006a7b3SFrode Isaksen 		printf("total: tx %.1fKB, rx %.1fKB\n",
5209006a7b3SFrode Isaksen 		       _write_count/1024.0, _read_count/1024.0);
5219006a7b3SFrode Isaksen 	} else
5225eca4d84SJoshua Clayton 		transfer(fd, default_tx, default_rx, sizeof(default_tx));
5235eca4d84SJoshua Clayton 
5245eca4d84SJoshua Clayton 	close(fd);
5255eca4d84SJoshua Clayton 
5265eca4d84SJoshua Clayton 	return ret;
5275eca4d84SJoshua Clayton }
528