驱动开发之platform总线与设备树:
platform总线
实现设备和驱动的分离,为了提高驱动的通用性。
实现分离后出现三种文件: 驱动代码(自己实现)
设备代码(自己实现)
虚拟总线代码(内核实现)
追内核源码(设备):
1 追内核源码:
2 struct platform_device //描述platform设备信息
3 {
4 const char *name;//可以用于匹配,也可以不用于匹配。但是不能省略,不管是否匹配一定出现在sysfs文件系统的/sys/bus/platform/devices下产生文件名
5 struct device dev;//描述了具体的设备信息
6 struct resource *resource; //资源结构体
7 const struct platform_device_id *id_entry;
8 };
struct device
{
void (*release)(struct device *dev);//释放资源,配合驱动的remove函数使用
};
1 struct resource
2 {
3 resource_size_t start;
4 resource_size_t end;
5 nsigned long flags;//描述了资源类型,platform总线中常见的资源类型有三种-->IORESOURCE_MEM(寄存器地址) IORESOURCE_IRQ IORESOURCE_DMA
6 };
7
8 如果flags传递的是IORESOURCE_MEM,start代表寄存器起始地址,end代表寄存器结束地址
1 struct platform_device_id
2 {
3 char name[PLATFORM_NAME_SIZE];//专门用来和驱动匹配的
4 };
追内核源码(驱动):
1 struct platform_driver
{
2 int (*probe)(struct platform_device *);//探测函数如果设备和驱动匹配成功则自动执行probe
3 int (*remove)(struct platform_device *);
4 struct device_driver driver;
5 const struct platform_device_id *id_table;//专门用于和设备匹配的
6 };
1 struct device_driver
2 {
3 const char *name;//可以用于和设备匹配,也可以不用于匹配,但是不能省略一定会在/sys/bus/platform/drivers下创建文件夹
4 };
platform总线接口:
1 platform_device_register(struct platform_device *);//注册设备(将设备结构体加入到设备链表中)
2 platform_device_unregister(struct platform_device *);//注销设备
1 platform_driver_register(struct platform_driver *);//注册驱动
2 platform_driver_unregister(struct platform_driver *);//注销驱动
test:
1 struct dev 2 { 3 char *name; 4 int a; 5 int b; 6 }; 7 8 struct drv 9 { 10 char *name; 11 int (*p)(struct dev *); 12 };
1 #include <stdio.h> 2 #include "head.h" 3 4 extern struct dev mydev; 5 extern struct drv mydrv; 6 7 int main(int argc, const char *argv[]) 8 { 9 if(strcmp(mydrv.name,mydev.name) == 0) 10 printf("%d ",mydrv.p(&mydev)); 11 return 0; 12 }
1 #include "head.h" 2 3 struct dev mydev = { 4 .name = "xxx", 5 .a = 10, 6 .b = 20, 7 };
1 #include "head.h" 2 3 int add(struct dev *info) 4 { 5 return info->a + info->b; 6 } 7 8 struct drv mydrv = { 9 .name = "xxx", 10 .p = add, 11 };
dev.c框架:
模块声明
加载函数
{
调用 platform_device_register();
}
卸载函数
{
调用 platform_device_unregister();
}
1 #include <linux/init.h> 2 #include <linux/module.h> 3 #include <linux/platform_device.h> 4 5 void fs4412_platdev_release(struct device *dev) 6 { 7 printk("release ok "); 8 } 9 10 struct platform_device pdev = { 11 .name = "xxx1", 12 .dev = { 13 .release = fs4412_platdev_release, 14 }, 15 16 }; 17 18 int fs4412_platdev_init(void) 19 { 20 platform_device_register(&pdev); 21 return 0; 22 } 23 module_init(fs4412_platdev_init); 24 25 void fs4412_platdev_exit(void) 26 { 27 platform_device_unregister(&pdev); 28 return; 29 } 30 module_exit(fs4412_platdev_exit); 31 MODULE_LICENSE("GPL");
drv.c框架:
模块声明
加载函数
{
调用 platform_driver_register();
}
卸载函数
{
调用 platform_driver_unregister();
}
1 #include <linux/init.h> 2 #include <linux/module.h> 3 #include <linux/platform_device.h> 4 5 int fs4412_platdrv_probe(struct platform_device *pdev) 6 { 7 printk("match ok "); 8 return 0; 9 } 10 11 int fs4412_platdrv_remove(struct platform_device *pdev) 12 { 13 printk("remove ok "); 14 return 0; 15 } 16 17 #if 0 18 struct platform_device_id idtbl = { 19 .name = "xxx1", 20 }; 21 #endif 22 23 struct platform_device_id idtbl[] = { 24 [0] = { 25 .name = "a", 26 }, 27 [1] = { 28 .name = "123", 29 }, 30 [2] = { 31 .name = "xxx1", 32 }, 33 }; 34 35 struct platform_driver pdrv = { 36 .driver = { 37 .name = "xxx", 38 }, 39 40 // .id_table = &idtbl, 41 .id_table = idtbl, 42 .probe = fs4412_platdrv_probe, 43 .remove = fs4412_platdrv_remove, 44 45 }; 46 47 int fs4412_platdrv_init(void) 48 { 49 platform_driver_register(&pdrv); 50 return 0; 51 } 52 module_init(fs4412_platdrv_init); 53 54 void fs4412_platdrv_exit(void) 55 { 56 platform_driver_unregister(&pdrv); 57 return; 58 } 59 module_exit(fs4412_platdrv_exit); 60 MODULE_LICENSE("GPL");
1 ---->>> vi -t platform_driver_register 过程 2 #define platform_driver_register(drv) 3 __platform_driver_register(drv, THIS_MODULE) 4 5 6 --->drv->driver.bus = &platform_bus_type; 7 8 9 --->struct bus_type platform_bus_type = { 10 .name = "platform", 11 .match = platform_match, 12 }; 13 14 --->if (of_driver_match_device(dev, drv))//用于和设备树匹配 15 return 1; 16 if (acpi_driver_match_device(dev, drv))//不知道,没用过 17 return 1; 18 19 --->if (pdrv->id_table) 20 return platform_match_id(pdrv->id_table, pdev) != NULL; 21 22 23 --->while (id->name[0]) 24 { 25 if (strcmp(pdev->name, id->name) == 0) 26 { 27 pdev->id_entry = id; 28 return id; 29 } 30 id++; 31 } 32 return (strcmp(pdev->name, drv->name) == 0); 33 34 ---->>>如果以上四种匹配方式都成功后则执行: 35 #define platform_driver_register(drv) 36 __platform_driver_register(drv, THIS_MODULE) 37 38 39 --->if (drv->probe) 40 drv->driver.probe = platform_drv_probe; 41 42 (--->表示向下追内核过程) 43 ret = drv->probe(dev);//执行自己定义的probe函数,并且传递了实参(后面可以应用是合法的)
platform_led:
1 #include <stdio.h> 2 #include <sys/types.h> 3 #include <sys/stat.h> 4 #include <fcntl.h> 5 6 int main(int argc, const char *argv[]) 7 { 8 int fd; 9 10 fd = open("/dev/led",O_RDWR); 11 12 sleep(3); 13 14 close(fd); 15 return 0; 16 }
1 #include <linux/init.h> 2 #include <linux/module.h> 3 #include <linux/platform_device.h> 4 #include <linux/device.h> 5 #include <linux/fs.h> 6 #include <asm/io.h> 7 8 int major; 9 struct class *cls; 10 struct device *devs; 11 12 unsigned int *gpx2con; 13 unsigned int *gpx1con; 14 unsigned int *gpf3con; 15 unsigned int *gpx2dat; 16 unsigned int *gpx1dat; 17 unsigned int *gpf3dat; 18 19 int fs4412_platdrv_open(struct inode *inode,struct file *filp) 20 { 21 writel((readl(gpx2dat) & ~(1 << 7)) | 1 << 7,gpx2dat); 22 writel((readl(gpx1dat) & ~(1 << 0)) | 1 << 0,gpx1dat); 23 writel((readl(gpf3dat) & ~(1 << 4)) | 1 << 4,gpf3dat); 24 writel((readl(gpf3dat) & ~(1 << 5)) | 1 << 5,gpf3dat); 25 26 return 0; 27 } 28 29 int fs4412_platdrv_close(struct inode *inode,struct file *filp) 30 { 31 writel((readl(gpx2dat) & ~(1 << 7)) ,gpx2dat); 32 writel((readl(gpx1dat) & ~(1 << 0)) ,gpx1dat); 33 writel((readl(gpf3dat) & ~(1 << 4)) ,gpf3dat); 34 writel((readl(gpf3dat) & ~(1 << 5)) ,gpf3dat); 35 return 0; 36 } 37 38 struct file_operations fops = { 39 .owner = THIS_MODULE, 40 .open = fs4412_platdrv_open, 41 .release = fs4412_platdrv_close, 42 }; 43 44 int fs4412_platdrv_probe(struct platform_device *pdev) 45 { 46 printk("match ok "); 47 48 major = register_chrdev(0,"plat-led",&fops); 49 cls = class_create(THIS_MODULE,"led"); 50 devs = device_create(cls,NULL,MKDEV(major,0),NULL,"led"); 51 52 gpx2con = ioremap(pdev->resource[0].start,4); 53 gpx2dat = gpx2con + 1; 54 55 gpx1con = ioremap(pdev->resource[1].start,4); 56 gpx1dat = gpx1con + 1; 57 58 gpf3con = ioremap(pdev->resource[2].start,4); 59 gpf3dat = gpf3con + 1; 60 61 62 writel((readl(gpx2con) & ~(0xf << 28)) | 1 << 28,gpx2con); 63 writel((readl(gpx1con) & ~(0xf << 0)) | 1 << 0,gpx1con); 64 writel((readl(gpf3con) & ~(0xff << 16)) | 0x11 << 16,gpf3con); 65 return 0; 66 } 67 68 int fs4412_platdrv_remove(struct platform_device *pdev) 69 { 70 device_destroy(cls,MKDEV(major,0)); 71 class_destroy(cls); 72 unregister_chrdev(major,"plat-led"); 73 printk("remove ok "); 74 return 0; 75 } 76 77 #if 0 78 struct platform_device_id idtbl = { 79 .name = "xxx1", 80 }; 81 #endif 82 83 struct platform_device_id idtbl[] = { 84 [0] = { 85 .name = "a", 86 }, 87 [1] = { 88 .name = "123", 89 }, 90 [2] = { 91 .name = "xxx1", 92 }, 93 }; 94 95 struct platform_driver pdrv = { 96 .driver = { 97 .name = "xxx", 98 }, 99 100 // .id_table = &idtbl, 101 .id_table = idtbl, 102 .probe = fs4412_platdrv_probe, 103 .remove = fs4412_platdrv_remove, 104 105 }; 106 107 #if 0 108 int fs4412_platdrv_init(void) 109 { 110 platform_driver_register(&pdrv); 111 return 0; 112 } 113 module_init(fs4412_platdrv_init); 114 115 void fs4412_platdrv_exit(void) 116 { 117 platform_driver_unregister(&pdrv); 118 return; 119 } 120 module_exit(fs4412_platdrv_exit); 121 #endif 122 module_platform_driver(pdrv); //接口代替了模块两要素 123 MODULE_LICENSE("GPL");
1 #include <linux/init.h> 2 #include <linux/module.h> 3 #include <linux/platform_device.h> 4 5 void fs4412_platdev_release(struct device *dev) 6 { 7 printk("release ok "); 8 } 9 10 struct resource res[] = { 11 { 12 .start = 0x11000c40, 13 .end = 0x11000c40 + 4 - 1, 14 .flags = IORESOURCE_MEM, 15 }, 16 17 { 18 .start = 0x11000c20, 19 .end = 0x11000c20 + 4 - 1, 20 .flags = IORESOURCE_MEM, 21 }, 22 23 { 24 .start = 0x114001e0, 25 .end = 0x114001e0 + 4 - 1, 26 .flags = IORESOURCE_MEM, 27 }, 28 }; 29 30 struct platform_device pdev = { 31 .name = "xxx1", 32 .dev = { 33 .release = fs4412_platdev_release, 34 }, 35 36 .resource = res, 37 }; 38 39 int fs4412_platdev_init(void) 40 { 41 platform_device_register(&pdev); 42 return 0; 43 } 44 module_init(fs4412_platdev_init); 45 46 void fs4412_platdev_exit(void) 47 { 48 platform_device_unregister(&pdev); 49 return; 50 } 51 module_exit(fs4412_platdev_exit); 52 MODULE_LICENSE("GPL");
1 ifeq ($(KERNELRELEASE),) 2 PWD = $(shell pwd) 3 KERNEL_DIR = /home/linux/linux-3.14/ 4 #KERNEL_DIR = /lib/modules/$(shell uname -r)/build/ 5 6 #start: 7 modules: 8 make -C $(KERNEL_DIR) M=$(PWD) modules 9 10 #end: 11 clean: 12 make -C $(KERNEL_DIR) M=$(PWD) clean 13 else 14 obj-m += dev.o drv.o 15 endif
设备树:
为什么出现设备树(故事源于Linux之父说过arm就是***):
1、最早期的驱动是硬件和逻辑写死的
2、当出现platform总线后将设备信息和驱动逻辑分离,为了提高驱动的通用性。
3、很多的设备代码会被放在arch/arm/mach-xxx或者plat-xxx目录下,每当更换平台时大部分的设备代码都需要修改,为了避免这种现象出现了设备树。
什么是设备树?
描述硬件信息的一种配置文件。
设备树的常见文件名:
xxx.dts 源文件
xxx.dtb 设备树二进制文件,这个文件的内存地址会以参数的形式传递给内核uImage
xxx.dtsi 设备树的头文件
exynos4412-fs4412.dts -》exynos4412.dtsi ——》exynos4x12.dtsi ——》exynos4.dtsi ——》skeleton.dtsi(包含)
设备树语法:节点、属性
所有节点都是基于根节点。
1 vi xxx.dtsi
2 /{
3 demo@地址1{ 正确的
4
5 };
6
7 demo@地址2{
8
9 };
10
11 标签:节点名
12 xxx:xxx{
13
14 };
15
16 };
17
18 demo1{ 错误的
19
20 };
21
22
23 vi yyy.dts
24 #include "xxx.dtsi"
/{
test{
属性 = <&xxx>;//说明test节点在引用xxx节点
}; <&标签名>;
};
属性:
默认属性、自定义属性(厂家自定义和自己定义)
1 #address-cells = <2>;当前节点的子节点中的reg属性有两个寄存器基地址
2 #size-cells = <1>;当前节点的子节点中的reg属性有1个寄存器偏移量
3
4 reg = <寄存器基地址1 寄存器偏移量1 寄存器基地址2 寄存器偏移量2 ...>;
5 compatible = ",",",",","; 用于和驱动匹配
6 compatible = "samsung,exynos4412","samsung,exynos4";
7
8
9 struct device_driver
10 {
11 const struct of_device_id *of_match_table;
12 }
13
14 struct of_device_id
15 {
16 char compatible[128];//数组的内容就是用来和设备树匹配的
17 }
led设备树信息:
1 xxx{
2 compatible = "fs4412,led";
3 reg = <0x11000c40 0x4 0x11000c20 0x4 0x114001e0 0x4>;
4 };
回到顶层目录执行make dtbs
cp arch/arm/boot/dts/exynos4412-fs4412.dtb /tftpboot
重启开发板