1.模块(一种特殊的内核代码写法,一般编译成ko文件,用于承载用户想要动态添加进内核的代码(不限于驱动))
a.写法
module_init(模块构造函数 );
module_init(模块析构函数 );
b.编译
标准的Makefile
c.模块相关工具
insmod/rmmod/lsmod/modinfo/modprobe/depmod
为了自动安装有依赖关系的模块,两个前提: 1.模块必须都拷贝到/lib/modules/3.14.0; 2.执行depmod
2.字符设备驱动框架
1.认知
a. 无系统编程(裸机编程.bin,hex)--有系统编程(安全,并发,丰富的驱动。。。elf,exe)
b. OS管理了所有驱动,而且各个操作系统管理结构(数据结构)不一定一样
c. 了解数据结构的意义
d. 回掉
e. 计算机喜欢数字而不是字符串
2.名词
设备文件: 一个特殊的文件用于抽象设备操作入口,用与隐藏设备号细节(域名-IP)
设备号:设备驱动的代号,主设备号 次设备号
驱动:让硬件工作起来的软件(裸机驱动 系统之下的驱动)
驱动框架:平台(操作系统)对驱动编写的要求
3.涉及的函数
申请设备号:
强制申请int register_chrdev_region(dev_t first, unsigned int count, char *name)
动态申请int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name)//name设备号说明信息(/proc/devices)
释放设备号:
void unregister_chrdev_region(dev_t first, unsigned int count);
初始化cdev:
void cdev_init(struct cdev *, const struct file_operations *);
注册cdev:
int cdev_add(struct cdev *, dev_t, unsigned);
移除cdev:
void cdev_del(struct cdev *);
1.长得像驱动
struct cdev myled{
.dev = 250
.fops{
.open = led_on;
.release = led_off;
}
};
2.放到内核中去
写到module.c
3.加入内核管理驱动的数据结构
cdev_add(&myled, 250, 1);
cdev_del(&myled);
1.内核希望你写驱动的时候采用总线、设备、驱动分离的方式来写,而为了分离没有具体(I2C,SPI,USB...)总线的设备发明了一个虚拟的platform_bus总线
a.platform_device: 注册设备必要的资源(基地址,引脚,中断号,波特率,ip,精度。。。)信息
b.platform_driver:实现设备操作做的算法(不包含具体数据,必须先匹配再获取);
1. Linux系统中以模块方式组织设备驱动程序,请列举在一个模块程序中必不可少的组成部分。 (可以写个Hello world模块的程序)__init __exit
2. 请说明一下从零创建一个linux字符设备驱动的步骤?
4. 简述Linux设备驱动中使用中断的步骤。
5. 简述信号量和自旋锁的异同和使用时的注意事项。
6. 简述命令 mknod /dev/zero c 1 5 的做用和命令各个部分的含义,并写出创建一个块设备节点的命令(把c改成b)。
7. 简述命令insmod,rmmod,lsmod,depmod,modprobe 的功能。
8. 简述platform设备基本原理, platform设备最大优势是什么?
9. 简述设备驱动程序和普通应用程序的异同点。
10. 简述mmap机制的作用和使用mmap的好处。
1. #include
#include
#include
static int hello_init(void)
{
printk(KERN_ALERT "Hello world enter!\n");
return 0;
}
static void hello_exit(void)
{
printk(KERN_ ALERT " Hello world exit!\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("Dual BSD/GPL");
2.
使用alloc_...方法动态注册设备号;
为cdev申请内存;
初始化设备并添加该设备;
完成file_operations中的相应函数功能;
4. request_irq,申请irq;
free_irq,释放irq。
5. 信号量和自旋锁都是解决互斥问题的基本手段,信号量的实现依赖于自旋锁。使用信号量机制时,进程若不能获得信号量,进程不会如自旋锁那样原地打转,而是让进程进入休眠等待状态。当使用信号量的开销时间T1大于使用自旋锁的开销T2时,宜使用自旋锁,若T2大于T1,则宜使用信号量。由于信号量所保护的临界区可能包含引起阻塞的代码,而自旋锁则绝对要避免用来保护这样的代码。信号量存在于进程上下文,而如果被保护的资源需要在中断或者软中断情况下使用,则只能选择自旋锁。
6. mknod,该命令用于创建设备节点。/dev/zero 表示设备节点创建的目录和名字,c 代表创建的设备节点为字符设备节点,1 代表主设备号,5 代表此设备号。
创建块设备节点的命令:mknod /dev/blockdev b 253 0
7.insmod:加载驱动模块到内核;
Rmmod:从内核删除驱动模块;
Lsmod:查看当前内核包含哪些模块
8.原理:
第一:定义一个platform_driver结构体,对probe和remove函数指针进行初始化,对driver里面的成员.owner,.name进行初始化;
第二:在模块的初始化函数中调用platform_drive_registe()初始化这个驱动,在卸载模块中调用platfrom_driver_unregister()注销这个驱动;
第三:将驱动中实用的设备资源信息通过传递进来的platform_device结构体指针获取。
第四:平台设备添加,一种是:用platform_device手动编码;另一种是在设备树中添加一个节点。
匹配过程:一种是:如果是平台设备与平台驱动的匹配以名字匹配,平台总线将会遍历
对方,如果名字相同,则调用平台驱动的probe函数,并且将匹配上的平台设备的结构体指针传给设备函数;另一种是:去匹配设备树中的设备节点里面的兼容属性的值,配上则调用平台驱动的probe函数,并且将匹配上的平台设备的结构体指针传给设备函数。
优势:
platform机制将本身的资源注册进内核,由内核统一管理,在驱动程序中使用这些资源时通过platform_device提供的标准接口进行申请并使用。这样提高了驱动和资源管理的独立性,并且拥有较好的可移植性和安全性
9.异同点:
驱动运行于内核,应用程序再用户空间;应用程序能调用C库;应用程序顺序执行,驱动被动的接受调用;应用程序的栈很大,驱动很小,通常4K;应用程序可以做浮点运算,驱动中很少做浮点运算。
10. 原理:mmap将一个文件或者其它对象映射进内存。文件被映射到多个页上,如果文件的大小不是所有页的大小之和,最后一个页不被使用的空间将会清零。
作用:让用户程序直接访问设备内存,在要求高性能的应用当中比较常用。应用程序使用的动态库映射到这个区域;应用程序调用mmap,将设备物理地址和这个区域的虚拟内存进行映射;