いとうと申します。困っている事があり、アドバイスをいただきたくメールし
ます。

TriState のデジタル気圧計に FreeBSD 8.3-RELEASE でアクセスを試みていま
す。こんな↓やつで、PIC18F2550 というチップを使っています。

- http://www.tristate.ne.jp/digitalbarome.htm
- http://www.tristate.ne.jp/ftp/manu029.pdf

umodem を使うと

ugen1.4: <Mecanique> at usbus1
umodem0: <Mecanique Swordfish Virtual COM Port, class 2/0, rev 2.00/1.10, addr 
4> on usbus1
umodem0: data interface 1, has no CM over data, has no break

と認識され、cu -l /dev/ttyU0 で一応の動作は確認できています。

ところが擬似コードで書くと(実際のスクリプトは長いので末尾に載せます)

#!/usr/bin/perl

while (1) {
      sysopen($fd, O_RDWR | O_NONBLOCK);
      tcgetattr();
      cflag |= CLOCAL;
      iflag |= ICANON;
      iflag &= ~(ICRNL | INLCR);
      oflag &= ~OPOST
      tcsetattr();
      select(undef, fds, undef, 1.0);
      syswrite();
      sleep(1); # これを入れないと応答が欠ける
      select(fds, undef, undef, 1.0);
      sysread();
      close();
}

のような操作をしたところ、回数は一定しないんですがループの15回目、24回
目、5回目、3回目、17回目、のような回数で、sysread()の手前の select()が
タイムアウトしてしまいます。

一度こうなると、cu でアクセスしても反応がありません。

また、手作業なので定量的なことは言えませんが、cu で コマンドを送って応
答を受信して切断、という操作を何度も繰り返すと、同様の事象が起きました。
接続したままコマンドの送信と応答の受信を繰り返しても、根気の続く範囲の
繰り返しでは、事象は起きませんでした。

この事象が発生したとき、FreeBSD の OS 自体はハングアップしておらず、他
のプロセスは何の問題もなく動作しているように見え、気圧計の側については
よくわからないんですが、LED、LCD の表示を見る限り正常動作を続けているよ
うに見えます。USB ケーブルを抜き差しすると元通りコマンドと応答の送受信
ができる状態に復帰します。

それと、カーネルの心得は全然ないんですが、何か手がかりがつかめないかと
思い、sysctl で hw.usb.umodem.debug を1にしてみると、事象が起きないとい
うお決まりのパターンです。

別の PC でですが、Ubuntu 12.04LTS では、同じスクリプトを実行しても事象
は起きません。

一方

#!/usr/bin/perl

sysopen($fd, O_RDWR | O_NONBLOCK);
tcgetattr();
cflag |= CLOCAL;
iflag |= ICANON;
iflag &= ~(ICRNL | INLCR);
oflag &= ~OPOST
tcsetattr();
while (1) {
      select(undef, fds, undef, 1.0);
      syswrite();
      sleep(1); # これを入れないと応答が欠ける
      select(fds, undef, undef, 1.0);
      sysread();
}

のように close()しないままループを回すと100回以上回しても平気です。

TriState の Web ページにある「★USB通信の不安定について」については、購
入時期がつい最近ですし、目視でも該当していないように見えること、上述の
ように Ubuntu では事象が起きないことから、手許の個体は該当していないと
判断しています。

以上のような状況で困っています。どなたかアドバイスをいただけないでしょ
うか? どうぞよろしくお願いします。

実際のスクリプト:
#!/usr/bin/perl

use strict;
use warnings;
use Fcntl;
use POSIX;

my ($fh, $fd, $fds, $tios, $tios0, $c_cflag, $c_iflag, $c_oflag,
        $tbuf, $rbuf, $tlen, $k, $n);

$k = 1;
$tbuf = "BARH\r\n";
$tlen = length($tbuf);
#while (1) {    # while (1) は、どちらか一方だけ生かす
        if (!sysopen($fh, $ARGV[0], O_RDWR | O_NONBLOCK) ) {
                print(STDERR "$0: open($ARGV[0]): $!\n");
                exit(1);
        }
        $fd = fileno($fh);
        $tios = POSIX::Termios->new();
        $tios0 = POSIX::Termios->new();
        if (!($tios->getattr($fd) ) ) {
                print(STDERR "$0: getattr($ARGV[0]): $!\n");
                exit(1);
        }
        $c_cflag = $tios->getcflag();
        $tios->setcflag($c_cflag | POSIX::CLOCAL);
        $c_iflag = $tios->getiflag();
        $tios->setiflag($c_iflag | POSIX::ICANON);
        $tios->setiflag($c_iflag & ~(POSIX::ICRNL | POSIX::INLCR) );
        $c_oflag = $tios->getoflag();
        $tios->setoflag($c_oflag & ~POSIX::OPOST);
        if (!$tios->setattr($fd, POSIX::TCSADRAIN) ) {
                close($fh);
                print(STDERR "$0: setattr($ARGV[0]): $!\n");
                exit(1);
        }
        while (1) {     # while (1) は、どちらか一方だけ生かす
                print(STDERR "k = $k\n");
                $fds = '';
                vec($fds, fileno($fh), 1) = 1;
                print(STDERR "calling send select()\n");
                if (!($n = select(undef, $fds, undef, 1.0) ) ) {
                        $tios0->setattr($fd, POSIX::TCSADRAIN);
                        close($fh);
                        print(STDERR "$0: send select(): timeout\n");
                        exit(1);
                }
                print(STDERR "send select(): n = $n\n");
                print(STDERR "calling syswrite()\n");
                if (!($n = syswrite($fh, $tbuf, $tlen) ) ) {
                        $tios0->setattr($fd, POSIX::TCSADRAIN);
                        close($fh);
                        print(STDERR "$0: write($ARGV[0]): $!\n");
                        exit(1);
                }
                print(STDERR "back from syswrite()\n");
                sleep(1);       # unanalyzed timing issue...
                $fds = '';
                vec($fds, fileno($fh), 1) = 1;
                print(STDERR "calling recv select()\n");
                if (!($n = select($fds, undef, undef, 1.0) ) ) {
                        $tios0->setattr($fd, POSIX::TCSADRAIN);
                        close($fh);
                        print(STDERR "$0: recv select(): timeout\n");
                        exit(1);
                }
                print(STDERR "calling sysread()\n");
                if (!($n = sysread($fh, $rbuf, 16) ) ) {
                        $tios0->setattr($fd, POSIX::TCSADRAIN);
                        close($fh);
                        print(STDERR "$0: read($ARGV[0]): $!\n");
                        exit(1);
                }
                print(STDERR "back from sysread()\n");
                print("n = $n, rbuf = $rbuf\r\n");
                $k++;
        }
        $tios0->setattr($fh, POSIX::TCSADRAIN);
        close($fh);
#}
exit(0);

-----
kkdlabs.jp, featuring Koh-ichi Ito as just another DNS freak in town.

メールによる返信