关于关于驱动设备模型相关概念请参考《Linux Device Drivers》等相关书籍,和内核源码目录...\Documentation\driver-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源码目录\drivers\input\keyboard下,提供了一个gpio_keys.c的平台驱动(platform_driver),这个就是一个简单地按键驱动,检测到按键状态,上报给输入子系统。

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

gpio_keys.c (不用任何改动)

 /*
* Driver for keys on GPIO lines capable of generating interrupts.
*
* Copyright 2005 Phil Blundell
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ #include <linux/module.h>
#include <linux/version.h> #include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/irq.h>
#include <linux/gpio_keys.h> #include <asm/gpio.h> static irqreturn_t gpio_keys_isr(int irq, void *dev_id)
{
int i;
/* [cgw]: 在gpio_keys_probe()中调用了request_irq(..., pdev),
* 因此dev_id指向了platform_device *pdev,通过dev_id间接传递
* platform_device *指针
*/
struct platform_device *pdev = dev_id;
/* [cgw]: 当platform_device 和 platform_driver匹配时,会通过
* probe()传递platform_device进来。在注册platform_device时,
* platform_device.dev.platform_data必须指向gpio_keys_platform_data *
*/
struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
/* [cgw]: probe()中已分配了一个struct input_dev,并通过platform_set_drvdata()
* 设置platform_device->dev->driver_data = input;
*/
struct input_dev *input = platform_get_drvdata(pdev); /* [cgw]: 轮询pdata->nbuttons个按键 */
for (i = ; i < pdata->nbuttons; i++) {
struct gpio_keys_button *button = &pdata->buttons[i];
int gpio = button->gpio; /* [cgw]: 某个gpio发生了中断 */
if (irq == gpio_to_irq(gpio)) {
/* [cgw]: 获得按键类型 */
unsigned int type = button->type ?: EV_KEY;
/* [cgw]: 获取按键状态 */
int state = (gpio_get_value(gpio) ? : ) ^ button->active_low;
/* [cgw]: 发送按键事件 */
input_event(input, type, button->code, !!state);
/* [cgw]: 发送同步事件 */
input_sync(input);
}
} return IRQ_HANDLED;
} static int __devinit gpio_keys_probe(struct platform_device *pdev)
{
/* [cgw]: 在gpio_keys_probe()中调用了request_irq(..., pdev),
* 因此dev_id指向了platform_device *pdev,通过dev_id间接传递
* platform_device *指针
*/
struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
struct input_dev *input;
int i, error; /* [cgw]: 分配一个输入设备 */
input = input_allocate_device();
/* [cgw]: 分配失败 */
if (!input)
return -ENOMEM; /* [cgw]: 设置platform_device->dev->driver_data = input */
platform_set_drvdata(pdev, input); /* [cgw]: 设置evdev.c支持的事件类型 */
input->evbit[] = BIT(EV_KEY);
/* [cgw]: 设置输入设备名,同platform_device的名字 */
input->name = pdev->name;
/* [cgw]: */
input->phys = "gpio-keys/input0";
/* [cgw]: 设置输入设备dev的父节点为platform_device->dev */
input->dev.parent = &pdev->dev; /* [cgw]: 设置输入设备总线类型,供应商,产品,版本 */
input->id.bustype = BUS_HOST;
input->id.vendor = 0x0001;
input->id.product = 0x0001;
input->id.version = 0x0100; /* [cgw]: 为pdata->nbuttons个按键申请中断 */
for (i = ; i < pdata->nbuttons; i++) {
struct gpio_keys_button *button = &pdata->buttons[i];
/* [cgw]: 获得gpio对应的中断号 */
int irq = gpio_to_irq(button->gpio);
/* [cgw]: 获得按键类型 */
unsigned int type = button->type ?: EV_KEY; /* [cgw]: 设置中断类型为边沿中断 */
set_irq_type(irq, IRQ_TYPE_EDGE_BOTH);
/* [cgw]: 申请中断,设置中断服务程序 */
error = request_irq(irq, gpio_keys_isr, IRQF_SAMPLE_RANDOM,
button->desc ? button->desc : "gpio_keys",
pdev);
if (error) {
printk(KERN_ERR "gpio-keys: unable to claim irq %d; error %d\n",
irq, error);
goto fail;
} /* [cgw]: 设置evdev.c支持的按键码 */
input_set_capability(input, type, button->code);
} /* [cgw]: 注册一个输入设备 */
error = input_register_device(input);
/* [cgw]: 注册失败 */
if (error) {
printk(KERN_ERR "Unable to register gpio-keys input device\n");
goto fail;
} return ; fail:
/* [cgw]: 释放中断 */
for (i = i - ; i >= ; i--)
free_irq(gpio_to_irq(pdata->buttons[i].gpio), pdev);
/* [cgw]: 释放输入设备占用的内存 */
input_free_device(input); return error;
} static int __devexit gpio_keys_remove(struct platform_device *pdev)
{
/* [cgw]: 在gpio_keys_probe()中调用了request_irq(..., pdev),
* 因此dev_id指向了platform_device *pdev,通过dev_id间接传递
* platform_device *指针
*/
struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
/* [cgw]: probe()中已分配了一个struct input_dev,并通过platform_set_drvdata()
* 设置platform_device->dev->driver_data = input;
*/
struct input_dev *input = platform_get_drvdata(pdev);
int i; /* [cgw]: 释放中断 */
for (i = ; i < pdata->nbuttons; i++) {
int irq = gpio_to_irq(pdata->buttons[i].gpio);
free_irq(irq, pdev);
} /* [cgw]: 注销输入设备 */
input_unregister_device(input); return ;
} struct platform_driver gpio_keys_device_driver = {
.probe = gpio_keys_probe,
.remove = __devexit_p(gpio_keys_remove),
.driver = {
.name = "gpio-keys",
}
}; static int __init gpio_keys_init(void)
{
/* [cgw]: 注册gpio_keys_device_driver平台驱动 */
return platform_driver_register(&gpio_keys_device_driver);
} static void __exit gpio_keys_exit(void)
{
/* [cgw]: 注销gpio_keys_device_driver平台驱动 */
platform_driver_unregister(&gpio_keys_device_driver);
} module_init(gpio_keys_init);
module_exit(gpio_keys_exit); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Phil Blundell <pb@handhelds.org>");
MODULE_DESCRIPTION("Keyboard driver for CPU GPIOs");

平台设备(platform_device):
keys_dev.c

 #include <linux/module.h>
#include <linux/version.h> #include <linux/init.h> #include <linux/kernel.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/timer.h>
#include <linux/init.h>
#include <linux/serial_core.h>
#include <linux/platform_device.h>
#include <linux/gpio_keys.h>
#include <linux/input.h>
#include <linux/irq.h> #include <asm/gpio.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h> /* [cgw]: 设置四个按键的键码,按键io,激活状态,按键名,按键类型 */
static struct gpio_keys_button keys_buff[] = {
{
KEY_A,
S3C2410_GPF0,
,
"A",
EV_KEY
}, {
KEY_B,
S3C2410_GPF2,
,
"B",
EV_KEY
}, {
KEY_C,
S3C2410_GPG3,
,
"C",
EV_KEY
}, {
KEY_D,
S3C2410_GPG11,
,
"D",
EV_KEY
},
}; static struct gpio_keys_platform_data keys_dev = {
.buttons = &keys_buff[],
.nbuttons = ARRAY_SIZE(keys_buff)
}; static void keys_dev_release(struct device * dev)
{
printk("keys_dev_release! \n");
} /* [cgw]: 分配一个平台设备 */
static struct platform_device keys_platform_dev = {
.name = "gpio-keys",
.id = -,
.dev = {
.release = keys_dev_release,
.platform_data = (void *)&keys_dev,
},
}; static int keys_dev_init(void)
{
/* [cgw]: 注册keys_platform_dev平台设备 */
platform_device_register(&keys_platform_dev);
return ;
} static void keys_dev_exit(void)
{
/* [cgw]: 注销keys_platform_dev平台设备 */
platform_device_unregister(&keys_platform_dev);
} module_init(keys_dev_init);
module_exit(keys_dev_exit); MODULE_LICENSE("GPL");

应用测试程序:

platform_test.c

 #include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <poll.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h> #include <linux/input.h> int fd; void my_signal_fun(int signum)
{
struct input_event buttons_event; /* [cgw]: 异步通知产生时返回的数据 */
read(fd, &buttons_event, sizeof(struct input_event)); /* [cgw]: 打印事件类型,事件码,事件值 */
printf("type: 0x%x code: 0x%x value: 0x%x\n",
buttons_event.type,
buttons_event.code,
buttons_event.value);
} int main(int argc, char **argv)
{
int ret, arg;
struct pollfd fds[];
unsigned long ver = ; fd = open("/dev/event1", O_RDWR | O_NONBLOCK); if (fd < )
{
printf("can't open!\n");
} ioctl(fd, EVIOCGVERSION, &ver);
printf("Ver:0x%x \n", ver); /* [cgw]: 设置文件标识符 */
fds[].fd = fd;
/* [cgw]: 设置应用程序要响应的事件 */
fds[].events = POLLIN; while ()
{
/* [cgw]: 休眠5S */
ret = poll(fds, , ); /* [cgw]: 唤醒或超时 */
//printf("wake up!\n");
if (ret == )
{
printf("time out\n");
}
else
{
my_signal_fun(arg);
}
} close(fd); return ;
}

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

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

 static int platform_match(struct device * dev, struct device_driver * drv)
{
struct platform_device *pdev = container_of(dev, struct platform_device, dev); return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == );
}

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

一旦匹配,就会调用

 static int platform_drv_probe(struct device *_dev)
{
struct platform_driver *drv = to_platform_driver(_dev->driver);
struct platform_device *dev = to_platform_device(_dev); return drv->probe(dev);
}

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

设备模型(device-model)之平台总线(bus),驱动(driver),设备(device)的更多相关文章

  1. 字符设备驱动、平台设备驱动、设备驱动模型、sysfs的比较和关联

    转载自:http://www.kancloud.cn/yueqian_scut/emlinux/106829 学习Linux设备驱动开发的过程中自然会遇到字符设备驱动.平台设备驱动.设备驱动模型和sy ...

  2. [kernel]字符设备驱动、平台设备驱动、设备驱动模型、sysfs几者之间的比较和关联

    转自:http://www.2cto.com/kf/201510/444943.html Linux驱动开发经验总结,绝对干货! 学习Linux设备驱动开发的过程中自然会遇到字符设备驱动.平台设备驱动 ...

  3. Linux设备模型(总线、设备、驱动程序和类)

    Linux设备驱动程序学习(13) -Linux设备模型(总线.设备.驱动程序和类)[转] 文章的例子和实验使用<LDD3>所配的lddbus模块(稍作修改). 提示:在学习这部分内容是一 ...

  4. linux device model简述

    参考: 1)<LINUX设备驱动程序>第十四章 Linux 设备模型 2)内核源码2.6.38 内核初始化的时候会对设备模型作初始化,见init/main.c: start_kernel- ...

  5. The Linux device model

    /sys和 /dev的疑问 1./dev 下放的是设备文件,是由应用层mknod创建的文件.假设底层驱动对mknod的设备号有相应的驱动,如open等函数.那么应用层open "/dev/* ...

  6. 《Linux Device Drivers》第十四章 Linux 设备型号

    基本介绍 2.6内核设备模型来提供的抽象叙述性描述的一般系统的结构,为了支持各种不同的任务 电源管理和系统关机 用户空间与通信 热插拔设备 设备类型 kobject.kset和子系统 kobject是 ...

  7. linux设备模型:扩展篇

    Linux设备模型组件:总线  一.定义:总线是不同IC器件之间相互通讯的通道;在计算机中,一个总线就是处理器与一个或多个不同外设之间的通讯通道;为了设备模型的目的,所有的设备都通过总线相互连接,甚至 ...

  8. Linux设备模型:基础篇

    linux提供了新的设备模型:总线(bus).设备(device).驱动(driver).其中总线是处理器与设备之间通道,在设备模型中,所有的设备都通过总线相连:设备是对于一个设备的详细信息描述,驱动 ...

  9. platform平台总线

    一.何为平台总线 (1)相对于usb.pci.i2c等物理总线来说,platform总线是虚拟的.抽象出来的.(2)CPU与外部通信的2种方式:地址总线式连接和专用协议类接口式连接.平台总线,是扩展到 ...

  10. Sysfs文件系统与Linux设备模型

    转:http://www.360doc.com/content/11/1218/16/1299815_173168170.shtml sysfs把连接在系统上的设备和总线组织成为一个分级的目录及文件, ...

随机推荐

  1. ADO.NET 核心对象简介

    ADO.NET是.NET中一组用于和数据源进行交互的面向对象类库,提供了数据访问的高层接口. ADO.NOT类库在System.Data命名空间内,根据我们访问的不同数据库选择命名空间,System. ...

  2. java https单向认证(忽略认证)并支持http基本认证

    https单向认证(忽略认证)并支持http基本认证, 温馨提示 1,jar包要导入对 2,有匿名类编译要注意 3,欢迎提问,拿走不谢!背景知识 Https访问的相关知识中,主要分为单向验证和双向验证 ...

  3. HTML5权威指南--Web Storage,本地数据库,本地缓存API,Web Sockets API,Geolocation API(简要学习笔记二)

    1.Web Storage HTML5除了Canvas元素之外,还有一个非常重要的功能那就是客户端本地保存数据的Web Storage功能. 以前都是用cookies保存用户名等简单信息.   但是c ...

  4. ASP.NET MVC5+EF6+EasyUI 后台管理系统(53)-工作流设计-我的批阅

    系列目录 前言:由于工作原因工作流一直没时间更新,虽然没有更新,但是批阅和申请差不多,改变一下数据的状态字段就行,有几个园友已经率先完成了 说句实话,一个工作流用文章表达很难,我起初以为这是一个很简单 ...

  5. K近邻法(KNN)原理小结

    K近邻法(k-nearst neighbors,KNN)是一种很基本的机器学习方法了,在我们平常的生活中也会不自主的应用.比如,我们判断一个人的人品,只需要观察他来往最密切的几个人的人品好坏就可以得出 ...

  6. 计算机程序的思维逻辑 (50) - 剖析EnumMap

    上节我们提到,如果需要一个Map的实现类,并且键的类型为枚举类型,可以使用HashMap,但应该使用一个专门的实现类EnumMap. 为什么要有一个专门的类呢?我们之前介绍过枚举的本质,主要是因为枚举 ...

  7. 举个栗子学习JavaScript设计模式

    目录 前言 创建型模式 单例模式 构造器+原型 简单工厂模式 工厂模式 创建型模式比较 结构性模式 模块模式 外观模式 混入模式 装饰模式 适配模式 行为型模式 观察者模式 中介者模式 命令模式 责任 ...

  8. The method getJspApplicationContext(ServletContext) is undefined for the type JspFactory

    The method getJspApplicationContext(ServletContext) is undefined for the type JspFactory 这是由于项目里面的一些 ...

  9. 遭遇Web print

    一直都知道Web打印还不太成熟,以前IE横行时,普遍都是采用打印相关的ActiveX控件,有些国产厂家做得不错,只是那时还没有付费能力,没有太多关注.而纯粹基于Web标准的打印,浏览器对CSS pri ...

  10. javascript的理解及经典案例

    js的简介: JavaScript是一种能让你的网页更加生动活泼的程式语言,也是目前网页中设计中最容易学又最方便的语言. 你可以利用JavaScript轻易的做出亲切的欢迎讯息.漂亮的数字钟.有广告效 ...