]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blame - tools/spi/spidev_test.c
UBUNTU: Ubuntu-5.15.0-39.42
[mirror_ubuntu-jammy-kernel.git] / tools / spi / spidev_test.c
CommitLineData
84a14ae8 1// SPDX-License-Identifier: GPL-2.0-only
22b238bd
AV
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 *
22b238bd
AV
8 * 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>
b78ce7ed 15#include <string.h>
470a072e 16#include <errno.h>
22b238bd
AV
17#include <getopt.h>
18#include <fcntl.h>
9006a7b3 19#include <time.h>
22b238bd 20#include <sys/ioctl.h>
8736f802 21#include <linux/ioctl.h>
7af475a5 22#include <sys/stat.h>
22b238bd
AV
23#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{
470a072e
TY
30 if (errno != 0)
31 perror(s);
32 else
33 printf("%s\n", s);
34
22b238bd
AV
35 abort();
36}
37
cd58310d 38static const char *device = "/dev/spidev1.1";
c2e78c34 39static uint32_t mode;
22b238bd 40static uint8_t bits = 8;
7af475a5 41static char *input_file;
983b2788 42static char *output_file;
22b238bd
AV
43static uint32_t speed = 500000;
44static uint16_t delay;
31a5c5a7 45static int verbose;
9006a7b3
FI
46static int transfer_size;
47static int iterations;
48static int interval = 5; /* interval in seconds for showing transfer rate */
22b238bd 49
bd207791 50static uint8_t default_tx[] = {
30061915
AR
51 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
bd207791
QZ
59static uint8_t default_rx[ARRAY_SIZE(default_tx)] = {0, };
60static char *input_tx;
30061915 61
b2cfc904
JC
62static void hex_dump(const void *src, size_t length, size_t line_size,
63 char *prefix)
b78ce7ed
AR
64{
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 }
35386dfd 78 printf(" |");
b78ce7ed
AR
79 while (line < address) {
80 c = *line++;
35386dfd 81 printf("%c", (c < 32 || c > 126) ? '.' : c);
b78ce7ed 82 }
35386dfd 83 printf("|\n");
b78ce7ed
AR
84 if (length > 0)
85 printf("%s | ", prefix);
86 }
87 }
88}
89
30061915
AR
90/*
91 * Unescape - process hexadecimal escape character
92 * converts shell input "\x23" -> 0x23
93 */
07eec628 94static int unescape(char *_dst, char *_src, size_t len)
30061915
AR
95{
96 int ret = 0;
a20874f7 97 int match;
30061915
AR
98 char *src = _src;
99 char *dst = _dst;
100 unsigned int ch;
101
102 while (*src) {
103 if (*src == '\\' && *(src+1) == 'x') {
a20874f7
JC
104 match = sscanf(src + 2, "%2x", &ch);
105 if (!match)
106 pabort("malformed input string");
107
30061915
AR
108 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)
22b238bd
AV
119{
120 int ret;
983b2788 121 int out_fd;
22b238bd
AV
122 struct spi_ioc_transfer tr = {
123 .tx_buf = (unsigned long)tx,
124 .rx_buf = (unsigned long)rx,
30061915 125 .len = len,
22b238bd
AV
126 .delay_usecs = delay,
127 .speed_hz = speed,
128 .bits_per_word = bits,
129 };
130
896fa735
GU
131 if (mode & SPI_TX_OCTAL)
132 tr.tx_nbits = 8;
133 else if (mode & SPI_TX_QUAD)
c2e78c34
GU
134 tr.tx_nbits = 4;
135 else if (mode & SPI_TX_DUAL)
136 tr.tx_nbits = 2;
896fa735
GU
137 if (mode & SPI_RX_OCTAL)
138 tr.rx_nbits = 8;
139 else if (mode & SPI_RX_QUAD)
c2e78c34
GU
140 tr.rx_nbits = 4;
141 else if (mode & SPI_RX_DUAL)
142 tr.rx_nbits = 2;
143 if (!(mode & SPI_LOOP)) {
896fa735 144 if (mode & (SPI_TX_OCTAL | SPI_TX_QUAD | SPI_TX_DUAL))
c2e78c34 145 tr.rx_buf = 0;
896fa735 146 else if (mode & (SPI_RX_OCTAL | SPI_RX_QUAD | SPI_RX_DUAL))
c2e78c34
GU
147 tr.tx_buf = 0;
148 }
149
22b238bd 150 ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
95b1ed2a 151 if (ret < 1)
22b238bd
AV
152 pabort("can't send spi message");
153
31a5c5a7 154 if (verbose)
30061915 155 hex_dump(tx, len, 32, "TX");
983b2788
JC
156
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)
edd3899c 164 pabort("not all bytes written to output file");
983b2788
JC
165
166 close(out_fd);
167 }
168
9006a7b3 169 if (verbose)
983b2788 170 hex_dump(rx, len, 32, "RX");
22b238bd
AV
171}
172
b7ed698c 173static void print_usage(const char *prog)
22b238bd 174{
9006a7b3 175 printf("Usage: %s [-DsbdlHOLC3vpNR24SI]\n", prog);
22b238bd
AV
176 puts(" -D --device device to use (default /dev/spidev1.1)\n"
177 " -s --speed max speed (Hz)\n"
178 " -d --delay delay (usec)\n"
b2cfc904 179 " -b --bpw bits per word\n"
7af475a5 180 " -i --input input data from a file (e.g. \"test.bin\")\n"
983b2788 181 " -o --output output data to a file (e.g. \"results.bin\")\n"
22b238bd
AV
182 " -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"
925d16a2 187 " -3 --3wire SI/SO signals shared\n"
31a5c5a7 188 " -v --verbose Verbose (show tx buffer)\n"
30061915 189 " -p Send data (e.g. \"1234\\xde\\xad\")\n"
925d16a2 190 " -N --no-cs no chip select\n"
c2e78c34
GU
191 " -R --ready slave pulls low to pause\n"
192 " -2 --dual dual transfer\n"
9006a7b3 193 " -4 --quad quad transfer\n"
896fa735 194 " -8 --octal octal transfer\n"
9006a7b3
FI
195 " -S --size transfer size\n"
196 " -I --iter iterations\n");
22b238bd
AV
197 exit(1);
198}
199
b7ed698c 200static void parse_opts(int argc, char *argv[])
22b238bd
AV
201{
202 while (1) {
cd58310d 203 static const struct option lopts[] = {
22b238bd
AV
204 { "device", 1, 0, 'D' },
205 { "speed", 1, 0, 's' },
206 { "delay", 1, 0, 'd' },
207 { "bpw", 1, 0, 'b' },
7af475a5 208 { "input", 1, 0, 'i' },
983b2788 209 { "output", 1, 0, 'o' },
22b238bd
AV
210 { "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' },
b55f627f
DB
216 { "no-cs", 0, 0, 'N' },
217 { "ready", 0, 0, 'R' },
c2e78c34 218 { "dual", 0, 0, '2' },
31a5c5a7 219 { "verbose", 0, 0, 'v' },
c2e78c34 220 { "quad", 0, 0, '4' },
896fa735 221 { "octal", 0, 0, '8' },
9006a7b3
FI
222 { "size", 1, 0, 'S' },
223 { "iter", 1, 0, 'I' },
22b238bd
AV
224 { NULL, 0, 0, 0 },
225 };
226 int c;
227
896fa735 228 c = getopt_long(argc, argv, "D:s:d:b:i:o:lHOLC3NR248p:vS:I:",
7af475a5 229 lopts, NULL);
22b238bd
AV
230
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;
7af475a5
JC
247 case 'i':
248 input_file = optarg;
249 break;
983b2788
JC
250 case 'o':
251 output_file = optarg;
252 break;
22b238bd
AV
253 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;
b55f627f
DB
271 case 'N':
272 mode |= SPI_NO_CS;
273 break;
31a5c5a7
AR
274 case 'v':
275 verbose = 1;
276 break;
b55f627f
DB
277 case 'R':
278 mode |= SPI_READY;
279 break;
30061915
AR
280 case 'p':
281 input_tx = optarg;
282 break;
c2e78c34
GU
283 case '2':
284 mode |= SPI_TX_DUAL;
285 break;
286 case '4':
287 mode |= SPI_TX_QUAD;
288 break;
896fa735
GU
289 case '8':
290 mode |= SPI_TX_OCTAL;
291 break;
9006a7b3
FI
292 case 'S':
293 transfer_size = atoi(optarg);
294 break;
295 case 'I':
296 iterations = atoi(optarg);
297 break;
22b238bd
AV
298 default:
299 print_usage(argv[0]);
22b238bd
AV
300 }
301 }
c2e78c34
GU
302 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;
896fa735
GU
307 if (mode & SPI_TX_OCTAL)
308 mode |= SPI_RX_OCTAL;
c2e78c34 309 }
22b238bd
AV
310}
311
5c437a40
JC
312static void transfer_escaped_string(int fd, char *str)
313{
0278b34b 314 size_t size = strlen(str);
5c437a40
JC
315 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
7af475a5
JC
332static 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);
e634b76c 344 if (tx_fd < 0)
7af475a5
JC
345 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
9006a7b3
FI
365static 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
22b238bd
AV
416int main(int argc, char *argv[])
417{
418 int ret = 0;
419 int fd;
420
421 parse_opts(argc, argv);
422
1f3c3632
TY
423 if (input_tx && input_file)
424 pabort("only one of -p and --input may be selected");
425
22b238bd
AV
426 fd = open(device, O_RDWR);
427 if (fd < 0)
428 pabort("can't open device");
429
430 /*
431 * spi mode
432 */
c2e78c34 433 ret = ioctl(fd, SPI_IOC_WR_MODE32, &mode);
22b238bd
AV
434 if (ret == -1)
435 pabort("can't set spi mode");
436
c2e78c34 437 ret = ioctl(fd, SPI_IOC_RD_MODE32, &mode);
22b238bd
AV
438 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
c2e78c34 463 printf("spi mode: 0x%x\n", mode);
9ec8ade8
GU
464 printf("bits per word: %u\n", bits);
465 printf("max speed: %u Hz (%u kHz)\n", speed, speed/1000);
22b238bd 466
5c437a40
JC
467 if (input_tx)
468 transfer_escaped_string(fd, input_tx);
7af475a5
JC
469 else if (input_file)
470 transfer_file(fd, input_file);
9006a7b3
FI
471 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
30061915 490 transfer(fd, default_tx, default_rx, sizeof(default_tx));
22b238bd
AV
491
492 close(fd);
493
494 return ret;
495}