“I2C驱动”的版本间的差异
Baiwen root(讨论 | 贡献) (创建页面,内容为“驱动框架分析 包含 此驱动框架涉及的文件 文件的用途 以及这些文件到底重要的结构体函数分析 <categorytree mode=all background-c...”) |
|||
(未显示1个用户的10个中间版本) | |||
第1行: | 第1行: | ||
− | 驱动框架分析 | + | =驱动涉及核心文件= |
+ | =驱动框架分析= | ||
+ | 内核中I2C的处理已经做好了,我们只需要做设备驱动程序相关的内容。 | ||
+ | 总线处理好了I2C协议,即总线知道如何收发数据,而不知道数据的含义,我们要做的只是设备相关层的代码。 | ||
+ | |||
+ | I2C协议中,先发出7bit“设备地址”,然后是1位“写”或“读”的标志位。然后接着是每发出8位数据有一个ACK位。 | ||
+ | |||
+ | |||
+ | 一般I2C驱动分为两层: | ||
+ | #总线层:知道设备如何读写。芯片厂家会帮我们做好。操作寄存器。drivers\i2c\busses | ||
+ | #设备层驱动层:知道数据的含义。drivers\i2c\chips | ||
+ | |||
+ | =核心文件函数分析= | ||
+ | |||
+ | ==分析:\drivers\i2c\busses\I2c-s3c2410.c== | ||
+ | ===1,找到probe函数:=== | ||
+ | |||
+ | <syntaxhighlight lang="c" > | ||
+ | int __init i2c_adap_s3c_init(void) | ||
+ | platform_driver_register(&s3c2410_i2c_driver); | ||
+ | </syntaxhighlight> | ||
+ | 注册一个平台设备,当内核中有同名“s3c2440-i2c”的平台设备时,“.probe = s3c24xx_i2c_probe,”就被调用。 | ||
+ | |||
+ | ===2,分析 probe 函数:=== | ||
+ | <syntaxhighlight lang="c" > | ||
+ | int s3c24xx_i2c_probe(struct platform_device *pdev) | ||
+ | -->i2c->clk = clk_get(&pdev->dev, "i2c"); 使能I2C时钟。 | ||
+ | -->I2C适配器结构“i2c_adapter”: | ||
+ | i2c->adap.algo_data = i2c; | ||
+ | i2c->adap.dev.parent = &pdev->dev; | ||
+ | -->ret = s3c24xx_i2c_init(i2c); 硬件相关初始化。 | ||
+ | -->request_irq(res->start, s3c24xx_i2c_irq, IRQF_DISABLED, pdev->name, i2c); 注册中断 | ||
+ | -->i2c_add_adapter(&i2c->adap); 注册I2C适配器 | ||
+ | -->i2c_register_adapter(adapter); | ||
+ | </syntaxhighlight > | ||
+ | |||
+ | |||
+ | =总线设备驱动”模型:= | ||
+ | 一,I2C总线驱动程序:插槽 (分析:linux-2.6.22.6\drivers\i2c\busses\i2c-s3c2410.c) | ||
+ | 1,分配结构:i2c_adapter: | ||
+ | |||
+ | int s3c24xx_i2c_probe(struct platform_device *pdev) | ||
+ | -->struct s3c24xx_i2c *i2c = &s3c24xx_i2c; | ||
+ | |||
+ | |||
+ | 2,设置结构i2c_adapter:核心是设置“i2c_algorithm算法结构”。 | ||
+ | a,如何收发起始信号、数据、响应等. | ||
+ | b,i2c_adapter结构中有i2c_algorithm算法结构。 | ||
+ | struct s3c24xx_i2c *i2c = &s3c24xx_i2c; | ||
+ | -->.algo = &s3c24xx_i2c_algorithm, 其中的核心就是算法 | ||
+ | |||
+ | ①,算法结构中“.master_xfer”是核心。 | ||
+ | int s3c24xx_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg *msgs, int num) | ||
+ | -->s3c24xx_i2c_doxfer(i2c, msgs, num); “doxfer”执行传输。 | ||
+ | |||
+ | ②,执行传输: | ||
+ | int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c, struct i2c_msg *msgs, int num) | ||
+ | -->s3c24xx_i2c_enable_irq(i2c); 使能中断 | ||
+ | -->s3c24xx_i2c_message_start(i2c, msgs); 起动传输,会产生各种中断。 | ||
+ | -->wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5); 等待事件完成。 | ||
+ | |||
+ | ③,起动传输:设置寄存器 | ||
+ | 寄存器: | ||
+ | S3C2410_IICCON :I2C控制寄存器 | ||
+ | S3C2410_IICSTAT :I2C状态寄存器 | ||
+ | S3C2410_IICADD : | ||
+ | S3C2410_IICDS :I2C DS寄存器 | ||
+ | S3C2440_IICLC : | ||
+ | void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c, struct i2c_msg *msg) | ||
+ | -->iiccon = readl(i2c->regs + S3C2410_IICCON); | ||
+ | -->writel(stat, i2c->regs + S3C2410_IICSTAT); | ||
+ | -->writeb(addr, i2c->regs + S3C2410_IICDS); | ||
+ | |||
+ | 3,注册“i2c_adapter”:i2c_add_adapter() | ||
+ | int s3c24xx_i2c_probe(struct platform_device *pdev) | ||
+ | -->i2c_add_adapter(&i2c->adap); | ||
+ | -->i2c_register_adapter(adapter); | ||
+ | |||
+ | int i2c_register_adapter(struct i2c_adapter *adap) | ||
+ | -->device_register(&adap->dev); | ||
+ | |||
+ | |||
+ | 二,(分析:linux-2.6.22.6\drivers\i2c\chips\eeprom.c) | ||
+ | i2c_add_driver | ||
+ | i2c_register_driver | ||
+ | driver->driver.bus = &i2c_bus_type; | ||
+ | driver_register(&driver->driver); | ||
+ | |||
+ | list_for_each_entry(adapter, &adapters, list) { | ||
+ | driver->attach_adapter(adapter); | ||
+ | i2c_probe(adapter, &addr_data, eeprom_detect); | ||
+ | i2c_probe_address // 发出S信号,发出设备地址(来自addr_data) | ||
+ | i2c_smbus_xfer | ||
+ | i2c_smbus_xfer_emulated | ||
+ | i2c_transfer | ||
+ | adap->algo->master_xfer // s3c24xx_i2c_xfer | ||
+ | int __init eeprom_init(void) | ||
+ | -->i2c_add_driver(&eeprom_driver); | ||
+ | -->i2c_register_driver(THIS_MODULE, driver); | ||
+ | |||
+ | 裸板程序中发现设备: | ||
+ | 先发一个“STATT”信号,再发出7bit的设备地址,若设备存在,则在第9个时钟里,此存在的设备(从机)会把“SDA”信号线拉低作为ACK回应,这样主机就知道有相对应地址的设备存在。 | ||
+ | |||
+ | int i2c_register_driver(struct module *owner, struct i2c_driver *driver) | ||
+ | -->driver->driver.bus = &i2c_bus_type; 总线是“i2c_bus_type”。 | ||
+ | -->driver_register(&driver->driver); 注册一个“i2c_driver”结构体。 | ||
+ | -->list_for_each_entry(adapter, &adapters, list) {driver->attach_adapter(adapter);} | ||
+ | 对"adapters"链表里的每一个成员,调用"i2c_driver"结构里面的attach_adapter()。 | ||
+ | |||
+ | |||
+ | 此实例代码中的"i2c_driver"结构体是“eeprom_driver”,它其中的“attach_adapter()”如下: | ||
+ | |||
+ | |||
+ | int eeprom_attach_adapter(struct i2c_adapter *adapter) | ||
+ | -->i2c_probe(adapter, &addr_data, eeprom_detect); 其中参2就是设备地址信息。 | ||
+ | -->i2c_probe_address(adapter,forces[kind][i + 1],kind, found_proc); 发出start信号,发出设备地址。 | ||
+ | -->i2c_smbus_xfer(adapter, addr, 0, 0, 0, I2C_SMBUS_QUICK, NULL); i2c传输 | ||
+ | -->adapter->algo->smbus_xfer(adapter,addr,flags,read_write,command,size,data); | ||
+ | 调用“i2c_adapter” 结构里面的成员“i2c_algorithm”结构中的“算法函数”。要是没有这个 | ||
+ | “smbus_xfer()”,就使用:此代码中是用下面的代码。 | ||
+ | -->i2c_smbus_xfer_emulated(adapter,addr,flags,read_write,command,size,data); | ||
+ | -->i2c_transfer(adapter, msg, num); | ||
+ | -->adap->algo->master_xfer(adap,msgs,num); | ||
+ | 调用“i2c_adapter” 结构里面的成员“i2c_algorithm”结构中的“算法函数-smbus_xfer()”。 | ||
+ | 对于“i2c_s3c2410.c” 中,这个“smbus_xfer()”函数即是: | ||
+ | |||
+ | |||
+ | |||
+ | USB总线会自动识别新接入的USB设备。但I2C总线不能。需要: | ||
+ | 1,发出START信号 | ||
+ | 2,发出设备地址。 | ||
+ | 才能知道是否有此设备存在。 | ||
+ | 从“i2c_adapter”结构的“adapter”链表取出“i2c总线”中的一个一个驱动程序(称为“适配器”),使用里面的“.smbus_xfer”函数发“start”信号、发设备地址(在I2C设备驱动的i2c_driver 结构中,成员“.id”就是表示支持哪些I2C设备)、 | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
包含 | 包含 | ||
此驱动框架涉及的文件 | 此驱动框架涉及的文件 | ||
文件的用途 | 文件的用途 | ||
− | |||
<categorytree mode=all background-color:white;">I2C驱动</categorytree> | <categorytree mode=all background-color:white;">I2C驱动</categorytree> | ||
+ | <categorytree mode=all background-color:white;">SAMSUNG-2440</categorytree> | ||
+ | |||
[[Category:I2C驱动]] | [[Category:I2C驱动]] |
2018年1月24日 (三) 18:14的最新版本
目录
驱动涉及核心文件
驱动框架分析
内核中I2C的处理已经做好了,我们只需要做设备驱动程序相关的内容。 总线处理好了I2C协议,即总线知道如何收发数据,而不知道数据的含义,我们要做的只是设备相关层的代码。
I2C协议中,先发出7bit“设备地址”,然后是1位“写”或“读”的标志位。然后接着是每发出8位数据有一个ACK位。
一般I2C驱动分为两层:
- 总线层:知道设备如何读写。芯片厂家会帮我们做好。操作寄存器。drivers\i2c\busses
- 设备层驱动层:知道数据的含义。drivers\i2c\chips
核心文件函数分析
分析:\drivers\i2c\busses\I2c-s3c2410.c
1,找到probe函数:
int __init i2c_adap_s3c_init(void)
platform_driver_register(&s3c2410_i2c_driver);
注册一个平台设备,当内核中有同名“s3c2440-i2c”的平台设备时,“.probe = s3c24xx_i2c_probe,”就被调用。
2,分析 probe 函数:
int s3c24xx_i2c_probe(struct platform_device *pdev)
-->i2c->clk = clk_get(&pdev->dev, "i2c"); 使能I2C时钟。
-->I2C适配器结构“i2c_adapter”:
i2c->adap.algo_data = i2c;
i2c->adap.dev.parent = &pdev->dev;
-->ret = s3c24xx_i2c_init(i2c); 硬件相关初始化。
-->request_irq(res->start, s3c24xx_i2c_irq, IRQF_DISABLED, pdev->name, i2c); 注册中断
-->i2c_add_adapter(&i2c->adap); 注册I2C适配器
-->i2c_register_adapter(adapter);
总线设备驱动”模型:
一,I2C总线驱动程序:插槽 (分析:linux-2.6.22.6\drivers\i2c\busses\i2c-s3c2410.c) 1,分配结构:i2c_adapter:
int s3c24xx_i2c_probe(struct platform_device *pdev) -->struct s3c24xx_i2c *i2c = &s3c24xx_i2c;
2,设置结构i2c_adapter:核心是设置“i2c_algorithm算法结构”。
a,如何收发起始信号、数据、响应等.
b,i2c_adapter结构中有i2c_algorithm算法结构。
struct s3c24xx_i2c *i2c = &s3c24xx_i2c;
-->.algo = &s3c24xx_i2c_algorithm, 其中的核心就是算法
①,算法结构中“.master_xfer”是核心。 int s3c24xx_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg *msgs, int num) -->s3c24xx_i2c_doxfer(i2c, msgs, num); “doxfer”执行传输。
②,执行传输: int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c, struct i2c_msg *msgs, int num) -->s3c24xx_i2c_enable_irq(i2c); 使能中断 -->s3c24xx_i2c_message_start(i2c, msgs); 起动传输,会产生各种中断。 -->wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5); 等待事件完成。
③,起动传输:设置寄存器 寄存器: S3C2410_IICCON :I2C控制寄存器 S3C2410_IICSTAT :I2C状态寄存器 S3C2410_IICADD : S3C2410_IICDS :I2C DS寄存器 S3C2440_IICLC : void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c, struct i2c_msg *msg) -->iiccon = readl(i2c->regs + S3C2410_IICCON); -->writel(stat, i2c->regs + S3C2410_IICSTAT); -->writeb(addr, i2c->regs + S3C2410_IICDS);
3,注册“i2c_adapter”:i2c_add_adapter() int s3c24xx_i2c_probe(struct platform_device *pdev) -->i2c_add_adapter(&i2c->adap); -->i2c_register_adapter(adapter);
int i2c_register_adapter(struct i2c_adapter *adap) -->device_register(&adap->dev);
二,(分析:linux-2.6.22.6\drivers\i2c\chips\eeprom.c)
i2c_add_driver
i2c_register_driver
driver->driver.bus = &i2c_bus_type;
driver_register(&driver->driver);
list_for_each_entry(adapter, &adapters, list) { driver->attach_adapter(adapter); i2c_probe(adapter, &addr_data, eeprom_detect); i2c_probe_address // 发出S信号,发出设备地址(来自addr_data) i2c_smbus_xfer i2c_smbus_xfer_emulated i2c_transfer adap->algo->master_xfer // s3c24xx_i2c_xfer int __init eeprom_init(void) -->i2c_add_driver(&eeprom_driver);
-->i2c_register_driver(THIS_MODULE, driver);
裸板程序中发现设备: 先发一个“STATT”信号,再发出7bit的设备地址,若设备存在,则在第9个时钟里,此存在的设备(从机)会把“SDA”信号线拉低作为ACK回应,这样主机就知道有相对应地址的设备存在。
int i2c_register_driver(struct module *owner, struct i2c_driver *driver) -->driver->driver.bus = &i2c_bus_type; 总线是“i2c_bus_type”。 -->driver_register(&driver->driver); 注册一个“i2c_driver”结构体。 -->list_for_each_entry(adapter, &adapters, list) {driver->attach_adapter(adapter);} 对"adapters"链表里的每一个成员,调用"i2c_driver"结构里面的attach_adapter()。
此实例代码中的"i2c_driver"结构体是“eeprom_driver”,它其中的“attach_adapter()”如下:
int eeprom_attach_adapter(struct i2c_adapter *adapter)
-->i2c_probe(adapter, &addr_data, eeprom_detect); 其中参2就是设备地址信息。
-->i2c_probe_address(adapter,forces[kind][i + 1],kind, found_proc); 发出start信号,发出设备地址。 -->i2c_smbus_xfer(adapter, addr, 0, 0, 0, I2C_SMBUS_QUICK, NULL); i2c传输 -->adapter->algo->smbus_xfer(adapter,addr,flags,read_write,command,size,data); 调用“i2c_adapter” 结构里面的成员“i2c_algorithm”结构中的“算法函数”。要是没有这个 “smbus_xfer()”,就使用:此代码中是用下面的代码。 -->i2c_smbus_xfer_emulated(adapter,addr,flags,read_write,command,size,data); -->i2c_transfer(adapter, msg, num); -->adap->algo->master_xfer(adap,msgs,num);
调用“i2c_adapter” 结构里面的成员“i2c_algorithm”结构中的“算法函数-smbus_xfer()”。 对于“i2c_s3c2410.c” 中,这个“smbus_xfer()”函数即是:
USB总线会自动识别新接入的USB设备。但I2C总线不能。需要: 1,发出START信号 2,发出设备地址。 才能知道是否有此设备存在。 从“i2c_adapter”结构的“adapter”链表取出“i2c总线”中的一个一个驱动程序(称为“适配器”),使用里面的“.smbus_xfer”函数发“start”信号、发设备地址(在I2C设备驱动的i2c_driver 结构中,成员“.id”就是表示支持哪些I2C设备)、
包含 此驱动框架涉及的文件 文件的用途