Skip to content

Instantly share code, notes, and snippets.

@wukaihua119
Last active December 27, 2021 16:03
Show Gist options
  • Save wukaihua119/48fd665e12ed634307f3ecae4527e2f3 to your computer and use it in GitHub Desktop.
Save wukaihua119/48fd665e12ed634307f3ecae4527e2f3 to your computer and use it in GitHub Desktop.
simple i2c device via ioctl()
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#define I2C_DEV_OPS 0xfe
#define I2C_DEV_WR 0x00
#define I2C_DEV_RD 0x01
struct i2c_ops{
int fd;
uint8_t busno;
uint8_t slvaddr;
uint8_t reg;
uint8_t *wr_buff;
uint8_t *rd_buff;
uint8_t ops;
size_t len;
};
static int __read_ioctl( struct i2c_ops *data );
static int __write_ioctl( struct i2c_ops *data );
typedef int (*func_ptr)( struct i2c_ops * );
func_ptr ops_func[] = {
__write_ioctl,
__read_ioctl,
};
static int __read_ioctl( struct i2c_ops *data ){
int ret;
uint8_t *wrbuf, *rdbuf;
wrbuf = calloc( 1, sizeof( uint8_t ) );
wrbuf[0] = data->reg;
rdbuf = calloc( data->len, sizeof( uint8_t ) );
struct i2c_msg msg[] = {
{
.addr = data->slvaddr,
.flags = 0,
.len = 1,
.buf = wrbuf
},
{
.addr = data->slvaddr,
.flags = I2C_M_RD | I2C_M_NOSTART,
.len = data->len,
.buf = rdbuf
},
};
struct i2c_rdwr_ioctl_data payload = {
.msgs = msg,
.nmsgs = sizeof(msg)/sizeof(struct i2c_msg),
};
ret = -1;
ret = ioctl( data->fd, I2C_RDWR, &payload );
#ifdef DBG
printf( "DBG: the return of ioctl() in %s() is %d, errno = %d\n", __FUNCTION__, ret, errno );
#endif
if( ret == -1 )
goto free;
data->rd_buff = calloc( data->len, sizeof( uint8_t ) );
memcpy( data->rd_buff, rdbuf, data->len );
#ifdef DBG
printf( "DBG: the val read from i2c device is " );
for( int i = 0; i < data->len; i++ )
printf( "0x%08x ", *(data->rd_buff+i) );
printf( "\n" );
#endif
free:
free( rdbuf );
free( wrbuf );
return ret;
}
static int __write_ioctl( struct i2c_ops *data ) {
int ret;
uint8_t *buf;
buf = calloc( data->len+1, sizeof( uint8_t ) );
buf[0] = data->reg;
memcpy( &buf[1], data->wr_buff, data->len );
struct i2c_msg msg = {
.addr = data->slvaddr,
.flags = 0,
.len = data->len+1,
.buf = buf
};
struct i2c_rdwr_ioctl_data payload = {
.msgs = &msg,
.nmsgs = sizeof(msg)/sizeof(struct i2c_msg),
};
ret = -1;
#ifdef DBG
printf( "DBG: %s ", __FUNCTION__ );
for( int i = 0; i < data->len+1; i++ )
printf( "0x%x ", buf[i] );
printf( "\n" );
#endif
ret = ioctl( data->fd, I2C_RDWR, &payload );
#ifdef DBG
printf( "DBG: the return of ioctl() in %s() is %d, errno = %d\n", __FUNCTION__, ret, errno );
#endif
free( buf );
return ret;
}
void open_i2cdev( int *fd, uint8_t bus, int ops ){
char i2cbus[256] = { 0 };
sprintf( i2cbus, "/dev/i2c-%d", bus );
*fd = open( i2cbus, ops );
}
int write_i2cdev( struct i2c_ops *data ){
unsigned long funcs;
if( ioctl(data->fd, I2C_FUNCS, &funcs) < 0 )
goto ret_;
if( funcs & I2C_FUNC_I2C ) {
return (*ops_func[data->ops])( data );
}
ret_:
printf( "ERROR: not spport the I2C operation\n" );
return -1;
}
int read_i2cdev( struct i2c_ops *data ){
unsigned long funcs;
if( ioctl(data->fd, I2C_FUNCS, &funcs) < 0 )
goto ret_;
if( funcs & I2C_FUNC_I2C ) {
return (*ops_func[data->ops])( data );
}
ret_:
printf( "ERROR: not spport the I2C operation\n" );
return -1;
}
int ops_i2cdev( struct i2c_ops *data ){
unsigned long funcs;
if( ioctl(data->fd, I2C_FUNCS, &funcs) < 0 )
goto ret_;
if( funcs & I2C_FUNC_I2C ) {
return (*ops_func[data->ops])( data );
}
ret_:
printf( "ERROR: not spport the I2C operation\n" );
return -1;
}
void call_help( void ){
printf( "Usage: \n" );
printf( " i2cutil <bus_no> <slv.addr.> <reg> <WR=0|RD=1> <len> [<data> ...]\n" );
}
int main( int argc, char *argv[] ){
struct i2c_ops *data;
int ret;
if( argc < 6 ){
call_help();
return 1;
}
data = (struct i2c_ops *) malloc( sizeof( struct i2c_ops ) );
if( data == NULL ){
printf( "ERROR: malloc() cannot allocate space for struct i2c_ops\n" );
return 1;
}
data->busno = strtol( argv[1], NULL, 10 );
data->slvaddr = strtol( argv[2], NULL, 16 );
data->reg = strtol( argv[3], NULL, 16 );
data->ops = strtol( argv[4], NULL, 10);
data->len = strtol( argv[5], NULL, 10);
if( !data->ops ){
data->wr_buff = calloc( data->len, sizeof( uint8_t ) );
for( int i = 0; i < data->len; i++ )
data->wr_buff[i] = strtol( argv[6+i], NULL, 16 );
}
#ifdef DBG
printf( "DBG: busno %d, slvaddr 0x%07x, reg 0x%x, ops %s, len %ld\n",
data->busno, data->slvaddr, data->reg, data->ops? "RD":"WR", data->len );
if( !data->ops )
for( int i = 0; i < data->len; i++ )
printf( "0x%x ", *(data->wr_buff+i) );
printf( "\n" );
#endif
errno = 0;
open_i2cdev( &data->fd, data->busno, O_RDWR );
if( data->fd == -1 ){
printf( "ERROR in open() /dev/i2c-%d, errno[%d]:%s\n",
data->busno, errno, strerror(errno) );
goto ret_;
}
if( data->ops & I2C_DEV_OPS ){
printf( "ERROR in operation, either in RD or WR\n" );
call_help();
goto ret_;
}else
ret = ops_i2cdev( data );
if( ret == -1 ){
printf( "ERROR in write_ioctl() to /dev/i2c-%d, errno[%d]:%s\n",
data->busno, errno, strerror(errno) );
goto ret_;
}
if( data->ops & I2C_DEV_RD ){
printf( "We get: \n" );
for( int j = 0; j < data->len; j++ )
printf( " 0x%x ", *(data->rd_buff+j) );
}
printf( "\n" );
ret_:
if( data->rd_buff )
free( data->rd_buff );
if( data->wr_buff )
free( data->wr_buff );
close( data->fd );
free( data );
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment