linux驱动模型——platform(2)

一. platform 组织架构

    1.1. platform工作体系都定义在drivers/base/platform.c中

    1.2. platform相关函数声明在include/linux/platform_device.h

    1.3. platform.c中两个重要结构体

        1.3.1. platform_device结构体

            a. 该结构体的name用于总线与device匹配

struct platform_device {
    const char    * name;                // 平台总线下设备的名字
    int        id;                    //当多个同类型设备时用以区分,比如s5pv210_led.0-->这里是name,0是id
    struct device    dev;                   // 所有设备通用的属性部分
    u32        num_resources;         // 设备使用到的resource的个数
    struct resource    * resource;            // 设备使用到的资源数组的首地址

    const struct platform_device_id    *id_entry;

    /* arch specific additions */
    struct pdev_archdata    archdata;       //用来提供扩展性的
};
View Code

        1.3.2. platform_driver结构体。

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;    // 设备ID表
};
View Code

    1.4. 平台总线体系的工作流程

        1.4.1. 系统启动时在bus系统中注册platform

        1.4.2. 内核移植的人负责提供platform_device

        1.4.3. 写驱动的人负责提供platform_driver

        1.4.4. platform的match函数发现driver和device匹配后,调用driver的probe函数来完成驱动的初始化和安装,然后设备就工作起来了

   1.5. platform.c中四个函数      

        1.5.1. platform_device_register

            a. 向总线中注册device

/**
 * platform_device_register - add a platform-level device
 * @pdev: platform device we're adding
 */
int platform_device_register(struct platform_device *pdev)
{
    device_initialize(&pdev->dev);
    return platform_device_add(pdev);
}
View Code

        1.5.2. platform_device_unregister

             a. 向总线中注销device

/**
 * platform_device_unregister - unregister a platform-level device
 * @pdev: platform device we're unregistering
 *
 * Unregistration is done in 2 steps. First we release all resources
 * and remove it from the subsystem, then we drop reference count by
 * calling platform_device_put().
 */
void platform_device_unregister(struct platform_device *pdev)
{
    platform_device_del(pdev);
    platform_device_put(pdev);
}
View Code

        1.5.3. platform_driver_register

            a. 向总线中注册driver

/**
 * platform_driver_register - register a driver for platform-level devices
 * @drv: platform driver structure
 */
int platform_driver_register(struct platform_driver *drv)
{
    drv->driver.bus = &platform_bus_type;
    if (drv->probe)
        drv->driver.probe = platform_drv_probe;
    if (drv->remove)
        drv->driver.remove = platform_drv_remove;
    if (drv->shutdown)
        drv->driver.shutdown = platform_drv_shutdown;

    return driver_register(&drv->driver);
}
View Code

        1.5.4. platform_driver_unregister

            a. 向总线中注销driver

/**
 * platform_driver_unregister - unregister a driver for platform-level devices
 * @drv: platform driver structure
 */
void platform_driver_unregister(struct platform_driver *drv)
{
    driver_unregister(&drv->driver);
}
View Code

二.led device 代码介绍

#include <linux/init.h>            // __init   __exit
#include <linux/module.h>      // module_init  module_exit
#include <mach/regs-gpio.h>
#include <mach/gpio-bank.h>

#include <mach/gpio.h> 
#include <linux/leds.h>
#include <asm/string.h> 

#include <linux/platform_device.h>
#include <mach/leds-gpio.h>
void s5pv210_led_release(struct device *dev);

static struct s5pv210_led_platdata s5pv210_led_pdata[] = {
    {
        .name        = "led0",
        .gpio        = S5PV210_GPJ0(3),
        .def_trigger    = "",
    },
    {
        .name        = "led1",
        .gpio        = S5PV210_GPJ0(4),
        .def_trigger    = "",
    },
    {
        .name        = "led2",
        .gpio        = S5PV210_GPJ0(5),
        .def_trigger    = "",
    }
};
static struct platform_device s5pv210_led[] = {
    {
    .name = "s5pv210_led",
    .id = 0,
    .dev = {
        .platform_data    = &s5pv210_led_pdata[0],
        .release = s5pv210_led_release,
        }
    },
    {
    .name = "s5pv210_led",
    .id    = 1,
    .dev = {
        .platform_data    = &s5pv210_led_pdata[1],
        .release = s5pv210_led_release,
        }
    },
    {
    .name = "s5pv210_led",
    .id    = 2,
    .dev = {
        .platform_data    = &s5pv210_led_pdata[2],
        .release = s5pv210_led_release,
        }
    }
};

void s5pv210_led_release(struct device *dev)
{
    printk(KERN_WARNING "s5pv210_led_release successful \n");
}
static struct platform_device *ps5pv210_led[] =
{
    &s5pv210_led[0],
    &s5pv210_led[1],
    &s5pv210_led[2]
};

static int __init s5pv210_led_init(void)
{
    int ret = -1;
    printk(KERN_INFO "device.c : s5pv210_led_init successful \n");    
    ret = platform_add_devices(ps5pv210_led, ARRAY_SIZE(ps5pv210_led));
    if (ret < 0) 
    {
        printk(KERN_WARNING "platform_add_devices fail \n");
    }
    return ret;
}

static void __exit s5pv210_led_exit(void)
{    
    int i = 0;
    printk(KERN_INFO "device.c : s5pv210_led_exit successful \n");

    for (i = 0; i < ARRAY_SIZE(ps5pv210_led); i++)
       platform_device_unregister(ps5pv210_led[i]); 
}




module_init(s5pv210_led_init);
module_exit(s5pv210_led_exit);

// MODULE_xxx这种宏作用是用来添加模块描述信息
MODULE_LICENSE("GPL");                // 描述模块的许可证
MODULE_AUTHOR("musk");                // 描述模块的作者
MODULE_DESCRIPTION("x210 LED device");    // 描述模块的介绍信息
MODULE_ALIAS("led_device");            // 描述模块的别名信息
View Code

    2.1. led devicec程序两种组织方式

        2.1.1. 把device相关必要的code 放在 mach-x210.c中

            a. mach-x210.c放着很多个device设备。是大部分设备共有的文件,一般驱动开发时也是将所要开发驱动的device部分放在此文件

            b.mach-x210.c中所有platform设备的定义的数据都会放在smdkc110_devices结构体中,该结构体在smdkc110_machine_init函数完成设备的register

            c.mach-x210.c中device随着开发板启动完成注册,故不能被注销,因此不需要relase函数

        2.1.2. 自己写个led_device.c

            a. led_device.c 把led当模块处理,可以insmod 也可以rmmod,其中rmmod需要调用relase函数

            b. led_device.c 中led device需要自己添加到总线上(platform_device_register)和从总线注销(platform_device_unregister)

            c. 正式驱动开发不这样。我这样写为了便于理解

    2.2. platform_add_devices函数

        2.2.1. 这个函数是同时注册多个device

        2.2.2. 这个函数struct platform_device **devs是个二位指针。我写程序时在这里栽了跟头

/**
 * platform_add_devices - add a numbers of platform devices
 * @devs: array of platform devices to add
 * @num: number of platform devices in array
 */
int platform_add_devices(struct platform_device **devs, int num)
{
    int i, ret = 0;

    for (i = 0; i < num; i++) {
        ret = platform_device_register(devs[i]);
        if (ret) {
            while (--i >= 0)
                platform_device_unregister(devs[i]);
            break;
        }
    }

    return ret;
}
View Code

三. led driver介绍

#include <linux/init.h>            // __init   __exit
#include <linux/module.h>      // module_init  module_exit
#include <mach/regs-gpio.h>
#include <mach/gpio-bank.h>

#include <asm/io.h>     //writel

#include <mach/gpio.h> 
#include <linux/leds.h>
#include <asm/string.h> 

#include <linux/platform_device.h>
#include <mach/leds-gpio.h>

 #include <linux/slab.h>

#define X210_LED_OFF 1U
#define X210_LED_ON  0U

static int s5pv210_led_probe(struct platform_device *dev);
void s5pv210_led_set(struct led_classdev *led_cdev,enum led_brightness brightness);
static int s5pv210_led_remove(struct platform_device *dev);

static struct platform_driver s5pv210_led_driver = {
    .probe        = s5pv210_led_probe,
    .remove        = s5pv210_led_remove,
    .driver        = {
        .name        = "s5pv210_led",
        .owner        = THIS_MODULE,
    },
};

struct s5pv210_gpio_led {
    struct led_classdev         cdev;
    struct s5pv210_led_platdata    *pdata;
};

// static struct gpio x210_led_gpio[] = 
// {
    // { S5PV210_GPJ0(3), GPIOF_OUT_INIT_HIGH,  "LED1" }, /* default to OFF */
    // { S5PV210_GPJ0(4), GPIOF_OUT_INIT_HIGH,  "LED2" }, /* default to OFF */
    // { S5PV210_GPJ0(5), GPIOF_OUT_INIT_HIGH,  "LED3" } /* default to OFF */
// };

static inline struct s5pv210_gpio_led *pdev_to_gpio(struct platform_device *dev)
{
    return platform_get_drvdata(dev);
}

static inline struct s5pv210_gpio_led *to_gpio(struct led_classdev *led_cdev)
{
    return container_of(led_cdev, struct s5pv210_gpio_led, cdev);
}

void s5pv210_led_set(struct led_classdev *led_cdev,enum led_brightness brightness)
{
    struct s5pv210_gpio_led *led = to_gpio(led_cdev);
    struct s5pv210_led_platdata *pd = led->pdata;
    printk(KERN_INFO "s5pv210_led1_set successful %d\n",brightness);

    gpio_set_value(pd->gpio,(brightness == LED_OFF) ? X210_LED_OFF:X210_LED_ON);

}    

static int s5pv210_led_remove(struct platform_device *dev)
{
    struct s5pv210_gpio_led *led = pdev_to_gpio(dev);
    struct s5pv210_led_platdata *platdata = dev->dev.platform_data;    // 取出platform_device结构体中的device结构体中的私有数据区的x210_platform指针
    
    led_classdev_unregister(&led->cdev);                               // 卸载led驱动
    gpio_set_value(platdata->gpio,X210_LED_OFF);                       // 关闭所有led
    gpio_free(platdata->gpio);                                         // 释放申请的GPIO资源
    kfree(led);                                                        // 释放内存,这个一定要放在最后

    return 0;
}

static int s5pv210_led_probe(struct platform_device *dev)
{
    int ret = -1;
    
    struct s5pv210_led_platdata *pdata = dev->dev.platform_data;
    struct s5pv210_gpio_led *led = NULL;
    printk(KERN_INFO "driver.c:  s5pv210_led_probe successful \n");
    led = kzalloc(sizeof(struct s5pv210_gpio_led), GFP_KERNEL);
    if (led == NULL) {
        dev_err(&dev->dev, "No memory for device\n");
        return -ENOMEM;
    }
    platform_set_drvdata(dev, led);                                     // 将pform值写入到传进来的platform_device结构体中的device结构体下的私有数据区中去,以便后面释放内存时用
    led->cdev.brightness_set = s5pv210_led_set;    
    led->cdev.name = pdata->name;
    led->pdata = pdata;
    
    
    ret = led_classdev_register(NULL, &led->cdev);                    //真正注册led驱动
    if (ret < 0) 
    {

        printk(KERN_WARNING "led_classdev_register fail \n");
        goto class_err; 
    }
    ret = gpio_request(pdata->gpio, pdata->name);                        // 向gpiolib管理器申请gpio资源 
    if (ret)
        {
         printk(KERN_WARNING "gpio_request_array fail \n");
         goto req_err;
    }
    gpio_direction_output(pdata->gpio, X210_LED_OFF);
    return 0;
    
    
req_err:
    platform_driver_unregister(&s5pv210_led_driver);
    
class_err:
    kfree(led);
    return ret;
    
}                  
static int __init s5pv210_led_init(void)
{
    printk(KERN_INFO "driver.c:  s5pv210_led_init successful \n");
    return platform_driver_register(&s5pv210_led_driver);
}

static void __exit s5pv210_led_exit(void)
{    
    printk(KERN_INFO "driver.c : s5pv210_led_exit successful \n");
    platform_driver_unregister(&s5pv210_led_driver);
    
    
}




module_init(s5pv210_led_init);
module_exit(s5pv210_led_exit);

// MODULE_xxx这种宏作用是用来添加模块描述信息
MODULE_LICENSE("GPL");                // 描述模块的许可证
MODULE_AUTHOR("musk");                // 描述模块的作者
MODULE_DESCRIPTION("x210 LED driver");    // 描述模块的介绍信息
MODULE_ALIAS("led_driver");            // 描述模块的别名信息
View Code

    3.1. 充分linux 数据和处理分离的思想

        3.1.1. driver只负责数据的处理和加工,没有规定或写死任何数据

        3.1.2. device提供driver所需要的数据

    3.2. s5pv210_led_probe函数

        3.2.1.  当我们注册driver时会去总线匹配device 如果匹配成功则会s5pv210_led_probe

        3.2.2.  当我们注册device时也会去总线匹配driver如果匹配成功则会s5pv210_led_probe

四.实操。

[root@musk210 driver_test]# ls
app_led_driver    app_led_driver.o  led_driver.ko
app_led_driver.c  led_device.ko
[root@musk210 driver_test]# insmod led_device.ko 
[ 3362.294966] device.c : s5pv210_led_init successful 
[root@musk210 driver_test]# insmod led_driver.ko 
[ 3372.295723] driver.c:  s5pv210_led_init successful 
[ 3372.300022] driver.c:  s5pv210_led_probe successful 
[ 3372.305379] driver.c:  s5pv210_led_probe successful 
[ 3372.310753] driver.c:  s5pv210_led_probe successful 
[root@musk210 driver_test]# ls /sys/bus/platform/devices/
alarm             s3c-adc           s3c-sdhci.3       s5p-ehci
android_pmem.1    s3c-csis          s3c-ts            s5p-hpd
arm-pmu.0         s3c-fimc.0        s3c2410-wdt       s5p-tvout
dm9000.0          s3c-fimc.1        s3c2440-i2c.0     s5pv210-nand
pm-wifi           s3c-fimc.2        s3c2440-i2c.1     s5pv210-uart.0
power.0           s3c-g2d           s3c2440-i2c.2     s5pv210-uart.1
pvrsrvkm          s3c-jpg           s3c24xx-pwm.0     s5pv210-uart.2
pwm-backlight     s3c-keypad        s3c24xx-pwm.1     s5pv210-uart.3
reg-s5pv210-pd.0  s3c-mfc           s3c24xx-pwm.2     s5pv210_led.0
reg-s5pv210-pd.1  s3c-pl330.0       s3c24xx-pwm.3     s5pv210_led.1
reg-s5pv210-pd.2  s3c-pl330.1       s3c64xx-iis.0     s5pv210_led.2
reg-s5pv210-pd.3  s3c-pl330.2       s3c64xx-iis.1     sec-fake-battery
reg-s5pv210-pd.4  s3c-sdhci.0       s3c_lcd           smdkc110-rtc
reg-s5pv210-pd.5  s3c-sdhci.1       s3cfb             soc-audio.1
regulatory.0      s3c-sdhci.2       s5p-cec           switch-gpio
[root@musk210 driver_test]# ls /sys/bus/platform/driver/
ls: /sys/bus/platform/driver/: No such file or directory
[root@musk210 driver_test]# ls /sys/bus/platform/drivers
alarm             ram_console       s3c-sdhci         s5pv210-uart
android_pmem      reg-s5pv210-pd    s3c-ts            s5pv210_led
arm-pmu           s3c-adc           s3c24xx-pwm       sec-fake-battery
dm9000            s3c-csis          s3c64xx-iis       smdkc110-rtc
i2c-gpio          s3c-fimc          s3cfb             soc-audio
max8698-pmic      s3c-g2d           s5p-cec           switch-gpio
pm-wifi           s3c-i2c           s5p-ehci          timed-gpio
power             s3c-jpg           s5p-hpd
pvrsrvkm          s3c-mfc           s5p-tvout
pwm-backlight     s3c-pl330         s5pv210-nand
[root@musk210 driver_test]# cd /sys/class/leds/
[root@musk210 leds]# ls
led0    led1    led2    mmc0::  mmc1::  mmc2::  mmc3::
[root@musk210 leds]# cd led0
[root@musk210 led0]# ls
brightness      max_brightness  power           subsystem       uevent
[root@musk210 led0]# echo 1 > brightness 
[ 3464.992199] s5pv210_led1_set successful 1
[root@musk210 led0]# echo 0 > brightness 
[ 3472.096432] s5pv210_led1_set successful 0
[root@musk210 led0]# cd ../led1
[root@musk210 led1]# echo 1 > brightness 
[ 3484.145032] s5pv210_led1_set successful 1
[root@musk210 led1]# cd -
/sys/class/leds/led0
[root@musk210 led0]# cd /driver_test/
[root@musk210 driver_test]# rmmod led_device.ko 
[ 3502.670697] device.c : s5pv210_led_exit successful 
[ 3502.676979] s5pv210_led_release successful 
[ 3502.683547] s5pv210_led_release successful 
[ 3502.689179] s5pv210_led_release successful 
[root@musk210 driver_test]# rmmod led_driver.ko 
[ 3509.670803] driver.c : s5pv210_led_exit successful
View Code

 

posted @ 2019-01-03 09:57  三七鸽  阅读(740)  评论(0编辑  收藏  举报