![image](https://habrastorage.org/webt/ck/mx/-x/ckmx-xfxoxqwc4xy_cmoxisqwts.jpeg)
В прошлой статье, мы с вами рассмотрели на что способен одноплатный компьютер, который стоит всего 1.000 рублей. Как мы выяснили, перспективы у данного девайса весьма неплохие, однако по факту, Orange Pi продаёт практически голую железку, которую нужно дорабатывать самому. Да, тут есть Ubuntu/Fedora, да, тут выведена гребенка с I2C/SPI — однако из коробки это всё работает криво-косо, либо не работает совсем. Даже обещанные шины SPI/I2C фактически не доступны в системе «из коробки». Материалов о доработке этого одноплатника в сети мало, поэтому я решил довести его до ума сам и поделится с вами — в том числе, готовыми бинарными образами! Интересно, на что способен доработанный одноплатник по цене ящика пива? :)
Над чем будем работать
В прошлой статье, мы с вами определились с потенциальными перспективами такого устройства. По цене 3х ESP32, производитель предлагает нам два полноценных вычислительных ARM-ядра, 256 мегабайт оперативной памяти, 512 мегабайт встроенной NAND-памяти, контроллер питания с возможностью работы от литий-ионных АКБ и 3G модем. Но в бочке меда нашлась ложка дегтя: никто не собирался это всё поддерживать и Orange Pi практически сразу «забили» на поддержку устройства, ограничившись портом Debian/Ubuntun на устройство.
![image](https://habrastorage.org/getpro/habr/post_images/db2/be4/69b/db2be469bfeafd950a4de83a24291513.jpg)
Более того, производитель даже не описал как работать с GPIO и шинами устройства — что фактически превращало его из одноплатника в обычную ТВ-приставку, только без нормального видеовыхода. Меня крайне удивило, почему над такой дешевой платой не хотело работать коммьюнити — большинство людей только видели всю ситуацию и шли оставлять негативный отзыв, не попытавшись даже разобраться. А ведь для опытного линуксоида-эмбеддера здесь работы на день-два!
Ко всему прочему, в Linux не работает GSM-стек. Да, совсем. Производитель даже не стал кооперироваться с MediaTek, чтобы попытаться реализовать работу с модемом на уровне системы. А ведь фактически, вся работа с модемом происходит лишь на уровне AT-команд. Так в чем же проблема была?
![image](https://habrastorage.org/getpro/habr/post_images/416/3d0/bf2/4163d0bf28176becd7d0ddb8105b2c1f.jpg)
Со всем этим мне и предстоит разобраться! Клонируем репозиторий с исходниками ядра и бежим собирать!
Собираем ядро. I2C и SPI
Вместо типичного Buildroot, Orange Pi использует свою собственную простую систему сборки на shell-скриптах: в качестве тулчейна используется уже готовый linaro. Отчасти, это связано с самими чипами, на которых работают их устройства — MediaTek, например, не использует Mainline ядро и в процессе сборке имеет ещё кучу шагов для подготовки финального образа. Там даже menuconfig не работает и все изменения приходится делать в уже сгенерированной когда-то конфигурации.
Клонируем репозиторий с системой сборки и запускаем скрипт:
git clone https://github.com/orangepi-xunlong/OrangePi_Build
cd OrangePi_Build
./Build_OrangePi.sh
![image](https://habrastorage.org/getpro/habr/post_images/6dc/d89/59a/6dcd8959af8ae2cb918447b50bc22c05.png)
Выбираем нашу плату — 3G IoT и ждем, пока система сборки фактически скачает все необходимое для сборки — исходный код ядра, папки external (драйвера, загрузчик и порт linux MediaTek). Обратите внимание, OrangePi даже систему сборки завязали на конкретной версии системы: только Ubuntu 18.04, но на самом деле, ядро соберется без проблем практически где угодно. После того, как все было скачано, переходим в папку с скриптом сборки и запускаем скрипт сборки:
cd ../OrangePi3G_iot/
./build.sh
А нет, не запускаем — скрипт жалуется на то, что не может поставить некоторые пакеты. Не беда — ставим bsdtar и python minimal вручную и идем править код скрипта. Находится в он scripts/general.sh: убираем оттуда устаревшие имена пакетов.
![image](https://habrastorage.org/getpro/habr/post_images/25b/88a/485/25b88a4858b2449a3102372b872f4cf4.png)
После этого, компиляция ядра должна пройти успешно. Обратите внимание на версию вашей платы — те, что продают сейчас — именно A. Если пытаться подкинуть им ядро для B, то они будут уходить в kernel panic из-за отсутствия eMMC.
![image](https://habrastorage.org/getpro/habr/post_images/5e9/5ac/c6d/5e95acc6de70f6dae5fd92144234e4b1.png)
Если mkbootimg будет жаловаться на libstdc++6, то ставим его x86 версию из репозиториев.
Готовое ядро будет лежать в output/kernel/boot.img, которое можно прошить на устройство. С одним маленьким нюансом — оно рассчитано на загрузку из внутренней памяти, которой критически мало для дистрибутива Linux! У нас нет boot_sd.img, который есть в оригинальном дистрибутиве. Попытка разобрать образ стандартным AndImgTool не увенчалась успехом — рамдиск встроен прямо в образ zImage, а не отдельно, как это обычно бывает у Android-образов.
![image](https://habrastorage.org/getpro/habr/post_images/a5b/038/a2b/a5b038a2b54b8c39b4d5ad98dbd930a5.png)
Покопавшись в скриптах сборки, я так и не понял логику создания boot_sd, ничего связанного с sd я не нашел даже grep'ом по всей папке. Ну что-ж, тогда попробуем обходным путем: скомпилируем нужные драйвера в виде загружаемых модулей (ko). Идём в наш конфиг, расположенный в linux/arch/arm/configs/3giot_defconfig и меняем CONFIG_I2C_CHARDEV и CONFIG_SPI_SPIDEV на m. Пояснение: y заставит систему сборки скомпоновать драйвер статически с ядром, а m выделит его в виде отдельного модуля ko, который затем можно загрузить через insmod.
![image](https://habrastorage.org/getpro/habr/post_images/392/f27/116/392f271168f65d282019a4e4b1864c4d.png)
Снова собираем ядро, на этот раз компиляция занимает не больше минуты. Нужные нам файлы появятся в linux/drivers/spi/spidev.ko и linux/drivers/i2c/i2c-d-ev.ko. Переносим их на хост-пк, а затем и на само устройство с помощью SSH:
![image](https://habrastorage.org/getpro/habr/post_images/619/a69/426/619a69426168c6726269c849837dfc69.png)
Загружаем модули ядра:
insmod i2c-dev.ko
И та-дам! Целых две i2c шины появилось в системе (/dev/i2c-0, /dev/i2c-1). Устанавливаем i2c-tools и идем проверять с помощью i2cdetect: первая шина полностью свободна под наши проекты, а на второй по некоторым адресам висит периферия (FM-радио как вариант):
![image](https://habrastorage.org/getpro/habr/post_images/a98/c6c/616/a98c6c616860b5a5644b5c05f9633a95.png)
I2C теперь точно работает! Но как насчет SPI?
insmod spidev.ko
Device or resource busy.
Увы! spidev нельзя подгружать динамически, только статически линковать с ядром, чего мы сделать пока не можем. Однако техническая возможность заставить работать SPI есть: например, написать свой драйвер, который транслирует команды из юзерспейса в SPI API, которое работает на уровне ядра.
GPIO
В прошлой статье, я вкратце рассказал, как работать с gpio из user-space на уровне терминала. Однако, большинство разработчиков потенциально будет пользоваться нативным API для GPIO — ну не всерьез же им парсить вывод состояния в консоль? Поэтому я решил написать крошечную библиотеку для работы с GPIO, такую же простую, как и DigitalWrite/DigitalRead!
Давайте сначала разберемся, как именно работать с драйвером GPIO. Для этого открываем исходники ядра и смотрим внимательно, что нам предлагает драйвер: в нашем случае, это вызовы IOCTL, да еще и простые и понятные. Это просто отлично! Я написал single-header библиотеку минут за 10: без проверки ошибок, но работоспособная.
void gpioInit();
void gpioSetDir(int num, byte dir);
byte gpioGetDir(int num);
void gpioWrite(int num, byte value);
byte gpioGetState(int num);
byte gpioRead(int num);
void gpioSetPullState(int num, byte enabled, byte up);
Пример использования (141 — крайний пин на гребенке):
#define GPIO_IMPL
#include "gpio.h"
#include <stdio.h>
void testPin(int pin)
{
printf("Pin state %i is %i\n", pin, gpioGetState(pin));
gpioSetDir(pin, 1);
gpioWrite(pin, 0);
printf("Pin state %i is %i\n", pin, gpioGetState(pin));
gpioWrite(pin, 1);
printf("Pin state %i is %i\n", pin, gpioGetState(pin));
}
int main(int argc, char** argv)
{
gpioInit();
testPin(141);
}
![image](https://habrastorage.org/getpro/habr/post_images/004/fd4/d75/004fd4d75127c6db1115dad3cbf9f889.png)
Модем
Скажу сразу: пока что завести модем мне не удалось, но я активно работаю над этим. В этой части статьи я распишу свои находки и догадки касательно модемов на чипах MediaTek.
В устройствах MediaTek, драйвер для общения с GPS, A-GPS и модемом один — ccci, судя по всему cross chip communication interface. Именно ccci создает устройства, с в которые поступает вход с микрофона и выход на динамики, а также он создает управляющие интерфейсы для общения с различными модулями этого SoC.
При старте ядра, ccci создаёт много устройств — ccci_ioctl, ccci_ipc, ccci_fs и самое нужное нам — ttyC0/ttyC1/ttyC2 — в зависимости от количества СИМ-карт в системе. Кроме ccci, в системе есть некий 6620_launcher — бинарник, который загружает прошивку Wi-Fi и gsm0710muxd — специальный сервис, который позволяет в GPRS-сетях одновременно разговаривать и сидеть в интернете.
На смартфонах MTK есть factory mode — так называемый тестовый режим, который гоняют на заводах. Вы, вероятно, когда-то видели китайские меню похожее на рекавери — это и есть factory mode. Из этого режима можно дозвонится в 911 и активировать модем без запуска Android и RIL. Как это работает? Идём читать исходники ядра!
В factory-режиме, для каждого теста, программа активирует модем заново. Для этого есть функции тестового режима для работы с AT-командами и для инициализации модема. Сначала, она открывает терминал /dev/ttyC0 — именно там происходит общение с модемом с помощью AT-команд:
![image](https://habrastorage.org/getpro/habr/post_images/8c7/16b/be2/8c716bbe227ecb60bf3490331181c489.png)
После этого, программа выводит модем из режима сна с помощью команды «AT+ESLP=0», инициализирует СИМ-карту с помощью команды «AT+ESIMS» и задает режим работы с помощью «AT+EFUN=1» и «AT+CREG=1». После этого, модем начинает искать сеть и доступен для обычного общения с помощью AT-команд. Однако, написав тестовую софтину для общения с модемом из под Debian, я получал ошибки вида Device not found. Почему? Пока не знаю. Однако я продолжаю изучать данный вопрос!
Заключение
Подготовленные мною файлы вы можете скачать на диске. Там скомпилированные модули ядра, библиотека для работы с GPIO и пару тестовых программ в качестве примеров.
К счастью, довести гаджет до ума мы смогли своими силами. Весьма странно, что такой крупный и уважаемый производитель как Orange Pi, банально решил «забить» на поддержку собственного устройства. И я лично считаю, что не стоит закидывать в долгий ящик их тем читателям, которые купили когда-то себе подобный девайс и забили, смирившись с отсутствием гайдов.
Немного энтузиазма, опыта и видения будущего проекта — и все получится :)