Linux内核学习实践之红外驱动分析—RCA38KHz软解码

来源:互联网 发布:知乎 法国街头 编辑:程序博客网 时间:2024/05/17 03:27

说明:本分析基于AM6C平台Linux3.0.8内核,其他内核版本仅供参考。

本文以Amlogic的红外驱动代码片段为例;对之前内核学习有一个很好的实践:

1.平台总线、设备及驱动部分;《Linux总线、设备与驱动》uvc设备分析,主要是总线驱动的match函数、设备和驱动的互相发现机制等。

2.中断处理部分,中断处理底半部;《Linux中断编程》中断处理底半部,复习中断的注册、使用,中断底半部的实现。

3.Linux内核input子系统注册输入设备及上报事件流程;Input子系统,复习Linux内核Input子系统的实现。

4.字符设备创建以及自动创建设备节点;《Linux设备节点创建》手动与自动创建设备节点,字符设备及其设备节点的动态创建。

一、驱动部分

1.模块加载、平台驱动注册

drivers/amlogic/input/remote/am_remote.c

[cpp] view plain copy
  1. module_init(remote_init);  
  2. DECLARE_TASKLET_DISABLED(tasklet, remote_tasklet, 0); //中断底半部  
  3. static int __devinit remote_init(void)  
  4. {  
  5.   printk(KERN_INFO "Remote Driver\n");  
  6.   return platform_driver_register(&remote_driver);  
  7. }  
  8. static struct platform_driver remote_driver = {  
  9.   .probe = remote_probe,                                                                                                                         
  10.   .remove = remote_remove,  
  11.   .suspend = remote_suspend,  
  12.   .resume = remote_resume,  
  13.   .driver = {  
  14.     .name = "meson-remote",  
  15.   },  
  16. };  

补充:平台设备部分,开机即被注册、导致平台驱动的探测函数执行

arch/arm/plat-meson/plat_dev_remote.c
[cpp] view plain copy
  1. struct platform_device meson_device_remote = {  
  2.   .name   = "meson-remote",  
  3.   .id     = -1,  
  4.   .dev    = {  
  5.     .platform_data = NULL,  
  6.   }  
  7. };  

2.平台驱动探测函数:

drivers/amlogic/input/remote/am_remote.c

[cpp] view plain copy
  1. static int remote_probe(struct platform_device *pdev)  
  2. {  
  3.   input_dev = input_allocate_device();  
  4.   input_dev->name = "aml_keypad"//设备名字“aml_keypad”  
  5.   ret = input_register_device(remote->input);//注册input设备  
  6.   hardware_init(pdev); //注册中断  
  7.   register_remote_dev(gp_remote); //操作配置文件的设备节点  
  8. }  
  9. //注册中断部分:  
  10. /* 
  11. static int hardware_init(struct platform_device *pdev) 
  12. { 
  13.   return request_irq(NEC_REMOTE_IRQ_NO, remote_interrupt, IRQF_SHARED, "keypad", (void *)remote_interrupt); 
  14. } 
  15. static irqreturn_t remote_interrupt(int irq, void *dev_id)                                                                                        
  16. { 
  17.   // disable keyboard interrupt and schedule for handling  
  18.   //  input_dbg("===trigger one  remoteads interupt \r\n"); 
  19.   tasklet_schedule(&tasklet);  //任务队列实现的中断底半部,也即remote_tasklet 
  20.   return IRQ_HANDLED; 
  21. } 
  22. */  

1)、补充:重新设定工作模式——中断的改变(这部分执行是当用户空间通过remotecfg命令重新配置后!!!),中断服务程序完成数据上报部分。

drivers/amlogic/input/remote/am_remote.c

[cpp] view plain copy
  1. static long remote_config_ioctl(struct file *filp, unsigned int cmd, unsigned long args){  
  2.   case REMOTE_IOC_SET_MODE:  
  3.     work_mode_config(remote->work_mode);  
  4.     break;  
  5. }  
  6. static int work_mode_config(unsigned int cur_mode){  
  7.   case REMOTE_WORK_MODE_RCA:  
  8.     free_irq(NEC_REMOTE_IRQ_NO, remote_interrupt); //释放之前注册中断  
  9.     gp_remote->fiq_handle_item.handle = remote_rca_bridge_isr;  
  10.     register_fiq_bridge_handle(&gp_remote->fiq_handle_item); //重新注册rca的中断    
  11.     request_fiq(NEC_REMOTE_IRQ_NO, &remote_fiq_interrupt);  
  12. }  
arch/arm/plat-meson/fiq-bridge.c
[cpp] view plain copy
  1. int  register_fiq_bridge_handle(bridge_item_t *c_item)  
  2. {  
  3.   bridge_item_t  *pitem;  
  4.   list_for_each_entry(pitem, &fiq_bridge_list, list)  
  5.   request_irq(BRIDGE_IRQ, &root_handle_isr, IRQF_SHARED , "fiq_bridge", &fiq_bridge_list)  
  6. }  
看看rca的中断处理函数

drivers/amlogic/input/remote/am_remote.c

[cpp] view plain copy
  1. static void remote_fiq_interrupt(void){  
  2.   remote_rca_reprot_key((unsigned long)gp_remote);  
  3. }  
drivers/amlogic/input/remote/sw_remote_rca38k.c
[cpp] view plain copy
  1. void remote_rca_reprot_key(unsigned long data){  
  2.   switch (remote_data->step) {  
  3.     case REMOTE_STATUS_WAIT:  
  4.       rca_software_mode_remote_wait(data);  
  5.       break;  
  6.     case REMOTE_STATUS_LEADER:  
  7.       rca_software_mode_remote_leader(data);  
  8.       break;  
  9.     case REMOTE_STATUS_DATA:  
  10.       rca_software_mode_remote_data(data);  
  11.       break;  
  12.     case REMOTE_STATUS_SYNC:  
  13.       rca_software_mode_remote_sync(data);  
  14.       break;  
  15.     default:  
  16.       break;  
  17.   }  
  18. }  
  19. static inline void rca_software_mode_remote_data(unsigned long data){  
  20.   fiq_bridge_pulse_trigger(&remote_data->fiq_handle_item);  
  21.   //gp_remote->fiq_handle_item.handle = remote_rca_bridge_isr;  
  22. }  
  23.   
  24. irqreturn_t remote_rca_bridge_isr(int irq, void *dev_id){  
  25.   rca_software_mode_remote_send_key((unsigned long)remote_data);  
  26. }  
  27. static inline int rca_software_mode_remote_send_key(unsigned long data){  
  28.   //普通按键正常上报  
  29.   if (((remote_data->custom_code[0] & 0xf) != (remote_data->cur_keycode & 0xf))  
  30.     &&(!is_factory_customer_code(remote_data->cur_keycode & 0xf))){   
  31.     //remote->custom_code[0]=0xff00; remote->custom_code[1]=0xffff;  
  32.     return 0; //对于红外customer用户码字段既不是0xf,也不属于工厂特殊处理部分的;直接返回,不上报。!!!!!!!!  
  33.   }  
  34.   //工厂按键特殊处理  
  35.   if(is_factory_customer_code(remote_data->cur_keycode & 0xf)){ //出现在remote.conf中用户码字段的,本平台特殊处理后event.type位为19  
  36.     //remote.conf中factorycust_begin到factorycust_end  
  37.   }  
  38.   remote_send_key(remote_data,keycode&0xff, 1);   
  39. }  
drivers/amlogic/input/remote/am_remote.c
[cpp] view plain copy
  1. void remote_send_key(struct remote *remote_data, unsigned int scancode, unsigned int type){  
  2.   input_event(remote_data->input, EV_KEY, key_map[fcode][scancode], type);  
  3.   input_sync(remote_data->input);  
  4. }  
2)、操作配置文件的设备节点:

drivers/amlogic/input/remote/am_remote.c

[cpp] view plain copy
  1. static int register_remote_dev(struct remote *remote){  
  2.   strcpy(remote->config_name, "amremote");  
  3.   ret = register_chrdev(0, remote->config_name, &remote_fops); //其中ioctl设备操作方法重要!!!  
  4.   remote->config_class = class_create(THIS_MODULE, remote->config_name);  
  5.   //动态创建设备节点"/dev/amremote",操作它的ioctl可以动态改变驱动中输入设备的映射关系  
  6.   remote->config_dev = device_create(remote->config_class, NULL, MKDEV(remote->config_major, 0), NULL, remote->config_name);  
  7. }  
二、用户空间部分remotecfg

external/remoteconf/irremote.c

[cpp] view plain copy
  1. #define DEVICE_NAME "/dev/amremote"  
  2. int main(int argc, char* argv[])  
  3. {  
  4.   device_fd = open(DEVICE_NAME, O_RDWR);  
  5.   //读取配置文件  
  6.   fp=fopen(argv[1], "r");  
  7.   ret = get_config_from_file(fp, remote);  
  8.   //设置工作模式等部分  
  9.   ioctl(device_fd, REMOTE_IOC_RESET_KEY_MAPPING, NULL);  
  10.   //按键码部分映射  
  11.   for(i = 0; i < 256; i++)  
  12.     if(key_map[i] != KEY_RESERVED){  
  13.       val = (i<<16) | key_map[i];  
  14.       ioctl(device_fd, REMOTE_IOC_SET_KEY_MAPPING, &val);  
  15.   }  
  16.   //用户码部分映射  
  17.   for(i = 0; i < FACTCUSTCODE_MAX; i++)  
  18.     if(factory_customercode_map[i] != 0xffffffff){  
  19.       val = (i<<16) | factory_customercode_map[i];  
  20.       ioctl(device_fd, REMOTE_IOC_SET_FACTORY_CUSTOMCODE, &val);  
  21.     }  
  22.   close(device_fd)  
  23. }  
看看get_config_from_file函数的实现;即如何解析配置文件

external/remoteconf/parsefile.c

[cpp] view plain copy
  1. int get_config_from_file(FILE *fp, remote_config_t *remote)   
  2. {  
  3.   //解析工作模式等部分  
  4.   value = strchr(line_data_buf, '=');  
  5.   if (remote_config_set(name, value, remote)) {  
  6.     printf("config file has not supported parameter:%s=%s\r\n", name, value);  
  7.   }  
  8.   //解析按键码部分映射  
  9.   if (strcasecmp(name, "key_end") == 0)   
  10.   value = strchr(line_data_buf, ' ');  
  11.   ircode = strtoul(name, NULL, 0);  
  12.   keycode = strtoul(value, NULL, 0) & 0xffff;  
  13.   remote->key_map[ircode] = keycode;  
  14.   //解析用户码部分映射  
  15.   if (strcasecmp(name, "factorycust_end") == 0)  
  16.   value = strchr(line_data_buf, ' ');  
  17.   index = strtoul(name, NULL, 0);  
  18.   custcode = strtoul(value, NULL, 0) & 0xffff;  
  19.   remote->factory_customercode_map[index] = custcode;     
  20. }  
阅读全文
0 0
原创粉丝点击