在总线设备驱动模型中,平台设备是写在c文件中。使用设备树时,平台设备事先并不存在,在dts文件中构造节点,节点里面含有资源。dts文件被编译成dtb文件,然后传递给内核。内核会解析dtb文件,得到一个个device_node,每个节点对应一个device_node结构体,每个device_node结构体变成一个platform_device结构体,该结构体中就含有资源,这些资源来源于dts文件。接下来的处理过程和总线设备驱动模型就一样了,如果设备与驱动相匹配,就调用驱动中的probe函数。可以这么认为,设备树是对总线设备驱动模型的一种改进,它仍然属于总线设备驱动模型的一种。

看一下这个设备树文件:

 // SPDX-License-Identifier: GPL-2.0
/*
* SAMSUNG SMDK2440 board device tree source
*
* Copyright (c) 2018 weidongshan@qq.com
* dtc -I dtb -O dts -o jz2440.dts jz2440.dtb
*/
//这里定义了一些宏,使用的是c语言的语法
#define S3C2410_GPA(_nr) ((0<<16) + (_nr))
#define S3C2410_GPB(_nr) ((1<<16) + (_nr))
#define S3C2410_GPC(_nr) ((2<<16) + (_nr))
#define S3C2410_GPD(_nr) ((3<<16) + (_nr))
#define S3C2410_GPE(_nr) ((4<<16) + (_nr))
#define S3C2410_GPF(_nr) ((5<<16) + (_nr))
#define S3C2410_GPG(_nr) ((6<<16) + (_nr))
#define S3C2410_GPH(_nr) ((7<<16) + (_nr))
#define S3C2410_GPJ(_nr) ((8<<16) + (_nr))
#define S3C2410_GPK(_nr) ((9<<16) + (_nr))
#define S3C2410_GPL(_nr) ((10<<16) + (_nr))
#define S3C2410_GPM(_nr) ((11<<16) + (_nr)) /dts-v1/; / {
model = "SMDK24440";
compatible = "samsung,smdk2440"; #address-cells = <>;
#size-cells = <>; memory@ {
device_type = "memory";
reg = <0x30000000 0x4000000>;
};
/*
cpus {
cpu {
compatible = "arm,arm926ej-s";
};
};
*/
chosen { //设置了内核的命令行参数
bootargs = "noinitrd root=/dev/mtdblock4 rw init=/linuxrc console=ttySAC0,115200";
}; led {
compatible = "jz2440_led"; //以后就使用compatible在内核里面找到能够支持这个节点的驱动程序,即找到能够支持这个节点的平台drv.
reg = <S3C2410_GPF() >;//reg是register的缩写。在arm系统里面,寄存器和内存是被同样对待的,因为寄存器的访问空间与内存的访问空间没什么差别。
};
};

单板启动之后,

cd /sys/devices/platform/

ls 下会看到xxxxxxx,假设是50005.led。

cd 50005.led

ls会看到这样的文件

driver_override    of_node       subsystem      modalias      power   uevent

cd of_node

ls

compatible     name      reg

cat compatible     就会显示  jz2440_led

cat  name             就会显示led

hexdump -C reg

对应着8个字节,

00 05   00 05  00  00  00  01

对应着两个数值,来源于    reg = <S3C2410_GPF(5) 1>

00 05   00 05    对应S3C2410_GPF(5)代表着寄存器的起始地址,对应着这个宏    #define S3C2410_GPF(_nr) ((5<<16) + (_nr)),表示高16位是5,低16位也是5.

00 00 00 01对应着是1,本意就是寄存器的大小。

在总线设备驱动模型中,是通过名字来进行设备与驱动的匹配。那么在利用设备树时,是利用什么进行匹配的呢。

 static int platform_match(struct device *dev, struct device_driver *drv)
{
struct platform_device *pdev = to_platform_device(dev);
struct platform_driver *pdrv = to_platform_driver(drv); /* Attempt an OF style match first */
if (of_driver_match_device(dev, drv))//用来判断从设备树中得到的platform_devcie与提供的platform_drv
                         //是否匹配,
return ; /* Then try to match against the id table */
if (pdrv->id_table)
return platform_match_id(pdrv->id_table, pdev) != NULL; /* fall-back to driver name match */
return (strcmp(pdev->name, drv->name) == );
}
 static inline int of_driver_match_device(struct device *dev,
const struct device_driver *drv)
{
  /*从这里可以看出,平台drv中有个成员变量,of_match_table*/
return of_match_device(drv->of_match_table, dev) != NULL;
}
 struct device_driver {
const char *name;
struct bus_type *bus; struct module *owner;
const char *mod_name; /* used for built-in modules */ bool suppress_bind_attrs; /* disables bind/unbind via sysfs */ const struct of_device_id *of_match_table; //该指针指向一项或多项of_device_id int (*probe) (struct device *dev);
int (*remove) (struct device *dev);
void (*shutdown) (struct device *dev);
int (*suspend) (struct device *dev, pm_message_t state);
int (*resume) (struct device *dev);
const struct attribute_group **groups; const struct dev_pm_ops *pm; struct driver_private *p;
};
 struct of_device_id
{
char name[];
char type[];
char compatible[];//从dts里得到的platform_device里有compatible属性,两者比较,如果一样的话,就是匹配。
#ifdef __KERNEL__
void *data;
#else
kernel_ulong_t data;
#endif
};
 struct platform_device {
const char * name;
int id;
struct device dev;//对于dts生成的设备platform_device,这里含有Of_node,of_node中含有属性,这含有哪些属性呢,这取决于设备树,比如:
//compatible pin等。这个compatible属性用来寻找支持它的platform_driver.这个compatible是最优先比较。
u32 num_resources;
struct resource * resource; const struct platform_device_id *id_entry; /* MFD cell pointer */
struct mfd_cell *mfd_cell; /* arch specific additions */
struct pdev_archdata archdata;
};

利用设备树编写的led驱动程序如下:

 #include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h> #define S3C2440_GPA(n) (0<<16 | n)
#define S3C2440_GPB(n) (1<<16 | n)
#define S3C2440_GPC(n) (2<<16 | n)
#define S3C2440_GPD(n) (3<<16 | n)
#define S3C2440_GPE(n) (4<<16 | n)
#define S3C2440_GPF(n) (5<<16 | n)
#define S3C2440_GPG(n) (6<<16 | n)
#define S3C2440_GPH(n) (7<<16 | n)
#define S3C2440_GPI(n) (8<<16 | n)
#define S3C2440_GPJ(n) (9<<16 | n) static int led_pin;
static volatile unsigned int *gpio_con;
static volatile unsigned int *gpio_dat; /* 123. 分配/设置/注册file_operations
* 4. 入口
* 5. 出口
*/ static int major;
static struct class *led_class; static unsigned int gpio_base[] = {
0x56000000, /* GPACON */
0x56000010, /* GPBCON */
0x56000020, /* GPCCON */
0x56000030, /* GPDCON */
0x56000040, /* GPECON */
0x56000050, /* GPFCON */
0x56000060, /* GPGCON */
0x56000070, /* GPHCON */
, /* GPICON */
0x560000D0, /* GPJCON */
}; static int led_open (struct inode *node, struct file *filp)
{
/* 把LED引脚配置为输出引脚 */
/* GPF5 - 0x56000050 */
int bank = led_pin >> ;
int base = gpio_base[bank]; int pin = led_pin & 0xffff;
gpio_con = ioremap(base, );
if (gpio_con) {
printk("ioremap(0x%x) = 0x%x\n", base, gpio_con);
}
else {
return -EINVAL;
} gpio_dat = gpio_con + ; *gpio_con &= ~(<<(pin * ));
*gpio_con |= (<<(pin * )); return ;
} static ssize_t led_write (struct file *filp, const char __user *buf, size_t size, loff_t *off)
{
/* 根据APP传入的值来设置LED引脚 */
unsigned char val;
int pin = led_pin & 0xffff; copy_from_user(&val, buf, ); if (val)
{
/* 点灯 */
*gpio_dat &= ~(<<pin);
}
else
{
/* 灭灯 */
*gpio_dat |= (<<pin);
} return ; /* 已写入1个数据 */
} static int led_release (struct inode *node, struct file *filp)
{
printk("iounmap(0x%x)\n", gpio_con);
iounmap(gpio_con);
return ;
} static struct file_operations myled_oprs = {
.owner = THIS_MODULE,
.open = led_open,
.write = led_write,
.release = led_release,
}; static int led_probe(struct platform_device *pdev)
{
struct resource *res; /* 根据platform_device的资源进行ioremap */
res = platform_get_resource(pdev, IORESOURCE_MEM, );
led_pin = res->start; major = register_chrdev(, "myled", &myled_oprs); led_class = class_create(THIS_MODULE, "myled");
device_create(led_class, NULL, MKDEV(major, ), NULL, "led"); /* /dev/led */ return ;
} static int led_remove(struct platform_device *pdev)
{
unregister_chrdev(major, "myled");
device_destroy(led_class, MKDEV(major, ));
class_destroy(led_class); return ;
} static const struct of_device_id of_match_leds[] = {
{ .compatible = "jz2440_led", .data = NULL },
{ /* sentinel */ }
}; struct platform_driver led_drv = {
.probe = led_probe,
.remove = led_remove,
.driver = {
.name = "myled",
.of_match_table = of_match_leds, /* 能支持哪些来自于dts的platform_device */
}
}; static int myled_init(void)
{
platform_driver_register(&led_drv);
return ;
} static void myled_exit(void)
{
platform_driver_unregister(&led_drv);
} module_init(myled_init);
module_exit(myled_exit); MODULE_LICENSE("GPL");

在设备树中形如下面这样的写法太别扭了

reg = <S3C2410_GPF(5) 1>;  
S3C2410_GPF(5)这个地方本来定义了引脚,你非要把它当做register。看一下能否将register去掉,能不能使用另外一种方法指定引脚。
 // SPDX-License-Identifier: GPL-2.0
/*
* SAMSUNG SMDK2440 board device tree source
*
* Copyright (c) 2018 weidongshan@qq.com
* dtc -I dtb -O dts -o jz2440.dts jz2440.dtb
*/ #define S3C2410_GPA(_nr) ((0<<16) + (_nr))
#define S3C2410_GPB(_nr) ((1<<16) + (_nr))
#define S3C2410_GPC(_nr) ((2<<16) + (_nr))
#define S3C2410_GPD(_nr) ((3<<16) + (_nr))
#define S3C2410_GPE(_nr) ((4<<16) + (_nr))
#define S3C2410_GPF(_nr) ((5<<16) + (_nr))
#define S3C2410_GPG(_nr) ((6<<16) + (_nr))
#define S3C2410_GPH(_nr) ((7<<16) + (_nr))
#define S3C2410_GPJ(_nr) ((8<<16) + (_nr))
#define S3C2410_GPK(_nr) ((9<<16) + (_nr))
#define S3C2410_GPL(_nr) ((10<<16) + (_nr))
#define S3C2410_GPM(_nr) ((11<<16) + (_nr)) /dts-v1/; / {
model = "SMDK24440";
compatible = "samsung,smdk2440"; #address-cells = <>;
#size-cells = <>; memory@ {
device_type = "memory";
reg = <0x30000000 0x4000000>;
};
/*
cpus {
cpu {
compatible = "arm,arm926ej-s";
};
};
*/
chosen {
bootargs = "noinitrd root=/dev/mtdblock4 rw init=/linuxrc console=ttySAC0,115200";
}; led {
compatible = "jz2440_led";
pin = <S3C2410_GPF()>;
};
};
 #include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h> #define S3C2440_GPA(n) (0<<16 | n)
#define S3C2440_GPB(n) (1<<16 | n)
#define S3C2440_GPC(n) (2<<16 | n)
#define S3C2440_GPD(n) (3<<16 | n)
#define S3C2440_GPE(n) (4<<16 | n)
#define S3C2440_GPF(n) (5<<16 | n)
#define S3C2440_GPG(n) (6<<16 | n)
#define S3C2440_GPH(n) (7<<16 | n)
#define S3C2440_GPI(n) (8<<16 | n)
#define S3C2440_GPJ(n) (9<<16 | n) static int led_pin;
static volatile unsigned int *gpio_con;
static volatile unsigned int *gpio_dat; /* 123. 分配/设置/注册file_operations
* 4. 入口
* 5. 出口
*/ static int major;
static struct class *led_class; static unsigned int gpio_base[] = {
0x56000000, /* GPACON */
0x56000010, /* GPBCON */
0x56000020, /* GPCCON */
0x56000030, /* GPDCON */
0x56000040, /* GPECON */
0x56000050, /* GPFCON */
0x56000060, /* GPGCON */
0x56000070, /* GPHCON */
, /* GPICON */
0x560000D0, /* GPJCON */
}; static int led_open (struct inode *node, struct file *filp)
{
/* 把LED引脚配置为输出引脚 */
/* GPF5 - 0x56000050 */
int bank = led_pin >> ;
int base = gpio_base[bank]; int pin = led_pin & 0xffff;
gpio_con = ioremap(base, );
if (gpio_con) {
printk("ioremap(0x%x) = 0x%x\n", base, gpio_con);
}
else {
return -EINVAL;
} gpio_dat = gpio_con + ; *gpio_con &= ~(<<(pin * ));
*gpio_con |= (<<(pin * )); return ;
} static ssize_t led_write (struct file *filp, const char __user *buf, size_t size, loff_t *off)
{
/* 根据APP传入的值来设置LED引脚 */
unsigned char val;
int pin = led_pin & 0xffff; copy_from_user(&val, buf, ); if (val)
{
/* 点灯 */
*gpio_dat &= ~(<<pin);
}
else
{
/* 灭灯 */
*gpio_dat |= (<<pin);
} return ; /* 已写入1个数据 */
} static int led_release (struct inode *node, struct file *filp)
{
printk("iounmap(0x%x)\n", gpio_con);
iounmap(gpio_con);
return ;
} static struct file_operations myled_oprs = {
.owner = THIS_MODULE,
.open = led_open,
.write = led_write,
.release = led_release,
}; static int led_probe(struct platform_device *pdev)
{
struct resource *res; /* 根据platform_device的资源进行ioremap */
res = platform_get_resource(pdev, IORESOURCE_MEM, );
if (res) {
led_pin = res->start;
}
else {
/* 获得pin属性 */
of_property_read_s32(pdev->dev.of_node, "pin", &led_pin);
} if (!led_pin)
{
printk("can not get pin for led\n");
return -EINVAL;
} major = register_chrdev(, "myled", &myled_oprs); led_class = class_create(THIS_MODULE, "myled");
device_create(led_class, NULL, MKDEV(major, ), NULL, "led"); /* /dev/led */ return ;
} static int led_remove(struct platform_device *pdev)
{
unregister_chrdev(major, "myled");
device_destroy(led_class, MKDEV(major, ));
class_destroy(led_class); return ;
} static const struct of_device_id of_match_leds[] = {
{ .compatible = "jz2440_led", .data = NULL },
{ /* sentinel */ }
}; struct platform_driver led_drv = {
.probe = led_probe,
.remove = led_remove,
.driver = {
.name = "myled",
.of_match_table = of_match_leds, /* 能支持哪些来自于dts的platform_device */
}
}; static int myled_init(void)
{
platform_driver_register(&led_drv);
return ;
} static void myled_exit(void)
{
platform_driver_unregister(&led_drv);
} module_init(myled_init);
module_exit(myled_exit); MODULE_LICENSE("GPL");
												

使用设备树来编写led驱动程序的更多相关文章

  1. BeagleBone Black Linux驱动程序开发入门(1): LED驱动程序

    这篇文章展示如何在BBB平台上编写LED驱动程序,本文的程序是根据国嵌S3C2440的LED驱动的例子并结合内核中OMAP系列的gpio操作来改的.本文中的程序包括驱动程序模块和用户空间程序.废话不多 ...

  2. 基于设备树的led驱动程序

    #include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include ...

  3. 11.ok6410之led驱动程序编写

    led驱动程序编写 本文主要包含三部分,led驱动程序led.c编写,编译驱动程序的makefile的编写,以及使用驱动程序的应用程序led_app的编写 一.led.c编写 #include < ...

  4. linux设备驱动程序-设备树(0)-dtb格式

    linux设备树dtb格式 设备树的一般操作方式是:开发人员根据开发需求编写dts文件,然后使用dtc将dts编译成dtb文件. dts文件是文本格式的文件,而dtb是二进制文件,在linux启动时被 ...

  5. linux设备驱动程序-设备树(1)-dtb转换成device_node

    linux设备驱动程序-设备树(1)-dtb转换成device_node 本设备树解析基于arm平台 从start_kernel开始 linux最底层的初始化部分在HEAD.s中,这是汇编代码,我们暂 ...

  6. linux设备驱动程序-设备树(3)-设备树多级子节点的转换

    linux设备驱动程序--设备树多级子节点的转换 在上一章:设备树处理之--device_node转换成platform_device中,有提到在设备树的device_node到platform_de ...

  7. linux设备驱动程序-i2c(2)-adapter和设备树的解析

    linux设备驱动程序-i2c(2)-adapter和设备树的解析 (注: 基于beagle bone green开发板,linux4.14内核版本) 在本系列linux内核i2c框架的前两篇,分别讲 ...

  8. Linux dts 设备树详解(二) 动手编写设备树dts

    Linux dts 设备树详解(一) 基础知识 Linux dts 设备树详解(二) 动手编写设备树dts 文章目录 前言 硬件结构 设备树dts文件 前言 在简单了解概念之后,我们可以开始尝试写一个 ...

  9. linux设备驱动程序-设备树(2)-device_node转换成platform_device

    设备树处理之--device_node转换成platform_device 以下讨论基于linux4.14,arm平台 platform device 设备树的产生就是为了替代driver中过多的pl ...

随机推荐

  1. 1.web2

    听说聪明的人都能找到答案http://123.206.87.240:8002/web2/ 直接查看源码~~~

  2. js 三种存储方式的区别

    javaScript有三种数据存储方式,分别是: sessionStorage localStorage cookie 相同点:都保存在浏览器端,同源的 不同点: ①传递方式不同 cookie数据始终 ...

  3. PKCS pfx cer x509

    PKCS pfx cer x509 参考 PKCS 15 个标准 PKCS The Public-Key Cryptography Standards (PKCS)是由美国RSA数据安全公司及其合作伙 ...

  4. 牛客小白月赛18 Forsaken喜欢数论

    牛客小白月赛18 Forsaken喜欢数论 题目传送门直接点标题 ​ Forsaken有一个有趣的数论函数.对于任意一个数xxx,f(x)f(x)f(x)会返回xxx的最小质因子.如果这个数没有最小质 ...

  5. Paper | U-Net: Convolutional Networks for Biomedical Image Segmentation

    目录 故事背景 U-Net 具体结构 损失 数据扩充 发表在2015 MICCAI.原本是一篇医学图像分割的论文,但由于U-Net杰出的网络设计,得到了8k+的引用. 摘要 There is larg ...

  6. 从零实现一个React:Luster(一):JSX解析器

    前言 这是之前在掘金发的两条沸点,懒得写了,直接复制过来作为前言了.然后这个项目可能之后还会继续写,增加一些路由或者模板引擎的指令什么的,但是再过没多久寒假就有大块时间了就可能不摸这个鱼去开其它坑了, ...

  7. python-10-列表、元组嵌套

    前言 元组.列表前面章节有讲解实例,本节内容是列表.元组的多嵌套. 一.列表嵌套 1.列表嵌套操作1 # 列表的嵌套 li = ['xiaolong', '小林', ['小龙', 'xiaol'], ...

  8. hdu-6071 Lazy Running

    In HDU, you have to run along the campus for 24 times, or you will fail in PE. According to the rule ...

  9. Visual Studio 2019 (VS2019)正式版安装 VisualSVN Server 插件

    VS2019 正式版最近刚刚推出来,目前 Ankhsvn 还不支持,它最高只支持 VS2017,全网搜索了一下,也没有找到.在 Stackoverflow 上看了一下,找到这篇问答: 自己按照这种方法 ...

  10. MySQL性能诊断与调优

    LAMP 系统性能调优,第 3 部分: MySQL 服务器调优http://www.ibm.com/developerworks/cn/linux/l-tune-lamp-3.html LoadRun ...