现在的位置: 首页 > 综合 > 正文

Linux内核学习实践之红外驱动分析

2014年02月03日 ⁄ 综合 ⁄ 共 4721字 ⁄ 字号 评论关闭

本文以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"; //设备名字“aml_keypad”
  ret = input_register_device(remote->input);//注册input设备
  hardware_init(pdev); //注册中断
  register_remote_dev(gp_remote); //操作配置文件的设备节点
}
//注册中断部分:
/*
static int hardware_init(struct platform_device *pdev)
{
  return request_irq(NEC_REMOTE_IRQ_NO, remote_interrupt, IRQF_SHARED, "keypad", (void *)remote_interrupt);
}
static irqreturn_t remote_interrupt(int irq, void *dev_id)                                                                                       
{
  // disable keyboard interrupt and schedule for handling 
  //  input_dbg("===trigger one  remoteads interupt \r\n");
  tasklet_schedule(&tasklet);  //任务队列实现的中断底半部,也即remote_tasklet
  return IRQ_HANDLED;
}
*/

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); //重新注册rca的中断  
}

arch/arm/plat-meson/fiq-bridge.c

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/sw_remote_rca38k.c

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))){ 
    //remote->custom_code[0]=0xff00; remote->custom_code[1]=0xffff;
    return 0; //对于红外customer用户码字段既不是0xf,也不属于工厂特殊处理部分的;直接返回,不上报。!!!!!!!!
  }
  //工厂按键特殊处理
  if(is_factory_customer_code(remote_data->cur_keycode & 0xf)){ //出现在remote.conf中用户码字段的,本平台特殊处理后event.type位为19
    //remote.conf中factorycust_begin到factorycust_end
  }
}

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); //其中ioctl设备操作方法重要!!!
  remote->config_class = class_create(THIS_MODULE, remote->config_name);
  //动态创建设备节点"/dev/amremote",操作它的ioctl可以动态改变驱动中输入设备的映射关系
  remote->config_dev = device_create(remote->config_class, NULL, MKDEV(remote->config_major, 0), NULL, remote->config_name);
}

二、用户空间部分remotecfg

external/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;   
}

抱歉!评论已关闭.