正点原子linux驱动笔记-字符设备驱动

1.linux驱动和模块加载函数

Linux驱动有两种运行方式

第一种是将驱动编译Linux内核中,这样当Linux内核启动的时候就会自动运行驱动程序。

第二种是将驱动编译成模块(Linux下模块扩展名为".ko"),在Linux内核启动后使用命令“insmod”或者“modprobe”命令加载驱动模块。在调式驱动的时候一般都选择将其编译为模块,这样我门就不需要重新烧写Linux内核,非常方便。将驱动编译成模块是最方便开发的。

先分析加载的模块的依赖关系,然后都会将所有依赖的模块都加载到内核中

module_init(xxx_init); //注册模块加载函数
module_exit(xxx_exit); //注册模块卸载函数
MODULE_LICENSE() //添加模块 LICENSE 信息
MODULE_AUTHOR() //添加模块作者信息/****************************************************
ICENSE: 许可证,我们使用GPL协议。GPL协议一般指GNU通用公共许可证。 
GNU通用公共许可证简称为GPL,是由自由软件基金会发行的用于计算机软件
的协议证书,使用该证书的软件被称为自由软件。
*****************************************************/

======================================

2.设备号

在Linux中,为了方便管理,每个设备都有一个设备号,设备号由主设备号和次设备号组成。主设备号表示某一个具体的驱动,次设备号表示使用这个驱动的各个设备。Linux提供一个dev_t的数据类型表示设备号,在include/linux/types.h里面。

typedef __kernel_dev_t dev_t;
typedef __u32 __kernel_dev_t;
typedef unsigned int __u32;

可以看出dev_t其实就是unsigned int类型,无符号32位 。其中高12位表示主设备号,低20位表示次设备号。所以主设备号范围0~4095。

=======================================

3.注册字符设备函数

我们需要向系统注册一个字符设备,使用函数register_chrdev。卸载驱动的时候需要注销掉前面注册的字符设备,使用函数unregister_chrdev,注销字符设备。

#include <linux/fs.h>
/* 注册字符设备函数 */
static inline int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops)
{return __register_chrdev(major, 0, 256, name, fops);
} 
/* 销毁字符设备函数 */
static inline void unregister_chrdev(unsigned int major, const char *name)
{__unregister_chrdev(major, 0, 256, name);
}/*****************************************************
参数说明:
major :主设备号。Linux下每一个设备都会有设备号,设备号分为主设备号和次设备号。后面有详细的讲。
name :设备的名字。指向遗传字符串。
fops :它时file_operations结构体的指针, file_operations前面我们也说过,就是 Linux 内核驱动操作 函数集合。它指向设备的操作函数集合变量。******************************************************/

设备号的获取

从dev_t获取主设备号和次设备号,MAJOR(dev_t),MINOR(dev_t)。也可以使用主设备号和次设备号构成dev_t,通过MKDEV(major,minor);

file_operations 函数操作集合

struct file_operations {struct module *owner;loff_t (*llseek) (struct file *, loff_t, int);ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);int (*iterate) (struct file *, struct dir_context *);unsigned int (*poll) (struct file *, struct poll_table_struct *);long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);long (*compat_ioctl) (struct file *, unsigned int, unsigned long);int (*mmap) (struct file *, struct vm_area_struct *);int (*mremap)(struct file *, struct vm_area_struct *);int (*open) (struct inode *, struct file *);int (*flush) (struct file *, fl_owner_t id);int (*release) (struct inode *, struct file *);int (*fsync) (struct file *, loff_t, loff_t, int datasync);int (*aio_fsync) (struct kiocb *, int datasync);int (*fasync) (int, struct file *, int);int (*lock) (struct file *, int, struct file_lock *);ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);int (*check_flags)(int);int (*flock) (struct file *, int, struct file_lock *);ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);int (*setlease)(struct file *, long, struct file_lock **, void **);long (*fallocate)(struct file *file, int mode, loff_t offset,loff_t len);void (*show_fdinfo)(struct seq_file *m, struct file *f);#ifndef CONFIG_MMUunsigned (*mmap_capabilities)(struct file *);#endif};

=================================

4.采用更方便方法注册字符设备

静态分配设备号需要我们检查当前系统中所有被使用了的设备号,然后挑选一个没有使用
的。而且静态分配设备号很容易带来冲突问题, Linux 社区推荐使用动态分配设备号,在注册字
符设备之前先申请一个设备号,系统会自动给你一个没有被使用的设备号,这样就避免了冲突。
卸载驱动的时候释放掉这个设备号。

int register_chrdev_region(dev_t from, unsigned count, const char *name)//@from :要分配的设备编号范围的初始值,明确了主设备号和起始次设备号
//@count:次设备号个数
//@name:相关联的设备名称. (可在/proc/devices目录下查看到), 也即本组设备的驱动名称
//@return: 成功返回0,失败返回负数

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
/*******************************************
dev :保存申请到的设备号。
baseminor :次设备号起始地址,alloc_chrdev_region 可以申请一段连续的多个设备号,这
些设备号的主设备号一样,但是次设备号不同,次设备号以 baseminor 为起始地址地址开始递
增。一般 baseminor 为 0,也就是说次设备号从 0 开始。
count :要申请的设备号数量。
name :设备名字。
******************************************/

========================================

5.用户空间和内核空间的数据传递

copy_to_user()函数和copy_from_user()函数

unsigned long copy_to_user(void *to, const void *from, unsigned long n);
这个函数的作用是将内核空间的数据复制到用户空间。其中to:目标地址(用户空间)
from:源地址(内核空间)
n:将要拷贝数据的字节数
返回:成功返回0,失败返回没有拷贝成功的数据字节数unsigned long copy_from_user(void *to, const void *from, unsigned long n);
这个函数的作用是将用户空间的数据复制到内核空间。其中to:目标地址(内核空间)
from:源地址(用户空间)
n:将要拷贝数据的字节数
返回:成功返回0,失败返回没有拷贝成功的数据字节数

==================================

6.代码示例

驱动代码示例:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/io.h>#define CHRDEVBASE_MAJOR      200           //主设备号
#define CHRDEVBASE_NAME     "chrdevbase"    //名字static char readbuf[100]; /*读缓冲 */
static char writebuf[100];  /* 写缓冲 */
static char kerneldata[] = {"kernel data!"};static int chrdevbase_open(struct inode *inode, struct file *filp)
{// printk("chrdevbase_open\r\n");return 0;
}static int chrdevbase_release(struct inode *inode, struct file *filp)
{// printk("chrdevbase_release\r\n");return 0;   
}static ssize_t chrdevbase_read(struct file *filp, __user char *buf, size_t count,loff_t *ppos)
{ int ret  = 0;//printk("chrdevbase_read\r\n");memcpy(readbuf, kerneldata, sizeof(kerneldata));ret = copy_to_user(buf, readbuf, count);if(ret == 0) {} else {}return 0;  
}static ssize_t chrdevbase_write(struct file *filp, const char __user *buf,size_t count, loff_t *ppos)
{int ret = 0;//printk("chrdevbase_write\r\n");ret = copy_from_user(writebuf, buf, count);if(ret == 0) {printk("kernel recevdata:%s\r\n", writebuf);} else {}return 0; 
}/** 字符设备 操作集合*/
static struct file_operations chrdevbase_fops={.owner = THIS_MODULE,.open = chrdevbase_open,.release = chrdevbase_release,.read = chrdevbase_read,.write = chrdevbase_write,
};static int __init chrdevbase_init(void)
{int ret = 0;printk("chrdevbase_init\r\n");/* 注册字符设备 */ret = register_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME, &chrdevbase_fops);if(ret < 0) {printk("chrdevbase init failed!\r\n");}return 0;
}static void __exit chrdevbase_exit(void)
{printk("chrdevbase_exit\r\n");	/* 注销字符设备 */unregister_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME);}/*模块入口与出口*/
module_init(chrdevbase_init);  /* 入口 */
module_exit(chrdevbase_exit);  /* 出口 */MODULE_LICENSE("GPL");      
MODULE_AUTHOR("zuozhongkai");

================================

应用层代码示例:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>/**argc:应用程序参数个数*argv[]:具体的参数内容,字符串形式 *./chrdevbaseAPP  <filename>  <1:2> 1表示读,2表示写* ./chrdevbaseAPP /dev/chrdevbase 1    表示从驱动里面读数据* ./chrdevbaseAPP /dev/chrdevbase 2    表示向驱动里面写数据* */
int main(int argc, char *argv[])
{int ret = 0;int fd = 0;char *filename;char readbuf[100], writebuf[100];static char usrdata[] = {"usr data!"};if(argc != 3) {printf("Error usage!\r\n");return -1;}filename = argv[1];fd = open(filename, O_RDWR);if(fd < 0 ) {printf("Can't open file %s\r\n", filename);return -1;}if(atoi(argv[2]) == 1){ /*  读 *//* read */ret = read(fd, readbuf, 50);if (ret < 0) {printf("read file %s failed!\r\n", filename);}else {printf("APP read data:%s\r\n", readbuf);}}/* write */if(atoi(argv[2]) == 2) { /* 写 */ memcpy(writebuf, usrdata, sizeof(usrdata));ret = write(fd, writebuf, 50);if (ret < 0) {printf("write file %s failed!\r\n", filename);}else {}}/* close */ret = close(fd);if(ret < 0) {printf("close file %s falied!\r\n", filename);}return 0 ;}

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

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

相关文章

《数据结构》--队列【各种实现,算法推荐】

一、认识队列 队列是一种常见的数据结构&#xff0c;按照先进先出&#xff08;FIFO&#xff0c;First In First Out&#xff09;的原则排列数据。也就是说&#xff0c;最早进入队列的元素最先被移除。队列主要支持两种基本操作&#xff1a; 入队&#xff08;enqueue&#xff0…

【微信小程序_9_WXSS模板样式】

摘要:本文主要介绍了微信小程序开发中的 WXSS。WXSS 类似于网页开发中的 CSS,具有其大部分特性同时又有扩展,如 rpx 尺寸单位、@import 样式导入等。其中 rpx 是解决屏适配的独特单位,有特定实现原理和不同设备的换算方式。@import 可导入外联样式表,有明确语法格式和示例…

爬虫设计思考之二

“所谓爬虫,其本质是一种计算机程序,它的行为看起来就像是蜘蛛在网上面爬行一样,顺着互联网这个“网”,一条线一条线地“爬行”。 一、认识爬虫 爬虫这个词对于非专业人士比较的陌生&#xff0c;但是实际却和我们的生活息息相关。例如我们国内经常使用的百度浏览器搜索&#x…

线性代数 行列式

一、行列式 1、定义 一个数学概念&#xff0c;主要用于 线性代数中&#xff0c;它是一个可以从方阵&#xff08;即行数和列数相等的矩阵&#xff09;形成的一个标量&#xff08;即一个单一的数值&#xff09; 2、二阶行列式 &#xff0c;像这样将一个式子收缩称为一个 2*2 的…

【数据结构】【链表代码】移除链表元素

移除链表元素 /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/struct ListNode* removeElements(struct ListNode* head, int val) { // 创建一个虚拟头节点&#xff0c;以处理头节点可能被删除的情况 struct…

【mysql 截断订单表order 报错】

truncate table order;这个是一个截断订单表的sql语句 看起来没有什么问题 但是实际执行的时候是会报错的 SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version…

27-云计算下一个十年技术Serverless

├──27-云计算下一个十年技术Serverless | ├──1-Serverless深度实战之Knative | | ├──1-使用Knative平台环境说明 | | ├──2-现阶段云原生应用领域介绍 | | ├──3-为什么要引入Serverless | | ├──4-Serverless应用场景 | | ├──5-Serve…

4. 单例模式线程安全问题--是否加锁

单例模式线程安全问题--是否加锁 是否加锁问题指什么&#xff1f;解决多线程并发来带的问题继承MonoBehaviour的单例模式不继承MonoBehaviour的单例模式 总结 是否加锁问题指什么&#xff1f; 如果程序当中存在多线程&#xff0c;我们需要考虑当多个线程同时访问同一个内存空间…

计算机毕业设计 内蒙古旅游景点数据分析系统的设计与实现 Python毕业设计 Python毕业设计选题 Spark 大数据【附源码+安装调试】

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

筛斗数据:如何提高数据治理的安全性

提高数据治理的安全性是一个多层次、多维度的任务&#xff0c;涉及技术、管理、法律等多个方面。以下是一些具体的策略和建议&#xff1a; 一、技术层面 数据加密&#xff1a;采用先进的加密算法对敏感数据进行加密存储和传输&#xff0c;确保数据在各个环节中的安全性。这包…

element-plus组件之Upload(2.0)

接上篇 下面的属性就对应着回调函数&#xff0c;下面就一一进行介绍。 因为element-plus在封装upload组件时就自带了一个预览和删除的图标&#xff0c;只是没有方法实现&#xff0c;这里进行指明。 就是在图片墙列表中&#xff0c;自动就带了这两个图标和遮罩&#xff0c;下面…

pip安装指定版本的tensorflow

安装CPU版本&#xff1a;(以2.9.0版本为例) pip install tensorflow2.9.0安装GPU版本&#xff1a;(以2.9.0版本为例) pip install tensorflow-gpu2.9.0若下载缓慢&#xff0c;使用阿里国内镜像源加速下载&#xff1a;(以2.9.0版本为例) pip install -i https://mirrors.aliy…

[C#]使用纯opencvsharp部署yolov11-onnx图像分类模型

【官方框架地址】 https://github.com/ultralytics/ultralytics.git 【算法介绍】 使用纯OpenCvSharp部署YOLOv11-ONNX图像分类模型是一项复杂的任务&#xff0c;但可以通过以下步骤实现&#xff1a; 准备环境&#xff1a;首先&#xff0c;确保开发环境已安装OpenCvSharp和必…

人脸识别face-api.js应用简介

前阵子学习了一下face-api.js &#xff0c;偶有心得&#xff0c;跟大家分享一下。 face-api.js的原始项目是https://github.com/justadudewhohacks/face-api.js &#xff0c;最后一个release是2020年3月22日的0.22.2版&#xff0c;组件较老&#xff0c;API文档很全&#xff0c;…

鸿蒙网络管理模块07——网络质量管理

如果你也对鸿蒙开发感兴趣&#xff0c;加入“Harmony自习室”吧&#xff01;扫描下方名片&#xff0c;关注公众号&#xff0c;公众号更新更快&#xff0c;同时也有更多学习资料和技术讨论群。 1、概述 HarmonyOS提供了一套网络网络质量管理的套件&#xff08;Network Boost Ki…

[论文笔记]DAPR: A Benchmark on Document-Aware Passage Retrieval

引言 今天带来论文DAPR: A Benchmark on Document-Aware Passage Retrieval的笔记。 本文提出了一个基准&#xff1a;文档感知段落检索(Document-Aware Passage Retrieval,DAPR)以及介绍了一些上下文段落表示的方法。 为了简单&#xff0c;下文中以翻译的口吻记录&#xff0c…

麒麟信安CentOS安全加固案例获评中国信通院第三届“鼎新杯”数字化转型应用奖

“鼎新杯”数字化转型应用大赛&#xff0c;由中国通信标准化协会主办、中国信息通信研究院承办&#xff0c;以落实国家“十四五”规划关于“加快数字化发展&#xff0c;建设数字中国”的总体要求为目标&#xff0c;意在打造一批具有产业引领与推广应用效应的企业数字化转型应用…

conda打包

tar 是一个在 Unix 和类 Unix 系统中常用的命令行工具&#xff0c;用于打包多个文件和目录到一个归档文件&#xff08;通常称为 tarball&#xff09;&#xff0c;以及从这些归档文件中解包文件和目录。 以下是使用 tar 进行打包和解包的基本用法&#xff1a; 打包&#xff08;…

OCR+PDF解析配套前端工具开源详解!

目录 一、项目简介 TextIn为相关领域的前端开发提供了优秀的范本。 目前项目已在Github上开源&#xff01; 二、性能特色 三、安装使用 安装依赖启动项目脚本命令项目结构 四、效果展示 面对日常生活和工作中常见的OCR识别、PDF解析、翻译、校对等场景&#xff0c;配套的…

【C++进阶】set的使用

1. 序列式容器和关联式容器 前面&#xff0c;我们已经接触过STL中的部分容器如&#xff1a;string、vector、list、deque、array、forward_list等&#xff0c;这些容器统称为序列式容器&#xff0c;因为逻辑结构为线性序列的数据结构&#xff0c;两个位置存储的值之间⼀般没有紧…