ioctlの引数

デバッグ情報を付けて再度テストしたところ、ioctl()の引数に問題があることが分かりました。

// 注: バグありコード
int axes() const
{
    int value;
    if (::ioctl(fd_, JSIOCGAXES, &value) != -1)
        return value;
    else
        return 0;
}

ioctl()はエラーなら-1を返します。
ここでエラーでないのにvalueに壊れた値が返ってきていました。
ioctl()の呼び出し前にvalueを0にしておくとなぜか正しい値が返ってきて、変だなぁと思ってヘッダを確認したところ、JSIOCGAXESのバッファサイズは8ビットになっていました。

#define JSIOCGAXES              _IOR('j', 0x11, __u8)                           /* get number of axes */

後ろの__u8が引数の(指しているデータの)型のようです。
8ビットだけ書き換えられて、残り24ビットは未初期化のゴミが入っていたわけですね。
というわけで、コードは次のようになりました。

boost::uint8_t axes() const
{
    boost::uint8_t value;
    if (::ioctl(fd_, JSIOCGAXES, &value) != -1)
        return value;
    else
        return 0;
}

ioctl()には文字列か構造体のポインタかintのポインタを渡すものだとばかり思い込んでいました。


教訓: ioctl()にポインタを渡す際はサイズに気をつけましょう。