【1】驱动大纲
1.linux内核模块
2.字符设备驱动
3.并发和竞态的解决方法
4.IO模型
5.linux内核中断(中断底半部,linux内核定时器) 内核中不能用delay();这几秒进程就不能执行了,这就不能体现linux系统的高效,相互矛盾了所以不可能在内核中断中使用延时了
6.pltform总线驱动—虚拟总线
7.i2c总线驱动 面试常见。尤为重要
8.spi总线驱动
9.块设备驱动
10.网卡设备驱动
【2】什么是驱动?它和ARM裸机驱动的有什么区别?
操作硬件工作这份代码就叫驱动。驱动依赖内核框架,用内核接口,
不存在main函数没有死循环,内核工作在3G~4G虚拟空间,不是直接
访问物理地址,而裸机直接操作寄存器,直接找地址
驱动:
通过软件操作硬件工作这个代码
ARM裸机驱动:
直接配置寄存器让硬件工作,由于没有内核的参与可以直接操作物理
地址,所以的代码都在一个main函数的while(1)中实现去操作硬件。
Linux内核驱动:
基于Linux内核来实现的驱动,必须依赖linux内核才能工作,驱动在
驱动在内核中可以并行执行。驱动面向的是内核内核和硬件两个对象。
在实现驱动的时候必须基于内核的框架编写驱动。
【3】驱动在内核中的层级及驱动的划分
user:(用户空间)
APP glibc [0-3G]
-------------------(系统调用swi,软中断实现)---------------------------------------------
kernel:(内核层)
5大功能:
内存管理:内存的申请、释放、区域划分、内存的映射等
进程管理:管理的进程的创建,销毁,状态切换,进程间通讯
网络管理:通过tcp 4层模式进而将数据发送给网卡驱动实现数据收发
文件管理:通过文件系统,jiffs,yaffs,ramdisk,ext2/ext3/ext4
来组织管理文件
设备管理:设备驱动的管理
字符设备驱动: 按照字节流访问,并且只能顺序访问
块设备驱动: 按照block/扇区来访问的,单位是512字节;
可以顺序访问也可以无序访问
网卡设备驱动:通过网络进行数据的收发的驱动代码
注:有设备节点的可以通过open read write访问,
字符。块设备驱动都有设备节点在/dev下存在,
网卡没有设备节点只能通过(socket来完成访问)
因为网卡发展在一切兼文件之前
hardware: LED LCD touchscreen camera 声卡 音频
U盘 硬盘 Flash
猫 路由器 DM9000
90%属于字符设备驱动
面试问题:帧缓存设备驱动属于字符设备—>LCD
【4】linux内核模块
内核模块三要素:
1.入口:资源的申请
2.出口:资源的释放
3.许可证:遵从GPL协议
内核模块的特性:
1.所有的驱动都需要通过内核模块才能实现
2.内核模块不会主动运行,也不会自动运行
3.安装内核模块的时候入口执行
4.卸载驱动的时候出口执行
【5】模块
入口
static int __init demo_init(void)
{
return 0;
}
//static修饰本函数只能在本文件中使用,不能别文件调用
//static作用修饰函数,修饰变量:延长变量生命周期;限定作用域。
//__init告诉编译器将入口函数放到.init.text的断中
module_init(demo_init);
//将入口函数地址告给module_init
//内核通过地址调到函数去执行
出口
static void __exit demo_exit(void)
{
}
module_exit(demo_exit);
许可证
MODULE_LICENSE("GPL");//遵从GPL协议
MODULE_AUTHOR("HSW_YX");
内部编译:在内核源码树内进行驱动的编译
外部编译:在内核源码树外进行驱动的编译
静态编译:把内核模块编译到uImage中
动态编译:讲内核模块编译生成*.ko文件,需要uImage才能执行
Makefile:
module:
make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd)
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd)
obj-m:=demo.o
=: 立即赋值
=?询问赋值
=+ 附加赋值
【6】模块的安装
sudo insmod demo.ko 安装驱动
lsmod 查看模块信息
sudo dmesg -C 直接清除
sudo dmesg -c 先打印在清除
sudo rmmod demo 卸载驱动
modinfo demo.ko 查看模块信息
【7】内核打印函数
printk(级别 "打印内容");//内核中的打印级别一共8个,数字越小级别越高
printk("message");//一般打印
/proc/sys/kernel/printk
4 4 1 7
终端级别 消息的默认级别 终端的最大级别 终端的最小级别
只有当消息的级别大于终端级别的时候消息才回被回显
修改级别
su root ==>1
echo 4 3 1 7 > /proc/sys/kernel/printk
【8】模块传递函数
vi-t module_param 查看
* Standard types are:
* byte, 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)
char-->byte
short-->short
int -->int
unsigned int ->uint
char *p = "hello word" ==>charp
module_param(变量名,类型,权限);
module_param_array(变量名,类型,长度,权限);
MODULE_PARM_DESC(变量名,“描述字段”);
module_param(name, type, perm)
功能:接收给驱动专递的参数
参数: @name :变量名
@type :变量的类型
@perm :权限,内核会根据变量的名字传参文件
注意对于other不能有写的权限
0664
0775
0774
0662 错
hsw@linux:~/HQ/driver/day1$ modinfo demo.ko
filename: /home/hsw/HQ/driver/day1/demo.ko
license: GPL
depends:
vermagic: 3.4.39 SMP preempt mod_unload ARMv7 p2v8
parm: back_light:this is back_light rang (0-255),defaulr:255 (ushort)
在开发板上加载驱动
insmod *ko 会在/sys/module/驱动名目录/parameters
带参数加载驱动
[root@iTOP-4418]# insmod demo.ko back_light=127
[root@iTOP-4418]# cat /sys/module/demo/parameters/back_light
127
不带参数加载是默认值
[root@iTOP-4418]# cat /sys/module/demo/parameters/back_light
255
若驱动不能卸载:mkdir -p /lib/modules/3.4.39
MODULE_PARM_DESC(_parm, desc)
功能:@_parm:变量名
参数:@desc :描述字符串
传递值的时候的方法:
1.在安装的时候传递
insmod demo.ko 变量名=值
2.通过属性文件
/sys/module/驱动名目录/parameters
echo 值 >变量文件中
[root@iTOP-4418]# cat back_light
100
[root@iTOP-4418]# echo 255 > back_light
[root@iTOP-4418]# cat back_light
255
/*================================================================
* Copyright (C) 2021 HSW_study Ltd. All rights reserved.
*
* 文件名称:demo.c
* 创 建 者:HSW
* 创建日期:2021年01月12日
* 描 述:模块传递函数代码
*
================================================================*/
#include <linux/module.h>
#include <linux/init.h>
char a = 65;
module_param(a,byte,0775);
MODULE_PARM_DESC(a,"char a =65");
char *p = "helloewold";
module_param(p,charp,0775);
MODULE_PARM_DESC(p,"char * p = helloewold");
unsigned short back_light =255;
module_param(back_light,ushort,0664);
MODULE_PARM_DESC(back_light,"this is back_light rang (0-255),defaulr:255");
int array[10]={0};
int num ;
int i=0 ;
module_param_array(array,int,&num,0775);
MODULE_PARM_DESC(array,"this is (int [10]) array");
static int __init demo_init(void)
{
printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
printk("back_light=%d\n",back_light);
printk("init a = %c\n",a);
printk("p =%s\n",p);
for(i=0;i<num;i++)
{
printk("array[%d]\n",array[i]);
}
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");
MODULE_AUTHOR("HSW_YX")
Makefile
#!/bin/bash
export ARCH=arm
obj-m += demo.o
KDIR :=/home/hsw/HQ/kernel-3.4.39
PWD ?= $(shell pwd)
all:
make -C $(KDIR) M=$(PWD) modules
clean:
rm -rf *.mod.c *.o *.order *.ko *.mod.o *.symvers
【9】导出符号表
EXPORT_SYMBOL_GPL(add);
功能:将变量或者函数的符号表导出来
实际就是一个模块调用另一个模块的的函数
编译的驱动会生成一个Module.symvers
在这个文件中就是导出的符号表的信息
0x00000000 add /home/driver/demoB/demo EXPORT_SYMBOL_GP
1.编译的时候使用:
先表一提供者(demoB),编译成功后会生成一个Module.symvers
在这份文件中就有导出符号表,然后将这个文件拷贝到调用者
目录下(demoA),编译调用者模块(如果没拷贝Module.symvers编
译时候就会提示undefined)
2.安装模块的顺序:
先安装提供者模块,在安装调用者模块
3.卸载模块的顺序
先卸载调用者模块,再卸载提供者模块
注意:如果你的驱动模块调用的是内核中的函数就不需要
拷贝Module.symvers,直接编译即可。编译本来依
赖内核中的Makefile,内核模块中已经生成符号表的
信息
/*================================================================
* Copyright (C) 2021 HSW_study Ltd. All rights reserved.
*
* 文件名称:demoA.c
* 创 建 者:HSW
* 创建日期:2021年01月12日
* 描 述:
*
================================================================*/
#include <linux/module.h>
#include <linux/init.h>
extern int add(int,int);
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__);
printk("sum=%d\n",add(200,100));
}
module_init(demoA_init);
module_exit(demoA_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("HSW_YX");
/*================================================================
* Copyright (C) 2021 HSW_study Ltd. All rights reserved.
*
* 文件名称:demoB.c
* 创 建 者:HSW
* 创建日期:2021年01月12日
* 描 述:
*
================================================================*/
#include <linux/module.h>
#include <linux/init.h>
int add(int a,int b)
{
return(a+b);
}
EXPORT_SYMBOL_GPL(add);
static int __init demoB_init(void)
{
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");
MODULE_AUTHOR("HSW_YX");
Makefile
#================================================================
# Copyright (C) 2021 HSW_study Ltd. All rights reserved.
#
# 文件名称:Makefile
# 创 建 者:HSW
# 创建日期:2021年01月12日
# 描 述:make后面加MODNAME=demoA或者demoB
#
#================================================================
#!/bin/bash
MODNAME?=demo
export ARCH=arm
obj-m += $(MODNAME).o
KDIR :=/home/hsw/HQ/kernel-3.4.39
PWD ?= $(shell pwd)
all:
make -C $(KDIR) M=$(PWD) modules
clean:
rm -rf *.mod.c *.o *.order *.ko *.mod.o *.symvers