美文网首页
openwrt (三)入门FAQ

openwrt (三)入门FAQ

作者: onelifeisall | 来源:发表于2018-05-23 10:46 被阅读66次

    openwrt作为一个基于linux开发的比较完善的嵌入式系统,可以快速移植到各种平台上。初次下载开源代码后,简单浏览后很是诧异,居然没看到uboot和kernel部分的代码,甚至没看到任何模块的代码,最多只是些patch和配置文件。
    按照文档编译后,发现多了些工程目录,进而发现了很多源码,猜测到大概是Makefile或feed脚本在编译时在线下载的代码。为了后来者,初次入门openwrt少踩坑,完成此章,内容多为我入门的困惑与解答。

    1. uboot和kernel在哪里?

    事实上,拿到marvell release代码后会看到:

        $ tree -L 1 marvell/
        marvell/
        ├── fastpath
        ├── fota
        ├── linux
        ├── lte-telephony
        ├── obm
        ├── services
        ├── swd
        ├── uboot
        └── webui
    

    这些明显是marvell自己定制过的一些代码包,针对这个平台算是找到了正确的代码位置,但我们并不了解这个过程,并且开源代码部分是没有这种结构的,所以我们要继续探究下去,提出问题:

    1. 官方release的openwrt没有定制的情况下uboot和kernel在哪?
    2. marvell这种定制是如何实现的?

    在目录结构和功能不了解的情况下会感觉无从下手而四处乱摸,通过上一章了解了目录结构,很自然的就会想到package目录,进去看看:

        $ cd package
        $ tree -L 1 boot 
        boot/
        ├── uboot-mvebu
        ├── uboot-mxs
        ├── uboot-omap 
        ├── uboot-oxnas
        ├── ...
        └── yamonenv
    

    最终发现,package/boot/uboot-mmp/ :

        $ cat package/boot/uboot-mmp/Makefile
        include $(TOPDIR)/rules.mk
    
        PKG_NAME:=u-boot
        PKG_VERSION:=2014
        PKG_RELEASE:=1
    
        USE_SOURCE_DIR:=$(MRVLDIR)/uboot
        PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
        PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(BUILD_VARIANT)/$(PKG_NAME)-$(PKG_VERSION)
    
        include $(INCLUDE_DIR)/package.mk
    

    这里面看到了'MRVLDIR',查询后发现MRVLDIR:=$(TOPDIR)/marvell,

        $ ls -l build_dir/target-arm_cortex-a7+neon-vfpv4_uClibc-1.0.25_eabi/u-boot-nezha3_dkb/
        total 0
        lrwxrwxrwx 1 tjd tjd 56 May 15 15:56 u-boot-2014 -> /home/tjd/datadisk/marvell/OpenWrt/openwrt/marvell/uboot
    

    确认无疑了!!

    package/boot/uboot-mmp/就是marvell的uboot定义的位置,变量USE_SOURCE_DIR指定了uboot使用的源码。

    知道了marvell定制uboot源码的方法,再看一下官方release版本的uboot,已Beaglebone black板项目为例:

        $ cat package/boot/uboot-omap/Makefile
        include $(TOPDIR)/rules.mk
        include $(INCLUDE_DIR)/kernel.mk
    
        PKG_VERSION:=2017.01
        PKG_RELEASE:=2
    
        PKG_HASH:=6c425175f93a4bcf2ec9faf5658ef279633dbd7856a293d95bd1ff516528ecf2
    
        include $(INCLUDE_DIR)/u-boot.mk
        include $(INCLUDE_DIR)/package.mk
    
        define U-Boot/Default
          BUILD_TARGET:=omap
          UBOOT_IMAGE:=u-boot.img MLO
          UENV:=default
        endef
    
        define U-Boot/omap4_panda
          NAME:=Pandaboard
          BUILD_DEVICES:=omap4-panda
        endef
    
        define U-Boot/am335x_boneblack
          NAME:=TI AM335x BeagleBone Black
          BUILD_DEVICES:=am335x-boneblack
        endef
        ...
    

    很遗憾,这里没有USE_SOURCE_DIR指定源码位置,只是指定了使用的uboot的版本号,目录中也没有源码:

        package/boot/uboot-omap/
        ├── files
        │   └── uEnv-default.txt
        ├── Makefile
        └── patches
            ├── 101-disable-thumb-omap3.patch
            ├── 102-minify-spl.patch
            ├── 103-disable-fat-write-spl.patch
            ├── 104-omap3-overo-enable-thumb.patch
            └── 105-serial-ns16550-bugfix-ns16550-fifo-not-enabled.patch
    
        2 directories, 7 files
    

    这里保存了针对bbb的uboot相关patch,联想到与硬件相关的内容是以patch存在的,u-boot应该是官方发布的原版代码,上一章提到了dl目录保存了从网络上下载的代码包,编译时会解压源码包到build_dir中,现在看一下:

        $ ls build_dir/target-arm_cortex-a8+vfpv3_musl_eabi/u-boot-am335x_boneblack/u-boot-2017.01/
        api    cmd        configs  drivers   fs       Kconfig   MAINTAINERS  MLO.byteswap  README           spl         tools       u-boot.cfg          u-boot.lds        u-boot.srec
        arch   common     disk     dts       include  lib       Makefile     net           scripts          System.map  u-boot      u-boot.cfg.configs  u-boot.map        u-boot.sym
        board  config.mk  doc      examples  Kbuild   Licenses  MLO          post          snapshot.commit  test        u-boot.bin  u-boot.img          u-boot-nodtb.bin
    

    前面看到package/boot/uboot-omap/中有patch,是不是已经合入了呢? 对比看看:

        $ cat package/boot/uboot-omap/patches/101-disable-thumb-omap3.patch
        Index: u-boot-2017.01/include/configs/ti_omap3_common.h
        ===================================================================
        --- u-boot-2017.01.orig/include/configs/ti_omap3_common.h
        +++ u-boot-2017.01/include/configs/ti_omap3_common.h
        @@ -80,4 +80,9 @@
         /* Now bring in the rest of the common code. */
         #include <configs/ti_armv7_omap.h>
    
        +/* beagleboard doesnt boot with thumb */
        +#ifdef CONFIG_SYS_THUMB_BUILD
        +#undef CONFIG_SYS_THUMB_BUILD
        +#endif
        +
         #endif /* __CONFIG_TI_OMAP3_COMMON_H__ */
    
        $ diff dl/u-boot-2017.01_unpack/include/configs/ti_omap3_common.h build_dir/target-arm_cortex-a8+vfpv3_musl_eabi/u-boot-am335x_boneblack/u-boot-2017.01/include/configs/ti_omap3_common.h
        82a83,87
        > /* beagleboard doesnt boot with thumb */
        > #ifdef CONFIG_SYS_THUMB_BUILD
        > #undef CONFIG_SYS_THUMB_BUILD
        > #endif
    

    看来patch是已经打上去了。

    1. 官方的uboot先解压到build_dir中,再打上package/boot/uboot-xxx/patches中的patch,再编译。
    2. marvell通过变量USE_SOURCE_DIR指定本地代码库,连接到build_dir中,然后编译。

    再看看kernel应该是类似的:区别是kernel不在package中而是target中,引用外部库的变量不是USE_SOURCE_DIR而是CONFIG_EXTERNAL_KERNEL_TREE

        $ cat target/linux/mmp/Makefile
        include $(TOPDIR)/rules.mk
    
        ARCH:=arm
        BOARD:=mmp
        BOARDNAME:=Marvell MMP
        SUBTARGETS=pxa1826
        FEATURES:=squashfs jffs2_nand nand ubifs
    
        LINUX_VERSION:=3.10.33
        CONFIG_EXTERNAL_KERNEL_TREE:=$(MRVLDIR)/linux
    
        define Target/Description
                Build firmware images for Marvell MMP SoC
        endef
    
        include $(INCLUDE_DIR)/target.mk
    

    2. 如何编译自己的代码包?

    用户态模块

    新建getevent包最终如下:

    $ tree package/xxx/
    xxx/
    └── getevent
        ├── Makefile
        └── src
            ├── getevent.c
            ├── getevent.h
            └── Makefile
    

    配置顶层Makefile

        $ vi package/xxx/getevent/Makefile
        include $(TOPDIR)/rules.mk
    
        PKG_NAME:=getevent
        PKG_RELEASE:=1
    
        # 定义编译软件包的路径
        PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
    
        include $(INCLUDE_DIR)/package.mk
    
        # 软件包描述,可以在make menuconfig中体现出来
        define Package/getevent
          SECTION:=Xxxx
          CATEGORY:=xxx ddd
          TITLE:=linux inputevent test tool
        endef
    
        # 本包安裝的配置文件,一行一个
        define Package/dnsmasq/conffiles
        #/etc/config/dhcp
        #/etc/dnsmasq.conf
        endef
    
        # make package/xxx/getevent/prepare时被执行
        define Build/Prepare
                mkdir -p $(PKG_BUILD_DIR)
                $(CP) ./src/* $(PKG_BUILD_DIR)/
        endef
        
        # 软件包的介绍信息
        define Package/helloworld/description
            helloworld,first self-made.
        endef
    
        # 对执行./configure时的特殊处理
        define Build/Configure
        endef
    
        # 执行make或make package/xxx/getevent/compile时处理 
        define Build/Compile
                $(MAKE) -C $(PKG_BUILD_DIR) \
                        CC="$(TARGET_CC)" \
                        CFLAGS="$(TARGET_CFLAGS) -Wall" \
                        LDFLAGS="$(TARGET_LDFLAGS)"
        endef
    
        # make install时处理 
        # INSTALL_DIR 决定了通过opkg安装ipk的目标目录
        define Package/getevent/install
                $(INSTALL_DIR) $(1)/usr/sbin
                $(INSTALL_BIN) $(PKG_BUILD_DIR)/getevent $(1)/usr/sbin/
        endef
    
        $(eval $(call BuildPackage,getevent))
    

    编辑src目录下的Makefile

        CC = gcc
        CFLAGS = -Wall
        OBJS = getevent.o
    
        all: getevent
    
        %.o: %.c
                $(CC) $(CFLAGS) -c -o $@ $<
    
        getevent: $(OBJS)
                $(CC) -o $@ $(OBJS)
    
        clean:
                rm -f getevent *.o
    

    注意,这里一个代码包中有两个Makefile

    下一步时make menuconfig,选择CONFIG_PACKAGE_getevent=mCONFIG_PACKAGE_getevent=y否则编译不出东西,会报:

        WARNING: skipping getevent -- package not selected
    

    config配置成m时会编译成ipk文件,可以按需安装;配置成y时,是buildin,内置到release软件中,无需安装。

    内核模块

    最终包如下:
    
        $ tree  khello/
        khello/
        ├── Makefile
        └── src
            ├── khello.c
            └── Makefile
    
        1 directory, 3 files
    
    配置顶层Makefile
    
        $ cat khello/Makefile
        #
        # Copyright (C) 2008 OpenWrt.org
        #
        # This is free software, licensed under the GNU General Public License v2.
        # See /LICENSE for more information.
        #
    
        include $(TOPDIR)/rules.mk
        include $(INCLUDE_DIR)/kernel.mk
    
        PKG_NAME:=khello
        PKG_RELEASE:=2
    
        include $(INCLUDE_DIR)/package.mk
    
        define KernelPackage/khello
          # 指定menuconfig的路径
          SUBMENU:=Other modules
          TITLE:=kernel Helloworld test
          # 依赖的模块
          DEPENDS:=
          # 目标文件
          FILES:=$(PKG_BUILD_DIR)/khello.ko
          KCONFIG:=
        endef
    
        define KernelPackage/khello/description
         Kernel module for helloword example.
        endef
    
        EXTRA_KCONFIG:= \
                CONFIG_HELLO=m
    
        EXTRA_CFLAGS:= \
                $(patsubst CONFIG_%, -DCONFIG_%=1, $(patsubst %=m,%,$(filter %=m,$(EXTRA_KCONFIG)))) \
                $(patsubst CONFIG_%, -DCONFIG_%=1, $(patsubst %=y,%,$(filter %=y,$(EXTRA_KCONFIG)))) \
    
        MAKE_OPTS:= \
                ARCH="$(LINUX_KARCH)" \
                CROSS_COMPILE="$(TARGET_CROSS)" \
                SUBDIRS="$(PKG_BUILD_DIR)" \
                EXTRA_CFLAGS="$(EXTRA_CFLAGS)" \
                $(EXTRA_KCONFIG)
    
        define Build/Prepare
                mkdir -p $(PKG_BUILD_DIR)
                $(CP) ./src/* $(PKG_BUILD_DIR)/
        endef
    
        define Build/Compile
                $(MAKE) -C "$(LINUX_DIR)" \
                        $(MAKE_OPTS) \
                        modules
        endef
    
        $(eval $(call KernelPackage,khello))
    

    编辑src目录下的Makefile

        $ cat package/revoview/khello/src/Makefile
        obj-$(CONFIG_HELLO)  += khello.o
    

    编辑khello.c

        $ cat package/revoview/khello/src/khello.c
        #include <linux/init.h>
        #include <linux/module.h>
        #include <linux/kernel.h>
    
        static int __init hello_init(void)
        {
                printk("Hello Kernel\n");
                return 0;
        }
        module_init(hello_init);
    
        static void __exit hell_exit(void)
        {
                printk("Bye Kernel");
        }
        module_exit(hell_exit);
        MODULE_LICENSE("GPL");
    

    3. 模块如何单独编译?

    1. 首先有个顶层Makefile,如上面的两个案例

    2. 如何模块需要区别于默认编译选项,Makefile中可以包含下面几个重要元素:

    • Build/Prepare

       make xxx/xxx/prepare
      

      在编译前解压或在build_dir/准备代码时调用,如:

       define Build/Prepare
           mkdir -p $(PKG_BUILD_DIR)
           $(CP) ./src/* $(PKG_BUILD_DIR)/
       endef
      
    • Build/Configure

       make xxx/xxx/configure
      

      在编译前执行./configure 做特殊处理,如:

       define Build/Configure
           $(CP) $(SCRIPT_DIR)/config.guess $(SCRIPT_DIR)/config.sub     
           $(PKG_BUILD_DIR)/support/
           $(call Build/Configure/Default, \
                 --enable-shared \
                   --enable-static \
                   --without-curses \
           )
       endef
      

      也可以这样用:

       define Build/Configure
               $(call Build/Configure/Default)
               $(if $(CONFIG_PCAP_HAS_USB),,$(SED) '/^#define PCAP_SUPPORT_USB/D' $(PKG_BUILD_DIR)/config.h)
               $(if $(CONFIG_PCAP_HAS_USB),,$(SED) 's/pcap-usb-linux.c *//' $(PKG_BUILD_DIR)/Makefile)
               $(if $(CONFIG_PCAP_HAS_BT),,$(SED) '/^#define PCAP_SUPPORT_BT/D' $(PKG_BUILD_DIR)/config.h)
               $(if $(CONFIG_PCAP_HAS_BT),,$(SED) 's/pcap-bt-linux.c *//' $(PKG_BUILD_DIR)/Makefile)
       endef
      

      需要注意的是:$(call Build/Configure/Default)是必需的。

    • Build/Compile

       make xxx/xxx/compile
      

      make时执行,可以增加编译参数

       define Build/Compile
               $(TARGET_CONFIGURE_OPTS) \
                       CFLAGS="$(TARGET_CFLAGS) -I$(STAGING_DIR)/usr/include -I$(PKG_BUILD_DIR)" \
                       LDFLAGS="$(TARGET_LDFLAGS)" \
                       $(MAKE) -C $(PKG_BUILD_DIR)
       endef
      
    • Build/InstallBuild/InstallDev

       make xxx/xxx/install
      

      在编译完成,安装时执行的特殊处理,如:

       define Build/InstallDev
               $(INSTALL_DIR) $(1)/usr/include
               $(CP) $(PKG_INSTALL_DIR)/usr/include/lua{,lib,conf}.h $(1)/usr/include/
               $(CP) $(PKG_INSTALL_DIR)/usr/include/lauxlib.h $(1)/usr/include/
               $(CP) $(PKG_INSTALL_DIR)/usr/include/lnum_config.h $(1)/usr/include/
               $(INSTALL_DIR) $(1)/usr/lib
               $(CP) $(PKG_INSTALL_DIR)/usr/lib/liblua.{a,so*} $(1)/usr/lib/
               ln -sf liblua.so.$(PKG_VERSION) $(1)/usr/lib/liblualib.so
               $(INSTALL_DIR) $(1)/usr/lib/pkgconfig
               $(CP) $(PKG_BUILD_DIR)/etc/lua.pc $(1)/usr/lib/pkgconfig/
       endef
      

    4. ipk是如何编译出来的

    openwrt的编译要区分是不是buildin, 对于buildin是指,选定的包编译时就就安装到rootfs中,否则,就是编译成ipk文件,通过opkg命令安装。
    
    区分是否buildin通过menuconfig配置,最终体现在.config中。
    
            < CONFIG_PACKAGE_getevent=y
            ---
            > CONFIG_PACKAGE_getevent=m
    
    `=m` 代表编译成ipk, 目标:`bin/pxa1826/packages/base/getevent_1_pxa1826.ipk`
    
    `=y` 代表buildin,目标:`staging_dir/target-arm_cortex-a7+neon-vfpv4_uClibc-1.0.25_eabi/root-mmp/usr/sbin/getevent`
    

    5. Patches是如何自动打上去的?

    通过make -n提取的make V=s package/utils/busybox/{clean,prepare}的过程如下

        . /source/marvell/include/shell.sh; bzcat /source/marvell/dl/u-boot-2017.01.tar.bz2 | tar -C /source/marvell/build_dir/target-arm_cortex-a8+vfpv3_musl_eabi/u-boot-am335x_boneblack/u-boot-2017.01/.. -xf -
        [ ! -d ./src/ ] || cp -fpR ./src/. /source/marvell/build_dir/target-arm_cortex-a8+vfpv3_musl_eabi/u-boot-am335x_boneblack/u-boot-2017.01
        if [ -d "./patches" ] && [ "$(ls ./patches | wc -l)" -gt 0 ]; then export PATCH="patch"; if [ -s "./patches/series" ]; then sed -e s,\\\#.*,, ./patches/series | grep -E \[a-zA-Z0-9\] | xargs -n1 /source/marvell/scripts/patch-kernel.sh "/source/marvell/build_dir/target-arm_cortex-a8+vfpv3_musl_eabi/u-boot-am335x_boneblack/u-boot-2017.01" "./patches"; else /source/marvell/scripts/patch-kernel.sh "/source/marvell/build_dir/target-arm_cortex-a8+vfpv3_musl_eabi/u-boot-am335x_boneblack/u-boot-2017.01" "./patches"; fi; fi
    
      1. 解压
    bzcat /source/marvell/dl/u-boot-2017.01.tar.bz2 | tar -C /source/marvell/build_dir/target-arm_cortex-a8+vfpv3_musl_eabi/u-boot-am335x_boneblack/u-boot-2017.01/.. -xf -
    
      1. 打patch
    /source/marvell/scripts/patch-kernel.sh "/source/marvell/build_dir/target-arm_cortex-a8+vfpv3_musl_eabi/u-boot-am335x_boneblack/u-boot-2017.01“ "./patches"
    

    可以在package/boot/uboot-omap目录下运行patch-kernel.sh测试。

    6. 如何创建自己的patch?

    • 安装quilt

        sudo apt-get install quilt
      
    • 准备quilt的配置文件

        cat > ~/.quiltrc <<EOF
        QUILT_DIFF_ARGS="--no-timestamps --no-index -p ab --color=auto"
        QUILT_REFRESH_ARGS="--no-timestamps --no-index -p ab"
        QUILT_SERIES_ARGS="--color=auto"
        QUILT_PATCH_OPTS="--unified"
        QUILT_DIFF_OPTS="-p"
        EDITOR="nano"
        EOF
      
    • 创建第一个patch

        $ make  package/utils/fbtest/{clean,prepare} V=s QUILT=1
        $ cd build_dir/target-*/fbtest
        $ quilt new patches/0001-test.patch
        $ quilt edit fbtest.c
        查看修改的内容
        $ quilt diff
        刷新0001-test.patch
        $ quilt refresh
        把patch挪到package目录
        $ cp build_dir/target-*/fbtest/patches/*.patch package/utils/fbtest/patches/
        
        编译看看patch是否生效
        
        $ make  package/utils/fbtest/{clean,prepare} V=s
    
    • 增加 patch

    和上面步骤一样,只要注意patch的名字序号递增。

        $ make  package/utils/fbtest/{clean,prepare} V=s QUILT=1
        $ cd build_dir/target-*/fbtest
        $ quilt new patches/0002-test2.patch
        $ quilt edit Makefile
        查看修改的内容
        $ quilt diff
        刷新0002-test.patch
        $ quilt refresh
        把patch挪到package目录
        $ cp build_dir/target-*/fbtest/patches/*.patch package/utils/fbtest/patches/
        
        编译看看patch是否生效
        
        $ make  package/utils/fbtest/{clean,prepare} V=s
    
    • 修改patch

        $ make  package/utils/fbtest/{clean,prepare} V=s QUILT=1
        $ cd build_dir/target-*/fbtest
        $ quilt push patches/0002-test2.patch
        $ quilt edit Makefile
        查看修改的内容
        $ quilt diff
        刷新0002-test.patch
        $ quilt refresh
        把patch挪到package目录
        $ cp build_dir/target-*/fbtest/patches/*.patch package/utils/fbtest/patches/
        
        编译看看patch是否生效
        
        $ make  package/utils/fbtest/{clean,prepare} V=s
      

    生成patch特别要注意的是:

    • 各个patch是不能有互相依赖关系的
    • 如果有依赖关系需要做特殊处理,比如0002依赖0001,需要将0001先导入(但不能用quilt 导入,可以是patch -p1之类的),然后再用quilt生成0002
    • 最后一步cp patch时不要将series文件也拷贝到package包中。

    7. rootfs源头在哪?

    有两个位置:

    1. package/base-file
    2. target/linux/xxx/base-files

    相关文章

      网友评论

          本文标题:openwrt (三)入门FAQ

          本文链接:https://www.haomeiwen.com/subject/ooxhjftx.html