【版权申明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权)
我的第一个设备树驱动程序-驱动端
1. 设备树与驱动的匹配方式
设备树与驱动的匹配方式与之前的设备驱动总线的匹配方式有一点差别, 设备驱动总线最简单的匹配方式使用的是(驱动端)struct device_driver中的name和(设备端)struct platform_device中的name进行匹配. 在设备树中, 设备树与驱动的匹配方式为(设备树端)节点的compatible属性与(驱动端)struct platform_driver下的struct of_device_id下的.compatible属性进行匹配. 如下所示:
设备端的信息注册:
# 设备驱动总线: 设备端
static struct resource led_resource[] = {
[0] = {
.start = 0x120D6100,
.end = 0x120D6100 + 1 - 1,
.flags = IORESOURCE_MEM,
}
};
static struct platform_device led_dev = {
.name = "platform_led", // 与驱动匹配所用到的name
.id = -1,
.num_resources = ARRAY_SIZE(led_resource),
.resource = led_resource, // 设备的资源信息
.dev = {
.release = led_release,
},
};
# 设备树
led {
compatible = "ybk_led"; // 与驱动匹配所用到的compatible
reg = <0x120D6100 1>; // 设备的资源信息
};
可见设备树会简洁非常多!
驱动端的信息匹配:
# 同时适配设备驱动总线以及设备树
static struct of_device_id of_match_table = {
.compatible = "ybk_led", // 与设备树匹配所用到的compatible
.data = NULL, // 存放设备树的reg
};
struct platform_driver led_drv = {
.probe = led_probe,
.remove = led_remove,
.driver = {
.name = "platform_led", // 与设备驱动总线-设备匹配所用到的name
.of_match_table = &of_match_table, // 设备树匹配用到的结构体!
}
};
2. 驱动如何使用设备树中的设备资源
在probe()函数中添加如下代码同时适配设备驱动总线以及设备树:
int led_probe(struct platform_device *pdev)
{
struct resource *res = NULL;
unsigned int led_pin;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);// 设备驱动总线获取设备资源的方法
if (res) { //如果res不为空, 则表示设备驱动总线匹配成功.
led_pin = res->start;
}
else { // 若res为空, 则尝试通过设备树获取设备资源.
of_property_read_u8(pdev->dev.of_node, "reg", (u8 *)&led_pin);// 驱动端获取设备树设备资源的函数.
}
printk("led_pin = %d\n", led_pin);
......
}
如上所示, of_property_read_u8()是驱动端获取设备树设备资源的函数, 因为我在设备树中reg = <0x120D6100 1>;定义的寄存器大小为1(也就是一个字节, u8).
该函数在linux/of.h文件中被定义, 同时也有其他函数可供我们使用.
static inline int of_property_read_u8(const struct device_node *np,
const char *propname,
u8 *out_value)
{
return of_property_read_u8_array(np, propname, out_value, 1);
}
static inline int of_property_read_u16(const struct device_node *np,
const char *propname,
u16 *out_value)
{
return of_property_read_u16_array(np, propname, out_value, 1);
}
static inline int of_property_read_u32(const struct device_node *np,
const char *propname,
u32 *out_value)
{
return of_property_read_u32_array(np, propname, out_value, 1);
}
亲身经历告诉我, u8的资源我用of_property_read_u32()函数运行时会出现段错误.
3. 附上半完整的设备树文件(整个设备树太大), 以及驱动代码
设备树:
/dts-v1/;
#include "hi3516cv500.dtsi"
/ {
model = "Hisilicon HI3516CV500 DEMO Board";
compatible = "hisilicon,hi3516cv500";
memory {
device_type = "memory";
reg = <0x82000000 0x20000000>;
};
led {
compatible = "ybk_led";
reg = <0x120D6100 1>;
};
};
驱动:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/cdev.h>
#include <linux/device.h>
static dev_t dev_num = 0;
static struct cdev *cdevice = NULL;
static struct class *sys_class = NULL;
static struct device *class_device = NULL;
static volatile unsigned int *gpio_data;
int led_open(struct inode *pinode, struct file *pfile)
{
printk("[%s]\n", __func__);
return 0;
}
ssize_t led_write(struct file *pfile, const char __user *userbuf, size_t size, loff_t *loff)
{
int ret = -1;
int val = -1;
printk("[%s]\n", __func__);
//long copy_from_user(void *to, const void __user * from, unsigned long n)
ret = copy_from_user(&val, userbuf, size);
if(ret == 0 && val == 1){
printk("[%s] 1\n", __func__);
*gpio_data |= (1<<6);
} else{
printk("[%s] 0\n", __func__);
*gpio_data &= ~(1<<6);
}
return size;
}
ssize_t led_read(struct file *pfile, char __user *userbuf, size_t size, loff_t *loff)
{
printk("[%s]\n", __func__);
// long copy_to_user(void __user *to, const void *from, unsigned long n)
return 0;
}
static struct file_operations led_fops = {
.owner = THIS_MODULE,
.open = led_open,
.read = led_read,
.write = led_write
};
int led_probe(struct platform_device *pdev)
{
struct resource *res = NULL;
unsigned int led_pin;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res) {
led_pin = res->start;
}
else {
/* 获得reg属性 */
of_property_read_u8(pdev->dev.of_node, "reg", (u8 *)&led_pin);
}
printk("led_pin = %d\n", led_pin);
if (!led_pin) {
return -EINVAL;
}
gpio_data = (int *)ioremap(led_pin, 1);
printk("led_probe, found led\n");
cdevice = cdev_alloc();
cdev_init(cdevice, &led_fops);
alloc_chrdev_region(&dev_num, 0, 1, "yangbkLed");
cdev_add(cdevice, dev_num, 1);
sys_class = class_create(THIS_MODULE, "yangbkClass");
class_device = device_create(sys_class, NULL, dev_num, NULL, "yangbkDevice");
return 0;
}
int led_remove(struct platform_device *pdev)
{
printk("led_remove, remove led\n");
device_destroy(sys_class, dev_num);
class_destroy(sys_class);
unregister_chrdev_region(dev_num, 1);
cdev_del(cdevice);
iounmap(gpio_data);
return 0;
}
static struct of_device_id of_match_table = {
.compatible = "ybk_led",
.data = NULL,
};
struct platform_driver led_drv = {
.probe = led_probe,
.remove = led_remove,
.driver = {
.name = "idle",
.of_match_table = &of_match_table,
}
};
static int __init led_drv_init(void)
{
platform_driver_register(&led_drv);
return 0;
}
static void __exit led_drv_exit(void)
{
platform_driver_unregister(&led_drv);
}
module_init(led_drv_init);
module_exit(led_drv_exit);
MODULE_AUTHOR("yangbkGit");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("platform driver model.");
MODULE_ALIAS("model");