<モータコントロールデバイスドライバ>
デバイスドライバの基本動作とモータコントロールボー
ドのブロック図
BSDなどのマルチタスクOS下でハードウェアの制御をする場合には注意する点は”ハードウェアと同期を取るために、ステータスのポーリングなどをループの中で行ってはいけな い” ということです。つまり、割込をうまく使って無駄なステータスリードなどを避け、本来の仕事以 外はシステムにCPUを返さなければいけないということです。これを守らないと他のタスクにCPU が回らず、システムのパフォーマンスが低下します。今回のモータコントロールのデバイスドライバでは FIFOとのデータのやり取りは全て割り込み処理処理で行っています。基本的な動作は以下です。
<FIFOへのライト時>
・フルフラグが立っていなければ1バイトライトする。
・フルフラグが立っていれば”tsleep”をタイムアウト付きで発行してシ
ステムに戻る。
・フルフラクが解除された時に割込を発生。
・割込ルーチンでtsleepを解除するために”wakeup”を発行。これ
により、ライト動作が再開される。
<FIFOからのリード時>
・エンプティフラグが立っていなければ1バイトリードする。
・エンプティフラグが立っていれば”tsleep”をタイムアウト付きで発行
してシステムに戻る。
・エンプティフラクが解除された時に割込を発生。
・割込ルーチンでtsleepを解除するために”wakeup”を発行。これ
により、リード動作が再開される。
このように、割込を使用することによって、フル/エンプティ時の待ち合わせのためにス テータスを読み続けることなく処理を行うことができます。
<デバイスドライバの紹介>
このデバイスドライバはモータコントロールボード上のFIFOに1バイト単位でデータをライ
ト/リードするためのFreeBSD3.1用ドライバです。現在はカーネルにスタティックにリンク。
・モータコントロールボード仕様
バス:ISAバス
IO空間:100-100H
100H:FIFO(W/R)
104H:8255 PA
105H:8255 PB
106H:8255 PC
107H:8255 CTRL
メモリ空間:無し
割込み:IRQ10
・3軸ステッピングモータコントロール用デバイスドライバ基本機能
ライト:FIFOへライト
リード:FIFOからリード
ioctl:コントロールボードのリセット
ティーチングユニット用ロータリエンコーダカウント読み出し
アプリケーションのPGID(プロセスグループID)登録/読み出し
割込:FIFOフル/エンプティフラグ解除
ロータリーエンコーダクロック
FIFO通信エラー(SIGUSR1をアプリケーションに送信)
テーブル移動リミットスイッチ(SIGUSR2をアプリケーションに送信)
<デバイスドライバmoz.cソースリスト FreeBSD3.2のスケルトンを編集>
/*
* moz.c
*
*
mozはFreeBSD3.X用パーソナルCNC
(汎用小型フライス盤CNC化のための)用ステッピング
*
モータコントロールボード用のデバイスドライバです。このプログラムは
*
/usr/share/examples/driverのmake_device_driverから作成したスケルトンを元に作成
* しました。
*
* FreeBSD3.4用のドライバの参考文献:
* Software Disige
Jun. 1999 "ISAバスを使って自分だけのデバイスを作ろう"
* Software Disige
Apr. 1999 "FreeBSDデバイスドライバ作製術"
*
FreeBSDカーネル入門(改定版)アスキー出版局
*
386/486マシンユーザのためのUNIXデバイスドライバ ソフトバンク
*
*/
#include
"moz.h"
/* generated file.. defines NMOZ */
#include <sys/param.h>
#include <sys/systm.h>
#include
<sys/conf.h>
/* cdevsw stuff */
#include
<sys/kernel.h>
/* SYSINIT stuff */
#include <sys/types.h>
#include
<sys/signalvar.h>
#include <sys/uio.h>
#include
<sys/malloc.h>
/* malloc region definitions */
#include
<machine/clock.h>
/* DELAY() */
#include
<i386/isa/isa.h>
/* ISA bus port definitions etc. */
#include
<i386/isa/isa_device.h>/*
ISA bus configuration structures */
#include <sys/mozio.h> /* moz IOCTL definitions */
#ifdef DEVFS
#include
<sys/devfsext.h>
/* DEVFS defintitions */
#endif /* DEVFS */
/* 各IOポートのオフセット値
*/
#define
MOZ_FIFO_WR
0x00 /* 0x100 FIFO ライトメモリ */
#define
MOZ_FIFO_RD
0x00 /* 0x100 FIFO リードメモリ */
#define
MOZ_P8255_0
0x04 /* 0x104 8255 port 0 */
#define
MOZ_P8255_1
0x05 /* 0x105 8255 port 1 */
#define
MOZ_P8255_2
0x06 /* 0x106 8255 port 2 */
#define
MOZ_P8255_C
0x07 /* 0x107 8255 port 制御 */
/* P8255の初期化データ */
#define
MOZ_P8255_INIT
0x93 /* p0:input P1:input P2l:intpu P2h:output */
/* P8255のポート0のビットの定義
*/
#define
MOZ_P8255_0_RE_X_PULS
0x01 /* ロータリエンコーダX軸+イネーブル */
#define
MOZ_P8255_0_RE_X_MINUS
0x02 /* ロータリエンコーダX軸?イネーブル */
#define
MOZ_P8255_0_RE_Y_PULS
0x04 /* ロータリエンコーダY軸+イネーブル */
#define
MOZ_P8255_0_RE_Y_MINUS
0x08 /* ロータリエンコーダY軸?イネーブル */
#define
MOZ_P8255_0_RE_Z_PULS
0x10 /* ロータリエンコーダZ軸+イネーブル */
#define
MOZ_P8255_0_RE_Z_MINUS
0x20 /* ロータリエンコーダZ軸?イネーブル */
#define
MOZ_P8255_0_RE_BUTTON1
0x40 /* ロータリエンコーダ汎用ボタン1 */
#define
MOZ_P8255_0_RE_BUTTON2
0x80 /* ロータリエンコーダ汎用ボタン2 */
/* P8255のポート1のビットの定義
*/
#define
MOZ_P8255_1_RE_INT
0x01 /* ロータリエンコーダクロック */
#define
MOZ_P8255_1_RE_DIR
0x02 /* ロータリエンコーダ回転方向 */
#define
MOZ_P8255_1_FF_INT
0x04 /* FIFO ライトメモリフルフラグ */
#define
MOZ_P8255_1_EF_INT
0x08 /* FIFO リードメモリエンプティフラグ */
#define
MOZ_P8255_1_CHK_ERR
0x10 /* チェックサムエラー */
#define
MOZ_P8255_1_LIM_ERR
0x20 /* リミットエラー */
/* P8255のポート2のビットの定義
*/
#define
MOZ_P8255_2_FI_FULL
0x01 /* FULL FLAG */
#define
MOZ_P8255_2_FI_EMPTY
0x02 /* EMPTY FLAG */
#define
MOZ_P8255_2_FI_HALF
0x04 /* HALF FLAG */
#define
MOZ_P8255_2_INTCLR
0x10 /* 割込みクリア */
#define
MOZ_P8255_2_RESET
0x80 /* リセット出力 */
/* P8255ポート2のセット、クリアコマンド
*/
#define
MOZ_P8255_2_INTCLR_BIT_SET
0x09 /* 割込みクリアのセット */
#define
MOZ_P8255_2_INTCLR_BIT_CLR
0x08 /* 割込みクリアのクリア */
#define
MOZ_P8255_2_RESET_BIT_SET
0x0f /* MOZボードリセットのセット */
#define
MOZ_P8255_2_RESET_BIT_CLR
0x0e /* MOZボードリセットのクリア */
/*
プローブルーチンから発行するMOZボードのDIAG実行コマンド
*/
#define COM_DIAG_NUM 0x10
#define COM_DIAG_COML 3
#define COM_DIAG_RESL 4
/*****************************************************/
/*
*/
/*
MOZボードのDIAGコマンドと応答データ
*/
/*
*/
/*
DIAGコマンド:10
*/
/*
動作:自己診断(簡単なメモリチェック)を行ない
*/
/*
結果を返します
*/
/*
*/
/*
コマンド:
*/
/*
SQ,10,Chk
*/
/*
*/
/*
コマンド総バイト数:
*/
/*
3
*/
/*
*/
/*
応答データ:
*/
/*
SQ,10,Res,Chk
*/
/*
Res=1:(Pass),
0:(Error)
*/
/*
*/
/*
応答総データ数:
*/
/*
4
*/
/*
*/
/*
SQ:シーケンス番号(0-ff)
*/
/*
Res:結果
*/
/*
Chk:チェックサム
*/
/*
数値は全て16進数です
*/
/*
*/
/*****************************************************/
/*
アタッチルーチンから発行するMOZボードのソフトウェアバージョン取得コマンド
*/
#define COM_VER_NUM 0x11
/*****************************************************/
/*
*/
/*
MOZボードのバージョン取得コマンドと応答データ
*/
/*
*/
/*
バージョン取得コマンド:10
*/
/*
動作:ソフトウェアのバージョンの結果を返します
*/
/*
現在は"V0.97"が返ります
*/
/*
*/
/*
コマンド:
*/
/*
SQ,11,Chk
*/
/*
*/
/*
コマンド総バイト数:
*/
/*
3
*/
/*
*/
/*
応答データ:
*/
/*
SQ,11,'V','0','.','9','7',Chk
*/
/*
*/
/*
応答総データ数:
*/
/*
8
*/
/*
*/
/*
SQ:シーケンス番号(0-ff)
*/
/*
Chk:チェックサム
*/
/*
数値は全て16進数です
*/
/*
*/
/*****************************************************/
/* tsleepで使用するプライオリティ
*/
#define MOZ_PRI (PZERO+10 |
PCATCH )
/*
MOZボードのコマンド、戻りデータのバッファサイズ
*/
#define MOZ_BUFFERSIZE 16
/* MOZボードのIOポートの長さ
*/
#define NUMPORTS 8 /*
0:FIFO-READ-WRITE,
4-7:P8255 */
/* ユニット番号を得るマクロ
*/
#define UNIT(dev) minor(dev)
/* デバッグ表示制御 */
/*
#define MOZ_INTR1_DEBUG
#define MOZ_INTR_DEBUG
#define MOZ_DEBUG
*/
#undef MOZ_INTR1_DEBUG
#undef MOZ_INTR_DEBUG
#undef MOZ_DEBUG
/*
* mozドライバのsoftc
*/
struct moz_softc {
struct isa_device
*dev;
/* デバイスの構造体のポインタ */
char rbuffer[MOZ_BUFFERSIZE]; /*
コマンドのバッファ
*/
char wbuffer[MOZ_BUFFERSIZE]; /*
応答データのバッファ
*/
int
pgid;
/* ドライバを使用しているプロセスのグループプロセスID */
long
x_count;
/*
ロータリエンコーダのX値
*/
long
y_count;
/*
ロータリエンコーダのY値
*/
long
z_count;
/*
ロータリエンコーダのZ値
*/
#ifdef DEVFS
static void *devfs_token;
#endif
};
/* softcの実体 */
typedef struct moz_softc
*sc_p;
static sc_p sca[NMOZ];
/* mozドライバのメジャー番号
*/
#define CDEV_MAJOR 200
/* 初期化ルーチンのプロトタイプ
*/
static
int
mozprobe (struct isa_device *);
static
int
mozattach (struct isa_device *);
/* ドライバルルーチンのプロトタイプ
*/
static
d_open_t
mozopen;
static
d_close_t
mozclose;
static
d_read_t
mozread;
static
d_write_t
mozwrite;
static
d_ioctl_t
mozioctl;
/* mmapとpollは使いません */
/*
static
d_mmap_t
mozmmap;
static
d_poll_t
mozpoll;
*/
/* デバイスドライバの関数定義テーブル
*/
static struct cdevsw
moz_cdevsw
=
{ mozopen,
mozclose,
mozread, mozwrite,
mozioctl,
nostop,
nullreset, nodevtotty,
seltrue,
nommap,
NULL, "moz", NULL, -1 };
/* プローブ、アタッチルーチンの登録
*/
struct isa_driver mozdriver
= {mozprobe,mozattach,"moz" };
/*
割込みハンドラのプロトタイプ
*/
static
ointhand2_t
mozintr;
/*
* mozprobe:
*
* プローブルーチン
*
*
MOZボードをリセットし、DIAGコマンドを出力して、その結果を入力します。
*
もし、DIAGコマンドが正常であればMOZボードが正常に動作していると判断し
*
1を返します。エラーが発生すると0を返します。
*
*/
static int
mozprobe (struct isa_device
*dev)
{
unsigned char val;
int unit = dev->id_unit;
sc_p scp = sca[unit];
/*
* ユニット番号をチェックします
*/
if (unit > NMOZ) {
printf("bad unit (%d)\n", unit);
return (0);
}
if (scp) {
printf("unit %d already attached\n", unit);
return (0);
}
/* MOZボードに搭載されている8255PIOコントローラを初期化します*/
outb(dev->id_iobase + MOZ_P8255_C, MOZ_P8255_INIT);
#ifdef MOZ_DEBUG
printf("MOZ_DEBUG init 8255 %x\n", dev->id_iobase);
#endif
/*********************************************************/
/* MOZボード上の割込みフラグとZ80CPUやその他のデバイスを */
/*
リセットします。
*/
/*********************************************************/
outb (dev->id_iobase + MOZ_P8255_C, MOZ_P8255_2_RESET_BIT_CLR);
outb (dev->id_iobase + MOZ_P8255_C, MOZ_P8255_2_INTCLR_BIT_CLR);
DELAY (1000); /* 1 ms delay */
outb (dev->id_iobase + MOZ_P8255_C, MOZ_P8255_2_RESET_BIT_SET);
outb (dev->id_iobase + MOZ_P8255_C, MOZ_P8255_2_INTCLR_BIT_SET);
DELAY(10000); /* 10 ms delay */
/*
* MOZボードにDIAGコマンドを出力します。
*/
outb(dev->id_iobase,
0);
/* シーケンス番号
*/
outb(dev->id_iobase,
COM_DIAG_NUM);
/*
DIAGコマンド
*/
outb(dev->id_iobase,
0xf0);
/* チェックサム (0 - (10)) */
DELAY(10000);
/* 10 ms
delay
*/
/* リードFIFOのエンプティフラグを得ます */
val = inb(dev->id_iobase + MOZ_P8255_2);
if (val & MOZ_P8255_2_FI_EMPTY) {
val =
inb(dev->id_iobase);
/* データがあるのでリードFIFOから1バイト入力します
*/
if (val !=
0)
/* シーケンス番号をチェックします
*/
return (0);
}
else return (0);
/* リードFIFOのエンプティフラグを得ます */
val = inb(dev->id_iobase + MOZ_P8255_2);
if (val & MOZ_P8255_2_FI_EMPTY) {
val =
inb(dev->id_iobase);
/* データがあるのでリードFIFOから1バイト入力します
*/
if (val !=
COM_DIAG_NUM)
/* コマンド番号をチェックします */
return (0);
}
else return (0);
/* リードFIFOのエンプティフラグを得ます */
val = inb(dev->id_iobase + MOZ_P8255_2);
if (val & MOZ_P8255_2_FI_EMPTY) {
val =
inb(dev->id_iobase);
/* データがあるのでリードFIFOから1バイト入力します
*/
if (val !=
0x01)
/* DIAGコマンドの結果をチェックします*/
return (0);
}
else return (0);
/* リードFIFOのエンプティフラグを得ます */
val = inb(dev->id_iobase + MOZ_P8255_2);
if (val & MOZ_P8255_2_FI_EMPTY) {
val =
inb(dev->id_iobase);
/* データがあるのでリードFIFOから1バイト入力します
*/
if (val !=
0xef)
/* チェックサムをチェックします */
return (0);
}
else return (0);
/* DIAGコマンドが正常なのでIOポートのサイズを返します */
return (NUMPORTS);
}
/*
* mozattach:
*
* アタッチルーチン
*
*
softcのアロケーションを行ない、MOZボードからソフトウェアバージョンを
*
取得しバージョンの表示します。現在のバージョンは"V0.97"です。
*
また、割込みハンドラの登録を行ないます。成功すると1を返します。
*
*/
static int
mozattach (struct isa_device
*dev)
{
int unit = dev->id_unit;
sc_p scp = sca[unit];
/* 割込みハンドラを登録します */
dev->id_ointr = mozintr;
/*
* softcのメモリをアロケートします。
*/
scp = malloc(sizeof(*scp), M_DEVBUF, M_NOWAIT);
if( scp == NULL) {
printf("moz%d failed to allocage driver strorage\n", unit);
return (0);
}
bzero(scp, sizeof(*scp));
sca[unit] = scp;
/*
* Store whatever seems wise.
*/
scp->dev = dev;
#if DEVFS
scp->devfs_token = devfs_add_devswf(&moz_cdevsw, unit, DV_CHR,
UID_ROOT, GID_KMEM, 0600, "moz%d", unit);
#endif
/*
* MOZボードにバージョン取得コマンドを出力します。
*/
outb(dev->id_iobase,
1);
/* シーケンス番号1 */
outb(dev->id_iobase,
COM_VER_NUM);
/* DIAGコマンド
*/
outb(dev->id_iobase,
0xee);
/* check sum (0 - (1+11)) */
DELAY(20000);
/* 20 ms
delay
*/
/*
* 戻り値を表示します。
*/
inb(dev->id_iobase);
/* シーケンス番号をスキップ */
DELAY(1000);
/* 1 ms
delay
*/
inb(dev->id_iobase);
/* コマンド番号をスキップ */
DELAY(1000);
/* 1 ms
delay
*/
printf("moz0: moz80 software version %c", inb(dev->id_iobase)); /* V
*/
DELAY(1000);
/* 1 ms
delay
*/
printf("%c", inb(dev->id_iobase)); /*
整数部を表示(0)
*/
DELAY(1000);
/* 1 ms
delay
*/
printf("%c", inb(dev->id_iobase)); /*
'.'を表示
*/
DELAY(1000);
/* 1 ms
delay
*/
printf("%c", inb(dev->id_iobase)); /*
少数点を表示(9)
*/
DELAY(1000);
/* 1 ms
delay
*/
printf("%c\n", inb(dev->id_iobase)); /*
小数点を表示(7)
*/
DELAY(1000);
/* 1 ms
delay
*/
inb(dev->id_iobase);
/* チェックサムをスキップ */
return 1;
}
/*
* Macro to check that
the unit number is valid
* Often this isn't
needed
as once the open() is performed,
* the unit number is
pretty much safe.. The exception would be if we
* implemented devices
that could "go away". in which case all these routines
* would be wise to
check
the number, DIAGNOSTIC or not.
*/
#define
CHECKUNIT(RETVAL)
do { \
/* the
do-while
is a safe way to do this grouping */ \
if (unit
> NMOZ) { \
printf(__FUNCTION__ ":bad unit %d\n", unit);\
return (RETVAL); \
} \
if (scp
==
NULL) { \
printf( __FUNCTION__ ": unit %d not attached\n", unit); \
return (RETVAL); \
} \
} while (0) \
#ifdef DIAGNOSTIC
#define
CHECKUNIT_DIAG(RETVAL)
CHECKUNIT(RETVAL)
#else /*
DIAGNOSTIC
*/
#define CHECKUNIT_DIAG(RETVAL)
#endif /* DIAGNOSTIC
*/
/*
* mozintr:
*
* 割込みハンドラ
*
* MOZボードの割込み要因
*
* 1.ロータリエンコーダのクロック割込み
*
X,Y,Zの各値をクロック割込のたびに方向スイッチにより
*
増減します。
*
*
2.ライトFIFOメモリのフルフラグクリア割込み
*
ライトFIFOのフルフラグがクリアされたことにより、
*
少なくとも1バイトがFIFOに書き込めると判断し、softcのポインタを
*
引数にしてtsleepでスリープしているライトルーチンをwakeupします。
*
*
3.リードFIFOメモリのエンプティフラグクリア割込み
*
リードFIFOのエンプティフラグがクリアされたことにより、
*
少なくとも1バイトがFIFOから読み込めると判断し、softcのポインタを
*
引数にしてtsleepでスリープしているリードルーチンをwakeupします。
*
*
4.MOZボードからのチェックサムエラー割込み
*
予めioctlによって設定しているグループプロセスIDに
*
USER1シグナルを送出します。上位のアプリケーションで
*
USRR1シグナルを捕捉することで、MOZボードとの通信で
*
エラーが発生したことを知ることができます。
*
* 5.MOZボードからのリミットスイッチ割込み
*
予めioctlによって設定しているグループプロセスIDに
*
USER2シグナルを送出します。上位のアプリケーションで
*
USRR2シグナルを捕捉することで、X,Y,Zいずれかの移動量が
*
リミットスイッチを超えたことを知ることができます。
*/
static void
mozintr(int unit)
{
sc_p scp = sca[unit];
int
port;
/* IOポートの先頭アドレス */
unsigned char intstat; /* 割込みステータス */
unsigned char dirport; /* ロータリエンコーダの方向スイッチ */
/* softcからIOポートの先頭アドレスを得ます */
port = scp->dev->id_iobase;
/* 割込みステータスを入力します */
intstat = inb(port + MOZ_P8255_1);
/* ロータリエンコーダの方向スイッチの値を得ます */
dirport = inb(port + MOZ_P8255_0);
/* 割込みの要因をチェックしてそれぞれ処理します */
if (intstat & MOZ_P8255_1_RE_INT) {
/******************************/
/* ロータリエンコーダの割込み */
/******************************/
#ifdef MOZ_INTR1_DEBUG
printf("MOZ INTR1_DEBUG:RE_INTR1: count %02x %ld %ld %ld\n",
dirport, scp->x_count, scp->y_count, scp->z_count);
#endif
/* ロータリエンコーダの方向?*/
if (intstat & MOZ_P8255_1_RE_DIR) {
/****************/
/* マイナス方向 */
/****************/
if (dirport & MOZ_P8255_0_RE_X_PULS)
/* X方向スイッチがプラスなのでX値をデクリメント */
scp->x_count--;
if (dirport & MOZ_P8255_0_RE_X_MINUS)
/* X方向スイッチがマイナスなのでX値をインクリメント */
scp->x_count++;
if (dirport & MOZ_P8255_0_RE_Y_PULS)
/* Y方向スイッチがプラスなのでY値をデクリメント */
scp->y_count--;
if (dirport & MOZ_P8255_0_RE_Y_MINUS)
/* Y方向スイッチがマイナスなのでY値をインクリメント */
scp->y_count++;
if (dirport & MOZ_P8255_0_RE_Z_PULS)
/* Z方向スイッチがプラスなのでZ値をデクリメント */
scp->z_count--;
if (dirport & MOZ_P8255_0_RE_Z_MINUS)
/* Z方向スイッチがマイナスなのでZ値をインクリメント */
scp->z_count++;
}
else {
/**************/
/* プラス方向 */
/**************/
if (dirport & MOZ_P8255_0_RE_X_PULS)
/* X方向スイッチがプラスなのでX値をインクリメント */
scp->x_count++;
if (dirport & MOZ_P8255_0_RE_X_MINUS)
/* X方向スイッチがマイナスなのでX値をデクリメント */
scp->x_count--;
if (dirport & MOZ_P8255_0_RE_Y_PULS)
/* Y方向スイッチがプラスなのでY値をインクリメント */
scp->y_count++;
if (dirport & MOZ_P8255_0_RE_Y_MINUS)
/* Y方向スイッチがマイナスなのでY値をデクリメント */
scp->y_count--;
if (dirport & MOZ_P8255_0_RE_Z_PULS)
/* Z方向スイッチがプラスなのでZ値をインクリメント */
scp->z_count++;
if (dirport & MOZ_P8255_0_RE_Z_MINUS)
/* Z方向スイッチがマイナスなのでZ値をデクリメント */
scp->z_count--;
}
}
if (intstat & MOZ_P8255_1_FF_INT) {
/********************************************/
/* ライトFIFOメモリのフルフラグクリア割込み */
/********************************************/
#ifdef MOZ_INTR_DEBUG
printf("MOZ INTR_DEBUG: FF_CLS_INTR intstat %02x\n", intstat);
#endif
/* ドライバのライトルーチンがtsleepでスリープしていれば起します */
wakeup((caddr_t)scp);
}
if (intstat & MOZ_P8255_1_EF_INT) {
/**************************************************/
/* リードFIFOメモリのエンプティフラグクリア割込み */
/**************************************************/
#ifdef MOZ_INTR_DEBUG
printf("MOZ INTR_DEBUG: EF_CLS_INTR intstat %02x\n", intstat);
#endif
/* ドライバのリードルーチンがtsleepでスリープしていれば起します */
wakeup((caddr_t)scp);
}
if (intstat & MOZ_P8255_1_CHK_ERR) {
/*************************************/
/* MOZボードからのチェックサム割込み */
/*************************************/
#ifdef MOZ_INTR1_DEBUG
printf("MOZ INTR1_DEBUG: SUM_ERR_INTR intstat %02x pgid %d\n", intstat,
scp->pgid);
#endif
/*
上位アプリケーションにSIGUSR1シグナルを送出します
*/
/* 上位アプリケーションはioctlを使って予めグループプロセスIDを */
/*
登録しておく必要があります
*/
gsignal(scp->pgid, SIGUSR1);
}
if (intstat & MOZ_P8255_1_LIM_ERR) {
/***************************************/
/* MOZボードからのリミットエラー割込み */
/***************************************/
#ifdef MOZ_INTR1_DEBUG
printf("MOZ INTR1_DEBUG: LIMIT_ERR_INTR intstat %02x pgid %d\n",
intstat,
scp->pgid);
#endif
/*
上位アプリケーションにSIGUSR2シグナルを送出します
*/
/* 上位アプリケーションはioctlを使って予めグループプロセスIDを */
/*
登録しておく必要があります
*/
gsignal(scp->pgid, SIGUSR2);
}
/* 割込みフラグをクリアします */
outb (scp->dev->id_iobase + MOZ_P8255_C,
MOZ_P8255_2_INTCLR_BIT_CLR);
outb (scp->dev->id_iobase + MOZ_P8255_C,
MOZ_P8255_2_INTCLR_BIT_SET);
return;
}
/*
* mozioctl:
*
* IOCTLルーチン
*
* 各種設定を行ないます
* 以下のコマンドを受け付けます
*
*
1.MOZ_IOCTL_RESET:
MOZボードをリセットします。
*
2.MOZ_IOCTL_SETPGID:
アプリケーションのグループプロセスIDをセットします。
*
3.MOZ_IOCTL_GETPGID:
アプリケーションのグループプロセスIDを取得します。
*
4.MOZ_IOCTL_CLR_RECOUNT:ロータリエンコーダの値をクリアします。
*
5.MOZ_IOCTL_GET_X_COUNT:ロータリエンコーダのX値を取得します。
*
6.MOZ_IOCTL_GET_Y_COUNT:ロータリエンコーダのY値を取得します。
*
7.MOZ_IOCTL_GET_Z_COUNT:ロータリエンコーダのZ値を取得します。
*
8.MOZ_IOCTL_GET_BUTTON:
ロータリエンコーダのスイッチやボタンの値を取得します。
*
*/
static int
mozioctl (dev_t dev, u_long
cmd, caddr_t data, int flag, struct proc *p)
{
int unit = UNIT (dev);
sc_p scp = sca[unit];
unsigned char dirport;
/*
* ユニット番号をチェックします
*/
CHECKUNIT_DIAG(ENXIO);
switch (cmd) {
case MOZ_IOCTL_RESET:
/******************************/
/* MOZボードをリセットします。*/
/******************************/
outb (scp->dev->id_iobase + MOZ_P8255_C,
MOZ_P8255_2_RESET_BIT_CLR);
outb (scp->dev->id_iobase + MOZ_P8255_C,
MOZ_P8255_2_INTCLR_BIT_CLR);
DELAY (1000); /* 1 ms delay */
outb (scp->dev->id_iobase + MOZ_P8255_C,
MOZ_P8255_2_RESET_BIT_SET);
outb (scp->dev->id_iobase + MOZ_P8255_C,
MOZ_P8255_2_INTCLR_BIT_SET);
DELAY(10000); /* 10 ms delay */
break;
case MOZ_IOCTL_SETPGID:
/******************************************************/
/*アプリケーションのグループプロセスIDをセットします。*/
/******************************************************/
scp->pgid = *(int *)data;
/* printf("MOZ IOCTL setpgid %d\n", scp->pgid); */
break;
case MOZ_IOCTL_GETPGID:
/****************************************************/
/*アプリケーションのグループプロセスIDを取得します。*/
/****************************************************/
*(int *)data = scp->pgid;
/* printf("MOZ IOCTL getpgid %d\n", scp->pgid); */
break;
case MOZ_IOCTL_CLR_RECOUNT:
/*****************************************/
/* ロータリエンコーダの値をクリアします。*/
/*****************************************/
scp->x_count = 0L;
scp->y_count = 0L;
scp->z_count = 0L;
break;
case MOZ_IOCTL_GET_X_COUNT:
/****************************************/
/* ロータリエンコーダのX値を取得します。*/
/****************************************/
*(long *)data = scp->x_count;
break;
case MOZ_IOCTL_GET_Y_COUNT:
/****************************************/
/* ロータリエンコーダのY値を取得します。*/
/****************************************/
*(long *)data = scp->y_count;
break;
case MOZ_IOCTL_GET_Z_COUNT:
/****************************************/
/* ロータリエンコーダのZ値を取得します。*/
/****************************************/
*(long *)data = scp->z_count;
break;
case MOZ_IOCTL_GET_BUTTON:
/*************************************************************/
/* ロータリエンコーダのスイッチや汎用ボタンの値を取得します。*/
/*************************************************************/
dirport = inb (scp->dev->id_iobase + MOZ_P8255_0);
*(unsigned char *)data = dirport;
break;
default:
return ENXIO;
}
return (0);
}
/*
* mozopen:
*
* オープンルーチン
*
*
MOZボードをリセットし、ロータリエンコーダの値をクリアします。
*
*/
static int
mozopen(dev_t dev, int
oflags,
int devtype, struct proc *p)
{
int unit = UNIT (dev);
sc_p scp = sca[unit];
/*
* ユニット番号をチェックします
*/
CHECKUNIT(ENXIO);
/*********************************************************/
/* MOZボード上の割込みフラグとZ80CPUやその他のデバイスを */
/*
リセットします。
*/
/*********************************************************/
outb (scp->dev->id_iobase + MOZ_P8255_C,
MOZ_P8255_2_RESET_BIT_CLR);
outb (scp->dev->id_iobase + MOZ_P8255_C,
MOZ_P8255_2_INTCLR_BIT_CLR);
DELAY (1000); /* 1 ms delay */
outb (scp->dev->id_iobase + MOZ_P8255_C,
MOZ_P8255_2_RESET_BIT_SET);
outb (scp->dev->id_iobase + MOZ_P8255_C,
MOZ_P8255_2_INTCLR_BIT_SET);
DELAY(10000); /* 10 ms delay */
#ifdef MOZ_DEBUG
printf("MOZ_DEBUG open resets Z80\n");
#endif
/* ロータリエンコーダの値をクリアします */
scp->x_count = 0L;
scp->y_count = 0L;
scp->z_count = 0L;
scp->pgid = 0;
return (0);
}
/*
* mozclose:
*
* クローズルーチン
*
* 特に何もしません。
*
*/
static int
mozclose(dev_t dev, int
fflag,
int devtype, struct proc *p)
{
int unit = UNIT (dev);
sc_p scp = sca[unit];
/*
* ユニット番号をチェックします
*/
CHECKUNIT_DIAG(ENXIO);
/* 何もしません */
return (0);
}
/*
* mozread:
*
* リードルーチン
*
* このリードルーチンはSoftware
Disige Jun. 1999 "ISAバスを使って自分だけのデバイスを作ろう"を
* 参考にしました。
*
*/
static int
mozread(dev_t dev, struct uio
*uio, int ioflag)
{
int unit = UNIT (dev);
sc_p scp = sca[unit];
int toread;
unsigned int amount;
unsigned char flag, data;
unsigned char *bufptr;
/*
* ユニット番号をチェックします
*/
CHECKUNIT_DIAG(ENXIO);
/* データ長を得ます */
amount = uio->uio_resid;
/* softcからバッファの先頭のポインタを得ます */
bufptr = scp->rbuffer;
/* リードデータがあるうちはループします */
while (amount > 0) {
/* リードFIFOメモリのエンプティフラグを得ます */
flag = inb(scp->dev->id_iobase + MOZ_P8255_2);
#ifdef MOZ_DEBUG
printf("MOZ_DEBUG read flag %02x\n", flag);
#endif
/* リードFIFOメモリはエンプティかチェック */
if (flag & MOZ_P8255_2_FI_EMPTY) {
/* データがあるので1バイトリードします */
data = inb(scp->dev->id_iobase);
/* 1バイトバッファに書きます */
*bufptr++ = data;
amount--;
}
/* エンプティフラグが立っていて、リードできないのでスリープします */
else tsleep((caddr_t)scp, MOZ_PRI, "Rsleep", hz/20);
#ifdef MOZ_DEBUG
printf("MOZ_DEBUG read flag %02x add %x data %02x\n", flag,
scp->dev->id_iobase,
*bufptr);
#endif
}
/* リードしたデータをバッファから必要バイト数をコピーし、リードした */
/* バイト数を戻します */
toread = (min(uio->uio_resid, sizeof(scp->rbuffer)));
return(uiomove(scp->rbuffer, toread, uio));
}
/*
* mozwrite:
*
* ライトルーチン
*
* このリードルーチンはSoftware
Disige Jun. 1999 "ISAバスを使って自分だけのデバイスを作ろう"を
* 参考にしました。
*
*/
static int
mozwrite(dev_t dev, struct
uio *uio, int ioflag)
{
int unit = UNIT (dev);
sc_p scp = sca[unit];
int towrite, res;
unsigned int amount;
unsigned char flag;
unsigned char *bufptr;
/*
* ユニット番号をチェックします
*/
CHECKUNIT_DIAG(ENXIO);
/* データの長さを得ます */
/* get length */
amount = uio->uio_resid;
/* softcからバッファのポインタを得ます */
bufptr = scp->wbuffer;
/* バッファに必要バイトコピーします */
towrite = (min(uio->uio_resid, sizeof(scp->wbuffer)));
res = uiomove(scp->wbuffer, towrite, uio);
/* ライトデータがあるうちはループします */
while (amount > 0) {
/* ライトFIFOメモリのフルフラグを得ます */
flag = inb(scp->dev->id_iobase + MOZ_P8255_2);
#ifdef MOZ_DEBUG
printf("MOZ_DEBUG write flag %02x\n", flag);
#endif
/* ライトFIFOメモリのフルフラグをチェックします */
if (flag & MOZ_P8255_2_FI_FULL) {
/* 書きこめるので、1バイトバッファからライトFIFOメモリに書き込みます */
outb(scp->dev->id_iobase, *bufptr++);
amount--;
}
/* フルフラグが立っていて、書きこめないのでスリープします */
else tsleep((caddr_t)scp, MOZ_PRI, "Wsleep", hz/20);
#ifdef MOZ_DEBUG
printf("MOZ_DEBUG write flag %02x add %x data %02x\n", flag,
scp->dev->id_iobase,
*bufptr);
#endif
}
/* ライトしたバイト数を返します */
return res;
}
#ifndef MOZ_MODULE
/*
* ローダブルモジュールでない時
*/
static void
moz_drvinit(void *unused)
{
dev_t dev;
dev = makedev(CDEV_MAJOR, 0);
cdevsw_add(&dev, &moz_cdevsw, NULL);
}
SYSINIT(mozdev,
SI_SUB_DRIVERS,
SI_ORDER_MIDDLE+CDEV_MAJOR,
moz_drvinit, NULL)
#else /* MOZ_MODULE
*/
/*
* ローダブルモジュールの時
*/
#include <sys/exec.h>
#include <sys/sysent.h>
#include <sys/lkm.h>
MOD_DEV (moz, LM_DT_CHAR, CDEV_MAJOR, &moz_cdevsw);
static struct isa_device
dev
= {0, &mozdriver, BASE_IO, IRQ, DMA, (caddr_t) PHYS_IO,
PHYS_IO_SIZE,
INT_INT, 0, FLAGS, 0, 0, 0,
0, 1, 0, 0};
static int
moz_load (struct lkm_table
*lkmtp, int cmd)
{
if (mozprobe (&dev)) {
mozattach (&dev);
uprintf ("moz driver loaded\n");
uprintf ("moz: interrupts not hooked\n");
return 0;
} else {
uprintf ("moz driver: probe failed\n");
return 1;
}
}
static int
moz_unload (struct lkm_table
*lkmtp, int cmd)
{
uprintf ("moz driver unloaded\n");
return 0;
}
static int
moz_stat (struct lkm_table
*lkmtp, int cmd)
{
return 0;
}
int
moz_mod (struct lkm_table
*lkmtp,
int cmd, int ver)
{
MOD_DISPATCH(moz, lkmtp, cmd, ver,
moz_load, moz_unload, moz_stat);
}
#endif /* MOZ_MODULE */