1. 用户和密码

1.1. Ubuntu Desktop 系统

Ubuntu Desktop 系统开机启动后,自动登录到 firefly 用户。

如果有连接调试串口,串口终端自动登录 root 用户。

  • firefly 用户密码: firefly

  • root 用户:默认没有设置 root 密码,firefly 用户通过 sudo passwd root 命令自行配置 root 密码。

1.2. Ubuntu Minimal 系统

  • Ubuntu Minimal 系统开机启动后,自动登录到 root 用户,密码为 firefly。

  • 系统已经添加 OpenGL ES, OpenCL, DRM 支持。

1.3. Buildroot 系统

  • 用户:root

  • 密码:firefly

2. ADB 使用

2.1. ADB

用Type-C data cable连接设备和主机,然后输入以下命令:

adb devices
adb shell

2.2. 网络ADB

查看开发板 IP 地址,PC 端通过网络访问:

adb connect + IP
adb shell

注意点: AIO-3399-JD4 / AIO-3399J 要支持使用 ADB 需要修改kernel/arch/arm64/boot/dts/rockchip/rk3399-firefly-aio.dtsi,将usbdrd_dwc3_0设置为peripheral模式,之后该usb只能作为从设备使用。

&usbdrd_dwc3_0 {
    dr_mode = "peripheral";
};

同理,AIO-3399Pro-JD4 要支持使用 ADB 需要修改kernel/arch/arm64/boot/dts/rockchip/rk3399pro-firefly-aioc.dts

然后重新编译和烧写Kernel。

3. linux-headers 和 linux-image

linux-headers 和 linux-image 是两个 deb 包,可安装在 Debian/Ubuntu 系统中。

linux-headers 包含各种头文件,可以让设备具有本地编译驱动的能力。

linux-images 包含编译内核时产生的驱动模块,将这些模块安装到设备中,设备才能 modprobe/insmod 进行使用。如果设备使用了 extboot,自行编译的 linux-image 内还会包含内核,安装即可直接升级内核,免去烧写步骤。

如何确定是否使用了 extboot?请前往板卡对应维基的编译 Ubuntu 固件页面,部分编译章节查看。如果没有 extboot 相关内容,则说明不支持。

3.1. 获取方法

3.1.1. 下载

Firefly 官方提供了两个 linux-headers 和 linux-image,方便客户在开发板进行驱动编译。官方提供的包中不含内核,安装后不会对内核进行升级,各板型均可使用。

各个板卡的下载请到 Firefly 官方《资料下载》页面下载。选择板卡后一般在“资源”处,名称为 linux-headers

3.1.2. 制作

官方提供的 headers 和 image 版本和实际固件可能有差异,并且有定制需求的客户也无法使用,因此建议通过 SDK 制作:

首先准备环境、获取 SDK、编译前配置,请前往不同板卡的维基查看,接下来在 SDK 根目录进行编译:

# 选择板卡配置文件
./build.sh xxxx.mk
# 编译
./build.sh kerneldeb

生成的文件会在 SDK 根目录:

linux-headers-x.xx.xxx_x.xx.xxx-xxx_arm64.deb
linux-image-x.xx.xxx_x.xx.xxx-xxx_arm64.deb

如果 SDK 使用了 extboot,那么 linux-image 包含内核,因此只能用于你在./build.sh xxxx.mk所选择的型号上。

3.2. 安装

以下以 ROC-RK3568-PC 安装为例:

将得到的 deb 包放入设备中,然后安装,对于 headers,安装后还需要进行编译处理:

对于使用了 extboot 的板卡,安装后重启即可完成内核更新,之后再进行编译 headers

# 安装
sudo dpkg -i linux-headers-4.19.172_4.19.172-189_arm64.deb
sudo dpkg -i linux-image-4.19.172_4.19.172-189_arm64.deb

# 准备编译环境
sudo apt install -y build-essential python libssl-dev

# 编译
cd /usr/src/linux-headers-4.19.172
make headers_check
make headers_install

# make scripts 可能会出错,如果出错在 tools,可以直接忽略,安装完成
make scripts

4. 导出设备系统

当用户已经在一台设备上完成工作环境的部署,需要将当前环境完整导出,以批量部署到其它同设备上,可以通过导出设备文件系统来备份当前的开发环境。

导出设备系统分为两步:

  1. 在设备上导出 Ubuntu 根文件系统 rootfs;

  2. 二次打包完整固件,将 Ubuntu rootfs 与发布固件的其他分区组合,完成二次打包,生成新的完整固件。

4.1. 导出 rootfs

注意以下操作均在设备端上操作!

  1. 在设备的 Ubuntu 环境下,安装 fireflydev

    sudo apt update
    sudo apt install fireflydev
    
  2. 安装 fireflydev 后,就能使用 ff_export_rootfs 脚本导出根文件系统

    • 建议使用容量较大的移动硬盘

    • 导出工具会执行 apt clean 等操作以减小文件系统大小

    • 将根文件系统导出,例如导出到 /media/firefly/AC91-C4AE/ 目录(需要等待一定时间):

    ff_export_rootfs /media/firefly/AC91-C4AE/
    
    • 压缩文件系统,删除不必要的空白空间以减少存储器资源的占用:

    # 有部分客户说导出的 rootfs 大小为 3.3G,可实际只用了 3G,原因是没有对 rootfs 进行压缩
    e2fsck -p -f Firefly_Ubuntu_18.04.6_rootfs.img
    resize2fs -M Firefly_Ubuntu_18.04.6_rootfs.img
    

4.2. 二次打包完整固件

注意以下操作均在 PC 机端(x86-64 架构)上操作!

  1. 安装必要的软件包:sudo apt-get install lib32stdc++6

  2. 下载二次打包工具:firefly-linux-repack(提取码:1234)

  3. 解压二次打包工具:

    tar -xzf firefly-linux-repack.tgz
    cd firefly-linux-repack
    

    目录如下:

    firefly-linux-repack
        ├── bin
        │   ├── afptool
        │   └── rkImageMaker
        ├── pack.sh # 打包脚本
        ├── Readme_en.md
        ├── Readme.md
        └── unpack.sh # 解包脚本
    
  4. 解包操作: 把官方发布的 Ubuntu 固件拷贝到 firefly-linux-repack 根目录,重命名为 update.img,执行解包脚本 unpack.sh。解包完成后,各分区文件在 output 目录下。

    mv /path/to/ROC-RK3566-PC_Ubuntu18.04-r21156_v1.2.4a_220519.img update.img
    ./unpack.sh
    
  5. 打包操作:保持当前目录结构,文件名等不变,接入移动硬盘到 PC 机,把前面导出的 Ubuntu rootfs 替换 output/Image/rootfs.img,然后执行打包脚本 pack.sh

    cp /media/customer/1878-4615/Firefly_Ubuntu_18.04.6_rootfs.img /path/to/firefly-linux-repack/output/Image/rootfs.img
    ./pack.sh
    
  6. 新的完整固件为当前目录的 new_update.img

5. GPIO 配置与使用

GPIO, 全称 General-Purpose Input/Output(通用输入输出),是一种软件运行期间能够动态配置和控制的通用引脚。

以下通过控制 ROC-RK3399-PC Pro 的 LED 为例,对于其他设备,方法是类似的。

ROC-RK3399-PC Pro 的主控是 RK3399,RK3399 有 5 组 GPIO bank:GPIO0~GPIO4,每组又以 A0~A7, B0~B7, C0~C7, D0~D7 作为编号区分。

5.1. GPIO 编号计算

ROC-RK3399-PC Pro 板载两个 LED,如下:

_images/gpio_led_1.png

DIY_LED 网络是接到引脚 GPIO0_B5:

_images/gpio_led_2.png

PIO pin 脚计算公式:

pin = bank * 32 + number

GPIO 小组编号计算公式:

number = group * 8 + X

例如 GPIO0_B5:

bank = 0;      // GPIO0_B5 => 0, bank ∈ [0,4]
group = 1;     // GPIO0_B5 => 1, group ∈ {(A=0), (B=1), (C=2), (D=3)}
X = 5;         // GPIO0_B5 => 5, X ∈ [0,7]
number = group * 8 + X = 1 * 8 + 5 = 13;
pin = bank * 32 + number = 0 * 32 + 13 = 13;

注意:这个引脚在官方发布的固件中默认已被 LED 子系统占用,因此首先需要找到以下节点将其 disable!

ROC-RK3399-PC Pro 是定义在arch/arm64/boot/dts/rockchip/rk3399-roc-pc.dtsi

user {
        status = "disabled"; // 添加这一行
        label = "firefly:yellow:user";
        linux,default-trigger = "ir-user-click";
        default-state = "off";
        gpios = <&gpio0 13 GPIO_ACTIVE_HIGH>;
        pinctrl-names = "default";
        pinctrl-0 = <&led_user>;
};

然后编译与重新烧写内核固件。

5.2. 用户态使用 GPIO

1、申请 GPIO

echo 13 > /sys/class/gpio/export

2、配置引脚方向

查看默认引脚方向:

cat /sys/class/gpio/gpio13/direction

配置成输出方向:

echo out > /sys/class/gpio/gpio13/direction

3、配置引脚输出电平

从前面的原理图可知,输出高电平为点亮 LED:

echo 1 > /sys/class/gpio/gpio13/value

熄灭 LED:

echo 0 > /sys/class/gpio/gpio13/value

5.3. 设备树使用 GPIO

在设备树中配置 GPIO,需要配置引脚的功能复用与电气属性

对于 rockchip 引脚,配置如下:

rockchip,pins = <PIN_BANK PIN_BANK_IDX MUX &phandle>

其中:

  • PIN_BANK:引脚所在的 bank

  • PIN_BANK_IDX:引脚所在 bank 的引脚号

  • MUX:功能复用配置,0 表示普通 GPIO,1-N 表示特殊的功能复用

  • phandle:引脚一般配置,例如内部上拉、电流强度等,在Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt文件中描述

配置 GPIO0_B5 引脚:

rockchip,pins = <0 13 RK_FUNC_GPIO &pcfg_pull_none>;

此处的含义:

  1. PIN_BANK等于0

  2. PIN_BANK_IDX等于13

  3. RK_FUNC_GPIO代表使用普通 GPIO 功能

  4. pcfg_pull_none代表普通配置

对于 LED,Linux 定义了一套 GPIO 子系统,设备树的配置如下:

/ {
	gpio_led: gpio-led {
		compatible = "gpio-leds";

		diy_led: diy-led {
			label = "diy-led";
			default-state = "on"; // 默认打开
			linux,default-trigger = "default-on"; // 默认触发
			gpios = <&gpio0 13 GPIO_ACTIVE_HIGH>; // 引脚设置
			pinctrl-names = "default";
			pinctrl-0 = <&diy_led_pin>; // 引用 pinctrl
		};
	};
};

&pinctrl {
	gpio-led-pin {
		diy_led_pin: diy-led-pin {
			rockchip,pins =
				<0 13 RK_FUNC_GPIO &pcfg_pull_none>;
		};
	};
};

然后编译与重新烧写内核固件,重启系统会看到 LED 默认点亮。

如果希望 LED 具有闪烁效果,可以修改linux,default-trigger属性实现:

linux,default-trigger = "timer";

配置该属性后,LED 默认每 500ms 间隔闪烁。

更多属性配置可以参考Documentation/devicetree/bindings/leds/leds-gpio.txt

以上设备树配置可以在arch/arm64/boot/dts/rockchip/firefly-gpio-demo.dtsi找到!有需求的用户在板极设备树中包含该文件即可(记得要首先 disable rk3399-roc-pc.dtsi里面冲突部分):

#include "firefly-gpio-demo.dtsi"

6. 网络配置

6.1. 以太网口通用参数配置

6.1.1. 查看以太网通用参数

以太网的通用参数包括:自协商,双工模式和接口速率

ethtool eth0

6.1.2. 配置以太网通用参数

6.1.2.1. 打开或关闭自协商

ethtool -s port_name autoneg { on | off }

6.1.2.2. 修改双工模式

ethtool -s port_name duplex { half | full }

注意:

  • 当以太网接口工作在自协商模式时,缺省情况下双工模式是和对端接口协商得到的。

  • 当以太网接口工作在非自协商模式时,缺省情况下双工模式为全双工模式。

6.1.2.3. 修改速率

ethtool -s port_name speed { 10 | 100 | 1000 }

注意:

  • 当以太网接口工作在自协商模式时,缺省情况下接口速率是和对端接口协商得到的。

  • 当以太网接口工作在非自协商模式时,缺省情况下接口速率为接口支持的最大接口速率。

6.1.3. 配置举例

手动设置 eth0 的接口速率为 100,工作在全双工模式下。

ethtool -s eth0 autoneg off
ethtool -s eth0 speed 100
ethtool -s eth0 duplex full

6.2. 使用Netplan管理网络

Netplan 是一个用于在 linux 系统上轻松配置网络的实用程序。您只需创建所需网络接口的 YAML 描述以及每个应配置的功能。根据此描述,Netplan 将为您选择的渲染器工具生成所有必要的配置。在Ubuntu18.04及其以上版本进行了支持。

6.2.1. 配置

要配置 netplan,请/etc/netplan/使用.yaml扩展名(例如/etc/netplan/config.yaml)保存配置文件,然后运行sudo netplan apply. 此命令解析配置并将其应用于系统。

注意:

  • 如果 netplan apply 报错,说明您的yaml配置文件未被系统支持,请仔细检查

  • 对于以太网口,必须保证有网线接入,并且网卡灯闪烁,才能保证Netplan配置生效

下面根据最常使用的工作场景进行配置,需要更多的配置案例教程,请阅读netplan官方实例

6.2.2. 基础配置

Netplan支持networkd和NetworkManager两种网络后端,一般为networkd

network:
  version: 2
  renderer: networkd

如果不存在networkd,可以使用NetworkManager,都是一样的。

network:
  version: 2
  renderer: NetworkManager

6.2.3. 以太网连接:动态IP

network:
  version: 2
  renderer: networkd
  ethernets:
    eth0:
      dhcp4: yes
    eth1:
      dhcp4: yes

6.2.4. 以太网连接:静态IP

network:
  version: 2
  renderer: networkd
  ethernets:
    eth0:
      addresses:
        - 10.10.10.3/24
      nameservers:
        addresses: [202.96.128.86]
      routes:
        - to: 0.0.0.0/0
          via: 10.10.10.1
    
    eth1:
      addresses:
        - 10.10.10.2/24
      nameservers:
        addresses: [202.96.128.86]
      routes:
        - to: 0.0.0.0/0
          via: 10.10.10.1

6.2.5. WIFI连接:静态IP

network:
  version: 2
  renderer: networkd
  wifis:
    wlan0:
      dhcp4: no
      dhcp6: no
      addresses: [192.168.1.200/24]
      nameservers:
        addresses: [202.96.128.86]
      access-points:
        "NETGEAR25":
            password: "ceshizhuanyong"
      routes:
        - to: 0.0.0.0/0
          via: 192.168.1.1

6.2.6. WIFI连接:动态IP

network:
  version: 2
  renderer: networkd
  wifis:
    wlan0:
      dhcp4: yes
      access-points:
        "NETGEAR25":
            password: "ceshizhuanyong"

6.3. 使用nmcli管理网络

nmcli是用来管理NetworkManager网络连接的命令行工具

6.3.1. 常用命令

  • 显示所有连接

    nmcli connection show
    
  • 显示连接信息

    nmcli connection show connection_name
    
  • 显示网络设备列表、其状态以及使用该设备的连接

    nmcli device
    
  • 激活连接

    nmcli connection up connection_name
    
  • 取消激活连接

    nmcli connection down connection_name
    
  • 删除连接

    nmcli connection del connection_name
    

6.3.2. 以太网连接:静态IP

假设进行配置以太网网卡为eth0,IP为192.168.1.10/24,默认网关为192.168.1.1,DNS服务器为202.96.128.86

  1. 为以太网连接添加新的连接

    nmcli connection add con-name Example-Connection ifname eth0 type ethernet
    
  2. 设置 IPv4 地址

    nmcli connection modify Example-Connection ipv4.addresses 192.168.1.10/24
    
  3. 将 IPv4 连接方法设置为 manual

    nmcli connection modify Example-Connection ipv4.method manual
    
  4. 设置 IPv4 默认网关

    nmcli connection modify Example-Connection ipv4.gateway 192.168.1.1
    
  5. 设置 IPv4 DNS 服务器地址

    nmcli connection modify Example-Connection ipv4.dns "202.96.128.86"
    
  6. 激活连接

    nmcli connection up Example-Connection
    

6.3.3. 以太网连接:动态IP

  1. 为以太网连接添加新的连接

    nmcli connection add con-name Example-Connection ifname eth0 type ethernet
    
  2. 激活连接

    nmcli connection up Example-Connection
    

6.3.4. WIFI连接:动态IP

  1. 确保 WiFi 被启用(默认)

    nmcli radio wifi on
    
  2. 刷新可用的 Wi-Fi 连接列表:

    nmcli device wifi rescan
    
  3. 查看可用的 Wi-Fi 接入点:

    nmcli dev wifi list
    
    IN-USE  SSID      MODE   CHAN  RATE        SIGNAL  BARS  SECURITY
    ...
            MyCafe    Infra  3     405 Mbit/s  85      ▂▄▆█  WPA1 WPA2
    
  4. 使用 nmcli 连接到 Wi-Fi 连接:

    nmcli dev wifi connect SSID-Name password wireless-password
    

    例如:

    nmcli dev wifi connect MyCafe password wireless-password
    

    请注意,如果要禁用 Wi-Fi 状态:

    nmcli radio wifi off
    

6.4. 快速创建无线AP热点

6.4.1. 对无线热点的IP局域网段无要求

在这种情况下,只需要使用nmcli命令创建一个无线AP热点即可:

nmcli device wifi hotspot ifname wlan0 con-name MyHostspot ssid MyHostspotSSID password 12345678

说明:

  • con-name:连接名称:这里设置为MyHostspot(可自定义)

  • ssid:创建的AP热点的名称:这里设置为MyHostspotSSID(可自定义)

  • password:创建的AP热点的密码:这里设置为12345678(可自定义)

6.4.2. 对无线热点的IP局域网段有要求

请阅读章节《创建桥接无线AP》

6.5. 创建桥接无线AP热点

6.5.1. 功能需求

假设有一局域网,网段为10.10.0.0,掩码为255.255.255.0。Firefly的开发板,以下简称Firefly Board,其网口通过路由器Router,获取到本局域网内的动态IP地址:为10.10.0.2

需求:将系统配置成软路由,具体要求如下:

(1)Firefly Board开启一个无线AP热点,平板和手机等外设通过该无线AP热点访问网络,进行上网。

(2)Firefly Board开启的无线热点局域网为:192.168.4.1

(3)Firefly Board如果有多个网口,要求eth0作为WAN口功能,自动从路由器获取IP地址,eth1作为LAN口功能,能够为接入的设备分配192.168.4.0/24网段的IP地址。

网络拓扑如下:

_images/wifi-bridge-topology.png

6.5.2. 安装管理AP热点必要的软件包

安装hostapdhostapd可以用来模拟软AP,所以是实现该功能必须的:

apt install hostapd

允许hostapd开机启动,这样重启之后无线AP热点会自动打开

systemctl unmask hostapd
systemctl enable hostapd

安装isc-dhcp-serverisc-dhcp-server用于为接入无线AP的设备自动分配IP地址和DNS服务器地址

apt install isc-dhcp-server 

允许isc-dhcp-server开启启动

systemctl enable isc-dhcp-server 

安装netfilter-persistent iptables-persistent:用于保存防火墙规则

apt install netfilter-persistent iptables-persistent

安装bridge-utils:用于创建虚拟网桥

apt install bridge-utils

6.5.3. 配置Netplan

目的是创建网桥br0,网桥IP为192.168.4.1。允许系统eth0网卡分配IP地址,禁止系统为eth1网卡分配IP地址,将eth1网卡绑定到网桥br0

假设netplan的配置文件为:/etc/netplan/netplan.yaml,内容如下所示:

network:
        version: 2
        renderer: networkd
        ethernets:
                eth0:
                        dhcp4: yes
                eth1:
                        dhcp4: no

        bridges:
                br0:
                        dhcp4: no
                        addresses:
                                - 192.168.4.1/24
                        interfaces:
                                - eth1

接着运行如下命令启用网络配置:

netplan apply

6.5.4. 配置hostapd

创建一个hostapd.conf配置文件,用来设置无线热点的名称,密码,信道等属性

vim /etc/hostapd.conf

在其中写入如下内容:

country_code=CN
interface=wlan0
bridge=br0
ssid=Example-Wifi-Name
hw_mode=g
channel=11
macaddr_acl=0
auth_algs=1
ignore_broadcast_ssid=0
wpa=2
wpa_passphrase=12345678
wpa_key_mgmt=WPA-PSK
wpa_pairwise=TKIP
rsn_pairwise=CCMP

重要参数说明:

  • country_code:国家码,中国使用CN

  • interface:开启无线AP热点的无线网卡

  • bridge:绑定到br0网桥,使得无线AP热点和以太网口在同一个局域网内

  • hw_mode:设置无线模式

  • channel:信道

  • ssid:无线AP名称,这里设置Example-Wifi-Name

  • wpa_passphrase:无线AP密码,这里设置为12345678

关于更多,hostapd.conf的配置无疑是非常复杂的,hw_mode支持的模式有a,gchannel信道与hw_modecountry_code等都有关系,这里不再展开。如果需要对这些无线参数进行更自动化且紧密的配置,可以使用OpenWRT软路由系统来代替Ubuntu系统。

接下来,需要配置hostapd的全局配置文件

vim /etc/default/hostapd

取消DAEMON_CONF的注释,设置它的值为上面创建的/etc/hostapd.conf

# Defaults for hostapd initscript
# 
# See /usr/share/doc/hostapd/README.Debian for information about alternative
# methods of managing hostapd.
#
# Uncomment and set DAEMON_CONF to the absolute path of a hostapd configuration
# file and hostapd will be started during system boot. An example configuration
# file can be found at /usr/share/doc/hostapd/examples/hostapd.conf.gz
#
DAEMON_CONF="/etc/hostapd.conf"

# Additional daemon options to be appended to hostapd command:-
#       -d   show more debug messages (-dd for even more)
#       -K   include key data in debug messages
#       -t   include timestamps in some debug messages
#
# Note that -B (daemon mode) and -P (pidfile) options are automatically
# configured by the init.d script and must not be added to DAEMON_OPTS.
#
#DAEMON_OPTS=""

重启hostapd服务

systemctl restart hostapd

到此,已经可以通过手机等设备,查看到有一个无线AP热点开启,名称为“Example-Wifi-Name”,但是连接之后无法为设备分配IP地址,设备会立即断开。

6.5.5. 配置isc-dhcp-server

isc-dhcp-server 作为一个dhcp服务器,为接入无线AP节点的设备,比如拓扑图中的Laptop1Laptop2自动分配IP地址和DNS服务器地址。

编辑/etc/dhcp/dhcpd.conf

vim /etc/dhcp/dhcpd.conf

用如下内容进行替换:

# 为设备指定DNS地址,多个DNS使用","隔开
option domain-name-servers 202.96.128.86,202.96.128.166,8.8.8.8,114.114.114.114;
default-lease-time 600;
max-lease-time 7200;
ddns-update-style none; ddns-updates off;

subnet 192.168.4.0 netmask 255.255.255.0 {
    range 192.168.4.2 192.168.4.200;
    option routers 192.168.4.1;
    option broadcast-address 192.168.4.255;
    option subnet-mask 255.255.255.0;
}

重要参数说明:

  • domain-name-servers:DNS服务器地址列表,为接入192.168.4.0/24网段的设备,分配DNS

  • subnet 192.168.4.0 netmask 255.255.255.0:定义子网网段192.168.4.0/24

  • range 192.168.4.2 192.168.4.200:分配的IP地址范围

  • option routers 192.168.4.1:默认路由

  • option broadcast-address 192.168.4.255:广播地址

  • option subnet-mask 255.255.255.0:子网掩码

重启isc-dhcp-server,让配置生效:

systemctl restart isc-dhcp-server

6.5.6. 开启IP转发

经过如上内容的配置,接入eth1的设备,和连接入无线AP热点的设备,都能获取到192.168.4.0/24网段的IP,且都能ping通192.168.4.1,也可以查看到设备获取到的DNS服务器地址。但是设备还无法访问internet。

开启IP转发

sysctl -w net.ipv4.ip_forward=1

设置MASQUERADE(地址欺骗)。MASQUERADESNAT作用大致一样,MASQUERADE不用指定明确的IP,会动态的将报文的源地址修改为指定网卡上可用的IP地址。

iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

注意,这里指定为eth0,让Firefly Board所有的IP包全部转发到eth0,让外设能够进行上网,这里也可以指定为任何能访问外网的网卡,比如4G网卡usb0wwan0,举一反三。

现在保存 IPv4(包括上面的规则)和 IPv6 的当前防火墙规则,以便在启动时由 netfilter-persistent 服务加载:

netfilter-persistent save

6.6. 使用ip和netplan配置IP地址和路由

6.6.1. 静态IP地址配置

一个网口接口上可以同时配置多个IP地址,这些IP地址可以属于同一网络,也可以不属于同一网络。第一个配置的IP地址默认为接口的主IP地址,后面配置的IP地址为接口的从IP地址。

6.6.1.1. 常用的IP配置命令:

// 设置接口的 IP 地址
ip address add PREFIX [ broadcast ADDR ] dev IFNAME

// 删除接口的 IP 地址
ip address del PREFIX dev IFNAME

// 查看接口的 IP 地址
ip address show/list [ dev IFNAME ]

// 清空接口的所有 IP 地址
ip address flush [ dev IFNAME ]

6.6.1.2. 配置举例:

为 eth0 接口配置主IP:192.168.2.2,从IP:192.168.2.3

  • 临时配置

    ip address add 192.168.2.2/24 dev eth0
    ip address add 192.168.2.3/24 dev eth0
    
  • 持久化配置:使用Netplan

    network:
      version: 2
      renderer: networkd
      ethernets:
        eth0:
          dhcp4: no
          addresses:
              - 192.168.2.2/24
              - 192.168.2.3/24
    

6.6.2. 动态IP地址配置

操作系统一般都会为网络接口自动分配IP地址。对于 buildroot 系统中,dhcpcd服务会发送dhcp请求到DHCP服务器(这里的DHCP服务器大概率是你的路由)请求接口的IP地址。而在Ubuntu系统中,会由NetworkManager来完成这一过程。

  • 临时配置

    udhcpc -i eth0/eth1
    
    # 或者
    dhclient eth0/eth1
    
  • 持久化配置:使用netplan

    network:
      version: 2
      renderer: networkd
      ethernets:
        eth0:
          dhcp4: yes
    

6.6.3. 静态路由配置

与静态路由相对的是动态路由,动态路由有OSPF和RIP,这两个协议只存在于路由器中。对于非路由器设备,如果某个目的网段无法直接到达,需要配置静态路由,告诉设备目的网段,出接口,下一跳的IP地址。

6.6.3.1. 常用的配置命令:

# 查看路由表
route -n 
# 或者
netstat -rn

# 添加 IP 静态路由
ip route add PREFIX via ADDRESS dev IFNAME [ metric METRIC ]

# 删除 IP 静态路由
ip route del PREFIX via ADDRESS dev IFNAME [ metric METRIC ]

# 清空 IP 路由
ip route flush dev IFNAME

6.6.3.2. 配置举例:

假设存在这样的一个网络拓扑,图中的Router1和Router2是我们的开发板设备,运行的系统是Ubuntu操作系统。在这个网络中,对于Router1,网段192.168.2.0/24和网段192.168.3.0/24,它们对于Router1属于直连网段,意味着对于PC-A来说,访问网段192.168.2.0/24和网段192.168.3.0/24是没有问题的,但是却不能访问网段192.168.4.0/24。这是因为对于网段192.168.4.0/24,对Router1来说是不可见的。需要在Router1配置静态路由,这条静态路由表明到目的网段192.168.4.0/24,需要经过下一条IP地址为192.168.3.2/24,出接口为Router1的eth1。同样的,对于Router2来说,网段192.168.3.0/24和网段192.168.4.0/24属于直连网段,PC-B也同样无法访问192.168.2.0/24网段,需要在Router2配置一条静态路由,表明到目的网络192.168.2.0/24,需要经过下一跳IP地址为192.168.3.1/24,出接口为为Router2的eth1。

_images/router1-20220629170401-d8fx48i.png

  • 临时配置

    • 对于Router1:

      # 开启IP转发功能
      echo 1 > /proc/sys/net/ipv4/ip_forward
      
      # 设置eth0, eth1的IP地址
      ip addr add 192.168.2.1/24 dev eth0
      ip addr add 192.168.3.1/24 dev eth1
      
      # 配置静态路由
      ip route add 192.168.4.0/24 via 192.168.3.2 dev eth1
      
    • 对于Router2:

      # 开启IP转发功能
      echo 1 > /proc/sys/net/ipv4/ip_forward
      
      # 设置eth0, eth1的IP地址
      ip addr add 192.168.4.1/24 dev eth0
      ip addr add 192.168.3.2/24 dev eth1
      
      # 配置静态路由
      ip route add 192.168.2.0/24 via 192.168.3.1 dev eth1
      
  • 持久化配置

    • 对Router1和Router2,执行以下命令永久开启IP转发

      sysctl -w net.ipv4.ip_forward=1
      
    • 对于Router1,配置Netplan

      network:
              version: 2
              renderer: networkd
              ethernets:
                      eth0:
                              addresses:
                                      - 192.168.2.1/24
                      eth1:
                              addresses:
                                      - 192.168.3.1/24
                              routes:
                                      - to: 192.168.4.0/24
                                        via: 192.168.3.2
      
    • 对于Router2,配置Netplan

      network:
              version: 2
              renderer: networkd
              ethernets:
                      eth0:
                              addresses:
                                      - 192.168.4.1/24
                      eth1:
                              addresses:
                                      - 192.168.3.2/24
                              routes:
                                      - to: 192.168.2.0/24
                                        via: 192.168.3.1
      

6.6.4. 默认路由配置

  • 临时配置

    对于通过DCHP服务动态获取IP地址的接口,操作系统会自动为其分配一条默认路由。对于静态IP地址配置,需要手动为其设置默认路由。

    还是以上面的例子来讲解,假设PC-A是一个Linux操作系统,我们需要进行如下配置:

    # 配置网卡 IP,假设其网卡为eth0
    ip addr add 192.168.2.2/24 dev eth0
    
    # 配置默认路由
    ip route add 0.0.0.0/0 via 192.168.2.1 dev eth0
    
  • 持久化配置:使用Netplan

    network:
            version: 2
            renderer: networkd
            ethernets:
                    eth0:
                            addresses:
                                    - 192.168.2.2/24
                            routes:
                                    - to: 0.0.0.0/0
                                      via: 192.168.2.1
    

6.6.5. 调整默认路由顺序

在双网口的开发板中,如果两个网口的IP地址是通过DHCP自动获取的,那么操作系统会生成两条默认路由,每个网口分别有一条默认路由,先插入网线的网口或者先获得IP的网口,会获得更高的路由优先级。如下所示,有两条默认路由,eth0网卡的默认路由优先级高于eth1。这就意味着开发板默认通信的时候,使用的是eth0网卡。

root@firefly:~# ip route list
default via 168.168.0.1 dev eth0 proto dhcp metric 100 
default via 168.168.0.1 dev eth1 proto dhcp metric 101 
168.168.0.0/16 dev eth0 proto kernel scope link src 168.168.110.72 metric 100 
168.168.0.0/16 dev eth1 proto kernel scope link src 168.168.110.111 metric 101

6.6.5.1. 配置举例:

假设存在一种情况,Wireless Router1的网段为192.168.3.0/24,Wireless Router2的网段为192.168.2.0/24,此时对于Firefly Board来说,eth0和eth1都是动态获取IP地址,如果eth0的默认路由的优先级比eth1的默认路由优先级高,通信时将使用eth0的默认路由,由于eth0所在网络无外网连接,Firefly Board就无法访问 Internet,此时可以通过修改默认路由的优先级来解决。

_images/router2-20220630162119-lgcya9l.png

其Netplan配置如下,eth1的metric数值小于eth0,数值越小优先级越高

network:
        version: 2
        ethernets:
                eth0:
                        dhcp4: yes
                        dhcp4-overrides:
                                route-metric: 200
                eth1:
                        dhcp4: yes
                        dhcp4-overrides:
                                route-metric: 100

6.7. iptables NAT配置

网络转换技术也称为NAT(Network Address Translation)技术,它的基本作用就是实现私有IP地址和公有IP地址之间的转换。

在Linux系统中,NAT可以细化为SNAT(Source Network Address Translation)和DNAT(Destinationnetwork address translation)。SNAT也称为源地址转换技术,用于当私网主机向外网主机发起网络通信时,IP数据包在到达外网网络之前,将IP数据包中的源IP修改为路由器或者防火墙的IP地址,这样外网主机就无法获知内网主机的私网IP地址。DNAT也称为目标地址转换技术,用于当外网主机需要访问内网主机提供的网络服务时,比如http,IP数据包到达路由器或者防火墙时,由它们将IP数据包中的目标IP改为提供网络服务的私网主机IP。

6.7.1. 常用命令

我们可以通过配置iptables的nat表,来实现SNAT和DNAT

# 查看nat规则
iptables -t nat -vnL

# 清空nat规则
iptables -t nat -F

# 添加一个SNAT规则,将内网的IP,映射到外网的IP
iptables -t nat -A POSTROUTING -s LocalIP -j SNAT --to-source ExtIP

# 添加一个DNAT规则,将外网的IP和端口,映射到内网的IP和端口
iptables -t nat -A PREROUTING -d ExtIP -p tcp|udp --dport PORT -j DNAT --to-destination LocalIP[:PORT]

iptables也支持MASQUERADE(地址欺骗),它的作用与SNAT基本相同,也可以起到源地址转换的作用。在一种特殊情况中,如果外网的IP地址不是一个固定且长期有效的IP地址,比如是通过pppoe进行拨号动态获取的IP地址,就可以使用MASQUERADE来实现源地址转换。MASQUERADE则不用指定明确的IP,会动态的将报文的源地址修改为指定网卡上可用的IP地址。

# 添加一个MASQUERADE规则,将内网的IP,映射到外网网卡所在的IP(这里的内网IP可以省略,则默认将所有内网的IP,都映射到外网网卡所在的IP)
iptables -t nat -A POSTROUTING [-s LocalIP] -o IFNAME -j MASQUERADE

6.7.2. 配置举例

假设存在这样的一个网络拓扑,用10.1.0.0/16来模拟一个公网网络,用192.168.1.0/24来模拟私有网络。图中的机器都是用Linux主机进行模拟的机器。

_images/router3-20220705091939-09uw91m.png

对于Router1,是一个连接内外网的路由器,其netplan配置如下:

network:
        version: 2
        renderer: networkd
        ethernets:
                eth0:
                        addresses:
                                - 192.168.1.3/24
                eth1:
                        addresses:
                                - 10.1.0.7/16

同时对于Router1,需要开启IP转发功能:

echo 1 > /proc/sys/net/ipv4/ip_forward

对于Internet PC,是一个外网的个人主机,其netplan配置如下:

network:
        version: 2
        renderer: networkd
        ethernets:
                eth0:
                        addresses:
                                - 10.1.0.6/16

对于Web Server,是一个私网服务器,提供http服务,其netplan配置如下:

network:
        version: 2
        renderer: networkd
        ethernets:
                eth0:
                        addresses:
                                - 192.168.1.100/24
                        routes:
                                - to: 0.0.0.0/0
                                  via: 192.168.1.3/24

6.7.3. SNAT

需求:目前的网络结构中,内网主机是无法访问外网的。

添加一条SNAT规则,修改内网主机发往外网的IP数据包,将源IP地址为192.168.1.0/24网段的IP,修改为10.1.0.7

iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -j SNAT --to-source 10.1.0.7

验证方法:

  • 在内网的Web Server,ping外网的Internet PC

    ~  ping -c 4 10.1.0.6                                                                                                                                                    ok 
    PING 10.1.0.6 (10.1.0.6) 56(84) bytes of data.
    64 bytes from 10.1.0.6: icmp_seq=1 ttl=63 time=2.15 ms
    64 bytes from 10.1.0.6: icmp_seq=2 ttl=63 time=2.12 ms
    64 bytes from 10.1.0.6: icmp_seq=3 ttl=63 time=1.99 ms
    64 bytes from 10.1.0.6: icmp_seq=4 ttl=63 time=2.14 ms
    
    --- 10.1.0.6 ping statistics ---
    4 packets transmitted, 4 received, 0% packet loss, time 7ms
    rtt min/avg/max/mdev = 1.989/2.098/2.147/0.063 ms
    
  • 在内网的Web Server,抓包

    root@firefly:/# tcpdump -i eth1 -nn icmp       
    tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
    listening on eth1, link-type EN10MB (Ethernet), capture size 262144 bytes
    03:33:37.503348 IP 10.1.0.7 > 10.1.0.6: ICMP echo request, id 53287, seq 1, length 64
    03:33:37.503603 IP 10.1.0.6 > 10.1.0.7: ICMP echo reply, id 53287, seq 1, length 64
    03:33:38.503348 IP 10.1.0.7 > 10.1.0.6: ICMP echo request, id 53287, seq 2, length 64
    03:33:38.503560 IP 10.1.0.6 > 10.1.0.7: ICMP echo reply, id 53287, seq 2, length 64
    03:33:39.504601 IP 10.1.0.7 > 10.1.0.6: ICMP echo request, id 53287, seq 3, length 64
    03:33:39.504812 IP 10.1.0.6 > 10.1.0.7: ICMP echo reply, id 53287, seq 3, length 64
    03:33:40.505347 IP 10.1.0.7 > 10.1.0.6: ICMP echo request, id 53287, seq 4, length 64
    03:33:40.505557 IP 10.1.0.6 > 10.1.0.7: ICMP echo reply, id 53287, seq 4, length 64
    

6.7.4. DNAT

需求:内网Web Server提供http服务,外网主机想要访问内网的web网页。

添加一条DNAT规则,修改外网发往内网的IP数据包,将目的IP地址,和端口号,修改为内网Web服务器的IP和端口号。

iptables -t nat -A PREROUTING -d 10.1.0.7 -p tcp --dport 8000 -j DNAT --to-destination 192.168.1.100:8000

验证方法:

  • 在外网Internet PC访问内网Web Server的Web服务

    root@firefly:/# wget http://10.1.0.7:8000/index.html
    --2021-02-19 03:31:12--  http://10.1.0.7:8000/index.html
    Connecting to 10.1.0.7:8000... connected.
    HTTP request sent, awaiting response... 200 OK
    Length: 41323 (40K) [text/html]
    Saving to: ‘index.html’
    
    index.html          100%[===================>]  40.35K  --.-KB/s    in 0.001s  
    
    2021-02-19 03:31:12 (29.8 MB/s) - ‘index.html’ saved [41323/41323]
    

6.7.5. MASQUERADE

需求:如果连接内外网的Router1,它的外网网卡只有一个,为eth1,且IP地址动态获取。

解决方法:添加一条MASQUERADE规则,将内网192.168.1.0/24发往外网的IP数据包,修改其源IP地址为eth1网卡的IP地址。

iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth1 -j MASQUERADE

6.8. iptables filter配置

iptables的filter表(过滤规则表),用于控制数据包是否允许进出及转发。filter 表可以控制的链路有 INPUT、FORWARD 和 OUTPUT。常用的动作有ACCEPT,DROP,REJECT。

6.8.1. 通用命令

# 清空filter表
iptables -t filter -F

# 显示filter表
iptables -t filter -nvL

6.8.2. ACCEPT:允许数据包通过

配置举例:默认情况下ssh使用22端口进行tcp通信,如果要开启远程访问,需要开启22端口的tcp连接。

iptables -A INPUT -t filter -p tcp --dport 22 -j ACCEPT

开启ssh访问,允许192.168.0.0/24网段进行访问

iptables -A INPUT -t filter -p tcp -s 192.168.0.0/24 --dport 22 -j ACCEPT

开启ssh访问,允许收到的数据包来源于eth0网卡

iptables -A INPUT -t filter -p tcp -i eth0 --dport 22 -j ACCEPT

开启ssh访问,允许192.168.0.0/24网段中MAC地址为00:50:8D:FD:E6:32的主机进行访问

iptables -A INPUT -t filter -p tcp -s 192.168.0.0/24 --dport 22 -m mac --mac-source 00:50:8D:FD:E6:32 -j ACCEPT

6.8.3. REJECT:拒绝数据包通过

REJECT动作的常用选项为–reject-with(使用–reject-with选项,可以设置提示信息,当对方被拒绝时,会提示对方为什么被拒绝)

对于ICMP协议,可用值如下,如果不提供,默认为icmp-port-unreachable

icmp-net-unreachable
icmp-host-unreachable
icmp-port-unreachable,
icmp-proto-unreachable
icmp-net-prohibited
icmp-host-pro-hibited
icmp-admin-prohibited

配置举例:拒接外部ping,并提示”Destination Host Unreachable”

iptables -A INPUT -t filter -p icmp -j REJECT --reject-with icmp-host-unreachable

6.8.4. DROP:丢弃数据包

配置举例:直接将外部ping包丢弃

iptables -A INPUT -t filter -p icmp -j DROP

7. Qt 支持

7.1. Qt 环境支持

Firefly 设备系统如果是 Ubuntu 22.04 可以直接通过 apt 安装 Qt 环境:

# 安装基础环境
apt update
apt install -y qtcreator qtbase5-dev

# 安装额外组件与开发环境,例如
apt install -y libqt5multimedia5 qtmultimedia5-dev libqt5quick5 qtdeclarative5-dev

安装后直接在设备上进行开发。

Ubuntu 18.04 或者 Ubuntu 20.04 需要借助电脑进行交叉编译,详情请看下一章

7.2. Qt 交叉编译环境支持

Firefly 发布了两个 Qt 交叉编译工具链,适用于以下环境,请根据需求选择:

  • Qt: 5.12.2

  • Host: x86-64 / Ubuntu 18.04

  • Target: Firefly RK3568 RK3566 RK3399 RK3328 PX30 / Ubuntu 18.04 Minimal&Desktop

  • Qt: 5.15

  • Host: x86-64 / Ubuntu 20.04

  • Target: Firefly RK3588 RK3568 RK3566 / Ubuntu 20.04 Desktop

工具链完整支持 wenEngine, 支持 EGLFS LinuxFB XCB 等 backend。

  • 下载地址

点击下载链接(提取码:FFQT)

  • 部署

详情参见工具链中的 Qt5.1x.x_Release.md 文件

注意,文档中所有路径的名称不可更改,否则会导致编译或者运行出错。

  • 编译

在host端,进入 Qt 工程目录,qmake && make 即可.

  • 运行

工具链中含有例程,用户在部署完成后,可以在 host 端 build demo,在 tartget 端运行 demo 以测试部署是否成功。

确定了使用哪个后端,就可以修改设备中 /etc/profile.d/target_qtEnv.sh 文件,去除对应平台环境变量前面的#使其一直生效

# 例如,使用 XCB ,则将文件内 XCB 部分前面的 # 删除

#XCB
export QT_QPA_PLATFORM=XCB
export QT_QPA_EGLFS_INTEGRATION=XCB_EGL

7.3. Qt 双屏异显

Firefly Ubuntu 系统可以使用 Qt 应用实现双屏显示和操作。

(1)进入桌面环境

export XAUTHORITY=/home/firefly/.Xauthority
export DISPLAY=:0

(2)设置环境变量

export QT_QPA_PLATFORM=xcb
export QT_QPA_EGLFS_INTEGRATION=XCB_EGL

(3)运行 Demo

./firefly_arm64_qt5.12.2_18.04/demo/double_panel_demo

(4)Demo 代码目录

firefly_arm64_qt5.12.2_18.04/example/double_panel_demo

(5)代码编译

cd example
qmake
make

(6)添加自己的 Qt 工程

  1. example 目录下添加用户自己的 Qt 项目工程。

  2. 编辑 example 目录下 gui.pro 文件。

  3. 假设工程目录名为 double_panel_demo,则在 gui.pro 文件中追加 SUBDIRS += double_panel_demo

  4. 执行命令qmake && make

(7)运行效果

_images/double_panel.jpg

7.4. Qt Creator

目标平台的系统是 Ubuntu 22.04 则不用看本章节,直接在设备上使用 qtcreator,无需特殊设置。

其他版本系统需要交叉编译 qt,请继续往下看:

下面介绍主机上 Qt Creator 的使用说明,在操作前,请先安装、配置好 Qt 交叉编译环境和运行环境

7.4.1. 安装

进入 Qt 官方下载页面,选择一个版本下载 qt-creator-opensource-linux-x86_64-x.x.x.run,下载完成之后,在终端执行./xxxx.run运行安装,注意文件需要有执行权限。

7.4.2. 配置

下面以 firefly-qt-5.12.2-aarch64 环境作为例子进行配置,目标平台是 Buildroot 系统:

目标平台系统不同,配置也稍有不同,所以请仔细查看文字说明,图片仅供参考,不要照搬图片中的配置

安装完成后,启动 Qt Creator,打开菜单 Tools -> Options,找到 Kits。

  • 配置 Qt Versions

    点击右侧 add 按钮添加,选择 Qt 环境安装位置中的 qmake 即可

    qmake:/opt/firefly-qt-5.12.2-aarch64/host/bin/qmake

_images/Qt-config-Versions.png

  • 配置 Compilers

    点击右侧 add 按钮添加 gcc 和 g++ 交叉编译器的位置

    如果主机安装了 crossbuild-essential-arm64,则编译器就在 /usr/bin/

    如果使用了第三方的交叉编译器,找到安装位置并添加即可

    如果目标平台是 Buildroot,则需要使用 Buildroot Qt 环境包中的编译器

    g++:/opt/firefly-qt-5.12.2-aarch64/host/bin/aarch64-buildroot-linux-gnu-g++

    gcc:/opt/firefly-qt-5.12.2-aarch64/host/bin/aarch64-buildroot-linux-gnu-gcc

_images/Qt-config-Compilers_1.png

_images/Qt-config-Compilers_2.png

为方便调试,配置 Debuggers 和 Devices 用于在线调试:

  • 配置 Debuggers

    首先主机中安装 gdb-multiarch:apt install -y gdb-multiarch

    检查目标机上是否存在 /usr/bin/gdbserver,没有的话需要安装:apt install -y gdbserver (Buildroot 自带,无需安装)

    回到主机的 Qt Creator,点击右侧 add 按钮添加 gdb

    选择主机中的 gdb-multiarch :/usr/bin/gdb-multiarch

_images/Qt-config-Debuggers.png

  • 配置 Devices

    设置好设备的 IP、用户名 (root) 和密码 (firefly) 。为了方便调试,可以在设备上设置静态 IP。

    GDB server 设置为 /usr/bin/gdbserver

_images/Qt-config-Devices.png

  • 配置 Kits

    将前面设置的配置项添加到 Kits。

    如果目标平台是 Ubuntu 系统,这一步也需要添加 sysroot 的路径

_images/Qt-config-Kits.png

7.4.3. 编译运行

打开 demo 程序,Welcome -> Open Project,选择要使用的 Kits:

_images/Qt-Choose-Kit.png

之后打开 Projects -> Run,配置命令行参数,这里设置为 -platform wayland

目标平台是 Ubuntu 则使用 -platform xcb (Ubuntu 桌面环境),或者根据需要选择 linuxfbeglfs

_images/Qt-command_line_arguments.png

配置环境变量,即 export XDG_RUNTIME_DIR=/tmp/.xdg

RK356X Buildroot 则需要使用 /var/run 而不是 /tmp/.xdg

目标平台是 Ubuntu 则需要根据之前设置的 platform 添加不同的环境变量,详情在 Qt 环境包中的说明文件中

如果目标平台的运行环境(本文开头提到的)之前已经配置好并成功运行 demo,此时可以直接点击右侧Fetch Device Environment 获取目标的环境变量

_images/Qt-set-environment.png

编译运行:

点击 Build 交叉编译 Qt 程序;点击 RunDebug 在设备上运行或调试程序。要重新运行程序时,记得手动点击 Stop 关闭已经运行的程序。

_images/Qt-Compile.png

编译生成目录和 demo 目录在同一位置。

8. Docker 支持

Firefly 发布的普通固件一般不满足 Docker 的运行要求,如果有需求,可以使用 SDK 打开内核的相关配置,重新编译烧录内核以支持 Docker。

(RK356X v1.2.4a 及以后版本 、RK3399/RK3588 默认支持 Docker,可直跳到 安装 Docker 步骤)

以下案例是基于 Firefly Ubuntu 20.04,内核配置部分是通用的!

8.1. 检查 Kernel 配置

首先需要通过工具检查当前设备的内核缺少了哪些 Docker 需要的配置。检测脚本check-config.sh可以前往社区论坛获取。

获取到脚本之后,开始进行检测:

#将脚本拷贝到SDK的kernel目录下
cp check-config.sh PathToSDK/kernel/
cd PathToSDK/kernel
chmod +x check-config.sh

#获取当前内核配置
make ARCH=arm64 firefly_linux_defconfig

#检测
./check-config.sh .config

执行后的结果如下,主要是两部分:

Generally Necessary:
- cgroup hierarchy: properly mounted [/sys/fs/cgroup]
- apparmor: enabled and tools installed
- CONFIG_NAMESPACES: enabled
- CONFIG_NET_NS: enabled
- CONFIG_PID_NS: enabled
- CONFIG_IPC_NS: enabled
- CONFIG_UTS_NS: enabled
- CONFIG_CGROUPS: enabled
......

Optional Features:
- CONFIG_USER_NS: enabled
- CONFIG_SECCOMP: enabled
- CONFIG_SECCOMP_FILTER: enabled
- CONFIG_CGROUP_PIDS: enabled
- CONFIG_MEMCG_SWAP: enabled
......

Generally Necessary: 表示必要的配置,如果有显示 missing 的地方,就需要在内核配置中打开它。 Optional Features: 是可选配置,根据需要打开。

8.1.1. 开启需要的配置

从上面的检测结果中得知需要打开哪些配置后,即可使用make ARCH=arm64 menuconfig进入菜单,搜索对应项目将其打开。请认真查看菜单中的操作说明,遇到不可选中的项目请注意依赖关系。

开启所有必要配置以及部分可选配置后,注意保存:

make ARCH=arm64 savedefconfig
mv defconfig arch/arm64/configs/firefly_linux_defconfig

之后进行编译内核:

#退回到SDK目录
cd ..
#编译内核
./build.sh kernel

8.2. 安装 Docker

烧录完新内核之后,可以开始在设备上安装 Docker (此安装方法同样适用于 PC):

  • 步骤1:快速安装

# 这里仅介绍直接使用脚本快速安装
apt-get update
wget -qO- https://get.docker.com/ | sh

等待安装成功之后应该会看见 Docker 版本信息

  • 步骤2:检查 docker 存储位置(该步骤仅适用于 PC 安装 docker)

如果你在 Firefly 设备中安装 docker,请跳过步骤2

# 执行
docker info | grep -i dir
# 执行结果
 Docker Root Dir: /var/lib/docker

返回的信息显示了 docker 的默认存储位置,该位置在不同电脑上可能不一样

镜像和容器会占用大量空间,因此,如果默认的位置空间不大,需要修改到空间充足的位置

再次强调,此步骤只用于 PC,Firefly 设备中,修改此位置会导致 docker 无法工作,请直接跳到下一步

# 先关闭 docker 服务
sudo systemctl stop docker

# 修改文件 /lib/systemd/system/docker.service
sudo vim /lib/systemd/system/docker.service

# 在这一行末尾添加想要修改的位置 --graph /home/firefly/docker/data
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --graph /home/firefly/docker/data

# 重启 docker 服务
sudo systemctl daemon-reload
sudo systemctl start docker

# 检查位置是否修改成功
docker info | grep -i dir
 Docker Root Dir: /home/firefly/docker/data
  • 步骤3:将自己的用户添加到 docker 组

sudo usermod -a -G docker firefly
# 添加后重启
sudo reboot
  • 步骤4:重启后运行 demo 测试是否正常:

firefly@firefly:~# docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
93288797bd35: Pull complete
Digest: sha256:cc15c5b292d8525effc0f89cb299f1804f3a725c8d05e158653a563f15e4f685
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (arm64v8)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/

9. ROS 支持

9.1. 安装 ROS

首先按照官方安装教程安装,根据系统选择对应ROS版本安装。 官方安装教程

9.1.1. 安装 GLX 库

rviz,gazebo是基于GLX编写的,我们系统目前只支持EGL,所以他们无法使用GPU加速,同时需要安装GLX库才能能够正常运行。

apt install -y libgl1-mesa-glx libgl1-mesa-dri libglx-mesa0
reboot

9.2. 更新 libqt5opengl5-dev

如果遇到rviz还不能运行,rqt报QOpenGLTimeMonitor等错误,需要更新官方的libqt5opengl5-dev, 执行下面操作,再尝试运行rqt、rviz和gazebo等程序

sed -i 's/.*wiki.t-firefly.com.*/\#&/' /etc/apt/sources.list
apt install libqt5opengl5-dev
sed -i '/.*wiki.t-firefly.com.*/s/^#//' /etc/apt/sources.list

9.3. wayland 下运行 rviz,rqt 和 gazebo 等程序

XWayland说明 基于GLX的程序在wayland运行,需要使用XWayland。使用 QT_QPA_PLATFORM=xcb 强制Qt应用程序使用X11

QT_QPA_PLATFORM=xcb rviz
QT_QPA_PLATFORM=xcb rqt
QT_QPA_PLATFORM=xcb gazebo
# 也可以将该环境设置到.bashrc,就可以直接运行rviz等程序。
echo "export QT_QPA_PLATFORM=xcb" >> /~/.bashrc

10. 显示架构支持

对于 Rockchip 平台,主要有以下几种显示架构可供选择:

  • Qt + Wayland

  • Qt + EGLFS

  • EGL program + X11

  • Wayland

  • None

多窗口的功能需求,选择:

  • X11

  • Wayland

桌面的功能需求,选择:

  • X11

4K 视频播放 + 全屏:

  • Qt + Wayland

  • Qt + EGLFS

  • X11

  • Wayland

4K 视频播放 + 多窗口:

  • X11

  • Qt + Wayland

  • Wayland

如果您对显示架构的技术不太理解,可以继续往下阅读。

10.1. X11

X11 是 X 显示协议的第 11 个版本。

X 协议已经延用了 30 年,X 协议的 Client/Server 结构起初是为了在以前硬件性能太弱的情况下,设备(Client 端)通过发送渲染请求给 X server(以前 X server 是运行在另一个独立的硬件)渲染显示。

但是随着现代硬件性能不断提升,同一个硬件系统上可以同时运行 Client 和 Server 了,但是这种远程通讯结构运用在本地机器上带来的后果就是性能的丢失,目前在 Debian 官方已经有分支在开发 Wayland 用于替换掉 X11,但是目前 Wayland 对现有软件兼容性并不好所有还没有正式替换使用。

_images/X_Logo.png

参考资料:

https://en.wikipedia.org/wiki/X.Org_Server
https://www.comptechdoc.org/os/linux/howlinuxworks/linux_hlxwindows.html
https://dri.freedesktop.org/wiki/DDX/
https://www.freedesktop.org/wiki/Software/Glamor/
https://en.wikipedia.org/wiki/X.Org_Server

10.2. Qt + EGLFS

Qt + EGLFS 是 Qt 自己实现的一个 GUI 系统,不支持多窗口,但也因此少了 window composite。

Qt + EGLFS 和 dri2 的方式类似,区别就在于 Qt + EGLFS 的 font buffer 在自己用 gpu composite 后,是直接送给 DRM 显示,而 X 里是送至 Window manager 做 composite,所以 EGLFS 在效率上是有优势的。

_images/qt_logo.jpeg

10.3. Qt + Wayland

Wayland 中,Weston 是 Wayland 显示协议中的具体实现,其对应关系就好比 Xorg (X server)和 X 的关系一样。

目前 Wayland 和 X 对比唯一缺点就是在兼容性上了,所以目前主流的系统版本中依然大部分使用 X。

Weston 不再使用 X 的 Client/Server 的结构,而是直接由合成器接受内核事件,并传递给 Client 端,由 Client 端直接渲染,只向合成器发送需要更新的区域,再由合成器通知内核安排翻页。

需要注意的是由于 Ubuntu/Debian 已经有 X11,所以 SDK 默认是在 Buildroot 添加了 Weston 的支持,实际上如果 Ubuntu/Debian 需要安装 Weston 的话也可以在 Minimal 版本上搭建。(Firefly Ubuntu 20.04,将会默认自带 Wayland 和 X,并且可以自由切换。)

_images/wayland_logo.png

建议使用 Buildroot/Yocto 做 Wayland 的开发。效率上 Wayland 要比 X11 好点,主要是兼容性问题。

如果不需要桌面,又要多窗口,可以尝试使用 Wayland。

10.4. None

除了 X11 和 Wayland 之外,还有 None,这也是嵌入式上接触比较多的。比如 MiniGUI,SDL皆是如此。

若要支持到 DRM 和 opengl 的话,就只能选择 Qt 了。

MiniGUI 是一个定位于轻量级的嵌入式图形库,对系统资源的需求完全考虑到了嵌入式设备的硬件情况,如 MiniGUI 库所占的空间最小可以裁剪到 500K 左右。

针对 Buildroot 系统适配在硬件资源比较紧张的设备上的特性,MiniGUI 搭配 Buildroot 是再适合不过了。

参考资料:

https://wayland.freedesktop.org/architecture.html
https://en.wikipedia.org/wiki/Wayland