I2C子系统详解4——I2C设备驱动层代码分析

以下内容源于朱有鹏嵌入式课程的学习与整理,如有侵权请告知删除。

一、前言

由I2C总线设备的驱动框架可知,I2C总线设备的驱动框架涉及的文件如下:

(1)I2C设备驱动层相关的文件
x210开发板的电容触摸屏gslX680采用I2C接口,因此以这个I2C设备为例进行分析。

其对应的驱动源代码文件是x210_kernel\drivers\input\touchscreen\gslX680.c文件

它由触摸屏IC原厂工程师提供,代码中涉及到很多触摸屏专业方面的知识,但无需理会。

gslX680.c文件进行了I2C设备驱动的注册,相应的I2C设备注册在mach-x210.c文件

(2)I2C核心层相关的文件
I2C核心层相关的文件是i2c-core.c文件,它由内核开发者提供,和具体的硬件操作无关。

(3)I2C总线驱动层相关的文件
algos目录:存放的是I2C通信的算法(主要是时序等内容)。

busses目录:存放的是各种已经编写好的、将来要向i2c核心层注册的适配器文件。

我们主要分析drivers\i2c\busses\i2c-s3c2410.c文件。


下面将对I2C设备驱动层相关文件进行分析,主要分析gslX680.c文件

二、I2C设备驱动层代码分析

同平台总线类似,I2C总线分为I2C总线设备层(struct i2c_client)和 I2C总线驱动层(struct i2c_driver)。

gslX680.c文件提供的是I2C总线驱动的注册,相应的I2C总线设备注册在mach-x210.c文件中。

1、I2C总线驱动的注册

static int __init gsl_ts_init(void)
{int ret;print_info("==gsl_ts_init==\n");ret = i2c_add_driver(&gsl_ts_driver);//注册I2C总线驱动print_info("ret=%d\n",ret);return ret;
}
module_init(gsl_ts_init);
static struct i2c_driver gsl_ts_driver = {.driver = {.name = GSLX680_I2C_NAME,//驱动的名字,并不用于设备和设备驱动的匹配.owner = THIS_MODULE,},
#ifndef CONFIG_HAS_EARLYSUSPEND.suspend	= gsl_ts_suspend,.resume	= gsl_ts_resume,
#endif.probe		= gsl_ts_probe,//被I2C总线层的probe函数调用.remove		= __devexit_p(gsl_ts_remove),.id_table	= gsl_ts_id,//用来匹配本驱动支持的设备
};

2、gsl_ts_probe函数分析

函数内容及分析如下:

static int __devinit gsl_ts_probe(struct i2c_client *client,const struct i2c_device_id *id)
{struct gsl_ts *ts;      //  设备驱动层封装的一个全局结构体int rc;print_info("GSLX680 Enter %s\n", __func__);if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {dev_err(&client->dev, "I2C functionality not supported\n");return -ENODEV;}ts = kzalloc(sizeof(*ts), GFP_KERNEL);  //  给 gsl_ts类型的指针申请分配内存if (!ts)return -ENOMEM;print_info("==kzalloc success=\n");ts->client = client;                    //  通过gsl_ts->client指针去指向传进来的i2c次设备i2c_clienti2c_set_clientdata(client, ts);         //  将gsl_ts作为i2c次设备的私有数据区中的设备驱动私有数据ts->device_id = id->driver_data;rc = gslX680_ts_init(client, ts);       //   初始化操作if (rc < 0) {dev_err(&client->dev, "GSLX680 init failed\n");goto error_mutex_destroy;}gsl_client = client;       //  通过一个全局的i2c_client指针gsl_client去指向传进来的i2c次设备i2c_clientgslX680_init();            //  gslX680 触摸屏相关的gpio初始化操作init_chip(ts->client);     //  gslX680触摸屏芯片相关的初始化操作check_mem_data(ts->client);rc=  request_irq(client->irq, gsl_ts_irq, IRQF_TRIGGER_RISING, client->name, ts); //  申请中断,这个中断是接在SoC的一个外部中断引脚上的if (rc < 0) {                                                                     // 当发生中断的时候表示有数据可以进行读取了,那么就会通知print_info( "gsl_probe: request irq failed\n");                               //  I2C主机去去读数据goto error_req_irq_fail;}/* create debug attribute *///rc = device_create_file(&ts->input->dev, &dev_attr_debug_enable);#ifdef CONFIG_HAS_EARLYSUSPENDts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;//ts->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB + 1;ts->early_suspend.suspend = gsl_ts_early_suspend;ts->early_suspend.resume = gsl_ts_late_resume;register_early_suspend(&ts->early_suspend);
#endif#ifdef GSL_MONITORprint_info( "gsl_ts_probe () : queue gsl_monitor_workqueue\n");INIT_DELAYED_WORK(&gsl_monitor_work, gsl_monitor_worker);gsl_monitor_workqueue = create_singlethread_workqueue("gsl_monitor_workqueue");queue_delayed_work(gsl_monitor_workqueue, &gsl_monitor_work, 1000);
#endifprint_info("[GSLX680] End %s\n", __func__);return 0;//exit_set_irq_mode:
error_req_irq_fail:free_irq(ts->irq, ts);error_mutex_destroy:input_free_device(ts->input);kfree(ts);return rc;
}

(1)gslX680_ts_init函数分析

static int gslX680_ts_init(struct i2c_client *client, struct gsl_ts *ts)
{struct input_dev *input_device;            //   定义一个 input_dev 结构体指针int rc = 0;printk("[GSLX680] Enter %s\n", __func__);ts->dd = &devices[ts->device_id];if (ts->device_id == 0) {ts->dd->data_size = MAX_FINGERS * ts->dd->touch_bytes + ts->dd->touch_meta_data;ts->dd->touch_index = 0;}ts->touch_data = kzalloc(ts->dd->data_size, GFP_KERNEL);if (!ts->touch_data) {pr_err("%s: Unable to allocate memory\n", __func__);return -ENOMEM;}input_device = input_allocate_device();    //  给input_device指针申请分配内存if (!input_device) {rc = -ENOMEM;goto error_alloc_dev;}ts->input = input_device;                  //  通过gsl_ts->input指针去指向input输入设备input_device->name = GSLX680_I2C_NAME;     //  设置input设备的名字input_device->id.bustype = BUS_I2C;        //  设置input设备的总线类型input_device->dev.parent = &client->dev;   //  设置input设备的父设备:   /sys/devices/platform/s3c2410-i2cn/i2c-%d/%d-%04x//  但是通过后面的分析可知,最终不是这个父设备input_set_drvdata(input_device, ts);       //  将gsl_ts结构体作为input设备的私有数据区中的设备驱动数据//   以下是对input_dev 输入设备的一些设置  设置input设备可以上报的事件类型set_bit(EV_ABS, input_device->evbit);set_bit(BTN_TOUCH, input_device->keybit);set_bit(EV_ABS, input_device->evbit);set_bit(EV_KEY, input_device->evbit);input_set_abs_params(input_device, ABS_X, 0, SCREEN_MAX_X, 0, 0);input_set_abs_params(input_device, ABS_Y, 0, SCREEN_MAX_Y, 0, 0);input_set_abs_params(input_device, ABS_PRESSURE, 0, 1, 0, 0);
#ifdef HAVE_TOUCH_KEYinput_device->evbit[0] = BIT_MASK(EV_KEY);//input_device->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);for (i = 0; i < MAX_KEY_NUM; i++)set_bit(key_array[i], input_device->keybit);
#endifclient->irq = IRQ_PORT;               //  触摸屏使用到的中断号ts->irq = client->irq;                //ts->wq = create_singlethread_workqueue("kworkqueue_ts");if (!ts->wq) {dev_err(&client->dev, "Could not create workqueue\n");goto error_wq_create;}flush_workqueue(ts->wq);INIT_WORK(&ts->work, gslX680_ts_worker);   //  初始化工作队列  当发生中断的时候在在中断处理函数中就会调用这个工作队列//  作为中断的下半部rc = input_register_device(input_device);  //   注册input设备if (rc)goto error_unreg_device;return 0;error_unreg_device:destroy_workqueue(ts->wq);
error_wq_create:input_free_device(input_device);
error_alloc_dev:kfree(ts->touch_data);return rc;
}

因为在中断中需要做的事情很多,所以在这里采用了中断上下半部的方式来处理,上半部就是probe函数中的申请中断时绑定的函数gsl_ts_irq。在这个函数中做了一些最必要做的事情,然后开启下半部,在下半部中继续执行未处理完的事情gslX680_ts_worker。

需要注意的是这里的中断指的是触摸屏方的中断信号,其思想是这样的:当有人按下电容触摸屏的时候,在触摸屏IC就会将产生的模拟量转化为数字量,当转换完成之后,由于I2C协议本身的限制,所有的通信周期都是由主机方发起,从机只能被动的相应。I2c控制器这边如何知道数据已经可以读取了呢?这就得通过触摸屏接口这边引出一个中断信号引脚来告知I2C主机控制器读取数据,然后I2C主机就会发起一次通信来读取数据。

所以由此可以推知,在gslX680_ts_worker(中断下半部)函数中需要做两件事: 读取触摸屏数据、向input核心层上报数据。

gslX680_ts_workergsl_ts_read(I2C总线设备驱动层提供的函数)i2c_master_recv (I2C子系统核心层提供的函数)i2c_transfer(I2C子系统核心层提供的函数)adap->algo->master_xfer(adap, msgs, num)(I2C子系统总线驱动层提供的函数)
gsl_ts_write(I2C总线设备驱动层提供的函数)i2c_master_send(I2C子系统核心层提供的函数)i2c_transfer(I2C子系统核心层提供的函数)adap->algo->master_xfer(adap, msgs, num)(I2C子系统总线驱动层提供的函数)

3、工作流程总结

当触摸屏有人按下并且在触摸屏IC中已经完成了AD转换之后就会产生一个中断信号给I2C控制器,就会触发I2C设备驱动层的中断函数。在设备驱动层的中断函数中会调用核心层提供的读写函数来读取触摸屏的数据,然后核心层最终调用的是I2C总线驱动层(适配器)中注册的通信算法函数来发起一个起始信号来开启一个接收数据的通信周期,之后的事情就都交给I2C总线驱动层的中断函数来读取数据了,当数据读取完成之后就会将数据存放在缓冲区中。所以我们最终在设备驱动层的中断函数中将读取到的数据进行一些处理,然后上报给input核心层。

4、i2c_client的来源

mach-x210.c文件,smdkc110_machine_init 函数调用 i2c_register_board_info函数,如下所示:

static void __init smdkc110_machine_init(void)
{//省略部分内容             //i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0));i2c_register_board_info(1, i2c_devs1, ARRAY_SIZE(i2c_devs1));i2c_register_board_info(2, i2c_devs2, ARRAY_SIZE(i2c_devs2));//省略部分内容
}
其中i2c_devs*的定义如下。
/* I2C0 */
static struct i2c_board_info i2c_devs0[] __initdata = {
#ifdef CONFIG_SND_SOC_WM8580{I2C_BOARD_INFO("wm8580", 0x1b),},
#endif
};/* I2C1 */
static struct i2c_board_info i2c_devs1[] __initdata = {
#ifdef CONFIG_VIDEO_TV20{I2C_BOARD_INFO("s5p_ddc", (0x74>>1)),},
#endif
#ifdef CONFIG_TOUCHSCREEN_GSLX680{I2C_BOARD_INFO("gslX680", 0x40),},
#endif
};/* I2C2 */
static struct i2c_board_info i2c_devs2[] __initdata = {{
#if defined(CONFIG_SMDKC110_REV03) || defined(CONFIG_SMDKV210_REV02)/* The address is 0xCC used since SRAD = 0 */I2C_BOARD_INFO("max8998", (0xCC >> 1)),.platform_data = &max8998_pdata,
#else/* The address is 0xCC used since SRAD = 0 */I2C_BOARD_INFO("max8698", (0xCC >> 1)),.platform_data = &max8698_pdata,
#endif},
};
struct i2c_board_info {char		type[I2C_NAME_SIZE];//设备名字unsigned short	flags;//属性unsigned short	addr;//设备从地址void		*platform_data;struct dev_archdata	*archdata;
#ifdef CONFIG_OFstruct device_node *of_node;
#endifint		irq;//设备使用的IRQ号,对应cpu的EINT
};

(1)实现原理分析

内核维护一个链表 __i2c_board_list,这个链表上链接的是I2C总线上挂接的所有硬件设备的信息结构体。即这个链表维护的是一个struct i2c_board_info结构体链表。真正的struct i2c_client在别的地方由__i2c_board_list链表中的各个节点内容来另外构建生成。

int __init i2c_register_board_info(int busnum,struct i2c_board_info const *info, unsigned len)
{int status;down_write(&__i2c_board_lock);/* dynamic bus numbers will be assigned after the last static one */if (busnum >= __i2c_first_dynamic_bus_num)__i2c_first_dynamic_bus_num = busnum + 1;for (status = 0; len; len--, info++) {struct i2c_devinfo	*devinfo;devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);if (!devinfo) {pr_debug("i2c-core: can't register boardinfo!\n");status = -ENOMEM;break;}devinfo->busnum = busnum;devinfo->board_info = *info;list_add_tail(&devinfo->list, &__i2c_board_list);//这里}up_write(&__i2c_board_lock);return status;
}

(2)函数调用层次

|……i2c_add_adapter/i2c_add_numbered_adapter
|…………i2c_register_adapter
|………………i2c_scan_static_board_info
|……………………i2c_new_device
|…………………………device_register

(3)总结

I2C总线的i2c_client的提供是内核通过i2c_add_adapter/i2c_add_numbered_adapter接口调用时自动生成的,生成的原料是mach-x210.c中的:

i2c_register_board_info(1, i2c_devs1, ARRAY_SIZE(i2c_devs1));

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/461127.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

golang java rpc_golang两种调用rpc的方法

本文实例讲述了golang两种调用rpc的方法。分享给大家供大家参考&#xff0c;具体如下&#xff1a;golang的rpc有两种方法进行调用&#xff0c;一种是rpc例子中给的&#xff1a;package mainimport ("net/rpc""net/http""log""net"&quo…

9、C语言 —— 指针的用处

为什么80%的码农都做不了架构师&#xff1f;>>> 1、用函数实现两个数的交换 ‍‍在没用函数之前&#xff0c;可以这样实现‍‍#include <stdio.h>int main() {int a 3;int b 7;int c;printf("交换前&#xff0c;a%d&#xff0c;b%d\n", a, b); …

内核中的竞争状态和互斥(简述)

以下内容源于朱有鹏《物联网大讲堂》课程的学习&#xff0c;如有侵权&#xff0c;请告知删除。 一、概念 &#xff08;1&#xff09;竞争状态&#xff08;简称竟态&#xff09;&#xff1b; &#xff08;2&#xff09;临界段&#xff08;某一段代码&#xff0c;该代码有可能…

MenuetOS

MenuetOS由芬兰人Ville Turjanmaa开发&#xff0c;是一个操作系统&#xff0c;用于和IBM PC兼容的电脑。 它由汇编语言写成&#xff0c;可以存入一只1.44MB的软盘中。 Menuet OS 的32位版本Menuet32使用GNU通用公共许可证发放&#xff0c;但64位版本Menuet64使用自己的协议发放…

php是一种,PHP是一种什么型的语言:()

案例分析一&#xff1a;假定CPU的主频是500MHz。硬盘采用DMA方式进行数据传送&#xff0c;其数据传输率为4MB/s, 每次DMA传输的数据量为8KB, 要求没有任何数据传输被错过。如果CPU在DMA初始化设置和启动硬盘操作等方面用了1000个时钟周期&#xff0c;并且在DMA传送完成后的中断…

java动态代理二cglib

2019独角兽企业重金招聘Python工程师标准>>> java动态代理 转载于:https://my.oschina.net/u/1430510/blog/290215

spring心得6--自动装配知识点讲解及案例分析

1.自动装配&#xff1a; spring3.2以上版本有四种自动装配类型&#xff1a; 1&#xff09;.byName:寻找和属性名相同的bean,若找不到&#xff0c;则装不上。 2&#xff09;.byType:寻找和属性类型相同的bean,找不到,装不上,找到多个抛异常。 3&#xff09;.constructor:按照参数…

中断的上下半部

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c;如有侵权请告知删除。 前言 因为输入类设备的输入都是异步事件&#xff0c;因此一般使用中断来处理和响应。 中断处理程序处于中断上下文中&#xff0c;不能和用户空间数据交互&#xff08;不能使用copy_to(from)_usr函数…

arrayPointer

1,分别使用指针加减 int wages[2] {100000000,20000000}; int *pw wages or int *pw &wages[0] 表示指针指向数组的首地址; pw表示地址,*pw表示取值,new分配的动态数组时 指针名称当数组名称使用eg pw[0],pw[2]分别表示指向数组wages的2个数组的元素值; 我们已知pw表示此…

php抓取动态数据,php+ajax实现无刷新动态加载数据技术

我们浏览有些网页的时候&#xff0c;当拉动浏览器的滚动条时到页底时&#xff0c;页面会继续自动加载更多内容供用户浏览。这种技术我暂且称它为滚屏加载技术。我们发现很多网站用到这种技术&#xff0c;必应图片搜索、新浪微博、QQ空间等将该技术应用得淋漓尽致。滚屏加载技术…

图片播放器小项目(详解)

以下内容源于朱有鹏《物联网大讲堂》课程的学习整理&#xff0c;如有侵权&#xff0c;请告知删除。一、开始动手写代码 1、Makefile介绍 &#xff08;1&#xff09;这是一个通用的项目管理的Makefile体系&#xff0c;自己写的&#xff08;有子文件夹组织的&#xff09;项目可以…

基于linux-2.6.32.2的servfox移植

说明&#xff1a;这篇文章是本人在做基于web客户端的远程监控系统课题期间&#xff0c;在移植servfox应用服务程序费了很大周折&#xff0c;所以写下的。 只是介绍了基于arm的servfox移植【因为其他部分的移植没有遇到过错误】 前言&#xff1a; 如何移植基于linux的USB摄像头驱…

Telnet远程访问思科交换机、路由器

一、实验目的Telnet远程访问思科交换机、路由器二、实验拓扑三、实验步骤1、PC1远程管理S11&#xff09;配置交换机的管理IPS1(config)#int vlan 1S1(config-if)#ip add 192.168.1.100 255.255.255.0S1(config-if)#no shu2&#xff09;开启S1的telnet远程管理服务S1(config)#li…

php redis 队列,Redis 实现队列

## Redis 实现队列Redis 实现队列场景说明&#xff1a;用于处理比较耗时的请求&#xff0c;例如批量发送邮件&#xff0c;如果直接在网页触发执行发送&#xff0c;程序会出现超时高并发场景&#xff0c;当某个时刻请求瞬间增加时&#xff0c;可以把请求写入到队列&#xff0c;后…

offsetTop测试见解

offsetTop一开始以为只要容器内div的top定位为负就行&#xff0c;结果今天刚做了个demo&#xff0c;发现好像不是那么回事&#xff0c;于是经过各种测试&#xff0c;得出了一些结论&#xff0c;结论看demo <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" &qu…

[置顶]       cocos2d-x 手游源码站

尊重开发者的劳动成果&#xff0c;转载的时候请务必注明出处&#xff1a;http://blog.csdn.net/haomengzhu/article/details/37829061 1、魔幻方块 链接&#xff1a;魔幻方块源码关键词&#xff1a;魔幻方块源码 源代码 Cocos2d-x2.0 游戏源码 益智 休闲 游戏 游戏类型&#xf…

include “ “ 与 include < > 的区别

#include <>格式&#xff1a;引用标准库头文件&#xff0c;编译器从标准库目录开始搜索。 #incluce " "格式&#xff1a;引用非标准库的头文件&#xff0c;编译器从用户的工作目录开始搜索&#xff0c;如果找不到&#xff0c;再到标准位置寻找。 预处理器发现…

php 字符串的比较大小,php怎么比较两个字符串的大小

比较字符串是任何编程语言的字符串处理功能中重要的特性之一。在PHP中除了可以使用比较运算符号(、)加以比较外&#xff0c;还提供了一系列的比较函数&#xff0c;使PHP可以进行更复杂的字符串比较。如strcmp()、strcasecmp()和strnatcmp()等函数。1、按字节顺序进行字符串比较…

Android SDK开发包国内下载地址

原帖地址&#xff1a;http://www.cnblogs.com/bjzhanghao/archive/2012/11/14/2769409.html 不知道是因为最近kaihui还是怎么的&#xff0c;打开android sdk官方网站特别的慢&#xff0c;想下载最新版本的platform几乎变成不可能完成的任务&#xff0c;不知道为什么Google不像…

SharePoint 2013 Workflow - Advanced Workflow Debugging with Fiddler

来自&#xff1a;Andrew Connell [MVP SharePoint] | 时间&#xff1a;2012-07-18 19:26:30 原文链接&#xff1a; http://www.andrewconnell.com/blog/archive/2012/07/18/sharepoint-2013-workflow-advanced-workflow-debugging-with-fiddler.aspx In previous posts Iv…