本文来自:http://hi.baidu.com/fcni_cn/item/561f6f3f0c391abc124b146a
最近看了韦东山驱动视频的i2c部分,总结出相关知识,分享给大家
<一>定义并填充i2c_driver:
- static const struct
i2c_device_id at24c08b_id[] = { -
{"at24c08b",0 }, //该i2c_driver所支持的i2c_client -
{} - };
- MODULE_DEVICE_TABLE(i2c,at24c08b_id);
- static struct i2c_driver at24c08b_driver ={
-
.driver={ -
.name="at24c08b", -
.owner = THIS_MODULE, -
}, -
.probe=at24c08b_probe, -
.remove=__devexit_p(at24c08b_remove), -
.id_table= at24c08b_id, - };
<二>模块初始化函数
- staticint __init at24c08b_init(void)
- {
-
returni2c_add_driver(&at24c08b_driver); - }
分析i2c_add_driver():
-
i2c_register_driver() -
driver->driver.bus=&i2c_bus_type; //设置i2c_driver的总线类型 -
driver_register() //这个函数结束后就会调用probe()函数 -
i2c_for_each_dev(driver,__process_new_driver); //对每一个存在的i2c_adapter,调用__process_new_driver()函数 -
i2c_do_add_adapter() -
i2c_detect(adap,driver); //检测i2c_check_functionality -
//我们的i2c_driver 没设置 address_list和detect()函数,所以到这里就返回了。 -
address_list=driver->address_list; -
if(!driver->detect|| !address_list) -
return0;
分析driver_register():
-
driver_find() //i2c_driver是否已经被注册 -
bus_add_driver() //将i2c_driver挂接到i2c总线i2c_bus_type上 -
driver_attach() -
//对i2c总线上的每一个i2c设备i2c_client都会调用__driver_attach,这里的dev即i2c_client,drv即i2c_driver -
bus_for_each_dev(drv->bus,NULL,drv, __driver_attach); -
driver_match_device(drv,dev) -
//调用i2c总线i2c_bus_type的match函数i2c_device_match -
return drv->bus->match ?drv->bus->match(dev, drv) : 1; -
i2c_device_match(dev,drv) //通过 dev,找到i2c_client -
//若i2c_client的名字和i2c_device_id的中名字相同,则匹配成功,才会调用后面的probe() -
i2c_match_id(driver->id_table,client) -
driver_probe_device() -
really_probe() -
//调用i2c总线i2c_bus_type的probe函数 -
dev->bus->probe(dev); -
i2c_device_probe() -
//调用到i2c_driver的probe()函数 -
driver->probe(client,i2c_match_id(driver->id_table,client))
i2c总线i2c_bus_type的定义如下:
- struct
bus_type i2c_bus_type = { -
.name ="i2c", -
.match =i2c_device_match, -
.probe =i2c_device_probe, -
.remove =i2c_device_remove, -
.shutdown =i2c_device_shutdown, -
... - };
<三>注册i2c设备相关信息
对于newstyle方式,需要通过i2c_register_board_info()函数注册i2c_board_info,向内核提供i2c设备的相关信息。
在arch/arm/mach-s3c2440/mach-mini2440.c添加如下代码:
(在mach-smdkc110.c中的smdkc110_machine_init中调用
i2c_board_info)
- static struct
i2c_board_info i2c_devices[] __initdata ={ -
{I2C_BOARD_INFO("at24c08b",0x50), }, //0x50是at24c08b的设备地址 - };
- staticvoid __init mini2440_machine_init(void)
- {
-
… -
i2c_register_board_info(0,i2c_devices,ARRAY_SIZE(i2c_devices)); - }
分析i2c_register_board_info():
-
struct i2c_devinfo *devinfo; //定义一个i2c_devinfo -
devinfo->board_info=*info; //保存i2c_board_info -
//将i2c_devinfo挂在链表__i2c_board_list上 ,存储所有i2c_board_info的全局链表 -
list_add_tail(&devinfo->list,&__i2c_board_list);
搜索__i2c_board_list可知:
- i2c_add_numbered_adapter()
//i2c-s3c2410.c中调用该函数(s3c24xx_i2c_probe)来注册一个i2c_adapter i2c-core.c -
i2c_add_adapter() -
i2c_register_adapter() -
i2c_scan_static_board_info() -
list_for_each_entry(devinfo,&__i2c_board_list,list) -
//利用i2c_adapter和i2c_board_info构造i2c_client -
if(devinfo->busnum== adapter->nr -
&&!i2c_new_device(adapter,&devinfo->board_info)) -
structi2c_client*client; -
client->adapter=adap; //设置i2c_client 的adapter -
client->addr=info->addr; //设置设备地址 -
…//继续设置i2c_client -
device_register() //将i2c设备i2c_client挂接到i2c总线上
分析device_register():
-
device_add() -
bus_add_device() -
//将设备挂接在总线上,对于i2c而言,即把i2c_client挂接到i2c_bus_type -
klist_add_tail(&dev->p->knode_bus,&bus->p->klist_devices);
ps:上述这一套分析适用于所有符合总线设备驱动模型的驱动,如usb总线,平台总线,pci总线,i2c总线等
<四>i2c_driver 的probe()函数
正常的注册字符设备即可,即:
(1)分配设备号:
(2)构造file_operations
(3)分配设置注册cdev:cdev_init(&cdev,&file_operations);cdev_add()
<五>file_operations的read()和write()函数
(1)read:
- {
- /*
检查该i2c_adapter是否支持读字节的功能 */ - i2c_check_functionality(I2C_FUNC_SMBUS_READ_BYTE_DATA)
- i2c_smbus_read_byte_data()
//从eeprom读一个字节的数据 - copy_to_user()
//拷贝至用户空间 - }
不是所有的I2C或者SMBus适配器实现了I2C规范上的所有功能,因此当访问I2C适配器时,
并不能完全假定适配器提供了你所需的功能。需要有一种检测适配器是否提供
了所需功能的方法。
对于不断更新的I2C适配器功能常量列表,参考
I2C_FUNC_I2C
I2C_FUNC_10BIT_ADDR
I2C_FUNC_SMBUS_READ_BYTE
I2C_FUNC_SMBUS_WRITE_BYTE
I2C_FUNC_SMBUS_READ_BYTE_DATA
I2C_FUNC_SMBUS_WRITE_BYTE_DATA
I2C_FUNC_SMBUS_READ_WORD_DATA
I2C_FUNC_SMBUS_WRITE_WORD_DATA
...
分析i2c_smbus_read_byte_data(I2C_SMBUS_BYTE_DATA):
- i2c_smbus_xfer(I2C_SMBUS_BYTE_DATA)
-
i2c_smbus_xfer_emulated(I2C_SMBUS_BYTE_DATA) -
msg[1].len = 1; -
...//设置读数据时的i2c_msg -
i2c_transfer() -
//最终调用到i2c-s3c2410.c中设置的i2c_adapter //的master_xfer()函数 -
adap->algo->master_xfer()
(2)write:
- {
- i2c_check_functionality()
- copy_from_user(); //获得用户空间的数据
- i2c_smbus_write_byte_data()//写数据到eeprom
- }
分析i2c_smbus_write_byte_data(I2C_SMBUS_BYTE_DATA):
- i2c_smbus_xfer(I2C_SMBUS_BYTE_DATA)
- i2c_smbus_xfer_emulated(I2C_SMBUS_BYTE_DATA)
-
msg[1].len = 2; -
...//设置写数据时的i2c_msg -
i2c_transfer() -
//最终调用到i2c-s3c2410.c中设置的i2c_adapter //的master_xfer()函数 -
adap->algo->master_xfer()