1-驱动

2017-02-03  本文已影响0人  ibo
驱动:
十天:
外设驱动:按键驱动、蜂鸣器驱动、ADC、I2C、输入子系统
学习驱动时需要的基础:
第一天的重点内容:模块、字符设备框架
什么是驱动?
现有内核中我们通常以模块的方式写驱动。
应用程序 模块
运行空间 用户空间 内核空间
入口 main 加载函数
调用的接口 c库或者系统调用 内核函数
释放空间 自动释放 必须手动释放
如何去操作一个驱动模块?
模块的三要素:
加载函数:
vim -t module_init 选择5
297 #define module_init(initfn)                 \
298     static inline initcall_t __inittest(void)       \
299     { return initfn; }                  \
300     int init_module(void) __attribute__((alias(#initfn)));

135 typedef int (*initcall_t)(void); <==> typedef int (*)(void) initcall_t

int init_module(void) __attribute__((alias(#initfn)));给默认加载函数取别名为initfn
module_init(initfn);告诉系统内核我们的自定义的模块入口为initfn

vim -t module_init 选择4
266 #define module_init(x)  __initcall(x);

212 #define __initcall(fn) device_initcall(fn)

207 #define device_initcall(fn)     __define_initcall(fn, 6)

176 #define __define_initcall(fn, id) \
177     static initcall_t __initcall_##fn##id __used \
178     __attribute__((__section__(".initcall" #id ".init"))) = fn

假设module_init(hello_init) <==> static initcall_t __initcall_hello_init6 __used __attribute__((__section__(.initcall6.init))) = hello_init
最终的结果的作用是通过initcall_t类型定义了一个变量__initcall_hello_init6 __used __attribute__((__section__(.initcall6.init))),同时这个变量被赋值为hello_init
上面的变量最终编译后会被放到.initcall6.init这个代码分段中。
进入到arch/arm/kernel/vmlinux.lds来寻找上面的分段
内核在什么时候调用module_init();?
for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1;level++)  
725 static initcall_t *initcall_levels[] __initdata = {
726     __initcall0_start,
727     __initcall1_start,
728     __initcall2_start,
729     __initcall3_start,
730     __initcall4_start,
731     __initcall5_start,
732     __initcall6_start, 这个符号就是我们的加载函数所在分段的起始地址
733     __initcall7_start,
734     __initcall_end,
735
    };
驱动程序的编译:
2、如果直接将驱动编译到uImage文件中,无论这个驱动使用不使用都会占用内存空间,所以为了节省空间我们通常选择编译成模块
模块文件名:hello.ko
$(shell uname -r) 这里的shell是Makefile的一个函数,作用就是在Makefile调用shell命令
uname -r 显示当前操作系统的内核版本
验证过程:
sudo dmesg -c 清除内核缓存区中的信息
sudo insmod hello.ko
dmesg

sudo rmmod hello
dmesg
上一篇 下一篇

猜你喜欢

热点阅读