【零】程序的总体框架:
设备树→预设信息→驱动.ko→文件ctl进行初始化→GPIO中断→定时器以及事件存储→nec_decode→scancode→map键值→上传input系统
为了复习设备树相关的操作,在这里使用设备树来进行一些预设,总体包括gpio、active_low、map_name、allowed_protos 这四个最主要的属性,这一点与参考代码100ask_irda.c是不一样的。参考源码并没有使用到设备树,所有的属性都是直接预设在程序中,申请IO直接通过IO号进行申请。
【一】设备树:
pinctrl_irda:irdagrp{
fsl,pins = <
MX6UL_PAD_CSI_VSYNC__GPIO4_IO19 0x000010B0
>;
};
这部分添加至&iomuxc节点下。
myirda{
compatible = "myirda,irdadrv";//驱动程序要找的名字
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_irda>;//引脚定义
gpios = <&gpio4 19 GPIO_ACTIVE_HIGH>;//默认1为高
active_low = <1>;//编码是否反向
linux,rc-map-name = "rc-hs0038-nec";//使用前需要注册映射
allowed_protos = <0x200>;//填0为可使用所有protocol,默认为NEC
};
设备树中没什么好说的,主要就是包括几个基本属性,其中active_low、linux,rc-map-name、allowed_protos这三个属性在驱动程序中需要用到。
这里简要介绍一下设备树属性读取的代码:
struct device_node *np = pdev->dev.of_node;
enum of_gpio_flags flags;
int gpio;
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
gpio = of_get_gpio_flags(np, 0, &flags);
if (gpio < 0) {
if (gpio != -EPROBE_DEFER)
printk("Failed to get gpio flags (%d)\n", gpio);
return gpio;
}
irda_dev.gpio_nr = gpio;
irda_dev.active_low = of_property_read_bool(np,"active_low");
/* probe() takes care of map_name == NULL or allowed_protos == 0 */
irda_dev.map_name = of_get_property(np, "linux,rc-map-name", NULL);
ret = of_property_read_u32_array(np, "allowed_protos",
&irda_dev.allowed_protos,1);
和大多数从设备树中读取相关配置差不多,比较麻烦的就是读取map_name、active_low以及allowed_protos,简要介绍一下这三个配置是干嘛的。
map_name: 通过NEC解码得到的数据与input键值的映射表的名字
active_low:是否在解码时进行反向编码(比如说原电平是高,但是解码时当它是低,为什么要这样做,是因为NEC协议的电平,详情看参考博客)
allowed_protos:允许的解码器种类,这里0x200表示的是RC_BIT_NEC,它定义在:
点开此头文件,可找到RC_BIT_NEC定义为:1ULL<<RC_TYPE_NEC,结合上方对RC_TYPE_NEC的定义可知:RC_BIT_NEC = 1ULL<< 9 即0x200
【二】应用层APP
之所以先说应用层APP的原因是个人建议先从抄应用层的代码开始学习Irda,除非你压根就不使用input系统和Linux系统内集成的NEC解码器,裸机电平解析可参考博客4。
应用层的代码如下:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <poll.h>
#include <signal.h>
#include <linux/input.h>
#define EVENT_PATH "/dev/input/event"
#define GPIO_IRDA_DEVICE_NAME "gpio_irda-ir"
#define IRDA_IOC_MAGIC 'i'
#define IRDA_IOCINIT _IOW(IRDA_IOC_MAGIC, 0, int)
#define GPIO0 ((4-1)*32+19)
struct event_msg{
int value;
int code;
};
static struct event_msg msg;
int getEventNumber(char *name)
{
FILE *fd;
int i, len, num;
char *str;
char buf[64], temp[4];
int flag = 0, file_row = 0, cur_row = 0;
fd = fopen("/proc/bus/input/devices", "r");
if(fd == NULL)
{
printf("getEventNumber:Can not open file\r\n");
return -1;
}
while(!feof(fd))
{
flag = fgetc(fd);
if(flag == '\n')
file_row++;
}
file_row = file_row + 1;
fseek(fd, 0, SEEK_SET);
cur_row = 1;
while(cur_row < file_row)
{
memset(buf, 0x00, sizeof(buf));
fgets(buf,sizeof(buf),fd);
cur_row ++;
if(strstr(buf, name) != NULL)
{
for(i=0; i<5; i++)
{
memset(buf, 0x00, sizeof(buf));
fgets(buf,sizeof(buf),fd);
if(strstr(buf, "Handlers") != NULL)
{
str = strchr(buf,'event') + 1;
strncpy(temp, str, 1);
num = (int)(temp[0]-'0');
fclose(fd);
return num;
}
}
}
}
fclose(fd);
return -1;
}
int main(int argc,char **argv)
{
char *filename;
char event_path[30];
int ret = 0;
int fd;
int fd_event;
int event_num = 0;
int pin = GPIO0;
struct input_event ev;
if(argc!=2)
{
printf("Usage:%s <dev>\n",argv[0]);
return -1;
}
filename = argv[1];
fd = open(filename,O_RDWR);
if(fd<0)
{
printf("Open file fail!\n");
goto ERROR;
}
if(ioctl(fd,IRDA_IOCINIT,&pin)<0)
{
printf("Init Irda fail!\n");
goto ERROR;
}else
{
printf("Init Irda successful\n");
}
event_num = getEventNumber(GPIO_IRDA_DEVICE_NAME);
printf("event num = %d\n",event_num);
sprintf(event_path,"%s%d",EVENT_PATH,event_num);
printf("%s\n",event_path);
fd_event = open(event_path,O_RDONLY);
if(fd_event < 0)
{
printf("Open event file fail!\n");
goto ERROR;
}
while(1)
{
ret = read(fd_event, &ev, sizeof(struct input_event));
if (ret < 0) {
printf("IRDA: read event error!\n");
}else
{
if(ev.type == EV_KEY)
{
msg.code = ev.code;
msg.value = ev.value;
printf("Msg->code:%d Msg->value:%d\n",msg.code,msg.value);
}
}
}
ERROR:
close(fd);
close(fd_event);
return 0;
}
这里最为核心的函数就是getEventNumber函数,不过这里并不是笔者实现的,而是笔者从模组参考代码中刨到的,它的功能非常重要,即通过设备名找到对应的event文件号。而笔者只是通过它找到的文件号重新形成了一个打开目录罢了,这里注意数值的长度是30,如果你的目录比较长要记得加长。
有的人可能会比较疑惑,为什么作者从设备树里整到了gpio还需要把io数值传入ioctl?实际上,这是为了与默认设备做兼容。在100ask_imx6ull板子中有一个默认的红外接收设备:/dev/irda,如果你和我一样对于自己的驱动程序怎么搞都搞不好的话,可以先使用这个应用程序加载官方默认的红外接收设备文件试试,以此来排除硬件层面的问题。使用方法为:./irda_app /dev/irda
[ 2072.596999] Registered IR keymap rc-100ask-nec
[ 2072.608106] input: gpio_irda-ir as /devices/virtual/rc/rc0/input3
[ 2072.623950] evbug: Connected device: input3 (gpio_irda-ir at gpio_irda-ir/input0)
[ 2072.642712] rc rc0: gpio_irda-ir as /devices/virtual/rc/rc0
[ 2072.655263] input: MCE IR Keyboard/Mouse (gpio-irda-rc) as /devices/virtual/input/input4
[ 2072.677303] evbug: Connected device: input4 (MCE IR Keyboard/Mouse (gpio-irda-rc) at /input0)
以上为正常运行时的打印,由此使用官方的红外遥控器即可出现键值打印。
在官方的代码中,在ioctl传入gpio之后才会开始申请gpio,因此不用担心会不会因为官方驱动而影响自家驱动申请不到gpio的情况,只要你不是同时运行两个程序。
最后在while(1)中读取ev的值,其中code为上传到Input系统的KEY代码(可不是NEC解码的值),value为键值,1表示按下,0表示松开。所以在按一次并松开时,会打印两行同一code但不同val,如下:
Msg->code:116 Msg->value:1
Msg->code:116 Msg->value:0
【三】驱动程序
#include <linux/module.h>
#include <linux/poll.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/irqflags.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/timer.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <media/rc-core.h>
#include <linux/platform_data/media/gpio-ir-recv.h>
#define irda_NAME "myirda"
#define GPIO_IRDA_DRIVER_NAME "gpio-irda-rc"
#define GPIO_IRDA_DEVICE_NAME "gpio_irda-ir"
#define IRDA_IOC_MAGIC 'i'
#define IRDA_IOCINIT _IOW(IRDA_IOC_MAGIC, 0, int)
struct gpio_rc_dev {
struct rc_dev *rcdev;
int gpio_nr;
bool active_low;
struct timer_list flush_timer;
};
struct irda_device{
/*file*/
int major;
dev_t devid;
struct cdev cdev;
struct class *class;
struct device *device;
/*irda dev*/
int gpio_nr;
bool active_low;
u32 allowed_protos;
const char *map_name;
};
static struct irda_device irda_dev;
static struct gpio_rc_dev *gpio_dev;
static struct rc_dev *rcdev;
static struct rc_map_table hs0038_nec[] = {
{ 0x45, KEY_POWER},
{ 0x47, KEY_MENU},
{ 0x44, KEY_T},//Test
{ 0x40, KEY_VOLUMEUP},
{ 0x43, KEY_BACK},//RETURN
{ 0x07, KEY_LAST},
{ 0x15, KEY_PLAYPAUSE},
{ 0x09, KEY_NEXT},
{ 0x16, KEY_0},
{ 0x19, KEY_VOLUMEDOWN},
{ 0x0d, KEY_C},
{ 0x0c, KEY_1},
{ 0x18, KEY_2},
{ 0x5e, KEY_3},
{ 0x08, KEY_4},
{ 0x1c, KEY_5},
{ 0x5a, KEY_6},
{ 0x42, KEY_7},
{ 0x52, KEY_8},
{ 0x4a, KEY_9},
};
static struct rc_map_list nec_hs0038_map = {
.map = {
.scan = hs0038_nec,
.size = ARRAY_SIZE(hs0038_nec),
.rc_type = RC_TYPE_NEC, //RC_TYPE_UNKNOWN //echo nec > /sys/class/rc/rc0/protocols
.name = "rc-hs0038-nec",
}
};
static irqreturn_t gpio_ir_recv_irq(int irq, void *dev_id)
{
struct gpio_rc_dev *gpio_dev = dev_id;
int gval;
int rc = 0;
enum raw_event_type type = IR_SPACE;
gval = gpio_get_value(gpio_dev->gpio_nr);
if (gval < 0)
goto err_get_value;
if (gpio_dev->active_low)
gval = !gval;
if (gval == 1)
type = IR_PULSE;
rc = ir_raw_event_store_edge(gpio_dev->rcdev, type);
if (rc < 0)
goto err_get_value;
mod_timer(&gpio_dev->flush_timer,
jiffies + nsecs_to_jiffies(gpio_dev->rcdev->timeout));
ir_raw_event_handle(gpio_dev->rcdev);
err_get_value:
return IRQ_HANDLED;
}
static void flush_timer(unsigned long arg)
{
struct gpio_rc_dev *gpio_dev = (struct gpio_rc_dev *)arg;
DEFINE_IR_RAW_EVENT(ev);
ev.timeout = true;
ev.duration = gpio_dev->rcdev->timeout;
ir_raw_event_store(gpio_dev->rcdev, &ev);
ir_raw_event_handle(gpio_dev->rcdev);
}
static int irda_init(void)
{
int rc;
gpio_dev = kzalloc(sizeof(struct gpio_rc_dev), GFP_KERNEL);
if (!gpio_dev)
return -ENOMEM;
rcdev = rc_allocate_device();
if (!rcdev) {
rc = -ENOMEM;
goto err_allocate_device;
}
rc = rc_map_register(&nec_hs0038_map);
if(rc<0)
{
printk("failed to map hs0038!\n");
goto err_map_register;
}
rcdev->priv = gpio_dev;
rcdev->driver_type = RC_DRIVER_IR_RAW;
rcdev->input_name = GPIO_IRDA_DEVICE_NAME;
rcdev->input_phys = GPIO_IRDA_DEVICE_NAME "/input0";
rcdev->input_id.bustype = BUS_HOST;
rcdev->input_id.vendor = 0x0001;
rcdev->input_id.product = 0x0001;
rcdev->input_id.version = 0x0100;
rcdev->dev.parent = NULL;
rcdev->driver_name = GPIO_IRDA_DRIVER_NAME;
rcdev->min_timeout = 0;
rcdev->timeout = MS_TO_NS(125);
rcdev->max_timeout = 10 * MS_TO_NS(125);
if(irda_dev.allowed_protos)
{
rcdev->allowed_protocols = irda_dev.allowed_protos;
printk("Use devtree protocols\n");
}else
{
rcdev->allowed_protocols = RC_BIT_ALL;//RC_BIT_NEC;
printk("Use default protocols\n");
}
rcdev->map_name = irda_dev.map_name?irda_dev.map_name:"rc-hs0038-nec";
gpio_dev->rcdev = rcdev;
gpio_dev->gpio_nr = irda_dev.gpio_nr;
gpio_dev->active_low = irda_dev.active_low;
setup_timer(&gpio_dev->flush_timer, flush_timer, (unsigned long)gpio_dev);
rc = gpio_request(gpio_dev->gpio_nr, "gpio-irda-recv");
if (rc < 0)
{
printk("gpio_request fail!\n");
goto err_gpio_request;
}else
{
printk("gpio_request successful!\n");
}
rc = gpio_direction_input(gpio_dev->gpio_nr);
if (rc < 0)
{
printk("gpio_direction fail\n");
goto err_gpio_direction_input;
}else
{
printk("gpio_direction successful!\n");
}
rc = rc_register_device(rcdev);
if (rc < 0) {
printk("failed to register rc device\n");
goto err_register_rc_device;
}
rc = request_any_context_irq(gpio_to_irq(gpio_dev->gpio_nr),
gpio_ir_recv_irq,
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
"gpio-irda-irq", gpio_dev);
if (rc < 0)
goto err_request_irq;
return 0;
err_request_irq:
rc_unregister_device(rcdev);
rcdev = NULL;
err_gpio_request:
err_gpio_direction_input:
gpio_free(gpio_dev->gpio_nr);
err_register_rc_device:
rc_free_device(rcdev);
err_map_register:
rc_map_unregister(&nec_hs0038_map);
err_allocate_device:
kfree(gpio_dev);
return rc;
}
static ssize_t irda_drv_read(struct file *file,char __user *buf,size_t size,loff_t *offset)
{
printk("File:%s Function:%s line: %d\n",__FILE__,__FUNCTION__,__LINE__);
return 0;
}
static int irda_drv_open(struct inode *node,struct file *file)
{
printk("File:%s Function:%s line: %d\n",__FILE__,__FUNCTION__,__LINE__);
return 0;
}
static int irda_drv_close(struct inode *node,struct file *file)
{
printk("File:%s Function:%s line: %d\n",__FILE__,__FUNCTION__,__LINE__);
free_irq(gpio_to_irq(gpio_dev->gpio_nr),gpio_dev);
del_timer_sync(&gpio_dev->flush_timer);
rc_unregister_device(gpio_dev->rcdev);
gpio_free(gpio_dev->gpio_nr);
kfree(gpio_dev);
rc_map_unregister(&nec_hs0038_map);
return 0;
}
static long irda_ioctl (struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret = 0;
printk("File:%s Function:%s line: %d\n",__FILE__,__FUNCTION__,__LINE__);
switch(cmd) {
case IRDA_IOCINIT:
ret = irda_init();
break;
default:
ret = -ENOTTY;
break;
}
return ret;
}
static struct file_operations irda_drv = {
.owner = THIS_MODULE,
.open = irda_drv_open,
.read = irda_drv_read,
.release = irda_drv_close,
.unlocked_ioctl = irda_ioctl,
};
static int irda_probe(struct platform_device *pdev)
{
int ret = 0;
struct device_node *np = pdev->dev.of_node;
enum of_gpio_flags flags;
int gpio;
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
gpio = of_get_gpio_flags(np, 0, &flags);
if (gpio < 0) {
if (gpio != -EPROBE_DEFER)
printk("Failed to get gpio flags (%d)\n", gpio);
return gpio;
}
irda_dev.gpio_nr = gpio;
irda_dev.active_low = of_property_read_bool(np,"active_low");
/* probe() takes care of map_name == NULL or allowed_protos == 0 */
irda_dev.map_name = of_get_property(np, "linux,rc-map-name", NULL);
ret = of_property_read_u32_array(np, "allowed_protos", &irda_dev.allowed_protos,1);
if(irda_dev.major)
{
irda_dev.devid = MKDEV(irda_dev.major,0);
ret = register_chrdev_region(irda_dev.devid,1,irda_NAME);
printk("register successful\r\n");
}else
{
ret = alloc_chrdev_region(&irda_dev.devid,0,1,irda_NAME);
irda_dev.major = MAJOR(irda_dev.devid);
printk("alloc_chrdev successful\r\n");
}
if(ret<0){
printk("%s Couldn't alloc_chrdev_region,ret = %d\r\n",irda_NAME,ret);
goto ERROR;
}
cdev_init(&irda_dev.cdev,&irda_drv);
ret = cdev_add(&irda_dev.cdev,irda_dev.devid,1);
if(ret<0)
{
printk("Cannot add cdev\n");
goto ERROR;
}
irda_dev.class = class_create(THIS_MODULE,irda_NAME);
if(IS_ERR(irda_dev.class))
{
printk("Cannot create class\n");
goto ERROR;
}else
{
printk("class_create successful\r\n");
}
irda_dev.device = device_create(irda_dev.class,NULL,irda_dev.devid,NULL,irda_NAME);
if(IS_ERR(irda_dev.device))
{
printk("Cannot create device\n");
goto ERROR;
}else
{
printk("device_create successful\r\n");
}
return 0;
ERROR:
unregister_chrdev_region(irda_dev.devid, 1);
return -1;
}
static int irda_remove(struct platform_device *pdev)
{
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
gpio_free(irda_dev.gpio_nr);
cdev_del(&irda_dev.cdev);
unregister_chrdev_region(irda_dev.devid,1);
device_destroy(irda_dev.class,irda_dev.devid);
class_destroy(irda_dev.class);
return 0;
}
static const struct of_device_id my_irda[]=
{
{ .compatible = "myirda,irdadrv"},
{ },
};
static struct platform_driver irda_driver = {
.probe = irda_probe,
.remove = irda_remove,
.driver = {
.name = "myirda",
.of_match_table = my_irda,
},
};
static int __init irda_modules_init(void)
{
int err;
printk("File:%s Function:%s line: %d\n",__FILE__,__FUNCTION__,__LINE__);
/* 1.register led_drv*/
err = platform_driver_register(&irda_driver);
return err;
}
/* 5. Exit Function */
static void __exit irda_modules_exit(void)
{
printk("File:%s Function:%s line: %d\n",__FILE__,__FUNCTION__,__LINE__);
platform_driver_unregister(&irda_driver);
}
/* 6. complete dev information*/
module_init(irda_modules_init);//init
module_exit(irda_modules_exit);//exit
MODULE_LICENSE("GPL");
程序的主要架构为:从设备树读取属性→文件ioctl→初始化→gpio中断→记录电平/定时器→解码器→input系统
设备树方面不再做解释,主要是两方面。
其一,初始化阶段:
主要初始化了rcdev的一系列属性以及针对从设备树上读取的属性来更改rcdev的属性,包括active_low、allowed_protos、map-name。
值得注意的是,在使用这个map-name之前,先使用了rc_map_register(&nec_hs0038_map)去注册它,并再关闭文件时进行了注销。这种写法是参考博客1,官方的做法是创建一个独立的.C文件,并将其作为模组进行装载到系统中,这里做法有点不一样,比较省事吧。
其二,中断与定时器:
这里的代码基本上就是从官方代码gpio-ir-recv.c上copy而来,主要的逻辑是:红外接收器→GPIO电平变化→中断触发→中断处理函数→记录电平→处理函数→定时器→定时器函数→事件保存与处理。
下面来讲一下map映射的原理:
nec scancode→KEY事件,即从NEC解码的数据映射至Linux input系统的按键事件。
第一个方面:如何获得scancode?
如果懒的话可直接复制官方的map,即:100ask_rc-nec.c中的内容。但是你一定要用它的通用模组的遥控器,否则可能出现scancode值没有映射的问题,这样的话就没有打印了,并且在read event文件时是阻塞阅读,没有打印就不会上报,就会和卡死一样。所以,针对于一般的情况,我们需要取得在内核打印的sancode,方法很简单,在ir-nec-decoder.c中的ir_nec_decode函数的case STATE_TRAILER_SPACE下最后几行的rc_keydown(dev,scancode,0)前加上一行打印,printk(“NEC sancode 0x%04x\n”,scancode)即可。加上之后,一定要注意要重新编译大内核并替换,以确保内核中的代码改变。
加上了之后打印可能为这样(要打开printk的打印等级):
[ 2115.955807] NEC scancode 0x0044
Msg->code:20 Msg->value:1
[ 2116.094961] NEC scancode 0x0044
Msg->code:20 Msg->value:0
那么现在还有一个问题,如何做到遥控器上的按键和input系统的按键相对应呢?这就不得不说到map了。
例如本代码中自定义的map中的这一行:{ 0x44, KEY_T},
它的意思是:scancode为0x44时,input系统键值上传字母"T"。实际上因为KEY中没有KEY_TEST,因此笔者使用KEY_T来替代遥控器上的Test按键。包括KEY_C也是替代遥控器上的C键。遥控器如下图:
值得注意的是,KEY_POWER以及其他的KEY键值都定义在 include/uapi/linux/input-event-codes.h中,如果你使用了一个其中没有定义的KEY键值,那么在编译时就会报错,如下:
解决办法时两种:在input-event-codes.h中找其他的键值去替换或者是直接在里面重新定义一个键值,重新定义的话也是需要重新编译大内核的噢。
在介绍完了这个之后,细心的小盆友可能已经发现,笔者的按键映射map是按照遥控器从左到右,从上到下依次排序的。
【四】Makefile
KERN_DIR = //home/book/100ask_imx6ull-sdk/Linux-4.9.88
all:
make -C $(KERN_DIR) M=`pwd` modules
$(CROSS_COMPILE)gcc -o irda_app irda_app.c
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order
rm -f irda_app
obj-m +=irda_my_drv.o
【五】使用
insmod irda_my_drv.ko
./irda_app /dev/myirda
【六】挖坑:既然学会了红外遥控器,不如用它来控制控制东西?接下来学习PWM控制电机,就拿它来控制电机吧!这样就完成了一个红外遥控器电风扇的简单项目。
参考博客:
https://www.cnblogs.com/lifexy/p/9783694.html
https://www.cnblogs.com/lifexy/p/9783914.html
https://blog.51cto.com/u_15315240/5110205
https://blog.youkuaiyun.com/weixin_71478434/article/details/126511672?ops_request_misc=&request_id=&biz_id=102&utm_term=Linux%20Irda%20%E5%BA%94%E7%94%A8&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduweb~default-0-126511672.142v88control,239v2insert_chatgpt&spm=1018.2226.3001.4187
https://blog.youkuaiyun.com/qq_41683305/article/details/124844274
https://blog.youkuaiyun.com/obanaganastar/article/details/124350911
参考代码:
100ask_irda.c
100ask_rc-nec.c
gpio-ir-recv.c