嵌入式:驱动开发 Day9

news2025/6/18 0:11:15

作业:通过platform总线驱动实现

a.应用程序通过阻塞的io模型来读取number变量的值
b.number是内核驱动中的一个变量
c.number的值随着按键按下而改变(按键中断) 例如number=0 按下按键number=1 ,再次按下按键number=0
d.在按下按键的时候需要同时将led1的状态取反
e.驱动中需要编写字符设备驱动
f.驱动中需要自动创建设备节点
g.这个驱动需要的所有设备信息放在设备树的同一个节点中

驱动程序:my_platform.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>
#include <linux/of_gpio.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/wait.h>

struct cdev *cdev;
unsigned int major = 0;
unsigned int minor = 0;
dev_t devno;
module_param(major, uint, 0664); //方便命令行传递major的值
struct class *cls;
struct device *dev;
struct resource *res;
unsigned int irqno;
struct gpio_desc *gpiono;
wait_queue_head_t wq_head; //定义一个等待队列头
unsigned int number = 0;
unsigned int condition = 0;

//定义中断处理函数
irqreturn_t key_handler(int irq, void *dev)
{
    gpiod_set_value(gpiono, !gpiod_get_value(gpiono));
    if (number == 0)
    {
        number = 1;
        condition = 1;
        wake_up_interruptible(&wq_head);
    }
    else
    {
        number = 0;
        condition = 1;
        wake_up_interruptible(&wq_head);
    }
    return IRQ_HANDLED;
}

//封装操作方法
int mycdev_open(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}

ssize_t mycdev_read(struct file *file, char *ubuf, size_t size, loff_t *lof)
{
    int ret;
    //判断IO方式
    if (file->f_flags & O_NONBLOCK) //非阻塞
    {
    }
    else
    {                                                 //阻塞
        wait_event_interruptible(wq_head, condition); //先检查condition再将进程休眠
    }
    ret = copy_to_user(ubuf, &number, size);
    if (ret)
    {
        printk("copy_to_user err\n");
        return -EIO;
    }
    condition = 0; //下一次硬件数据没有就绪
    return 0;
}

ssize_t mycdev_write(struct file *file, const char *ubuf, size_t size, loff_t *lof)
{
    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,
    .read = mycdev_read,
    .write = mycdev_write,
    .release = mycdev_close,
};

//封装probe函数
int pdrv_probe(struct platform_device *pdev)
{
    //初始化等待队列
    init_waitqueue_head(&wq_head);

    int ret;
    //1.为字符设备驱动对象申请空间
    cdev = cdev_alloc();
    if (cdev == NULL)
    {
        printk("申请字符设备驱动对象空间失败\n");
        ret = -EFAULT;
        goto out1;
    }
    printk("申请字符设备驱动对象空间成功\n");

    //2.初始化字符设备驱动对象
    cdev_init(cdev, &fops);
    printk("初始化字符设备驱动对象成功\n");

    //3.申请设备号
    if (major > 0) //静态指定设备号
    {
        ret = register_chrdev_region(MKDEV(major, minor), 3, "myled");
        if (ret)
        {
            printk("静态申请设备号失败\n");
            goto out2;
        }
    }
    else if (major == 0)
    { //动态申请设备号
        ret = alloc_chrdev_region(&devno, minor, 3, "myled");
        if (ret)
        {
            printk("动态申请设备号失败\n");
            goto out2;
        }
        major = MAJOR(devno); //获取主设备号
        minor = MINOR(devno); //获取次设备号
    }
    printk("申请设备号成功\n");

    //4.注册字符设备驱动对象
    ret = cdev_add(cdev, MKDEV(major, minor), 3);
    if (ret)
    {
        printk("注册字符设备驱动对象失败\n");
        goto out3;
    }
    printk("注册字符设备驱动对象成功\n");

    //向上提交目录信息
    cls = class_create(THIS_MODULE, "myled");
    if (IS_ERR(cls))
    {
        printk("向上提交目录失败\n");
        ret = -PTR_ERR(cls);
        goto out4;
    }
    printk("向上提交目录成功\n");

    //向上提交设备节点信息
    int i;
    for (i = 0; i < 3; i++)
    {
        dev = device_create(cls, NULL, MKDEV(major, i), NULL, "myled%d", i);
        if (IS_ERR(dev))
        {
            printk("向上提交设备节点信息失败\n");
            ret = -PTR_ERR(dev);
            goto out5;
        }
    }
    printk("向上提交设备信息成功\n");

    //platform
    //获取MEM类型的资源
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if (res == NULL)
    {
        printk("获取MEM类型资源失败\n");
        return -ENXIO;
    }

    //获取中断类型的资源
    irqno = platform_get_irq(pdev, 0);
    if (irqno < 0)
    {
        printk("获取中断类型资源失败\n");
        return -ENXIO;
    }

    //设备树匹配成功后,设备树结点指针可以通过pdev->dev.of_node获取
    //基于设备树节点信息获取gpio_desc对象指针
    gpiono = gpiod_get_from_of_node(pdev->dev.of_node, "led1-gpio", 0, GPIOD_OUT_LOW, NULL);
    if (IS_ERR(gpiono))
    {
        printk("解析GPIO管脚信息失败\n");
        return -ENXIO;
    }
    printk("解析GPIO管脚信息成功\n");

    //注册按键中断
    ret = request_irq(irqno, key_handler, IRQF_TRIGGER_FALLING, "key_init", NULL);
    if (ret < 0)
    {
        printk("注册按键%d中断失败\n", i);
        return ret;
    }
    printk("注册按键中断成功\n");

    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), 3);
out2:
    //释放设备驱动对象空间
    kfree(cdev);
out1:
    return ret;
}

//封装remove函数
int pdrv_remove(struct platform_device *pdev)
{
    //注销中断
    free_irq(irqno, NULL);
    //释放GPIO信息
    gpiod_put(gpiono);
    int i;
    //释放设备节点信息
    for (i = 0; i < 3; i++)
    {
        device_destroy(cls, MKDEV(major, i));
    }
    //销毁目录
    class_destroy(cls);
    //注销字符设备驱动对象
    cdev_del(cdev);
    //释放设备号
    unregister_chrdev_region(MKDEV(major, minor), 3);
    //释放设备驱动对象空间
    kfree(cdev);
    return 0;
}

//构建设备树匹配表
struct of_device_id oftable[] = {
    {.compatible = "hqyj,myplatform"},
    {}, //防止数组越界
};

//定义platform驱动信息对象并初始化
struct platform_driver pdrv = {
    .probe = pdrv_probe,
    .remove = pdrv_remove,
    .driver = {
        .name = "bbbbb",
        .of_match_table = oftable, //用于设备树匹配
    },
};

//一键注册platform宏
module_platform_driver(pdrv);
MODULE_LICENSE("GPL");

应用程序:test.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
int main(){
    unsigned int number;
	int fd = open("/dev/myled0", O_RDWR);
	if(fd < 0){
		printf("设备文件打开失败\n");
		exit(-1);
	}
	while(1){
		read(fd, &number, sizeof(number));
		printf("number = %d\n", number);
	}


	close(fd);

	return 0;
}

实验现象:

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

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

相关文章

VHOST-SCSI代码分析(1)VHOST SCSI设备模拟

VHOST SCSI设备的模拟是由QEMU和HOST共同实现的&#xff0c;QEMU模拟VHOST SCSI设备配置空间等&#xff0c;而对于虚拟机通知HOST和HOST通知虚拟机机制由HOST内核实现。 在QEMU中VHOST SCSI设备继承关系如下&#xff1a; 其它设备以及对应class_init函数和realize具现化实现与V…

什么是亲子经济,小红书母婴品牌营销须知!

亲子经济已经成为当今社会的新兴概念&#xff0c;今天将要探讨亲子经济的定义、重要性以及&#xff0c;什么是亲子经济&#xff0c;小红书母婴品牌营销须知&#xff01; 一、亲子经济的定义与重要性 1.定义 亲子经济是指以儿童和家庭为中心的经济活动。其中包括了亲子旅游、教育…

中国股市杠杆是多少倍?炒股加9倍杠杆可行不?

中国股市杠杆通常情况下是在3&#xff5e;5倍之间&#xff0c;而在某些情况下&#xff0c;最高可能有十倍的杠杆。 杠杆炒股&#xff0c;即融资融券交易&#xff0c;是一种高风险高收益的投资方式。通过融资融券交易&#xff0c;投资者可以借入资金来扩大自己的投资规模&#…

容器管理工具 Docker生态架构及部署

目录 一、Docker生态架构 1.1 Docker Containers Are Everywhere 1.2 生态架构 1.2.1 Docker Host 1.2.2 Docker daemon 1.2.3 Registry 1.2.4 Docker client 1.2.5 Image 1.2.6 Container 1.2.7 Docker Dashboard 1.3 Docker版本 二、Docker部署 2.1 使用YUM源部署…

【Linux基础】第31讲 Linux用户和用户组权限控制命令(三)

用户组管理命令 每个用户都有一个用户组&#xff0c;系统可以对一个用户组中的所有用户进行集中管理。不同Linux系统对用户组的规定有所不同。如Linux下的用户属于与它同名的用户组&#xff0c;这个用户组在创建用户时同时创建。用户组的管理涉及用户组的添加、删除和修改。组…

Jmeter 分布式压测

‍你可以使用 JMeter 来模拟高并发秒杀场景下的压力测试。这里有一个例子&#xff0c;它模拟了同时有 5000 个用户&#xff0c;循环 10 次的情况‍。 请求默认配置 token 配置 秒杀接口 结果分析 但是&#xff0c;实际企业中&#xff0c;这种压测方式根本不满足实际需求。下面介…

IPv6协议基本概念

目前大多数设备节点支持IPv6和IPv4双栈,但随着IPv6逐渐应用,某些设备已经只支持纯IPv6,即IPv6 Only。 一、IPv6地址格式 1、IPv6地址表示方式 IPv6可以写成一组8个十六进制数,用冒号(:)分割。也可以写成128位2进制的0和1。 32即8x4,8表示8组16进制数,4表示每组16禁止包…

Vivado下PLL实验

文章目录 前言一、CMT&#xff08;时钟管理单元&#xff09;1、CMT 简介2、FPGA CMT 框图3、MMCM 框图4、PLL 框图 二、创建工程1、创建工程2、PLL IP 核配置3、进行例化 三、进行仿真1、创建仿真文件2、进行仿真设置3、进行行为级仿真 四、硬件验证1、引脚绑定2、生成比特流文…

QT-day4

画一个时钟 widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QPaintEvent> #include <QDebug> #include <QPainter> #include <QTimer> #include <QTime>QT_BEGIN_NAMESPACE namespace Ui { class Widget; } Q…

红队打靶:Fowsniff打靶思路详解(vulnhub)

目录 写在开头 第一步&#xff1a;主机发现和端口扫描 第二步&#xff1a;Web渗透 第三步&#xff1a;pop3服务器渗透 第四步&#xff1a;获取初始立足点 第五步&#xff1a;ssh登陆的banner脚本提权 总结与思考 写在开头 本篇博客在自己的理解之上根据大佬红队笔记的…

初学phar反序列化

以下内容参考大佬博客&#xff1a;PHP Phar反序列化浅学习 - 跳跳糖 首先了解phar是什么东东 Phar是PHP的压缩文档&#xff0c;是PHP中类似于JAR的一种打包文件。它可以把多个文件存放至同一个文件中&#xff0c;无需解压&#xff0c;PHP就可以进行访问并执行内部语句。 默认开…

性能测试 —— Tomcat监控与调优:Jconsole监控

JConsole的图形用户界面是一个符合Java管理扩展(JMX)规范的监测工具&#xff0c;JConsole使用Java虚拟机(Java VM)&#xff0c;提供在Java平台上运行的应用程序的性能和资源消耗的信息。在Java平台&#xff0c;标准版(Java SE平台)6&#xff0c;JConsole的已经更新到目前的外观…

Windows平台Qt6中UTF8与GBK文本编码互相转换、理解文本编码本质

快速答案 UTF8转GBK QString utf8_str"中UTF文"; std::string gbk_str(utf8_str.toLocal8Bit().data());GBK转UTF8 std::string gbk_str_given_by_somewhere"中GBK文"; QString utf8_strQString::fromLocal8Bit(gbk_str_given_by_somewhere.data());正文…

20-SpringCloudAlibaba-3

七 分布式事物处理 1 认识本地事物 什么是事物 事务就是针对数据库的一组操作&#xff0c;它可以由一条或多条SQL语句组成&#xff0c;同一个事务的操作具备同步的特点&#xff0c;事务中的语句要么都执行&#xff0c;要么都不执行。 举个栗子&#xff1a; 你去小卖铺买东西&…

《已解决 Bug TypeError: Cannot read property ‘props‘ of undefined (React)》

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页: &#x1f405;&#x1f43e;猫头虎的博客&#x1f390;《面试题大全专栏》 &#x1f995; 文章图文并茂&#x1f996…

2023_Spark_实验十一:RDD高级算子操作

//checkpoint &#xff1a;sc.setCheckpointDir("hdfs://Master:9000/ck") // 设置检查点val rdd sc.textFile("hdfs://Master:9000/input/word.txt").flatMap(_.split(" ")).map((_,1)).reduceByKey(__) // 执行wordcount任务的转换rdd.checkp…

Windows虚拟机访问网页证书错误问题

问题&#xff1a; 显示证书错误&#xff0c;图片加载不出来&#xff0c;看着很别扭&#xff0c;如下&#xff1a; 方法: 1.先导出可用的证书&#xff1a; 可以将自己正常环境的证书导出来&#xff08;google浏览器为例&#xff09; 浏览器右上角三个竖点——设置——隐私设…

阿里云服务器开放的一个新端口,重启防火墙,端口未启动

问题&#xff1a; 阿里云网页开放的一个新端口后&#xff0c;重启防火墙&#xff0c;端口未启动&#xff0c;之前配置的也都停止了。 解决&#xff1a; 原因可能是阿里的服务控制了&#xff0c;只能一个个端口开启了。把新配置新端口也单独启用。 开启80端口指令 firewall-cm…

mysql启动不了问题

突然昨天早上起来&#xff0c;就发生了这一幕&#xff1a; 启动MySQL服务时出现&#xff02;mysql本地计算机上的MySQL服务启动后停止。某些在未由其他服务或程序使用时将自动停止&#xff02; 几经周折&#xff0c;终于在一个大佬的贴下求得了启动成功的经验&#xff0c;其中我…

Qt---day4---9.20

qt完成时钟&#xff1a; 头文件&#xff1a; #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QPaintEvent> #include <QtDebug> #include <QPainter> #include <QTimerEvent> #include <QTime>QT_BEGIN_NAMESPACE names…