linux驱动学习心得--以I2C做实例

一、概述


在设备驱动模型中,需要关心总线、设备和驱动这3个实体,
总线将设备和驱动绑定。
在系统每注册一个设备的时候,会寻找与之匹配的驱动;
同样,系统每注册一个驱动的时候,会寻找匹配的设备,而匹配由总线完成。
注册设备与驱动不分先后顺序。

对于没有总线得设备,需要一种虚拟的总线,称为platform总线,相应的设备称为platform_device,而驱动成为platform_driver。android把内部i2c、LCD等归纳到platform_device,便于统一管理。

二、驱动学习流程

以i2c为例 ,一般而言所说得驱动包含两部分
  1、bus_driver用于将设备挂载在i2总线上,代码在i2c\bus\i2c_qup.c中,使用得总线由主芯片支持决定。
  2、设备本身而言的驱动。代码在driver\misc\eeprom\microp_nutorn.c中。由外设或者从设备的芯片支持决定。

针对驱动得开发过程,针对性得学习时大致有以下流程
  1、定义 、注册platform_device 包含它的子类实例
  2、定义、注册 platform_driver  包含它的子类实例

注意点:device是一个类的概念,这一类得device共用一条总线。每个子类是挂载在这条总线下得不同得从设备。
              同样的,driver包含总线得driver以及针对每个从设备不同的driver

2.1定义注册platform_device 

1、定义platform_device 资源信息
代码在 kernel/arch/arm/mach-msm/device-8064.c
定义系统需求得resource
它描述了一个I2C总线设备的资源,设备驱动会根据flags获取相应的资源信息
A当为 IORESOURCE_MEM 表示描述的是内存类型的资源信息,此时 start end 表示起始内存得地址,同类设备可占据两个内存区域,这个一般是固定设计好得值。移植过程中尽量不变。
B当为IORESOURCE_IRQ表示描述了这个 I2C 设备的中断资源,此时 start      end表示设备只用得中断号起始值。
C当为IORESOURCE_IO 表示 描述的是GPIO资源信息。此时 start end 表示    设备只用得GPIO值。可在GPIO table中对比。

static struct resource resources_qup_i2c_gsbi3[] = {
{
.name	= "gsbi_qup_i2c_addr",
.start	= MSM_GSBI2_PHYS,
.end	= MSM_GSBI2_PHYS + 4 - 1,
.flags	= IORESOURCE_MEM,
}, 
{
.name	= "i2c_clk",
.start	= 25,
.end	= 25,
.flags	= IORESOURCE_IO,
},
}; 
有resource 信息,就可以定义 platform_device 

struct platform_device apq8064_device_qup_i2c_gsbi3= { 

 .name  = "qup_i2c",  //与driver注册是名称匹配

 .id  = MSM_GSBI3_QUP_I2C_BUS_ID, 

 .num_resources = ARRAY_SIZE(gsbi3_qup_i2c_resources), 

 .resource = gsbi3_qup_i2c_resources, 
}; 

2、注册

前面前定义好了 platform_device 结构体后 在板级支持文件 board-xxx.c文件中进行注册 代码在:
 kernel/arch/arm/mach-msm/boad-8064.c

 static struct platform_device *common_not_mpq_devices[] __initdata = 
{ 
&apq8064_device_qup_i2c_gsbi3, 
};
 //在初始化时会向系统中添加该类设备
 static void __init apq8064_common_init(void)
 {
 platform_add_devices(common_not_mpq_devices );
 }   

3、设备client device

前面仅仅定义并注册了设备的大类,也就是总线级别得device,
现在可以针对每个从设备添加对应的client的地址等信息
代码在: kernel/arch/arm/mach-msm/boad-8064.c
 定义每个slaver得地址等信息

 static struct i2c_board_info __initdata enterprise_nuvoton_microp[] = {      
            I2C_BOARD_INFO("microp", 0x15), //microp名称要与具体driver中相同,
            .irq=MSM_GPIO_TO_INT(7),
            .platform_data	= &nuvoton_microp_pdata,        
};
4、注册

最终在 初始化函数中被加载到board中得of_node中去。

static void __init register_i2c_devices(void)
{
i2c_register_board_info();
}

2.2定义注册platform_driver

 1、总线驱动程序

需要实现结构体platform_driver中得接口
I2C总线我们使用得高通平台,所以是QUP,代码在
i2c/bus/i2c_qup.c 

static struct platform_driver qup_i2c_driver = {
.probe		= qup_i2c_probe,
.remove		= __devexit_p(qup_i2c_remove),
.driver		= {
            .name	= "qup_i2c",   // 与platform_device 中name要一样
            .owner	= THIS_MODULE,
            .pm = &i2c_qup_dev_pm_ops,
            //.of_match_table = i2c_qup_dt_match,
},
};

如果使用device tree需要额外定义

static struct of_device_id i2c_qup_dt_match[] = {
{
.compatible = "qcom,i2c-qup",
},
{}
};
//其中 "qcom,i2c-qup" 与DTS中总线得属性得要一样

在初始时中初始化函数qup_i2c_init_driver被调用, 

static int __init qup_i2c_init_driver(void)
{
return platform_driver_register(&qup_i2c_driver);
}

platform_driver_register是注册驱动的函数在其中会扫描board上或者设备树中得总线信息并进行匹配,如果匹配成功 i2c_qup_probe()函数回被调用.
在使用platform_add_devices 添加来几个相同qup类得总线device,此时 i2c_qup_probe()函数就被调用几次
probe函数 是每个driver最重要配置函数。基础操作都在这里面。
 在定义得资源会在这里具体得实现。详细可参考代码
2、设备驱动

第1步把bus驱动做好来,现在是设备驱动,针对每个client从设备得驱动,代码:
driver/misc/eeprom/microp_nuvoton.c
添加具体驱动信息。

static struct i2c_driver microp_nuvoton_driver = {
.driver = {
.name   = TEGRA_MICROP_NAME,
.owner  = THIS_MODULE,
},
.probe      = microP_probe,
.remove     = microP_remove,
.suspend= microp_suspend,
.resume= microp_resume, 
.id_table   = microP_id,
}; 

在初始化时会被添加到驱动list中。所有的驱动有一个总得list,负责对其进行resume和suepend进行相关得操作

static int __init microP_init(void)
{ 
       return i2c_add_driver(µp_nuvoton_driver);
}

添加驱动时,name与 "microp"匹配。microP_probe会被调用。
在这里会针对中得资源进行申请。同时做一些针对本驱动得定时器、或者延时的work、使能关闭中断等操作。


三、注意事项

个人认为在参考移植得代码时除去probe中得相关初始化操作外着重主要以下几点
1、resume、suspend函数:对电源管理影响较大
2、定时器或者延时work等:确定延时运行得work,定时运行得work
3、中断处理函数:确认定义的中断触发条件,输入类驱动重点。
4、数据接受和发送得接口函数:大数据收发类驱动得重点。一般有指令模式和数据模式
5、其他一些类似使能、关闭某些功能得标志位得操作函数:影响自身或着其他驱动上层


你可能感兴趣的:(Linux设备驱动综合)