0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #include <stdint.h>
0012 #include <unistd.h>
0013 #include <stdio.h>
0014 #include <stdlib.h>
0015 #include <string.h>
0016 #include <errno.h>
0017 #include <getopt.h>
0018 #include <fcntl.h>
0019 #include <time.h>
0020 #include <sys/ioctl.h>
0021 #include <linux/ioctl.h>
0022 #include <sys/stat.h>
0023 #include <linux/types.h>
0024 #include <linux/spi/spidev.h>
0025
0026 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
0027
0028 static void pabort(const char *s)
0029 {
0030 if (errno != 0)
0031 perror(s);
0032 else
0033 printf("%s\n", s);
0034
0035 abort();
0036 }
0037
0038 static const char *device = "/dev/spidev1.1";
0039 static uint32_t mode;
0040 static uint8_t bits = 8;
0041 static char *input_file;
0042 static char *output_file;
0043 static uint32_t speed = 500000;
0044 static uint16_t delay;
0045 static int verbose;
0046 static int transfer_size;
0047 static int iterations;
0048 static int interval = 5;
0049
0050 static uint8_t default_tx[] = {
0051 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0052 0x40, 0x00, 0x00, 0x00, 0x00, 0x95,
0053 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0054 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0055 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0056 0xF0, 0x0D,
0057 };
0058
0059 static uint8_t default_rx[ARRAY_SIZE(default_tx)] = {0, };
0060 static char *input_tx;
0061
0062 static void hex_dump(const void *src, size_t length, size_t line_size,
0063 char *prefix)
0064 {
0065 int i = 0;
0066 const unsigned char *address = src;
0067 const unsigned char *line = address;
0068 unsigned char c;
0069
0070 printf("%s | ", prefix);
0071 while (length-- > 0) {
0072 printf("%02X ", *address++);
0073 if (!(++i % line_size) || (length == 0 && i % line_size)) {
0074 if (length == 0) {
0075 while (i++ % line_size)
0076 printf("__ ");
0077 }
0078 printf(" |");
0079 while (line < address) {
0080 c = *line++;
0081 printf("%c", (c < 32 || c > 126) ? '.' : c);
0082 }
0083 printf("|\n");
0084 if (length > 0)
0085 printf("%s | ", prefix);
0086 }
0087 }
0088 }
0089
0090
0091
0092
0093
0094 static int unescape(char *_dst, char *_src, size_t len)
0095 {
0096 int ret = 0;
0097 int match;
0098 char *src = _src;
0099 char *dst = _dst;
0100 unsigned int ch;
0101
0102 while (*src) {
0103 if (*src == '\\' && *(src+1) == 'x') {
0104 match = sscanf(src + 2, "%2x", &ch);
0105 if (!match)
0106 pabort("malformed input string");
0107
0108 src += 4;
0109 *dst++ = (unsigned char)ch;
0110 } else {
0111 *dst++ = *src++;
0112 }
0113 ret++;
0114 }
0115 return ret;
0116 }
0117
0118 static void transfer(int fd, uint8_t const *tx, uint8_t const *rx, size_t len)
0119 {
0120 int ret;
0121 int out_fd;
0122 struct spi_ioc_transfer tr = {
0123 .tx_buf = (unsigned long)tx,
0124 .rx_buf = (unsigned long)rx,
0125 .len = len,
0126 .delay_usecs = delay,
0127 .speed_hz = speed,
0128 .bits_per_word = bits,
0129 };
0130
0131 if (mode & SPI_TX_OCTAL)
0132 tr.tx_nbits = 8;
0133 else if (mode & SPI_TX_QUAD)
0134 tr.tx_nbits = 4;
0135 else if (mode & SPI_TX_DUAL)
0136 tr.tx_nbits = 2;
0137 if (mode & SPI_RX_OCTAL)
0138 tr.rx_nbits = 8;
0139 else if (mode & SPI_RX_QUAD)
0140 tr.rx_nbits = 4;
0141 else if (mode & SPI_RX_DUAL)
0142 tr.rx_nbits = 2;
0143 if (!(mode & SPI_LOOP)) {
0144 if (mode & (SPI_TX_OCTAL | SPI_TX_QUAD | SPI_TX_DUAL))
0145 tr.rx_buf = 0;
0146 else if (mode & (SPI_RX_OCTAL | SPI_RX_QUAD | SPI_RX_DUAL))
0147 tr.tx_buf = 0;
0148 }
0149
0150 ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
0151 if (ret < 1)
0152 pabort("can't send spi message");
0153
0154 if (verbose)
0155 hex_dump(tx, len, 32, "TX");
0156
0157 if (output_file) {
0158 out_fd = open(output_file, O_WRONLY | O_CREAT | O_TRUNC, 0666);
0159 if (out_fd < 0)
0160 pabort("could not open output file");
0161
0162 ret = write(out_fd, rx, len);
0163 if (ret != len)
0164 pabort("not all bytes written to output file");
0165
0166 close(out_fd);
0167 }
0168
0169 if (verbose)
0170 hex_dump(rx, len, 32, "RX");
0171 }
0172
0173 static void print_usage(const char *prog)
0174 {
0175 printf("Usage: %s [-DsbdlHOLC3vpNR24SI]\n", prog);
0176 puts(" -D --device device to use (default /dev/spidev1.1)\n"
0177 " -s --speed max speed (Hz)\n"
0178 " -d --delay delay (usec)\n"
0179 " -b --bpw bits per word\n"
0180 " -i --input input data from a file (e.g. \"test.bin\")\n"
0181 " -o --output output data to a file (e.g. \"results.bin\")\n"
0182 " -l --loop loopback\n"
0183 " -H --cpha clock phase\n"
0184 " -O --cpol clock polarity\n"
0185 " -L --lsb least significant bit first\n"
0186 " -C --cs-high chip select active high\n"
0187 " -3 --3wire SI/SO signals shared\n"
0188 " -v --verbose Verbose (show tx buffer)\n"
0189 " -p Send data (e.g. \"1234\\xde\\xad\")\n"
0190 " -N --no-cs no chip select\n"
0191 " -R --ready slave pulls low to pause\n"
0192 " -2 --dual dual transfer\n"
0193 " -4 --quad quad transfer\n"
0194 " -8 --octal octal transfer\n"
0195 " -S --size transfer size\n"
0196 " -I --iter iterations\n");
0197 exit(1);
0198 }
0199
0200 static void parse_opts(int argc, char *argv[])
0201 {
0202 while (1) {
0203 static const struct option lopts[] = {
0204 { "device", 1, 0, 'D' },
0205 { "speed", 1, 0, 's' },
0206 { "delay", 1, 0, 'd' },
0207 { "bpw", 1, 0, 'b' },
0208 { "input", 1, 0, 'i' },
0209 { "output", 1, 0, 'o' },
0210 { "loop", 0, 0, 'l' },
0211 { "cpha", 0, 0, 'H' },
0212 { "cpol", 0, 0, 'O' },
0213 { "lsb", 0, 0, 'L' },
0214 { "cs-high", 0, 0, 'C' },
0215 { "3wire", 0, 0, '3' },
0216 { "no-cs", 0, 0, 'N' },
0217 { "ready", 0, 0, 'R' },
0218 { "dual", 0, 0, '2' },
0219 { "verbose", 0, 0, 'v' },
0220 { "quad", 0, 0, '4' },
0221 { "octal", 0, 0, '8' },
0222 { "size", 1, 0, 'S' },
0223 { "iter", 1, 0, 'I' },
0224 { NULL, 0, 0, 0 },
0225 };
0226 int c;
0227
0228 c = getopt_long(argc, argv, "D:s:d:b:i:o:lHOLC3NR248p:vS:I:",
0229 lopts, NULL);
0230
0231 if (c == -1)
0232 break;
0233
0234 switch (c) {
0235 case 'D':
0236 device = optarg;
0237 break;
0238 case 's':
0239 speed = atoi(optarg);
0240 break;
0241 case 'd':
0242 delay = atoi(optarg);
0243 break;
0244 case 'b':
0245 bits = atoi(optarg);
0246 break;
0247 case 'i':
0248 input_file = optarg;
0249 break;
0250 case 'o':
0251 output_file = optarg;
0252 break;
0253 case 'l':
0254 mode |= SPI_LOOP;
0255 break;
0256 case 'H':
0257 mode |= SPI_CPHA;
0258 break;
0259 case 'O':
0260 mode |= SPI_CPOL;
0261 break;
0262 case 'L':
0263 mode |= SPI_LSB_FIRST;
0264 break;
0265 case 'C':
0266 mode |= SPI_CS_HIGH;
0267 break;
0268 case '3':
0269 mode |= SPI_3WIRE;
0270 break;
0271 case 'N':
0272 mode |= SPI_NO_CS;
0273 break;
0274 case 'v':
0275 verbose = 1;
0276 break;
0277 case 'R':
0278 mode |= SPI_READY;
0279 break;
0280 case 'p':
0281 input_tx = optarg;
0282 break;
0283 case '2':
0284 mode |= SPI_TX_DUAL;
0285 break;
0286 case '4':
0287 mode |= SPI_TX_QUAD;
0288 break;
0289 case '8':
0290 mode |= SPI_TX_OCTAL;
0291 break;
0292 case 'S':
0293 transfer_size = atoi(optarg);
0294 break;
0295 case 'I':
0296 iterations = atoi(optarg);
0297 break;
0298 default:
0299 print_usage(argv[0]);
0300 }
0301 }
0302 if (mode & SPI_LOOP) {
0303 if (mode & SPI_TX_DUAL)
0304 mode |= SPI_RX_DUAL;
0305 if (mode & SPI_TX_QUAD)
0306 mode |= SPI_RX_QUAD;
0307 if (mode & SPI_TX_OCTAL)
0308 mode |= SPI_RX_OCTAL;
0309 }
0310 }
0311
0312 static void transfer_escaped_string(int fd, char *str)
0313 {
0314 size_t size = strlen(str);
0315 uint8_t *tx;
0316 uint8_t *rx;
0317
0318 tx = malloc(size);
0319 if (!tx)
0320 pabort("can't allocate tx buffer");
0321
0322 rx = malloc(size);
0323 if (!rx)
0324 pabort("can't allocate rx buffer");
0325
0326 size = unescape((char *)tx, str, size);
0327 transfer(fd, tx, rx, size);
0328 free(rx);
0329 free(tx);
0330 }
0331
0332 static void transfer_file(int fd, char *filename)
0333 {
0334 ssize_t bytes;
0335 struct stat sb;
0336 int tx_fd;
0337 uint8_t *tx;
0338 uint8_t *rx;
0339
0340 if (stat(filename, &sb) == -1)
0341 pabort("can't stat input file");
0342
0343 tx_fd = open(filename, O_RDONLY);
0344 if (tx_fd < 0)
0345 pabort("can't open input file");
0346
0347 tx = malloc(sb.st_size);
0348 if (!tx)
0349 pabort("can't allocate tx buffer");
0350
0351 rx = malloc(sb.st_size);
0352 if (!rx)
0353 pabort("can't allocate rx buffer");
0354
0355 bytes = read(tx_fd, tx, sb.st_size);
0356 if (bytes != sb.st_size)
0357 pabort("failed to read input file");
0358
0359 transfer(fd, tx, rx, sb.st_size);
0360 free(rx);
0361 free(tx);
0362 close(tx_fd);
0363 }
0364
0365 static uint64_t _read_count;
0366 static uint64_t _write_count;
0367
0368 static void show_transfer_rate(void)
0369 {
0370 static uint64_t prev_read_count, prev_write_count;
0371 double rx_rate, tx_rate;
0372
0373 rx_rate = ((_read_count - prev_read_count) * 8) / (interval*1000.0);
0374 tx_rate = ((_write_count - prev_write_count) * 8) / (interval*1000.0);
0375
0376 printf("rate: tx %.1fkbps, rx %.1fkbps\n", rx_rate, tx_rate);
0377
0378 prev_read_count = _read_count;
0379 prev_write_count = _write_count;
0380 }
0381
0382 static void transfer_buf(int fd, int len)
0383 {
0384 uint8_t *tx;
0385 uint8_t *rx;
0386 int i;
0387
0388 tx = malloc(len);
0389 if (!tx)
0390 pabort("can't allocate tx buffer");
0391 for (i = 0; i < len; i++)
0392 tx[i] = random();
0393
0394 rx = malloc(len);
0395 if (!rx)
0396 pabort("can't allocate rx buffer");
0397
0398 transfer(fd, tx, rx, len);
0399
0400 _write_count += len;
0401 _read_count += len;
0402
0403 if (mode & SPI_LOOP) {
0404 if (memcmp(tx, rx, len)) {
0405 fprintf(stderr, "transfer error !\n");
0406 hex_dump(tx, len, 32, "TX");
0407 hex_dump(rx, len, 32, "RX");
0408 exit(1);
0409 }
0410 }
0411
0412 free(rx);
0413 free(tx);
0414 }
0415
0416 int main(int argc, char *argv[])
0417 {
0418 int ret = 0;
0419 int fd;
0420 uint32_t request;
0421
0422 parse_opts(argc, argv);
0423
0424 if (input_tx && input_file)
0425 pabort("only one of -p and --input may be selected");
0426
0427 fd = open(device, O_RDWR);
0428 if (fd < 0)
0429 pabort("can't open device");
0430
0431
0432
0433
0434
0435 request = mode;
0436 ret = ioctl(fd, SPI_IOC_WR_MODE32, &mode);
0437 if (ret == -1)
0438 pabort("can't set spi mode");
0439
0440
0441 ret = ioctl(fd, SPI_IOC_RD_MODE32, &mode);
0442 if (ret == -1)
0443 pabort("can't get spi mode");
0444
0445
0446
0447
0448 if (request != mode)
0449 printf("WARNING device does not support requested mode 0x%x\n",
0450 request);
0451
0452
0453
0454
0455 ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
0456 if (ret == -1)
0457 pabort("can't set bits per word");
0458
0459 ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
0460 if (ret == -1)
0461 pabort("can't get bits per word");
0462
0463
0464
0465
0466 ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
0467 if (ret == -1)
0468 pabort("can't set max speed hz");
0469
0470 ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
0471 if (ret == -1)
0472 pabort("can't get max speed hz");
0473
0474 printf("spi mode: 0x%x\n", mode);
0475 printf("bits per word: %u\n", bits);
0476 printf("max speed: %u Hz (%u kHz)\n", speed, speed/1000);
0477
0478 if (input_tx)
0479 transfer_escaped_string(fd, input_tx);
0480 else if (input_file)
0481 transfer_file(fd, input_file);
0482 else if (transfer_size) {
0483 struct timespec last_stat;
0484
0485 clock_gettime(CLOCK_MONOTONIC, &last_stat);
0486
0487 while (iterations-- > 0) {
0488 struct timespec current;
0489
0490 transfer_buf(fd, transfer_size);
0491
0492 clock_gettime(CLOCK_MONOTONIC, ¤t);
0493 if (current.tv_sec - last_stat.tv_sec > interval) {
0494 show_transfer_rate();
0495 last_stat = current;
0496 }
0497 }
0498 printf("total: tx %.1fKB, rx %.1fKB\n",
0499 _write_count/1024.0, _read_count/1024.0);
0500 } else
0501 transfer(fd, default_tx, default_rx, sizeof(default_tx));
0502
0503 close(fd);
0504
0505 return ret;
0506 }