对于无操作系统的驱动,就是裸机,也就是我们常说的单片机。我们并不需要操作系统,可以直接使用单片机进行通断控制,从编程角度来看,就是直接控制寄存器。
对于有操作系统时的驱动(此处来到我们要说的linux系统驱动),我们开发驱动需要基于linux的各种框架进行编程,按照linux系统给出的框架进行编程,可以提供一个统一的接口给应用程序调用(统一的接口就是指dev下的设备节点)。
linux驱动分为字符设备、网络设备,块设备三类(此处不再赘述,可百度)
linux源码目录是一个树状结构,下图为linux顶层目录的各个文件说明
一个最简单的驱动加载程序应具备以下几个方面:
1.头文件
2.驱动加载函数
3.驱动卸载函数
4.许可证声明。linux是开源的驱动在加载的时候也需要遵守相关协议,可以接收到的license有GPL、GPL v2等
5.模块参数(可选)选择是否被编译为内核模块
#include <linux/module.h>
#include <linux/init.h>
static int helloworld_init(void)//模块加载函数需要返回一个int类型
{
printk("hello world\n");//驱动程序的打印需要使用printk!!!!因为我们并没有加载stdio.h这个头文件,难道不是嘛
return 0;
}
static void helloworld_exit(void)//驱动卸载程序是没有返回值的
{
printk("bye world\n");
}
module_init(helloworld_init);//此处调用模块加载函数,输出hello world
module_exit(helloworld_exit);//此处调用模块卸载函数,输出bye world
MODUBLE_LICENSE("GPL");//此处是声明调用的协议
那么如何编译linux驱动程序呢?
共有两种方法:
第一:将驱动程序放在Linux内核当中,然后编译Linux内核,将驱动编译到Linux内核当中。
第二:将驱动编译成内核模块,独立于Linux内核之外
此处的内核模块是指,linux所具有的功能,使用Linux模块可以减少内核体积,加快速度。内核模块的后缀是.ko
将驱动编译为内核模块往往通过makefile,以下为makefile的代码
#在#号后边表示注释
#此处-m是指编译为模块,“+=” 表示对变量进行追加赋值,表示把目标文件hello.o作为模块进行编译
obj-m += hello.o
#对于变量赋值的情况 “:=”是立刻赋值,就是a:= $(object) 变量a就已经等于 变量object了,此处为内核源码的绝对路径,你需要根据自己的实际路径进行填写
KDIR := /home/topeet/linux-kernel
#"?="FOO ?= bar
#其含义是,如果 FOO 没有被定义过,那么变量 FOO 的值就是“bar”。而此处$(shell pwd)表示使用命令 pwd获取当前地址
PWD ?= $(shell pwd)
all:
make -C $(KDIR) M=$(PWD) modules
clean:
rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.order
在编译之前需要,首先编译通过linux内核,然后设置环境变量
交叉编译器也需要根据自己的实际路径进行填写
模块加载命令
insmod模块名
modprobe 模块名
模块卸载命令
rmmod 模块名
查看模块命令
lsmod或者用cat /pro/modules
查看内核模块信息
modinfo 模块名
在linux内核源码使用命令make menuconfig,记得使用这条命令之前,先用环境变量初始化linux的架构,有三种方式,空的,不对驱动进行操作,*编译进内核,M编译成linux内核模块