Amlogic_t962x_android7.1红外遥控驱动浅析

IR遥控分析资料:

1、红外解码器对红外遥控输入信号进行解码。支持两种操作模式:
[1]、硬件解码IR传输协议兼容帧解码器模式(NEC 三菱 Thomson 东芝 Sony SIRC RC5 RC6 RCMM Duokan Comcast Sanyo模式)
[2]、通用可编程时间测量帧解码器模式(通用模式)。

2、红外解码说明
2.1在硬件解码模式下,解码器采用信号模式搜索机制对数据帧进行解码。它可以检测逻辑0、1、00、01、10和11,以及数据帧的开始和结束。每当解码器检测和解码数据帧时,数据都保存在数据寄存器中

2.2在一般模式下,解码器采用边缘检测机制对数据帧进行解码。它可以检测每个输入信号的边缘,并记录两个边缘之间的时间。时间测量结果保存在控制寄存器中

2.3用户应根据遥控器的选择,设置适当的操作模式【重点】。

2.4在信号输入和解码器之间有一个简单的基于时间的信号滤波器。该滤波器可编程,有助于提高信号的完整性

3、红外译码器功能块中的寄存器分为3组
3.1控制和时钟寄存器,用于配置红外解码器和获取解码器状态。它也用于配置时钟检测速率。
3.2调谐寄存器用于调整硬件解码器模式的检测参数。
3.3数据寄存器,用于保存硬件解码器模式下的解码数据。

红外解码器有13种工作模式:12种硬件解码模式和一般模式。
在硬件解码模式下,数据帧基于硬件解码红外传输协议。
在一般模式下,可以对软件进行编程以适应其他传输协议,这些协议应该是基于脉冲距离编码的;

嵌入式处理器与红外译码器之间的通信是基于中断的。启用IR解码器并检测到任何有效的远程控制器信号输入后,将产生一个中断。处理器通过读取IR译码寄存器来处理中断并获取详细信息;

红外解码器的信号检测和解码是基于时间的。在所有的检测和计算处理中,将元素时间间隔称为T率,作为基本的检测窗口时间单元。软件可以通过设置适当的值到寄存器 AO_MF_IR_DEC_REG0 来配置速率;

滤波器可用于过滤输入信号在任何一种工作模式。滤波器的工作逻辑是:在信号从低到高的上升沿后,信号保持在高的时间最少的滤波器T滤波器前被接受为有效的高信号。在信号从高到低的下降沿之后,
信号至少要保持在低的T滤波器上,才能被接受为有效的低信号。

4、驱动代码流程浅析:
4.1dts部分,文件:common/arch/arm64/boot/dts/amlogic/mesontxlx.dtsi

 306                 remote_pins:remote_pin{
 307                         amlogic,setmask = <AO 0x1>;
 308                         amlogic,clrmask = <AO 0x200000>;
 309                         amlogic,pins = "GPIOAO_6";
 310                 };
 759         remote:rc@0xff808040 {
 760                 compatible = "amlogic, aml_remote";
 761                 dev_name = "meson-remote";
 762                 reg = <0x0 0xff808040 0x00 0x44>,
 763                         <0x0 0xff808000 0x00 0x20>;
 764                 status = "okay";
 765                 protocol = <REMOTE_TYPE_NEC>; //配置IR 控制器的初始模式->支持的协议,这里默认配置NEC
 766                 interrupts = <0 196 1>;
 767                 pinctrl-names = "default";
 768                 pinctrl-0 = <&remote_pins>;
 769                 map = <&custom_maps>;
 770                 max_frame_time = <200>; /*set software decoder max frame time*/
 771         };
773         custom_maps:custom_maps {
 774                 mapnum = <3>;
 775                 map0 = <&map_0>;
 776                 map1 = <&map_1>;
 777                 map2 = <&map_2>;
 778                 map_0: map_0{
 779                         mapname = "amlogic-remote-1";
 780                         customcode = <0xfb04>;
 781                         release_delay = <80>;
 782                         size  = <44>;   //keymap size
 783                         keymap = <REMOTE_KEY(0x01, KEY_1)
 784                                 REMOTE_KEY(0x02, KEY_2)
 785                                 REMOTE_KEY(0x03, KEY_3)
 786                                 REMOTE_KEY(0x04, KEY_4)
 787                                 REMOTE_KEY(0x05, KEY_5)
 788                                 REMOTE_KEY(0x06, KEY_6)
 789                                 REMOTE_KEY(0x07, KEY_7)
 790                                 REMOTE_KEY(0x08, KEY_8)
 791                                 REMOTE_KEY(0x09, KEY_9)
 792                                 REMOTE_KEY(0x0a, KEY_0)
 793                                 REMOTE_KEY(0x1F, KEY_FN_F1)
 794                                 REMOTE_KEY(0x15, KEY_MENU)
 795                                 REMOTE_KEY(0x16, KEY_TAB)
 796                                 REMOTE_KEY(0x0c, KEY_CHANNELUP)
 797                                 REMOTE_KEY(0x0d, KEY_CHANNELDOWN)
 798                                 REMOTE_KEY(0x0e, KEY_VOLUMEUP)
 799                                 REMOTE_KEY(0x0f, KEY_VOLUMEDOWN)
 800                                 REMOTE_KEY(0x11, KEY_HOME)
 801                                 REMOTE_KEY(0x1c, KEY_RIGHT)
 802                                 REMOTE_KEY(0x1b, KEY_LEFT)
 803                                 REMOTE_KEY(0x19, KEY_UP)
 804                                 REMOTE_KEY(0x1a, KEY_DOWN)
 805                                 REMOTE_KEY(0x1d, KEY_ENTER)
 806                                 REMOTE_KEY(0x17, KEY_MUTE)
 807                                 REMOTE_KEY(0x49, KEY_FINANCE)
 						};
 					};

4.2部分代码构成,其他可到该目录查看:

drivers/amlogic/input/remote/remote_meson.c //中心代码
drivers/amlogic/input/remote/remote_cdev.c //字符设备相关代码
drivers/amlogic/input/remote/remote_regmap.c //寄存器操作相关代码
drivers/amlogic/input/remote/remote_core.c //框架相关代码
drivers/amlogic/input/remote/sysfs.c //文件系统相关代码
drivers/amlogic/input/remote/remote_raw.c //原始码值的接收和处理

我这里从remote_probe函数开始跟:

我这里从remote_probe函数开始跟:
	->remote_allocate_device;//
		->dev->input_device = input_allocate_device(); //分配并注册一个输入设备
	->ir_input_device_init(dev->input_device, &pdev->dev, "aml_keypad"); //初始化输入子设备

	->ir_hardware_init(struct platform_device *pdev) //初始化IR设备
	---------------------------------------------------------------
		->ret = ir_get_devtree_pdata(pdev); //这里去获取dts的配置参数
			->ret = of_property_read_u32(pdev->dev.of_node,"protocol", &chip->protocol); //这里解析dts配置的protocol参数,设置ir的协议支持,
																						  //如chip->protocol = 1(NEC协议),protocol的宏定义在./include/dt-bindings/input/meson_rc.h中
																						  //如果没有设置,则默认是支持进入NEC协议(chip->protocol = 1)模式
			->ret = get_custom_tables(pdev->dev.of_node, chip); //获取在dts的客制映射信息,红外码值和linux code映射以及其他参数
		->chip->set_register_config(chip, chip->protocol); //设置IR寄存器进入对应chip->protocol配置的协议模式
				->chip->set_register_config = ir_register_default_config; //配置IR寄存器,函数原型在remote_regmap.c中
					->ir_contr_init(struct remote_chip *chip, int type, unsigned char id); //配置ir寄存器初始化
					->remote_reg_write(chip, LEGACY_IR_ID, REG_REG1, 0x0); //禁用旧的红外控制器
					->ir_contr_init(chip, MULTI_IR_TYPE_MASK(type), MULTI_IR_ID); //初始化多格式红外控制器的寄存器,设置IR控制器进入对应的IR协议模式
																				  //MULTI_IR_ID = 0,在./drivers/amlogic/input/remote/remote_meson.h中定义
						->remote_reg_write(chip, id, REG_REG1, 0x01); //重设ir解码器并禁用ir解码器的状态机
						->for (  ; size > 0;  ) { 
							remote_reg_write(chip, id, reg_map->reg, reg_map->val); //根据reg_map,依次设置当前IR控制器寄存器值
							}
		->ret = request_irq(chip->irqno, ir_interrupt, IRQF_SHARED, "keypad", (void *)chip); //这里申请中断,注册中断执行函数: ir_interrupt
				/* 有遥控按键的时候会触发中断,调用中断子程序 ir_interrupt */
				->在 ir_interrupt 中利用多格式红外控制器的时间测量实现多协议的软件解码(可到该函数去看看)
					->if (MULTI_IR_SOFTWARE_DECODE(rc->protocol)) { //识别软件是否解码
						-----------------ir_interrupt-----------------
						rc->ir_work = MULTI_IR_ID;
						duration = val*10*1000;
						type    = RAW_PULSE;
						sprintf(buf, "------\n");
						debug_log_printk(rc->r_dev, buf);
						->remote_raw_event_store_edge(rc->r_dev, type, duration); //存储原始码值
							->rc = remote_raw_event_store(dev, &ev);
								->kfifo_in(&dev->raw->kfifo, ev, sizeof(*ev)) != sizeof(*ev) //Kfifo是内核里面的一个first in first out数据结构,
																							 //它采用环形循环队列(ringbuffer)的数据结构来实现;它提供一个无边界的字节流服务
						->remote_raw_event_handle(rc->r_dev);
							->wake_up_process(dev->raw->thread);
						-----------------ir_interrupt----------------
					->else
						---------------------------------------------
						for (cnt = 0; cnt < (ENABLE_LEGACY_IR(rc->protocol)
									? 2:1); cnt++) {
							remote_reg_read(rc, cnt, REG_STATUS, &contr_status);
							if (IR_DATA_IS_VALID(contr_status)) {
									rc->ir_work = cnt;
									break;
							}
						}
						----------------------------------------------
					->tasklet_schedule(&tasklet); //在适当的时候调度tasklet任务机制
						->amlremote_tasklet //在tasklet中需要先获取扫描码,然后获取解码状态,状态可能是从获取扫描码功能设置的标志
							->scancode = chip->ir_contr[chip->ir_work].get_scancode(chip); //获取扫描码,即原始码值
								->.get_scancode      = ir_nec_get_scancode,
									->ir_nec_get_scancode(struct remote_chip *chip) //NEC获取扫描码的接口函数
										----------------- 读取解码器状态值和数据帧寄存器值 ------------------ |---------------------------------|
										->remote_reg_read(chip, MULTI_IR_ID, REG_STATUS, &decode_status); //  |###### 读取解码器的状态值 ###### |
										->chip->decode_status = status; /*set decode status*/ //设置解码器状态|---------------------------------|
																								   |--------------------------|
										->remote_reg_read(chip, MULTI_IR_ID, REG_FRAME, &code); // |###读取数据帧寄存器数据###|寄存器定义在./drivers/amlogic/input/remote/remote_meson.h
											->chip->r_dev->cur_hardcode = code;                    |--------------------------|
											->code = (code >> 16) & 0xff;
											->return code; //经过一定处理[把数据帧寄存器数据左移16bit然后和0xff与运算后取值]后返回得到遥控码值code;
														   //假如读到数据帧寄存器值为framecode=0x6699bd02的遥控码值是0x99
										----------------- 读取解码器状态值和数据帧寄存器值 ------------------
	
							->status = chip->ir_contr[chip->ir_work].get_decode_status(chip); //获取解码状态值
								->.get_decode_status = ir_nec_get_decode_status, //NEC获取解码器状态值
									->status = chip->decode_status; //解码器状态值为decode_status
							->if (status == REMOTE_NORMAL) { //REMOTE_NORMAL == 0,码器状态值为0,说明按键码是新按下的码值
															 //
								remote_dbg(chip->dev, "receive scancode=0x%x\n", scancode);
                     			remote_keydown(chip->r_dev, scancode, status); //上报键值和状态
							 } else if (status & REMOTE_REPEAT) { //REMOTE_REPEAT == 1, 解码器状态值为1,说明按键码重复之前的码值
																  //status解码器状态值在./drivers/amlogic/input/remote/remote_core.h宏定义。
								remote_dbg(chip->dev, "receive repeat\n");
								->remote_keydown(chip->r_dev, scancode, status); //按键重复,上报键值和状态
							} else{
								dev_err(chip->dev, "receive error %d\n", status);
							}							
									\-----------------------------------------------------------------/
									->remote_keydown(chip->r_dev, scancode, status); /*上报键值和状态 */|
									|	->ir_report_rel(dev, scancode, status)) { /*报告相对轴事件*/    |
									|	->keycode = dev->getkeycode(dev, scancode);                    |
									|	  ir_do_keydown(dev, scancode, keycode); /*上报按键事件 */      |
									|		->if (KEY_RESERVED != keycode) { 						   |
									|			dev->keypressed = true;								   |
									|			dev->last_scancode = scancode; /*红外遥控码值*/         |
									|			dev->last_keycode = keycode;   /*linux code值*/        |
									|			input_report_key(dev->input_device, keycode, 1); //input子系统上报linux键值码,keycode在include/dt-bindings/input/input.h宏定义,
												//这里在adb执行getevent,然后操作遥控可以get到上报的linux code值【16进制数据】
									|			input_sync(dev->input_device);  				 //这里转换成16进制report
									/-----------------------------------------------------------------\	
							
							->.get_custom_code   = ir_nec_get_custom_code //NEC获取客户码
								->custom_code = chip->r_dev->cur_hardcode & 0xffff; //这里是取数据帧寄存器数据的低16位为custom_code
																					//假如读到数据帧寄存器值为framecode=0x6699bd02, 则客户码是0xbd02
										
		
		->tasklet_enable(&tasklet); //使能tasklet任务机制
		---------------------------------------------------------
		
	->ret = ir_cdev_init(chip); //初始化字符设备[]
		->ret = alloc_chrdev_region(&chip->chr_devno, 0, 1, AML_REMOTE_NAME); //动态分配设备号
			->ret = ir_sys_device_attribute_init(chip); //初始化sys文件系统
				->class_register(&remote_class); //注册class
					->dev = device_create(&remote_class,  NULL, chip->chr_devno, chip, chip->dev_name); //基于设备类创建设备节点
						-------------------------
						->static ssize_t protocol_show; //cat节点protocol查看当前的IR协议模式
						
						->static ssize_t protocol_store; //echo > 节点protocol设置选择进入的IR协议模式
							->ret = kstrtoint(buf, 0, &protocol); //设置进入对应的红外协议模式
								->chip->protocol = protocol;
								->chip->set_register_config(chip, chip->protocol);
						-------------------------

		休眠:
		.suspend = remote_suspend,
			->remote_suspend(struct platform_device *pdev, pm_message_t state); //休眠调用
				->disable_irq(chip->irqno); //关闭中断
		

		
		唤醒:
		.resume = remote_resume,
			->remote_resume(struct platform_device *pdev); //唤醒调用
				->chip->set_register_config(chip, chip->protocol); //重新设置IR寄存器进入对应chip->protocol配置的协议模式

5、remote相关节点

gtt92x:/ # ls -l sys/devices/virtual/remote/amremote/
total 0
-rw-r--r-- 1 root root 4096 1970-02-24 15:27 debug_enable
-rw-r--r-- 1 root root 4096 1970-02-24 15:27 debug_log
-r--r--r-- 1 root root 4096 1970-02-24 15:27 dev
-rw-r--r-- 1 root root 4096 1970-02-24 15:27 keymap
-r--r--r-- 1 root root 4096 1970-02-24 15:27 map_tables
drwxr-xr-x 2 root root    0 1970-02-24 15:26 power
-rw-r--r-- 1 root root 4096 1970-02-24 15:29 protocol
-rw-r--r-- 1 root root 4096 1970-02-24 15:27 repeat_enable
lrwxrwxrwx 1 root root    0 1970-02-24 15:27 subsystem -> ../../../../class/remote
-rw-r--r-- 1 root root 4096 1970-02-24 15:26 uevent
gtt92x:/ #

节点sys/devices/virtual/remote/amremote/protocol可以设置当前的红外协议模式,当前配置为NEC模式,在dts配置protocol = <REMOTE_TYPE_NEC>;

echo 1 > sys/devices/virtual/remote/amremote/protocol //设置为NEC协议REMOTE_TYPE_NEC模式
echo 131 > sys/devices/virtual/remote/amremote/protocol //设置为RAW协议REMOTE_TYPE_RAW_XMP_1模式

相关的红外协议模式在./include/dt-bindings/input/meson_rc.h中有宏定义,可以根据meson_rc.h往节点protocol写入对应数值设置当前支持的红外协议模式。

 17 /*hardware decode one protocol by using multi-format IR controller*/
 18 #define     REMOTE_TYPE_UNKNOW      0x00
 19 #define     REMOTE_TYPE_NEC         0x01
 20 #define     REMOTE_TYPE_DUOKAN      0x02
 21 #define     REMOTE_TYPE_XMP_1       0x03
 22 #define     REMOTE_TYPE_RC5         0x04
 23 #define     REMOTE_TYPE_RC6         0x05
 24 #define     REMOTE_TYPE_TOSHIBA     0x06
 25
 26 /*hardware decode one protocol by using legacy IR controller*/
 27 #define     REMOTE_TYPE_LEGACY_NEC  0xff
 28
 29 /**
 30   *software decode multiple protocols by using Time measurement
 31   *of multi-format IR controller
 32   *using bit[7] to identify the software decode
 33   */
 34 #define     REMOTE_TYPE_RAW_NEC     0x81
 35 #define     REMOTE_TYPE_RAW_XMP_1   0x83
  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是使用Amlogic平台播放MP4文件的示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/mman.h> #include <linux/videodev2.h> #include <linux/v4l2-controls.h> #define VIDEO_DEVICE "/dev/video10" int main(int argc, char **argv) { int fd = open(VIDEO_DEVICE, O_RDWR); if (fd < 0) { perror("Failed to open video device"); return -1; } struct v4l2_capability cap; if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0) { perror("Failed to query capabilities"); close(fd); return -1; } struct v4l2_format fmt = { .type = V4L2_BUF_TYPE_VIDEO_OUTPUT, .fmt.pix = { .width = 1920, .height = 1080, .pixelformat = V4L2_PIX_FMT_H264, .field = V4L2_FIELD_NONE, .bytesperline = 0, .sizeimage = 0, .colorspace = V4L2_COLORSPACE_DEFAULT, }, }; if (ioctl(fd, VIDIOC_S_FMT, &fmt) < 0) { perror("Failed to set format"); close(fd); return -1; } struct v4l2_buffer buf = { .type = V4L2_BUF_TYPE_VIDEO_OUTPUT, .memory = V4L2_MEMORY_MMAP, }; if (ioctl(fd, VIDIOC_REQBUFS, &buf) < 0) { perror("Failed to request buffers"); close(fd); return -1; } buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; buf.memory = V4L2_MEMORY_MMAP; if (ioctl(fd, VIDIOC_QUERYBUF, &buf) < 0) { perror("Failed to query buffer"); close(fd); return -1; } void *data = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset); if (data == MAP_FAILED) { perror("Failed to mmap buffer"); close(fd); return -1; } struct stat st; if (stat(argv[1], &st) < 0) { perror("Failed to stat file"); munmap(data, buf.length); close(fd); return -1; } int file = open(argv[1], O_RDONLY); if (file < 0) { perror("Failed to open file"); munmap(data, buf.length); close(fd); return -1; } int offset = 0; while (offset < st.st_size) { int size = read(file, data, buf.length); if (size < 0) { perror("Failed to read file"); munmap(data, buf.length); close(fd); close(file); return -1; } buf.bytesused = size; if (ioctl(fd, VIDIOC_QBUF, &buf) < 0) { perror("Failed to queue buffer"); munmap(data, buf.length); close(fd); close(file); return -1; } if (ioctl(fd, VIDIOC_DQBUF, &buf) < 0) { perror("Failed to dequeue buffer"); munmap(data, buf.length); close(fd); close(file); return -1; } offset += size; } munmap(data, buf.length); close(fd); close(file); return 0; } ``` 请注意,上面的代码仅适用于特定的Amlogic平台和特定的MP4文件。如果您想在其他平台或播放其他类型的文件,请根据需要进行修改。另外,请确保您已经安装了正确的视频驱动程序和相关的库。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

零意@

您的打赏将是我继续创作的动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值