IR框架分为四层:IR Driver层,IR core层, Decoder层, Keymap层。
iput_dev层 input_core层 input_handle层 映射层

红外遥控器驱动 类似于输入子系统中的 input_device层 硬件初始化,fops操作

IR Driver层: 需要实现IR Driver的一些init,interrupt处理,file_operations操作等。
IR core层:需要实现提供给decoder层,keymap层的相关接口,raw数据处理接口,input相关接口等。
Decoder层:需要实现不同protocols的解析函数。
Keymap层:需要实现针对不同IR的keymap映射关系。

这里我们根据公司的中性遥控器 为例

IR Driver层代码: kernel\mstar2\drv\ir_mirc\mstar_ir.c
IR CORE层代码: kernel\mstar2\drv\ir_mirc\ir_core.c
protocols: kernel\mstar2\drv\ir_mirc\protocols\ir-nec-decoder.c ==>nec解码 Decoder层
keymaps: kernel\mstar2\drv\ir_mirc\keymaps\keymap-mstar-tv.c ==>把按键的扫描值映射成keycode值 keycode值是上报给android层处理的

IR 的配置有两种:

  1. Ir_config.h 进行配置,在编译阶段配置好客户要使用的遥控器。 ==>我们公司是使用了这种配置方式
  2. Ir_config.ini进行配置,可以动态配置客户要使用的遥控器。
    两种配置方式可以单独配置使用,也可以同时配置使用。

1> 分析配置文件
文件路径: mstar2/drv/ir_mirc/lr_config.h

#ifndef _IR_CONFIG_H_
#define _IR_CONFIG_H_
#include "mstar_ir.h"     //==>这3个.h文件 前两个就是驱动文件的.h文件,ir_common.h文件是我们公司对应的配置文件之一 (定义了头码,解码类型...)
#include "ir_core.h"
#include "ir_common.h"/****************** For Customer Config  Start [can modify by customers] ********************/
//Number of IR should this chip supported       该芯片应支持的IR数: ==>  表示该系统支持2款遥控器
#define IR_SUPPORT_NUM 2//Add & Modify Customer IR with Differ Headcode Here   在这里添加和修改不同头码的客户IR   ==> ir_config这个结构体中的成员就是一种遥控器的信息
static IR_Profile_t ir_config[IR_SUPPORT_NUM]=
{// {IR_TYPE_NEC,NUM_KEYMAP_MSTAR_TV,0},         // Mstar IR customer code//{IR_TYPE_TOSHIBA,NUM_KEYMAP_SKYWORTH_TV,0},   // skyworth toshiba ir//{IR_TYPE_NEC,NUM_KEYMAP_CHANGHONG_TV,0},      // changhong_RL78B /Toshiba CT-90436 IR customer code//{IR_TYPE_NEC,NUM_KEYMAP_HISENSE_TV,0},        // Hisense IR customer code//{IR_TYPE_RCA,NUM_KEYMAP_TCL_RCA_TV,0},        // TCL RCA  customer code//{IR_TYPE_P7051,NUM_KEYMAP_P7051_STB,0},       // Panasonic 7051 IR customer code//{IR_TYPE_RC5, NUM_KEYMAP_RC5_TV, 0},            // RC5 customer code{IR_TYPE_NEC, NUM_KEYMAP_MSTAR_TV, 0},//{IR_TYPE_RC6,NUM_KEYMAP_KATHREIN_TV,0},       // Kathrein RC6 customer code//{IR_TYPE_KONKA,NUM_KEYMAP_KONKA_TV,2},{IR_TYPE_PANASONIC,NUM_KEYMAP_PANASONIC_TV,2},
};//IR Debug level for customer setting
static IR_DBG_LEVEL_e ir_dbglevel = IR_DBG_ERR;//IR Speed level for customer setting
static IR_SPEED_LEVEL_e ir_speed = IR_SPEED_FAST_H;/****************** For Customer Config  End [can modify by customers] ********************/#endif代码分析 ir_config:
//Description  of IR
typedef struct IR_Profile_s {IR_Type_e eIRType;         //协议类型u32 u32HeadCode;          //头码值u32 u32IRSpeed;                //遥控器灵敏度
}IR_Profile_t;我们这里配置了两种遥控器{IR_TYPE_NEC, NUM_KEYMAP_MSTAR_TV, 0},  {IR_TYPE_PANASONIC,NUM_KEYMAP_PANASONIC_TV,2},
我们通常使用的是{IR_TYPE_NEC, NUM_KEYMAP_MSTAR_TV, 0},  即NEC编码协议
NUM_KEYMAP_MSTAR_TV值的来源|
mstar2/drv/ir_mirc/ir_common.h 文件中定义的  ==>3个头文件之一      ****************** 这就是我们在配置kernel 遥控器信息中的一个步骤
在ir_common.h中为了实现客户的定制化遥控器功能  我们通过宏来区分客户不同的NEC协议的头码//每个宏定义对应一个客户遥控器
#define CUSTOMER_IR_MSTAR 0
#define CUSTOMER_IR_SYKJWB  1                   //中性
#define CUSTOMER_IR_PROMETH_EN  2
#define CONFIG_CUSTOMER_VIEWSONIC  3
#define CONFIG_CUSTOMER_PROWISE  4
#define CONFIG_CUSTOMER_WJCIRTEXT  5#define CUSTOMER_IR_SELECT  CONFIG_CUSTOMER_WJCIRTEXT //CUSTOMER_IR_MSTAR   最终宏 //不同的客户有主见不同的NEC遥控器的头码
#if(CUSTOMER_IR_SELECT == CUSTOMER_IR_SYKJWB)
#define    NUM_KEYMAP_MSTAR_TV                   0x4040
#elif(CUSTOMER_IR_SELECT == CUSTOMER_IR_PROMETH_EN)
#define    NUM_KEYMAP_MSTAR_TV                0x01FE
#elif(CUSTOMER_IR_SELECT == CONFIG_CUSTOMER_VIEWSONIC )
#define    NUM_KEYMAP_MSTAR_TV                0x0000
#elif(CUSTOMER_IR_SELECT == CONFIG_CUSTOMER_PROWISE)
#define    NUM_KEYMAP_MSTAR_TV                  0x20DF
#elif(CUSTOMER_IR_SELECT == CONFIG_CUSTOMER_WJCIRTEXT)
#define    NUM_KEYMAP_MSTAR_TV                  0x4040
#else
#define    NUM_KEYMAP_MSTAR_TV                0x807F代码分析: 配置IR debug 级别
//IR Debug level for customer setting
static IR_DBG_LEVEL_e ir_dbglevel = IR_DBG_ERR;
==> 默认设置成error打印
关键点: 在IR驱动中 除了在_MDrv_IR_ISR中不能加log打印外,其他地方都能加log进行打印
修改打印级别的方法: *********** *****************
先找到存放打印级别的属性文件,这个属性文件是在 mstar_ir.c中实现的 mstar_ir_drv_probe() --> mstar_ir_creat_sysfs_attr() --> device_create_file(dev, &dev_attr_IRDebug)
find / -name IRDebug  ==>先找到该属性文件的路径
echo 4 > xxx/IRDebug   ==>修改该属性文件的打印级别
cat  xxx/IRDebug      ==>查看属性文件的当前打印级别和支持的打印级别
==> 其实这个实现方法 不就是printk中设置7个级别的打印方法吗?
具体操作
IFP:/ #su
IFP:/ # find / -name IRDebug
/sys/devices/ir/IRDebugIFP:/sys/devices/ir # ls
IRDebug   IRProtocols  driver           modalias  subsystem
IREnable  IRSpeed      driver_override  of_node   uevent
IREvent   IRTimeout    input            power  开头是大写的文件都是通过函数mstar_ir_creat_sysfs_attr创建的查看 打印级别
IFP:/sys/devices/ir # cat IRDebug
Current Debug Level:  1修改打印级别为 4
IFP:/sys/devices/ir #echo 4 > IRDebug代码分析: 配置IR Speed
//IR Speed level for customer setting
static IR_SPEED_LEVEL_e ir_speed = IR_SPEED_FAST_H;  // IR_SPEED_FAST_H == 0  ==> 表示不会附带重复波形,按一下就发送一个波形
ir_speed:要设置的原因 ,因为不同的遥控器的灵敏度可能不同,也许你短按一下按键,发送出去的是好几个重复波形,如果不做处理会就会造成,我按一下,上层相应几下的情况
故需要设置这个属性来过滤附带的重复波形,   ir_speed = 2 ==> 过滤两个波形

2> 分析ir_driver驱动文件 mstar_ir.c

//驱动入口函数
module_init(mstar_ir_drv_init_module);//加载函数
static int __init mstar_ir_drv_init_module(void)
{int ret = 0;ret = platform_driver_register(&Mstar_ir_driver);   //注册设备驱动  使用了platform框架来实现 input_dev层if (ret){IRDBG_ERR("Register Mstar IR Platform Driver Failed!");}
#ifdef CONFIG_MIRC_INPUT_DEVICE
#if(CONFIG_IR_KEYMAP_MSTAR_NEC)init_key_map_mstar_tv();
#endif
#if(CONFIG_IR_KEYMAP_TCL_RCA)init_key_map_tcl_tv();
#endif
#if(CONFIG_IR_KEYMAP_TCL)init_key_map_tcl_tv();
#endif
#if(CONFIG_IR_KEYMAP_CHANGHONG)init_key_map_changhong_tv();
#endif
#if(CONFIG_IR_KEYMAP_HISENSE)init_key_map_hisense_tv();
#endif
#if (CONFIG_IR_KEYMAP_HAIER)init_key_map_haier_tv();
#endif
#if(CONFIG_IR_KEYMAP_KONKA)init_key_map_konka_tv();
#endif
#if(CONFIG_IR_KEYMAP_SKYWORTH)init_key_map_skyworth_tv();
#endif
#if(CONFIG_IR_KEYMAP_PANASONIC_7051)init_key_map_p7051_stb();
#endif
#if(CONFIG_IR_KEYMAP_KATHREIN)
init_key_map_rc6_kathrein();
#endif
#if(CONFIG_IR_KEYMAP_RC5)init_key_map_rc5_tv();
#endif
#if(CONFIG_IR_KEYMAP_METZ)init_key_map_metz_rm18();init_key_map_metz_rm19();
#endif
#if(CONFIG_IR_KEYMAP_PANASONIC)init_key_map_panasonic_tv();
#endif
#endifreturn ret;
}
==>
注册完设备驱动之后,紧接着有注册了多款遥控器,最终通过MIRC_Map_Register将按键映射表添加到链表keymap_list中,方便后面通过scancode 寻找keycode。
#ifdef CONFIG_MIRC_INPUT_DEVICE 这个宏=1
==> kernel根目录下的 .config文件中有如下定义
#
# Mstar IR Config
#
# CONFIG_IR_DYNAMIC_CONFIG is not set
CONFIG_MIRC_INPUT_DEVICE=y   除了直接修改.config文件外,还可以通过make menuconfig来修改配置
查看 ir_mirc目录下的Kconfig文件,内容如下
config MSTAR_IR_REFACTOR
tristate "Mstar IR Driver"
default y
helpMstar IR decoder driver function
menu "Mstar IR Config"depends on MSTAR_IR_REFACTOR
config IR_DYNAMIC_CONFIGdepends on MIRC_INPUT_DEVICEtristate "load ir config(header,protocol,keymap) from /config/ir_config"default n                     ==> 对应CONFIG_IR_DYNAMIC_CONFIG is not set
config MIRC_INPUT_DEVICE            bool "IR Key send to input subsystem"default y                        ==> 对应CONFIG_MIRC_INPUT_DEVICE=y
if MIRC_INPUT_DEVICE
source "drivers/mstar2/drv/ir_mirc/keymaps/Kconfig"
endif
endmenuCONFIG_IR_KEYMAP_MSTAR_NEC这个宏 同理查看.config文件有如下定义
CONFIG_IR_KEYMAP_MSTAR_NEC=y
CONFIG_IR_KEYMAP_TCL_RCA=y
CONFIG_IR_KEYMAP_TCL=y
CONFIG_IR_KEYMAP_CHANGHONG=y
CONFIG_IR_KEYMAP_HISENSE=y
CONFIG_IR_KEYMAP_HAIER=y
CONFIG_IR_KEYMAP_KONKA=y
CONFIG_IR_KEYMAP_SKYWORTH=y
CONFIG_IR_KEYMAP_PANASONIC_7051=y
CONFIG_IR_KEYMAP_KATHREIN=y
CONFIG_IR_KEYMAP_RC5=y
CONFIG_IR_KEYMAP_METZ=y
CONFIG_IR_KEYMAP_PANASONIC=y
CONFIG_MSTAR_IOMAP=y然后配置: 前面的ir_mirc目录下的Kconfig文件中说明如果对应CONFIG_MIRC_INPUT_DEVICE=y就执行mstar2/drv/ir_mirc/keymaps/Kconfig这个文件
mstar2/drv/ir_mirc/keymaps/Kconfig文件的内容
menu "Mstar IR Keymap Select"
depends on MIRC_INPUT_DEVICE
config IR_KEYMAP_MSTAR_NECbool "Mstar NEC keymaps"default yhelpMstar nec keymap
config IR_KEYMAP_TCL_RCAbool "TCL RCA keymaps"default yhelpTCL RCA keymaps
config IR_KEYMAP_TCLbool "TCL keymaps"default yhelpTCL tv keymaps
config IR_KEYMAP_CHANGHONGbool "Changhong IR keymaps"default yhelpChanghong tv keymaps
config IR_KEYMAP_HISENSEbool "Hisense IR keymaps"default yhelpHisense tv keymaps
config IR_KEYMAP_HAIERbool "Haier IR keymaps"default yhelpHaier tv keymaps
config IR_KEYMAP_KONKAbool "Konka IR keymaps"default yhelpKonka tv keymaps
config IR_KEYMAP_SKYWORTHbool "Skyworth IR keymaps"default yhelpSkyworth tv keymaps
config IR_KEYMAP_PANASONIC_7051bool "Pnasonic 7051 IR keymaps"default yhelppanasonic tv keymaps
config IR_KEYMAP_KATHREINbool "Kathrein RC6 MODE_6A IR keymaps"default yhelpKathrein tv keymaps
config IR_KEYMAP_RC5bool "RC5 IR keymaps"default yhelpRC5 tv keymaps
config IR_KEYMAP_METZbool "metz IR keymaps"default yhelpmetz tv keymaps
config IR_KEYMAP_PANASONICbool "Panasonic IR keymaps"default yhelpPanasonic tv keymaps

endmenu

==> 显然这里就是要我们配置要注册哪些遥控器了
这里我们以我们公司现在的映射文件keymap-mstar-tv.c为例简单跟读
#if(CONFIG_IR_KEYMAP_MSTAR_NEC)init_key_map_mstar_tv();
#endif这里init_key_map_mstar_tv函数就是keymap-mstar-tv.c文件中的加载函数  故这里就相当于加载了keymap-mstar-tv.c文件对应的映射驱动******** ************************************这里开始分析keymap-mstar-tv.c文件 即映射文件: 扫描码映射成keycode,然后将映射表注册到ir_core中的存放所有映射表的链表中
int init_key_map_mstar_tv(void)
{return MIRC_Map_Register(&mstar_tv_map);       //将该文件配置的映射表添加到ir_core中统一存放映射表的链表keymap_list中, 目的是为了方便后面通过scancode 寻找keycode 即后面 有按键按下,我知道了它的扫描码,直接去这个链表中寻找它对应的keycode值即可
}keymap-mstar-tv.c文件的按键映射表 mstar_tv_map
static struct key_map_list mstar_tv_map = {.map = {.scan     = mstar_tv,             //具体的映射关系表.size     = ARRAY_SIZE(mstar_tv),    //多少个按键映射对.name     = NAME_KEYMAP_MSTAR_TV,    //该按键映射表的名称   在ir_common.h中定义 #define NAME_KEYMAP_MSTAR_TV                  "ir-mstar-tv"  .headcode = NUM_KEYMAP_MSTAR_TV,  //头码               在ir_common.h中定义 #define    NUM_KEYMAP_MSTAR_TV                   0x4040}
};
具体的映射关系表的定义
static struct key_map_table mstar_tv[] = {
#if(CUSTOMER_IR_SELECT == CUSTOMER_IR_SYKJWB){ 0x0A, KEY_POWER }, //POWER{ 0x0F, KEY_MUTE }, //MUTE{ 0x40, KEY_MENU }, //MENU{ 0x18, KEY_BACK }, //RETURN{ 0x0B, KEY_UP }, //UP{ 0x0E, KEY_DOWN }, //DOWN{ 0x10, KEY_LEFT }, //LEFT{ 0x11, KEY_RIGHT }, //RIGHT{ 0x0D, KEY_ENTER }, //OK{ 0x15, KEY_VOLUMEUP }, //V+{ 0x1C, KEY_VOLUMEDOWN }, //V-{ 0x1B, KEY_HOME }, //HOME no result{ 0x33, KEY_FN_F1 }, //OPS{ 0x37, KEY_FN_F2 }, //WB{ 0x41, KEY_KP1 }, //SOURCE{ 0x0C, KEY_FN_F6 }, //SCREENSHOT{ 0x00, KEY_F1},    //F1{ 0x00, KEY_F1},    //F1
#elif (CUSTOMER_IR_SELECT == CONFIG_CUSTOMER_PROWISE)    { 0x52, KEY_POWER }, //POWER    { 0x0D, KEY_MENU }, //MENU    { 0x47, KEY_UP }, //UP    { 0x4D, KEY_DOWN }, //DOWN    { 0x49, KEY_LEFT }, //LEFT   { 0x4B, KEY_RIGHT }, //RIGHT    { 0x4A, KEY_ENTER }, //OK   { 0x07, KEY_KP1 }, //SOURCE { 0x48, KEY_HOME }, //HOME  { 0x03, KEY_VOLUMEUP }, //V+  { 0x4E, KEY_F10}, //FREEZE   { 0x0A, KEY_FN_F5 }, //SETTING  { 0x41, KEY_VOLUMEDOWN }, //V-  { 0x5C, KEY_KP8 }, //TOUCH { 0x40, KEY_BACK }, //RETURN   { 0x53, KEY_MUTE }, //MUTE
... ...}
==> 会进行客制化处理,不同的客户遥控器通过定义宏区分,最后通过宏CUSTOMER_IR_SELECT来表示选择哪个客户遥控器   **** 这些宏在ir_common.h中定义
分析映射对
{ 0x0A, KEY_POWER }, //POWER
0x0A: 按键的扫描码 ,在遥控器规格书中得到
KEY_POWER: keycode按键值,即该按键代表的作用
当遥控器按下POWER键,触发中断,中断会上报 0x0A这个值到ir_core层,ir_core会把这个值发送给解码驱动ir-nec-decoder.c ,然后按键解码驱动会匹配keymap链表中的那些映射表,然后找到该扫描码对应的keycode值,
然后就会将keycode值上报给Android。分析是如何将每一个遥控器的映射表注册到keymap_list链表中的?
MIRC_Map_Register(&mstar_tv_map);|
//key map functions     ******** 这个函数是定义在 ir_core.c中  所以keymap_list应该也是在这里定义的   *** static LIST_HEAD(keymap_list);   *************** ir_core.c中记录了所有映射表 **********
int MIRC_Map_Register(struct key_map_list *map)
{spin_lock(&key_map_lock);list_add_tail(&map->list, &keymap_list);       //把keymap-mstar-tv.c文件配置的映射表添加到keymap_list中spin_unlock(&key_map_lock);return 0;
}********** 重新回到 mstar_ir.c 文件
mstar_ir_drv_init_module函数
1. 注册设备驱动  2. 注册遥控器映射表到ir_core层 因为在mstar_ir.c中通过platform框架实现设备驱动,所以下面按照平台驱动分析代码static struct platform_driver Mstar_ir_driver = {.probe      = mstar_ir_drv_probe,.remove     = mstar_ir_drv_remove,.suspend    = mstar_ir_drv_suspend,.resume     = mstar_ir_drv_resume,.driver = {
#if defined(CONFIG_OF)   //设备树匹配   在.config文件中有如下定义:  CONFIG_OF=y  ==> 说明该系统支持设备树.of_match_table = mstarir_of_device_ids,
#endif.name   = "Mstar-ir",.owner  = THIS_MODULE,.bus   = &platform_bus_type,}
};设备树匹配表
static struct of_device_id mstarir_of_device_ids[] = {
{.compatible = "Mstar-ir"},{},};设备树配置文件 arch/arm/boot/dts/m7221_an.dts中有如下配置
ir {compatible = "mstar-ir";
};************ 好像匹配是区分大小写的啊  这里大小写不一样。。?   ?????????????????
猜测是非设备树匹配
848_pm_mboot_kernel/kernel$ grep -rn "Mstar-ir" ./
==>
./mstar2/hal/m7221/cpu/arm/chip_arch.c:161:     .name           = "Mstar-ir",匹配成功调用probe函数
static int mstar_ir_drv_probe(struct platform_device *pdev)
{//判断是否匹配成功  !(pdev->name): 传统模式 ,strcmp(pdev->name,"Mstar-ir"):匹配name字段;pdev->id!=0:匹配设备树IRDBG_ERR("wjc_ir mstar_ir.c  mstar_ir_drv_probe \n");int ret=0;if (!(pdev->name) || strcmp(pdev->name,"Mstar-ir")|| pdev->id!=0){ret = -ENXIO;}IRDev.u32IRFlag = 0;ret = mstar_ir_cdev_init();       //创建字符设备if (ret < 0){IRDBG_ERR("mstar_ir_cdev_init Failed! \n");}ret = mstar_ir_creat_sysfs_attr(pdev);   //创建属性文件if (ret < 0){IRDBG_ERR("mstar_ir_creat_sysfs_attr Failed! \n");}ret = mstar_ir_register_device(pdev);         //********* 这里就非常重要了  这个是input子系统操作的地方 这里实现了input子系统3步骤,if (ret < 0){IRDBG_ERR("mstar_ir_register_device Failed! \n");}pdev->dev.platform_data=&IRDev;      //将IR设备驱动对应的设备信息全局变量保存到platform_device中mstar_ir_customer_config();          //加载遥控器配置  即把我们在ir_config.h中配置的信息加载进来#ifdef CONFIG_MIRC_INPUT_DEVICEmstar_ir_init(0);                   //中断操作   ********   IRDev.u32IRFlag |= (IRFLAG_IRENABLE|IRFLAG_HWINITED);
#endifreturn ret;
}驱动基本框架_创建字符设备
static int mstar_ir_cdev_init(void)
{int ret;dev_t dev;IR_PRINT("##### Mstar IR Cdev Init #####\n");//注册设备号if (IRDev.s32IRMajor) {dev = MKDEV(IRDev.s32IRMajor, IRDev.s32IRMinor);ret = register_chrdev_region(dev, MOD_IR_DEVICE_COUNT, MDRV_NAME_IR);} else {ret = alloc_chrdev_region(&dev, IRDev.s32IRMinor, MOD_IR_DEVICE_COUNT, MDRV_NAME_IR);IRDev.s32IRMajor = MAJOR(dev);}if ( 0 > ret) {IRDBG_ERR("Unable to get major %d\n", IRDev.s32IRMajor);return ret;}//cdev操作 将fops和设备号建立联系cdev_init(&IRDev.cDevice, &IRDev.IRFop);if (0!= (ret= cdev_add(&IRDev.cDevice, dev, MOD_IR_DEVICE_COUNT))) {IRDBG_ERR("Unable add a character device\n");unregister_chrdev_region(dev, MOD_IR_DEVICE_COUNT);return ret;}return 0;
}创建属性文件
static int mstar_ir_creat_sysfs_attr(struct platform_device *pdev)
{struct device *dev = &(pdev->dev);int ret = 0;IRDBG_INFO("Debug Mstar IR Creat Sysfs\n");if ((ret = device_create_file(dev, &dev_attr_IRProtocols)))       //系统采用什么协议goto err_out;if ((ret = device_create_file(dev, &dev_attr_IRDebug)))         //打印等级 默认error级别 等级1goto err_out;if ((ret = device_create_file(dev, &dev_attr_IRSpeed)))           //灵敏度,过滤多少个波形   默认0goto err_out;if ((ret = device_create_file(dev, &dev_attr_IREnable)))goto err_out;if ((ret = device_create_file(dev, &dev_attr_IREvent)))goto err_out;
#ifdef CONFIG_MIRC_INPUT_DEVICEif ((ret = device_create_file(dev, &dev_attr_IRTimeout)))goto err_out;
#endifreturn 0;
err_out:return ret;
}测试现象:
IFP:/sys/devices/ir # ls
IRDebug   IRProtocols  driver           modalias  subsystem
IREnable  IRSpeed      driver_override  of_node   uevent
IREvent   IRTimeout    input            power  dev_attr_IRProtocols ==>  IRProtocols文件    ==>猜测生成的文件名称就是dev_attr_IRProtocols后面的这个字符串
IFP:/sys/devices/ir # cat IRProtocols
Eenable Protocols Name:NEC  input子系统操作
int mstar_ir_register_device(struct platform_device *pdev)
{struct mstar_ir_dev *dev = NULL;int ret;
#ifdef CONFIG_MIRC_INPUT_DEVICEint i = 0;
#endif//1. alloc struct ir_dev   ==> mstar_ir_dev即定义了一个红外遥控器的信息结构体 dev = kzalloc(sizeof(*dev), GFP_KERNEL);if (!dev)return -ENOMEM;//2. init locks         初始化自旋锁和互斥体spin_lock_init(&dev->keylock);spin_lock_init(&irq_read_lock);mutex_init(&dev->lock);mutex_lock(&dev->lock);//3. init other argsdev->priv = &IRDev;//4. init readfifo & sem & waitqueueret= kfifo_alloc(&dev->read_fifo,MAX_IR_DATA_SIZE,GFP_KERNEL);        //申请空间  dev->read_fifo: 猜测是用于存放按键中断中读取到的按键扫描码if (ret<0){IRDBG_ERR("ERROR kfifo_alloc!\n");goto out_dev;}sema_init(&dev->sem, 1);                     //信号量init_waitqueue_head(&dev->read_wait);       #ifdef CONFIG_MIRC_INPUT_DEVICE//5. alloc struct input_devdev->input_dev = input_allocate_device();     //***************** input_dev层 步骤1: 申请一个input_dev对象if (!dev->input_dev){ret = -ENOMEM;goto out_kfifo;}input_set_drvdata(dev->input_dev, dev);            // dev_set_drvdata(&dev->dev, data) ==> dev->driver_data = data ==>  dev->input_dev->dev->driver_data = dev  相当于在input_dev中保存它父类mstar_ir_dev的信息dev->input_name = MSTAR_IR_DEVICE_NAME;           //#define MSTAR_IR_DEVICE_NAME    "MStar Smart TV IR Receiver"dev->input_phys = "/dev/ir";dev->driver_name = MSTAR_IR_DRIVER_NAME;  dev->map_num = NUM_KEYMAP_MSTAR_TV; //default :mstar keymap//6. init&register  input device   ===> *************** input_dev层步骤2   初始化input_dev对象dev->input_dev->id.bustype = BUS_I2C;         //input_dev结构体成员分析: https://blog.csdn.net/ielife/article/details/7814108   dev->input_dev->id.vendor = INPUTID_VENDOR;       //#define INPUTID_VENDOR 0x3697UL;   这4个属性bustype,vendor,product,vendor是input_dev和input_handler匹配的条件  dev->input_dev->id.product = INPUTID_PRODUCT; //#define INPUTID_PRODUCT 0x0001;     也决定了我们android中对应的kl文件的命名   Vendor_3697_Product_0001.kldev->input_dev->id.version = INPUTID_VERSION;    //#define INPUTID_VERSION 0x0001; set_bit(EV_KEY, dev->input_dev->evbit);     //表示该input设备要上报的事件类型包括: EV_KEY(按键事件),EV_REP(重复事件),EV_MSC中的MSC_SCAN表示扫描按键  ==>表示可以向上上报一个scancode(scancode==遥控器规格书中的值)set_bit(EV_REP, dev->input_dev->evbit);      set_bit(EV_MSC, dev->input_dev->evbit);       set_bit(MSC_SCAN, dev->input_dev->mscbit);    dev->input_dev->dev.parent = &(pdev->dev);    dev->input_dev->phys = dev->input_phys;       //input_phys: 设备节点名称  *******   "/dev/ir"dev->input_dev->name = dev->input_name;        //输入设备的名称    "MStar Smart TV IR Receiver"for (i = 0; i<KEY_CNT; i++)                    //注册所有按键{__set_bit(i, dev->input_dev->keybit);    //设置遥控器按键对应的按键值  input.h中定义的那个按键值}__clear_bit(BTN_TOUCH,dev->input_dev->keybit);// IR device without this caseret = input_register_device(dev->input_dev);        //***************** input_dev层 步骤3: 注册input_dev对象到input_core层中存放所有input_dev对象的链表中,并尝试去driver_list中匹配该device对应的driver,匹配成功执行connect函数,然后创建一个handled对象将device和driver建立联系
#endif//7. register IR Data ctrlret = MIRC_Data_Ctrl_Init(dev);                    //********** 非常关键  注册IR 数据kthread线程MIRC_Data_Ctrl_Thread从kfifo中获取原始的按键数据,并将数据发送给匹配的decoder文件去解析if (ret < 0){IRDBG_ERR("Init IR Raw Data Ctrl Failed!\n");goto out_unlock;return -EINVAL;}IRDev.pIRDev= dev;mutex_unlock(&dev->lock);return 0;out_unlock:mutex_unlock(&dev->lock);
#ifdef CONFIG_MIRC_INPUT_DEVICEinput_unregister_device(dev->input_dev);dev->input_dev = NULL;
#endif
out_kfifo:kfifo_free(&dev->read_fifo);
out_dev:kfree(dev);return ret;
}struct mstar_ir_dev {struct mutex             lock;              //lock for handle mstar_ir_dev menber variablesstruct ir_raw_data_ctrl  *raw;              //shot count handle structu64                      enabled_protocols; //surport decodersspinlock_t               keylock;           //spinlockvoid                     *priv;             //IR_SPEED_LEVEL_e         speed;             //ir speed levelu8                       filter_flag;struct semaphore         sem;wait_queue_head_t        read_wait;struct kfifo_rec_ptr_1   read_fifo;         //for user space read
#ifdef CONFIG_MIRC_INPUT_DEVICEstruct input_dev         *input_dev;bool                     keypressed;u32                      last_keycode;u32                      last_scancode;const char               *input_name;const char               *input_phys;char                     *driver_name;u32                       map_num;
#endifIR_Mode_e                ir_mode;IR_Profile_t             support_ir[IR_SUPPORT_MAX];u8                       support_num;
};/** Used to (un)register raw event clients  用于(un)注册原始事件客户端   */
int MIRC_Data_Ctrl_Init(struct mstar_ir_dev *dev)
{int ret;if (!dev)return -EINVAL;dev->raw = kzalloc(sizeof(*dev->raw), GFP_KERNEL);   //申请raw内存空间 ,raw中存放原始按键信息if (!dev->raw)return -ENOMEM;dev->raw->dev = dev;dev->enabled_protocols = 0;             //支持的解码协议 ret= kfifo_alloc(&dev->raw->kfifo,         sizeof(struct ir_raw_data) * MAX_IR_DATA_SIZE,              GFP_KERNEL);if (ret < 0)goto out;spin_lock_init(&dev->raw->lock);      //初始化自旋锁 dev->raw->thread = kthread_run(MIRC_Data_Ctrl_Thread, dev->raw,"Mstar_ir"); //kthread_run是一个宏定义,功能是创建并启动内核线程 创建一个名为"Mstar_ir"的线程,将dev->raw作为参数传递给该线程对应的线程函数MIRC_Data_Ctrl_Threadif (IS_ERR(dev->raw->thread)) {ret = PTR_ERR(dev->raw->thread);goto out;}#ifdef CONFIG_MIRC_INPUT_DEVICE// init timer event up         初始化一个定时器dev->raw->keyup_jiffies = jiffies;//record timeout for event upinit_timer(&(dev->raw->timer_keyup));dev->raw->timer_keyup.function = MIRC_Timer_Proc;       //超时处理函数dev->raw->timer_keyup.expires =jiffies+ msecs_to_jiffies(eventtimeout);     //设置的定时时间dev->raw->timer_keyup.data = (unsigned long)dev;        //传递给超时函数的参数
#endifreturn 0;
out:kfree(dev->raw);dev->raw = NULL;return ret;
}raw成员: 保存着原始按键值
struct ir_raw_data_ctrl {struct task_struct      *thread;       //线程spinlock_t              lock;struct kfifo_rec_ptr_1  kfifo;           /* fifo for the pulse/space durations  脉冲/空间持续时间的fifo*/struct mstar_ir_dev     *dev;struct ir_scancode      prev_sc;      //前一个扫描码struct ir_scancode      this_sc;        //当前扫描码u8                      u8RepeatFlag;
#ifdef CONFIG_MIRC_INPUT_DEVICEunsigned long           keyup_jiffies;     //time recordstruct timer_list       timer_keyup;       //定时器  事件发生定时器
#endif
};"Mstar_ir"线程分析: 关键步骤总结分析   该函数定义在ir_core.c
static int MIRC_Data_Ctrl_Thread(void *data)
{//1. 从raw->kfifo中获取数据到ev中,获取的元素数量为sizeof(ev)   ==这个fifo中的数据就是按键的原始值retval = kfifo_out(&raw->kfifo, &ev, sizeof(ev));//2. 将获取到的数据发送给对应的decoder匹配协议解析数据   mutex_lock(&ir_decoder_lock);      else //hw decoder    //对应IR_TYPE_FULLDECODE_MODE NEC full解码 支持两个头码的遥控器   在IR_MSTAR_DTV.h 中有如下定义:#define IR_MODE_SEL             IR_TYPE_FULLDECODE_MODE{raw->this_sc.scancode = ev.duration;raw->u8RepeatFlag = ev.pulse;get_scancode = 1; //是否获取到扫描值的标志  1=获取扫描值,读后面就是对这个获取到的按键扫描值进行处理IRDBG_MSG("keycode =%x  repeatflag =%d \n",raw->this_sc.scancode,raw->u8RepeatFlag);}//3. 匹配映射表,获取映射后按键对应的keycode值,上报event事件MIRC_Keydown(raw->dev, raw->this_sc.scancode);MIRC_Keyup(raw->dev);
}//设置遥控器配置    就是ir_config.h中的配置  配置遥控器的打印级别,速率,支持的遥控器的协议和头码
static void mstar_ir_customer_config(void)
{MIRC_Set_IRDBG_Level(ir_dbglevel);     MIRC_Set_IRSpeed_Level(ir_speed);MIRC_IRCustomer_Config(ir_config,IR_SUPPORT_NUM);return ;
}//中断操作
static void mstar_ir_init(int bResumeInit)
{int ret = 0;struct mstar_ir_dev *dev = IRDev.pIRDev;//Prevent residual value exist in sw fifo when system wakeup from standby status    防止系统从待机状态唤醒时sw fifo中存在剩余值REG(REG_IR_CTRL) &= (0xFF00); //disable IR ctrl regs                                   失能IR控制寄存器REG(REG_IR_SEPR_BIT_FIFO_CTRL) &= (0xFF); //disable IR FIFO settingsif(NULL == dev)return ;switch(dev->ir_mode)          //根据不同的解码模式来做好相应的寄存器配置{case IR_TYPE_FULLDECODE_MODE:            //我们代码中设置的是硬解码  {mstar_ir_hw_fulldecode_config();}break;case IR_TYPE_RAWDATA_MODE :{mstar_ir_hw_rawdecode_config();}break;case IR_TYPE_HWRC5_MODE :{mstar_ir_hw_rc5decode_config();}break;case IR_TYPE_HWRC5X_MODE :{mstar_ir_hw_rc5decode_config();}break;case IR_TYPE_HWRC6_MODE :{mstar_ir_hw_rc6decode_config();}break;case IR_TYPE_SWDECODE_MODE :{mstar_ir_sw_decode_config();}break;default:IRDBG_INFO("No IR Mode Support!\n");break;}}if ((!bResumeInit) &&(ir_irq_sel ==0xFF))        //这里猜测是如果这个解码方式前面没有配置过,在这里配置中断{ir_irq_enable = 0;if((dev->ir_mode == IR_TYPE_HWRC5_MODE)||(dev->ir_mode == IR_TYPE_HWRC5X_MODE)||(dev->ir_mode == IR_TYPE_HWRC6_MODE)){#ifdef E_FIQEXPL_IR_INT_RCret = request_irq(E_FIQEXPL_IR_INT_RC, _MDrv_IR_RC_ISR, SA_INTERRUPT, "IR_RC", &IRDev);ir_irq_sel = 1;#elseprintk("E_FIQEXPL_IR_INT_RC IRQ not Found\n");#endif}else{ //申请中断 ret = request_irq(INT_NUM_IR_ALL, _MDrv_IR_ISR, SA_INTERRUPT, "IR", &IRDev);ir_irq_sel = 0;      //这次申请了说明该解码方式以及配置好了,防止后面再次配置}if (ret < 0){IRDBG_ERR("IR IRQ registartion ERROR!\n");}else{ir_irq_enable = 1;IRDBG_INFO("IR IRQ registartion OK!\n");}}return ;
}//做好硬解码的寄存器配置  头码 + NEC解码的时序
static void mstar_ir_hw_fulldecode_config(void)
{//1. set customer code0REG(REG_IR_CCODE) = ((u32IRHeaderCode[0]&0xff)<<8)|((u32IRHeaderCode[0]>>8) &0xff);//2. set customer code1REG(REG_IR_CCODE1) =((u32IRHeaderCode[1]&0xff)<<8)|((u32IRHeaderCode[1]>>8) &0xff);if(ir_config_mode == IR_TYPE_FULLDECODE_MODE){return ;}ir_config_mode = IR_TYPE_FULLDECODE_MODE;  //表示现在这个系统的解码配置为: 硬解码IR_PRINT("##### Mstar IR HW Full Decode Config #####\n");//3. set NEC timing & FIFO depth 16 bytes_MDrv_IR_Timing();REG(REG_IR_SEPR_BIT_FIFO_CTRL) = 0xF00UL;//[10:8]: FIFO depth, [11]:Enable FIFO fullREG(REG_IR_CCODE1_CHK_EN) |= IR_CCODE1_CHK_EN;//4. Glitch Remove (for prevent Abnormal waves)  reg_ir_glhrm_num = 4拢卢reg_ir_glhrm_en = 1REG(REG_IR_GLHRM_NUM) = 0x804UL;//5.set full decode modeREG(REG_IR_GLHRM_NUM) |= (0x3UL <<12);//6.set Data bits ===>two byte customer code +IR DATA bits(32bits)+timeout clear+timeout value 4 high bitsREG(REG_IR_TIMEOUT_CYC_H_CODE_BYTE) = IR_CCB_CB | 0x30UL | ((IR_RP_TIMEOUT >> 16) & 0x0FUL);//7.set IR clock DIVREG(REG_IR_CKDIV_NUM_KEY_DATA) = IR_CKDIV_NUM;   //set clock div --->IR clock =1MHZ//8.reg_ir_wkup_key_sel ---> out key valueREG(REG_IR_FIFO_RD_PULSE) |= 0x0020UL; //wakeup key sel//9. set ctrl enable IRREG(REG_IR_CTRL) = IR_TIMEOUT_CHK_EN |IR_INV            |IR_RPCODE_EN      |IR_LG01H_CHK_EN   |IR_DCODE_PCHK_EN  |IR_CCODE_CHK_EN   |IR_LDCCHK_EN      |IR_EN;
}NEC红外编码的发送波形
//Time, upper bound, low bound
{9000,20,(-20)},// header code time
{4500,20,(-20)},//off code time
{2500,20,(-20)},                    // off code repeat time
{560,35,(-30)},                     // logical 0/1 high time
{1120,20,(-20)},                    // logical 0 time
{2240,20,(-20)},                    // logical 1 time0代表低电平,1=高电平
引导码: 0:9ms,1:4.5ms
引导重复码: 0:9ms,1:2.24ms
数据1: 0:0.56ms.1:(2.25-0.56)ms
数据0: 0:0.56ms.1:(1.12-0.56)ms一个完整的数据波形
引导码/引导重复码  地址  地址取反    数据  数据取反硬解码模式中配置中断: 申请一个中断号为INT_NUM_IR_ALL,中断名称为"IR",中断处理函数为_MDrv_IR_ISR的中断
ret = request_irq(INT_NUM_IR_ALL, _MDrv_IR_ISR, SA_INTERRUPT, "IR", &IRDev);
SA_INTERRUPT: 中断处理的属性,若设置SA_INTERRUPT,标明中断处理程序是快速处理程序,快速处理程序被调用时屏蔽所有中断   //将遥控器按键中断设置成快速中断,为了对按键进行快速响应分析中断处理函数 **************************
irqreturn_t _MDrv_IR_ISR(int irq, void *dev_id)
{IRModHandle *mstar_dev = dev_id;      //获取申请中断时传递进来的参数  IRDev指针 IR设备驱动信息结构体int ret = 0;if (NULL == mstar_dev)return -EINVAL;if(mstar_dev->pIRDev->ir_mode == IR_TYPE_FULLDECODE_MODE)          //硬解码ret = mstar_ir_isr_getdata_fulldecode(mstar_dev);else if(mstar_dev->pIRDev->ir_mode == IR_TYPE_RAWDATA_MODE)ret = mstar_ir_isr_getdata_rawdecode(mstar_dev);else{spin_lock(&irq_read_lock);while ( ((REG(REG_IR_SHOT_CNT_H_FIFO_STATUS) & IR_FIFO_EMPTY) != IR_FIFO_EMPTY)){ret = mstar_ir_isr_getdata_swdecode(mstar_dev);if(ret < 0){spin_unlock(&irq_read_lock);return IRQ_HANDLED;}REG(REG_IR_FIFO_RD_PULSE) |= 0x0001;}spin_unlock(&irq_read_lock);}if(ret >= 0)MIRC_Data_Wakeup(mstar_dev->pIRDev);return IRQ_HANDLED;
}mstar_ir_isr_getdata_fulldecode(mstar_dev)   //获取触发中断的按键值,并将按键值写入到fifo中*******|
int mstar_ir_isr_getdata_fulldecode(IRModHandle *mstar_dev)
{int ret = 0;u8 u8Ir_Index = 0;u8 u8Keycode = 0;u8 u8Repeat = 0;u32 u32IRSpeed = 0;if(REG(REG_IR_SHOT_CNT_H_FIFO_STATUS) & IR_FIFO_EMPTY){return -1;}DEFINE_IR_RAW_DATA(ev);u8Keycode = REG(REG_IR_CKDIV_NUM_KEY_DATA) >> 8;    u8Repeat = (REG(REG_IR_SHOT_CNT_H_FIFO_STATUS) & IR_RPT_FLAG)? 1 : 0;u8Ir_Index = REG(REG_IR_SHOT_CNT_H_FIFO_STATUS) & 0x40? 1 : 0;//IR remote controller TX1 send flagREG(REG_IR_FIFO_RD_PULSE) |= 0x0001;ev.duration = (u32IRHeaderCode[u8Ir_Index]<<8) | u8Keycode;            //ev中保存了该中断触发导致的按键的原始按键值(扫描值)ev.pulse = u8Repeat;IRDBG_INFO(" u8Ir_Index = %d\n",u8Ir_Index);IRDBG_INFO("[Full Decode] Headcode =%x Key =%x\n",u32IRHeaderCode[u8Ir_Index],u8Keycode);mstar_dev->pIRDev->map_num = mstar_dev->pIRDev->support_ir[u8Ir_Index].u32HeadCode;u32IRSpeed = mstar_dev->pIRDev->support_ir[u8Ir_Index].u32IRSpeed;if(u8Repeat){if (((u32IRSpeed != 0)&&( u8RepeatCount < (u32IRSpeed - 1)))|| ((u32IRSpeed == 0)&&(u8RepeatCount < mstar_dev->pIRDev->speed))){u8RepeatCount ++;mstar_ir_nec_clearfifo();return -1;}}else{u8RepeatCount = 0;}ret = MIRC_Data_Store(mstar_dev->pIRDev, &ev);          //********** 非常关键  将触发中断的按键值写入fifo中if (ret < 0){IRDBG_ERR("Store IR data Error!\n");}mstar_ir_nec_clearfifo();return ret;
}//ir_core.c文件  *********** 这里就失ir_core层中保存了按键中断触发分按键值了   前面在mstar_ir.c中的驱动入口函数中将解码映射表也注册到了ir_core中  *****  所以在ir_core中万事俱备(有原始值,有映射表),开始解码
int MIRC_Data_Store(struct mstar_ir_dev *dev, struct ir_raw_data *ev)
{if (!dev->raw)return -EINVAL;if (kfifo_in(&dev->raw->kfifo, ev, sizeof(*ev)) != sizeof(*ev))   //kfifo_in函数就是将数据写入到fifo中   / kfifo_in函数就是读取fifo中的数据return -ENOMEM;return 0;
}MIRC_Data_Wakeup(mstar_dev->pIRDev);   //唤醒"mstar_ir"线程来读取fifo中的按键值,然后解析获取keycode,然后input_event上报数据到eventX文件|
void MIRC_Data_Wakeup(struct mstar_ir_dev *dev)
{unsigned long flags;if (!dev->raw)return;spin_lock_irqsave(&dev->raw->lock, flags);wake_up_process(dev->raw->thread);           //唤醒执行dev->raw->thread线程  在 mstar_ir_register_device()-> MIRC_Data_Ctrl_Init(dev) --> dev->raw->thread = kthread_run(MIRC_Data_Ctrl_Thread, dev->raw,"Mstar_ir ; ===>所以这里就会执行Mstar_ir线程对应的MIRC_Data_Ctrl_Thread函数spin_unlock_irqrestore(&dev->raw->lock, flags);
}//再次具体分析 读取fifo中按键原始值,匹配映射表,解析数据,input系统上报数据的实现 ****************
static int MIRC_Data_Ctrl_Thread(void *data)
{//1. 读取fifo中按键原始值retval = kfifo_out(&raw->kfifo, &ev, sizeof(ev));  //kfifo_out函数就是读取fifo中的数据,并将读取到的数据保存在ev中//2. 获取原始值之后就需要解析原始值    解析按键按下时的原始值MIRC_Keydown(raw->dev, raw->this_sc.scancode);}//解析按键按下时的原始值
void MIRC_Keydown(struct mstar_ir_dev *dev, int scancode)
{unsigned long flags;u32 keycode = MIRC_Keycode_From_Map(dev->map_num, scancode);           //非常重要 这里就是匹配映射表,获取该原始扫描按键扫描值对应的keycode值if (keycode == KEY_RESERVED){IRDBG_ERR("Key Map Match scancode Failed!\n");return ;}spin_lock_irqsave(&dev->keylock, flags);MIRC_Do_Keydown(dev, scancode, keycode);                              //获取keycode之后上报按键按下的数据spin_unlock_irqrestore(&dev->keylock, flags);
}u32 keycode = MIRC_Keycode_From_Map(dev->map_num, scancode);   |
static u32 MIRC_Keycode_From_Map(u32 keymapnum,u32 scancode)
{struct key_map_list *map = NULL;u32 keycode = KEY_RESERVED;u8 i=0;bool match_flag = false;spin_lock(&key_map_lock);list_for_each_entry(map, &keymap_list, list)  //匹配映射表{if (((scancode>>8) == map->map.headcode) || (keymapnum == map->map.headcode)){IRDBG_INFO("[Match] Keymap Name: %s\n",map->map.name);match_flag = true;break;//find IR map}}if (match_flag)      //获取原始值映射后的keycode值{for (i = 0; i<map->map.size; i++)  //map.size:映射表中的映射对数量{if (((map->map.scan[i].scancode&0xff) == (scancode&0xff)) && ((scancode>>8) == map->map.headcode))  //选这里 (map->map.scan[i].scancode&0xff) == (scancode&0xff)) 在映射表中找到匹配的scancode{IRDBG_INFO("[Match 8bit Scancode] Scancode =%x -----> Keycode= %x\n",scancode,map->map.scan[i].keycode); //,map->map.scan[i].keycode: 就是该scancode对应的keycode值keycode = map->map.scan[i].keycode;break;//match scancode, and return keycode}if ( ((map->map.scan[i].scancode&0xffff) == (scancode&0xffff)) && (keymapnum == map->map.headcode)){IRDBG_INFO("[Match 16bit Scancode] Scancode =%x -----> Keycode= %x\n",scancode,map->map.scan[i].keycode);keycode = map->map.scan[i].keycode;break;//match scancode, and return keycode}}}spin_unlock(&key_map_lock);return keycode;
}   keymapnum的值?
mstar_ir_register_device() 中dev->map_num = NUM_KEYMAP_MSTAR_TV; //default :mstar keymap   这里就一直调即可
所以keymapnum == NUM_KEYMAP_MSTAR_TV     //#define    NUM_KEYMAP_MSTAR_TV                   0x4040   这个值就是我们在ir_common.h中定义的不同客户的头码值,与ir_config.h中定义的遥控器头码值相同 {IR_TYPE_NEC, NUM_KEYMAP_MSTAR_TV, 0},在驱动入口函数中有如下步骤 加载解码驱动,在该解码驱动中有如下操作,将映射包添加到ir_core层的key_map_list链表中
int MIRC_Map_Register(struct key_map_list *map)
{spin_lock(&key_map_lock);list_add_tail(&map->list, &keymap_list);       //把keymap-mstar-tv.c文件配置的映射表map添加到keymap_list中  在ir_core层中创建了keymap_list链表:static LIST_HEAD(keymap_list); spin_unlock(&key_map_lock);return 0;
}匹配映射表
list_for_each_entry(map, &keymap_list, list)  //遍历映射表
{if (((scancode>>8) == map->map.headcode) || (keymapnum == map->map.headcode))  //匹配头码  keymapnum == map->map.headcode {IRDBG_INFO("[Match] Keymap Name: %s\n",map->map.name);match_flag = true;     //匹配成功标志位置turebreak;//find IR map}
}==> 例如:nec解码驱动  我们注册的映射表:mstar_tv_map  故: map->map.headcode == NUM_KEYMAP_MSTAR_TV ************* 这不就匹配上了吗?
static struct key_map_list mstar_tv_map = {.map = {.scan     = mstar_tv,             //具体的映射关系表.size     = ARRAY_SIZE(mstar_tv),    //多少个按键映射对.name     = NAME_KEYMAP_MSTAR_TV,    //该按键映射表的名称   在ir_common.h中定义 #define NAME_KEYMAP_MSTAR_TV                  "ir-mstar-tv"  .headcode = NUM_KEYMAP_MSTAR_TV,  //头码               在ir_common.h中定义 #define    NUM_KEYMAP_MSTAR_TV                   0x4040}
};匹配成功开始解码,获取keycode,这个看前面的代码注释获取keycode值之后,就需要把keycode值上报
MIRC_Do_Keydown(dev, scancode, keycode);|
static void MIRC_Do_Keydown(struct mstar_ir_dev *dev, int scancode,u32 keycode)
{bool new_event = !dev->keypressed ||dev->last_scancode != scancode;        //new_event表示该按键是否为新按键 (是否和上一个按键不同)if (new_event && dev->keypressed)MIRC_Do_Keyup(dev, false);input_event(dev->input_dev, EV_MSC, MSC_SCAN, scancode);        // ************ 底层需要我们自己实现的驱动的最后一步 上报扫描码if (new_event && keycode != KEY_RESERVED) {  //非常/* Register a keypress */dev->keypressed = true;dev->last_scancode = scancode;dev->last_keycode = keycode;input_report_key(dev->input_dev, keycode, 1);                //这个就是当是新按键被按下,需要重新上报一下新的keycode值     1:代表按键按下   }input_sync(dev->input_dev);          //上报同步事件 即将上报信息立刻方法出去
}后面的流程就是input子系统的流程了: 大概流程为
上报的数据,分发给订阅了该input_dev的input_handle,然后input_handle在此将数据分发给与它相关的input_handle,最后在input_handle中的event方法中将数据分发给所有打开该设备文件的缓冲区中  所以现在 你可以通过  getevent -l /dev/input/event0来查看上报信息   input子系统的设备文件就是eventX类型,主设备号13,这些都是在input_hander层就写死的总结: 遥控器上报数据的流程   ***************
加载遥控器驱动 Mstar_ir.c,加载解码驱动将映射表注册到ir_core层中的keymap_list链表中
遥控器按键按下;进入中断处理函数,获取按键scancode值,并将值保存在fifo缓冲区中,好像"MSTAR_IR"线程; "MSTAR_IR"线程读取fifo中的数据,然后开始解析数据,先获取该遥控器驱动匹配的映射表,然后根据scancode值找到相应的keycode值,最后input上报按键值调试方法: mstar_遥控器文档  :设置printk和IRDEUG的打印等级查看打印信息
IFP:/ # [  310.833687] [IR ERR ]: _MDrv_IR_ISR[629]: [  310.838228] ************* ^_^ ************* [wjc_ir] [mstar_ir.c]  _MDrv_IR_ISR enter request_irq
[  310.847193] [  310.848861] [IR ERR ]: _MDrv_IR_ISR[635]:
[  310.853571] ************* ^_^ ************* [wjc_ir] [mstar_ir.c]  _MDrv_IR_ISR mstar_dev->pIRDev->ir_mode = 5
*********** WTF  ir_mode不是IR_TYPE_FULLDECODE_MODE,而是IR_TYPE_SWDECODE_MODE,
typedef enum{IR_TYPE_FULLDECODE_MODE = 0,   /* NEC full deocoder mode */ nec full解码,支持两个头码遥控器  他只会解NEC编码的遥控器IR_TYPE_RAWDATA_MODE,          /* NEC raw decoder mode*/IR_TYPE_HWRC5_MODE,            /* RC5 decoder mode*/IR_TYPE_HWRC5X_MODE,           /* RC5_ext decoder mode*/IR_TYPE_HWRC6_MODE,            /* RC6_mode0 decoder mode*/IR_TYPE_SWDECODE_MODE,         /* SW decoder mode*/       软解码,支持多种不同协议IR_TYPE_MAX_MODE
}IR_Mode_e;在ir_config.h中 我们定义了两种编码的遥控器   ==> 所以应该采用IR_TYPE_SWDECODE_MODE 解码方式
#define IR_SUPPORT_NUM 2
static IR_Profile_t ir_config[IR_SUPPORT_NUM]=
{// {IR_TYPE_NEC,NUM_KEYMAP_MSTAR_TV,0},         // Mstar IR customer code//{IR_TYPE_TOSHIBA,NUM_KEYMAP_SKYWORTH_TV,0},   // skyworth toshiba ir//{IR_TYPE_NEC,NUM_KEYMAP_CHANGHONG_TV,0},      // changhong_RL78B /Toshiba CT-90436 IR customer code//{IR_TYPE_NEC,NUM_KEYMAP_HISENSE_TV,0},        // Hisense IR customer code//{IR_TYPE_RCA,NUM_KEYMAP_TCL_RCA_TV,0},        // TCL RCA  customer code//{IR_TYPE_P7051,NUM_KEYMAP_P7051_STB,0},       // Panasonic 7051 IR customer code//{IR_TYPE_RC5, NUM_KEYMAP_RC5_TV, 0},            // RC5 customer code{IR_TYPE_NEC, NUM_KEYMAP_MSTAR_TV, 0},//{IR_TYPE_RC6,NUM_KEYMAP_KATHREIN_TV,0},       // Kathrein RC6 customer code//{IR_TYPE_KONKA,NUM_KEYMAP_KONKA_TV,2},{IR_TYPE_PANASONIC,NUM_KEYMAP_PANASONIC_TV,2},
};//Description  of IR
typedef struct IR_Profile_s {IR_Type_e eIRType;u32 u32HeadCode;u32 u32IRSpeed;
}IR_Profile_t;代码具体实现流程如下 即_MDrv_IR_ISR mstar_dev->pIRDev->ir_mode = 5 这个的来源
在遥控器驱动 Mstar_ir.c中
驱动入口函数      ret = platform_driver_register(&Mstar_ir_driver);
probe函数         mstar_ir_customer_config(); |MIRC_IRCustomer_Config(ir_config,IR_SUPPORT_NUM);|MIRC_IRCustomer_Init();int MIRC_IRCustomer_Init(void)
{struct mstar_ir_dev *dev = IRDev.pIRDev;IR_Type_e IrType = IR_TYPE_MAX;u8 i = 0;bool bIsDiffType = false;if (NULL == dev)return -EINVAL;for(i = 0; i<dev->support_num; i++)     //#define IR_SUPPORT_NUM 2   {if(dev->support_ir[i].eIRType == dev->support_ir[i+1].eIRType)   {IrType = dev->support_ir[i].eIRType;bIsDiffType = false;}else         //会执行这里的代码 因为IR_TYPE_PANASONIC !=  IR_TYPE_NEC{dev->ir_mode = IR_TYPE_SWDECODE_MODE;  //这里就是ir_mode的来来源bIsDiffType = true;break;}}if(bIsDiffType == false)  //不走这里{if(IrType == IR_TYPE_NEC){if(dev->support_num <=2){//set customer code0u32IRHeaderCode[0] = dev->support_ir[0].u32HeadCode;u32IRHeaderCode[1] = dev->support_ir[1].u32HeadCode;dev->ir_mode = IR_TYPE_FULLDECODE_MODE;}else{dev->ir_mode = IR_TYPE_RAWDATA_MODE;}}else if(IrType == IR_TYPE_RC5){dev->ir_mode = IR_TYPE_HWRC5_MODE;}else if(IrType == IR_TYPE_RC5X){dev->ir_mode = IR_TYPE_HWRC5X_MODE;}else if(IrType == IR_TYPE_RC6_MODE0){dev->ir_mode = IR_TYPE_HWRC6_MODE;}else{dev->ir_mode = IR_TYPE_SWDECODE_MODE;}}if(IR_TYPE_SWDECODE_MODE == dev->ir_mode)        //这里要走路{for(i = 0;i<(dev->support_num);i++){switch(dev->support_ir[i].eIRType){case IR_TYPE_NEC :nec_decode_init();     //这个解码驱动有加载  我们中性的是nec编码,等等如何解码后面在分析  这个解码 == 根据接收到的遥控器发送的波形的时序,如何根据NEC协议解码 *******break;case IR_TYPE_RCA:rca_decode_init();break;case IR_TYPE_P7051:p7051_decode_init();break;case IR_TYPE_RC5:case IR_TYPE_RC5X:rc5_decode_init();break;case IR_TYPE_RC6_MODE0:case IR_TYPE_RC6:rc6_decode_init();break;case IR_TYPE_TOSHIBA:toshiba_decode_init();break;case IR_TYPE_RCMM:rcmm_decode_init();break;case IR_TYPE_KONKA:konka_decode_init();break;case IR_TYPE_PANASONIC:panasonic_decode_init();  //这个解码驱动要加载break;default :IRDBG_ERR("IR TYPE not define,please check!!!\n");break;}}}return 0;
}故前面的代码跟读就有误了 ******************** WTF  先按下source按键,在按下menu按键后,menu按键的log分析,******我们这里只是跟踪了按键按下时的流程 **** ****** wjc_ir是我们添加的打印   第一次尝试  没有进入到按键按下处理的函数的log
IFP:/ # [  281.707665] [IR ERR ]: _MDrv_IR_ISR[630]:
[  281.712385] ************* ^_^ ************* [wjc_ir] [mstar_ir.c]  _MDrv_IR_ISR enter request_irq
[  281.721347] [  281.723015] [IR ERR ]: _MDrv_IR_ISR[636]:
[  281.727724] ************* ^_^ ************* [wjc_ir] [mstar_ir.c]  _MDrv_IR_ISR mstar_dev->pIRDev->ir_mode = 5                //软解码
[  281.737728] [  281.739398] [IR ERR ]: mstar_ir_isr_getdata_swdecode[597]:
[  281.745582] ************* ^_^ ************* [wjc_ir] [mstar_ir.c]  mstar_ir_isr_getdata_swdecode                             //获取按下menu键,遥控器发送的波形 duration/pulse值
[  281.754544] [  281.756214] [IR ERR ]: MIRC_Data_Store[247]:
[  281.761184] ************* ^_^ ************* [wjc_ir] [mstar_ir.c]  MIRC_Data_Store add scancode                              //将获取到的波形数据存放在fifo中     循环处理9次
[  281.769971] [  281.771644] [IR ERR ]: mstar_ir_isr_getdata_swdecode[597]:
[  281.777829] ************* ^_^ ************* [wjc_ir] [mstar_ir.c]  mstar_ir_isr_getdata_swdecode
[  281.786791] [  281.788462] [IR ERR ]: MIRC_Data_Store[247]:
[  281.793430] ************* ^_^ ************* [wjc_ir] [mstar_ir.c]  MIRC_Data_Store add scancode
[  281.802217] [  281.803890] [IR ERR ]: mstar_ir_isr_getdata_swdecode[597]:
[  281.810075] ************* ^_^ ************* [wjc_ir] [mstar_ir.c]  mstar_ir_isr_getdata_swdecode
[  281.819036] [  281.820707] [IR ERR ]: MIRC_Data_Store[247]:
[  281.825676] ************* ^_^ ************* [wjc_ir] [mstar_ir.c]  MIRC_Data_Store add scancode
[  281.834462] [  281.836132] [IR ERR ]: mstar_ir_isr_getdata_swdecode[597]:
[  281.842317] ************* ^_^ ************* [wjc_ir] [mstar_ir.c]  mstar_ir_isr_getdata_swdecode
[  281.851280] [  281.852951] [IR ERR ]: MIRC_Data_Store[247]:
[  281.857920] ************* ^_^ ************* [wjc_ir] [mstar_ir.c]  MIRC_Data_Store add scancode
[  281.866707] [  281.868377] [IR ERR ]: mstar_ir_isr_getdata_swdecode[597]:
[  281.874561] ************* ^_^ ************* [wjc_ir] [mstar_ir.c]  mstar_ir_isr_getdata_swdecode
[  281.883523] [  281.885192] [IR ERR ]: MIRC_Data_Store[247]:
[  281.890162] ************* ^_^ ************* [wjc_ir] [mstar_ir.c]  MIRC_Data_Store add scancode
[  281.898951] [  281.900621] [IR ERR ]: mstar_ir_isr_getdata_swdecode[597]:
[  281.906806] ************* ^_^ ************* [wjc_ir] [mstar_ir.c]  mstar_ir_isr_getdata_swdecode
[  281.915772] [  281.917442] [IR ERR ]: MIRC_Data_Store[247]:
[  281.922411] ************* ^_^ ************* [wjc_ir] [mstar_ir.c]  MIRC_Data_Store add scancode
[  281.931199] [  281.932869] [IR ERR ]: mstar_ir_isr_getdata_swdecode[597]:
[  281.939055] ************* ^_^ ************* [wjc_ir] [mstar_ir.c]  mstar_ir_isr_getdata_swdecode
[  281.948017] [  281.949685] [IR ERR ]: MIRC_Data_Store[247]:
[  281.954654] ************* ^_^ ************* [wjc_ir] [mstar_ir.c]  MIRC_Data_Store add scancode
[  281.963443] [  281.965113] [IR ERR ]: mstar_ir_isr_getdata_swdecode[597]:
[  281.971298] ************* ^_^ ************* [wjc_ir] [mstar_ir.c]  mstar_ir_isr_getdata_swdecode
[  281.980261] [  281.981930] [IR ERR ]: MIRC_Data_Store[247]:
[  281.986899] ************* ^_^ ************* [wjc_ir] [mstar_ir.c]  MIRC_Data_Store add scancode
[  281.995687] [  281.997357] [IR ERR ]: mstar_ir_isr_getdata_swdecode[597]:
[  282.003542] ************* ^_^ ************* [wjc_ir] [mstar_ir.c]  mstar_ir_isr_getdata_swdecode
[  282.012504] [  282.014174] [IR ERR ]: MIRC_Data_Store[247]:
[  282.019142] ************* ^_^ ************* [wjc_ir] [mstar_ir.c]  MIRC_Data_Store add scancode
[  282.027929] [  282.029602] [IR ERR ]: MIRC_Data_Wakeup[264]:
[  282.034657] ************* ^_^ ************* [wjc_ir] [ir_core.c]  MIRC_Data_Wakeup Wakeup thread                             //将数据都存放在fifo之后,唤醒线程去处理数据
[  282.043532] [  282.045232] [IR ERR ]: MIRC_Data_Ctrl_Thread[147]:
[  282.050900] ************* ^_^ ************* [wjc_ir] [ir_core.c]  MIRC_Data_Ctrl_Thread thread_get_scancode = 15            //这里就是将前面存放在fifo中的数据取出来 这个数据不是scancode 而是duration,实际上应该会有pulse值
[  282.061299] [  282.062997] [IR ERR ]: MIRC_Data_Ctrl_Thread[147]:
[  282.068523] ************* ^_^ ************* [wjc_ir] [ir_core.c]  MIRC_Data_Ctrl_Thread thread_get_scancode = 575
[  282.078899] [  282.080583] [IR ERR ]: MIRC_Data_Ctrl_Thread[147]:
[  282.086094] ************* ^_^ ************* [wjc_ir] [ir_core.c]  MIRC_Data_Ctrl_Thread thread_get_scancode = 495
[  282.096466] [  282.098134] [IR ERR ]: MIRC_Data_Ctrl_Thread[147]:
[  282.103635] ************* ^_^ ************* [wjc_ir] [ir_core.c]  MIRC_Data_Ctrl_Thread thread_get_scancode = 1615
[  282.114081] [  282.115747] [IR ERR ]: MIRC_Data_Ctrl_Thread[147]:
[  282.121260] ************* ^_^ ************* [wjc_ir] [ir_core.c]  MIRC_Data_Ctrl_Thread thread_get_scancode = 495
[  282.131862] [  282.133541] [IR ERR ]: MIRC_Data_Ctrl_Thread[147]:
[  282.139047] ************* ^_^ ************* [wjc_ir] [ir_core.c]  MIRC_Data_Ctrl_Thread thread_get_scancode = 16383
[  282.149822] [  282.151491] [IR ERR ]: MIRC_Data_Ctrl_Thread[147]:
[  282.156996] ************* ^_^ ************* [wjc_ir] [ir_core.c]  MIRC_Data_Ctrl_Thread thread_get_scancode = 8319
[  282.170418] [  282.172151] [IR ERR ]: MIRC_Data_Ctrl_Thread[147]:
[  282.177860] ************* ^_^ ************* [wjc_ir] [ir_core.c]  MIRC_Data_Ctrl_Thread thread_get_scancode = 2175
[  282.188571] [  282.190242] [IR ERR ]: MIRC_Data_Ctrl_Thread[147]:
[  282.195766] ************* ^_^ ************* [wjc_ir] [ir_core.c]  MIRC_Data_Ctrl_Thread thread_get_scancode = 495 加get_scancode的打印
[  253.961074] ************* ^_^ ************* [wjc_ir] [ir_core.c]  MIRC_Data_Ctrl_Thread ev.duration = 495 , ev.pulse = 1 ,get_scancode = 0 所以前面的解析,上报的代码跟踪就不对了,前面的那种是nec full解码方式的数据解析和上报,  队员软解码的解析应该为下面代码的操作
static int MIRC_Data_Ctrl_Thread(void *data)
{... if(raw->dev->ir_mode == IR_TYPE_SWDECODE_MODE) //sw decoder{struct ir_decoder_handler *handler;IRDBG_MSG("shotcount[ %5d  %d ]\n",ev.duration, ev.pulse);list_for_each_entry(handler, &ir_decoder_list, list){if(handler->decode(raw->dev, ev) > 0){get_scancode = 1;break;}}IRDBG_ERR("************* ^_^ ************* [wjc_ir] [ir_core.c]  %s ev.duration = %d , ev.pulse = %d ,get_scancode = %d \n",__FUNCTION__,ev.duration,ev.pulse,get_scancode);}...
}调用NEC遥控器编码对应的解码驱动
if(handler->decode(raw->dev, ev) > 0)先分析在Mstar_ir.c中probe函数执行的加载nec解码驱动 //调用关系 和前面分析ir_mode来源一致
nec_decode_init();|
int nec_decode_init(void)    //ir-nec0decoder.c文件
{if(u8InitFlag_nec == FALSE)      //还没有初始化就执行这里 {scancode = 0;mapnum = 0;KeyTime = 0;memset(&nec,0,sizeof(IR_NEC_Spec_t));MIRC_Decoder_Register(&nec_handler);IR_PRINT("[IR Log] NEC Spec Protocol Init\n");u8InitFlag_nec = TRUE;}else{IR_PRINT("[IR Log] NEC Spec Protocol Has been Initialized\n");}return 0;
}MIRC_Decoder_Register(&nec_handler);|
/** Extension interface - used to register the IR decoders    扩展接口-用于注册红外解码器*/
int MIRC_Decoder_Register(struct ir_decoder_handler *handler)   //ir_core.c文件
{mutex_lock(&ir_decoder_lock);list_add_tail(&handler->list, &ir_decoder_list);       //将nec红外解码器注册到ir_decoder_list链表中 ,  static LIST_HEAD(ir_decoder_list); 在ir_core.c中创建了该链表protocols |= handler->protocols;MIRC_Set_Protocols(protocols);                           mutex_unlock(&ir_decoder_lock);return 0;
}MIRC_Set_Protocols(protocols); |
void MIRC_Set_Protocols(u64 data)       //设置IRDev.pIRDev->enabled_protocols 成员变量的值
{protocols = data;IRDev.pIRDev->enabled_protocols = protocols;
}static struct ir_decoder_handler nec_handler = {      //这就是nec解码器的信息  .protocols  = (1<<IR_TYPE_NEC),.decode       = ir_nec_decode,
};IRDev.pIRDev->enabled_protocols = protocols;  ==>   IRDev.pIRDev->enabled_protocols =  1<<IR_TYPE_NEC == 1<<0 == 1   //这个就成员代表的是当前遥控器可以用那些解码器来解码,一个位代表一种解码器分析解码*********** ************
list_for_each_entry(handler, &ir_decoder_list, list){       //遍历ir_decoder_list链表,该链表中存放了nec_handler这个解码器,if(handler->decode(raw->dev, ev) > 0){get_scancode = 1;break;}}
handler->decode(raw->dev, ev) |
static int ir_nec_decode(struct mstar_ir_dev *dev, struct ir_raw_data ev)   //NEC协议详解: https://www.cnblogs.com/lifexy/p/9783694.html
{IRDBG_ERR("************* ^_^ ************* [wjc_ir] [ir-nec-decoder.c]  %s  \n",__FUNCTION__);IR_NEC_Spec_t *data = &nec;struct ir_scancode *sc = &dev->raw->this_sc;struct ir_scancode *prevsc = &dev->raw->prev_sc;u8 i = 0;u8 u8Addr = 0;                    //地址u8 u8AddrInv =0;               //地址取反u8 u8Cmd =0;                 //数据u8 u8CmdInv =0;                    //数据取反        NEC协议中一个完整的数据波形为: 引导码 地址 地址取反 数据 数据取反 if (!(dev->enabled_protocols & (1<<IR_TYPE_NEC)))      //判断是否为NEC编码  return -EINVAL;switch (data->eStatus){case STATE_INACTIVE:     //判断是否为9ms,是则进入下一个判断,判断是引导码还是重复码if (!ev.pulse)break;if (eq_margin(ev.duration,NEC_HEADER_PULSE_LWB,NEC_HEADER_PULSE_UPB))  //NEC_HEADER_PULSE_LWB = NEC_HEADER_PULSE_UPB = 9ms {IRDBG_ERR("************* ^_^ ************* [wjc_ir] [ir-nec-decoder.c]  %s ev.duration = %d 1. [case STATE_INACTIVE] jx_ydm:9ms\n",__FUNCTION__,ev.duration);data->u8BitCount = 0;data->eStatus = STATE_HEADER_SPACE;//IRDBG_INFO("NEC_HEADER_PULSE\n");return 0;}elsebreak;case STATE_HEADER_SPACE:   //判断是引导码,还是重复码if (ev.pulse)break;if (eq_margin(ev.duration,NEC_HEADER_SPACE_LWB,NEC_HEADER_SPACE_UPB))   //NEC_HEADER_SPACE_LWB =  NEC_HEADER_SPACE_UPB = 4.5ms   判断是否为4.5ms,是则为引导码,是引导码接下来就去判断数据的值{IRDBG_ERR("************* ^_^ ************* [wjc_ir] [ir-nec-decoder.c]  %s ev.duration = %d  2.1 HEADER  [case STATE_HEADER_SPACE] if HEADER(4.5ms)/REPORT(2.25ms) \n",__FUNCTION__,ev.duration);data->eStatus = STATE_BIT_PULSE;return 0;}else if (eq_margin(ev.duration,NEC_REPEAT_SPACE_LWB,NEC_REPEAT_SPACE_UPB)) //重复码  这里就将u8RepeatFlag值置为1   *********** 连续按键时的情况{//repeat//IRDBG_INFO("[NEC] TIME =%ld\n",(MIRC_Get_System_Time()-KeyTime));IRDBG_ERR("************* ^_^ ************* [wjc_ir] [ir-nec-decoder.c]  %s ev.duration = %d  2.2 REPEAT  [case STATE_HEADER_SPACE] if HEADER(4.5ms)/REPORT(2.25ms) \n",__FUNCTION__,ev.duration);if (prevsc->scancode_protocol == (1<<IR_TYPE_NEC) && ((u32)(MIRC_Get_System_Time()- KeyTime) <= NEC_REPEAT_TIMEOUT)){IRDBG_ERR("************* ^_^ ************* [wjc_ir] [ir-nec-decoder.c] 111111111111 \n");KeyTime = MIRC_Get_System_Time();if (((speed != 0)&&( data->u8RepeatTimes >= (speed - 1)))|| ((speed == 0)&&(data->u8RepeatTimes >= dev->speed))){
#ifdef CONFIG_MIRC_INPUT_DEVICEIRDBG_ERR("************* ^_^ ************* [wjc_ir] [ir-nec-decoder.c] 2222222 \n");sc->scancode = scancode;sc->scancode_protocol = (1<<IR_TYPE_NEC);dev->map_num = mapnum;dev->raw->u8RepeatFlag = 1;
#elsesc->scancode_protocol = (1<<IR_TYPE_NEC);sc->scancode = scancode|0x01;//repeat
#endifmemset(data,0,sizeof(IR_NEC_Spec_t));return 1;}data->u8RepeatTimes ++;//IRDBG_INFO("[NEC] repeattimes =%d \n",data->u8RepeatTimes);}else{IRDBG_ERR("************* ^_^ ************* [wjc_ir] [ir-nec-decoder.c] 333333333 \n");scancode = 0;mapnum = NUM_KEYMAP_MAX;}data->eStatus = STATE_INACTIVE;return 0;}break;case STATE_BIT_PULSE:  //判断是否为0.56ms,如果是在判断是数据1还是0if (!ev.pulse)break;IRDBG_ERR("************* ^_^ ************* [wjc_ir] [ir-nec-decoder.c]  %s ev.duration = %d  3. [case STATE_BIT_PULSE] if data_duration(0.56ms) \n",__FUNCTION__,ev.duration);if (!eq_margin(ev.duration,NEC_BIT_PULSE_LWB,NEC_BIT_PULSE_UPB))break;data->eStatus = STATE_BIT_SPACE;return 0;case STATE_BIT_SPACE:    //判断是数据1还是0if (ev.pulse)break;IRDBG_ERR("************* ^_^ ************* [wjc_ir] [ir-nec-decoder.c]  %s ev.pulse = %d  4. [case STATE_BIT_SPACE] if data_space(1=2.25-0.56,0=1.12-0.56) \n",__FUNCTION__,ev.pulse);//printk("************* ^_^ ************* [wjc_ir] [ir-nec-decoder.c] text_printk \n");data->u32DataBits <<= 1;if (eq_margin(ev.duration,NEC_BIT_1_SPACE_LWB,NEC_BIT_1_SPACE_UPB))         //如果为2.25-0.56ms,就是数据1data->u32DataBits |= 1;                                                   //配合前面的移位操作实现对最终上报数据的拼接获取工作else if (!eq_margin(ev.duration,NEC_BIT_0_SPACE_LWB,NEC_BIT_0_SPACE_UPB))    //如果为1.12-0.56ms就是数据0 break;data->u8BitCount++;if (data->u8BitCount == NEC_NBITS)     //NEC_NBITS =32,=32表示数据接收完成了{IRDBG_ERR("************* ^_^ ************* [wjc_ir] [ir-nec-decoder.c]  %s decode_data_finish   5. [case STATE_BIT_SPACE]  \n",__FUNCTION__);u8Addr    = bitrev8((data->u32DataBits >> 24) & 0xff);u8AddrInv = bitrev8((data->u32DataBits >> 16) & 0xff);u8Cmd   = bitrev8((data->u32DataBits >>  8) & 0xff);u8CmdInv  = bitrev8((data->u32DataBits >>  0) & 0xff);IRDBG_INFO("[NEC] u8Addr = %x u8AddrInv = %x u8Cmd = %x u8CmdInv = %x\n",u8Addr,u8AddrInv,u8Cmd,u8CmdInv);  IRDBG_ERR("[wjc_ir] [ir-nec-decoder.c] u8Addr = %x u8AddrInv = %x u8Cmd = %x u8CmdInv = %x\n",u8Addr,u8AddrInv,u8Cmd,u8CmdInv); for (i= 0;i<dev->support_num;i++){if(dev->support_ir[i].eIRType == IR_TYPE_NEC){if((((u8Addr<<8) | u8AddrInv) == dev->support_ir[i].u32HeadCode)){IRDBG_ERR("[wjc_ir] [ir-nec-decoder.c] match_headcode_ok headcode = %x \n",dev->support_ir[i].u32HeadCode); if (u8Cmd == (u8)~u8CmdInv) {
#ifdef CONFIG_MIRC_INPUT_DEVICE     //执行这里 ************sc->scancode = (u8Addr<<16) | (u8AddrInv<<8) | u8Cmd;    //u8Cmd 就是声明值,遥控器开发文档中的值sc->scancode_protocol = (1<<IR_TYPE_NEC);scancode = sc->scancode;                  speed = dev->support_ir[i].u32IRSpeed;dev->map_num = dev->support_ir[i].u32HeadCode;mapnum = dev->map_num;        dev->raw->u8RepeatFlag = 0;         //非重复码即非相同按键IRDBG_ERR("[wjc_ir] [ir-nec-decoder.c] jiaoyan_data_ok  sc->scancode = %d \n",scancode);
#elsesc->scancode = (u8Cmd<<8) |0x00;sc->scancode |= (0x01UL << 28);sc->scancode_protocol = (1<<IR_TYPE_NEC);scancode = sc->scancode;speed = dev->support_ir[i].u32IRSpeed;
#endif  KeyTime = MIRC_Get_System_Time();memset(data,0,sizeof(IR_NEC_Spec_t));return 1;            /*********** 非常关键,返回值大于0,实现get_scancode=1,下面的代码就是前面分析的根据scancode的值匹配映射表,获取keycode,然后上报数据}}}}}elsedata->eStatus = STATE_BIT_PULSE;return 0;default:break;}  data->eStatus = STATE_INACTIVE;return -EINVAL;
}******************** 重中之重:不能在中断函数:_MDrv_IR_ISR中添加打印,会导致程序出现问题  **************************打印分析: 因为中断中不能添加打印所以,从唤醒线程,即从线程中开始分析打印  *********** 最终版    按下音量+ 按键
duration:持续时间
pulse: 高低电平  ev.pulse = 1表示低电平,ev.pulse = 0表示高电平    *******非常重要
[  164.202167] [  164.203854] [IR ERR ]: MIRC_Data_Ctrl_Thread[148]:
[  164.209349] ************* ^_^ ************* [wjc_ir] [ir_core.c]  MIRC_Data_Ctrl_Thread [sw_decoder] ev.duration = 16383 , ev.pulse = 0 ,get_scancode = 0   //???????????? 还不知道16383代表什么意思 。。
[  164.223195] [  164.224862] [IR ERR ]: ir_nec_decode[36]:
[  164.229575] ************* ^_^ ************* [wjc_ir] [ir-nec-decoder.c]  ir_nec_decode
[  164.237682] [  164.239355] [IR ERR ]: ir_nec_decode[56]:
[  164.244081] ************* ^_^ ************* [wjc_ir] [ir-nec-decoder.c]  ir_nec_decode ev.duration = 8319 1. [case STATE_INACTIVE] jx_ydm:9ms               //9ms
[  164.256800] [  164.258467] [IR ERR ]: MIRC_Data_Ctrl_Thread[148]:
[  164.263976] ************* ^_^ ************* [wjc_ir] [ir_core.c]  MIRC_Data_Ctrl_Thread [sw_decoder] ev.duration = 8319 , ev.pulse = 1 ,get_scancode = 0
[  164.278842] [  164.280524] [IR ERR ]: ir_nec_decode[36]:
[  164.285236] ************* ^_^ ************* [wjc_ir] [ir-nec-decoder.c]  ir_nec_decode
[  164.293346] [  164.295011] [IR ERR ]: ir_nec_decode[70]:
[  164.299723] ************* ^_^ ************* [wjc_ir] [ir-nec-decoder.c]  ir_nec_decode ev.duration = 4223  2.1 HEADER  [case STATE_HEADER_SPACE] if HEADER(4.5ms)/REPORT(2.25ms)    //4.5ms ,9ms+4.5ms表示是引导码 下面开始判断数据是1还是0
[  164.315568] [  164.317234] [IR ERR ]: MIRC_Data_Ctrl_Thread[148]:
[  164.322728] ************* ^_^ ************* [wjc_ir] [ir_core.c]  MIRC_Data_Ctrl_Thread [sw_decoder] ev.duration = 4223 , ev.pulse = 0 ,get_scancode = 0
[  164.336480] [  164.338146] [IR ERR ]: ir_nec_decode[36]:
[  164.342862] ************* ^_^ ************* [wjc_ir] [ir-nec-decoder.c]  ir_nec_decode
[  164.350975] [  164.352646] [IR ERR ]: ir_nec_decode[117]:
[  164.357444] ************* ^_^ ************* [wjc_ir] [ir-nec-decoder.c]  ir_nec_decode ev.duration = 527  3. [case STATE_BIT_PULSE] if data_duration(0.56ms)                    //0.56ms 数据波形前缀正确
[  164.371546] [  164.373211] [IR ERR ]: MIRC_Data_Ctrl_Thread[148]:
[  164.378707] ************* ^_^ ************* [wjc_ir] [ir_core.c]  MIRC_Data_Ctrl_Thread [sw_decoder] ev.duration = 527 , ev.pulse = 1 ,get_scancode = 0
[  164.392378] [  164.394044] [IR ERR ]: ir_nec_decode[36]:
[  164.398756] ************* ^_^ ************* [wjc_ir] [ir-nec-decoder.c]  ir_nec_decode
[  164.406863] [  164.408532] [IR ERR ]: ir_nec_decode[127]:
[  164.413329] ************* ^_^ ************* [wjc_ir] [ir-nec-decoder.c]  ir_nec_decode ev.duration = 559  4. [case STATE_BIT_SPACE] if data_space(1=2.25-0.56,0=1.12-0.56)
[  164.428644] [  164.430310] *** ^_^ *** [wjc_ir] [ir-nec-decoder.c] bit0 = 0                                                                                                     //0.56ms 数据0
[  164.435979] [IR ERR ]: MIRC_Data_Ctrl_Thread[148]: [  164.441313] ************* ^_^ ************* [wjc_ir] [ir_core.c]  MIRC_Data_Ctrl_Thread [sw_decoder] ev.duration = 559 , ev.pulse = 0 ,get_scancode = 0
... .... 下面就开始一直解析数据  即解析32个数据波形中分别代码的值
bit0 = 0           bit8 = 0           bit16 = 1          bit24 = 0
bit1 = 0           bit9 = 0           bit17 = 0          bit25 = 1
bit2 = 0           bit10 = 0          bit18 = 1          bit26 = 0
bit3 = 0           bit11 = 0          bit19 = 0          bit27 = 1
bit4 = 0           bit12 = 0          bit20 = 1          bit28 = 0
bit5 = 0           bit13 = 0          bit21 = 0          bit29 = 1
bit6 = 1           bit14 = 1          bit22 = 0          bit30 = 1
bit7 = 0           bit15 = 0          bit23 = 0          bit31 = 1
==>                    ==>                    ==>                    ==>
0x40                0x40                0x15                0xea[  168.217732] [wjc_ir] [ir-nec-decoder.c] u8Addr = 40 u8AddrInv = 40 u8Cmd = 15 u8CmdInv = ea          //刚刚好就是前面得到的4个值   *******xixi ^_^
[  168.226152] [  168.227828] [IR ERR ]: ir_nec_decode[161]:
[  168.232631] [wjc_ir] [ir-nec-decoder.c] match_headcode_ok headcode = 4040                           //这个就是u8Addr<<8 | u8AddrInv的值 ,这不就是头码吗?  *******
[  168.239531] [  168.241249] [IR ERR ]: ir_nec_decode[173]:
[  168.246051] [wjc_ir] [ir-nec-decoder.c] jiaoyan_data_ok  sc->scancode = 4210709                      //这个是校验数据ok的打印   ****************  4210709 >> 8 ==0x4040  头码,  sc->scancode = (u8Addr<<16) | (u8AddrInv<<8) | u8Cmd;
[  168.253459] [  168.255190] [IR ERR ]: MIRC_Data_Ctrl_Thread[148]:
[  168.260686] ************* ^_^ ************* [wjc_ir] [ir_core.c]  MIRC_Data_Ctrl_Thread [sw_decoder] ev.duration = 1615 , ev.pulse = 0 ,get_scancode = 1  //32bit数据全部解析完 get_scancode置1 然后开始通过scancode值获取keycode值然后input上报
[  168.274448] [  168.276175] [IR ERR ]: MIRC_Data_Ctrl_Thread[160]:
[  168.281672] ***** ^_^ ******* [wjc_ir] [ir_core.c] MIRC_Data_Ctrl_Thread raw->u8RepeatFlag = 0
[  168.290389] [  168.292111] [IR ERR ]: MIRC_Data_Ctrl_Thread[173]:
[  168.297611] ***** ^_^ ******* [wjc_ir] [ir_core.c] MIRC_Data_Ctrl_Thread  raw->dev->filter_flag == 0
[  168.306848] [  168.308566] [IR ERR ]: MIRC_Data_Ctrl_Thread[184]:
[  168.314064] ***** ^_^ ******* [wjc_ir] [ir_core.c] MIRC_Data_Ctrl_Thread  diff protocol  //第一次按按键是不同的协议 后面按应该就是相同的协议了
[  168.322180] [  168.323910] [IR ERR ]: MIRC_Keydown[642]:
[  168.328623] ************* ^_^ ************* [wjc_ir] [ir_core.c]  MIRC_Keydown  process_key_press        //进入MIRC_Keydown函数
[  168.337500] [  168.339220] [IR ERR ]: MIRC_Keycode_From_Map[594]:
[  168.344720] ************* ^_^ ************* [wjc_ir] [ir_core.c]  MIRC_Keycode_From_Map  match_keymap_ok keycode= 115  //MIRC_Keycode_From_Map函数中 按键的扫描值 = c->scancode中的低8位即可   代码实现: if (((map->map.scan[i].scancode&0xff) == (scancode&0xff)) && ((scancode>>8) == map->map.headcode))
[   49.100590] ************* ^_^ ************* [wjc_ir] [ir_core.c]  MIRC_Do_Keydown input data input_keycode = 115 ,keyvalue = 1             //这就是 最终input上报的数据如果是开机之后按键按了好几次 ==> 相同协议条件达成
+
按同一个按键的打印   开机之后,间隔的按下v+
前面32bit打印就不展示了 都是一样的 *********
[   85.654837] [IR ERR ]: ir_nec_decode[154]:

[ 85.654838] [wjc_ir] [ir-nec-decoder.c] u8Addr = 40 u8AddrInv = 40 u8Cmd = 15 u8CmdInv = ea
[ 85.654840]
[ 85.654841] [IR ERR ]: ir_nec_decode[161]:
[ 85.654841] [wjc_ir] [ir-nec-decoder.c] match_headcode_ok headcode = 4040
[ 85.654843]
[ 85.654844] [IR ERR ]: ir_nec_decode[173]:
[ 85.654844] [wjc_ir] [ir-nec-decoder.c] jiaoyan_data_ok sc->scancode = 4210709
[ 85.654846]
[ 85.654848] [IR ERR ]: MIRC_Data_Ctrl_Thread[148]:
[ 85.654848] ************* _ ************* [wjc_ir] [ir_core.c] MIRC_Data_Ctrl_Thread [sw_decoder] ev.duration = 1615 , ev.pulse = 0 ,get_scancode = 1
[ 85.654850]
[ 85.654851] [IR ERR ]: MIRC_Data_Ctrl_Thread[160]:
[ 85.654851] ***** _ ******* [wjc_ir] [ir_core.c] MIRC_Data_Ctrl_Thread raw->u8RepeatFlag = 0
[ 85.654853]
[ 85.654854] [IR ERR ]: MIRC_Data_Ctrl_Thread[173]:
[ 85.654854] ***** _ ******* [wjc_ir] [ir_core.c] MIRC_Data_Ctrl_Thread raw->dev->filter_flag == 0
[ 85.654856]
[ 85.654858] [IR ERR ]: MIRC_Data_Ctrl_Thread[192]:
[ 85.654858] ************* _ ************* [wjc_ir] [ir_core.c] MIRC_Data_Ctrl_Thread same protocol diff key press = 1615
[ 85.654860]
[ 85.654861] [IR ERR ]: MIRC_Data_Ctrl_Thread[199]:
[ 85.654861] ***** _ ******* [wjc_ir] [ir_core.c] MIRC_Data_Ctrl_Thread same protocol + diff key press //这个是按键按下
[ 85.654863]
[ 85.654864] [IR ERR ]: MIRC_Keydown[642]:
[ 85.654865] ************* _ ************* [wjc_ir] [ir_core.c] MIRC_Keydown process_key_press
[ 85.654867]
[ 85.654868] [IR ERR ]: MIRC_Keycode_From_Map[594]:
[ 85.654869] ************* _ ************* [wjc_ir] [ir_core.c] MIRC_Keycode_From_Map match_keymap_ok keycode= 115
[ 85.654905]
[ 85.654918] [IR ERR ]: MIRC_Do_Keydown[637]:
[ 85.654918] ************* _ ************* [wjc_ir] [ir_core.c] MIRC_Do_Keydown input data input_keycode = 115 ,keyvalue = 1
[ 85.655012]
[ 85.655014] [IR ERR ]: ir_nec_decode[36]:
[ 85.655014] ************* _ ************* [wjc_ir] [ir-nec-decoder.c] ir_nec_decode
[ 85.655017]
[ 85.655019] [IR ERR ]: MIRC_Data_Ctrl_Thread[148]:
[ 85.655019] ************* _ ************* [wjc_ir] [ir_core.c] MIRC_Data_Ctrl_Thread [sw_decoder] ev.duration = 495 , ev.pulse = 1 ,get_scancode = 0
[ 85.655021]
[ 85.655022] [IR ERR ]: ir_nec_decode[36]:
[ 85.655023] ************* _ ************* [wjc_ir] [ir-nec-decoder.c] ir_nec_decode
[ 85.655024]
[ 85.655026] [IR ERR ]: MIRC_Data_Ctrl_Thread[148]:
[ 85.655027] ************* _ ************* [wjc_ir] [ir_core.c] MIRC_Data_Ctrl_Thread [sw_decoder] ev.duration = 16383 , ev.pulse = 0 ,get_scancode = 0
[ 85.655029]
[ 85.655030] [IR ERR ]: ir_nec_decode[36]:
[ 85.655030] ************* _ ************* [wjc_ir] [ir-nec-decoder.c] ir_nec_decode
[ 85.655032]
[ 85.655033] [IR ERR ]: ir_nec_decode[56]:
[ 85.655033] ************* _ ************* [wjc_ir] [ir-nec-decoder.c] ir_nec_decode ev.duration = 8319 1. [case STATE_INACTIVE] jx_ydm:9ms //这里就会在一次判断,最终发现是相同协议,相同按键按下
[ 85.655035]
[ 85.655037] [IR ERR ]: MIRC_Data_Ctrl_Thread[148]:
[ 85.655037] ************* _ ************* [wjc_ir] [ir_core.c] MIRC_Data_Ctrl_Thread [sw_decoder] ev.duration = 8319 , ev.pulse = 1 ,get_scancode = 0
[ 85.655039]
[ 85.655040] [IR ERR ]: ir_nec_decode[36]:
[ 85.655040] ************* _ ************* [wjc_ir] [ir-nec-decoder.c] ir_nec_decode
[ 85.655042]
[ 85.655044] [IR ERR ]: ir_nec_decode[77]:
[ 85.655044] ************* _ ************* [wjc_ir] [ir-nec-decoder.c] ir_nec_decode ev.duration = 2175 2.2 REPEAT [case STATE_HEADER_SPACE] if HEADER(4.5ms)/REPORT(2.25ms)
[ 85.655046]
[ 85.655047] [IR ERR ]: ir_nec_decode[80]:
[ 85.655047] ************* _ ************* [wjc_ir] [ir-nec-decoder.c] 111111111111
[ 85.655048]
[ 85.655049] [IR ERR ]: ir_nec_decode[86]:
[ 85.655050] ************* _ ************* [wjc_ir] [ir-nec-decoder.c] 2222222
[ 85.655051]
[ 85.655053] [IR ERR ]: MIRC_Data_Ctrl_Thread[148]:
[ 85.655053] ************* _ ************* [wjc_ir] [ir_core.c] MIRC_Data_Ctrl_Thread [sw_decoder] ev.duration = 2175 , ev.pulse = 0 ,get_scancode = 1
[ 85.655055]
[ 85.655056] [IR ERR ]: MIRC_Data_Ctrl_Thread[160]:
[ 85.655056] ***** _ ******* [wjc_ir] [ir_core.c] MIRC_Data_Ctrl_Thread raw->u8RepeatFlag = 1
[ 85.655058]
[ 85.655059] [IR ERR ]: MIRC_Data_Ctrl_Thread[173]:
[ 85.655060] ***** _ ******* [wjc_ir] [ir_core.c] MIRC_Data_Ctrl_Thread raw->dev->filter_flag == 0
[ 85.655061]
[ 85.655063] [IR ERR ]: MIRC_Data_Ctrl_Thread[204]:
[ 85.655063] ************* _ ************* [wjc_ir] [ir_core.c] MIRC_Data_Ctrl_Thread same protocol Repeat key press = 2175 //*********** 相同协议 相同按键
[ 85.655066]
[ 85.655067] [IR ERR ]: ir_nec_decode[36]:
[ 85.655067] ************* _ ************* [wjc_ir] [ir-nec-decoder.c] ir_nec_decode
[ 85.655069]
[ 85.655071] [IR ERR ]: MIRC_Data_Ctrl_Thread[148]:
[ 85.655071] ************* _ ************* [wjc_ir] [ir_core.c] MIRC_Data_Ctrl_Thread [sw_decoder] ev.duration = 495 , ev.pulse = 1 ,get_scancode = 0

************** 没了 说明前面一开始的分析是ok 的。。

Mstar平台_遥控器驱动相关推荐

  1. ESP8266_19MQTT协议接入ONENET平台_订阅主题

    ESP8266_01搭建开发环境 ESP8266_02程序的编译与下载 ESP8266_03SDK与Makefile的基本用法 ESP8266_04管脚控制与软件定时器 ESP8266_05 ESP8 ...

  2. MTK平台的LCM驱动移植

    一.Android系统开机启动过程及驱动概述: MTK平台的显示驱动分两块,一块在LK代码里面,一块在kernel代码里面: LK的lcm驱动路径在:vendor\mediatek\proprieta ...

  3. Manjaro开机黑屏卡住_显卡驱动问题解决及配置源和搜狗输入法安装。

    Manjaro开机黑屏卡住_显卡驱动问题解决及配置源和搜狗输入法安装. 参考文章: (1)Manjaro开机黑屏卡住_显卡驱动问题解决及配置源和搜狗输入法安装. (2)https://www.cnbl ...

  4. 支持驱动最好的linux软件,Linux平台设备和驱动

    一 platform总线 一个现实的linux设备驱动通常需要挂接在一种总线上,对于本身依附于PCI,USB,IIC,SPI等的设备而言,这自然不是问题,但是在嵌入式系统里面,SOC系统中集成的独立的 ...

  5. 基于WF设计业务流程平台_权限在流程模板外部映射

    基于WF设计业务流程平台_权限在流程模板外部映射 前面的几篇文章我介绍了一种权限与流程模板相结合的设计方式,今天我介绍一种权限在流程模板外部映射的计方式. 限在流程模板外部映射,主要的实现思路是: 在 ...

  6. 小程序模板网站平台_小程序模板平台哪个好

    小程序模板网站平台_小程序模板平台哪个好?分享一个微信小程序模板平台,超60个行业的小程序模板免费使用,页面内容丰富样式多样的,小程序界面模板. 微信小程序模板网站平台 微信小程序模板平台的存在,就是 ...

  7. 云之讯融合通讯开放平台_提供融合语音,短信,VoIP,视频和IM等通讯API及SDK。...

    云之讯融合通讯开放平台_提供融合语音,短信,VoIP,视频和IM等通讯API及SDK. 云之讯融合通讯开放平台_提供融合语音,短信,VoIP,视频和IM等通讯API及SDK. undefined 全明 ...

  8. 服务器有必要更新主板芯片组吗,芯片组驱动要不要更新_芯片组驱动卸载了会怎么样...

    本文首先介绍了芯片组驱动的重要性,其次介绍了芯片组驱动卸载带来的后果,最后阐述了芯片组驱动要不要更新以及更新的步骤教程,具体的跟随小编一起来了解一下. 芯片组驱动的重要性 芯片组是构成主板电路的核心. ...

  9. rk3399平台电量计cw2015驱动分析

    rk3399平台电量计cw2015驱动分析 文章目录 rk3399平台电量计cw2015驱动分析 cw2015电路设计 cw2015 2芯电池设计参考 电路设计参考注意事项 板级配置 dts配置实例 ...

  10. 商用短链平台_第8章_ 账号微服务注册模块+短信验证码+阿里云OSS开发实战

    商用短链平台_第8章_ 账号微服务注册模块+短信验证码+阿里云OSS开发实战 文章目录 商用短链平台_第8章_ 账号微服务注册模块+短信验证码+阿里云OSS开发实战 第八章 账号微服务注册模块+短信验 ...

最新文章

  1. FreeSwitch 的初始化及其模块加载过程
  2. python avg函数_学习python第三天之多行函数
  3. python-docx官方声明
  4. 使用NET USE将USB端口模拟为LPT1
  5. 单片机数据转换php,51单片机之数据转移指令MOV、MOVX、MOVC等
  6. Nginx配置实例-反向代理实例:根据访问的路径跳转到不同端口的服务中
  7. Ruby中如何识别13位的时间戳
  8. php static_castunsigned int,static_cast揭密
  9. 这篇Nature子刊文章的蛋白组学数据PCA分析竟花费了我两天时间来重现|附全过程代码...
  10. JavaScript比较两个数组的内容是否相同
  11. hibernate 多表查询
  12. Bearcat pomelo game 实战 -- treasures
  13. 论电子病历文本编辑器
  14. MySQL 运维 - 高阶SQL语句
  15. uni-app初学步骤教程:
  16. H5实时上传位置定位 pc生成轨迹;h5保持后台运行
  17. UbuntuMate安装中文输入法
  18. 【Python】——Excel
  19. 内存走线 菊花链_[转帖]关于DDR4内存颗粒、单双面、主板布线和双通道的那些事儿...
  20. 触发器的Sd和Rd的作用(转)

热门文章

  1. awwwards环形动画_我在参加awwwards设计事宜和figma配置时学到的东西
  2. iphone如何刷android系统升级,iphone4s如何刷成android系统?
  3. springboot vue uniapp公交路线查询系统源码
  4. 置换和轮换(新姿势,摘自黑书)
  5. 苹果备份有什么用_数据备份用什么软件好?好用的数据备份软件分享
  6. LeetCode 5773 插入后的最大值(中等 贪心)
  7. Python 发出警报声音 简单播放声音 beep 在linux 上
  8. 重走机器学习之路——形而上者谓之道
  9. matlab剪切板中内容清除,清除剪贴板的内容
  10. python中confusion matrix_Confusion matrix理解