一般的なデバイスドライバ
デバイスドライバについて
今回はSH7706LSRのLinux上での割り込みを扱います。割り込みはハードウェア資源なので、
デバイスドライバもユーザプログラムと同様のソフトウェアですが、
Linuxカーネルのバージョンアップによって、
Linuxデバイスドライバにはいくつもの種類があり、
対象ハードウェアについて
デバイスドライバの対象とするハードウェアは、
スイッチはプルアップになっているため、
汎用LEDはSH7706プロセッサのSCP4
ハードウェアアクセスについて
デバイスドライバはLinuxカーネルの一部分なのでI/
Linuxはもともとインテルi80386プロセッサのプロテクトモードを活用するためのソフトウェアが出発点となっているので、
そのため、
また、
#define PORT_PGDR 0xa400012cUL
#define PORT_SCDR 0xa4000136UL
汎用スイッチと汎用LEDのデバイスドライバ
デバイスドライバの入力仕様は、
汎用スイッチと汎用LEDのデバイスドライバのソースコードはリスト1となります。
01: #include <linux/module.h>
02: #include <linux/init.h>
03: #include <linux/device.h>
04: #include <linux/ctype.h>
05: #include <linux/poll.h>
06: #include <asm/io.h>
07:
08: #define PORT_PGDR 0xa400012cUL
09: #define PORT_SCDR 0xa4000136UL
10:
11: static int leddev_major;
12:
13: static ssize_t led_read(struct file * file, char __user * buf, size_t count, loff_t * ppos) {
14: ssize_t n;
15: char c;
16:
17: for(n = 0;n < count;n++) {
18: if(ctrl_inb(PORT_PGDR) & 0x10) {
19: c = '0';
20: } else {
21: c = '1';
22: }
23: copy_to_user(buf + n, &c, 1);
24: }
25: return count;
26: }
27:
28: static ssize_t led_write(struct file * file, const char __user * buf, size_t count, loff_t * ppos) {
29: ssize_t n;
30: char c;
31:
32: for(n = 0;n < count;n++) {
33: copy_from_user(&c, buf + n, 1);
34: if(c == '1') {
35: ctrl_outb(ctrl_inb(PORT_SCDR) | 0x10, PORT_SCDR);
36: }
37: if(c == '0') {
38: ctrl_outb(ctrl_inb(PORT_SCDR) & ~0x10, PORT_SCDR);
39: }
40: }
41: return count;
42: }
43:
44: static const struct file_operations led_fops = {
45: .owner = THIS_MODULE,
46: .read = led_read,
47: .write = led_write,
48: };
49:
50: #define CHRDEV "leddev"
51: static int __init leddev_init (void) {
52: leddev_major = register_chrdev(0, CHRDEV, &led_fops);
53: printk(KERN_INFO "LED/SW Device Driver\n");
54: return 0;
55: }
56:
57: static void __exit leddev_cleanup (void) {
58: unregister_chrdev(leddev_major, CHRDEV);
59: printk(KERN_INFO "LED/SW Device Driver Exit\n");
60: }
61:
62: module_init(leddev_init);
63: module_exit(leddev_cleanup);
64:
65: MODULE_LICENSE("GPL");
通常のユーザプログラムとLinuxとの呼び出しインターフェースはmain関数となりますが、
初期化関数と終了関数の名称は任意でかまいません。初期化関数では対象となるハードウェアの初期化やキャラクタ型デバイスドライバの登録を行います。今回はハードウェアの初期化は不要なので、
register_
終了関数では対象となるハードウェアの停止処理やキャラクタ型デバイスドライバの登録解除を行い、
汎用スイッチと汎用LEDのデバイスドライバでは入力と出力のみを行うので、
ここで気をつけなければいけない点は、
デバイスドライバのコンパイル
今回は適当な開発用のフォルダ内にさらにデバイスドライバ用のフォルダを用意し、
# tar xvjf linux-2.6.28.10.tar.bz2 # cd linux-2.6.28.10 # patch -p1 < ../linux-2.6.28.10-shmin-3.patch # cd ..
デバイスドライバ用のフォルダにはソースコード
TARGET:= leddev.ko
all: ${TARGET}
leddev.ko: leddev.c
make ARCH=sh CROSS_COMPILE=sh3-linux- -C ../linux-2.6.28.10 M=`pwd` modules
clean:
make ARCH=sh CROSS_COMPILE=sh3-linux- -C ../linux-2.6.28.10 M=`pwd` clean
obj-m:= leddev.o
clean-files := *.o *.ko *.order *.mod.[co] *~
デバイスドライバのコンパイルは以下のようにします。
# make
デバイスドライバはleddev.
デバイスドライバの実行
SH7706LSR上でLinuxを起動させて、
~ # insmod /lib/modules/`uname -r`/leddev.ko LED/SW Device Driver
デバイスドライバを組み込んだらメジャー番号が割り当てられます。これを用いてリスト3のスクリプトでデバイスドライバ用のデバイスファイルを作成します。
01: #!/bin/sh
02:
03: for item in `cat /proc/devices`;
04: do
05: if [ $item = leddev ]; then
06: mknod /dev/leddev c $major 0
07: fi
08: major=$item
09: done
デバイスドライバの情報は、
01: #!/usr/bin/perl
02:
03: open($fd, "</dev/leddev") or die "Cannot open /dev/leddev: $!";
04: $c = getc $fd;
05: print $c;
06: close $fd
汎用LEDの制御をするスクリプトはリスト5となり、
01: #!/usr/bin/perl
02:
03: open($fd, ">/dev/leddev") or die "Cannot open /dev/leddev: $!";
04: print $fd "1";
05: close $fd;
デバイスドライバの切り離しは以下のようにします。
~ # rmmod leddev LED/SW Device Driver Exit
Linuxでのハードウェア割り込み利用
タイマー割り込みドライバ
組込みボードではハードウェア割り込みを使うことが多いですが、
SH7706のタイマーはTMUと呼ばれTMU0~TMU2までの3チャンネル存在しますが、
01: #include <linux/init.h>
02: #include <linux/irq.h>
03: #include <linux/interrupt.h>
04: #include <linux/module.h>
05: #include <asm/signal.h>
06: #include <asm/irq.h>
07: #include <asm/io.h>
08: #include <asm/timer.h>
09:
10: #define PORT_SCDR 0xa4000136UL
11:
12: #define TSTR_TMU1 0x02
13: #define TCR_UNF 0x0100
14: #define TCR_UNIE 0x0020
15: #define TCR_UP_EDGE 0x0000
16: #define TCR_DOWN_EDGE 0x0008
17: #define TCR_BOTH_EDGE 0x0010
18: #define TCR_CLK4 0x0000
19: #define TCR_CLK16 0x0001
20: #define TCR_CLK64 0x0002
21: #define TCR_CLK256 0x0003
22:
23: #define TIMER1_IRQ 17
24:
25: static char *id="tmu1 interrupt";
26:
27: static irqreturn_t tmu1_interrupt(int irq, void *dev_id) {
28: ctrl_outb(ctrl_inb(PORT_SCDR) ^ 0x10, PORT_SCDR);
29: ctrl_outw(ctrl_inw(TMU1_TCR) & ~TCR_UNF, TMU1_TCR); // TCR_1 &= ~TCR_UNF;
30: return IRQ_HANDLED;
31: }
32:
33: static int tmu1_init(void) {
34: int i;
35:
36: i = request_irq(TIMER1_IRQ, tmu1_interrupt, 0, "tmu1_int", id);
37: if (i < 0) return i;
38:
39: ctrl_outb(ctrl_inb(PORT_SCDR) | 0x10, PORT_SCDR);
40:
41: ctrl_outw(TCR_UNIE | TCR_UP_EDGE | TCR_CLK4, TMU1_TCR); // 0.125uS(8MHz)
42: ctrl_outl(8000000, TMU1_TCOR);
43: ctrl_outl(8000000, TMU1_TCNT);
44: ctrl_outb(ctrl_inb(TMU_012_TSTR) | TSTR_TMU1, TMU_012_TSTR);
45:
46: printk(KERN_INFO "The interrupt driver by TMU1\n");
47: return 0;
48: }
49:
50: static void tmu1_exit(void) {
51: ctrl_outb(ctrl_inb(PORT_SCDR) & ~0x10, PORT_SCDR);
52: ctrl_outw(ctrl_inw(TMU1_TCR) & ~TCR_UNIE, TMU1_TCR);
53: ctrl_outb(ctrl_inb(TMU_012_TSTR) & ~TSTR_TMU1, TMU_012_TSTR);
54: free_irq(TIMER1_IRQ, id);
55: printk(KERN_INFO "The interrupt driver removed\n");
56: }
57:
58: module_init(tmu1_init);
59: module_exit(tmu1_exit);
60:
61: MODULE_LICENSE("GPL");
仕様としてはTMU1を1秒間隔でタイマー割り込みを発生させ、
第1引数は割り込み番号、
- TMU0割り込み 16番
- TMU1割り込み 17番
- TMU2割り込み 18番
TMU1の設定はリスト6の41行目でTMU1の割り込みを有効にし、
デバイスドライバのコンパイルと実行
デバイスドライバ用のフォルダにはソースコード(tmu1.
TARGET:= tmu1.ko
all: ${TARGET}
tmu1.ko: tmu1.c
make ARCH=sh CROSS_COMPILE=sh3-linux- -C ../linux-2.6.28.10 M=`pwd` modules
clean:
make ARCH=sh CROSS_COMPILE=sh3-linux- -C ../linux-2.6.28.10 M=`pwd` clean
obj-m:= tmu1.o
clean-files := *.o *.ko *.order *.mod.[co] *~
デバイスドライバのコンパイルは以下のようにします。
# make
デバイスドライバは tmu1.
~ # insmod /lib/modules/`uname -r`/tmu1.ko The interrupt driver by TMU1
次回は
次回は組込みボードの表示デバイスで多用されるLCDをターゲットとしてデバイスドライバについて解説します。