楽天Kobo Touchをディスプレイにする


汎用e-inkデバイスとして8000円で売ったら売れるかもね。
というわけで、Kobo Touchで動くVNCビューアをコンパイルしてみた。本来は意地でも一行もコードを書かないという企画だったのだけど、諸般の事情(後述)で十数行のコードが必要になってしまった。。

手持ちの撮影なのでへろへろになってしまった。。三脚的な奴を買ったほうが良いに違いない。

接近版。

ツールチェインの準備

ここを読むような人には説明不要だと思うけど、linuxのツールチェインはcrosstool-ngで簡単に準備することができる。ただし、Koboに使用されているlibcは微妙に古く、最近のgcc + makeの組み合わせでは正常にビルドできない。
今回はeglibc 2_15をビルドした。これはKoboに使用されているglibc 2.11.1とABI互換を保っている。ついでに言うとLinuxもマッチするバージョンが無いが、近いバージョンを適当に選べばOK。

ヘッダファイルの準備

KoboカーネルソースコードGitHubリポジトリに置いている。これに、必要なヘッダファイルも含まれている。

Linuxを展開し、include/linuxを生成したツールチェインのsysroot/usr/include/linuxと置き換える。

ドキュメントの準備

Freescaleに登録していれば、

から"i.MX50 EVK Linux Documentation Bundle" をダウンロードできる。mx50_linux.pdfに必要な情報は載っている。
まぁ実際にはドキュメントを読まなくても必要な情報はソースコードに十分書かれている。
他の記述も興味深い。地味にOpenVGをサポートしているが、Koboでは使用されていないように見える。

SDLとlibvncclientの準備

SDLは最新のSDL2でなく、SDL 1.2を使う必要がある。というのも、SDL2からは通常のフレームバッファコンソール(fbcon)のサポートが無くなってしまったため。Koboのe-inkディスプレイはカーネルフレームバッファ(/dev/fb0)に見える。
libvncclientは普通にビルドできる。添付のCMakeLists.txtを適当に修正し、vncclientだけがビルドされるようにする。
両方をビルドできたら、libvncclientのSDLvncviewerサンプルをビルドする。

罠への対処

ここからが重要なポイント。。

  • export SDL_NOMOUSE=1 する

まず、SDLのfbconドライバはマウスがopenできないとクラッシュするという伝統的なバグがある。これを避けるために環境変数SDL_NOMOUSEを指定する必要がある。

  • VNCサーバのサイズは800 x 600にする

今回のシステムでは誰もフレームバッファの縮小を実装していないので、最初から800 x 600にしてく必要がある。
他のサイズだと失敗する。

今回最大の罠。Koboカーネルはe-inkフレームバッファに書き込んでも自動的にアップデートを行わない。なので、毎回手動でioctlして画面を更新する必要がある。

#include <fcntl.h>
#include <sys/types.h>
#include <linux/mxcfb.h>
#include <sys/ioctl.h>

int mxcfb = -1;
static void
mxc_damage(int x, int y, int w, int h){
    struct mxcfb_update_data param;
    const int MARKER = 999;
    int r;
    if(mxcfb<0){
        mxcfb = open("/dev/fb0", O_RDWR);
    }
    if(mxcfb<0){
        printf("mxcfb open error!\n");
    }

    param.update_region.left = x;
    param.update_region.top = y;
    param.update_region.width = w;
    param.update_region.height = h;
    param.waveform_mode = WAVEFORM_MODE_AUTO;
    param.update_mode = UPDATE_MODE_PARTIAL;
    param.update_marker = MARKER;
    param.temp = 0;
    param.flags = 0;
    /* alt_buffer_data */
    r = ioctl(mxcfb, MXCFB_SEND_UPDATE, &param);
    printf("send update = %d\n",r);
#if 0
    r = ioctl(mxcfb, MXCFB_WAIT_FOR_UPDATE_COMPLETE, &MARKER);
    printf("wait = %d\n",r);
#endif

}

興味深いのはflagsで、ここの設定によって白黒2値の高速アップデート(PDFやブラウザでスクロールすると発生するアレ)やアップデート時の反転を制御することができる。
今回はflagsにゼロを入れているので、反転無しの16段階グレースケールアップデートになる。なので残像が残って汚い。
あと、今回waitは外している。アップデートにはそれなりに時間が掛かるので、本来はwaitを入れる必要がある。

misc

↑のdamageコードが今回唯一書いたコードで、こんなにコーディングなしでVNCクライアントが作れるのは非常に便利だと思った。
今回はSDL側(UpdateRect)にこのコードを仕込んだが、本来はアプリケーションがアップデートの挙動を制御するはず。
本来のGUIシェル(Nickel)との共存方法を考えないといけない。多分Home長押しをトラップしてNickelを殺したり再起動したりする小さなDaemonを書くのがいいだろう。