嵌入式LINUX驱动学习之15 i2c总线源码分析

tech2022-08-26  125

嵌入式LINUX驱动学习之15 i2c总线源码分析

一、i2c设备的注册1.1、struct i2c_board_info结构体及头文件1.2、i2c_register_board_info()函数头文件1.2.1、i2c_register_board_info()函数实现1.2.2 i2c_register_board_info的配套函数arch_initcall() 1.3 i2c_new_device() 和 i2c_get_adapter();1.3.1 i2c_new_device() 通过模块的方式添加一个i2c设备1.3.2 i2c_get_adapter() 从内核I2C总线中获取总线适配器 1.4 i2c_new_probed_device()从地址列表中查找匹配的地址二、i2c驱动软件信息2.1结构体及函数 三、i2c设备控制命令

一、i2c设备的注册

1.1、struct i2c_board_info结构体及头文件

//头文件位置 : include/linux/i2c.h //结构体定义 struct i2c_board_info { char type[I2C_NAME_SIZE]; //i2c_client.name unsigned short flags; //i2c_client.flags unsigned short addr; //i2c_client.addr void *platform_data; //i2c_client.dev.platform_data //........省略更多............ }; //struct i2c_board_info结构体对象可以用下面的宏进行定义 #define I2C_BOARD_INFO(dev_type, dev_addr) \ .type = dev_type, .addr = (dev_addr) //使用: struct i2c_board_info i2c_obj_info[] = { { I2C_BOARD_INFO("i2c设备匹配名称“,i2c设备物理地址) } }

1.2、i2c_register_board_info()函数头文件

使用方法见: i2c代码举例(三轴加速度传感器MMA8653)方式一

//头文件位置 : include/linux/i2c.h //函数原型: int i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned n)/* 参数说明: busum : i2x设备所属总线; info :定义的struct i2c_board_info结构体对象取首地址,用于描述i2c设备,可以是数组; n :struct i2c_board_info对象个数,即i2c设备数量

1.2.1、i2c_register_board_info()函数实现

//源码位置:drivers/i2c/i2c-boardinfo.c int __init i2c_register_board_info(int busnum,\ struct i2c_board_info const *info, unsigned len) { int status; down_write(&__i2c_board_lock);//获取信号量写锁操作 /* 动态分配总线号 */ if (busnum >= __i2c_first_dynamic_bus_num) __i2c_first_dynamic_bus_num = busnum + 1; /* 注册info中的i2c设备 */ for (status = 0; len; len--, info++) { struct i2c_devinfo *devinfo; devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL); /*当内核空间分配内存失败时执行的操作*/ if (!devinfo) { pr_debug("i2c-core: can't register boardinfo!\n"); status = -ENOMEM; break; } devinfo->busnum = busnum; devinfo->board_info = *info; list_add_tail(&devinfo->list, &__i2c_board_list);//将devinfo节点加入到__i2c_board_list链表中; } up_write(&__i2c_board_lock);//释放信号量写锁 return status;//返回0 或错误号 -ENOMEM; }

1.2.2 i2c_register_board_info的配套函数arch_initcall()

//头文件位置 : include/linux/init.h #define __define_initcall(level,fn,id) \ static initcall_t __initcall_##fn##id __used \ __attribute__((__section__(".initcall" level ".init"))) = fn #define arch_initcall(fn) __define_initcall("3",fn,3)

1.3 i2c_new_device() 和 i2c_get_adapter();

使用方法见: i2c代码举例(三轴加速度传感器MMA8653)方式二

1.3.1 i2c_new_device() 通过模块的方式添加一个i2c设备

/* 头文件:include/linux/i2c.h 源码位置:drivers/i2c/i2c-core.c 函数原型: */ struct i2c_client * i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info); /*参数说明: adap : struct i2c_apater结构体对象地址 info : struct i2c_board_info 结构体对象地址 功能:向i2c总线添加一个i2c从设备 返回值 :成功:i2c从设备信息,保存在struct i2c_client结构体中 */

1.3.2 i2c_get_adapter() 从内核I2C总线中获取总线适配器

/* 头文件:include/linux/i2c.h 源码位置:drivers/i2c/i2c-core.c 函数原型: */ struct i2c_adapter *i2c_get_adapter(int nr) /*参数说明: nr : I2C总线编号 功能: 根据I2C总线编号,获取I2C总线适配器 */

1.4 i2c_new_probed_device()从地址列表中查找匹配的地址

和i2c_new_device()二选一使用即可

/* 头文件:include/linux/i2c.h 源码位置:drivers/i2c/i2c-core.c 函数原型: */ extern struct i2c_client * i2c_new_probed_device(struct i2c_adapter *adap, struct i2c_board_info *info, unsigned short const *addr_list, int (*probe)(struct i2c_adapter *, unsigned short addr)); /*参数说明: addr_list : i2c从设备可能的地址 probe : 当配置了对应的函数时,匹配成功,执行对应的函数, 如果匹配成功不需要执行对应函数,可以用NULL 功能: 根据I2C总线编号,获取I2C总线适配器 */

二、i2c驱动软件信息

2.1结构体及函数

//头文件位置 : include/linux/i2c.h struct i2c_driver { int (*probe)(struct i2c_client *, const struct i2c_device_id *);/*当软件驱动和硬件驱动匹配成功时,执行的函数*/ int (*remove)(struct i2c_client *); /*当硬件驱动卸载或当前驱动卸载时执行的函数*/ struct device_driver driver; /*主要使用const char *name;成员,用于和硬件驱动匹配*/ //...........省略更多........................ } int i2c_register_driver(struct module *owner, struct i2c_driver *driver); //注册软件驱动 i2c_add_driver(driver);//宏定义如下: /* #define i2c_add_driver(driver) \ i2c_register_driver(THIS_MODULE, driver) */ void i2c_del_driver(struct i2c_driver *driver); //移除软件驱动 /* 参数说明: owner : 一般用THIS_MODULE; driver: struct i2c_driver结构体对象取地址; */

三、i2c设备控制命令

//源码位置 : include/linux/i2c.h /* //Documentation/i2c/smbus-protocol S (1 bit) : Start bit P (1 bit) : Stop bit Rd/Wr (1 bit) : Read/Write bit. Rd equals 1, Wr equals 0. A, NA (1 bit) : Accept and reverse accept bit. Addr (7 bits): I2C 7 bit address. Note that this can be expanded as usual to get a 10 bit I2C address. Comm (8 bits): Command byte, a data byte which often selects a register on the device. Data (8 bits): A plain data byte. Sometimes, I write DataLow, DataHigh for 16 bit data. Count (8 bits): A data byte containing the length of a block operation. [..]: Data sent by I2C device, as opposed to data sent by the host adapter. */ s32 i2c_smbus_read_byte(const struct i2c_client *client); //S Addr Rd [A] [Data] NA P s32 i2c_smbus_write_byte(const struct i2c_client *client, u8 value); //S Addr Wr [A] Data [A] P s32 i2c_smbus_read_byte_data(const struct i2c_client *client,u8 command); //S Addr Wr [A] Comm [A] S Addr Rd [A] [Data] NA P s32 i2c_smbus_write_byte_data(const struct i2c_client *client,u8 command, u8 value); //S Addr Wr [A] Comm [A] Data [A] P s32 i2c_smbus_read_word_data(const struct i2c_client *client,u8 command); //S Addr Wr [A] Comm [A] S Addr Rd [A] [DataLow] A [DataHigh] NA P s32 i2c_smbus_write_word_data(const struct i2c_client *client,u8 command, u16 value); //S Addr Wr [A] Comm [A] DataLow [A] DataHigh [A] P s32 i2c_smbus_read_block_data(const struct i2c_client *client,u8 command, u8 *values); //S Addr Wr [A] Comm [A] S Addr Rd [A] [Count] A [Data] A [Data] A ... A [Data] NA P s32 i2c_smbus_write_block_data(const struct i2c_client *client,u8 command, u8 length, const u8 *values); //S Addr Wr [A] Comm [A] Count [A] Data [A] Data [A] ... [A] Data [A] P //...........省略更多,可以根据时芯片序图找函数,查找方式: //...........先根据芯片时序图,到Documentation/i2c/smbus-protocol文档中找对应时序图的函数, //...........再根据找到的函数,到include/i2c.h中打函数的原型...................
最新回复(0)