Bootstrap

Linux platform驱动

一. 简介

二. 总线 驱动 设备

 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");

;