このページは「24環境」の情報です

第19回 ウォッチドックタイマードライバ(CAT709)

海老原祐太郎
2004/1/16

今回はSH7709S CPU内蔵のウォッチドックタイマーのドライバを実装します。
SH7709SにはWDTが内蔵されており、アプリケーションプログラムの暴走を検出して
リセット・再起動かけることができます。

SH7709Sのハードウェア仕様は

はじめにSH7709S CPUの内蔵ウォッチドックタイマーの仕様を確認してみます。
仕様書によるとプリスケーラが4096分の1であり、カウンターは8bitでオーバーフロー発生となっています。

周辺クロックφP=29.4912MHz
29,4912MHz / 4096 = 7200Hz
7200Hz = 138.888uSec
138.888uSec x 256 = 35.555328msec

最大時間でも35.55msecでリセット動作となりますのでこのままでは短すぎます。
従って10msec毎のカーネルタイマー割り込み周期でWDTをクリアすることにして、
あとはソフトウェアカウントダウンで時間を延長します。

WDTドライバ仕様

今回作成するドライバの設計仕様は以下のとおりとします。

  • キャラクター型ドライバ
  • アスキーの'1'〜'9'のキャラクタをwriteすることにより、書き込んだ秒数後にWDTリセット。
    すなわち1秒以内に'1'を書き続けるか。9秒以内に'9'を書き続ける
  • '0'を書き込むとディセーブル

コンパイル方法

コンパイル方法は第16回 デバイスドライバ入門編(CAT709)第17回 cross-tools for REDHAT9と同じです。

基本的にはデバイスドライバの開発は開発機で行います。
NFSでCAT709からマウントし、ロードと実行はCAT709で行います。

説明するよりもコードを載せたほうが早いので、コードを載せます。
sh7709wdt.c というファイル名で開発機に保存してください。

/*
 * Sh7709wdt.c
 *   for linux-2.4 kernel
 *   written by Y.Ebihara
 *      version 0.10  2004.1.16
 */

#define MODULE
#define __KERNEL__ 

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/timer.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/semaphore.h>

#define DEFAULT_MAJOR_NUM   244
#define DEFAULT_MINOR_NUM   0
#define DEVNAME             "sh7709wdt"

/* REGISTERS */
#define WTCNT 0xffffff84
#define WTCSR 0xffffff86

/* global var */
static volatile int wdt_countdown=0;
// static struct semaphore sh7709wdt_sem1;
static struct timer_list tl;

/* -------------------------------------------------------- */
/* タイマーハンドラ(10msec毎に呼ばれる(はず)              */
/* -------------------------------------------------------- */

void sh7709wdt_timer_handler(unsigned long unused){
  if(wdt_countdown > 0){
    ctrl_outw(0x5a00,WTCNT); // wdtゼロクリア(ここから33.555328msec後にwdtタイムアウト)
    wdt_countdown--;
  }

  init_timer(&tl);
  tl.expires=jiffies+HZ/100;
  tl.data=0;
  tl.function=sh7709wdt_timer_handler;
  add_timer(&tl);

  return;
}

/* -------------------------------------------------------- */
/* file operation method                                    */
/* キャラクター型デバイスドライバ                           */
/* -------------------------------------------------------- */

static int sh7709wdt_open(struct inode *inode, struct file *filp){
  int minor;
  // マイナー番号取得マクロ
  minor=MINOR(inode->i_rdev);
  if(minor != DEFAULT_MINOR_NUM){
    printk("<1>" DEVNAME " open() error");
    return -ENODEV;
  }
  filp->private_data = (void*)minor;
  MOD_INC_USE_COUNT; 

  return 0;
}

static int sh7709wdt_close(struct inode *inode, struct file *filp){
  MOD_DEC_USE_COUNT;
  return 0;
}


static int sh7709wdt_write(struct file *filp, const char *user_buf,size_t  count,loff_t *ppos){
  char c;
  copy_from_user(&c,user_buf,1);

  // '0'〜'9'以外は受け付けません
  if(c<'0' || c>'9')
    return -EINVAL;

  // '0'が書き込まれたらWDTディセーブル
  if(c=='0'){
    ctrl_outw(0x5a00,WTCNT); // wdtゼロクリア
    ctrl_outw(0xa547,WTCSR); //WDTディセーブル,PowerOnReset動作,プリスケーラ4096
    printk("<1>" DEVNAME " wdt disabled\n");
    return count;
  }

  // '0'〜'9'だった場合は
  if((ctrl_inb(WTCSR) & 0x80) == 0){
    // WDTが初期化されていなかったら
    // WDTの初期化を行う
    ctrl_outw(0x5a00,WTCNT); // wdtゼロクリア
    ctrl_outw(0xa5c7,WTCSR); //WDTイネーブル,PowerOnReset動作,プリスケーラ4096
    printk("<1>" DEVNAME " wdt enabled\n");
  }
  // 100倍(というかHZ倍)した値をwdt_countdownに格納
  wdt_countdown = (c-'0')*HZ;
  return count;
}


static int sh7709wdt_read(struct file *filp, char *user_buf,size_t  count,loff_t *ppos){
  char buffer[16];
  int len;
  len = sprintf(buffer,"%d\n",wdt_countdown);
  copy_to_user(user_buf,buffer,len);
  return len;
}

static struct file_operations sh7709wdt_fops = {
  owner:THIS_MODULE,           // おまじない
  open:sh7709wdt_open,         // open
  read:sh7709wdt_read,         // read
  write:sh7709wdt_write,        // read
  release:sh7709wdt_close      // close
}; 


/* -------------------------------------------------------- */
/* driver module load / unload */
/* -------------------------------------------------------- */

static int init_module(void){
  printk("<1>sh7709wdt hello majorno=%d\n",DEFAULT_MAJOR_NUM);
  if(register_chrdev(DEFAULT_MAJOR_NUM,DEVNAME,&sh7709wdt_fops) != 0){
    printk("<1>sh7709wdt register fail\n");
    return -1;
  }

  init_timer(&tl);
  tl.expires=jiffies+1;
  tl.data=0;
  tl.function=sh7709wdt_timer_handler;
  add_timer(&tl);

  return 0;
}

static void cleanup_module(void){
  printk("<1>sh7709wdt goodby\n");
  unregister_chrdev(DEFAULT_MAJOR_NUM,DEVNAME);
  del_timer(&tl);
}

MODULE_LICENSE("GPL");

Makefileは以下のようになります。
KERNEL=行は、CAT709のカーネルを展開し、config し、make したことのあるディレクトリを指定します(超重要)

KERNEL=/home/ebihara/cat709-root-dir/usr/src/linux
CFLAGS=-O2 -I${KERNEL}/include
CC=sh3-linux-gcc

all:sh7709wdt.o

clean:
        rm *.o      # rmの前にはTABを入れてね

コンパイルは make するだけです。
出来上がった sh7709wdt.o がデバイスドライバになります。
fileコマンドで確かめると Hitachi SH の relocateble(再配置可能) オブジェクトになっていることが分かります。

$ make
$ file sh7709wdt.o
sh7709wdt.o: ELF 32-bit LSB relocatable, Hitachi SH, version 1 (SYSV), not stripped

デバイスドライバのロードと実行

以下の作業は CAT709 で行います。

ドライバを組み込む
# insmod /NFSマウント/開発機のどこかのディレクトリ/sh7709wdt.o
sh7709wdt hello majorno=244

組み込まれたか確認する
# lsmod
Module                  Size  Used by    Not tainted
sh7709wdt               1568   0  (unused)             ← 組み込まれた 
ds                      6332   0  (unused)
sh3_ss                  3212   3
pcmcia_core            34880   0  [ds sh3_ss]

メジャー番号を調べる
# cat /proc/devices
Character devices:
  1 mem
  2 pty
  3 ttyp
  4 ttyS
  5 cua
 10 misc
 90 mtd
108 ppp
128 ptm
136 pts
162 raw
204 ttySC
205 cusc
244 sh7709wdt   ←244番に登録された
254 pcmcia

そうしたらデバイスファイルも作ります。
デバイスファイルへのファイル操作が、デバイスドライバへの操作になります。

# rommode rw

メジャー番号244, マイナー番号0
# mknod /dev/sh7709wdt c 244 0

# rommode ro

シェルから使ってみます

# echo 9 > /dev/sh7709wdt        ← 9を書き込んだ
sh7709wdt wdt enabled            ← WDTがイネーブルになった
#                                ← 9秒待ってみる
9秒後にリセット
SH IPL+g version 0.11, Copyright (C) 2001 Free Software Foundation, Inc.
build Jun 20 2003 17:46:44

This software comes with ABSOLUTELY NO WARRANTY; for details type `w'.
This is free software, and you are welcome to redistribute it under
certain conditions; type `l' for details. 

RTC clock is 2004/01/16 20:40:40
mac addr:00:03:82:02:00:7c

>

当然9秒以内に'9'を書き込んだ場合はそこから9秒以内にまた'9'を書き込んでください。
WDTの動作をやめるときは'0'を書き込みます。

組 込 み 

ドライバの動作検証ができたのでCAT709に保存します。
出来上がったドライバは、/lib/modules/カーネルバージョン/kernel/drivers/misc/
ディレクトリに保存します。最初 misc ディレクトリが無いので作っておきます。

以下の作業は CAT709で行います。

# rommode rw
# mkdir /lib/modules/2.4.21/kernel/drivers/misc
# cp sh7709wdt.o /lib/modules/2.4.21/kernel/drivers/misc
# depmod -a
# vi /etc/modules

1行追加する
sh7709wdt


# rommode ro

/etc/modules ファイルにモジュール名を追加しておくと、
起動時に自動的にロードしてくれます。
ただし、モジュール名とファイル名を関連付けるため、
depmod -a を1回実行しておく必要があります(超重要)

ユーザープログラムからの利用例

ユーザープログラムからの利用例のサンプルプログラムを載せます。

#include <stdio.h>
#include <fcntl.h>

#define DEVICE "/dev/sh7709wdt"

int main(){
  int fd;
  int i;
  char wdt_limit='5';  // 5秒の意味

  fd=open(DEVICE,O_RDWR);
  if(fd<0){
    perror("");
    return 1;
  }

  for(i=0; i<10; i++){
    // wdtのクリア
    write(fd,&wdt_limit,1);  // write(fd,転送元アドレス,転送バイト数)

    // i秒待つ
    printf("sleep(%d)\n",i);
    sleep(i);
  }
}

実行例

# ./a.out
sh7709wdt wdt enabled
sleep(0)
sleep(1)
sleep(2)
sleep(3)
sleep(4)
sleep(5)
sleep(6)

SH IPL+g version 0.11, Copyright (C) 2001 Free Software Foundation, Inc.
build Jun 20 2003 17:46:44

This software comes with ABSOLUTELY NO WARRANTY; for details type `w'.
This is free software, and you are welcome to redistribute it under
certain conditions; type `l' for details.

RTC clock is 2004/01/16 20:54:58
mac addr:00:03:82:02:00:7c 

>

詳しくは 

ここで取り上げたのはlinuxデバイスドライバの一番簡単な例です。
勉強するなら オライリー出版 のLINUXデバイスドライバ 第2版 が最適です。 左のリンクで購入できます。

Last-modified: 2006-01-19 (木) 16:40:50 (5749d)