<モータコントロールデバイスドライバ>

 

デバイスドライバの基本動作とモータコントロールボー ドのブロック図
 

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 */