在总线设备驱动模型中,平台设备是写在c文件中。使用设备树时,平台设备事先并不存在,在dts文件中构造节点,节点里面含有资源。dts文件被编译成dtb文件,然后传递给内核。内核会解析dtb文件,得到一个个device_node,每个节点对应一个device_node结构体,每个device_node结构体变成一个platform_device结构体,该结构体中就含有资源,这些资源来源于dts文件。接下来的处理过程和总线设备驱动模型就一样了,如果设备与驱动相匹配,就调用驱动中的probe函数。可以这么认为,设备树是对总线设备驱动模型的一种改进,它仍然属于总线设备驱动模型的一种。
看一下这个设备树文件:
1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * SAMSUNG SMDK2440 board device tree source 4 * 5 * Copyright (c) 2018 [email protected] 6 * dtc -I dtb -O dts -o jz2440.dts jz2440.dtb 7 */ 8 //这里定义了一些宏,使用的是c语言的语法 9 #define S3C2410_GPA(_nr) ((0<<16) + (_nr)) 10 #define S3C2410_GPB(_nr) ((1<<16) + (_nr)) 11 #define S3C2410_GPC(_nr) ((2<<16) + (_nr)) 12 #define S3C2410_GPD(_nr) ((3<<16) + (_nr)) 13 #define S3C2410_GPE(_nr) ((4<<16) + (_nr)) 14 #define S3C2410_GPF(_nr) ((5<<16) + (_nr)) 15 #define S3C2410_GPG(_nr) ((6<<16) + (_nr)) 16 #define S3C2410_GPH(_nr) ((7<<16) + (_nr)) 17 #define S3C2410_GPJ(_nr) ((8<<16) + (_nr)) 18 #define S3C2410_GPK(_nr) ((9<<16) + (_nr)) 19 #define S3C2410_GPL(_nr) ((10<<16) + (_nr)) 20 #define S3C2410_GPM(_nr) ((11<<16) + (_nr)) 21 22 /dts-v1/; 23 24 / { 25 model = "SMDK24440"; 26 compatible = "samsung,smdk2440"; 27 28 #address-cells = <1>; 29 #size-cells = <1>; 30 31 memory@30000000 { 32 device_type = "memory"; 33 reg = <0x30000000 0x4000000>; 34 }; 35 /* 36 cpus { 37 cpu { 38 compatible = "arm,arm926ej-s"; 39 }; 40 }; 41 */ 42 chosen { //设置了内核的命令行参数 43 bootargs = "noinitrd root=/dev/mtdblock4 rw init=/linuxrc console=ttySAC0,115200"; 44 }; 45 46 47 led { 48 compatible = "jz2440_led"; //以后就使用compatible在内核里面找到能够支持这个节点的驱动程序,即找到能够支持这个节点的平台drv. 49 reg = <S3C2410_GPF(5) 1>;//reg是register的缩写。在arm系统里面,寄存器和内存是被同样对待的,因为寄存器的访问空间与内存的访问空间没什么差别。 50 }; 51 };
单板启动之后,
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,本意就是寄存器的大小。
在总线设备驱动模型中,是通过名字来进行设备与驱动的匹配。那么在利用设备树时,是利用什么进行匹配的呢。
1 static int platform_match(struct device *dev, struct device_driver *drv) 2 { 3 struct platform_device *pdev = to_platform_device(dev); 4 struct platform_driver *pdrv = to_platform_driver(drv); 5 6 /* Attempt an OF style match first */ 7 if (of_driver_match_device(dev, drv))//用来判断从设备树中得到的platform_devcie与提供的platform_drv
//是否匹配, 8 return 1; 9 10 /* Then try to match against the id table */ 11 if (pdrv->id_table) 12 return platform_match_id(pdrv->id_table, pdev) != NULL; 13 14 /* fall-back to driver name match */ 15 return (strcmp(pdev->name, drv->name) == 0); 16 }
1 static inline int of_driver_match_device(struct device *dev, 2 const struct device_driver *drv) 3 {
/*从这里可以看出,平台drv中有个成员变量,of_match_table*/ 4 return of_match_device(drv->of_match_table, dev) != NULL; 5 }
1 struct device_driver { 2 const char *name; 3 struct bus_type *bus; 4 5 struct module *owner; 6 const char *mod_name; /* used for built-in modules */ 7 8 bool suppress_bind_attrs; /* disables bind/unbind via sysfs */ 9 10 const struct of_device_id *of_match_table; //该指针指向一项或多项of_device_id 11 12 int (*probe) (struct device *dev); 13 int (*remove) (struct device *dev); 14 void (*shutdown) (struct device *dev); 15 int (*suspend) (struct device *dev, pm_message_t state); 16 int (*resume) (struct device *dev); 17 const struct attribute_group **groups; 18 19 const struct dev_pm_ops *pm; 20 21 struct driver_private *p; 22 };
1 struct of_device_id 2 { 3 char name[32]; 4 char type[32]; 5 char compatible[128];//从dts里得到的platform_device里有compatible属性,两者比较,如果一样的话,就是匹配。 6 #ifdef __KERNEL__ 7 void *data; 8 #else 9 kernel_ulong_t data; 10 #endif 11 };
1 struct platform_device { 2 const char * name; 3 int id; 4 struct device dev;//对于dts生成的设备platform_device,这里含有Of_node,of_node中含有属性,这含有哪些属性呢,这取决于设备树,比如:
//compatible pin等。这个compatible属性用来寻找支持它的platform_driver.这个compatible是最优先比较。 5 u32 num_resources; 6 struct resource * resource; 7 8 const struct platform_device_id *id_entry; 9 10 /* MFD cell pointer */ 11 struct mfd_cell *mfd_cell; 12 13 /* arch specific additions */ 14 struct pdev_archdata archdata; 15 };
利用设备树编写的led驱动程序如下:
1 #include <linux/module.h> 2 #include <linux/kernel.h> 3 #include <linux/fs.h> 4 #include <linux/init.h> 5 #include <linux/delay.h> 6 #include <linux/uaccess.h> 7 #include <asm/irq.h> 8 #include <asm/io.h> 9 #include <linux/of.h> 10 #include <linux/of_device.h> 11 #include <linux/of_platform.h> 12 #include <linux/platform_device.h> 13 14 #define S3C2440_GPA(n) (0<<16 | n) 15 #define S3C2440_GPB(n) (1<<16 | n) 16 #define S3C2440_GPC(n) (2<<16 | n) 17 #define S3C2440_GPD(n) (3<<16 | n) 18 #define S3C2440_GPE(n) (4<<16 | n) 19 #define S3C2440_GPF(n) (5<<16 | n) 20 #define S3C2440_GPG(n) (6<<16 | n) 21 #define S3C2440_GPH(n) (7<<16 | n) 22 #define S3C2440_GPI(n) (8<<16 | n) 23 #define S3C2440_GPJ(n) (9<<16 | n) 24 25 static int led_pin; 26 static volatile unsigned int *gpio_con; 27 static volatile unsigned int *gpio_dat; 28 29 /* 123. 分配/设置/注册file_operations 30 * 4. 入口 31 * 5. 出口 32 */ 33 34 static int major; 35 static struct class *led_class; 36 37 static unsigned int gpio_base[] = { 38 0x56000000, /* GPACON */ 39 0x56000010, /* GPBCON */ 40 0x56000020, /* GPCCON */ 41 0x56000030, /* GPDCON */ 42 0x56000040, /* GPECON */ 43 0x56000050, /* GPFCON */ 44 0x56000060, /* GPGCON */ 45 0x56000070, /* GPHCON */ 46 0, /* GPICON */ 47 0x560000D0, /* GPJCON */ 48 }; 49 50 static int led_open (struct inode *node, struct file *filp) 51 { 52 /* 把LED引脚配置为输出引脚 */ 53 /* GPF5 - 0x56000050 */ 54 int bank = led_pin >> 16; 55 int base = gpio_base[bank]; 56 57 int pin = led_pin & 0xffff; 58 gpio_con = ioremap(base, 8); 59 if (gpio_con) { 60 printk("ioremap(0x%x) = 0x%x\n", base, gpio_con); 61 } 62 else { 63 return -EINVAL; 64 } 65 66 gpio_dat = gpio_con + 1; 67 68 *gpio_con &= ~(3<<(pin * 2)); 69 *gpio_con |= (1<<(pin * 2)); 70 71 return 0; 72 } 73 74 static ssize_t led_write (struct file *filp, const char __user *buf, size_t size, loff_t *off) 75 { 76 /* 根据APP传入的值来设置LED引脚 */ 77 unsigned char val; 78 int pin = led_pin & 0xffff; 79 80 copy_from_user(&val, buf, 1); 81 82 if (val) 83 { 84 /* 点灯 */ 85 *gpio_dat &= ~(1<<pin); 86 } 87 else 88 { 89 /* 灭灯 */ 90 *gpio_dat |= (1<<pin); 91 } 92 93 return 1; /* 已写入1个数据 */ 94 } 95 96 static int led_release (struct inode *node, struct file *filp) 97 { 98 printk("iounmap(0x%x)\n", gpio_con); 99 iounmap(gpio_con); 100 return 0; 101 } 102 103 104 static struct file_operations myled_oprs = { 105 .owner = THIS_MODULE, 106 .open = led_open, 107 .write = led_write, 108 .release = led_release, 109 }; 110 111 112 static int led_probe(struct platform_device *pdev) 113 { 114 struct resource *res; 115 116 /* 根据platform_device的资源进行ioremap */ 117 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 118 led_pin = res->start; 119 120 major = register_chrdev(0, "myled", &myled_oprs); 121 122 led_class = class_create(THIS_MODULE, "myled"); 123 device_create(led_class, NULL, MKDEV(major, 0), NULL, "led"); /* /dev/led */ 124 125 return 0; 126 } 127 128 static int led_remove(struct platform_device *pdev) 129 { 130 unregister_chrdev(major, "myled"); 131 device_destroy(led_class, MKDEV(major, 0)); 132 class_destroy(led_class); 133 134 return 0; 135 } 136 137 138 static const struct of_device_id of_match_leds[] = { 139 { .compatible = "jz2440_led", .data = NULL }, 140 { /* sentinel */ } 141 }; 142 143 144 struct platform_driver led_drv = { 145 .probe = led_probe, 146 .remove = led_remove, 147 .driver = { 148 .name = "myled", 149 .of_match_table = of_match_leds, /* 能支持哪些来自于dts的platform_device */ 150 } 151 }; 152 153 154 static int myled_init(void) 155 { 156 platform_driver_register(&led_drv); 157 return 0; 158 } 159 160 static void myled_exit(void) 161 { 162 platform_driver_unregister(&led_drv); 163 } 164 165 166 167 module_init(myled_init); 168 module_exit(myled_exit); 169 170 171 MODULE_LICENSE("GPL");
在设备树中形如下面这样的写法太别扭了
reg = <S3C2410_GPF(5) 1>;
S3C2410_GPF(5)这个地方本来定义了引脚,你非要把它当做register。看一下能否将register去掉,能不能使用另外一种方法指定引脚。
1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * SAMSUNG SMDK2440 board device tree source 4 * 5 * Copyright (c) 2018 [email protected] 6 * dtc -I dtb -O dts -o jz2440.dts jz2440.dtb 7 */ 8 9 #define S3C2410_GPA(_nr) ((0<<16) + (_nr)) 10 #define S3C2410_GPB(_nr) ((1<<16) + (_nr)) 11 #define S3C2410_GPC(_nr) ((2<<16) + (_nr)) 12 #define S3C2410_GPD(_nr) ((3<<16) + (_nr)) 13 #define S3C2410_GPE(_nr) ((4<<16) + (_nr)) 14 #define S3C2410_GPF(_nr) ((5<<16) + (_nr)) 15 #define S3C2410_GPG(_nr) ((6<<16) + (_nr)) 16 #define S3C2410_GPH(_nr) ((7<<16) + (_nr)) 17 #define S3C2410_GPJ(_nr) ((8<<16) + (_nr)) 18 #define S3C2410_GPK(_nr) ((9<<16) + (_nr)) 19 #define S3C2410_GPL(_nr) ((10<<16) + (_nr)) 20 #define S3C2410_GPM(_nr) ((11<<16) + (_nr)) 21 22 /dts-v1/; 23 24 / { 25 model = "SMDK24440"; 26 compatible = "samsung,smdk2440"; 27 28 #address-cells = <1>; 29 #size-cells = <1>; 30 31 memory@30000000 { 32 device_type = "memory"; 33 reg = <0x30000000 0x4000000>; 34 }; 35 /* 36 cpus { 37 cpu { 38 compatible = "arm,arm926ej-s"; 39 }; 40 }; 41 */ 42 chosen { 43 bootargs = "noinitrd root=/dev/mtdblock4 rw init=/linuxrc console=ttySAC0,115200"; 44 }; 45 46 47 led { 48 compatible = "jz2440_led"; 49 pin = <S3C2410_GPF(5)>; 50 }; 51 };
1 #include <linux/module.h> 2 #include <linux/kernel.h> 3 #include <linux/fs.h> 4 #include <linux/init.h> 5 #include <linux/delay.h> 6 #include <linux/uaccess.h> 7 #include <asm/irq.h> 8 #include <asm/io.h> 9 #include <linux/of.h> 10 #include <linux/of_device.h> 11 #include <linux/of_platform.h> 12 #include <linux/platform_device.h> 13 14 #define S3C2440_GPA(n) (0<<16 | n) 15 #define S3C2440_GPB(n) (1<<16 | n) 16 #define S3C2440_GPC(n) (2<<16 | n) 17 #define S3C2440_GPD(n) (3<<16 | n) 18 #define S3C2440_GPE(n) (4<<16 | n) 19 #define S3C2440_GPF(n) (5<<16 | n) 20 #define S3C2440_GPG(n) (6<<16 | n) 21 #define S3C2440_GPH(n) (7<<16 | n) 22 #define S3C2440_GPI(n) (8<<16 | n) 23 #define S3C2440_GPJ(n) (9<<16 | n) 24 25 static int led_pin; 26 static volatile unsigned int *gpio_con; 27 static volatile unsigned int *gpio_dat; 28 29 /* 123. 分配/设置/注册file_operations 30 * 4. 入口 31 * 5. 出口 32 */ 33 34 static int major; 35 static struct class *led_class; 36 37 static unsigned int gpio_base[] = { 38 0x56000000, /* GPACON */ 39 0x56000010, /* GPBCON */ 40 0x56000020, /* GPCCON */ 41 0x56000030, /* GPDCON */ 42 0x56000040, /* GPECON */ 43 0x56000050, /* GPFCON */ 44 0x56000060, /* GPGCON */ 45 0x56000070, /* GPHCON */ 46 0, /* GPICON */ 47 0x560000D0, /* GPJCON */ 48 }; 49 50 static int led_open (struct inode *node, struct file *filp) 51 { 52 /* 把LED引脚配置为输出引脚 */ 53 /* GPF5 - 0x56000050 */ 54 int bank = led_pin >> 16; 55 int base = gpio_base[bank]; 56 57 int pin = led_pin & 0xffff; 58 gpio_con = ioremap(base, 8); 59 if (gpio_con) { 60 printk("ioremap(0x%x) = 0x%x\n", base, gpio_con); 61 } 62 else { 63 return -EINVAL; 64 } 65 66 gpio_dat = gpio_con + 1; 67 68 *gpio_con &= ~(3<<(pin * 2)); 69 *gpio_con |= (1<<(pin * 2)); 70 71 return 0; 72 } 73 74 static ssize_t led_write (struct file *filp, const char __user *buf, size_t size, loff_t *off) 75 { 76 /* 根据APP传入的值来设置LED引脚 */ 77 unsigned char val; 78 int pin = led_pin & 0xffff; 79 80 copy_from_user(&val, buf, 1); 81 82 if (val) 83 { 84 /* 点灯 */ 85 *gpio_dat &= ~(1<<pin); 86 } 87 else 88 { 89 /* 灭灯 */ 90 *gpio_dat |= (1<<pin); 91 } 92 93 return 1; /* 已写入1个数据 */ 94 } 95 96 static int led_release (struct inode *node, struct file *filp) 97 { 98 printk("iounmap(0x%x)\n", gpio_con); 99 iounmap(gpio_con); 100 return 0; 101 } 102 103 104 static struct file_operations myled_oprs = { 105 .owner = THIS_MODULE, 106 .open = led_open, 107 .write = led_write, 108 .release = led_release, 109 }; 110 111 112 static int led_probe(struct platform_device *pdev) 113 { 114 struct resource *res; 115 116 /* 根据platform_device的资源进行ioremap */ 117 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 118 if (res) { 119 led_pin = res->start; 120 } 121 else { 122 /* 获得pin属性 */ 123 of_property_read_s32(pdev->dev.of_node, "pin", &led_pin); 124 } 125 126 if (!led_pin) 127 { 128 printk("can not get pin for led\n"); 129 return -EINVAL; 130 } 131 132 133 major = register_chrdev(0, "myled", &myled_oprs); 134 135 led_class = class_create(THIS_MODULE, "myled"); 136 device_create(led_class, NULL, MKDEV(major, 0), NULL, "led"); /* /dev/led */ 137 138 return 0; 139 } 140 141 static int led_remove(struct platform_device *pdev) 142 { 143 unregister_chrdev(major, "myled"); 144 device_destroy(led_class, MKDEV(major, 0)); 145 class_destroy(led_class); 146 147 return 0; 148 } 149 150 151 static const struct of_device_id of_match_leds[] = { 152 { .compatible = "jz2440_led", .data = NULL }, 153 { /* sentinel */ } 154 }; 155 156 157 struct platform_driver led_drv = { 158 .probe = led_probe, 159 .remove = led_remove, 160 .driver = { 161 .name = "myled", 162 .of_match_table = of_match_leds, /* 能支持哪些来自于dts的platform_device */ 163 } 164 }; 165 166 167 static int myled_init(void) 168 { 169 platform_driver_register(&led_drv); 170 return 0; 171 } 172 173 static void myled_exit(void) 174 { 175 platform_driver_unregister(&led_drv); 176 } 177 178 179 180 module_init(myled_init); 181 module_exit(myled_exit); 182 183 184 MODULE_LICENSE("GPL");标签:led,驱动程序,driver,platform,树来,device,include,struct From: https://www.cnblogs.com/kn-zheng/p/17105582.html