はじめに
こんにちはー!
実は今年度の目標としてRaspberry Piを利用してドライバ開発をすることを挙げていました。ただ、正直なところJavaScriptの数値計算ライブラリの方に興味がうつってしまい、疎かになってしまいした。申し訳ございません。
この記事のスタンス
- 実際に手を動かして動作検証する
- デバイスモジュールを開発する準備まで
なお、ドライバ開発では主に以下の本を見ながら調査を行っていこうと思います。この記事では説明しないGPIOドライバ、I2Cドライバ、SPIドライバなどまで記載がされています。
以下、上記の本では動作しなかった部分の更新点など参考にさせて頂いたサイト
用語集
本記事で利用するかもしれないLinux用語などを解説します。
ビルドツリー
ビルドするためのソースコードやファイル群(フォルダの中に木構造のように構築されるのでこう呼ぶのかな)。ビルド環境とか言ったほうが分かりやすいかもしれない。kernelが置いてあるディレクトリ群は、カーネルツリー、カーネルソースツリーなどと呼ばれる。
ツールチェーン
ビルドするためのファイル一式。コンパイラやリンカー、ライブラリなどがあります。本記事では、クロス開発を行うためのライブラリやクロスコンパイラなどを指して利用します。
マウント
Linuxで外部デバイスを、ファイルシステムとして認識させる機能のこと。どこにでもマウントできるが、/mnt
や/media
がよく利用されるようです。コマンドは、mount
で実行できる。取り外す際は、umount
でアンマウントすること。
本記事では、SDカードに書き込む際にこのマウントコマンドを利用する。なお本記事では使用しないが、OS起動時に自動でマウントする場合は、fstab
が利用されます。
カーネルモジュール
後でカーネルへ組み込みが可能なソフトウェアの仕組み。ドライバは、カーネルに予め組み込むか、モジュールとして後で配布して組み込む方法があります。
カーネルモジュールのファイルとしての拡張子は、*.ko
となります。
以下のような操作を行えます。
lsmod
利用中のモジュールを列挙insmod
モジュールを追加rmmod
モジュールを除去
デバイスノード
Linuxでは、全てをファイルとして情報のやりとりをするのですが、その中でもデバイスとの情報のやり取りに使用するのが、/dev/
配下の特殊なファイルです。これをデバイスノード、デバイスファイルと呼びます。
開発の下準備
ハードウェアの準備
今回はラズパイは、Raspberry Pi Zero WHを使っていきたいと思います。私は、「Raspberry Pi Zero WH Official Simple Kit」を利用しましたが、2020年3月現在売り切れ中なようです。
ソフトウェアの準備
2020年3月時点のUbuntuの安定板を利用します。他にも色々使いそうなのをおいておきます。
仮想環境に、OSをインストールする方法は、「Linuxで動作するCプログラムのデバッグ環境構築1」の「開発環境を整えよう」を参照してください。Debianでもよいと思いますが、今回は本で紹介してあったUbuntuにしてみました。
ビルド環境の構築とkernelのビルド
PCにカーネルツリーと、ツールチェーンを整えたいと思います。というのもラズパイでビルドすると非常に時間がかかるためです。
Raspberry Piの公式ページに「Kernel building」とありまして、これはRaspbian上から環境構築する方法ですが、かなり参考になります。
Ubuntu でビルド環境を整えていきます。どれが必要か分かりませんが、とりあえず以下を導入します。
sudo apt install gcc make perl packages dkms build-essential module-assistant net-tools libncurses5-dev sudo apt install git git-core bc bison flex libssl-dev make
次に、ビルド関係のデータを置くフォルダを準備します。
mkdir ~/pi cd ./pi
カーネルとクロスコンパイラをRaspberry PiのGitHubのリポジトリから落としてきます。--depth 1
というのは最新のデータのみ落とすという意味です。付けていないと過去の履歴を全て落としてくるようなので、つけておきましょう。
git clone --depth 1 https://github.com/raspberrypi/linux git clone --depth 1 https://github.com/raspberrypi/tools
linux
ディレクトリがカーネルツリー、tools
ディレクトリがツールチェーンです。
次に、カーネルツリーでLinuxカーネルをビルドしていきます。
cd ./linux
以下のようにコマンドを実行し、ビルド用の設定ファイルを作成します。ここで、$ARCH
はARM用のカーネルを設定。$KERNEL
はラズベリーパイWH用、bcmrpi_defconfig
はデフォルトの設定を指します。
$ARCH
を設定しないと、Ubuntuが動作しているx86-64
でビルドしていしまうので注意です。
export ARCH=arm export KERNEL=kernel make bcmrpi_defconfig
次にx86-64
でARM用のソフトをビルドできるクロスコンパイラを設定したうえでビルドを開始します。make -j4
はCPUのコア数を設定しておきます。並列にビルドするので早くなります。
export CROSS_COMPILE=~/pi/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin/arm-linux-gnueabihf- make -j4
補足ですが、arm-bcm2708
やgcc-linaro-arm-linux-gnueabihf-raspbian-x64
というのはクロスコンパイラの種類を表しています。それぞれ次のような意味を持っています。
用語 | 意味 |
bcm2708 | Raspberry Piで利用しているCPUのファミリー名。 |
linaro | ARM系CPUのLinuxカーネルなどを開発する非営利組織。 |
eabi | Embedded Application Binary Interface の略称。組み込みシステム向けのバイナリファイルの定義。 |
hf | hard-floatの略称。-mfloat-abi=hard オプション付きで浮動小数点演算が高速化する。 |
x64 | x64で動作する64ビットOS向けのクロスコンパイラ |
環境変数のCROSS_COMPILE
にビルドコマンドの接頭を設定しておくことで、クロスコンパイラの種類の切り替えを行っているわけです。
SDカードへのインストール
SDカードに、「Win32 Disk Imager」を利用して、「Raspbian Buster with desktop」の書きこみをして、ラズパイが正常起動することを一度確かめてください。確かめたらSDカードを抜いて、このSDカードに上記で作ったビルドデータを書き込んでいきます。
予め、VirutalBoxのOSの設定からUSBデバイスを追加した後に、Ubuntuを再起動させておきます。
SDカードを指す前に一旦以下のコマンドで、デバイス情報を確認します。
ls /dev/sd*
そして、SDカードを指してから再度上記のコマンドを実行すると、増えているデバイスがあります。これがSDカードの番号となります。
以下が実際に試してみた結果です。
$ ls /dev/sd* /dev/sda /dev/sda1 $ ls /dev/sd* /dev/sda /dev/sda1 /dev/sdb /dev/sdb1 /dev/sdb2
せっかくなので、色々情報を確認しておきましょう。ちなみにSDカードは16GBを利用しています。
$ dmesg | grep sdb ... [...] sd 3:0:0:0: [sdb] 31116288 512-byte logical blocks: (15.9 GB/14.8 GiB) [...] sd 3:0:0:0: [sdb] Write Protect is off [...] sd 3:0:0:0: [sdb] Mode Sense: 03 00 00 00 [...] sd 3:0:0:0: [sdb] No Caching mode page found [...] sd 3:0:0:0: [sdb] Assuming drive cache: write through [...] sdb: sdb1 sdb2 [...] sd 3:0:0:0: [sdb] Attached SCSI removable disk ... [...] sd 3:0:0:0: [sdb] tag#0 FAILED Result: hostbyte=DID_OK driverbyte=DRIVER_SENSE [...] sd 3:0:0:0: [sdb] tag#0 Sense Key : Unit Attention [current] [...] sd 3:0:0:0: [sdb] tag#0 Add. Sense: Not ready to ready change, medium may have changed [...] sd 3:0:0:0: [sdb] tag#0 CDB: Write(10) 2a 00 00 08 8f d0 00 00 08 00 [...] blk_update_request: I/O error, dev sdb, sector 561104 op 0x1:(WRITE) flags 0x800 phys_seg 1 prio class 0 [...] Buffer I/O error on dev sdb2, logical block 3578, lost async page write [...] EXT4-fs (sdb2): error loading journal
$ df Filesystem 1K-blocks Used Available Use% Mounted on ... /dev/sdb1 258095 53464 204631 21% /media/build/boot
$ sudo fdisk /dev/sdb コマンド (m でヘルプ): p ディスク /dev/sdb: 14.9 GiB, 15931539456 バイト, 31116288 セクタ 単位: セクタ (1 * 512 = 512 バイト) セクタサイズ (論理 / 物理): 512 バイト / 512 バイト I/O サイズ (最小 / 推奨): 512 バイト / 512 バイト ディスクラベルのタイプ: dos ディスク識別子: 0xea7d04d6 デバイス 起動 開始位置 最後から セクタ サイズ Id タイプ /dev/sdb1 8192 532479 524288 256M c W95 FAT32 (LBA) /dev/sdb2 532480 31116287 30583808 14.6G 83 Linux
fdisk
の結果から分かるように、/dev/sdb1
が起動用ファイル、/dev/sdb2
がルートファイルシステムとなります。
まずは、モジュールをルートファイルシステムに入れ込みます。
cd ~/pi/linux/ sudo mount /dev/sdb2 /mnt sudo make ARCH=arm INSTALL_MOD_PATH=/mnt/ modules_install sync sudo umount /mnt
次は、カーネルをインストールします。
cd ~/pi/linux/ sudo mount /dev/sdb1 /mnt sudo cp ./arch/arm/boot/Image /mnt/kernel.img sync sudo umount /mnt
上記の操作が完了したら、SDカードを再度ラズパイに差し込んでカーネルのバージョンを確認してみましょう。作成したカーネルに指し変わっていると思います。
pi@raspberrypi:~ $ cat /proc/version Linux version 4.19.106+ (natade@natade-VirtualBox) (gcc version 4.8.3 20140303 (prerelease) (crosstool-NG linaro-1.13.1+bzr2650 - Linaro GCC 2014.03)) #1 Thu Mar 5 14:39:01 JST 2020
参考
- qa stack – CPUがBCM2708、BCM2835と呼ばれることがあるのはなぜですか?
- ひまじめ – EABIとOABI
- ひまじめ – ARM EABIでの浮動小数点演算
- Wikipedia – Application Binary Interface
- linux – arm-eabi arm-gnueabiコンパイラとgnueabi-hfコンパイラの違い
カーネルモジュール基礎編
カーネルモジュール
最初に簡単なカーネルモジュールを一度作ってみます。通常デバイスは、/dev/配下のデバイスノードと呼ばれる場所でファイルを介して情報のやりとりするものですが、ここでは一切そのようなことをせず、初期化と終了の2つしか機能がないカーネルモジュールを作ってみたいと思います。
環境変数設定ファイルの準備
まず、今後よく使うであるクロスコンパイラのための環境変数を設定するスクリプト(env.sh
)を作っておきましょう。
#!/bin/sh export KERNEL=kernel export INSTALL_MOD_PATH=/mnt/ export ARCH=arm export CROSS_COMPILE=~/raspberry/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin/arm-linux-gnueabihf-
このファイルは今後、以下のように環境変数を初期化するときに利用します。
. env.sh
Makefileの準備
カーネルモジュールをビルドするためのMakefile
を作成します。
# カーネルツリーの場所 KPATH := $(HOME)/pi/linux # 「フォルダ名.c」をビルドする ExeName = $(lastword $(subst /, ,$(PWD))) obj-m := $(ExeName).o all: make -C $(KPATH) M=$(PWD) modules clean: make -C $(KPATH) M=$(PWD) clean
例えば、abc
というカーネルモジュールを作成したい場合は、
/abc/abc.c
/abc/Makefile
上記のようにフォルダ名と同等のファイルを用意して、
make
とコマンドを実行することで、abc.ko
が作成されます。
ソースコード
以下のソースコードのファイルを、TestModA
というフォルダ内に、TestModA.c
というファイル名で用意します。
#include <linux/module.h> MODULE_AUTHOR("natade."); MODULE_LICENSE("GPL"); static int testmoda_init(void) { printk(KERN_INFO "init TestModA\n"); return 0; } static void testmoda_exit(void) { printk(KERN_INFO "exit TestModA\n"); } module_init(testmoda_init); module_exit(testmoda_exit);
ビルドして確認
同一フォルダ内にMakefile
も設置してビルドします。
/TestModA$ make make -C /pi/linux M=/pi/modules/TestModA modules make[1]: ディレクトリ '/pi/linux' に入ります CC [M] /pi/modules/TestModA/TestModA.o Building modules, stage 2. MODPOST 1 modules CC /pi/modules/TestModA/TestModA.mod.o LD [M] /pi/modules/TestModA/TestModA.ko make[1]: ディレクトリ '/pi/linux' から出ます /TestModA$ ls -l 合計 28 -rw-r--r-- 1 build build 181 Makefile -rw-rw-r-- 1 build build 0 Module.symvers -rw-r--r-- 1 build build 294 TestModA.c -rw-rw-r-- 1 build build 3872 TestModA.ko -rw-rw-r-- 1 build build 891 TestModA.mod.c -rw-rw-r-- 1 build build 2428 TestModA.mod.o -rw-rw-r-- 1 build build 2752 TestModA.o -rw-rw-r-- 1 build build 58 modules.order
ここで、TestModA.ko
をラズパイに設置して、以下のように動作を確認してみましょう。
pi@raspberrypi:~/Test $ lsmod | grep "Test" pi@raspberrypi:~/Test $ sudo insmod ./TestModA.ko pi@raspberrypi:~/Test $ lsmod | grep "TestModA" TestModA 16384 0 pi@raspberrypi:~/Test $ sudo rmmod TestModA pi@raspberrypi:~/Test $ tail /var/log/messages | grep "TestModA" raspberrypi kernel: [80069.394590] init TestModA raspberrypi kernel: [80079.774588] exit TestModA
rmmod
するときは、lsmod
で表示されたモジュール名を入力する必要があるので拡張子は不要です。
参考
キャラクタ型デバイス
カーネルドライバは、バイト単位でやりとりするキャラクタ型デバイスか、一定のメモリのブロック型でデータをやりとりするブロック型デバイスの2種類があります。ここでは、簡単に作れるキャラクタ型デバイスを作りたいと思います。
ソースコード
以下のソースコードのファイルを、TestModB
というフォルダ内に、TestModB.c
というファイル名で用意します。
内容としては基本的なカーネルモジュールに、デバイスノードへキャラクタ型デバイスとして登録する処理、解除する処理、書き込みイベントが発生時の動作を追加したものとなります。本を参考にキャラクタ型ノードを複数拡張する場合も備えたような形になっています。
#include <linux/module.h> #include <linux/moduleparam.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/types.h> #include <linux/kdev_t.h> #include <linux/errno.h> #include <linux/cdev.h> #include <linux/sched.h> #include <linux/stat.h> #include <linux/io.h> #include <linux/ioport.h> #include <linux/init.h> #include <linux/version.h> #include <linux/device.h> #include <linux/slab.h> #include <linux/delay.h> #include <linux/uaccess.h> MODULE_AUTHOR("natade."); MODULE_LICENSE("GPL"); #define TESTMOD_DEVNAME "testmodb" #define TESTMOD_MAJOR 0 #define TESTMOD_MINOR 0 #define TESTMOD_NUM_DEVS 1 // デバイス数 static int _testmod_major = TESTMOD_MAJOR; static int _testmod_minor = TESTMOD_MINOR; static struct class *testmod_class = NULL; // 登録するデバイスの情報(今回は、1つのみ) static struct cdev *testmod_cdev_array = NULL; static ssize_t testmod_write(struct file *filep, const char __user *buf, size_t count, loff_t *f_pos) { printk(KERN_INFO "write\n"); if(count > 0) { // 1文字だけ抽出する char x = 0; if(copy_from_user( &x, buf, sizeof(char) )) { return -EFAULT; } printk(KERN_INFO "%c\n", x ); // 読み込んだバイト数を返す return sizeof(char); } return 0; } struct file_operations testmod_fops = { .write = testmod_write, }; static int testmod_init(void) { int ret = 0; printk(KERN_INFO "init\n"); // キャラクタデバイス番号の動的取得 { dev_t dev_major; ret = alloc_chrdev_region( &dev_major, TESTMOD_MINOR, TESTMOD_NUM_DEVS, TESTMOD_DEVNAME ); if(ret < 0) { printk(KERN_ERR "alloc_chrdev_region[%d]\n", ret); return ret; } _testmod_major = MAJOR(dev_major); } testmod_class = class_create(THIS_MODULE, TESTMOD_DEVNAME); if(IS_ERR(testmod_class)) { return PTR_ERR(testmod_class); } // キャラクタデバイス構造体の確保(同様のデバイスを増やす場合に利用) { size_t size; size = sizeof(struct cdev) *TESTMOD_NUM_DEVS; testmod_cdev_array = (struct cdev*)kmalloc(size, GFP_KERNEL); } // キャラクタデバイスの登録 { // 複数ある場合はデバイスの番号だけ繰り返す用に作る必要がある dev_t dev_minor; int i = 0; dev_minor = MKDEV(_testmod_major, _testmod_minor); cdev_init(&(testmod_cdev_array[i]), &testmod_fops); testmod_cdev_array[i].owner = THIS_MODULE; ret = cdev_add( &(testmod_cdev_array[i]), dev_minor, 1); if( ret < 0 ) { printk(KERN_ERR "cdev_add[%d][%d]\n", ret, i); return ret; } device_create( testmod_class, NULL, dev_minor, NULL, TESTMOD_DEVNAME"%u", _testmod_minor + i ); } return 0; } static void testmod_exit(void) { printk(KERN_INFO "exit\n"); // キャラクタデバイスの解除 { // 複数ある場合はデバイスの番号だけ繰り返す用に作る必要がある dev_t devno; int i = 0; cdev_del(&(testmod_cdev_array[i])); devno = MKDEV(_testmod_major, _testmod_minor + i); device_destroy(testmod_class, devno); } // キャラクタデバイスの開放 { dev_t devno; kfree(testmod_cdev_array); devno = MKDEV(_testmod_major,_testmod_minor); unregister_chrdev_region(devno, TESTMOD_NUM_DEVS); class_destroy(testmod_class); } } module_init(testmod_init); module_exit(testmod_exit);
補足
file_operations
には、イベントが発生時に実行されるコールバック関数を設定できます。今回は、write
のみを設定することで、書き込みが発生したら実行するようにしました。
ここの定義の種類は~/pi/linux/include/linux/fs.h
に記載があります。
struct file_operations { struct module *owner; loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); ssize_t (*read_iter) (struct kiocb *, struct iov_iter *); ssize_t (*write_iter) (struct kiocb *, struct iov_iter *); int (*iterate) (struct file *, struct dir_context *); int (*iterate_shared) (struct file *, struct dir_context *); __poll_t (*poll) (struct file *, struct poll_table_struct *); long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); long (*compat_ioctl) (struct file *, unsigned int, unsigned long); int (*mmap) (struct file *, struct vm_area_struct *); unsigned long mmap_supported_flags; int (*open) (struct inode *, struct file *); int (*flush) (struct file *, fl_owner_t id); int (*release) (struct inode *, struct file *); int (*fsync) (struct file *, loff_t, loff_t, int datasync); int (*fasync) (int, struct file *, int); int (*lock) (struct file *, int, struct file_lock *); ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); int (*check_flags)(int); int (*flock) (struct file *, int, struct file_lock *); ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int); ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); int (*setlease)(struct file *, long, struct file_lock **, void **); long (*fallocate)(struct file *file, int mode, loff_t offset, loff_t len); void (*show_fdinfo)(struct seq_file *m, struct file *f); #ifndef CONFIG_MMU unsigned (*mmap_capabilities)(struct file *); #endif ssize_t (*copy_file_range)(struct file *, loff_t, struct file *, loff_t, size_t, unsigned int); int (*clone_file_range)(struct file *, loff_t, struct file *, loff_t, u64); int (*dedupe_file_range)(struct file *, loff_t, struct file *, loff_t, u64); int (*fadvise)(struct file *, loff_t, loff_t, int); } __randomize_layout;
よく利用されるイベントは以下のようなものがあります。デバイスは複数のプロセスから同時にオープンされる場合もあり、考慮した設計が必要です。
関数名 | イベントの発火タイミング |
open | ファイルのオープン時。 |
release | ファイルのクローズ時。 |
read | ファイルから読み込んだタイミング。戻り値に読み込めたバイト数をセットする。 |
write | ファイルに書き込みを行ったタイミング。戻り値に書きこめたバイト数をセットする。 |
システムコールのエラー時には以下のような負の値を返す必要があります。
名前 | 種類 |
-EIO | IOエラー。 |
-EBUSY | リソースが使用中。 |
-EFAULT | 参照エラー。 |
ビルドして確認
先ほどと同様に同一フォルダ内にMakefile
を設置してビルドして、ラズパイ上で動作を試していきます。
root@raspberrypi:# insmod ./TestModB.ko root@raspberrypi:# ls /dev/ -l | grep "test" crw------- 1 root root 240, 0 Mar 8 20:24 testmodb0 root@raspberrypi:# echo ABC123 > /dev/testmodb0 root@raspberrypi:# rmmod TestModB root@raspberrypi:# tail /var/log/messages ... Mar 8 20:31:31 raspberrypi kernel: [ 633.388028] init Mar 8 20:31:31 raspberrypi kernel: [ 637.784709] write Mar 8 20:31:31 raspberrypi kernel: [ 637.784735] A Mar 8 20:31:31 raspberrypi kernel: [ 637.784748] write Mar 8 20:31:31 raspberrypi kernel: [ 637.784756] B Mar 8 20:31:31 raspberrypi kernel: [ 637.784764] write Mar 8 20:31:31 raspberrypi kernel: [ 637.784771] C Mar 8 20:31:31 raspberrypi kernel: [ 637.784777] write Mar 8 20:31:31 raspberrypi kernel: [ 637.784784] 1 Mar 8 20:31:31 raspberrypi kernel: [ 637.784790] write Mar 8 20:31:31 raspberrypi kernel: [ 637.784798] 2 Mar 8 20:31:31 raspberrypi kernel: [ 637.784804] write Mar 8 20:31:31 raspberrypi kernel: [ 637.784811] 3 Mar 8 20:31:48 raspberrypi kernel: [ 651.248362] exit ...
上記を見てわかるように、insmod
すると/dev/testmodb0
が作成され、書き込みをすると1文字ずつ処理ができることがわかります。
参考
以下、作成予定だった記事
デバイスドライバについて
これまで、デバイスモジュールの基本的な作り方を説明しました。ここでは、デバイスのタイプに合わせてどのように作っていくか流れだけざっくりまとめてみたいと思っていましたが、使いたいi2c用デバイスが今はなかったり、気力がなくまとめられませんでした。また、何か機会があったらまとめていきたいとおもいます
デバイスツリーについて
デバイスを作るにあたり、ハードウェアの情報をソースコードに記載する場合があるのですが、Linuxの中に組み込んでおく方法が一般的なようです。そのためには、デバイスツリーと呼ばれる領域に情報を入れておく必要があるようです。
例えば、Raspberry Piのデバイスツリーであれば、~/pi/linux/arch/arm/boot/dts/
配下に以下のファイルで置いてあります。以下は一部を抜粋しました。
- bcm2709-rpi-2-b.dts
- bcm2710-rpi-2-b.dts
- bcm2710-rpi-3-b.dts
- bcm2710-rpi-2-b-plus.dts
- bcm2710-rpi-3-b.dts
- bcm2710-rpi-3-b-plus.dts
- bcm2710-rpi-cm3.dts
- bcm2711-rpi-4-b.dts
中身を確認したい方はGitHubのrpi-4.19.y
ブランチのdtsフォルダ内を見てみるといいと思います。なお拡張子によって以下の意味を持っています。
- dts … ボード固有情報
- dtsi … SoC固有情報
- dtb … dtsとdtsiを利用してデバイスツリーコンパイラ
evice-tree-compiler
でビルドしたデータ。
参考
- @iwatake2222 – 組み込みLinuxデバイスドライバの作り方 (11)
- @koara-local – [Linux][kernel] Device Tree についてのまとめ
- とあるエンジニアの備忘log – Device Tree 入門
- yuki-sato – 8. Zynq デバイスドライバとデバイスツリーを作る
おわりに
お疲れ様です。
今回、基礎編として、ドライバ開発の下準備や、実際に動くデバイスモジュールの開発を行ってみました。ユーザーランドではなく、カーネル空間のプログラムということで、特有の関数を利用できたり、ハードウェアに近いプログラムができるので、より開発手法の自由度が上がったかと思います。
実際は、I2Cドライバ開発の話まで記載しようと思いましたが、上記の記載通り作りたいものがないため、気力がなくなってしまいました・・・もし、さらに進めていきたい方がいらっしゃいましたら、「RaspberryPiで学ぶ ARMデバイスドライバープログラミング」を活用すると良いでしょう。
今後についてですが、ちょっとドライバ開発よりもYoctoの方に興味がありまして、以下の記事を参考にRaspberry Piに入れてみたい感じはあります。
- @propella – Yocto で Raspberry Pi 用 Linux を作る
- @Nao1215 – Raspberry Pi3 with Yocto Project:環境構築
- @naohikowatanabe – Yocto で RaspberryPi3 OS を3種類の方法で build する
Linuxは覚えることの量が半端ないので、少しずつ勉強の方を進めていきたいと思います。
ここまで読んできただき、ありがとうございました!
コメント