本篇博客同时发布于:https://lyq.blogd.club/2018/11/08/build-openwrt/
前言
由于本人酷爱刷机,可以说是几天不刷机手就痒的那种,于是最近想看看能否在安卓下写一个工具箱(当然必须要MD材料化的工具箱,Material Design YES!!!),去实现一些简单的操作,例如用linux的busybox里面的dd命令,比如:
$ dd if = .../recovery.img of = /dev/block/.../by-name/recovery
这个命令去刷一下TWRP的第三方recovery。
但是吧,看了几天发现安卓入门怪复杂的,目前也仅仅看了看xml的几种布局,回头一想Java只会基本语法,高级特性啥也不会。
立即推 -> 放弃。。。
当然,放弃是不可能放弃的,这辈子都不可能放弃的,然后决定用Kotlin写吧,故又看了几天的Kotlin,话说这东西语法是真的牛批,简洁,面向对象。
上面说的这么多,和编译OpenWrt的笔记有什么关系吗?!
当然有,如果我今天不看Kotlin,就不会头昏脑胀,不头昏脑涨,就不会想着换点别的东西折腾折腾。
OpenWrt
OpenWRT是一个高度模块化、自动化的嵌入式Linux系统,拥有强大的网络组建和扩展性,常常被用于工控设备、电话、小型机器人、智能家居、路由器以及VOIP设备中,其中在智能路由器上有广泛使用。同时它还提供了100多个已编译好的软件,而且数量还在不断增加。
OpenWRT支持各种处理器架构,无论是ARM、X86、PowerPC或者MIPS都有很好的支持。其多达3000多种软件包,囊括从工具链(toolchain),到内核(Linux Kernel),到软件包(packages),再到根文件系统(rootfs)整个体系,是的开发者只需要简单的一个make命令就可以方便快速的定制一个具有特定功能的嵌入式系统。
对于想学习嵌入式Linux开发的工程师来说,OpenWRT是非常适合的。引用
我第一次接触Openwrt是在2013年,那是正是高二,家里有一个Tp-Link的wr740路由器,mips架构,4M的Flash,16M的内存,当时刷过dd-wrt和openwrt。
第二次是在大二了,当时为了用路由器上校园网,撸了几台k2,研究了如何在openwrt等第三方路由器固件下实现锐捷和安腾的认证。
今天我要给k2编译一下Openwrt最新的Snapshot固件,顺便解决一下不识别16M闪存的问题。
编译环境
- Ubuntu 18.06 Windows SubSystem Linux
- 硬盘 > 100G
- 编译所需dependencies就不说了
同步源码
git clone https://github.com/lede-project/source.git
并不很大,几百Mb。主要是后面编译的时候还要在线下载很多东西。
目录结构如下
.
├── tools – automake, autoconf, sed, cmake
├── toolchain/binutils – as, ld, …
├── toolchain/gcc – gcc, g++, cpp, …
├── target/linux – kernel modules
├── package – core and feed packages
├── target/linux – kernel image
└── target/linux/image – firmware image file generation
Feeds
更新源:
./scripts/feeds update -a
依据feeds.conf.default文件中的仓库地址来下载相应的软件,它提供了一些额外的扩展。
安装源:
./scripts/feeds install -a
如果少了一些依赖会提示:
WARNING: Makefile 'package/utils/busybox/Makefile' has a dependency on 'libpam', which does not exist
WARNING: Makefile 'package/utils/busybox/Makefile' has a build dependency on 'libpam', which does not exist
WARNING: Makefile 'package/network/utils/curl/Makefile' has a dependency on 'libgnutls', which does not exist
WARNING: Makefile 'package/network/utils/curl/Makefile' has a dependency on 'libopenldap', which does not exist
WARNING: Makefile 'package/network/utils/curl/Makefile' has a dependency on 'libidn2', which does not exist
WARNING: Makefile 'package/network/utils/curl/Makefile' has a dependency on 'libssh2', which does not exist
WARNING: Makefile 'package/boot/kexec-tools/Makefile' has a dependency on 'liblzma', which does not exist
WARNING: Makefile 'package/network/services/lldpd/Makefile' has a dependency on 'libnetsnmp', which does not exist
WARNING: Makefile 'package/network/utils/nftables/Makefile' has a dependency on 'jansson', which does not exist
这时候我谷歌了一番
“And re-run ./scripts/feeds update -a
successful, no error out.”
于是我也re-run了一番。。。
Collecting package info: done
Installing all packages from feed packages.
Installing all packages from feed luci.
Installing all packages from feed routing.
Installing all packages from feed telephony.
结果没报错。。。这真的是玄学吗
下载之后的文件位于feeds文件夹内。
编译
然后就是编译了,执行make menuconfig,menuconfig拥有一个文本界面,包括处理的目标平台,要编译的软件包,要被包含进固件的软件包和一些内核设置,长这样:
这边有三种选项:
-
< >该代码将不会被编译 -
<M>该代码将被交叉编译,生成的ipk软件包将被放在 /bin/packages/mipsel_24kc/base(以mt7620为例), 但该软件包不会放入固件中,需要你自己去安装 -
<X>该代码将被放入固件中 (on the SqashFS partition)
K2,故选择Target System 为(MediaTek Ralink MIPS),Subtarget为(MT 7620 based boards),由于K2是有官方资瓷的,所以Target Profile选择(Phicomm PSG1218 rev.A,rev.B应该是K2P)
同时LuCi要勾一下(Luci - Collections - luci和Luci - modules - translation - Chinese)
弄完save一下,保存为默认的.config
按两下exit退出,输入
make V=99 -j1
开始编译,这里最好不要用多线程编译,我一开始-j4总是出错,编译的时候不能断网,会下载很多东西,然后就是漫长的等待了。
支持16Mb闪存
之前刷OP官方提供的固件,闪存只识别为8M,所以要修改一下 \source\target\linux\ramips\dts\PSG1218.dtsi
[email protected] {
compatible = "jedec,spi-nor";
reg = <0>;
spi-max-frequency = <10000000>;
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
[email protected] {
label = "u-boot";
reg = <0x0 0x30000>;
read-only;
};
[email protected] {
label = "u-boot-env";
reg = <0x30000 0x10000>;
read-only;
};
factory: [email protected] {
label = "factory";
reg = <0x40000 0x10000>;
read-only;
};
[email protected] {
label = "firmware";
reg = <0x50000 0x7b0000>;
};
};
};
这个dts(devives tree source)定义了一系列板载参数,拉出来这一段代码与分区有关,<>第一个参数表示起始地址,第二个参数表示大小。
从0x0 -> 0x2FFFF,一共是196608个地址,每个地址里面存放一个字节的数据,196608 / 1024 = 192, 192Kbit的存储空间保存的是u-boot,即引导文件所在分区。
同理,0x30000 -> 0x3FFFF,的64Kbit存储空间保存的是u-boot-env,保存的是u-boot的环境变量(不太了解)。
0x40000 -> 0x4FFFF的64Kbit保存的是factory,不知道是什么,估计存放的是MAC地址和一些原厂增益参数。
从0x50000 -> 0x7FFFFF的7.6875Mbit空间是固件可用存储空间大小,总地址0x0 -> 0x7FFFFF,0x800000个地址,8Mbit。
所以为了加大对存储空间的支持,把<0x50000 0x7b0000>这个参数改成<0x50000 0xfb0000>,再重新编译一下,这样就可以了。
验证一下是否识别16M闪存
刷入未修改dts编译出来的img镜像:
可用空间只有可怜的4M…
刷入修改过的dts编译出来的img镜像:
可以看到有11M的可用空间,顺手scp上传了一首我B的歌到/root下,9.58M,证明确实可用10M空间。
如果输入cat /proc/mtd那么看到详细的分区大小,第一层分别是u-boot、u-boot(env)、factory、firmware,我们刷写固件就是刷写firmware分区。第二层分区kernel和rootfs,第三层分区rootfs_data。
Breed不死u-boot的原理就是在刷写的时候去除固件中的u-boot分区,达到自身不被覆盖、不死的效果。
添加编译自己的软件
首先于package/utils创建一个helloworld文件夹,然后
cp package/utils/nvram/Makefile package/utils/helloworld
拷贝一份写好的Makefile。
然后 nano Makeile
可以看到
#
# Copyright (C) 2009-2010 Jo-Philipp Wich <[email protected]>
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
PKG_NAME:=nvram
PKG_RELEASE:=10
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
PKG_FLAGS:=nonshared
include $(INCLUDE_DIR)/package.mk
define Package/nvram
SECTION:=utils
CATEGORY:=Base system
TITLE:=Userspace port of the Broadcom NVRAM manipulation tool
MAINTAINER:=Jo-Philipp Wich <[email protected]>
DEPENDS:[email protected]_brcm47xx||@TARGET_bcm53xx||@TARGET_ar71xx||@TARGET_ath79
endef
define Package/nvram/description
This package contains an utility to manipulate NVRAM on Broadcom based devices.
It works on bcm47xx (Linux 2.6) without using the kernel api.
endef
define Build/Compile
$(MAKE) -C $(PKG_BUILD_DIR) \
CC="$(TARGET_CC)" \
CFLAGS="$(TARGET_CFLAGS) -Wall" \
LDFLAGS="$(TARGET_LDFLAGS)"
endef
define Package/nvram/install
$(INSTALL_DIR) $(1)/usr/sbin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/nvram $(1)/usr/sbin/
ifneq ($(CONFIG_TARGET_brcm47xx),)
$(INSTALL_DIR) $(1)/etc/init.d
$(INSTALL_BIN) ./files/nvram.init $(1)/etc/init.d/nvram
endif
endef
我们只要修改一些必要的东西即可。
修改后的Makefile:
include $(TOPDIR)/rules.mk
PKG_NAME:=helloworld
PKG_RELEASE:=1
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
include $(INCLUDE_DIR)/package.mk
define Package/helloworld
SECTION:=utils
CATEGORY:=Utilities
TITLE:=helloworld
MAINTAINER:[email protected]
endef
define Package/helloworld/description
This is a Demo to test build-in package.
endef
define Build/Prepare
mkdir -p $(PKG_BUILD_DIR)
$(CP) ./src/* $(PKG_BUILD_DIR)/
endef
define Build/Configure
endef
define Build/Compile
$(MAKE) -C $(PKG_BUILD_DIR) \
CC="$(TARGET_CC)" \
CFLAGS="$(TARGET_CFLAGS) -Wall" \
LDFLAGS="$(TARGET_LDFLAGS)"
endef
define Package/helloworld/install
$(INSTALL_DIR) $(1)/bin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/helloworld $(1)/bin/
endef
$(eval $(call BuildPackage,helloworld))
再创建一个src文件夹,其中创建一个helloworld.c和Makefile。
helloworld.c:
#include <stdio.h>
#include <stdlib.h>
int main(int argc,char *argv[]){
printf("Hello World!");
return 0;
}
Makefile:
APP_NAME = helloworld
OBJ = helloworld.o
ORC = helloworld.c
$(APP_NAME):$(OBJ)
%(OBJ):$(ORC)
$(CC) $(CFLAGS) -c $(ORC)
clean:
rm $(OBJ) $(APP_NAME)
目录结构如下:
.
├── Makefile
└── src
├── Makefile
└── helloworld.c
1 directory, 3 files
返回顶层目录执行make menuconfig,选择Utilities,可以看到helloworld出现了,这时候可以选择编译进系统或者只编译单独的ipk。
当然,这样重新编译也太麻烦了,回到顶层目录执行
make package/utils/helloworld/compile V=99
可单独编译出ipk文件,这个文件位于bin/packages/mipsel_24kc/base/helloworld_1_mipsel_24kc.ipk
把这个文件上传到路由器上并安装:
scp bin/packages/mipsel_24kc/base/helloworld_1_mipsel_24kc.ipk [email protected]:/tmp
ssh [email protected]
cd /tmp
opkg install helloworld_1_mipsel_24kc.ipk
安装完成输出:
Installing helloworld (1) to root...
Configuring helloworld.
这时候输入helloworld,运行一下:
[email protected]:/tmp# helloworld
Hello World!
OK,这样就完成了编译自己的*.ipk程序。
当然,值得一提的是,如果你只需要交叉编译自己的软件,并不需要下载整个源码,可以去openwrt官网下载对应的sdk,例如mt7620 openwrt v18.06.01的sdk下载地址是:这里
,大体结构与openwrt源码一样。解压之后将要编译的程序放到package目录,执行
make menuconfig
make package/helloworld/compile V=99
即可生成ipk。
这种方法与我之前的交叉编译不同(见:1,2),虽略为繁琐,但是可以生成ipk文件,方便包管理。
这里的helloworld使用的是Makefile进行编译,如果对于一些使用Automake,需要./configure才能生成Makefile的程序,那么就要修改上一层目录的Makefile了。
添加自己的文件到固件中
如果有一个文件,hello.config想放到路由器固件目录的/etc/config/,那么把它移动到buildroot下的/files/etc/config/目录再执行make即可。文件夹不存在就自己创建。
烧录一下编译出来的bin:
执行ls /files/etc/config/hello.config看一下是否把文件编译进了系统:
Image Builder的使用
openwrt官方提供了一个imagebuilder工具,顾名思义,用来构建固件img的。根据官方介绍,这个东西对于以下方面很有用:
- 想在小Flash机器中塞入更多软件
- 想跟随snapshots开发版本
- 设备RAM小于等于32MB,OPKG不能正常工作
- 大闪存设备,想要一个特殊的固件。
Image Builder在编译系统时候会自动生成(编译需要生成img镜像,所以这个是必须的),可以在make menuconfig中勾选,这样在编译固件的时候会同时在bin/targets/ramips/mt7620/中生成Image Builder。当然,也可以自己下载,例如mt7620 openwrt v18.06.01的imgbuilder下载地址是:这里。
在这个目录里,package的源是由repositories.conf所指定的,是opkg配置格式。
## Place your custom repositories here, they must match the architecture and version.
# src/gz %n http://downloads.openwrt.org/releases/18.06.0-rc2
# src custom file:///usr/src/openwrt/bin/ramips/packages
## Remote package repositories
src/gz openwrt_core http://downloads.openwrt.org/releases/18.06.0-rc2/targets/ramips/mt7621/packages
src/gz openwrt_base http://downloads.openwrt.org/releases/18.06.0-rc2/packages/mipsel_24kc/base
src/gz openwrt_luci http://downloads.openwrt.org/releases/18.06.0-rc2/packages/mipsel_24kc/luci
src/gz openwrt_packages http://downloads.openwrt.org/releases/18.06.0-rc2/packages/mipsel_24kc/packages
src/gz openwrt_routing http://downloads.openwrt.org/releases/18.06.0-rc2/packages/mipsel_24kc/routing
src/gz openwrt_telephony http://downloads.openwrt.org/releases/18.06.0-rc2/packages/mipsel_24kc/telephony
## This is the local package repository, do not remove!
src imagebuilder file:packages
所以如果想加入本地项目,添加一行:
src custom file:///yours/bin/ramips/packages
添加远端项目同理,像src/gz这样既可。
编译固件img用make image ***这个命令,由于暂时没有使用过,所以就不写了。