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
- module_init(remote_init);
- DECLARE_TASKLET_DISABLED(tasklet, remote_tasklet, 0);
- static int __devinit remote_init(void)
- {
- printk(KERN_INFO "Remote Driver\n");
- return platform_driver_register(&remote_driver);
- }
- static struct platform_driver remote_driver = {
- .probe = remote_probe,
- .remove = remote_remove,
- .suspend = remote_suspend,
- .resume = remote_resume,
- .driver = {
- .name = "meson-remote",
- },
- };
补充:平台设备部分,开机即被注册、导致平台驱动的探测函数执行
arch/arm/plat-meson/plat_dev_remote.c- struct platform_device meson_device_remote = {
- .name = "meson-remote",
- .id = -1,
- .dev = {
- .platform_data = NULL,
- }
- };
2.平台驱动探测函数:
drivers/amlogic/input/remote/am_remote.c
- static int remote_probe(struct platform_device *pdev)
- {
- input_dev = input_allocate_device();
- input_dev->name = "aml_keypad";
- ret = input_register_device(remote->input);
- hardware_init(pdev);
- register_remote_dev(gp_remote);
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
1)、补充:重新设定工作模式——中断的改变(这部分执行是当用户空间通过remotecfg命令重新配置后!!!),中断服务程序完成数据上报部分。
drivers/amlogic/input/remote/am_remote.c
- static long remote_config_ioctl(struct file *filp, unsigned int cmd, unsigned long args){
- case REMOTE_IOC_SET_MODE:
- work_mode_config(remote->work_mode);
- break;
- }
- static int work_mode_config(unsigned int cur_mode){
- case REMOTE_WORK_MODE_RCA:
- free_irq(NEC_REMOTE_IRQ_NO, remote_interrupt);
- gp_remote->fiq_handle_item.handle = remote_rca_bridge_isr;
- register_fiq_bridge_handle(&gp_remote->fiq_handle_item);
- request_fiq(NEC_REMOTE_IRQ_NO, &remote_fiq_interrupt);
- }
arch/arm/plat-meson/fiq-bridge.c- int register_fiq_bridge_handle(bridge_item_t *c_item)
- {
- bridge_item_t *pitem;
- list_for_each_entry(pitem, &fiq_bridge_list, list)
- request_irq(BRIDGE_IRQ, &root_handle_isr, IRQF_SHARED , "fiq_bridge", &fiq_bridge_list)
- }
看看rca的中断处理函数drivers/amlogic/input/remote/am_remote.c
- static void remote_fiq_interrupt(void){
- remote_rca_reprot_key((unsigned long)gp_remote);
- }
drivers/amlogic/input/remote/sw_remote_rca38k.c- void remote_rca_reprot_key(unsigned long data){
- switch (remote_data->step) {
- case REMOTE_STATUS_WAIT:
- rca_software_mode_remote_wait(data);
- break;
- case REMOTE_STATUS_LEADER:
- rca_software_mode_remote_leader(data);
- break;
- case REMOTE_STATUS_DATA:
- rca_software_mode_remote_data(data);
- break;
- case REMOTE_STATUS_SYNC:
- rca_software_mode_remote_sync(data);
- break;
- default:
- break;
- }
- }
- static inline void rca_software_mode_remote_data(unsigned long data){
- fiq_bridge_pulse_trigger(&remote_data->fiq_handle_item);
-
- }
-
- irqreturn_t remote_rca_bridge_isr(int irq, void *dev_id){
- rca_software_mode_remote_send_key((unsigned long)remote_data);
- }
- static inline int rca_software_mode_remote_send_key(unsigned long data){
-
- if (((remote_data->custom_code[0] & 0xf) != (remote_data->cur_keycode & 0xf))
- &&(!is_factory_customer_code(remote_data->cur_keycode & 0xf))){
-
- return 0;
- }
-
- if(is_factory_customer_code(remote_data->cur_keycode & 0xf)){
-
- }
- remote_send_key(remote_data,keycode&0xff, 1);
- }
drivers/amlogic/input/remote/am_remote.c- void remote_send_key(struct remote *remote_data, unsigned int scancode, unsigned int type){
- input_event(remote_data->input, EV_KEY, key_map[fcode][scancode], type);
- input_sync(remote_data->input);
- }
2)、操作配置文件的设备节点:drivers/amlogic/input/remote/am_remote.c
- static int register_remote_dev(struct remote *remote){
- strcpy(remote->config_name, "amremote");
- ret = register_chrdev(0, remote->config_name, &remote_fops);
- 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);
- }
二、用户空间部分remotecfgexternal/remoteconf/irremote.c
- #define DEVICE_NAME "/dev/amremote"
- int main(int argc, char* argv[])
- {
- device_fd = open(DEVICE_NAME, O_RDWR);
-
- fp=fopen(argv[1], "r");
- ret = get_config_from_file(fp, remote);
-
- ioctl(device_fd, REMOTE_IOC_RESET_KEY_MAPPING, NULL);
-
- for(i = 0; i < 256; i++)
- if(key_map[i] != KEY_RESERVED){
- val = (i<<16) | key_map[i];
- ioctl(device_fd, REMOTE_IOC_SET_KEY_MAPPING, &val);
- }
-
- for(i = 0; i < FACTCUSTCODE_MAX; i++)
- if(factory_customercode_map[i] != 0xffffffff){
- val = (i<<16) | factory_customercode_map[i];
- ioctl(device_fd, REMOTE_IOC_SET_FACTORY_CUSTOMCODE, &val);
- }
- close(device_fd)
- }
看看get_config_from_file函数的实现;即如何解析配置文件external/remoteconf/parsefile.c
- int get_config_from_file(FILE *fp, remote_config_t *remote)
- {
-
- value = strchr(line_data_buf, '=');
- if (remote_config_set(name, value, remote)) {
- printf("config file has not supported parameter:%s=%s\r\n", name, value);
- }
-
- if (strcasecmp(name, "key_end") == 0)
- value = strchr(line_data_buf, ' ');
- ircode = strtoul(name, NULL, 0);
- keycode = strtoul(value, NULL, 0) & 0xffff;
- remote->key_map[ircode] = keycode;
-
- if (strcasecmp(name, "factorycust_end") == 0)
- value = strchr(line_data_buf, ' ');
- index = strtoul(name, NULL, 0);
- custcode = strtoul(value, NULL, 0) & 0xffff;
- remote->factory_customercode_map[index] = custcode;
- }