代码目录:common\drivers\amlogic\input\new_remote\
模块图如下
IR介绍
我的遥控器是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相关代码
相关的一些东西熟悉了一下
读dtd文件的信息
of_property_read_u32
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
最终解决鼠标问题
查看数据手册:
红外信号的一个数据帧
关于repeat
即在release的时间内如上图的108ms,又接收到了一个leading active和一个leading idle和一个562.5us的pulse就表示是repeat key,会设置状态寄存器的最低位为1;关于repeat的设置只有一个设置idle的时间的设置,如下
示波器抓波形,看按一次的idle时间和一直按着的idle时间