i2c_adapter、i2c_client等结构体分析
结构体之间的包含关系
i2c_adapter结构体
i2c_adapter的重要成员
struct i2c_algorithm *algo // 主控的数据传输的算法
struct device dev // 主控分配的device结构体,可以通过dev获得i2c_adapter等操作。
int nr // 适配器编号,即:i2c通道编号。驱动可以通过这个编号获得i2c_adapter
i2c_adapter的内存分配
在主控程序的probe函数中会分配i2c_adapter的内存
i2c_adapter的使用
驱动可以直接通过i2c_get_adapter函数,以adapter编号为参数获得i2c_adapter结构体,进而调用其相关函数完成数据传输。
注意:
i2c_get_adapter函数会调用idr_find(&i2c_adapter_idr, nr);
i2c_add_numbered_adapter和i2c_add_adapter函数中都会调用idr_alloc向i2c_adapter添加adapter i2c_get_adapter;
调用后要使用i2c_put_adapter释放掉adapter。
i2c_client结构体
i2c_client的重要成员
unsigned short addr // 外设的地址(7bit)
struct i2c_adapter *adapter // 本外设所关联的主控适配器
struct device dev // device结构体
i2c_client的内存分配和使用
(1)i2c-dev.c
open函数中分配i2c_client结构体,设置adapter等成员,将file->private_data指向本结构体;
write,read,ioctl等函数中通过file->private_data获得i2c_client结构体,进行其他操作。
(2)外设驱动
分配i2c_client结构体,设置其adapter等成员,驱动中可以将i2c_client作为参数调用i2c_transfer、i2c_master_send、i2c_master_recv等函数。
(3)主控、mux驱动层
如果主控设备(i2c_add_adapter)和mux驱动(i2c_add_driver)都存在,则最终会分配i2c_client结构体,并将其挂到mux驱动(i2c_driver)的clients链表上边。
I2C系统调用过程
整体调用过程
调用过程路径1
调用过程路径2
I2C底层框架
底层调用流程例1
(i2c-hisilicon.c)
底层调用流程例2
(i2c-hisi-v110.c)
底层调用流程例3
用到mux时的流程
(一般不用mux)
驱动程序使用流程
(ipc_stm8.c)
注意:
i2c_put_adapter最好在初始化或者打开时 进行,不能放在module_exit修饰的退出函数中。如果放在了module_exit函数修饰的
出函 数中,会导致rmmod时失败。
原因:i2c_get_adapter会调用 try_module_get函数使i2c_adapter使用计数加一, rmmod函数会先检查模块使用计数,再调用自己 定义的函数,如果i2c_put_adapter在退出函数中, 那么在rmmod检查时使用计数还是1,会导致 rmmod失败,无法卸载。
mux驱动流程
(一般不用mux)
(pca9541.c)
ak4954操作流程
示例源码(应用调用控制器)
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <linux/types.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#define I2C_DEV "/dev/i2c-1"
#define MEM_DEV "/dev/mem"
#define PAGE_SIZE_MASK 0xfffff000
#define PAGE_SIZE 4096
static int g_fd = -1;
static int g_slave_addr;
struct i2c_rdwr_ioctl_data g_i2c_data;
static u8 g_i2c_addr[] = {
0x12,0x13,0x14,0x15,0x16
};
static u8 g_i2c_reg_val[] = {
0x08,0x05,0x11,0x03,0x30
};
int test_i2c_write(unsigned short reg,
unsigned char addr_byte_num,unsigned char val)
{
unsigned char buf[4];
int ret;
g_i2c_data.nmsgs = 1;
buf[0] = reg & 0xff;
buf[1] = val;
(g_i2c_data.msgs[0]).len = 2;
(g_i2c_data.msgs[0]).addr = g_slave_addr;
(g_i2c_data.msgs[0]).buf = buf;
(g_i2c_data.msgs[0]).flags = 0;
ret = ioctl(g_fd, I2C_RDWR, &g_i2c_data);
if(ret < 0) {
printf("i2c ioctl error(write)\n");
return -1;
}
return 0;
}
int test_i2c_read(unsigned short reg,
unsigned char addr_byte_num,unsigned char *val)
{
int ret;
unsigned char reg_buf[4] = {0};
unsigned char val_buf[4] = {0};
if(addr_byte_num == 2) {
reg_buf[0] = (reg >> 8) & 0xFF;
reg_buf[1] = reg & 0xFF;
} else if(addr_byte_num == 1){
reg_buf[0] = reg & 0xff;
}
(g_i2c_data.msgs[0]).len = addr_byte_num;
(g_i2c_data.msgs[0]).addr = g_slave_addr;
(g_i2c_data.msgs[0]).flags = 0;
(g_i2c_data.msgs[0]).buf = reg_buf;
(g_i2c_data.msgs[1]).len = 1;
(g_i2c_data.msgs[1]).addr = g_slave_addr;
(g_i2c_data.msgs[1]).flags = I2C_M_RD;
(g_i2c_data.msgs[1]).buf = val_buf;
g_i2c_data.nmsgs = 2;
ret = ioctl(g_fd, I2C_RDWR, &g_i2c_data);
if(ret < 0) {
printf("i2c ioctl error(read)\n");
return -1;
}
*val = val_buf[0];
return 0;
}
int test_i2c_init(void)
{
int ret = -1;
g_fd = open(I2C_DEV, O_RDWR, 0);
if(g_fd < 0) {
printf("open %s error\n",I2C_DEV);
return -1;
}
ret = ioctl(g_fd, I2C_TENBIT, 0);
if(ret < 0) {
printf("set 7 bit error\n");
goto out;
}
ret = ioctl(g_fd, I2C_SLAVE_FORCE, g_slave_addr);
if (ret < 0)
{
printf("set slave address err!\n");
goto out;
}
g_i2c_data.msgs = (struct i2c_msg *)malloc(g_i2c_data.nmsgs * sizeof(struct i2c_msg));
if(g_i2c_data.msgs == NULL) {
ret = -1;
goto out;
}
return 0;
out:
if(g_fd > 0)
close(g_fd);
if(g_i2c_data.msgs != NULL)
free(g_i2c_data.msgs);
return ret;
}
static void *memmap(unsigned long phy_addr)
{
int memfd = -1;
unsigned long phy_addr_in_page, page_diff;
unsigned long size_in_page = PAGE_SIZE;
void *addr = NULL;
memfd = open (MEM_DEV, O_RDWR | O_SYNC);
if (memfd < 0) {
printf("memmap():open %s error!\n", MEM_DEV);
return NULL;
}
phy_addr_in_page = phy_addr & PAGE_SIZE_MASK;
page_diff = phy_addr - phy_addr_in_page;
addr = mmap ((void *)0, size_in_page, PROT_READ|PROT_WRITE, MAP_SHARED, memfd, phy_addr_in_page);
if (addr == MAP_FAILED) {
printf("memmap():mmap @ 0x%lx error\n", phy_addr_in_page);
close(memfd);
return NULL;
}
if(memfd > 0)
close(memfd);
return (void *)(addr + page_diff);
}
int write_reg(u32 phy_addr, u32 value)
{
void *vir_addr = NULL;
vir_addr = memmap(phy_addr);
if(vir_addr == NULL) {
return -1;
}
*(unsigned int *)vir_addr = value;
return 0;
}
int read_reg(u32 phy_addr, u32 *value)
{
void *vir_addr = NULL;
vir_addr = memmap(phy_addr);
if(vir_addr == NULL) {
return -1;
}
*value = *(unsigned int *)vir_addr;
return 0;
}
void peripheral_reset(void)
{
unsigned int val;
/* reset */
read_reg(0x10200000, &val);
val &= ~(0x1 << 0);
write_reg(0x10200000, val);
usleep(10000);
val |= (0x1 << 0);
write_reg(0x10200000, val);
}
static void memcmp_print(const void * src, const void * dest, unsigned int cnt)
{
unsigned int i = 0, diff_index = 0, diff_flag = 0;
const unsigned char *buf1, *buf2;
if(!src || !dest){
printf("address is NULL\n");
return;
}
for(i = 0, buf1 = src, buf2 = dest; i < cnt; i++){
if(*(buf1 + i) != *(buf2 + i)){
diff_flag = 1;
diff_index = i;
break;
}
}
if(diff_flag){
printf("Compare result : error\n"
"The first difference is : \n"
"src[%u] = %#x\n"
"dest[%u] = %#x\n",
diff_index, *(buf1 + diff_index),
diff_index, *(buf2 + diff_index)
);
}else{
printf("Compare result : ok\n");
}
}
int main(int argc, char *argv[])
{
int i = 0, ret = -1;
unsigned char r_buf[5] = {0};
g_slave_addr = 0x29;
peripheral_reset();
test_i2c_init();
for(i = 0; i < sizeof(g_i2c_addr)/sizeof(g_i2c_addr[0]); i++){
test_i2c_write( g_i2c_addr[i], 1, g_i2c_reg_val[i]);
}
memset(r_buf, 0, 5);
for(i = 0; i < sizeof(g_i2c_addr)/sizeof(g_i2c_addr[0]); i++){
test_i2c_read( g_i2c_addr[i], 1, &r_buf[i]);
}
memcmp_print(g_i2c_reg_val, r_buf, 5);
close(g_fd);
free(g_i2c_data.msgs);
return 0;
}