外部記憶装置

脳みそ小さすぎるからメモしとく

Android-x86でLinux Kernelのドライバにパッチを当てた

この記事は、自作OS Advent Calendar 2018

adventar.org

の 12/20 の記事として書かれました。

はじめに

怒涛の2018年もあと残すところ10日ほどになりました。皆様いかがお過ごしでしょうか?

僕は10月から大学の講義が始まって以来、講義やサークルのロボット製作などが忙しく、自作OSの方はxv6をx86-64ネイティブに完全移植する作業を開始した事ぐらいしか進捗はありません。

今回の自作OS Advent Calendar 2018では、xv6を64bitUEFIで起動したことやNICのデバドラ・プロトコルスタックの実装をしたことを書いても良かったのですが、どれも中途半端な進展なので あえてアドカレに書くために新しくネタを見つけてきました。

Android-x86とは?

Android-x86とは、その名前の通りAndroidx86のプロセッサを積んだPCで動かそうというプロジェクトです。

www.android-x86.org

Android自体はオープンソースなプロジェクトでかつ、Linux Kernelを改造したものが用いられているため、 x86で動かすこと自体は困難ではないようです。 が、x86とはいっても主に使われるハードウェアがWindowsを搭載したタブレットや2in1ノートのような、 ハードウェア構成が特殊なものが多いため全ての機能を完動させることは難しいようです。

今回利用したタブレットも中国製のWindows搭載のタブレットCube iWork10(いわゆる中華Winタブ)で、タッチパネルのドライバに問題がありました。 折角の機会なので、今回はこのタッチパネルを正常に動作させるためのパッチを作成しました。

今回の環境

今回の環境は以下のとおりです。

  • ハード: Cube iWork10
  • OS: Android-x86 8.1-rc2
  • Kernel: Linux Kernel 4.18.14

問題の症状

今回発生したタッチパネルの問題とは、タッチ位置と認識される位置が反転していることです。 横方向をx,縦方向をyとすると、x座標が反転している感じです。

f:id:PiBVT:20181220144704p:plain
問題の症状

問題となるドライバの調査

まず、タッチパネルに使われているドライバ(kernel module)を探すことにします。

幸いなことに、Android-x86にはターミナルエミュレータがあり、各種コマンドでタッチパネルのドライバを調査しました。

f:id:PiBVT:20181220124959p:plain
lsmod
f:id:PiBVT:20181220124949p:plain
modinfo goodix
以上より、タッチパネルのドライバはgoodixであるようなので、goodix.cにパッチをあてることにします。

linux/drivers/input/touchscreen/goodix.cの140行目付近にある

/*
 * Those tablets have their coordinates origin at the bottom right
 * of the tablet, as if rotated 180 degrees
 */
static const struct dmi_system_id rotated_screen[] = {
#if defined(CONFIG_DMI) && defined(CONFIG_X86)
    {
        .ident = "WinBook TW100",
        .matches = {
            DMI_MATCH(DMI_SYS_VENDOR, "WinBook"),
            DMI_MATCH(DMI_PRODUCT_NAME, "TW100")
        }
    },
    {
        .ident = "WinBook TW700",
        .matches = {
            DMI_MATCH(DMI_SYS_VENDOR, "WinBook"),
            DMI_MATCH(DMI_PRODUCT_NAME, "TW700")
        },
    },
#endif
    {}
};

や、700行目付近の

if (dmi_check_system(rotated_screen)) {
        ts->prop.invert_x = true;
        ts->prop.invert_y = true;
        dev_dbg(&ts->client->dev,
            "Applying '180 degrees rotated screen' quirk\n");
}

あたりが参考になりそうです。これは、タッチパネルを180°回転させるための処理のようなので、この処理を参考にx座標だけを反転させる処理を追加することにします。

パッチの方針

以下の方針でパッチを適用することにしました。

  1. rotated_screenと同様にDMI(SMBIOS)を利用した端末認識を行う
  2. invert_x=true,invert_y=falseの処理をrotated_screenと同様の処理で行う。

DMI(SMBIOS)の確認

DMI(SMBIOS)はハードのベンダーや型番などの固有情報をもつ領域らしいです。

Linuxから確認する場合はdmidecodeが使えるようなのですが、Android-x86にはインストールされていなかったので、Windows 10側から確認することにします。

Windowsの場合は、DirectX診断ツール(dxdiag)から確認できます。 スタートメニューの検索欄で「dxdiag」と検索することで起動できます。

f:id:PiBVT:20181220125517p:plain
DirectX診断ツール
このように各種情報が取得できました。今回必要な情報である、製造元は「cube」,型番は「i15-T」であることが分かります。

パッチ作成

rotated_screenを参考に以下のパッチを作成しました。

diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
index 6a4ffe800194..4f646607e3f7 100644
--- a/drivers/input/touchscreen/goodix.c
+++ b/drivers/input/touchscreen/goodix.c
@@ -145,6 +145,18 @@ static const struct dmi_system_id rotated_screen[] = {
        {}
 };
 
+static const struct dmi_system_id x_inverted_screen[] = {
+#if defined(CONFIG_DMI) && defined(CONFIG_X86)
+       {
+               .ident = "cube i15-T",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "cube"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "i15-T")
+               }
+       },
+#endif
+       {}
+};

@@ -707,6 +719,11 @@ static int goodix_configure_dev(struct goodix_ts_data *ts)
                ts->prop.invert_y = true;
                dev_dbg(&ts->client->dev,
                        "Applying '180 degrees rotated screen' quirk\n");
+       }else if (dmi_check_system(x_inverted_screen)) {
+               ts->prop.invert_x = true;
+               ts->prop.invert_y = false;
+               dev_dbg(&ts->client->dev,
+                       "Applying 'x-axsis mirrored  screen' quirk\n");
        }
 
        error = input_mt_init_slots(ts->input_dev, ts->max_touch_num,

処理の内容としては、DMIの製造元(DMI_SYS_VENDOR)がcube,型番(DMI_PRODUCT_NAME)がi15-Tに一致する場合は、タッチスクリーンのx座標を反転するという内容です。

Android-x86をビルドする

いよいよ、パッチをあてたLinux Kernelを含めたAndroid-x86をビルドします。 詳細な手順は

www.android-x86.org

ここにあります。

ビルド環境の構築

まずは、ビルド環境を構築します。 大量のソースコードコンパイルする必要があるので、今回は逸般の誤家庭には必ず1台あるラックサーバーを使うことにしました。

  • ハード:VMWare ESXi on FUJITSU RX300S7
  • CPU: Xeon E5-2630相当 10コア
  • Memory:24GB
  • HDD:200GB *OS: Ubuntu 18.10

ソースコードは約40GBほどあるので、余裕をみて200GBほどHDDを確保しておくことをおすすめします。 ビルドにあたり、以下のパッケージをインストールする必要があります。

repo
git
build-essential
libncurses5
m4
curl
openjdk-8-jdk
bison
libc6:i386
libncurses5:i386
libstdc++6:i386
bison:i386
libssl-dev
python-mako
libxml2-utils
isohybrid

インストールします。

$ sudo dpkg --add-architecture i386
$ sudo apt update
$ sudo apt upgrade
$ sudo apt install repo git build-essential libncurses5 m4 curl openjdk-8-jdk bison libssl-dev python-mako libxml2-utils isohybrid
$ sudo apt install libc6:i386 libncurses5:i386 libstdc++6:i386 bison:i386

以上でビルド環境の構築は終了です。

ソースコードのダウンロード

公式の手順に従って、ソースコードをダウンロードします

$ mkdir android-x86
$ cd android-x86
$ repo init -u git://git.osdn.net/gitroot/android-x86/manifest -b $branch
$ repo sync --no-tags --no-clone-bundle

約30〜40GBほどダウンロードするので、3時間程かかります。就寝前など時間があるときにダウンロードしましょう。

パッチの適用

Linux Kernelは、android_x86/kernelにあるので、android_x86/kernel/drivers/input/touchscreen/goodix.c に先程のパッチを適用します。

ビルド

Android-x86をビルドします。 ビルドオプションには、eng,user,userdebugの3種類ありますが、今回はuserdebugでビルドすることにします。当初はuserでビルドしたのですが、永久に再起動をする症状が出てしまいました。

$ . build/envsetup.sh
$ lunch android_x86_64-userdebug
$ m -j10 iso_img

f:id:PiBVT:20181220010306p:plain
ビルド開始
これでビルドが始まります。これも大体3.5時間かかるので、時間があるときに行いましょう。

ビルドしたイメージを実機で動かす

f:id:PiBVT:20181220144714p:plain
ビルド完了
ビルドが完了すると、完成したイメージはandroid_x86/out/target/product/x86_64/android_x86_64.isoに保存されます。 このイメージを適当にdd等でUSBメモリに書き込み、タブレットで起動してみましょう。

正常にタッチパネルが動作するようになりました!

まとめ

自作OSについて何か書くつもりだったのですが、Linuxの話になってしまいました。 自作OSも楽しいのですが、既存のOSを改造する(今回はドライバでしたが)というのも楽しいもので、勉強になります。 来年はネットワークスタックを書き上げてHTTPサーバーを動作させたり、ファイルシステムの実装をしたいと思っています。 2018年も残すところわずかとなりましたが、年末の休みを利用してまた何か楽しいことをやるつもりです。