0
点赞
收藏
分享

微信扫一扫

Linux的I2C子系统

i2c_adapter、i2c_client等结构体分析 

结构体之间的包含关系

Linux的I2C子系统_#include

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系统调用过程

整体调用过程

Linux的I2C子系统_#include_02

调用过程路径1

Linux的I2C子系统_#include_03

调用过程路径2

I2C底层框架

 底层调用流程例1

(i2c-hisilicon.c)

Linux的I2C子系统_linux_04底层调用流程例2

(i2c-hisi-v110.c)

Linux的I2C子系统_子系统_05

底层调用流程例3

Linux的I2C子系统_#include_06

用到mux时的流程

(一般不用mux)

Linux的I2C子系统_linux_07

驱动程序使用流程

(ipc_stm8.c)

Linux的I2C子系统_子系统_08

注意:
 
​  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)

Linux的I2C子系统_linux_09

ak4954操作流程

Linux的I2C子系统_#define_10

示例源码(应用调用控制器)

#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;
}


举报

相关推荐

0 条评论