ZYBO (Zynq) 初心者ガイド (12) LinuxカーネルモジュールでLチカ

- [1回目: 開発環境の準備](https://qiita.com/take-iwiw/items/966f252f6ca954aff08b)
- [2回目: Hello Worldプロジェクト](https://qiita.com/take-iwiw/items/24a8a94741fdbb80f62a)
- [3回目: PSのGPIOでLチカ](https://qiita.com/take-iwiw/items/39d2ed00fa88a1204fce)
- [4回目: PLのAXI GPIOでPSからLチカ](https://qiita.com/take-iwiw/items/a3b935a4e4dbd41593e1)
- [5回目: PLだけでLチカ](https://qiita.com/take-iwiw/items/efcdfca1866b9941fd2a)
- [6回目: 自作IPでLチカ](https://qiita.com/take-iwiw/items/b0f2ce7b4653bc6f32b9)
- [7回目: ブートイメージを作る](https://qiita.com/take-iwiw/items/ece0cdf83e6e1908fad8)
- [8回目: Linux起動する](https://qiita.com/take-iwiw/items/6e6915f7318689818368)
- [9回目: Linuxカーネルを少しカスタマイズする](https://qiita.com/take-iwiw/items/6f2644d16cbfdb021d07)
- [10回目: LinuxのRootFSをカスタマイズする / PythonでHello World](https://qiita.com/take-iwiw/items/94a90047978664159681)
- [11回目: LinuxユーザアプリケーションでLチカ](https://qiita.com/take-iwiw/items/5e4ab9a106acfda2c866)
- __[12回目: LinuxカーネルモジュールでLチカ](https://qiita.com/take-iwiw/items/e7efc48d92d7b979efdc) <--- 今回の内容__
- [13回目: LAN(Ethernet 0)を使う](https://qiita.com/take-iwiw/items/ac489acaca1ab54d7ce8)
- [14回目: Linuxユーザアプリをデバッグする / RootFSに取り込む](https://qiita.com/take-iwiw/items/8134ca7098622219b177)
- [15回目: Linux起動時にアプリケーションを自動実行させる](https://qiita.com/take-iwiw/items/50065df6cdd1ba8bcbd6)
- [16回目: Linuxから自作IPをUIOで制御する](https://qiita.com/take-iwiw/items/da91ce4dc2a8a8df3c0a)
- [17回目: Linuxで自作IPのデバイスドライバを作る](https://qiita.com/take-iwiw/items/05eb1204cb51ce0a5c03)
- [18回目: IoT化してスマホからLチカ](https://qiita.com/take-iwiw/items/e9311292c5078d785699)

# 環境
- 開発用PC: Windows 10 64-bit 
	- Vivado 2017.4 WebPACKライセンス
	- Xilinx SDK 2017.4  <- 今回は使わない
- 開発用PC (Linux): Ubuntu 16.04 本家 (日本語版じゃない) (on VirtualBox 5.2.4)
	- PetaLinux 2017.4
- ターゲットボード: ZYBO (Z7-20) 

Windows環境は[1回目](https://qiita.com/take-iwiw/items/966f252f6ca954aff08b)、Linux環境は[8回目](https://qiita.com/take-iwiw/items/6e6915f7318689818368)を参照。
使用するハードウェア(hdf)は[8回目](https://qiita.com/take-iwiw/items/6e6915f7318689818368)を参照。

# LinuxカーネルモジュールでLチカ
前回は、Linuxユーザ空間で動くアプリケーションを作成して、そこからLチカを行いました。今回は、Linuxのカーネル空間で動くモジュールを作成して、そこからLチカを行います。
面倒なので、/devや/sysといったインターフェースは作りませんが、デバイスドライバとして使う際には必要になります。詳しくはこちら(https://qiita.com/take-iwiw/items/1fdd2e0faaaa868a2db2 )の記事をご参考ください。
「Lチカ」と言いましたが、実際には、ロード時にLED点灯、アンロード時にLED消灯、という簡単な仕様のカーネルモジュールを作ります。


# プロジェクトの準備
新規PetaLinuxプロジェクトの作成からやります。とりあえず、下記操作でBOOT.binとimage.ubを作成して、ちゃんと起動できることを確認します。これはいつも通りの手順で、[8回目](https://qiita.com/take-iwiw/items/6e6915f7318689818368)と同じです。

```sh:開発PCのターミナル
cd ~/work/peta
petalinux-create --type project --template zynq --name SimplePS
cd SimplePS/
petalinux-config --get-hw-description=../project_1.sdk
petalinux-build
petalinux-package --boot --force --fsbl images/linux/zynq_fsbl.elf --fpga images/linux/design_1_wrapper.bit --u-boot
```

# カーネルモジュールを作成する
下記コマンドで、mymoduleというLinuxのカーネル空間で動くモジュールを作成します。コードは、`project-spec/meta-user/recipes-modules/mymodule`に作られます。後で編集しますが、ひとまずはこのままにします。`petalinux-build -x package`でこのアプリケーションを含む`image.ub`が再作成されます。

(ug1144-petalinux-tools-reference-guide.pdfによると、`petalinux-build -x package`の前に`petalinux-build -c rootfs`が必要らしいが、無くても動いた)

```sh:開発PCのターミナル
petalinux-create -t modules --name mymodule --enable
petalinux-build -x package
```


作成された`image/linux/image.ub`をSDカードに上書きコピーしてZyboを起動します。(BOOT.binはそのまま)
すると、作成したカーネルモジュールが`/lib/modules/4.9.0-xilinx-v2017.4/extra/mymodule.ko`にインストールされており、modprobe(ロード)すると、Hello world的なメッセージが出力されていることが分かります。

```sh:Zyboのターミナル
root@SimplePS:~# ls /lib/modules/4.9.0-xilinx-v2017.4/extra/mymodule.ko
/lib/modules/4.9.0-xilinx-v2017.4/extra/mymodule.ko
root@SimplePS:~# modprobe mymodule
mymodule: loading out-of-tree module taints kernel.
<1>Hello module world.
<1>Module parameters were (0xdeadbeef) and "default"
```

## コードを編集して、再ビルドする
コードを編集して、ビルド、image.ubを再作成するには、以下のようにします(エディタは適当に置き換えてください)。どういうコードにするかは後述。

```sh:開発PCのターミナル
code project-spec/meta-user/recipes-modules/mymodule/files/mymodule.c &
petalinux-build -c mymodule -x do_install
petalinux-build -x package
```

`petalinux-build -c mymodule -x do_install`は省略可能。バイナリ(.ko)自体は、下記に作成される。違いはおそらくstripの有無

- build/tmp/work/plnx_arm-xilinx-linux-gnueabi/mymodule/1.0-r0/mymodule.ko
- build/tmp/work/plnx_arm-xilinx-linux-gnueabi/mymodule/1.0-r0/package/lib/modules/4.9.0-xilinx-v2017.4/extra/mymodule.ko


# レジスタ直叩きでLチカ
モジュールロード(mymodule_init)のタイミングで、GPIOの設定と、High出力をします。モジュールアンロード(mymodule_exit)のタイミングで、Low出力をします。レジスタを叩くために、`ioremap_nocache()`で物理アドレス->仮想アドレスに変換しています。

```c:project-spec/meta-user/recipes-modules/mymodule/files/mymodule.c
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/io.h>

/* Standard module information, edit as appropriate */
MODULE_LICENSE("GPL");
MODULE_AUTHOR
    ("Xilinx Inc.");
MODULE_DESCRIPTION
    ("mymodule - loadable module template generated by petalinux-create -t modules");


#define REG(address) *(volatile unsigned int*)(address)
#define GPIOPS_BASE   (0xE000A000)
#define GPIOPS_DATA_0 (0x0040)
#define GPIOPS_DIRM_0 (0x0204)
#define GPIOPS_OEN_0  (0x0208)

static int __init mymodule_init(void)
{
	printk("mymodule_init\n");
	int address;
	address = (int)ioremap_nocache(GPIOPS_BASE, 0x1000);
	
	/* Set MIO7 as output */
    REG(address + GPIOPS_DIRM_0) |= 1 << 7;
    REG(address + GPIOPS_OEN_0)  |= 1 << 7;

	/* Set MIO7 as High */
	REG(address + GPIOPS_DATA_0) |= 1 << 7;

    iounmap((void*)address);
	return 0;
}


static void __exit mymodule_exit(void)
{
	printk("mymodule_exit\n");
	
	int address;
	address = (int)ioremap_nocache(GPIOPS_BASE, 0x1000);

	/* Set MIO7 as Low */
	REG(address + GPIOPS_DATA_0) &= ~(1 << 7);
	
    iounmap((void*)address);
}

module_init(mymodule_init);
module_exit(mymodule_exit);
```

image.ubを再作成して、SDカードに入れて起動します。そして、MIO7に相当するGPIOを有効にします。その後、mymodule.koをロード、アンロードします。そのタイミングでLED(LD4)がチカチカするはずです。

```sh:Zyboターミナル
root@SimplePS:~# echo 913 > /sys/class/gpio/export
root@SimplePS:~# modprobe mymodule
mymodule: loading out-of-tree module taints kernel.
mymodule_init
root@SimplePS:~# modprobe -r mymodule
mymodule_exit
root@SimplePS:~# modprobe mymodule
mymodule_init
```


# GPIO制御関数でLチカ
カーネル空間で使用できる、GPIO制御関数を使用してLEDを制御します。

```c:project-spec/meta-user/recipes-modules/mymodule/files/mymodule.c
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/gpio.h>

/* Standard module information, edit as appropriate */
MODULE_LICENSE("GPL");
MODULE_AUTHOR
    ("Xilinx Inc.");
MODULE_DESCRIPTION
    ("mymodule - loadable module template generated by petalinux-create -t modules");

static int __init mymodule_init(void)
{
	printk("mymodule_init\n");
    
    /* LED用のGPIO913(MIO7)を使用する */
    gpio_request(913, NULL);
    gpio_export(913, true);

	/* LED用のGPIO913(MIO7)を出力にする。初期値は1(High) */
    gpio_direction_output(913, 1);
    
	/* LED用のGPIO913(MIO7)に1(High)を出力にする */
    gpio_set_value(913, 1);

	return 0;
}


static void __exit mymodule_exit(void)
{
	printk("mymodule_exit\n");
	
    /* LED用のGPIO913(MIO7)に0(Low)を出力にする */
    gpio_set_value(913, 0);
}

module_init(mymodule_init);
module_exit(mymodule_exit);
```

image.ubを再作成して、SDカードに入れて起動します。mymodule.koをロード、アンロードします。そのタイミングでLED(LD4)がチカチカするはずです。今回は、手動でGPIOを有効にする必要はありません。


```sh:Zyboターミナル
root@SimplePS:~# modprobe mymodule
mymodule: loading out-of-tree module taints kernel.
mymodule_init
root@SimplePS:~# modprobe -r mymodule
mymodule_exit
root@SimplePS:~# modprobe mymodule
mymodule_init
```

# おわりに
~~ここまでで、Zybo(Zynq)の基本的な使い方を理解できました。ひとまずこの連載を終わりにしようと思います。引き続きやるべき内容としては以下がありますが、どれもハマりそうなので、この「初心者ガイド」のスコープ外としました。~~

- LANを使えるようにする
- Linuxアプリのデバッグ環境
- デバイスツリーの設定

LAN設定が出来たので、やっぱり続く　⇒　https://qiita.com/take-iwiw/items/ac489acaca1ab54d7ce8