yocto-第29篇-源码树外添加Linux驱动模块(hello驱动实例)

By: fu linux
E-mail: fulinux@sina.com
Blog: https://blog.csdn.net/fulinus
喜欢的盆友欢迎点赞和订阅!
你的喜欢就是我写作的动力!

问题导向

前面讲的很多内容都是如何添加或修改应用程序,很多盆友应该很好奇如何在yocto中添加或修改BSP的东西,比如如何添加一个熟知的hello驱动实例,

上一篇我们编译成功的SDK也可以用来编译linux内核源码树外的驱动模块,SDK交叉工具链之所以可以用来编译linux kernel模块,是因为SDK镜像中包含了开发使用的dev-pikgs, 内核开发相关的kernel-dev 和kernel-devsrc packages已经安装到SDK Image中了。以此按照使用SDK的步骤之后,在驱动模块源码里面直接就可以make编译。

但是对于我希望整合到yocto项目的要求来说显然是不符合我们的气质的

本篇讲解如何在自己的layer中添加一个hello驱动的recipe。

hello驱动recipe模板

好在poky/meta-skeleton的layer中已经为我们提供了一个hello驱动模板,只需要做简单的复制既可以实现我们的需求。meta-skeleton中的skeleton英文单词的意思是骨架的意思,这个meta-skeleton的layer就是一个框架性质的层,用于提供创建layer的一个参考。例如hello模块的recipe模板如下路径:

hello驱动模块的BB文件

cd poky/
ls meta-skeleton/recipes-kernel/hello-mod/
files  hello-mod_0.1.bb
ls meta-skeleton/recipes-kernel/hello-mod/files/
COPYING  hello.c  Makefile
cat meta-skeleton/recipes-kernel/hello-mod/hello-mod_0.1.bb
SUMMARY = "Example of how to build an external Linux kernel module"
LICENSE = "GPLv2"
LIC_FILES_CHKSUM = "file://COPYING;md5=12f884d2ae1ff87c09e5b7ccc2c4ca7e"

inherit module

SRC_URI = "file://Makefile \
           file://hello.c \
           file://COPYING \
          "

S = "${WORKDIR}"

# The inherit of module.bbclass will automatically name module packages with
# "kernel-module-" prefix as required by the oe-core build environment.

RPROVIDES_${PN} += "kernel-module-hello"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

驱动源码hello.c

参考如下:

cat meta-skeleton/recipes-kernel/hello-mod/files/hello.c 
  • 1
/******************************************************************************
 *
 *   Copyright (C) 2011  Intel Corporation. All rights reserved.
 *
 *   This program is free software;  you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; version 2 of the License.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
 *   the GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program;  if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 *****************************************************************************/

#include <linux/module.h>

int init_module(void)
{
        printk("Hello World!\n");
        return 0;
}

void cleanup_module(void)
{
        printk("Goodbye Cruel World!\n");
}

MODULE_LICENSE("GPL");
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

驱动Makefile文件

cat meta-skeleton/recipes-kernel/hello-mod/files/Makefile
obj-m := hello.o

SRC := $(shell pwd)

all:
        $(MAKE) -C $(KERNEL_SRC) M=$(SRC)

modules_install:
        $(MAKE) -C $(KERNEL_SRC) M=$(SRC) modules_install

clean:
        rm -f *.o *~ core .depend .*.cmd *.ko *.mod.c
        rm -f Module.markers Module.symvers modules.order
        rm -rf .tmp_versions Modules.symvers
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

这里需要注意的重要一点是KERNEL_SRC变量。module类将此变量和KERNEL_PATH变量设置为${STAGING_KERNEL_DIR},并提供构建模块所需的Linux内核构建信息。如果你的模块Makefile使用不同的变量,你可以重写do_compile任务(或者说是函数),或者为Makefile创建一个patch文件,以使用更典型的KERNEL_SRC或KERNEL_PATH变量。

还有一个需要的License文件COPYING,它对于的md5值是:

poky]$ md5sum meta-skeleton/recipes-kernel/hello-mod/files/COPYING 
12f884d2ae1ff87c09e5b7ccc2c4ca7e  meta-skeleton/recipes-kernel/hello-mod/files/COPYING
  • 1
  • 2

与bb文件中的值匹配哦

这里说明下,一般我们会在recipe目录下创建一个files目录,然后然后将补丁文件或者源码文件等其他文件放在files目录下,然后在bb文件中引用时,在 SRC_URI 变量后面用**“file://”**前缀引用相应的文件。参考上面的hello-mod_0.1.bb文件。

在meta-mylayer中添加模块的recipe

之前的项目中我们已经创建了我们自己的meta-mylayer,下面在meta-mylayer中创建一个recipe目录,命名为recipes-mymodules,过程如下:

poky]$ mkdir -p meta-mylayer/recipes-mymodules
  • 1

之后将hello-mod目录复制到我们自己的recipe目录下:

poky]$ cp -rf meta-skeleton/recipes-kernel/hello-mod/ meta-mylayer/recipes-mymodules/
  • 1

好了,可以开始编译了:

poky]$ source oe-init-build-env 
build]$ bitbake hello-mod
  • 1
  • 2

没有任何错误,编译完成了。我们看下ko文件所在目录:

build]$ cd tmp/work/qemux86_64-poky-linux/hello-mod/0.1-r0/
0.1-r0]$ ls image/ 
etc  lib  usr
0.1-r0]$ ls image/lib/modules/5.4.50-yocto-standard/extra/hello.ko 
  • 1
  • 2
  • 3
  • 4

hello-mod模块编译出来的模块就是hello.ko文件,我们看到image目录下还有etc/和user目录,etc下创建了两个子目录用于添加模块的启动脚本,但是因为我们没有写这个启动脚本所以是空目录,而user目录如下:

cat image/usr/include/hello-mod/Module.symvers
#空
  • 1
  • 2

驱动模块添加到images中

此时模块编译完成,但是驱动模块并没有在文件系统中,我们需要在机器的配置文件中添加下面4个之一变量,参考如下:

  • MACHINE_ESSENTIAL_EXTRA_RDEPENDS
  • MACHINE_ESSENTIAL_EXTRA_RRECOMMENDS
  • MACHINE_EXTRA_RDEPENDS
  • MACHINE_EXTRA_RRECOMMENDS

每个变量都有不同的含义,这里我们使用MACHINE_EXTRA_RRECOMMENDS,而不是MACHINE_EXTRA_RDEPENDS是因为我们希望即便镜像中无法包含该模块,也不会导致构建失败。因为这个是一个无关紧要的模块,对于系统正常运行没有任何影响。
使用MACHINE_EXTRA_RRECOMMENDS变量如下:

MACHINE_EXTRA_RRECOMMENDS += "kernel-module-hello"
  • 1

该值是通过将不带.ko扩展名的模块文件名附加到字符串“kernel module-”中而得到的。

在哪里添加呢?如下所示:

poky]$ vim meta/conf/machine/qemux86-64.conf 
  • 1

找到下面这行:

MACHINE_EXTRA_RRECOMMENDS = "kernel-module-snd-ens1370 kernel-module-snd-rawmidi"
  • 1

在该行后面追加hello模块,如下:

MACHINE_EXTRA_RRECOMMENDS = "kernel-module-snd-ens1370 kernel-module-snd-rawmidi kernel-module-hello"
  • 1

重新使能一下环境编译(通常我修改了bb文件后保险起见会重新source环境):

poky]$ source oe-init-build-env 
build]$ bitbake hello-mod
build]$ bitbake core-image-sato
  • 1
  • 2
  • 3

为确保hello.ko文件已经在文件系统里面了我们可以先看下rootfs里面是否存在:

build]$ ls tmp/work/qemux86_64-poky-linux/core-image-sato/1.0-r0/rootfs/lib/modules/5.4.50-yocto-standard/extra/
hello.ko
  • 1
  • 2

是存在的。

验证驱动加载和卸载演示

接着运行验证:

build]$ runqemu qemux86-64
  • 1

另起一个终端:

$ ssh-keygen -f "/home/peeta/.ssh/known_hosts" -R 192.168.7.2
$ ssh root@192.168.7.2
  • 1
  • 2

首先清空内核log:

root@qemux86-64:~# dmesg -c
  • 1

然后加载驱动:

root@qemux86-64:~# modprobe hello
root@qemux86-64:~# dmesg
[  292.480170] Hello World!
  • 1
  • 2
  • 3

打印了驱动初始化字符串。

卸载驱动演示

root@qemux86-64:~# rmmod hello
root@qemux86-64:~# dmesg      
[  292.480170] Hello World!
[  354.720800] Goodbye Cruel World!
  • 1
  • 2
  • 3
  • 4

打印了驱动移除时的字符串。

综上hello模块添加成功了。

End~