驱动 day8 作业

news2024/11/27 16:36:02

1.在内核模块中启用定时器,定时1s,让led1 一秒亮、一秒灭
2.基于gpio子系统完成LED灯驱动的注册,应用程序测试
1.mychrdev_timer.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/device.h>
#include <linux/slab.h>
#include<linux/cdev.h>
#include <linux/uaccess.h>
#include<linux/of.h>
#include<linux/gpio.h>
#include<linux/of_gpio.h>
#include <linux/timer.h>
struct cdev *cdev;
unsigned int major=500;//主设备号的起始值
unsigned int minor=0;//次设备号的起始值
dev_t devno;//设备号
struct class *cls;//用于保存目录信息
struct device *dev;//用于保存设备节点信息
struct device_node *dnode;//设备树节点结构体
struct gpio_desc *gpiono;//gpio属性结构体
//分配一个定时器对象
struct timer_list mytimer;
//定义操作方法结构体变量并赋值
struct file_operations fops =
{
};
void mytimer_func(struct timer_list *timer)
{
	printk("定时器处理函数执行\n");
	if(gpiod_get_value(gpiono)==0)
	{
		//设置管脚输出高电平,亮灯
		gpiod_set_value(gpiono,1);
	}else{
		//设置管脚输出低电平,灭灯;
		gpiod_set_value(gpiono,0);
	}
	//再次启动定时器
	mod_timer(timer,jiffies+HZ);

}

static int __init mycdev_init(void)
{
   	int ret;
    //1.分配字符设备驱动对象空间  cdev_alloc
    cdev=cdev_alloc();
    if(cdev==NULL)
    {
        printk("申请字符设备驱动对象空间失败\n");
        ret=-EFAULT;
        goto out1;
    }
    printk("字符设备驱动对象申请成功\n");
    //2.字符设备驱动对象部分初始化  cdev_init
    cdev_init(cdev,&fops);
    //3.申请设备号  register_chrdev_region/alloc_chrdev_region
    if(major>0)//静态申请设备号
    {
        ret=register_chrdev_region(MKDEV(major,minor),1,"mychrdev");
        if(ret)
        {
            printk("静态指定设备号失败\n");
            goto out2;
        }
    }
    else//动态申请设备号
    {
        ret=alloc_chrdev_region(&devno,minor,1,"mychrdev");
        if(ret)
        {
            printk("动态申请设备号失败\n");
            goto out2;
        }
        major=MAJOR(devno);//根据设备号得到主设备号
        minor=MINOR(devno);//根据设备号得到次设备号
    }
    printk("申请设备号成功\n");
    //4.注册字符设备驱动对象  cdev_add()
    ret=cdev_add(cdev,MKDEV(major,minor),1);
    if(ret)
    {
        printk("注册字符设备驱动对象失败\n");
        goto out3;
    }
    printk("注册字符设备驱动对象成功\n");
    //5.向上提交目录
    cls=class_create(THIS_MODULE,"mychrdev");
    if(IS_ERR(cls))
    {
        printk("向上提交目录失败\n");
        ret=-PTR_ERR(cls);
        goto out4;
	}
	printk("向上提交目录成功\n");
	//6.向上提交设备节点
	dev=device_create(cls,NULL,MKDEV(major,minor),NULL,"mychrdev");
	if(IS_ERR(dev))
	{
		printk("向上提交节点信息失败\n");
		ret=-PTR_ERR(dev);
		goto out5;
	}
	printk("向上提交设备节点信息成功\n");
	//解析led灯的设备树节点
	dnode=of_find_node_by_path("/myleds");
	if(dnode==NULL)
	{
		printk("解析设备树节点失败\n");
		return -ENXIO;
	}
	printk("解析设备树节点成功\n");
	//根据设备树节点解析led1 gpio结构体并向内核注册
	gpiono=gpiod_get_from_of_node(dnode,"led1",0,GPIOD_OUT_LOW,NULL);
	if(IS_ERR(gpiono))
	{
		printk("申请gpio失败\n");
		return -PTR_ERR(gpiono);
	}
	mytimer.expires=jiffies+HZ;
	timer_setup(&mytimer,mytimer_func,0);
	add_timer(&mytimer);
	return 0;
out5:
	//销毁上面提交的设备信息
	device_destroy(cls,MKDEV(major,minor));
	class_destroy(cls);
out4:
	cdev_del(cdev);
out3:
    unregister_chrdev_region(MKDEV(major,minor),1);
out2:
    kfree(cdev);
out1:
    return ret;	
}

static void __exit mycdev_exit(void)
{
	//灭灯
	gpiod_set_value(gpiono,0);
	//注销gpio信息
	gpiod_put(gpiono);
	//1.销毁设备节点信息
	device_destroy(cls,MKDEV(major,minor));
	//2.销毁目录信息
	class_destroy(cls);
	//3.注销对象  cdev_del()
	cdev_del(cdev);
	//4.释放设备号   unregister_chrdev_region()
	unregister_chrdev_region(MKDEV(major,minor),1);
	//5.释放对象空间  kfree()
    kfree(cdev);    
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

2 test.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
int main(int argc, char const *argv[])
{
	int fd1,fd2,fd3;
	char a,b;
	char buf[10]={0};
	while(1)
	{
		printf("请输入两个字符\n");
		printf("请选择要操作的灯1(LED1) 2(LED2) 3(LED3) 4(EXIT)\n");
		scanf("%c",&a);
		getchar();
		if(a == '4')
			return 0;
		switch(a)
		{
		case '1':
			fd1 = open("/dev/myled1",O_RDWR);
			if (fd1 < 0)
			{
				printf("打开设备文件失败\n");
				exit(-1);
			}  
			break;
		case '2':
			fd2 = open("/dev/myled2",O_RDWR); 
			if (fd2 < 0)
			{
				printf("打开设备文件失败\n");
				exit(-1);
			} 
			break;
		case '3':
			fd3 = open("/dev/myled3",O_RDWR); 
			if (fd3 < 0)
			{
				printf("打开设备文件失败\n");
				exit(-1);
			}
			break;
		}
		//从终端读取
		printf("第二个字符:0(关灯) 1(开灯)\n");
		scanf("%c",&b);
		getchar();
		buf[0]=a;
		buf[1]=b;
		switch(a)
		{
		case '1':
			write(fd1,buf,sizeof(buf));
			break;
		case '2':
			write(fd2,buf,sizeof(buf));
			break;
		case '3':
			write(fd3,buf,sizeof(buf));
			break;
		}
		memset(buf,0,sizeof(buf));
	}
    close(fd1);
    close(fd2);
    close(fd3);
    return 0;
}

led.c

#include <linux/init.h>
#include <linux/module.h>
#include<linux/fs.h>
#include<linux/device.h>
#include<linux/cdev.h>
#include<linux/slab.h>
#include <linux/io.h>
#include<linux/of.h>
#include<linux/gpio.h>
#include<linux/of_gpio.h>
struct cdev *cdev;//字符设备驱动对象空间首地址
unsigned int major=200;//主设备号
unsigned int minor=0;//次设备号的起始值
dev_t devno;//设备号变量
char kbuf[128]={0};
struct class *cls;//存放向上提交目录的返回值
struct device *dev;//存放向上提交设备节点信息结构体
struct device_node *dnode;
struct gpio_desc *gpiono1;
struct gpio_desc *gpiono2;
struct gpio_desc *gpiono3;
int mycdev_open(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}
ssize_t mycdev_write(struct file *file, const char *ubuf, size_t size, loff_t *lof)
{   
    int ret; 
    if(sizeof(kbuf)<size)//判断用户空间的需求是否能被驱动满足,满足不了就给能给的最好的
        size=sizeof(kbuf);
    ret=copy_from_user(kbuf,ubuf,size);
    if(ret)
    {
        printk("copy_from_user filed\n");
        return -EIO;
    }
    switch(kbuf[0]){
        case '1'://LED1
            if(kbuf[1]=='0')//关灯			
				gpiod_set_value(gpiono1,0);
            else//开灯
				gpiod_set_value(gpiono1,1);
            break;
        case '2'://LED2
            if(kbuf[1]=='0')//关灯
				gpiod_set_value(gpiono2,0);
            else//开灯
				gpiod_set_value(gpiono2,1);
            break;
        case '3'://LED3
            if(kbuf[1]=='0')//关灯
				gpiod_set_value(gpiono3,0);
            else//开灯
				gpiod_set_value(gpiono3,1);
            break;
    }
    return 0;
}
int mycdev_close(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}

// 定义操作方法结构体变量并赋值
struct file_operations fops = {

    .open = mycdev_open,
	.write = mycdev_write,
    .release = mycdev_close,
};
static int __init mycdev_init(void)
{
    int ret,i;
    //1.分配字符设备驱动对象空间  cdev_alloc
    cdev = cdev_alloc();
    if (!cdev) {
        printk(KERN_ALERT "cdev_alloc failed\n");
        return -ENOMEM;
    }
    printk("字符设备驱动对象空间申请成功\n");
    //2.字符设备驱动对象部分初始化  cdev_init
    cdev_init(cdev, &fops);
    //3.申请设备号  register_chrdev_region/alloc_chrdev_region
    if(major>0)//静态申请设备号
    {
        ret=register_chrdev_region(MKDEV(major,minor),3,"myled");
        if(ret)
        {
            printk("静态指定设备号失败\n");
            goto out2;
        }
    }
    else//动态申请设备号
    {
        ret=alloc_chrdev_region(&devno,minor,3,"myled");
        if(ret)
        {
            printk("动态申请设备号失败\n");
            goto out2;
        }
        major=MAJOR(devno);//根据设备号得到主设备号
        minor=MINOR(devno);//根据设备号得到次设备号
    }
    printk("申请设备号成功\n");
    //4.注册字符设备驱动对象  cdev_add()
    ret=cdev_add(cdev,MKDEV(major,minor),3);
    if(ret)
    {
        printk("注册字符设备驱动对象失败\n");
        goto out3;
    }
    printk("注册字符设备驱动对象成功\n");
    //5.向上提交目录
    cls=class_create(THIS_MODULE,"myled");
    if(IS_ERR(cls))
    {
        printk("向上提交目录失败\n");
        ret=-PTR_ERR(cls);
		goto out4;
	}
	printk("向上提交目录成功\n");
	//6.向上提交设备节点
	for(i=0;i<3;i++)
	{
		dev=device_create(cls,NULL,MKDEV(major,i),NULL,"myled%d",i+1);
		if(IS_ERR(dev))
		{
			printk("向上提交节点信息失败\n");
			ret=-PTR_ERR(dev);
			goto out5;
		}
	}
	printk("向上提交设备节点信息成功\n");
	//解析led灯的设备树节点
	dnode=of_find_node_by_path("/myleds");
	if(dnode==NULL)
	{
		printk("解析设备树节点失败\n");
		return -ENXIO;
	}
	printk("解析设备树节点成功\n");
	//根据设备树节点解析led1 gpio结构体并向内核注册
	gpiono1=gpiod_get_from_of_node(dnode,"led1",0,GPIOD_OUT_LOW,NULL); 
	if(IS_ERR(gpiono1))
	{
		printk("申请gpio失败\n");
		return -PTR_ERR(gpiono1);
	} 
	//根据设备树节点解析led2 gpio结构体并向内核注册
	gpiono2=gpiod_get_from_of_node(dnode,"led2",0,GPIOD_OUT_LOW,NULL); 
	if(IS_ERR(gpiono2))
	{
		printk("申请gpio失败\n");
		return -PTR_ERR(gpiono2);
	} 
	//根据设备树节点解析led3 gpio结构体并向内核注册
	gpiono3=gpiod_get_from_of_node(dnode,"led3",0,GPIOD_OUT_LOW,NULL); 
	if(IS_ERR(gpiono3))
	{
		printk("申请gpio失败\n");
		return -PTR_ERR(gpiono3);
	} 
	return 0;
out5:
	for(--i;i>=0;i++)
	{
		//销毁上面提交的设备信息
		device_destroy(cls,MKDEV(major,i));
	}
	class_destroy(cls);
out4:
	cdev_del(cdev);
out3:
    unregister_chrdev_region(MKDEV(major,minor),1);
out2:
    kfree(cdev);
out1:
    return ret;
}
static void __exit mycdev_exit(void)
{

	//灭灯,注销gpio信息
	int i;
	gpiod_set_value(gpiono1,0);
	gpiod_put(gpiono1);
	gpiod_set_value(gpiono2,0);
	gpiod_put(gpiono2);
	gpiod_set_value(gpiono3,0);
	gpiod_put(gpiono3);
	
	//1.销毁设备信息  device_destroy
	for(i=0;i<3;i++)
	{
		device_destroy(cls,MKDEV(major,i));
	}
	//2.销毁目录  class_destroy
    class_destroy(cls);
    //3.注销对象  cdev_del()
    cdev_del(cdev);
    //4.释放设备号   unregister_chrdev_region()
    unregister_chrdev_region(MKDEV(major,minor),3);
    //5.释放对象空间  kfree()
    kfree(cdev);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/742263.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Qt DAY5 Qt制作简易网络聊天室

服务器 widget.h文件 #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QTcpServer> #include <QTcpSocket> #include <QVector>//向量&#xff0c;函数类模板 #include <QMessageBox>namespace Ui { class Widget; }class Wid…

STM32+FreeRTOS 使用SystemView监控系统

前言 本文以STM32F407ZET6 FreeRTOS V9.0作为演示&#xff0c;其它的Cortex M芯片同样可以参考此文&#xff0c;其他内核和RTOS理论上也支持&#xff0c;本文暂时不做研究。 所以开始阅读本文前&#xff0c;需要一块能运行FreeRTOS的Cortex M芯片&#xff0c;如果没有移植好…

发一下接口自动化测试框架(python3+requests+excel)

Git&#xff1a; https://github.com/lilinyu861/Interface-Test 环境配置&#xff1a; 开发工具&#xff1a;pycharm2018Excel 开发框架&#xff1a;python3requestsexcel 接口自动化测试框架介绍&#xff1a; 此接口测试框架&#xff0c;首先由用户设计原始的测试用例并为…

webpack笔记二

文章目录 背景拆分环境清除上次构建产物插件&#xff1a;clean-webpack-plugin合并配置文件插件&#xff1a;webpack-merge实时更新和预览效果&#xff1a;webpack-dev-server babel配置参考 背景 webpack笔记一 在前面的学习&#xff0c;完成了webpack的基本配置&#xff0c…

C++教程——const修饰指针、结构体、文件操作

const修饰指针 常量指针 指针常量 const既修饰指针&#xff0c;又修饰常量 指针与数组 结构体 通过指针访问结构体变量中的数据 结构体中const使用场景 文件操作 写文件 读文件 读取数据的方式 二进制读写文件 写文件 读文件

master、origin master和origin/master

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

线程任务分支合并框架

1、原理 2、实用类 &#xff08;1&#xff09; ForkJoinPool 分支合并池 类比> 线程池 &#xff08;2&#xff09; ForkJoinTask ForkJoinTask 类比> FutureTask &#xff08;3&#xff09; RecursiveTask 递归任务&#xff1a;继承后可以实现递归(自己调自己)调用…

从小白到大神之路之学习运维第57天--------shell脚本实例应用3.0--以及————结合“三贱客”之“grep”的相关用法

第三阶段基础 时 间&#xff1a;2023年7月11日 参加人&#xff1a;全班人员 内 容&#xff1a; shell实例 目录 一、循环的基本使用 while随机循环 二、case控制服务的基本应用 1、case的语法格式 2、使用case写脚本&#xff0c;以以下实验为主 例1&#xff1a;控…

vue + Luckysheet 实现在线Excel表格操作

需求千千万&#xff0c;又是难熬的一天&#xff01; 效果图&#xff1a; Luckysheet官网网站&#xff1a;快速上手 | Luckysheet文档 1、引入&#xff08;两种&#xff09; 第一种CDN引入 在项目中的 public ---> index.html 中添加代码&#xff0c;如下&#xff1a; &…

91.qt qml-圆角毛玻璃 高斯模糊

qml中使用毛玻璃,可以通过两个类型GaussianBlur或者FastBlur使用,效果如下所示: 接下来先来介绍FastBlur和GaussianBlur 1.FastBlur介绍 FastBlur提供比GaussianBlur更低的模糊质量,但渲染速度更快。FastBlur效果通过使用源内容缩小和双线性滤波的算法模糊源内容来软化源内…

【免费送书活动第一期】赠送实体图书《深入浅出Java虚拟机》JVM原理与实战

赠送实体图书四本&#xff08;免费赠送&#xff09; 《深入浅出JAVA虚拟机&#xff1a;JVM原理与实战》 内容简介&#xff1a; 本书主要以 Java 虚拟机的基本特性及运行原理为中心&#xff0c;深入浅出地分析 JVM 的组成结构和底层实现&#xff0c;介绍了很多性能调优的方案和…

idea编译时遇到的bug

1、 D:\workspace\spark\src\main\Scala\WordCount.scala:3:8 WordCount is already defined as object WordCount object WordCount { 解决参考博客&#xff1a;Error:(21, 8) FlumePushWordCount is already defined as object FlumePushWordCount object FlumePushWor_WSQ(…

Switch超频图文说明

Switch超频图文说明 有些游戏&#xff0c;官方锁频导致游戏卡顿&#xff0c;可以通过超频提升游戏体验。抽空研究了下&#xff0c;发现超频可以在大气层 Atmosphere 和 SX OS系统中使用。 正巧最近有同学询问&#xff0c;就整理个教程好了。 Switch超频图文说明 按自己的破解系…

14-Vue插槽(slot),制作可复用组件

什么是 slot ? Vue 将 <slot>元素作为承载分发内容的出口。插槽内可以包含任何模板代码&#xff0c;包括 HTML或其它组件。 在某些组件的模板中&#xff0c;有一部分区域需要父组件来指定 <!-- message组件&#xff1a;一个弹窗消息 --> <div class"mes…

【工具】浏览器自带下载加速功能

&#x1f41a;作者简介&#xff1a;花神庙码农&#xff08;专注于Linux、WLAN、TCP/IP、Python等技术方向&#xff09;&#x1f433;博客主页&#xff1a;花神庙码农 &#xff0c;地址&#xff1a;https://blog.csdn.net/qxhgd&#x1f310;系列专栏&#xff1a;善假于物&#…

srt转rtmp(mpegts -> flv)

一、使用ffmpeg拉流srt转rtmp推流过程中遇到音视频问题 1、音频 虽然从mpegts到flv都是AAC格式&#xff0c;但是mpegts多了ADTS头&#xff0c;在flvenc的时候会报错误&#xff1a; Malformed AAC bitstream detected: use the audio bitstream filter aac_adtstoasc to fix it…

JAVA克隆

更多精彩 先案例后讲解&#xff0c;这里是代码教父&#xff0c;今天讲解JAVA中的clone 目录 什么是clone如何实现clone 浅克隆深克隆小结 什么时候使用cloneclone 相关类库的实现分析 什么是clone 在Java中&#xff0c;克隆&#xff08;Clone&#xff09;指的是创建一个现有对…

linux命令与shell编程

文章目录 一、概念linux内存嵌入式嵌入式层次图判断小端和大端 二、linux系统操作命令ls查看cd 命令pwd命令touch 创建文件mkdir 创建目录chmod 修改权限man命令cp 拷贝mv 移动rm命令cat命令echo 命令tty命令->查看当前终端号clear 命令ldd命令 ->查看文件依赖哪些库prin…

make makefile

文章目录 make是一个命令makefile or Makefile是一个当前目录下的文件使用&#xff1a;生成可执行文件清理 作用依赖关系依赖方法make会自动推导makefile中的依赖关系栈式结构为什么清理的时候要make 加上clean?make后面可以直接跟要生成的可执行文件,指定名称的依赖关系和依赖…

Python第二天之容器学习

1.List 容器无非就增删改查 1.添加 name_list [aaa,bbb,ccc,ddd] name_list.append(b1) name_list.insert(1,xxx) print(name_list)append 是在后面追加 而insert是自己定义下表插入 name_list [aaa,bbb,ccc,ddd] name_list2 [qqq,222,111] name_list.extend(name_list…