みつきんのメモ

組み込みエンジニアです。Interface誌で「Yocto Projectではじめる 組み込みLinux開発入門」連載中

yocto pulseaudio.socketをレシピからenableする

pulseaudioのUnitはUser Unitであるが、yoctoのUser Unitの取り扱いに問題がありboot時に正しく実行されない。 boot時にpulseaudio.socketが実行されるようにするための方法を調査したのでメモ。

systemdのenableの仕掛け

まず、「systemctl enable」をした時に何が起こるかを見てみる。

System Unitの場合

$ systemctl enable dropbear.socket
Created symlink from /etc/systemd/system/sockets.target.wants/dropbear.socket to /lib/systemd/system/dropbear.socket.

/etc/systemd/system/sockets.target.wants/dropbear.socketに/lib/systemd/system/dropbear.socketのシンボリックリンクが作成される。

つまり下記と同じになる。

$ ln -s /lib/systemd/system/dropbear.socket /etc/systemd/system/sockets.target.wants/dropbear.socket

さらに、Unitの状態をenabledに変更している。この情報がどこに存在するかは今回は見つけられなかった。 シンボリックリンクをはっただけの状態は、linkedとなる。

User Unitの場合

$ systemctl --user enable pulseaudio.socket
Created symlink from /home/root/.config/systemd/user/sockets.target.wants/pulseaudio.socket to /usr/lib/systemd/user/pulseaudio.socket.

こちらも/home/root/.config/systemd/user/sockets.target.wants/pulseaudio.socketに/usr/lib/systemd/user/pulseaudio.socketのシンボリックリンクが作成される。

$ ln -s /usr/lib/systemd/user/pulseaudio.socket /home/root/.config/systemd/user/sockets.target.wants/pulseaudio.socket

こちらも、Unitの状態をenabledに変更する。

つまり、Unit状態がenabledであり、XXXX.target.wants以下にUnit Fileのシンボリックファイルがあれば、自動実行されるようになっている。 どちらか片方だけでは自動実行は正しく動作しない。

Unit fileの格納場所

System UnitとUser Unitはそれぞれ下記の場所に格納されている。

種別 格納場所
System Unit /etc/systemd/system
User Unit /usr/lib/systemd/user

enable用のシンボリックリンクの格納場所

下記のディレクトリの下に「XXXX.target.wants」というディレクトリがあり、その中に格納されている。

種別 格納場所
System Unit /etc/systemd/system
User Unit ${HOME}/.config/systemd/user

Unit種別がsocketの場合は「socket.target.wants」になるようだが、 serviceの場合は「bluetooth.target.wants」や「multi-user.target.wants」など命名規則が見つけられなかった。

しかし、基本的にはsystemctlコマンドを使用するため、自身が気にする必要があるケースは非常に少ないだろう。

レシピでenableを行う

ここにこんな書き込みがあった。

Sure, you can manually create the link as you're told in this answer. Though, the better way would be to use the systemd.bbclass. You'd do this by inherit systemd in your recipe, and then specify the name of the service file in SYSTEMD_SERVICE = "name_of_the_file". This will automatically enable the service for you.

つまり下記の様にすると、自動的にenableされた状態になる。

inherit systemd

SYSTEMD_SERVICE = "name_of_the_file"

ただこれは、User Unitではうまく動作しない。 pulseaudio.incで下記のようになっている。

SYSTEMD_SERVICE_${PN}-server = "pulseaudio.service"

しかし、実機ではpulseaudio.serviceは実行されない。

$ systemctl --user status pulseaudio.service
● pulseaudio.service - Sound Service
   Loaded: loaded (/usr/lib/systemd/user/pulseaudio.service; enabled; vendor preset: enabled)
   Active: inactive (dead)

enableはされているが、実際にはpulseaudio.serviceは起動されていない。 systemd.bbclassの実装が、User Unitに関してはうまくケアできていない状態のようだ。

対処方法

根本の原因はsystemd.bbclass及びsystemd-systemctl-nativeのsystemctlがUser Unitをきちんと取り扱っていない事のようなので、 汎用性のある対処はビルドシステムを修正しないと難しい。

今回はpulseaudio.serviceに絞って対処する。

meta-local-systemd/recipes-multimedia/pulseaudio/pulseaudio_%.bbappend

FILES_${PN} += "${ROOT_HOME}/.config/systemd/user/*"
SYSTEMD_SERVICE_${PN}-server = "pulseaudio.socket"

do_install_append() {
    install -d ${D}${ROOT_HOME}/.config/systemd/user/sockets.target.wants
    ln -sf ${D}/${systemd_user_unitdir}/pulseaudio.socket \
       ${D}${ROOT_HOME}/.config/systemd/user/sockets.target.wants/pulseaudio.socket
}

現在は、pulseaudio.serviceが中途半端な状態でenableされているが、 このbbappendにより、pulseaudio.serviceのenableは行わず、 pulseaudio.socketをenableし、必要なシンボリックリンクの作成を行う。

raspberrypi2 yocto systemdでpulseaudio

yoctoのpulseaudioをsystemdで動かしてみた。

今回はレシピを作成したりはしないので若干手順は端折っている。

pulseaudioのインストール

pulseaudioを追加するためには、local.confに下記を追加する。

DISTRO_FEATURES_append = " pulseaudio pam"
IMAGE_INSTALL_append = " pulseaudio pulseaudio-server"

pulseaudioだけじゃ何も入らないので注意。 さりげにDISTRO_FEATURESにpamを追加していることも重要。

また、DISTRO_FEATURESにはすでにsystemdが追加してあると想定。

fidoのpulseaudioのレシピにはsystemd対応が不完全で、 意図したとおりに動作するためにはレシピを修正する必要がある。

しかし、masterやjethroのレシピは必要な修正はすでに入っているため、 作業はmasterかjethroで行うこととする。

User Unitについて

systemdのunitにはシステム全体に一つのインスタンスが実行されるSystem Unitと ユーザーごとにインスタンスが実行されるUser Unitがある。

pulseaudioはUser Unitとなるため、systemctlでの操作には--userオプションが必要になる。

$ systemctl --user status pulseaudio

systemctl --userでエラー

User Unitを動作させるには、pamの機能が有効化されている必要があるため、先述したように DISTRO_FEATURESにpamを追加しておく必要がある。これを忘れると下記のようなエラーに悩まされる事になる。

$ systemctl --user
Failed to get D-Bus connection: No such file or directory

また、sshなどリモートのシェルでも同様のエラーが出るので注意。 ターゲット直接かシリアルコンソールで操作する必要がある。

Socket Activation

systemdには、サービスにアクセスされた時に初めてデーモンを起動する、「Socket Activation」という機能がある。

これは、昔あったinetdスーパーデーモンの動作に似ている。

pulseaudioはSocket Activationに対応している。これを使用するためには下記のようにする。

$ systemctl start --user pulseaudio.socket
$ systemctl enable --user pulseaudio.socket

一時的に試すだけならstartのみで良い。

この時点では、pulseaudioは起動されていないが、pulseaudioに接続しようとするアプリケーションが実行された時点でpulseaudioが起動される。

pulseaudioの動作確認

alsaをpulseaudio経由で再生するように設定して、aplayでwavファイルを再生してみる。

alsa-utilsのインストール

aplayを使用するためにalsa-utilsをインストールする。 local.confに下記を追加する。

IMAGE_INSTALL_append = " alsa-utils"

asound.confの作成

ターゲット側で「/etc/asound.conf」を下記の内容で作成する

pcm.pulse {
    type pulse
}

ctl.pulse {
    type pulse
}

pcm.!default {
    type pulse
}
ctl.!default {
    type pulse
}

aplayでファイル再生

pulseaudioの設定が有効になっているか下記のコマンドで確認する。

$ aplay -L
...
pulse
    PulseAudio Sound Server
...

aplayの前後でpsコマンドでプロセスを確認し、aplayの後でpulseaudioが起動していれば、 Socket Activationは成功。

$ ps
  PID USER       VSZ STAT COMMAND
...
$ aplay -D pulse test.wav
$ ps
  PID USER       VSZ STAT COMMAND
...
  311 root      157m S <  /usr/bin/pulseaudio --daemonize=no

ちなみに、sshd(dropbear)もSocket Activationになっているため、 リモートから接続要求が来て初めて起動される。

raspberrypi2 yoctoでsystemd

RPi2でpulseaudioを導入しようとしたところ、pulseaudioをサービスとして使用するには、systemdを利用するほうが従来のsysvinitよりも簡単に行えそうだということがわかった。 ただ単に、初期化スクリプトがsystemd用のもののみ用意されていたというだけの事なのだが。

勉強も兼ねてyoctoでsystemdを導入する。

SysVinitとsystemd

Linuxにおいてユーザーランドのシステム初期化は昔からSysVinitが利用されてきた。 しかし比較的新し目のシステムではsystemdが導入され、大きな流れで見るとsystemdに移行しつつあるようにも思える。

SysVinitとsystemdについてはここが解りやすいと思う。

ざっくり言うと以下のような点でsystemdに利点がある。

  • 各サービスについて、より細かい制御ができる
  • システム起動時の処理を並列化しやすいため、起動時間を短縮できる
  • サービスをオンデマンド化できる

systemdを実際に使用してみるにはここがわかりやすい。

systemdの有効化

yoctoのdev-manualによると、local.confに下記を追加すると、systemdが有効になるらしい。

DISTRO_FEATURES_append = " systemd"
VIRTUAL-RUNTIME_init_manager = "systemd"
DISTRO_FEATURES_BACKFILL_CONSIDERED = "sysvinit"
VIRTUAL-RUNTIME_initscripts = ""

dhcpについて

IPアドレスを自動取得する場合、busyboxのudhcpcかdhclientを使用することになるが、 pokyの起動シーケンスではnetwork managerによってどちらかのdhcp-clientが自動的に起動されるため udhcpc、dhclientともに自動的に起動しないようになっている。 具体的には、sysvinitの場合は起動スクリプトが/etc/init.dに置かれないようになっているし、systemdの場合はunitがmaskされている。

systemdに切り替えるとsysvinitでは自動的に起動していたeth0が起動しなくなるので、 それを回避するためには、別途ネットワークの設定を行う必要がある。

※connmanの記事は移動しました

PiTFT3.5でQML(Quick2)アプリケーションが実行できない理由

RPi2にPiTFT3.5を実装したyocto環境でQt5を組み込みQt Quick2のQMLアプリケーションを実行してみたところエラーがでて実行できなかった。 その原因を調査したのでメモ。

原因はOpenGL

Qt Quick2はOpenGLに依存している

Qt Quick2はOpenGLに依存しており、OpenGLが使用できない環境では実行することができない。 OpenGLが無い状況下でQMLアプリケーションを実行すると下記のエラーが発生する。

QtQuick: failed to create OpenGL context

PiTFT3.5ではOpenGLは使用できない

RPi2ではOpenGLGPUで描画するために、高速なI/Fで接続されている必要があるが、 PiTFT3.5はGPIOヘッダ上のSPIにより接続されているので、この要件が満たせない。

2.2インチや2.8インチのPiTFTではGPUを使用せずにOpenGLを描画することができるが、この仕組みを使用しても、3.5インチのPiTFTでは転送するピクセル数が多く、リフレッシュが間に合わないため実用に耐えないとのこと。

回避策はないか

結果的にはどれもうまくいかなかった。 ソフトウェアの方でQMLのレンダリングOpenGL使用しない手段がいくつか考えられたが、どれも使用できなかった。

Qt Quick 2D Renderer

QMLの描画処理の中でOpenGLに依存する部分をソフトウェアでエミュレーションする、OpenGLが使えない環境のためのプラグイン。 組み込み環境などを想定している。

これは有償機能なので、今回は試していない。

Mesa OpenGL backend

platformをxcbに限定して、Mesaの機能でソフトウェアでの描画を試みる。

LIBGL_ALWAYS_SOFTWARE

LibGLが使用する環境変数で、この変数がセットされている場合は常にソフトウェアでレンダリングする様になる。

Mesaが持っているソフトウェアレンダラは下記のものがある。

  • llvmpipe - LLVMを使用しx86向けのJITコードをマルチスレッドで生成する。
  • softpipe - Galliumドライバのリファレンス。
  • swrast - Mesaのレガシーかつオリジナルのソフトウェアラスタライザ。

meta-raspberrypi環境で使用されるmesa-glにはswrastが含まれている。しかし、platform xcbとして、LIBGL_ALWAYS_SOFTWAREをセットしても「QtQuick: failed to create OpenGL context」は解消されなかった。

Mesaの機能についての詳細は下記を参照。

QT_XCB_FORCE_SOFTWARE_OPENGL

LIBGL_ALWAYS_SOFTWAREの影響をQtアプリのみ限定するための環境変数。 QT_XCB_FORCE_SOFTWARE_OPENGLがセットされていると、Qtアプリケーション内部で自動的に LIBGL_ALWAYS_SOFTWAREが有効化される。 結局はLIBGL_ALWAYS_SOFTWAREが効果なしだったためこちらも効果なし。

Ubuntu 15.10でbitbake qemu-nativeエラー

Ubuntu 15.10でcore-image-satoなどを作ろうとすると、qemu-nativeのdo_configureでエラーになる。

DEBUG: Executing python function sysroot_cleansstate
DEBUG: Python function sysroot_cleansstate finished
DEBUG: Executing shell function autotools_preconfigure
DEBUG: Shell function autotools_preconfigure finished
DEBUG: Executing python function autotools_copy_aclocals
DEBUG: Python function autotools_copy_aclocals finished
DEBUG: Executing shell function do_configure

ERROR: User requested feature sdl
       configure was not able to find it.
       Install SDL devel

WARNING: exit code 1 from a shell command.

config.log

configureでのエラーの詳細をみるためconfig.logを開いてみるとdbus関連の関数が見つからないという。

/usr/lib/x86_64-linux-gnu/pulseaudio/libpulsecommon-6.0.so: undefined reference to `dbus_watch_get_enabled@LIBDBUS_1_3'
/usr/lib/x86_64-linux-gnu/pulseaudio/libpulsecommon-6.0.so: undefined reference to `dbus_message_iter_recurse@LIBDBUS_1_3'
/usr/lib/x86_64-linux-gnu/pulseaudio/libpulsecommon-6.0.so: undefined reference to `dbus_connection_unref@LIBDBUS_1_3'
...(省略)...

原因

nativeではない方のqemuのbitbakeは問題なく通る。

Ubuntudbusのバージョンが1.8から1.10に上がったことで、DBusのABIに変更があったことが問題らしい。

dbusのレシピのバージョンを上げる

yocto環境のdbusのレシピを1.8.20から1.10.2に変更することで対応できるらしい。

すでにpatchは考案され、議論が進行中なので、メインラインに取り込まれるのは時間の問題だが、今すぐ試したいというせっかちな人のために一応人柱をやっておく。

この対策について

https://bugzilla.yoctoproject.org/show_bug.cgi?id=8553

ここによると、dbusのバージョンを上げる対策はリジェクトされたらしい。 dbusのバージョンを上げることでエラーを回避できるのはわかっているのだが、 他のディストリビューション環境では破綻すること、リリース前にdbusのバージョンを上げるのはデンジャラスだということが理由らしい。

どの道、このトピックについては有効期限付きのものだと思っていてほしい。

環境

作業環境はjethroのブランチをベースにする。

パッチのダウンロード及び取り込み

念の為、作業用に「dbus-patch-test」というブランチを作成し、下記の要領でパッチを取り込む。

$ cd ~/rpi2/poky
$ wget http://patchwork.openembedded.org/patch/106579/raw -O dbus-upgrade-to-1.10.2.patch
$ git checkout -b dbus-patch-test
$ git apply dbus-upgrade-to-1.10.2.patch

bitbake

エラーが発生していたqemu-nativeをbitbakeしてみる。

$ cd ~/rpi2
$ source poky/oe-init-build-env build
$ bitbake qemu-native

問題なく作成できた。

Build Configuration:
BB_VERSION        = "1.28.0"
BUILD_SYS         = "x86_64-linux"
NATIVELSBSTRING   = "Ubuntu-15.10"
TARGET_SYS        = "arm-poky-linux-gnueabi"
MACHINE           = "raspberrypi2"
DISTRO            = "poky"
DISTRO_VERSION    = "2.0"
TUNE_FEATURES     = "arm armv7a vfp thumb neon callconvention-hard vfpv4 cortexa7"
TARGET_FPU        = "vfp-vfpv4-neon"
meta
meta-yocto
meta-yocto-bsp    = "dbus-patch-test:fc45deac89ef63ca1c44e763c38ced7dfd72cbe1"
meta-raspberrypi  = "master:0776b86c6629b7294ff61e67609f2d4e10e9712c"

NOTE: Preparing RunQueue
NOTE: Executing SetScene Tasks
NOTE: Executing RunQueue Tasks
NOTE: Tasks Summary: Attempted 289 tasks of which 277 didn't need to be rerun and all succeeded.

raspberrypi2 yocto2.0(Jethro)

yoctoの時期リリース版では、gccのバージョンが4から5に上がるなど大きな変更が入っている。 それに伴ってバージョンも1.9をスキップするようだ。 gitの方でもJethroの名でブランチが切られ、開発が進んでいる様子。

meta-raspberrypiでも、gcc5でのビルドに対応するなどjethroと組み合わせてビルドすることが可能になっている。 カーネルのバージョンも3.18から4.1に上がっている。

まだ、開発中のステータスだが試してみることにした。

Jethroブランチの取得

ベース環境を取得

ベース環境を以下の要領で取得。

$ git clone git://git.yoctoproject.org/poky.git
$ cd poky
$ git checkout -b jethro origin/jethro
$ git clone git://git.yoctoproject.org/meta-raspberrypi

環境変数を設定

$ source poky/oe-init-build-env

bblayers.conf

bblayers.confを下記のように修正

BBLAYERS ?= " \
  /home/mickey/rpi2/poky/meta \
  /home/mickey/rpi2/poky/meta-yocto \
  /home/mickey/rpi2/poky/meta-yocto-bsp \
  /home/mickey/rpi2/poky/meta-raspberrypi \
  "

local.conf

local.confに下記を追加。

MACHINE ?= "raspberrypi2"
BB_NUMBER_THREADS = "6"
PARALLEL_MAKE = "-j 6"
VIDEO_CAMERA = "1"
GPU_MEM = "128"
DL_DIR ?= "${TOPDIR}/../downloads"

FETCHCMD_wget = "/usr/bin/env wget -t 2 -T 6000 -nv --passive-ftp --no-check-certificate"

bitbakeおよび書き込み

下記でイメージをビルド

$ bitbake rpi-basic-image

下記でイメージを書き込み

$ sudo dd if=./tmp/deploy/images/raspberrypi2/rpi-basic-image-raspberrypi2.rpi-sdimg of=/dev/sdb bs=40M

sdbは環境によりsdcなどに変化する可能性がある。

動作確認

ディストロもPoky 2.0になっている。

$ cat /etc/issue
Poky (Yocto Project Reference Distro) 2.0 \n \l

meta-raspberypiのカーネルも4.1になっている。

$ root@raspberrypi2:~# uname -a
Linux raspberrypi2 4.1.10 #1 SMP PREEMPT Mon Nov 2 23:32:02 JST 2015 armv7l GNU/Linux

raspberrypi2 yoctoでPiTFT3.5 (X11編)

以前にyocto + PiTFT3.5でコンソールを動作させたが、今回はcore-image-satoでX11ベースのGUIを動作させたのでメモ。

meta-pitft35-rpiについて

以前作成したmeta-pitft35-rpiをcore-image-satoに対応し、pitft3.5を装着した状態でこのイメージを書き込むと、自動的にX11が起動するようにした。 その際、タッチスクリーンも有効化されるので、マウスやキーボードなどを接続しなくてもRPi2を操作できるようになる。

このレイヤはHX8357Dサポートのパッチを、meta-raspberrypiのカーネルへバックポートして、dtoverlayなど、必要な設定をしたもの。

環境の作成

meta-pitft35-rpiを使用した環境の作成を記載する。

yoctoの基本環境およびmeta-raspberrypi

まいどお馴染みの基本環境のダウンロード

$ mkdir ~/work/rpi2
$ cd ~/work/rpi2
$ git clone git://git.yoctoproject.org/poky.git
$ git checkout -b fido origin/fido
$ cd ~/work/rpi2/poky
$ git clone git://git.yoctoproject.org/meta-raspberrypi

meta-pitft35-rpi

今回作成したレイヤのダウンロード

$ cd ~/work/rpi2/poky
$ git clone https://github.com/mickey-happygolucky/meta-pitft35-rpi.git

ビルド環境の設定

ビルドのための環境変数の設定とビルドディレクトリの作成。 このコマンド実行後は自動的にビルドディレクトリに移動される。

$ cd ~/work/rpi2
$ source poky/oe-init-build-env build_pitft35

今回はビルドディレクトリを、build_pitft35とする。

conf/bblayers.conf

レイヤーの設定。

BBLAYERS ?= " \
  /home/mickey/work/rpi2/poky/meta \
  /home/mickey/work/rpi2/poky/meta-yocto \
  /home/mickey/work/rpi2/poky/meta-yocto-bsp \
  /home/mickey/work/rpi2/poky/meta-raspberrypi \
  /home/mickey/work/rpi2/poky/meta-pitft35-rpi \
  "

今回は、meta-raspberrypiとmeta-pitft35-rpiを追加している。

conf/local.conf

MACHINE ?= "raspberrypi2"
BB_NUMBER_THREADS = "6"
PARALLEL_MAKE = "-j 6"
VIDEO_CAMERA = "1"
GPU_MEM = "128"
LICENSE_FLAGS_WHITELIST += "commercial"
DL_DIR ?= "${TOPDIR}/../downloads"

KERNEL_DEVICETREE_append = " pitft35-resistive-overlay.dtb"

RPI_PITFT35 = "1"

PREFERRED_PROVIDER_virtual/egl = "vc-graphics-hardfp"
PREFERRED_PROVIDER_virtual/libgles2 = "vc-graphics-hardfp"

「KERNEL_DEVICETREE_append = " pitft35-resistive-overlay.dtb"」で、 pitft35のdtoverlayをビルド対象に含め、 「RPI_PITFT35 = "1"」でこれを有効化している。

「PREFERRED_PROVIDER_virtual/egl」と「PREFERRED_PROVIDER_virtual/libgles2」の行は、meta-raspberrypiでcore-image-satoをbitbakeする際に発生するエラーを回避している。

動作確認

ビルド

今回はX11を動作させるためにcore-image-satoをビルドする。

$ cd ~/work/rpi2/build_pitft35
$ bitbake core-image-sato

焼きこみ

$ sudo dd if=./tmp/deploy/images/raspberrypi2/core-image-sato-raspberrypi2.rpi-sdimg of=/dev/sdb bs=40M

RPi2での確認

PiTFT3.5を装着して起動すると、しばらくしてGUIが表示される。 タッチパネルで操作することができる。

f:id:mickey_happygolucky:20151006021201j:plain