G-sensor & TP --- MID 驱动

来源:互联网 发布:生驹里奈 知乎 编辑:程序博客网 时间:2024/04/27 17:33

          决定自己去分析下我工作中会经常涉及到的几个驱动,暂定的有TP(义隆2127),G-SENSOR(bma150),CAMERA(GC0308),CODEC(es8328)这几个驱动吧,因为现在的LCD不需要驱动,而USB-wifi方面,并没有去公开源代码,采用的是直接加载KO文件,所以在USB设备驱动方面,只能更多的参考usb-Skeleton.c这个标准文件。

第一节我们分析的是TP的驱动,TP驱动,在接口方面很简单,使用I2C来传递命令和数据,其中主要涉及到的内容有I2C设备,输入子系统,中断以及下半部机制,工作队列等内容吧。

从头开始分析代码:一切驱动的入口module_init(),在驱动的最后位置找了下,没看到module_init()函数,倒是看到这个late_initcall(elan_ktf2k_ts_init)和module_exit(elan_ktf2k_ts_exit)相对应,原来late_initcall(elan_ktf2k_ts_init)就是这个驱动的入口。有关late_initcall()和module_init()函数的区别可以参考此文章:http://blog.csdn.net/cstk502/article/details/6579231

我们接着看elan_ktf2k_ts_init()内部的主要内容:

首先定义了一个i2c_board_info结构体,以及i2c_adapter和i2c_client指针。紧接着就是对i2c_board_info结构体和i2c_adapter指针进行填充,这两个结构体的主要功能基本是为了收集资源,client = i2c_new_device(adapter, &info);这个函数利用i2c_board_info结构体和i2c_adapter指针生成了一个新的i2c_client,并且将这个i2c_clint注册到了i2c_device链表中。

err =i2c_add_driver(&ektf2k_ts_driver);这个函数的作用就是将ektf2k_ts_driver注册到i2c_driver链表中,在每次注册完i2c_device或者i2c_driver后,都会有个匹配过程,主要是通过名字匹配,如果匹配成功,则会调用i2c_driver的probe函数。接下来我们去看看ektf2k_ts_driver的probe函数elan_ktf2k_ts_probe。

elan_ktf2k_ts_probe函数开头有两个非常重要的参数定义:

struct elan_ktf2k_i2c_platform_data *pdata;主要是为了接受i2c_clint中i2c_board_info传来的资源,structelan_ktf2k_ts_data *ts;这个是为了综合整个TP驱动中使用的所有资源,包括中断,输入子系统,工作队列等,应该说,整个驱动最难的地方在于如何设计这个结构体。

开始会进行对adapter有效性的测试,ts的内存分配等。主要的工作在下面:

ts->elan_wq =create_singlethread_workqueue("elan_wq"); 创建一个工作队列

INIT_WORK(&ts->work, elan_ktf2k_ts_work_func); 设置ts中的任务,这个任务相当重要elan_ktf2k_ts_work_func

ts->client = client;

i2c_set_clientdata(client, ts);
将ts和i2c_clint进行相互绑定

fw_err = elan_ktf2k_ts_setup(client);
读取hello_packet,检查通信是否正常

ts->input_dev = input_allocate_device()
创建input_device

紧接着会对input_device进行一系列的设置,有关input_device的原理,可以参见这篇文章:

http://blog.csdn.net/wealoong/article/details/7580916

err =input_register_device(ts->input_dev); 设置之后自然是注册

现在仅仅最后一步,也是最重要的一步,注册中断,因为整个TP的数据传输都是采用中断方式实现的。

elan_ktf2k_ts_register_interrupt(ts->client); 这是整个中断注册函数的封装。

现在我们仔细对elan_ktf2k_ts_register_interrupt(ts->client);这个中断注册函数进行分析。

err = request_irq(_sui_irq_num,elan_ktf2k_ts_irq_handler,0/*IRQF_TRIGGER_FALLING*/, client->name, ts); 中断注册函数

中断处理函数elan_ktf2k_ts_irq_handler()中主要的内容为:

queue_work(ts->elan_wq,&ts->work); 将任务添加到队列

有关工作队列的使用,可以参考这篇文章:http://www.cnblogs.com/wwang/archive/2010/10/27/1862202.html

我们继续跟踪下工作队列中任务函数:elan_ktf2k_ts_work_func()

elan_ktf2k_ts_recv_data(ts->client,buf); 利用i2c接受数据

elan_ktf2k_ts_report_data(ts->client,buf); 数据分析,然后上报到输入子系统

其中有关数据分析,主要是和通信协议有关。

整个驱动中,最重要的是elan_ktf2k_ts_data这个结构体,这个结构体中,最重要的几个元素,i2c_clint,input_device,workqueue_struct,work_struct等,产生中断,中断处理函数将任务放到工作队列,任务是通过i2c接受数据,分析数据,上报到输入子系统

通过分析,发现惠鼎,敦泰的驱动在流程上都是一样的,主要区别有两点,一是ts_data结构体,二是在通信数据分析不一样。


        G-sensor驱动和TP驱动基本是一样的,因为他们都是通过i2c来传输命令和数据,另外都将数据解析后,上报到输入子系统中。整个驱动的流程和TP驱动是一样的,我还是讲整个驱动流程分析一下。

驱动入口函数:late_initcall(BMA250_init);

进入BMA250_init()函数,开头非常明显定义了三个重要变量

struct i2c_board_info info;

struct i2c_adapter *adapter;

struct i2c_client *client;

紧接着就是info和adapter收集资源,注册i2c_clint,函数为client = i2c_new_device(adapter, &info);接下来是注册i2c_driver,函数为i2c_add_driver(&bma250_driver);根据匹配过程,probe函数被调用,进入到bma250_probe()函数。

bma250_probe()函数开头定义了一个重要的指针,struct bma250_data *data;看到这个,我们会觉得和TP的驱动如此相似,有一点不一样的话,bma250_data结构体中有一个regulator指针,应该可以通过编程控制电源供应。有关regulator的详细资料,可以参考:

http://lhsblog01.blog.163.com/blog/static/1020045192010221104120218/

regulator =regulator_get(&client->dev, buf);

err = regulator_set_voltage(regulator,2800000, 2800000);

err = regulator_enable(regulator);

设置电源管理

接着会检查adapter,分配structbma250_data的空间:

i2c_check_functionality(client->adapter, I2C_FUNC_I2C)

data = kzalloc(sizeof(struct bma250_data), GFP_KERNEL);

然后读取芯片id同时检查i2c能否正常通信

tempvalue= i2c_smbus_read_byte_data(client, BMA250_CHIP_ID_REG);

对structbma250_data结构体进行设置和初始化工作

i2c_set_clientdata(client,data);

data->bma250_client = client;

mutex_init(&data->value_mutex);

mutex_init(&data->mode_mutex);

mutex_init(&data->enable_mutex);

bma250_set_bandwidth(client,BMA250_BW_SET);

bma250_set_range(client,BMA250_RANGE_SET);

INIT_WORK(&data->irq_work,bma250_irq_work_func);

data->IRQ = client->irq;

然后会去注册中断

err= request_irq(data->IRQ, bma250_irq_handler, IRQF_TRIGGER_RISING,

"bma250",data);

接下来我们会发现一个很有趣的代码:

INIT_DELAYED_WORK(&data->work,bma250_work_func);

atomic_set(&data->delay,BMA250_MAX_DELAY);

atomic_set(&data->enable, 0);

这里会去初始化一个延时任务,设置延时时间。

原来在structbma250_data结构体中,有一个work_struct,就是中断处理下半部的任务,同时有一个delayed_work是用来定时上报G-sensor的xyz坐标的,我去查看了bma150中的代码,发现bma150中并没有采用中断的方式,而是直接采用延时任务,应该是bma250添加了中断处理。

还剩下的事情就是输入子系统的申请,绑定和注册了

dev= input_allocate_device();

input_set_drvdata(dev,data);

data->input= dev;

err= input_register_device(dev);

除了在内核中注册中断子系统以外,还要将它映射到用户空间,供用户空间使用,下面这个函数会实现:

err =sysfs_create_group(&data->input->dev.kobj,&bma250_attribute_group);

有关sysfs的更多知识可以参考:

http://blog.csdn.net/suwenqiang2011/article/details/8613818

关于G-sensor的处理流程,我们到中断处理函数bma250_irq_handler()以及延时任务函数bma250_work_func()中去看看就知道了

bma250_irq_handler()的内容仅仅有一个函数

schedule_work(&data->irq_work);

为什么这里和TP驱动中的中断处理函数内容:queue_work(ts->elan_wq,&ts->work);有所不一样呢,原来在TP驱动中,申请了一个工作队列,而G-sensor驱动使用的是默认工作队列,在每个cpu核上,都有一个默认的工作队列。

进入中断下半部,bma250_irq_work_func()主要就是接受数据,解析,然后上报,和TP的流程是一样的。

另外延时任务函数bma250_work_func()的内容也很简单,时间到了就读取G-sensor的当前值,然后上报:

bma250_read_accel_xyz(bma250->bma250_client,&acc);

input_report_abs(bma250->input,ABS_X, acc.x);

input_report_abs(bma250->input,ABS_Y, acc.y);

input_report_abs(bma250->input,ABS_Z, acc.z);

input_sync(bma250->input);

重新调度延时任务:

schedule_delayed_work(&bma250->work,delay);

整个G-sensor驱动的分析就完成了,主要使用到了i2c设备驱动,中断下半部-工作队列,延时任务,中断,输入子系统等知识。


from:http://software.intel.com/zh-cn/blogs/2013/03/19/mid-g-sensor/?utm_campaign=CSDN&utm_source=intel.csdn.net&utm_medium=Link&utm_content=others-%20G-sensor

原创粉丝点击