首页 > 其他分享 >t113-c-led驱动篇(调用设备树)

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

时间:2023-07-10 19:12:32浏览次数:48  
标签:调用 led struct t113 linux include 设备 define

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

查看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 <[email protected]>");
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 <[email protected]>");
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的参考

标签:调用,led,struct,t113,linux,include,设备,define
From: https://www.cnblogs.com/recodemo/p/17538904.html

相关文章

  • java级联调用的空指针问题
    Java中的空指针异常(NullPointerException)通常是由于在一个对象引用上调用了空引用的方法或访问了空引用上的实例变量而引起的。当你尝试使用一个空引用时,就会抛出空指针异常。一个常见的情况是在级联调用中出现空指针异常。级联调用是指对一个对象的多个方法调用进行链式操作。例如......
  • ionic cordova 打包Rlease版本包出现异常Execution failed for task ':app:mergeRelea
    异常: 解决方法:找到android=》app下的build.gradle文件,如下增加如下配置 运行ioniccordovabuildandroid--release打包语句正常执行 ......
  • KBP210-ASEMI大功率LED驱动器桥堆KBP210
    编辑:llKBP210-ASEMI大功率LED驱动器桥堆KBP210型号:KBP210品牌:ASEMI封装:KBP-4恢复时间:≥200n0s正向电流:2A反向耐压:1000V芯片个数:4引脚数量:4类型:整流桥、桥堆特性:薄体扁桥、插件桥堆浪涌电流:60A正向压降:1.1V封装尺寸:如图工作温度:-50°C~150°CKBP210应用范围适配器......
  • winform openFileDialog 多个文件上传
       ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------privatevoidbutton1_Click(objectsender,EventArgse){......
  • js/ts文件中,导入i18n报错:Must be called at the top of a `setup` function
    import{useI18n}from'vue-i18n';const{t}=useI18n(); 会在非组件情况下报错: 此时我们需要将上述导入方式改为:importi18nfrom'@/lang/i18n';//@/lang/i18n为语言包位置const{t}=i18n.global;......
  • 一种LED灯闪烁的实现方法
    1.闪烁流程的实现voidLightFlicker(void)//闪灯处理:清码对码{ if(Flicker)//有闪灯计数{ if(PwmAdjustmenting==0)//调节已经稳定 { PwmAdjustmenting=1;//处于不稳定态 if(tickstatus==0) { Brightness=0;//熄灯 tickstatus=1; } elseif(tick......
  • js调用浏览器打印功能
    1.打印开始结束的信息2.点击打印按钮<buttontype="button"onclick="doPrint()">打印</button><script>functiondoPrint(){console.log(33333333333)bdhtml=window.document.body.innerHTML;sprnstr="<!--startprint--&......
  • labview 调用,联合halcon 编程最近在做项目,主界面使用labview,图像识别部分使用halcon,然
    labview调用,联合halcon编程最近在做项目,主界面使用labview,图像识别部分使用halcon,然后返回结果到labview再进一步判断显示。具体流程1.使用labview采集图像2,图像传递给halcon处理3.labview读取halcon的处理结果这里共享的知识点有以下几个1.labview的里面的图像,也就是image类型......
  • docker with non root priviledge
    RunningDockerContainersasNon-RootUserhttps://www.geeksforgeeks.org/running-docker-containers-as-non-root-user/Bydefault,DockerContainersrunasRootUsers.Now,ifyouarerunningapplicationsinsideDockerContainers,youhaveaccesstoallth......
  • feign 微服务调用,post请求如何在URL 后面带参数
    ​ 在Feign微服务调用中,可以通过在URL后面添加参数来进行POST请求。参数可以以两种方式传递:作为路径参数或查询参数。 路径参数:可以将参数添加到URL的路径中,并使用@PathVariable注解来获取参数的值。例如:@FeignClient(name="example-service")publicinterfaceExample......