new style I2C设备驱动…

来源:互联网 发布:网络教育文凭出国留学 编辑:程序博客网 时间:2024/05/24 23:15
本文来自:http://hi.baidu.com/fcni_cn/item/561f6f3f0c391abc124b146a                                                                     


最近看了韦东山驱动视频的i2c部分,总结出相关知识,分享给大家  .在内核中有两种方式的i2c设备驱动的编写方法,一种legacy方式,一种是newstyle方式。韦东山视频和宋宝华书籍里讲解的都是legacy方式,但是在新版本内核中,legacy方式的i2c设备驱动已经编译不过去了,因为几个主要的内核函数都已经不存在了,即内核不再支持legacy方式的i2c设备驱动。下面讲解下newstyle方式的i2c设备驱动。



<一>定义并填充i2c_driver:

  1. static const struct  i2c_device_id  at24c08b_id[] = { 
  2.     {"at24c08b",0 },         //该i2c_driver所支持的i2c_client
  3.     {} 
  4. }; 
  5. MODULE_DEVICE_TABLE(i2c,at24c08b_id); 
  6. static struct i2c_driver at24c08b_driver ={ 
  7.     .driver={ 
  8.         .name="at24c08b", 
  9.       .owner = THIS_MODULE, 
  10.     },
  11.     .probe=at24c08b_probe, 
  12.         .remove=__devexit_p(at24c08b_remove), 
  13.         .id_table= at24c08b_id
  14. };



<二>模块初始化函数
  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);    //检测i2c_check_functionality
  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,这里的devi2c_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函数i2c_device_match
  8.                     return drv->bus->match ?drv->bus->match(dev, drv) : 1;
  9.                         i2c_device_match(dev,drv)    //通过 dev,找到i2c_client
  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. struct   bus_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添加如下代码:
(在mach-smdkc110.c中的smdkc110_machine_init中调用i2c_board_info
  1. static struct  i2c_board_info i2c_devices[]  __initdata ={ 
  2.     {I2C_BOARD_INFO("at24c08b",0x50), },   //0x50是at24c08b的设备地址 
  3. };
  4. staticvoid __init mini2440_machine_init(void)
  5. {
  6.     
  7.     i2c_register_board_info(0,i2c_devices,ARRAY_SIZE(i2c_devices));
  8. }


分析i2c_register_board_info(): (i2c_board_info注册过程 i2c-boardinfo.c)
  1.     struct i2c_devinfo    *devinfo;    //定义一个i2c_devinfo
  2.         devinfo->board_info=*info;     //保存i2c_board_info
  3.             //将i2c_devinfo挂在链表__i2c_board_list上 存储所有i2c_board_info的全局链表
  4.             list_add_tail(&devinfo->list,&__i2c_board_list);


搜索__i2c_board_list可知:
  1. i2c_add_numbered_adapter()     //i2c-s3c2410.c中调用该函数(s3c24xx_i2c_probe)来注册一个i2c_adapter      i2c-core.c
  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_driver 的probe()函数
正常的注册字符设备即可,即:
(1)分配设备号: alloc_chrdev_region()
(2)构造file_operations
(3)分配设置注册cdev:cdev_init(&cdev,&file_operations);cdev_add()


<五>file_operations的read()和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适配器功能常量列表,参考
I2C_FUNC_I2C                                 无格式i2c-level命令
I2C_FUNC_10BIT_ADDR                    处理10-bit地址的扩展
I2C_FUNC_SMBUS_READ_BYTE         处理SMBusread_byte命令
I2C_FUNC_SMBUS_WRITE_BYTE        处理SMBus write_byte命令
I2C_FUNC_SMBUS_READ_BYTE_DATA    处理SMBusread_byte_data命令
I2C_FUNC_SMBUS_WRITE_BYTE_DATA   处理SMBuswrite_byte_data命令
I2C_FUNC_SMBUS_READ_WORD_DATA   处理SMBusread_word_data命令
I2C_FUNC_SMBUS_WRITE_WORD_DATA  处理SMBuswrite_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_check_functionality()
  3. copy_from_user(); //获得用户空间的数据
  4. i2c_smbus_write_byte_data()//写数据到eeprom
  5. }

分析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()


  • 下一篇:I2C设备驱动编写,structi2c_device_id,structi2c_driver,i2c_add_driver,i2c_register_board_info

原创粉丝点击