Linux旋钮驱动调试(安卓)

本文详细介绍了在Linux环境下,针对安卓平台如何进行旋钮驱动的开发与调试。首先阐述了旋钮的特性,即通过检测电平变化来判断旋钮的正反旋转。接着,展示了驱动程序的编写,包括初始化、中断处理函数以及GPIO配置。在设备树部分,讲解了如何配置旋钮GPIO属性。然后,提到了使用`getevent`工具进行input事件调试的方法。最后,文章给出了驱动的改进版,引入了线程处理旋钮事件,提高了响应的稳定性。
摘要由CSDN通过智能技术生成

Linux旋钮驱动调试(安卓)

一、旋钮特性

旋钮转动时会有相位差,我们只需要根据这个特点去判断是正旋还是反旋做出相应的操作即可:
正旋输出两次时:
在这里插入图片描述
反旋输出两次时:
在这里插入图片描述
由上图可以发现,判断正反旋其实很简单。判断哪个引脚先出现电平变化即可。

二、驱动编写

#include<linux/init.h>
#include<linux/kernel.h>
#include<linux/module.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/irq.h>
#include <linux/of_gpio.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/platform_device.h>

#define TAG    "knod"

#define pr_info(fmt, arg...) printk(KERN_ERR fmt, ##arg)
#define DEBUG_ON    1

#define DEBUG(fmt, arg...)          do {\
					if (DEBUG_ON)\
						pr_info("<<-DEBUG->> [%d]"fmt"\n", __LINE__, ##arg);\
			} while (0)
				
#define PIN_MAX 4

int knod_pin[PIN_MAX] = {0};
int knod_start[PIN_MAX/2] = {-1};
int knod_key_events[PIN_MAX] = {KEY_VOLUMEDOWN,KEY_VOLUMEUP,KEY_VOLUMEDOWN,KEY_VOLUMEUP};
/*业务需求需要两个旋钮*/

struct input_dev *input_dev;	

static int input_dev_init(struct platform_device *pdev)
{
	int ret = 0;
	int i = 0;
	
	input_dev = input_allocate_device();
	if (!input_dev)
	{
		input_free_device(input_dev);
		return -ENOMEM;
	}

	input_dev->name = "knod";
	input_dev->dev.parent = &pdev->dev;
	input_dev->id.bustype = BUS_HOST;
	input_dev->id.vendor = 0x0001;
	input_dev->id.product = 0x0001;
	input_dev->id.version = 0x0100;

	set_bit(EV_KEY, input_dev->evbit);
	
	for(i = 0; i < PIN_MAX; i ++)
		__set_bit(knod_key_events[i] & KEY_MAX, input_dev->keybit);
	
	input_set_drvdata(input_dev,pdev);

	/*register device*/
	ret = input_register_device(input_dev);
	if (ret < 0) 
	{
		input_free_device(input_dev);
		DEBUG("##@@input register failed.ret=%d\n",ret);
		return ret;
	}

	DEBUG("##@@input register ok.\n");

	return 0;
}

irqreturn_t knod1_handler(int irq,void *date)
{

	if (!(gpio_get_value(knod_pin[0])))
		knod_start[0] = 1;
	else	
		knod_start[0] = 0;
	
	return IRQ_HANDLED;
}

irqreturn_t knod2_handler(int irq,void *date)
{
	int state;
	
	state = gpio_get_value(knod_pin[1]);
	
	if ((knod_start[0]) == (state)) 
	{ 
		input_report_key(input_dev, knod_key_events[0], 1);
		input_sync(input_dev);
		input_report_key(input_dev, knod_key_events[0], 0);
		input_sync(input_dev);
	}
	
	if ((knod_start[0]) == (!state))
	{
		input_report_key(input_dev, knod_key_events[1], 1);
		input_sync(input_dev);
		input_report_key(input_dev, knod_key_events[1], 0);
		input_sync(input_dev);
	}
	return IRQ_HANDLED;
}

irqreturn_t knod3_handler(int irq,void *date)
{
	if (!(gpio_get_value(knod_pin[2])))
		knod_start[1] = 1;
	else
		knod_start[1] = 0;
	
	return IRQ_HANDLED;
}

irqreturn_t knod4_handler(int irq,void *date)
{
	int state;
	
	state = gpio_get_value(knod_pin[3]);
	
	if ((knod_start[1]) == (state)) 
	{ 
		input_report_key(input_dev, knod_key_events[2], 1);
		input_sync(input_dev);
		input_report_key(input_dev, knod_key_events[2], 0);
		input_sync(input_dev);
	}
	
	if ((knod_start[1]) == (!state))
	{
		input_report_key(input_dev, knod_key_events[3], 1);
		input_sync(input_dev);
		input_report_key(input_dev, knod_key_events[3], 0);
		input_sync(input_dev);
	}
	
	return IRQ_HANDLED;
}



static int irq_gpio_init(void)
{
	int i = 0;
	int ret = -1;
	int irq[PIN_MAX ] = {-1};
	
	char *knod_named[PIN_MAX] = {"knob_1_gpio1","knob_1_gpio2","knob_2_gpio1","knob_2_gpio2"};
	char *request_knod_named[PIN_MAX] = {"1_knod_pin","2_knod_pin","3_knod_pin","4_knod_pin"};
	char *knod_irq_name[PIN_MAX] = {"knod-1","knod-2","knod-3","knod-4"};

	irqreturn_t (*knod_handler[PIN_MAX])(int irq,void *date);
	static struct device_node *knod_node = NULL;
	
	
	knod_node = of_find_compatible_node(NULL, NULL, "xxxx, knod");
	if(knod_node == NULL)
	{
		DEBUG("@@of_find_compatible_node_fail !!!\n");
		goto of_find_compatible_node_fail;
	}

	knod_handler[0] = knod1_handler;
	knod_handler[1] = knod2_handler;
	knod_handler[2] = knod3_handler;
	knod_handler[3] = knod4_handler;
	
	
	for(i = 0; i < PIN_MAX;i ++)
	{
		knod_pin[i] =  of_get_named_gpio(knod_node,knod_named[i], 0);
		if (!gpio_is_valid(knod_pin[i])) 
		{
			DEBUG("@@of_get_named_gpio_fail !!!,knod_pin[%d] = %d\n",i,knod_pin[i]);
			goto of_get_named_gpio_fail;
		}
	
		gpio_free(knod_pin[i]);
		ret = gpio_request(knod_pin[i], request_knod_named[i]);
		if (ret < 0) 
		{
			DEBUG("@@gpio_request_fail !!!,button_pin[%d] = %d\n",i,knod_pin[i]);
			goto gpio_request_fail;
		}
		
		gpio_direction_input(knod_pin[i]);
			
		irq[i] = gpio_to_irq(knod_pin[i]);
		if(irq[i] < 0)
		{
			DEBUG("@@request irq is failed !!!,irq[%d] = %d\n",i,irq[i]);
			goto gpio_to_irq_fail;
		}
		
		ret = request_irq(irq[i], 
						knod_handler[i],
						IRQ_TYPE_EDGE_BOTH,
						knod_irq_name[i],
						NULL);
		if(ret != 0)
		{
			DEBUG("@@@request_irq_fail !!! ret=%d,irq[%d]=%d\n",ret,i,irq[i]);
			goto request_irq_fail;
		}
		
		DEBUG("@@refunction irq ret = %d,request_irq suc = %s\n",ret,request_knod_named[i]);		
	}

	return 0;

	
request_irq_fail:	
gpio_to_irq_fail:
gpio_request_fail:
	while(--i > -1)
		gpio_free(knod_pin[i]);	
of_get_named_gpio_fail:
of_find_compatible_node_fail:
	return ret;
	
}

static int knod_probe(struct platform_device *pdev)
{
	int ret = 0,i;
	
	DEBUG("knod_probe\n");
	
	ret = irq_gpio_init();
	if(ret != 0)
	{
		DEBUG("irq_gpio_init error\n");
		return ret;
	}

	ret = input_dev_init(pdev);
	if(ret != 0)
	{
		DEBUG("input_dev_init error\n");
		goto input_dev_init_error;
	}
	
	return 0;
	
input_dev_init_error:
	for(i = 0; i < PIN_MAX; i++ )
		gpio_free(knod_pin[i]);	
	
	return ret;	
}

static int knod_remove(struct platform_device *pdev)
{
	int i = 0;
	
	input_free_device(input_dev);	
	
	for(i = 0; i < PIN_MAX; i++ )
		gpio_free(knod_pin[i]);
	
	return 0;
}

 static struct platform_driver knod_driver = {
	.driver = {
			.name = "knod-dev",
			.owner = THIS_MODULE,
	},
	.remove = knod_remove,
	.probe = knod_probe,
};

static struct platform_device knod_device = {
    .name = "knod-dev",
    .id = -1,
};  

static int __init knod_init(void)
{
	int ret = -1;
	
	DEBUG("knod_init\n");
	
	ret = platform_device_register(&knod_device);
	if (ret) 
	{
		DEBUG("platform_device_register_failed ");
		goto platform_device_register_failed;
	}

	ret = platform_driver_register(&knod_driver);
    	if (ret) 
	{
       		DEBUG("platform_driver_register_failed ");
       		goto platform_driver_register_failed;
    }
	
	return 0;
	
platform_driver_register_failed:
	platform_device_unregister(&knod_device);
platform_device_register_failed:
	return ret;	
	
}

static void __exit knod_exit(void)
{
	DEBUG("knod exit\r\n");
	platform_driver_unregister(&knod_driver);
	platform_device_unregister(&knod_device);
}

module_init(knod_init);
module_exit(knod_exit);

MODULE_LICENSE("GPL");

三、GPIO配置(设备树)

1、dtsi配置节点属性

		knod: knod{
			compatible = "xxxx, knod";/*与驱动相对应*/
		};

2、dts配置gpio属性

&knod{
	knob_1_gpio1 = <&pio0 77 0>;
	knob_1_gpio2 = <&pio0 78 0>;
	knob_2_gpio1 = <&pio0 73 0>;
	knob_2_gpio2 = <&pio0 74 0>;
	interrupt-parent = <&pio0>;/*对应的gpio0中断控制器*/
	interrupts = <120 IRQ_TYPE_EDGE_BOTH>,<121 IRQ_TYPE_EDGE_BOTH>,<116 IRQ_TYPE_EDGE_BOTH>,<117 IRQ_TYPE_EDGE_BOTH>;/*对应的gpio中断号,中断触发方式(上下沿)*/
	status = "okay";
};

四、input事件调试

检查是否上报用getevent查看就足够了,想要了解具体的上报流程可以参考以下网址:
Android input键值从底层到应用层映射流程与修改方法

五、驱动改进版

#include<linux/init.h>
#include<linux/kernel.h>
#include<linux/module.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/irq.h>
#include <linux/of_gpio.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/platform_device.h>
#include <linux/sched.h>  
#include <linux/kthread.h> 
#include <linux/slab.h>


#define TAG    "knod"

#define pr_info(fmt, arg...) printk(KERN_ERR fmt, ##arg)
#define GPIO_DEBUG_ON    1
#define GPIO_DEBUG(fmt, arg...)          do {\

					if (GPIO_DEBUG_ON)\

						pr_info("<<-DEBUG->> [%d]"fmt"\n", __LINE__, ##arg);\

			} while (0)


#define PIN_MAX 4

#define KEY_AIR_CONDITION_UP   763
#define KEY_AIR_CONDITION_DOWN 764

#define KNOD1_RIGHT	1
#define KNOD1_LEFT	2
#define KNOD2_RIGHT	3
#define KNOD2_LEFT	4

#define KNOD1_ID 0
#define KNOD2_ID 1

#define KNOD_PIN1 0
#define KNOD_PIN2 1

#define KNOD_THREAD_STOP 0
#define KNOD_THREAD_RUN  1

#define KNOD_KEY_DOWN 1
#define KNOD_KEY_UP   0

#define KNOD_NAMED {\
"knob_1_gpio1","knob_1_gpio2",\
"knob_2_gpio1","knob_2_gpio2",\
}

#define REQUEST_KNOD_NAMED {\
"1_knod_pin","2_knod_pin",\
"3_knod_pin","4_knod_pin",\
}

#define KNOD_IRQ_NAME {\
"knod-1","knod-2",\
"knod-3","knod-4",\
}

#define KNOD_KEY_EVENTS {\
KEY_AIR_CONDITION_DOWN,KEY_AIR_CONDITION_UP,\
KEY_VOLUMEDOWN,KEY_VOLUMEUP,\
}

#define KNOD_SENSITIVITY  20

struct knod_info
{
	u8 knod_start;
	
	s32 irq[2];
	s32 knod_right_count;
	s32 knod_left_count;
	u32 knod_pin[2];
	u32 knod_key_events[2];
};

struct knod_struct
{
	u8 knod_status;
	u8 thread_flage;
	
	spinlock_t irq_lock;
	
	struct input_dev *input_dev;	
	struct task_struct *knod_input_task;
	struct completion origin_completion;
	struct knod_info knod[PIN_MAX/2];
};

struct knod_struct *knod;

void input_knod_event(u32 knod_id,u32 knod_pin)
{
#if 0
	s32 count = 0;

	if(knod_pin == KNOD_PIN2)
		count = knod->knod[knod_id].knod_right_count;
	else
		count = knod->knod[knod_id].knod_left_count;

	GPIO_DEBUG("@@count = %d\n",count);

	while(count > 0)
	{
		if(1)	
		{

		}	

		--count;
	}
#endif
	input_report_key(knod->input_dev, knod->knod[knod_id].knod_key_events[knod_pin], KNOD_KEY_DOWN);
	input_sync(knod->input_dev);
	input_report_key(knod->input_dev, knod->knod[knod_id].knod_key_events[knod_pin], KNOD_KEY_UP);
	input_sync(knod->input_dev);
	
}

int knod_input_thread(void *data)
{
	u32 i = 0;
	
	while(1)
	{
		//GPIO_DEBUG("@@knod_input_thread knod->knod_status = %d,knod->thread_flage = %d\n",knod->knod_status,knod->thread_flage);
		if(knod->knod_status == 0)
		{
			wait_for_completion(&knod->origin_completion);
		}

		msleep(10);//Filter the fast rotation within 10ms

		switch(knod->knod_status)
		{
			case KNOD1_RIGHT:	
				input_knod_event(KNOD1_ID,KNOD_PIN2);break;

			case KNOD1_LEFT:
				input_knod_event(KNOD1_ID,KNOD_PIN1);break;

			case KNOD2_RIGHT:
				input_knod_event(KNOD2_ID,KNOD_PIN2);break;
			
			case KNOD2_LEFT:
				input_knod_event(KNOD2_ID,KNOD_PIN1);break;

			default:break;
		}

		for(i = 0;i < (PIN_MAX/2);i ++)
		{
			knod->knod[i].knod_left_count = 0;
			knod->knod[i].knod_right_count = 0;			
		}

		knod->thread_flage = KNOD_THREAD_STOP;	
		wait_for_completion(&knod->origin_completion);
	}
	return 0;
}

static int input_dev_init(struct platform_device *pdev)
{
	int ret = 0;
	int i = 0,j = 0;
	
	knod->input_dev = input_allocate_device();
	if (!knod->input_dev)
	{
		input_free_device(knod->input_dev);
		return -ENOMEM;
	}

	knod->input_dev->name = "knod";
	knod->input_dev->dev.parent = &pdev->dev;
	knod->input_dev->id.bustype = BUS_HOST;
	knod->input_dev->id.vendor = 0x0001;
	knod->input_dev->id.product = 0x0001;
	knod->input_dev->id.version = 0x0100;

	set_bit(EV_KEY, knod->input_dev->evbit);
	
	for(i = 0; i < (PIN_MAX/2); i ++)
		for(j = 0; j < 2;j ++)
			__set_bit(knod->knod[i].knod_key_events[j] & KEY_MAX, knod->input_dev->keybit);
	
	input_set_drvdata(knod->input_dev,pdev);

	/*register device*/
	ret = input_register_device(knod->input_dev);
	if (ret < 0) 
	{
		input_free_device(knod->input_dev);
		GPIO_DEBUG("##@@input register failed.ret=%d\n",ret);
		return ret;
	}

	GPIO_DEBUG("##@@input register ok.\n");

	return 0;
}

void knod_count(u32 knod_id,u32 knod_pin)
{
	u8 state;


	if(knod_pin == KNOD_PIN1)
	{
		if (!(gpio_get_value(knod->knod[knod_id].knod_pin[knod_pin])))
			knod->knod[knod_id].knod_start = 1;
		else	
			knod->knod[knod_id].knod_start = 0;
	}
	else if(knod_pin == KNOD_PIN2)
	{
		state = gpio_get_value(knod->knod[knod_id].knod_pin[knod_pin]);	
	
		if ((knod->knod[knod_id].knod_start) == (state)) 
		{ 
			if(knod_id == KNOD1_ID)
				knod->knod_status = KNOD1_LEFT;
			else 
				knod->knod_status = KNOD2_LEFT;

			++(knod->knod[knod_id].knod_left_count);
			GPIO_DEBUG("knod->knod[%d].knod_left_count=%d!!!\n",knod_id,knod->knod[knod_id].knod_left_count);
		}
	
		if ((knod->knod[knod_id].knod_start) == (!state))
		{
			if(knod_id == KNOD1_ID)
				knod->knod_status = KNOD1_RIGHT;
			else 
				knod->knod_status = KNOD2_RIGHT;

			++(knod->knod[knod_id].knod_right_count);
			GPIO_DEBUG("knod->knod[%d].knod_right_count=%d!!!\n",knod_id,knod->knod[knod_id].knod_right_count);
		}

		if(knod->thread_flage == KNOD_THREAD_STOP)	
		{
			complete(&knod->origin_completion);
			knod->thread_flage = KNOD_THREAD_RUN;
		}
	}
	else
	{
		GPIO_DEBUG("knod_pin error\n");
	}

}


irqreturn_t knod1_handler(int irq,void *date)
{
	unsigned long irqflags = 0;

	spin_lock_irqsave(&knod->irq_lock, irqflags);//prevent irq from nesting

	knod_count(KNOD1_ID,KNOD_PIN1);

	spin_unlock_irqrestore(&knod->irq_lock, irqflags);
	
	return IRQ_HANDLED;
}

irqreturn_t knod2_handler(int irq,void *date)
{
	unsigned long irqflags = 0;	

	spin_lock_irqsave(&knod->irq_lock, irqflags);//prevent irq from nesting

	knod_count(KNOD1_ID,KNOD_PIN2);

	spin_unlock_irqrestore(&knod->irq_lock, irqflags);

	return IRQ_HANDLED;
}

irqreturn_t knod3_handler(int irq,void *date)
{
	unsigned long irqflags = 0;

	spin_lock_irqsave(&knod->irq_lock, irqflags);//prevent irq from nesting

	knod_count(KNOD2_ID,KNOD_PIN1);

	spin_unlock_irqrestore(&knod->irq_lock, irqflags);	

	return IRQ_HANDLED;
}

irqreturn_t knod4_handler(int irq,void *date)
{
	unsigned long irqflags = 0;

	spin_lock_irqsave(&knod->irq_lock, irqflags);//prevent irq from nesting

	knod_count(KNOD2_ID,KNOD_PIN2);
	
	spin_unlock_irqrestore(&knod->irq_lock, irqflags);
	
	return IRQ_HANDLED;
}



static int irq_gpio_init(void)
{
	u32 i = 0,j = 0,count = 0;
	u32 ret = -1;

	u8 *knod_named[PIN_MAX] = KNOD_NAMED;
	u8 *request_knod_named[PIN_MAX] = REQUEST_KNOD_NAMED;
	u8 *knod_irq_name[PIN_MAX] = KNOD_IRQ_NAME;
	u32 knod_key_events[PIN_MAX] = KNOD_KEY_EVENTS;

	irqreturn_t (*knod_handler[PIN_MAX])(int irq,void *date);

	static struct device_node *knod_node = NULL;
	
	
	knod_node = of_find_compatible_node(NULL, NULL, "audio, knod");
	if(knod_node == NULL)
	{
		GPIO_DEBUG("@@of_find_compatible_node_fail !!!\n");
		goto of_find_compatible_node_fail;
	}


	knod = (struct knod_struct *)kmalloc( sizeof(struct knod_struct),GFP_KERNEL);
    	if (knod == NULL)
    	{
       		ret = -ENOMEM;
        	GPIO_DEBUG("[knob]:kmalloc err\n");
        	goto kmalloc_error;
    	}

	memset(knod,0,sizeof(struct knod_struct));

	knod_handler[0] = knod1_handler;
	knod_handler[1] = knod2_handler;
	knod_handler[2] = knod3_handler;
	knod_handler[3] = knod4_handler;
	

	for(i = 0,count = 0; i < (PIN_MAX/2) && count < PIN_MAX;i ++)
	{
		for(j = 0; j < 2;j ++,count++)
		{
			knod->knod[i].knod_pin[j] = of_get_named_gpio(knod_node,knod_named[count],0);
			if (!gpio_is_valid(knod->knod[i].knod_pin[j])) 
			{
				GPIO_DEBUG("@@of_get_named_gpio_fail !!!,knod->knod[%d]->knod_pin[%d] = %d\n",i,j,knod->knod[i].knod_pin[j]);
				goto of_get_named_gpio_fail;
			}
			
			gpio_free(knod->knod[i].knod_pin[j]);
			ret = gpio_request(knod->knod[i].knod_pin[j], request_knod_named[count]);
			if (ret < 0) 
			{
				GPIO_DEBUG("@@of_get_named_gpio_fail !!!,knod->knod[%d]->knod_pin[%d] = %d\n",i,j,knod->knod[i].knod_pin[j]);
				goto gpio_request_fail;
			}
		
			knod->knod[i].irq[j] = gpio_to_irq(knod->knod[i].knod_pin[j]);
			if(knod->knod[i].irq[j] < 0)
			{
				GPIO_DEBUG("@@request irq is failed !!!,knod->knod[%d]->irq[%d]  = %d\n",i,j,knod->knod[i].irq[j]);
				goto gpio_to_irq_fail;
			}	

			ret = request_irq(knod->knod[i].irq[j], 
						knod_handler[count],
						IRQ_TYPE_EDGE_BOTH,
						knod_irq_name[count],
						NULL);
			if(ret != 0)
			{
				GPIO_DEBUG("@@request irq is failed !!!,ret =%d,knod->knod[%d]->irq[%d]  = %d\n",ret,i,j,knod->knod[i].irq[j]);
				goto request_irq_fail;
			}
		
			GPIO_DEBUG("@@refunction irq ret = %d,request_irq suc = %s\n",ret,request_knod_named[count]);			
			memcpy(&knod->knod[i].knod_key_events[j],&knod_key_events[count],sizeof(knod_key_events[count]));
		}		
	}


	return 0;
	
request_irq_fail:	
gpio_to_irq_fail:
gpio_request_fail:
	for(i = 0; i < (PIN_MAX/2);i ++)
		for(j = 0; j < 2;j ++)
			gpio_free(knod->knod[i].knod_pin[j]);	
of_get_named_gpio_fail:
kmalloc_error:
of_find_compatible_node_fail:
	return ret;	
}

static int knod_probe(struct platform_device *pdev)
{
	int ret = 0,i,j;
	
	GPIO_DEBUG("knod_probe\n");
	
	ret = irq_gpio_init();
	if(ret != 0)
	{
		GPIO_DEBUG("irq_gpio_init error\n");
		goto irq_gpio_init_error;
	}

	init_completion(&knod->origin_completion);

   	knod->knod_input_task = kthread_create(knod_input_thread, NULL, "knod_input_task");
   	if(IS_ERR(knod->knod_input_task))
	{
	     	printk("Unable to start kernel thread.\n");
     		ret = PTR_ERR(knod->knod_input_task);
     		knod->knod_input_task = NULL;

     		goto kthread_create_error;
   	}

	wake_up_process(knod->knod_input_task);

	ret = input_dev_init(pdev);
	if(ret != 0)
	{
		GPIO_DEBUG("input_dev_init error\n");
		goto input_dev_init_error;
	}

	return 0;
	
input_dev_init_error:
kthread_create_error:
	for(i = 0; i < (PIN_MAX/2);i ++)
		for(j = 0; j < 2;j ++)
			gpio_free(knod->knod[i].knod_pin[j]);
irq_gpio_init_error:
	return ret;	
}

static int knod_remove(struct platform_device *pdev)
{
	int i = 0,j = 0;

	kthread_stop(knod->knod_input_task);	

	input_free_device(knod->input_dev);	
	
	for(i = 0; i < (PIN_MAX/2);i ++)
		for(j = 0; j < 2;j ++)
			gpio_free(knod->knod[i].knod_pin[j]);
	
	return 0;
}

 static struct platform_driver knod_driver = {
	.driver = {
			.name = "knod-dev",
			.owner = THIS_MODULE,
	},
	.remove = knod_remove,
	.probe = knod_probe,
};

static struct platform_device knod_device = {
    .name = "knod-dev",
    .id = -1,
};  

static int __init knod_init(void)
{
	int ret = -1;
	
	GPIO_DEBUG("knod_init\n");
	
	ret = platform_device_register(&knod_device);
	if (ret) 
	{
		GPIO_DEBUG("platform_device_register_failed ");
		goto platform_device_register_failed;
	}

	ret = platform_driver_register(&knod_driver);
    	if (ret) 
	{
       		GPIO_DEBUG("platform_driver_register_failed ");
       		goto platform_driver_register_failed;
    	}
	
	return 0;
	
platform_driver_register_failed:
	platform_device_unregister(&knod_device);
platform_device_register_failed:
	return ret;	
	
}

static void __exit knod_exit(void)
{
	GPIO_DEBUG("knod exit\r\n");
	platform_driver_unregister(&knod_driver);
	platform_device_unregister(&knod_device);
}

module_init(knod_init);
module_exit(knod_exit);

MODULE_LICENSE("GPL");
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值