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, ¤t);
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