newbusベースのデバイスドライバソースコードのスケルトン
FreeBSDのデバイスドライバ(?というか、どちらかというとカーネルモジュール)を書く機会があったのだけれど、資料が/usr/src/sys/dev 以下のソースコードくらいしかなくて、思いのほか苦戦してしまった。折角書いたので、スケルトンファイルとして公開してみる。
以下の説明では、ディレクトリ構成が以下のようになっていると仮定する。
skelton_device_driver |-- Makefile `-- test.c
サンプルのビルド方法
skelton_device_driver ディレクトリの中で、
$ make $ kldload ./test.ko
でカーネルに読み込む。さすがにソースコードの中身全部を解説するのはしんどいので、代わりに作成する際に参考にしたサイトを以下に示しておく:
特に、一番下のFreeBSDマニュアル検索ページは、とても情報が新しくかなり使えるサイトである。
FreeBSDのドライバを書こうとしている方の、手助けになれれば幸いです。
以下が各ファイルの中身。
# Makefile # Declare Name of kernel module KMOD = test # Enumerate Source files for kernel module SRCS = test.c SRCS += device_if.h bus_if.h .include <bsd.kmod.mk>
以下に、test.c の中身を示す。おおまかにいうと、pciからIRQを分けてもらって、割り込みがそのIRQにたいしてかかれば "Wow!Interrput was occued!"などと表示されるプログラムである。
/* test.c * written by big-eyed-hamster * http://d.hatena.ne.jp/big-eyed-hamster */ /* including for "kldloading", "kldunloading" */ #include <sys/param.h> #include <sys/module.h> #include <sys/kernel.h> #include <sys/systm.h> /* including for handling IRQ */ #include <sys/bus.h> #include <machine/resource.h> /*To use SYS_RES_IRQ*/ #include <sys/rman.h> /*To use RF_ACTIVE*/ /* prototype declaration of device functions. */ static int test_probe(device_t dev); static int test_attach(device_t dev); static int test_detach(device_t dev); int test_intr(void *v); /* need for "kldloading", "kldunloading" */ static int test_modevent( struct module *module, int event, void *args) { int e = 0; switch (event) { case MOD_LOAD: uprintf("Hello Kernel Module in FreeBSD!\n"); break; case MOD_UNLOAD: uprintf("Bye ,Kernel Module!\n"); break; default: e = EOPNOTSUPP; } return (e); } static moduledata_t test_conf ={ "test", test_modevent, NULL }; DECLARE_MODULE(test, test_conf, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); /* Interrupt Handler */ int test_intr(void *v) { uprintf("Wow! A interrupt occured!\n"); return (1); } /* need for using IRQ */ static device_method_t test_methods[] = { DEVMETHOD(device_probe,test_probe), DEVMETHOD(device_attach,test_attach), DEVMETHOD(device_detach,test_detach), { 0, 0 } }; struct test_softc { device_t test_dev; struct resource *irq; int irq_rid; void *irq_handle; void *cookie; }; static driver_t test_driver = { "test", test_methods, sizeof(struct test_softc), }; static int test_probe(device_t self) { struct resource *res; struct test_softc *test_softc; uprintf("proving...\n"); uprintf("start to examine whether IRQ is free or not..."); test_softc = device_get_softc(self); res = bus_alloc_resource_any(self,SYS_RES_IRQ,&test_softc->irq_rid,RF_ACTIVE); //res = BUS_ALLOC_RESOURCE_ANY(self,SYS_RES_IRQ,&test_softc->irq_rid,RF_ACTIVE); if ( res == NULL ){ uprintf("failed.\n"); return (1); } uprintf("succeeded.\n"); uprintf("Next, release resource..."); bus_release_resource(self,SYS_RES_IRQ,test_softc->irq_rid,res); //BUS_RELEASE_RESOURCE(self,SYS_RES_IRQ,test_softc->irq_rid,res); uprintf("succeeded.\n"); return (0); } static int test_attach(device_t self) { #if 1 struct test_softc *test_softc; uprintf("attacing...\n"); test_softc = device_get_softc(self); test_softc->irq = bus_alloc_resource_any(self,SYS_RES_IRQ,&test_softc->irq_rid,RF_ACTIVE); if ( test_softc->irq == NULL ){ uprintf("failed.\n"); return (1); } uprintf("succeeded.\n"); uprintf("Next, start to register interrput handler..."); bus_setup_intr(self,test_softc->irq,INTR_TYPE_MISC,NULL,(driver_intr_t*)test_intr,test_softc,&test_softc->cookie); uprintf("succeeded.\n"); #endif return (0); } static int test_detach(device_t self) { struct test_softc *test_softc; uprintf("detaching...\n"); test_softc = device_get_softc(self); uprintf("Teardowning interrput handler...\n"); bus_teardown_intr(self,test_softc->irq,test_softc->cookie); uprintf("succeeded."); uprintf("Next, start to release IRQ..."); bus_release_resource(self,SYS_RES_IRQ,test_softc->irq_rid,test_softc->irq); return (0); } devclass_t test_devclass; DRIVER_MODULE(test, pci, test_driver, test_devclass, 0, 0);