Linuxにおけるplatform device APIについて
はじめに
自分でドライバ(というかカーネルモジュール)を作ることなんてほとんどないし、 昨今はそういうローレベルな話は流行らないような気がするけど、 それでもやはりドライバを作りたいときがある。 今回は、Linuxが勝手に情報を見つけてよしなにやってくれないデバイスに関して ドライバを作る際に便利そうなplatform device APIを紹介する。
platform device APIとは、PCIeデバイスやUSBデバイスのようなLinux KernelとかBIOSが うまいこと情報を見つけてきてくれるようなデバイスではないものに対して、 IRQ X番で割り込みが上げられるよ、とか、MMIO空間はアドレスxxxxからxxxxまでだよ、などの デバイスが持つ資源の情報を手動でカーネルに登録するためのもの。
そもそもそんなデバイスなんてあるのか
例えばOn Chipのデバイスがそうらしい。情報を出す規格があるわけではないので、MMIO空間は見えても、 何ができるか*1まではスペックシートを 見ないとわからんことが多い(と思う)。
なお、PCI(PCIeも含む)は規格でどういうことができるかを示すレジスタ(Capability Register)を 実装しなきゃいけないので、そういう心配は無用。 Capability Registerに関してはこれがわかりやすい。 BIOSがPCI Expressを初期化する手順が見えてきた: なひたふJTAG日記
デバイス登録までのおおよその流れ
以下の2ステップで終わる。簡単!
- platform deviceの資源情報を持つ構造体を作成する
- platform_device_register_simpleでplatform deviceを登録する
platform deviceの資源情報を持つ構造体の作成
これはまあ普通に構造体を作るだけ。新しく例を作るのも何なので、 The platform device API [LWN.net]に乗ってる例(MMIO空間が0x10000000~0x10001000まで、 IRQ 20に繋がれているデバイス)を以下に引用する。
#include <linux/platform_device.h> static struct resource foomatic_resources[] = { { .start = 0x10000000, .end = 0x10001000, .flags = IORESOURCE_MEM, .name = "io-memory" }, { .start = 20, .end = 20, .flags = IORESOURCE_IRQ, .name = "irq", } }
platform_device_register_simpleでplatform deviceを登録する
platform_device_register_simpleのインタフェースは以下のとおり。
入れればよい。
// linux 4.9 include/linux/platform_device.h 136 static inline struct platform_device *platform_device_register_simple( 137 const char *name, int id, 138 const struct resource *res, unsigned int num) 139 { 140 return platform_device_register_resndata(NULL, name, id, 141 res, num, NULL, 0); 142 }
platform deviceを登録したあとはどうすればいいの
platform deviceのdriverを登録する。 以下のplatform_driverの構造体のうち、 最低限
- probe
- remove
- driver
があればOK。ID tableはなくてもよい。
struct platform_driver { int (*probe)(struct platform_device *); int (*remove)(struct platform_device *); void (*shutdown)(struct platform_device *); int (*suspend)(struct platform_device *, pm_message_t state); int (*resume)(struct platform_device *); struct device_driver driver; const struct platform_device_id *id_table; };
要はこんな感じ(The platform device API [LWN.net]に乗ってる例を引用)。
static struct platform_driver i2c_gpio_driver = { .driver = { .name = "i2c-gpio", .owner = THIS_MODULE, }, .probe = i2c_gpio_probe, .remove = __devexit_p(i2c_gpio_remove), };
driverを作るのに役に立つかもしれないAPI
このへんのAPIで登録したデバイス情報を引いてこられるけど、 どういう場面で使うのかいまいちよくわからない。 platform deviceのようなニッチデバイスの情報を登録する人とドライバ作る人が別なのかな。
struct resource *platform_get_resource(struct platform_device *pdev, unsigned int type, unsigned int n); struct resource *platform_get_resource_byname(struct platform_device *pdev, unsigned int type, const char *name); int platform_get_irq(struct platform_device *pdev, unsigned int n);
参考
*1:例えば割り込みがあげられるとか、そういうの