驱动开发(一)

概要:
1、模块、外部编译
2、字符设备框架(函数接口和结构体的关系)
3、字符设备框架、platform框架
4、设备树、led驱动、蜂鸣器驱动
5、内核中断子系统,按键驱动,中断上下半部。
6、adc驱动,内核的IO模型(阻塞、非阻塞、异步通知、多路复用)
7、I2C总线驱动、I2C设备驱动
8、输入子系统

知识补充:追内核:

make tags 
vi -t xxx

一、什么是驱动?driver老司机
  可以操作硬件,同时还会给应用程序提供交互的接口。

二、上层的程序如何操作硬件

  1. 系统调用:本质上是一个函数接口
  2. 函数的声明:存放在指定头文件中
  3. 函数的定义:在内核空间中
  4. 函数的调用:应用程序中使用

三、模块基本特性

1、什么是模块?
运行在内核空间中的一段代码

应用程序               模块
运行空间       用户空间             内核空间
入口函数         main               加载函数
调用接口      库函数或者系统调用       内核函数(主要是fs提供)
空间释放     自动释放              手动释放

模块不是驱动,它是实现驱动的一种方法。
在内核中驱动、文件系统、网络协议栈都可以用模块来实现。

2、模块的三要素:模块的声明、加载函数、卸载函数
模块的声明:MODULE_LICENSE(“协议名称”);
常见的协议:GPL BSD
加载函数:
-------->默认加载函数

1 int init_module(void);//源码是由驱动的开发者定义的。 
-------->*自定义加载函数* 

297 #define module_init(initfn) \
298 static inline initcall_t __inittest(void) \
299 { return initfn; } \
300 int init_module(void) __attribute__((alias(#initfn)));
/*给默认加载函数去别名,叫做initfn*/

复制代码

1 分析:
2 int a[2][3];
3 int (*p)[3]; <==> int (*)[3] p;
4 
5 int (*p)(void) <==> int (*)(void) p; 
6 
7 int (*initcall_t)(void);<==> typedef int (*)(void) initcall_t 函数指针类型
8 initfn是一个函数名,类型和initcall_t类型一致。
9 initfn的返回值为int,形参为void



卸载函数:
--------->默认卸载函数

void cleanup_module(void);
--------->自定义卸载函数

 303 #define module_exit(exitfn) 
 304 static inline exitcall_t __exittest(void) //inline 内联函数,不进行入栈出栈操作,操作几次占几份内存(避免循环使用)
305 { return exitfn; } 
306 void cleanup_module(void) __attribute__((alias(#exitfn)));

typedef void (*exitcall_t)(void);

3、模块的编译方法
内部编译:驱动源码存放在内核的指定目录下进行编译

 a.cp demo.c drivers/char 
 b.vi drivers/char/Kconfig 
    ----->添加选项
         config DEMO
         tristate "选项名称"
 c.vi drivers/char/Makefile
        添加:obj-$(CONFIG_DEMO) += demo.o 
 d.进入menuconfig中,找到选项选为M
 e.make modules
 f.cp drivers/char/demo.ko /rootfs 
 g.开发板启动后在开发板上执行insmod  demo.ko   

外部编译:驱动源码不在内核指定目录下(比内部编译方法方便)

 1 #include <linux/init.h>
 2 #include <linux/module.h>
 3 MODULE_LICENSE("GPL");
 4 
 5 //如果不使用__init加载函数直接被编译到.text分段中
 6 //如果使用__init加载函数会被编译到.text.init分段中
 7 int __init demo_init(void)//自定义加载函数
 8 {
 9 
10     printk("demo_init\n");
11     return 0;
12 }
13 module_init(demo_init);//给默认加载函数取别名
14 
15 //如果不使用__exit,当将驱动代码直接编译到内核中时,卸载函数也会参与编译
16 //如果使用__exit,当驱动代码直接编译到内核中,卸载函数不会参与编译
17 void __exit demo_exit(void)
18 {
19     printk("demo_exit\n");
20 }
21 module_exit(demo_exit);

1、自己写Makefile

2、调用到内核提供的模块编译方法
3、通知内核模块编译方法哪些源文件参与编译

内核顶层目录Makefile中:

1248 # The following are the only valid targets when building external
1249 # modules.
1250 # make M=dir clean Delete all automatically generated files
1251 # make M=dir modules Make all modules in specified dir 
1252 # make M=dir Same as 'make M=dir modules'

自己的Makefile:

 1 ifeq ($(KERNELRELEASE),)                      /*避免死循环执行makefile*/
 2 PWD = $(shell pwd)                          /*当前路径模块*/
 3 #KERNEL_DIR = /home/linux/linux-3.14/         /*用这个目录,需要将模块文件拷贝到开发板上执行*/
 4 KERNEL_DIR = /lib/modules/$(shell uname -r)/build/      /*需要在ubuntu中使用模块文件*/
 5 
 6 7 modules:
 8     make -C $(KERNEL_DIR) M=$(PWD) modules     /*-C进入路径KERNEL_DIR,找到寻找modules目标,最后回来*/
 9 

11 clean: 

12   make -C $(KERNEL_DIR) M=$(PWD) clean 

13 else 
14
 obj-m += demo1.o //(-m模块 -y -n)
//xxx-objs := ogj1.o,obj2.o
//obj-m += xxx.o  (打包生成xxx.ko文件)
15 endif

模块符号表导出:
符号本质就是一个函数名或者变量名。

1 EXPORT_SYMBOL_GPL();
2 EXPORT_SYMBOL();
3 功能:将符号信息存放到Module.symvers文件中
假设B模块要使用A模块中的一个函数。

1、模块A的函数下调用EXPORT_SYMBOL_GPL(函数名);
2、编译模块A
3、加载模块A
4、拷贝模块A的Module.symvers文件给模块B
5、编译模块B
6、加载模块B

运行过程:

1、执行自己的Makefile
2、进入内核顶层目录的Makefile,寻找modules目标
3、进入scripts/Makefile.modpost
4、回到自己的Makefile

4、模块的命令

1 加载模块:insmod xxx.ko

2 卸载模块:rmmod xxx

3 dmesg 显示内核打印信息到终端上

4 dmesg -c清空内核打印信息 


printk(printf)的级别问题:

  消息级别: 0 1 2 3 4 5 6 7
  控制台级别: 1 2 3 4 5 6 7 8
    ----------》数字越小级别越高《-------------------
    如果需要直接打印数据到终端上,那么必须保证消息级别大于控制台级别。

ubuntu内核: 4(当前控制台级别) 4(当前消息级别) 1(控制台级别的最小值) 7(默认的控制台级别)
Linux—3.14内核: 7 4 1 7

地址映射函数:
static inline void __iomem *ioremap(phys_addr_t offset, unsigned long size)

功能:地址映射
参数1:物理地址
参数2:映射的字节数
返回值:虚拟地址

/----------------------------------------作业部分-------------------------------------/
实验点亮exynos4412-fs4412的led2
1.vi dome.c

  1 #include <linux/module.h>
  2 #include <linux/init.h>
  3 #include <asm/io.h>    /* vi -t 追到两个相关头文件(/include/asm-generic/io.h)如果使用此头文件,会报错*/
  4 MODULE_LICENSE("GPL");
  5 int __init demo_init(void)
  6 {
  7     void __iomem *CON = (void __iomem*)ioremap(0x11000c20,4);
  8     void __iomem *DAT = (void __iomem*)ioremap(0x11000c24,4); //也可通过地址偏移来实现(void *DAT = CON + 4)  
  9     //*CON = (*CON & (~(0xf << 0))) | (1 << 0);
     writel(readl(CON &(~(0xf << 0))) | (1 << 0)),CON);//驱动程序的写法,(从内存映射的i/o空间读/写数据)
 10     //*DAT = *DAT | (1 << 0);
     writel(readl(DAT | (1 << 0)),DAT);  //readl中--->l:4byte  w:2byte  b:1byte
 11     printk("This is sb!\n");
 12     return 0;
 13 }
 14 module_init(demo_init);
 15 
 16 void __exit demo_exit(void)
 17 {
 18   
 19 }
 20 module_exit(demo_exit); 

复制代码
2.vi makefile

  1 ifeq ($(KERNELRELEASE),)
  2 PWD = $(shell pwd)
  3 KERNEL_DIR = /home/linux/linux-3.14/
  4 modules:
  5     make -C $(KERNEL_DIR) M=$(PWD) modules
  6 clean:
  7     make -C $(KERNEL_DIR) M=$(PWD) clean
  8 else
  9 obj-m += demo.o                                                      
 10 endif

3.make

linux@ubuntu:~/lxq/class/drivers/1day$ make
make -C /home/linux/linux-3.14/  M=/home/linux/lxq/class/drivers/1day modules
make[1]: Entering directory `/home/linux/linux-3.14'
  CC [M]  /home/linux/lxq/class/drivers/1day/demo.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /home/linux/lxq/class/drivers/1day/demo.mod.o
  LD [M]  /home/linux/lxq/class/drivers/1day/demo.ko  /*生成成功*/

4 cp ~/lxq/class/drivers/1day/demo.ko /rootfs/

5.make clean

6.开发板启动后在开发板上执行insmod demo.ko -------------->led灯点亮

--------------------------------->学习路漫漫<------------------------------

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
0.基础的基础 |-学习WIN64驱动开发的硬件准备 |-配置驱动开发环境 ------------------------------ 1.驱动级HelloWorld |-配置驱动测试环境 |-编译和加载内核HelloWorld ------------------------------ 2.内核编程基础 |-WIN64内核编程的基本规则 |-驱动程序与应用程序通信 |-内核里使用内存 |-内核里操作字符串 |-内核里操作文件 |-内核里操作注册表 |-内核里操作进线程 |-驱动里的其它常用代码 ------------------------------ 3.内核HOOK与UNHOOK |-系统调用、WOW64与兼容模式 |-编程实现突破WIN7的PatchGuard |-系统服务描述表结构详解 |-SSDT HOOK和UNHOOK |-SHADOW SSDT HOOK和UNHOOK |-INLINE HOOK和UNHOOK ------------------------------ 4.无HOOK监控技术 |-无HOOK监控进线程启动和退出 |-无HOOK监控模块加载 |-无HOOK监控注册表操作 |-无HOOK监控文件操作 |-无HOOK监控进线程句柄操作 |-使用对象回调监视文件访问 |-无HOOK监控网络访问 |-无HOOK监视修改时间 ------------------------------ 5.零散内容 |-驱动里实现内嵌汇编 |-DKOM隐藏进程+保护进程 |-枚举和隐藏内核模块 |-强制结束进程 |-强制读写进程内存 |-枚举消息钩子 |-强制解锁文件 |-初步探索PE32+格式文件 ------------------------------ 6.用户态HOOK与UNHOOK |-RING3注射DLL到系统进程 |-RING3的INLINE HOOK和UNHOOK |-RING3的EAT HOOK和IAT HOOK ------------------------------ 7.反回调 |-枚举与删除创建进线程回调 |-枚举与删除加载映像回调 |-枚举与删除注册表回调 |-枚举与对抗MiniFilter |-枚举与删除对象回调

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值