ソースビルドについて

2ヶ月くらい前に NexusOne を手に入れて、CyanogenMod をソースビルドして入れてみたら、カメラが動かなかった。
原因を追ったら、手間がやたらとかかったので記録しておく。

ビルドしたCyanogenModはupdate-cm-6.1.0-RC1-N1-signedで、
repo syncを初めてしたのは11月1日くらいだったと思う。
ビルドは本家の説明のとおりにしたつもりだった。


現象は、
カメラを起動したら、真っ暗のままで何も表示されず、固まったようだった。
HOMEボタンを何度か押すと復帰できた。


原因は、
ビルド時にプロプライエタリなバイナリをNexusOneから取り出す./extract-files.shを実行する際に、
取り出す対象の端末に入っているliboemcamera.soが720Pに対応していなかったためだった。


解決策は、
配布されているupdate-cm-6.0.0-N1-signed.zipを素直に入れてから、バイナリを取り出せばいい。


調べた経緯を書いていく。
カメラを起動したときのログをとると下記が出てきた。

11-16 01:15:28.040 D/dalvikvm(  158): GC_EXTERNAL_ALLOC freed 13339 objects / 690480 bytes in 66ms
11-16 01:15:28.080 E/mm-camera(  120): main: MSM_CAM_IOCTL_REGISTER_PMEM ioctl failed for MSM_PMEM_AF type. ioctl return value is 4294967295 
11-16 01:15:28.080 E/mm-camera(  120): isp3a_init_ctrl failed
11-16 01:15:32.140 W/WindowManager(  158): App freeze timeout expired.

E/mm-cameraのメッセージを、frameworksやhardware, deviceのなかでgrepしても、ソースが見つからなかった。
MSM_CAM_IOCTL_REGISTER_PMEMでgrepをかけたところ、kernelソース以外で怪しいところとしては、
hardware/msm7k/libcamera2/QualcommCameraHardware.cppがでてきた。
それと、liboemcamera.soでも使っている。
MSM_CAM_IOCTL_REGISTER_PMEM は、ioctlのパラメータとして使用しており、REGISTERとあるので、カーネル領域のメモリを確保しているようだ。

ここで、libcamera.soでも、liboemcamera.soでもカーネル領域を確保しているため、割り当てに失敗しているかと考えて、カーネルログをみた。
しかし、下記のようにエラーらしいエラーは見つからなかった。

<6>[  316.639099] msm_open_common: open control0
<6>[  316.789611] >>>>>>>>(2010-11-21 03:59:07.865182928 UTC)<<<<<<<
<6>[  316.790252] msm_open_common: open config0
<6>[  316.814575] msm_release_config: config0
<6>[  316.815246] __msm_release:sync->opencnt:2 
<6>[  316.815856] release config atomic_set pmsm->opened as 0

ここで行き詰まって、6.0.0の出来合いのイメージを入れてみたら、カメラが正常に動くことを確認した。

次に、QualcommCameraHardware.cppにgit blameをかけて、MSM_CAM_IOCTL_REGISTER_PMEMを使っている関数register_bufの更新日を見たところ、下記のように 2010-06-28 21:34:03 とでた。
6.0.0がリリースされたのが8月なので、libcamera.so はシロであるとわかった。
ちなみに、libcamera.so にMSM_CAM_IOCTL_REGISTER_PMEMでgrepをかけても、何も出てこないのは、バイナリをstripしているためだと思う。

6b4906b6 (Michael Casadevall 2010-01-12 19:05:16 -0500 1845)     pmemBuf.type     = pmem_type;
6b4906b6 (Michael Casadevall 2010-01-12 19:05:16 -0500 1846)     pmemBuf.fd       = pmempreviewfd;
6b4906b6 (Michael Casadevall 2010-01-12 19:05:16 -0500 1847)     pmemBuf.offset   = offset;
6b4906b6 (Michael Casadevall 2010-01-12 19:05:16 -0500 1848)     pmemBuf.len      = size;
6b4906b6 (Michael Casadevall 2010-01-12 19:05:16 -0500 1849)     pmemBuf.vaddr    = buf;
6b4906b6 (Michael Casadevall 2010-01-12 19:05:16 -0500 1850)     pmemBuf.y_off    = 0;
6b4906b6 (Michael Casadevall 2010-01-12 19:05:16 -0500 1851)     pmemBuf.cbcr_off = size * 2 / 3; //PAD_TO_WORD(size * 2 / 3);
38f3ad1b (Eddie Ringle       2010-06-28 21:34:03 -0400 1852)     pmemBuf.vfe_can_write   = true;
6b4906b6 (Michael Casadevall 2010-01-12 19:05:16 -0500 1853) 
6b4906b6 (Michael Casadevall 2010-01-12 19:05:16 -0500 1854)     LOGV("register_buf: camfd = %d, reg = %d buffer = %p",
6b4906b6 (Michael Casadevall 2010-01-12 19:05:16 -0500 1855)          camfd, !register_buffer, buf);
6b4906b6 (Michael Casadevall 2010-01-12 19:05:16 -0500 1856)     if (ioctl(camfd,
6b4906b6 (Michael Casadevall 2010-01-12 19:05:16 -0500 1857)               register_buffer ?
6b4906b6 (Michael Casadevall 2010-01-12 19:05:16 -0500 1858)               MSM_CAM_IOCTL_REGISTER_PMEM :
6b4906b6 (Michael Casadevall 2010-01-12 19:05:16 -0500 1859)               MSM_CAM_IOCTL_UNREGISTER_PMEM,
6b4906b6 (Michael Casadevall 2010-01-12 19:05:16 -0500 1860)               &pmemBuf) < 0) {
6b4906b6 (Michael Casadevall 2010-01-12 19:05:16 -0500 1861)         LOGE("register_buf: MSM_CAM_IOCTL_(UN)REGISTER_PMEM fd %d error %s",
6b4906b6 (Michael Casadevall 2010-01-12 19:05:16 -0500 1862)              camfd,
6b4906b6 (Michael Casadevall 2010-01-12 19:05:16 -0500 1863)              strerror(errno));
6b4906b6 (Michael Casadevall 2010-01-12 19:05:16 -0500 1864)         return false;
6b4906b6 (Michael Casadevall 2010-01-12 19:05:16 -0500 1865)     }
6b4906b6 (Michael Casadevall 2010-01-12 19:05:16 -0500 1866)     return true;
6b

また行き詰まったので、grepを、E/mm-cameraのエラーメッセージで、ソース全体にかけたところ、liboemcamera.soがでてきた。
この時点で、熟練した開発者なら、ビルド手順に問題があったことに気づいただろう。
そもそも、この様な間違いはしない気がする。

自分は何もピンとこなかったため、ioctlで-1(0xFFFFFFFF)が返されているので、カーネルソースを見ることにした。
MSM_CAM_IOCTL_REGISTER_PMEMを利用していて、一番関係がありそうなのは、
kernel-msm/drivers/media/video/msm/msm_camera.c だった。
利用している関数は、下記になる。

static long msm_ioctl_common(struct msm_device *pmsm,
		unsigned int cmd,
		void __user *argp)
{
	CDBG("%s\n", __func__);
	switch (cmd) {
	case MSM_CAM_IOCTL_REGISTER_PMEM:
		return msm_register_pmem(pmsm->sync, argp);
	case MSM_CAM_IOCTL_UNREGISTER_PMEM:
		return msm_pmem_table_del(pmsm->sync, argp);
	default:
		return -EINVAL;
	}
}

CDBGが抑制されていることがわかったが、make menuconfigでみてもログを出す項目は無かった。
今振り返ると、menuonfigでみた.config は、自分でビルドした6.1.0の/proc/config.gzだったため、出来合いの6.0.0でみたらあるかもしれない。
CDBGの定義を見ると、下記のとおりだった。

#ifdef CONFIG_MSM_CAMERA_DEBUG
#define CDBG(fmt, args...) printk(KERN_INFO "msm_camera: " fmt, ##args)
#else
#define CDBG(fmt, args...) do { } while (0)
#endif

そこで、CONFIG_MSM_CAMERA_DEBUGをソースに直接定義して、自分でいくつかログを埋め込んでビルドをし直した。
カーネルのビルドは、ここを参考にした。
カーネルビルドしてから、ソース全体をビルドしたら、kernel_msmをclean(make mrproper)しろと言われたので、トップディレクトリでmakeをするだけでよかったかもしれない。

決め手になったカーネルログは、下記のとおり。

<6>[  111.912475] msm_camera: msm_ioctl_common: cmd1074031874:
<6>[  111.912811] msm_camera: __msm_register_pmem:infotype:7
<6>[  111.913452] msm_camera: __msm_register_pmem:other
<6>[  111.913787] msm_camera: msm_ioctl_config: cmd 2 DONE

liboemcamera.so は、MSM_PMEM_AF(7)を渡したといっているのに、kernelドライバは無効パラメータ(other)と判断している。
MSM_PMEM_AFの定義は、kernel-msm/include/media/msm_camera.hにあった。

#ifdef CONFIG_720P_CAMERA
・
・
・
#define MSM_PMEM_AF           6
#define MSM_PMEM_AEC          7
・
・
・
#else
・
・
・
#define MSM_PMEM_AEC_AWB		6
#define MSM_PMEM_AF			7

と、CONFIG_720P_CAMERAで振り分けられていて、
ここで、liboemcamera.soがおかしいと気づいた。

適当に探した6月9日の記事では、froyoでは720Pに対応していないとあった。
CONFIG_720P_CAMERAを無効にしたら動くとも思ったが、
別のプロプライエタリなバイナリも同じような爆弾を抱えていそうだったため、とりあえず6.0.0をバイナリの抽出元にして、再度ビルドをしたら、
カメラも動いた。

720Pになっているかは、いままでろくにカメラを使ったことがなかったためわからないが、/proc/config.gz の CONFIG_720P_CAMERA は有効になっているので、720Pで動いているのだと思う。