1.驱动代码
1.1头文件
#include <linux/init.h>
#include <linux/module.h>
1.2装载与卸载函数声明
- 当生成的
.ko文件被装载的时候,内核会调用module_init函数,向系统申请资源
- 当生成的
.ko文件被卸载的时候,内核会调用module_exit函数,释放资源
module_init(hello_drv_init);
module_exit(hello_drv_exit);
1.3装载与卸载函数实现
/*模块装载函数实现*/
static int __init hello_drv_init(void)
{
return 0;
}
/*模块卸载函数实现*/
static void __exit hello_drv_exit(void)
{
}
1.4GPL声明
MODULE_LICENSE("GPL");
1.5最简单的驱动
#include <linux/init.h>
#include <linux/module.h>
/*模块装载函数实现*/
static int __init hello_drv_init(void)
{
printk("--------%s---------\n", __FUNCTION__);
return 0;
}
/*模块卸载函数实现*/
static void __exit hello_drv_exit(void)
{
printk("--------%s---------\n", __FUNCTION__);
}
module_init(hello_drv_init);
module_exit(hello_drv_exit);
MODULE_LICENSE("GPL");
2.编译驱动代码
2.1编写Makefile
- 在第一次执行
make指令后,会自动执行Makefile文件
- 首先判断
KERNELRELEASE宏是否为空,默认为空,故执行make -C $(KERNEL_DIR) M=$(CUR_DIR) modules
- 执行后Makefile会调用内核里面的Makefile将
KERNELRELEASE赋值
- 之后再次执行Makefile,此可会跳转到
obj-m += hello_driver.o编译内核
- 通用Makefile文件
# 指定保存ko文件的文件夹,可以是文件系统的某个文件夹
ROOTFS_DIR = /home/hanqi/my_drivers_ko
# 比较函数
ifeq ($(KERNELRELEASE), )
# 指向编译后内核的路径,一定是编译之后的内核!!!!
KERNEL_DIR = /home/hanqi/pi/linux-rpi-4.14.y
# 指向当前文件路径,即保存驱动.c文件的路径
CUR_DIR = $(shell pwd)
all:
# 编译驱动,需指定交叉编译工具路径
make -C $(KERNEL_DIR) M=$(CUR_DIR) modules ARCH=arm CROSS_COMPILE=/home/hanqi/pi/toolchain/tools-master/arm-bcm2708/arm-linux-gnueabihf/bin/arm-linux-gnueabihf-
clean:
# 清除编译后的驱动文件
make -C $(KERNEL_DIR) M=$(CUR_DIR) clean
install:
# 拷贝驱动.ko文件到目标文件夹
cp -raf *.ko $(ROOTFS_DIR)
else
# 指定需要编译的驱动文件,此处为hello_driver.o
obj-m += hello_driver.o
endif
hanqi@hanqi-PC:~/my_drivers$ ls
hello_driver.c Makefile
# 执行make指令
hanqi@hanqi-PC:~/my_drivers$ make
make -C /home/hanqi/pi/linux-rpi-4.14.y M=/home/hanqi/my_drivers modules ARCH=arm CROSS_COMPILE=/home/hanqi/pi/toolchain/tools-master/arm-bcm2708/arm-linux-gnueabihf/bin/arm-linux-gnueabihf-
make[1]: Entering directory '/home/hanqi/pi/linux-rpi-4.14.y'
CC [M] /home/hanqi/my_drivers/hello_driver.o
Building modules, stage 2.
MODPOST 1 modules
CC /home/hanqi/my_drivers/hello_driver.mod.o
LD [M] /home/hanqi/my_drivers/hello_driver.ko
make[1]: Leaving directory '/home/hanqi/pi/linux-rpi-4.14.y'
hanqi@hanqi-PC:~/my_drivers$ ls
hello_driver.c hello_driver.mod.c hello_driver.o modules.order
hello_driver.ko hello_driver.mod.o Makefile Module.symvers
# 拷贝ko文件
hanqi@hanqi-PC:~/my_drivers$ make install
cp -raf *.ko /home/hanqi/my_drivers_ko
# 清除多余文件
hanqi@hanqi-PC:~/my_drivers$ make clean
make -C /home/hanqi/pi/linux-rpi-4.14.y M=/home/hanqi/my_drivers clean
make[1]: Entering directory '/home/hanqi/pi/linux-rpi-4.14.y'
CLEAN /home/hanqi/my_drivers/.tmp_versions
CLEAN /home/hanqi/my_drivers/Module.symvers
make[1]: Leaving directory '/home/hanqi/pi/linux-rpi-4.14.y'
hanqi@hanqi-PC:~/my_drivers$ ls
hello_driver.c Makefile
# 拷贝成功
hanqi@hanqi-PC:~/my_drivers$ ls /home/hanqi/my_drivers_ko/
hello_driver.ko
hanqi@hanqi-PC:~/my_drivers$
2.2直接修改内核源码目录
# 进入源码下的字符设备驱动文件夹
hanqi@hanqi-PC:~/my_drivers$ cd /home/hanqi/pi/linux-rpi-4.14.y/drivers/char
# 将当前编写好的驱动代码拷贝到内核驱动目录下
hanqi@hanqi-PC:~/pi/linux-rpi-4.14.y/drivers/char$ cp ~/my_drivers/hello_driver.c hello_driver.c
# 修改当前驱动路径下的Makefile文件
hanqi@hanqi-PC:~/pi/linux-rpi-4.14.y/drivers/char$ vim Makefile
# 在其中添加我们自己的驱动hello_driver.o
obj-y += mem.o random.o
obj-m += hello_driver.o # 自己的驱动
obj-$(CONFIG_TTY_PRINTK) += ttyprintk.o
obj-y += misc.o
obj-$(CONFIG_ATARI_DSP56K) += dsp56k.o
obj-$(CONFIG_VIRTIO_CONSOLE) += virtio_console.o
obj-$(CONFIG_RAW_DRIVER) += raw.o
obj-$(CONFIG_SGI_SNSC) += snsc.o snsc_event.o
obj-$(CONFIG_MSPEC) += mspec.o
obj-$(CONFIG_UV_MMTIMER) += uv_mmtimer.o
obj-$(CONFIG_IBM_BSR) += bsr.o
obj-$(CONFIG_SGI_MBCS) += mbcs.o
obj-$(CONFIG_BFIN_OTP) += bfin-otp.o
...
# 回到内核根目录下编译驱动,注意此时只需要编译内核,不用加镜像,设备树的选项
hanqi@hanqi-PC:~/pi/linux-rpi-4.14.y$ ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KERNEL=kernel7 make -j2 modules
CHK include/config/kernel.release
CHK include/generated/uapi/linux/version.h
CHK include/generated/utsrelease.h
CHK scripts/mod/devicetable-offsets.h
CHK include/generated/timeconst.h
CHK include/generated/bounds.h
CHK include/generated/asm-offsets.h
CALL scripts/checksyscalls.sh
CHK kernel/config_data.h
Building modules, stage 2.
MODPOST 1523 modules
CC drivers/char/hello_driver.mod.o
LD [M] drivers/char/hello_driver.ko # 编译好的驱动
# 将.ko文件拷贝到树莓派上
hanqi@hanqi-PC:~/pi/linux-rpi-4.14.y$ scp drivers/char/hello_driver.ko pi@192.168.1.39:/home/pi
pi@192.168.1.39's password:
hello_driver.ko 100% 3412 327.1KB/s 00:00
# 在树莓派上装载驱动
pi@raspberrypi:~/my_drivers $ sudo insmod hello_driver.ko
# 查看驱动
pi@raspberrypi:~/my_drivers $ lsmod
Module Size Used by
hello_driver 16384 0
fuse 106496 3
brcmfmac 278528 0
brcmutil 16384 1 brcmfmac
cfg80211 569344 1 brcmfmac
rfkill 24576 4 cfg80211
snd_bcm2835 32768 1
snd_pcm 98304 1 snd_bcm2835
snd_timer 32768 1 snd_pcm
snd 69632 5 snd_timer,snd_bcm2835,snd_pcm
uio_pdrv_genirq 16384 0
uio 20480 1 uio_pdrv_genirq
fixed 16384 0
i2c_dev 16384 0
ip_tables 24576 0
x_tables 32768 1 ip_tables
ipv6 425984 24
# 卸载驱动
pi@raspberrypi:~/my_drivers $ sudo rmmod hello_driver
# 查看后台打印信息
pi@raspberrypi:~/my_drivers $ dmesg
...
[ 1180.032406] ---------hello_drv_init---------
[ 1382.148499] ---------hello_drv_exit---------
...
2.3相关指令
insmod装载驱动,装载驱动时候需要加ko后缀名称
rmmod卸载驱动,卸载驱动时候不用加ko后缀名称
lsmod查看已安装的驱动
dmesg查看后台打印信息