对应自己的实验目录:5-dts-led

一、需要注意的地方

二、涉及的函数的用法介绍

1.注册设备号
int register_chrdev_region(dev_t from, unsigned count, const char *name)
  • @from: 申请的设备号
  • @count: 需要申请的设备数量
  • @name: 设备的名称
  • Return:0,成功 <0,失败
2.申请设备号
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
  • @dev: 被申请的设备号指针
  • @baseminor: 需要申请的第一个子设备号
  • @count: 需要申请的设备数量
  • @name: 设备的名称
  • Return:0,成功 <0,失败
3.初始化和注册字符设备
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
  • @cdev: 字符设备结构体指针
  • @fops: file_operations结构体指针
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
  • @p: 字符设备结构体指针
  • @dev: 设备号
  • @count: 连续的副设备号数量(设备的数量)
  • Return: <0,失败
4.创建类和设备(挂载设备节点)
struct class *class_create(owner, name)  //宏定义
  • @owner: 写THIS_MODULE
  • @name: 节点名称
  • Returns: 通过 IS_ERR() 检查错误,PTR_ERR() 显示错误
struct device *device_create(struct class *class, struct device *parent, \dev_t devt, void *drvdata, const char *fmt, ...)
  • @class: 指向类的指针
  • @parent: 指向父设备结构体(目前的只写NULL)
  • @devt: 设备号
  • @drvdata: (目前的只写NULL)
  • @fmt: 设备名
  • Returns: 通过 IS_ERR() 检查错误,PTR_ERR() 显示错误
5.获取设备节点
static inline struct device_node *of_find_node_by_path(const char *path)
  • @path: 节点在设备树里的路径
  • Returns: NULL,失败
6.获取属性的元素个数
int of_property_count_elems_of_size(const struct device_node *np, \const char *propname, int elem_size)
  • @np: 设备树节点指针
  • @propname: 属性名
  • @elem_size: 单个元素的大小
  • Returns: >0,成功;<0,失败
      -EINVAL 属性不存在或长度不匹配、 -ENODATA 没有值
7.从属性中获取32位整数数组(寄存器组的地址和长度)
int of_property_read_u32_array(const struct device_node *np, \const char *propname, u32 *out_values, \size_t sz)
  • @np: 设备树节点指针
  • @propname: 属性名
  • @out_values: 数组的指针
  • @sz: 需要读取的数组个数
  • Returns: 0,成功;<0,失败
      -EINVAL 属性不存在、-ENODATA 属性没值、 -EOVERFLOW属性长度不够
8.寄存器地址映射
void __iomem*ioremap(cookie,size) // 宏定义
  • @cookie: 寄存器地址
  • @size: 寄存大小
9.寄存器取消映射
void iounmap(volatile void __iomem *addr) // 宏定义
  • @addr: 取消映射的虚拟地址
10.内存申请
static __always_inline void *kmalloc(size_t size, gfp_t flags)
  • @size: 申请内存的大小
  • @flags: 申请的内存类型 (一般是:GFP_KERNEL) ;详情:文件slab.h 行365
11.内存释放
static void kfree(void *where) // 宏定义
  • @where: 需要释放的内存地址
12.销毁设备
void device_destroy(struct class *class, dev_t devt)
  • @class: 类的指针
  • @devt: 设备号
13.销毁类
extern void class_destroy(struct class *cls)
  • @cls: 类的指针
14.销毁字符设备
void cdev_del(struct cdev *p)
  • @p: 字符设备结构体的指针
15.释放设备号
void unregister_chrdev_region(dev_t from, unsigned count)
  • @from: 设备号
  • @count: 设备的数量

三、编写过程

1、编写设备树节点

在设备树的根节点下面创建一个节点,在reg属性里编写需要的寄存器地址和长度。

/ {alphaled {compatible = "alphaled";#address-cells = <1>;#size-cells = <1>;status = "okay";reg = < 0X020C406C 0X04 /* CCM_CCGR1_BASE */0X020E0068 0X04 /* SW_MUX_GPIO1_IO03_BASE */0X020E02F4 0X04 /* SW_PAD_GPIO1_IO03_BASE */0X0209C000 0X04 /* GPIO1_DR_BASE */0X0209C004 0X04 /* GPIO1_GDIR_BASE */>;};
};

2、驱动代码

2.1 驱动源文件

#include "include.h"#define LEDDTS_NAME "led"
#define LEDDTS_NUM  1struct Leddts_dev {dev_t devid;struct cdev cdev;struct class *class;struct device *device;int major;int minor;struct device_node *dev_node;int reg_num;u32 *reg_value;
};static unsigned int* va_ccm_ccgr1 = NULL;        // static void __iomem*
static unsigned int* va_mux_gpio1_io03 = NULL; // static void __iomem*
static unsigned int* va_pad_gpio1_io03 = NULL; // static void __iomem*
static unsigned int* va_gpio1_dr = NULL;       // static void __iomem*
static unsigned int* va_gpio1_gdir = NULL;     // static void __iomem*struct Leddts_dev leddts_dev;int leddts_open (struct inode *inode, struct file *filep)
{unsigned int val;/* 打开时钟 */val = readl(va_ccm_ccgr1);val |= 0x0c000000;writel(val, va_ccm_ccgr1);/* 设置复用 */val = readl(va_mux_gpio1_io03);val = 0x05;writel(val, va_mux_gpio1_io03);/* 设置电气属性 */val = readl(va_pad_gpio1_io03);val = 0x190a1;writel(val, va_pad_gpio1_io03);/* 设置方向 */val = readl(va_gpio1_gdir);val |= 1<<3;writel(val, va_gpio1_gdir);return 0;
}
int leddts_release (struct inode *inode, struct file *filep)
{return 0;
}
ssize_t leddts_read (struct file *filep, char __user *buf, size_t cnt, loff_t *offt)
{return 0;
}
ssize_t leddts_write (struct file *filep, const char __user *buf, size_t cnt, loff_t *offt)
{int ret;unsigned int val;char data;ret = copy_from_user(&data, buf, 1);if(ret < 0) {return 0;}/* 设置电平 */val = readl(va_gpio1_dr);if(data == 1)val |= 1<<3;elseval &=~ (1<<3);writel(val, va_gpio1_dr);return 1;
}struct file_operations fops = {.owner = THIS_MODULE,.open = leddts_open,.read = leddts_read,.write = leddts_write,.release = leddts_release
};/*** @brief 驱动入口* */
static int __init leddts_init(void)
{int ret = 0;int i;/* 申请一个空闲设备号 */leddts_dev.major = 0;if(leddts_dev.major) {leddts_dev.devid = MKDEV(leddts_dev.major, 0);ret = register_chrdev_region(leddts_dev.devid, LEDDTS_NUM, LEDDTS_NAME);} else {ret = alloc_chrdev_region(&leddts_dev.devid, 0, LEDDTS_NUM, LEDDTS_NAME);leddts_dev.major = MAJOR(leddts_dev.devid);leddts_dev.minor = MINOR(leddts_dev.devid);}if(ret) {ret = -EINVAL;goto fail_regi_chrdev;}/* 注册设备 */leddts_dev.cdev.owner = THIS_MODULE;cdev_init(&leddts_dev.cdev, &fops);ret = cdev_add(&leddts_dev.cdev, leddts_dev.devid, LEDDTS_NUM);if(ret < 0) {goto fail_cdev_add;}/* 挂载设备节点 */leddts_dev.class = class_create(THIS_MODULE, LEDDTS_NAME);if (IS_ERR(leddts_dev.class)) {ret = PTR_ERR(leddts_dev.class);goto fail_class_create;}leddts_dev.device = device_create(leddts_dev.class, NULL, leddts_dev.devid, NULL, LEDDTS_NAME);if (IS_ERR(leddts_dev.device)) {ret = PTR_ERR(leddts_dev.class);goto fail_device_create;}/* 1、获取节点 */leddts_dev.dev_node = of_find_node_by_path("/alphaled");if(leddts_dev.dev_node == NULL) {ret = -EINVAL;goto fail_find_node;}/* 2、读取数字属性的数组 */leddts_dev.reg_num = of_property_count_elems_of_size(leddts_dev.dev_node, "reg", sizeof(u32));if(leddts_dev.reg_num < 0) {ret = -EINVAL;goto fail_property_elems_size;}leddts_dev.reg_value = (u32*)kmalloc(sizeof(u32)*leddts_dev.reg_num, GFP_KERNEL);ret = of_property_read_u32_array(leddts_dev.dev_node, "reg", leddts_dev.reg_value, leddts_dev.reg_num);if(ret != 0) {goto fail_read_u32_property;}/* 打印属性值 */for(i=0; i<leddts_dev.reg_num; i+=2) {printk("reg = %x  %x \r\n", leddts_dev.reg_value[i], leddts_dev.reg_value[i+1]);}/* 寄存器映射 */va_ccm_ccgr1      = ioremap(leddts_dev.reg_value[0], leddts_dev.reg_value[1]);va_mux_gpio1_io03 = ioremap(leddts_dev.reg_value[2], leddts_dev.reg_value[3]);va_pad_gpio1_io03 = ioremap(leddts_dev.reg_value[4], leddts_dev.reg_value[5]);va_gpio1_dr       = ioremap(leddts_dev.reg_value[6], leddts_dev.reg_value[7]);va_gpio1_gdir     = ioremap(leddts_dev.reg_value[8], leddts_dev.reg_value[9]);return 0;fail_read_u32_property:kfree(leddts_dev.reg_value);
fail_property_elems_size:
fail_find_node:device_destroy(leddts_dev.class, leddts_dev.devid);
fail_device_create:class_destroy(leddts_dev.class);
fail_class_create:cdev_del(&leddts_dev.cdev);
fail_cdev_add:unregister_chrdev_region(leddts_dev.devid, LEDDTS_NUM);
fail_regi_chrdev:return ret;
}/*** @brief 驱动出口* */
static void __exit leddts_exit(void)
{iounmap(va_ccm_ccgr1);iounmap(va_mux_gpio1_io03);iounmap(va_pad_gpio1_io03);iounmap(va_gpio1_dr);iounmap(va_gpio1_gdir);kfree(leddts_dev.reg_value);device_destroy(leddts_dev.class, leddts_dev.devid);class_destroy(leddts_dev.class);cdev_del(&leddts_dev.cdev);unregister_chrdev_region(leddts_dev.devid, LEDDTS_NUM);
}module_init(leddts_init);
module_exit(leddts_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ls");

2.2需要的头文件

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <asm/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>

5、设备树操作reg相关推荐

  1. Linux内核 设备树操作常用API【转】

    转自:https://www.linuxidc.com/Linux/2017-02/140818.htm 一文中介绍了设备树的语法,这里主要介绍内核中提供的操作设备树的API,这些API通常都在&qu ...

  2. Linux设备树翻译计划

    本文翻译自Device Tree Usage主页: http://devicetree.org/Device_Tree_Usage 此译文为本人原创,若要转载请注明! Linux device tre ...

  3. 【正点原子MP157连载】第二十三章 Linux设备树-摘自【正点原子】STM32MP1嵌入式Linux驱动开发指南V1.7

    1)实验平台:正点原子STM32MP157开发板 2)购买链接:https://item.taobao.com/item.htm?&id=629270721801 3)全套实验源码+手册+视频 ...

  4. linux uart寄存器 代替 printk,Linux驱动学习之设备树(设备树下的LED驱动实验),...

    Linux驱动学习之设备树(设备树下的LED驱动实验), 概念 Linux内核从3.x开始引入设备树的概念,用于实现驱动代码与设备信息相分离.相当于从驱动代码分离出来的配置文件,比如串口的波特率通过设 ...

  5. linux驱动开发学习2 设备树

    设备树 dtb板级信息文件,大势所趋,一定要学  如何确定要编译哪个DTS文件:查看linux下的arch/arm/boot/dts/Makefile  DTS语言有属于自己的语法:以树形来描述设备信 ...

  6. 驱动开发基础知识——设备树

    BSP开发工程师[原来BSP就是那些被指臃肿的文件啊 BSP的出生 Linux经过不断的发展,原先嵌入式系统的三层结构逐步演化成为一种四层结构. 这个新增加的中间层次位于操作系统和硬件之间,包含了系统 ...

  7. 【正点原子Linux连载】第四十三章 Linux设备树 -摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

    1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址: ...

  8. 设备树dts/dtsi格式

    说明:后续的博文参考自韦东山老师的设备树视屏,老师用的是2440的开发板,我用的是s5pv210的开发板.原理一样 一.前言 简单的说,如果要使用Device Tree,首先用户要了解自己的硬件配置和 ...

  9. 关于linux设备树的简单理解(基于linux-5.13.5)

    1. 设备树文件 内核版本: linux-5.13.5 设备树文件所在路径:linux-5.13.5\arch\arm\boot\dts 每个xxx.dts对应一个板子 dts目录下有两种文件:xxx ...

  10. Linux 设备驱动开发 —— 设备树在platform设备驱动中的使用

    关与设备树的概念,我们在Exynos4412 内核移植(六)-- 设备树解析 里面已经学习过,下面看一下设备树在设备驱动开发中起到的作用 Device Tree是一种描述硬件的数据结构,设备树源(De ...

最新文章

  1. “数学不行,啥都干不好!”骨灰级程序员:这比努力重要1000倍
  2. Mars 算法实践——人脸识别
  3. 后退到的页面为什么没有执行js_为什么中层没有执行力?
  4. label smoothing(标签平滑)
  5. 第一百七十四节,jQuery,Ajax进阶
  6. iPhoneX 序列适配方案
  7. 【线上分享】高性能视频推理引擎优化
  8. Web Storage API的介绍和使用
  9. corosync和pacemaker实现httpd和mysql双集群
  10. stm32f10x单片机进阶--spi使用
  11. 前端布局总结--居中
  12. 如何看公司的财务数据
  13. 编译器错误信息: CS1010: 常数中有换行符
  14. 常见方案 目录 1. 发现目前 WEB 上主流的视频直播方案有 HLS 和 RTMP, 1 2. 实现直播的方法有很多,但是常用的,就这几个。 3个直播协议:rtmp、rtsp、hls。 和三个端:
  15. 显卡用什么软件作压力测试,推荐一个显卡的跑分跟压力测试的软件TimeSpy
  16. Arduino DHT11温湿度传感器数据示例
  17. 【bug解决】上传图片后,取消这次上传 再次执行上传,上次的图片还存在
  18. 2015年自我激励及2014年的总结
  19. 成立大数据公司 新华三“数据引擎”要干些什么事?
  20. 地理信息:统计工作的护航者

热门文章

  1. 案例分享:陕西西安599条背街小巷改造提升,推行“多杆合一”
  2. 我是怎样的一个人感悟
  3. 绕过tp路由器管理密码_路由器管理员密码忘了怎么办 路由器管理员密码忘了解决方法【介绍】...
  4. 李白最经典的20首诗排行榜
  5. 微信小程序.阿里巴巴矢量图标库iconfont使用
  6. 【软开云】基于华为软开云用敏捷思想管理项目团队一点思路(2)
  7. Tibco Designer -- 循环遍历
  8. 通达信自带指标 均线多头排列(DTPL)
  9. 3D目标检测/点云/遥感数据集汇总
  10. 再迎顶尖科学家,百度研究院为何如此吸引大师级AI人才?