---恢复内容开始---

---

title: platform驱动分离

tags: linux

date: 2018/11/30 09:24:37

toc: true

platform驱动分离

框架结构

platformbus总线结构将一个硬件驱动分为device设备与driver驱动两个部分,使用platform设备将两个联系到一起.

简单来说,就是将以前所写的驱动,将数据与算法分离,左边dev表示可变的数据,右边的driver表示稳定的算法.比如I2C/USB等总线协议可以理解为稳定的算法,具体的不同的EEPROM/led等可以理解为数据,包含了特殊的自身信息,比如io信息等.

与输入子系统联系

输入子系统是内核定义了一套统一的标准接口,而platform是将左侧的设备层分割为数据与算法

设备描述

这里的设备文件使用platform_device来描述,结构如下,总体来说可以称之为设备描述

  • platform_device_register 注册设备文件,匹配后会调用驱动算法的probe
  • platform_device_unregister卸载设备
  • 具体数据描述如下

驱动算法

这里将其称为驱动算法,其实也是一个正常的驱动程序,区别在于可以获取到上面注册的设备文件信息.当注册设备文件或者注册驱动文件的时候,都会调用驱动算法的probe.

  • platform_driver_register用来注册驱动,当注册设备文件或者注册驱动文件的时候,都会调用驱动算法的probe.
  • platform_driver_unregister用来卸载驱动
  • platform框架中的管理结构如下:
    //driver
struct platform_driver {
//函数
(*probe) //匹配到设备文件的name与driver中的name一致将会在注册时执行
(*remove) //卸载驱动出口,卸载驱动设备的时候应该也会调用
(*shutdown)
(*suspend)
(*suspend_late)
(*resume_early)
(*resume)
//结构体
struct device_driver driver
{
.name //用作匹配
}
};
  • probe,当有设备文件和驱动算法匹配的时候自动执行,一般用来获取资源文件信息等,注册驱动,ioremap等,可以理解为执行驱动的第一个程序了

注意:

  1. 这里的platform_driver也是一个普通的驱动,可以用最原始的方式来注册驱动,创建class类,再在下面添加具体的设备文件来使用mdev机制自动创建所需要的文件.
  2. 也可以使用输入子系统的 框架,输入子系统所需要的驱动程序需要一套约定的接口,我们可以将输入子系统中的左侧的设备驱动再分割成设备描述与驱动算法.

注册机制

驱动算法使用platform_driver_register来注册,如下:

int platform_driver_register(struct platform_driver *drv)
{
drv->driver.bus = &platform_bus_type; //(1)挂接到虚拟总线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;
if (drv->suspend)
drv->driver.suspend = platform_drv_suspend;
if (drv->resume)
drv->driver.resume = platform_drv_resume; return driver_register(&drv->driver); //(2) 注册到driver目录下
}
  1. 从代码上看,platform只是只是bus中的一种,挂载到platform_bus_type

    struct bus_type platform_bus_type = {
    .nam = "platform", //设备名称
    .dev_attrs = platform_dev_attrs, //设备属性、含获取sys文件名,该总线会放在/sys/bus下
    .match = platform_match, //匹配函数,成功调用driver的.probe函数
    .uevent = platform_uevent, //消息传递,比如热插拔操作
    .suspend = platform_suspend, //电源管理的低功耗挂起
    .suspend_late = platform_suspend_late,
    .resume_early = platform_resume_early,
    .resume = platform_resume,   //恢复
    };
  2. 可以看到匹配函数match=platform_match,是匹配devdrivername

    static int platform_match(struct device * dev, struct device_driver * drv)
    {
    /*找到所有的device设备*/
    struct platform_device *pdev = container_of(dev, struct platform_device, dev);
    return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);
    }
  3. driver_register()函数就是用来创建/sys/bus/platform/driver

程序

设备文件

#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> /* 分配/设置/注册一个platform_device */ static struct resource led_resource[] = {
[0] = {
.start = 0x56000050,
.end = 0x56000050 + 8 - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = 5,
.end = 5,
.flags = IORESOURCE_IRQ,
} }; static void led_release(struct device * dev)
{
} static struct platform_device led_dev = {
.name = "myled",
.id = -1,
.num_resources = ARRAY_SIZE(led_resource),
.resource = led_resource,
.dev = {
.release = led_release,
},
}; static int led_dev_init(void)
{
platform_device_register(&led_dev);
return 0;
} static void led_dev_exit(void)
{
platform_device_unregister(&led_dev);
} module_init(led_dev_init);
module_exit(led_dev_exit); MODULE_LICENSE("GPL");

驱动算法

/* 分配/设置/注册一个platform_driver */
#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 <asm/uaccess.h>
#include <asm/io.h> static int major; static struct class *cls;
static volatile unsigned long *gpio_con;
static volatile unsigned long *gpio_dat;
static int pin; static int led_open(struct inode *inode, struct file *file)
{
//printk("first_drv_open\n");
/* 配置为输出 */
*gpio_con &= ~(0x3<<(pin*2));
*gpio_con |= (0x1<<(pin*2));
return 0;
} static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
int val; //printk("first_drv_write\n"); copy_from_user(&val, buf, count); // copy_to_user(); if (val == 1)
{
// 点灯
*gpio_dat &= ~(1<<pin);
}
else
{
// 灭灯
*gpio_dat |= (1<<pin);
} return 0;
} static struct file_operations led_fops = {
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
.open = led_open,
.write = led_write,
}; static int led_probe(struct platform_device *pdev)
{
struct resource *res; /* 根据platform_device的资源进行ioremap */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
gpio_con = ioremap(res->start, res->end - res->start + 1);
gpio_dat = gpio_con + 1; res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
pin = res->start; /* 注册字符设备驱动程序 */ printk("led_probe, found led\n"); major = register_chrdev(0, "myled", &led_fops); cls = class_create(THIS_MODULE, "myled"); class_device_create(cls, NULL, MKDEV(major, 0), NULL, "led"); /* /dev/led */ return 0;
} static int led_remove(struct platform_device *pdev)
{
/* 卸载字符设备驱动程序 */
/* iounmap */
printk("led_remove, remove led\n"); class_device_destroy(cls, MKDEV(major, 0));
class_destroy(cls);
unregister_chrdev(major, "myled");
iounmap(gpio_con); return 0;
} struct platform_driver led_drv = {
.probe = led_probe,
.remove = led_remove,
.driver = {
.name = "myled",
}
}; static int led_drv_init(void)
{
platform_driver_register(&led_drv);
return 0;
} static void led_drv_exit(void)
{
platform_driver_unregister(&led_drv);
} module_init(led_drv_init);
module_exit(led_drv_exit); MODULE_LICENSE("GPL");

测试程序

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h> /* led_test on
* led_test off
*/
int main(int argc, char **argv)
{
int fd;
int val = 1;
fd = open("/dev/led", O_RDWR);
if (fd < 0)
{
printf("can't open!\n");
}
if (argc != 2)
{
printf("Usage :\n");
printf("%s <on|off>\n", argv[0]);
return 0;
} if (strcmp(argv[1], "on") == 0)
{
val = 1;
}
else
{
val = 0;
} write(fd, &val, 4);
return 0;
}

测试

  1. 注册两个驱动,会自动生成文件/sys/bus/platform/下的devicesdrives生成驱动
  2. 测试使用./test on 点灯

    ---恢复内容结束---

    ---

    title: platform驱动分离

    tags: linux

    date: 2018/11/30 09:24:37

    toc: true


platform驱动分离

框架结构

platformbus总线结构将一个硬件驱动分为device设备与driver驱动两个部分,使用platform设备将两个联系到一起.

简单来说,就是将以前所写的驱动,将数据与算法分离,左边dev表示可变的数据,右边的driver表示稳定的算法.比如I2C/USB等总线协议可以理解为稳定的算法,具体的不同的EEPROM/led等可以理解为数据,包含了特殊的自身信息,比如io信息等.

与输入子系统联系

输入子系统是内核定义了一套统一的标准接口,而platform是将左侧的设备层分割为数据与算法

设备描述

这里的设备文件使用platform_device来描述,结构如下,总体来说可以称之为设备描述

  • platform_device_register 注册设备文件,匹配后会调用驱动算法的probe
  • platform_device_unregister卸载设备
  • 具体数据描述如下

驱动算法

这里将其称为驱动算法,其实也是一个正常的驱动程序,区别在于可以获取到上面注册的设备文件信息.当注册设备文件或者注册驱动文件的时候,都会调用驱动算法的probe.

  • platform_driver_register用来注册驱动,当注册设备文件或者注册驱动文件的时候,都会调用驱动算法的probe.

  • platform_driver_unregister用来卸载驱动

  • platform框架中的管理结构如下:

    //driver
    struct platform_driver {
    //函数
    (*probe) //匹配到设备文件的name与driver中的name一致将会在注册时执行
    (*remove) //卸载驱动出口,卸载驱动设备的时候应该也会调用
    (*shutdown)
    (*suspend)
    (*suspend_late)
    (*resume_early)
    (*resume)
    //结构体
    struct device_driver driver
    {
    .name //用作匹配
    }
    };
  • probe,当有设备文件和驱动算法匹配的时候自动执行,一般用来获取资源文件信息等,注册驱动,ioremap等,可以理解为执行驱动的第一个程序了

注意:

  1. 这里的platform_driver也是一个普通的驱动,可以用最原始的方式来注册驱动,创建class类,再在下面添加具体的设备文件来使用mdev机制自动创建所需要的文件.
  2. 也可以使用输入子系统的 框架,输入子系统所需要的驱动程序需要一套约定的接口,我们可以将输入子系统中的左侧的设备驱动再分割成设备描述与驱动算法.

注册机制

驱动算法使用platform_driver_register来注册,如下:

int platform_driver_register(struct platform_driver *drv)
{
drv->driver.bus = &platform_bus_type; //(1)挂接到虚拟总线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;
if (drv->suspend)
drv->driver.suspend = platform_drv_suspend;
if (drv->resume)
drv->driver.resume = platform_drv_resume; return driver_register(&drv->driver); //(2) 注册到driver目录下
}
  1. 从代码上看,platform只是只是bus中的一种,挂载到platform_bus_type

    struct bus_type platform_bus_type = {
    .nam = "platform", //设备名称
    .dev_attrs = platform_dev_attrs, //设备属性、含获取sys文件名,该总线会放在/sys/bus下
    .match = platform_match, //匹配函数,成功调用driver的.probe函数
    .uevent = platform_uevent, //消息传递,比如热插拔操作
    .suspend = platform_suspend, //电源管理的低功耗挂起
    .suspend_late = platform_suspend_late,
    .resume_early = platform_resume_early,
    .resume = platform_resume,   //恢复
    };
  2. 可以看到匹配函数match=platform_match,是匹配devdrivername

    static int platform_match(struct device * dev, struct device_driver * drv)
    {
    /*找到所有的device设备*/
    struct platform_device *pdev = container_of(dev, struct platform_device, dev);
    return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);
    }
  3. driver_register()函数就是用来创建/sys/bus/platform/driver

程序

设备文件

#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> /* 分配/设置/注册一个platform_device */ static struct resource led_resource[] = {
[0] = {
.start = 0x56000050,
.end = 0x56000050 + 8 - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = 5,
.end = 5,
.flags = IORESOURCE_IRQ,
} }; static void led_release(struct device * dev)
{
} static struct platform_device led_dev = {
.name = "myled",
.id = -1,
.num_resources = ARRAY_SIZE(led_resource),
.resource = led_resource,
.dev = {
.release = led_release,
},
}; static int led_dev_init(void)
{
platform_device_register(&led_dev);
return 0;
} static void led_dev_exit(void)
{
platform_device_unregister(&led_dev);
} module_init(led_dev_init);
module_exit(led_dev_exit); MODULE_LICENSE("GPL");

驱动算法

/* 分配/设置/注册一个platform_driver */
#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 <asm/uaccess.h>
#include <asm/io.h> static int major; static struct class *cls;
static volatile unsigned long *gpio_con;
static volatile unsigned long *gpio_dat;
static int pin; static int led_open(struct inode *inode, struct file *file)
{
//printk("first_drv_open\n");
/* 配置为输出 */
*gpio_con &= ~(0x3<<(pin*2));
*gpio_con |= (0x1<<(pin*2));
return 0;
} static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
int val; //printk("first_drv_write\n"); copy_from_user(&val, buf, count); // copy_to_user(); if (val == 1)
{
// 点灯
*gpio_dat &= ~(1<<pin);
}
else
{
// 灭灯
*gpio_dat |= (1<<pin);
} return 0;
} static struct file_operations led_fops = {
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
.open = led_open,
.write = led_write,
}; static int led_probe(struct platform_device *pdev)
{
struct resource *res; /* 根据platform_device的资源进行ioremap */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
gpio_con = ioremap(res->start, res->end - res->start + 1);
gpio_dat = gpio_con + 1; res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
pin = res->start; /* 注册字符设备驱动程序 */ printk("led_probe, found led\n"); major = register_chrdev(0, "myled", &led_fops); cls = class_create(THIS_MODULE, "myled"); class_device_create(cls, NULL, MKDEV(major, 0), NULL, "led"); /* /dev/led */ return 0;
} static int led_remove(struct platform_device *pdev)
{
/* 卸载字符设备驱动程序 */
/* iounmap */
printk("led_remove, remove led\n"); class_device_destroy(cls, MKDEV(major, 0));
class_destroy(cls);
unregister_chrdev(major, "myled");
iounmap(gpio_con); return 0;
} struct platform_driver led_drv = {
.probe = led_probe,
.remove = led_remove,
.driver = {
.name = "myled",
}
}; static int led_drv_init(void)
{
platform_driver_register(&led_drv);
return 0;
} static void led_drv_exit(void)
{
platform_driver_unregister(&led_drv);
} module_init(led_drv_init);
module_exit(led_drv_exit); MODULE_LICENSE("GPL");

测试程序

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h> /* led_test on
* led_test off
*/
int main(int argc, char **argv)
{
int fd;
int val = 1;
fd = open("/dev/led", O_RDWR);
if (fd < 0)
{
printf("can't open!\n");
}
if (argc != 2)
{
printf("Usage :\n");
printf("%s <on|off>\n", argv[0]);
return 0;
} if (strcmp(argv[1], "on") == 0)
{
val = 1;
}
else
{
val = 0;
} write(fd, &val, 4);
return 0;
}

测试

  1. 注册两个驱动,会自动生成文件/sys/bus/platform/下的devicesdrives生成驱动
  2. 测试使用./test on 点灯

platform驱动分离的更多相关文章

  1. 四、分离分层的 platform驱动

    学习目标: 学习实现platform机制的分层分离,并基于platform机制,编写led设备和驱动程序: 一.分离分层 输入子系统.usb设备比驱动以及platform类型的驱动等都体现出分离分层机 ...

  2. Linux Platform驱动模型(二) _驱动方法

    在Linux设备树语法详解和Linux Platform驱动模型(一) _设备信息中我们讨论了设备信息的写法,本文主要讨论平台总线中另外一部分-驱动方法,将试图回答下面几个问题: 如何填充platfo ...

  3. Linux Platform驱动模型(二) _驱动方法【转】

    转自:http://www.cnblogs.com/xiaojiang1025/archive/2017/02/06/6367910.html 在Linux设备树语法详解和Linux Platform ...

  4. Linux 驱动框架---platform驱动框架

    Linux系统的驱动框架主要就是三个主要部分组成,驱动.总线.设备.现在常见的嵌入式SOC已经不是单纯的CPU的概念了,它们都会在片上集成很多外设电路,这些外设都挂接在SOC内部的总线上,不同与IIC ...

  5. platform驱动之probe函数

    驱动注册的probe函数 probe函数在设备驱动注册最后收尾工作,当设备的device 和其对应的driver 在总线上完成配对之后,系统就调用platform设备的probe函数完成驱动注册最后工 ...

  6. platform怎么实现数据数据和驱动分离

    一些重要的结构体: struct platform_device { const char * name; int id; struct device dev; u32 num_resources; ...

  7. Linux Platform驱动模型(一)-设备信息

    我在Linux字符设备驱动框架一文中简单介绍了Linux字符设备编程模型,在那个模型中,只要应用程序open()了相应的设备文件,就可以使用ioctl通过驱动程序来控制我们的硬件,这种模型直观,但是从 ...

  8. Linux驱动之平台设备驱动模型简析(驱动分离分层概念的建立)

    Linux设备模型的目的:为内核建立一个统一的设备模型,从而有一个对系统结构的一般性抽象描述.换句话说,Linux设备模型提取了设备操作的共同属性,进行抽象,并将这部分共同的属性在内核中实现,而为需要 ...

  9. Linux Platform驱动模型(一) _设备信息

    我在Linux字符设备驱动框架一文中简单介绍了Linux字符设备编程模型,在那个模型中,只要应用程序open()了相应的设备文件,就可以使用ioctl通过驱动程序来控制我们的硬件,这种模型直观,但是从 ...

随机推荐

  1. python + PyQt5 实现 简易计算器

    忽然想起之前一直想写个简单的计算器,今天就写了一下,界面有些简陋,但是基本功能实现没有问题 以下是源码: # --*-- coding:utf-8 --*-- import sys from PyQt ...

  2. Jenkins插件安装

    默认的Jenkins平台继有的功能是有限的,插件为Jenkins提供了各种扩展功能,有了插件,Jenkins就会变得更加强大,这里推荐两个不错的插件: 点击菜单栏中“系统管理”--“插件管理”--“A ...

  3. SQL 和 NoSQL 的区别

    一.概念 SQL (Structured Query Language) 数据库,指关系型数据库.主要代表:SQL Server,Oracle,MySQL(开源),PostgreSQL(开源). No ...

  4. wangEditor的使用

    wangEditor的使用 第一步,将其下载,并引入项目中. 第二步,引入js <script type="text/javascript" src="/plugi ...

  5. 使用PowerDesigner 15对现有数据库进行生成图表结构

    PowerDesigner的安装和基本使用,我就不阐述了.大家可以到这里看看:http://www.blogjava.net/wangdetian168/archive/2011/04/07/Powe ...

  6. 8个Python高效数据分析的技巧

    一行代码定义List 下面是使用For循环创建列表和用一行代码创建列表的对比. x = [1,2,3,4] out = [] for item in x: out.append(item**2) pr ...

  7. iOS开发基础-UIScrollView实现图片缩放

    当用户在 UIScrollView 上使用捏合手势时, UIScrollView 会给 UIScrollViewDelegate 协议发送一条消息,并调用代理的 viewForZoomingInScr ...

  8. SpringCloud(5)路由网关Spring Cloud Zuul

    一个简单的微服务系统如下图: 1.为什么需要Zuul Zuul很容易实现 负载均衡.智能路由 和 熔断器,可以做身份认证和权限认证,可以实现监控,在高流量状态下,对服务进行降级. 2.路由网关 继续前 ...

  9. c++学习之初话 函数指针和函数对象 的因缘

    函数指针可以方便我们调用函数,但采用函数对象,更能体现c++面向对象的程序特性. 函数对象的本质:()运算符的重载.我们通过一段代码来感受函数指针和函数对象的使用: int AddFunc(int a ...

  10. MySQL中的float和decimal类型有什么区别

    decimal 类型可以精确地表示非常大或非常精确的小数.大至 1028(正或负)以及有效位数多达 28 位的数字可以作为 decimal类型存储而不失其精确性.该类型对于必须避免舍入错误的应用程序( ...