line
BSDでCNC

デバイスドライバ

MOZボード用デバイスドライバの紹介をします。 3.Xから4.Xに変更する時の
注意点などを中心に紹介します。
line

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を追加しました。

line

/*
 * moz.c
 *
 * mozはFreeBSD4.X用パーソナルCNC (小型フライス盤CNC化のための)用ステッピング
 * モータコントロールボード用(MOZボード)のデバイスドライバです。
 *
 * FreeBSD3.4で動作していたドライバをFreeBSD4.3で動作させるために、各参考文献を元に
 * NEWBUSに対応するように変更しました。
 *
 * 2003/2/18 XViewツール(GUI)から制御するためにポーリングルーチンpollを実装
 *           tsleepのタイムアウト無しに設定
 *           tsleepの戻り値がエラーの場合復帰するように修正
 *
 */

#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);

line

back