嵌入式培训机构四个月实训课程笔记(完整版)-Linux ARM驱动编程第五天-ARM Linux编程之字符设备驱动(物联技术666)

链接:https://pan.baidu.com/s/1V0E9IHSoLbpiWJsncmFgdA?pwd=1688
提取码:1688

教学内容:

1、内核模块的简单框架:

__init __exit执行完后就释放空间

简单框架:包含三个部分

1)模块初始化和模块退出函数

2)注册模块函数

3)模块许可

//***************************************************

#include <linux/module.h>    /*module_init()*/

#include <linux/kernel.h>        /* printk() */

#include <linux/init.h>            /* __init __exit */

static int __init myModule_init(void)   //红色名可自定义,使用static保证只能在本文件中调用

{

       /* Module init code */

       PRINTK("myModule_init\n");

       return 0;

}

static void __exit myModule_exit(void)          //红色名可自定义

{

       /* Module exit code */

       PRINTK("myModule_exit\n");

       return;

}

module_init(myModule_init);    //注册模块函数

module_exit(myModule_exit);   //注册

MODULE_AUTHOR("zhangda");       /*模块作者,可选*/

MODULE_LICENSE("GPL");                   /*模块许可证明,描述内核模块的许可权限,必须*/

MODULE_DESCRIPTION("A simple Hello World Module"); /*模块说明,可选*/

//******************************************

makefile的编写

驱动有两种方式,一为内核树之内,一为内核树以外,前者有点复杂,涉及到将驱动放到合适的内核树目录,修改相应的Makefile以及Kconfig文件,不过,天下无难易之事,为之,难亦不难了;后者所做的劳动就不用那么多了。这个Makfile只适合于后者。此外,内核的Makefile跟一般的应用程序的Makefile不太一样,就像驱动程序跟应用程序,内核头文件跟应用程序头文件等等,没必然关系,或者说是两码事,两者不能混为一谈。再有一点,驱动是跟内核打交道的,你的系统中必须有一个内核源代码。

//*******************************************

obj-m := module_test.o    //目标文件

ifneq ($(KERNELRELEASE),)

KERNELDIR = $(KERNELRELEASE)

else

KERNELDIR = /home/zhangda/linux_kernel/linux-2.6.34    //内核源代码地址

endif

$(PWD) := $(shell pwd)

default:

       $(MAKE) -C $(KERNELDIR) M=$(PWD) modules

       rm -rf *.o *.mod.c *.order *.symvers

clean:

       rm -rf *.ko

//***************************************************

2、模块间符号的相互引用

       在模块编程或内核编程中经常会遇到需要调用其它模块中符号的情况,在内核中专门有一张表来管理这些符号,可以通过 cat/proc/kallsyms查看该内核符号表我们也可以自己编写一个模块,并导出其符号供其它模块使用。

//****************************************

//****************************************

上图1,通过EXPORT_SYMBOL导出模块符号,方便图2使用符号函数。由于是给文件外函数使用,所以不能使用static修饰函数。

3linux字符设备驱动结构

描述字符设备的结构体(详细参考内核和宋宝华的设备驱动书)

//**************************

struct cdev {

       struct kobject kobj;     //内嵌kobject 对象

       struct module *owner;      //所属模块

       const struct file_operations *ops;    //文件操作结构

       struct list_head list;   

       dev_t dev;    //设备号

       unsigned int count;

};

//*****************************

设备号为32位,高12位为主设备号,低20位为次设备号;

此结构体描述了字符设备的信息,其中struct file_operations *ops结构体是向用户提高SPI接口函数的重要结构体。

file_operations :

//*******************************************

#include <linux/fs.h>

struct file_operations {

       struct module *owner;

// 指向拥有该结构的模块的指针,避免正在操作时被卸载,一般为初始化为THIS_MODULES

       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 (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);

       ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);

       int (*readdir) (struct file *, void *, filldir_t);

       unsigned int (*poll) (struct file *, struct poll_table_struct *);

       int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);//io控制接口

       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 (*open) (struct inode *, struct file *);//打开接口

       int (*flush) (struct file *, fl_owner_t id);

       int (*release) (struct inode *, struct file *);//关闭接口

       int (*fsync) (struct file *, struct dentry *, 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 **);

};

//****************************************

字符驱动设备的注册:

int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops)

(内联函数)

第一个参数:主设备号,int型,0为内核自动分配

第二个参数:设备名

第三个参数:file_operations的结构体地址

返回分配的主设备号,大于0,错误返回负值。

void unregister_chrdev(MAJOR_NR, DRIVER_NAME);(内联函数)

第一个参数:分配的主设备号

第二个参数:设备名

//*********************

MAJOR_NR = register_chrdev(MAJOR_NR, DRIVER_NAME, &GPG_fops);

if(MAJOR_NR < 0)

{

       PRINTK("register char device fail!\n");

       return MAJOR_NR;

}

unregister_chrdev(MAJOR_NR, DRIVER_NAME);

//*****************************

自动创建节点:

#include<linux/device.h>

static struct class *my_class;

my_class= class_create(THIS_MODULE, "my_class");

device_create(my_class,NULL,dev_n,NULL,"hello");

注意:dev_n = MKDEV(MAJOR_NR, MINOR_NR);

设备卸载删除类和设备节点

device_destroy(my_class,dev_n);

class_destroy(my_class);

//******************************

my_class=class_create(THIS_MODULE,"udev_gpg");

device_create(my_class,NULL, MKDEV(MAJOR_NR, MINOR_NR), NULL,DRIVER_NAME);

device_destroy(my_class,MKDEV(MAJOR_NR, MINOR_NR));

class_destroy(my_class);

//******************************

用户态与内核态数据的交互:

用户应用程序与驱动程序分属于不同的进程空间,因此二者之间的数据应当采用以下函数进行交换

#include <asm/uaccess.h>

copy_to_user(user_buffer, kernel_buffer, n)

//从内核空间拷贝n字节数据到用户空间

copy_from_user(kernel_buffer, user_buffer, n)

//从用户空间拷贝n字节数据到内核空间

put_user(kernel_value, user_buffer)

//从内核空间拷贝一数据变量到用户空间

get_user(kernel_value, user_buffer)

//从用户空间拷贝一数据变量到内核空间

(内核空间数据可是任意类型)

字符设备驱动的流程:

1)、建立__init和__exit函数;并注册这2个函数module_init,module_exit,声明MODULE_LICENSE;

2)、创建file_operations结构体,并指明函数地址;

3)、字符设备驱动的注册,在__init函数里面注册,在__exit注销;

4)、在__init函数里面完成节点创建,在__exit注销节点;

5)、编写file_operations结构体中的函数,具体功能可以结合裸机驱动编写功能

例如:

//***********************************************

#include <linux/module.h>             /*module_init()*/

#include <linux/kernel.h> /* printk() */

#include <linux/init.h>      /* __init __exit */

#include <linux/fs.h>        /* file_operation */

#include <asm/uaccess.h> /* copy_to_user, copy_from_user */      

#include <linux/device.h>  /*class ,class_create ,device_create 等*/

#define GPGCON      (*(volatile unsigned long *)S3C2410_GPGCON) //虚拟地址

#define GPGDAT     (*(volatile unsigned long *)S3C2410_GPGDAT)

#define GPGUP      (*(volatile unsigned long *)S3C2410_GPGUP)

#include "my_ioctl.h"

#define MAJOR_NAME "my_ioctl_drv"

static int MAJOR_NR = 0;

static int MINOR_NR = 0;

struct class *myclass;

static int io_open(struct inode *inode, struct file *filp)

{     }

static int io_release(struct inode *inode, struct file *filp)

{     }

static ssize_t io_read(struct file *filp, char *buf,size_t count, loff_t *f_pos)

{     }

static ssize_t io_write(struct file *filp, const char *buf,size_t count, loff_t *f_pos)

{     }

static int io_ioctl(struct inode *inode, struct file *file,unsigned int cmd, unsigned long arg)

{     }

static struct file_operations ioctl_file_opt = {

       .owner = THIS_MODULE,

       .write = io_write,

       .read = io_read,

       .ioctl = io_ioctl,

       .open = io_open,

       .release = io_release,

};

static int __init my_inctl_init(void)

{

       PRINTK("come in init\n");

       MAJOR_NR = register_chrdev(MAJOR_NR, DRIVER_NAME, &ioctl_file_opt);

       if(MAJOR_NR < 0)

       {

             PRINTK("register char device fail!\n");

             return MAJOR_NR;

       }

       myclass=class_create(THIS_MODULE,"my_ioctl");

       device_create(myclass,NULL, MKDEV(MAJOR_NR, MINOR_NR), NULL,"my_ioctl");

       return 0;

}

static void __exit my_inctl_exit(void)

{

       if(MAJOR_NR > 0)

       {

             unregister_chrdev(MAJOR_NR, DRIVER_NAME);

             device_destroy(myclass,MKDEV(MAJOR_NR, MINOR_NR));

             class_destroy(myclass);

       }

       PRINTK("exit ioctl\n");

       return;

}

module_init(my_inctl_init);

module_exit(my_inctl_exit);

MODULE_LICENSE("GPL");

MODULE_AUTHOR("HB");

//***************************************************

配置s3c2410的文件

cp arch/arm/configs/s3c2410_defconfig .config

make menuconfig(执行s3c2410配置)

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

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

相关文章

Python算法题集_将有序数组转换为二叉搜索树

Python算法题集_将有序数组转换为二叉搜索树 题108&#xff1a;将有序数组转换为二叉搜索树1. 示例说明2. 题目解析- 题意分解- 优化思路- 测量工具 3. 代码展开1) 标准求解【极简代码递归】2) 改进版一【多行代码递归】3) 改进版二【极简代码递归传递下标】 4. 最优算法 本文为…

备战蓝桥杯---图论之最小生成树

首先&#xff0c;什么是最小生成树&#xff1f; 他就是无向图G中的所有生成树中树枝权值总和最小的。 如何求&#xff1f; 我们不妨采用以下的贪心策略&#xff1a; Prim算法&#xff08;复杂度&#xff1a;&#xff08;nm)logm)&#xff1a; 我们对于把上述的点看成两个集…

NX二次开发树列表双击快速进入编辑状态

先将这几个树列表回调注释给解开 int TreeColumn0;//定义一个全局边量记录点击的那一列NXOpen::BlockStyler::Tree::BeginLabelEditState OnBeginLabelEditCallback(NXOpen::BlockStyler::Tree *tree,NXOpen::BlockStyler::Node *node,int columID) {if(columnIDTreeColumnID)…

无人机基本知识,无人机遥控器功能详解与调试方法

无人机作为一种新兴的飞行器&#xff0c;近年来在各个领域得到了广泛的应用。而无人机遥控器则是控制无人机飞行的重要工具。 无人机遥控器是一种无线设备&#xff0c;通过它来远程控制无人机的飞行。遥控器通常包括一个或多个摇杆&#xff0c;用于控制无人机的各种动作&#x…

QGIS004:【10栅格地形分析工具箱】-坡度、坡向、山体阴影

摘要&#xff1a;QGIS栅格地形分析工具箱常用工具有坡度、坡向、山体阴影等选项&#xff0c;本文介绍各选项的基本操作。 实验数据&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1gYZ_om4AlSdal0bts2mt-A?pwd4rrn 提取码&#xff1a;4rrn 一、坡度 工具功能&…

B端系统从0到1:有几步,其中需求分析要做啥?

一款B系统从无到有都经历了啥&#xff0c;而其中的需求分析又要做什么&#xff1f;贝格前端工场给老铁们做一下分析&#xff0c;文章写作不易&#xff0c;如果咱们有界面设计和前端开发需求&#xff0c;别忘了私信我呦&#xff0c;开始了。 一、B端系统从0到1都有哪些要走的步骤…

Vue练习3:组件开发3(页面切换)

预览 ——————————————————————————————————————————— 组件文档 Pager组件 属性 属性名含义类型必填默认值current当前页码&#xff08;总数据量/单页容量&#xff09;Number否1total总数据量Number否0limit单页容量Number否10vis…

Day-02-01

内容管理模块项目开发 Swagger的使用 1. 导入依赖 <!-- Spring Boot 集成 swagger --> <dependency><groupId>com.spring4all</groupId><artifactId>swagger-spring-boot-starter</artifactId> </dependency> 2. 配置信息 # 在app…

为何重复造轮子

重复造轮子&#xff0c;意思是说&#xff0c;一个项目本身存在开源组件&#xff0c;但开发团队还是选择重新手写一套组件库或框架的情况&#xff0c;这在软件业界比比皆是。 下面说下游戏项目里重复造轮子的几点原因。 一&#xff0c;精简化 一般开源项目为了适应多场景多业…

【制作100个unity游戏之25】3D背包、库存、制作、快捷栏、存储系统、砍伐树木获取资源、随机战利品宝箱2(附带项目源码)

效果演示 文章目录 效果演示系列目录前言拖放、交换物品绘制拖拽物品插槽UI修改Inventory&#xff0c;控制拖放功能 源码完结 系列目录 前言 欢迎来到【制作100个Unity游戏】系列&#xff01;本系列将引导您一步步学习如何使用Unity开发各种类型的游戏。在这第25篇中&#xf…

什么原因导致百度百科建立一直审核不通过?

百科词条对网络营销实在是太重要了&#xff0c;不管是个人还是企业想在网上开展业务&#xff0c;都必要建立百科词条。自己动手编辑百科词条&#xff0c;搞个几十次也审核不过的情况比比皆是。 为什么百度百科总是审核不通过&#xff1f;百度官方发表过声明表示百度百科词条是人…

【JS逆向+Python模拟API请求】逆向某一个略微中等的混淆网站,并模拟调用api请求 仅供学习。注:不是源代码混淆,而是一个做代码混淆业务的网站,

逆向日期&#xff1a;2024.02.16 使用工具&#xff1a;Node.js 加密方法&#xff1a;RSA标准库 文章全程已做去敏处理&#xff01;&#xff01;&#xff01; 【需要做的可联系我】 AES解密处理&#xff08;直接解密即可&#xff09;&#xff08;crypto-js.js 标准算法&#xf…

ubuntu22.04安装jenkins并配置

准备 更新系统 sudo apt update sudo apt upgrade环境准备 jdk 安装 sudo apt install openjdk-11-jdk验证 java -versiongit ubuntu配置git maven ubuntu配置maven 部署 添加 Jenkins 存储库 导入Jenkins存储库的GPG密钥 wget -q -O - https://pkg.jenkins.io/de…

el-upload组件的简单使用

最近公司的一个二期项目&#xff0c;开始要求复刻原有一期的功能页面。原先一期又不打算继续维护了&#xff0c;源码都没有。页面基本都涉及到了文件上传&#xff0c;以前很少使用到这个组件&#xff0c;公司有现成的表单设计器&#xff0c;文件上传都在组件里面拖动上传。在这…

【JavaEE】_线程与多线程的创建

目录 1. 线程的概念 2. 创建与使用多线程 2.1 方式1&#xff1a;继承Thread类 2.2 方式2&#xff1a; 实现Runnable接口 2.3 以上两种创建线程方式的对比 3. 多线程的优势-增加运行速度 1. 线程的概念 进程的存在是由于系统的多任务执行需求&#xff0c;这也要求程序员进…

LabVIEW卫星电视接收仿真系统

LabVIEW卫星电视接收仿真系统 随着卫星电视数字化的加速&#xff0c;传统模拟信号接收系统已无法满足需求。设计一套船载数字卫星电视接收系统&#xff0c;通过LabVIEW环境进行仿真实验&#xff0c;验证系统设计的可行性与有效性&#xff0c;满足数字信号接收的高精度要求&…

嵌入式Qt Qt中的信号处理

一.Qt中的信号处理 Qt消息模型&#xff1a; - Qt封装了具体操作系统的消息机制 - Qt遵循经典的GUI消息驱动事件模型 Qt中定义了与系统消息相关的概念; Qt中的消息处理机制&#xff1a; Qt的核心 QObject::cinnect函数&#xff1a; Qt中的“新”关键字&#xff1a; 实验1 初探…

Rust 基本环境安装

rust 基本介绍请看上一篇文章&#xff1a;rust 介绍 rustup 介绍 rustup 是 Rust 语言的安装器和版本管理工具。通过 rustup&#xff0c;可以轻松地安装 Rust 编译器&#xff08;rustc&#xff09;、标准库和文档。它也允许你切换不同的 Rust 版本或目标平台&#xff0c;以及…

petalinux安装的问题:

1. 安装是成功的&#xff0c;但是安装位置&#xff0c;就是用来存放petalinux的文件夹里没有文件 我是照着正点的文档安装的&#xff0c;出现的一个问题就是最后执行文件这里&#xff1a; -d 后面这个文件夹的路径&#xff0c;我看网上的教程也都是跟文档一致的 /opt/pkg/peta…

每日五道java面试题之java基础篇(十一)

目录: 第一题. Java死锁如何避免&#xff1f;第二题. 为什么⽤线程池&#xff1f;解释下线程池参数&#xff1f;第三题. 线程池的底层⼯作原理第四题. ReentrantLock中tryLock()和lock()⽅法的区别第五题. Sychronized和ReentrantLock的区别? 第一题. Java死锁如何避免&#x…