必须使用非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 libglib2.0-dev gperf
// 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 gperf
// 根据wiki,ArchLinux部分必选包在AUR里面
// 从 2015-12-11 开始,bcc和jikes貌似从AUR里消失了,可能是 C++ ABI 更新的缘故,不安装这两个貌似也可以
$ yaourt -S bcc jikes
// ArchLinux 可选
# pacman -S subversion
$ git clone git://git.openwrt.org/openwrt.git
这时,就会出现名为openwrt
的文件夹,这就是将来我们的工作目录。
如果已经有了以前的版本库,需要按照下面的命令更新,其他情况参照 Git 的 Manpage
// 必须进入工作目录才能更新
$ cd openwrt/
$ git pull
在下载之前可以通过查看feeds.conf.default
文件,来检查哪些文件需要包含在环境中,如果已经有了feeds.conf
则需要看这个文件。更新只需要重复命令即可
$ cd openwrt/
$ ./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/*
编译之后的文件主要有以下几类:
- bin/.trx 文件: 路由器固件。如果没有另外一种也不必惊慌。关于.bin和.trx的区别,一种说法是,第一次刷路由器的时候,需要用.bin文件,如果需要再升级,则不能再使用 .bin文件,而需要用 .trx文件。原因是 .bin是将路由器的相关配置信息和 .trx封装在一起而生成的封包,也就是说是包含路由器版本信息的 .trx 在第一次刷固件的时候,我们需要提供这样的信息,而在后续升级时则不再需要,用 .trx文件即可。
- packages文件夹: 里面包含了我们在配置文件里设定的所有编译好的软件包。
- (可选)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
- (可选)OpenWrt-ImageBuilder-**.tar.bz2: 这个可以简单打包自己需要的Package和配置文件进入固件中。我的文件名为
OpenWrt-ImageBuilder-ar71xx_generic-for-linux-x86_64.tar.bz2
- (可选)OpenWrt-Toolchain-**.tar.bz2: 这个是交叉编译工具链。我的是
OpenWrt-Toolchain-ar71xx-for-mips_34kc-gcc-4.8-linaro_uClibc-0.9.33.2.tar.bz2
- 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 master // 从官方拉下来最新代码,一般只取得master就行了,其他分支根据需求选择
$ git checkout master // 切换到主分支,或者是切换到你需要merge到的分支
$ git merge upstream/master // 自动merge进上面切换到的分支中,可以自动或者手动解决冲突
这种方法进行的是三方合并,每次都会产生一个合并的commit,看一下git log顿时瞎眼,对于强迫症而言怎么能忍?
另外一种单线git log的方式,适用于各种强迫症患者:
既然是单线方式,最有可能的也就是git-rebase
了,基本思路是本地建立一个upstream分支,跟踪上游的变化,只使用git-fetch
拉取上游,定期在本地master上进行rebase.
$ git fetch upstream master // 继续拉取上游最新commit
$ git checkout -b upstream upstream/master //以 upstream/master 为分界点取出一个名为upstream的分支
$ git checkout master // 切换到主分支
$ git rebase -i upstream // 将 upstream 分支rebase进本地的主分支
$ git branch -d upstream // 最后可以删除upstream分支,如果没有完全rebase进来的话是不会让你删除这个分支的
上面的方法有一个shortcut,前提设置好upstream作为远程跟踪,之后使用
$ git pull upstream master --rebase
如果使用 git pull --rebase
则是默认对 origin master 进行操作。
当然git-rebase我也用不好,如果发现方法不对或者有更好办法欢迎PR
这样,我们就能时刻和官方保持一致了.
在 OpenWrt 的 trunk 分支中有一个文件夹名为 doc
,在这个文件夹中包含一部分文档可供参考,是用 LaTeX 写的,我们需要编译之后才能阅读的更顺畅,编译之后默认生成 .pdf 和 .htm 文件。
在 Ubuntu 我们可以使用 apt-get 从网络上直接下载包来安装。
我们用到的三个比较重要的命令有 apt-cache search
、apt-cache show
和 sudo 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 的嵌入式内核开发利器!
这个部分的文件是单独的,与其他的文件比如各种 Patch 没有较大的关联,在重新编译之前只需要make clean
就行。
我们以添加 TP-Link TL-WR2041N v1 版本为例子进行说明:
新手刚开始添加支持的时候最好在取出最新的 trunk 代码之后立即进行,防止其他文件干扰,如果之前做过类似的操作或者懂得目录结构、知道文件是做什么用的,可以忽略这个建议。
删除 tmp/ 目录,这个是大坑,因为这个文件夹内的内容全是生成的,没必要进行更改。
$ rm -rf 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
得到的文件就是基本的支持文件了,我们一个一个说。
这个文件是 openwrt 初始化时闪烁的灯的配置,一般的路由启动的时候只闪烁 System 灯或者电源灯,基本没有修改的价值,仿照修改即可。
比如2041n v1:
tl-wdr3500 | \
tl-wr2041n-v1 | \
........
tl-wr941nd)
status_led="tp-link:green:system"
添加到该位置就行。
这个文件是定义 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"
;;
该文件定义初始化网络信息,主要是交换机和 vlan,不过我遇到怪异的事情是,(貌似是在设备 mach 文件中定义的问题,看了一下网络初始化的脚本 ucidef_set_interfaces_lan_wan
紧跟着的第一个参数是定义 wan 的,第二个是定义 lan 的,比如下面的 eth0 是 wan 而 eth1 是 lan 。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"
;;
从 https://github.com/openwrt/openwrt/commit/dd300dc979eb65b189656b34e8b2cfaeba0f6afb 以及之前的一些commit来看,官方使用原来的 uci-defaults-new.sh代替了uci-defaults.sh,主要影响的应该就是02_network的添加了,而且目前是统一放在 /etc/board.d
里作为原来的/etc/uci-defaults
. 对于switch部分,基本构成是"数字|@或者:|数字",用"@"的应该是cpu port和switch口相连的,用":"的应该是写vlan的,写在最前面的数字(@和:前)是对应设备lan wan插口,书写顺序可能是按照switch端口从0开始递增,先写lan部分再写wan部分。官方目前还没写注释,新写法依然存在困惑。
对于 wr2041n v1:
tl-wr2041n-v1)
ucidef_set_interfaces_lan_wan "eth0" "eth1"
ucidef_add_switch "switch0" \
"0@eth0" "1:lan:4" "2:lan:3" "3:lan:2" "4:lan:1"
;;
该文件是定义系统更新时验证的,基本上不用怎么更改。
比如 2041n v1:
tl-wdr3500 | \
tl-wr2041n-v1 | \
添加到了wdr3500的下方。
第一个是硬件 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"
;;
这个就是很重要的设备支持 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
是设备初始化的主函数名称。
该文件主要修改如下两行,剩下的内容需要仔细学习 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 中定义。
该文件定义 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))
该文件是生成固件文件的工具,里面包含 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 信息,与其他的保持一致就好。
比如 2041n v1:
CONFIG_ATH79_MACH_TL_WR2041N_V1=y
CONFIG_ 后面的内容和上面 mach 硬件支持文件中定义的保持一致。
这个是内核支持的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 需要首先准备代码树:
$ 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
如果发现问题,需要重新进行编辑,下面介绍怎么编辑补丁文件.
还是需要首先准备代码树:
$ 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的位置很重要,它决定补丁的应用情况.
以 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可以使用make目标:refresh.
$ make package/example/refresh V=s // 软件包
$ make target/linux/refresh V=s // 内核
当需要引入新功能做更改时,经常需要多次修改补丁。为了加速,可以在编辑操作(edit)之间保留做完准备操作的代码树.
- 刚开始如上文所述准备好代码树;
- 切换到准备好的代码目录;
- 前往需要修改的patch位置;
- 修改文件并保存对patch文件的更改;
- 通过
quilt push -a
命令完全应用剩下的补丁文件; - 切换到顶层目录运行
make package/example/{compile,install}
,如果是内核的补丁,使用make target/linux/{compile,install}
命令; - 测试二进制文件。如果再次需要进行更改,从第二步重复操作;
- 最后使用
make package/example/update
命令 ,如果是内核补丁,使用make target/linux/update
命令将patch文件应用到buildroot中。
到这里基本就完成了 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 的管理操作。
从 https://github.com/openwrt/openwrt/commit/1e82d9f741cd827e33bfa3d8b3cadffaeb2773c1 开始重新安排了linux内核的板级支持架构,以后添加新板子可以不用添加patch,只需要修改如下文件(目前仅限于 ar71xx):
target/linux/ar71xx/files/arch/mips/ath79/Kconfig.openwrt
target/linux/ar71xx/files/arch/mips/ath79/Makefile
target/linux/ar71xx/files/arch/mips/ath79/machtypes.h
对于原来修改 arch/mips/ath79/Kconfig
的,添加到Kconfig.openwrt
里面,以此类推,可以参考上面所述以前添加linux内核patch的说明照猫画虎。官方的这种修改避免每次添加新板子需要重新组织内核patch序列,不管是OP的核心开发者合并修改还是给项目贡献代码都很方便。
再次记得, 删除 tmp 目录
$ rm -rf 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