t113-c-led驱动篇(调用设备树)

发布时间 2023-07-10 19:03:08作者: 悠闲的小莫

既然已经学会了调用驱动,那么接下来简单点个灯

查看led所在寄存器

我板子的led所控制的是pd22,所在寄存器应该是0x02000098

然而这和我在设备树上找到的地址有出入,很奇怪,那就不管这个了自己添加一个吧。

自己在board.dts上加一个ledio:

我们看见这里有个&pio,而pio又是在dtsi中的soc/pio上,此时的寄存器是我们所需要的,那就在这里添加好了

这里有个坑,查询寄存器的时候最好不要用of_find_property,这个会以一种很奇怪的形式呈现寄存器,说对又不对,说不对它又好像有点对

根据大佬的讲述,应该是大小端的问题,数组倒过来读了,以一个字节为单位的倒转

复制、修改先前框架

// #include "linux/module.h"
// #include "linux/fs.h"
// //#include "linux/stddef.h"
// #include "linux/types.h"
// //#include "crypto/if_alg.h"

#include "leddriver.h"


#define number 			1										//设备数量
#define ds_num_na "led_driver"					//分配设备号的名称
#define ds_class_na "led_driver"				//创建驱动文件时候类的名字
#define ds_drive_na "led_driver"				//创建驱动文件的名字

//注册字符型设备

struct leddriver{
	int major;											      //设备号
	int ma;								  						//主设备号
	int mi;						   		  						 //次设备号
	struct cdev chadv;				   //初始化一个cdev的句柄,主要是在init时候用来初始化
	struct class *chabases;						//创建一个类
	struct device *chabasedev;			  //创建类下的一个设备
	struct device_node *pw_nd;			//节点的句柄
	struct property *compatible;		 //提取的属性的句柄
	uint32_t real_reg;
}led_st;


//节点和文件,不详细解说
static int chabase_open(struct inode *inode, struct file *file)
{
	return 0;
}
//节点和文件,不详细解说
static int chabase_release(struct inode *inode, struct file *file)
{
	return 0;
}
//节点和文件,不详细解说,buffer和count三常用的
static ssize_t chabase_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
{
	return 0;
}
//节点和文件,不详细解说
static ssize_t chabase_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
{
	return 0;
}

static const struct file_operations chabase = {
	.owner		= THIS_MODULE,
	.open		= chabase_open,
	.read		= chabase_read,
	.release	= chabase_release,
	.write		= chabase_write,
};

static int __init led_in(void)
{
	int err;u32 reg_var[4];//寄存器容器
	printk("led_driver init \r\n");
	led_st.mi=0;
    // major=register_chrdev(0,"chabase",&chabase);//如果用这个作为设备号的分配将会一次占完此设备号下的所有子设备号节点
	// //因此我们不用这个
	//注册字符型设备,并且设备号为200,子设备号(后20位)在此默认为0,网上说设备号设为0为自动获取
	//返回的的好像是主设备号字段

	if(!led_st.ma)
	{
		err=alloc_chrdev_region(&led_st.ma,led_st.mi,number,ds_num_na);//如果未定义设备号用,主设备号内和给,次是设备号自己定,返回值是朱设备号
		if(err<0)
		{
			printk("failed to alloc chidev");
			return err;
		}
		led_st.ma=MAJOR(led_st.ma);
	}
	else{
		err=register_chrdev_region(MKDEV(led_st.ma,led_st.mi),number,ds_num_na);
		if(err<0){
			printk("failed to alloc chidev");
			return err;
		}
	}
	led_st.major=MKDEV(led_st.ma,led_st.mi);

	//创建字符型设备
	//先初始化在创建
	//printk("cdev_initing");
	led_st.chadv.owner=THIS_MODULE;
	cdev_init(&led_st.chadv,&chabase);


	//printk("ading cdev_initing");
	cdev_add(&led_st.chadv, led_st.major,number);									//创建

	//printk("majior is %d \r\n",major);
	led_st.chabases=class_create(THIS_MODULE,ds_class_na);//自动创建类,类的名字
	if(IS_ERR(led_st.chabases))															   //由于返回的是指针,所以药用iserr来检查是否出错
		return PTR_ERR(led_st.chabases);											  //如果出错则用ptrerr返回错误类型

	led_st.chabasedev=device_create(led_st.chabases,NULL, MKDEV(led_st.ma,led_st.mi),NULL,ds_drive_na);//创建的类,父节点一般为null,设备号
	//	节点可能用到的数据,节点的名字
	if(IS_ERR(led_st.chabasedev))
		goto fail_dcreate;
		//return PTR_ERR(led_st.chabasedev);

	//firstdrv_class_devs[0] = class_device_create(firstdrv_class,NULL,MKDEV(major,0),NULL,"leds"); 
	//firstdrv_class_devs[minor] = class_device_create(firstdrv_class,NULL,MKDEV(major,minor),NULL,"led%d",minor);


////////////////////////////////////////////////////////查询设备树


	led_st.pw_nd=of_find_node_by_path("/soc@3000000/pinctrl@2000000"); 			 //获取gpio的节点
	if(IS_ERR(led_st.pw_nd)){
		printk("failed to create node \r\n");
		goto fail_all;
	}
	else{
		printk("create node \r\n");
	}

	err=of_property_read_u32_array(led_st.pw_nd,"reg",reg_var,4);		//获取属性
	if(err){
		printk("failed to fine compatible \r\n");
		goto fail_all;
	}
	else{
		printk("fine compatible \r\n");
	}

	led_st.real_reg=(uint32_t)reg_var[1];
	printk("now reg is %x \r\n",led_st.real_reg);


	//kfree(led_st.pw_nd);
	led_st.pw_nd=of_find_node_by_path("/soc@3000000/pinctrl@2000000/led_pin@0"); 			 //再次获取gpio的节点
	if(IS_ERR(led_st.pw_nd)){
		printk("failed to create node \r\n");
		goto fail_all;
	}
	else{
		printk("create node \r\n");
	}

	
	//led_st.compatible=of_find_property(led_st.pw_nd,"reg",	NULL);		//获取属性
	err=of_property_read_u32_array(led_st.pw_nd,"reg",reg_var,4);
	if(err<0){
		printk("failed to fine compatible \r\n");
		goto fail_all;
	}
	else{
		printk("fine compatible \r\n");
	}

	led_st.real_reg+=(uint32_t)(reg_var[1]);
	printk("now reg is %x \r\n",led_st.real_reg);


    return 0;
fail_all:
	device_destroy(led_st.chabases,led_st.major);
fail_dcreate:
	class_destroy(led_st.chabases);
fail_class:
	cdev_del(&led_st.chadv);
fail_cdev_add:
fail_cdev:
	unregister_chrdev_region(led_st.major,number);
fail_chrdv:
	return -22;

}

static void __exit led_out(void)
{
	//unregister_chrdev(200,"chabase");//注销

	unregister_chrdev_region(led_st.major,number);

	//注销字符型设备
	cdev_del(&led_st.chadv);

	//退出后要进行注销
	//先注销节点再注销类
	device_destroy(led_st.chabases,led_st.major);
    class_destroy(led_st.chabases);
}

MODULE_LICENSE("GPL");
MODULE_AUTHOR("xiaomo <2712767798@qq.com>");
MODULE_DESCRIPTION("testting");
// MODULE_ALIAS("ipt_limit");
// MODULE_ALIAS("ip6t_limit");

module_init(led_in);
module_exit(led_out);

测试是否能够查询得出来

算是成功了

但是有个隐患,我在申请的节点的时候用的都是指针,这种指针返回一般都会又内存的申请,但是我并不知道如何去释放它,所以很有可能会导致后面的内存问题

调取寄存器并映射

这里有个问题,就是不知道为什么设备树所用的不是头文件定义的数组形式的寄存器,而是以字符串的形式定义的,这样的话我有理由怀疑关于引脚是有一个官方的头文件或者是直接在驱动内定好的寄存器,我觉得应该是有头文件的。

但是我找不到任何关于这方面的信息,要么是以.o文件隐藏了,要么就是在驱动内定死了,这就很麻烦了,意味着我每次都要从寄存器开始写

先不管,先写完程序,

代码:

驱动

// #include "linux/module.h"
// #include "linux/fs.h"
// //#include "linux/stddef.h"
// #include "linux/types.h"
// //#include "crypto/if_alg.h"

#include "leddriver.h"

uint32_t var;

#define number 			1										//设备数量
#define ds_num_na "led_driver"					//分配设备号的名称
#define ds_class_na "led_driver"				//创建驱动文件时候类的名字
#define ds_drive_na "led_driver"				//创建驱动文件的名字


#define iopd22_ctrlmov		24
#define iopd22_datamov	  22
#define iopd22_drvmov	   24
#define iopd22_pullmov	   12

#define iopd22_ctrlmask	    0xf0ffffff							//pd22的ctrlmask,data是0x01<<22就行
#define iopd22_drvlmask	   0xFCFFFFFF							//pd22的drvmask
#define iopd22_pullmask	   0xFFFFCFFF							//pd22的pullmask

static void __iomem *pd_ctrl;
static void __iomem *pd_data;
static void __iomem *pd_drv;
static void __iomem *pd_pull;

//注册字符型设备

struct leddriver{
	int major;											      //设备号
	int ma;								  						//主设备号
	int mi;						   		  						 //次设备号
	struct cdev chadv;				   //初始化一个cdev的句柄,主要是在init时候用来初始化
	struct class *chabases;						//创建一个类
	struct device *chabasedev;			  //创建类下的一个设备
	struct device_node *pw_nd;			//节点的句柄
	struct property *compatible;		 //提取的属性的句柄
	uint32_t real_reg;
}led_st;


//节点和文件,不详细解说
static int chabase_open(struct inode *inode, struct file *file)
{
	return 0;
}
//节点和文件,不详细解说
static int chabase_release(struct inode *inode, struct file *file)
{
	return 0;
}
//节点和文件,不详细解说,buffer和count三常用的
static ssize_t chabase_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
{
	return 0;
}
//节点和文件,不详细解说
static ssize_t chabase_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
{
	char data[7];
	memset(data,'\0',7);
	printk("count is %d;size is %d \r\n",count,sizeof(buffer));
	if(copy_from_user(data,buffer,count)){
		return -22;
	}
	if(!strcmp(data,"okay")){//开灯
		var=readl(pd_data);
		var&=~(1<<22);
		var+=(u32)(0)<<iopd22_datamov;//output
		writel(var,pd_data);
		printk("open the led \r\n");
	}
	else if(!strcmp(data,"close")){
		var=readl(pd_data);
		var&=~(1<<22);
		var+=(u32)(1)<<iopd22_datamov;//output
		writel(var,pd_data);
		printk("close the led \r\n");
	}
	else{
		printk("error the led,buffer is %s \r\n",data);
	}
	return 0;
}

static const struct file_operations chabase = {
	.owner		= THIS_MODULE,
	.open		= chabase_open,
	.read		= chabase_read,
	.release	= chabase_release,
	.write		= chabase_write,
};

static int __init led_in(void)
{
	int err;u32 reg_var[4];//寄存器容器
	printk("led_driver init \r\n");
	led_st.mi=0;
    // major=register_chrdev(0,"chabase",&chabase);//如果用这个作为设备号的分配将会一次占完此设备号下的所有子设备号节点
	// //因此我们不用这个
	//注册字符型设备,并且设备号为200,子设备号(后20位)在此默认为0,网上说设备号设为0为自动获取
	//返回的的好像是主设备号字段

	if(!led_st.ma)
	{
		err=alloc_chrdev_region(&led_st.ma,led_st.mi,number,ds_num_na);//如果未定义设备号用,主设备号内和给,次是设备号自己定,返回值是朱设备号
		if(err<0)
		{
			printk("failed to alloc chidev");
			return err;
		}
		led_st.ma=MAJOR(led_st.ma);
	}
	else{
		err=register_chrdev_region(MKDEV(led_st.ma,led_st.mi),number,ds_num_na);
		if(err<0){
			printk("failed to alloc chidev");
			return err;
		}
	}
	led_st.major=MKDEV(led_st.ma,led_st.mi);

	//创建字符型设备
	//先初始化在创建
	//printk("cdev_initing");
	led_st.chadv.owner=THIS_MODULE;
	cdev_init(&led_st.chadv,&chabase);


	//printk("ading cdev_initing");
	err=cdev_add(&led_st.chadv, led_st.major,number);									//创建
	if(err<0){
		printk("failed to alloc chidev");
		goto fail_cdev_add;
	}
	//printk("majior is %d \r\n",major);
	led_st.chabases=class_create(THIS_MODULE,ds_class_na);//自动创建类,类的名字
	if(IS_ERR(led_st.chabases))															   //由于返回的是指针,所以药用iserr来检查是否出错
		goto fail_class;											  //如果出错则用ptrerr返回错误类型

	led_st.chabasedev=device_create(led_st.chabases,NULL, MKDEV(led_st.ma,led_st.mi),NULL,ds_drive_na);//创建的类,父节点一般为null,设备号
	//	节点可能用到的数据,节点的名字
	if(IS_ERR(led_st.chabasedev))
		goto fail_dcreate;
		//return PTR_ERR(led_st.chabasedev);

	//firstdrv_class_devs[0] = class_device_create(firstdrv_class,NULL,MKDEV(major,0),NULL,"leds"); 
	//firstdrv_class_devs[minor] = class_device_create(firstdrv_class,NULL,MKDEV(major,minor),NULL,"led%d",minor);


////////////////////////////////////////////////////////查询设备树


	led_st.pw_nd=of_find_node_by_path("/soc@3000000/pinctrl@2000000"); 			 //获取gpio的节点
	if(IS_ERR(led_st.pw_nd)){
		printk("failed to create node \r\n");
		goto fail_all;
	}
	else{
		printk("create node \r\n");
	}

	err=of_property_read_u32_array(led_st.pw_nd,"reg",reg_var,4);		//获取属性
	if(err){
		printk("failed to fine reg \r\n");
		goto fail_all;
	}
	else{
		printk("fine reg \r\n");
	}

	led_st.real_reg=(uint32_t)reg_var[1];
	printk("now reg is %x \r\n",led_st.real_reg);


	//kzfree(led_st.pw_nd);
	led_st.pw_nd=of_find_node_by_path("/soc@3000000/pinctrl@2000000/led_pin@0"); 			 //再次获取gpio的节点
	if(IS_ERR(led_st.pw_nd)){
		printk("failed to create node \r\n");
		goto fail_all;
	}
	else{
		printk("create node \r\n");
	}

	
	//led_st.compatible=of_find_property(led_st.pw_nd,"reg",	NULL);		//获取属性
	err=of_property_read_u32_array(led_st.pw_nd,"reg",reg_var,4);
	if(err<0){
		printk("failed to fine compatible \r\n");
		goto fail_all;
	}
	else{
		printk("fine reg \r\n");
	}

	led_st.real_reg+=(uint32_t)(reg_var[1]);
	printk("now reg is %x \r\n",led_st.real_reg);


	//////////////////////////////////////////////////////////地址映射
	// pd_ctrl=ioport_map(led_st.real_reg,4);
	// pd_data=ioport_map(led_st.real_reg+0x08,4);
	// pd_drv=ioport_map(led_st.real_reg+0x14,4);
	// pd_pull=ioport_map(led_st.real_reg+0x20,4);

	pd_ctrl=ioremap(led_st.real_reg,4);
	pd_data=ioremap(led_st.real_reg+0x08,4);
	pd_drv=ioremap(led_st.real_reg+0x14,4);
	pd_pull=ioremap(led_st.real_reg+0x20,4);

	printk("address mux sessuccd \r\n");

	///////////////////////////////////////////////////////////led初始化
	var=readl(pd_ctrl);
	printk("ctrl data is %x \r\n",var);
	var&=iopd22_ctrlmask;
	var+=(u32)(0x0001)<<iopd22_ctrlmov;//output
	writel(var,pd_ctrl);

	var=readl(pd_drv);
	var&=iopd22_drvlmask;
	var+=(u32)(0x01)<<iopd22_drvmov;//驱动等级1
	writel(var,pd_drv);

	var=readl(pd_pull);
	var&=iopd22_pullmask;
	var+=(u32)(0x10)<<iopd22_pullmov;//下拉
	writel(var,pd_pull);

	printk("led init sessuccd \r\n");

    return 0;
fail_all:
	device_destroy(led_st.chabases,led_st.major);
fail_dcreate:
	class_destroy(led_st.chabases);
fail_class:
	cdev_del(&led_st.chadv);
fail_cdev_add:
fail_cdev:
	unregister_chrdev_region(led_st.major,number);
fail_chrdv:
	return -22;

}

static void __exit led_out(void)
{
	//取消的地址映射
	ioport_unmap(pd_ctrl);
	ioport_unmap(pd_data);
	ioport_unmap(pd_drv);
	ioport_unmap(pd_pull);

	//unregister_chrdev(200,"chabase");//注销

	unregister_chrdev_region(led_st.major,number);

	//注销字符型设备
	cdev_del(&led_st.chadv);

	//退出后要进行注销
	//先注销节点再注销类
	device_destroy(led_st.chabases,led_st.major);
    class_destroy(led_st.chabases);
}

MODULE_LICENSE("GPL");
MODULE_AUTHOR("xiaomo <2712767798@qq.com>");
MODULE_DESCRIPTION("testting");
// MODULE_ALIAS("ipt_limit");
// MODULE_ALIAS("ip6t_limit");

module_init(led_in);
module_exit(led_out);

应用

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

//一个参数个数一个参数字符串,首个字符是自己的应用
int main(int argc,char *argv[])
{
    char *st = "close";
    int number;
    int flie;
    ssize_t count;
    flie = open("/dev/led_driver",O_RDWR);
    if(flie<0)
    {
        printf("failed to open\r\n");
        return -22;
    }
    else
    {
        printf("open led_driver\r\n");
    }
    if((number=atoi(argv[1]))==(int)(1)){
        count = write(flie,"okay",5);
        if(count<0)//如果为负数失败并且错误类型保存在全局变量errno中,
        {
            printf("failed to write\r\n");
        }
        else
        {
            printf("write it\r\n");
        }
    }
    else if((number=atoi(argv[1]))==(int)(0)){
        count = write(flie,st,strlen(st));
        if(count<0)//如果为负数失败并且错误类型保存在全局变量errno中,
        {
            printf("failed to write\r\n");
        }
        else
        {
            printf("write it %s \r\n",st);
        }
    }
    else{
            printf("error\r\n");
    }

    count = close(flie);
    if(count<0)//如果为负数失败并且错误类型保存在全局变量errno中,
    {
        printf("failed to close\r\n");
        return -22;
    }
    else
    {
        printf("close it\r\n");
    }

    return 0;
}

makefile

KEDIR = /home/momo/T113/Tina-Linux/lichee/linux-5.4
CROSS_COMPILESS = /home/momo/T113/Tina-Linux/prebuilt/gcc/linux-x86/arm/toolchain-sunxi-musl/toolchain/bin/arm-openwrt-linux-muslgnueabi-

CURRENT-PATH := $(shell pwd)

obj-m := leddriver.o

build: kernel_modules

kernel_modules:
			${MAKE} -C ${KEDIR} M=${CURRENT-PATH} modules
			$(CROSS_COMPILESS)gcc -o led_app leddriver_app.c 
clean:
			${MAKE} -C ${KEDIR} M=${CURRENT-PATH} clean
			rm -rf modules.order
			rm -f led_app

结果

成功驱动led灯

kmalloc的参考