の続き。デバイスドライバは書いたことがなかったので備忘録として
参考資料たち。
Introduction to Linux Device Drivers - Part 1 The Basics
Introduction to Linux Device Drivers - Part 2 Platform and Character Drivers
どうやらデバイスドライバには種類があって
キャラクタ、ブロックなど。
動画ではキャラクタドライバについて解説していて、
/devや/class以下にデバイスに対応するディスクリプタを作成。
ユーザープログラムからは、それをopen, write, read...などしてデバイスと情報をやり取りする。
デバイスドライバで書くものは、そのopenやwrite, read...が呼ばれたときの動作。(という感じらしい。)
今回はまだそこまで作り込まないで
純粋にデバイスドライバの最初の処理と、割り込みハンドラだけを入れて動作を確認する。
platform device API
The platform device API [LWN.net]
これを利用する。
Makefile
ZYBO-Embedded_Linux_Hands-on_Tutorial.pdf
のp28
にMakefileが乗っている。
P_DIR := $(shell pwd) obj-m := mysw.o all: make -C ../../Linux-Digilent-Dev/ M=$(P_DIR) modules clean: make -C ../../Linux-Digilent-Dev/ M=$(P_DIR) clean
`-C dir, --directory=dir
makefile を読み込むなどの動作の前に、ディレクトリ dir に移動する。複数の -C オプションが指定されている場合、それぞれは前の指定に対する相対パスと解 釈される。例えば、 -C / -C etc は -C /etc と同じ意味である。このオプションは通常、 make を再帰的に呼び出す時に使われる。
本体コード
#include <linux/init.h> #include <linux/module.h> #include <asm/uaccess.h> /* Needed for copy_from_user */ #include <asm/io.h> /* Needed for IO Read/Write Functions */ #include <linux/proc_fs.h> /* Needed for Proc File System Functions */ #include <linux/seq_file.h> /* Needed for Sequence File Operations */ #include <linux/platform_device.h> /* Needed for Platform Driver Functions */ #include <linux/slab.h> /* Needed for kmalloc and kfree */ #include <linux/interrupt.h> #define DRIVER_NAME "mySw" #define DEVICE_NAME "mySW_hoge" struct resource *res; int irq; unsigned long remap_size; static irqreturn_t mysw_isr(int irq, void *dev_id) { printk("interrupt occured %d\n", irq); return IRQ_HANDLED; } static int simple_probe(struct platform_device *pdev) { struct resource *irq_res; printk(KERN_ALERT "probe\n"); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if(!res) { dev_err(&pdev->dev, "cant get mem resource\n"); return -ENODEV; } remap_size = res->end - res->start + 1; printk(KERN_INFO"name:%s\n", res->name); printk(KERN_INFO"start:0x%08lx\n",(unsigned long)res->start); printk(KERN_INFO"size:0x%08lx\n",(unsigned long)remap_size); //irq = platform_get_irq(pdev, 0); irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if(!irq_res) { dev_err(&pdev->dev, "cant get irq resource\n"); return -ENODEV; } irq = irq_res->start; if(irq < 0) { dev_err(&pdev->dev, "cant get irq resource\n"); return -ENODEV; } printk(KERN_INFO"irq_num:%d\n", irq); if(request_irq(irq, mysw_isr, IRQF_TRIGGER_RISING, DEVICE_NAME, NULL)) { printk(KERN_ERR"fail to request irq %d\n", irq); return -EIO; } else { printk(KERN_INFO"registered IRQ %d\n", irq); } return 0; } static int simple_remove(struct platform_device *pdev) { free_irq(irq, NULL); printk(KERN_ALERT "removed\n"); return 0; } static const struct of_device_id simple_of_match[] = { {.compatible = "xlnx,mySw-1.0"}, {}, }; MODULE_DEVICE_TABLE(of, simple_of_match); static struct platform_driver simple_driver = { .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, .of_match_table = simple_of_match, }, .probe = simple_probe, .remove = simple_remove, }; module_platform_driver(simple_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("HOGE."); MODULE_DESCRIPTION(DRIVER_NAME ": MYLED driver (Simple Version)"); MODULE_ALIAS(DRIVER_NAME);
最初に実行されるのはinsmodされたときに
module_platform_driver();
のマクロによって展開された__init ~~~ ()
という関数
Linux/include/linux/platform_device.h - Linux Cross Reference - Free Electrons
227 #define module_platform_driver(__platform_driver) \
228 module_driver(__platform_driver, platform_driver_register, \
229 platform_driver_unregister)
Linux/include/linux/device.h - Linux Cross Reference - Free Electrons
1458 #define module_driver(__driver, __register, __unregister, ...) \
1459 static int __init __driver##_init(void) \
1460 { \
1461 return __register(&(__driver) , ##__VA_ARGS__); \
1462 } \
1463 module_init(__driver##_init); \
1464 static void __exit __driver##_exit(void) \
1465 { \
1466 __unregister(&(__driver) , ##__VA_ARGS__); \
1467 } \
1468
initでやらせたい処理もないので、
module_platform_driver(simple_driver);
を使う。
(もし別々に書く必要があるなら
手動で module_init()マクロを使って
initする関数の中で個別にplatform_driver_registerやplatform_device_register_simpleなどを呼ぶ必要があり、
exitの前にplatform_driver_unregisterなどをする。
Linux Kernel: How do the probe function of Device Driver gets called? - Quora)
今回は深追いしない。
static const struct of_device_id simple_of_match[] = { {.compatible = "xlnx,mySw-1.0"}, {}, }; MODULE_DEVICE_TABLE(of, simple_of_match);
が重要で.compatible
にはdevicetreeで自作したデバイスの定義内に入れたcompatible
プロパティと同じ。
insmod hoge.ko
をしたあとは(__init~~が呼ばれて色々あったあとに)デバイスが見つかったらprobeが呼ばれる。
なのでsimple_probe関数内で
デバイスのリソースを取得し、
割り込み番号から割り込みハンドラを登録する。
次はまとめ