/* * SPI testing utility (using spidev driver) * * Copyright (c) 2007 MontaVista Software, Inc. * Copyright (c) 2007 Anton Vorontsov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License. * * Cross-compile with cross-gcc -I/path/to/cross-kernel/include */ #include #include #include #include #include #include #include #include #include #include #include #include #include /* gettimeofday, timeval (for timestamp in microsecond) */ #include /* ftime, timeb (for timestamp in millisecond) */ #include /* time_t, time (for timestamp in second) */ #include #include #define SPI_LOOP_INTERVAL_US 5000 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) #define PACKET_LENGTH 8 #define REQ_MAG_X "\x00\x04\x00\x00\x00\x00\xc0\xf1" #define REQ_MAG_Y "\x00\x08\x00\x00\x00\x00\xc1\xe1" #define REQ_MAG_Z "\x00\x0c\x00\x00\x00\x00\x01\x10" #define REQ_PING "\x00\x00\x0a\x4d\x52\x58\x55\xaf" #define CRC16 0x8005 #define CRC_LENGTH 2 enum { SPI_RX, SPI_TX, SPI_NUM }; static const char* device = "/dev/spidev1.1"; static uint32_t mode; static uint8_t bits = 8; static char* input_file; static char* output_file; static uint32_t speed = 500000; static uint16_t delay; static int verbose; char* input_tx; int test_case; static void pabort(const char* s); static int unescape(char* _dst, char* _src, size_t len); static void print_usage(const char* prog); static void set_spi_mode(); static void transfer_file(int fd, char* filename); static void transfer_escaped_string(char* str); static void parse_opts(int argc, char* argv[]); static void transferN(struct spi_ioc_transfer *buf, size_t len); static void TransferWithOpenedFile(int fd, uint8_t const* tx, uint8_t const* rx, size_t len); static long long int GetMicroSeconds(); static void TransferAdisTestLong(); static void TransferNAdisTestLong(); static void TransferNAdisTestLong2(); static void TransferAdisTestShort(); static void SetPthreadPriority(int priority); void *main_thread() { SetPthreadPriority(99); set_spi_mode(mode); if (input_tx) { transfer_escaped_string(input_tx); } else if (input_file) { // int fd; // transfer_file(fd, input_file); } else if (test_case == 1) { } else if (test_case == 20) { TransferAdisTestLong(); } else if (test_case == 21) { TransferAdisTestShort(); } else if (test_case == 22) { TransferNAdisTestLong(); } else if (test_case == 23) { TransferNAdisTestLong2(); } else { printf("Something is wrong!!\n"); } } int main(int argc, char* argv[]) { pthread_t mthread; parse_opts(argc, argv); pthread_create(&mthread, NULL, main_thread, NULL); pthread_join(mthread, NULL); return 0; } static void hex_dump(const void* src, size_t length, size_t line_size, char* prefix) { const unsigned char* address = src; long long int timestamp_usec; /* timestamp in microsecond */ timestamp_usec = GetMicroSeconds(); printf("%.6lf ", timestamp_usec / 1000000.0); printf("%s | ", prefix); while (length-- > 0) { printf("%02X ", *address++); } printf("\n"); } static void transfer(uint8_t const* tx, uint8_t const* rx, size_t len) { int fd; fd = open(device, O_RDWR); if (fd < 0) pabort("can't open device"); TransferWithOpenedFile(fd, tx, rx, len); close(fd); } static void transferN(struct spi_ioc_transfer *buf, size_t len) { int ret; int fd; fd = open(device, O_RDWR); if (fd < 0) pabort("can't open device"); ret = ioctl(fd, SPI_IOC_MESSAGE(len), buf); if (ret < 1) { printf("error code %d\n", ret); pabort("can't send spi message"); } close(fd); } static void TransferWithOpenedFile(int fd, uint8_t const* tx, uint8_t const* rx, size_t len) { int ret; struct spi_ioc_transfer tr = { .tx_buf = (unsigned long long)tx, .rx_buf = (unsigned long long)rx, .len = len, .delay_usecs = delay, .speed_hz = speed, .bits_per_word = bits, }; 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; } uint32_t start_time = GetMicroSeconds(); ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); uint32_t end_time = GetMicroSeconds(); // printf("%.6f: diff2=%d us\n", end_time/1000000.0, end_time-start_time); if (ret < 1) pabort("can't send spi message"); if (verbose) hex_dump(tx, len, 32, "TX"); if (output_file) { int out_fd; 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) pabort("not all bytes written to output file"); close(out_fd); } if (verbose) hex_dump(rx, len, 32, "RX"); } static void TransferAdisTestLong() { size_t size = 86; uint8_t rx[86]; uint8_t tx[86] = { 0x7E, 0x0, 0x0E, 0x0, 0x10, 0x0, 0x12, 0x0, 0x14, 0x0, 0x16, 0x0, 0x18, 0x0, 0x1A, 0x0, 0x1C, 0x0, 0x1E, 0x0, 0x20, 0x0, 0x22, 0x0, 0x24, 0x0, 0x26, 0x0, 0x28, 0x0, 0x2A, 0x0, 0x2C, 0x0, 0x2E, 0x0, 0x30, 0x0, 0x40, 0x0, 0x42, 0x0, 0x44, 0x0, 0x46, 0x0, 0x48, 0x0, 0x4A, 0x00, 0x4C, 0x00, 0x4E, 0x00, 0x50, 0x0, 0x52, 0x0, 0x54, 0x0, 0x56, 0x0, 0x7E, 0x00, 0x8, 0x0, 0x7e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }; // transfer(tx, rx, 2); // printf("prod id 0x%02X%02X\n", rx[0], rx[1]); while (1) { bool no_error = true; uint32_t start_time = GetMicroSeconds(); transfer(tx, rx, size); uint32_t end_time = GetMicroSeconds(); no_error &= (rx[4] == 0x40); no_error &= (rx[66] == 0x40); no_error &= (rx[70] == 0x40); no_error &= (rx[5] == 0x60); no_error &= (rx[67] == 0x60); no_error &= (rx[71] == 0x60); no_error &= (rx[69] == 0); no_error &= ((rx[68] & ~0x3) == 0); if (no_error == false) { printf("s prod id 0x%02X%02X\n", rx[4], rx[5]); printf("e prod id 0x%02X%02X\n", rx[66], rx[67]); printf("sys_e_flag 0x%02X%02X\n", rx[68], rx[69]); printf("e prod id 0x%02X%02X\n", rx[70], rx[71]); printf("%.6f: diff=%d us\n", end_time/1000000.0, end_time-start_time); hex_dump(rx, 86, 32, "RX"); } uint32_t end_time2 = GetMicroSeconds(); // every 5ms usleep(5000-(end_time2-start_time)); } } // This doesn't work with Jetson NX because spi-tegra114.c doesn't support SPI_IOC_MESSAGE(N) /* [when the size is 6] root@cerberus:~# ./spidev_test_ioc_message_n -D /dev/spidev0.0 -s 1000000 -b 8 -H -O -t 22 -v priority: 99, ret: 0, policy: 1 start time: 1600662898.918802 s spi mode: 0x3 bits per word: 8 max speed: 1000000 Hz (1000 KHz) 1600662898.919029 TX | 7E 00 1600662898.919080 RX | 00 00 1600662898.919236 TX | 7E 00 1600662898.919277 RX | 40 60 prod id 0x4060 1602.144436: diff=307 us 1600662898.919623 TX | 7E 00 0E 00 10 00 1600662898.919641 RX | 40 60 40 60 09 3C [when the size is 8] root@cerberus:~# ./spidev_test_ioc_message_n -D /dev/spidev0.0 -s 1000000 -b 8 -H -O -t 22 -v priority: 99, ret: 0, policy: 1 start time: 1600662952.318174 s spi mode: 0x3 bits per word: 8 max speed: 1000000 Hz (1000 KHz) 1600662952.318415 TX | 7E 00 1600662952.318440 RX | 00 00 1600662952.318617 TX | 7E 00 1600662952.318634 RX | 40 60 prod id 0x4060 error code -1 can't send spi message: Input/output error Aborted */ static void TransferNAdisTestLong() { size_t size = 8; uint8_t rx[66]; uint8_t tx[66] = { 0x7E, 0x0, 0x0E, 0x0, 0x10, 0x0, 0x12, 0x0, 0x14, 0x0, 0x16, 0x0, 0x18, 0x0, 0x1A, 0x0, 0x1C, 0x0, 0x1E, 0x0, 0x20, 0x0, 0x22, 0x0, 0x24, 0x0, 0x26, 0x0, 0x28, 0x0, 0x2A, 0x0, 0x2C, 0x0, 0x2E, 0x0, 0x30, 0x0, 0x40, 0x0, 0x42, 0x0, 0x44, 0x0, 0x46, 0x0, 0x48, 0x0, 0x4A, 0x00, 0x4C, 0x00, 0x4E, 0x00, 0x50, 0x0, 0x52, 0x0, 0x54, 0x0, 0x56, 0x0, 0x7E, 0x00, 0, 0}; struct spi_ioc_transfer tr_buf[size/2]; int i; transfer(tx, rx, 2); transfer(tx, rx, 2); printf("prod id 0x%02X%02X\n", rx[0], rx[1]); for (i=0; i 0x23 */ static int unescape(char* _dst, char* _src, size_t len) { int ret = 0; int match; char* src = _src; char* dst = _dst; unsigned int ch; while (*src) { if (*src == '\\' && *(src + 1) == 'x') { match = sscanf(src + 2, "%2x", &ch); if (!match) pabort("malformed input string"); src += 4; *dst++ = (unsigned char)ch; } else { *dst++ = *src++; } ret++; } return ret; } static void print_usage(const char* prog) { printf("Usage: %s [-DsbdlHOLC3]\n", prog); puts(" -D --device device to use (default /dev/spidev1.1)\n" " -s --speed max speed (Hz)\n" " -t --test test messages: 1: mag once, 2: mag loop\n" " -d --delay delay (usec)\n" " -b --bpw bits per word\n" " -i --input input data from a file (e.g. \"test.bin\")\n" " -o --output output data to a file (e.g. \"results.bin\")\n" " -l --loop loopback\n" " -H --cpha clock phase\n" " -O --cpol clock polarity\n" " -L --lsb least significant bit first\n" " -C --cs-high chip select active high\n" " -3 --3wire SI/SO signals shared\n" " -v --verbose Verbose (show tx buffer)\n" " -p Send data (e.g. \"1234\\xde\\xad\")\n" " -N --no-cs no chip select\n" " -R --ready slave pulls low to pause\n" " -2 --dual dual transfer\n" " -4 --quad quad transfer\n"); exit(1); } static void SetPthreadPriority(int priority) { struct sched_param param; int policy; int ret; param.sched_priority = priority; ret = pthread_setschedparam(pthread_self(), SCHED_FIFO, ¶m); if (0 != ret) { printf("Unsuccessful in setting thread realtime prio, RET %d\n", ret); } ret = pthread_getschedparam(pthread_self(), &policy, ¶m); priority = param.sched_priority; printf("priority: %d, ret: %d, policy: %d\n", priority, ret, policy); }