设备驱动框架之misc简单模板

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

#include  
#include  
#include  
#include  
#include 

#define DEVICE_NAME		"xxx"			/* Device name */
#define COMPAT_PROPT	"xxx,xxx"		/* Compatible property of the device matched with this driver. */

/* Device information structure. */
struct misc_info { 
	struct miscdevice miscdev;		/* misc device */ 
	int gpio_id;
};

/* 
 * @description :	Open the device.
 * @param – inode :	The inode passed to the driver.
 * @param - filp :	Device file, the file structure has a member variable called 'private_data', 
 * 					which is normally pointed to the device structure when it is opened.
 * @return :		0: Successful; Others: Failed.
 */
static int misc_open(struct inode *inode, struct file *filp) 
{ 

	return 0; 
} 

/* 
 * @description :	Write to the device
 * @param - filp :	Device file, indicating the opened file descriptor.
 * @param - buf :	The data to write to the device.
 * @param - cnt :	The length of the data to be written.
 * @param - offt :	Offset relative to the first address of the file.
 * @return : 		The number of bytes written, negative means the write failed.
 */ 
static ssize_t misc_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt) 
{ 
	int ret;
	char kern_buf[5];
	struct misc_info *p_misc_info = filp->private_data;
	
	
	ret = copy_from_user(kern_buf, buf, cnt);
	if (ret < 0 )
	{
		printk(KERN_ERR "%s: Failed to copy data from user buffer!\n");
		return -EFAULT;
	}
	
	if (kern_buf[0] == '0')
		gpio_set_value(p_misc_info->gpio_id, 0);
	else if (kern_buf[0] == '1')
		gpio_set_value(p_misc_info->gpio_id, 1);

	return cnt;
}

/* The device operation function structure. */
static struct file_operations misc_fops = {
	.owner = THIS_MODULE,
	.open = misc_open,
	.write = misc_write,
}; 

/* 
 * @description :	Initialize the device.
 * @param -pdev:	Pointer to platform device.
 * @return :		0: Successful; Others: Failed.
 */
static int misc_init(struct platform_device *pdev) 
{ 
	int ret;
	const char *str;
	struct misc_info *p_misc_info  = platform_get_drvdata(pdev);
	
	/* Get gpio id. */
	p_misc_info->gpio_id = of_get_named_gpio(pdev->dev.of_node, DEVICE_NAME"-gpio", 0);
	if (!gpio_is_valid(p_misc_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_misc_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_misc_info->gpio_id, DEVICE_NAME"-GPIO");
	if (ret)
	{
		dev_err(&pdev->dev, "Failed to request GPIO!");
		return ret;
	}
	
	/* Set GPIO as output mode and default value. */
	ret = of_property_read_string(pdev->dev.of_node, "default-state", &str);
	if (!ret)
	{
		if (strcmp(str, "on"))
			gpio_direction_output(p_misc_info->gpio_id, 0);
		else
			gpio_direction_output(p_misc_info->gpio_id, 1);
	}
	else
		gpio_direction_output(p_misc_info->gpio_id, 0);
	
	return 0;
}

/* 
 * @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 misc_probe(struct platform_device *pdev)
{
	int ret;
	struct misc_info *p_misc_info;
	
	dev_info(&pdev->dev, "Driver and device matched successfully!\n");
	
	/* Allocate memory for struct led_info */
	p_misc_info = devm_kzalloc(&pdev->dev, sizeof(struct misc_info), GFP_KERNEL);
	if (!p_misc_info)
		return -ENOMEM;
	
	/* Store the misc_info pointer in pdev->dev.driver_data for later use */
	platform_set_drvdata(pdev, p_misc_info);
	
	/* Misc device init */
	ret = misc_init(pdev);
	if (ret)
		return ret;

	/* Init the led_cdev */
	p_misc_info->miscdev.name = DEVICE_NAME;
	p_misc_info->miscdev.minor = MISC_DYNAMIC_MINOR;	/* Dynamically assign minor device id */
	p_misc_info->miscdev.fops = &misc_fops;
	
	/* Register misc device */
	return misc_register(&p_misc_info->miscdev); 
} 

/* 
 * @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 misc_remove(struct platform_device *pdev) 
{ 
	/* Get the struct misc_info pointer which is stored in pdev->dev.driver_data before */
	struct misc_info *p_misc_info = platform_get_drvdata(pdev);
	
	/* Some Reset code */
	/* Turn off the beeper when logging off the module */
	gpio_set_value(p_misc_info->gpio_id, 0);
	
	/* Unregister the misc device */
	misc_deregister(&p_misc_info->miscdev);
	
	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 misc_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 = misc_probe,					//probe function
	.remove = misc_remove,				//remove function
};

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

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

你可能感兴趣的:(Linux,linux)