Last active
December 27, 2021 16:03
-
-
Save wukaihua119/48fd665e12ed634307f3ecae4527e2f3 to your computer and use it in GitHub Desktop.
simple i2c device via ioctl()
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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