红外遥控器驱动流程

代码目录:common\drivers\amlogic\input\new_remote\

模块图如下

红外遥控器驱动流程_第1张图片

IR介绍

红外遥控器驱动流程_第2张图片

我的遥控器是NEC  MODE

先说一下整个过程

在init.amlogic.rc文件中,启动remotecfg服务,调用bin文件把remote.conf解析并且把相关信息写入到驱动中

service remotecfg /system/bin/remotecfg /system/etc/remote.conf
on property:svc.bootanim=running
    start remotecfg

bin文件源码目录external/remoteconf , 功能为:解析remote.conf文件,通过ioctl 把解析的内容填入驱动中的全局的变量struct remote *gp_remote的成员中。

make help

make meson8b_defconfig

查看.config文件,看出驱动是直接编译进内核中。

driver结构体,suspend休眠,resume唤醒,dt_match 在dtd文件中有相关内容(寄存器基址,以及pin脚)

static struct platform_driver remote_driver = {
	.probe = remote_probe,
	.remove = remote_remove,
	.suspend = remote_suspend,
	.resume = remote_resume,
	.driver = {
		.name = "meson-remote",
		.of_match_table = remote_dt_match,	
	},
};

看probe函数,做的事情大概为:设置引脚,设置ir寄存器,ir寄存器初始化,构建remote以及初始化,注册input以及初始化,申请中断,创建设备文件以及提供fops等等 具体如下

static int remote_probe(struct platform_device *pdev)
{
    //do something
    aml_set_reg32_mask(P_AO_RTI_PIN_MUX_REG, (1 << 0));//设置某GPIO脚功能为IR
    ret = of_property_read_u32(pdev->dev.of_node,"ao_baseaddr",&ao_baseaddr);//读取寄存器基址
    ao_baseaddr = P_AO_MF_IR_DEC_LDR_ACTIVE; //最终使用的是这个
    //do something
    remote = kzalloc(sizeof(struct remote), GFP_KERNEL); //分配空间构建remote
    input_dev = input_allocate_device();  //注册input
    /*remote初始化的一些操作*/
    tasklet_enable(&tasklet);//使能tasklet
    tasklet.data = (unsigned long)remote;
    setup_timer(&remote->timer, remote_release_timer_sr, 0);//设置timer的响应函数
    /*input 的一些初始化*/
    if (hardware_init(pdev)) {  //设置寄存器,申请irq
	goto err3;
    }
    register_remote_dev(gp_remote);//注册设备,创建设备相关文件,已经相应的fops(给前面的remotecfg写配置用)
}

其他的不做介绍,这里重点看一下这个函数

int remote_hw_reprot_key(struct remote *remote_data)
{
	static int last_scan_code;
	int i;
	get_cur_scancode(remote_data);    //读scancode,包含customcode和scancode
	get_cur_scanstatus(remote_data);  //读寄存器,如果是repeat的ir信号,那么最低位为1
	if(remote_data->status)// repeat enable & come in S timer is open
		return 0;
	if (remote_data->cur_lsbkeycode) {	//key first press 第一次按压
		if(remote_data->ig_custom_enable)//一直成立
		{   //根据custom_code,看有没有相应的config,如果有,保存对应的index到map_num中
			for(i = 0; i < ARRAY_SIZE(remote_data->custom_code);){
				if (remote_data->custom_code[i] != get_cur_key_domian[remote_data->work_mode](remote_data,CUSTOMDOMAIN)) {
					//return -1;
					i++;
				}
				else{
					remote_data->map_num = i;
					break;
				}
				if(i == ARRAY_SIZE(remote_data->custom_code))
				{
					input_dbg("Wrong custom code is 0x%08x\n", remote_data->cur_lsbkeycode);
					return -1;
				}
			}
		}
		repeat_count = 0;
		if (remote_data->timer.expires > jiffies) {//上一次按压的释放没有上报,在这里先上报上一个按键的释放
			remote_data->remote_send_key(remote_data->input,remote_data->repeat_release_code , 0,0);
		}

		remote_data->remote_send_key(remote_data->input,get_cur_key_domian[remote_data->work_mode](remote_data,KEYDOMIAN), 1,0);//上报本次按键按压
		remote_data->repeat_release_code = get_cur_key_domian[remote_data->work_mode](remote_data,KEYDOMIAN);//保存本次scancode
		remote_data->enable_repeat_falg = 1;//释放按键的时候会清0,用于重复按键的上报
		if((remote_data->work_mode > DECODEMODE_NEC) && remote_data->enable_repeat_falg){//因为我使用的就是NEC所以这一段不看
			if (remote_data->repeat_enable) {
				remote_data->repeat_timer.data = (unsigned long)remote_data;
				//here repeat  delay is time interval from the first frame end to first repeat end.
				remote_data->repeat_tick = jiffies;
				mod_timer(&remote_data->repeat_timer,  jiffies + msecs_to_jiffies(remote_data->repeat_delay));
				remote_data->status = TIMER;
			}
			else{
				setup_timer(&remote_data->rel_timer, remote_rel_timer_sr, 0);
				mod_timer(&remote_data->timer,  jiffies );
				remote_data->rel_timer.data = (unsigned long)remote_data;
				mod_timer(&remote_data->rel_timer,  jiffies + msecs_to_jiffies(remote_data->relt_delay));
				remote_data->status = TIMER;
			}
		}
		for (i = 0; i < ARRAY_SIZE(remote_data->key_repeat_map[remote_data->map_num]); i++){//解析寄存器的值,查之前配置remote时的map表,如果map有对应的那么want_repeat_enable=1,否是=0
			if (remote_data->key_repeat_map[remote_data->map_num][i] == remote_data->repeat_release_code) {
				remote_data->want_repeat_enable = 1;
			}
			else{
				remote_data->want_repeat_enable = 0;
			}
		}

		if (remote_data->repeat_enable && remote_data->want_repeat_enable) {
			remote_data->repeat_tick = jiffies + msecs_to_jiffies(remote_data->input->rep[REP_DELAY]);//设置input的上报间隔,如果上一次设置的时间没到,那么就不上报
		}
		if(remote_data->repeat_enable)
			mod_timer(&remote_data->timer, jiffies + msecs_to_jiffies(remote_data->release_delay+remote_data->repeat_delay));//设置释放的时间,即这次接收到ir中断和下次ir中断之间的时间如果大于这个设置的值,那么就报释放;
		else
			mod_timer(&remote_data->timer, jiffies + msecs_to_jiffies(remote_data->release_delay+remote_data->repeat_delay));
	}
	else if((remote_data->frame_status & REPEARTFLAG) && remote_data->enable_repeat_falg){	//repeate key 重复按键的上报,CPU寄存器中的重复按键的status和enable_repeat_falg要同时成立才会上报,碰到的鼠标功能问题移动一点点就停下来,第一要看这个status是否是REPEARTFLAG(如果是这个问题,要看寄存器的设置),第二再看enable_repeat_falg(如果是这个问题那么看timer的设置)
#ifdef CONFIG_AML_HDMI_TX
       extern int rc_long_press_pwr_key;
		if((remote_data->repeat_release_code == 0x1a) && (!cec_repeat)) {
            rc_long_press_pwr_key = 1;
		    cec_repeat = 10;
		    mdelay(20);
		}
		if(remote_data->repeat_release_code == 0x1a)
 		    cec_repeat--;

#endif
		//printk("mark...\n");
		if (remote_data->repeat_enable) {//始终成立
			repeat_count++;
			if (remote_data->repeat_tick < jiffies) {//如果input的时间还没到,那么就不上报,到了就上报
				if(repeat_count > 1)
					remote_data->remote_send_key(remote_data->input,remote_data->repeat_release_code, 2,0);//注意连续键,上报的status是2
				remote_data->repeat_tick  + = msecs_to_jiffies(remote_data->input->rep[REP_PERIOD]); //重设input的时间,我觉得有点不合理,想改成下面的,如果连续按压的话,按照这样的设置,这个input的间隔时间会越来越小,下面这种就是每次都固定
				//remote_data->repeat_tick = jiffies + msecs_to_jiffies(remote_data->input->rep[REP_PERIOD]);
			}
		} else {//不走这里
			if (remote_data->timer.expires > jiffies) {
				mod_timer(&remote_data->timer, jiffies + msecs_to_jiffies(remote_data->release_delay));
			}
			return -1;
		}
		mod_timer(&remote_data->timer, jiffies + msecs_to_jiffies(remote_data->release_delay) + msecs_to_jiffies(110));//重新设置释放时间间隔
	}
	last_scan_code = remote_data->cur_lsbkeycode;
	remote_data->cur_keycode = last_scan_code;//保存这次的scancode
	remote_data->cur_lsbkeycode = 0;   
	remote_data->timer.data = (unsigned long)remote_data;
	return 0;	
}
remote_send_key这个函数就不说了,反正鼠标功能其实都是报按键,在上层转换成鼠标功能,具体看framework中input相关代码

相关的一些东西熟悉了一下

  1. 读dtd文件的信息

    of_property_read_u32

  2. tasklet

    tasklet_enable(&tasklet);

        tasklet.data = (unsigned long)remote;

        DECLARE_TASKLET_DISABLED(tasklet, remote_tasklet, 0);

        tasklet_schedule(&tasklet);

    3.   irq

        request_irq(NEC_REMOTE_IRQ_NO, remote_interrupt, IRQF_SHARED, "keypad", (void *)remote_interrupt);

    4.    input

        input_dev = input_allocate_device();

input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL) | BIT_MASK(EV_ABS);

input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);

input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y) | BIT_MASK(REL_WHEEL);

input_dev->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) | BIT_MASK(BTN_EXTRA);

for (i = 0; i < KEY_MAX; i++) {

set_bit(i, input_dev->keybit);

}

input_dev->name = "aml_keypad";

input_dev->phys = "keypad/input0";

input_dev->dev.parent = &pdev->dev;

input_dev->id.bustype = BUS_ISA;

input_dev->id.vendor = 0x0001;

input_dev->id.product = 0x0001;

input_dev->id.version = 0x0100;

remote->repeat_enable = 0;

input_dev->rep[REP_DELAY] = 0xffffffff;

input_dev->rep[REP_PERIOD] = 0xffffffff;

input_dev->keycodesize = sizeof(unsigned short);

input_dev->keycodemax = 0x1ff;

ret = input_register_device(remote->input);

    5.    timer

        setup_timer(&remote->timer, remote_release_timer_sr, 0);

        remote_data->timer.data = (unsigned long)remote_data;

        mod_timer(&remote_data->timer, jiffies + msecs_to_jiffies(remote_data->release_delay+remote_data->repeat_peroid));

    6.    DEVICE_ATTR

        static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, remote_enable_show, remote_enable_store);

        ret = device_create_file(&pdev->dev, &dev_attr_enable);

    7.    字符设备驱动

        ret = register_chrdev(0, remote->config_name, &remote_fops);

        remote->config_major = ret;

        remote->config_class = class_create(THIS_MODULE, remote->config_name);

        remote->config_dev = device_create(remote->config_class, NULL, MKDEV(remote->config_major, 0), NULL, remote->config_name);

    8.    static long remote_config_ioctl(struct file *filp, unsigned int cmd, unsigned long args)

        ret = copy_from_user(&val, argp, sizeof(unsigned long));

    9.    mutex_lock(&remote_file_mutex);

        mutex_unlock(&remote_file_mutex);


qq: 741790551


最终解决鼠标问题

红外遥控器驱动流程_第3张图片


查看数据手册:

红外信号的一个数据帧

红外遥控器驱动流程_第4张图片

关于repeat 

红外遥控器驱动流程_第5张图片

红外遥控器驱动流程_第6张图片

即在release的时间内如上图的108ms,又接收到了一个leading active和一个leading idle和一个562.5us的pulse就表示是repeat key,会设置状态寄存器的最低位为1;关于repeat的设置只有一个设置idle的时间的设置,如下

红外遥控器驱动流程_第7张图片

示波器抓波形,看按一次的idle时间和一直按着的idle时间







你可能感兴趣的:(linux,driver,ir-remote)