Linux Input子系统驱动模板

内核版本:4.14.0
基于设备树、platform虚拟总线架构、GPIO子系统、GPIO按键

#include  
#include  
#include  
#include  
#include  
#include  
#include  

#define DEVICE_NAME		"ps_key0"			/* Device name */
#define COMPAT_PROPT	"navigator,ps_key0"	/* Compatible property of the device matched with this driver. */
#define EVENT_CODE		KEY_0				/* Event code */

/* Device information structure. */
struct input_info { 
	struct input_dev *p_inputdev;
	struct timer_list timer;
	int gpio_id;
	int irq_id;
};

/* Interrupt handler */
static irqreturn_t irq_handler(int irq_id, void *arg)
{	
	struct input_info *p_input_info = (struct input_info *)arg;

	/* Disable input interrupt */
	disable_irq_nosync(p_input_info->irq_id);
	
	mod_timer(&p_input_info->timer, jiffies+msecs_to_jiffies(15));
	
	return IRQ_HANDLED;
} 

/* Timer function */
static void timer_function(unsigned long arg)
{
	struct input_info *p_input_info = (struct input_info *)arg;
	int val;
	
	val = gpio_get_value(p_input_info->gpio_id);
	
	/* Report event */
	input_report_key(p_input_info->p_inputdev, EVENT_CODE, !val);
	input_sync(p_input_info->p_inputdev);
	
	/* Enable input interrupt */
	enable_irq(p_input_info->irq_id);
}

/* 
 * @description :	Initialize the device.
 * @param -pdev:	Pointer to platform device.
 * @return :		0: Successful; Others: Failed.
 */
static int input_init(struct platform_device *pdev) 
{ 
	int ret;
	unsigned long IntrTrigType;
	struct input_info *p_input_info  = platform_get_drvdata(pdev);
	
	/* Get gpio id. */
	p_input_info->gpio_id = of_get_named_gpio(pdev->dev.of_node, DEVICE_NAME"-gpio", 0);
	if (!gpio_is_valid(p_input_info->gpio_id))
	{
		dev_err(&pdev->dev, "Failed to get gpio id!"); 
		return -EINVAL;
	}
	
	printk(KERN_INFO "%s: gpio id = %d\n", DEVICE_NAME, p_input_info->gpio_id);
	
	/* 
	 * Request the use of GPIO from the gpio subsystem. 
	 * Equivalent to gpio_request(), it is also used to request GPIO resources (pins) from the
	 * system. However, the function has the devm_ prefix, which means that it is a version of the function
	 * that includes device resource management. Therefore, when using it, the struct device pointer of the
     * device needs to be specified, and there is no need to manually release GPIO when uninstalling the driver.
	 */
	ret = devm_gpio_request(&pdev->dev, p_input_info->gpio_id, DEVICE_NAME"-GPIO");
	if (ret)
	{
		dev_err(&pdev->dev, "Failed to request GPIO!");
		return ret;
	}
	
	/* Set GPIO as input mode */
	gpio_direction_input(p_input_info->gpio_id);
	
	/* Get intr id */
	p_input_info->irq_id = irq_of_parse_and_map(pdev->dev.of_node, 0);
	if (!p_input_info->irq_id)
	{
		dev_err(&pdev->dev, "Failed to get intr id!\n"); 
		return -EINVAL;
	}
	
	/* Get the trigger type from device tree. */
	IntrTrigType = irq_get_trigger_type(p_input_info->irq_id);
	if (IntrTrigType == IRQF_TRIGGER_NONE)
		IntrTrigType =  IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING; 
	
	/* Request the intr */
	return devm_request_irq(&pdev->dev, p_input_info->irq_id, irq_handler, IntrTrigType, "PS_KEY0 Intr", p_input_info);
}

/* 
 * @description :	Probe function of the platform, it will be executed when the 
 * 					platform driver and platform device matching successfully.
 * @param -pdev :	Pointer to platform device.
 * @return :		0: Successful; Others: Failed.
 */
static int input_probe(struct platform_device *pdev)
{
	int ret;
	struct input_info *p_input_info;
	
	dev_info(&pdev->dev, "Driver and device matched successfully!\n");
	
	/* Allocate memory for struct led_info */
	p_input_info = devm_kzalloc(&pdev->dev, sizeof(struct input_info), GFP_KERNEL);
	if (!p_input_info)
		return -ENOMEM;
	
	/* Store the misc_info pointer in pdev->dev.driver_data for later use */
	platform_set_drvdata(pdev, p_input_info);
	
	/* Input device init */
	ret = input_init(pdev);
	if (ret)
		return ret;
	
	/* Init timer */
	init_timer(&p_input_info->timer);
	p_input_info->timer.function = timer_function;
	p_input_info->timer.data = (unsigned long)p_input_info;

	/* Init the input_dev */
	p_input_info->p_inputdev = devm_input_allocate_device(&pdev->dev);
	if (!p_input_info->p_inputdev)
		return -ENOMEM; 
	
	p_input_info->p_inputdev->name = DEVICE_NAME;
	
	__set_bit(EV_KEY, p_input_info->p_inputdev->evbit);	/* Key event */
	__set_bit(EV_REP, p_input_info->p_inputdev->evbit);	/* Repeated event */
	__set_bit(EVENT_CODE, p_input_info->p_inputdev->keybit);	/* Event-code */
		
	/* Register input device */
	return input_register_device(p_input_info->p_inputdev); 
} 

/* 
 * @description :	Release some resources. This function will be executed when the platform
 *					driver module is unloaded.
 * @param :			None.
 * @return :		0: Successful; Others: Failed.
 */
static int input_remove(struct platform_device *pdev) 
{ 
	/* Get the struct input_info pointer which is stored in pdev->dev.driver_data before */
	struct input_info *p_input_info = platform_get_drvdata(pdev);
	
	/* Some Reset code */
	/* Delete timer */
	del_timer_sync(&p_input_info->timer);
	
	/* Unregister the input device */
	input_unregister_device(p_input_info->p_inputdev);
	
	dev_info(&pdev->dev, "Driver has been removed!\n"); 

	return 0;
} 

/* Match table */
static const struct of_device_id device_of_match[] = {
	{.compatible = COMPAT_PROPT},
	{/* Sentinel */}
};

/* 
 * Declare device matching table. Note that this macro is generally used to dynamically 
 * load and unload drivers for hot-pluggable devices such as USB devices.
 */
MODULE_DEVICE_TABLE(of, device_of_match); 

/* Platform driver struct */
static struct platform_driver input_driver = {
	.driver = {
		.name = DEVICE_NAME,				//Drive name, used to match device who has the same name.
		.of_match_table = device_of_match,	//Used to match the device tree who has the same compatible property.
	},
	.probe = input_probe,					//probe function
	.remove = input_remove,				//remove function
};

/*
 * Register or unregister platform driver,
 * and Register the entry and exit functions of the Module.
 */
module_platform_driver(input_driver);

/* 
 *  Author, driver information and LICENSE.
 */ 
MODULE_AUTHOR("蒋楼丶");
MODULE_DESCRIPTION(DEVICE_NAME" Driver Based on Input Subsystem"); 
MODULE_LICENSE("GPL");

你可能感兴趣的:(Linux,linux,运维,服务器)