当前位置: 首页 > 工具软件 > reg > 使用案例 >

5、设备树操作reg

巫马善
2023-12-01

对应自己的实验目录: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  1

struct 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;
    else
        val &=~ (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>
 类似资料: