G-sensor & TP --- MID 驱动

本文详细分析了TP和G-sensor驱动的实现细节,包括I2C接口、中断处理、工作队列、输入子系统等关键组件。TP驱动通过I2C接口与硬件通信,主要涉及I2C设备、输入子系统、中断、工作队列等内容;G-sensor驱动同样使用I2C接口,并将数据解析后上报至输入子系统。分析了两个驱动的入口函数、关键变量、资源收集、驱动注册、中断处理及工作队列的使用。对比了两者在数据处理和电源管理上的不同之处。

          决定自己去分析下我工作中会经常涉及到的几个驱动,暂定的有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.youkuaiyun.com/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.youkuaiyun.com/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.youkuaiyun.com/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=优快云&utm_source=intel.youkuaiyun.com&utm_medium=Link&utm_content=others-%20G-sensor

LOCAL S32 cover_reload(DS_MSG *msg) { S32 ret = ERROR; LENS_MASK_RELOAD_MSG lens_mask_msg = {0}; S32 cover_region_enabled = get_cover_enabled(); TP_COVER_ATTR_S tp_cover_attr_s = {0}; COVER_DEBUG("Cover reload"); lens_mask_msg.enabled = get_lens_mask_info_enabled(); #ifdef GPIO_LENS_MASK_ENABLED LENS_MASK_CONTROL_MSG lens_mask_control_msg = {0}; if (lens_mask_msg.enabled) { lens_mask_control_msg.mode = LENS_MASK_CONST_ON; } else { lens_mask_control_msg.mode = LENS_MASK_CONST_OFF; } CAP_SEND(LENS_MASK_CONTROL_MID, (U8 *)&lens_mask_control_msg, sizeof(LENS_MASK_CONTROL_MSG)); #endif /* 向 AMS 发送镜头遮蔽的消息 */ if (TRUE == ds_path_id_exist(msg->id, msg->num, LENS_MASK_INFO_PATH)) { CAP_SEND(COVER_LENS_MASK_RELOAD, (U8 *)&lens_mask_msg, sizeof(LENS_MASK_RELOAD_MSG)); #ifdef PRIVACY_LOG CAP_LOG(LOG_SAFETY, LOG_NOTYPE, “Cover reload to %d”, lens_mask_msg.enabled); #endif } if (ds_path_id_exist(msg->id, msg->num, IMAGE_SWITCH_PATH)) { cover_region_reload(); } if (FALSE == lens_mask_msg.enabled && FALSE == cover_region_enabled && FALSE == g_local_camera_disable) { /* 将开启 cover 变为关闭状态 */ if (NULL != g_cover_handler) { cover_handler_destroy(); CAP_SEND(COVER_EVENT_RELOAD, NULL, 0); } return SLP_ENONE; } if (NULL == g_cover_handler) { cover_start_effect(); CAP_SEND(COVER_EVENT_RELOAD, NULL, 0); return SLP_ENONE; } /* 由于 cover enabled 和 cover 区域是分为两个请求进行发送的,这里是为了避免载入两次 */ if (TRUE == ds_path_id_exist(msg->id, msg->num, COVER_PATH)) { return SLP_ENONE; } if (TRUE == ds_path_id_exist(msg->id, msg->num, COVER_REGION_INFO_LIST_PATH) #ifdef DUAL_CAM || TRUE == ds_path_id_exist(msg->id, msg->num, COVER_CHN2_REGION_INFO_LIST_PATH) #endif ) { COVER_REGION_INFO region_info = {0}; #ifdef DUAL_CAM COVER_REGION_INFO chn2_region_info = {0}; #endif if (0 == ds_read(COVER_REGION_INFO_PATH_PREFIX “1”, &region_info, sizeof(COVER_REGION_INFO)) #ifdef DUAL_CAM || 0 == ds_read(COVER_CHN2_REGION_INFO_PATH_PREFIX “1”, &chn2_region_info, sizeof(COVER_REGION_INFO)) #endif ) { if (FALSE == g_last_null_table) { COVER_ERROR(“cover region_info changed, table is null, write flash”); ds_save_usr_cfg(); g_last_null_table = TRUE; } } else { g_last_null_table = FALSE; } } if (TRUE == lens_mask_msg.enabled) { ret = init_lens_mask_cover_attr(&tp_cover_attr_s); } else { ret = init_tp_cover_attr_s(&tp_cover_attr_s); } // ret = init_lens_mask_cover_attr(&tp_cover_attr_s); if (OK == ret && OK == g_cover_handler->ops->cover_set_attr(&tp_cover_attr_s)) { CAP_SEND(COVER_EVENT_RELOAD, NULL, 0); return SLP_ENONE; } cover_handler_destroy(); CAP_SEND(COVER_EVENT_RELOAD, NULL, 0); return SLP_ESYSTEM; },另外typedef struct tp_cover_attr_s { U32 max_width; /* 主码流分辨率,宽 / U32 max_height; / 主码流分辨率,高 / U32 num; / 遮挡块个数,为0时即为无遮挡 / #ifdef PRIVACY_MASK_SUPPORT PRIVACY_MASK_REGION regions[PM_REGION_INFO_LIST_SIZE]; / 遮挡块位置、大小信息 / #else REGION_INFO_S regions[COVER_REGION_INFO_LIST_SIZE]; / 遮挡块位置、大小信息 / #ifdef DUAL_CAM U32 chn2_num; / 遮挡块个数,为0时即为无遮挡 / REGION_INFO_S chn2_regions[COVER_REGION_INFO_LIST_SIZE]; / 遮挡块位置、大小信息 */ #endif #endif }TP_COVER_ATTR_S;还有LOCAL S32 init_resolution(TP_COVER_ATTR_S *tp_cover_attr_s) { VIDEO_MAIN video_main = {0}; char resolution[VIDEO_RESOLUTION_LEN + 1] = {0}; char *resolution_split = NULL; /* 获取 main resolution */ ds_read(VIDEO_MAIN_PATH, &video_main, sizeof(VIDEO_MAIN)); strcpy(resolution, video_main.resolution); resolution_split = strstr(resolution, "*"); if (NULL == resolution_split) { COVER_ERROR("Resolution format wrong"); return ERROR; } *resolution_split = 0; tp_cover_attr_s->max_width = atoi(resolution); tp_cover_attr_s->max_height = atoi(resolution_split + 1); return OK; },LOCAL S32 init_lens_mask_cover_attr(TP_COVER_ATTR_S *tp_cover_attr_s) { if (OK != init_resolution(tp_cover_attr_s)) { return ERROR; } tp_cover_attr_s->num = 1; #ifdef QUADRILATERAL_MASK_SUPPORT tp_cover_attr_s->regions[0].pt1_x = 0; tp_cover_attr_s->regions[0].pt1_y = 0; tp_cover_attr_s->regions[0].pt2_x = 10000; tp_cover_attr_s->regions[0].pt2_y = 0; tp_cover_attr_s->regions[0].pt3_x = 10000; tp_cover_attr_s->regions[0].pt3_y = 10000; tp_cover_attr_s->regions[0].pt4_x = 0; tp_cover_attr_s->regions[0].pt4_y = 10000; #else tp_cover_attr_s->regions[0].x = 0; tp_cover_attr_s->regions[0].y = 0; tp_cover_attr_s->regions[0].w = RES_PRECISION; tp_cover_attr_s->regions[0].h = RES_PRECISION; #ifdef DUAL_CAM tp_cover_attr_s->chn2_num = 1; tp_cover_attr_s->chn2_regions[0].x = 0; tp_cover_attr_s->chn2_regions[0].y = 0; tp_cover_attr_s->chn2_regions[0].w = RES_PRECISION; tp_cover_attr_s->chn2_regions[0].h = RES_PRECISION; #endif #endif return OK; },这些代码涉及隐私模式还有加入若干个隐私块的处理,我想在摄像头关闭的时候使用这个隐私模式,在底层出流加入一个满的的隐私块。但是这里哪里区分了码流?我想要主码流遮,但是子码流不遮,模拟两个摄像头
10-17
LOCAL S32 set_cover_attr_s(COVER_HANDLER_T *cover_handler, TP_COVER_ATTR_S *tp_cover_attr_s) { if (NULL == cover_handler) { return ERROR; } if (OK != cover_handler->ops->cover_init(tp_cover_attr_s)) { COVER_ERROR("cover_init failed."); return ERROR; } if (OK != cover_handler->ops->cover_set_attr(tp_cover_attr_s)) { COVER_ERROR("cover_set_attr failed."); goto SET_COVER_ATTR_ERR; } if (OK != cover_handler->ops->cover_start(tp_cover_attr_s)) { COVER_ERROR("cover_start failed."); goto SET_COVER_ATTR_ERR; } return OK; SET_COVER_ATTR_ERR: return ERROR; }},而且LOCAL S32 cover_start(void) { IMAGE_SWITCH image_switch; COVER_REGION_INFO region_info = {0}; #ifdef DUAL_CAM COVER_REGION_INFO chn2_region_info = {0}; #endif #ifdef GPIO_LENS_MASK_ENABLED LENS_MASK_CONTROL_MSG lens_mask_control_msg = {0}; if (get_lens_mask_info_enabled()) { lens_mask_control_msg.mode = LENS_MASK_CONST_ON; } else { lens_mask_control_msg.mode = LENS_MASK_CONST_OFF; } CAP_SEND(LENS_MASK_CONTROL_MID, (U8 *)&lens_mask_control_msg, sizeof(LENS_MASK_CONTROL_MSG)); #endif if (NULL == g_cover_handler) { cover_start_effect(); } memset(&image_switch, 0, sizeof(IMAGE_SWITCH)); ds_read(IMAGE_SWITCH_PATH, &image_switch, sizeof(IMAGE_SWITCH)); origin_flip_type = image_switch.flip_type; if (0 == ds_read(COVER_REGION_INFO_PATH_PREFIX "1", &region_info, sizeof(COVER_REGION_INFO)) #ifdef DUAL_CAM || 0 == ds_read(COVER_CHN2_REGION_INFO_PATH_PREFIX "1", &chn2_region_info, sizeof(COVER_REGION_INFO)) #endif ) { g_last_null_table = TRUE; } else { g_last_null_table = FALSE; } return OK; }},都没有关于子码流的,chn2_num只是遮挡块的数字?
10-17
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值