Suche senden
Hochladen
ラズパイでデバイスドライバを作ってみた。
•
3 gefällt mir
•
4,563 views
Kazuki Onishi
Folgen
チップセットの概要とデバイスドライバの実装の話。
Weniger lesen
Mehr lesen
Software
Melden
Teilen
Melden
Teilen
1 von 44
Jetzt herunterladen
Downloaden Sie, um offline zu lesen
Empfohlen
UEFI時代のブートローダ
UEFI時代のブートローダ
Takuya ASADA
Intel AVX-512/富岳SVE用SIMDコード生成ライブラリsimdgen
Intel AVX-512/富岳SVE用SIMDコード生成ライブラリsimdgen
MITSUNARI Shigeo
プロセスとコンテキストスイッチ
プロセスとコンテキストスイッチ
Kazuki Onishi
目grep入門 +解説
目grep入門 +解説
murachue
Dalvik仮想マシンのアーキテクチャ 改訂版
Dalvik仮想マシンのアーキテクチャ 改訂版
Takuya Matsunaga
C++でできる!OS自作入門
C++でできる!OS自作入門
uchan_nos
何となく勉強した気分になれるパーサ入門
何となく勉強した気分になれるパーサ入門
masayoshi takahashi
3種類のTEE比較(Intel SGX, ARM TrustZone, RISC-V Keystone)
3種類のTEE比較(Intel SGX, ARM TrustZone, RISC-V Keystone)
Kuniyasu Suzaki
Empfohlen
UEFI時代のブートローダ
UEFI時代のブートローダ
Takuya ASADA
Intel AVX-512/富岳SVE用SIMDコード生成ライブラリsimdgen
Intel AVX-512/富岳SVE用SIMDコード生成ライブラリsimdgen
MITSUNARI Shigeo
プロセスとコンテキストスイッチ
プロセスとコンテキストスイッチ
Kazuki Onishi
目grep入門 +解説
目grep入門 +解説
murachue
Dalvik仮想マシンのアーキテクチャ 改訂版
Dalvik仮想マシンのアーキテクチャ 改訂版
Takuya Matsunaga
C++でできる!OS自作入門
C++でできる!OS自作入門
uchan_nos
何となく勉強した気分になれるパーサ入門
何となく勉強した気分になれるパーサ入門
masayoshi takahashi
3種類のTEE比較(Intel SGX, ARM TrustZone, RISC-V Keystone)
3種類のTEE比較(Intel SGX, ARM TrustZone, RISC-V Keystone)
Kuniyasu Suzaki
Fuchsia概略その1
Fuchsia概略その1
l_b__
ARM CPUにおけるSIMDを用いた高速計算入門
ARM CPUにおけるSIMDを用いた高速計算入門
Fixstars Corporation
LLVM overview 20110122
LLVM overview 20110122
nothingcosmos
PHPとシグナル、その裏側
PHPとシグナル、その裏側
do_aki
Portacle : Common Lispのオールインワン開発環境
Portacle : Common Lispのオールインワン開発環境
Satoshi imai
0章 Linuxカーネルを読む前に最低限知っておくべきこと
0章 Linuxカーネルを読む前に最低限知っておくべきこと
mao999
initramfsについて
initramfsについて
Kazuhiro Nishiyama
nftables: the Next Generation Firewall in Linux
nftables: the Next Generation Firewall in Linux
Tomofumi Hayashi
Hyper vを理解する
Hyper vを理解する
Naoki Abe
新入社員のための大規模ゲーム開発入門 サーバサイド編
新入社員のための大規模ゲーム開発入門 サーバサイド編
infinite_loop
大規模サービスを支えるネットワークインフラの全貌
大規模サービスを支えるネットワークインフラの全貌
LINE Corporation
Intro to SVE 富岳のA64FXを触ってみた
Intro to SVE 富岳のA64FXを触ってみた
MITSUNARI Shigeo
さわってみようTOPPERS/SSP
さわってみようTOPPERS/SSP
NSaitoNmiri
C#や.NET Frameworkがやっていること
C#や.NET Frameworkがやっていること
信之 岩永
CRC-32
CRC-32
7shi
Multi Chassis LAG for Cloud builders
Multi Chassis LAG for Cloud builders
Juniper Networks (日本)
Jenkins と groovy
Jenkins と groovy
Kohsuke Kawaguchi
Linux女子部 systemd徹底入門
Linux女子部 systemd徹底入門
Etsuji Nakai
いまさら聞けないarmを使ったNEONの基礎と活用事例
いまさら聞けないarmを使ったNEONの基礎と活用事例
Fixstars Corporation
4章 Linuxカーネル - 割り込み・例外 2
4章 Linuxカーネル - 割り込み・例外 2
mao999
x86とコンテキストスイッチ
x86とコンテキストスイッチ
Masami Ichikawa
Prosym2012
Prosym2012
MITSUNARI Shigeo
Weitere ähnliche Inhalte
Was ist angesagt?
Fuchsia概略その1
Fuchsia概略その1
l_b__
ARM CPUにおけるSIMDを用いた高速計算入門
ARM CPUにおけるSIMDを用いた高速計算入門
Fixstars Corporation
LLVM overview 20110122
LLVM overview 20110122
nothingcosmos
PHPとシグナル、その裏側
PHPとシグナル、その裏側
do_aki
Portacle : Common Lispのオールインワン開発環境
Portacle : Common Lispのオールインワン開発環境
Satoshi imai
0章 Linuxカーネルを読む前に最低限知っておくべきこと
0章 Linuxカーネルを読む前に最低限知っておくべきこと
mao999
initramfsについて
initramfsについて
Kazuhiro Nishiyama
nftables: the Next Generation Firewall in Linux
nftables: the Next Generation Firewall in Linux
Tomofumi Hayashi
Hyper vを理解する
Hyper vを理解する
Naoki Abe
新入社員のための大規模ゲーム開発入門 サーバサイド編
新入社員のための大規模ゲーム開発入門 サーバサイド編
infinite_loop
大規模サービスを支えるネットワークインフラの全貌
大規模サービスを支えるネットワークインフラの全貌
LINE Corporation
Intro to SVE 富岳のA64FXを触ってみた
Intro to SVE 富岳のA64FXを触ってみた
MITSUNARI Shigeo
さわってみようTOPPERS/SSP
さわってみようTOPPERS/SSP
NSaitoNmiri
C#や.NET Frameworkがやっていること
C#や.NET Frameworkがやっていること
信之 岩永
CRC-32
CRC-32
7shi
Multi Chassis LAG for Cloud builders
Multi Chassis LAG for Cloud builders
Juniper Networks (日本)
Jenkins と groovy
Jenkins と groovy
Kohsuke Kawaguchi
Linux女子部 systemd徹底入門
Linux女子部 systemd徹底入門
Etsuji Nakai
いまさら聞けないarmを使ったNEONの基礎と活用事例
いまさら聞けないarmを使ったNEONの基礎と活用事例
Fixstars Corporation
4章 Linuxカーネル - 割り込み・例外 2
4章 Linuxカーネル - 割り込み・例外 2
mao999
Was ist angesagt?
(20)
Fuchsia概略その1
Fuchsia概略その1
ARM CPUにおけるSIMDを用いた高速計算入門
ARM CPUにおけるSIMDを用いた高速計算入門
LLVM overview 20110122
LLVM overview 20110122
PHPとシグナル、その裏側
PHPとシグナル、その裏側
Portacle : Common Lispのオールインワン開発環境
Portacle : Common Lispのオールインワン開発環境
0章 Linuxカーネルを読む前に最低限知っておくべきこと
0章 Linuxカーネルを読む前に最低限知っておくべきこと
initramfsについて
initramfsについて
nftables: the Next Generation Firewall in Linux
nftables: the Next Generation Firewall in Linux
Hyper vを理解する
Hyper vを理解する
新入社員のための大規模ゲーム開発入門 サーバサイド編
新入社員のための大規模ゲーム開発入門 サーバサイド編
大規模サービスを支えるネットワークインフラの全貌
大規模サービスを支えるネットワークインフラの全貌
Intro to SVE 富岳のA64FXを触ってみた
Intro to SVE 富岳のA64FXを触ってみた
さわってみようTOPPERS/SSP
さわってみようTOPPERS/SSP
C#や.NET Frameworkがやっていること
C#や.NET Frameworkがやっていること
CRC-32
CRC-32
Multi Chassis LAG for Cloud builders
Multi Chassis LAG for Cloud builders
Jenkins と groovy
Jenkins と groovy
Linux女子部 systemd徹底入門
Linux女子部 systemd徹底入門
いまさら聞けないarmを使ったNEONの基礎と活用事例
いまさら聞けないarmを使ったNEONの基礎と活用事例
4章 Linuxカーネル - 割り込み・例外 2
4章 Linuxカーネル - 割り込み・例外 2
Ähnlich wie ラズパイでデバイスドライバを作ってみた。
x86とコンテキストスイッチ
x86とコンテキストスイッチ
Masami Ichikawa
Prosym2012
Prosym2012
MITSUNARI Shigeo
What is Metasepi?
What is Metasepi?
Kiwamu Okabe
Python physicalcomputing
Python physicalcomputing
Noboru Irieda
Rの高速化
Rの高速化
弘毅 露崎
PostgreSQL - C言語によるユーザ定義関数の作り方
PostgreSQL - C言語によるユーザ定義関数の作り方
Satoshi Nagayasu
Rのデータ構造とメモリ管理
Rのデータ構造とメモリ管理
Takeshi Arabiki
ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)
ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)
inaz2
Rを用いたGIS
Rを用いたGIS
Mizutani Takayuki
2011.09.18 v7から始めるunix まとめ
2011.09.18 v7から始めるunix まとめ
Makiko Konoshima
Buffer overflow
Buffer overflow
ionis111
Subprocess no susume
Subprocess no susume
Makoto Kishimoto
Slide
Slide
Takefumi MIYOSHI
Python で munin plugin を書いてみる
Python で munin plugin を書いてみる
ftnk
dofilewrite and vn_write
dofilewrite and vn_write
kusabanachi
V6 unix in okinawa
V6 unix in okinawa
magoroku Yamamoto
デバドラを書いてみよう!
デバドラを書いてみよう!
Masami Ichikawa
0622
0622
bobuhiro11
スタート低レイヤー #0
スタート低レイヤー #0
Kiwamu Okabe
Dive into RTS - another side
Dive into RTS - another side
Kiwamu Okabe
Ähnlich wie ラズパイでデバイスドライバを作ってみた。
(20)
x86とコンテキストスイッチ
x86とコンテキストスイッチ
Prosym2012
Prosym2012
What is Metasepi?
What is Metasepi?
Python physicalcomputing
Python physicalcomputing
Rの高速化
Rの高速化
PostgreSQL - C言語によるユーザ定義関数の作り方
PostgreSQL - C言語によるユーザ定義関数の作り方
Rのデータ構造とメモリ管理
Rのデータ構造とメモリ管理
ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)
ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)
Rを用いたGIS
Rを用いたGIS
2011.09.18 v7から始めるunix まとめ
2011.09.18 v7から始めるunix まとめ
Buffer overflow
Buffer overflow
Subprocess no susume
Subprocess no susume
Slide
Slide
Python で munin plugin を書いてみる
Python で munin plugin を書いてみる
dofilewrite and vn_write
dofilewrite and vn_write
V6 unix in okinawa
V6 unix in okinawa
デバドラを書いてみよう!
デバドラを書いてみよう!
0622
0622
スタート低レイヤー #0
スタート低レイヤー #0
Dive into RTS - another side
Dive into RTS - another side
ラズパイでデバイスドライバを作ってみた。
1.
RaspberryPiで デバイスドライバを作ってみた。 byk‑onishi
2.
自己紹介 名前:大西和貴( @_k_onishi_ ) 出身:京都 所属:SAKURAInternetInc. アプリケーションエンジニア (Go(Goji),Typescript(React.js),PHP,LXC,Etc...) 趣味: CPU,Kernel,仮想化,CTF(Pwn),マルウェア 読書、ピアノ、ギター、カラオケ
3.
概要 対象となるチップセットのメモリマップの概要 及びキャラクタデバイスドライバの実装。
4.
環境 PaspberryPi3ModelB $ sudo cat
/etc/os-release PRETTY_NAME="Raspbian GNU/Linux 9 (stretch)" NAME="Raspbian GNU/Linux" VERSION_ID="9" VERSION="9 (stretch)" ID=raspbian ID_LIKE=debian HOME_URL="http://www.raspbian.org/" SUPPORT_URL="http://www.raspbian.org/RaspbianForums" BUG_REPORT_URL="http://www.raspbian.org/RaspbianBugs" $ sudo uname -a Linux raspberrypi 4.9.41-v7+ #1023 SMP Tue Aug 8 16:00:15 BST 2
5.
Chipset まずデータシートからチップセットを調べる。 Processer BroadcomBCM2387chipset. 1.2GHzQuad‑CoreARMCortex‑A53(64Bit) 引用:https://www.terraelectronica.ru/pdf/show? pdf_file=%2Fds%2Fpdf%2FT%2FTechicRP3.pdf 上記から BCM2387 であることがわかる。
6.
BCM2387 https://web.stanford.edu/class/cs140e/docs/BCM2837‑ARM‑ Peripherals.pdf
7.
ペリフェラルレジスタ ペリフェラルデバイスのレジスタは、ペリフェラル(プログラマブ ル・カウンタや割り込み制御、シリアル通信ポートなどのハードウ ェア)の動作を設定したり、動作状況を読み出したりするためのレ ジスタである。 引用:https://ja.wikipedia.org/wiki/レジスタ_(コンピュータ)#ペリ フェラルデバイスのレジスタ
8.
データシートを見ると以下のような記述があった。 Peripherals(atphysicaladdress0x3F000000on)aremapped intothekernelvirtualaddress spacestartingataddress0xF2000000. 引用:https://web.stanford.edu/class/cs140e/docs/BCM2837‑ ARM‑Peripherals.pdfp6 上記からペリフェラルレジスタが 0x3F000000 からマッピングされてい ることがわかる。
9.
ドキュメントから bcm_host_get_peripheral_address() でペリフェ ラルレジスタのアドレスを取得できることがわかったので実際に以下の コードで確かめる。 https://www.raspberrypi.org/documentation/hardware/raspberrypi/peri pheral_addresses.md $
cat samples/get_peripheral_address.c #include <stdio.h> #include <bcm_host.h> int main(int argc, char* argv[]) { printf("0x%08Xn", bcm_host_get_peripheral_address()); return 0; } $ gcc samples/get_peripheral_address.c -I/opt/vc/include -L/opt $ ./myfile 0x3F000000
10.
/proc/iomem からもメモリマップが確認できる。 $ sudo
cat /proc/iomem 00000000-3b3fffff : System RAM 00008000-00afffff : Kernel code 00c00000-00d3da63 : Kernel data 3f006000-3f006fff : dwc_otg 3f007000-3f007eff : /soc/dma@7e007000 3f00b840-3f00b84e : /soc/vchiq 3f00b880-3f00b8bf : /soc/mailbox@7e00b880 3f101000-3f102fff : /soc/cprman@7e101000 3f200000-3f2000b3 : /soc/gpio@7e200000 3f201000-3f201fff : /soc/serial@7e201000 3f201000-3f201fff : /soc/serial@7e201000 3f202000-3f2020ff : /soc/sdhost@7e202000 3f215000-3f215007 : /soc/aux@0x7e215000 3f300000-3f3000ff : /soc/mmc@7e300000 3f980000-3f98ffff : dwc_otg
11.
GPIO制御用レジスタ TheGPIOhas41registers.Allaccessesareassumedtobe32‑bit. https://web.stanford.edu/class/cs140e/docs/BCM2837‑ARM‑ Peripherals.pdfp90 BCM2387のデータシートp90にGPIOレジスタのマッピング表が記載さ れている。
12.
Address Field Name Description
Size Read/Write 0x7E200000 GPFSEL0 GPIOFunction Select0 32 R/W 0x7E200004 GPFSEL1 GPIOFunction Select1 32 R/W 0x7E200008 GPFSEL2 GPIOFunction Select2 32 R/W 0x7E20000C GPFSEL3 GPIOFunction Select3 32 R/W 0x7E200010 GPFSEL4 GPIOFunction Select4 32 R/W 0x7E200014 GPFSEL5 GPIOFunction Select5 32 R/W
13.
Address Field Name Description
Size Read/Write 0x7E20001C GPSET0 GPIOPinOutput Set0 32 W 0x7E200020 GPSET1 GPIOPinOutput Set1 32 W 0x7E200024 ‑ Reserved ‑ ‑ 0x7E200028 GPCLR0 GPIOPinOutput Clear0 32 W 0x7E20002C GPCLR1 GPIOPinOutput Clear1 32 W 0x7E200030 ‑ Reserved ‑ ‑ 0x7E200034 GPLEV0 GPIOPinLevel0 32 R 0x7E200038 GPLEV1 GPIOPinLevel1 32 R
14.
GPIO"FunctionSelect"Registers(GPFSEL0~ GPFSEL5) Thefunctionselectregistersareusedtodefinetheoperationof thegeneral‑purposeI/O pins https://web.stanford.edu/class/cs140e/docs/BCM2837‑ARM‑ Peripherals.pdfp91
15.
Bit(s) FieldName Description
Type Reset 29‑27 FSEL9 FSEL9‑FunctionSelect9 000=GPIOPin9isaninput 001=GPIOPin9isanoutput R/W 0 26‑24 FSEL8 FSEL8‑FunctionSelect8 R/W 0 23‑21 FSEL7 FSEL7‑FunctionSelect7 R/W 0 20‑18 FSEL6 FSEL6‑FunctionSelect6 R/W 0 17‑15 FSEL5 FSEL5‑FunctionSelect5 R/W 0
16.
Bit(s) FieldName Description
Type Reset 14‑12 FSEL4 FSEL4‑FunctionSelect4 R/W 0 11‑9 FSEL3 FSEL3‑FunctionSelect3 R/W 0 8‑6 FSEL2 FSEL2‑FunctionSelect2 R/W 0 5‑3 FSEL1 FSEL1‑FunctionSelect1 R/W 0 2‑0 FSEL0 FSEL0‑FunctionSelect0 R/W 0 3bitずつが各ピンに対応しており、同じ要領で GPFSEL5 まで続き54ピ ン全てに対応する。
17.
GPIOPinOutputSetRegisters(GPSET0~GPSET1) TheoutputsetregistersareusedtosetaGPIOpin. https://web.stanford.edu/class/cs140e/docs/BCM2837‑ARM‑ Peripherals.pdfp95 アウトプット用のレジスタで各ビットが各ピンに対応している。 GPSET0 を例に対応表をいかに示す。 Bit(s) FieldName
Description Type Reset 31‑0 SETn(n=0..31) 0=Noeffect 1=SetGPIOpinn R/W 0 同じ要領で GPSET1 を用いて54ピンまで対応している。
18.
GPIOPinOutputClearRegisters(GPCLR0~ GPCLR1) Theoutputclearregisters)areusedtoclearaGPIOpin. https://web.stanford.edu/class/cs140e/docs/BCM2837‑ARM‑ Peripherals.pdfp95 これはアウトプットのクリア用レジスタで先ほどと同様に各ビットが各 ピンに対応している。 GPCLR0 を例に対応表をいかに示す。 Bit(s) FieldName
Description Type Reset 31‑0 CLRn(n=0..31) 0=Noeffect 1=ClearGPIOpinn R/W 0 同じ要領で GPCLR1 を用いて54ピンまで対応している。
19.
GPIOPinLevelRegisters(GPLEV0~GPLEV1) Thepinlevelregistersreturntheactualvalueofthepin. https://web.stanford.edu/class/cs140e/docs/BCM2837‑ARM‑ Peripherals.pdfp96 ピンの値を取得する用のレジスタで先ほどと同様に各ビットが各ピンに 対応している。 GPLEV0 を例に対応表をいかに示す。 Bit(s) FieldName
Description Type Reset 31‑0 LEVn (n=0..31) 0=GPIOpinnis low 1=GPIOpinnis high R/W 同じ要領で GPLEV1 を用いて54ピンまで対応している。
20.
実装に関して /dev/mem mmap() PAGE_SIZE
21.
/dev/mem memはコンピュータのメインメモリーイメージのキャラクターデ バイスファイル(characterdevicefile)である。 https://linuxjm.osdn.jp/html/LDP_man‑pages/man4/mem.4.html
22.
mmap() ファイルやデバイスをメモリーにマップ/アンマップする。 https://linuxjm.osdn.jp/html/LDP_man‑pages/man2/mmap.2.html 当該関数を用いて先ほどのデバイスファイルをプロセスのメモリにマッ ピングしアクセスを行う。
23.
PAGE_SIZE 仮想メモリを扱う単位であるページのサイズを取得する。これは mmap() 関数でマッピングするメモリのサイズに用いる(ページサイズ でマッピングするのが定石らしい) $ getconf
PAGE_SIZE 4096
24.
実装 https://github.com/k‑onishi/gpio‑lib
25.
キャラクタデバイス作成(カーネルモジュール) システムコールに対応する関数の作成 メジャー番号とマイナー番号の取得 キャラクタデバイス構造体の初期化 ドライバの登録 デバイスのクラス登録 デバイスファイルの作成
26.
システムコール対応の関数テーブル作成 struct file_operations my_file_ops
= { .owner = THIS_MODULE, .open = my_open, .release = my_close, .read = my_read, .write = my_write, .unlocked_ioctl = my_ioctl, /* 64 bits */ .compat_ioctl = my_ioctl, /* 32 bits */ }; open 及び release で物理アドレスと仮想アドレスのマッピングを行 い、 ioctl で操作対象のピンの変更、そして実際の読み書きは read 及 び write で行う。
27.
file_operations は実装できるシステムコールに対応した処理を保持 する構造体で以下のように定義されている。 struct file_operations
{ struct module *owner; loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char __user *, size_t ssize_t (*aio_read) (struct kiocb *, char __user *, ssize_t (*write) (struct file *, const char __user *, ssize_t (*aio_write) (struct kiocb *, const char __user int (*readdir) (struct file *, void *, filldir_t); unsigned int (*poll) (struct file *, struct poll_table_ int (*ioctl) (struct inode *, struct file *, unsigned long (*unlocked_ioctl) (struct file *, unsigned int long (*compat_ioctl) (struct file *, unsigned int, int (*mmap) (struct file *, struct vm_area_struct *);
28.
int (*open) (struct
inode *, struct file *); int (*flush) (struct file *); int (*release) (struct inode *, struct file *); int (*fsync) (struct file *, struct dentry *, int datas int (*aio_fsync) (struct kiocb *, int datasync); int (*fasync) (int, struct file *, int); int (*lock) (struct file *, int, struct file_lock *); ssize_t (*readv) (struct file *, const struct iovec *, ssize_t (*writev) (struct file *, const struct iovec *, ssize_t (*sendfile) (struct file *, loff_t *, size_t ssize_t (*sendpage) (struct file *, struct page *, unsigned long (*get_unmapped_area)(struct file *, int (*check_flags)(int); int (*dir_notify)(struct file *filp, unsigned long int (*flock) (struct file *, int, struct file_lock *); };
29.
メジャー番号とマイナー番号の取得 以下では alloc_chrdev_region 関数でメジャー番号の取得を行い、 dev_t
型の変数から MAJOR マクロを用いてメジャー番号を取得してい る。 dev_t dev; alloc_ret = alloc_chrdev_region(&dev, MINOR_NUMBER_START, NUMBE if (alloc_ret != 0) { printk(KERN_ERR "failed to alloc_chrdev_region()n"); return -1; } major_number = MAJOR(dev);
30.
dev_t は以下のように定義されており unsigned
int であることがわ かる。そしてその変数から決められた上位12ビットをメジャー番号とし ている。 typedef __u32 __kernel_dev_t; typedef __kernel_dev_t dev_t; // include/linux/kdev_t.h #define MINORBITS 20 #define MINORMASK ((1U << MINORBITS) - 1) #define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS)) #define MINOR(dev) ((unsigned int) ((dev) & MINORMASK)) #define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
31.
alloc_chrdev_region() は以下のように定義されており __register_chrdev_region の戻り値からメジャー番号とマイナー番 号を設定している。 int
alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigne const char *name) { struct char_device_struct *cd; cd = __register_chrdev_region(0, baseminor, count, name if (IS_ERR(cd)) return PTR_ERR(cd); *dev = MKDEV(cd->major, cd->baseminor); return 0; }
32.
キャラクタデバイス構造体の初期化 /* initialize cdev
and function table */ cdev_init(&my_char_dev, &my_file_ops); my_char_dev.owner = THIS_MODULE;
33.
cdev_init() は以下のように定義されておりキャラクタデバイス構造 体のopsメンバに引数であるシステムコールハンドラ関数のテーブルを 設定している。 void cdev_init(struct
cdev *cdev, struct file_operations *fops) { memset(cdev, 0, sizeof *cdev); INIT_LIST_HEAD(&cdev->list); cdev->kobj.ktype = &ktype_cdev_default; kobject_init(&cdev->kobj); cdev->ops = fops; }
34.
cdev は以下のように定義されており、先ほど作成したシステムコール テーブルを保持するメンバが存在する。 struct cdev
{ struct kobject kobj; struct module *owner; struct file_operations *ops; struct list_head list; dev_t dev; unsigned int count; };
35.
ドライバの登録 デバイスドライバの登録は cdev_add() で行う。キャラクタデバイス構 造体やデバイス番号を保持している変数、当該デバイスに割り当てるマ イナー番号の数などの設定を行う。 cdev_err
= cdev_add(&my_char_dev, dev, NUMBER_MINOR_NUMBER); if (cdev_err != 0) { printk(KERN_ERR "failed to cdev_add()n"); unregister_chrdev_region(dev, NUMBER_MINOR_NUMBER); return -1; }
36.
デバイスのクラス登録 class_create() でクラス登録を行う。当該処理 で /sys/class/my_device/
が作成される。 my_char_dev_class = class_create(THIS_MODULE, DEVICE_NAME); if (IS_ERR(my_char_dev_class)) { printk(KERN_ERR "class_create()n"); cdev_del(&my_char_dev); unregister_chrdev_region(dev, NUMBER_MINOR_NUMBER); return -1; }
37.
デバイスファイルの作成 device_create() で実際に指定のメジャー番号及びマイナー番号のデ バイスを登録する。(ex. sys/class/my_device/my_device
) device_create(my_char_dev_class, NULL, MKDEV(major_number,
38.
実装に関して module_init()&module_exit() PAGE_SIZE ioremap_nocache() get_user()&put_user();
39.
module_init()&module_exit() モジュールのロード及びアンロード時に呼び出される関数を登録。 先ほどの説明した一連の処理は module_init の引数となっている関数 内で行う。 module_init(my_init); module_exit(my_exit);
40.
PAGE_SIZE カーネルが仮想アドレス空間においてメモリをチャンクとして扱う際の サイズ。 // include/asm-i386/page.h #define PAGE_SHIFT
12 #define PAGE_SIZE (1UL << PAGE_SHIFT)
41.
ioremap_nocache() カーネル空間に物理アドレスのマッピングを行う。 base_address = (int)ioremap_nocache( GPIO_ADDRESS,
PAGE_SIZE); return 0;
42.
get_user()&put_user() ユーザ空間<‑>カーネル空間でデータのコピーを行う。 static ssize_t my_write(struct
file* file, const char __user* b { int mode; get_user(mode, &buff[0]); : (省略) :
43.
static ssize_t my_read(struct
file* file, char __user* buff, size_t count, loff_t *pos) { int num_reg = (pin_number / NUM_PIN_EACH_REG); int offset = (pin_number % NUM_PIN_EACH_REG); int addr = base_address + GPLEV0_OFFSET + (REG_GAP * num_reg); unsigned int reg_value = MEMORY(addr); int value = ((reg_value >> offset) & 1UL); put_user(value + '0', &buff[0]); return count; }
44.
実装 https://github.com/k‑onishi/gpio‑driver
Jetzt herunterladen