読者です 読者をやめる 読者になる 読者になる

zybo 割り込み③ device driver編


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...が呼ばれたときの動作。(という感じらしい。)

今回はまだそこまで作り込まないで
純粋にデバイスドライバの最初の処理と、割り込みハンドラだけを入れて動作を確認する。

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

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>    /* 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関数内で
バイスのリソースを取得し、
割り込み番号から割り込みハンドラを登録する。

次はまとめ