ハードウェアを意識したプログラミングの基礎(後編)
2.Linuxデバイス・ドライバの例
● The Linux Driver Model
Linux 2.6から入ったDriver Modelには,良い点がいろいろとあります.筆者はその中でも,デバイス,バス,ドライバの定義を分けて記述できるところが気に入っています.これだけでも,多くのシステムにデバイス・ドライバを移植する人にはとても便利な機能だと思います.
Driver Modelが標準化される前は,デバイス・ドライバにデバイスのことも接続方法も記述する必要がありました.しかしこれらをきれいに分離することで,アドレスや割り込みのようにシステム依存の部分と機能を提供するドライバの部分に分けることができるようになりました.
それではここからは実際のコードを使って説明します.参考に使っているのは,アットマークテクノから発売されているSUZAKU-V注4というCPUボードです.このボードで使用しているEthernet MAC/PHYの処理を行うLSIを例に説明します.バスとデバイス,そしてドライバの三つを意識して読んでください.
注4;以下のURLを参照.http://www.atmark-techno.com/suzaku-v
このLSIはプラットホーム固有のバスにつながっているため,プラットホーム・デバイスという仕組みを使用します.
137: #define SMC91111_BASE_ADDR 0xf0e00300
138: #define SMC91111_REG_SIZE 16
139: #define SMC91111_IRQ 1
140:
141: static struct resource smc91x_resources[] = {
142: [0] = {
143: .start = SMC91111_BASE_ADDR,
144: .end = SMC91111_BASE_ADDR + SMC91111_REG_SIZE - 1,
145: .flags = IORESOURCE_MEM,
146: },
147: [1] = {
148: .start = SMC91111_IRQ,
149: .end = SMC91111_IRQ,
150: .flags = IORESOURCE_IRQ,
151: },
152: };
153:
154: static struct platform_device smc91x_device = {
155: .name = "smc91x",
156: .id = -1,
157: .num_resources = ARRAY_SIZE(smc91x_resources),
158: .resource = smc91x_resources,
159: };
160:
リスト9の137行目から139行目にSUZAKU-Vで使用している SMSC9111の情報が定義されています.この情報を141行目から始まる struct resourceに入れます.リソース情報はさらに,struct platform_deviceの resourceメンバ変数に代入されます.名前を入れ,SUZAKU-Vで使われているSMSC91111の情報がすべてまとまりました.この情報は,ほかのプラットホーム・デバイスと一緒にまとめられます(リスト10).
187: static struct platform_device *sv_devices[] __initdata = {
:
(中略)
:
189: &smc91x_device,
:
(中略)
:
194: };
195:
196: static int __init sv_device_init(void)
197: {
198: return platform_add_devices(sv_devices, ARRAY_SIZE(sv_devices));
199: }
まとめられたシステム固有の情報は,システムの初期化時に登録されます.ここまでで,プラットホーム・バスにデバイスが登録されたことになります.
さて,このデバイスを動かすためのデバイス・ドライバが必要になります.デバイス・ドライバは,
drivers/net/smc911x.c
です.もちろんこのデバイス・ドライバはプラットホーム・バス用になっています.platform_driver構造体を自分自身の情報で初期化し(リスト11),ドライバの初期化関数で自分自身の情報をカーネルに登録します(リスト12).これでデバイスとデバイス・ドライバが結び付けられ,動作するようになります.
105: #define CARDNAME "smc911x"
:
(中略)
:
2326: static struct platform_driver smc911x_driver = {
2327: .probe = smc911x_drv_probe,
2328: .remove = smc911x_drv_remove,
2329: .suspend = smc911x_drv_suspend,
2330: .resume = smc911x_drv_resume,
2331: .driver = {
2332: .name = CARDNAME,
2333: },
2334: };
2336: static int __init smc911x_init(void)
2337: {
2338: return platform_driver_register(&smc911x_driver);
2339: }
2340:
● 具体的にどうやって結ばれるのか
以下ではデバイスとデバイス・ドライバが結び付けられるまでの過程を説明していきます.
デバイス・ドライバを書く上でフレームワークの中まですべて知る必要はありませんが,中味を知っているとデバイス・ドライバに対する理解が深まるでしょう.
まずは,デバイス・ドライバの中で呼ばれたplatform_driver_register()から始めます(リスト13).
442: int platform_driver_register(struct platform_driver *drv)
443: {
444: drv->driver.bus = &platform_bus_type;
445: if (drv->probe)
446: drv->driver.probe = platform_drv_probe;
447: if (drv->remove)
448: drv->driver.remove = platform_drv_remove;
449: if (drv->shutdown)
450: drv->driver.shutdown = platform_drv_shutdown;
451: if (drv->suspend)
452: drv->driver.suspend = platform_drv_suspend;
453: if (drv->resume)
454: drv->driver.resume = platform_drv_resume;
455: return driver_register(&drv->driver);