Не все USB-устрой­ства начина­ют работать на Android сра­зу пос­ле под­клю­чения. В этой статье я покажу, как решить проб­лему, на при­мере широко рас­простра­нен­ного Bluetooth-адап­тера CSR 4.0. Нам понадо­бит­ся соб­рать из исходни­ков ядро Linux и покопать­ся в интерфей­сной биб­лиоте­ке Android.

Сов­ремен­ное железо ред­ко пред­полага­ет что‑то доделы­вать руками или допили­вать в про­шив­ках. Одна­ко есть целый класс устрой­ств «ноунейм», которые прос­то‑таки взы­вают к уме­лым рукам и пыт­ливым умам. Нем­ного сме­кал­ки, и мож­но зас­тавить их работать не хуже име­нитых ана­логов.

Вот, нап­ример, ничем не при­меча­тель­ная ТВ‑прис­тавка в виде чер­ной квад­ратной плас­тиковой короб­ки с над­писью «MXQ Pro 4K» на крыш­ке и бюд­жетной начин­кой: SOC RK3229, 1 Гбайт RAM и 8 Гбайт NAND. У меня она уже око­ло года поч­ти бес­перебой­но тран­сли­рова­ла на телеви­зор YouTube и каналы интернет‑телеви­дения, музици­рова­ла и сооб­щала све­жие новос­ти.

В самом начале ее муль­тимедий­ной жиз­ни харак­тер у прис­тавки был строп­тивый, и при каж­дом удоб­ном слу­чае она норови­ла завис­нуть или перезаг­рузить­ся. Эту проб­лему уда­лось решить, уста­новив сгла­жива­ющий филь­тр в цепи питания и заменив ком­плектный блок питания более качес­твен­ным.

В какой‑то момент я подумал, не исполь­зовать ли мне эту прис­тавку еще и для игр. Бес­про­вод­ной кон­трол­лер подошел бы для это­го как нель­зя луч­ше.

В целом кон­трол­леры мож­но раз­делить на две катего­рии: RF (Radio Frequency, ради­очас­тотные) и BT (Bluetooth), оба в ито­ге работа­ют по ради­ока­налу. Вари­ант с RF тре­бует исполь­зовать проп­риетар­ный донгл (и занимать им порт USB), а Bluetooth дол­жен под­держи­вать­ся на сто­роне прис­тавки.

За­то Bluetooth уни­вер­сален и, помимо под­клю­чения устрой­ств вво­да, может быть исполь­зован для вывода зву­ка на бес­про­вод­ные колон­ки, фай­лового обме­на со смар­тфо­ном и так далее. Мой выбор был пре­доп­ределен тем, что в бли­жай­шем магази­не нашел­ся лишь BT-кон­трол­лер VR-PARK. Не обра­зец изящ­ного дизай­на, но не любовать­ся же на него!

К сожале­нию, на пла­те ТВ‑прис­тавки отсутс­тву­ет аппа­рат­ный BT-модуль, одна­ко в нас­трой­ках ее опе­раци­онной сис­темы Android 7.1.2 я видел пункт вклю­чения Bluetooth, что вро­де бы поз­воляло наде­ять­ся на под­дер­жку сте­ка про­токо­лов. Воз­можно, пос­ле под­клю­чения модуля Bluetooth в USB сра­бота­ет магия и сра­зу пред­ста­вит­ся воз­можность нас­ладить­ся игрой?

С эти­ми мыс­лями я при­купил USB-адап­тер Bluetooth с лаконич­ной над­писью «CSR 4.0» на кор­пусе. Вер­нувшись домой, я вста­вил его в порт прис­тавки, и тот весело замигал све­тоди­одом. А пос­ле вклю­чения Bluetooth в нас­трой­ках... ничего не про­изош­ло. То есть поч­ти ничего, если не счи­тать воз­никно­вения задачи, решению которой пос­вящена эта статья: нас­тро­ить ТВ‑прис­тавку на исполь­зование адап­тера USB Bluetooth.

Ког­да воз­ника­ет проб­лема, иссле­дова­тель пер­вым делом идет в интернет, что­бы соб­рать све­дения о ней. На удив­ление, матери­алов ока­залось нем­ного. То ли ник­то не под­клю­чал USB-адап­тер к прис­тавке, то ли у всех он работал из короб­ки, но пос­ледние содер­жатель­ные обсужде­ния датиро­вались 2013 годом и отно­сились к авто­мобиль­ной сис­теме муль­тимедиа.

Ре­зуль­тат бес­систем­ных попыток отыс­кать работа­ющие нас­трой­ки не порадо­вал: я толь­ко потерял нес­коль­ко часов и осоз­нал, что при­дет­ся искать собс­твен­ный путь. А для это­го необ­ходимо понимать, как под­систе­ма Bluetooth встро­ена в Android.

 

Архитектура подсистемы Bluetooth в Android

С точ­ки зре­ния прог­раммис­та, цен­траль­ный ком­понент под­систе­мы Bluetooth в Android — сис­темная служ­ба Bluetooth (пред­став­ляющая собой при­ложе­ние для Android), которая через Binder IPC дает при­ложе­ниям дос­туп к услу­гам Bluetooth-про­филей, а сама исполь­зует при этом JNI-интерфей­сы, реали­зован­ные на уров­не HAL и ведущие к ком­понен­там сте­ка Bluetooth и рас­ширени­ям пос­тавщи­ка (в дво­ичных биб­лиоте­ках).

Архитектура подсистемы Bluetooth в Android 7
Ар­хитек­тура под­систе­мы Bluetooth в Android 7

Эту схе­му мож­но про­дол­жить вниз по сис­темной иерар­хии, и тог­да ста­нет вид­но, что биб­лиоте­ки HAL исполь­зуют интерфейс hci0 для дос­тупа к HCI-сокетам ядра опе­раци­онной сис­темы, которые через драй­вер поз­воля­ют вза­имо­дей­ство­вать с аппа­рат­ным Bluetooth-модулем, под­клю­чен­ным к сис­темной пла­те шиной SDIO, UART или USB.

Нижний уровень подсистемы Bluetooth в Android
Ниж­ний уро­вень под­систе­мы Bluetooth в Android

Ос­талось выяс­нить, на каком имен­но уров­не у нас проб­лема, которая меша­ет адап­теру взять и зарабо­тать.

Опе­раци­онная сис­тема Android — это ядро Linux, поверх которо­го работа­ет гипер­визор вир­туаль­ных машин Dalvik. Начинать ана­лиз разум­но с ниж­него уров­ня, пос­ледова­тель­но убеж­даясь в том, что есть физичес­кое под­клю­чение, опе­раци­онная сис­тема и драй­вер видят адап­тер, он дос­тупен на уров­не HAL и так далее — до момен­та, ког­да он дол­жен работать.

Что­бы иссле­довать ОС и вно­сить в нее изме­нения, нужен дос­туп к коман­дной обо­лоч­ке с пра­вами супер­поль­зовате­ля. Об осно­вах исполь­зования кон­соли Android мож­но про­читать в статье «Кон­соль­ный Android».

warning

Вме­шатель­ство в опе­раци­онную сис­тему с пра­вами супер­поль­зовате­ля может нарушить работос­пособ­ность устрой­ства и вывес­ти его из строя. При­нимая решение выпол­нить опи­сан­ные в статье рекомен­дации, иссле­дова­тель берет на себя пол­ную ответс­твен­ность за воз­можные негатив­ные пос­ледс­твия сво­их дей­ствий.

 

Рекогносцировка

В сис­темных сооб­щени­ях dmesg упо­мина­ния о Bluetooth встре­чают­ся в сле­дующих кон­тек­стах:

[1] Bluetooth: Core ver 2.16
[2] NET: Registered protocol family 31
[3] Bluetooth: HCI device and connection manager initialized
[4] Bluetooth: HCI socket layer initialized
[5] Bluetooth: L2CAP socket layer initialized
[6] Bluetooth: SCO socket layer initialized
. . .
[7] Bluetooth: HCI UART driver ver 2.2
[8] Bluetooth: HCI H4 protocol initialized
[9] Bluetooth: HCILL protocol initialized
[10] rtk_btusb: RTKBT_RELEASE_NAME: 20170109_TV_ANDROID_6.x
[11] rtk_btusb: Realtek Bluetooth USB driver module init, version 4.1.2
[12] rtk_btusb: Register usb char device interface for BT driver
[13] usbcore: registered new interface driver rtk_btusb
. . .
[14] Bluetooth: RFCOMM TTY layer initialized
[15] Bluetooth: RFCOMM socket layer initialized
[16] Bluetooth: RFCOMM ver 1.11
[17] Bluetooth: BNEP (Ethernet Emulation) ver 1.3
[18] Bluetooth: BNEP filters: protocol multicast
[19] Bluetooth: BNEP socket layer initialized
[20] Bluetooth: HIDP (Human Interface Emulation) ver 1.2
[21] Bluetooth: HIDP socket layer initialized
. . .
[23] usb 5-1: New USB device found, idVendor=0a12, idProduct=0001
[24] usb 5-1: New USB device strings: Mfr=0, Product=2, SerialNumber=0
[25] usb 5-1: Product: CSR8510 A10

Стро­ка 1 говорит о том, что ядро Linux соб­рано с под­дер­жкой сте­ка Bluetooth, стро­ка 2 сооб­щает о регис­тра­ции в ядре семей­ства про­токо­лов Bluetooth. Сра­зу пос­ле это­го ини­циали­зиру­ются слои сокетов HCI, L2CAP и SCO (3–6).

За­тем акти­виру­ются драй­веры, пред­назна­чен­ные для вза­имо­дей­ствия с адап­терами Bluetooth, под­клю­чаемы­ми по шинам UART и USB (7–13). К сожале­нию, пос­ледний «понима­ет» толь­ко кон­трол­леры Realtek, а наш адап­тер CSR этим драй­вером не под­держи­вает­ся.

Пос­ледний блок сооб­щений (14–21) информи­рует об ини­циали­зации высоко­уров­невых про­токо­лов RFCOMM, BNEP и HIDP сте­ка Bluetooth. Стро­ки 23–25 соот­ветс­тву­ют реак­ции опе­раци­онной сис­темы на под­клю­чение USB-адап­тера и говорят о том, что на физичес­ком уров­не он опоз­нает­ся шиной USB.

Ин­терес­но, где же находит­ся драй­вер адап­тера? Коман­да lsmod выдала спи­сок из двух модулей, сре­ди которых rtk_btusb не ока­залось. Зна­чит, он интегри­рован в ядро Linux. Но спи­сок непус­той, а зна­чит, ядро соб­рано с под­дер­жкой модулей. Запом­ним это на будущее.

Я изу­чил фай­ловую сис­тему ТВ‑прис­тавки и нашел сле­дующие ком­понен­ты, име­ющие отно­шение к под­систе­ме Bluetooth:

/app/Bluetooth/Bluetooth.apk
/system/lib/libbluetooth_jni.so
/system/lib/hw/audio.a2dp.default.so
/system/lib/hw/bluetooth.default.so
/system/lib/hw/bluetooth_rtk.default.so
/system/lib/libbt-vendor_uart.so
/system/lib/libbt-vendor_usb.so
/system/lib/rtkbt/...
/system/vendor/libbt-vendor.so
/system/etc/bluetooth/bt_did.conf
/system/etc/bluetooth/bt_stack.conf
/system/etc/bluetooth/bt_vendor.conf
/system/etc/bluetooth/rtkbt.conf
/system/etc/bluetooth/rtkbt_plugins.conf

Сле­дующие коман­ды, пос­ледова­тель­но при­менен­ные к фай­лам биб­лиотек, помогут нам понять их зависи­мость друг от дру­га:

$ strings /system/lib/hw/название-библиотеки.so | grep '.so'
$ strings /system/lib/hw/название-библиотеки.so | grep '.conf'

До­бавив эти ком­понен­ты на схе­му, я получил сле­дующую кар­тину.

Взаимосвязь компонентов подсистемы Bluetooth в операционной системе исследуемой ТВ-приставки
Вза­имос­вязь ком­понен­тов под­систе­мы Bluetooth в опе­раци­онной сис­теме иссле­дуемой ТВ‑прис­тавки

Соб­ранной информа­ции дос­таточ­но, что­бы пред­положить, что драй­веры на ТВ‑прис­тавке не под­держи­вают Bluetooth-адап­теры на кон­трол­лерах CSR. Пос­коль­ку ядро име­ет под­дер­жку модулей, мож­но попытать­ся испра­вить дело, соб­рав соот­ветс­тву­ющий модуль с необ­ходимым драй­вером.

 

Модуль ядра с драйвером USB-адаптера

В качес­тве плат­формы для сбор­ки я исполь­зовал 64-бит­ную ОС Bodhi Linux 6.0.0 (осно­ван­ную на Ubuntu 20.04 LTS). У нее есть минима­лис­тичный уста­новоч­ный образ, в который вхо­дят инс­тру­мен­ты метапа­кета build essential. Для наших работ дос­таточ­но вир­туаль­ной машины с опе­ратив­ной памятью 1 Гбайт и накопи­телем 20 Гбайт. Пос­ле уста­нов­ки сис­темы и син­хро­низа­ции с репози­тори­ем надо уста­новить допол­нитель­ные пакеты unzip, python2 и libncurses5 и соз­дать сим­воличес­кую ссыл­ку на интер­пре­татор:

$ sudo ln -s /usr/bin/python2.7 /usr/bin/python

Соз­дадим каталог для работы:

$ mkdir $HOME/tvbox && cd $HOME/tvbox

Что­бы соб­рать модуль драй­вера, нуж­ны исходни­ки ядра Linux той вер­сии, которая исполь­зует­ся в опе­раци­онной сис­теме ТВ‑прис­тавки, в дан­ном слу­чае 3.10.104. К счастью, на GitHub быс­тро нашел­ся репози­торий с ядра­ми Linux, адап­тирован­ными для устрой­ств на плат­форме Rockchip. Забира­ем исходни­ки из него:

$ wget -C kernel-release-3.10.zip https://github.com/rockchip-linux/kernel/archive/refs/heads/release-3.10.zip

Те­перь надо рас­паковать получен­ный архив в рабочий каталог и про­верить, соот­ветс­тву­ет ли вер­сия ядра ожи­дани­ям:

$ cd kernel-release-3.10
$ head Makefile
VERSION = 3
PATCHLEVEL = 10
SUBLEVEL = 104

Что­бы пос­мотреть, какие кон­фигура­ции дос­тупны для плат­формы Rockchip, выпол­ним коман­ду

$ make help | grep rockchip
rockchip_chromium_defconfig - Build for rockchip_chromium
rockchip_defconfig - Build for rockchip

Ва­риант для опе­раци­онной сис­темы Chromium OS отбра­сыва­ем, оста­ется rockchip_defconfig. Ищем нуж­ный кон­фиг:

$ find ./arch -name 'rockchip_defconfig'
./arch/arm64/configs/rockchip_defconfig
./arch/arm/configs/rockchip_defconfig

В нашей прис­тавке сто­ит чип RK3229. Он 32-раз­рядный, поэто­му нам акту­ален вто­рой вари­ант. Для кон­фигури­рова­ния ядра, как написа­но в kernel-release-3.10/README и kernel-release-3.10/android/configs/README, надо выпол­нить коман­ды

$ ARCH=arm scripts/kconfig/merge_config.sh \
arch/arm/configs/rockchip_defconfig \
android/configs/android-base.cfg \
android/configs/android-recommended.cfg

Да­лее в толь­ко что соз­данный файл .config надо добавить стро­ку CONFIG_MODULES=y (пос­ле стро­ки CONFIG_MODULES is not set), что­бы вклю­чить под­дер­жку модулей ядром, а так­же стро­ку CONFIG_BT_HCIBTUSB=m (пос­ле стро­ки CONFIG_BT_HCIBTUSB is not set) — для сбор­ки драй­вера USB-адап­теров Bluetooth как модуля ядра. Пос­ле это­го выпол­ним коман­ду

$ ARCH=arm make olddefconfig

Ис­ходные тек­сты ядра скон­фигури­рова­ны и готовы к сбор­ке модуля. Собирать будем с помощью инс­тру­мен­тов Linaro. Их нуж­но извлечь из архи­ва gcc-linaro-4.9.4-2017.01-x86_64_arm-eabi.tar.xz в каталог tvbox, пос­ле чего из под­катало­га kernel-release-3.10 выпол­няем коман­ду

$ PATH=$HOME/tvbox/gcc-linaro-4.9.4-2017.01-x86_64_arm-eabi/bin:$PATH \
ARCH=arm CROSS_COMPILE=arm-eabi- make modules

Че­рез неп­родол­житель­ное вре­мя модуль ядра будет сфор­мирован в фай­ле kernel-release-3.10/drivers/bluetooth/btusb.ko. Этот файл надо записать на ТВ‑прис­тавку в каталог /system/lib/modules и поп­робовать запус­тить:

# insmod /system/lib/modules/btusb.ko
insmod: failed to load btusb.ko: Exec format error

Не­уда­ча: нам сооб­щили, что файл име­ет неп­равиль­ный фор­мат. Деталь­ное опи­сание проб­лемы мож­но най­ти в сооб­щени­ях ядра:

# dmesg | tail
btusb: version magic '3.10.104 SMP preempt ARMv7 p2v8 ' should be '3.10.104 SMP preempt mod_unload ARMv7 p2v8 '

Ока­зыва­ется, соб­ранная вер­сия не име­ет атри­бута mod_unload. При­дет­ся вер­нуть­ся к исходным тек­стам и в файл .config пос­ле стро­ки # CONFIG_MODULE_UNLOAD is not set добавить параметр CONFIG_MODULE_UNLOAD=y, а затем пов­торить сбор­ку:

$ make clean
$ make olddefconfig
$ make modules

За­писы­ваем новый модуль на ТВ‑прис­тавку и запус­каем:

# insmod /system/lib/modules/btusb.ko

Про­веря­ем резуль­тат:

# dmesg | tail
usbcore: registered new interface driver btusb

# lsmod
Module Size Used by
btusb 14685 0 [permanent]

Не­уже­ли получи­лось? Пока что все говорит о том, что модуль зарабо­тал. Для про­вер­ки вос­поль­зуем­ся коман­дой rfkill (она ста­новит­ся дос­тупной пос­ле уста­нов­ки BusyBox). Сна­чала надо выпол­нить коман­ду rfkill list без под­клю­чен­ного модуля и запом­нить ее вывод (если он не пус­той). Затем под­клю­чить модуль и пов­торить выпол­нение коман­ды:

# rfkill list
0: hci0: bluetooth
Soft blocked: no
Hard blocked: no

В выводе дол­жны появить­ся стро­ки, соот­ветс­тву­ющие под­клю­чен­ному адап­теру. Потом адап­тер надо отклю­чить и сно­ва выпол­нить rfkill list. Теперь стро­ки, соот­ветс­тву­ющие адап­теру, дол­жны исчезнуть из вывода. Толь­ко такое динамич­ное поведе­ние говорит о работос­пособ­ности драй­вера.

Аль­тер­натив­ный спо­соб про­вер­ки — наб­людение за содер­жимым катало­га /sys/class/rfkill. При под­клю­чении адап­тера Bluetooth в нем дол­жен соз­давать­ся под­каталог rfkillN (где N — номер устрой­ства), а при отклю­чении — исче­зать.

Из фай­ла /init.connectivity.rc, который содер­жит коман­ды, выпол­няемые при заг­рузке сис­темы для ини­циали­зации ком­муника­цион­ных устрой­ств, мож­но узнать, что фай­лам /sys/class/rfkill/rfkillN/{type,state} надо наз­начить вла­дель­ца bluetooth:net_bt_stack, а фай­лу state — еще и пра­ва дос­тупа 0664. Сде­лаем это и попыта­емся акти­виро­вать Bluetooth в нас­трой­ках Android. Увы, но бегунок эле­мен­та управле­ния сно­ва воз­вра­щает­ся в положе­ние «Выкл.».

На этом эта­пе воз­ника­ет соб­лазн раз­добыть ути­литы hciconfig и hcitool, что­бы про­дол­жить нас­тра­ивать под­систе­му Bluetooth над ядром Linux, но это тупико­вый путь. Наша задача — зас­тавить работать Bluetooth в Android. А для это­го надо при­нять, что задача обес­печить работу драй­вера на уров­не ядра выпол­нена, и обра­тить­ся к нашей схе­ме за сле­дующей целью. Ей ока­залась «биб­лиоте­ка про­изво­дите­ля» libbt-vendor.so — имен­но она дол­жна обес­печить интерфейс меж­ду драй­вером ядра и под­систе­мой Bluetooth в Android.

www

Для авто­мати­зации опи­сан­ной выше работы — получе­ния исходных тек­стов ядра, его нас­трой­ки и сбор­ки модуля — можешь вос­поль­зовать­ся моими сце­нари­ями на bash.

 

Библиотека взаимодействия с драйвером

С биб­лиоте­кой libbt-vendor.so дело ока­залось нес­коль­ко слож­нее, чем с драй­вером. На сай­те с исходни­ками Android обна­ружи­лись толь­ко матери­алы по биб­лиоте­кам для адап­теров на чипах Realtek и Broadcom. Единс­твен­ный все­ляющий надеж­ду репози­торий на GitHub с опи­сани­ем «A libbt-vendor.so for usb bluetooth on Android» датиро­ван сен­тябрем 2014 года и содер­жит единс­твен­ный ком­мит — форк какого‑то дав­но исчезнув­шего источни­ка.

Продолжение доступно только участникам

Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте

Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее

Вариант 2. Открой один материал

Заинтересовала статья, но нет возможности стать членом клуба «Xakep.ru»? Тогда этот вариант для тебя! Обрати внимание: этот способ подходит только для статей, опубликованных более двух месяцев назад.


  • Подпишись на наc в Telegram!

    Только важные новости и лучшие статьи

    Подписаться

  • Подписаться
    Уведомить о
    5 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии