zoukankan      html  css  js  c++  java
  • 设备模型(device-model)之平台总线(bus),驱动(driver),设备(device)

    关于关于驱动设备模型相关概念请参考《Linux Device Drivers》等相关书籍,和内核源码目录...Documentationdriver-model

    简单来说总线(bus),驱动(driver),设备(device)这三者之间的关系就是:驱动开发者可以通过总线(bus)来将驱动(driver)和设备(device)进行隔离,这样的好处就是开发者可以将相对稳定不变的驱动(driver)独立起来,可以通过总线(bus)来桥接与之匹配的设备(device)。设备(device)只需要提供与硬件相关的底层硬件的配置,如io,中断等。

    platform.c 提供了一个平台总线(platform_bus),和注册平台设备(platform_device)和平台驱动(platform_driver)的相关接口,其中平台总线(platform_bus)已经编进内核,开发者只需要提供平台设备(platform_device)和平台驱动(platform_driver)的相关代码就行了。

    在linux源码目录driversinputkeyboard下,提供了一个gpio_keys.c的平台驱动(platform_driver),这个就是一个简单地按键驱动,检测到按键状态,上报给输入子系统。

    因此,开发者需要做的就是提供一个平台设备(platform_device),以向平台驱动(platform_driver)提供相关的硬件配置,如按键IO,中断号,按键码等等。

    gpio_keys.c (不用任何改动)

      1 /*
      2  * Driver for keys on GPIO lines capable of generating interrupts.
      3  *
      4  * Copyright 2005 Phil Blundell
      5  *
      6  * This program is free software; you can redistribute it and/or modify
      7  * it under the terms of the GNU General Public License version 2 as
      8  * published by the Free Software Foundation.
      9  */
     10 
     11 #include <linux/module.h>
     12 #include <linux/version.h>
     13 
     14 #include <linux/init.h>
     15 #include <linux/fs.h>
     16 #include <linux/interrupt.h>
     17 #include <linux/irq.h>
     18 #include <linux/sched.h>
     19 #include <linux/pm.h>
     20 #include <linux/sysctl.h>
     21 #include <linux/proc_fs.h>
     22 #include <linux/delay.h>
     23 #include <linux/platform_device.h>
     24 #include <linux/input.h>
     25 #include <linux/irq.h>
     26 #include <linux/gpio_keys.h>
     27 
     28 #include <asm/gpio.h>
     29 
     30 static irqreturn_t gpio_keys_isr(int irq, void *dev_id)
     31 {
     32     int i;
     33     /* [cgw]: 在gpio_keys_probe()中调用了request_irq(..., pdev),
     34      * 因此dev_id指向了platform_device *pdev,通过dev_id间接传递
     35      * platform_device *指针
     36      */
     37     struct platform_device *pdev = dev_id;
     38     /* [cgw]: 当platform_device 和 platform_driver匹配时,会通过
     39      * probe()传递platform_device进来。在注册platform_device时,
     40      * platform_device.dev.platform_data必须指向gpio_keys_platform_data *
     41      */
     42     struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
     43     /* [cgw]: probe()中已分配了一个struct input_dev,并通过platform_set_drvdata()
     44      * 设置platform_device->dev->driver_data = input;
     45      */
     46     struct input_dev *input = platform_get_drvdata(pdev);
     47 
     48     /* [cgw]: 轮询pdata->nbuttons个按键 */
     49     for (i = 0; i < pdata->nbuttons; i++) {
     50         struct gpio_keys_button *button = &pdata->buttons[i];
     51         int gpio = button->gpio;
     52         
     53         /* [cgw]: 某个gpio发生了中断 */
     54         if (irq == gpio_to_irq(gpio)) {
     55             /* [cgw]: 获得按键类型 */
     56             unsigned int type = button->type ?: EV_KEY;
     57             /* [cgw]: 获取按键状态 */
     58             int state = (gpio_get_value(gpio) ? 1 : 0) ^ button->active_low;
     59             /* [cgw]: 发送按键事件 */
     60             input_event(input, type, button->code, !!state);
     61             /* [cgw]: 发送同步事件 */
     62             input_sync(input);
     63         }
     64     }
     65 
     66     return IRQ_HANDLED;
     67 }
     68 
     69 static int __devinit gpio_keys_probe(struct platform_device *pdev)
     70 {
     71     /* [cgw]: 在gpio_keys_probe()中调用了request_irq(..., pdev),
     72      * 因此dev_id指向了platform_device *pdev,通过dev_id间接传递
     73      * platform_device *指针
     74      */
     75     struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
     76     struct input_dev *input;
     77     int i, error;
     78 
     79     /* [cgw]: 分配一个输入设备 */
     80     input = input_allocate_device();
     81     /* [cgw]: 分配失败 */
     82     if (!input)
     83         return -ENOMEM;
     84 
     85     /* [cgw]: 设置platform_device->dev->driver_data = input */
     86     platform_set_drvdata(pdev, input);
     87 
     88     /* [cgw]: 设置evdev.c支持的事件类型 */
     89     input->evbit[0] = BIT(EV_KEY);
     90     /* [cgw]: 设置输入设备名,同platform_device的名字 */
     91     input->name = pdev->name;
     92     /* [cgw]:  */
     93     input->phys = "gpio-keys/input0";
     94     /* [cgw]: 设置输入设备dev的父节点为platform_device->dev */
     95     input->dev.parent = &pdev->dev;
     96     
     97     /* [cgw]: 设置输入设备总线类型,供应商,产品,版本 */
     98     input->id.bustype = BUS_HOST;
     99     input->id.vendor = 0x0001;
    100     input->id.product = 0x0001;
    101     input->id.version = 0x0100;
    102 
    103     /* [cgw]: 为pdata->nbuttons个按键申请中断 */
    104     for (i = 0; i < pdata->nbuttons; i++) {
    105         struct gpio_keys_button *button = &pdata->buttons[i];
    106         /* [cgw]: 获得gpio对应的中断号 */
    107         int irq = gpio_to_irq(button->gpio);
    108         /* [cgw]: 获得按键类型 */
    109         unsigned int type = button->type ?: EV_KEY;
    110 
    111         /* [cgw]: 设置中断类型为边沿中断 */
    112         set_irq_type(irq, IRQ_TYPE_EDGE_BOTH);
    113         /* [cgw]: 申请中断,设置中断服务程序 */
    114         error = request_irq(irq, gpio_keys_isr, IRQF_SAMPLE_RANDOM,
    115                      button->desc ? button->desc : "gpio_keys",
    116                      pdev);
    117         if (error) {
    118             printk(KERN_ERR "gpio-keys: unable to claim irq %d; error %d
    ",
    119                 irq, error);
    120             goto fail;
    121         }
    122 
    123         /* [cgw]: 设置evdev.c支持的按键码 */
    124         input_set_capability(input, type, button->code);
    125     }
    126 
    127     /* [cgw]: 注册一个输入设备 */
    128     error = input_register_device(input);
    129     /* [cgw]: 注册失败 */
    130     if (error) {
    131         printk(KERN_ERR "Unable to register gpio-keys input device
    ");
    132         goto fail;
    133     }
    134 
    135     return 0;
    136 
    137  fail:
    138     /* [cgw]: 释放中断 */
    139     for (i = i - 1; i >= 0; i--)
    140         free_irq(gpio_to_irq(pdata->buttons[i].gpio), pdev);
    141     /* [cgw]: 释放输入设备占用的内存 */
    142     input_free_device(input);
    143 
    144     return error;
    145 }
    146 
    147 static int __devexit gpio_keys_remove(struct platform_device *pdev)
    148 {
    149     /* [cgw]: 在gpio_keys_probe()中调用了request_irq(..., pdev),
    150      * 因此dev_id指向了platform_device *pdev,通过dev_id间接传递
    151      * platform_device *指针
    152      */
    153     struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
    154     /* [cgw]: probe()中已分配了一个struct input_dev,并通过platform_set_drvdata()
    155      * 设置platform_device->dev->driver_data = input;
    156      */
    157     struct input_dev *input = platform_get_drvdata(pdev);
    158     int i;
    159 
    160     /* [cgw]: 释放中断 */
    161     for (i = 0; i < pdata->nbuttons; i++) {
    162         int irq = gpio_to_irq(pdata->buttons[i].gpio);
    163         free_irq(irq, pdev);
    164     }
    165 
    166     /* [cgw]: 注销输入设备 */
    167     input_unregister_device(input);
    168 
    169     return 0;
    170 }
    171 
    172 struct platform_driver gpio_keys_device_driver = {
    173     .probe        = gpio_keys_probe,
    174     .remove        = __devexit_p(gpio_keys_remove),
    175     .driver        = {
    176         .name    = "gpio-keys",
    177     }
    178 };
    179 
    180 static int __init gpio_keys_init(void)
    181 {
    182     /* [cgw]: 注册gpio_keys_device_driver平台驱动 */
    183     return platform_driver_register(&gpio_keys_device_driver);
    184 }
    185 
    186 static void __exit gpio_keys_exit(void)
    187 {
    188     /* [cgw]: 注销gpio_keys_device_driver平台驱动 */
    189     platform_driver_unregister(&gpio_keys_device_driver);
    190 }
    191 
    192 module_init(gpio_keys_init);
    193 module_exit(gpio_keys_exit);
    194 
    195 MODULE_LICENSE("GPL");
    196 MODULE_AUTHOR("Phil Blundell <pb@handhelds.org>");
    197 MODULE_DESCRIPTION("Keyboard driver for CPU GPIOs");

    平台设备(platform_device):
    keys_dev.c

     1 #include <linux/module.h>
     2 #include <linux/version.h>
     3 
     4 #include <linux/init.h>
     5 
     6 #include <linux/kernel.h>
     7 #include <linux/types.h>
     8 #include <linux/interrupt.h>
     9 #include <linux/list.h>
    10 #include <linux/timer.h>
    11 #include <linux/init.h>
    12 #include <linux/serial_core.h>
    13 #include <linux/platform_device.h>
    14 #include <linux/gpio_keys.h>
    15 #include <linux/input.h>
    16 #include <linux/irq.h>
    17 
    18 #include <asm/gpio.h>
    19 #include <asm/io.h>
    20 #include <asm/arch/regs-gpio.h>
    21 
    22 /* [cgw]: 设置四个按键的键码,按键io,激活状态,按键名,按键类型 */
    23 static struct gpio_keys_button keys_buff[4] = {
    24     {
    25         KEY_A,
    26         S3C2410_GPF0,
    27         1,
    28         "A",
    29         EV_KEY
    30     },
    31     
    32     {
    33         KEY_B,
    34         S3C2410_GPF2,
    35         1,
    36         "B",
    37         EV_KEY
    38     },
    39 
    40     {
    41         KEY_C,
    42         S3C2410_GPG3,
    43         1,
    44         "C",
    45         EV_KEY
    46     },
    47 
    48     {
    49         KEY_D,
    50         S3C2410_GPG11,
    51         1,
    52         "D",
    53         EV_KEY
    54     },
    55 };
    56 
    57 
    58 static struct gpio_keys_platform_data keys_dev = {
    59     .buttons = &keys_buff[0],
    60     .nbuttons = ARRAY_SIZE(keys_buff)
    61 };
    62 
    63 static void keys_dev_release(struct device * dev)
    64 {
    65     printk("keys_dev_release! 
    ");
    66 }
    67 
    68 /* [cgw]: 分配一个平台设备 */
    69 static struct platform_device keys_platform_dev = {
    70     .name         = "gpio-keys",
    71     .id           = -1,
    72     .dev = { 
    73         .release = keys_dev_release,
    74         .platform_data = (void *)&keys_dev,
    75     },
    76 };
    77 
    78 
    79 static int keys_dev_init(void)
    80 {
    81     /* [cgw]: 注册keys_platform_dev平台设备 */
    82     platform_device_register(&keys_platform_dev);
    83     return 0;
    84 }
    85 
    86 static void keys_dev_exit(void)
    87 {
    88     /* [cgw]: 注销keys_platform_dev平台设备 */
    89     platform_device_unregister(&keys_platform_dev);
    90 }
    91 
    92 module_init(keys_dev_init);
    93 module_exit(keys_dev_exit);
    94 
    95 MODULE_LICENSE("GPL");


    应用测试程序:

    platform_test.c

     1 #include <sys/types.h>
     2 #include <sys/stat.h>
     3 #include <fcntl.h>
     4 #include <stdio.h>
     5 #include <poll.h>
     6 #include <signal.h>
     7 #include <sys/types.h>
     8 #include <unistd.h>
     9 #include <fcntl.h>
    10 
    11 #include <linux/input.h>
    12 
    13 
    14 
    15 int fd;
    16 
    17 void my_signal_fun(int signum)
    18 {
    19     struct input_event buttons_event;
    20 
    21     /* [cgw]: 异步通知产生时返回的数据 */
    22     read(fd, &buttons_event, sizeof(struct input_event));
    23 
    24     /* [cgw]: 打印事件类型,事件码,事件值 */
    25     printf("type: 0x%x code: 0x%x value: 0x%x
    ", 
    26            buttons_event.type,
    27            buttons_event.code,  
    28            buttons_event.value);
    29 }
    30 
    31 int main(int argc, char **argv)
    32 {
    33     int ret, arg;
    34     struct pollfd fds[1];
    35     unsigned long ver = 0;
    36 
    37     fd = open("/dev/event1", O_RDWR | O_NONBLOCK);
    38     
    39     if (fd < 0)
    40     {
    41         printf("can't open!
    ");
    42     }
    43 
    44     ioctl(fd, EVIOCGVERSION, &ver);
    45     printf("Ver:0x%x 
    ", ver);
    46 
    47     /* [cgw]: 设置文件标识符 */
    48     fds[0].fd     = fd;
    49     /* [cgw]: 设置应用程序要响应的事件 */
    50     fds[0].events = POLLIN;
    51 
    52     while (1)
    53     {
    54         /* [cgw]: 休眠5S */
    55         ret = poll(fds, 1, 5000);
    56         
    57         /* [cgw]: 唤醒或超时 */
    58         //printf("wake up!
    ");
    59         if (ret == 0)
    60         {
    61             printf("time out
    ");
    62         }
    63         else
    64         {
    65             my_signal_fun(arg);
    66         }
    67     }
    68 
    69     close(fd);
    70     
    71     return 0;
    72 }

    平台设备(platform_device) keys_platform_dev 是怎样找到与之匹配的平台驱动(platform_driver) gpio_keys_device_driver的呢?

    因为他们都注册到了平台总线(platform_bus)上。平台驱动(platform_driver)和平台设备(platform_device)会相互查找彼此是否匹配,匹配的条件就是,

    1 static int platform_match(struct device * dev, struct device_driver * drv)
    2 {
    3     struct platform_device *pdev = container_of(dev, struct platform_device, dev);
    4 
    5     return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);
    6 }

    platform_device->name == platform_driver->driver->name 即他们的名字就是:“gpio-keys”

    一旦匹配,就会调用

    1 static int platform_drv_probe(struct device *_dev)
    2 {
    3     struct platform_driver *drv = to_platform_driver(_dev->driver);
    4     struct platform_device *dev = to_platform_device(_dev);
    5 
    6     return drv->probe(dev);
    7 }

    drv->probe() = platform_driver->probe() = gpio_keys_probe()

    这样平台驱动(platform_driver)就可以获得平台设备(platform_device)的 struct platform_device结构的数据了,即keys_platform_dev,从这个结构就可以获得相关配置,以使能平台驱动(platform_driver) gpio_keys.c中相应的IO和中断。

    实验现象:

    # insmod gpio_keys.ko                     //安装平台驱动(platform_driver)
    # insmod keys_dev.ko                      //安装平台设备(platform_device)
    input: gpio-keys as /class/input/input1
    # ./platform_test                         //运行应用测试程序
    Ver:0x10000                               //用ioctl获取输入子系统的版本号
    type: 0x1 code: 0x2e value: 0x1           //按下"C"键
    type: 0x0 code: 0x0 value: 0x0            //因为调用了input_sync()
    type: 0x1 code: 0x2e value: 0x0           //松开(弹起)"C"键
    type: 0x0 code: 0x0 value: 0x0            //因为调用了input_sync()
    type: 0x1 code: 0x30 value: 0x1           //... ...
    type: 0x0 code: 0x0 value: 0x0
    type: 0x1 code: 0x30 value: 0x0
    type: 0x0 code: 0x0 value: 0x0
    type: 0x1 code: 0x1e value: 0x1
    type: 0x0 code: 0x0 value: 0x0
    type: 0x1 code: 0x1e value: 0x0
    type: 0x0 code: 0x0 value: 0x0
    type: 0x1 code: 0x20 value: 0x1
    type: 0x0 code: 0x0 value: 0x0
    type: 0x1 code: 0x20 value: 0x0
    type: 0x0 code: 0x0 value: 0x0
    time out
    time out
    time out
  • 相关阅读:
    Struts2+Spring3+Mybatis3开发环境搭建
    spring+struts2+mybatis
    【LeetCode】Populating Next Right Pointers in Each Node
    【LeetCode】Remove Duplicates from Sorted Array
    【LeetCode】Remove Duplicates from Sorted Array II
    【LeetCode】Binary Tree Inorder Traversal
    【LeetCode】Merge Two Sorted Lists
    【LeetCode】Reverse Integer
    【LeetCode】Same Tree
    【LeetCode】Maximum Depth of Binary Tree
  • 原文地址:https://www.cnblogs.com/hackfun/p/5951235.html
Copyright © 2011-2022 走看看