writing-clients 和訳

これは、LinuxをI2CまたはSMBusにおいて、プロトコルの(スレーブでは無く)マスターとした、デバイスドライバを書きたい人のための小さなガイドです。

ドライバーをセットアップするには、いくつかのことを行う必要があります。
いくつかのことはオプションであり、いくつかのことは少し、あるいはまったく違った方法で行うことができます。
ルールブックとしてではなく、ガイドとして使用してください!


一般的な注釈
===============

カーネルの名前空間はできるだけクリーンに保つようにしてください。
その為の最良の方法は、すべてのグローバルシンボルに一意な接頭語を使うことです。
これはエクスポートされたシンボルに対して特に重要ですが、エクスポートされていないシンボルに対しても行うのがよいでしょう。
このチュートリアルでは `foo_’ というプレフィックスを使います。


ドライバーの構造
====================

通常、1つのドライバ構造体を実装し、すべてのクライアントをそこからインスタンス化します。
ドライバ構造体には、一般的なアクセス・ルーチンが含まれており、あなたが提供するデータを持つフィールド以外は、ゼロ初期化する必要があることを覚えておいてください。
クライアント構造体は、ドライバー・モデルのデバイス・ノードやI2Cアドレスのような、デバイス固有の情報を保持します。

static struct i2c_device_id foo_idtable[] = {
{ “foo”, my_id_for_foo },
{ “bar”, my_id_for_bar },
{ }
};

MODULE_DEVICE_TABLE(i2c, foo_idtable);

static struct i2c_driver foo_driver = {
.driver = {
.name = “foo”,
.pm = &foo_pm_ops, /* optional */
},

.id_table = foo_idtable,
.probe = foo_probe,
.remove = foo_remove,
/* if device autodetection is needed: */
.class = I2C_CLASS_SOMETHING,
.detect = foo_detect,
.address_list = normal_i2c,

.shutdown = foo_shutdown, /* optional */
.command = foo_command, /* optional, deprecated */
}

nameフィールドはドライバー名で、スペースを含んではなりません。
MODULE_ALIAS(この例では “foo “を渡す)を使ってモジュールに別の名前を追加することもできますが、(ドライバーをモジュールとしてコンパイルできる場合は)モジュール名と一致させる必要があります。
ドライバ名がモジュール名と一致しない場合、モジュールは自動的にロードされません(hotplug/coldplug)。

その他のフィールドはすべてコールバック関数用です。


追加のクライアントデータ
=================

各クライアント構造体には特別な `data’ フィールドがあり、任意の構造体を指すことができます。
これをデバイス固有のデータを保持するために使うべきです。

/* store the value */
void i2c_set_clientdata(struct i2c_client *client, void *data);

/* retrieve the value */
void *i2c_get_clientdata(const struct i2c_client *client);

カーネル 2.6.34 以降では、remove() や probe() が失敗した場合に `data’ フィールドを NULL にする必要はなくなりました。
これらの場合は i2c-core が自動的にこれを行います。i2c-coreがこのフィールドに触れるのは、この時だけです。


クライアントへのアクセス
====================

有効なクライアント構造があるとします。ある時点で、クライアントから情報を収集したり、クライアントに新しい情報を書き込んだりする必要があります。

これには、foo_read関数とfoo_write関数を定義しておくと一般的です。
場合によっては、i2c関数を直接呼び出す方が簡単なこともありますが、多くのチップは、カプセル化しやすい何らかのレジスタ値のアイデアを持っています。

以下の関数は単純な例です。単純にコピーできるものではありません。

int foo_read_value(struct i2c_client *client, u8 reg)
{
if (reg < 0x10) /* byte-sized register */
return i2c_smbus_read_byte_data(client, reg);
else /* word-sized register */
return i2c_smbus_read_word_data(client, reg);
}

int foo_write_value(struct i2c_client *client, u8 reg, u16 value)
{
if (reg == 0x10) /* Impossible to write – driver error! */
return -EINVAL;
else if (reg < 0x10) /* byte-sized register */
return i2c_smbus_write_byte_data(client, reg, value);
else /* word-sized register */
return i2c_smbus_write_word_data(client, reg, value);
}


プロービングとアタッチメント
=====================

Linux I2Cスタックは元々、PCマザーボード上のハードウェア・モニタリング・チップへのアクセスをサポートするために書かれたものです。
そのため、I2CよりもSMBus(およびPC)に適したいくつかの仮定が組み込まれていました。
これらの前提の1つは、ほとんどのアダプタやデバイス・ドライバが、デバイスの存在をプローブするSMBUS_QUICKプロトコルをサポートしていることでした。
もう1つは、デバイスとそのドライバは、このようなプローブ・プリミティブだけを使って十分に設定できるということだった。

LinuxとそのI2Cスタックが、組み込みシステムやDVBアダプターのような複雑なコンポーネントで広く使われるようになると、これらの仮定はより問題になってきました。
割り込みを発行するI2Cデバイスのドライバは、より多くの(そして異なる)コンフィギュレーション情報を必要とし、プロトコル・プロービングでは識別できないチップのバリエーションを扱うドライバや、正しく動作するためにボード固有の情報を必要とするようになっています。


デバイスドライバのバインディング
———————

システム基盤、典型的にはボード固有の初期化コードやブートファームウェアは、どのようなI2Cデバイスが存在するかを報告します。
例えば、カーネルやブートローダーに、I2Cデバイスを識別し、IRQやその他の配線、チップタイプなどのボード固有の設定情報にリンクするテーブルがあるかもしれません。
それを使って、I2Cデバイスごとにi2c_clientオブジェクトを作成することができます。

このバインディング・モデルを使用するI2Cデバイス・ドライバは、Linuxの他の種類のドライバと同じように動作する:デバイスにバインドするためのprobe()メソッドと、バインドを解除するためのremove()メソッドを提供します。

static int foo_probe(struct i2c_client *client, const struct i2c_device_id *id);
static int foo_remove(struct i2c_client *client);

i2c_driverはクライアントのハンドルは作成しません。
ハンドルは、foo_probe() 中で使用することができます。
foo_probe()が成功(負のステータスコードではなくゼロ)を報告した場合、ハンドルを保存して foo_remove()が返されるまで使用することができます。
このバインディング・モデルは、ほとんどのLinuxドライバで使用されています。

probe関数は、id_table name フィールドのエントリがデバイス名と一致したときに呼び出されます。
一致したエントリーが渡されるので、ドライバはテーブルのどれが一致したかを知ることができます。


デバイスの作成
—————

あるI2CバスにI2Cデバイスが接続されていることが分かっていれば、i2c_board_info構造体にデバイス・アドレスとドライバ名を入れてi2c_new_device()を呼び出すだけで、そのデバイスをインスタンス化できます。
これでデバイスが生成され、ドライバコアが適切なドライバを探して、その probe() メソッドを呼び出します。
ドライバが異なるデバイス・タイプをサポートしている場合、type フィールドを使って希望のタイプを指定できます。
必要であれば、IRQとプラットフォーム・データも指定できます。

あるデバイスがあるI2Cバスに接続されていることは知っていても、そのデバイスが使っている正確なアドレスがわからないことがあります。
例えばTVアダプタでは、同じドライバが何十もの微妙に異なるモデルをサポートし、I2Cデバイス・アドレスがモデルごとに変わっているような場合に、このようなことが起こります。
i2c_new_probed_device() は i2c_new_device() と似ていますが、プローブする I2C アドレスのリストを追加で受け取る点が異なります。
リストの中で最初に応答があったアドレスに対してデバイスが生成されます。
アドレス範囲に複数のデバイスが存在すると予想される場合は、 i2c_new_probed_device() をその回数だけ呼び出すだけでよいです。

i2c_new_device() または i2c_new_probed_device() の呼び出しは、 通常 I2C バスドライバ内で行われます。
返された i2c_client リファレンスは、後で使用するために保存しておくとよいでしょう。


デバイスの検出
—————-

あるI2CバスにどのI2Cデバイスが接続されているか、事前にわからないことがあります。
例えば、PCのSMBus上のデバイスを監視するハードウェアの場合です。
このような場合、ドライバがサポートされているデバイスを自動的に検出するようにするとよいでしょう。
これは、レガシーモデルがどのように動作していたかであり、現在は標準ドライバモデルの拡張として利用可能です。

単純に、サポートされているデバイスの識別を試みる検出コールバック(サポートされているデバイスは0を返し、サポートされていないデバイスは-ENODEVを返す)、プローブするアドレスのリスト、デバイスタイプ(またはクラス)を定義して、そのタイプのデバイスが接続されている可能性がある(他に列挙されていない)I2Cバスだけがプローブされるようにする必要があります。
例えば、自動検出が必要なハードウェアモニタリングチップ用のドライバは、 そのクラスを I2C_CLASS_HWMON に設定し、 I2C_CLASS_HWMON を含むクラスを持つ I2C アダプタだけがこのドライバによってプローブされます。
一致するクラスがないからといって、指定されたI2Cアダプタでそのタイプのデバイスを使用できなくなるわけではないことに注意しましょう。デバイスの明示的なインスタンス化はまだ可能です。

このメカニズムは純粋にオプションであり、すべてのデバイスに適しているわけではないことに注意してください。
そうでなければ、誤検出が発生しやすく、事態が急速に悪化する可能性があります。
I2Cプロトコルには、デバイスを識別する標準的な方法はおろか、与えられたアドレスにチップが存在することを検出する標準的な方法も含まれていないことを覚えておいてください。
さらに悪いことに、バス転送に関連するセマンティクスが欠如しているため、同じ転送が、あるチップからは読み出し操作とみなされ、別のチップからは書き込み操作とみなされる可能性があります。
これらの理由から、可能な限り、自動検出よりも明示的なデバイスのインスタンス化を常に優先すべきです。


デバイスの削除
—————

i2c_new_device() または i2c_new_probed_device() を使用して作成された各 I2C デバイスは、 i2c_unregister_device() をコールすることで登録を解除できます。
明示的に呼び出さない場合、デバイスドライバモデル内でデバイスが親を存続させることはできない為、I2C バス自体が削除される前に自動的に呼び出されます。


ドライバーの初期化
=======================

カーネルが起動したとき、あるいはあなたのフー・ドライバー・モジュールが挿入されたとき、いくつかの初期化をしなければならない。
幸運なことに、通常はドライバー・モジュールを登録するだけで十分です。

static int __init foo_init(void)
{
return i2c_add_driver(&foo_driver);
}
module_init(foo_init);

static void __exit foo_cleanup(void)
{
i2c_del_driver(&foo_driver);
}
module_exit(foo_cleanup);

The module_i2c_driver() macro can be used to reduce above code.

module_i2c_driver(foo_driver);

いくつかの関数は `__init’ でマークされていることに注意して下さい。 これらの関数はカーネルの起動(またはモジュールのロード)が完了した後に削除することができます。
同様に、`__exit’ でマークされた関数は、そのコードがコンパイル時にカーネルに組み込まれる際は呼び出されません。


ドライバーの情報
==================

/* 自分の名前とEメールアドレスを代入する */
MODULE_AUTHOR(“Frodo Looijaard <frodol@dds.nl>”
MODULE_DESCRIPTION(“Driver for Barf Inc. Foo I2C devices”);

/* いくつかの非GPLライセンスも認められています。 */
MODULE_LICENSE(“GPL”);


電源管理
================

トランシーバを低電力モードにしたり、システム・ウェイクアップ・メカニズムをアクティブにするなど、I2Cデバイスがシステムの低電力状態に入る際に特別な処理が必要な場合は、ドライバのdev_pm_ops(suspendやresumeなど)に適切なコールバックを実装してください。

これらは標準的なドライバ・モデルのコールであり、他のドライバ・スタックと同じように動作します。
これらのコールはスリープすることができ、サスペンドまたはレジュームされるデバイスへのI2Cメッセージングを使用することができます(これらのコールが発行されるとき、それらの親I2Cアダプタはアクティブであり、IRQはまだ有効であるため)。


システムのシャットダウン
===============

システムのシャットダウンや再起動(kexecを含む)時に、I2Cデバイスに特別な処理が必要な場合(何かをオフにするような場合)には、shutdown()メソッドを使用します。

繰り返しになりますが、これは標準的なドライバ・モデルの呼び出しであり、他のドライバ・スタックと同じように動作します。


コマンド機能
================

汎用的なioctlのような関数のコールバックがサポートされています。これが必要になることはめったにないだろうし、いずれにせよこの使用は非推奨なので、新しい設計では使用しないほうがいいでしょう。


送信と受信
=====================

デバイスと通信したい場合、そのための関数がいくつかあります。
これらはすべて<linux/i2c.h>にあります。

もし、プレーンなI2C通信とSMBusレベルの通信のどちらかを選択できるのであれば、後者を使用してください。
すべてのアダプタはSMBusレベルのコマンドを理解しますが、プレーンI2Cを理解できるのは一部のアダプタだけです!


通常のI2C通信
———————–

int i2c_master_send(struct i2c_client *client, const char *buf, int count);
int i2c_master_recv(struct i2c_client *client, char *buf, int count);

これらのルーチンは、クライアントから/クライアントへいくつかのバイトを読み書きします。
クライアントにはi2cアドレスが含まれているので、それを含める必要はありません。
第2引数には読み書きするバイト数、第3引数には読み書きするバイト数(バッファの長さより小さくなければならず、msg.len は u16 なので 64k 以下でなければならない)を指定します。
実際に読み書きされたバイト数が返されます。

int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msg, int num);

これは一連のメッセージを送信します。
各メッセージはリードでもライトでもよく、どのようにでも混在させることができます。
トランザクションは結合されます。トランザクション間ではストップビットは送られません。
i2c_msg構造体には、各メッセージのクライアントアドレス、メッセージのバイト数、メッセージデータが格納されます。

I2Cプロトコル自体の詳細については、`i2c-protocol’ ファイルを参照すること。


SMBus通信
——————-

s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data);

上記は一般的な SMBus 関数です。以下の全ての関数はこの関数を用いて実装されています。
上記の関数を直接使用しないでください!

s32 i2c_smbus_read_byte(struct i2c_client *client);
s32 i2c_smbus_write_byte(struct i2c_client *client, u8 value);
s32 i2c_smbus_read_byte_data(struct i2c_client *client, u8 command);
s32 i2c_smbus_write_byte_data(struct i2c_client *client,
u8 command, u8 value);
s32 i2c_smbus_read_word_data(struct i2c_client *client, u8 command);
s32 i2c_smbus_write_word_data(struct i2c_client *client,
u8 command, u16 value);
s32 i2c_smbus_read_block_data(struct i2c_client *client,
u8 command, u8 *values);
s32 i2c_smbus_write_block_data(struct i2c_client *client,
u8 command, u8 length, const u8 *values);
s32 i2c_smbus_read_i2c_block_data(struct i2c_client *client,
u8 command, u8 length, u8 *values);
s32 i2c_smbus_write_i2c_block_data(struct i2c_client *client,
u8 command, u8 length,
const u8 *values);

以下は利用者がいないためi2c-coreから削除されたが、必要であれば後で追加することができます。

s32 i2c_smbus_write_quick(struct i2c_client *client, u8 value);
s32 i2c_smbus_process_call(struct i2c_client *client, u8 command, u16 value);
s32 i2c_smbus_block_process_call(struct i2c_client *client, u8 command, u8 length, u8 *values);

これらのトランザクションはすべて、失敗時に負のerrno値を返します。
writeトランザクションは成功すると0を返し、readトランザクションは読み取った値を返すが、ブロックトランザクションは例外で、読み取った値の数を返します。
ブロック・バッファは32バイトより長くする必要はありません。

実際のSMBusプロトコルの詳細については、`smbus-protocol’ ファイルを参照すること。


汎用ルーチン
========================

以下に、これまで言及されなかったすべての汎用ルーチンを列挙します。

/* 特定のアダプタのアダプタ番号を返す */
int i2c_adapter_id(struct i2c_adapter *adap);

コメント

タイトルとURLをコピーしました