Android Linux驱动初见

前言

要更好的掌握Android系统,必然绕不过去的就是 Kernel 层的驱动程序。

所谓驱动程序,就是一个与硬件交互的程序,他既可以支撑硬件之间的交互,也可以支撑硬件与软件之间的交互。

Linux Kernel驱动程序大体上分为三类:

  1. 字符设备
  2. 块设备
  3. 网络设备

简单来说,字符设备可以像文件一样操作,正常情况下无法随机寻址。而块设备可以当做是比较特殊的字符设备,不同的是块设备传输单位是块,比如硬盘操作就是以块为单位进行操作。而网络设备则是使用套接字通信。

由于内核和驱动程序运行在内核态,所以驱动程序无法使用位于用户态实现的C库函数(但内核本身有提供一些库),另外驱动程序和内核缺乏用户态程序一样的内存保护机制,一旦越界,很有可能导致整个kernel崩溃,所以务必注意。

驱动程序结构

一个典型的驱动程序至少有如下几个部分:

  1. 模块头文件

    <linux/module.h> <linux/init.h> 这几乎是驱动程序必须使用的,其包含了大量宏以及模块加载释放等函数定义

  2. 模块加载函数

    提供一个返回值为 int 的函数作为加载函数,并且使用 module_init 加载

  3. 模块卸载函数

    提供一个返回值为 void 的函数作为退出函数,并且使用 module_exit 来取消加载

  4. 模块许可声明

    如果你的模块没有使用 MODULE_LICENSE 声明权限,你的模块将无法通过编译,要注意的是内核中有很多 GPL 协议的部分,一旦使用你的模块就必须同样使用这个协议,因为该协议有传染性。

一个HelloWorld模块

下面我们开始尝试一下编写自己的HelloWorld,按照结构,至少拥有这几部分:

#include <linux/init.h>
#include <linux/module.h>

static int helloworld_init(void)
{
	printk(KERN_INFO "Hello World, Module loaded. \n");
	
	return 0;
}

static void helloworld_exit(void)
{
	printk(KERN_INFO "Goodbye world, Module unloaded. \n ");
}

module_init(helloworld_init); //入口函数
module_exit(helloworld_exit); //出口函数

MODULE_LICENSE("GPL");						//协议声明
MODULE_AUTHOR("GARFIELD");					//作者
MODULE_DESCRIPTION("Just a hello world!");	//描述,可不写

这样一来,一个基本的框架就有了,然后就是编译了。

编译模块驱动

由于我的平台是RK3399 Android 7.1,为了不扰乱原本的模块目录,因此我这里新建一个目录,并修改 MakefileKconfig 文件。

首先新建一个目录 hello_drvierkernel/drivers/char 中,当然这里你随意。

然后我将这个.c文件放到 kernel/drivers/char/hello_driver 目录中,然后为其编写一个 Kconfig

cd kernel/drivers/char/hello_driver
touch Kconfig
gedit Kconfig

# "Kconfig" 加入如下内容
config HELLOWORLD
	tristate "Hello world Modules"
	help
	  Just a hello world modules
	  
touch Makefile
gedit Makefile

# "Makefile" 加入如下内容
obj-$(CONFIG_HELLOWORLD)		+= helloworld.o

然后,还需要在上一层目录的 Makefile 中的最后一行加入对该目录的包含,否则就不会进去编译啦。

##  "kernel/drivers/char/Makefile"
obj-$(CONFIG_HELLOWORLD)		+= hello_driver/

完成以上内容添加后,开始设置内核,打开该模块。

make menuconfig

在这里插入图片描述

选中后按 M 让其按模块重新编译内核 make -j8 ARCH=arm64 ,就可以得到ko驱动啦。

在这里插入图片描述

测试驱动

由于驱动按模块编译,所以不需要重新刷kernel到板,只要把ko文件push上去执行就可以了。

adb push helloworkd.ko /system/lib/modules/
adb shell insmod /system/lib/modules/helloworld.ko
adb shell rmmod /system/lib/modules/helloworkd.ko

执行后可以看到驱动入口函数和出口函数都被调用,打印了信息:

[ 3310.624502] Hello World, Module loaded. 
[ 3393.958614] Goodbye world, Module unloaded.

单独编译驱动模块

由于上述方法编译还是比较麻烦,所以可以通过编写make文件来手动编译模块。

因为我是跨平台的,所以要配置两个目录,一个是kernel的目录,另一个是交叉编译器的目录,但是如果你不是跨平台的话后者是可以忽略的

我的驱动是单独放在的 hello_driver 目录里,所以要将上面编写过的 Makefile 改动如下。

## KERNELRELEASE 是Kernel顶层Makefile定义的,如果没有执行过,则会指定Kernel目录及交叉编译编译器目录,执行独立编译。
## 若定义过,实际上等于是kernel在全编,正常执行obj-m即可
## 若在该目录直接执行的Make,则走else分支单独编译
ifneq ($(KERNELRELEASE),)
	obj-m += helloworld.o
else
	KDIR := /home/garfield/Firefly339/kernel
all:
	make -C $(KDIR) M=$(PWD) modules ARCH=arm64 CROSS_COMPILE=/home/garfield/Firefly339/prebuilts/gcc/linux-x86/aarch64/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-
clean:
	rm -f *.ko *.o *.mod.o *.mod.c *.symvers modul*
endif
/linux-x86/aarch64/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-
clean:
	rm -f *.ko *.o *.mod.o *.mod.c *.symvers modul*
endif
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值