驱动开发——入门到入职1

这篇具有很好参考价值的文章主要介绍了驱动开发——入门到入职1。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1.驱动的种类

字符设备驱动:按照字节流来访问,只能顺序访问,不能无序访问的设备

块设备驱动:按照block(512字节)访问,可以随机访问的设备。

网络设备驱动:网络设备没有设备节点,控制网卡硬件,负责网络数据收发的代码就是网络设备驱动

2.linux内核模

2.1内核模块的三要

  1. 入口:资源申请,在安装驱动的时候执行insmod

  2. 出口:资源释放,在卸载驱动的时候执行rmmod

  3. 许可证:内核模块必须遵从GPL开源协议

2.2内核模块代码实例

 #include <linux/init.h>
 #include <linux/module.h>
 // 入口
 // static:限定作用域
 // int:返回值类型
 // __init:给编译器使用,将demo_init放在.init.text段中
 // #define __init __section(".init.text")  vmlinux.lds 链接脚本
 // uImage-zImage-Image-vmlinux
 // demo_init:入口函数的名字,一般的写法:led_init adc_init uart_init
 // (void):没有参数
 static int __init demo_init(void)
 {
     return 0;
 }
 // 出口
 // static:限定作用域
 // void:返回值类型
 // __exit:给编译器使用,将demo_exit放在.exit.text段中
 // #define __exit __section(".exit.text")  vmlinux.lds 链接脚本
 // uImage-zImage-Image-vmlinux
 // demo_exit:出口函数的名字,一般的写法:led_exit adc_exit uart_exit
 // (void):没有参数
 static void __exit demo_exit(void)
 {
 }
 module_init(demo_init); //告诉内核入口
 module_exit(demo_exit); //告诉内核出口
 MODULE_LICENSE("GPL"); // 许可证

2.5内核模块的编

方法1:内部编译

  1. 将demo.c拷贝到/drivers/char/

  2. 修改当前目录下的Kconfig文件

    473 config DEMO

    474 tristate "demo demo demo"

  3. 通过make menuconfig进行选配

    <M> demo demo demo (NEW)

    Y :编译到uImage

    M:模块化编译

  4. .config生成编译选项

    CONFIG_DEMO=m

    CONFIG_DEMO=y

    #CONFIG_DEMO is not set

  5. 修改Makefile文件

    obj-$(CONFIG_demo) += demo.o

    obj-m +=demo.o

    obj-y +=demo.o

  6. 编译

    make uImage LOADADDR=0xc2000000

    make modules ----->demo.ko

方法2:外部编译

 KERNELDIR := /home/linux/linux-5.10.61
 PWD := $(shell pwd)
 all:
  make -C $(KERNELDIR) M=$(PWD) modules
  @#make -C $(KERNELDIR) 进入内核顶层目录下
  @#读取内核顶层目录下的Makefile文件对着它执行
  @#make M=$(PWD) modules
  @#对着当前PWD路径执行make modules模块化编译
 clean:
  make -C $(KERNELDIR) M=$(PWD) clean
 ​
 obj-m:=demo.o

通用的Makefile

make arch=arm modname=demo 编译开发板可以安装的模块

make arch=x86 modname=demo 编译ubuntu可以安装的模块

 arch ?= x86
 modname ?= demo
 ​
 ifeq ($(arch),arm)
  KERNELDIR := /home/linux/linux-5.10.61
 else
  KERNELDIR := /lib/modules/$(shell uname -r)/build/
 endif
 ​
 PWD := $(shell pwd)
 all:
  make -C $(KERNELDIR) M=$(PWD) modules
 clean:
  make -C $(KERNELDIR) M=$(PWD) clean
 ​
 obj-m:=$(modname).o

2.6内核模块的安装

sudo insmod demo.ko 安装内核模块

lsmod 查看内核模块

sudo rmmod demo 卸载内核模块

3.内核模块打印语句printk使用

3.1printk的语法格式

printk(消息级别 "控制格式"); //指定消息级别

printk("控制格式"); //使用默认消息级别

3.2printk的打印级别

内核中一共有8种打印级别,数值越小级别越高。

 #define KERN_EMERG   "0"    /* system is unusable */
 #define KERN_ALERT   "1"    /* action must be taken immediate*/
 #define KERN_CRIT    "2"    /* critical conditions */        
 #define KERN_ERR     "3"    /* error conditions */
 #define KERN_WARNING     "4"    /* warning conditions */
 #define KERN_NOTICE  "5"    /* normal but significant conditi*/
 #define KERN_INFO    "6"    /* informational */
 #define KERN_DEBUG   "7"    /* debug-level messages */

3.3printk通过打印级别过来信息

只有消息的级别高于终端的级别的时候消息才会在终端上回显

cat /proc/sys/kernel/printk

4 4 1 7

4:终端的级别

4:消息默认级别

1:终端最大级别

7:终端最小级别

修改默认消息级别的方法:

su root

echo 4 3 1 7 > /proc/sys/kernel/printk

3.4进入/退出虚拟终端

进入虚拟终端:ctrl + fn +alt +[F2~F6]

退出虚拟终端:ctrl + fn + alt + F1

3.5printk函数的实例

 #include <linux/init.h>
 #include <linux/module.h>
 // 入口
 static int __init demo_init(void)
 {
     printk(KERN_ERR "this is test printk kern_err\n");
     printk(KERN_INFO "this is test printk kern_info\n");
     printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
     return 0;
 }
 // 出口
 static void __exit demo_exit(void)
 {
     printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
 }
 module_init(demo_init); //告诉内核入口
 module_exit(demo_exit); //告诉内核出口
 MODULE_LICENSE("GPL"); // 许可证

3.6内核打印消息的查看方法

dmesg (从内核启动到当前时候内核打印的所有信息)

sudo dmesg -C //直接清除打印信息

sudo dmesg -c //先将信息显示到终端上然后在清除

4.内核模块传参

4.1内核模块传参的API

 module_param(name, type, perm)
 功能:接收命令行传递的参数
 参数:
     @name:变量名
     @type:变量的类型 
         /* Standard types are:
          *  byte, hexint, short, ushort, int, uint, long, ulong
          *  charp: a character pointer  
          *  bool: a bool, values 0/1, y/n, Y/N.
          *  invbool: the above, only sense-reversed (N = true).
          */
  @perm:权限,如果填写的不是0,就是在sys目录下产生一个文件,文件的权限就是perm
    最大权限0664
 MODULE_PARM_DESC(_parm, desc)
 功能:对传递的变量进行字符串描述
 参数:
     @_parm:变量名
     @desc:描述的字符串

4.2内核模块传参的实例

 #include <linux/init.h>
 #include <linux/module.h>
 ​
 int backlight=127;
 module_param(backlight,int,0664);
 MODULE_PARM_DESC(backlight,"this is backlight var,range[0-255],default:127");
 ​
 // 入口
 static int __init demo_init(void)
 {
     printk("%s:%d\n",__func__,backlight);
     printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
     return 0;
 }
 // 出口
 static void __exit demo_exit(void)
 {
     printk("%s:%d\n",__func__,backlight);
     printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
 }
 module_init(demo_init); //告诉内核入口
 module_exit(demo_exit); //告诉内核出口
 MODULE_LICENSE("GPL"); // 许可证

4.3内核模块传参的测试流程

  1. 编译模块 make arch=x86 modname=demo

  2. 查看内核模块可传参的变量

  3. 安装命令行传参

    sudo insmod demo.ko a=255

  4. 通过属性文件传参

    /sys/module/驱动命名的目录/parameters

    -rw-rw-r-- 1 root root 4096 3月 27 16:24 a

    cat a //查看a文件中的内容

    su root

    echo 128 > a //修改a文件中的内容

  5. 通过dmesg查看现象dmesg

4.4内核模块传参练习

  1. 使用内核模块传参1个字节的变量

  2. 使用内核模块传参传递字符串

 #include <linux/init.h>
 #include <linux/module.h>
 ​
 unsigned char a = 'A';  //不能够传递字符
 module_param(a, byte, 0664);
 MODULE_PARM_DESC(a, "this is uchar var");
 ​
 char* p = "hello driver";   //字符串中不能有空格
 module_param(p, charp, 0664);
 MODULE_PARM_DESC(p, "this is char * var");
 // 入口
 static int __init demo_init(void)
 {
     printk("%s:a = %c\n", __func__, a);
     printk("%s:p = %s\n", __func__, p);
     printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
     return 0;
 }
 // 出口
 static void __exit demo_exit(void)
 {
     printk("%s:a = %c\n", __func__, a);
     printk("%s:p = %s\n", __func__, p);
     printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
 }
 module_init(demo_init); // 告诉内核入口
 module_exit(demo_exit); // 告诉内核出口
 MODULE_LICENSE("GPL"); // 许可证
 MODULE_AUTHOR("dzs daizs_bj@hqyj.com");

5.导出符号表

5.1什么是导出符号表

在内核中有两个模块demoA和demoB.如果demoA模块中实现了add函数,

此时demoB模块是可以调用demoA中的add函数的,因为两个模块都运行

同一个3-4G的内核空间,但是demoB如何拿到demoA中的add函数那?通

过导出符号表完成。

5.2导出符号表的API

 EXPORT_SYMBOL_GPL(sym)
 功能:导出符号表
 参数:
     @sym:函数名

5.3导出符号表的实例

demoA.c

 #include <linux/init.h>
 #include <linux/module.h>
 int add(int a,int b)
 {
     return (a+b);
 }
 EXPORT_SYMBOL_GPL(add);
 ​
 static int __init demoA_init(void)
 {
     printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
     return 0;
 }
 static void __exit demoA_exit(void)
 {
     printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
 }
 module_init(demoA_init);
 module_exit(demoA_exit);
 MODULE_LICENSE("GPL");

demoB.c

 #include <linux/init.h>
 #include <linux/module.h>
 extern int add(int ,int);
 ​
 static int __init demoB_init(void)
 {
     printk("sum = %d\n",add(100,200));
     printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
     return 0;
 }
 static void __exit demoB_exit(void)
 {
     printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
 }
 module_init(demoB_init);
 module_exit(demoB_exit);
 MODULE_LICENSE("GPL");

5.4测试流程

编译:

首先先编译demoA模块,生成符号表文件Module.symvers。

在编译demoB之前需要将这个文件拷贝到demoB的目录下,否则

会出现add 函数undefined。如果是5.10以后的内核使用如下方式

符号表路径

安装:

先安装demoA模块,在安装demoB模块,因为demoB依赖demoA

模块,可以通过modinfo查看依赖

卸载:

先卸载demoB模块,在卸载demoA模块。文章来源地址https://www.toymoban.com/news/detail-821410.html

到了这里,关于驱动开发——入门到入职1的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C#从入门到入坟(不易,转载请注明出处)

    安装Visual Studio。 下载地址:https://visualstudio.microsoft.com/zh-hans/ 可以选择社区版本,是可以免费使用的。 下载之后配置安装。 按照自己的工作需要,勾选相应的组件和安装位置,进行安装即可。 目前C#开发的两种框架 运行于windows的.Net Framework 可以跨平台的.Net6 项目名称 建议

    2024年02月05日
    浏览(44)
  • 【javaweb】学习日记Day3 - Ajax 前后端分离开发 入门

    目录 一、Ajax 1、简介 2、Axios (没懂 暂留) (1)请求方式别名 (2)发送get请求 (3)发送post请求 (4)案例 二、前端工程化 1、Vue项目-目录结构 2、Vue项目-启动 (1)vscode页面启动 (2)cmd命令框启动 3、配置Vue端口号 4、Vue项目开发流程 三、Vue组件库 - Element  1、快速入门

    2024年02月12日
    浏览(38)
  • FHQ-Treap(非旋treap/平衡树)——从入门到入坟

           作者:hsez_yyh        链接: FHQ-Treap——从入门到入坟_hsez_yyh的博客-CSDN博客        来源:湖北省黄石二中信息竞赛组        著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。         平衡树有很多种,其中 fhq-treap 可以说是最

    2024年02月16日
    浏览(39)
  • 驱动开发:文件微过滤驱动入门

    MiniFilter 微过滤驱动是相对于 SFilter 传统过滤驱动而言的,传统文件过滤驱动相对来说较为复杂,且接口不清晰并不符合快速开发的需求,为了解决复杂的开发问题,微过滤驱动就此诞生,微过滤驱动在编写时更简单,多数 IRP 操作都由过滤管理器 (FilterManager或Fltmgr) 所接管,

    2024年02月09日
    浏览(48)
  • linux驱动开发入门(学习记录)

    2023.7.6及7.7 、

    2024年02月15日
    浏览(38)
  • Windows 10驱动开发入门(五):创建虚拟显示器 Indirect Display驱动开发

    在开发或者办公中,越大的屏幕看起来就显示越舒服了,通常我们的做法是有两块屏幕,这样显示的内容就变多了,可以很容易提高办公的效率。 在设置中 显示 中,如果我们有两块屏幕,在显示器中自然的会出现两个,在其中可以对两块屏幕进行相应的设置。 在这个驱动中

    2023年04月16日
    浏览(44)
  • Windows 驱动开发 新手入门(四)

    本系列所有文章 Windows 驱动开发 新手入门(一) Windows 驱动开发 新手入门(二) Windows 驱动开发 新手入门(三) Windows 驱动开发 新手入门(四) 本篇文章介绍一下设备对象,这是写驱动过滤的基础,比如键盘,串口等等的过滤。 PDO 是 Phsical Device Object 的缩写,直译就是物

    2023年04月08日
    浏览(50)
  • day2 驱动开发 c语言

    通过驱动开发给pcb板子点灯。 u-boot已经提前移植到了emmc中。 灯也是一种字符型设备。 编程流程需要先注册设备,然后创建结点,然后操作电灯相关寄存器 应用层直接调用read write来打开字符设备进行操作。 这样写会造成无法处理内核页面请求的虚拟地址内部错误,没找到解

    2024年02月15日
    浏览(38)
  • 驱动开发:内核封装WFP防火墙入门

    WFP框架是微软推出来替代TDIHOOK传输层驱动接口网络通信的方案,其默认被设计为分层结构,该框架分别提供了用户态与内核态相同的AIP函数,在两种模式下均可以开发防火墙产品,以下代码我实现了一个简单的驱动过滤防火墙。 WFP 框架分为两大层次模块,用户态基础过滤引擎

    2024年02月08日
    浏览(37)
  • 韦东山嵌入式Liunx入门驱动开发五

    本人学习完韦老师的视频,因此来复习巩固,写以笔记记之。 韦老师的课比较难,第一遍不知道在说什么,但是坚持看完一遍,再来复习,基本上就水到渠成了。 看完视频复习的同学观看最佳! 基于 IMX6ULL-PRO 参考视频 Linux快速入门到精通视频 参考资料 :01_嵌入式Linux应用

    2024年03月08日
    浏览(60)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包