回顾字符设备驱动程序
编写字符设备驱动程序的步骤
- 确定主设备号
- 创建file_operations结构体
- 在里面填充drv_open/drv_read/drv_ioctl等函数
- 注册file_operations结构体
- register_chrdev(major, &fops, name)
- cdev_init
- 创建入口函数调用
- 以及出口函数
- 在出口函数unregister_chrdev
- 辅助函数(帮助系统自动创建设备节点)
- class_create
- device_create
I2C驱动程序的层次
I2C Core就是I2C核心层,它的作用:
- 提供统一的访问函数,比如i2c_transfer、i2c_smbus_xfer等
- 实现
I2C总线-设备-驱动模型
,管理:I2C设备(i2c_client)、I2C设备驱动(i2c_driver)、I2C控制器(i2c_adapter)
I2C总线-设备-驱动模型
根据 总线-设备-驱动
模型,分为三部分,与设备相关联的i2c_client需要绑定硬件相关的信息。与driver相关的功能由i2c_driver提供。通过i2c_bus_type中i2c_device_match函数,依次比较设备树,id_table将两者绑定起来,若比较成功,接着调用i2c_device_probe函数进行注册。
i2c_driver
i2c_driver表明能支持哪些设备:
- 使用of_match_table来判断
- 设备树中,某个I2C控制器节点下可以创建I2C设备的节点
- 如果I2C设备节点的compatible属性跟of_match_table的某项兼容,则匹配成功
- i2c_client.name跟某个of_match_table[i].compatible值相同,则匹配成功
- 设备树中,某个I2C控制器节点下可以创建I2C设备的节点
- 使用id_table来判断
- i2c_client.name跟某个id_table[i].name值相同,则匹配成功
i2c_driver跟i2c_client匹配成功后,就调用i2c_driver.probe函数。
i2c_client
i2c_client表示一个I2C设备,创建i2c_client的方法有4种:
1.通过设备树来创建。
以at24c02为例,通过compatible属性来匹配创建:
2.有时候无法知道该设备挂载哪个I2C bus下,无法知道它对应的I2C bus number。
但是可以通过其他方法知道对应的i2c_adapter结构体。
可以使用下面两个函数来创建i2c_client:
- i2c_new_device
- 会创建i2c_client,即使该设备并不存在
- i2c_new_probed_device
- 它成功的话,会创建i2c_client,并且表示这个设备肯定存在
需要同时提供adapter和board_info信息。
“tda18211”为需要和后续的device_id中的字符串匹配的名称,如果匹配才会调用probe函数。0x60为从设备地址。
- 它成功的话,会创建i2c_client,并且表示这个设备肯定存在
3.由i2c_driver.detect函数来判断是否有对应的I2C设备并生成i2c_client
4.通过用户空间(user-space)生成
调试时、或者不方便通过代码明确地生成i2c_client时,可以通过用户空间来生成。
// 创建一个i2c_client, .name = "eeprom", .addr=0x50, .adapter是i2c-3
# echo eeprom 0x50 > /sys/bus/i2c/devices/i2c-3/new_device
// 删除一个i2c_client
# echo 0x50 > /sys/bus/i2c/devices/i2c-3/delete_device
i2c驱动的注册过程
整体的层次关系从应用层
到设备层
到核心层
到 adapter层
到实际硬件
可以参考I2C驱动程序的层次,而函数之间的调用关系为i2c_register_driver->driver_register(&driver->driver)->driver_find。
所以这里主要介绍一下这几个重要函数的作用。
Reference:D:\ubuntu iso\100ask\Linux-4.9.88\drivers\iio\dac\ad5064.c
i2c_register_driver
ad5064_i2c_driver
name:支持哪些设备,主要判断设备树中的I2C设备
probe:找到能支持的设备后,该函数被调用。
- probe函数中,分配/注册/设置 file_operation结构体
- file_operation中,使用i2c_transfer等函数发起I2C传输。
id_table:能支持哪些设备
i2c_transfer
client:I2C的从设备句柄
buf:向从设备写的数据
count:传输的字节数
i2c_transfer实际调用的是adapter下面实现的master_xfer函数,
i2c_device_id
driver_register
i2c_register_driver会调用driver_register将设备驱动添加到总线的设备驱动链表中,在此之前,会初始化一个clients的链表,用来存放具体I2C设备信息。
在driver_register中,通过driver_find来判断驱动是否已经注册,然后会调用bus_add_driver将设备驱动添加到总线上。
kobject_uevent函数发送KOBJ_ADD / KOBJ_REMOVE等事件,udev会调用mknod函数,在用户空间创建/dev/XXX文件节点。这里的操作可以代替我们手动调用class_create和device_create创建设备节点。