Здраствуйте меня зовут Дмитрий. Как-то я купил OrangePI i96, но к сожалению производитель давно забыл об этой плате, прошивки для неё построены на ядре 3.10.62(актуальная версия на момент написания статьи 6.5.1). Поэтому я решил собрать собственную прошивку на актуальной версии ядра. Сборка будет проходить полностью из исходников.
Для начала хочу отметить что для сборки прошивки я использовал WSL (Windows Subsystem for Linux) второй версии, а в качестве гостевой системы была установлена Ubuntu, в принципе все собирается прекрасно но были некоторые проблемы, о которых будет сказано позже. Также хочу отметить что несмотря на то что во время работы над проектом, мне пришлось переустановить Windows я не потерял свои файлы. Если по каким-то причинам WSL потеряет образ, то систему можно восстановить из ext4.vhdx файла при помощи команды:
wsl --import-in-place <new_distro_name> <path_to_ext4.vhdx>
Главное не потеряйте ext4.vhdx файл и все будет в порядке.
Компиляция uboot
Сначала создадим папку в которой будем проводить сборку:
mkdir OrangePI
cd OrangePI
Поскольку Uboot идущий по умолчанию все прекрасно запускает, то менять его нет никакого смысла. Поэтому будем делать все по инструкции от авторов OrangePI i96:
wget https://github.com/orangepi-xunlong/toolchain/archive/refs/heads/arm-linux-gnueabi-1.13.1.zip
unzip arm-linux-gnueabi-1.13.1.zip
Хочу заметить, что этот тулчайн мы будем использовать только для компиляции uboot.
wget https://github.com/orangepi-xunlong/OrangePiRDA_uboot/archive/refs/heads/master.zip
unzip master.zip
cd OrangePiRDA_uboot-master
Создаем конфиг и компилируем. Путь к тулчайну у вас может отличатся. Цифра после j равна количеству потоков вашего процессора.
make CROSS_COMPILE=/home/dmitry/OrangePI/toolchain-arm-linux-gnueabi-1.13.1/gcc-linaro-1.13.1-2012.02-x86_64_arm-linux-gnueabi/bin/arm-linux-gnueabi- rda8810_config
make CROSS_COMPILE=/home/dmitry/OrangePI/toolchain-arm-linux-gnueabi-1.13.1/gcc-linaro-1.13.1-2012.02-x86_64_arm-linux-gnueabi/bin/arm-linux-gnueabi- -j16
У меня во время компиляции возникла ошибка (error while loading shared libraries: libz.so.1). Я преодолел её командой:
sudo apt-get install lib32z1
После компиляции должен появится файл u-boot.rda следует его запомнить он понадобится нам в будущем.
Создание корневой директории
Cоздадим директорию rootfs, это будет образ файловой системы нашего устройства. Для корректной работы устройства в этой директории должны находится определенные папки, в которые в последствии будут примонтированы необходимые для работы системные каталоги.
mkdir rootfs
cd rootfs
mkdir -p dev root sys proc run tmp var/run var/log
Компиляция ядра
Сначала нужно определится с тулчайном. Чип RDA8810PL использует набор команд ARMv7 с аппаратной поддержкой команд с плавающей запятой. Я использовал gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf (hf значит аппаратная поддержка команд с плавающей запятой)
wget https://releases.linaro.org/components/toolchain/binaries/7.5-2019.12/arm-linux-gnueabihf/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz
tar -xvf gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz
Потом надо скачать исходники ядра с сайта kernel.org. На момент создания статьи актуальная версия 6.5.1, с ней и будем работать.
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.5.1.tar.xz
tar -xvf linux-6.5.1.tar.xz
cd linux-6.5.1
Необходимо отметить что Opange PI i96 построена на основе чипа RDA8810PL, для работы которого необходимы драйверы которых нету в стандартном ядре. И хотя у нас есть исходники старого ядра от разработчиков . Просто взять файлы драйверов из старого ядра и положить их в новое нельзя, потому что с тех пор изменилась архитектура ядра. Современное ядро использует архитектуру которая называется Device Tree.
Раньше само ядро инициализировало устройства. А поскольку у каждого одноплатника свой набор устройств, то ядро у каждого было свое. Теперь список устройств задается в файле Device Tree(который имеет расширение .dts , .dtb после компиляции), а ядро уже считывает устройства из этого файла. Благодаря этому ядро у всех одноплатников одно и то же отличается только файл Device Tree.
К счастью я подправил файлы драйверов чтобы они могли работать с новым ядром. Мой репозиторий. Вам нужно распаковать его в паку с исходниками ядра. Хочу заметить что я сначала положил оригинальные файлы а потом уже модифицированные, так что можно посмотреть что мне пришлось подправить.
Если вы вдруг на компилируете что-то не то всегда можно ввести команду:
make distclean
И она вернет все к первоначальному виду.
Приступим к компиляции. Задаем настройки по умолчанию. Путь к тулчайну у вас может быть другой.
make ARCH=arm CROSS_COMPILE=/home/dmitry/OrangePI/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf- defconfig
make ARCH=arm CROSS_COMPILE=/home/dmitry/OrangePI/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf- menuconfig
Заходим в меню и отмечаем опции:
System type-->RDA Micro SoCs
Device drivers-->Character devices-->Enable TTY-->Serial Drivers-->RDA Micro serial port support-->Console on RDA Micr serial port
Device drivers-->GPIO Support-->Memory Mapped GPIO drivers-->RDA Micro GPIO controller support
Device drivers-->RDA support
RDA support это не опция а подменю. В него нужно войти и выбрать все опции в нем. После этого выходим и сохраняемся.
Когда вы попытаетесь включить опцию "RdaMicro IEEE802.11 emdedded FullMac WLAN driver". То система скажет вам что она зависит от другой опции которая включена как модуль и поэтому эта опция тоже будет включена как модуль. К сожалению если включить эту опцию как модуль то не произойдет инициализации сети при загрузке (я проверял). Поэтому надо найти опцию:
Networking support-->Wireless-->cfg80211-wireless configuration API
И изменить её тип с m на просто включено. После этого удастся нормально включить опцию "RdaMicro IEEE802.11 emdedded FullMac WLAN driver".
Компиляция j равно количеству потоков вашего процессора.
make ARCH=arm CROSS_COMPILE=/home/dmitry/OrangePI/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf- -j16
Также можно компилировать ядро по частям:
make ARCH=arm CROSS_COMPILE=/home/dmitry/OrangePI/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf- -j16 zImage
make ARCH=arm CROSS_COMPILE=/home/dmitry/OrangePI/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf- -j16 dtbs
make ARCH=arm CROSS_COMPILE=/home/dmitry/OrangePI/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf- -j16 modules
Если вы используете WSL и копировали файлы драйверов через проводник Windows то все папки будут иметь владельца root и вы получите ошибку permission denay. Чтобы это избежать нужно ввести команду:
sudo chown -R "имя вашего акаунта":"имя вашего акаунта" drivers/rda
После компиляции. Устанавливаем модули ядра в нашу папку rootfs.
make ARCH=arm CROSS_COMPILE=/home/dmitry/OrangePI/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf- modules_install INSTALL_MOD_PATH=../rootfs
Также создаем папку sysroot в которой будут хранится заголовки библиотек и устанавливаем их.
mkdir -p ../sysroot/usr
make ARCH=arm CROSS_COMPILE=/home/dmitry/OrangePI/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf- INSTALL_HDR_PATH=../sysroot/usr headers_install
Ядро мы объединяем с файлом Device tree командой cat. Тут может возникнуть вопрос а зачем это нужно ведь мы положим файл Device tree в образ. Здесь я сам не понимаю до конца (возможно это вызвано тем что u-boot у нас старой версии и он ни знает ничего о Device tree), но при загрузке будет использоваться тот файл который мы присоединим к ядру.
cat /home/dmitry/OrangePI/linux-6.5.1/arch/arm/boot/zImage /home/dmitry/OrangePI/linux-6.5.1/arch/arm/boot/dts/unisoc/rda8810pl-orangepi-i96.dtb > zImage+dtb
Компиляция glibc
glibc это стандартная библиотека языка C. Поскольку все программы в Linux написаны на C то без этой библиотеки ничего не запустится.
Для кроcс-компиляции необходимо задать несколько переменных среды. Эти переменные будут сохранятся до перезагрузки консоли.
export PATH=/home/dmitry/OrangePI/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin:$PATH
export CHOST=arm-linux-gnueabihf
export CC=arm-linux-gnueabihf-gcc
export AR=arm-linux-gnueabihf-ar
export CXX=arm-linux-gnueabihf-g++
Ключевое слово export нужно для экспортирования этих переменных в дочерние процессы. Если установочный скрипт будет производить настройки не в основном а в дочернем процессе, то без export произойдет ошибка.
Итак приступим:
wget https://ftp.gnu.org/gnu/glibc/glibc-2.38.tar.gz
tar -xzf glibc-2.38.tar.gz
cd glibc-2.38
mkdir build
cd build
../configure --prefix=/usr --host=arm-linux-gnueabihf CFLAGS="--sysroot=/home/dmitry/OrangePI/sysroot -O2" CC=arm-linux-gnueabihf-gcc AR=arm-linux-gnueabihf-ar CXX=arm-linux-gnueabihf-g++
make CC=arm-linux-gnueabihf-gcc AR=arm-linux-gnueabihf-ar CXX=arm-linux-gnueabihf-g++ -j16
make DESTDIR=`pwd`/glibcBuild install
Я на всякий случай передаю переменные CC AR CXX, хотя этого можно и не делать.
Также во время компиляции возникали ошибки.
fatal error: gmp.h: No such file or directory
решение:
sudo apt-get install libgmp3-dev libmpfr-dev
fatal error: mpc.h: No such file or directory
решение:
sudo apt-get install libmpc-dev
Теперь можно зайти в папку glibcBuild и рассортировать полученные файлы.
В папку rootfs:
etc
lib
sbin
usr/bin
usr/lib/*.so
usr/libexec
usr/sbin
usr/share
Здесь и далее в папку rootfs мы будем класть только файлы *.so это динамические библиотеки файлы с расширениями *.o *.a нас не интересуют.
В папку sysroot:
lib
usr/include
usr/lib
В sysroot кладем все подряд эту папку мы будем использовать для компиляции программ.
Компиляции busybox
Busybox это легковесный набор утилит для встраиваемой электроники. Самое то для одноплатника.
wget https://busybox.net/downloads/busybox-1.36.1.tar.bz2
tar -xvjf busybox-1.36.1.tar.bz2
cd busybox-1.36.1
make CROSS_COMPILE=/home/dmitry/OrangePI/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf- defconfig
make CROSS_COMPILE=/home/dmitry/OrangePI/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf- -j16
Busybox ставится в папку _install. Все содержимое этой папки надо перенести в rootfs.
Компиляция библиотеки libxcrypt
wget https://github.com/besser82/libxcrypt/releases/download/v4.4.36/libxcrypt-4.4.36.tar.xz
tar -xvf libxcrypt-4.4.36.tar.xz
cd libxcrypt-4.4.36
mkdir build
./configure --prefix=/home/dmitry/OrangePI/libxcrypt-4.4.36/build --host=arm-linux-gnueabihf
make CC=arm-linux-gnueabihf-gcc AR=arm-linux-gnueabihf-ar CXX=arm-linux-gnueabihf-g++ -j16
make install
Содержимое папки build/lib/*.so в rootfs всё остальное в sysroot.
Компиляция DropBear
Поскольку Orange PI i96 не имеет ни каких способов вывода изображения, то единственный способ взаимодействия с ним это через терминал. Поэтому будем ставить ssh сервер DropBear.
wget https://matt.ucc.asn.au/dropbear/releases/dropbear-2022.83.tar.bz2
tar -xjf dropbear-2022.83.tar.bz2
cd dropbear-2022.83
./configure --prefix=/home/dmitry/OrangePI/rootfs --disable-zlib --host=arm-linux-gnueabihf CC=arm-linux-gnueabihf-gcc LD=arm-linux-gnueabihf-ld
make install
Для корректной работы DropBear необходимо сгенерировать ключи.
cd ../rootfs
mkdir etc/dropbear
dropbearkey -t dss -f etc/dropbear/dropbear_dss_host_key
dropbearkey -t rsa -f etc/dropbear/dropbear_rsa_host_key
Компиляция библиотеки libnl-3
wget http://www.infradead.org/~tgr/libnl/files/libnl-3.2.25.tar.gz
tar -xzf libnl-3.2.25.tar.gz
cd libnl-3.2.25
mkdir build
./configure --host=arm-linux-gnueabihf --prefix=/home/dmitry/OrangePI/libnl-3.2.25/build
make -j16 CC=arm-linux-gnueabihf-gcc AR=arm-linux-gnueabihf-ar CXX=arm-linux-gnueabihf-g++
make install
cd include
make install
Из папки build копируем в rootfs все кроме include. А в sysroot все папки.
Компиляция iw
Чтобы воспользоваться терминалом нам понадобится сеть, iw это утилита для настройки wi-fi соединения.
Перед тем как мы начнем нужно задать переменную PKG_CONFIG_PATH и установить пакет pkg-config если его нет.
export PKG_CONFIG_PATH=/home/dmitry/OrangePI/sysroot/lib/pkgconfig
sudo apt-get install -y pkg-config
wget https://www.kernel.org/pub/software/network/iw/iw-3.15.tar.gz
tar -xzf iw-3.15.tar.gz
cd iw-3.15
make -j16 CC=arm-linux-gnueabihf-gcc AR=arm-linux-gnueabihf-ar CXX=arm-linux-gnueabihf-g++
cp iw ../rootfs/sbin/
Компиляция библиотеки OpenSSL
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
wget https://www.openssl.org/source/openssl-1.1.1h.tar.gz
tar -xzf openssl-1.1.1h.tar.gz
cd openssl-1.1.1h
mkdir build
./Configure linux-generic32 --prefix=/home/dmitry/OrangePI/openssl-1.1.1h/build
make -j16 CC=arm-linux-gnueabihf-gcc AR=arm-linux-gnueabihf-ar CXX=arm-linux-gnueabihf-g++
make install
Как всегда в rootfs папка lib\libcrypto.so и lib\libssl.so. В sysroot все остальное.
Компиляция wpa_supplicant
При помощи iw мы можем устанавливать соединение только с точками доступа на которых не используется шифрование. Чтобы установить соединение с шифрованием нам понадобится пакет wpa_supplicant.
wget https://w1.fi/releases/wpa_supplicant-2.10.tar.gz
tar -xzf wpa_supplicant-2.10.tar.gz
cd wpa_supplicant-2.10/wpa_supplicant
cp defconfig .config
Мне пришлось внести некоторые изменения в файл .config. Во первых я раскоментировал строки с путями к OpenSSL и подправил эти пути:
CFLAGS += -I/home/dmitry/OrangePI/sysroot/include
CFLAGS += -I/home/dmitry/OrangePI/sysroot/include/libnl3
LIBS += -L/home/dmitry/OrangePI/sysroot/lib
После закомментировал следующие строки:
#CONFIG_DRIVER_MACSEC_LINUX=y
#CONFIG_MACSEC=y
#CONFIG_CTRL_IFACE_DBUS_INTRO=y
#CONFIG_CTRL_IFACE_DBUS_NEW=y
Компиляция:
make CC=arm-linux-gnueabihf-gcc AR=arm-linux-gnueabihf-ar CXX=arm-linux-gnueabihf-g++ -j16
make install DESTDIR=/home/dmitry/OrangePI/rootfs
Создание конфигурационных файловой
При создании конфигурационных файлов самое главное не использовать блокнот Windows, потому что Windows и Linux используют разные символы окончания строки. Если не хочется использовать консоль то можно например использовать Notepad++, у него в меню Edit->EOL Conversion можно выбрать Unix подобное окончание строки.
Создаем файл с пользователем root у которого домашняя папка /. То есть при входе мы будем видеть корневой каталог.
cd rootfs/etc
cat >> passwd
root:x:0:0:root:/:/bin/sh
^D
Символ ^D означает что надо нажать сочетание клавиш CTRL+D, чтобы закончить редактирование.
В файле shadow содержатся пароли в зашифрованном виде. Здесь зашифрован пароль root.
cat >> shadow
root:LlNBpbQmj8p7g:1:0:99999:7:::
^D
Также создадим файл group. Группа wheel нужна для wpa_supplicant.
cat >> group
root:x:0:root
wheel:x:10:root
^D
В файле wpa_supplicant.conf хранится информация о том к какой сети мы будем подключатся. Также хранится пароль в виде шестнадцатеричного числа. Чтобы получить это число нужно установить wpa_supplicant на компьютер и воспользоваться утилитой wpa_passphrase:
wpa_passphrase i96 123456789
Здесь имя сети i96 пароль 123456789
cat >> wpa_supplicant.conf
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=wheel
network={
ssid="i96"
psk=c96a7f97cfee8b9f9ce9aae88c4cc6c5f1962f6dca48ea7444d80a70a300b5b1
}
^D
Файл resolv.conf содержит адреса DNS серверов:
cat >> resolv.conf
nameserver 1.1.1.1
nameserver 1.0.0.1
^D
Добавим ещё несколько файлов для конфигурации сети.
cat >> hosts
127.0.0.1 localhost
^D
cat >> hostname
OrangePI
^D
cat >> host.conf
order hosts, bind
multi on
^D
cat >> networks
loopback 127.0.0.0
localnet 127.0.0.0
^D
Дальше пойдут файлы которые слишком большие для ввода c помощью cat. Ссылка на эти файлы будет приведена в конце статьи.
Файл profile содержит начальное значение переменной PATH. Это полезно, например wap_suplicant устанавливается не в /usr/sbin а в /usr/local/sbin. Также я добавил пути к каталогу opt.
Скрипты инициализации системы
Файл inittab отвечает за такие вещи как загрузка, выключение а также нажатие AltCtrlDel. Справка.
cat >> inittab
::sysinit:/etc/rc.d/rc.S
::respawn:-/bin/sh -l
::ctrlaltdel:/sbin/reboot
::shutdown:/etc/rc.d/rc.0
^D
Создадим папку для скриптов:
mkdir rc.d
cd rc.d
Скрипт rc.0 размонтировывает диски при выключении.
cat >> rc.0
#!/bin/sh
sync
/sbin/umount -a -r > /dev/null 2>&1
^D
Дальше файл rc.S который выполняется при загрузке:
Hidden text
#!/bin/sh
# Первым делом смонтируем procfs. Без нее ничего не полетит
/bin/mount -v proc /proc -t proc 1> /dev/null
/bin/mkdir /dev/pts
/bin/mount -t devpts -o noexec,nosuid,gid=5,mode=0620 devpts /dev/pts
PATH=/bin:/usr/bin:/sbin:/usr/sbin
# Мы не будем насиловать карточку временным файлами, будем держать их в пямяти
/bin/mount -t tmpfs none -o size=150M,nodev,nosuid,noatime /tmp
/bin/mount -t tmpfs none -o size=10M,nodev,nosuid,noatime /var/log
/bin/mount -t tmpfs none -o size=5M,nodev,nosuid,noatime /var/run
/bin/mount -t tmpfs none -o size=5M,nodev,nosuid,noatime /run
# Удалим (если он был) файл о смонтированных ранее ФС и создадим пустой
/bin/rm -f /etc/mtab{,~,.tmp} && /bin/touch /etc/mtab
# Смонтируем sysfs и запустим ldconfig для обновления информации о библиотеках
/bin/mount -v sysfs /sys -t sysfs 1> /dev/null
if [ -x /sbin/ldconfig ]; then
/sbin/ldconfig 1> /dev/null
fi
# Запустим системный логгер и логгер ядра
/sbin/syslogd 2> /dev/null
sleep 1
/sbin/klogd -c 3 1> /dev/null
modprobe loop 1> /dev/null 2> /dev/null
if [ -x /etc/rc.d/rc.modules ]; then
. /etc/rc.d/rc.modules
fi
if [ -x /etc/rc.d/rc.network ]; then
. /etc/rc.d/rc.network start
fi
# Очистим экран
/bin/setterm -blank 0 2>/dev/null
# Создадим файл приглашений
echo > /etc/motd
echo "`/bin/uname -a | /usr/bin/cut -d\ -f1,3`." >> /etc/motd
echo >> /etc/motd
# Выдадим прилашение для входа в консоль
echo "Press <Enter>..."
Скрипт инициализации сети rc.network:
Hidden text
#!/bin/sh
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/sbin
net_start() {
ifconfig wlan0 up
iw dev wlan0 scan > /dev/null
wpa_supplicant -B -D nl80211 -i wlan0 -c /etc/wpa_supplicant.conf
sleep 2
ifconfig lo up 127.0.0.1
route add -net 127.0.0.0 netmask 255.255.255.0 lo
ifconfig wlan0 192.168.1.252 netmask 255.255.255.0
route add default gw 192.168.1.1 wlan0
dropbear
route
}
net_stop() {
if [ -f /var/run/udhcpc.pid ]; then
kill -9 `cat /var/run/udhcpc.pid`
rm /var/run/udhcpc.pid
fi
ip set link wlan0 down
}
case "$1" in
'start')
net_start
;;
'stop')
net_stop
;;
'restart')
net_stop
net_start
;;
*)
echo "Usage $0 start|stop|restart"
esac
Скрипт сканирует точки доступа и подключается к точки доступа параметры которой записаны в файле wpa_supplicant.conf. После этого назначается IP адрес. Если вы хотите подключится к незащищённой точки доступа (хотя я бы не советовал создавать такие точки доступа) то вместо строчки wap_suplicant нужно ввести строку iw dev wlan0 connect "имя незащищённой точки доступа". А если вы просто хотите узнать статус соединения можно использовать команду iw dev wlan0 link. Команда ifconfig -a выводит список сетевых интерфейсов
Здесь надо пояснить. У меня скрипт устроен так что IP адрес всегда выбирается 192.168.1.252. А IP адрес роутера с которым связывается одноплатник равен 192.168.1.1. Но если вы хотите чтобы при инициализации система сама находила адрес роутера, а также присваивала себе незанятый IP. Нужно строчки:
ifconfig wlan0 192.168.1.252 netmask 255.255.255.0
route add default gw 192.168.1.1 wlan0
Заменить на:
udhcpc -i wlan0 -p /var/run/udhcpc.pid -s /etc/scripts/udhcp.sh
И добавить в папку /etc/scripts скрипт udhcp.sh
Hidden text
#!/bin/sh
ACTION="$1"
case "${ACTION}" in
"renew"|"bound")
/sbin/ifconfig ${interface} ${ip} netmask ${subnet}
/sbin/route del default > /dev/null 2>&1
for i in "${router}"; do
/sbin/route add default gw $i dev ${interface}
done
;;
esac
Не забываем разрешить выполнение всех скриптов.
chmod u+x rc.0
chmod u+x rc.S
chmod u+x rc.network
Создание файла uInitrd
Чтобы загрузка системы считалась успешной. Ядро должно выполнить две вещи. Это смонтировать корневую директорию и выполнить скрипт инициализации. Но чтобы смонтировать корневую директорию на раздел SD карты например нужно загрузить драйверы SD карты, но чтобы их загрузить нужно уже иметь корневую директорию из которой мы подгрузим драйверы. Отсюда возникает дилемма чтобы смонтировать корневую директорию нам нужно уже иметь корневую директорию. Чтобы разорвать этот порочный круг создается образ uInitrd, в котором содержатся драйверы для дальнейшей работы. Этот образ разворачивается в оперативной памяти.
Кстати если в образ uInitrd положить загрузочный скрипт например такой файл init
Hidden text
#! /bin/sh
mount -t sysfs sysfs /sys
mount -t proc proc /proc
mount -t devtmpfs udev /dev
mkdir /dev/pts
mount -t devpts -o noexec,nosuid,gid=5,mode=0620 devpts /dev/pts
/sbin/syslogd
sleep 1
/sbin/klogd -c 3
. /etc/rc.d/rc.network start
dropbear
sysctl -w kernel.printk="2 4 1 7"
/bin/sh
poweroff -f
То система выполнит его и на этом остановится и мы получим Linux с корневой системой смонтированной в оперативной памяти, любые изменения в которой после перезагрузки исчезают.
Итак приступим к созданию uInitrd. Сначала мы архивируем папку rootfs и потом сжимаем её утилитой gzip.
cd rootfs
find . | cpio -H newc -ov --owner root:root > ../initramfs.cpio
cd ..
gzip initramfs.cpio
Утилита mkimage добавляет заголовок необходимый для u-boot.
mkimage -A arm -T ramdisk -n uInitrd -d initramfs.cpio.gz uInitrd
Создание файла boot.scr
boot.scr это сценарий загрузки системы. Этот сценарий предназначается для u-boot.
Создаем файл boot.cmd
setenv bootargs "earlycon initcall_debug console=ttyRDA2,921600n8 root="/dev/mmcblk0p2" rootfstype="ext4" rootwait rw"
ext2load mmc 0:1 ${kernel_addr} zImage+dtb
ext2load mmc 0:1 ${script_addr} rda8810pl-orangepi-i96.dtb
ext2load mmc 0:1 ${initrd_addr} uInitrd
ext2load mmc 0:1 ${modem_addr} modem.bin
mdcom_loadm ${modem_addr}
mdcom_check 1
bootz ${kernel_addr} ${initrd_addr}:${filesize} ${script_addr}
И cкомпилируем его утилитой mkimage:
mkimage -C none -A arm -T script -d boot.cmd boot.scr
Файл modem.bin необходим, без него ядро не стартует. Кроме того он нужен если вы хотите передать файл по UART в u-boot. Например когда я редактировал файлы драйверов мне постоянно приходилось загружать новые ядра но перезаписывать их на SD карту очень долго. Поэтому я создал образ без ядра системы. В результате чего u-boot загружался но загрузить ядро он не мог и загрузка останавливалась. После этого я вводил команду loady в консоль u-boot это переводило u-boot в режим получения файла (как раз эта команда не работает без modem.bin) и в TerTetm меню File->Transfert->YMODEM->send после пересылки ядра загружал его командой:
0x82000000 ${initrd_addr}:${filesize} ${script_addr}
Ссылка на modem.bin в конце статьи.
Сборка образа
Ну собственно все приготовления закончились дальше будет быстрее.
Сначала создадим заголовок размером 2 мегабайта:
dd if=/dev/zero bs=1M count=2 of="NewImg.img"
Копируем u-boot по смещению 256. 256 это не 256 байт а 256 блоков (dd это утилита блочного копирования) по умолчанию один блок равен 512 байт. Почему именно это смещение? Потому что оно зашито в более "глубокой" прошивки. Ну то есть наша прошивка, а есть ещё одна прошивка которая зашита прямо в чип RDA, она исполняется сразу после включения и передает управление по смещению 256*512. Там находится u-boot который уже решает откуда брать ядро и прочие файлы необходимые для загрузки операционной системы.
dd if=OrangePiRDA_uboot-master/u-boot.rda conv=notrunc seek=256 of="NewImg.img"
Создадим раздел для загрузки. И раздел для корневой директории здесь я установил размер 300Мб но по желанию можно увеличить:
dd if=/dev/zero bs=1M count=50 of="NewImg.img"1
dd if=/dev/zero bs=1M count=300 of="NewImg.img"2
Создаем файловые системы на этих разделах.
mkfs.ext2 -L BOOT "NewImg.img"1
mkfs.ext4 -O ^metadata_csum -F -b 4096 -E stride=2,stripe-width=1024 -L rootfs "NewImg.img"2
Монтируем загрузочную директорию и копируем в неё ядро системы device tree файл и сценарий загрузки:
mkdir -p /tmp/tmp
sudo mount "NewImg.img"1 /tmp/tmp
sudo cp -rf linux-6.5.1/zImage+dtb /tmp/tmp
sudo cp -rf linux-6.5.1/arch/arm/boot/dts/unisoc/rda8810pl-orangepi-i96.dtb /tmp/tmp
sudo cp -rf uInitrd /tmp/tmp
sudo cp -rf boot.cmd /tmp/tmp
sudo cp -rf boot.scr /tmp/tmp
sync
sudo umount /tmp/tmp
Монтируем вторую директорию и копируем в неё содержимое папки rootfs:
sudo mount "NewImg.img"2 /tmp/tmp
sudo cp -rT rootfs /tmp/tmp
sync
sudo umount /tmp/tmp
Объединяем все части в одну:
dd if="NewImg.img"1 conv=notrunc oflag=append bs=1M seek=2 of="NewImg.img"
dd if="NewImg.img"2 conv=notrunc oflag=append bs=1M seek=52 of="NewImg.img"
Создание таблицы разделов
Пока что образ нерабочий поскольку нет таблицы разделов. Таблица разделов создается при помощи программы fdisk.
fdisk "NewImg.img"
o
n
p
1
4096
+50M
n
p
2
106496
w
После начала редактирования мы вводим команду "o" чтобы создать пустую таблицу разделов MBR. Затем командой "n" создаем новый раздел. Выбираем тип номер и первый сектор. Дело в том что размер сектора равен 512 байт, то есть один килобайт равен двум секторам. Размер заголовка 2 мегабайта то есть 2048 килобайт или 4096 сектора. Размер раздела можно указать в мегабайтах. Тоже самое делаем со вторым разделом с той лишь разницей что программа автоматически предложит последний сектор и останется только с ней согласится. Выход командой w.
Таблицу разделов можно посмотреть командой:
sudo parted NewImg.img unit s print
Можно также монтировать эти разделы только смещение нужно указывать не в секторах а в байтах (2097152 = 4096 * 512) и (54525952 = 106496 * 512).
sudo mount -o loop,offset=2097152 NewImg.img /tmp/tmp
sudo mount -o loop,offset=54525952 NewImg.img /tmp/tmp
Всё осталось только записать образ на SD карту например при помощи Win32DiskImager.
Запуск
Как я уже говорил нужно на роутере настроить точку доступа с именем i96 и паролем 123456789, у роутера IP должен быть 192.168.1.1. Параметры сети настраиваются в файле /etc/wpa_supplicant.conf а IP в файле /etc/rc.d/rc.network. После включения нужно подождать некоторое время i96 не самый быстрый одноплатник. Вход root root.
Конечно busybox предоставляет широкий выбор приложений но их не всегда хватает. Откуда же можно брать приложения? Ну во первых их можно компилировать из исходников, но делать это очень долго, поэтому в сети существуют проекты которые предоставляют уже скомпилированные пакеты это Entware и Optware. Вот страница форума здесь нужно выбрать набор команд нашего чипа. Давайте для примера установим Entware.
Заходим в папку root и скачиваем установочный скрипт.
cd /root
wget http://bin.entware.net/armv7sf-k3.2/installer/generic.sh
chmod u+x generic.sh
./generic.sh
После этого мы получим целую корневую директорию в папке opt. Также у нас появится менеджер пакетов opkg. Давайте например установим sftp сервер он позволит перебрасывать файлы на одноплатник при помощи приложения WinSCP:
opkg update
opkg install openssh-sftp-server
Здесь есть маленький момент. Дело в том что DropBear мы компилировали из исходников и он расположен в корневой директории поэтому он ищет sftp сервер в директории /usr/libexec/sftp-server. А sftp сервер устанавливается в директорию opt. Чтобы они смогли найти друг друга надо создать символьную ссылку командой.
ln -s /opt/lib/sftp-server /usr/libexec/sftp-server
После перезагрузки sftp сервер начнет работать. Также можно установить текстовый редактор nano. Midnight commander я пытался установить, но у меня не работали стрелки и функциональные клавиши, поэтому я не смог даже из него выйти. На этом все.
Про камеру.
Не знаю помогут-ли кому-то мои изыскания но отрицательный результат тоже результат. Итак я пытался заставить работать камеру через интерфейс SCI. Но после нескольких недель мучения я понял несколько вещей.
1) Напряжение питания ядра камеры 2,9В (пин 10) а должно быть 1,8В. И изменить это никак не получается, я перепробовал все доступные регуляторы напряжения, которые есть в i96 ноль реакции.
2) Кроме SCI интерфейса камера так-же подключается к i2c интерфейсу. По этому интерфейсу идет управление камерой. I2c интерфейс выведен на пины 3 и 5 это линии SCK и SDA. На линии SCK всегда +3 вольта, так и должно быть, а вот на линии SDA 0. И этого быть не должно, интерфейс i2c предполагает что линии SCK и SDA должны быть подтянуты к шине питания, 0 на них может появляется лишь во время обращения к устройству и то только на короткое время.
Из всего этого можно заключить что интерфейс SCI на i96 нерабочий. Ну либо у меня бракованная плата, хотя Wi-Fi работает а чип Wi-Fi также подключается к RDA через i2c.
У меня даже возникло сомнение, а кто-либо подключал когда-нибудь к i96 камеру через SCI интерфейс?
Ссылки:
Файлы modem.bin, profile, protocols, services, termcap. (Если ссылка не действует, то файлы можно достать из готового образа)
Мой репозиторий с файлами ядра.
Собранный образ. (В принципе он должен работать и на Orange PI 2g-iot но у меня нет этой платы, проверить не могу.)
Ссылка на файлы взятые из оригинального ядра OrangePI i96. Они получены путем сравнения образа от разработчиков и ядра Linux 3.10.62.