【RV1126】按键中断--使用输入子系统事件方式

news2025/2/13 20:24:26

文章目录

  • 选择GPIO
  • 修改设备树
  • 修改驱动源码probe:
  • 增加外部中断以及定时器服务函数。
  • 命令行测试中断
    • 确定按键的输入子系统的event
    • 使用hexdump命令测试按键实际效果
  • 测试应用程序
  • 完整的驱动代码

我使用的是荣品的pro-rv1126。

选择GPIO

结合原理图
在这里插入图片描述决定使用UART1接口
在这里插入图片描述
接了一个独立按键模块,默认上拉,按下时低电平。
在这里插入图片描述

修改设备树

选择的这个GPIO默认是UART1,所以要先注释掉设备树中的描述。
编辑文件:rp_rv1126_sdk/kernel/arch/arm/boot/dts/rongpin/rv1126_1109_common.dtsi

/*&uart1 {
	pinctrl-names = "default";
	pinctrl-0 = <&uart1m0_xfer>;
	status = "okay";
};*/

在下面的文件中还需要增加内容。
编辑文件:rp_rv1126_sdk/kernel/arch/arm/boot/dts/pro-rv1126.dts

...
	//rpgpio init
	rp_gpio {
		status = "okay";
		compatible = "rp_gpio";

        gpio0b7{
            gpio_num = <&gpio0 RK_PB7 IRQ_TYPE_EDGE_FALLING>;
            gpio_function = <2>;
        }
        ...
...

修改驱动源码probe:

rv1126的gpio采用的是gpiolib架构,工程师可以不必查阅寄存器即可完成配置,在kernal/drivers/rongpin/rp_gpio.c中修改。

static int rp_gpio_probe(struct platform_device *pdev) {
	struct device_node *np = pdev->dev.of_node;
	struct device_node *child_np;
	struct device *dev = &pdev->dev;
	static struct proc_dir_entry *root_entry_gpio;
	enum of_gpio_flags  gpio_flags;
	int ret = 0;
	int gpio_cnt = 0;	
	char gpio_name_num[GPIO_NUM_MAX];
	int gpio_in_cnt = 0;
	int cnt =0;

	gpio_data = devm_kzalloc(&pdev->dev, sizeof(struct rp_gpio_data),GFP_KERNEL);
	if (!gpio_data) {
		dev_err(&pdev->dev, "failed to allocate memory\n");
		return -ENOMEM;
	}

	gpio_data->gpio_dts_num = of_get_child_count(np);
        printk("rp_gpio prepare build %d gpio\n",gpio_data->gpio_dts_num);

    	if (gpio_data->gpio_dts_num == 0){
        	dev_info(&pdev->dev, "no gpio defined\n");
	}

	/* create node */
	root_entry_gpio = proc_mkdir("rp_gpio", NULL);
	
	for_each_child_of_node(np, child_np)
	{
		/* parse dts */
		gpio_data->rp_gpio_num[gpio_cnt].gpio_num = of_get_named_gpio_flags(child_np, "gpio_num", 0, &gpio_flags);
		if (!gpio_is_valid(gpio_data->rp_gpio_num[gpio_cnt].gpio_num)){
			return -1;
		}		

		gpio_data->rp_gpio_num[gpio_cnt].gpio_name = (char*)child_np -> name;
		gpio_data->rp_gpio_num[gpio_cnt].action = gpio_flags;
		gpio_data->rp_gpio_num[gpio_cnt].gpio_ctrl = gpio_cnt;
		of_property_read_u32(child_np, "gpio_function", &(gpio_data->rp_gpio_num[gpio_cnt].gpio_function));

		printk("rp_gpio request %s\n",gpio_data->rp_gpio_num[gpio_cnt].gpio_name);

		
		switch(gpio_data->rp_gpio_num[gpio_cnt].gpio_function) {
			case GPIO_FUNCTION_INPUT :		/* init input gpio */
				ret = gpio_request(gpio_data->rp_gpio_num[gpio_cnt].gpio_num, "gpio_num");
				if (ret < 0)
				{
					printk("gpio%d request error\n",gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
				}else{
					printk("success request gpio %d in\n",gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
					
					//gpio_set_value(gpio_data->rp_gpio_num[gpio_cnt].gpio_num,!gpio_data->rp_gpio_num[gpio_cnt].action);
					gpio_direction_input(gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
					event_flag = gpio_flags;
					of_property_read_u32(child_np, "send_mode", &(gpio_data->rp_gpio_num[gpio_cnt].send_mode));
					of_property_read_u32(child_np, "gpio_event", &(gpio_data->rp_gpio_num[gpio_cnt].gpio_event));
					gpio_in_cnt++;
				}
				break;
		#if 1 /* add code for liefyuan */
			case GPIO_FUNCTION_IRQ :		/* init input gpio */
				
				ret = gpio_request(gpio_data->rp_gpio_num[gpio_cnt].gpio_num, "gpio_num");
				if (ret < 0)
				{
					printk("gpio%d request error\n",gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
				}else{
					printk("success request gpio %d irq\n",gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
					
					//gpio_set_value(gpio_data->rp_gpio_num[gpio_cnt].gpio_num,!gpio_data->rp_gpio_num[gpio_cnt].action);
					printk("gpio_to_irq(gpio_data->rp_gpio_num[gpio_cnt].gpio_num) = %d\n",gpio_to_irq(gpio_data->rp_gpio_num[gpio_cnt].gpio_num));
					irq_gpio = gpio_data->rp_gpio_num[gpio_cnt].gpio_num;
					gpio_direction_input(gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
					ret  = request_irq(gpio_to_irq(gpio_data->rp_gpio_num[gpio_cnt].gpio_num),
								button_irq_handler,
								//IRQ_TYPE_EDGE_FALLING,
								IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
								"key_irq",
								NULL);
 
					
					//1、分配一个input_dev结构体
					buttons_dev = input_allocate_device();
					if(!buttons_dev)
					{
						printk("input_allocate_device error!\n");
						return -ENOMEM;
					}
					buttons_dev->name = "input_key";
					buttons_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
					input_set_capability(buttons_dev, EV_KEY, KEY_0);
				
					ret = input_register_device(buttons_dev);
					if (ret) {
						printk("register input device failed!\r\n");
						return ret;
					}
 
					//初始化定时器,用于按键消抖
					timer_setup(&buttons_timer,my_buttons_timer_function,0);
					buttons_timer.expires = jiffies + msecs_to_jiffies(20);
					add_timer(&buttons_timer);
				}
				break;
		#endif
				
			case GPIO_FUNCTION_OUTPUT :		/* init output gpio */
				ret = gpio_request(gpio_data->rp_gpio_num[gpio_cnt].gpio_num, "gpio_num");
				if (ret < 0){
					printk("gpio%d request error\n",gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
					//return ret;
				}else{
					gpio_direction_output(gpio_data->rp_gpio_num[gpio_cnt].gpio_num,!gpio_data->rp_gpio_num[gpio_cnt].action);
					printk("success request gpio%d out\n",gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
				}
				break;
				
			case GPIO_FUNCTION_FLASH :
				ret = gpio_request(gpio_data->rp_gpio_num[gpio_cnt].gpio_num, "gpio_num");
				if (ret < 0){
					printk("gpio%d request error\n",gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
					//return ret;
				}else{
					gpio_direction_output(gpio_data->rp_gpio_num[gpio_cnt].gpio_num,!gpio_data->rp_gpio_num[gpio_cnt].action);
					printk("success request gpio%d flash\n",gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
					gpio_in_cnt++;

				}
				break;
		}
		
		sprintf(gpio_name_num,gpio_data->rp_gpio_num[gpio_cnt].gpio_name,gpio_cnt);
		proc_create(gpio_name_num, 0666 , root_entry_gpio , &gpio_ops);
		gpio_cnt++;
	}
	
	if (gpio_in_cnt > 0)
	{
		/* init timer */
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0))		
		/* old linux version timer api */
		init_timer(&(gpio_data->mytimer));
		gpio_data->mytimer.expires = jiffies + msecs_to_jiffies(10000);
		gpio_data->mytimer.function = send_event;
		add_timer(&(gpio_data->mytimer));
#else
		timer_setup(&(gpio_data->mytimer), send_event, 0);
		gpio_data->mytimer.expires = jiffies + msecs_to_jiffies(10000);
		add_timer(&(gpio_data->mytimer));
#endif		
		
		/* init struct input_dev */ 
		gpio_data->input = devm_input_allocate_device(dev);
		gpio_data->input->name = "gpio_event";      /* pdev->name; */
		gpio_data->input->phys = "gpio_event/input1";
		gpio_data->input->dev.parent = dev;
		gpio_data->input->id.bustype = BUS_HOST;
		gpio_data->input->id.vendor = 0x0001;
		gpio_data->input->id.product = 0x0001;
		gpio_data->input->id.version = 0x0100;
		for(cnt = 0; cnt < gpio_cnt; cnt++){
			if (gpio_data->rp_gpio_num[cnt].gpio_function == 1){
				input_set_capability(gpio_data->input, EV_KEY, gpio_data->rp_gpio_num[cnt].gpio_event);
			}
		}
		ret = input_register_device(gpio_data->input);
	}
	
	
	gpio_wq = create_singlethread_workqueue("gpio_wq");
	INIT_WORK(&gpio_work, gpio_work_func);
	
	platform_set_drvdata(pdev, gpio_data);	
	return 0;
}

代码中的case GPIO_FUNCTION_IRQ是新增加的,主要是申请IO、设置IO方向、注册中断函数、配置输入子系统、初始化定时器。其中定时器是用来消抖的。

按键信号通过输入子系统传递到应用程序。

增加外部中断以及定时器服务函数。

unsigned irq_gpio;
static struct input_dev *buttons_dev;
static struct timer_list buttons_timer;
 
static irqreturn_t button_irq_handler(int irq, void *dev_id)
{
    mod_timer(&buttons_timer, jiffies+msecs_to_jiffies(20));
    return IRQ_HANDLED;
}
 
 
//定时器中断处理函数
static void my_buttons_timer_function(struct timer_list* list)
{
	int gpio_value = 0;
	gpio_value = gpio_get_value(irq_gpio);
 
	if(gpio_value == 0)
	{
		input_report_key(buttons_dev, KEY_0, 1);
		input_sync(buttons_dev);
	}
	if(gpio_value == 1)
	{
		input_report_key(buttons_dev, KEY_0, 0);
		input_sync(buttons_dev);
	}
}

命令行测试中断

确定按键的输入子系统的event

编写应用程序之前,先使用命令cat /proc/bus/input/devices确定驱动使用的输入子系统的事件号。

使用命令:cat /proc/bus/input/devices

[root@RV1126_RV1109:/]# cat /proc/bus/input/devices
I: Bus=0019 Vendor=0000 Product=0000 Version=0000
N: Name="rk805 pwrkey"
P: Phys=rk805_pwrkey/input0
S: Sysfs=/devices/platform/ff3f0000.i2c/i2c-0/0-0020/rk805-pwrkey/input/input0
U: Uniq=
H: Handlers=event0
B: PROP=0
B: EV=3
B: KEY=100000 0 0 0

I: Bus=0018 Vendor=dead Product=beef Version=28bb
N: Name="goodix-ts"
P: Phys=input/ts
S: Sysfs=/devices/virtual/input/input1
U: Uniq=
H: Handlers=event1
B: PROP=2
B: EV=b
B: KEY=40000800 40 0 0 0
B: ABS=2658000 0

I: Bus=0019 Vendor=0001 Product=0001 Version=0100
N: Name="gpio_event"
P: Phys=gpio_event/input1
S: Sysfs=/devices/platform/rp_power/input/input2
U: Uniq=
H: Handlers=event2
B: PROP=0
B: EV=3
B: KEY=100000 0 0 0

I: Bus=0000 Vendor=0000 Product=0000 Version=0000
N: Name="input_key"
P: Phys=
S: Sysfs=/devices/virtual/input/input3
U: Uniq=
H: Handlers=event3
B: PROP=0
B: EV=100003
B: KEY=800

I: Bus=0019 Vendor=0001 Product=0001 Version=0100
N: Name="adc-keys"
P: Phys=adc-keys/input0
S: Sysfs=/devices/platform/adc-keys/input/input4
U: Uniq=
H: Handlers=event4
B: PROP=0
B: EV=3
B: KEY=800 600 0 0 2

I: Bus=0000 Vendor=0000 Product=0000 Version=0000
N: Name="rockchip,rk809-codec Headphones"
P: Phys=ALSA
S: Sysfs=/devices/platform/rk809-sound/sound/card1/input5
U: Uniq=
H: Handlers=event5
B: PROP=0
B: EV=21
B: SW=4

I: Bus=0003 Vendor=046d Product=c52f Version=0111
N: Name="Logitech USB Receiver"
P: Phys=usb-ffe00000.usb-1.3/input0
S: Sysfs=/devices/platform/ffe00000.usb/usb1/1-1/1-1.3/1-1.3:1.0/0003:046D:C52F.0001/input/input6
U: Uniq=
H: Handlers=event6
B: PROP=0
B: EV=17
B: KEY=ffff0000 0 0 0 0 0 0 0 0
B: REL=143
B: MSC=10

I: Bus=0003 Vendor=046d Product=c52f Version=0111
N: Name="Logitech USB Receiver Consumer Control"
P: Phys=usb-ffe00000.usb-1.3/input1
S: Sysfs=/devices/platform/ffe00000.usb/usb1/1-1/1-1.3/1-1.3:1.1/0003:046D:C52F.0002/input/input7
U: Uniq=
H: Handlers=event7
B: PROP=0
B: EV=1f
B: KEY=300ff 0 0 0 0 483ffff 17aff32d bf544446 0 0 1 130ff3 8b17c000 677bfa d9415fed 9ed680 4400 0 10000002
B: REL=40
B: ABS=1 0
B: MSC=10

结合代码中关于输入设备结构体参数的描述:

//1、分配一个input_dev结构体
buttons_dev = input_allocate_device();
if(!buttons_dev)
{
	printk("input_allocate_device error!\n");
	return -ENOMEM;
}
buttons_dev->name = "input_key";
buttons_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
input_set_capability(buttons_dev, EV_KEY, KEY_0);

名字是:“input_key”
所以可以知道:

I: Bus=0000 Vendor=0000 Product=0000 Version=0000
N: Name="input_key"
P: Phys=
S: Sysfs=/devices/virtual/input/input3
U: Uniq=
H: Handlers=event3
B: PROP=0
B: EV=100003
B: KEY=800

就是按键事件对应的就是:/dev/input/event3

使用hexdump命令测试按键实际效果

使用hexdump /dev/input/eventX命令辅助调试

[root@RV1126_RV1109:/]# hexdump /dev/input/event3
0000000 028b 0000 4ed0 0009 0001 000b 0001 0000
0000010 028b 0000 4ed0 0009 0000 0000 0000 0000
0000020 028b 0000 05ae 000d 0001 000b 0000 0000
0000030 028b 0000 05ae 000d 0000 0000 0000 0000

按键按下释放有数据打印,说明驱动基本上是通了!

另外在上电打印信息与设备树代码对比:
在这里插入图片描述

测试应用程序

由上面可以知道自定义的那个按键的节点是/dev/input/event3由此可以编写一个应用层的测试程序。

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

int main(int argc, char **argv)
{
    int fd = 0;
    struct input_event buttons_event;
    unsigned long cur_ms = 0;
    data = data;
    
    fd = open("/dev/input/event3", O_RDWR);
    if (fd < 0)
        printf("can't open!\n");
 
    while (1)
    {
        read(fd, &buttons_event, sizeof(struct input_event));
 
//        if(buttons_event.type == EV_SYN)
//            continue;
 
        cur_ms = (buttons_event.time.tv_sec * 1000) + (buttons_event.time.tv_usec/1000);
 
        //打印时间,事件类型,事件码,事件值
        printf("cur_ms:%ld type:0x%x code:%d value:%d\n",
            cur_ms,
            buttons_event.type,
            buttons_event.code,
            buttons_event.value);
    }
    close(fd);
    return 0;
}

完整的驱动代码

最后完整的rp_gpio.c

/* ****************************
*rpdzkj power contral driver *
* Platform : Rockchip Android *
* Version : v2.0.1  *
* Update Date : 20220124 *
* author :  jeff  *
*********************************/

#include <linux/module.h>
#include <linux/err.h>
#include <linux/gpio.h>
#include <linux/kernel.h>
#include <linux/of_gpio.h>
#include <linux/of_platform.h>
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/proc_fs.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <asm/uaccess.h>
#include <linux/string.h>
#include <linux/uaccess.h>
#include <linux/irq.h>
#include <linux/of_irq.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/version.h>

#define GPIO_NUM_MAX 40

#define GPIO_FUNCTION_OUTPUT 0
#define GPIO_FUNCTION_INPUT 1
#define GPIO_FUNCTION_IRQ 2
#define GPIO_FUNCTION_FLASH 3

static int flash_flag = 0;
static int flash_counter = 0;

struct rp_gpio {
	int gpio_num;		//gpui num
	int gpio_irq;
	int action;			//gpio flag
	int gpio_event;		//input only
	int send_mode;		//input only
	int gpio_function;	//gpio function,i/o
	int gpio_ctrl;
	char *gpio_name;
};

struct rp_gpio_data {
	struct rp_gpio rp_gpio_num[GPIO_NUM_MAX];
	struct input_dev *input;
	struct timer_list mytimer;
	int gpio_dts_num;
};

static struct rp_gpio_data *gpio_data = NULL;
static int event_flag = 0;
static int open_now = 0;
static char* file_name = NULL;


static struct workqueue_struct *gpio_wq;
static struct work_struct  gpio_work;

/************************************************************************/
unsigned irq_gpio;
static struct input_dev *buttons_dev;
static struct timer_list buttons_timer;
 
static irqreturn_t button_irq_handler(int irq, void *dev_id)
{
    mod_timer(&buttons_timer, jiffies+msecs_to_jiffies(20));
    return IRQ_HANDLED;
}
 
 
//定时器中断处理函数
static void my_buttons_timer_function(struct timer_list* list)
{
	int gpio_value = 0;
	gpio_value = gpio_get_value(irq_gpio);
 
	if(gpio_value == 0)
	{
		input_report_key(buttons_dev, KEY_0, 1);
		input_sync(buttons_dev);
	}
	if(gpio_value == 1)
	{
		input_report_key(buttons_dev, KEY_0, 0);
		input_sync(buttons_dev);
	}
}
/*****************************************************************************/

static int gpio_open(struct inode *inode, struct file *file)
{
	struct dentry* dent = file->f_path.dentry;
	int i = 0;

	file_name = (char*)(dent->d_name.name);

	for (i = 0; i < gpio_data->gpio_dts_num; i++){
		if(!strcmp(file_name,gpio_data->rp_gpio_num[i].gpio_name)){
			open_now = i;
		}
	}
	return 0;
}


static ssize_t gpio_write(struct file *file, const char *buffer,size_t count, loff_t *data)
{
	char buf[2]={0};
	char s1[]="1";
	
	if(copy_from_user(&buf[0],buffer,1)){
		printk("failed to copy data to kernel space\n");
		return -EFAULT;     
	}

	if(!strcmp(buf,s1)){
		gpio_set_value(gpio_data->rp_gpio_num[open_now].gpio_num,1);
//		printk("%s write 1 succeed\n",gpio_data->rp_gpio_num[open_now].gpio_name);
	}else{	
		gpio_set_value(gpio_data->rp_gpio_num[open_now].gpio_num,0);
//		printk("%s write 0 succeed\n",gpio_data->rp_gpio_num[open_now].gpio_name);
	}
	return count;
}


static ssize_t gpio_read(struct file *file, char __user * buffer, size_t count, loff_t *data)
{
	int gpio_val = 0;
	int len = 0;
	char s[10] = {0};

	if(*data)
		return 0;

	gpio_val = gpio_get_value(gpio_data->rp_gpio_num[open_now].gpio_num);
//	printk("get %s value %d\n",gpio_data->rp_gpio_num[open_now].gpio_name,gpio_val);

	len = sprintf(s+len, "%d\n",gpio_val);	

	return simple_read_from_buffer(buffer, count, data, s, 2);
}


static const struct file_operations gpio_ops = {
	.owner          = THIS_MODULE,
    .open           = gpio_open,
    .write          = gpio_write,
    .read           = gpio_read,
};


static void gpio_work_func(struct work_struct *work){
	int gpio_value = 0;
	int i = 0;
	
	flash_counter++;
				
	for(i = 0; i <= gpio_data->gpio_dts_num; i++) {
		switch(gpio_data->rp_gpio_num[i].gpio_function) {
			case GPIO_FUNCTION_INPUT :
				gpio_value  = gpio_get_value(gpio_data->rp_gpio_num[i].gpio_num);

				if(gpio_value == 1){
					input_report_key(gpio_data->input, gpio_data->rp_gpio_num[i].gpio_event, 1);
					input_sync(gpio_data->input);
				}
				if(gpio_value == 0){
					input_report_key(gpio_data->input, gpio_data->rp_gpio_num[i].gpio_event, 0);
					input_sync(gpio_data->input);
				}
	
            //printk("\n%s gpio num %d  %d\n",__func__,gpio_data->rp_gpio_num[i].gpio_num,gpio_value);
            //printk("\n%s send event %d\n",__func__,gpio_data->rp_gpio_num[i].gpio_event);
				break;
			case GPIO_FUNCTION_FLASH :
				if (10 == flash_counter)
					{
						gpio_set_value(gpio_data->rp_gpio_num[i].gpio_num,!flash_flag);
					}
				break;
		}
	}
	
	if (10 == flash_counter)
		{
			flash_flag = !flash_flag;
			flash_counter = 0;
		}
	
}

#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0))
static void send_event(unsigned long data){
#else
static void send_event(struct timer_list* list){
#endif

	queue_work(gpio_wq, &gpio_work);
	
	mod_timer(&(gpio_data->mytimer), jiffies + msecs_to_jiffies(200));
}


static int rp_gpio_probe(struct platform_device *pdev) {
	struct device_node *np = pdev->dev.of_node;
	struct device_node *child_np;
	struct device *dev = &pdev->dev;
	static struct proc_dir_entry *root_entry_gpio;
	enum of_gpio_flags  gpio_flags;
	int ret = 0;
	int gpio_cnt = 0;	
	char gpio_name_num[GPIO_NUM_MAX];
	int gpio_in_cnt = 0;
	int cnt =0;

	gpio_data = devm_kzalloc(&pdev->dev, sizeof(struct rp_gpio_data),GFP_KERNEL);
	if (!gpio_data) {
		dev_err(&pdev->dev, "failed to allocate memory\n");
		return -ENOMEM;
	}

	gpio_data->gpio_dts_num = of_get_child_count(np);
        printk("rp_gpio prepare build %d gpio\n",gpio_data->gpio_dts_num);

    	if (gpio_data->gpio_dts_num == 0){
        	dev_info(&pdev->dev, "no gpio defined\n");
	}

	/* create node */
	root_entry_gpio = proc_mkdir("rp_gpio", NULL);
	
	for_each_child_of_node(np, child_np)
	{
		/* parse dts */
		gpio_data->rp_gpio_num[gpio_cnt].gpio_num = of_get_named_gpio_flags(child_np, "gpio_num", 0, &gpio_flags);
		if (!gpio_is_valid(gpio_data->rp_gpio_num[gpio_cnt].gpio_num)){
			return -1;
		}		

		gpio_data->rp_gpio_num[gpio_cnt].gpio_name = (char*)child_np -> name;
		gpio_data->rp_gpio_num[gpio_cnt].action = gpio_flags;
		gpio_data->rp_gpio_num[gpio_cnt].gpio_ctrl = gpio_cnt;
		of_property_read_u32(child_np, "gpio_function", &(gpio_data->rp_gpio_num[gpio_cnt].gpio_function));

		printk("rp_gpio request %s\n",gpio_data->rp_gpio_num[gpio_cnt].gpio_name);

		
		switch(gpio_data->rp_gpio_num[gpio_cnt].gpio_function) {
			case GPIO_FUNCTION_INPUT :		/* init input gpio */
				ret = gpio_request(gpio_data->rp_gpio_num[gpio_cnt].gpio_num, "gpio_num");
				if (ret < 0)
				{
					printk("gpio%d request error\n",gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
				}else{
					printk("success request gpio %d in\n",gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
					
					//gpio_set_value(gpio_data->rp_gpio_num[gpio_cnt].gpio_num,!gpio_data->rp_gpio_num[gpio_cnt].action);
					gpio_direction_input(gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
					event_flag = gpio_flags;
					of_property_read_u32(child_np, "send_mode", &(gpio_data->rp_gpio_num[gpio_cnt].send_mode));
					of_property_read_u32(child_np, "gpio_event", &(gpio_data->rp_gpio_num[gpio_cnt].gpio_event));
					gpio_in_cnt++;
				}
				break;
		#if 1 /* add code for liefyuan */
			case GPIO_FUNCTION_IRQ :		/* init input gpio */
				
				ret = gpio_request(gpio_data->rp_gpio_num[gpio_cnt].gpio_num, "gpio_num");
				if (ret < 0)
				{
					printk("gpio%d request error\n",gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
				}else{
					printk("success request gpio %d irq\n",gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
					
					//gpio_set_value(gpio_data->rp_gpio_num[gpio_cnt].gpio_num,!gpio_data->rp_gpio_num[gpio_cnt].action);
					printk("gpio_to_irq(gpio_data->rp_gpio_num[gpio_cnt].gpio_num) = %d\n",gpio_to_irq(gpio_data->rp_gpio_num[gpio_cnt].gpio_num));
					irq_gpio = gpio_data->rp_gpio_num[gpio_cnt].gpio_num;
					gpio_direction_input(gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
					ret  = request_irq(gpio_to_irq(gpio_data->rp_gpio_num[gpio_cnt].gpio_num),
								button_irq_handler,
								//IRQ_TYPE_EDGE_FALLING,
								IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
								"key_irq",
								NULL);
 
					
					//1、分配一个input_dev结构体
					buttons_dev = input_allocate_device();
					if(!buttons_dev)
					{
						printk("input_allocate_device error!\n");
						return -ENOMEM;
					}
					buttons_dev->name = "input_key";
					buttons_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
					input_set_capability(buttons_dev, EV_KEY, KEY_0);
				
					ret = input_register_device(buttons_dev);
					if (ret) {
						printk("register input device failed!\r\n");
						return ret;
					}
 
					//初始化定时器,用于按键消抖
					timer_setup(&buttons_timer,my_buttons_timer_function,0);
					buttons_timer.expires = jiffies + msecs_to_jiffies(20);
					add_timer(&buttons_timer);
				}
				break;
		#endif
				
			case GPIO_FUNCTION_OUTPUT :		/* init output gpio */
				ret = gpio_request(gpio_data->rp_gpio_num[gpio_cnt].gpio_num, "gpio_num");
				if (ret < 0){
					printk("gpio%d request error\n",gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
					//return ret;
				}else{
					gpio_direction_output(gpio_data->rp_gpio_num[gpio_cnt].gpio_num,!gpio_data->rp_gpio_num[gpio_cnt].action);
					printk("success request gpio%d out\n",gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
				}
				break;
				
			case GPIO_FUNCTION_FLASH :
				ret = gpio_request(gpio_data->rp_gpio_num[gpio_cnt].gpio_num, "gpio_num");
				if (ret < 0){
					printk("gpio%d request error\n",gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
					//return ret;
				}else{
					gpio_direction_output(gpio_data->rp_gpio_num[gpio_cnt].gpio_num,!gpio_data->rp_gpio_num[gpio_cnt].action);
					printk("success request gpio%d flash\n",gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
					gpio_in_cnt++;

				}
				break;
		}
		
		sprintf(gpio_name_num,gpio_data->rp_gpio_num[gpio_cnt].gpio_name,gpio_cnt);
		proc_create(gpio_name_num, 0666 , root_entry_gpio , &gpio_ops);
		gpio_cnt++;
	}
	
	if (gpio_in_cnt > 0)
	{
		/* init timer */
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0))		
		/* old linux version timer api */
		init_timer(&(gpio_data->mytimer));
		gpio_data->mytimer.expires = jiffies + msecs_to_jiffies(10000);
		gpio_data->mytimer.function = send_event;
		add_timer(&(gpio_data->mytimer));
#else
		timer_setup(&(gpio_data->mytimer), send_event, 0);
		gpio_data->mytimer.expires = jiffies + msecs_to_jiffies(10000);
		add_timer(&(gpio_data->mytimer));
#endif		
		
		/* init struct input_dev */ 
		gpio_data->input = devm_input_allocate_device(dev);
		gpio_data->input->name = "gpio_event";      /* pdev->name; */
		gpio_data->input->phys = "gpio_event/input1";
		gpio_data->input->dev.parent = dev;
		gpio_data->input->id.bustype = BUS_HOST;
		gpio_data->input->id.vendor = 0x0001;
		gpio_data->input->id.product = 0x0001;
		gpio_data->input->id.version = 0x0100;
		for(cnt = 0; cnt < gpio_cnt; cnt++){
			if (gpio_data->rp_gpio_num[cnt].gpio_function == 1){
				input_set_capability(gpio_data->input, EV_KEY, gpio_data->rp_gpio_num[cnt].gpio_event);
			}
		}
		ret = input_register_device(gpio_data->input);
	}
	
	
	gpio_wq = create_singlethread_workqueue("gpio_wq");
	INIT_WORK(&gpio_work, gpio_work_func);
	
	platform_set_drvdata(pdev, gpio_data);	
	return 0;
}

static int rp_gpio_remove(struct platform_device *pdev)
{
    return 0;
}


static const struct of_device_id rp_gpio_of_match[] = {
    { .compatible = "rp_gpio" },
    { }
};

static struct platform_driver rp_gpio_driver = {
    .probe = rp_gpio_probe,
    .remove = rp_gpio_remove,
    .driver = {
                .name           = "rp_gpio",
                .of_match_table = of_match_ptr(rp_gpio_of_match),
        },
};

module_platform_driver(rp_gpio_driver);
MODULE_LICENSE("GPL");

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/645454.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SIFT算法分析

引言 尺度不变特征变换&#xff08;SITF&#xff09;是传统机器学习算法当中比较难的一个算法&#xff0c;步骤也相对其它传统机器学习算法要更多一些。 使用场景 以上是一个图像拼接的过程&#xff0c;两张图像拼接成一张图&#xff0c;有时候由于镜头限制&#xff0c;不能一…

基于Google breakpad编译构建和使用案例

Googlepad是一款用于程序崩溃时自动生成转储文件&#xff08;.dmp&#xff09;的可跨平台开源库。 1.Googlepad代码下载地址&#xff1a; git clone https://chromium.googlesource.com/breakpad/breakpad 如果翻不了墙可以在gitee上下载breakpad模块。 2.Googlepad采用gyp来…

C语言之实用调试技巧(2)

如何写出好&#xff08;易于调试&#xff09;的代码 优秀的代码&#xff1a; 1. 代码运行正常 2. bug很少 3. 效率高 4. 可读性高 5. 可维护性高 6. 注释清晰 7. 文档齐全 常见的 coding 技巧&#xff1a; 1. 使用assert 2. 尽量使用const 3. 养成良好的编码风格 4. …

Linux防火墙学习笔记14

firewalld是什么&#xff1f; Firewalld属于动态防火墙&#xff0c;是CentOS7系统中用于对netfilter内核模块用户空间管理工具。 Firewalld仅仅代替了iptables service部分&#xff0c;其底层还是使用iptables作为防火墙规则管理工具。 Firewalld中zone概念及作用&#xff1…

盖茨预言AI助理成标配,AI+RPA打破AI准入高门槛!

根据微软联合创始人比尔盖茨的预测&#xff0c;未来顶级的人工智能公司将会开发一种全新的“个人AI助理”。比尔盖茨表示&#xff0c;“个人AI助理”将会具有出色的功能&#xff0c;可以改变人们的生活方式以及工作方式。无论哪一家公司能够赢得AI助理竞争先机&#xff0c;都会…

耗时半月,终于把CSDN上的Java面试八股文整理成了PDF合集(Java基础+Redis+MySQL+多线程与高并发+JVM+分布式+Spring+微服务)

大家好&#xff0c;最近有不少小伙伴在后台留言&#xff0c;近期的面试越来越难了&#xff0c;要背的八股文越来越多了&#xff0c;考察得越来越细&#xff0c;越来越底层&#xff0c;明摆着就是想让我们徒手造航母嘛&#xff01;实在是太为难我们这些程序员了。 这不&#xf…

Android:OKHttp

特点 支持HTTP2/SPDYSocket自动选择最好路线&#xff0c;并支持自动重连拥有自动维护的Socket连接池&#xff0c;减少握手次数拥有队列线程池&#xff0c;轻松写并发拥有Interceptors轻松处理请求与响应&#xff08;比如透明GZIP压缩&#xff09;实现基于Headers的缓存策略 基…

JDBC Apache—DBUtils 详解(通俗易懂)

目录 一、前言 二、Apache—DBUtils的引入 1.传统使用ResultSet的缺点 : 2.改进方法 : 3.改进方法的模拟实现 : 三、Apache—DBUtils的使用 1.基本介绍 : 2.准备工作 : 3.DBUtils查询(DQL) : 4.query方法源码分析 : 5.DBUtils处理(DML) : 四、总结 一、前言 第六节…

Web3下的去中心化契约

随着Web3的兴起&#xff0c;智能合约成为了这一新兴领域中最为重要的概念之一。智能合约是一种在区块链上执行的可编程代码&#xff0c;其作用类似于传统世界中的合约&#xff0c;但具有更多的灵活性和安全性。本文将介绍智能合约的基本概念、工作原理以及在Web3下的应用场景。…

如何提取视频里面的音频?简单三个方法即可完成!

分享3个简单易上手的视频提取音频方法&#xff0c;这些方法可以帮助你单独提取保存视频中的人物对话音频内容和背景音乐&#xff0c;并且提取成功的音频文件还可用于其他视频创作。 方法一&#xff1a;PR提取音频 Adobe Premiere Pro&#xff08;简称PR&#xff09;不仅可以用…

番外篇2 离线服务器 环境安装与配置

&#xff08;离线远程服务器旧版torch的卸载与安装问题&#xff09; Step4: 查看自己是否已经成功安装了Anaconda,输入此命令conda --version -------------------------------------------------------------------------------------------------------- Step1:离线创建con…

为什么浏览器突然打不开网页了?

苏生不惑第433 篇原创文章&#xff0c;将本公众号设为星标&#xff0c;第一时间看最新文章。 前几天写了什么是cookie总有人问我 Cookie 是什么&#xff1f;&#xff0c;说到Charles代理工具&#xff0c;但是为什么关了它就打不开网页呢&#xff1f;今天来说说这个。 Charles可…

企企通联合创始人兼总架构师杨华:剖析SRM顾问长期主义项目实践

近日&#xff0c;国产大飞机C919商业首飞成功引起广泛关注&#xff0c;此后&#xff0c;我们的出行选择中新增了一项“自己国家的大飞机”&#xff0c;给国人带来了更多的期待和自豪。 走难而正确的路&#xff0c;国产大飞机C919从项目立项到“一飞冲天”&#xff0c;花了十六年…

激活函数ReLU和SiLU的区别

文章目录 前言ReLU&#xff08;Rectified Linear Unit&#xff09;Leaky ReLUFReLU&#xff08;Flatten ReLU&#xff09;SiLU&#xff08;Sigmoid Linear Unit&#xff09;总结 前言 在这里&#xff0c;我就简单写一下两个激活函数的概念以及区别&#xff0c;详细的过程可以看…

2023年新风口,抖音的产业带服务商招募?怎么开通?

抖音电商致力于成为用户发现并获得优价好物的首选平台。众多抖音创作者通过短视频/直播等丰富的内容形式&#xff0c;给用户提供更个性化、更生动、更高效的消费体验。抖音电商积极引入优质合作伙伴&#xff0c;为品牌发展、商家变现提供多元的服务。 抖音产业带服务商招募区域…

操作系统常识

4.进程同步 1.什么是临界区&#xff1f;什么是临界资源 在计算机系统中&#xff0c;临界资源指的是被多个并发执行的线程或进程共享访问的某个资源&#xff0c;如共享内存区、共享文件等。 临界区指的是访问临界资源的那部分代码片段&#xff0c;它是一段需要保护的代码区域…

项目管理工具究竟能否提升效率?看看它们的作用和优势

随着各种类型的项目不断涌现&#xff0c;项目管理工具在现代社会变得越来越重要。作为一个项目经理&#xff0c;需要确保项目在时间和预算方面得到控制&#xff0c;并且达成预期的效果。在这个过程中&#xff0c;项目管理工具可以带来很多帮助。 首先 项目管理工具可以为项目经…

推动绿色计算 共迎绿色未来|2023开放原子全球开源峰会绿色基础设施技术分论坛圆满收官

6 月 11 日&#xff0c;2023 开放原子全球开源峰会绿色基础设施技术分论坛圆满举行。蚂蚁集团 4 位专家带来了蚂蚁在探索打磨“绿色计算”实践中的核心技术领域一线观察。 蚂蚁集团高级技术专家、数据中间件负责人李玉明 李玉明分享了《开源分布式事务框架 Seata 以及其在金融…

电商--抢购架构总结

文章目录 背景业务流程业务难点技术难点技术方案技术方向具体落地客户端流控网关流控容器流控后端接口流控数据库流控 流控总结优化读取加速异步化流程处理系统扩容 压测监控 总结参考文献 背景 这是个在做NFT电商项目时遇到的场景&#xff0c;要求运营可以商家某个系列的NFT商…

顺序栈与链栈

简介 栈和队列是两种重要的线性结构。从数据结构角度看&#xff0c; 栈和队列也是线性表&#xff0c; 其特殊性在于栈和队列的基本操作是线性表操作的子集&#xff0c; 它们是操作受限的线性表。 栈 (stack) 是限定仅在表尾进行插入或删除操作的线性表。 因此&#xff0c; 对…