OpenWRT 編譯環境搭建

配置編譯環境

必須使用非root用戶,ArchLinux需要創建新用戶。

安裝依賴包

// Ubuntu 14.04 必選
# apt-get install asciidoc bash bc binutils bzip2 fastjar flex git-core g++ build-essential util-linux gawk libgtk2.0-dev intltool jikespg zlib1g-dev genisoimage libncurses5-dev libssl-dev patch perl-modules python2.7-dev rsync ruby sdcc unzip wget gettext xsltproc libboost1.55-dev libboost1.55-tools-dev libxml-parser-perl libusb-dev bin86 bcc bzr ecj sharutils openjdk-7-jdk zip gcc-multilib quilt
// Ubuntu 14.04 可選
# apt-get install subversion mercurial cvs
// ArchLinux 必選
# pacman -S base-devel
# pacman -S [--needed] asciidoc b43-fwcutter bash bc bin86 boost binutils bzip2 cdrkit fastjar flex gawk gettext git gtk2 intltool jdk7-openjdk libusb libxslt ncurses openssl patch perl python2 rsync ruby sdcc sharutils unzip util-linux wget zlib gcc make perl-extutils-makemaker findutils libstdc++5 lib32-libstdc++5
// libstdc++  可能需要版本6,待測
// 根據wiki,ArchLinux部分必選包在AUR裡面
$ yaourt -S bcc jikes
// ArchLinux 可選
# pacman -S subversion

首次配置需要檢出源代碼,這裡用subversion檢出開發trunk分支

$ svn co svn://svn.openwrt.org/openwrt/trunk/

這時,就會出現名為trunk的文件夾,這就是將來我們的工作目錄。 如果已經有了以前的版本庫,需要按照下面的命令更新

// 必須進入工作目錄才能更新
$ cd trunk/
$ svn update

更新和安裝feeds(需要VPN)

在下載之前可以通過查看feeds.conf.default文件,來檢查哪些文件需要包含在環境中。更新只需要重複命令即可

$ cd trunk/
$ ./scripts/feeds update -a
$ ./scripts/feeds install -a

配置編譯信息

必須先選擇好目標(target)類型才能執行 make defconfig

$ make defconfig
$ make prereq
$ make menuconfig

其中斜線(/)可以進行搜索。 剛開始可以考慮選擇編譯SDK用於開發,編譯ImageGenerator(a.k.a ImageBuilder)簡單打包自己需要的Package和配置文件進入固件中,也可以同時打包好工具鏈(ToolChain)方便以後使用,這樣的話時間會稍微長一些。

開始編譯

一般情況,進行全部編譯時使用一個簡單的命令,因為編譯過程會下載很多文件,目前許多項目的repo都遷移到GitHub上了,一般情況下不必使用VPN,但是如果網絡條件很差,可以考慮使用一個靠譜的VPN服務提供商。

$ make V=99

編譯一個單獨的軟件包(例如cups軟件包)

$ make package/cups/compile V=99

如果特殊原因需要分析編譯報錯信息:

$ make V=99 2>&1 |tee build.log |grep -i error

則將編譯的所有輸出信息保存在build.log中,將error信息打印在屏幕上。

還發現一個記錄log的好辦法,打開make menuconfig裡面的Advanced configuration options (for developers),接著開啟Enable log files during build process,這樣在使用make V=99的時候自動會生成logs文件夾,裡面詳細記錄了各個階段的日誌文件,相比上述輸出日誌的形式,這種更加全面,實際使用過程發現好像反應稍微慢了點,不過沒有錯誤才是最好的,不是麼?

編譯結束

編譯結束後,所有的文件都會放在編譯根目錄下的 bin/yourtarget/ ,例如 /bin/ar71xx

$ ls bin/*

編譯之後的文件主要有以下幾類:

  1. bin/.trx 文件: 路由器固件。如果沒有另外一種也不必驚慌。關於.bin和.trx的區別,一種說法是,第一次刷路由器的時候,需要用.bin文件,如果需要再升級,則不能再使用 .bin文件,而需要用 .trx文件。原因是 .bin是將路由器的相關配置信息和 .trx封裝在一起而生成的封包,也就是說是包含路由器版本信息的 .trx 在第一次刷固件的時候,我們需要提供這樣的信息,而在後續升級時則不再需要,用 .trx文件即可。
  2. packages文件夾: 裡麵包含了我們在配置文件裡設定的所有編譯好的軟件包。
  3. (可選)OpenWrt-SDK.**.tar.bz2: 這個也就是我們定製編譯好的OpenWRT SDK環境。我們將用這個來進行OpenWrt軟件包的開發。我的 TP-Link WR841N v7 編譯的SDK文件名是OpenWrt-SDK-ar71xx-for-linux-x86_64-gcc-4.8-linaro_uClibc-0.9.33.2.tar.bz2
  4. (可選)OpenWrt-ImageBuilder-**.tar.bz2: 這個可以簡單打包自己需要的Package和配置文件進入固件中。我的文件名為OpenWrt-ImageBuilder-ar71xx_generic-for-linux-x86_64.tar.bz2
  5. (可選)OpenWrt-Toolchain-**.tar.bz2: 這個是交叉編譯工具鏈。我的是OpenWrt-Toolchain-ar71xx-for-mips_34kc-gcc-4.8-linaro_uClibc-0.9.33.2.tar.bz2
  6. md5sums 文件: 這個文件記錄了所有我們編譯好的文件的MD5值,來保證文件的完整性。

編譯完成後,一定要將編譯好的bin目錄進行備份

清理以及再編譯

建議現在清理編譯產生的文件,以免下次編譯時造成衝突,也就是每次編譯完成之後把需要的東西複製到別處,然後立即清理(因為要生成的文件如果存在的話,可能不會被替換),執行make clean

注意:在執行clean命令,確保已經將編譯好的Image進行了備份。清理工作會清除bin目錄。

$ make clean

除了清除生成的目錄,還想清除交叉編譯工具(以及工具鏈目錄)

$ make dirclean

清除所有相關的東西,包括下載的軟件包,配置文件,feed內容等(不建議使用)

$ make distclean

對於更新feeds後出現的錯誤:ERROR:please fix package/feeds/packages/mc/Makefile 等類似的問題,需要執行這條語句進行系統的清理

對於清理組件,比如軟件包的清理

比如清理 linux

$ make target/linux/clean

清理 base-files 軟件包

$ make package/base-files/clean

清理 Luci

$ make package/luci/clean

和官方倉庫保持同步

很多時候,我們在自己的某個Repository裡面進行修改或者開發操作,所以要保證我們的代碼與官方的代碼保持同步。

官方倉庫的地址是 git://git.openwrt.org/openwrt.git 或者 http://git.openwrt.org/openwrt.git。其他在GitHub上的倉庫貌似都是鏡像,比如openwrt-es以及openwrt-mirror

如果還沒進行過多少修改的代碼,可以直接fork給出的repo,然後可以方便的使用github本身看我們的分支和官方的代碼有多少的超前和滯後提交,可以作為一個提醒。

接下來,我們建立一個遠程倉庫名為upstream

$ git remote -v   // 首先列出遠程倉庫,一般只有一個origin

$ git remote add upstream http://git.openwrt.org/openwrt.git

這樣就建立好了,我們可以接著列出遠程倉庫看一下.如果需要變更或者刪除遠程倉庫的話,請參考git remote的manpage.

有的人喜歡使用git pull,但是官方推薦的是使用fetch然後merge,這樣有什麼區別還有衝突都一目瞭然.

$ git fetch upstream    // 從官方拉下來最新代碼

$ git checkout master   // 切換到主分支,或者是切換到你需要merge到的分支

$ git merge upstream/master // 自動merge進上面切換到的分支中,可以自動或者手動解決衝突

這樣,我們就能時刻和官方保持一致了.

OpenWRT 編譯 doc 文件

在 OpenWrt 的 trunk 分支中有一個文件夾名為 doc,在這個文件夾中包含一部分文檔可供參考,是用 LaTeX 寫的,我們需要編譯之後才能閱讀的更順暢,編譯之後默認生成 .pdf 和 .htm 文件。

在 Ubuntu 我們可以使用 apt-get 從網絡上直接下載包來安裝。

我們用到的三個比較重要的命令有 apt-cache searchapt-cache showsudo apt-get install

首先使用

$ apt-cache search latex

我們可以看到許多包被列出。選擇安裝texlive-latex-base, 它的描述是:Tex Live: Basic LaTex packages

sudo apt-get install texlive-latex-base

這樣就安裝好Latex了,可以直接使用。 但編譯中文時,由於沒有安裝CJK中文環境,會提示找不到CJK包。

$ apt-cache search cjk

選擇安裝latex-cjk-all, 它的描述是:Installs all LaTex CJK packages.

sudo apt-get install latex-cjk-all

這樣就可以使用中文環境了。

有些.sty文件可能沒有安裝,例如:lastpage.sty. 這個時候不要到網絡上去詢問是因為什麼, Latex的輸出錯誤信息已經很明顯了。 使用下面的命令來查找相應的包。 注意不要加.sty文件後綴

$ apt-cache search lastpage

可以看到需要下面的包,以及對這個包的描述:texlive-latex-extra - TeX Live: LaTeX supplementary packages 選擇安裝即可:

sudo apt-get install texlive-latex-extra

完成上面的這三步,就可以基本滿足平時的應用需求了。如果以後需要使用新的包,可以使用上面的方法來查找相應的安裝包,並選擇安裝即可。

增加設備支持

前提

如果是專業搞過嵌入式開發的,懂得架構以及其他必要知識的可以忽略這個前提,接下來就是我要說的了,找一款和你想要添加的設備很相近且官方已經有了支持的型號,至少要是相同的平臺,比如ar71xx.這部分基本上圍繞這個前提來展開。

所需

完整 OpenWrt 開發環境. 包括編輯器, 配置好的 quilt 工具.

假設之前已經編譯過 bin, 有完整的 .config 和 toolchain.

目前看到的很多教程教人直接修改現有的 patch 文件或者是 tmp/ 文件夾下的文件,更甚者胡亂猜測,這樣會導致一些問題。

比如源碼更新的時候,許多 Patch 文件的偏移(offset)會發生變更,主要體現在 linux 內核版本升級的時候,這樣直接修改現成的 Patch 就會增添許多煩惱,其實官方有一個很好的東東,那就是這個 quilt,它是用來管理代碼樹中的 patch 的嵌入式內核開發利器!

增加 Device-Specific 支持文件

這個部分的文件是單獨的,與其他的文件比如各種 Patch 沒有較大的關聯,在重新編譯之前只需要make clean就行。

我們以添加 TP-Link TL-WR2041N v1 版本為例子進行說明:

新手剛開始添加支持的時候最好在取出最新的 trunk 代碼之後立即進行,防止其他文件幹擾,如果之前做過類似的操作或者懂得目錄結構、知道文件是做什麼用的,可以忽略這個建議。

刪除 tmp/ 目錄,這個是大坑,因為這個文件夾內的內容全是生成的,沒必要進行更改。

$ rm -rvf tmp/

首先,如同前文所述,確定好相近的型號,方便添加支持。我這個型號的板子與國外的 WDR3500 相似,與國內的 TP-Link TL-WR941N v6 相同,我是通過 WDR3500 進行添加的。

搜索以下相關的文件,以確定需要修改什麼文件,之後照貓畫虎。

運行 grep wdr3500 trunk/* -r -l 得到的結果如下,我已經做好了分類:

// 這部分是Device-Specific的
trunk/target/linux/ar71xx/base-files/etc/diag.sh
trunk/target/linux/ar71xx/base-files/etc/uci-defaults/01_leds
trunk/target/linux/ar71xx/base-files/etc/uci-defaults/02_network
trunk/target/linux/ar71xx/base-files/lib/upgrade/platform.sh
trunk/target/linux/ar71xx/base-files/lib/ar71xx.sh
trunk/target/linux/ar71xx/files/arch/mips/ath79/mach-tl-wdr3500.c
// 這部分是製作鏡像的相關文件
trunk/target/linux/ar71xx/image/Makefile
trunk/target/linux/ar71xx/generic/profiles/tp-link.mk
trunk/tools/firmware-utils/src/mktplinkfw.c
// 這部分是內核支持相關的文件
trunk/target/linux/ar71xx/config-3.10
trunk/target/linux/ar71xx/patches-3.10/610-MIPS-ath79-openwrt-machines.patch

得到的文件就是基本的支持文件了,我們一個一個說。

diag.sh

這個文件是 openwrt 初始化時閃爍的燈的配置,一般的路由啟動的時候只閃爍 System 燈或者電源燈,基本沒有修改的價值,仿照修改即可。

比如2041n v1:

    tl-wdr3500 | \
    tl-wr2041n-v1 | \
    ........
    tl-wr941nd)
        status_led="tp-link:green:system"

添加到該位置就行。

uci-defaults/01_leds

這個文件是定義 led 的信息的,簡單的比如 ucidef_set_led_netdev 多用於定義 WAN 口,修改最後的 eth0 為你對應的設備名稱; ucidef_set_led_wlan 多用於定義 WLAN 口,最後的 phyxtpt 目前見到的只有 x=0 和1兩種情況,這個和設備初始出來的phy物理設備相關; ucidef_set_led_switch 用來定義交換機的配置,最後的 0x00 部分是端口掩碼,一般的順序如下。

至於更詳細的定義過程,需要參考 packages/base-files/files/lib/functions 目錄的 uci-defaults.sh 腳本。(目前新加的文件 uci-defaults-new.sh 文件,看樣子是通過 JSON 傳值的,應該是應用於新的設備或者新的架構的)

比如2041n v1:

tl-wr2041n-v1)
    ucidef_set_led_netdev "wan" "WAN" "tp-link:green:wan" "eth0"
    ucidef_set_led_switch "lan1" "LAN1" "tp-link:green:lan1" "switch0" "0x02"
    ucidef_set_led_switch "lan2" "LAN2" "tp-link:green:lan2" "switch0" "0x04"
    ucidef_set_led_switch "lan3" "LAN3" "tp-link:green:lan3" "switch0" "0x08"
    ucidef_set_led_switch "lan4" "LAN4" "tp-link:green:lan4" "switch0" "0x10"
    ucidef_set_led_netdev "wlan" "WLAN" "tp-link:green:wlan" "wlan0"
    ucidef_set_led_wlan "wlan" "WLAN" "tp-link:green:wlan" "phy1tpt"
    ;;
uci-defaults/02_network

該文件定義初始化網絡信息,主要是交換機和 vlan,不過我遇到怪異的事情是,ucidef_set_interfaces_lan_wan 緊跟著的第一個參數是定義 wan 的,第二個是定義 lan 的,比如下面的 eth0 是 wan 而 eth1 是 lan 。(貌似是在設備 mach 文件中定義的問題,看了一下網絡初始化的腳本 uci-defaults.sh 中的定義,應該第一個參數是 lan ,第二個是 wan) 比如 wr2041n v1:

tl-wr2041n-v1)
    ucidef_set_interfaces_lan_wan "eth0" "eth1"
    ucidef_add_switch "switch0" "1" "1"
    ucidef_add_switch_vlan "switch0" "1" "0 1 2 3 4"
    ;;
lib/upgrade/platform.sh

該文件是定義系統更新時驗證的,基本上不用怎麼更改。

比如 2041n v1:

    tl-wdr3500 | \
    tl-wr2041n-v1 | \

添加到了wdr3500的下方。

lib/ar71xx.sh

第一個是硬件 magic number,可以用 WinHex 打開固件文件,查看對應的偏移即可找到,model 定義的是顯示的設備名稱,下面的 *"TL-WR2041N v1" 要對應設備支持的 mach C語言設備文件中定義好的名稱;同樣的, name 定義的是內部傳遞的值,最好與上面添加的行一致。簡單來說,涉及到型號名稱的最好都保持一致。

比如 2041n v1:

        "204100"*)
        model="TP-Link TL-WR2041N"
        ;;

    *"TL-WR2041N v1")
        name="tl-wr2041n-v1"
        ;;
files/arch/mips/ath79/mach-tl-wdr3500.c

這個就是很重要的設備支持 C語言文件了,開頭的前綴 mach 最好保留,mach後的名稱需要與以後添加的linux patch支持名稱保持一致。

一般用於定義 led、flash 以及 PHY4 等等涉及到的端口信息,有的mach文件還涉及到 ART 的偏移量(offset),ART是存儲無線數據的分區,如果設置不當會導致路由沒有無線。

為了最快的添加路由支持,可以採用sed, awk工具批量改名的方法,寫出一個設備支持文件來,當然也可以使用 vim, emacs批量完成。

比如 2041n v1: 新建文件 target/linux/ar71xx/files/arch/mips/ath79/mach-tl-wr2041n-v1.c,詳情請戳這裡

值得注意的是: MIPS_MACHINE(ATH79_MACH_TL_WR2041N_V1, "TL-WR2041N-v1", "TP-LINK TL-WR2041N v1", wr2041n_setup); 第一部分要與 config-3.10 文件中添加的內容以及後面添加的linux kernel patch中的內容一致;"TL-WR2041N-v1" 是定義的設備名稱,最好與 image 的 Makefile一致;"TP-LINK TL-WR2041N v1" 與前面涉及到的 lib/ar71xx.sh 文件相關;最後的 wr2041n_setup 是設備初始化的主函數名稱。

鏡像生成的相關文件

trunk/target/linux/ar71xx/image/Makefile

該文件主要修改如下兩行,剩下的內容需要仔細學習 Makefile的寫法才能看懂。

$(eval $(call MultiProfile,TLWR2041,TLWR2041NV1))

該行定義多Profile,一般是一個型號生成多個硬件版本支持的定義

$(eval $(call SingleProfile,TPLINK-LZMA,64kraw,TLWR2041NV1,tl-wr2041n-v1,TL-WR2041N-v1,ttyS0,115200,0x20410001,1,4Mlzma))

該行是單獨設備的支持信息,第一個參數是使用什麼生成辦法,2041n v1是使用 TPLINK-LZMA 方法,前面有對應的說明,從裡面也可以看到我們需要修改 bin/mktplinkfw 文件的源代碼,該源代碼在 trunk/tools/firmware-utils/src/mktplinkfw.c 中 接下來可以根據對 TPLINK-LZMA 的定義看到依次傳進去的參數,TLWR2041NV1 是 Profile 名稱,tl-wr2041n-v1 是生成的鏡像名稱,也(可能)是傳進去的設備型號,TL-WR2041N-v1 最好對應前面 mach 文件 MIPS_MACHINE 函數的信息,主要是在內核日誌和系統日誌中體現設備型號,後面的是默認 tty 信息以及 baudrate 波特率,接下來的 magic number是設備的硬件ID,1 是版本,一般不用變,4Mlzma是生成固件的layout信息,該值和硬件ID都在 mktplinkfw.c 中定義。

trunk/target/linux/ar71xx/generic/profiles/tp-link.mk

該文件定義 Profile 信息,主要定義 NAME,會顯示在 make menuconfig 之後的設備選項裡; PACKAGES 定義默認編譯安裝的包,一般是網卡驅動以及其他必備的包,如 usb支持。

比如 2041n v1:

define Profile/TLWR2041
    NAME:=TP-LINK TL-WR2041N
    PACKAGES:=
endef

define Profile/TLWR2041/Description
    Package set optimized for the TP-LINK TL-WR2041N.
endef
$(eval $(call Profile,TLWR2041))
trunk/tools/firmware-utils/src/mktplinkfw.c

該文件是生成固件文件的工具,裡麵包含 TP-LINK 固件 bin 文件的結構和 md5 hash 驗證算法,比如定義硬件 magic number,flash layout, 以及其他信息。

比如 2041n v1:

#define HWID_TL_WR2041N_V1    0x20410001   //定義硬件ID

{
        .id        = "TL-WR2041Nv1",
        .hw_id        = HWID_TL_WR2041N_V1,
        .hw_rev        = 1,
        .layout_id    = "4Mlzma",
},

定義使用的信息。

config-3.10

添加內核支持的 config 信息,與其他的保持一致就好。

比如 2041n v1:

CONFIG_ATH79_MACH_TL_WR2041N_V1=y

CONFIG_ 後面的內容和上面 mach 硬件支持文件中定義的保持一致。

patches-3.10/610-MIPS-ath79-openwrt-machines.patch

這個是內核支持的patch文件,需要使用 quilt 創建以及以後維護,前一陣子trunk將大部分分散的補丁都移動到這個patch中了,如果要將patch提交到官方儘量放在這裡,自己用的話不要幹擾其他patch的應用就可以了。我這裡將補丁編號為920,一般來說是最後一個應用的patch了。

管理補丁文件

請通過軟件包管理器安裝 quilt,當然一般如同上文配置好編譯環境就已經安裝好了 quilt。

然後寫入以下配置文件到 ~/.quiltrc

cat > ~/.quiltrc <<EOF
QUILT_DIFF_ARGS="--no-timestamps --no-index -pab --color=auto"
QUILT_REFRESH_ARGS="--no-timestamps --no-index -pab"
QUILT_PATCH_OPTS="--unified"
QUILT_DIFF_OPTS="-p"
EDITOR="nano"
EOF

接著進行一下 export EDITOR ,當然這個 EDITOR 的變量不要和現有的變量衝突,可以自己任意更改,只要應用到上面的 .quiltrc 文件中就好了。如果怕麻煩可以寫到 ~/.bashrc 文件中,以後就比較方便,不用每次進行 export。

常用的quilt命令如下,會一一說明。

new        新建空白patch
add        添加要修改的文件到patch中
remove        刪除patch
top        查看當前patch位置,相當於一個指針
next        未知
rename        重命名patch
unapplied    查看未成功應用的patch
applied        查看已經成功應用的patch
graph        未知
patches        未知
upgrade        未知
delete        刪除patch
grep        應該類似於 grep 命令
pop        去往上一個patch,如果附加patch名稱則按照patch序列一直應用到特定的patch位置
series        查看patch列表,一般這個順序就是patch應用的順序,可以用來進行 pop 和 push 定位
diff        查看區別
previous    未知
edit        編輯當前patch中的文件
push        去往最新的patch,會一直應用到最後一個可以成功應用的patch
files        查看patch中包含的修改文件
refresh        刷新patch,相當於保存patch到 patches 文件夾,還沒有應用到buildroot處

幾種管理操作

增加一個新的Patch

為一個現有的軟件包添加一個新的 Patch 需要首先準備代碼樹:

$ make package/example/{clean,prepare} V=s QUILT=1

如果是 host-side 的軟件包需要進行下面的代碼樹準備操作:

$ make package/example/host/{clean,prepare} V=s QUILT=1

這個步驟將解壓源碼包的 tarball 並且按照預先排好的 quilt patch 順序進行準備工作(簡單的講,按照補丁文件前面的序號給 tarball 依次打好補丁),接下來會輸出詳細的打補丁情況,如果有未成功的會有 Hunk fail 字樣;補丁應用成功的話也有兩種情況,第一,行號偏移正常,這也就是我們期望的,第二,雖然應用成功了,但是存在代碼行號偏移,出現的字樣是 offset ,並且會提示補丁具體應用到了哪行,我們可以針對打補丁時給出的提示修改補丁文件的行號,也可以通過 quilt 修改.

接下來切換到代碼目錄中,值得注意的是,如果在 Makefile 中存在多個編譯選項(build variants),需要切換到對應的代碼目錄中.

$ cd build_dir/target-*/BUILD_VARIANT/example-*

接下來應用全部的已存在的補丁文件:

$ quilt push -a

通過 quilt new 命令創建一個全新空白的補丁文件:

$ quilt new 010-main_code_fix.patch

命名的時候需要注意的是,補丁文件的名字必須以數字開頭,一般是三位,並且補足前面的零,數字之後緊跟一個短橫線(-),接下來簡單描述這個補丁文件是做什麼用的,以便以後好查找;選定數字的時候,數字必須大於現有的最後一個 patch 的數字前綴,這個要求的原因可以通過 quilt series 來驗證,排序的順序就是補丁應用的順序,而排序是通過前面的數字決定的;對補丁文件的描述要言簡意賅,儘量用地道的英文術語.

創建好空白的補丁文件之後,必須要有修改的代碼文件與之相關聯,我們通過 quilt add 來進行這個操作,一旦添加好了文件,就像往常一樣進行修改就可以了. 可以通過 quilt edit 命令來同時完成上面的關聯文件操作以及修改操作,這個修改過程調用的文本編輯器是前面在 .quiltrc 文件裡面定義好的 EDITOR 變量,注意export.

$ quilt edit src/main.c  // 通過quilt的edit命令編輯代碼文件

這樣上面的代碼文件就被加到這個新建的 patch 中了,這樣一直重複操作,直到所有需要添加到該 patch 中的文件都修改完.

所有的修改操作都進行完之後,可以通過 quilt diff 命令查看修改的內容.

$ quilt diff          // 很類似 git diff 不是嗎

如果發現修改都處於預期,就可以把修改寫入到剛才新建的patch文件中了,注意這時候的patch還在 build_dir 裡面.

$ quilt refresh

接下來切換到 buildroot 的頂層目錄.

$ cd ../../../      // 這個目錄比修改 kernel 補丁時低了一層

接下來,我們將剛才新建的 patch 移動到 buildroot ,這樣我們就可以將這個補丁進行提交等其他操作了.

$ make package/example/update V=s

這樣,我們的補丁添加工作就完成了,不放心的話可以通過下面的命令進行驗證:

$ make package/example/{clean,compile} package/index V=s

如果發現問題,需要重新進行編輯,下面介紹怎麼編輯補丁文件.

編輯一個現有的patch

還是需要首先準備代碼樹:

$ make package/example/{clean,prepare} V=s QUILT=1

接下來切換到代碼目錄中.

$ cd build_dir/target-*/example-*

列出現有的所有patch文件:

$ quilt series

前往需要編輯的patch文件,當然前提是這個push到的patch需要應用成功,否則需要push到提前一個patch或者push的時候加上 -f 參數強制運行,然後直接quilt edit即可,當然這個是後話.

$ quilt push 010-main_code_fix.patch

當輸入的patch文件名合法的話,quilt push 命令會只應用patch 系列(patch series)到給出的文件名,所以提前用 quilt series 命令看好文件名再操作,然後用 quilt top 命令看自己當前處於哪個patch中確保萬無一失,如果不幸超過了所要去的補丁位置,可以通過 quilt pop 命令移除已經應用的補丁按照列表反向前進.

接下來通過 quilt edit 命令編輯每個需要編輯的文件.

$ quilt edit src/main.c    // 熟悉的命令

接下來檢查編輯的文件是否被包含在patch中了:

$ quilt files

檢查更改信息:

$ quilt diff

如果發現修改都處於預期,就可以把修改寫入patch文件中了,注意這時候的patch還在 build_dir 裡面.

$ quilt refresh

切換到buildroot的頂層目錄中.

$ cd ../../../

接下來,我們將剛才更新好的patch移動到buildroot中:

$ make package/example/update V=s

這樣,我們的補丁修改工作就完成了,不放心的話可以通過下面的命令進行驗證:

$ make package/example/{clean,compile} package/index V=s
新增或修改內核補丁

新增和修改內核補丁和操作安裝包的補丁差不太多,只有 make 的 target 和操作代碼目錄是不同的.

特別需要注意的是,對於內核補丁,存在一個附加的子目錄,一個是 generic/ 目錄,這個裡麵包含對所有目標架構的補丁文件;另一個是 platform/ 目錄,這裡麵包含特定架構的補丁文件,一般是在 make menuconfig 中配置好的架構,也可以在 .config 文件中找到.

準備內核代碼樹:

$ make target/linux/{clean,prepare} V=s QUILT=1

對穩定版AA(Attitude Adjustment)來說,內核代碼樹在這裡:

$ cd build_dir/linux-*/linux-3.*

對主幹分支,現在是BB(Barrier Breaker),代碼樹在這裡:

$ cd build_dir/target-*/linux-*/linux-3.*

我們將剛才操作好的patch移動到buildroot中通過下面的命令:

$ make target/linux/update package/index V=s

注意:Patch文件的前綴必須正確,指明是 generic 還是 platform,如果前綴不正確,可能應用操作會出問題.

不放心的話可以通過下面的命令進行驗證,首先去頂層目錄,一般是 target/ 所在的目錄:

$ cd ../../../../

然後再次準備代碼樹,查看更改是否已經應用了:

$ make target/linux/{clean,prepare} V=s QUILT=1

最後要說的是,放置patch的位置很重要,它決定補丁的應用情況.

增加或編輯工具鏈 Patch(這部分一般不會涉及)

以 gcc 為例:

編譯過toolchain或者是看toolchain目錄的主Makefile可以發現gcc有三個編譯階段:initial, minimal和final,如果要修改gcc的patch,需要使用minimal那個階段。

準備工具鏈代碼樹:

$ make toolchain/gcc/minimal/{clean,prepare} V=99 QUILT=1

代碼樹路徑取決於選擇的庫以及 gcc :

$ cd build_dir/toolchain-mips_r2_gcc-4.3.3+cs_uClibc-0.9.30.1/gcc-linaro-<version>

通過如下命令更新Patch:

$ make toolchain/gcc/minimal/update V=99

對於其餘的binutils, gdb, glibc之類的由於沒有細分階段,所以直接按照軟件包那樣的格式套用就行了。

比如 make toolchain/(gdb|glibc|binutils)/{clean,prepare} V=99 QUILT=1, make toolchain/(gdb|glibc|binutils)/update V=99

刷新Patch

當軟件包更新或者內核更新時,現有的patch文件可能不會完全應用或者存在其他提示。想要重新構建Patch列表中的全部patch可以使用make目標:refresh.

$ make package/example/refresh V=s    // 軟件包
$ make target/linux/refresh V=s       // 內核
不清理代碼樹重複修改補丁

當需要引入新功能做更改時,經常需要多次修改補丁。為了加速,可以在編輯操作(edit)之間保留做完準備操作的代碼樹.

  1. 剛開始如上文所述準備好代碼樹;
  2. 切換到準備好的代碼目錄;
  3. 前往需要修改的patch位置;
  4. 修改文件並保存對patch文件的更改;
  5. 通過 quilt push -a 命令完全應用剩下的補丁文件;
  6. 切換到頂層目錄運行 make package/example/{compile,install} ,如果是內核的補丁,使用 make target/linux/{compile,install} 命令;
  7. 測試二進制文件。如果再次需要進行更改,從第二步重複操作;
  8. 最後使用 make package/example/update 命令 ,如果是內核補丁,使用 make target/linux/update 命令將patch文件應用到buildroot中。
準備 linux 內核 patch

到這裡基本就完成了 OpenWrt 的設備支持代碼. 為了支持我們的設備, Linux 代碼樹的部分文件也需要做改動, OpenWrt 採用了 patch 的方式實現.

回退到源代碼的根目錄 ~/trunk .

清理並準備 patch 樹:

$ make target/linux/{clean,prepare} V=s QUILT=1

進入內核代碼目錄:

$ cd build_dir/target-*/linux-ar71xx_generic/linux-3.x/

這裡就是內核代碼樹了, 裡面的代碼是已經打過所有 patch 的, 可以用 quilt push 檢查看是不是這樣:

$ quilt push

File series fully applied, ends at patch platform/xxx-xxxxxxxxxxxxxx.patch.

這條輸出也告訴我們, 當前最頂的 patch 是 platform/xxx

為我的 WL-WR2041N v1 新建個 patch:

$ quilt new platform/920-MIPS-ath79-add-TL-WR2041N-v1-support.patch

選擇的數字需要大於剛才的那個 xxx , 然後 quilt 會自動把這個 patch 設置為當前 patch, 所有的改動都針對這個 patch.

注意修改文件之前一定要看自己位於哪個patch下,否則修改都會更新到那個patch.

然後就是增加代碼了

$ quilt edit arch/mips/ath79/Kconfig
$ quilt edit arch/mips/ath79/Makefile
$ quilt edit arch/mips/ath79/machtypes.h

至於怎麼改, 參考這些文件裡其他硬件的配置

比如 2041n v1:

--- a/arch/mips/ath79/Kconfig
+++ b/arch/mips/ath79/Kconfig
@@ -900,6 +900,16 @@ config ATH79_MACH_TL_WR1043ND_V2
     select ATH79_DEV_USB
     select ATH79_DEV_WMAC

+config ATH79_MACH_TL_WR2041N_V1
+    bool "TP-LINK TL-WR2041N v1 board support"
+    select SOC_AR934X
+    select ATH79_DEV_AP9X_PCI if PCI
+    select ATH79_DEV_ETH
+    select ATH79_DEV_GPIO_BUTTONS
+    select ATH79_DEV_LEDS_GPIO
+    select ATH79_DEV_M25P80
+     select ATH79_DEV_WMAC
+
 config ATH79_MACH_TL_WR2543N
     bool "TP-LINK TL-WR2543N/ND support"
     select SOC_AR724X
--- a/arch/mips/ath79/Makefile
+++ b/arch/mips/ath79/Makefile
@@ -116,6 +116,7 @@ obj-$(CONFIG_ATH79_MACH_TL_WR841N_V9)    +=
 obj-$(CONFIG_ATH79_MACH_TL_WR941ND)    += mach-tl-wr941nd.o
 obj-$(CONFIG_ATH79_MACH_TL_WR1041N_V2)    += mach-tl-wr1041n-v2.o
 obj-$(CONFIG_ATH79_MACH_TL_WR1043ND)    += mach-tl-wr1043nd.o
+obj-$(CONFIG_ATH79_MACH_TL_WR2041N_V1)    += mach-tl-wr2041n-v1.o
 obj-$(CONFIG_ATH79_MACH_TL_WR1043ND_V2)    += mach-tl-wr1043nd-v2.o
 obj-$(CONFIG_ATH79_MACH_TL_WR2543N)    += mach-tl-wr2543n.o
 obj-$(CONFIG_ATH79_MACH_TL_WR703N)    += mach-tl-wr703n.o
--- a/arch/mips/ath79/machtypes.h
+++ b/arch/mips/ath79/machtypes.h
@@ -134,6 +134,7 @@ enum ath79_mach_type {
     ATH79_MACH_TL_WR1041N_V2,    /* TP-LINK TL-WR1041N v2 */
     ATH79_MACH_TL_WR1043ND,        /* TP-LINK TL-WR1043ND */
     ATH79_MACH_TL_WR1043ND_V2,    /* TP-LINK TL-WR1043ND v2 */
+    ATH79_MACH_TL_WR2041N_V1,       /* TP-LINK TL-WR2041N v1 */
     ATH79_MACH_TL_WR2543N,        /* TP-LINK TL-WR2543N/ND */
     ATH79_MACH_TL_WR703N,        /* TP-LINK TL-WR703N */
     ATH79_MACH_TL_WR710N,        /* TP-LINK TL-WR710N */

僅作為參考。

然後驗證下修改的內容:

$ quilt diff # 查看 diff
$ quilt refresh # 保存所有 diff 到 patch 文件

這個時候我們的 patch 文件還在 build_dir 裡, 大概位置是 patches/platform/ 下. 需要同步到 OpenWrt 代碼樹.

退回到頂層工作目錄, 執行:

$ make target/linux/update V=s

同步完成後, patch 文件會出現在 target/linux/ar71xx/patches-3.x/ 下.

以後代碼更新後需要編輯 patch 時,遵從上面的 quilt 對 patch 的管理操作。

開始編譯

再次記得, 刪除 tmp 目錄

$ rm -rvf tmp/
$ make menuconfig

幾個問題解決的案例

參考鏈接

http://wiki.openwrt.org/doc/devel/add.new.device http://wiki.openwrt.org/doc/devel/hw.hacking.first.steps http://wiki.openwrt.org/doc/devel/patches http://andelf.diandian.com/post/2013-05-22/40050677370 http://tilt.lib.tsinghua.edu.cn/node/841 http://www.openwrt.org.cn/bbs/forum.php?mod=viewthread&tid=211 http://www.tuicool.com/kans/481707042 http://www.openwrt.org.cn/bbs/forum.php?mod=viewthread&tid=60&extra=page%3D1