俺的Android1.6まとめ

提供: STUDIO DDT ONLINE
移動: 案内検索

コードの書けない人(俺)が、謎のx86端末でAndroidを動かすためのいろいろメモ。ちなみに、Linux全般にも詳しくない。

なお、本ページには2010年以降の情報はありません。最新のx86端末向けAndroid情報は、www.androidx86.orgを参照すべきです。

ビルド環境

腐ってもx86端末なので、クロスコンパイラなどは不要。

OS

多分なんでも良い。Ubuntu 9.04と9.10でビルドできることは確認した。

可能な限りクリーンな方がOSビルド環境としては良さげ。

Java JDK

Android本家ではJava 6 is not supported, because of incompatibilities with @Override.って書いてるので、SunのJDK5.0.x。

と言いつつも、Ubuntu 9.10ではsun-java5-*パッケージ群がなくなってしまったので、sun-java6-*をインストールしてビルドできることは確認した。@Overrideがなんちゃら的な問題は起きていないように見えるけど、細かいところはわからない。

OpenJDKでやったことはない。

GCCとか

Geode向けのパラメータを使ってるので、4.3以降必須。

Ubuntu 9.10ではgcc 4.4になっていて後に引けない感じだが、特に問題なくビルドできた。Atom系とかを使っている人は、問答無用で4.4以降を使うのが良さげ。

コンパイルオプション関連

CFLAGSに"--mcpu=geode --mtune=geode --march=geode"とか入れてみているけれど、効果があるのかわからない。

あ、逆にコンパイルエラーの原因になったりもするので、それはその時考える。

その他の環境変数とか

USE_CCACHEに何か値を入れておくと、フルビルドが速くなるそうなので、何か入れておけ、らしいが、それって本当?

BoardConfig.mk内のTARGET_NO_BOOTLOADERとかTARGET_NO_KERNELとかTARGET_NO_RADIOIMAGEとかは、正直なんのためにあるのかいまいちわからないが、とりあえず謎の端末用にはdiskinstallerとか関係ないので、全部trueを入れてある。local_manifestでもremove-project要素を使って、diskinstallerとx86プロジェクトのnewinstallerは削除してしまっている。

PRODUCT_LOCALESはちゃんと指定しておいた方が良い。どうも指定したものの最上位がデフォルトのロケールになっているようなので、ja_JPを入れておく。この辺は、eeepc.mkファイルのような特定プロダクト向けのmkファイルに付け加えておくと良い。

そもそも、自分でproductを掘るほうがより便利だと気づいたので、Githubに謎の端末用のプロダクトページを作るようにした。まぁ、Android-x86のproduct/asus/eeepcを丸コピーして、.gitを削除し、必要な部分を書き換えてから、git initしなおしてGithubにpushするだけではあるのだけど。各種変数類や、プリインストールするアプリもこれで選択できるようになったので、SMSとかMMSとか電話サービス依存のアプリをそもそもインストールしない、ということもこれで簡単になった。

Android SDK

アプリ開発にあんまり興味がないのでSDK使う気があまりなかったのだけれど、logcatをリモートで見たいのでそれだけのためにセットアップ。

adb

謎の端末はUSBホスト機能しかもっていないため、USB接続してadbを使う、といった事ができない。そのため、LAN内からリモートでadbを使えるようにしないといけない。

接続したいPCで

export ADBHOST=ターゲットのIP
abd kill-server		// いったん止めてから
abd start-server	// スタートさせる、という慣習
abd device		// つながったか確認

とかやる。

あとはabd logcatとかddmsでログを眺める。

am

なんかintentをコマンドラインから叩けるそうだ。かっこいい!

adb shellでコンソールに接続したあと、

am start -a android.intent.action.MAIN -n com.android.settings/.Settings

とかやると設定画面が開く。燃える!

えー、正直アプリ開発者じゃねーので intentいじりたい時があまりないです。

kernel

謎の端末はGeode LX 433MhzとコンパニオンチップCS5536のセットで、あんまりAndroid系での情報がない。一応、こことかにGeode用の参考情報とかあるが、フォーラムにポストされた情報のコピペっぽいので発展がない。 テキトーにkernelを構築するとinitを見に行くところでsegfaultでpanicを起こすので、何かポイントがあるのだと思う。

donutがmergeされたあたりでkernelは.repo/manifests/default.xmlに含まれなくなったので、個別にgitで引っ張ってくるか、local_manifest辺りに

<project path="kernel" name="kernel/common" revision="refs/heads/android-2.6.27" />

というふうに付け加えておく。

gitで個別に持ってくるときは、こんな感じ。抜粋気味。

# git clone時にbranch指定することもできるんだろうけど、よくわかんねーの。
>git clone git://218.211.38.204/android-x86/kernel/common.git
# だからリモートのbranchも列挙して
>git branch -a
 *(no branch)
  x86/android-2.6.27
  x86/android-2.6.29
# それでcheckoutさせれば良いみたい。
>git checkout -b local-android-2.6.29 x86/android-2.6.29

まぁ、kernelはAndroidとは別にビルドするほうがいい。

ちなみに、Android-x86プロジェクトの方では、2.6.29以降はx86向けのパッチ済みが落ちてくる模様。2.6.29環境以前はGoogleからのコピーのままっぽいので、パッチ等は個別に持ってきて当てないとダメ。

kernel 2.6.27

.config

こんな.configを使っている。このwikiではアップロード禁止してたので個別にUpしないと。

当てるpatch

patch-hosting-for-android-x86-supportにあるもの。

  • alarm.patch

最新のAndroid-x86と謎の端末のセットではinitがsegfault起こすので、ANDROID_BUILD_HOME/system/core/init/property_service.c内の、PA_SIZEを元に戻しておくこと。

#define PA_SIZE 32768

という感じ。PA_SIZEって何なのかはわからん。

kernel option

コンフィグ作るところでは、x86向けに作られていないANDROID_PMEMは切ること。ファイルシステムは書き込み可能にしておく。謎の端末的にはacpi=off nolapicも付けておく。

フレームバッファにLXfbを使うとフリッカーの嵐でどうしようもないので、VESAfbを使う。ywrapオプションとか使うと面白いことになるので、使わない。というか、video=vesafb:1024x768-16@60だけでOK。フレームバッファコンソールを使うときは、それに合わせてvga=791を付け加える。androidboot.consoleオプションは、あえて何も指定しない。

そのうちLXfbちゃんと使ってみたい。LXfb向けにcopybitライブラリを作ってやれば、高速な2D描画が期待できるらしいのだが、ライブラリに必要なインターフェースがわからん。ヘッダファイルはあった気がするが。

kernel 2.6.32

TODO

Android Userland

cupcake

2009/06末ごろのcupcakeでは、起動時にlibdvm.soがsegfaultを起こして無限ループに陥るので、android-1.5ブランチまで立ち戻ってみると、ちゃんと起動する。

repo init -u git://android.git.kernel.org/platform/manifest.git -b android-1.5

ちなみに、2009/07にdonutがmasterにマージされてから、x86用のプロジェクトがGoogleのGitリポジトリから分離されてしまったので、このcupcakeのビルド方法は、そのままじゃ実践しづらい感じです。新しいAndroid-x86プロジェクトのほうではcupcakeってサポートされてないっぽいので。

当てるpatch

patch-hosting-for-android-x86-supportにあるもの。最低限。

  • 0001-fixed-different-build-breaks-added-mouse-cursor-sup.patch
  • 0001-1.-added-scripts-to-build-install-image.patch
  • eventhub.patch
  • e2fsprogs.patch

その他の変更点

バッテリーフル化

ここを参考に。いちおう条件付きコンパイルさせるようにする。(てゆーか、そもそもバッテリ積んでない機器のことも考えてほしいなぁ。TARGET_NO_BATTERYオプションみたいなのとかさ)

パスは、frameworks/base/services/jni/com_android_server_BatteryService.cpp

 static void setBooleanField(JNIEnv* env, jobject obj, const char* path, jfieldID fieldID)
{
    const int SIZE = 16;
    char buf[SIZE];

#if TARGET_PLATFORM == eee_701
    jboolean value = true;
#else   
    jboolean value = false;

    if (readFromFile(path, buf, SIZE) > 0) {
        if (buf[0] == '1') {
            value = true;
        }
    }
#endif

    env->SetBooleanField(obj, fieldID, value);
}
static void setIntField(JNIEnv* env, jobject obj, const char* path, jfieldID fieldID)
{
    const int SIZE = 128;
    char buf[SIZE];

#if TARGET_PLATFORM == eee_701
    jint value = 100;
#else  
    jint value = 0;
     if (readFromFile(path, buf, SIZE) > 0) {
        value = atoi(buf);
    }
#endif

    env->SetIntField(obj, fieldID, value);
}
static void android_server_BatteryService_update(JNIEnv* env, jobject obj)
{
    setBooleanField(env, obj, AC_ONLINE_PATH, gFieldIds.mAcOnline);
#if TARGET_PLATFORM == eee_701
    env->SetBooleanField(obj,gFieldIds.mUsbOnline,false);
#else
    setBooleanField(env, obj, USB_ONLINE_PATH, gFieldIds.mUsbOnline);
#endif
    setBooleanField(env, obj, BATTERY_PRESENT_PATH, gFieldIds.mBatteryPresent);

    setIntField(env, obj, BATTERY_CAPACITY_PATH, gFieldIds.mBatteryLevel);
    setIntField(env, obj, BATTERY_VOLTAGE_PATH, gFieldIds.mBatteryVoltage);
#if TARGET_PLATFORM == eee_701
    /*Zero ;)*/
    env->SetIntField(obj,gFieldIds.mBatteryTemperature,0);
#else
    setIntField(env, obj, BATTERY_TEMPERATURE_PATH, gFieldIds.mBatteryTemperature);
#endif

    const int SIZE = 128;
    char buf[SIZE];

#if TARGET_PLATFORM == eee_701
    env->SetIntField(obj, gFieldIds.mBatteryStatus, gConstants.statusFull);
#else
    if (readFromFile(BATTERY_STATUS_PATH, buf, SIZE) > 0)
        env->SetIntField(obj, gFieldIds.mBatteryStatus, getBatteryStatus(buf));
#endif

#if TARGET_PLATFORM == eee_701
    env->SetIntField(obj, gFieldIds.mBatteryHealth, gConstants.healthGood);
#else
    if (readFromFile(BATTERY_HEALTH_PATH, buf, SIZE) > 0)
        env->SetIntField(obj, gFieldIds.mBatteryHealth, getBatteryHealth(buf));
#endif

#if TARGET_PLATFORM == eee_701
    env->SetObjectField(obj, gFieldIds.mBatteryTechnology, env->NewStringUTF("1"));
#else
    if (readFromFile(BATTERY_TECHNOLOGY_PATH, buf, SIZE) > 0)
        env->SetObjectField(obj, gFieldIds.mBatteryTechnology, env->NewStringUTF(buf));
#endif
}
device.cの修正

x86 kernelではビルドエラーになるため、特に何も考えずにAndroid pmem driverを無効にしてkernelを作成してある。そのための変更っぽい?変更点は ここを参考に。でも正直、この変更がどういった意味を持つのかは、さっぱりわからない。

-    { "/dev/pmem_gpu",      0660,   AID_SYSTEM,     AID_GRAPHICS,   1 },
-    { "/dev/pmem_adsp",     0660,   AID_SYSTEM,     AID_AUDIO,      1 },
-    { "/dev/pmem_camera",   0660,   AID_SYSTEM,     AID_CAMERA,     1 },
+    { "/dev/pmem_gpu",      0660,   AID_SYSTEM,     AID_GRAPHICS,   0 },
+    { "/dev/pmem_adsp",     0660,   AID_SYSTEM,     AID_AUDIO,      0 },
+    { "/dev/pmem_camera",   0660,   AID_SYSTEM,     AID_CAMERA,     0 },
keylayout/quwerty.kl

普通に日本語キーボード配列なUSBキーボードを接続すると、BACKボタンとかMENUボタンが使えなかったりして不便な場合があるので、適当なキーを使うようにする。やっぱりここを参考に。/usr/src/linux-headers-2.6.xx/include/linux/input.h とかにキーマップの定義があるので、使いたいキーを捜してなんとなく割り当てる。

感想

  • 電話とかSMSはもちろん動かない。
  • カメラ関係はダミー。
  • 連絡先とカレンダーは落ちる。なんでだろう。logcat追うとかは面倒なのでやってない。

Donut

Android-x86プロジェクトに改められてから、クリーンなUbuntu 9.04に環境を構築してビルドできることを確認。

カスタマイズしたkernel configを使いたい場合は、別途別ディレクトリを掘ってそちらで個別にAndroid-x86のリポジトリから2.6.29を引っ張ってきたほうが、ユーザランドのビルドで躓かないので手っ取り早い。

ただし、CONFIG_EARLYSUSPENDとかのカーネルオプションに依存しているところがあるので、カスタマイズしたkernelでは電源制御関係で不具合がでるかもしれない。なので、Power Management>Wake LockあたりはEnableにしておくと良さげ。TARGET_NO_KERNELを使うなら、bootable/diskinstaller とかその辺をガン無視させるようにしないと怒られるかも。謎の端末ではlocal_manifestでdiskinstallerとnewinstallerをremove-projectしてるので、もうその辺はどうでもいい。

起動方法自体は今までの方法(手動でrootfsをコピーするやつ)で起動させることができる。

一度起動してしまえば、特筆するところがないが、SIM関連含めてAndroid-x86はカスタマイズされているのでよくわからん。

AndroidBoard.mkやAndroidProduct.mk、ひいてはeeepc.mkなんかでパッケージ構成なんかをいじると吉。

OpenWnn

donutになってから、日本語インプットメソッドであるOpenWnnがGoogleのmasterにマージされている。利用するには、envsetup.shとやらを使って個別にビルドしてやるとよい。らしい。例えば、こんな感じ。らしい。

> source mydroid/build/envsetup.sh
> cd mydroid/packages/inputmethods/OpenWnn
> mm

出力はAndroid.mkにあるとおりになる。らしい。

ていうか、Android-x86を特に改変せずに使っている人は、eeepc.mk内のTARGET_PRODUCTに入れたいアプリを最初から書き込んでおけばもっと楽ちん。

なんでlibWnnEngDicとかlibWnnJpnDicとかlibwnndictとかを併記させているのかというと、これらの必須ライブラリがapkパッケージの中ではarmabi的なディレクトリにパックされてしまうせいで、x86ではライブラリが読めねーよ的エラー吐いて止まっちゃうため。こうしておくと、勝手に/system/libに上記ライブラリがコピーされる(OpenWnn的には/system/libも参照する造りになっている)。この問題をそもそも解決させるためには、ビルドスクリプトをどうにかしないといけないのかな?

#こんな感じで
PRODUCT_PACKAGES += \
	OpenWnn \
	libWnnEngDic \
	libWnnJpnDic \
	libwnndict

最新のAndroid-x86では、問題が修正されたおかげでOpenWnnもちゃんと動作するようになっている。ただし、XGA環境では、ちょっと表示がアレな風味になることもある。

その他の変更点

タイムゾーン

Androidのタイムゾーン設定は携帯電話網から引っ張ってくることを「自動」と言っているだけで、非電話端末では自動設定とか意味がない。なので無理やり設定を入れてやる。 やり方はいくつかあって、

  • ビルド時に埋め込む
  • デフォルト値にする
  • init内で書き込む

とかだろうか……。

1番目のやつは、プロダクトのビルド設定内(Android-x86のeeepc.mkとか)に

PRODUCT_PROPERTY_OVERRIDES += \
		persist.sys.timezone=Asia/Tokyo

とか入れる。build.propの最後のほうに追加されることになる、はず。

2番目のやつは、フルビルド後のdefault.propに

persist.sys.timezone=Asia/Tokyo

とか入れる。

最後のやつは、init.rcでもプロダクトごとのinitでも良いんだけど、

setprop persist.sys.timezone Asia/Tokyo

とかやる。

多分、どれかやればいける。はず。

電話の感度インジケータを消す

謎の端末ではGSMとか3Gとかカンケーないので、ステータスバー上にアイコンとか出ていても用いられることがない。ので表示させないようにする。極めてアドホック。

--- a/services/java/com/android/server/status/StatusBarPolicy.java
+++ b/services/java/com/android/server/status/StatusBarPolicy.java
@@ -428,7 +428,7 @@ public class StatusBarPolicy {
         mPhone = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
         mPhoneData = IconData.makeIcon("phone_signal",
                 null, com.android.internal.R.drawable.stat_sys_signal_null, 0, 0);
-        mPhoneIcon = service.addIcon(mPhoneData, null);
+//        mPhoneIcon = service.addIcon(mPhoneData, null);

         // phone_evdo_signal
         mPhoneEvdoData = IconData.makeIcon("phone_evdo_signal",
@@ -899,7 +899,7 @@ public class StatusBarPolicy {
             } else {
                 mPhoneData.iconId = com.android.internal.R.drawable.stat_sys_signal_null;
             }
-            mService.updateIcon(mPhoneIcon, mPhoneData, null);
+//            mService.updateIcon(mPhoneIcon, mPhoneData, null);
             mService.setIconVisibility(mPhoneEvdoIcon,false);
             return;
         }
@@ -979,7 +979,7 @@ public class StatusBarPolicy {
         }

         mPhoneData.iconId = iconList[iconLevel];
-        mService.updateIcon(mPhoneIcon, mPhoneData, null);
+//        mService.updateIcon(mPhoneIcon, mPhoneData, null);
     }

     private final void updateDataNetType() {
@@ -1272,7 +1272,7 @@ public class StatusBarPolicy {
                 break;
 
         }
-        mService.updateIcon(mPhoneIcon, mPhoneData, null);
+//        mService.updateIcon(mPhoneIcon, mPhoneData, null);
     }
バッテリーアイコン

そもそも謎の端末はバッテリー積んでいないし、ACPI対応がアレなのでバッテリー状態とか関係ない。Donutのリソースファイルを眺めてみてもACアダプタ接続の時専用のアイコンは用意されてない(AC接続=バッテリーチャージ)ので、常時フル化とかせずに非表示にする方向性に。

--- a/services/java/com/android/server/status/StatusBarPolicy.java
+++ b/services/java/com/android/server/status/StatusBarPolicy.java
@@ -422,7 +422,7 @@ public class StatusBarPolicy {
         // battery
         mBatteryData = IconData.makeIcon("battery",
                 null, com.android.internal.R.drawable.stat_sys_battery_unknown, 0, 0);
-        mBatteryIcon = service.addIcon(mBatteryData, null);
+//        mBatteryIcon = service.addIcon(mBatteryData, null);
 
         // phone_signal
         mPhone = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
@@ -580,7 +580,7 @@ public class StatusBarPolicy {
     private final void updateBattery(Intent intent) {
         mBatteryData.iconId = intent.getIntExtra("icon-small", 0);
         mBatteryData.iconLevel = intent.getIntExtra("level", 0);
-        mService.updateIcon(mBatteryIcon, mBatteryData, null);
+//        mService.updateIcon(mBatteryIcon, mBatteryData, null);
 
         int plugged = intent.getIntExtra("plugged", 0);
         int level = intent.getIntExtra("level", -1);
@@ -620,6 +620,7 @@ public class StatusBarPolicy {
             Log.d(TAG, "plugged=" + plugged + " oldPlugged=" + oldPlugged + " level=" + level
                     + " mBatteryThreshold=" + mBatteryThreshold + " oldThreshold=" + oldThreshold);
         }
+/*
         if (plugged == 0
                 && ((oldPlugged != 0 && level < mBatteryThresholds[BATTERY_THRESHOLD_WARNING])
                     || (mBatteryThreshold > oldThreshold
@@ -657,6 +658,7 @@ public class StatusBarPolicy {
                 }
             }
         }
+*/
     }

ちなみに、このパッチの後半部分で、バッテリーレベル低下時にアラートのイベントを送る部分もコメントアウトさせている。謎の端末以外にパッチすることはお勧めしない。

バッテリーフル化再び

設定画面の端末情報から様子を見ようとすると落ちるので、とりあえずバッテリーがいつでも最良な状態であるように返す。cupcakeの時と基本は同じだと思っていたら、それなりに様子が違っていた。

--- a/services/jni/com_android_server_BatteryService.cpp
+++ b/services/jni/com_android_server_BatteryService.cpp
@@ -72,49 +72,12 @@ static BatteryManagerConstants gConstants;

 static jint getBatteryStatus(const char* status)
 {
-    switch (status[0]) {
-        case 'C': return gConstants.statusCharging;         // Charging
-        case 'D': return gConstants.statusDischarging;      // Discharging
-        case 'F': return gConstants.statusFull;             // Not charging
-        case 'N': return gConstants.statusNotCharging;      // Full
-        case 'U': return gConstants.statusUnknown;          // Unknown
-            
-        default: {
-            LOGW("Unknown battery status '%s'", status);
-            return gConstants.statusUnknown;
-        }
-    }
+    return gConstants.statusFull;      // Always Full
 }
 
 static jint getBatteryHealth(const char* status)
 {
-    switch (status[0]) {
-        case 'D': return gConstants.healthDead;         // Dead
-        case 'G': return gConstants.healthGood;         // Good
-        case 'O': {
-            if (strcmp(status, "Overheat") == 0) {
-                return gConstants.healthOverheat;
-            } else if (strcmp(status, "Over voltage") == 0) {
-                return gConstants.healthOverVoltage;
-            }
-            LOGW("Unknown battery health[1] '%s'", status);
-            return gConstants.healthUnknown;
-        }
-        
-        case 'U': {
-            if (strcmp(status, "Unspecified failure") == 0) {
-                return gConstants.healthUnspecifiedFailure;
-            } else if (strcmp(status, "Unknown") == 0) {
-                return gConstants.healthUnknown;
-            }
-            // fall through
-        }
-            
-        default: {
-            LOGW("Unknown battery health[2] '%s'", status);
-            return gConstants.healthUnknown;
-        }
-    }
+    return gConstants.healthGood;         // Always Good
 }

@@ -131,34 +94,20 @@ static void android_server_BatteryService_update(JNIEnv* env, jobject obj)
   }
   gLock.unlock();
 
-  env->SetBooleanField(obj,gFieldIds.mAcOnline,
-                    gBss->is_ac_online()==1 ?true:false);
-  env->SetBooleanField(obj,gFieldIds.mUsbOnline,
-                    gBss->is_usb_online()==1 ?true:false);
-  env->SetBooleanField(obj,gFieldIds.mBatteryPresent,
-                    gBss->is_usb_online()==1 ?true:false);
+  env->SetBooleanField(obj,gFieldIds.mAcOnline, true);
+  env->SetBooleanField(obj,gFieldIds.mUsbOnline, false);
+  env->SetBooleanField(obj,gFieldIds.mBatteryPresent, true);
 
-  env->SetIntField(obj,gFieldIds.mBatteryLevel,gBss->get_bat_level());
-  env->SetIntField(obj,gFieldIds.mBatteryVoltage,gBss->get_bat_voltage());
-  env->SetIntField(obj,gFieldIds.mBatteryTemperature,
-                   gBss->get_bat_temperature());
+  env->SetIntField(obj,gFieldIds.mBatteryLevel, 0);
+  env->SetIntField(obj,gFieldIds.mBatteryVoltage, 0);
+  env->SetIntField(obj,gFieldIds.mBatteryTemperature, 0);
     
   const int SIZE = 128;
   char buf[SIZE];
-  if (gBss->get_bat_status(buf, SIZE) > 0)
-      env->SetIntField(obj, gFieldIds.mBatteryStatus, getBatteryStatus(buf));
-  else
-      env->SetIntField(obj, gFieldIds.mBatteryStatus, 'U');
-
-  if (gBss->get_bat_health(buf,SIZE)>0)
-      env->SetIntField(obj, gFieldIds.mBatteryHealth, getBatteryHealth(buf));
-  else
-      env->SetIntField(obj, gFieldIds.mBatteryHealth, 'U');
-
-  if (gBss->get_bat_tech(buf,SIZE)>0)
-      env->SetObjectField(obj, gFieldIds.mBatteryTechnology, env->NewStringUTF(buf));
-
+  env->SetIntField(obj, gFieldIds.mBatteryStatus, getBatteryStatus(buf));
+  env->SetIntField(obj, gFieldIds.mBatteryHealth, getBatteryHealth(buf));
+  env->SetObjectField(obj, gFieldIds.mBatteryTechnology, env->NewStringUTF("NO BATTERY"));
 }

Eclair

2009/12/05ごろから、Eclairをmergeしたx86プロジェクト版がビルド可能になっている。

  • external/opencoreでのビルドオプションが悪さをする場合がある。-Wnoなんちゃらなオプションを削る。64bit環境向け?
  • preloaded-classesファイルがおかしいかもなので、何となくチェック。
  • VESAfbが死ぬほど遅い。

やりたいこと

  • 電話機能とかいらないので、そもそも外したい。どうやるの?
    • 対応するプロダクトのAndroidBoard.mkやAndroidProduct.mkをいじってやればよい。面倒なら、/system/appにあるapkファイルを消してしまえば、とりあえずアプリ自体は消えてしまう。本質的にそれが正しいのかはともかく。依存関係のあるパッケージを消すと、起動できないものやユーザランドを巻き込んで落ちてしまったりすることもある。
  • バッテリー積んでないのに表示がウザい
    • 表示がウザいなら、そもそも表示だけ止めてしまえばいいじゃない!
  • LXfbを使った高速描画。
    • LXfbを使えるようにする。
    • copybitライブラリを実装する。あるいは、libGLとかその辺に手を入れると良いっぽい。とりあえず俺には難しい。
    • よりアドホックには、Surface作成の時に、Dim SurfaceやBlur Surfaceの作成をつぶしてしまえば、特定のシチュエーションでかなりマシになる。
  • タッチスクリーンとGPIO、DSPの利用とか。
    • タッチスクリーンは解決。
    • 前面ボタンはTI PCA9539 IO拡張チップ経由で接続されているらしいので、このチップのIOアドレス(CS5536がI2Cとして用意しているのを探せばいいのかな)をドライバロード時に渡してあげれば、道が開けそうな予感。
    • CS5536_GPIO自体はLEDで使ってるっぽいので、GPIO_LEDドライバが使えそうかどうか検討かな。

ハードウェア

基本的に謎の端末用なので、普通の人向けでないセクション。

PCIデバイスマップ

  • /sys/devices/pci0000:00/0000:00:01.0
    • vendor 0x1022 AMD
    • device 0x2080 Unknown(GeodeLX内のなにか)
    • dmesg PCI: 0000:00:01.0 reg 10 io port: [ac1c, ac1f]
  • /sys/devices/pci0000:00/0000:00:01.1
    • vendor 0x1022 AMD
    • device 0x2081 GeodeLX graphics adapter
    • dmesg PCI: 0000:00:01.1 reg 10 32bit mmio: [50000000, 51ffffff]
    • dmesg PCI: 0000:00:01.1 reg 14 32bit mmio: [4fffc000, 4fffffff]
    • dmesg PCI: 0000:00:01.1 reg 18 32bit mmio: [4fff8000, 4fffbfff]
    • dmesg PCI: 0000:00:01.1 reg 1c 32bit mmio: [4fff4000, 4fff7fff]
    • dmesg PCI: 0000:00:01.1 reg 20 32bit mmio: [4fff0000, 4fff3fff]
  • /sys/devices/pci0000:00/0000:00:0d.0
    • vendor 0x104c TI
    • device 0x9065 TMX320C6412
    • dmesg PCI: 0000:00:0d.0 reg 10 32bit mmio: [ef800000, efbfffff]
    • dmesg PCI: 0000:00:0d.0 reg 14 32bit mmio: [ef000000, ef7fffff]
    • dmesg PCI: 0000:00:0d.0 reg 18 io port: [df30, df3f]
  • /sys/devices/pci0000:00/0000:00:0e.0
    • vendor 0x10ec Realtek
    • device 0x8139 RTL-8139/8139C/8139D
    • dmesg PCI: 0000:00:0e.0 reg 10 io port: [de00, deff]
    • dmesg PCI: 0000:00:0e.0 reg 14 32bit mmio: [eef00000, eef000ff]
    • dmesg pci 0000:00:0e.0: supports D1
    • dmesg pci 0000:00:0e.0: supports D2
  • /sys/devices/pci0000:00/0000:00:0f.0
    • vendor 0x1022 AMD
    • device 0x2090 CS5536 GPIO(cs5535_gpio.mod.cに記載あり)
    • dmesg PCI: 0000:00:0f.0 reg 10 io port: [6000, 6007]
    • dmesg PCI: 0000:00:0f.0 reg 14 io port: [6100, 61ff]
    • dmesg PCI: 0000:00:0f.0 reg 18 io port: [6200, 623f]
    • dmesg PCI: 0000:00:0f.0 reg 1c io port: [0, 1f]
    • dmesg PCI: 0000:00:0f.0 reg 20 io port: [9d00, 9d7f]
    • dmesg PCI: 0000:00:0f.0 reg 24 io port: [9c00, 9c3f]
    • dmesg pci 0000:00:0f.0: default IRQ router [1022/2090]
  • /sys/devices/pci0000:00/0000:00:0f.2
    • vendor 0x1022 AMD
    • device 0x209a CS5536 IDE Controller
    • dmesg PCI: 0000:00:0f.2 reg 20 io port: [eff0, efff]
  • /sys/devices/pci0000:00/0000:00:0f.3
    • vendor 0x1022 AMD
    • device 0x2093 CS5536 Audio Controller
    • dmesg PCI: 0000:00:0f.3 reg 10 io port: [dd80, ddff]
  • /sys/devices/pci0000:00/0000:00:0f.4
    • vendor 0x1022 AMD
    • device 0x2094 CS5536 OHCI USB Host Controller
    • dmesg PCI: 0000:00:0f.4 reg 10 32bit mmio: [eff00000, eff00fff]
    • dmesg pci 0000:00:0f.4: PME# supported from D0 D3hot D3cold
    • dmesg pci 0000:00:0f.4: PME# disabled
  • /sys/devices/pci0000:00/0000:00:0f.5
    • vendor 0x1022 AMD
    • device 0x2095 CS5536 EHCI USB Host Controller
    • dmesg PCI: 0000:00:0f.5 reg 10 32bit mmio: [eee00000, eee00fff]
    • dmesg pci 0000:00:0f.5: PME# supported from D0 D3hot D3cold
    • dmesg pci 0000:00:0f.5: PME# disabled

タッチスクリーン

  • コントローラはgunze AHL-71N
  • 入力はシリアルポート COM1。/dev/ttyS1

カーネルドライバへのパッチ

カーネルドライバはAHL-51S用のものがほぼ丸々使える。ただし、謎の端末特有の仕様、およびAndroidでタッチスクリーンとして認識されるために必要な記述を追加する必要があるっぽい。

/* drivers/input/touchscreen/gunze.c */
/* 実際のパケットを処理するらしきところ */
static void gunze_process_packet(struct gunze* gunze)
{
	struct input_dev *dev = gunze->dev;

	if (gunze->idx != GUNZE_MAX_LENGTH || gunze->data[5] != ',' ||
		(gunze->data[0] != 'T' && gunze->data[0] != 'R')) {
		printk(KERN_WARNING "gunze.c: bad packet: >%.*s<\n", GUNZE_MAX_LENGTH, gunze->data);
		return;
	}

--	input_report_abs(dev, ABS_X, simple_strtoul(gunze->data + 1, NULL, 10));
++	input_report_abs(dev, ABS_X, simple_strtoul(gunze->data + 6, NULL, 10));	# XとYが逆になっている
--	input_report_abs(dev, ABS_Y, 1024 - simple_strtoul(gunze->data + 6, NULL, 10));
++	input_report_abs(dev, ABS_Y, simple_strtoul(gunze->data + 1, NULL, 10));	# XとYが逆になっている
++	input_report_abs(dev, ABS_PRESSURE, gunze->data[0] == 'T');				# 追加
	input_report_key(dev, BTN_TOUCH, gunze->data[0] == 'T');
	input_sync(dev);
}

static int gunze_connect(struct serio *serio, struct serio_driver *drv)
{
	/* 中略 */

	input_dev->name = "Gunze AHL-51S TouchScreen";
	input_dev->phys = gunze->phys;
	input_dev->id.bustype = BUS_RS232;
	input_dev->id.vendor = SERIO_GUNZE;
	input_dev->id.product = 0x0051;
	input_dev->id.version = 0x0100;
	input_dev->dev.parent = &serio->dev;
	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
	input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
	input_set_abs_params(input_dev, ABS_X, 26, 994, 0, 0);				# 値をちょっと調整
	input_set_abs_params(input_dev, ABS_Y, 26, 994, 0, 0);				# 値をちょっと調整
++	input_set_abs_params(input_dev, ABS_PRESSURE, 0, 1, 0, 0);				# 追加
++	input_set_abs_params(input_dev, ABS_TOOL_WIDTH, 0, 15, 0, 0);			# 追加

	/* 後略 */
}

正直、ABS_TOOL_WIDTHが何を意味しているのかわからない。タッチする指の太さ的な意味なのかな?でもまぁ、細かけぇこたぁいいんだよ。

inputattach

inputattachを書いて、init.rc内にserviceとして記入してやると、入力デバイスとして認識される。inputattachはググるとそのまま使えそうなコードが引っかかるので、AHL-51S用の記述を付け加えてやる。シリアルポート経由でInput subsystemを使うときは、なんらかのデーモンを使ってCOMポートと紐付ける必要があるんだそうだ。

// static struct input_types input_types[]
{ "--gunze",		"-gnz",	"Gunze serial touchscreen",
	B19200, CS8|CSTOPB,
	SERIO_GUNZE,		0xff,	0xff,	1,	NULL },

idとextraはgunze.cではSERIO_ANYとなっていて0xffらしいのだが、合わせなくても別に良いっぽい?とりあえず合わせておくが。

コンパイルは特に何も考えずにstaticで。一応stripしてから適当なところにコピー。

gcc -static -o inputattach inputattach.c
strip inputattach
cp inputattach /Android_Root/system/bin

サービス登録ってinit.eeepc.rcに書いても良いんだろうか。わからんのでinit.rcにサービスとして登録する。

#init.rc
service inputattach /system/bin/inputattach --daemon --gunze /dev/ttyS1
	oneshot

oneshotつけ忘れるとPanicする。

tslib

で、本来的にはタッチスクリーン用ライブラリはtslibになって久しいらしい。TODO

ハードウェアキー

謎の端末に計4つあるハードウェアキーを、Androidでどうにか動作させるのが目標。

課題

  • データバスに何を使っているのか
  • データ形式が何であるのか

データバス

謎の端末のデータシートをちゃんと見たら、TIのPCA9539 IO Expanderチップに対して前面のハードウェアキーが接続されていることがわかった。PCA9539へはCS5536からI2C接続されているっぽい。PCA9539のドライバをどう扱えば良いのかはTODO。

GPIOは前面パネルに接続された電源LEDとフラッシュディスクアクセスLEDの制御で使っているようなので、あんまり関係なかったっぽい。

データ形式

TODO

電源ボタン

電源ボタンの入力は直接CS5536に入ってきているようなので、GPIO28が用いられているのは確かなのかなぁ、と思うのだが・・・・・・。

ACPI

というか、BIOSがそのへん対応してなさげ。ACPIドライバを読み込む時点でダミーみたいなものを混ぜ込んでおけば良いのかな。

実現できたら良いな

  • CPUの動的なクロック変更
    • Geode LXのCPUクロック変更は、MSRに値をぶち込むとできるらしい。参照先
  • サスペンド
    • というかhaltをどうにかしたい。
  • AndroidからのLCDバックライト制御。
    • Ctl+Alt+F1でコンソールを表示したままにして放っておくとバックライトを消してくれたりするので、とりあえずバックライト消すくらいは何とかなるんじゃないかと思っているがががが。

ALSA

TODO.

  • CS5536のオーディオドライバ部分は読み込まれているのだが、正直音の通るルートが普通じゃないのでどうしたものか。
    • AC97 Codecチップがいて、DSPからI2Sで音とCS5536からくる音を選んでいるっぽい。
    • 別系統(I2Sを使うような系統、DSPやエコーキャンセラー)に分岐してるところもある。
  • そもそもの話、Linuxがどういった仕組みで音を鳴らしているものなのかがわからない。

DSP

TODO.

  • とりあえずDSPがどこにマップされているのかは分かった。