一. 简介
二. 总线 驱动 设备
1.驱动:platform_driver
platform_driver 结 构 体 表 示 platform 驱 动
struct platform_driver {
int (*probe)(struct platform_device *);
int (*remove)(struct platform_device *);
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*resume)(struct platform_device *);
struct device_driver driver;
const struct platform_device_id *id_table;
bool prevent_deferred_probe;
};
probe 函数,当驱动与设备匹配成功以后 probe 函数就会执行
driver 成员,为 device_driver 结构体变量,
Linux 内核里面大量使用到了面向对象的思维,device_driver 相当于基类,
提供了最基础的驱动框架。 plaform_driver 继承了这个基类,
然后在此基础上又添加了一些特有的成员变量。
struct device_driver {
const char *name;
struct bus_type *bus;
struct module *owner;
const char *mod_name; /* used for built-in modules */
bool suppress_bind_attrs; /* disables bind/unbind via sysfs */
enum probe_type probe_type;
const struct of_device_id *of_match_table; //采用设备树的时候驱动使用的匹配表
const struct acpi_device_id *acpi_match_table;
int (*probe) (struct device *dev);
int (*remove) (struct device *dev);
void (*shutdown) (struct device *dev);
int (*suspend) (struct device *dev, pm_message_t state);
int (*resume) (struct device *dev);
const struct attribute_group **groups;
const struct dev_pm_ops *pm;
struct driver_private *p;
};
注册/卸载
int platform_driver_register (struct platform_driver *driver)
void platform_driver_unregister(struct platform_driver *driver)
2. 设备(设备树中节点)
如图:
3.示例
static int xxx_probe(struct platform_device *dev)
{
....
}
static const struct of_device_id xxx_of_match[] = {
{ .compatible = "xxx-gpio" },
{ /* Sentinel */ }
};
/*
* platform 平台驱动结构体
*/
static struct platform_driver xxx_driver = {
.driver = {
.name = "xxx",
.of_match_table = xxx_of_match,
},
.probe = xxx_probe,
.remove = xxx_remove,
};
module_platform_driver(myled_driver);
#if 0
/* 驱动模块加载 */
static int __init xxxdriver_init(void)
{
...
return platform_driver_register(&xxx_driver);
}
/* 驱动模块卸载 */
static void __exit xxxdriver_exit(void)
{
platform_driver_unregister(&xxx_driver);
}
#endif
三. led设备框架
内核源码 drivers/leds 目录下有两个文件 ledclass.c 和 led-core.c,它们就是 LED 驱动框架的核心文件,由内核开发工作者进行维护和升级。
在 drivers/leds 目录下有很多 leds-xxx.c 文件,这些文件就是使用 LED 驱动框架编写的 LED 驱动程序源文件。
1.API
int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
void led_classdev_unregister(struct led_classdev *led_cdev)
返回值:成功返回 0,失败则返回一个负数
parent: LED 设备的父设备, struct device 类型指针变量。
led_cdev:需要注册的 LED 设备结构体, struct led_classdev 类型指针变量
struct led_classdev 结构体来描述一个 LED 设备:
struct led_classdev {
const char *name;
enum led_brightness brightness;
enum led_brightness max_brightness;
int flags;
...
};
2 示例
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/of.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/uaccess.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <linux/gfp.h>
#include <linux/leds.h>
struct myled_dev {
int gpio;
struct led_classdev led_dev;
};
static int led_gpio_init(struct platform_device *pdev)
{
struct myled_dev * psled = platform_get_drvdata(pdev);
/*gpio操作:1.获取gpio; 2.申请使用; 3.设置输入输出 4.设置初始值*/
psled->gpio = of_get_named_gpio(pdev->dev.of_node, "led-gpio", 0);
printk("of_get_named_gpio\r\n");
int ret = devm_gpio_request(&pdev->dev,psled->gpio, "led-gpio");
ret = gpio_direction_output(psled->gpio, 0);
return 0;
}
void myled_brightness_set(struct led_classdev *led_cdev1, enum led_brightness brightness)
{
struct myled_dev *psled = container_of(led_cdev1,struct myled_dev,led_dev);
int val;
val = brightness;
if(val == LED_OFF)
gpio_set_value(psled->gpio, LED_OFF);
else
gpio_set_value(psled->gpio, val);
return ;
}
int myled_brightness_set_blocking(struct led_classdev *led_cdev, enum led_brightness brightness)
{
myled_brightness_set(led_cdev, brightness);
return 0;
}
static int test_probe(struct platform_device *pdev)
{
dev_info(&pdev->dev, "LED device and driver matched successfully!\n");
struct myled_dev *psled = devm_kzalloc(&pdev->dev,sizeof(struct myled_dev),GFP_KERNEL);
if(!psled)
return -ENOMEM;
platform_set_drvdata(pdev,psled);
led_gpio_init(pdev);
psled->led_dev.name = "myled";
psled->led_dev.max_brightness = LED_FULL;
psled->led_dev.brightness = LED_OFF;
psled->led_dev.brightness_set = myled_brightness_set;
psled->led_dev.brightness_set_blocking = myled_brightness_set_blocking;
led_classdev_register(&pdev->dev, &psled->led_dev);
return 0;
}
static int test_remove(struct platform_device *dev)
{
;
return 0;
}
static const struct of_device_id test_of_match[] = {
{ .compatible = "m0mo,led" },
{ /* Sentinel */ }
};
/*
* platform 平台驱动结构体
*/
static struct platform_driver psled_driver = {
.driver = {
.name = "psled",
.of_match_table = test_of_match,
},
.probe = test_probe,
.remove = test_remove,
};
module_platform_driver(psled_driver);
MODULE_AUTHOR("m0mo");
MODULE_DESCRIPTION("New Char Device Driver");
MODULE_LICENSE("GPL");