ARM——驱动——inmod加载内核模块

在上一篇文章的代码上添加出错处理

#include <linux/init.h>       // 包含初始化宏和函数  
#include <linux/kernel.h>     // 包含内核函数和变量  
#include <linux/fs.h>         // 包含文件操作的结构和函数  
#include <linux/kdev_t.h>     // 旧的设备号定义,现在通常包含在<linux/kdev_t.h>或不需要直接包含  
#include <linux/cdev.h>       // 包含字符设备操作的结构和函数  
#include <linux/module.h>     // 包含模块操作的宏和函数  // 定义主设备号和次设备号  
#define MAJOR_NUM 255  
#define MINOR_NUM 0  
// 定义设备名称  
#define DEV_NAME "demo"  
// 定义要注册的设备数量(这里为1,因为次设备号固定为0)  
#define DEV_NUM 1  // 文件打开操作  
static int open (struct inode * inode, struct file * file)  
{  printk("demo open ...\n");  return 0; // 成功返回0  
}  // 文件读取操作  
static ssize_t read (struct file * file, char __user * buf, size_t len, loff_t * offset)  
{  printk("demo read ...\n");  return 0; // 这里仅作为示例,实际应该返回读取的字节数  
}  // 文件写入操作  
static ssize_t write (struct file * file, const char __user * buf, size_t len, loff_t * offset)  
{  printk("demo write ...\n");  return 0; // 这里仅作为示例,实际应该返回写入的字节数  
}  // 文件关闭操作,注意这里使用了.release而不是.close,因为.close在Linux 2.6.33之后被弃用  
static int close (struct inode * inode, struct file * file)  
{  printk("demo close ...\n");  return 0; // 成功返回0  
}  // 文件操作结构  
static struct file_operations fops =   
{  .owner = THIS_MODULE,  // 指向拥有该结构的模块  .open = open,          // 打开操作  .read = read,          // 读取操作  .write = write,        // 写入操作  .release = close       // 关闭操作(注意使用.release)  
};  // 字符设备结构体  
static struct cdev cdev;  
// 设备号  
static dev_t dev;  // 模块初始化函数  
static int __init demo_init(void)  
{  int ret = 0;  dev = MKDEV(MAJOR_NUM, MINOR_NUM); // 创建设备号  cdev_init(&cdev, &fops); // 初始化字符设备  // 添加字符设备到系统  ret = cdev_add(&cdev, dev, DEV_NUM);  if(ret < 0)  goto err_cdev_add;  // 注册设备号到系统,但这一步实际上是多余的,因为我们已经手动指定了主设备号  // 并且通常在调用cdev_add之前不需要调用register_chrdev_region  // 除非你不确定主设备号是否已经被占用,或者你想要动态分配一个主设备号  ret = register_chrdev_region(dev, DEV_NUM, DEV_NAME);  if(ret < 0)  goto err_register_chrdev_region;  printk("demo_init  ###################\n");  return ret; // 这里应该返回0表示成功,但由于之前的错误处理,这里实际上不会执行  err_cdev_add:  cdev_del(&cdev); // 如果cdev_add失败,则删除字符设备  printk("demo cdev_add failed\n");  return ret; // 返回错误码  err_register_chrdev_region:  unregister_chrdev_region(dev, DEV_NUM); // 如果register_chrdev_region失败,则注销设备号  cdev_del(&cdev); // 删除字符设备  printk("demo register_chrdev_region failed\n"); // 注意这里修正了打印信息  return ret; // 返回错误码  
}  // 模块清理函数  
static void __exit demo_exit(void)  
{  unregister_chrdev_region(dev, DEV_NUM); // 注销设备号  cdev_del(&cdev); // 删除字符设备  printk("demo_exit  ################

 一、驱动模块的加载

静态 编译到内核
这种是必须要加载到内核中的驱动,或者特别特别常用的驱动,只要开机就启动。

动态 编译到模块中

开机以后自己安装的驱动 开机后加载,试一下功能,不行就卸载掉,省区每次都要开机的情况。

1、在menu中将选项选成M(M即编译成一个模块)

但如果想要编译成模块 ,
之前不能<*>即,原来的内核中就不能加载过,
如果加载过在使用模块加载,会导致重复加载。

所以:需要先不选中再把uImage复制到根文件系统中。

make出一个uImage

再拷贝到根目录文件中。

2、选中<M>后,make modules  对应模块选项配置位M

将这个文件考到根目录文件中(也可以直接开发板挂载)

3、加载驱动模块

(1)用命令

insmod demo.ko    //动态加载驱动模块

insmod命令用于将指定的模块文件(通常是.ko文件)加载到Linux内核中。这使得用户可以在不重启系统的情况下,向内核添加新的功能或驱动。

关于GPL

GPL许可证的主要目标是保证软件对用户是自由的,即用户可以自由地运行、复制、分发、研究、改变和改进软件。同时,它要求软件必须以源代码的形式发布,以便其他人可以在此基础上进行修改和再发布。

任何遵循GPL许可发布的软件,其源代码都是开放的,任何人在任何时间都可以获取、修改和重新发布,但必须保证修改后的软件也遵循GPL许可。

GPL许可证有两个版本:GPL v2和G

删除原先的mod再insmod即可

(2)查看已经动态加载的驱动模块 

lsmod 

(3)卸载驱动模块

rmmod

4、设置设备节点,运行程序

运行app程序即再/home/Linux/tftpboot中写一个app程序

运行成功。

二、写一个led驱动加载模块

(1)ioremap

void __iomem *ioremap(phys_addr_t offset, unsigned long size);

ioremap 函数是用来将物理内存地址映射到内核的虚拟地址空间上,使得内核可以像访问普通内存一样访问这部分物理内存。

用这个函数在    static int __init led_init(void)  将物理地址映射到虚拟地址上,使内核可以访问地址。

(2)iounmap解除映射函数

void iounmap(void __iomem *addr);

 

在Linux内核中,ioremap 函数是用来将物理内存地址映射到内核的虚拟地址空间上,使得内核可以像访问普通内存一样访问这部分物理内存。这在设备驱动开发中尤其常见,因为许多硬件设备都需要通过特定的物理地址来访问其寄存器或内存区域

在 static void __exit led_exit(void)中用这个函数来解除寄存器地址与内核的映射

(3)声明寄存器的地址中添加寄存器的地址

(5)在/drivers/char/Makefile 中加入  命令

(5)/drivers/char/Kconfig中增加选项

(6)进入开发板按照一中运行。

(7)源代码

驱动程序

led.c

#include<linux/init.h>
#include<linux/kernel.h>
#include<linux/fs.h>
#include<linux/kdev_t.h>
#include<linux/cdev.h>
#include<linux/module.h>
#include <linux/io.h>#define MAJOR_NUM 256
#define MINOR_NUM 0
#define DEV_NAME "led"
#define DEV_NUM 1#define GPBCON 0x56000010
#define GPBDAT 0x56000014static volatile unsigned long * gpbcon;
static volatile unsigned long * gpbdat;void init_led(void)						
{// 配置GPB5引脚功能为输出*gpbcon &= ~(3 << 10);*gpbcon |= (1 << 10);// 将GPB5引脚电平置高*gpbdat |= (1 << 5);
}void led_on(void)
{// 将GPB5引脚电平置低*gpbdat &= ~(1 << 5);
printk("led_on!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");}void led_off(void)
{// 将GPB5引脚电平置高*gpbdat |= (1 << 5);
printk("led_off!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
}static int open(struct inode *inode,struct file *file)
{init_led();printk("led open..\n");return 0;
}static ssize_t read (struct file * file, char __user * buf, size_t len, loff_t * offset)
{printk("led read ...\n");return 0;
}static ssize_t write (struct file * file, const char __user * buf, size_t len, loff_t * offset)
{if(strcmp(buf,"led_on")==0){led_on();}if(strcmp(buf,"led_off")==0){led_off();}printk("led write ...\n");return 0;
}static int close (struct inode * inode, struct file * file)
{printk("led close ...\n");return 0;
}static struct file_operations fops=
{.owner = THIS_MODULE,.open  =  open,.read  =  read,.write =  write,.release = close
};static struct cdev cdev;
static dev_t dev;static int __init led_init(void)
{int ret = 0;dev=MKDEV(MAJOR_NUM,MINOR_NUM);cdev_init(&cdev,&fops);ret =cdev_add(&cdev,dev,DEV_NUM);if(ret<0){goto err_cdev_add;}ret = register_chrdev_region(dev,DEV_NUM,DEV_NAME);if(ret<0){goto err_register_chrdev_region;}printk("led_init ##########################\n");gpbcon=ioremap(GPBCON,sizeof(*gpbcon));gpbdat=ioremap(GPBDAT,sizeof(*gpbdat));return ret;err_cdev_add:cdev_del(&cdev);printk("led cdev_add failed\n");return ret;
err_register_chrdev_region:unregister_chrdev_region(dev,DEV_NUM);cdev_del(&cdev);printk("led resgister_chrdev_region\n");return ret;
}static void __exit led_exit(void)
{iounmap(gpbcon);iounmap(gpbdat);unregister_chrdev_region(dev,DEV_NUM);cdev_del(&cdev);printk("led_exit #########################\n");
}module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");

led_app.c

#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>int main(int argc, const char *argv[]) {int fd = open("/dev/led", O_RDWR);if (fd < 0) {perror("open led");return -1;}while(1){write(fd,"led_on",strlen("led_on"));sleep(2);write(fd,"led_off",strlen("led_off"));sleep(2);}close(fd);return 0;
}

(7)其他注意事项!!!!

但这里不能用strcpy和memcpy,因为是从内村空间往内核空间拷贝函数

用一个函数   copy_from_user()

copy_from_user() 是 Linux 内核中用于从用户空间安全地复制数据到内核空间的一个函数。这个函数是内核提供的一种机制,用于在用户程序和内核之间传递数据,但确保了内核在访问用户空间内存时不会引发错误(如访问违规或段错误)。

函数原型

unsigned long copy_from_user(void *to, const void __user *from, unsigned long n);

  • to:指向内核空间中的缓冲区的指针,数据将被复制到这个缓冲区。
  • from:指向用户空间中的数据的指针。注意,这个指针被标记为 __user,这是一个特殊的类型,用于指示该指针指向用户空间。
  • n:要复制的字节数。

这里的字节数不能乱写,所以定义一个len_cp看谁小就拷贝谁,保证谁的空间都不会超过

修正好的源代码如下

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <linux/module.h>
#include <asm/io.h>
#include <asm/string.h>
#include <asm/uaccess.h>#define MAJOR_NUM 253
#define MINOR_NUM 0
#define DEV_NAME "led"
#define DEV_NUM 1
#define GPBCON 0x56000010
#define GPBDAT 0x56000014static volatile unsigned long * gpbcon;
static volatile unsigned long * gpbdat;static void init_led(void)						
{// 配置GPB5引脚功能为输出*gpbcon &= ~(3 << 10);*gpbcon |= (1 << 10);// 将GPB5引脚电平置高*gpbdat |= (1 << 5);
}static void led_on(void)
{// 将GPB5引脚电平置低*gpbdat &= ~(1 << 5);
}static void led_off(void)
{// 将GPB5引脚电平置高*gpbdat |= (1 << 5);
}static int open (struct inode * inode, struct file * file)
{init_led();printk("led open ...\n");return 0;
}static ssize_t read (struct file * file, char __user * buf, size_t len, loff_t * offset)
{//copy_to_user(buf, data, len);printk("led read ...\n");return 0;
}static ssize_t write (struct file * file, const char __user * buf, size_t len, loff_t * offset)
{unsigned char data[12] = {0};size_t len_cp = sizeof(data) < len ? sizeof(data) : len;copy_from_user(data, buf, len_cp);if(!strcmp(data, "ledon"))led_on();else if(!strcmp(data, "ledoff"))led_off();elsereturn -1;printk("led write ...\n");return len_cp;
}static int close (struct inode * inode, struct file * file)
{printk("led close ...\n");return 0;
}static struct file_operations fops = 
{.owner = THIS_MODULE,.open = open,.read = read,.write = write,.release = close
};
static struct cdev cdev;
static dev_t dev;static int __init led_init(void)
{int ret = 0;dev = MKDEV(MAJOR_NUM, MINOR_NUM);cdev_init(&cdev, &fops);ret = cdev_add(&cdev, dev, DEV_NUM);if(ret < 0)goto err_cdev_add;ret = register_chrdev_region(dev, DEV_NUM, DEV_NAME);if(ret < 0)goto err_register_chrdev_region;gpbcon = ioremap(GPBCON, sizeof(*gpbcon));gpbdat = ioremap(GPBDAT, sizeof(*gpbdat));printk("led_init  ...\n");return ret;err_cdev_add:cdev_del(&cdev);printk("led cdev_add failed\n");return ret;err_register_chrdev_region:unregister_chrdev_region(dev, DEV_NUM);cdev_del(&cdev);printk("led register_chrdev_region\n");	return ret;
}static void __exit led_exit(void)
{iounmap(gpbcon);iounmap(gpbdat);unregister_chrdev_region(dev, DEV_NUM);cdev_del(&cdev);printk("led_exit  ###############################\n");
}module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");

三、自动创建设备节点

(1)关于设备

①字符设备(数据的访问是有顺序的)

定义
字符设备是指在I/O传输过程中以字符为单位进行传输的设备。这类设备在数据传输时,数据被视为连续的字符流,逐个字符地进行读写操作。

特点

  • 数据传输单位:以字符为单位,即字节流的形式进行数据传输。
  • 缓冲区:通常没有内部缓冲区,数据会立即传输到设备或从设备中读取,这意味着字符设备的读写操作是实时的。
  • 访问模式:通常是顺序访问,即按照数据的顺序进行读写操作,不支持随机访问。
  • 应用场景:常用于与用户进行交互的设备,如键盘、鼠标、串口等,以及某些需要按字符处理的设备。

在操作系统中的表示
在UNIX或Linux系统中,字符设备以特别文件方式在文件目录树中占据位置并拥有相应的结点。这些设备文件可以使用与普通文件相同的文件操作命令进行打开、关闭、读、写等操作。

②块设备(数据访问是随机的)

定义
块设备是I/O设备中的一类,它将信息存储在固定大小的块中,每个块都有自己的地址。这类设备允许在设备的任意位置读取一定长度的数据。

特点

  • 数据传输单位:以固定大小的块为单位进行数据传输,数据块的大小通常在512字节到32768字节之间。
  • 缓冲区:通常具有内部缓冲区,用于在访问硬件之前缓存数据,以提高数据传输效率。
  • 访问模式:支持随机访问,即可以按块为单位在设备的任意位置进行读写操作。
  • 应用场景:常用于需要大量数据存储和读写操作的场景,如硬盘、U盘、SD卡等。

在操作系统中的表示
在操作系统中,块设备通过块设备驱动程序来管理和控制其读写操作。这些驱动程序负责提供访问设备的接口和功能,并处理设备的读写请求。

③网络设备(以套接字形式存在,只有名字,会集成负载的网络协议)

定义
网络设备是用来将各类服务器、PC、应用终端等节点相互连接,构成信息通信网络的专用硬件设备。

类型
网络设备包括信息网络设备、通信网络设备、网络安全设备等,具体如交换机、路由器、防火墙、网桥、集线器、网关、VPN服务器、网络接口卡(NIC)、无线接入点(WAP)、调制解调器、5G基站、光端机、光纤收发器等。

作用
网络设备的主要作用是构建网络,实现不同节点之间的信息通信和数据传输。它们通过物理连接和逻辑协议,将各个独立的计算设备连接成一个整体的网络系统。

综上所述,字符设备、块设备和网络设备在数据传输方式、应用场景和操作系统中的作用等方面存在显著差异。每种设备都有其独特的特点和用途,共同构成了现代计算机系统和网络的基础。


为什么我们的驱动需要手动创建节点呢

下一篇文件介绍自动设置节点。欢迎批评指正

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PyTorch——transforms

接着上一篇&#xff0c;我们这一篇讲transforms 1、什么是transform 首先transform是来自PyTorch的一个扩展库——【torchvision】&#xff0c;【torchvision】这个库提供了许多计算机视觉相关的工具和功能&#xff0c;能够在神经网络中&#xff0c;将图像、数据集、预处理模型…

【系统架构设计】软件架构设计(1)

【系统架构设计】软件架构设计&#xff08;1&#xff09; 软件架构概述架构需求与软件质量属性软件架构风格数据流风格批处理序列管道-过滤器2者风格比较 仓库风格--黑板系统 层次系统架构风格二层及三层C/S架构风格MVCMVP 面向服务的架构 软件架构概述 基于架构的软件开发模型…

网络通信tcp

一、udp案例 二、基于tcp: tcp //c/s tcp 客户端: 1.建立连接 socket bind connect 2.通信过程 read write close tcp服务器: 1.建立连接 socket bind listen accept 2.通信过程 read write close connect函数 int connect(int sockfd, con…

postgresql 集群文档

https://www.cnblogs.com/Alicebat/p/14148933.html [命令] Pacemaker 命令 pcs cluster &#xff08;管理节点&#xff09; – Eternal Center PostgreSQL实战之物理复制和逻辑复制&#xff08;五&#xff09;_postgresql 流复制和物理复制-CSDN博客 https://jingyan.baidu…

【Windows】深度学习环境部署

引言 1 Windows环境准备 1.1 VSCode Visual Studio Code&#xff08;简称 VSCode&#xff09;是一款由微软开发的开源代码编辑器。它非常受开发者欢迎&#xff0c;因为它功能强大、扩展性好&#xff0c;并且支持多种编程语言。VSCode 尤其适合 Python 开发&#xff0c;特别是…

网络 通信

一、客户端接收(也可以bind) 1. socket socket 函数 用于创建一个套接字&#xff08;socket&#xff09;&#xff0c;这是网络通信的基础。 它的原型如下&#xff1a;int socket(int domain, int type, int protocol); 参数&#xff1a; domain&#xff1a;指定协议族&…

虚幻5|AI视力系统,听力系统,预测系统(3)预测系统

虚幻5|AI视力系统&#xff0c;听力系统&#xff0c;预测系统&#xff08;2&#xff09;听力系统-CSDN博客 一&#xff0c;添加一个AI预感感官配置 1.选中我们的AIPerception,右侧细节添加一个AI预感感官配置&#xff0c;然后我们把所有感官的年龄都调成5&#xff0c;是所有 2…

全网最简单的Java设计模式【九】策略模式-实战中最常用的设计模式之一

策略模式是一种行为设计模式&#xff0c;它允许你定义一系列的算法&#xff0c;把它们一个个封装起来&#xff0c;并且使它们可以相互替换。该模式让算法的变化独立于使用算法的客户。在实际开发中&#xff0c;策略模式可以帮助我们减少大量的 if-else 或 switch 条件判断语句&…

premiere2023暴力压缩视频

目录 1. 需求说明2. 压缩流程 1. 需求说明 要将三段视频拼接起来&#xff0c;时长超过了1h&#xff0c;然后压缩到200M以内的视频。 这是三段视频的信息&#xff1a; 合并三个视频文件意味着总时长增加了。较长的视频文件通常也会更大&#xff0c;即使比特率相同。 当我把三段…

vscode 阅读linux内核(vscode+clangd)

此插件曾在vim里用过&#xff0c;非常好用。 首先先在vscode 里下载clangd插件 这只是客户端&#xff0c;还需下载个服务器&#xff08;这在coc插件里也有说明&#xff09; sudo apt install clangd 下载完后可以 clangd --version 查看版本信息&#xff0c;如果能查看&#x…

UE5 日期时间蓝图变量 加减节点

参考链接&#xff1a;Having troubles with DateTime in UE5 - General / Feedback & Requests - Epic Developer Community Forums (unrealengine.com) 直接粘贴到UE5蓝图图表可用。&#xff08;反之相加&#xff0c;用负号操作一下&#xff09; 减号蓝图节点&#xff08;…

8.22-docker的部署及其使用

docker 1.docker环境部署以及语法 [rootdocker ~]# cat << EOF | tee /etc/modules-load.d/k8s.conf> overlay> br_netfilter> EOFoverlaybr_netfilter[rootdocker ~]# modprobe overlay[rootdocker ~]# modprobe br_netfilter[rootdocker ~]# cat /etc/module…

AI绘画工具 Stable Diffusion【插画转绘】:建筑 | 风景| 人像照片的插画转绘制作教程,照片秒变插画风格图片!

大家好&#xff0c;我是画画的小强 关于Stable Diffusion 的插画转绘&#xff0c;今天给大家分享一种制作方法。我们先看一下效果图。 一. 图片转插画的制作方法 本期教程我们将使用AI绘画工具Stable Diffusion&#xff0c;关于SD的安装和入门使用可以看看我的往期入门教程…

122-域信息收集应用网络凭据CS插件AdfindBloodHound

参考&#xff1a;【内网安全】域信息收集&应用网络凭据&CS插件&Adfind&BloodHound_ladon adfinder bloodhound-CSDN博客 工作组和域环境 我的理解&#xff1a; 工作组就是还是局域网一样只是大一点里面的电脑很多&#xff0c;每个电脑还是都是单独的电脑没有…

解决arcmap发布影像切片,注册传输数据慢的问题

1、本地服务器进行切片步骤&#xff1a; 开发环境按照正常方案发布影像切片。 2、生产环境切片步骤 a、使用范文等同影像文件范围的矢量面数据&#xff0c;作为切片数据发布切片服务 重点&#xff1a;【服务名称必须一致】 b、获取开发环境切片的切片方案文件conf.xml 重点&am…

SSL/TLS协议信息泄露漏洞修复

概述&#xff1a;CVE-2016-2183 是一个涉及 SSL/TLS 协议信息泄露的漏洞&#xff0c;也被称为 "SWEET32" 攻击。该漏洞利用了某些对称加密算法&#xff08;如 3DES&#xff09;的弱点&#xff0c;攻击者可以通过捕获和分析大量的加密流量&#xff0c;可能会恢复明文数…

矩阵--旋转图像

给定一个 n n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。 你必须在 原地 旋转图像&#xff0c;这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。 示例 &#xff1a; 输入&#xff1a;matrix [[1,2,3],[4,5,6],[7,8,9]] 输出&a…

数据结构——算法和算法分析

目录 算法和算法分析 算法 算法设计的要求 算法效率的度量 算法的存储空间需求 算法和算法分析 算法 算法是对特定问题求解步骤的一种描述&#xff0c;它是指令的有限序列&#xff0c;其中每一条指令表示一个或多个操作。 一个算法具有下列5个重要的特性&#xff1a; &…

Python3:多行文本内容转换为标准的cURL请求参数值

背景 在最近的工作中&#xff0c;经常需要处理一些接口请求的参数&#xff0c;参数来源形式很多&#xff0c;可能是Excel、知识库文档等&#xff0c;有些数据形式比较复杂&#xff0c;比如多行或者包含很多不同的字符&#xff0c;示例如下&#xff1a; **客服质检分析指引** …

BaseCTF [第一周]Ez Xor

笔记。 64ida打开。 走&#xff01; 逆向逆向&#xff0c;逆向往前看。 因为异或算法&#xff0c;A ^BC >>> C^BA 所以在只需要知道密钥key就可以了。 是不是头大&#xff1f; 没事 这里介绍另一种方法>>> IDA 动态调试去获取key值、密文值 。(灵活使用工…