【1】课程大纲
1.Linux内核模块
2.字符设备驱动
3.并发和竞态的解决方法
4.IO模型
5.linux内核中断
6.Platform总线驱动
7.i2c总线驱动
8.spi总线驱动
9.块设备驱动
10.网卡设备驱动
【2】工作岗位
Linux设备驱动开发
Android设备驱动开发
【3】什么是驱动?arm裸机驱动和Linux设备驱动有什么区别?
驱动:通过软件驱动硬件工作的代码
共同点:
都可以驱动硬件工作
不同点:
arm裸机驱动:只能执行一份代码,在main中按照先后
顺序来执行,这份代码可以单独执行,不需要依赖其
他的代码。
Linux设备驱动:它是基于Linux内核才能够操作硬件,
如果没有Linux内核,驱动不能单独执行,也不能自动
执行(必须通过应用层控制才能执行)。可以同时安装
多个Linux设备驱动
【4】Linux系统层次
应用层: [0-3G]
APP glibc
---------------(系统调用swi)------------------------------
内核层:5大功能 [3-4G]
文件管理:通过文件系统ext2 ext3 ext4 yaffs jiffe等来
组织管理文件
内存管理:通过内存(slab)管理单元来分配和释放内存的。
进程管理:管理的是进程的创建,销毁,进程的调度等功能
网络管理:通过网络协议栈进行网络数据的收发
设备管理:设备驱动的管理
字符设备驱动:按照字节流来访问,并且是顺序访问的。
块设备驱动:按照块来访问(512字节),可以顺序访问
也可以无序访问
网卡设备驱动:网卡设备驱动是没有设备文件存在的。
(通过这种设备进行网络数据传输)
硬件层:LED CAMERA LCD TOUCHSCREEN
声卡 显卡 BEEP USB鼠标 U盘 硬盘
FLASH 交换机 路由器 猫 网卡等
【5】Linux设备驱动的使用
demo.c <==== 字符设备驱动
Kconfig :生成选项菜单的文件
/home/linux/kernel/kernel-3.4.39/drivers/char
config FARSIGHT_DEMO (选项名)
tristate "test test test ..." (选项菜单名)
help
this is a driver demo
.config :保存选配号的选项
CONFIG_FARSIGHT_DEMO=y
CONFIG_FARSIGHT_DEMO=m
#CONFIG_FARSIGHT_DEMO is not set
Makefile :用来编译驱动模块的
obj-$(CONFIG_FARSIGHT_DEMO) += demo.o
obj-y +=demo.o
obj-m +=demo.o
编译:
y:
make uImage ===>将驱动编译到uImage中
m:
make modules ===>将驱动编译生成xxx.ko文件
【6】linux内核模块
模块三要素:内核模块不能单独执行,也不能自动自行
入口
static int __init demo_init(void)
{
return 0;
}
module_init(demo_init);
出口
static void __exit demo_exit(void)
{
}
module_exit(demo_exit);
许可证
MODULE_LICENSE("GPL");
内部编译:在内核源码树中编译
外部编译:在内核源码树外进行编译
Makefile:
KERNELDIR:= /lib/modules/$(shell uname -r)/build/
//这是Makefile中的一个变量,记录ubuntu内核的路径的
//$(shell 命令) 在Makefile中起一个终端,执行命令,将
//将命令执行的结果作为返回值
PWD:=$(shell pwd)
//PWD它是记录驱动所在目录的一个变量。
all:
//all是make执行的时候的默认的标签,
//这个all的名字也可以随意指定(如modules)
make -C $(KERNELDIR) M=$(PWD) modules
//make -C dir 进入到dir目录下执行它里面的Makefile文件
//M 它是记录程序当前路径的一个变量
//make modules 编译模块,只编译M指定的目录下的模块
clean:
make -C $(KERNELDIR) M=$(PWD) clean
//make -C dir 进入到dir目录下执行它里面的Makefile文件
//M= 它是记录程序当前路径的一个变量
//make clean 清除编译,只清除M指定的目录下的模块文件
obj-m:=demo.o
//指定的要编译的模块的名字
Makefile中四种赋值的区别?
=
查找Makefile中变量最后一次被赋值
的结果,然后将它赋值给新的变量
var1=abc
var2=$(var1)
var1=def
all:
@echo $(var1) #def
@echo $(var2) #def
:= 立即赋值
var1=abc
var2:=$(var1)
var1=def
all:
@echo $(var1) #def
@echo $(var2) #abc
+= 附加赋值
var1=abc
var2=def
var2+=$(var1)
all:
@echo $(var1) #abc
@echo $(var2) #def abc
?= 询问赋值:询问变量之前是否被赋值过,
如果变量之前没有被赋值过本次赋值成立,
否则本次赋值不成立
var1?=def
all:
@echo $(var1) #def
var1=abc
var1?=def
all:
@echo $(var1) #abc
【7】驱动安装和卸载的命令
sudo insmod xxx.ko 安装驱动
lsmod 查看模块是否被安装了
sudo rmmod xxx 卸载驱动
modinfo xxx.ko 查看模块中的各种信息的命令
dmesg 查看printk打印的消息
sudo dmesg -c 先在终端上显示一遍,然后清空消息
sudo dmesg -C 直接清空消息
【8】内核打印函数
printk(打印级别 “控制格式”,变量);
printk(“控制格式”,变量); //使用的是默认级别
内核的八个打印级别如下,数字越小优先级越高。
#define KERN_EMERG “<0>”
/* system is unusable /
#define KERN_ALERT “<1>”
/ action must be taken immediately /
#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 condition /
#define KERN_INFO “<6>”
/ informational /
#define KERN_DEBUG “<7>”
/ debug-level messages */
打印级别问题:
proc/sys/kernel$ ls printk
cat /proc/sys/kernel/printk
4 4 1 7
终端的级别 消息的默认级别 终端的最大级别 终端的最小级别
只有当消息的级别大于终端的级别的
时候,消息才会显示。
由于ubuntu的终端已经被开发者修改过了,所以
即使消息的级别大于控制台的级别,消息依然不会
显示,使用虚拟控制台才执行代码
ctrl +alt +[F1-F6] 进入虚拟终端
ctrl +alt + F7 退出虚拟终端
修改默认级别的办法:
su root
echo 4 3 1 7 > /proc/sys/kernel/printk
【9】内核模块传参问题
在工作中做开发的时候,在购买屏幕硬件的时候,给你提供了
这个屏幕的驱动(xxx.ko),比如通过命令行传参的形式修改
屏幕的默认最大亮度。
Standard types are:
byte, short, ushort, int, uint, long, ulong 注意:没有char类型!!
charp: a character pointer
bool: a bool, values 0/1, y/n, Y/N.
invbool: the above, only sense-reversed (N = true).
module_param(name, type, perm)
功能:接收命令行传的参数
参数:
@name:变量名
@type: 变量的类型
@perm: 权限 0664 0775
MODULE_PARM_DESC(_parm, desc)
功能:对命令行传递的参数进行描述
参数:
@_parm:变量
@desc:描述的字符串
安装驱动的时候传参:
sudo insmod demo.ko light=80
在运行的使用传参
/sys/module/驱动命名的目录/parameters/
以变量命名的文件
su root
echo 70 > light