BSDでCNC
デバイスドライバ
MOZボード用デバイスドライバの紹介をします。
3.Xから4.Xに変更する時の
注意点などを中心に紹介します。
moz.cはFreeBSD4.X用のステッピング
モータコントロールボード(MOZボード)用のデバイス
ドライバです。FreeBSD3.4で動作していたドライバ
をFreeBSD4.3で動作させるために、以下
の参考文献を基にNEWBUSに対応するように変更しまし
た。4.XのNEWBUSは動的コンフィギュ
レーションに対応するためにprobeやattachルーチ
ンなどのデバイスの検出や初期化が3.Xから
変更されています。ドライバの本体であるread,
write,ioctlなど基本的な部分の変更の必要は
ありませんが、softcの扱い方が異なっているために若干変更が必要
です。
4.X関係の参考文献:
4.Xのセクション9(カーネルインターフェース)のマニュアル
参考WEB:
http://www.freebsd.org/
"ISA
device drivers"
http://www.daemonnews.org/
"Writing
an ISA device driver"
"How
to Write Kernel Driver with NEWBUS"
参考ソースコード
led.c http://big.endian.de/misc/led.c
joy.c (FreeBSD3.4/4.3)
sio.c (FreeBSD3.4/4.3)
3.X関係のドライバの参考図書:
FreeBSDカーネル入門(改定版) アスキー出版局
386/486マシンユーザのためのUNIXデバイスドライバ ソフトバンク
Software Disige
Jun. 1999 "ISAバスを使って自分だけのデバイスを作ろう"
Software Disige
Apr. 1999 "FreeBSDデバイスドライバ作製術"
以下に4.Xで動作しているmozデバイスドライバの
ソースを示します。コメントは日本語で入れて
いますが注意する部分は色分けしてみました。
青緑:主に3.Xから4.Xへの変更点などの注意事項を表します。
緑:注目点などを緑で表します。
3.Xのドライバのソースを見るにはここをクリックしてください。
2003/2/18
モータ制御のプログラムをGUI(XView)で作成する予定のため、ポーリング用のmozpollを追加しました。
#include "moz.h"
#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
#include <sys/bus.h>
#include <sys/errno.h>
#include <sys/conf.h>
#include <sys/module.h>
#include <sys/uio.h>
#include <machine/bus.h>
#include <machine/resource.h>
#include <sys/rman.h>
#include <sys/signalvar.h>
#include <isa/isavar.h>
#include <sys/mozio.h> /* IOCTL用のヘッダファイル*/
#define
MOZ_IOADDR
0x100 /* IOポートの先頭アドレス */
#define
MOZ_IOSIZE
8 /*
IOポートのサイズ
*/
#define
MOZ_IRQ
10 /*
IRQの番号
*/
/* 各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 制御 */
#define
MOZ_P8255_INIT
0x93 /*
P8255の初期化データ
*/
/* p0:input P1:input P2l:intpu P2h:output */
/* P8255のポート0のビットの定義 */
#define
MOZ_P8255_0_RE_X_PULS
0x01 /* ロータリエンコーダX軸+イネーブルSW */
#define
MOZ_P8255_0_RE_X_MINUS
0x02 /* ロータリエンコーダX軸−イネーブルSW */
#define
MOZ_P8255_0_RE_Y_PULS
0x04 /* ロータリエンコーダY軸+イネーブルSW */
#define
MOZ_P8255_0_RE_Y_MINUS
0x08 /* ロータリエンコーダY軸−イネーブルSW */
#define
MOZ_P8255_0_RE_Z_PULS
0x10 /* ロータリエンコーダZ軸+イネーブルSW */
#define
MOZ_P8255_0_RE_Z_MINUS
0x20 /* ロータリエンコーダZ軸−イネーブルSW */
#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進数です
*/
/*
*/
/*****************************************************/
/* MOZボードのコマンド、応答データのバッファサイズ
*/
#define MOZ_BUFFERSIZE 16
/* デバッグ表示制御 */
/*
#define MOZ_INTR1_DEBUG
#define MOZ_INTR_DEBUG
#define MOZ_DEBUG
*/
#undef MOZ_INTR1_DEBUG
#undef MOZ_INTR_DEBUG
#undef MOZ_DEBUG
/* 初期化ルーチンのプロトタイプ */
static void mozidentify (driver_t *driver,
device_t parent);
static int mozprobe(device_t);
static int mozattach(device_t);
/* ドライバルルーチンのプロトタイプ */
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;
static
d_poll_t
mozpoll;
/*
* mozドライバの
softcのテンプレート
*
* (注意)3.4用
ではsoftcの実体が広域変数として以下のようにドライバ内
* にありました。
* typedef
struct moz_softc *scp;
* static
scp sca[NMOZ];
*
4.Xではテンプレートのみです。したがって各ルーチン内ではsoftcの
* ポインタを用意し
て実体のアドレスをコピーしてから使用します。
* また、割込みハン
ドラの引数はsoftcのポインタが渡されるように変更
* されています。
* 3.XではIO
ポートのアドレスは"struct
isa_device *dev"の中にiobaseが
* あり、scp-
>dev->id_iobaseのように使用していました。4.Xではsoftcの
* メンバとして
iobaseを用意して、scp->iobaseとして使用します。
*
*/
struct moz_softc {
u_int
iobase;
/*
MOZボードのIOポート先頭アドレス
*/
struct resource *portres; /*
IOポートのリソースへのポインタ
*/
struct resource
*irqres;
/*
IRQのリソースへのポインタ
*/
void
*cookie;
/*
割込みハンドラの登録で使うcookie
*/
struct selinfo selp; /* for poll func.
2003/2/13
*/
char rbuffer[MOZ_BUFFERSIZE]; /*
コマンドのバッファ
*/
char wbuffer[MOZ_BUFFERSIZE]; /*
応答データのバッファ
*/
int
pgid;
/* ドライバを使用しているプロセスのプロセスID */
long
x_count;
/*
ロータリエンコーダのX値
*/
long
y_count;
/*
ロータリエンコーダのY値
*/
long
z_count;
/*
ロータリエンコーダのZ値
*/
};
/* デバイスクラスの定義 */
static devclass_t moz_devclass;
/* mozドライバのメジャー番号 */
#define MAJOR 200
/* デバイスドライバの関
数定義テーブル
* (注意)3.Xとはテーブル内の関数名が異なっています。
* 3.Xでのスケルトンは以下のようになっていました。
* 4.Xではメジャー番号も必要です。
* mozopen
* mozclose
* mozread
* mozwrite
* mozioctl
* nullstop
* nullreset
* nodevtotty
* mozpoll
* mozmmap
* NULL
* "moz"
* NULL
* -1
*/
static struct cdevsw moz_cdevsw = {
mozopen,
/* open */
mozclose, /*
close */
mozread,
/* read */
mozwrite, /*
write */
mozioctl, /*
ioctl */
mozpoll,
/* poll */
nommap,
/* mmapは無し */
nostrategy, /* strategyは無し
*/
"moz",
/*
MOZ
*/
MAJOR,
/* majorは200 */
nodump,
/* dumpは無し */
nopsize,
/* psizeは無し */
0,
/* flags */
-1
};
/* 割込みハンドラのプロ
トタイプ
*/
/* (注意)割込みハンドラの引
数が4.Xと3.Xでは異なっています。
*/
/* 3.X
の割込みハンドラへはunit番号が引数として渡されていましたが
*/
/* 4.Xhではsoftcのポ
インタが引数として渡されます。
*/
/* voidとして定義してキャ
ストして使用します。
*/
static void
mozintr
__P((void *arg));
/*
* mozidentify:
*
* アイデンティファ
イルーチン
*
* MOZボードはPnPではないため明示的にIOポートアドレスとIRQ番号を設定します。
* このルーチンはled.cを参考にしました。
* (注意)このアイ
デンティファイは3.Xではありませんでした。
*
*/
static void
mozidentify (driver_t *driver, device_t
parent)
{
devclass_t dc;
device_t child;
/* mozのデバイスクラスを探します */
dc = devclass_find("moz");
if (devclass_get_device(dc, 0) == NULL) {
/* デバイスクラスが見つかったのでmozをISAバスに追加します */
child = BUS_ADD_CHILD(parent, 0, "moz", -1);
/* IOポートの先頭アドレスを設定します */
bus_set_resource(child, SYS_RES_IOPORT, 0, MOZ_IOADDR,
1);
/* IRQ番号を設定します */
bus_set_resource(child, SYS_RES_IRQ, 0, MOZ_IRQ,
1);
#ifdef MOZ_DEBUG
printf("MOZ_DEBUG IN IDENTIFY: end of identify\n");
#endif
}
}
/*
* mozprobe:
*
* プローブルーチン
*
* IOポートアドレスのリソースのアロケーションを行ないます。
*
MOZボードをリセットし、DIAGコマンドを出力して、その応答データを入力します。
* もし、DIAGコマンドが正常であればMOZボードが正常に動作していると判断し
* プローブが成功したとして0を返します。
*(注意)3.Xでは成
功するとIOポートのアドレスの範囲のサイズを返していました。
* また、引数はstruct
isa_device *devとなっていました。
*/
static int
mozprobe (device_t dev)
{
struct moz_softc *scp;
struct resource *portres;
int rid;
int unit;
u_int iobase;
u_char val;
/* softcのポインタを得ます */
scp = (struct moz_softc *) device_get_softc(dev);
if (scp == NULL) {
device_printf(dev, "probe: bad softc\n");
return (ENXIO);
}
rid = 0;
/* (注意)
* start = 0, end = ~0(0xffffffff) は特別な意味付けがあり、
* 予めbus_set_resource()によって値がセットされていることを
* 示します。詳しくは以下の参考文献の
* FreeBSD Developers' Handbookのbus_alloc_resource()関数の
* 説明を参照してください。
*/
portres = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid,
0, ~0,
MOZ_IOSIZE, RF_ACTIVE);
/* リソースをチェックします */
if (!portres)
return (ENXIO);
/* softcへIOポートのリソースをセットします */
scp->portres = portres;
/*
* ユニット番号をチェックします
*/
unit = device_get_unit(dev);
if (unit > NMOZ) {
device_printf(dev, "probe: bad unit (%d)\n", unit);
return (ENXIO);
}
/*
* MOZボードのIOポート先頭アドレスを得ます
*/
iobase = rman_get_start(portres);
/* softcへIOポートの先頭アドレスをセットします */
scp->iobase = iobase;
#ifdef MOZ_DEBUG
device_printf(dev, "MOZ_DEBUG PROBE: iobase = 0x%x\n", iobase);
#endif
/* MOZボードに搭載されている8255PIOコントローラを初期化します*/
outb(iobase + MOZ_P8255_C, MOZ_P8255_INIT);
/*
* MOZボード上の割込みフラグとZ80CPUやその他のデバイスを
* リセットします。
*/
outb (iobase + MOZ_P8255_C, MOZ_P8255_2_RESET_BIT_CLR);
outb (iobase + MOZ_P8255_C, MOZ_P8255_2_INTCLR_BIT_CLR);
DELAY (1000); /* 1 ms delay */
outb (iobase + MOZ_P8255_C, MOZ_P8255_2_RESET_BIT_SET);
outb (iobase + MOZ_P8255_C, MOZ_P8255_2_INTCLR_BIT_SET);
DELAY(10000); /* 10 ms delay */
/*
* MOZボードにDIAGコマンドを出力します。
*/
outb(iobase,
0);
/* シーケンス番号
*/
outb(iobase, COM_DIAG_NUM);
/*
DIAGコマンド
*/
outb(iobase,
0xf0);
/* チェックサム (0 - (10)) */
DELAY(10000);
/* 10 ms
delay
*/
/*
* DIAGコマンドの戻り値を入力します
*/
/* リードFIFOのエンプティフラグを得ます */
val = inb(iobase + MOZ_P8255_2);
if (val & MOZ_P8255_2_FI_EMPTY) {
val =
inb(iobase);
/* データがあるのでリードFIFOから1バイト入力します */
if (val !=
0)
/*
シーケンス番号をチェックします
*/
return (ENXIO);
}
else return (ENXIO);
/* リードFIFOのエンプティフラグを得ます */
val = inb(iobase + MOZ_P8255_2);
if (val & MOZ_P8255_2_FI_EMPTY) {
val =
inb(iobase);
/* データがあるのでリードFIFOから1バイト入力します */
if (val !=
COM_DIAG_NUM)
/*
コマンド番号をチェックします
*/
return (ENXIO);
}
else return (ENXIO);
/* リードFIFOのエンプティフラグを得ます */
val = inb(iobase + MOZ_P8255_2);
if (val & MOZ_P8255_2_FI_EMPTY) {
val =
inb(iobase);
/* データがあるのでリードFIFOから1バイト入力します */
if (val !=
0x01)
/*
DIAGコマンドの結果をチェックします
*/
return (ENXIO);
}
else return (ENXIO);
/* リードFIFOのエンプティフラグを得ます */
val = inb(iobase + MOZ_P8255_2);
if (val & MOZ_P8255_2_FI_EMPTY) {
val =
inb(iobase);
/* データがあるのでリードFIFOから1バイト入力します */
if (val !=
0xef)
/*
チェックサムをチェックします
*/
return (ENXIO);
}
else return (ENXIO);
/* DIAGコマンドが正常なので0を返します */
return
(0);
}
/*
* attach:
*
* アタッチルーチン
*
* IRQのリソースのアロケーションを行ない、割込みハンドラの登録を行ないます。
*
またMOZボードからソフトウェアバージョンを取得しバージョン番号を表示します。
* 現在のバージョンは"V0.97"です。また、make_devを使用してカーネルにmozドライバを
* 登録します。成功すると0を返します。
* (注意)3.Xで
は成功すると1を返していました。
* また、引数もstruct
isa_device *devとなっていました。
*
*/
static int
mozattach (device_t
dev)
{
struct moz_softc *scp;
struct resource *irqres;
int rid;
int res;
/* device_t devの時はdevice_get_softc()を使ってsoftcのポインタを得ます
*/
scp = (struct moz_softc *) device_get_softc(dev);
rid = 0;
/*(注意)
* start = 0, end = ~0(0xffffffff) は特別な意味付けがあり、
* 予めbus_set_resource()によって値がセットされていることを
* 示します。 詳しくは以下の参考文献の
* FreeBSD Developers' Handbookのbus_alloc_resource()関数の
* 説明を参照してください。
*/
irqres = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0,
~0,
1, RF_ACTIVE);
if (irqres) {
/* IRQのリソースがセットされていたので割込みハンドラmozintr()を登録します
*/
/* フラグには通常のキャラクタデバイスの割込みタイプINTR_TYPE_TTYをセットします
*/
res = BUS_SETUP_INTR(device_get_parent(dev), dev, irqres, INTR_TYPE_TTY,
mozintr, scp,
&scp->cookie);
}
else {
device_printf(dev, "attach: unable to allocate interrupt resource\n");
return (ENXIO);
}
/* softcへIRQのリソースをセットします */
scp->irqres
= irqres;
#ifdef MOZ_DEBUG
device_printf(dev, "MOZ_DEBUG IN ATTACH alloc IRQ reseource\n");
#endif
/*
* MOZボードにバージョン取得コマンドを出力します。
*/
outb(scp->iobase,
1);
/* シーケンス番号1 */
outb(scp->iobase, COM_VER_NUM); /*
DIAGコマンド
*/
outb(scp->iobase,
0xee);
/* check sum (0 - (1+11)) */
DELAY(20000);
/* 20 ms
delay
*/
/*
* 戻り値を表示します。
*/
inb(scp->iobase);
/* シーケンス番号をスキップ */
DELAY(1000);
/* 1 ms
delay
*/
inb(scp->iobase);
/* コマンド番号をスキップ */
DELAY(1000);
/* 1 ms
delay
*/
printf("moz0: moz80 software version %c", inb(scp->iobase));
/*
Vを表示
*/
DELAY(1000);
/* 1 ms
delay
*/
printf("%c", inb(scp->iobase)); /*
整数部を表示(0)
*/
DELAY(1000);
/* 1 ms
delay
*/
printf("%c", inb(scp->iobase)); /*
'.'を表示
*/
DELAY(1000);
/* 1 ms
delay
*/
printf("%c", inb(scp->iobase)); /*
少数点を表示(9)
*/
DELAY(1000);
/* 1 ms
delay
*/
printf("%c\n", inb(scp->iobase)); /*
小数点を表示(7)
*/
DELAY(1000);
/* 1 ms
delay
*/
inb(scp->iobase);
/* チェックサムをスキップ */
/* カーネルにmozドライバを登録します */
make_dev(&moz_cdevsw, 0, UID_ROOT, GID_WHEEL, 0644, "moz0");
return (0);
}
/*
* intr:
*
* 割込みハンドラ
*
* MOZボードの割込み要因
*
* 1.ロータリエン
コーダのクロック割込み
*
X,Y,Zの各値をクロック割込のたびに方向スイッチにより増減します。
*
* 2.ライト
FIFOメモリのフルフラグクリア割込み
*
ライトFIFOのフルフラグがクリアされたことにより、
*
少なくとも1バイトがFIFOに書き込めると判断し、softcのポインタを
*
引数にしてtsleepでスリープしているルーチンをwakeupします。
*
* 3.リード
FIFOメモリのエンプティフラグクリア割込み
*
リードFIFOのエンプティフラグがクリアされたことにより、
*
少なくとも1バイトがFIFOから読み込めると判断し、softcのポインタを
*
引数にしてtsleepでスリープしているルーチンをwakeupします。
*
* 4.MOZボード
からのチェックサムエラー割込み
*
open()で設定したデバイスを使用しているプロセスIDに
*
USER1シグナルを送出します。上位のアプリケーションで
*
USRR1シグナルを捕捉することで、MOZボードとの通信で
*
エラーが発生したことを知ることができます。
*
* 5.MOZボード
からのリミットスイッチ割込み
*
open()で設定したデバイスを使用しているプロセスIDに
*
USER2シグナルを送出します。上位のアプリケーションで
*
USRR2シグナルを捕捉することで、X,Y,Zいずれかの移動量が
*
リミットスイッチを超えたことを知ることができます。
*
* (注意)割込みハ
ンドラの引数にsoftcのポインタが渡されるように変更されています。
*
3.Xではunit番号が引数として渡されていました。
*/
void
mozintr(void *arg)
{
u_int
port;
/* IOポートの先頭アドレス */
unsigned char intstat; /* 割込みステータス */
unsigned char dirport; /* ロータリエンコーダの方向スイッチ */
struct moz_softc *scp;
/* voidをキャストして使用します。*/
scp = (struct moz_softc *)arg;
/* softcからIOポートの先頭アドレスを得ます */
port = scp->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シグナルを送出します */
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シグナルを送出します */
gsignal(scp->pgid, SIGUSR2);
}
/* 割込みフラグをクリアします */
outb (scp->iobase + MOZ_P8255_C, MOZ_P8255_2_INTCLR_BIT_CLR);
outb (scp->iobase + MOZ_P8255_C, MOZ_P8255_2_INTCLR_BIT_SET);
return;
}
/*
*
* ポーリングルーチン
* XViewツール(GUI)から制御するためにポーリングルーチンを追加
* 2003/2/12
*
*/
static int
mozpoll(dev_t dev, int events, struct proc
*p)
{
struct moz_softc *scp;
int unit, revents = 0;
unsigned char ioflag;
/*
* ユニット番号をチェックします
*/
unit = minor(dev) >> 16;
if (unit > NMOZ) {
printf("poll: bad unit (%d)\n", unit);
return (ENXIO);
}
/* devclass_get_softc()を使用してsoftcのポインタを得ます */
scp = devclass_get_softc(moz_devclass, unit);
/* WRITE */
if (events & (POLLOUT | POLLWRNORM)) {
/* フラグデータを得ます*/
ioflag = inb(scp->iobase + MOZ_P8255_2);
#ifdef MOZ_DEBUG_POLL
printf("MOZ_DEBUG IN POLL write flag %02x\n", ioflag);
#endif
/* ライトFIFOメモリのフルフラグをチェックします */
if (ioflag & MOZ_P8255_2_FI_FULL)
revents |= (events & (POLLOUT | POLLWRNORM));
else
selrecord(p, &scp->selp);
}
/* READ */
if (events & (POLLIN | POLLRDNORM)) {
/* フラグデータを得ます*/
ioflag = inb(scp->iobase + MOZ_P8255_2);
#ifdef MOZ_DEBUG_POLL
printf("MOZ_DEBUG IN POLL read flag %02x\n", ioflag);
#endif
/* リードFIFOメモリはエンプティかチェック */
if (ioflag & MOZ_P8255_2_FI_EMPTY)
revents |= (events & (POLLIN | POLLRDNORM));
else
selrecord(p, &scp->selp);
}
return (revents);
}
/*
* 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;
struct moz_softc *scp;
unsigned char dirport;
/*
* ユニット番号をチェックします
*/
unit = minor(dev) >> 16;
if (unit > NMOZ) {
printf("ioctl:bad unit (%d)\n", unit);
return (ENXIO);
}
/* devclass_get_softc()を使用してsoftcのポインタを得ます */
scp = devclass_get_softc(moz_devclass, unit);
switch (cmd) {
case MOZ_IOCTL_RESET:
/*********************************************************/
/* MOZボード上の割込みフラグとZ80CPUやその他のデバイスを */
/*
リセットします。
*/
/*********************************************************/
outb (scp->iobase + MOZ_P8255_C, MOZ_P8255_2_RESET_BIT_CLR);
outb (scp->iobase + MOZ_P8255_C, MOZ_P8255_2_INTCLR_BIT_CLR);
DELAY (1000); /* 1 ms delay */
outb (scp->iobase + MOZ_P8255_C, MOZ_P8255_2_RESET_BIT_SET);
outb (scp->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->iobase + MOZ_P8255_0);
*(unsigned char *)data = dirport;
break;
default:
return (ENXIO);
}
return (0);
}
/*
* mozopen:
*
* オープンルーチン
*
* MOZボードをリセットし、ロータリエンコーダの値をクリアします。
* ドライバをオープンしたプロセスのプロセスIDをsoftcへセットします。
*
*/
static int
mozopen(dev_t dev, int flags, int mode,
struct
proc *p)
{
struct moz_softc *scp;
int unit;
/*
* ユニット番号をチェックします
*/
unit = minor(dev) >> 16;
if (unit > NMOZ) {
printf("open: bad unit (%d)\n", unit);
return (ENXIO);
}
/* devclass_get_softc()を使用してsoftcのポインタを得ます */
scp = devclass_get_softc(moz_devclass, unit);
/*
ドライバを使用しているプロセスのプロセスIDをsoftcへセットします
*/
/* これは割込みルーチンで検出したチェックサムエラーやリミットエラーをシグナルを
*/
/*
使ってアプリケーションに知らせるためにです。
*/
scp->pgid = p->p_pid;
/*********************************************************/
/* MOZボード上の割込みフラグとZ80CPUやその他のデバイスを */
/*
リセットします。
*/
/*********************************************************/
outb (scp->iobase + MOZ_P8255_C, MOZ_P8255_2_RESET_BIT_CLR);
outb (scp->iobase + MOZ_P8255_C, MOZ_P8255_2_INTCLR_BIT_CLR);
DELAY (1000); /* 1 ms delay */
outb (scp->iobase + MOZ_P8255_C, MOZ_P8255_2_RESET_BIT_SET);
outb (scp->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;
return (0);
}
/*
* mozclose:
*
* クローズルーチン
*
* 特に何もしません。
*
*/
static int
mozclose(dev_t dev, int flag, int mode,
struct
proc *p)
{
int unit;
/*
* ユニット番号をチェックします
*/
unit = minor(dev) >> 16;
if (unit > NMOZ) {
printf("close: bad unit (%d)\n", unit);
return (ENXIO);
}
/* 何もしません */
return (0);
}
/*
* mozread:
*
* リードルーチン
*
* このリードルーチンはSoftware
Disige Jun. 1999 "ISAバスを使って自分だけのデバイスを作ろう"を
* 参考にしました。
*/
static int
mozread(dev_t dev, struct uio *uio, int flag)
{
struct moz_softc *scp;
int unit;
int toread;
unsigned int amount;
unsigned char ioflag, data;
unsigned char *bufptr;
/*
* ユニット番号をチェックします
*/
unit = minor(dev) >> 16;
if (unit > NMOZ) {
printf("read: bad unit (%d)\n", unit);
return (ENXIO);
}
/* devclass_get_softc()を使用してsoftcのポインタを得ます */
scp = devclass_get_softc(moz_devclass, unit);
/* データ長を得ます */
amount = uio->uio_resid;
/* softcからバッファの先頭のポインタを得ます */
bufptr = scp->rbuffer;
/* リードデータがあるうちはループします */
while (amount > 0) {
/* リードFIFOメモリのエンプティフラグを得ます */
ioflag = inb(scp->iobase + MOZ_P8255_2);
#ifdef MOZ_DEBUG
printf("MOZ_DEBUG IN READ read flag %02x\n", ioflag);
#endif
/* リードFIFOメモリはエンプティかチェック */
if (ioflag & MOZ_P8255_2_FI_EMPTY) {
/* データがあるので1バイトリードします */
data = inb(scp->iobase);
/* 1バイトバッファに書きます */
*bufptr++ = data;
amount--;
}
/* エンプティフラグが立っていて、リードできないのでタイムアウト無しでスリープします
*/
else {
err = tsleep((void *)scp, PZERO+10 | PCATCH, "Rsleep", 0);
if (err)
return (err);
}
#ifdef MOZ_DEBUG
printf("MOZ_DEBUG IN READ read flag %02x add %x data %02x\n",
ioflag, scp->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
flag)
{
struct moz_softc *scp;
int unit;
int towrite, res;
unsigned int amount;
unsigned char ioflag;
unsigned char *bufptr;
/*
* ユニット番号をチェックします
*/
unit = minor(dev) >> 16;
if (unit > NMOZ) {
printf("write: bad unit (%d)\n", unit);
return (ENXIO);
}
/* devclass_get_softc()を使用してsoftcのポインタを得ます */
scp = devclass_get_softc(moz_devclass, unit);
/* データの長さを得ます */
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メモリのフルフラグを得ます */
ioflag = inb(scp->iobase + MOZ_P8255_2);
#ifdef MOZ_DEBUG
printf("MOZ_DEBUG IN WRITE write flag %02x\n", ioflag);
#endif
/* ライトFIFOメモリのフルフラグをチェックします */
if (ioflag & MOZ_P8255_2_FI_FULL) {
/* 書きこめるので、1バイトバッファからライトFIFOメモリに書き込みます */
outb(scp->iobase, *bufptr++);
amount--;
}
/* フルフラグが立っていて、書きこめないのでタイムアウト無しでスリープします
*/
else {
err = tsleep((void *)scp, PZERO+10 | PCATCH, "Wsleep", 0);
if (err)
return (err);
}
#ifdef MOZ_DEBUG
printf("MOZ_DEBUG IN WRITE write flag %02x add %x data %02x\n",
ioflag, scp->iobase, *bufptr);
#endif
}
/* ライトしたバイト数を返します */
return res;
}
/* 初期化ルーチンのメソッドの定義 */
static device_method_t moz_methods[] = {
/* Device interface */
DEVMETHOD(device_identify, mozidentify),
DEVMETHOD(device_probe,
mozprobe),
DEVMETHOD(device_attach,
mozattach),
{ 0, 0 }
};
static driver_t moz_driver = {
"moz",
moz_methods,
sizeof(struct moz_softc),
};
/* kldを使わないでスタテックリンクで使用する場合は引数の最後の2つ
*/
/* load_function,load_argumentを0とします
*/
DRIVER_MODULE(moz, isa, moz_driver,
moz_devclass,
0, 0);