blob: 83844f8b862ada41db6ce2c061851e263a0d416d [file] [log] [blame]
Thomas Gleixner84a14ae2019-05-28 09:57:07 -07001// SPDX-License-Identifier: GPL-2.0-only
Anton Vorontsov22b238b2007-07-31 00:38:44 -07002/*
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 *
Anton Vorontsov22b238b2007-07-31 00:38:44 -07008 * Cross-compile with cross-gcc -I/path/to/cross-kernel/include
9 */
10
11#include <stdint.h>
12#include <unistd.h>
13#include <stdio.h>
14#include <stdlib.h>
Adrian Remondab78ce7e2015-03-10 16:12:30 -040015#include <string.h>
Tiezhu Yang470a0722020-02-13 12:16:07 +080016#include <errno.h>
Anton Vorontsov22b238b2007-07-31 00:38:44 -070017#include <getopt.h>
18#include <fcntl.h>
Frode Isaksen9006a7b2017-03-21 16:48:46 +010019#include <time.h>
Anton Vorontsov22b238b2007-07-31 00:38:44 -070020#include <sys/ioctl.h>
Baruch Siach8736f802016-08-12 16:04:33 +030021#include <linux/ioctl.h>
Joshua Clayton7af475a2015-11-18 14:30:39 -080022#include <sys/stat.h>
Anton Vorontsov22b238b2007-07-31 00:38:44 -070023#include <linux/types.h>
24#include <linux/spi/spidev.h>
25
26#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
27
28static void pabort(const char *s)
29{
Tiezhu Yang470a0722020-02-13 12:16:07 +080030 if (errno != 0)
31 perror(s);
32 else
33 printf("%s\n", s);
34
Anton Vorontsov22b238b2007-07-31 00:38:44 -070035 abort();
36}
37
WANG Congcd583102007-10-16 01:27:47 -070038static const char *device = "/dev/spidev1.1";
Geert Uytterhoevenc2e78c32014-02-25 11:40:18 +010039static uint32_t mode;
Anton Vorontsov22b238b2007-07-31 00:38:44 -070040static uint8_t bits = 8;
Joshua Clayton7af475a2015-11-18 14:30:39 -080041static char *input_file;
Joshua Clayton983b278862015-11-18 14:30:40 -080042static char *output_file;
Anton Vorontsov22b238b2007-07-31 00:38:44 -070043static uint32_t speed = 500000;
44static uint16_t delay;
Adrian Remonda31a5c5a2015-03-10 16:12:31 -040045static int verbose;
Frode Isaksen9006a7b2017-03-21 16:48:46 +010046static int transfer_size;
47static int iterations;
48static int interval = 5; /* interval in seconds for showing transfer rate */
Anton Vorontsov22b238b2007-07-31 00:38:44 -070049
Qing Zhangbd207792020-06-11 20:56:51 +080050static uint8_t default_tx[] = {
Adrian Remonda30061912015-03-10 16:12:32 -040051 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
52 0x40, 0x00, 0x00, 0x00, 0x00, 0x95,
53 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
54 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
55 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
56 0xF0, 0x0D,
57};
58
Qing Zhangbd207792020-06-11 20:56:51 +080059static uint8_t default_rx[ARRAY_SIZE(default_tx)] = {0, };
60static char *input_tx;
Adrian Remonda30061912015-03-10 16:12:32 -040061
Joshua Claytonb2cfc902015-11-18 14:30:42 -080062static void hex_dump(const void *src, size_t length, size_t line_size,
63 char *prefix)
Adrian Remondab78ce7e2015-03-10 16:12:30 -040064{
65 int i = 0;
66 const unsigned char *address = src;
67 const unsigned char *line = address;
68 unsigned char c;
69
70 printf("%s | ", prefix);
71 while (length-- > 0) {
72 printf("%02X ", *address++);
73 if (!(++i % line_size) || (length == 0 && i % line_size)) {
74 if (length == 0) {
75 while (i++ % line_size)
76 printf("__ ");
77 }
Geert Uytterhoeven35386df2018-09-03 19:33:23 +020078 printf(" |");
Adrian Remondab78ce7e2015-03-10 16:12:30 -040079 while (line < address) {
80 c = *line++;
Geert Uytterhoeven35386df2018-09-03 19:33:23 +020081 printf("%c", (c < 32 || c > 126) ? '.' : c);
Adrian Remondab78ce7e2015-03-10 16:12:30 -040082 }
Geert Uytterhoeven35386df2018-09-03 19:33:23 +020083 printf("|\n");
Adrian Remondab78ce7e2015-03-10 16:12:30 -040084 if (length > 0)
85 printf("%s | ", prefix);
86 }
87 }
88}
89
Adrian Remonda30061912015-03-10 16:12:32 -040090/*
91 * Unescape - process hexadecimal escape character
92 * converts shell input "\x23" -> 0x23
93 */
Andrew Morton07eec622015-04-16 12:49:29 -070094static int unescape(char *_dst, char *_src, size_t len)
Adrian Remonda30061912015-03-10 16:12:32 -040095{
96 int ret = 0;
Joshua Claytona20874f2015-11-18 14:30:41 -080097 int match;
Adrian Remonda30061912015-03-10 16:12:32 -040098 char *src = _src;
99 char *dst = _dst;
100 unsigned int ch;
101
102 while (*src) {
103 if (*src == '\\' && *(src+1) == 'x') {
Joshua Claytona20874f2015-11-18 14:30:41 -0800104 match = sscanf(src + 2, "%2x", &ch);
105 if (!match)
106 pabort("malformed input string");
107
Adrian Remonda30061912015-03-10 16:12:32 -0400108 src += 4;
109 *dst++ = (unsigned char)ch;
110 } else {
111 *dst++ = *src++;
112 }
113 ret++;
114 }
115 return ret;
116}
117
118static void transfer(int fd, uint8_t const *tx, uint8_t const *rx, size_t len)
Anton Vorontsov22b238b2007-07-31 00:38:44 -0700119{
120 int ret;
Joshua Clayton983b278862015-11-18 14:30:40 -0800121 int out_fd;
Anton Vorontsov22b238b2007-07-31 00:38:44 -0700122 struct spi_ioc_transfer tr = {
123 .tx_buf = (unsigned long)tx,
124 .rx_buf = (unsigned long)rx,
Adrian Remonda30061912015-03-10 16:12:32 -0400125 .len = len,
Anton Vorontsov22b238b2007-07-31 00:38:44 -0700126 .delay_usecs = delay,
127 .speed_hz = speed,
128 .bits_per_word = bits,
129 };
130
Geert Uytterhoeven896fa732020-04-16 12:18:35 +0200131 if (mode & SPI_TX_OCTAL)
132 tr.tx_nbits = 8;
133 else if (mode & SPI_TX_QUAD)
Geert Uytterhoevenc2e78c32014-02-25 11:40:18 +0100134 tr.tx_nbits = 4;
135 else if (mode & SPI_TX_DUAL)
136 tr.tx_nbits = 2;
Geert Uytterhoeven896fa732020-04-16 12:18:35 +0200137 if (mode & SPI_RX_OCTAL)
138 tr.rx_nbits = 8;
139 else if (mode & SPI_RX_QUAD)
Geert Uytterhoevenc2e78c32014-02-25 11:40:18 +0100140 tr.rx_nbits = 4;
141 else if (mode & SPI_RX_DUAL)
142 tr.rx_nbits = 2;
143 if (!(mode & SPI_LOOP)) {
Geert Uytterhoeven896fa732020-04-16 12:18:35 +0200144 if (mode & (SPI_TX_OCTAL | SPI_TX_QUAD | SPI_TX_DUAL))
Geert Uytterhoevenc2e78c32014-02-25 11:40:18 +0100145 tr.rx_buf = 0;
Geert Uytterhoeven896fa732020-04-16 12:18:35 +0200146 else if (mode & (SPI_RX_OCTAL | SPI_RX_QUAD | SPI_RX_DUAL))
Geert Uytterhoevenc2e78c32014-02-25 11:40:18 +0100147 tr.tx_buf = 0;
148 }
149
Anton Vorontsov22b238b2007-07-31 00:38:44 -0700150 ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
Hector Palacios95b1ed22010-04-29 15:02:28 -0700151 if (ret < 1)
Anton Vorontsov22b238b2007-07-31 00:38:44 -0700152 pabort("can't send spi message");
153
Adrian Remonda31a5c5a2015-03-10 16:12:31 -0400154 if (verbose)
Adrian Remonda30061912015-03-10 16:12:32 -0400155 hex_dump(tx, len, 32, "TX");
Joshua Clayton983b278862015-11-18 14:30:40 -0800156
157 if (output_file) {
158 out_fd = open(output_file, O_WRONLY | O_CREAT | O_TRUNC, 0666);
159 if (out_fd < 0)
160 pabort("could not open output file");
161
162 ret = write(out_fd, rx, len);
163 if (ret != len)
Fabio Estevamedd38992015-12-04 10:59:14 -0200164 pabort("not all bytes written to output file");
Joshua Clayton983b278862015-11-18 14:30:40 -0800165
166 close(out_fd);
167 }
168
Frode Isaksen9006a7b2017-03-21 16:48:46 +0100169 if (verbose)
Joshua Clayton983b278862015-11-18 14:30:40 -0800170 hex_dump(rx, len, 32, "RX");
Anton Vorontsov22b238b2007-07-31 00:38:44 -0700171}
172
Ladinu Chandrasingheb7ed6982009-09-22 16:43:42 -0700173static void print_usage(const char *prog)
Anton Vorontsov22b238b2007-07-31 00:38:44 -0700174{
Frode Isaksen9006a7b2017-03-21 16:48:46 +0100175 printf("Usage: %s [-DsbdlHOLC3vpNR24SI]\n", prog);
Anton Vorontsov22b238b2007-07-31 00:38:44 -0700176 puts(" -D --device device to use (default /dev/spidev1.1)\n"
177 " -s --speed max speed (Hz)\n"
178 " -d --delay delay (usec)\n"
Joshua Claytonb2cfc902015-11-18 14:30:42 -0800179 " -b --bpw bits per word\n"
Joshua Clayton7af475a2015-11-18 14:30:39 -0800180 " -i --input input data from a file (e.g. \"test.bin\")\n"
Joshua Clayton983b278862015-11-18 14:30:40 -0800181 " -o --output output data to a file (e.g. \"results.bin\")\n"
Anton Vorontsov22b238b2007-07-31 00:38:44 -0700182 " -l --loop loopback\n"
183 " -H --cpha clock phase\n"
184 " -O --cpol clock polarity\n"
185 " -L --lsb least significant bit first\n"
186 " -C --cs-high chip select active high\n"
Geert Uytterhoeven925d16a2014-02-20 16:01:43 +0100187 " -3 --3wire SI/SO signals shared\n"
Adrian Remonda31a5c5a2015-03-10 16:12:31 -0400188 " -v --verbose Verbose (show tx buffer)\n"
Adrian Remonda30061912015-03-10 16:12:32 -0400189 " -p Send data (e.g. \"1234\\xde\\xad\")\n"
Geert Uytterhoeven925d16a2014-02-20 16:01:43 +0100190 " -N --no-cs no chip select\n"
Geert Uytterhoevenc2e78c32014-02-25 11:40:18 +0100191 " -R --ready slave pulls low to pause\n"
192 " -2 --dual dual transfer\n"
Frode Isaksen9006a7b2017-03-21 16:48:46 +0100193 " -4 --quad quad transfer\n"
Geert Uytterhoeven896fa732020-04-16 12:18:35 +0200194 " -8 --octal octal transfer\n"
Frode Isaksen9006a7b2017-03-21 16:48:46 +0100195 " -S --size transfer size\n"
196 " -I --iter iterations\n");
Anton Vorontsov22b238b2007-07-31 00:38:44 -0700197 exit(1);
198}
199
Ladinu Chandrasingheb7ed6982009-09-22 16:43:42 -0700200static void parse_opts(int argc, char *argv[])
Anton Vorontsov22b238b2007-07-31 00:38:44 -0700201{
202 while (1) {
WANG Congcd583102007-10-16 01:27:47 -0700203 static const struct option lopts[] = {
Anton Vorontsov22b238b2007-07-31 00:38:44 -0700204 { "device", 1, 0, 'D' },
205 { "speed", 1, 0, 's' },
206 { "delay", 1, 0, 'd' },
207 { "bpw", 1, 0, 'b' },
Joshua Clayton7af475a2015-11-18 14:30:39 -0800208 { "input", 1, 0, 'i' },
Joshua Clayton983b278862015-11-18 14:30:40 -0800209 { "output", 1, 0, 'o' },
Anton Vorontsov22b238b2007-07-31 00:38:44 -0700210 { "loop", 0, 0, 'l' },
211 { "cpha", 0, 0, 'H' },
212 { "cpol", 0, 0, 'O' },
213 { "lsb", 0, 0, 'L' },
214 { "cs-high", 0, 0, 'C' },
215 { "3wire", 0, 0, '3' },
David Brownellb55f6272009-06-30 11:41:26 -0700216 { "no-cs", 0, 0, 'N' },
217 { "ready", 0, 0, 'R' },
Geert Uytterhoevenc2e78c32014-02-25 11:40:18 +0100218 { "dual", 0, 0, '2' },
Adrian Remonda31a5c5a2015-03-10 16:12:31 -0400219 { "verbose", 0, 0, 'v' },
Geert Uytterhoevenc2e78c32014-02-25 11:40:18 +0100220 { "quad", 0, 0, '4' },
Geert Uytterhoeven896fa732020-04-16 12:18:35 +0200221 { "octal", 0, 0, '8' },
Frode Isaksen9006a7b2017-03-21 16:48:46 +0100222 { "size", 1, 0, 'S' },
223 { "iter", 1, 0, 'I' },
Anton Vorontsov22b238b2007-07-31 00:38:44 -0700224 { NULL, 0, 0, 0 },
225 };
226 int c;
227
Geert Uytterhoeven896fa732020-04-16 12:18:35 +0200228 c = getopt_long(argc, argv, "D:s:d:b:i:o:lHOLC3NR248p:vS:I:",
Joshua Clayton7af475a2015-11-18 14:30:39 -0800229 lopts, NULL);
Anton Vorontsov22b238b2007-07-31 00:38:44 -0700230
231 if (c == -1)
232 break;
233
234 switch (c) {
235 case 'D':
236 device = optarg;
237 break;
238 case 's':
239 speed = atoi(optarg);
240 break;
241 case 'd':
242 delay = atoi(optarg);
243 break;
244 case 'b':
245 bits = atoi(optarg);
246 break;
Joshua Clayton7af475a2015-11-18 14:30:39 -0800247 case 'i':
248 input_file = optarg;
249 break;
Joshua Clayton983b278862015-11-18 14:30:40 -0800250 case 'o':
251 output_file = optarg;
252 break;
Anton Vorontsov22b238b2007-07-31 00:38:44 -0700253 case 'l':
254 mode |= SPI_LOOP;
255 break;
256 case 'H':
257 mode |= SPI_CPHA;
258 break;
259 case 'O':
260 mode |= SPI_CPOL;
261 break;
262 case 'L':
263 mode |= SPI_LSB_FIRST;
264 break;
265 case 'C':
266 mode |= SPI_CS_HIGH;
267 break;
268 case '3':
269 mode |= SPI_3WIRE;
270 break;
David Brownellb55f6272009-06-30 11:41:26 -0700271 case 'N':
272 mode |= SPI_NO_CS;
273 break;
Adrian Remonda31a5c5a2015-03-10 16:12:31 -0400274 case 'v':
275 verbose = 1;
276 break;
David Brownellb55f6272009-06-30 11:41:26 -0700277 case 'R':
278 mode |= SPI_READY;
279 break;
Adrian Remonda30061912015-03-10 16:12:32 -0400280 case 'p':
281 input_tx = optarg;
282 break;
Geert Uytterhoevenc2e78c32014-02-25 11:40:18 +0100283 case '2':
284 mode |= SPI_TX_DUAL;
285 break;
286 case '4':
287 mode |= SPI_TX_QUAD;
288 break;
Geert Uytterhoeven896fa732020-04-16 12:18:35 +0200289 case '8':
290 mode |= SPI_TX_OCTAL;
291 break;
Frode Isaksen9006a7b2017-03-21 16:48:46 +0100292 case 'S':
293 transfer_size = atoi(optarg);
294 break;
295 case 'I':
296 iterations = atoi(optarg);
297 break;
Anton Vorontsov22b238b2007-07-31 00:38:44 -0700298 default:
299 print_usage(argv[0]);
Anton Vorontsov22b238b2007-07-31 00:38:44 -0700300 }
301 }
Geert Uytterhoevenc2e78c32014-02-25 11:40:18 +0100302 if (mode & SPI_LOOP) {
303 if (mode & SPI_TX_DUAL)
304 mode |= SPI_RX_DUAL;
305 if (mode & SPI_TX_QUAD)
306 mode |= SPI_RX_QUAD;
Geert Uytterhoeven896fa732020-04-16 12:18:35 +0200307 if (mode & SPI_TX_OCTAL)
308 mode |= SPI_RX_OCTAL;
Geert Uytterhoevenc2e78c32014-02-25 11:40:18 +0100309 }
Anton Vorontsov22b238b2007-07-31 00:38:44 -0700310}
311
Joshua Clayton5c437a42015-11-18 14:30:38 -0800312static void transfer_escaped_string(int fd, char *str)
313{
Geert Uytterhoeven0278b342016-09-09 09:02:51 +0200314 size_t size = strlen(str);
Joshua Clayton5c437a42015-11-18 14:30:38 -0800315 uint8_t *tx;
316 uint8_t *rx;
317
318 tx = malloc(size);
319 if (!tx)
320 pabort("can't allocate tx buffer");
321
322 rx = malloc(size);
323 if (!rx)
324 pabort("can't allocate rx buffer");
325
326 size = unescape((char *)tx, str, size);
327 transfer(fd, tx, rx, size);
328 free(rx);
329 free(tx);
330}
331
Joshua Clayton7af475a2015-11-18 14:30:39 -0800332static void transfer_file(int fd, char *filename)
333{
334 ssize_t bytes;
335 struct stat sb;
336 int tx_fd;
337 uint8_t *tx;
338 uint8_t *rx;
339
340 if (stat(filename, &sb) == -1)
341 pabort("can't stat input file");
342
343 tx_fd = open(filename, O_RDONLY);
Michal Vokáče634b762016-11-04 11:30:03 +0100344 if (tx_fd < 0)
Joshua Clayton7af475a2015-11-18 14:30:39 -0800345 pabort("can't open input file");
346
347 tx = malloc(sb.st_size);
348 if (!tx)
349 pabort("can't allocate tx buffer");
350
351 rx = malloc(sb.st_size);
352 if (!rx)
353 pabort("can't allocate rx buffer");
354
355 bytes = read(tx_fd, tx, sb.st_size);
356 if (bytes != sb.st_size)
357 pabort("failed to read input file");
358
359 transfer(fd, tx, rx, sb.st_size);
360 free(rx);
361 free(tx);
362 close(tx_fd);
363}
364
Frode Isaksen9006a7b2017-03-21 16:48:46 +0100365static uint64_t _read_count;
366static uint64_t _write_count;
367
368static void show_transfer_rate(void)
369{
370 static uint64_t prev_read_count, prev_write_count;
371 double rx_rate, tx_rate;
372
373 rx_rate = ((_read_count - prev_read_count) * 8) / (interval*1000.0);
374 tx_rate = ((_write_count - prev_write_count) * 8) / (interval*1000.0);
375
376 printf("rate: tx %.1fkbps, rx %.1fkbps\n", rx_rate, tx_rate);
377
378 prev_read_count = _read_count;
379 prev_write_count = _write_count;
380}
381
382static void transfer_buf(int fd, int len)
383{
384 uint8_t *tx;
385 uint8_t *rx;
386 int i;
387
388 tx = malloc(len);
389 if (!tx)
390 pabort("can't allocate tx buffer");
391 for (i = 0; i < len; i++)
392 tx[i] = random();
393
394 rx = malloc(len);
395 if (!rx)
396 pabort("can't allocate rx buffer");
397
398 transfer(fd, tx, rx, len);
399
400 _write_count += len;
401 _read_count += len;
402
403 if (mode & SPI_LOOP) {
404 if (memcmp(tx, rx, len)) {
405 fprintf(stderr, "transfer error !\n");
406 hex_dump(tx, len, 32, "TX");
407 hex_dump(rx, len, 32, "RX");
408 exit(1);
409 }
410 }
411
412 free(rx);
413 free(tx);
414}
415
Anton Vorontsov22b238b2007-07-31 00:38:44 -0700416int main(int argc, char *argv[])
417{
418 int ret = 0;
419 int fd;
420
421 parse_opts(argc, argv);
422
Tiezhu Yang1f3c3632020-02-13 12:16:06 +0800423 if (input_tx && input_file)
424 pabort("only one of -p and --input may be selected");
425
Anton Vorontsov22b238b2007-07-31 00:38:44 -0700426 fd = open(device, O_RDWR);
427 if (fd < 0)
428 pabort("can't open device");
429
430 /*
431 * spi mode
432 */
Geert Uytterhoevenc2e78c32014-02-25 11:40:18 +0100433 ret = ioctl(fd, SPI_IOC_WR_MODE32, &mode);
Anton Vorontsov22b238b2007-07-31 00:38:44 -0700434 if (ret == -1)
435 pabort("can't set spi mode");
436
Geert Uytterhoevenc2e78c32014-02-25 11:40:18 +0100437 ret = ioctl(fd, SPI_IOC_RD_MODE32, &mode);
Anton Vorontsov22b238b2007-07-31 00:38:44 -0700438 if (ret == -1)
439 pabort("can't get spi mode");
440
441 /*
442 * bits per word
443 */
444 ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
445 if (ret == -1)
446 pabort("can't set bits per word");
447
448 ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
449 if (ret == -1)
450 pabort("can't get bits per word");
451
452 /*
453 * max speed hz
454 */
455 ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
456 if (ret == -1)
457 pabort("can't set max speed hz");
458
459 ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
460 if (ret == -1)
461 pabort("can't get max speed hz");
462
Geert Uytterhoevenc2e78c32014-02-25 11:40:18 +0100463 printf("spi mode: 0x%x\n", mode);
Geert Uytterhoeven9ec8ade2020-06-08 12:00:49 +0200464 printf("bits per word: %u\n", bits);
465 printf("max speed: %u Hz (%u kHz)\n", speed, speed/1000);
Anton Vorontsov22b238b2007-07-31 00:38:44 -0700466
Joshua Clayton5c437a42015-11-18 14:30:38 -0800467 if (input_tx)
468 transfer_escaped_string(fd, input_tx);
Joshua Clayton7af475a2015-11-18 14:30:39 -0800469 else if (input_file)
470 transfer_file(fd, input_file);
Frode Isaksen9006a7b2017-03-21 16:48:46 +0100471 else if (transfer_size) {
472 struct timespec last_stat;
473
474 clock_gettime(CLOCK_MONOTONIC, &last_stat);
475
476 while (iterations-- > 0) {
477 struct timespec current;
478
479 transfer_buf(fd, transfer_size);
480
481 clock_gettime(CLOCK_MONOTONIC, &current);
482 if (current.tv_sec - last_stat.tv_sec > interval) {
483 show_transfer_rate();
484 last_stat = current;
485 }
486 }
487 printf("total: tx %.1fKB, rx %.1fKB\n",
488 _write_count/1024.0, _read_count/1024.0);
489 } else
Adrian Remonda30061912015-03-10 16:12:32 -0400490 transfer(fd, default_tx, default_rx, sizeof(default_tx));
Anton Vorontsov22b238b2007-07-31 00:38:44 -0700491
492 close(fd);
493
494 return ret;
495}