上一篇文章学习了字符设备的注册,操作过的小伙伴都知道上一篇文章中测试驱动时是通过手动创建设备节点的,现在开始学习怎么自动挂载设备节点和设备树信息的获取,这篇文章中的源码将会是我以后编写字符驱动的模板。

一、准备材料

开发环境:VMware

操作系统:ubuntu

开发版:湃兔i2S-6UB

库文件:linux开发板或ubuntu的内核源码

二、自动创建设备节点

需要用到的头文件 #include <linux/device.h>,需要用到的函数如下所示

struct class *class_create(owner, name)
void class_destroy(struct class *class)
struct device *device_create(struct class *class, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...)
void device_destroy(struct class *class, dev_t devt);

owner: THIS_MODULE

name: 设备节点的名称(也就是/dev目录下的文件名)

class:类

parent:NULL

devt:设备号

drvdata:NULL

fmt:设备节点的名称

三、获取设备树信息

为了帮助像我一样才接触linux驱动,对设备树不是很理解的小伙伴,所系这里就不对设备树进行详细的介绍。可以将设备树简单的理解为,设备树的存在是方便linux内核研究人员专心的研究内核的功能,通过设备树将板载的描述文件和内核分开,使得内核文件不在臃肿。有需要的小伙伴可以了解Device Tree

设备树文件在内核源码的“arch/arm/boot/dts”目录下,设备树的描述文件是'.dtsi',每个开发板对应的文件不同,比如我的开发板的描述文件是i2c6ulxb-i2s6ull-emmc.dtsi,打开可以看到的信息如图所示:



在这里我就不对设备进行更改了,我对backlight节点信息进行读取,有需要了解设备树语法的小伙伴可以了解Linux设备树语法详解

我在驱动中读取设备树的主要函数有以下几个,想了解更多of函数的小伙伴可以了解linux设备树常用of操作函数

inline struct device_node *of_find_node_by_path(const char *path)
int of_property_read_string(struct device_node *nd, const char *propname, const char *out_string);
int of_property_read_u32_array(struct device_node *nd, const char *propname, u32 *out_value)

path:带有全路径的节点名,可以使用节点的别名

np:设备节点

proname 要读取的属性名字

out_string:读取到的字符串值

out_value:读取到的数组值

通过这几个函数,就可以将设备树种的信息的读取出来了,接下载看源码

四、程序源码

驱动文件chrdevtemp.c

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h> #define CHRDEVTEMP_NAME "chrdevtemp"
#define CHRDEVTEMP_COUNT 1 /* 设备结构体 */
struct chrtemp_dev{
dev_t devid; /* 设备号 */
int major; /* 主设备号 */
int minor; /* 次设备号 */
struct cdev cdev; /* 字符设备 */
struct class *class; /* 类结构体 */
struct device *device; /* 设备 */
struct device_node *nd; /* 设备节点 */
int gpio_number; /*gpio的编号*/
}; struct chrtemp_dev chrdevtemp; static char readbuf[100];
static char writebuf[100];
static char kerneldata[] = {"hello This is the kernel data"}; static int chrdevtemp_open(struct inode *inode, struct file *filp)
{
filp->private_data = &chrdevtemp;
return 0;
} static int chrdevtemp_release(struct inode *inode, struct file *filp)
{
return 0;
} static ssize_t chrdevtemp_read(struct file *filp, __user char *buf, size_t count, loff_t *ppos)
{
int ret = 0;
memcpy(readbuf, kerneldata, sizeof(kerneldata));
ret = copy_to_user(buf, readbuf, count);
if (ret == 0) { } else { } return 0;
} static ssize_t chrdevtemp_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos)
{
int ret = 0; ret = copy_from_user(writebuf, buf, count);
if (ret == 0) {
printk("kernel recevdata:%s\r\n", writebuf);
} else { } return 0;
} /*
* 字符设备操作集合
*/
static const struct file_operations chrdevtemp_fops = {
.owner = THIS_MODULE,
.open = chrdevtemp_open,
.release = chrdevtemp_release,
.read = chrdevtemp_read,
.write = chrdevtemp_write,
}; /*
* 模块入口
*/
static int __init chrdevtemp_init(void)
{
int ret = 0;
const char *str;
u32 brightness[8];
u8 i; printk("chrdevtemp_init\r\n"); /* 申请设备号 */
chrdevtemp.major = 0; /* 设置设备号由内存分配 */
if (chrdevtemp.major){
chrdevtemp.devid = MKDEV(chrdevtemp.major, 0);
ret = register_chrdev_region(chrdevtemp.devid, CHRDEVTEMP_COUNT, CHRDEVTEMP_NAME);
} else {
ret = alloc_chrdev_region(&chrdevtemp.devid, 0, CHRDEVTEMP_COUNT, CHRDEVTEMP_NAME);
chrdevtemp.major = MAJOR(chrdevtemp.devid);
chrdevtemp.minor = MINOR(chrdevtemp.devid);
}
if (ret < 0) {
printk("chrdevtemp chrdev_region err!\r\n");
goto fail_devid;
} /* 注册字符设备 */
chrdevtemp.cdev.owner = chrdevtemp_fops.owner;
cdev_init(&chrdevtemp.cdev, &chrdevtemp_fops);
ret = cdev_add(&chrdevtemp.cdev, chrdevtemp.devid, CHRDEVTEMP_COUNT);
if (ret < 0) {
goto fail_cdev;
} /* 自动创建设备节点 */
chrdevtemp.class = class_create(THIS_MODULE, CHRDEVTEMP_NAME);
if (IS_ERR(chrdevtemp.class)) {
ret = PTR_ERR(chrdevtemp.class);
goto fail_class;
} chrdevtemp.device = device_create(chrdevtemp.class, NULL, chrdevtemp.devid, NULL, CHRDEVTEMP_NAME);
if (IS_ERR(chrdevtemp.device)) {
ret = PTR_ERR(chrdevtemp.device);
goto fail_device;
} /* 获取设备树的属性内容 */
chrdevtemp.nd = of_find_node_by_path("/backlight");
if (chrdevtemp.nd == NULL) {
ret = -EINVAL;
goto fail_findnd;
} /* 获取字符串属性 */
ret = of_property_read_string(chrdevtemp.nd, "compatible", &str);
if (ret < 0) {
goto fail_rs;
} else {
printk("status is: %s\r\n", str);
} /* 获取数组 */
ret = of_property_read_u32_array(chrdevtemp.nd, "brightness-levels", brightness, 8);
if (ret < 0) {
goto fail_rs;
} else {
printk("brightness-levels: ");
for(i = 0; i < 8; i++){
printk("%d ", brightness[i]);
}
printk("\r\n");
} return 0; fail_rs:
fail_findnd:
device_destroy(chrdevtemp.class, chrdevtemp.devid);
fail_device:
class_destroy(chrdevtemp.class);
fail_class:
cdev_del(&chrdevtemp.cdev);
fail_cdev:
unregister_chrdev_region(chrdevtemp.devid, CHRDEVTEMP_COUNT);
fail_devid:
return ret; } /*
* 模块出口
*/
static void __exit chrdevtemp_exit(void)
{
printk("chrdevtemp_exit\r\n"); /* 删除字符设备 */
cdev_del(&chrdevtemp.cdev); /* 释放字符设号 */
unregister_chrdev_region(chrdevtemp.devid, CHRDEVTEMP_COUNT); /* 摧毁设备 */
device_destroy(chrdevtemp.class, chrdevtemp.devid); /* 摧毁类 */
class_destroy(chrdevtemp.class);
} /*
* 模块注册入口
*/
module_init(chrdevtemp_init);
/*
* 模块注册出口
*/
module_exit(chrdevtemp_exit); MODULE_LICENSE("GPL");
MODULE_AUTHOR("jiaozhu");

Makefile文件

KERNELDIR := /home/xfg/linux/imx_7ull/i2x_6ub/system_file/i2SOM-iMX-Linux

CURRENT_PATH := $(shell pwd)
obj-m := chrdevtemp.o build: kernel_modules kernel_modules:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

测试程序需就使用之前编写的hello2App.c文件

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h> /*
*argc:应用程序参数个数
*argv[]:具体的参数内容,字符串形式
*./hello2App <filename> <1:2> 1表示读,2表示写
*./hello2App /dev/hello2 1 表示从驱动里面读数据
*./hello2App /dev/hello2 2 表示向驱动里面写数据
* */ int main(int argc, char *argv[])
{
int ret = 0;
int fd = 0;
char *filename;
char readbuf[100], writebuf[100];
static char usrdata[] = {"hell0 This is user data!"}; if(argc !=3) {
printf("Instruction usage error!!!\r\n");
printf("./helle2App <filename> <1:2> 1表示读,2表示写\r\n");
printf("./hello2App ./dev/hello2 1 \r\n");
return -1;
} filename = argv[1]; fd = open(filename, O_RDWR);
if(fd < 0) { } if(atoi(argv[2]) ==1){
ret = read(fd, readbuf, 50);
if(ret <0) {
printf("read file %s failed!\r\n", filename);
} else {
printf("App read data:%s\r\n", readbuf);
}
} if(atoi(argv[2]) == 2) {
memcpy(writebuf, usrdata, sizeof(usrdata));
ret = write(fd,writebuf, 50);
if(ret <0) {
printf("write file %s failed\r\n", filename);
} else { }
} ret =close(fd);
if(ret <0) {
printf("close file %s falied!\r\n", filename);
} return 0;
}

五、测试

将驱动文件和应用文件进行编译

make
arm-linux-gnueabihf-gcc

将编译后的驱动文件可应用文件拷贝到开发板中,然后加载驱动,结果如下图所示:



将读取的信息和设备树文件中的信息对比,说明读取成功。

通过应用读取设备节点,测试设备节点是否加载成功,结果如下图所示:



可知通过自动创界设备节点成功。

六、问题

1.自动创建设备节点时出现如下错误



解决办法:此错误是因为我运行的内核版本和写驱动是用的内核版本不一致导致,只需要控制版本一致后从新烧写内核文件即可。

参考文献

Device Tree:http://www.wowotech.net/device_model/why-dt.html

Linux设备树语法详解:https://www.cnblogs.com/xiaojiang1025/p/6131381.html

linux设备树常用of操作函数:https://blog.csdn.net/wang_518/article/details/108923399

linux驱动之获取设备树信息的更多相关文章

  1. Linux 获取设备树源文件(DTS)里描述的资源

    Linux 获取设备树源文件(DTS)里的资源 韩大卫@吉林师范大学 在linux使用platform_driver_register() 注册 platform_driver 时, 需要在 plat ...

  2. Linux 获取设备树源文件(DTS)里的资源【转】

    本文转载自:http://blog.csdn.net/keleming1/article/details/51036000 http://www.cnblogs.com/dyllove98/archi ...

  3. Linux kernel 有关 spi 设备树参数解析

    一.最近做了一个 spi 设备驱动从板级设备驱动升级到设备树设备驱动,这其中要了解 spi 设备树代码的解析. 二. 设备树配置如下: 503 &spi0 { 504 status = &qu ...

  4. I.MX6 linux eGalaxTouch 自动获取设备节点

    I.MX6 linux eGalaxTouch 自动获取设备节点 \\\\\\\\\\\\\\-*- 目录 -*-///////////// | 一. 需求: | 二. /proc/bus/input ...

  5. Linux 获取设备树源文件(DTS)里描述的资源【转】

    转自:http://www.linuxidc.com/Linux/2013-07/86839.htm 转自:http://blog.sina.com.cn/s/blog_636a55070101mce ...

  6. Linux 总线、设备、驱动模型 与 设备树

    1.总线.设备.驱动模型 本着高内聚.低耦合的原则,Linux 把设备驱动模型分为了总线.设备和驱动三个实体,这三个实体在内核里的职责分别如下: 设备和驱动向总线进行注册,总线负责把设备和对应的驱动绑 ...

  7. ARM Linux 3.x的设备树(Device Tree)

    http://blog.csdn.net/21cnbao/article/details/8457546 宋宝华 Barry Song <21cnbao@gmail.com> 1.     ...

  8. ARM Linux 3.x的设备树(Device Tree)

    1. ARM Device Tree起源 Linus Torvalds在2011年3月17日的ARM Linux邮件列表宣称“this whole ARM thing is a f*cking pai ...

  9. Linux驱动设计——字符设备驱动(一)

    Linux字符设别驱动结构 cdev结构体 struct cdev { struct kobject kobj; struct module *owner; const struct file_ope ...

随机推荐

  1. Java读取SQL server数据库

    要打开SQL server 的三个服务,然后再执行代码. package com.sql; import java.sql.SQLException; import java.sql.Statemen ...

  2. TheSuperego 实验六 团队作业3:项目需求分析与原型设计

    项目 内容 课程班级博客链接 班级博客 这个作业要求链接 作业要求 团队名称 TheSuperego 团队成员分工描述 杨丽霞:组织QQ会议,合理明确组内分工,推进任务,实施关于我们原型设计陈来弟:负 ...

  3. 视频教学动作修饰语:CVPR2020论文解析

    视频教学动作修饰语:CVPR2020论文解析 Action Modifiers: Learning from Adverbs in Instructional Videos 论文链接:https://a ...

  4. Yolo:实时目标检测实战(上)

    Yolo:实时目标检测实战(上) YOLO:Real-Time Object Detection 你只看一次(YOLO)是一个最先进的实时物体检测系统.在帕斯卡泰坦X上,它以每秒30帧的速度处理图像, ...

  5. Docker Buildx插件

    Docker Buildx插件 Overview Docker Buildx是一个CLI插件,它扩展了Docker命令,完全支持Moby BuildKit builder toolkit提供的功能.它 ...

  6. JavaScript 中精度问题以及解决方案

    JavaScript 中的数字按照 IEEE 754 的标准,使用 64 位双精度浮点型来表示.其中符号位 S,指数位 E,尾数位M分别占了 1,11,52 位,并且在 ES5 规范 中指出了指数位E ...

  7. 从实力的角度出发来思考这道AOP题目

    文/楠木大叔 技术更迭,一往无前.技术人总是要不断学习以适应社会的发展和行业对我们的要求.每隔一段时间,就会有纷至沓来的新技术,新知识,新概念,我们应该如何应对,是被逼到墙角,还是主动出击? 导读 从 ...

  8. python取整函数 向上取整 向下取整 四舍五入

    向上取整 >>> import math >>> math.ceil(3.5) 4 >>> math.ceil(3.4) 4 >>&g ...

  9. 自动发布.NET Core Web应用

    1 原因和目的 相信很多开发者都需要将自己的编写的应用进行编译并部署到服务器上,这个过程在个人或小型团队的项目中都是一个简单的事情.但是对于并行化开发而言,就需要通过工具来辅助这个过程.于是,我参考了 ...

  10. 【题解】Luogu P2889 [USACO07NOV]挤奶的时间Milking Time

    Luogu P2889 [USACO07NOV]挤奶的时间Milking Time 题目描述 传送门Bessie is such a hard-working cow. In fact, she is ...