Brief
在linux 应用层使用/dev/i2c-%d
的原因有以下几点
- 有些器件的驱动需要经常的进行优化改进,并且不是完全稳定可靠,放在内核驱动中可能导致内核oops
- 目前的习惯是内核的设备树仅针对核心部分,这样相同的配置可用于不同的硬件,其他的如gpio,spi,i2c等容易变动的地方仅保留了用户接口,没有具体的设备驱动,个人习惯
为啥要封装一层
- 主要是因为担心总线上多个设备多线程的使用
/dev/i2c-%d
接口对从设备进行通讯,不加以限制会导致I2C通讯过程不完整.
其他
- 多进程情况下安全访问 可以使用sem或者mutex的进程共享属性,实现多进程间的同步
Code
/******************************************************************************
* LJSY C/C++ SOURCE FILE
******************************************************************************
* @file : i2cmod.c
* @brief : This module encapsulates the application layer's access to /dev/i2c-%d device files to ensure the integrity of bus access
* @version : 1.0
* @author : zhangbin (zhangbin.eos@foxmail.com)
* @date : 2023/03/21
*****************************************************************************/
/*=============INCLUDE ======================================*/
#include <linux/i2c-dev.h>
#include <linux/i2c.h>
#include <stdlib.h> /*< for malloc interface>*/
#include <string.h> /*< for mem interface */
#include <unistd.h>
#include <stdio.h>
#include <stdint.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <pthread.h>
#include "i2cmod.h"
#include "common_type.h"
#include "zlog.h"
#include "myzlog.h"
/*=============STATIC DEFINE=================================*/
#define I2C_DEV_FORMAT "/dev/i2c-%d" // 0,1,2,3 ...
/*=============STATIC ENUM===================================*/
/*=============STATIC STRUCT ================================*/
/*=============STATIC FUNC DEFINE============================*/
/**
* @brief 总线互斥锁,也能再这个接口中添加文件建议锁
* @param bus 总线句柄
* @return int
*/
static int i2cmod_bus_lock_init(struct i2cmod_bus * bus);
static int i2cmod_bus_lock_deinit(struct i2cmod_bus * bus);
static int i2cmod_bus_lock(struct i2cmod_bus * bus);
static int i2cmod_bus_module_TryLock(struct i2cmod_bus * bus);
static int i2cmod_bus_unlock(struct i2cmod_bus * bus);
static int i2cmod_set_slave_addr(struct i2cmod_bus * bus, int slave_addr);
/*=============STATIC VARIABLE===============================*/
/**
* @brief 根据设备上的总线数量来修改这个结构体
*/
static struct i2cmod_bus m_i2cmod_bus_list[] = {
{
.id = 0,
.fd = -1,
.request_cnt = 0,
},
{
.id = 1,
.fd = -1,
.request_cnt = 0,
},
};
/*=============EXTERN VARIABLE===============================*/
/*=============EXTERN FUNC===================================*/
void i2cmod_init(void)
{
for (int i = 0; i < ARRAYSIZE(m_i2cmod_bus_list); i++) {
struct i2cmod_bus * bus = &m_i2cmod_bus_list[i];
if (bus->fd > 0) {
close(bus->fd);
bus->fd = -1;
}
sprintf(bus->name, I2C_DEV_FORMAT, bus->id);
bus->fd = open(bus->name, O_RDWR);
if (bus->fd < 0) {
pr_syserr_log("open %s error:", bus->name);
}
//init mutex lock
i2cmod_bus_lock_init(bus);
}
}
struct i2cmod_bus * i2cmod_request_handle(int bus)
{
struct i2cmod_bus * objbus = NULL;
//Find the bus from the list and return the bus related information
for (int i = 0; i < ARRAYSIZE(m_i2cmod_bus_list); i++) {
objbus = &m_i2cmod_bus_list[i];
if (bus == objbus->id) {
i2cmod_bus_lock(objbus);
objbus->request_cnt++;
i2cmod_bus_unlock(objbus);
break;
}
}
if (objbus == NULL) {
pr_log(MYLOG_ERROR, "no found bus %d\n", bus);
return NULL;
}
else {
return objbus;
}
}
void i2cmod_free_handle(struct i2cmod_bus * bus)
{
i2cmod_bus_lock(bus);
bus->request_cnt--;
i2cmod_bus_unlock(bus);
}
int i2cmod_read(struct i2cmod_bus * bus, uint8_t slave, uint8_t regaddr, uint8_t * buff, uint16_t datasize)
{
int ret = -1;
if (bus == NULL) {
pr_log(MYLOG_ERROR, "handle is NULL\n");
return -1;
}
if (bus->fd < 0) {
pr_log(MYLOG_ERROR, "bus %d invalid\n", bus->id);
return -1;
}
i2cmod_bus_lock(bus);
//读数据有2个msg
struct i2c_msg msg[2] = {
{
.addr = slave,
.flags = 0,
.buf = ®addr,
.len = sizeof(regaddr),
},
{
.addr = slave,
.flags = I2C_M_RD,
.buf = buff,
.len = datasize,
},
};
struct i2c_rdwr_ioctl_data rdwr_msg = {
.msgs = msg,
.nmsgs = ARRAYSIZE(msg),
};
ret = ioctl(bus->fd, I2C_RDWR, &rdwr_msg);
i2cmod_bus_unlock(bus);
if (ret < 0) {
pr_syserr_log("bus %d slave %02X reg %02X", bus->id, slave, regaddr);
}
return ret == -1 ? ret : datasize;
}
int i2cmod_write(struct i2cmod_bus * bus, uint8_t slave, uint8_t regaddr, const uint8_t * data, uint16_t datasize)
{
int ret = -1;
if (bus == NULL) {
pr_log(MYLOG_ERROR, "handle is NULL\n");
return -1;
}
if (bus->fd < 0) {
pr_log(MYLOG_ERROR, "bus %d invalid\n", bus->id);
return -1;
}
if (datasize + sizeof(regaddr) > sizeof(bus->wbuff)) {
pr_log(MYLOG_ERROR, "datasize %d must < %ld\n", datasize, sizeof(bus->wbuff) - sizeof(regaddr));
return -1;
}
i2cmod_bus_lock(bus);
memcpy(&bus->wbuff[0], ®addr, sizeof(regaddr));
memcpy(&bus->wbuff[sizeof(regaddr)], data, datasize);
//写数据是1个msg
struct i2c_msg msg[1] = {
{
.addr = slave,
.flags = 0,
.buf = bus->wbuff,
.len = datasize + 1,
}
};
struct i2c_rdwr_ioctl_data rdwr_msg = {
.msgs = msg,
.nmsgs = ARRAYSIZE(msg),
};
ret = ioctl(bus->fd, I2C_RDWR, &rdwr_msg);
i2cmod_bus_unlock(bus);
if (ret < 0) {
pr_syserr_log("bus %d slave %02X reg %02X", bus->id, slave, regaddr);
}
return ret == -1 ? ret : datasize;
}
int i2cmod_read16(struct i2cmod_bus * bus, uint8_t slave, uint16_t regaddr, uint8_t * buff, uint16_t datasize)
{
if (bus == NULL) {
pr_log(MYLOG_ERROR, "handle is NULL\n");
return -1;
}
if (bus->fd < 0) {
pr_log(MYLOG_ERROR, "bus %d invalid\n", bus->id);
return -1;
}
i2cmod_bus_lock(bus);
//memcpy(&wbuff[0],®addr,sizeof(regaddr));
if (write(bus->fd, ®addr, sizeof(regaddr)) != sizeof(regaddr)) {
pr_syserr_log("bus %d slave %02X reg %02X", bus->id, slave, regaddr);
goto failed;
}
if (read(bus->fd, buff, datasize) != datasize) {
pr_syserr_log("bus %d slave %02X reg %02X", bus->id, slave, regaddr);
goto failed;
}
i2cmod_bus_unlock(bus);
return datasize;
failed:
i2cmod_bus_unlock(bus);
return -1;
}
int i2cmod_write16(struct i2cmod_bus * bus, uint8_t slave, uint16_t regaddr, const uint8_t * data, uint16_t datasize)
{
int ret = -1;
if (bus == NULL) {
pr_log(MYLOG_ERROR, "handle is NULL\n");
return -1;
}
if (bus->fd < 0) {
pr_log(MYLOG_ERROR, "bus %d invalid\n", bus->id);
return -1;
}
if (datasize + sizeof(regaddr) > sizeof(bus->wbuff)) {
pr_log(MYLOG_ERROR, "datasize %d must < %ld\n", datasize, sizeof(bus->wbuff) - sizeof(regaddr));
return -1;
}
i2cmod_bus_lock(bus);
memcpy(&bus->wbuff[0], ®addr, sizeof(regaddr));
memcpy(&bus->wbuff[sizeof(regaddr)], data, datasize);
int totalsize = datasize + sizeof(regaddr);
ret = write(bus->fd, bus->wbuff, totalsize);
if (ret != totalsize) {
pr_syserr_log("bus %d slave %02X reg %02X", bus->id, slave, regaddr);
ret = -1;
}
i2cmod_bus_unlock(bus);
return ret;
}
/*=============STATIC FUNC===================================*/
static int i2cmod_bus_lock_init(struct i2cmod_bus * bus)
{
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP);
return pthread_mutex_init(&bus->lock, &attr);
}
static int i2cmod_bus_lock_deinit(struct i2cmod_bus * bus)
{
return pthread_mutex_destroy(&bus->lock);
}
static int i2cmod_bus_lock(struct i2cmod_bus * bus)
{
return pthread_mutex_lock(&bus->lock);
}
static int i2cmod_bus_module_TryLock(struct i2cmod_bus * bus)
{
return pthread_mutex_trylock(&bus->lock);
}
static int i2cmod_bus_unlock(struct i2cmod_bus * bus)
{
return pthread_mutex_unlock(&bus->lock);
}
static int i2cmod_set_slave_addr(struct i2cmod_bus * bus, int slave_addr)
{
if (ioctl(bus->fd, I2C_SLAVE_FORCE/*I2C_SLAVE*/, slave_addr) < 0) {
/* ERROR HANDLING; you can check errno to see what went wrong */
pr_syserr_log("ioctl %d:\n", slave_addr);
return (-1);
}
return (0);
}
/*===========================================================*/
/*END */
/******************************************************************************
* LJSY C/C++ HEAD FILE
******************************************************************************
* @file : i2cmod.h
* @brief :
* @version : 1.0
* @author : zhangbin (zhangbin.eos@foxmail.com)
* @date : 2023/03/21
*****************************************************************************/
#ifndef __I2CMOD_H__
#define __I2CMOD_H__
#ifdef __cplusplus
extern "C" {
#endif
/*=============INCLUDE=======================================*/
#include <stdint.h>
#include <pthread.h>
/*=============EXTERN DEFINE=================================*/
#define I2C_WBUFF_MAX (512+2)
/*=============EXTERN ENUM===================================*/
/*=============EXTERN STRUCT ================================*/
typedef struct i2cmod_bus {
int id;//bus id
char name[32];//dev name
int fd;//i2c dev file handle
size_t request_cnt;//Bus Request Count
uint8_t wbuff[I2C_WBUFF_MAX];
pthread_mutex_t lock;
} OBJ_I2CMOD_BUS;
/*=============EXTERN VARIABLE===============================*/
/*=============EXTERN FUNC===================================*/
/**
* @brief 模块初始化
*/
extern void i2cmod_init(void);
/**
* @brief 从模块中申请一个接口的访问句柄
* @param bus i2c bus id:0,1,2 ...
* @return void* bus handle
*/
extern struct i2cmod_bus * i2cmod_request_handle(int bus);
/**
* @brief 释放一个接口的访问句柄
* @param bus bus handle
*/
extern void i2cmod_free_handle(struct i2cmod_bus * bus);
/**
* @brief i2c读写接口
* @param handle bus handle
* @param slave slave device addr 7bit
* @param regaddr device's reg addr
* @param buff data buffer
* @param datasize data len
* @return int -1=failed;
*/
extern int i2cmod_read(struct i2cmod_bus * bus, uint8_t slave, uint8_t regaddr, uint8_t * buff, uint16_t datasize);
extern int i2cmod_write(struct i2cmod_bus * bus, uint8_t slave, uint8_t regaddr, const uint8_t * data, uint16_t datasize);
extern int i2cmod_read16(struct i2cmod_bus * bus, uint8_t slave, uint16_t regaddr, uint8_t * buff, uint16_t datasize);
extern int i2cmod_write16(struct i2cmod_bus * bus, uint8_t slave, uint16_t regaddr, const uint8_t * data, uint16_t datasize);
#ifdef __cplusplus
}
#endif
#endif//__I2CMOD_H__