linux驱动开发---day3(自启动创建设备节点进行点灯实验、ioctl函数实现点灯实验)

news2024/11/14 6:51:50

自动创建设备节点udev机制的实现过程

  1. udev是自动创建设备节点,逻辑在用户空间
  2. 过程:1)首先注册字符设备驱动,得到标识设备的设备号
               2)获得设备信息后,先创建一个设备类,向上提交目录信息,使用class_create
               3)然后创建设备对象,向上提交设备节点信息,使用device_create
               4)在向上提交设备节点信息后,会自动发起热插拔事件通知udev进程去查询设备节点的信息,以及在/dev下创建设备节点

自动创建设备节点:(led点灯)

my_led.c:

#include <linux/init.h>
#include <linux/module.h>
#include<linux/fs.h>
#include<linux/io.h>
#include<linux/device.h>
#include"head.h"

int major;
char kbuf[128]={0};
gpio_t *vir_led1;
gpio_t *vir_led2;
gpio_t *vir_led3;
unsigned int *vir_rcc;
struct class *cls;
struct device *dev;
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)
{
    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
     unsigned long ret;
    //向用户空间读取拷贝
    if(size>sizeof(kbuf))//用户空间期待读取的大小内核满足不了,那就给内核支持的最大大小
        size=sizeof(kbuf);
    ret=copy_to_user(ubuf,kbuf,size);
    if(ret)//拷贝失败
    {
        printk("copy_to_user filed\n");
        return ret;
    }
    return 0;
}
ssize_t mycdev_write(struct file *file, const char  *ubuf, size_t size, loff_t *lof)
{
    unsigned long ret;
    //从用户空间读取数据
    if(size>sizeof(kbuf))//用户空间期待读取的大小内核满足不了,那就给内核支持的最大大小
        size=sizeof(kbuf);
    ret=copy_from_user(kbuf,ubuf,size);
    if(ret)//拷贝失败
    {
        printk("copy_to_user filed\n");
        return ret;
    }
    switch(kbuf[0]){
        case '1'://LED1
            if(kbuf[1]=='0')//关灯
                vir_led1->ODR &= (~(1<<10));
            else//开灯
                vir_led1->ODR |= 1<<10;
            break;
        case '2'://LED2
            if(kbuf[1]=='0')//关灯
                vir_led2->ODR &= (~(1<<10));
            else//开灯
                vir_led2->ODR |= 1<<10;
            break;
        case '3'://LED3
            if(kbuf[1]=='0')//关灯
                vir_led3->ODR &= (~(1<<8));
            else//开灯
                vir_led3->ODR |= 1<<8;
            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,
    .read=mycdev_read,
    .write=mycdev_write,
    .release=mycdev_close,
};

int all_led_init(void)
{
    //寄存器地址的映射
    vir_led1=ioremap(PHY_LED1_ADDR,sizeof(gpio_t));
    if(vir_led1==NULL)
    {
        printk("ioremap filed:%d\n",__LINE__);
        return -ENOMEM;
    }
     vir_led2=ioremap(PHY_LED2_ADDR,sizeof(gpio_t));
    if(vir_led2==NULL)
    {
        printk("ioremap filed:%d\n",__LINE__);
        return -ENOMEM;
    }
     vir_led3=vir_led1;
    vir_rcc=ioremap(PHY_RCC_ADDR,4);
    if(vir_rcc==NULL)
    {
        printk("ioremap filed:%d\n",__LINE__);
        return -ENOMEM;
    }
    printk("物理地址映射成功\n");
    //寄存器的初始化
    //rcc
    (*vir_rcc) |= (3<<4);
    //led1
    vir_led1->MODER &= (~(3<<20));
    vir_led1->MODER |= (1<<20);
    vir_led1->ODR &= (~(1<<10));
    //led2
    vir_led2->MODER &= (~(3<<20));
    vir_led2->MODER |= (1<<20);
    vir_led2->ODR &= (~(1<<10));
    //led3
    vir_led3->MODER &= (~(3<<16));
    vir_led1->MODER |= (1<<16);
    vir_led1->ODR &= (~(1<<8));
    printk("寄存器初始化成功\n");

    return 0;
}

static int __init mycdev_init(void)
{
    //字符设备驱动注册
    major=register_chrdev(0,"mychrdev",&fops);
    if(major<0)
    {
        printk("字符设备驱动注册失败\n");
        return major;
    }
    printk("字符设备驱动注册成功:major=%d\n",major);

    //寄存器映射以及初始化
    all_led_init();

    //向上提交目录
    cls=class_create(THIS_MODULE,"mychrdev");
    if(IS_ERR(cls))
    {
        printk("向上提交目录失败\n");
        return -PTR_ERR(cls);
    }
    printk("向上提交目录成功\n");

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

    return 0;
}
static void __exit mycdev_exit(void)
{
    //销毁设备节点信息
    int i;
    for(i=0;i<3;i++)
    {
        device_destroy(cls,MKDEV(major,i));
    }
    //销毁目录信息
    class_destroy(cls);
    //取消地址映射
    iounmap(vir_led1);
    iounmap(vir_led2);
    iounmap(vir_rcc);
    //注销字符设备驱动
    unregister_chrdev(major,"mychrdev");


}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

test.c

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


int main(int argc, char const *argv[])
{
    char buf[128]={0};
    int fd=open("/dev/mychrdev0",O_RDWR);
    if(fd<0)
    {
        printf("打开设备文件失败\n");
        exit(-1);
    }
    while(1)
    {
        //从终端读取
        printf("请输入两个字符\n");
        printf("第一个字符:1(LED1) 2(LED2) 3(LED3)\n");
        printf("第二个字符:0(关灯) 1(开灯)\n");
        printf("请输入>");
        fgets(buf,sizeof(buf),stdin);
        buf[strlen(buf)-1]='\0';
        //向设备文件中写
        write(fd,buf,sizeof(buf));
    }

    
    close(fd);

    return 0;
}

head.h

#ifndef __HEAD_H__
#define __HEAD_H__ 
typedef struct{
    unsigned int MODER;
    unsigned int OTYPER;
    unsigned int OSPEEDR;
    unsigned int PUPDR;
    unsigned int IDR;
    unsigned int ODR;
}gpio_t;
#define PHY_LED1_ADDR 0X50006000
#define PHY_LED2_ADDR    0X50007000
#define PHY_LED3_ADDR 0X50006000
#define PHY_RCC_ADDR    0X50000A28
#endif 

测试过程:

make arch=arm modname=my_led

cp my_led.ko ~/nfs/rootfs/

arm-linux-gnueabihf-gcc test.c 

 cp a.out ~/nfs/rootfs/

---------------------------------------------------------------------------------------------------------------------------------

insmod my_led.ko---------->./a.out

通过ls -l /dev/mychrdev*指令查看创建的设备文件

rmmod my_led

测试现象:

ioctl函数实现点灯实验---添加第三个参数(第三个参数为指针)

myled.c:

#include <linux/init.h>
#include <linux/module.h>
#include<linux/fs.h>
#include<linux/io.h>
#include<linux/device.h> 
#include"head.h"

int major;
char kbuf[128]={0};
gpio_t *vir_led1;
gpio_t *vir_led2;
gpio_t *vir_led3;
unsigned int *vir_rcc;
struct class *cls;
struct device *dev;
int mycdev_open(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    return 0;
}

long mydev_ioctl(struct file* file,unsigned int cmd,unsigned long arg)
{
    int which;
    //获取arg对应用户空间中的值
    int ret=copy_from_user(&which,(void *)arg,4);
    if(ret)
    {
        printk("从用户空间获取数据失败\n");
        return -EIO;
    }
    switch(cmd)
    {
        case LED_ON://开灯
        switch(which)
        {
            case 1:
                 vir_led1->ODR |= 1<<10;
                break;
            case 2:
                vir_led2->ODR |= 1<<10;
                break;
            case 3:
                vir_led3->ODR |= 1<<8;
                break;
        }
        break;
        case LED_OFF: //关灯
        switch(which)
        {
            case 1:
                 vir_led1->ODR &= (~(1<<10));
                break;
            case 2:
                vir_led2->ODR &= (~(1<<10));
                break;
            case 3:
                vir_led3->ODR &= (~(1<<8));
                break;
        }
        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,
    .unlocked_ioctl=mydev_ioctl,
    .release=mycdev_close,
};

int all_led_init(void)
{
    //寄存器地址的映射
    vir_led1=ioremap(PHY_LED1_ADDR,sizeof(gpio_t));
    if(vir_led1==NULL)
    {
        printk("ioremap filed:%d\n",__LINE__);
        return -ENOMEM;
    }
     vir_led2=ioremap(PHY_LED2_ADDR,sizeof(gpio_t));
    if(vir_led2==NULL)
    {
        printk("ioremap filed:%d\n",__LINE__);
        return -ENOMEM;
    }
     vir_led3=vir_led1;
    vir_rcc=ioremap(PHY_RCC_ADDR,4);
    if(vir_rcc==NULL)
    {
        printk("ioremap filed:%d\n",__LINE__);
        return -ENOMEM;
    }
    printk("物理地址映射成功\n");
    //寄存器的初始化
    //rcc
    (*vir_rcc) |= (3<<4);
    //led1
    vir_led1->MODER &= (~(3<<20));
    vir_led1->MODER |= (1<<20);
    vir_led1->ODR &= (~(1<<10));
    //led2
    vir_led2->MODER &= (~(3<<20));
    vir_led2->MODER |= (1<<20);
    vir_led2->ODR &= (~(1<<10));
    //led3
    vir_led3->MODER &= (~(3<<16));
    vir_led1->MODER |= (1<<16);
    vir_led1->ODR &= (~(1<<8));
    printk("寄存器初始化成功\n");

    return 0;
}

static int __init mycdev_init(void)
{
    //字符设备驱动注册
    major=register_chrdev(0,"mychrdev",&fops);
    if(major<0)
    {
        printk("字符设备驱动注册失败\n");
        return major;
    }
    printk("字符设备驱动注册成功:major=%d\n",major);

    //寄存器映射以及初始化
    all_led_init();

    //向上提交目录
    cls=class_create(THIS_MODULE,"mychrdev");
    if(IS_ERR(cls))
    {
        printk("向上提交目录失败\n");
        return -PTR_ERR(cls);
    }
    printk("向上提交目录成功\n");

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

    return 0;
}
static void __exit mycdev_exit(void)
{
    //销毁设备节点信息
    int i;
    for(i=0;i<3;i++)
    {
        device_destroy(cls,MKDEV(major,i));
    }
    //销毁目录信息
    class_destroy(cls);
    //取消地址映射
    iounmap(vir_led1);
    iounmap(vir_led2);
    iounmap(vir_rcc);
    //注销字符设备驱动
    unregister_chrdev(major,"mychrdev");


}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

test.c:

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


int main(int argc, char const *argv[])
{
    int a,b;
    char buf[128]={0};
    int fd=open("/dev/mychrdev0",O_RDWR);
    if(fd<0)
    {
        printf("打开设备文件失败\n");
        exit(-1);
    }
    while(1)
    {
        //从终端读取
        printf("请输入对LED灯的控制:0(关灯) 1(开灯)\n");
        scanf("%d",&a);
        printf("请输入要控制的灯:1(LED1) 2(LED2) 3(LED3)\n");
        scanf("%d",&b);
        switch(a)
        {
            case 1:
                ioctl(fd,LED_ON,&b);//开灯
                break;
            case 0:
                ioctl(fd,LED_ON,&b);//关灯
                break;
        }
    }

    close(fd);

    return 0;
}

head.h:

#ifndef __HEAD_H__
#define __HEAD_H__ 
typedef struct{
    unsigned int MODER;
    unsigned int OTYPER;
    unsigned int OSPEEDR;
    unsigned int PUPDR;
    unsigned int IDR;
    unsigned int ODR;
}gpio_t;
#define PHY_LED1_ADDR 0X50006000
#define PHY_LED2_ADDR    0X50007000
#define PHY_LED3_ADDR 0X50006000
#define PHY_RCC_ADDR    0X50000A28

//构建LED开关功能码,添加ioctl第三个参数
#define LED_ON _IOW('l',1,int)
#define LED_OFF _IOW('l',0,int)
#endif 

测试步骤同上,以下为现象:

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

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

相关文章

微服务如何改变软件开发:实战经验与最佳实践分享

文章目录 什么是微服务&#xff1f;微服务实战经验1. 定义明确的服务边界2. 使用API网关3. 自动化部署和持续集成4. 监控和日志记录 微服务最佳实践1. 文档和通信2. 弹性设计3. 安全性4. 版本控制5. 监控和警报 微服务的未来 &#x1f389;欢迎来到架构设计专栏~微服务如何改变…

【Redis专题】一线大厂Redis高并发缓存架构实战与性能优化

目录 前言为什么要用Redis前置知识一、缓存问题1.1 缓存击穿1.2 缓存穿透1.3 缓存雪崩 二、缓存方案2.1 普通缓存2.2 冷热分离2.3 多级缓存2.4 缓存预热 *课程内容一、一个案例引发的思考&#xff08;电商场景&#xff09;二、代码优化方案的演进2.1 问题一&#xff1a;纯【读】…

自动生成bug异常追踪-SRE与开发自动化协同

作者&#xff1a;观测云 数据智能部 产品方案架构师 范莹莹 简介 生产环境 bug 的定义&#xff1a;RUM 应用和 APM 应用的 error_stack 信息被捕捉后成为 bug。 以 APM 新增错误巡检为例&#xff0c;当出现新错误时&#xff0c;在观测云控制台的「事件」模块下生成新的事件报…

比elk还香的日志平台

作者 观测云 产品技术专家 深圳办公室 黄小龙 写在前面的话 日志是开发人员记录系统运行状态的最佳手段&#xff0c;是一个系统的重要组成部分。日志通常不属于系统的核心功能&#xff0c;但却是我们了解系统运行用的最多的功能。对于开发和运维人员来说&#xff0c;好的日志可…

LeetCode【28. 找出字符串中第一个匹配项的下标】

不要用珍宝装饰自己&#xff0c;而要用健康武装身体 给你两个字符串 haystack 和 needle &#xff0c;请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标&#xff08;下标从 0 开始&#xff09;。如果 needle 不是 haystack 的一部分&#xff0c;则返回 -1 。 …

微软亚洲研究院:给张图片普通人也能轻松完成图片编辑

效果 代码 https://github.com/Fantasy-Studio/Paint-by-Example 问题 之前图像编辑使用语言引导&#xff0c;作者研究使用example作为引导来进行图像编辑&#xff0c;直接融合&#xff08;复制粘贴&#xff09;会导致明显的融合伪影 利用生成先验&#xff1a;从预训练的文…

事务管理AOP

事务管理 事务回顾 概念&#xff1a;事务是一组操作的集合&#xff0c;它是一个不可分割的工作单位&#xff0c;这些操作要么同时成功&#xff0c;要么同时失败 操作&#xff1a; 开启事务&#xff1a;一组操作开始前&#xff0c;开启事务&#xff0d;start transaction/be…

使用SSH通过FinalShell远程连接Ubuntu服务器

文章目录 SSH远程连接Ubuntu服务器Ubuntu服务器端安装SSH服务用户创建及sudo权限配置Linux账号管理useradd&#xff08;adduser&#xff09;userdelusermod切换用户 FinalShell端 参考文献 Windows系统Ubuntu16.04.6&#xff08;以上版本通用&#xff09;虚拟机、服务器 SSH远…

飞机大战小游戏

欢迎来到程序小院 飞机大战 玩法&#xff1a; 单机屏幕任意位置开始&#xff0c;点击鼠标左键滑动控制飞机方向&#xff0c;射击打掉飞机&#xff0c;途中遇到精灵吃掉可产生联排发送子弹&#xff0c;后期会有Boss等来战哦^^。开始游戏https://www.ormcc.com/play/gameStart/1…

Java 并发编程面试题——Fork/Join 框架

目录 1.什么是 Fork/Join 框架&#xff1f;2.什么是工作窃取算法&#xff1f;它有什么作用&#xff1f;有什么优缺点&#xff1f;3.如何设计一个 Fork/Join 框架&#xff1f;4.如何使用 Fork/Join 框架&#xff1f;5.Fork/Join 框架的实现原理是什么&#xff1f;5.1.ForkJoinTa…

数据结构-leetcode-数组形式的整数加法

解题图解&#xff1a; 下面是代码&#xff1a; /*** Note: The returned array must be malloced, assume caller calls free().*/ int* addToArrayForm(int* num, int numSize, int k, int* returnSize){int k_tem k;int klen0;while(k_tem){//看看k有几位k_tem /10;klen;}i…

【无公网IP内网穿透】Java支付宝沙箱环境支付,SDK接口远程调试

目录 1.测试环境 2.本地配置 3. 内网穿透 3.1 下载安装cpolar内网穿透 3.2 创建隧道 4. 测试公网访问 5. 配置固定二级子域名 5.1 保留一个二级子域名 5.2 配置二级子域名 6. 使用固定二级子域名进行访问 1.测试环境 MavenSpring bootJdk 1.8 2.本地配置 获取支付…

MapModule模块 基于folium的二次封装,让绘图更简洁

背景 开源库 GpsAndMap 的 MapModule 模块基于 folium 模块进行了二次封装&#xff0c;引入 GpsModule 模块中的GPS坐标类作为数据基础&#xff0c;集成了10种地图瓦片可以直接添加引用&#xff0c;提供了直观便利的图层管理功能。 引入模块 pip install GpsAndMap 模块结构…

将整数S按照指定的进制输出为RR的数据类型为字符串numpy.base_repr

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 将整数S按照指定的进制输出为R R的数据类型为字符串 numpy.base_repr 下列代码最后一次输出的结果是&#xff1f; import numpy as np num 10 print("【显示】num&#xff1a;", …

考研408 | 【计算机组成原理】 数据的表示和运算

进位计数制 十进制计数法&#xff1a; 推广&#xff1a;r进制计数法 任意进制-->十进制&#xff1a; 二进制<-->八进制、十六进制&#xff1a; 各种进制的常见书写方式&#xff1a; 十进制-->任意进制&#xff1a; 十进制-->二进制&#xff08;拼凑法&#xff…

七个LLM的狼人杀之夜

深度学习自然语言处理 原创作者&#xff1a;Winnie 在最新一篇论文中&#xff0c;研究团队让一群大语言模型(LLM)开了一局狼人杀游戏。通过多种Prompt方法集成&#xff0c;LLM不仅成功地参与了游戏&#xff0c;还涌现出了信任、欺诈和领导力等团体能力。 Paper: Exploring Larg…

校园网web免认真,大量服务器

服务器加满了&#xff0c;没有几个人来&#xff0c;传点图片看实力 什么方法解web认证方式校园网&#xff1f; 一般的校园网是对学生免费开放的&#xff0c;假如你是学生输入学号密码上网就是了&#xff0c;假如你不是那就是想蹭网了&#xff0c;再假如你不想让管理员或上网行为…

idea部署javaSE项目(awt+swing项目)/idea导入eclipse的javaSE项目

一.idea打开项目 选择需要部署的项目 二、设置JDK 三、引入数据库驱动包 四、执行sql脚本 四、修改项目的数据库连接 找到数据库连接文件 五.其他系统实现 JavaSwing实现学生选课管理系统 JavaSwing实现学校教务管理系统 JavaSwingsqlserver学生成绩管理系统 JavaSwing用…

【mysql】1044 - Access denied for user ‘root‘@‘%‘ to database ‘test_job‘

登录mysql 为新建的库创建账号和授权 创建和授权命令 grant all on test_job.* to job% identified by 12345;flush privileges; 返回信息 grant all on test_job.* to job% identified by 12345 > 1044 - Access denied for user root% to database test_job > 查询…

vscode和HBuilderx设置快捷键注释

一、vscode设置快捷键注释 1.打开vscode&#xff0c;使用快捷键&#xff1a;ctrlshiftp mac的话快捷键是&#xff1a;commandshiftp 然后在行中输入snippets 2.选择“新建”&#xff0c;选择将要配置的文件类型&#xff08;以vue类型为例&#xff09;我这里创建的名字为vue.…