Blame view

tools/spi/spidev_test.c 9.92 KB
84a14ae8c   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
22b238bdb   Anton Vorontsov   spidev_test utility
2
3
4
5
6
7
  /*
   * SPI testing utility (using spidev driver)
   *
   * Copyright (c) 2007  MontaVista Software, Inc.
   * Copyright (c) 2007  Anton Vorontsov <avorontsov@ru.mvista.com>
   *
22b238bdb   Anton Vorontsov   spidev_test utility
8
9
10
11
12
13
14
   * Cross-compile with cross-gcc -I/path/to/cross-kernel/include
   */
  
  #include <stdint.h>
  #include <unistd.h>
  #include <stdio.h>
  #include <stdlib.h>
b78ce7ed5   Adrian Remonda   spi: spidev_test:...
15
  #include <string.h>
22b238bdb   Anton Vorontsov   spidev_test utility
16
17
  #include <getopt.h>
  #include <fcntl.h>
9006a7b32   Frode Isaksen   spi: spidev_test:...
18
  #include <time.h>
22b238bdb   Anton Vorontsov   spidev_test utility
19
  #include <sys/ioctl.h>
8736f8022   Baruch Siach   spi: spidev_test:...
20
  #include <linux/ioctl.h>
7af475a5b   Joshua Clayton   spi: spidev_test:...
21
  #include <sys/stat.h>
22b238bdb   Anton Vorontsov   spidev_test utility
22
23
24
25
26
27
28
29
30
31
  #include <linux/types.h>
  #include <linux/spi/spidev.h>
  
  #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
  
  static void pabort(const char *s)
  {
  	perror(s);
  	abort();
  }
cd58310d7   WANG Cong   Documentation/spi...
32
  static const char *device = "/dev/spidev1.1";
c2e78c34e   Geert Uytterhoeven   spi: spidev_test:...
33
  static uint32_t mode;
22b238bdb   Anton Vorontsov   spidev_test utility
34
  static uint8_t bits = 8;
7af475a5b   Joshua Clayton   spi: spidev_test:...
35
  static char *input_file;
983b27886   Joshua Clayton   spi: spidev_test:...
36
  static char *output_file;
22b238bdb   Anton Vorontsov   spidev_test utility
37
38
  static uint32_t speed = 500000;
  static uint16_t delay;
31a5c5a72   Adrian Remonda   spi: spidev_test:...
39
  static int verbose;
9006a7b32   Frode Isaksen   spi: spidev_test:...
40
41
42
  static int transfer_size;
  static int iterations;
  static int interval = 5; /* interval in seconds for showing transfer rate */
22b238bdb   Anton Vorontsov   spidev_test utility
43

30061915b   Adrian Remonda   spi: spidev_test:...
44
45
46
47
48
49
50
51
52
53
54
  uint8_t default_tx[] = {
  	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  	0x40, 0x00, 0x00, 0x00, 0x00, 0x95,
  	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  	0xF0, 0x0D,
  };
  
  uint8_t default_rx[ARRAY_SIZE(default_tx)] = {0, };
  char *input_tx;
b2cfc9042   Joshua Clayton   spi: spidev_test:...
55
56
  static void hex_dump(const void *src, size_t length, size_t line_size,
  		     char *prefix)
b78ce7ed5   Adrian Remonda   spi: spidev_test:...
57
58
59
60
61
62
63
64
65
66
67
68
69
70
  {
  	int i = 0;
  	const unsigned char *address = src;
  	const unsigned char *line = address;
  	unsigned char c;
  
  	printf("%s | ", prefix);
  	while (length-- > 0) {
  		printf("%02X ", *address++);
  		if (!(++i % line_size) || (length == 0 && i % line_size)) {
  			if (length == 0) {
  				while (i++ % line_size)
  					printf("__ ");
  			}
35386dfd1   Geert Uytterhoeven   spi: spidev_test:...
71
  			printf(" |");
b78ce7ed5   Adrian Remonda   spi: spidev_test:...
72
73
  			while (line < address) {
  				c = *line++;
35386dfd1   Geert Uytterhoeven   spi: spidev_test:...
74
  				printf("%c", (c < 32 || c > 126) ? '.' : c);
b78ce7ed5   Adrian Remonda   spi: spidev_test:...
75
  			}
35386dfd1   Geert Uytterhoeven   spi: spidev_test:...
76
77
  			printf("|
  ");
b78ce7ed5   Adrian Remonda   spi: spidev_test:...
78
79
80
81
82
  			if (length > 0)
  				printf("%s | ", prefix);
  		}
  	}
  }
30061915b   Adrian Remonda   spi: spidev_test:...
83
84
85
86
  /*
   *  Unescape - process hexadecimal escape character
   *      converts shell input "\x23" -> 0x23
   */
07eec628f   Andrew Morton   Documentation/spi...
87
  static int unescape(char *_dst, char *_src, size_t len)
30061915b   Adrian Remonda   spi: spidev_test:...
88
89
  {
  	int ret = 0;
a20874f78   Joshua Clayton   spi: spidev_test:...
90
  	int match;
30061915b   Adrian Remonda   spi: spidev_test:...
91
92
93
94
95
96
  	char *src = _src;
  	char *dst = _dst;
  	unsigned int ch;
  
  	while (*src) {
  		if (*src == '\\' && *(src+1) == 'x') {
a20874f78   Joshua Clayton   spi: spidev_test:...
97
98
99
  			match = sscanf(src + 2, "%2x", &ch);
  			if (!match)
  				pabort("malformed input string");
30061915b   Adrian Remonda   spi: spidev_test:...
100
101
102
103
104
105
106
107
108
109
110
  			src += 4;
  			*dst++ = (unsigned char)ch;
  		} else {
  			*dst++ = *src++;
  		}
  		ret++;
  	}
  	return ret;
  }
  
  static void transfer(int fd, uint8_t const *tx, uint8_t const *rx, size_t len)
22b238bdb   Anton Vorontsov   spidev_test utility
111
112
  {
  	int ret;
983b27886   Joshua Clayton   spi: spidev_test:...
113
  	int out_fd;
22b238bdb   Anton Vorontsov   spidev_test utility
114
115
116
  	struct spi_ioc_transfer tr = {
  		.tx_buf = (unsigned long)tx,
  		.rx_buf = (unsigned long)rx,
30061915b   Adrian Remonda   spi: spidev_test:...
117
  		.len = len,
22b238bdb   Anton Vorontsov   spidev_test utility
118
119
120
121
  		.delay_usecs = delay,
  		.speed_hz = speed,
  		.bits_per_word = bits,
  	};
c2e78c34e   Geert Uytterhoeven   spi: spidev_test:...
122
123
124
125
126
127
128
129
130
131
132
133
134
135
  	if (mode & SPI_TX_QUAD)
  		tr.tx_nbits = 4;
  	else if (mode & SPI_TX_DUAL)
  		tr.tx_nbits = 2;
  	if (mode & SPI_RX_QUAD)
  		tr.rx_nbits = 4;
  	else if (mode & SPI_RX_DUAL)
  		tr.rx_nbits = 2;
  	if (!(mode & SPI_LOOP)) {
  		if (mode & (SPI_TX_QUAD | SPI_TX_DUAL))
  			tr.rx_buf = 0;
  		else if (mode & (SPI_RX_QUAD | SPI_RX_DUAL))
  			tr.tx_buf = 0;
  	}
22b238bdb   Anton Vorontsov   spidev_test utility
136
  	ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
95b1ed2ac   Hector Palacios   spi: spidev_test ...
137
  	if (ret < 1)
22b238bdb   Anton Vorontsov   spidev_test utility
138
  		pabort("can't send spi message");
31a5c5a72   Adrian Remonda   spi: spidev_test:...
139
  	if (verbose)
30061915b   Adrian Remonda   spi: spidev_test:...
140
  		hex_dump(tx, len, 32, "TX");
983b27886   Joshua Clayton   spi: spidev_test:...
141
142
143
144
145
146
147
148
  
  	if (output_file) {
  		out_fd = open(output_file, O_WRONLY | O_CREAT | O_TRUNC, 0666);
  		if (out_fd < 0)
  			pabort("could not open output file");
  
  		ret = write(out_fd, rx, len);
  		if (ret != len)
edd3899c8   Fabio Estevam   spi: spidev_test:...
149
  			pabort("not all bytes written to output file");
983b27886   Joshua Clayton   spi: spidev_test:...
150
151
152
  
  		close(out_fd);
  	}
9006a7b32   Frode Isaksen   spi: spidev_test:...
153
  	if (verbose)
983b27886   Joshua Clayton   spi: spidev_test:...
154
  		hex_dump(rx, len, 32, "RX");
22b238bdb   Anton Vorontsov   spidev_test utility
155
  }
b7ed698cc   Ladinu Chandrasinghe   Documentation/: f...
156
  static void print_usage(const char *prog)
22b238bdb   Anton Vorontsov   spidev_test utility
157
  {
9006a7b32   Frode Isaksen   spi: spidev_test:...
158
159
  	printf("Usage: %s [-DsbdlHOLC3vpNR24SI]
  ", prog);
22b238bdb   Anton Vorontsov   spidev_test utility
160
161
162
163
164
165
  	puts("  -D --device   device to use (default /dev/spidev1.1)
  "
  	     "  -s --speed    max speed (Hz)
  "
  	     "  -d --delay    delay (usec)
  "
b2cfc9042   Joshua Clayton   spi: spidev_test:...
166
167
  	     "  -b --bpw      bits per word
  "
7af475a5b   Joshua Clayton   spi: spidev_test:...
168
169
  	     "  -i --input    input data from a file (e.g. \"test.bin\")
  "
983b27886   Joshua Clayton   spi: spidev_test:...
170
171
  	     "  -o --output   output data to a file (e.g. \"results.bin\")
  "
22b238bdb   Anton Vorontsov   spidev_test utility
172
173
174
175
176
177
178
179
180
181
  	     "  -l --loop     loopback
  "
  	     "  -H --cpha     clock phase
  "
  	     "  -O --cpol     clock polarity
  "
  	     "  -L --lsb      least significant bit first
  "
  	     "  -C --cs-high  chip select active high
  "
925d16a20   Geert Uytterhoeven   spi/spidev_test: ...
182
183
  	     "  -3 --3wire    SI/SO signals shared
  "
31a5c5a72   Adrian Remonda   spi: spidev_test:...
184
185
  	     "  -v --verbose  Verbose (show tx buffer)
  "
30061915b   Adrian Remonda   spi: spidev_test:...
186
187
  	     "  -p            Send data (e.g. \"1234\\xde\\xad\")
  "
925d16a20   Geert Uytterhoeven   spi/spidev_test: ...
188
189
  	     "  -N --no-cs    no chip select
  "
c2e78c34e   Geert Uytterhoeven   spi: spidev_test:...
190
191
192
193
  	     "  -R --ready    slave pulls low to pause
  "
  	     "  -2 --dual     dual transfer
  "
9006a7b32   Frode Isaksen   spi: spidev_test:...
194
195
196
197
198
199
  	     "  -4 --quad     quad transfer
  "
  	     "  -S --size     transfer size
  "
  	     "  -I --iter     iterations
  ");
22b238bdb   Anton Vorontsov   spidev_test utility
200
201
  	exit(1);
  }
b7ed698cc   Ladinu Chandrasinghe   Documentation/: f...
202
  static void parse_opts(int argc, char *argv[])
22b238bdb   Anton Vorontsov   spidev_test utility
203
204
  {
  	while (1) {
cd58310d7   WANG Cong   Documentation/spi...
205
  		static const struct option lopts[] = {
22b238bdb   Anton Vorontsov   spidev_test utility
206
207
208
209
  			{ "device",  1, 0, 'D' },
  			{ "speed",   1, 0, 's' },
  			{ "delay",   1, 0, 'd' },
  			{ "bpw",     1, 0, 'b' },
7af475a5b   Joshua Clayton   spi: spidev_test:...
210
  			{ "input",   1, 0, 'i' },
983b27886   Joshua Clayton   spi: spidev_test:...
211
  			{ "output",  1, 0, 'o' },
22b238bdb   Anton Vorontsov   spidev_test utility
212
213
214
215
216
217
  			{ "loop",    0, 0, 'l' },
  			{ "cpha",    0, 0, 'H' },
  			{ "cpol",    0, 0, 'O' },
  			{ "lsb",     0, 0, 'L' },
  			{ "cs-high", 0, 0, 'C' },
  			{ "3wire",   0, 0, '3' },
b55f627fe   David Brownell   spi: new spi->mod...
218
219
  			{ "no-cs",   0, 0, 'N' },
  			{ "ready",   0, 0, 'R' },
c2e78c34e   Geert Uytterhoeven   spi: spidev_test:...
220
  			{ "dual",    0, 0, '2' },
31a5c5a72   Adrian Remonda   spi: spidev_test:...
221
  			{ "verbose", 0, 0, 'v' },
c2e78c34e   Geert Uytterhoeven   spi: spidev_test:...
222
  			{ "quad",    0, 0, '4' },
9006a7b32   Frode Isaksen   spi: spidev_test:...
223
224
  			{ "size",    1, 0, 'S' },
  			{ "iter",    1, 0, 'I' },
22b238bdb   Anton Vorontsov   spidev_test utility
225
226
227
  			{ NULL, 0, 0, 0 },
  		};
  		int c;
9006a7b32   Frode Isaksen   spi: spidev_test:...
228
  		c = getopt_long(argc, argv, "D:s:d:b:i:o:lHOLC3NR24p:vS:I:",
7af475a5b   Joshua Clayton   spi: spidev_test:...
229
  				lopts, NULL);
22b238bdb   Anton Vorontsov   spidev_test utility
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
  
  		if (c == -1)
  			break;
  
  		switch (c) {
  		case 'D':
  			device = optarg;
  			break;
  		case 's':
  			speed = atoi(optarg);
  			break;
  		case 'd':
  			delay = atoi(optarg);
  			break;
  		case 'b':
  			bits = atoi(optarg);
  			break;
7af475a5b   Joshua Clayton   spi: spidev_test:...
247
248
249
  		case 'i':
  			input_file = optarg;
  			break;
983b27886   Joshua Clayton   spi: spidev_test:...
250
251
252
  		case 'o':
  			output_file = optarg;
  			break;
22b238bdb   Anton Vorontsov   spidev_test utility
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
  		case 'l':
  			mode |= SPI_LOOP;
  			break;
  		case 'H':
  			mode |= SPI_CPHA;
  			break;
  		case 'O':
  			mode |= SPI_CPOL;
  			break;
  		case 'L':
  			mode |= SPI_LSB_FIRST;
  			break;
  		case 'C':
  			mode |= SPI_CS_HIGH;
  			break;
  		case '3':
  			mode |= SPI_3WIRE;
  			break;
b55f627fe   David Brownell   spi: new spi->mod...
271
272
273
  		case 'N':
  			mode |= SPI_NO_CS;
  			break;
31a5c5a72   Adrian Remonda   spi: spidev_test:...
274
275
276
  		case 'v':
  			verbose = 1;
  			break;
b55f627fe   David Brownell   spi: new spi->mod...
277
278
279
  		case 'R':
  			mode |= SPI_READY;
  			break;
30061915b   Adrian Remonda   spi: spidev_test:...
280
281
282
  		case 'p':
  			input_tx = optarg;
  			break;
c2e78c34e   Geert Uytterhoeven   spi: spidev_test:...
283
284
285
286
287
288
  		case '2':
  			mode |= SPI_TX_DUAL;
  			break;
  		case '4':
  			mode |= SPI_TX_QUAD;
  			break;
9006a7b32   Frode Isaksen   spi: spidev_test:...
289
290
291
292
293
294
  		case 'S':
  			transfer_size = atoi(optarg);
  			break;
  		case 'I':
  			iterations = atoi(optarg);
  			break;
22b238bdb   Anton Vorontsov   spidev_test utility
295
296
297
298
299
  		default:
  			print_usage(argv[0]);
  			break;
  		}
  	}
c2e78c34e   Geert Uytterhoeven   spi: spidev_test:...
300
301
302
303
304
305
  	if (mode & SPI_LOOP) {
  		if (mode & SPI_TX_DUAL)
  			mode |= SPI_RX_DUAL;
  		if (mode & SPI_TX_QUAD)
  			mode |= SPI_RX_QUAD;
  	}
22b238bdb   Anton Vorontsov   spidev_test utility
306
  }
5c437a401   Joshua Clayton   spi: spidev_test:...
307
308
  static void transfer_escaped_string(int fd, char *str)
  {
0278b34bf   Geert Uytterhoeven   spi: spidev_test:...
309
  	size_t size = strlen(str);
5c437a401   Joshua Clayton   spi: spidev_test:...
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
  	uint8_t *tx;
  	uint8_t *rx;
  
  	tx = malloc(size);
  	if (!tx)
  		pabort("can't allocate tx buffer");
  
  	rx = malloc(size);
  	if (!rx)
  		pabort("can't allocate rx buffer");
  
  	size = unescape((char *)tx, str, size);
  	transfer(fd, tx, rx, size);
  	free(rx);
  	free(tx);
  }
7af475a5b   Joshua Clayton   spi: spidev_test:...
326
327
328
329
330
331
332
333
334
335
336
337
  static void transfer_file(int fd, char *filename)
  {
  	ssize_t bytes;
  	struct stat sb;
  	int tx_fd;
  	uint8_t *tx;
  	uint8_t *rx;
  
  	if (stat(filename, &sb) == -1)
  		pabort("can't stat input file");
  
  	tx_fd = open(filename, O_RDONLY);
e634b76ca   Michal Vokáč   spi: spidev_test:...
338
  	if (tx_fd < 0)
7af475a5b   Joshua Clayton   spi: spidev_test:...
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
  		pabort("can't open input file");
  
  	tx = malloc(sb.st_size);
  	if (!tx)
  		pabort("can't allocate tx buffer");
  
  	rx = malloc(sb.st_size);
  	if (!rx)
  		pabort("can't allocate rx buffer");
  
  	bytes = read(tx_fd, tx, sb.st_size);
  	if (bytes != sb.st_size)
  		pabort("failed to read input file");
  
  	transfer(fd, tx, rx, sb.st_size);
  	free(rx);
  	free(tx);
  	close(tx_fd);
  }
9006a7b32   Frode Isaksen   spi: spidev_test:...
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
  static uint64_t _read_count;
  static uint64_t _write_count;
  
  static void show_transfer_rate(void)
  {
  	static uint64_t prev_read_count, prev_write_count;
  	double rx_rate, tx_rate;
  
  	rx_rate = ((_read_count - prev_read_count) * 8) / (interval*1000.0);
  	tx_rate = ((_write_count - prev_write_count) * 8) / (interval*1000.0);
  
  	printf("rate: tx %.1fkbps, rx %.1fkbps
  ", rx_rate, tx_rate);
  
  	prev_read_count = _read_count;
  	prev_write_count = _write_count;
  }
  
  static void transfer_buf(int fd, int len)
  {
  	uint8_t *tx;
  	uint8_t *rx;
  	int i;
  
  	tx = malloc(len);
  	if (!tx)
  		pabort("can't allocate tx buffer");
  	for (i = 0; i < len; i++)
  		tx[i] = random();
  
  	rx = malloc(len);
  	if (!rx)
  		pabort("can't allocate rx buffer");
  
  	transfer(fd, tx, rx, len);
  
  	_write_count += len;
  	_read_count += len;
  
  	if (mode & SPI_LOOP) {
  		if (memcmp(tx, rx, len)) {
  			fprintf(stderr, "transfer error !
  ");
  			hex_dump(tx, len, 32, "TX");
  			hex_dump(rx, len, 32, "RX");
  			exit(1);
  		}
  	}
  
  	free(rx);
  	free(tx);
  }
22b238bdb   Anton Vorontsov   spidev_test utility
410
411
412
413
414
415
416
417
418
419
420
421
422
423
  int main(int argc, char *argv[])
  {
  	int ret = 0;
  	int fd;
  
  	parse_opts(argc, argv);
  
  	fd = open(device, O_RDWR);
  	if (fd < 0)
  		pabort("can't open device");
  
  	/*
  	 * spi mode
  	 */
c2e78c34e   Geert Uytterhoeven   spi: spidev_test:...
424
  	ret = ioctl(fd, SPI_IOC_WR_MODE32, &mode);
22b238bdb   Anton Vorontsov   spidev_test utility
425
426
  	if (ret == -1)
  		pabort("can't set spi mode");
c2e78c34e   Geert Uytterhoeven   spi: spidev_test:...
427
  	ret = ioctl(fd, SPI_IOC_RD_MODE32, &mode);
22b238bdb   Anton Vorontsov   spidev_test utility
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
  	if (ret == -1)
  		pabort("can't get spi mode");
  
  	/*
  	 * bits per word
  	 */
  	ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
  	if (ret == -1)
  		pabort("can't set bits per word");
  
  	ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
  	if (ret == -1)
  		pabort("can't get bits per word");
  
  	/*
  	 * max speed hz
  	 */
  	ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
  	if (ret == -1)
  		pabort("can't set max speed hz");
  
  	ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
  	if (ret == -1)
  		pabort("can't get max speed hz");
c2e78c34e   Geert Uytterhoeven   spi: spidev_test:...
452
453
  	printf("spi mode: 0x%x
  ", mode);
22b238bdb   Anton Vorontsov   spidev_test utility
454
455
456
457
  	printf("bits per word: %d
  ", bits);
  	printf("max speed: %d Hz (%d KHz)
  ", speed, speed/1000);
7af475a5b   Joshua Clayton   spi: spidev_test:...
458
459
  	if (input_tx && input_file)
  		pabort("only one of -p and --input may be selected");
5c437a401   Joshua Clayton   spi: spidev_test:...
460
461
  	if (input_tx)
  		transfer_escaped_string(fd, input_tx);
7af475a5b   Joshua Clayton   spi: spidev_test:...
462
463
  	else if (input_file)
  		transfer_file(fd, input_file);
9006a7b32   Frode Isaksen   spi: spidev_test:...
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
  	else if (transfer_size) {
  		struct timespec last_stat;
  
  		clock_gettime(CLOCK_MONOTONIC, &last_stat);
  
  		while (iterations-- > 0) {
  			struct timespec current;
  
  			transfer_buf(fd, transfer_size);
  
  			clock_gettime(CLOCK_MONOTONIC, &current);
  			if (current.tv_sec - last_stat.tv_sec > interval) {
  				show_transfer_rate();
  				last_stat = current;
  			}
  		}
  		printf("total: tx %.1fKB, rx %.1fKB
  ",
  		       _write_count/1024.0, _read_count/1024.0);
  	} else
30061915b   Adrian Remonda   spi: spidev_test:...
484
  		transfer(fd, default_tx, default_rx, sizeof(default_tx));
22b238bdb   Anton Vorontsov   spidev_test utility
485
486
487
488
489
  
  	close(fd);
  
  	return ret;
  }