Raspberry Pi でドライバ開発したい(基礎編)

Linux
スポンサーリンク

はじめに

こんにちはー!

実は今年度の目標として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-bcm2708gcc-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

参考

カーネルモジュール基礎編

カーネルモジュール

最初に簡単なカーネルモジュールを一度作ってみます。通常デバイスは、/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でビルドしたデータ。
参考

おわりに

お疲れ様です。

今回、基礎編として、ドライバ開発の下準備や、実際に動くデバイスモジュールの開発を行ってみました。ユーザーランドではなく、カーネル空間のプログラムということで、特有の関数を利用できたり、ハードウェアに近いプログラムができるので、より開発手法の自由度が上がったかと思います。

実際は、I2Cドライバ開発の話まで記載しようと思いましたが、上記の記載通り作りたいものがないため、気力がなくなってしまいました・・・もし、さらに進めていきたい方がいらっしゃいましたら、「RaspberryPiで学ぶ ARMデバイスドライバープログラミング」を活用すると良いでしょう。

今後についてですが、ちょっとドライバ開発よりもYoctoの方に興味がありまして、以下の記事を参考にRaspberry Piに入れてみたい感じはあります。

Linuxは覚えることの量が半端ないので、少しずつ勉強の方を進めていきたいと思います。

ここまで読んできただき、ありがとうございました!

コメント

タイトルとURLをコピーしました