2007-07-31 11:38:44 +04:00
/*
* SPI testing utility ( using spidev driver )
*
* Copyright ( c ) 2007 MontaVista Software , Inc .
* Copyright ( c ) 2007 Anton Vorontsov < avorontsov @ ru . mvista . com >
*
* 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 <stdint.h>
# include <unistd.h>
# include <stdio.h>
# include <stdlib.h>
2015-03-10 23:12:30 +03:00
# include <string.h>
2007-07-31 11:38:44 +04:00
# include <getopt.h>
# include <fcntl.h>
# include <sys/ioctl.h>
2015-11-19 01:30:39 +03:00
# include <sys/stat.h>
2007-07-31 11:38:44 +04:00
# include <linux/types.h>
# include <linux/spi/spidev.h>
# define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
static void pabort ( const char * s )
{
perror ( s ) ;
abort ( ) ;
}
2007-10-16 12:27:47 +04:00
static const char * device = " /dev/spidev1.1 " ;
2014-02-25 14:40:18 +04:00
static uint32_t mode ;
2007-07-31 11:38:44 +04:00
static uint8_t bits = 8 ;
2015-11-19 01:30:39 +03:00
static char * input_file ;
2015-11-19 01:30:40 +03:00
static char * output_file ;
2007-07-31 11:38:44 +04:00
static uint32_t speed = 500000 ;
static uint16_t delay ;
2015-03-10 23:12:31 +03:00
static int verbose ;
2007-07-31 11:38:44 +04:00
2015-03-10 23:12:32 +03:00
uint8_t default_tx [ ] = {
0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF ,
0x40 , 0x00 , 0x00 , 0x00 , 0x00 , 0x95 ,
0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF ,
0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF ,
0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF ,
0xF0 , 0x0D ,
} ;
uint8_t default_rx [ ARRAY_SIZE ( default_tx ) ] = { 0 , } ;
char * input_tx ;
2015-11-19 01:30:42 +03:00
static void hex_dump ( const void * src , size_t length , size_t line_size ,
char * prefix )
2015-03-10 23:12:30 +03:00
{
int i = 0 ;
const unsigned char * address = src ;
const unsigned char * line = address ;
unsigned char c ;
printf ( " %s | " , prefix ) ;
while ( length - - > 0 ) {
printf ( " %02X " , * address + + ) ;
if ( ! ( + + i % line_size ) | | ( length = = 0 & & i % line_size ) ) {
if ( length = = 0 ) {
while ( i + + % line_size )
printf ( " __ " ) ;
}
printf ( " | " ) ; /* right close */
while ( line < address ) {
c = * line + + ;
printf ( " %c " , ( c < 33 | | c = = 255 ) ? 0x2E : c ) ;
}
printf ( " \n " ) ;
if ( length > 0 )
printf ( " %s | " , prefix ) ;
}
}
}
2015-03-10 23:12:32 +03:00
/*
* Unescape - process hexadecimal escape character
* converts shell input " \x23 " - > 0x23
*/
2015-04-16 22:49:29 +03:00
static int unescape ( char * _dst , char * _src , size_t len )
2015-03-10 23:12:32 +03:00
{
int ret = 0 ;
2015-11-19 01:30:41 +03:00
int match ;
2015-03-10 23:12:32 +03:00
char * src = _src ;
char * dst = _dst ;
unsigned int ch ;
while ( * src ) {
if ( * src = = ' \\ ' & & * ( src + 1 ) = = ' x ' ) {
2015-11-19 01:30:41 +03:00
match = sscanf ( src + 2 , " %2x " , & ch ) ;
if ( ! match )
pabort ( " malformed input string " ) ;
2015-03-10 23:12:32 +03:00
src + = 4 ;
* dst + + = ( unsigned char ) ch ;
} else {
* dst + + = * src + + ;
}
ret + + ;
}
return ret ;
}
static void transfer ( int fd , uint8_t const * tx , uint8_t const * rx , size_t len )
2007-07-31 11:38:44 +04:00
{
int ret ;
2015-11-19 01:30:40 +03:00
int out_fd ;
2007-07-31 11:38:44 +04:00
struct spi_ioc_transfer tr = {
. tx_buf = ( unsigned long ) tx ,
. rx_buf = ( unsigned long ) rx ,
2015-03-10 23:12:32 +03:00
. len = len ,
2007-07-31 11:38:44 +04:00
. delay_usecs = delay ,
. speed_hz = speed ,
. bits_per_word = bits ,
} ;
2014-02-25 14:40:18 +04:00
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 ;
}
2007-07-31 11:38:44 +04:00
ret = ioctl ( fd , SPI_IOC_MESSAGE ( 1 ) , & tr ) ;
2010-04-30 02:02:28 +04:00
if ( ret < 1 )
2007-07-31 11:38:44 +04:00
pabort ( " can't send spi message " ) ;
2015-03-10 23:12:31 +03:00
if ( verbose )
2015-03-10 23:12:32 +03:00
hex_dump ( tx , len , 32 , " TX " ) ;
2015-11-19 01:30:40 +03:00
if ( output_file ) {
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 )
2015-12-04 15:59:14 +03:00
pabort ( " not all bytes written to output file " ) ;
2015-11-19 01:30:40 +03:00
close ( out_fd ) ;
}
if ( verbose | | ! output_file )
hex_dump ( rx , len , 32 , " RX " ) ;
2007-07-31 11:38:44 +04:00
}
2009-09-23 03:43:42 +04:00
static void print_usage ( const char * prog )
2007-07-31 11:38:44 +04:00
{
printf ( " Usage: %s [-DsbdlHOLC3] \n " , prog ) ;
puts ( " -D --device device to use (default /dev/spidev1.1) \n "
" -s --speed max speed (Hz) \n "
" -d --delay delay (usec) \n "
2015-11-19 01:30:42 +03:00
" -b --bpw bits per word \n "
2015-11-19 01:30:39 +03:00
" -i --input input data from a file (e.g. \" test.bin \" ) \n "
2015-11-19 01:30:40 +03:00
" -o --output output data to a file (e.g. \" results.bin \" ) \n "
2007-07-31 11:38:44 +04:00
" -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 "
2014-02-20 19:01:43 +04:00
" -3 --3wire SI/SO signals shared \n "
2015-03-10 23:12:31 +03:00
" -v --verbose Verbose (show tx buffer) \n "
2015-03-10 23:12:32 +03:00
" -p Send data (e.g. \" 1234 \\ xde \\ xad \" ) \n "
2014-02-20 19:01:43 +04:00
" -N --no-cs no chip select \n "
2014-02-25 14:40:18 +04:00
" -R --ready slave pulls low to pause \n "
" -2 --dual dual transfer \n "
" -4 --quad quad transfer \n " ) ;
2007-07-31 11:38:44 +04:00
exit ( 1 ) ;
}
2009-09-23 03:43:42 +04:00
static void parse_opts ( int argc , char * argv [ ] )
2007-07-31 11:38:44 +04:00
{
while ( 1 ) {
2007-10-16 12:27:47 +04:00
static const struct option lopts [ ] = {
2007-07-31 11:38:44 +04:00
{ " device " , 1 , 0 , ' D ' } ,
{ " speed " , 1 , 0 , ' s ' } ,
{ " delay " , 1 , 0 , ' d ' } ,
{ " bpw " , 1 , 0 , ' b ' } ,
2015-11-19 01:30:39 +03:00
{ " input " , 1 , 0 , ' i ' } ,
2015-11-19 01:30:40 +03:00
{ " output " , 1 , 0 , ' o ' } ,
2007-07-31 11:38:44 +04:00
{ " loop " , 0 , 0 , ' l ' } ,
{ " cpha " , 0 , 0 , ' H ' } ,
{ " cpol " , 0 , 0 , ' O ' } ,
{ " lsb " , 0 , 0 , ' L ' } ,
{ " cs-high " , 0 , 0 , ' C ' } ,
{ " 3wire " , 0 , 0 , ' 3 ' } ,
2009-06-30 22:41:26 +04:00
{ " no-cs " , 0 , 0 , ' N ' } ,
{ " ready " , 0 , 0 , ' R ' } ,
2014-02-25 14:40:18 +04:00
{ " dual " , 0 , 0 , ' 2 ' } ,
2015-03-10 23:12:31 +03:00
{ " verbose " , 0 , 0 , ' v ' } ,
2014-02-25 14:40:18 +04:00
{ " quad " , 0 , 0 , ' 4 ' } ,
2007-07-31 11:38:44 +04:00
{ NULL , 0 , 0 , 0 } ,
} ;
int c ;
2015-11-19 01:30:40 +03:00
c = getopt_long ( argc , argv , " D:s:d:b:i:o:lHOLC3NR24p:v " ,
2015-11-19 01:30:39 +03:00
lopts , NULL ) ;
2007-07-31 11:38:44 +04:00
if ( c = = - 1 )
break ;
switch ( c ) {
case ' D ' :
device = optarg ;
break ;
case ' s ' :
speed = atoi ( optarg ) ;
break ;
case ' d ' :
delay = atoi ( optarg ) ;
break ;
case ' b ' :
bits = atoi ( optarg ) ;
break ;
2015-11-19 01:30:39 +03:00
case ' i ' :
input_file = optarg ;
break ;
2015-11-19 01:30:40 +03:00
case ' o ' :
output_file = optarg ;
break ;
2007-07-31 11:38:44 +04:00
case ' l ' :
mode | = SPI_LOOP ;
break ;
case ' H ' :
mode | = SPI_CPHA ;
break ;
case ' O ' :
mode | = SPI_CPOL ;
break ;
case ' L ' :
mode | = SPI_LSB_FIRST ;
break ;
case ' C ' :
mode | = SPI_CS_HIGH ;
break ;
case ' 3 ' :
mode | = SPI_3WIRE ;
break ;
2009-06-30 22:41:26 +04:00
case ' N ' :
mode | = SPI_NO_CS ;
break ;
2015-03-10 23:12:31 +03:00
case ' v ' :
verbose = 1 ;
break ;
2009-06-30 22:41:26 +04:00
case ' R ' :
mode | = SPI_READY ;
break ;
2015-03-10 23:12:32 +03:00
case ' p ' :
input_tx = optarg ;
break ;
2014-02-25 14:40:18 +04:00
case ' 2 ' :
mode | = SPI_TX_DUAL ;
break ;
case ' 4 ' :
mode | = SPI_TX_QUAD ;
break ;
2007-07-31 11:38:44 +04:00
default :
print_usage ( argv [ 0 ] ) ;
break ;
}
}
2014-02-25 14:40:18 +04:00
if ( mode & SPI_LOOP ) {
if ( mode & SPI_TX_DUAL )
mode | = SPI_RX_DUAL ;
if ( mode & SPI_TX_QUAD )
mode | = SPI_RX_QUAD ;
}
2007-07-31 11:38:44 +04:00
}
2015-11-19 01:30:38 +03:00
static void transfer_escaped_string ( int fd , char * str )
{
size_t size = strlen ( str + 1 ) ;
uint8_t * tx ;
uint8_t * rx ;
tx = malloc ( size ) ;
if ( ! tx )
pabort ( " can't allocate tx buffer " ) ;
rx = malloc ( size ) ;
if ( ! rx )
pabort ( " can't allocate rx buffer " ) ;
size = unescape ( ( char * ) tx , str , size ) ;
transfer ( fd , tx , rx , size ) ;
free ( rx ) ;
free ( tx ) ;
}
2015-11-19 01:30:39 +03:00
static void transfer_file ( int fd , char * filename )
{
ssize_t bytes ;
struct stat sb ;
int tx_fd ;
uint8_t * tx ;
uint8_t * rx ;
if ( stat ( filename , & sb ) = = - 1 )
pabort ( " can't stat input file " ) ;
tx_fd = open ( filename , O_RDONLY ) ;
if ( fd < 0 )
pabort ( " can't open input file " ) ;
tx = malloc ( sb . st_size ) ;
if ( ! tx )
pabort ( " can't allocate tx buffer " ) ;
rx = malloc ( sb . st_size ) ;
if ( ! rx )
pabort ( " can't allocate rx buffer " ) ;
bytes = read ( tx_fd , tx , sb . st_size ) ;
if ( bytes ! = sb . st_size )
pabort ( " failed to read input file " ) ;
transfer ( fd , tx , rx , sb . st_size ) ;
free ( rx ) ;
free ( tx ) ;
close ( tx_fd ) ;
}
2007-07-31 11:38:44 +04:00
int main ( int argc , char * argv [ ] )
{
int ret = 0 ;
int fd ;
parse_opts ( argc , argv ) ;
fd = open ( device , O_RDWR ) ;
if ( fd < 0 )
pabort ( " can't open device " ) ;
/*
* spi mode
*/
2014-02-25 14:40:18 +04:00
ret = ioctl ( fd , SPI_IOC_WR_MODE32 , & mode ) ;
2007-07-31 11:38:44 +04:00
if ( ret = = - 1 )
pabort ( " can't set spi mode " ) ;
2014-02-25 14:40:18 +04:00
ret = ioctl ( fd , SPI_IOC_RD_MODE32 , & mode ) ;
2007-07-31 11:38:44 +04:00
if ( ret = = - 1 )
pabort ( " can't get spi mode " ) ;
/*
* bits per word
*/
ret = ioctl ( fd , SPI_IOC_WR_BITS_PER_WORD , & bits ) ;
if ( ret = = - 1 )
pabort ( " can't set bits per word " ) ;
ret = ioctl ( fd , SPI_IOC_RD_BITS_PER_WORD , & bits ) ;
if ( ret = = - 1 )
pabort ( " can't get bits per word " ) ;
/*
* max speed hz
*/
ret = ioctl ( fd , SPI_IOC_WR_MAX_SPEED_HZ , & speed ) ;
if ( ret = = - 1 )
pabort ( " can't set max speed hz " ) ;
ret = ioctl ( fd , SPI_IOC_RD_MAX_SPEED_HZ , & speed ) ;
if ( ret = = - 1 )
pabort ( " can't get max speed hz " ) ;
2014-02-25 14:40:18 +04:00
printf ( " spi mode: 0x%x \n " , mode ) ;
2007-07-31 11:38:44 +04:00
printf ( " bits per word: %d \n " , bits ) ;
printf ( " max speed: %d Hz (%d KHz) \n " , speed , speed / 1000 ) ;
2015-11-19 01:30:39 +03:00
if ( input_tx & & input_file )
pabort ( " only one of -p and --input may be selected " ) ;
2015-11-19 01:30:38 +03:00
if ( input_tx )
transfer_escaped_string ( fd , input_tx ) ;
2015-11-19 01:30:39 +03:00
else if ( input_file )
transfer_file ( fd , input_file ) ;
2015-11-19 01:30:38 +03:00
else
2015-03-10 23:12:32 +03:00
transfer ( fd , default_tx , default_rx , sizeof ( default_tx ) ) ;
2007-07-31 11:38:44 +04:00
close ( fd ) ;
return ret ;
}