内核版本:4.14.0
基于设备树、GPIO子系统
#include <linux/module.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>
#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 const 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 */
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 miscdevice struct */
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 misc_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, misc_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 = misc_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");