实例Accelerometer Sensors: bma2x2

1.简述

根据i2C驱动的开发流程去分析i2c从机设备bma2x2(加速度sensors)的驱动;以及分析其挂载的控制器(适配器)i2c-msm-v2的驱动。

2.I2C总线驱动i2c-msm-v2

2.1 定义总线驱动:platform_driver

static struct platform_driver i2c_msm_driver = {.probe  = i2c_msm_probe,.remove = i2c_msm_remove,.driver = {.name           = "i2c-msm-v2",.owner          = THIS_MODULE,.pm             = &i2c_msm_pm_ops,.of_match_table = i2c_msm_dt_match,},
};

2.2 设备树匹配方法

static struct of_device_id i2c_msm_dt_match[] = {{.compatible = "qcom,i2c-msm-v2",},{}
};

2.3 注册平台驱动(加载模块)

static int i2c_msm_init(void)
{return platform_driver_register(&i2c_msm_driver);
}
arch_initcall(i2c_msm_init);

2.4 总线驱动probe函数

当调用platform_driver_register()函数注册平台驱动结构体时,如果平台设备(i2c下的compatible)和平台驱动匹配成功后,会调用probe()函数,来初始化适配器硬件、添加i2c_adapter。实现模板如下。

static int xxx_i2c_probe(structplatform_device *pdev)
{......//从设备树展开的资源树的device_node中获取信息  if (of_property_read_u32(np, "zzz", &yyy)){xxx_i2c->speed_mode=xxx;xxx_i2c->hs_clock=xxx;......}/*初始化适配器信息 */  strlcpy(i2c_xxx->adap.name,"xxx-i2c",sizeof(i2c_xxx->adap.name));  i2c_xxx->adap.owner   = THIS_MODULE;  i2c_xxx->adap.algo    = &xxx_i2c_algorithm;  i2c_xxx->adap.retries= 2; ......   /* 获取资源信息(I/O、中断等),映射寄存器 */  res= platform_get_resource(pdev, IORESOURCE_MEM, 0);
i2c_xxx->ioarea= request_mem_region(res->start,
resource_size(res),  pdev->name);  i2c_xxx->regs= ioremap(res->start, resource_size(res));
....../*设置I2C核心需要的信息 */  i2c_xxx->adap.algo_data= i2c_xxx;  i2c_xxx->adap.dev.parent= &pdev->dev;  i2c_xxx->adap.dev.of_node = np ....../*申请中断 */  i2c_xxx->irq= ret = platform_get_irq(pdev, 0);  ret= request_irq(i2c_xxx->irq, s3c24xx_i2c_irq, 0,  dev_name(&pdev->dev), i2c_xxx);  ....../*注册I2C控制器 */  ret = i2c_add_adapter(&i2c_xxx->adap);
....../* 注册I2C适配器 */  ret= i2c_add_numbered_adapter(&i2c_xxx->adap);
....../* 设置驱动数据 */platform_set_drvdata(pdev, i2c);......
}

2.5 总线驱动remove函数

static int i2c_msm_remove(struct platform_device *pdev)
{struct i2c_msm_ctrl *ctrl = platform_get_drvdata(pdev);//确保注销总线驱动前数据发送完毕mutex_lock(&ctrl->xfer.mtx);ctrl->pwr_state = I2C_MSM_PM_SYS_SUSPENDED;pm_runtime_disable(ctrl->dev); //禁用pm管理相关操作i2c_msm_frmwrk_unreg(ctrl);     //注销adaptermutex_unlock(&ctrl->xfer.mtx);mutex_destroy(&ctrl->xfer.mtx);i2c_msm_dma_teardown(ctrl);i2c_msm_dbgfs_teardown(ctrl);i2c_msm_rsrcs_irq_teardown(ctrl);  //释放中断资源i2c_msm_rsrcs_clk_teardown(ctrl);  //释放时钟资源i2c_msm_rsrcs_mem_teardown(ctrl);  //释放IO内存i2x_msm_blk_free_cache(ctrl);       //释放cache
return 0;
}

2.6 algorithm通信方法

static const struct i2c_algorithm i2c_msm_frmwrk_algrtm = {.master_xfer     = i2c_msm_frmwrk_xfer, //通信方法.functionality = i2c_msm_frmwrk_func, //检测通信方法支持的功能或协议
};

2.7 xfer通信方法

master_xfer()函数实现总线上数据传输,与具体的适配器有关,其实现模板如下。

static int i2c_adapter_xxx_xfer(structi2c_adapter *adap, struct i2c_msg *msgs, int num)
{  ......  for (i = 0; i < num; i++) {  i2c_adapter_xxx_start();  /*产生开始位START*/ if (msgs[i]->flags & I2C_M_RD) {    /*若是读取消息*/  /*发送从机设备读地址*/
i2c_adapter_xxx_setaddr((msg->addr << 1) | 1);i2c_adapter_xxx_wait_ack();   /*获得从机设备的ACK*/  /*读取len长度的数据到buf中*/i2c_adapter_xxx_readbytes(msgs[i]->buf,msgs[i]->len);    } else {    //若是写消息i2c_adapter_xxx_setaddr(msg->addr << 1);  //发送从机设备写地址i2c_adapter_xxx_wait_ack();  /*获得从机设备的ACK*/i2c_adapter_xxx_writebytes(msgs[i]->buf, msgs[i]->len);//写}  }  i2c_adapter_xxx_stop();    /*产生停止位STOP*/
}

函数模板中的i2c_adapter_xxx_start()、i2c_adapter_xxx_setaddr()等函数用于完成适配器的底层硬件操作,与I2C适配器和CPU的具体硬件直接相关,需要由工程师根据芯片的datasheet来实现。

2.8 功能检测函数func

static u32 i2c_msm_frmwrk_func(struct i2c_adapter *adap)
{return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL &
~ I2C_FUNC_SMBUS_QUICK);
}

返回与自己选用的协议相关的宏(数值)。这些宏是在内核中定义好的,可直接用。

2.9 总线驱动模块初始化、卸载函数

static int i2c_msm_init(void)
{return platform_driver_register(&i2c_msm_driver);
}
arch_initcall(i2c_msm_init);static void i2c_msm_exit(void)
{platform_driver_unregister(&i2c_msm_driver);
}
module_exit(i2c_msm_exit);

3.I2C设备驱动bma2x2

3.1 定义设备驱动i2c_driver

static struct i2c_driver bma2x2_driver = {.driver = {.owner  = THIS_MODULE,.name   = SENSOR_NAME,.of_match_table = bma2x2_of_match,},.suspend    = bma2x2_suspend,.resume     = bma2x2_resume,.id_table   = bma2x2_id,.probe      = bma2x2_probe,.remove     = bma2x2_remove,.shutdown   = bma2x2_shutdown,
};

3.2 设备树匹配

static const struct of_device_id bma2x2_of_match[] = {{ .compatible = "vendor, SENSOR_NAME", },{ },
};

3.3 设备驱动probe函数

static int bma2x2_probe(struct i2c_client *client,const struct i2c_device_id *id)
{
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
{adap->algo->functionality(); //总线驱动部分if (client->dev.of_node) {//省略了:利用client->dev初始化bma2x2_data数据//从设备树中获取相关资源信息如gpio、中断(具体看dts文件)err = bma2x2_parse_dt(&client->dev, pdata);}} else {//若dts中不存在数据,获取平台数据pdata = client->dev.platform_data;}err = bma2x2_power_ctl(data, true);//电源管理控制if (bma2x2_soft_reset(client) < 0)//重启sensorsbma2x2_check_chip_id(client, data);/* read and check chip id */bma2x2_open_init(client, data);  //设置bandwidth、range(会在sysfs中留有接口)err = bma2x2_pinctrl_init(data);//根据dts获取的信息初始化pinctrl   /* check interrupt feature enable state */if ((pdata->use_int2 && (!BMA2x2_IS_INT2_ENABLED())) ||(!pdata->use_int2 && (!BMA2x2_IS_INT1_ENABLED()))) {......}//初始化GPIO为中断引脚if (pdata->use_int2) {data->int_flag = pdata->int2_flag;err = bma2x2_get_interrupt_gpio(data,pdata->gpio_int2);} else {data->int_flag = pdata->int1_flag;err = bma2x2_get_interrupt_gpio(data,pdata->gpio_int1);}//申请中断号err = request_irq(data->IRQ, bma2x2_irq_handler,data->int_flag, "bma2x2", data);//申请input devicedev = devm_input_allocate_device(&client->dev);//申请input interrupt devicedev_interrupt = devm_input_allocate_device(&client->dev);dev->name = SENSOR_NAME;dev->id.bustype = BUS_I2C;input_set_capability(dev, EV_ABS, ABS_MISC);input_set_abs_params(dev, ABS_X, ABSMIN, ABSMAX, 0, 0);input_set_abs_params(dev, ABS_Y, ABSMIN, ABSMAX, 0, 0);input_set_abs_params(dev, ABS_Z, ABSMIN, ABSMAX, 0, 0);input_set_drvdata(dev, data);err = input_register_device(dev);//注册input设备dev_interrupt->name = "bma_interrupt";dev_interrupt->id.bustype = BUS_I2C;input_set_capability(dev_interrupt, EV_REL,SLOW_NO_MOTION_INTERRUPT);......input_set_capability(dev_interrupt, EV_ABS,FLAT_INTERRUPT);input_set_drvdata(dev_interrupt, data);
//注册input interrupt设备err = input_register_device(dev_interrupt); data->dev_interrupt = dev_interrupt;data->input = dev;return 0;......  //error处理
}

3.4 设备驱动模块初始化、卸载函数

static int __init BMA2X2_init(void)

{pr_crit_once("NWY %s: test for debug.\n", __FUNCTION__);return i2c_add_driver(&bma2x2_driver);
}
module_init(BMA2X2_init);static void __exit BMA2X2_exit(void)
{i2c_del_driver(&bma2x2_driver);
}
module_exit(BMA2X2_exit);

4.初始化匹配流程分析

在内核中增加log,查看打印信息可知:
(1)平台总线接收到78b6000.i2c平台设备的注册后,调用总线驱动的probe()函数,初始化i2c控制器对象adapter;注册adapter到i2c总线,添加到其设备链表中;匹配关联总线驱动。
(2)注册设备驱动bma2x2。
(3)调用设备驱动bma2x2_probe()函数,在注册i2c_client前会初始化i2c_client->adap,因此将i2c_client和adapter关联起来,注册驱动到i2c总线时,匹配关联绑定i2c_client,此时i2c_client和i2c_driver关联起来。

5.小结

总线驱动platform_driver:msm_i2c_ driver通过platform_bus与设备树i2c-4节点进行匹配,完成适配器i2c_adapter的实现,具体是在总线驱动的probe()函数中通过平台设备信息初始化adapter,并注册adapter;
设备驱动i2c_driver:bna2x2_driver通过i2c_bus与设备树i2c-4节点下的子节点进行匹配,完成设备i2c_client的实现。注意,adapter和i2c_client都被放到i2c总线的设备链表中。四者联系的总流程:
(1)内核初始化platform_bus,具体是在driver_init()中,比任何xxx_initcall都要早;
(2)初始化i2c_bus(postcore_initcall,等级2);
(3)展开设备树成各个平台设备信息,具体是调用等级为3的arch_initcall中的customize_machine()函数,再调用各个平台的init_machine()函数实现;
(4)注册总线驱动xxx_i2c_driver(等级4的subsys_initcall或等级6的module_init),遍历platform_bus中设备链表的每个平台设备(dts展开得到),以找到与该驱动匹配的设备,若找到则调用总线驱动的probe()函数,即xxx_i2c_driver->probe(),在此通过找到的平台设备的信息初始化adapter设备的信息,然后调用了i2c_add_adapter()函数;
(5)在i2c_add_adapter()函数中,调用device_register()函数对adapter设备进行注册,添加到i2c总线的设备链表中进行管理,并匹配、关联、绑定对应总线驱动。此时i2c_adapter与总线驱动联系起来。
(6)其后遍历i2c_bus上的i2c_driver链表,都调用__process_new_adapter()函数,其又调用了i2c_new_device()函数,使得i2c_driver去探测并实例化注册i2c_client,在注册i2c_client前会初始化i2c_client->adap,因此将i2c_client和adapter关联起来;在注册i2c_client时会遍历i2c_bus上驱动链表,匹配、关联、绑定合适的i2c_driver,此时i2c_driver和i2c_client联系起来;若此时i2c_driver仍未注册,则等待其注册时由i2c_driver匹配i2c_client。

小结:所有的联系始于总线驱动的注册,关键在于i2c_add_adapter()函数,以实例化i2c_client为目的,先联系adapter,再联系i2c_driver。

I2C适配器驱动及设备驱动代码详解相关推荐

  1. 【genius_platform软件平台开发】第五十八讲:Linux系统之V4L2视频驱动-VIDIOC_REQBUFS向驱动申请帧缓冲代码详解

    VIDIOC_REQBUFS向驱动申请帧缓冲代码详解 1. 概述 2. 应用层 3. 内核驱动 3.1 vb2_ioctl_reqbufs函数 3.2 vb2_core_reqbufs函数 3.3 _ ...

  2. linux i2c核心,总线与设备驱动,Linux2.6.37 I2C驱动框架分析(一)

    最近工作中又使用到了I2C,所以借S3C2440开发板GT2440为硬件平台温习一遍I2C驱动体系. linux内核中IIC驱动的体系框架 linux内核中IIC部分驱动代码位于:/drivers/i ...

  3. 【Linux驱动开发】设备树详解(二)设备树语法详解

    ​ 活动地址:CSDN21天学习挑战赛 [Linux驱动开发]设备树详解(一)设备树基础介绍 [Linux驱动开发]设备树详解(二)设备树语法详解 [Linux驱动开发]设备树详解(三)设备树Kern ...

  4. 网络驱动开发样例snull详解(基于3.10.0)

    网络驱动开发样例snull详解(基于3.10.0) 本章素材为ldd3书中的网络驱动snull部分.由于现在内核的更新,导致其在最新的内核中无法编译该网络驱动,需要针对修改,顾为此文(内核3.10.0 ...

  5. 【驱动】linux设备驱动·字符设备驱动开发

    Preface 前面对linux设备驱动的相应知识点进行了总结,现在进入实践阶段! <linux设备驱动入门篇>:http://infohacker.blog.51cto.com/6751 ...

  6. 34.驱动--块设备驱动

    Linux块设备IO子系统(一) _驱动模型 - Abnor - 博客园 22.Linux-块设备驱动之框架详细分析(详解) - 诺谦 - 博客园 23.Linux-块设备驱动(详解) - 诺谦 - ...

  7. Linux驱动——platform设备驱动实验

    文章目录 1. 驱动的分离和分层 1.1 驱动的分离 1.3 驱动的分层 2. 实验程序编写 2.1 leddevice.c编写 2.2 leddriver.c编写 3. 测试 4. 总结 1. 驱动 ...

  8. 字符设备驱动、平台设备驱动、设备驱动模型、sysfs的比较和关联

    参考原文:https://www.kancloud.cn/yueqian_scut/emlinux/106829 对原文笔误地方做了修改.重新排版 目录 字符设备驱动.平台设备驱动.设备驱动模型.sy ...

  9. Linux驱动-字符设备驱动

    Linux驱动-字符设备驱动 前言 一.预备知识 1.file_operations结构体 2.地址映射 二.涉及的API函数 1.字符设备驱动 1.1.设备号 1.1.1.register_chrd ...

  10. 《验收测试驱动开发:ATDD实例详解》—第1章1.2节临时停车

    本节书摘来自异步社区<验收测试驱动开发:ATDD实例详解>一书中的第1章1.2节临时停车,作者[德]Markus Gärtner,更多章节内容可以访问云栖社区"异步社区" ...

最新文章

  1. MySQL数据库介绍、安装(服务端软件安装、客户端软件安装(图形化界面客户端和命令行客户端))
  2. 网站优化如何才能避免无效收录?
  3. Nginx 学习笔记(四) Nginx+keepalived
  4. C#中的扩展方法,Linq,IO和多线程的定义和实例
  5. Invalid nested tag br found, expected closing tag li 错误解决
  6. 第三季-第6课-静态函数库设计
  7. stm32死机问题的处理
  8. 测试老鸟常用的自动化测试工具有哪些?
  9. 软件备份(拷贝构造函数)
  10. 联合查询(多表查询)
  11. EBS之JTF_Grid 开发总结
  12. 涉密计算机设备保密管理系统,保密室设备——涉密计算机及移动存储介质保密管理系统(三合一)...
  13. UINO优锘:用悬疑舞台剧的方式打开3D开发工程师的一天
  14. 用批处理调用Rundll32添加打印机命令说明文件
  15. [BIM]BIM中IFD介绍
  16. IDEA中Resource Bundle ‘application‘
  17. office快捷键设置
  18. SLAM学习——李群与李代数
  19. redis 的6种过期策略
  20. Dart_Flutter【插件介绍+平台发布+视频】【180个网址导航】

热门文章

  1. 阿里云OSS对象存储服务上传失败问题之一
  2. 网络探测和诊断工具 - mtr
  3. 团体程序设计天梯赛-练习集(并查集)
  4. 基因序列分析(生物信息学论坛)
  5. linux remount命令详解,linux mount命令参数及用法详解
  6. 【Sys】不能打开要写入的文件:“X:\Y\Z.xx“单击[Abort]放弃安装,[Retry]重新尝试写入文件,或[Ignore]忽略这个文件。
  7. java毕业设计实验室主页系统源码+lw文档+mybatis+系统+mysql数据库+调试
  8. QUTOJ 1218: 核电站问题 递推
  9. 《图书馆笔记本防盗器》工程测试版发布!
  10. Android拨号器---熟练控件与布局的应用