new style I2C设备驱动分析

本文介绍Linux内核中新的I2C设备驱动编写方法,包括定义i2c_driver、模块初始化、注册i2c设备信息及实现probe函数等内容,并详细分析了i2c总线模型和文件操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近看了韦东山驱动视频的i2c部分,总结出相关知识,分享给大家            

在内核中有两种方式的i2c设备驱动的编写方法,一种legacy方式,一种是newstyle方式。韦东山视频和宋宝华书籍里讲解的都是legacy方式,但是在新版本内核中,legacy方式的i2c设备驱动已经编译不过去了,因为几个主要的内核函数都已经不存在了,即内核不再支持legacy方式的i2c设备驱动。下面讲解下newstyle方式的i2c设备驱动。

<>定义并填充i2c_driver:
复制代码
  1. staticconst struct i2c_device_id at24c08b_id[] = { 
  2.     {"at24c08b", 0 }, //该i2c_driver所支持的i2c_client
  3.     {} 
  4. }; 
  5. MODULE_DEVICE_TABLE(i2c,at24c08b_id); 
  6. /*定义并填充i2c_driver:
  7. *probe设备探测函数,i2c_add_driver()时会被调用 
  8. *remove设备卸载函数; 
  9. */
  10. staticstruct i2c_driver at24c08b_driver = { 
  11.     .driver= { 
  12.         .name= "at24c08b", 
  13.        .owner = THIS_MODULE, 
  14.     },
  15.     .probe= at24c08b_probe, 
  16.         .remove= __devexit_p(at24c08b_remove), 
  17.         .id_table= at24c08b_id, 
  18. };



<>模块初始化函数
复制代码
  1. staticint __init at24c08b_init(void) 
  2. {  
  3.     returni2c_add_driver(&at24c08b_driver); 
  4. }


分析i2c_add_driver():
复制代码
  1.     i2c_register_driver()
  2.         driver->driver.bus= &i2c_bus_type;//设置i2c_driver的总线类型
  3.         driver_register()//这个函数结束后就会调用probe()函数                                   
  4.           i2c_for_each_dev(driver,__process_new_driver);//对每一个存在的i2c_adapter,调用__process_new_driver()函数                                    
  5.               i2c_do_add_adapter()
  6.                 i2c_detect(adap,driver);
  7.                     //我们的i2c_driver没设置address_list和detect()函数,所以到这里就返回了。
  8.                     address_list= driver->address_list;
  9.                     if(!driver->detect || !address_list)
  10.                         return0;

分析driver_register()
复制代码
  1.     driver_find()//i2c_driver是否已经被注册
  2.     bus_add_driver()//将i2c_driver挂接到i2c总线i2c_bus_type上
  3.         driver_attach()
  4.             //对i2c总线上的每一个i2c设备i2c_client都会调用__driver_attach,这里的dev即i2c_client,drv即i2c_driver
  5.             bus_for_each_dev(drv->bus,NULL, drv, __driver_attach);
  6.                 driver_match_device(drv,dev)
  7.                     //调用i2c总线i2c_bus_type的match函数
  8.                     returndrv->bus->match ? drv->bus->match(dev, drv) : 1;
  9.                         i2c_device_match()
  10.                             //若i2c_client的名字和i2c_device_id的中名字相同,则匹配成功,才会调用后面的probe()
  11.                             i2c_match_id(driver->id_table,client)
  12.                 driver_probe_device()
  13.                     really_probe()
  14.                         //调用i2c总线i2c_bus_type的probe函数
  15.                         dev->bus->probe(dev);
  16.                             i2c_device_probe()
  17.                                 //调用到i2c_driver的probe()函数
  18.                                 driver->probe(client, i2c_match_id(driver->id_table, client))     


i2c总线i2c_bus_type的定义如下:
复制代码
  1. structbus_type i2c_bus_type = {
  2.     .name        ="i2c",
  3.     .match        =i2c_device_match,
  4.     .probe        =i2c_device_probe,
  5.     .remove        =i2c_device_remove,
  6.     .shutdown    =i2c_device_shutdown,
  7.     ...
  8. };


<>注册i2c设备相关信息
对于newstyle方式,需要通过i2c_register_board_info()函数注册i2c_board_info,向内核提供i2c设备的相关信息。
arch/arm/mach-s3c2440/mach-mini2440.c添加如下代码:
复制代码
  1. /*I2C设备at24c08b的相关信息*/
  2. staticstruct i2c_board_info i2c_devices[] __initdata = { 
  3.     {I2C_BOARD_INFO("at24c08b", 0x50), }, //0x50是at24c08b的设备地址 
  4. };
  5. staticvoid __init mini2440_machine_init(void)
  6. {
  7.     …
  8.     i2c_register_board_info(0,i2c_devices,ARRAY_SIZE(i2c_devices));
  9. }


分析i2c_register_board_info()
复制代码
  1.     structi2c_devinfo    *devinfo;//定义一个i2c_devinfo
  2.         devinfo->board_info= *info;//保存i2c_board_info
  3.             //将i2c_devinfo挂在链表__i2c_board_list上
  4.             list_add_tail(&devinfo->list,&__i2c_board_list);


搜索__i2c_board_list可知:
复制代码
  1. i2c_add_numbered_adapter()//i2c-s3c2410.c中调用该函数来注册一个i2c_adapter
  2.     i2c_add_adapter()
  3.         i2c_register_adapter()
  4.             i2c_scan_static_board_info()
  5.                 list_for_each_entry(devinfo,&__i2c_board_list, list) 
  6.                     //利用i2c_adapter和i2c_board_info构造i2c_client
  7.                     if(devinfo->busnum == adapter->nr
  8.                     &&!i2c_new_device(adapter,&devinfo->board_info))
  9.                         structi2c_client *client;
  10.                         client->adapter= adap;//设置i2c_client的adapter
  11.                         client->addr= info->addr;//设置设备地址
  12.                         …//继续设置i2c_client
  13.                         device_register()//将i2c设备i2c_client挂接到i2c总线上


分析device_register()
复制代码
  1.     device_add()
  2.         bus_add_device()
  3.             //将设备挂接在总线上,对于i2c而言,即把i2c_client挂接到i2c_bus_type
  4.             klist_add_tail(&dev->p->knode_bus,&bus->p->klist_devices);  

ps:上述这一套分析适用于所有符合总线设备驱动模型的驱动,usb总线,平台总线,pci总线,i2c总线等


<>i2c_driverprobe()函数
正常的注册字符设备即可,即:
(1)分配设备号:alloc_chrdev_region()
(2)构造file_operations
(3)分配设置注册cdevcdev_init(&cdev,&file_operations); cdev_add()


<>file_operationsread()write()函数             
(1)read:
复制代码
  1. {
  2. /* 检查该i2c_adapter是否支持读字节的功能 */
  3. i2c_check_functionality(I2C_FUNC_SMBUS_READ_BYTE_DATA)
  4. i2c_smbus_read_byte_data()//从eeprom读一个字节的数据
  5. copy_to_user()//拷贝至用户空间
  6. }

不是所有的I2C或者SMBus适配器实现了I2C规范上的所有功能,因此当访问I2C适配器时,
并不能完全假定适配器提供了你所需的功能。需要有一种检测适配器是否提供
了所需功能的方法。
对于不断更新的I2C适配器功能常量列表,参考<linux/i2c.h>
I2C_FUNC_I2C    
无格式i2c-level命令
I2C_FUNC_10BIT_ADDR    
处理10-bit地址的扩展
I2C_FUNC_SMBUS_READ_BYTE 
处理SMBus read_byte命令
I2C_FUNC_SMBUS_WRITE_BYTE    
处理SMBus write_byte命令
I2C_FUNC_SMBUS_READ_BYTE_DATA    
处理SMBus read_byte_data命令
I2C_FUNC_SMBUS_WRITE_BYTE_DATA
处理SMBus write_byte_data命令
I2C_FUNC_SMBUS_READ_WORD_DATA
处理SMBus read_word_data命令
I2C_FUNC_SMBUS_WRITE_WORD_DATA
处理SMBus write_word_data命令
...


分析i2c_smbus_read_byte_data(I2C_SMBUS_BYTE_DATA)

复制代码
  1. i2c_smbus_xfer(I2C_SMBUS_BYTE_DATA)
  2.        i2c_smbus_xfer_emulated(I2C_SMBUS_BYTE_DATA)
  3.                msg[1].len = 1;
  4.                ...//设置读数据时的i2c_msg
  5.                i2c_transfer()
  6.                //最终调用到i2c-s3c2410.c中设置的i2c_adapter     //的master_xfer()函数
  7.                        adap->algo->master_xfer()


(2)write:
复制代码
  1. {
  2. /* 检查该i2c_adapter是否支持读字节的功能 */
  3. i2c_check_functionality()
  4. copy_from_user(); //获得用户空间的数据
  5. i2c_smbus_write_byte_data()//写数据到eeprom
  6. }

分析i2c_smbus_write_byte_data(I2C_SMBUS_BYTE_DATA)

复制代码
  1. i2c_smbus_xfer(I2C_SMBUS_BYTE_DATA)
  2. i2c_smbus_xfer_emulated(I2C_SMBUS_BYTE_DATA)
  3.          msg[1].len = 2;
  4.          ...//设置写数据时的i2c_msg
  5.          i2c_transfer()
  6.                 //最终调用到i2c-s3c2410.c中设置的i2c_adapter     //的master_xfer()函数
  7.                 adap->algo->master_xfer()

ps:附件中有legacy和new style的at24cxx的驱动代码和测试代码,适用于mini2440
### 回答1: 函数i2c_smbus_read_byte_data是一个用于读取I2C从设备上指定寄存器的数据的 \u51fd\u6570i2c_smbus_read_byte_data\u662f\u4e00\u4e2a\u7528\u4e8e\u8bfb\u53d6I2C\u4ece\u8bbe\u5907\u4e0a\u6307\u5b9a\u5bc4\u5b58\u5668\u7684\u6570\u636e\u7684\u51fd\u6570\u3002 I2C\u662f\u4e00\u79cd\u901a\u4fe1\u534f\u8bae\uff0c\u7528\u4e8e\u8bbe\u5907\u4e4b\u95f4\u7684\u6570\u636e\u4ea4\u6362\u3002\u51fd\u6570i2c_smbus_read_byte_data\u7528\u4e8e\u4ece\u8bbe\u5907\u4e0a\u7684\u6307\u5b9a\u5bc4\u5b58\u5668\u8bfb\u53d6\u4e00\u4e2a\u5b57\u8282\u6570\u636e\uff0c\u5e76\u8fd4\u56de\u8be5\u6570\u636e\u3002 \u4f7f\u7528\u8be5\u51fd\u6570\u524d\uff0c\u9700\u8981\u5148\u786e\u5b9aI2C\u8bbe\u5907\u7684\u5730\u5740\u548c\u5b9a\u5740\uff0c\u6307\u5b9a\u8981\u8bfb\u53d6\u7684\u5bc4\u5b58\u5668\u7684\u5730\u5740\u3002\u7136\u540e\uff0c\u4f7f\u7528i2c_smbus_read_byte_data\u51fd\u6570\u53ef\u4ee5\u8bfb\u53d6\u6307\u5b9a\u5bc4\u5b58\u5668\u4e0a\u6307\u5b9a\u5730\u5740\u7684\u6570\u636e\uff0c\u5e76\u8fd4\u56de\u8be5\u6570\u636e\u3002 \u603b\u4e4b\u8005\uff0c\u51fd\u6570i2c_smbus_read_byte_data\u662f\u4e00\u4e2a\u5f88\u5bb9\u6613\u4f7f\u7528\u7684\u51fd\u6570\uff0c\u4e3aI2C\u4ea4\u6362\u63d0\u4f9b\u4e86\u4e00\u4e2a\u7cbe\u5f69\u7684\u63a5\u53e3\u3002 ### 回答2: i2c_smbus_read_byte_data是Linux内核中一种I2C设备驱动中可用的函数,提供了从I2C从设备读取单个字节数据的功能。其中,i2c表示I2C总线,smbus是System Management Bus的缩写,代表了一种I2C总线上的智能交互协议。 在使用i2c_smbus_read_byte_data函数时,需要传入3个参数。第一个参数为指向I2C设备结构体的指针,第二个参数是从设备的寄存器地址,第三个参数是用于存储读取数据的8位整数指针。 当调用i2c_smbus_read_byte_data函数后,内核会向I2C从设备发送一个读取数据的命令,并指定从设备中的哪个寄存器地址中读取数据。从设备接收到这个命令后,会将数据通过I2C总线发送给主设备,内核驱动程序会自动读取这个数据并将其作为一个整数类型返回给调用者。若发生错误,会返回负值。 当使用i2c_smbus_read_byte_data函数时,需要注意以下几点: 1. 需要对I2C从设备的寄存器地址非常熟悉,否则读取到的数据将可能不是正确的数据。 2. 内核会自动将标准I2C通信协议转换成SMBus交互协议,需要保证SMBus协议与I2C从设备的通信是兼容的。 3. 在调用函数前,需要通过i2c_slave_addr函数将主设备和从设备进行绑定。 ### 回答3: i2c_smbus_read_byte_data是Linux内核中的一个函数,它用于从I2C设备中读取一个字节的数据。 I2C是一种串行通信协议,可以连接多个设备,使用两个线路进行数据传输。I2C设备可以是传感器、控制器等各种电子设备,这些设备都有一个独特的地址。 i2c_smbus_read_byte_data函数的作用是从指定的I2C设备中读取一个字节的数据。这个函数需要传入三个参数,分别是指向I2C设备的指针、设备的地址、以及需要读取的寄存器的地址。 函数执行流程如下: 1. 首先,函数会向设备发送一个“寄存器地址”命令,并等待设备的应答。 2. 然后,函数会从设备中读取一个字节的数据,并返回该数据。 如果操作成功,i2c_smbus_read_byte_data函数会返回一个字节的数据,否则会返回一个错误码。 总之,i2c_smbus_read_byte_data函数是Linux内核中用于读取I2C设备数据的常用函数,它可以从指定的I2C设备中读取一个字节的数据,有助于实现对各种电子设备的控制和数据读取。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值