LoginSignup
3
1

More than 5 years have passed since last update.

Raspberry Pi Mouse の /dev/rtswitch の挙動について

Last updated at Posted at 2019-02-01

はじめに

Raspberry Pi Mouse というのはRaspberry Piを搭載した車輪型ロボットです。

Raspberry Pi Mouse | 製品情報 | 株式会社ア-ルティ

Raspberry Pi Mouse に搭載されているセンサやアクチュエータは Linux ではデバイスファイルとして見えるようになっています。
/dev/rtswitch{0,1,2}はこのロボットの三つのタクトスイッチの状態へのアクセスを提供しているようです。

以下の記事で Raspberry Pi Mouse のタクトスイッチの読み取りで困っている人がいたので、この記事を書くことにしました。

Pythonでデバイスをリアルタイムに読み書きをする方法?

上掲の記事ではプログラムを Python で実装しています。
この記事を書いた彼が悩んでいたことは、/dev/rtswitch0open して一度 read するとそれ以降何も値が読み取れないことでした。
なぜこのようなことになっているのか探っていくことにしましょう。

公式マニュアルを読む

さて、問題のタクトスイッチの読み取りですが、まずは公式マニュアル
RaspberryPi Mouse 取扱い説明書 2.0版
を見ていくことにしましょう。
マニュアルの6.5節(19ページ以降)にタクトスイッチについての記述があります。
図6-5(マニュアル20ページ)を見ると、cat コマンドを使ってタクトスイッチの状態を調べることができるようです。

公式マニュアルから引用すると、タクトスイッチが押されている状態では

$ cat /dev/rtswitch0
0

となって 0 が返ってきて、タクトスイッチが押されていない状態では

$ cat /dev/rtswitch0
1

となって 1 が返ってくるようです。

ここで cat の挙動を思い出して見ましょう。cat コマンドは与えられたファイルから読み出せる限り読み出したデータを標準出力に書き出します。
すると上記の挙動からして /dev/rtswitch{0,1,2} は一度状態を出力するともうそれ以上は何も出力しないと考えられます。
もし、読み出される度に、つまり read システムコールが発行される度にタクトスイッチの状態を返すのだとすれば、cat は無限に /dev/rtswitch{0,1,2} を読み出し続けいつまでも止まらなくなってしまいます。

従って、/dev/rtstwitch{0,1,2} はタクトスイッチの状態を読み出す度に open して read するのが想定されている使い方ということになります。

ソースコードを読む

ここからはLinuxのデバイスドライバについての知識が必要ですが、この記事は特にそのあたりの知識はなくても読めるかと思います。
詳しく知りたい場合は、オライリーから出版されている
Linuxデバイスドライバ 第3版
を読んでください。

幸いにして Raspberry Pi Mouse のデバイスドライバのソースコードが公開されているので、これを見ていくことにしましょう。
src/drivers/rtmouse.c というファイルがそのソースコードになります。

このソースコードの1193行目から1196行目に /dev/rtswitch の定義があります。

src/dev/drivers/rtmouse.c
/* /dev/rtswitch */ 
static struct file_operations sw_fops = { 
    .open = dev_open, .read = sw_read, .release = dev_release,
};

file_operations構造体は Linux のソースコード linux/fs.h で定義されている構造体で、デバイスのファイル操作関数を表現します。
/dev/rtswitch においては openreadrelease の三つの操作が定義されています。
まずは open がどのように実装されているかを見ていきます。

関数dev_opensrc/drivers/rtmouse.c
の751行目から764行目にありますが、実質的な操作は何もしていません。

次に関数sw_readを見ていきます。この関数が本質的です。
この関数の宣言を見ると

src/drivers/rtmouse.c
static ssize_t sw_read(struct file *filep, char __user *buf, size_t count, loff_t *f_pos)

となっています。
引数filepの型はfile構造体のポインタで、この構造体にはOSが管理するファイルについての情報が色々と入っています。
引数bufはユーザーランドにあるメモリバッファです。ここに読み出したデータが書き込まれます。引数countはシステムコールを呼び出したユーザーが何bytesのデータを欲しがっているかが入っています。
そして、引数f_posはファイルのオフセットへのポインタです。

src/drivers/rtmouse.cの524-525行目を見ると、

src/drivers/rtmouse.c
if (*f_pos > 0)
    return 0; /* End of file */

となっているので、オフセットが 0 より大きい場合には End of File になっていてそれ以上データを読み出しません。
なので、/dev/rtswitch を一度読み出したらもうそれ以上は何も読み出せないというのは正しそうに思えます。

しかしながら、我々はファイルのオフセットを変更する手段を持っています。それは lseek システムコールです。ユーザー側から

lseek(fd, 0, SEEK_SET)

とすればオフセットが0に戻って、/dev/rtswitchからまたタクトスイッチの状態を読み出せるようになるはずだというのがわかります。
追記
実物が手元にないのでlseekの部分は実際に上手くいくかわからない。
この部分はあまり信用しないでほしい。
ただ、このページ
https://www.oreilly.com/library/view/linux-device-drivers/0596000081/ch03s03.html
を読む限り、

The llseek method is used to change the current read/write position in a file, and the new position is returned as a (positive) return value. The loff_t is a “long offset” and is at least 64 bits wide even on 32-bit platforms. Errors are signaled by a negative return value. If the function is not specified for the driver, a seek relative to end-of-file fails, while other seeks succeed by modifying the position counter in the file structure (described in Section 3.4 later in this chapter).

とあって、file_operations構造体でllseekが指定されていない場合でもEOFからのシーク以外は可能らしいので多分大丈夫なはずです。

追記(2019/2/3)
lseekのくだりは完全に私の勘違いで間違っています。
Linuxのlseekシステムコールはソースコードを読むと、file_operations構造体のllseekメンバがNULLである場合は、no_llseekという-ESPIPEを返すだけの関数を呼ぶようです。

まとめ

以上で公式マニュアルとソースコードを読むことで /dev/rtswitchの挙動を理解することができました。

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1