openwrt (三)入門FAQ

openwrt作為一個(gè)基于linux開(kāi)發(fā)的比較完善的嵌入式系統(tǒng),可以快速移植到各種平臺(tái)上。初次下載開(kāi)源代碼后,簡(jiǎn)單瀏覽后很是詫異,居然沒(méi)看到uboot和kernel部分的代碼,甚至沒(méi)看到任何模塊的代碼,最多只是些patch和配置文件。
按照文檔編譯后,發(fā)現(xiàn)多了些工程目錄,進(jìn)而發(fā)現(xiàn)了很多源碼,猜測(cè)到大概是Makefile或feed腳本在編譯時(shí)在線下載的代碼。為了后來(lái)者,初次入門openwrt少踩坑,完成此章,內(nèi)容多為我入門的困惑與解答。

1. uboot和kernel在哪里?

事實(shí)上,拿到marvell release代碼后會(huì)看到:

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

這些明顯是marvell自己定制過(guò)的一些代碼包,針對(duì)這個(gè)平臺(tái)算是找到了正確的代碼位置,但我們并不了解這個(gè)過(guò)程,并且開(kāi)源代碼部分是沒(méi)有這種結(jié)構(gòu)的,所以我們要繼續(xù)探究下去,提出問(wèn)題:

  1. 官方release的openwrt沒(méi)有定制的情況下uboot和kernel在哪?
  2. marvell這種定制是如何實(shí)現(xiàn)的?

在目錄結(jié)構(gòu)和功能不了解的情況下會(huì)感覺(jué)無(wú)從下手而四處亂摸,通過(guò)上一章了解了目錄結(jié)構(gòu),很自然的就會(huì)想到package目錄,進(jìn)去看看:

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

最終發(fā)現(xiàn),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',查詢后發(fā)現(xiàn)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

確認(rèn)無(wú)疑了!!

package/boot/uboot-mmp/就是marvell的uboot定義的位置,變量USE_SOURCE_DIR指定了uboot使用的源碼。

知道了marvell定制uboot源碼的方法,再看一下官方release版本的uboot,已Beaglebone black板項(xiàng)目為例:

    $ 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
    ...

很遺憾,這里沒(méi)有USE_SOURCE_DIR指定源碼位置,只是指定了使用的uboot的版本號(hào),目錄中也沒(méi)有源碼:

    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

這里保存了針對(duì)bbb的uboot相關(guān)patch,聯(lián)想到與硬件相關(guān)的內(nèi)容是以patch存在的,u-boot應(yīng)該是官方發(fā)布的原版代碼,上一章提到了dl目錄保存了從網(wǎng)絡(luò)上下載的代碼包,編譯時(shí)會(huì)解壓源碼包到build_dir中,現(xiàn)在看一下:

    $ 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,是不是已經(jīng)合入了呢? 對(duì)比看看:

    $ 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

看來(lái)patch是已經(jīng)打上去了。

  1. 官方的uboot先解壓到build_dir中,再打上package/boot/uboot-xxx/patches中的patch,再編譯。
  2. marvell通過(guò)變量USE_SOURCE_DIR指定本地代碼庫(kù),連接到build_dir中,然后編譯。

再看看kernel應(yīng)該是類似的:區(qū)別是kernel不在package中而是target中,引用外部庫(kù)的變量不是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. 如何編譯自己的代碼包?

用戶態(tài)模塊

新建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中體現(xiàn)出來(lái)
    define Package/getevent
      SECTION:=Xxxx
      CATEGORY:=xxx ddd
      TITLE:=linux inputevent test tool
    endef

    # 本包安裝的配置文件,一行一個(gè)
    define Package/dnsmasq/conffiles
    #/etc/config/dhcp
    #/etc/dnsmasq.conf
    endef

    # make package/xxx/getevent/prepare時(shí)被執(zhí)行
    define Build/Prepare
            mkdir -p $(PKG_BUILD_DIR)
            $(CP) ./src/* $(PKG_BUILD_DIR)/
    endef
    
    # 軟件包的介紹信息
    define Package/helloworld/description
        helloworld,first self-made.
    endef

    # 對(duì)執(zhí)行./configure時(shí)的特殊處理
    define Build/Configure
    endef

    # 執(zhí)行make或make package/xxx/getevent/compile時(shí)處理 
    define Build/Compile
            $(MAKE) -C $(PKG_BUILD_DIR) \
                    CC="$(TARGET_CC)" \
                    CFLAGS="$(TARGET_CFLAGS) -Wall" \
                    LDFLAGS="$(TARGET_LDFLAGS)"
    endef

    # make install時(shí)處理 
    # INSTALL_DIR 決定了通過(guò)opkg安裝ipk的目標(biāo)目錄
    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

注意,這里一個(gè)代碼包中有兩個(gè)Makefile

下一步時(shí)make menuconfig,選擇CONFIG_PACKAGE_getevent=mCONFIG_PACKAGE_getevent=y否則編譯不出東西,會(huì)報(bào):

    WARNING: skipping getevent -- package not selected

config配置成m時(shí)會(huì)編譯成ipk文件,可以按需安裝;配置成y時(shí),是buildin,內(nèi)置到release軟件中,無(wú)需安裝。

內(nèi)核模塊

最終包如下:

    $ 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:=
      # 目標(biāo)文件
      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. 模塊如何單獨(dú)編譯?

  1. 首先有個(gè)頂層Makefile,如上面的兩個(gè)案例

  2. 如何模塊需要區(qū)別于默認(rèn)編譯選項(xiàng),Makefile中可以包含下面幾個(gè)重要元素:

  • Build/Prepare

     make xxx/xxx/prepare
    

    在編譯前解壓或在build_dir/準(zhǔn)備代碼時(shí)調(diào)用,如:

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

     make xxx/xxx/configure
    

    在編譯前執(zhí)行./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時(shí)執(zhí)行,可以增加編譯參數(shù)

     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
    

    在編譯完成,安裝時(shí)執(zhí)行的特殊處理,如:

     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是如何編譯出來(lái)的

openwrt的編譯要區(qū)分是不是buildin, 對(duì)于buildin是指,選定的包編譯時(shí)就就安裝到rootfs中,否則,就是編譯成ipk文件,通過(guò)opkg命令安裝。

區(qū)分是否buildin通過(guò)menuconfig配置,最終體現(xiàn)在.config中。

        < CONFIG_PACKAGE_getevent=y
        ---
        > CONFIG_PACKAGE_getevent=m

`=m` 代表編譯成ipk, 目標(biāo):`bin/pxa1826/packages/base/getevent_1_pxa1826.ipk`

`=y` 代表buildin,目標(biāo):`staging_dir/target-arm_cortex-a7+neon-vfpv4_uClibc-1.0.25_eabi/root-mmp/usr/sbin/getevent`

5. Patches是如何自動(dòng)打上去的?

通過(guò)make -n提取的make V=s package/utils/busybox/{clean,prepare}的過(guò)程如下

    . /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目錄下運(yùn)行patch-kernel.sh測(cè)試。

6. 如何創(chuàng)建自己的patch?

  • 安裝quilt

      sudo apt-get install quilt
    
  • 準(zhǔn)備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
    
  • 創(chuàng)建第一個(gè)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
    查看修改的內(nèi)容
    $ 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的名字序號(hào)遞增。

    $ make  package/utils/fbtest/{clean,prepare} V=s QUILT=1
    $ cd build_dir/target-*/fbtest
    $ quilt new patches/0002-test2.patch
    $ quilt edit Makefile
    查看修改的內(nèi)容
    $ 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
      查看修改的內(nèi)容
      $ 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特別要注意的是:

  • 各個(gè)patch是不能有互相依賴關(guān)系的
  • 如果有依賴關(guān)系需要做特殊處理,比如0002依賴0001,需要將0001先導(dǎo)入(但不能用quilt 導(dǎo)入,可以是patch -p1之類的),然后再用quilt生成0002
  • 最后一步cp patch時(shí)不要將series文件也拷貝到package包中。

7. rootfs源頭在哪?

有兩個(gè)位置:

  1. package/base-file
  2. target/linux/xxx/base-files
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容