2007年01月

このエントリーを含むブックマーク 2007年01月09日

ネットワーク経由で La Fonera にパッケージをインストール

La Fonera (FON ソーシャル ルータ) のベースとなっている OpenWrt は、 パッケージ管理システムとして ipkg を利用している。 La Fonera 用の ipkg feed を 作ってみた。 この feed を使うには、 以下のように /etc/ipkg.conf に 「src gcd http://www.gcd.org/fonera」 を追加し、 「ipkg update」 を実行する。

root@OpenWrt:/# echo "src gcd http://www.gcd.org/fonera" >> /etc/ipkg.conf
root@OpenWrt:/# ipkg update
Downloading http://www.gcd.org/fonera/Packages
Updated list of available packages in /usr/lib/ipkg/lists/gcd
Done.

すると、feed のリスト (パッケージ名の一覧) が /usr/lib/ipkg/lists/gcd に保存される。 あとは、「ipkg install パッケージ名」などと実行するだけで、 パッケージをネットワーク経由で La Fonera にインストールできる。

例えば stone をインストールするには、 次のように「ipkg install stone」と実行するだけでよい。

root@OpenWrt:~# ipkg install stone
Installing stone (2.3c-2.2.4.12) to root...
Downloading http://www.gcd.org/fonera/stone_2.3c-2.2.4.12_mips.ipk
Installing libopenssl (0.9.8d-1) to root...
Downloading http://www.gcd.org/fonera/libopenssl_0.9.8d-1_mips.ipk
Installing zlib (1.2.3-3) to root...
Downloading http://www.gcd.org/fonera/zlib_1.2.3-3_mips.ipk
Installing libpthread (0.9.28-8) to root...
Downloading http://www.gcd.org/fonera/libpthread_0.9.28-8_mips.ipk
Configuring libopenssl
Configuring libpthread
Configuring stone
Configuring zlib
Done.

stone の実行に必要な、libopenssl, zlib, libpthread パッケージの インストールも自動的に行なわれている。 このようにパッケージの依存関係も管理してくれるので便利なのであるが、 残念ながら現行の La Fonera 上の ipkg にはバグがある。 インストールしたパッケージをアンインストールしようとして、

ipkg remove stone

などと実行してはいけない。 /usr/bin/stone だけでなく、 /usr/bin ディレクトリまでもが削除されてしまう。 なぜこんなバグがあるかというと、 mini_fo の rmdir の実装にバグがあるからだ。 すなわち、空でないディレクトリに対して rmdir を行なっても、 ディレクトリが削除されてしまうことがある。 試しに、あまり重要そうでないディレクトリに対して rmdir してみる:

root@OpenWrt:/www/images# ls -a table
.                 bottom-right.gif  logo.gif          top-left.gif
..                bottom.gif        right.gif         top-right.gif
bottom-left.gif   left.gif          sidebar.gif       top.gif
root@OpenWrt:/www/images# rmdir table
root@OpenWrt:/www/images# ls table
ls: table: No such file or directory

La Fonera のファイルシステムは mini_fo (mini fan-out) を使っている。 つまり squashfs を read only でマウントした「上」に、 jffs を重ねてマウントしている。 前述の例で言えば、 /www/images/table ディレクトリは squashfs 上にあり、 jffs 上には /www/images/table ディレクトリは存在しない。 「rmdir table」などファイルシステムに対する変更は、 「上」に重ねられた jffs に対して行なわれるが、 ディレクトリが空か否かの判断まで、 「上」のファイルシステムに対してのみ行なっているのだろう、 jffs 上では /www/images/table ディレクトリの中にはファイルが存在しない (ディレクトリが存在しないので当然だが) ため、 rmdir がエラーにならずにディレクトリの削除が行なわれてしまう。

La Fonera の ipkg (busybox に含まれている) のソース ipkg_remove.c を 確認してみると、

    for (iter = installed_dirs.head; iter; iter = iter->next) {
            file_name = iter->data;
 
            if (rmdir(file_name) == 0) {
                 ipkg_message(conf, IPKG_INFO, "  deleting %s\n", file_name);
                 removed_a_dir = 1;
                 str_list_remove(&installed_dirs, &iter);
            }
    }

などとなっている。 つまりディレクトリが空かどうかは判断せず、 いきなり rmdir してみて成功すればヨシとしている。 このため、「ipkg remove パッケージ名」を実行すると、 空でないディレクトリまで削除してしまうのだろう。

空でないディレクトリに対して rmdir を実行したら ENOTEMPTY を返すのがスジであるので、 mini_fo 側を修正すべきだとは思うのだが、 とりあえず sh スクリプト版の ipkg を拾ってきて、 rmdir するまえに中身の確認をするように変更し、 /usr/bin/ipkg を この修正済み sh スクリプトで置き換えてみた。

root@OpenWrt:~# wget http://www.gcd.org/fonera/ipkg-0.9-1.32.sh
Connecting to www.gcd.org[60.32.85.216]:80
ipkg-0.9-1.32.sh     100% |*****************************| 27925       00:00 ETA
root@OpenWrt:~# chmod 755 ipkg-0.9-1.32.sh
root@OpenWrt:~# mv ipkg-0.9-1.32.sh /usr/bin/ipkg

などと置き換えるとよいだろう。 これで安全にパッケージをアンインストールできるようになる。

root@OpenWrt:~# ipkg remove stone
ipkg_remove: Removing stone...
ipkg_remove: Warning: Not removing the following directories since they are not
empty:
 /usr /usr/bin
Done.

/usr および /usr/bin ディレクトリが空でなかったので削除できなかった旨が、 表示されている。

La Fonera の自動アップデートのまとめ

La Fonera の自動アップデートは、 cron から呼び出される /bin/thinclient プログラムによって行なわれている。 このアップデートは ipkg とは独立した形で行なわれているので、 ipkg でパッケージのインストールを行なう場合は、 この自動アップデートで何が行なわれているか把握しておく必要がある。 確認できたアップデートについてまとめてみた。

0.7.0 rev 2 -> 0.7.0 rev 3 (upgrade.fon)
修正: /usr/lib/webif/validate.awk
0.7.0 rev 3 -> 0.7.0 rev 4 (upgrade.fon)
[WiFi] 暗号方式のデフォルトを WPA+TKIP に変更
修正: /etc/init.d/rcS /sbin/ifup /www/cgi-bin/webif/*.sh
変更: /lib/modules/2.4.32/wlan.o /lib/modules/2.4.32/ath_ahb.o
追加: /etc/hotplug.d/iface/10-ppp_hack
0.7.0 rev 4 -> 0.7.1 rev 1 (upgrade.fon) 2006-11-21 0.7.1 rev 1
[Web interface] 多言語サポート
[Web interface] ポートフォワード機能
[NTP] ntpclient による時刻同期
修正: /bin/thinclient /etc/functions.sh /usr/lib/webif/*
変更: /usr/bin/webif-page
追加: /etc/config/ntpservers /etc/config/openports /etc/config/webif /etc/init.d/N45ntpclient /usr/lib/webif/lang /usr/sbin/adjtimex /usr/sbin/ntpclient /www/cgi-bin/webif/adv_pf.sh /www/cgi-bin/webif/language.sh
0.7.1 rev 1 -> 0.7.1 rev 2 (upgrade.fon) 2007-01-04 01:00 JST
[Web interface] 任意のコマンドを実行させられてしまう脆弱性を修正
[NTP] crontab に複数の ntpclient 呼び出しが登録されてしまうバグを修正
修正: /etc/init.d/N45ntpclient /usr/lib/webif/validate.awk /www/cgi-bin/webif/*.sh
変更: /usr/bin/haserl

特に重要なのが、0.7.1 rev 2 で行なわれた、 Web インタフェースにおける脆弱性の修正だろう。 POST した入力データのチェックが厳密になった。 La Fonera に ssh でログインする最も手軽な方法が、 この脆弱性を利用する方法だっただけに、 0.7.1 rev 2 にアップデートする際は注意が必要である。



hiroaki_sengoku at 06:51|この記事のURLComments(1)TrackBack(0)La Fonera 
このエントリーを含むブックマーク 2007年01月04日

La Fonera (FON ソーシャル ルータ) のファーム ウェアのソース コードは、 Fon blog に書かれているように、 http://download.fon.com/firmware/fonera/latest/fonera.tar.bz2 から取得できる。 これを展開して make を実行すると、 「OpenWrt Configuration」ダイアログが表示される。 現行の La Fonera のファーム ウェアと同じものをビルドするだけなら Configuration の変更は不要。そのまま save して exit すればビルドが行なわれる。

stone を make するには、 libpthread と libopenssl が必要なので、 以下のように設定する。
まず、

     Base system  --->

を選択して、以下のように libpthread を make するように指定する:

<*> libpthread.......................................... POSIX threa

続いて、

    Libraries  --->

を選択して、以下のように libopenssl も make するように指定しておく:

<M> libopenssl..................................... Open source SSL
< >   openssl-util.............................. OpenSSL command lin
<M> zlib................. Library implementing the deflate compressi

「Do you wish to save your new OpenWrt configuration?」 と聞かれるので「Yes」と答える。 するとクロスコンパイル環境とファームウェアのビルドがどんどん進む。

*** End of OpenWrt configuration.
*** Execute 'make' to build the OpenWrt or try 'make help'.

make[2] toolchain/install
make[3] -C toolchain install
make[4] -C toolchain/sed prepare
make[4] -C toolchain/sed compile
make[4] -C toolchain/sed install
make[4] -C toolchain/kernel-headers prepare
make[4] -C toolchain/kernel-headers compile
make[4] -C toolchain/kernel-headers install
make[4] -C toolchain/sstrip prepare
make[4] -C toolchain/sstrip compile
make[4] -C toolchain/sstrip install
make[4] -C toolchain/uClibc prepare
make[4] -C toolchain/binutils prepare
make[4] -C toolchain/binutils compile
make[4] -C toolchain/binutils install
make[4] -C toolchain/gcc prepare
make[4] -C toolchain/gcc compile
make[4] -C toolchain/uClibc compile
make[4] -C toolchain/uClibc install
make[4] -C toolchain/gcc install
make[4] -C toolchain/ipkg-utils prepare
make[4] -C toolchain/ipkg-utils compile
make[4] -C toolchain/ipkg-utils install
make[4] -C toolchain/libnotimpl prepare
make[4] -C toolchain/libnotimpl compile
make[4] -C toolchain/libnotimpl install
make[4] -C toolchain/lzma prepare
make[4] -C toolchain/lzma compile
make[4] -C toolchain/lzma install
make[4] -C toolchain/squashfs prepare
make[4] -C toolchain/squashfs compile
make[4] -C toolchain/squashfs install
make[4] -C toolchain/jffs2 prepare
make[4] -C toolchain/jffs2 compile
make[4] -C toolchain/jffs2 install

「install」と表示されているが、 これは「./staging_dir_mips」ディレクトリへのインストール。 「./staging_dir_mips」にクロスコンパイル環境がインストールされた後に、 これを使ってカーネルやライブラリ等のコンパイルが進む。

make[2] target/compile
make[3] -C target compile
make[4] -C target/utils prepare
make[4] -C target/utils compile
make[4] -C target/utils install
make[4] -C target/linux prepare
make[5] -C target/linux/ar531x-2.4 prepare
make[4] -C target/linux compile
make[5] -C target/linux/ar531x-2.4 compile
make[6] -C target/linux/ar531x-2.4 modules
make[6] -C target/linux/ar531x-2.4 packages
make[4] -C target/image/ar531x compile
make[2] package/compile
make[3] -C package compile
make[4] -C package compile-targets
make[5] -C package/base-files compile
make[5] -C package/bridge compile
make[5] -C package/busybox compile
make[5] -C package/chillispot compile
make[5] -C package/dnsmasq compile
make[5] -C package/dropbear compile
make[5] -C package/foncheckrsa compile
make[5] -C package/haserl compile
make[5] -C package/madwifi compile
make[5] -C package/hostapd compile
make[5] -C package/iptables compile
make[5] -C package/mini_fo compile
make[5] -C package/mtd compile
make[5] -C package/libpcap compile
make[5] -C package/linux-atm compile
make[5] -C package/ppp compile
make[5] -C package/pptp compile
make[5] -C package/iproute2 compile
make[5] -C package/qos compile
make[5] -C package/webif compile
make[5] -C package/wireless-tools compile
make[5] -C package/zlib compile
make[5] -C package/openssl compile

「OpenWrt Configuration」ダイアログで指定しなかった パッケージまで表示されるが、 これは単に make がそのパッケージのディレクトリの Makefile を実行しただけで、 コンパイルなどは何も行なわずに抜けている。

make[2] package/install
make[3] -C package install
make[4] -C package install-targets
make[5] -C package/base-files install
make[5] -C package/bridge install
make[5] -C package/busybox install
make[5] -C package/chillispot install
make[5] -C package/dnsmasq install
make[5] -C package/dropbear install
make[5] -C package/foncheckrsa install
make[5] -C package/haserl install
make[5] -C package/hostapd install
make[5] -C package/iptables install
make[5] -C package/madwifi install
make[5] -C package/mini_fo install
make[5] -C package/mtd install
make[5] -C package/ppp install
make[5] -C package/pptp install
make[5] -C package/qos install
make[5] -C package/iproute2 install
make[5] -C package/webif install
make[5] -C package/wireless-tools install
make[2] target/install
make[3] -C target install
make[4] -C target/image/ar531x clean
make[5] -C target/image/generic/lzma-loader clean
make[4] -C target/utils prepare
make[4] -C target/utils compile
make[4] -C target/utils install
make[4] -C target/linux prepare
make[5] -C target/linux/ar531x-2.4 prepare
make[4] -C target/linux compile
make[5] -C target/linux/ar531x-2.4 compile
make[4] -C target/linux install
make[5] -C target/linux/ar531x-2.4 install
make[4] -C target/image/ar531x compile
make[4] -C target/image/ar531x install
make[5] -C target/image/generic/lzma-loader clean compile

以上で、クロスコンパイル環境とファームウェアのビルドが全て完了。 所要時間は 30分ほど (私の Celeron D 345 マシンの場合)。

ビルドしたクロスコンパイル環境は、 「staging_dir_mips/bin」に PATH を通すだけで使用できる。

stone を make してみる。 まずは CVS レポジトリから最新版を checkout する:

senri % cvs -d:pserver:anonymous@cvs.sourceforge.jp:/cvsroot/stone login
Logging in to :pserver:anonymous@cvs.sourceforge.jp:2401/cvsroot/stone
CVS password: 
senri % cvs -z3 -d:pserver:anonymous@cvs.sourceforge.jp:/cvsroot/stone co stone
cvs checkout: Updating stone
U stone/GPL.txt
U stone/Makefile
U stone/README.en.txt
U stone/README.txt
U stone/cryptoapi.c
U stone/dist
U stone/global.h
U stone/logmsg.mc
U stone/md5.h
U stone/md5c.c
U stone/stone.1
U stone/stone.1.ja
U stone/stone.c
U stone/stone.cnf
U stone/stone.sh
U stone/stone.spec

次に make する:

senri % cd stone
senri % make fon-ssl
make CC="mips-linux-uclibc-gcc" SSL_LIBS="-lssl -lcrypto" TARGET=fon ssl_stone
make[1]: Entering directory `stone'
make FLAGS="-DUSE_POP -DUSE_SSL " LIBS=" -lssl -lcrypto" fon
make[2]: Entering directory `stone'
make CC="mips-linux-uclibc-gcc" FLAGS="-Wall -DPTHREAD -DUNIX_DAEMON -DPRCTL -DUSE_POP -DUSE_SSL " LIBS="-lpthread -lssl -lcrypto" stone
make[3]: Entering directory `stone'
mips-linux-uclibc-gcc  -Wall -DPTHREAD -DUNIX_DAEMON -DPRCTL -DUSE_POP -DUSE_SSL  -o stone stone.c -lpthread -lssl -lcrypto
make[3]: Leaving directory `stone'
mips-linux-uclibc-strip stone
make[2]: Leaving directory `stone'
make[1]: Leaving directory `stone'

なお、CVS から checkout した最新版でなくても、 例えば stone 2.3c において

senri:/usr/src/stone-2.3c % mips-linux-uclibc-gcc  -Wall -DPTHREAD -DUNIX_DAEMON -DPRCTL -DUSE_POP -DUSE_SSL -o stone stone.c -lpthread -lssl -lcrypto

などとコンパイルすることにより La Fonera 版 stone を make することができる。 つまり特に変更することなく make できるわけで、 いかに La Fonera がフツーの Linux マシンであるかが分かる。

make した stone および、 libpthread, libopenssl を La Fonera へコピーすれば (libopenssl は SSL 版 stone の場合のみ必要)、 La Fonera 上で stone を実行することができる。

root@OpenWrt:~# stone
Jan  4 00:18:08.478674 1024 start (2.3c) [14946]
Jan  4 00:18:08.488958 1024 stone 2.3c  http://www.gcd.org/sengoku/stone/
Jan  4 00:18:08.570372 1024 Copyright(C)2007 by Hiroaki Sengoku <sengoku@gcd.org>
Jan  4 00:18:08.579479 1024 using OpenSSL 0.9.8d 28 Sep 2006  http://www.openssl.org/
Usage: stone <opt>... <stone> [-- <stone>]...
opt:  -h opt            ; help for <opt> more
      -h stone          ; help for <stone>
      -h ssl            ; help for <SSL>, see -q/-z opt


hiroaki_sengoku at 09:32|この記事のURLComments(1)TrackBack(0)stone 開発日記  | La Fonera
このエントリーを含むブックマーク 2007年01月01日

あけましておめでとうございます。 今年もよろしくお願いします。

fj.comp.dev.misc に昨年12月26日に投稿された記事 「USB赤外線リモコン on Mac OS X」で、
河野真治 @ 琉球大学情報工学 さん曰く:

パソコン用学習リモコン PC-OP-RS1
昔は、Crossam 2+ と、そのUSB version があったんだけど、 ちょっと高い。もう売ってないし。 これは、5,000円切っている。 もちろん、ドライバはWindows しかありませんが、 シリアルドライバなんて、なんとでもなる。libusb使ってもいいし。

鎌倉にお参りした帰り、たまたま立ち寄った ラゾーナ川崎 のビックカメラにて、 上記 PC-OP-RS1 を見つけたので衝動買い。

BUFFALOの学習リモコンPC-OP-RS1をLinux(fc3)で使う」を参考にしながら、 perl でテストプログラムを書いてみる。 PC-OP-RS1 は普通のシリアルデバイスとして Linux から見えるので、 CPAN の Device::SerialPort を使えば簡単にコントロールできる。 赤外線リモコンの信号を受信するには、

--> 0x72
<-- 0x59
(リモコン受信)
<-- 0x53
<-- リモコンデータ * 240Byte
<-- 0x45

とするだけ。 そして、受信した 240バイトのデータ(固定長)を、

--> 0x74
<-- 0x59
--> 0x31 ※ 1ch=0x31,2ch=0x32,3ch=0x33,4ch=0x34
<-- 0x59
--> リモコンデータ * 240Byte
<-- 0x45

などと PC-OP-RS1 へ送ってやれば、 PC-OP-RS1 から赤外線が発射される。
ビデオの赤外線受信部近くに、PC-OP-RS1 ↓ の送信部を設置した (両面テープでラックに固定)。

  PC-OP-RS1 & VTR

ラック下段にある PC の上の黒い箱状の ↑ モノが PC-OP-RS1 本体 (拡大写真)。

即席で作った perl スクリプト「irrc」(後述) を、 次のように「-r」オプション付で実行しておいて、 PC-OP-RS1 の受光部へ赤外線リモコンを向けてリモコンのボタンを押す。

% irrc -r
ffffffff0f00000080ff000000fc030000f01fc07f000000fe01fc070000e03f000000ff01fe030000f01f000080ff00fe010000f80fe03f000000ff000000fc07f01fc03f000000ff01fc07f80fe03f807f00ff01fc03f80f0000c07f00ff00fe03f80fe01fc07f00ffffffff07000000c03f000000ff010000f80fe01f000000ff00fe030000f01f0000807f00ff010000f80f0000c03f80ff000000fc07f00f0000c07f000000fe03f807f01f000080ff00fe01fc07f00fe03f80ff00fe01fc070000e03f807f00ff01fc03f80fe03f80ffffffff01000000f01f0000000000000000000000000000000000feffff

すると、このように 240 バイトのデータが 16進数で表示される。 このデータを irrc スクリプトの先頭部分で、

$Ir{'aPower'} = [
    pack("H*", "ffffffff ... 中略 .... 00feffff"),
    ];

などと定義する(ちなみに上記データは私の自宅のエアコンの電源ON/OFF)。 複数のボタンのシーケンスを定義する場合は、

$Ir{'AB'} = [
    pack("H*", "ボタンA を押したときの送信データ"),
    pack("H*", "ボタンB を押したときの送信データ"),
    ];

などと定義すればよい。 ここで定義したシーケンス名 (上記「aPower」や「AB」) を、 irrc スクリプトの引数として渡せば、 PC-OP-RS1 から赤外線が発射される。

以下、irrc スクリプト全体:

#!/usr/bin/perl
use strict;
use warnings;
use Device::SerialPort;
use Getopt::Std;

my %Ir;
$Ir{'vPower'} = [
    pack("H*", "ffffffffffffffffffffff0700000000007ef0831ff8c00f7e00003f00800ffc00003f00801f00c00700f00300f8c10f7c00003f00801f00e00700f0831f00c00f7ee0033ff8c10ffc00003ff00100fc00007e00001f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
    ];
$Ir{'aPower'} = [
    pack("H*", "ffffffff0f00000080ff000000fc030000f01fc07f000000fe01fc070000e03f000000ff01fe030000f01f000080ff00fe010000f80fe03f000000ff000000fc07f01fc03f000000ff01fc07f80fe03f807f00ff01fc03f80f0000c07f00ff00fe03f80fe01fc07f00ffffffff07000000c03f000000ff010000f80fe01f000000ff00fe030000f01f0000807f00ff010000f80f0000c03f80ff000000fc07f00f0000c07f000000fe03f807f01f000080ff00fe01fc07f00fe03f80ff00fe01fc070000e03f807f00ff01fc03f80fe03f80ffffffff01000000f01f0000000000000000000000000000000000feffff"),
    ];

our ($opt_v, $opt_r, $opt_d, $opt_c);
getopts("vrd:c:") || &help;
my $Verbose = $opt_v;
my $device;
if ($opt_d) {
    $device = $opt_d;
} else {
    $device = "/dev/ttyUSB0";
}
my $ch = 1;
if ($opt_c && $opt_c =~ /^[1-4]$/) {
    $ch = ord($opt_c) - ord('0');
    print STDERR "Ch: $ch\n" if $Verbose;
}
my $port = &openDev;
&sendData($port, "69");		# LED command
&expect("4f", &readData($port, 1));
if ($opt_r) {
    my $data = &receive($port);
    print unpack("H*", $data), "\n";
}
while ($_ = shift @ARGV) {
    if (defined $Ir{$_}) {
	print STDERR "Tx: $_\n" if $Verbose;
	for my $ir (@{$Ir{$_}}) {
	    &transmit($port, $ch, $ir);
	}
    } else {
	print STDERR "Unknown command: $_\n";
    }
}
exit 0;

sub openDev {
    my $port = new Device::SerialPort($device) || die;
    $port->user_msg(1);
    $port->error_msg(1);
    $port->baudrate(115200);
    $port->databits(8);
    $port->parity("none");
    $port->stopbits(1);
    $port->handshake("none");
    $port->read_const_time(100); # 0.1 sec
    $port->read_char_time(5);
    $port;
}

sub transmit {
    my ($port, $ch, $data) = @_;
    &sendData($port, "74");		# transmit
    &expect("59", &readData($port, 1));
    &sendData($port, sprintf("3%d", $ch % 10));
    &expect("59", &readData($port, 1));
    $port->write($data) || die;
    &expect("45", &readData($port, 1));
}

sub receive {
    my ($port) = @_;
    &sendData($port, "72");		# receive
    &expect("59", &readData($port, 1));
    &expect("53", &readData($port, 1, -1));
    my $data = &readData($port, 240);
    &expect("45", &readData($port, 1));
    $data;
}

sub sendData {
    my ($port, $str) = @_;
    $port->write(pack("H*", $str)) || die;
}

sub readData {
    my ($port, $len, $timeout) = @_;
    my $i = 0;
    my $j = 0;
    my $data;
    if (! defined $timeout) {
	$timeout = 10;
    }
    while ($i < $len) {
	my ($l, $d) = $port->read(1);
	if ($l > 0) {
	    $data .= $d;
	    $i += $l;
	    $j = 0;
	} else {
	    $j++;
	    if ($timeout > 0 && $j > $timeout) {
		print STDERR "TIMEOUT to read $len byte\n";
		exit 1;
	    }
	}
    }
    if ($Verbose) {
	print STDERR "read: ", unpack("H*", $data), "\n";
    }
    $data;
}

sub expect {
    my ($ex, $d) = @_;
    my $str = unpack("H*", $d);
    if ($str ne $ex) {
	print STDERR "expect $ex, but got $str\n";
	exit 1;
    }
}

sub help {
    print STDERR <<EOF;
Usage: psoprs1.pl [opt] <com>...
opt:   -d <dev>   set device
       -c <ch>    set channel
       -r         receive
       -v         verbose
EOF
    print "com: ", join(" ", sort keys %Ir), "\n";
    exit 1;
}

「irrc vPower」を実行すれば、ビデオの電源を ON/OFF し、 「irrc -c 2 aPower」を実行すれば、エアコンの電源を ON/OFF できる。 もちろんエアコンの電源を Linux から ON/OFF できてもあまり嬉しくないが、 CS/BS 放送や CATV のチューナのチャンネルを Linux から切り替えて、 そのチューナの出力を Linux マシンで予約録画できると便利。