e-tipsmemo.hatenablog.com
の続き。デバイスドライバは書いたことがなかったので備忘録として
参考資料たち。
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...が呼ばれたときの動作。(という感じらしい。)
今回はまだそこまで作り込まないで
純粋にデバイスドライバの最初の処理と、割り込みハンドラだけを入れて動作を確認する。
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
Man page of MAKE
`-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>
#include <asm/io.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#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_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関数内で
デバイスのリソースを取得し、
割り込み番号から割り込みハンドラを登録する。
次はまとめ