嵌入式LINUX驱动学习之15 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中打函数的原型...................