内核版本:4.14.0
基于设备树、GPIO子系统
#include <linux/module.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
#define DEVICE_CNT 1 /* Number of device id */
#define DEVICE_NAME "led" /* Device name */
#define COMPAT_PROPT "xxx,led" /* Compatible property of the device matched with this driver. */
/* Device information structure. */
struct led_info {
struct led_classdev led_cdev; /* Led device */
int gpio_id;
};
/*
* @description : Initialize the device.
* @param -pdev: Pointer to platform device.
* @return : 0: Successful; Others: Failed.
*/
static int led_init(struct platform_device *pdev)
{
int ret;
const char *str;
struct led_info *p_led_info = platform_get_drvdata(pdev);
/* Get gpio id. */
p_led_info->gpio_id = of_get_named_gpio(pdev->dev.of_node, DEVICE_NAME"-gpio", 0);
if (!gpio_is_valid(p_led_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_led_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_led_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_led_info->gpio_id, 0);
else
gpio_direction_output(p_led_info->gpio_id, 1);
}
else
gpio_direction_output(p_led_info->gpio_id, 0);
return 0;
}
/*
* @description : Set up brightness of the led, can not be blocked.
* @param -p_led_cdev : Pointer to struct led_classdev.
* @param -brightness : Brightness value.
* @return : None.
*/
static void led_brightness_set(struct led_classdev *p_led_cdev, enum led_brightness brightness)
{
/* Get the address of the structure through the address of its member variable */
struct led_info *p_led_info = container_of(p_led_cdev, struct led_info, led_cdev);
if (brightness == LED_ON)
gpio_set_value(p_led_info>gpio_id, 1);
else
gpio_set_value(p_led_info->gpio_id, 0);
}
/*
* @description : Set up brightness of the led, can be blocked.
* @param -p_led_cdev : Pointer to struct led_classdev.
* @param -brightness : Brightness value.
* @return : 0.
*/
static int led_brightness_set_blocking(struct led_classdev *p_led_cdev, enum led_brightness brightness)
{
led_brightness_set(p_led_cdev, brightness);
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 led_probe(struct platform_device *pdev)
{
int ret;
struct led_info *p_led_info;
dev_info(&pdev->dev, "Driver and device matched successfully!\n");
/* Allocate memory for struct led_info */
p_led_info = devm_kzalloc(&pdev->dev, sizeof(struct led_info), GFP_KERNEL);
if (!p_led_info)
return -ENOMEM;
/* Store the led_info pointer in pdev->dev.driver_data for later use */
platform_set_drvdata(pdev, p_led_info);
/* Led init */
ret = led_init(pdev);
if (ret)
return ret;
/* Init the led_cdev */
p_led_info->led_cdev.name = DEVICE_NAME;
p_led_info->led_cdev.brightness = LED_OFF; //Set the default brightness
p_led_info->led_cdev.max_brightness = LED_FULL;
/* Bind the LED brightness setting function that cannot block */
p_led_info->led_cdev.brightness_set = led_brightness_set;
/* Bind the LED brightness setting function that can block */
p_led_info->led_cdev.brightness_set_blocking = led_brightness_set_blocking;
/* Register LED device */
return led_classdev_register(&pdev->dev, &p_led_info->led_cdev);
}
/*
* @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 led_remove(struct platform_device *pdev)
{
/* Get the led_info pointer which is stored in pdev->dev.driver_data before */
struct led_info *p_led_info = platform_get_drvdata(pdev);
/* Unregister the LED device */
led_classdev_unregister(&p_led_info->led_cdev);
dev_info(&pdev->dev, "Driver has been removed!\n");
return 0;
}
/* Match table */
static const struct of_device_id led_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 led_driver = {
.driver = {
.name = DEVICE_NAME, //Drive name, used to match device who has the same name.
.of_match_table = led_of_match, //Used to match the device tree who has the same compatible property.
},
.probe = led_probe, //probe function
.remove = led_remove, //remove function
};
/*
* Register or unregister platform driver,
* and Register the entry and exit functions of the Module.
*/
module_platform_driver(led_driver);
/*
* Author, driver information and LICENSE.
*/
MODULE_AUTHOR("蒋楼丶");
MODULE_DESCRIPTION(DEVICE_NAME" Driver Based on LED Driver Framework");
MODULE_LICENSE("GPL");