设备树是什么_devicetree[通俗易懂]

设备树是什么_devicetree[通俗易懂]设备树浅谈led一、设备树是什么?二、自己写一个简单的LED驱动配合自己的treeinit:注册driver到platformexit:从platform卸载driverled_drvled

设备树是什么_devicetree[通俗易懂]"

一、设备树是什么?


除去官方的定义,我学完了设备树(中断那儿)还没有深入了解,我觉得设备树就是device的另外一种描述,总的来看还是platform下的东西,之前我们的设备资源是放在platform的下的device中的,用了设备树也还是,唯一的区别就是什么生成了platform下的设备节点,原来我们用的是device.c去device_create去生成节点,用的是.name去匹配,现在用的是dtb去指定描述,bootloader传给内核,最后也是一样的在platform下去生成相应的device,用compatible去匹配,其实都一样,只是设备树更直观,更简洁,比如你如果指定10个device都需要修改资源的时候,难道要去改10个.c吗,设备树文件一个dts全部搞定,而且设备树的语法也很简单,所以还是很建议的,但是学完明白一个道理,设备树好不好,还是看驱动支持到不到位,你毕竟只是去指定资源,人家驱动用不用是人家的事,所以驱动还是得撸着走啊!*
.
.
.

二、自己写一个简单的LED驱动配合自己的tree


自己写一个驱动那不肯定就可以随心所欲嘛,自己用设备树提供资源,再用自己写的driver去获取,刚好也练练驱动,就写个灯的驱动,也试试不在windows下面写,直接在服务器上用vim去写一写,刚学了手大佬的操作,以前都是SI。


首先理一理自己每一步要干嘛,以Module形式那就肯定有module_init,module_exit.

因为是以模块的形式加载的,所以第一肯定是这两个 涉及到insmod rmmod

init: 注册driver到platform

module_init(myled_init);

static int myled_init(void)
{ 
   
	platform_driver_register(&led_drv);     
	return 0;
}

理解:向平台注册驱动 因为我们采用的是platform的方式,那么不管设备信息device是谁提供怎么提供,驱动必须是我们自己注册进去,所以第一步肯定是向平台注册我们的驱动



exit: 从platform卸载driver

module_exit(myled_exit);
static void myled_exit(void)
{ 
   
	platform_driver_unregister(&led_drv);    
}

理解:既然是以Module形式加载那么肯定是可加载也就希望可卸载 不然不如直接编译进内核 所以退出的时候还是要卸载掉驱动




led_drv

这个就是我们的核心了,后续的都是围绕这个展开,我们来一探究竟

struct platform_driver led_drv = { 
   
	.probe		= led_probe,
	.remove		= led_remove,
	.driver		= { 
   
		.name	= "myled",
		.of_match_table = of_match_leds, /* 能支持哪些来自于dts的platform_device */
	}
};

这个函数看了一下首先是一个标准的platform_driver类型,因为必须是这样才符合上一层调用的规范,所以我们自己写这些驱动时一定要多参考参考内核里的驱动的写法.这3个成员函数都很重要。

static const struct of_device_id of_match_leds[] = { 
   
	{ 
    .compatible = "jz2440_led", .data = NULL },
	{ 
    /* sentinel */ }
};

这个就是设备树匹配这个驱动的关键,.compatible跟设备树中对应device保持一致,这个地方就是设备和驱动match的关键。

.
.
.

led_probe:

static int led_probe(struct platform_device *pdev)
{ 
   
	struct resource		*res;

	/* 根据platform_device的资源进行ioremap */
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	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(0, "myled", &myled_oprs);

	led_class = class_create(THIS_MODULE, "myled");
	device_create(led_class, NULL, MKDEV(major, 0), NULL, "led"); /* /dev/led */
	
	return 0;
}

首先probe的重要性不言而喻,probe是当platform_device和platform_driver匹配时,调用的函数,很多核心的东西,先是获取设备资源,能到probe说明设备已经找到了并且是匹配的,然后这里其实先是用platform_dev所拥有的IORESOURCE_MEM资源去获取,如果获取不到然后就是我们的设备树了,这里我反正自己更喜欢设备树的方式,更直接明了。
而且这里我们能看见去of_node里去获取,当然这里的of_node也是属于pdev的,有人可能也疑惑pdev我们这里没赋值这个函数指针怎么传进来的呢,又怎么是我们想要的呢,那当然啊,match过的!肯定是你喜欢的那个,哈哈。然后就是要个设备号,先是自动生成一个major,再设定次设备号为0,MKDEV合成一个符合调用要求的,调用函数能认识的数据,我自己称它为总设备号吧,然后创建类,在类下面创建设备。

这里可以看见其实ops是通过主设备号绑的device,这里我猜测如果是主设备号相同,其实次设备号不同的device我创建几个,应该是可以使用这个驱动的。只是如果那我同时操作这些设备时会发生什么呢。下次问问超哥。

myled_oprs

这其实就是驱动的核心了,file_operations结构体的填充实现。
.

static struct file_operations myled_oprs = { 
   
	.owner = THIS_MODULE,
	.open  = led_open,
	.write = led_write,
	.release = led_release,
};

open release 一般是硬件资源的一些初始化在这里做
这里就是做了一些IO资源的分配处理
因为我们这里是用了MMU的,所以操作的都是虚拟地址,所以有一个物理到虚拟地址的映射,gpio寄存器的配置

static int led_open (struct inode *node, struct file *filp)
{ 
   
	/* 把LED引脚配置为输出引脚 */
	/* GPF5 - 0x56000050 */
	int bank = led_pin >> 16;
	int base = gpio_base[bank];

	int pin = led_pin & 0xffff;
	gpio_con = ioremap(base, 8);
	if (gpio_con) { 
   
		printk("ioremap(0x%x) = 0x%x\n", base, gpio_con);
	}
	else { 
   
		return -EINVAL;
	}
	
	gpio_dat = gpio_con + 1;

	*gpio_con &= ~(3<<(pin * 2));
	*gpio_con |= (1<<(pin * 2));  

	return 0;
}
static int led_release (struct inode *node, struct file *filp)
{ 
   
	printk("iounmap(0x%x)\n", gpio_con);
	iounmap(gpio_con);
	return 0;
}

.
.
.

write read就是涉及一些对硬件的控制操作或者底层数据的读取,也很简单

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, 1);

	if (val)
	{ 
   
		/* 点灯 */
		*gpio_dat &= ~(1<<pin);
	}
	else
	{ 
   
		/* 灭灯 */
		*gpio_dat |= (1<<pin);
	}

	return 1; /* 已写入1个数据 */
}

.
.
.

led_remove

static int led_remove(struct platform_device *pdev)
{ 
   
	unregister_chrdev(major, "myled");
	device_destroy(led_class,  MKDEV(major, 0));
	class_destroy(led_class);
	
	return 0;
}

.remove很简单,就是当驱动卸载时或者设备移除时,它会卸载或者说删除probe中的设备节点,因为当你卸载掉设备或者驱动任何一方时,就像match是device和driver相亲,成功调用probe结婚,在probe中创建的设备结点就像是结婚证,离婚的时候,也是双方的事情。所以很多人疑惑为什么release了还要remove,因为release是一般用来释放资源(结婚时用到的),remove就是去删除结婚证,告诉用户,已经离婚了,这些资源已经没了,设备也不能使用了。

‘’


总结

总结就是今天大概复习了一下platform下驱动与平台与设备的关系,理了一个以后写驱动大体的框架,后面一章准备掌握一下设备树的写法,分析一下JZ2440的设备树文件的每一个部分

今天的文章设备树是什么_devicetree[通俗易懂]分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/72200.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注