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

---

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 XML解析之DOM

    DOM说明: DOM:Document Object Model API DOM是一种跨语言的XML解析机制,DOM把整个XML文件或字符串在内存中解析为树型结构方便访问. https://docs. ...

  2. tomcat设置开机启动

    一.windows 1. 下载tomcat 2. 进入bin目录,查看是否存在service.dat,如果没有自行创建 3. 打开cmd,进入tomcat>bin目录 说明:用法: servic ...

  3. centos7.4系统升级kernel内核

    在实验环境下,已安装了最新的CentOS 7.4操作系统,现在需要升级内核版本. 实验环境CentOS-7-x86_64-Minimal-1708.iso CentOS Linux release 7 ...

  4. LeetCode算法题-Trim a Binary Search Tree(Java实现)

    这是悦乐书的第284次更新,第301篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第152题(顺位题号是669).给定二叉搜索树以及L和R的最低和最高边界,修剪树以使其所 ...

  5. linux 下 命令行中运行 selenium chrome 问题

    1.chrome 现在不允许使用root运行了. 2.无界面 chromedriver 调用chrome 会出错. <另外一定要匹配 chromedriver和chrome 的版本. 要不会出各 ...

  6. ✔ OI Diary ★

    一 | 2019-3-28 1.整晨,云之考矣,暴后皆不会,邃无感而写斯普雷尔,然则午后知暴可六十哉. 然则斯普雷毙,虽特判之矣,然则暴只判二十哉,呜呼! ​2.午间归宿,视白购书一本,目触,感之甚集 ...

  7. leetcode 263. Ugly Number 、264. Ugly Number II 、313. Super Ugly Number 、204. Count Primes

    263. Ugly Number 注意:1.小于等于0都不属于丑数 2.while循环的判断不是num >= 0, 而是能被2 .3.5整除,即能被整除才去除这些数 class Solution ...

  8. 2018-2019-2 20175228实验一《Java开发环境的熟悉》实验报告

    一.实验内容及步骤 (一)使用JDk编译.运行简单的Java程序 实验步骤如下: 实验截图如下: (二)使用IDEA调试程序 1.设置断点2.单步运行:Step Into(快捷捷F7)和Step Ov ...

  9. iOS开发基础-图片切换(2)之懒加载

    延续:iOS开发基础-图片切换(1),对(1)里面的代码进行改善. 在 ViewController 类中添加新的数组属性:  @property (nonatomic, strong) NSArra ...

  10. 在Bootstrap开发框架中使用dataTable直接录入表格行数据

    在Winform开发的时候,我们很多时候可以利用表格控件来直接录入数据,不过在Web上较少看到,其实也可以利用dataTable对象处理直接录入表格行数据,这个可以提高数据的录入方便,特别是在一些简单 ...