/************************************************/
/* uPD7210 driver				*/
/*----------------------------------------------*/
/*	for FreeBSD 3.4-RELEASE			*/
/*						*/
/************************************************/
/* (note)
  flags: bit0 - Talk only
	 bit1 - Listen only
	 bit2 -/Controler
  options:
	 GPIB_PC9801_29	NEC PC-9801-29, 29N
	 GPIB_CONTEC_PC	CONTEC GP-IB(PC)
*/

/***** Feature test switches ************************************************/
#include "gpib.h"
#if NGPIB > 0

/***** System headers *******************************************************/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/proc.h>
#include <sys/conf.h>
#include <sys/fcntl.h>
#include <sys/kernel.h>
#include <sys/uio.h>
#include <sys/syslog.h>
#include <sys/sysctl.h>
#include <machine/clock.h>
#include <i386/isa/isa_device.h>


/***** Local headers ********************************************************/
#if defined(PC98)
  #include <pc98/pc98/upd7210.h>
#else
  #include <i386/isa/upd7210.h>
#endif


/***** Depend on expansion board ********************************************/
#if defined(GPIB_PC9801_29)
  #define T1679		0x08	/* 遷移時間 (clock 8MHz) */
  #define TRM01		0x30	/* T/R2=CIC / T/R3=PE */
  #define MODE_T1	1	/* B2にセットするモード（高速/低速） */

  #define data_in(p)		inb(p->iobase+0x00)	/* uPD7210 Read register */
  #define interrupt_status1(p)	inb(p->iobase+0x02)
  #define interrupt_status2(p)	inb(p->iobase+0x04)
  #define serial_poll_status(p)	inb(p->iobase+0x06)
  #define address_status(p)	inb(p->iobase+0x08)
  #define command_pass_through(p) inb(p->iobase+0x0a)
  #define address0(p)		inb(p->iobase+0x0c)
  #define address1(p)		inb(p->iobase+0x0e)

  #define byte_out(p,d)		outb(p->iobase+0x00,(d)) /* uPD7210 Write register */
  #define interrupt_mask1(p,d)	outb(p->iobase+0x02,(d))
  #define interrupt_mask2(p,d)	outb(p->iobase+0x04,(d))
  #define serial_poll_mode(p,d)	outb(p->iobase+0x06,(d))
  #define address_mode(p,d)	outb(p->iobase+0x08,(d))
  #define auxiliary_mode(p,d)	outb(p->iobase+0x0a,(d))
  #define address(p,d)		outb(p->iobase+0x0c,(d))
  #define end_of_string(p,d)	outb(p->iobase+0x0e,(d))

#elif defined(GPIB_CONTEC_PC)
  #define T1679		0x08	/* 遷移時間 (clock 8MHz) */
  #define TRM01		0x30	/* T/R2=CIC / T/R3=PE */
  #define MODE_T1	1	/* B2にセットするモード（高速/低速） */

  #define data_in(p)		inb(p->iobase+0x0000)	/* uPD7210 Read register */
  #define interrupt_status1(p)	inb(p->iobase+0x0400)
  #define interrupt_status2(p)	inb(p->iobase+0x0800)
  #define serial_poll_status(p)	inb(p->iobase+0x0c00)
  #define address_status(p)	inb(p->iobase+0x1000)
  #define command_pass_through(p) inb(p->iobase+0x1400)
  #define address0(p)		inb(p->iobase+0x1800)
  #define address1(p)		inb(p->iobase+0x1c00)

  #define byte_out(p,d)		outb(p->iobase+0x0000,(d)) /* uPD7210 Write register */
  #define interrupt_mask1(p,d)	outb(p->iobase+0x0400,(d))
  #define interrupt_mask2(p,d)	outb(p->iobase+0x0800,(d))
  #define serial_poll_mode(p,d)	outb(p->iobase+0x0c00,(d))
  #define address_mode(p,d)	outb(p->iobase+0x1000,(d))
  #define auxiliary_mode(p,d)	outb(p->iobase+0x1400,(d))
  #define address(p,d)		outb(p->iobase+0x1800,(d))
  #define end_of_string(p,d)	outb(p->iobase+0x1c00,(d))

  int IRQ_MASK_ADRS;

#else
  #define T1679		0x08	/* 遷移時間 (clock 8MHz) */
  #define TRM01		0x30	/* T/R2=CIC / T/R3=PE */
  #define MODE_T1	1	/* B2にセットするモード（高速/低速） */

  #define data_in(p)		inb(p->iobase+0x00)	/* uPD7210 Read register */
  #define interrupt_status1(p)	inb(p->iobase+0x02)
  #define interrupt_status2(p)	inb(p->iobase+0x04)
  #define serial_poll_status(p)	inb(p->iobase+0x06)
  #define address_status(p)	inb(p->iobase+0x08)
  #define command_pass_through(p) inb(p->iobase+0x0a)
  #define address0(p)		inb(p->iobase+0x0c)
  #define address1(p)		inb(p->iobase+0x0e)

  #define byte_out(p,d)		outb(p->iobase+0x00,(d)) /* uPD7210 Write register */
  #define interrupt_mask1(p,d)	outb(p->iobase+0x02,(d))
  #define interrupt_mask2(p,d)	outb(p->iobase+0x04,(d))
  #define serial_poll_mode(p,d)	outb(p->iobase+0x06,(d))
  #define address_mode(p,d)	outb(p->iobase+0x08,(d))
  #define auxiliary_mode(p,d)	outb(p->iobase+0x0a,(d))
  #define address(p,d)		outb(p->iobase+0x0c,(d))
  #define end_of_string(p,d)	outb(p->iobase+0x0e,(d))

#endif



/***** Constat values *******************************************************/
#define MY_ADDRESS	30	/* my gpib address. less than 31 */
#define TIMEOUT		5	/* sec */
#define DELIMITER	GPIB_DLM_CR+GPIB_DLM_LF+GPIB_DLM_EOI
#ifndef GPIB_BUFFERSIZE
  #define GPIB_BUFFERSIZE 1024	/* send/receive buffer size */
#endif


/* uPD7210 Auxiliary Command */
#define IMMEDIATE_EXECUTE_PON		0x00
#define CHIP_RESET			0x02
#define FINISH_HANDSHAKE		0x03
#define TRIGGER				0x04
#define CLEAR_RETURN_TO_LOCAL		0x05
#define SET_RETURN_TO_LOCAL		0x0d
#define SEND_EOI			0x06
#define NON_VALID			0x07
#define VALID				0x0f
#define SET_PARALLEL_POLL_FLAG		0x01
#define CLEAR_PARALLEL_POLL_FLAG	0x09
#define GO_TO_STANDBY			0x10
#define TAKE_CONTROL_ASYNCHRONOUSLY	0x11
#define TAKE_CONTROL_SYNCHRONOUSLY	0x12
#define TAKE_CONTROL_SYNCHRONOUSLY_ON_END 0x1a
#define LISTEN				0x13
#define LISTEN_WITH_CONTINUOUS_MODE	0x1b
#define LOCAL_UNLISTEN			0x1c
#define EXECUTE_PARALLEL_POLL		0x1d
#define SET_IFC				0x1e
#define CLEAR_IFC			0x16
#define SET_REN				0x1f
#define CLEAR_REN			0x17
#define DISABLE_SYSTEM_CONTROL		0x14

/* inturrupt status resister bit mask */
#define BIT_DI	0x01	/* DI  : DataIn  bit in Interrupt status 1 */
#define BIT_DO	0x02	/* DO  : DataOut bit in Interrupt status 1 */
#define BIT_CO	0x08	/* CO  : Command Out bit in interrupt status 2 */
#define BIT_END	0x10	/* END : EOI status in Interrupt status 1 */
#define BIT_ATN 0x40	/* /ATN: Attention bit in Address Status */

/* GPIB multiline message */
#define DCL	0x14	/* Device Clear */
#define SDC	0x04	/* Select Device Clear */
#define GET	0x08	/* Group Execute Trigger */
#define TCT	0x09	/* Take Control */
#define LLO	0x11	/* Local Lockout */
#define GTL	0x01	/* Go to Local */
#define UNL	0x3f	/* Unlisten */
#define UNT	0x5f	/* Untalk */
#define TAG	0x40	/* Talker Address Group */
#define LAG	0x20	/* Listener Address Group */
#define SPE	0x18	/* Serial Pole Enable */
#define SPD	0x19	/* Serial Pole Disable */
#define PPC	0x05	/* Parallel Poll Configure */
#define PPU	0x15	/* Parallel Poll Unconfigure */
#define PPE	0x60	/* Parallel Poll Enable */
#define PPD	0x70	/* Parallel Poll Disable */


/***** Macros ***************************************************************/
#define FLAGS_TALKONLY(f)	((f)&0x01)
#define FLAGS_LISTENONLY(f)	((f)&0x02)
#define FLAGS_IS_CONTROLLER(f)	(!((f)&0x03))
#define aux_command(p,c)	auxiliary_mode((p),0x1f&(c))


/***** Typedefs *************************************************************/
typedef struct
{
    int iobase;			/* uPD7210のIOベースアドレス */
    int unit;			/* ユニットNo */
    int flags;			/* 動作フラグ */
    int myaddress;
    int timeout;
    int default_delimiter;
    int delimiter;
    int statusbyte;
    
    int volatile inuse;		/* 使用中フラグ */
    int volatile atn;		/* ATNユニライン の状態 */
    enum
    {	STATE_NONE,
	STATE_TALK, STATE_EOS1, STATE_EOS2,	/* 送信中 */
	STATE_LISTEN,				/* 受信中 */
	STATE_COMMAND,				/* コマンド送信中 */
	STATE_POLL,				/* ポーリング中 */
    } volatile state;

    char buf[GPIB_BUFFERSIZE];	/* 送受信バッファ */
    char volatile *pbuf;	/* バッファ内送受信ポイント */
    char volatile *pbuf_end;
} GPIB_SOFTC;



/***** Function prototypes **************************************************/
static int wait_if_other_use (GPIB_SOFTC *psc);
static void gpib_initalize (void *unused);
static int gpib_probe (struct isa_device *dev);
static int gpib_attach (struct isa_device *dev);
static void gpib_interrupt (int unit);
static void upd7210_atnon (GPIB_SOFTC *psc);
static void upd7210_atnoff (GPIB_SOFTC *psc);
static int upd7210_ifc (GPIB_SOFTC *psc);
static int upd7210_remote (GPIB_SOFTC *psc);
static int upd7210_local (GPIB_SOFTC *psc);
static int  upd7210_comout( GPIB_SOFTC *psc, char *cmd, int msglen );
static int  upd7210_unt( GPIB_SOFTC *psc );
static int  upd7210_unl( GPIB_SOFTC *psc );
static int  upd7210_sta( GPIB_SOFTC *psc, int talker );
static int  upd7210_sla( GPIB_SOFTC *psc, int listener[] );
static int  upd7210_dcl( GPIB_SOFTC *psc );
static int upd7210_command (GPIB_SOFTC *psc);
static int upd7210_talk (GPIB_SOFTC *psc);
static int upd7210_listen (GPIB_SOFTC *psc);



/***** device driver information ********************************************/
static char  driver_name[] = "gpib";
#define CDEV_MAJOR 210
struct isa_driver  gpibdriver =
{
    gpib_probe, gpib_attach, driver_name
};

static d_open_t  gpib_open;
static d_close_t  gpib_close;
static d_read_t  gpib_read;
static d_write_t  gpib_write;
static d_ioctl_t  gpib_ioctl;
static struct cdevsw  gpib_cdevsw =
{
    gpib_open,		/* open */
    gpib_close,		/* close */
    gpib_read,		/* read */
    gpib_write,		/* write */
    gpib_ioctl,		/* ioctl */
    nostop,		/* stop */
    noreset,		/* reset */
    nodevtotty,		/* devtootty */
    seltrue,		/* poll */
    nommap,		/* mmap */
    nostrat,		/* strategy */
    driver_name,	/* pointer of driver name */
    NULL,		/* (spare) */
    -1,
    nodump,
    nopsize,
};


/***** Local variables ******************************************************/
static GPIB_SOFTC  gpib_softc[NGPIB];


/***** Global variables *****************************************************/



/***** Local functions ******************************************************/
/*==========================================================================
 * 排他制御
 *--------------------------------------------------------------------------
 * arg: psc
 * ret: int	0で正常終了。
 *--------------------------------------------------------------------------
 * (note)
 *  制御権取れたら、psc->inuseをセットして帰る。
*/
static int  wait_if_other_use( GPIB_SOFTC *psc )
{
    int ipl = splhigh();
    while( psc->inuse )
    {
	int ret = tsleep( (void*)&psc->inuse, PWAIT|PCATCH, "pwait", 0 );
	if( ret != 0 )
	{
	    splx( ipl );
	    return ret;
	}
    }
    psc->inuse = 1;
    splx( ipl );

    return 0;
}



/***** Global functions *****************************************************/
/*==========================================================================
 * 初期化：デバイスドライバ登録
 *--------------------------------------------------------------------------
 * arg: 
 * ret: 
 *--------------------------------------------------------------------------
 * 
*/
static void  gpib_initalize( void *unused )
{
    static int gpib_devsw_installed = 0;
    dev_t dev;

    if( gpib_devsw_installed ) return;
    dev = makedev( CDEV_MAJOR, 0 );
    cdevsw_add( &dev, &gpib_cdevsw, NULL );
    gpib_devsw_installed = 1;
}



/*==========================================================================
 * 初期化：デバイスプローブルーチン
 *--------------------------------------------------------------------------
 * arg: dev	configファイルの内容がセットされる。
 * ret: int	I/Oアドレス占有範囲をバイト数で返す。
 *		エラーならば 0を返す。
 *--------------------------------------------------------------------------
 * devが指す装置を検出する。
 * configファイルに指定した回数分call される。
*/
static int  gpib_probe( struct isa_device *dev )
{
    GPIB_SOFTC *psc;
    int i;

    psc = &gpib_softc[dev->id_unit];
    psc->iobase = dev->id_iobase;
    aux_command( psc, CHIP_RESET );
    if( data_in( psc ) != 0 ) return 0;		/* device not installed */

#if defined(GPIB_CONTEC_PC)
    /* (note)
       Contec GP-IB(PC) は、独自に割り込み許可レジスタを持っている。
       だから、割り込み番号からそのアドレスを算出して、取っておく。
    */
    for( i = 2; i <= 7; i++ )
    {
	if( (1 << i) == dev->id_irq ) break;
    }
    if( i <= 7 ) IRQ_MASK_ADRS = 0x2f0 + i;
#endif
    return 16;	/* change this */
}



/*==========================================================================
 * 初期化：デバイスアタッチルーチン
 *--------------------------------------------------------------------------
 * arg: dev	isa device 構造体
 * ret: int	
 *--------------------------------------------------------------------------
 *
*/
static int  gpib_attach( struct isa_device *dev )
{
    GPIB_SOFTC *psc;
#if defined(GPIB_PC9801_29)
    printf( "gpib%d: type PC-9801-29\n", dev->id_unit );
#elif defined(GPIB_CONTEC_PC)
    printf( "gpib%d: type Contec GP-IB(PC)\n", dev->id_unit );
#endif

    psc = &gpib_softc[dev->id_unit];
    psc->iobase = dev->id_iobase;	/* make softc structure */
    psc->unit = dev->id_unit;
    psc->flags = dev->id_flags;
    psc->myaddress = MY_ADDRESS;
    psc->inuse = 0;
    psc->state = STATE_NONE;
    psc->timeout = TIMEOUT;
    psc->default_delimiter = DELIMITER;
    psc->statusbyte = 0;

    aux_command( psc, CHIP_RESET );	/* ソフト・リセット */
    interrupt_mask1( psc, 0x03 );	/* 割り込みマスクセット */
    interrupt_mask2( psc, 0x08 );	/*  enable DI,DO,CO only */
    address( psc, MY_ADDRESS );		/* GPIBアドレスのセット */
    address( psc, 0xe0 );		/* （レジスタ１は禁止）*/
    auxiliary_mode( psc, 0x20|T1679 );	/* 遷移タイムセット */
    auxiliary_mode( psc, 0x80|0x00 );	/* Aux(A) Normal handshake, disable EOS */
    auxiliary_mode( psc, 0xa0|(MODE_T1?0x04:0x00));	/* Aux (B) */
    auxiliary_mode( psc, 0xc0|0x00 );	/* Aux (E) */

    switch( dev->id_flags & 0x03 )	/*  動作モード設定 */
    {
    case 1: address_mode( psc, TRM01|0x80 ); break; /* talkonly */
    case 2: address_mode( psc, TRM01|0x40 ); break; /* listenonly */
    default:address_mode( psc, TRM01|0x01 ); break; /* addressable mode 1 */
    }

    aux_command( psc, IMMEDIATE_EXECUTE_PON );

    if( FLAGS_IS_CONTROLLER(dev->id_flags) )	/* GPIB Bus Initalize */
    {
	upd7210_ifc( psc );
	upd7210_remote( psc );
    }

    dev->id_ointr = gpib_interrupt;

#if defined(GPIB_CONTEC_PC)
    outb(IRQ_MASK_ADRS, 0 );
#endif
    return 1;
}



/*==========================================================================
 * OPEN
 *--------------------------------------------------------------------------
 * arg: dev
 *	flag
 *	mode
 *	p
 * ret: int	
 *--------------------------------------------------------------------------
*/
static int  gpib_open( dev_t dev, int flag, int mode, struct proc *p )
{
    int unit, gpibadrs;

    unit = minor(dev)/32;		/* GPIB maximum address is 30 */
    gpibadrs = minor(dev)%32;

    if( unit >= NGPIB ) return ENXIO;
    if( gpibadrs > GPIB_MAX_ADDRESS ) return ENXIO;

    return 0;
}



/*==========================================================================
 * CLOSE
 *--------------------------------------------------------------------------
 * arg: dev
 *	flag
 *	mode
 *	p
 * ret: int	
 *--------------------------------------------------------------------------
*/
static int  gpib_close( dev_t dev, int flag, int mode, struct proc *p )
{
    int unit, gpibadrs;

    unit = minor(dev)/32;		/* GPIB maximum address is 30 */
    gpibadrs = minor(dev)%32;

    if( unit >= NGPIB ) return ENXIO;
    if( gpibadrs > GPIB_MAX_ADDRESS ) return ENXIO;

    return 0;
}



/*==========================================================================
 * READ
 *--------------------------------------------------------------------------
 * arg: dev
 *	uio
 *	flag
 * ret: int	
 *--------------------------------------------------------------------------
*/
static int  gpib_read( dev_t dev, struct uio *uio, int flag )
{
    GPIB_SOFTC *psc;
    int unit, gpibadrs;
    int ret, len;

    unit = minor(dev)/32;		/* GPIB maximum address is 30 */
    gpibadrs = minor(dev)%32;

    if( unit >= NGPIB ) return ENXIO;
    if( gpibadrs > GPIB_MAX_ADDRESS ) return ENXIO;

    psc = &gpib_softc[unit];
    if( (ret = wait_if_other_use( psc )) != 0 ) return ret;

    ret = EIO;
    /* デリミタキャラクタの設定 */
    switch( psc->default_delimiter & (GPIB_DLM_CR|GPIB_DLM_LF) )
    {
    case GPIB_DLM_CR:
	auxiliary_mode( psc, 0x80 | 0x14 );	/* Aux (A), set END bit with EOS message */
	end_of_string( psc, 0x0d );
	break;

    case GPIB_DLM_LF:
    case GPIB_DLM_CR|GPIB_DLM_LF:
	auxiliary_mode( psc, 0x80 | 0x14 );	/* Aux (A), set END bit with EOS message */
	end_of_string( psc, 0x0a );
	break;

    default:
	auxiliary_mode( psc, 0x80 | 0x10 );	/* Aux (A), no set END bit with EOS message */
    }

    psc->pbuf = psc->pbuf_end = psc->buf;
    *psc->pbuf_end++ = UNT;
    *psc->pbuf_end++ = UNL;
    *psc->pbuf_end++ = gpibadrs | TAG;
    *psc->pbuf_end   = psc->myaddress | LAG;
    if( upd7210_command( psc ) != GPIB_NOERR ) goto RETURN;

    psc->pbuf = psc->buf;
    psc->pbuf_end = psc->pbuf + sizeof(psc->buf) -1;
    if( upd7210_listen( psc ) != GPIB_NOERR ) goto RETURN;
    len = psc->pbuf - psc->buf;
    uiomove( psc->buf, uio->uio_resid < len? uio->uio_resid: len, uio );

    psc->pbuf = psc->pbuf_end = psc->buf;
    *psc->pbuf_end++ = UNT;
    *psc->pbuf_end   = UNL;
    if( upd7210_command( psc ) != GPIB_NOERR ) goto RETURN;
    ret = 0;

RETURN:
    upd7210_atnon( psc );
    psc->inuse = 0;
    wakeup( (void*)&psc->inuse );
    return ret;
}



/*==========================================================================
 * WRITE
 *--------------------------------------------------------------------------
 * arg: dev
 *	uio
 *	flag
 * ret: int	
 *--------------------------------------------------------------------------
*/
static int  gpib_write( dev_t dev, struct uio *uio, int flag )
{
    GPIB_SOFTC *psc;
    int unit, gpibadrs;
    int ret;

    unit = minor(dev)/32;		/* GPIB maximum address is 30 */
    gpibadrs = minor(dev)%32;

    if( unit >= NGPIB ) return ENXIO;
    if( gpibadrs > GPIB_MAX_ADDRESS ) return ENXIO;

    psc = &gpib_softc[unit];
    if( uio->uio_resid > GPIB_BUFFERSIZE ) return ENOMEM;
    if( (ret = wait_if_other_use( psc )) != 0 ) return ret;

    ret = EIO;
    psc->delimiter = psc->default_delimiter;
    psc->pbuf = psc->pbuf_end = psc->buf;
    *psc->pbuf_end++ = UNT;
    *psc->pbuf_end++ = UNL;
    *psc->pbuf_end++ = psc->myaddress | TAG;
    *psc->pbuf_end   = gpibadrs | LAG;
    if( upd7210_command( psc ) != GPIB_NOERR ) goto RETURN;

    psc->pbuf = psc->buf;
    psc->pbuf_end = psc->pbuf + uio->uio_resid - 1;
    uiomove( psc->buf, uio->uio_resid, uio );
    if( upd7210_talk( psc ) != GPIB_NOERR ) goto RETURN;

    psc->pbuf = psc->pbuf_end = psc->buf;
    *psc->pbuf_end++ = UNT;
    *psc->pbuf_end   = UNL;
    if( upd7210_command( psc ) != GPIB_NOERR ) goto RETURN;
    ret = 0;

RETURN:
    upd7210_atnon( psc );
    psc->inuse = 0;
    wakeup( (void*)&psc->inuse );
    return ret;
}



/*==========================================================================
 * IOCTL
 *--------------------------------------------------------------------------
 * arg: dev
 *	cmd
 *	data
 *	flag
 *	p
 * ret: int	
 *--------------------------------------------------------------------------
*/
static int  gpib_ioctl( dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p )
{
    GPIB_PACKET *ppkt;
    GPIB_SOFTC *psc;
    int unit, gpibadrs;
    int ret = 0;

    unit = minor(dev)/32;		/* GPIB maximum address is 30 */
    gpibadrs = minor(dev)%32;

    if( unit >= NGPIB ) return ENXIO;
    if( gpibadrs > GPIB_MAX_ADDRESS ) return ENXIO;

    psc = &gpib_softc[unit];
    ppkt = (GPIB_PACKET*)data;
    if( (ret = wait_if_other_use( psc )) != 0 ) return ret;

    switch( cmd )
    {
    case GPIB_DELIM:
	psc->delimiter = (GPIB_DLM_CR|GPIB_DLM_LF|GPIB_DLM_EOI) & *((int *)data);
	break;
	
    case GPIB_TIMEOUT:
	psc->timeout = *((int *)data);
	break;

    case GPIB_ATNON:
	if( !FLAGS_IS_CONTROLLER(psc->flags) )
	    ppkt->errno = GPIB_ECTRL;
	else
	{
	    upd7210_atnon( psc );
	    ppkt->errno = GPIB_NOERR;
	}
	break;
	
    case GPIB_ATNOFF:
	if( !FLAGS_IS_CONTROLLER(psc->flags) )
	    ppkt->errno = GPIB_ECTRL;
	else
	{
	    upd7210_atnoff( psc );
	    ppkt->errno = GPIB_NOERR;
	}
	break;

    case GPIB_IFC:	ppkt->errno = upd7210_ifc( psc ); break;
    case GPIB_REMOTE:	ppkt->errno = upd7210_remote( psc ); break;
    case GPIB_LOCAL:	ppkt->errno = upd7210_local( psc ); break;
    case GPIB_COMMAND:	ppkt->errno = upd7210_comout( psc, ppkt->pbuf, ppkt->msglen ); break;
    case GPIB_UNT:	ppkt->errno = upd7210_unt( psc ); break;
    case GPIB_UNL:	ppkt->errno = upd7210_unl( psc ); break;
    case GPIB_STA:	ppkt->errno = upd7210_sta( psc, ppkt->talker ); break;
    case GPIB_SLA:	ppkt->errno = upd7210_sla( psc, ppkt->listener ); break;
    case GPIB_DCL:	ppkt->errno = upd7210_dcl( psc ); break;

    case GPIB_GTL:
    case GPIB_LLO:
    case GPIB_SDC:
    case GPIB_GET:
    case GPIB_TALK:
    case GPIB_LISTEN:
    case GPIB_SP:
    case GPIB_REQ:
    }

    psc->inuse = 0;
    wakeup( (void*)&psc->inuse );
    return ret;
}



/*==========================================================================
 * 割り込みルーチン
 *--------------------------------------------------------------------------
 * arg: unit
 * ret: 
 *--------------------------------------------------------------------------
 * 
*/
static void  gpib_interrupt( int unit )
{
    int data, is1, is2;
    GPIB_SOFTC *psc;

    psc = &gpib_softc[unit];

    is1 = interrupt_status1( psc );
    is2 = interrupt_status2( psc );

    if( is1 & BIT_DI ) data = data_in( psc );

    switch( psc->state )
    {
    case STATE_NONE:
	break;

    case STATE_TALK:
	if( !( is1 & BIT_DO ) ) break;			/* error? */
	if( psc->pbuf < psc->pbuf_end )			/* データ送出 */
	{
	    byte_out( psc, *psc->pbuf++ );
	    break;
	}

	if( psc->pbuf == psc->pbuf_end )		/* 最終データ? */
	{
	    if( psc->delimiter == GPIB_DLM_EOI ) aux_command( psc, SEND_EOI );
	    byte_out( psc, *psc->pbuf++ );
	    break;
	}

	/* 全データを送出し終って、デリミタを付加するか判断する。 */
	psc->state = STATE_EOS1;
	switch( psc->delimiter & (GPIB_DLM_CR|GPIB_DLM_LF) )
	{
	case GPIB_DLM_CR:		/* CR only */
	    if( psc->delimiter & GPIB_DLM_EOI ) aux_command( psc, SEND_EOI );
	    byte_out( psc, 0x0d );
	    break;

	case GPIB_DLM_LF:		/* LF only */
	    if( psc->delimiter & GPIB_DLM_EOI ) aux_command( psc, SEND_EOI );
	    byte_out( psc, 0x0a );
	    break;

	case GPIB_DLM_CR|GPIB_DLM_LF:	/* CR+LF (see below) */
	    byte_out( psc, 0x0d );
	    break;

	default:
	    psc->state = STATE_NONE;
	    break;
	}
	break;

    case STATE_EOS1:
	if( !( is1 & BIT_DO ) ) break;			/* error ? */
	if( (psc->delimiter & (GPIB_DLM_CR|GPIB_DLM_LF))
	    == (GPIB_DLM_CR|GPIB_DLM_LF) )		/* CR+LF */
	{
	    psc->state = STATE_EOS2;
	    if( psc->delimiter & GPIB_DLM_EOI ) aux_command( psc, SEND_EOI );
	    byte_out( psc, 0x0a );
	    break;
	}
	psc->state = STATE_NONE;
	break;

    case STATE_EOS2:
	if( !( is1 & BIT_DO ) ) break;			/* error ? */
	psc->state = STATE_NONE;
	break;
	
    case STATE_LISTEN:
	if( !( is1 & BIT_DI ) ) break;			/* error ? */
	if( psc->pbuf > psc->pbuf_end )			/* buffer full !! */
	{
	    psc->state = STATE_NONE;
	    break;
	}
	*psc->pbuf++ = data;
	if( is1 & BIT_END ) psc->state = STATE_NONE;
	break;

    case STATE_COMMAND:
	if( !( is2 & BIT_CO ) ) break;			/* error ? */
	if( psc->pbuf <= psc->pbuf_end )
	    byte_out( psc, *psc->pbuf++ );
	else
	    psc->state = STATE_NONE;
	break;

    case STATE_POLL:
	if( !( is1 & BIT_DI ) ) break;			/* error ? */
	*psc->pbuf = data;
	psc->state = STATE_NONE;
	break;
    }

#if defined(GPIB_CONTEC_PC)
    outb( IRQ_MASK_ADRS, 0 );
#endif

    if( psc->state == STATE_NONE ) wakeup( psc );
}



/***** uPD7210 - GPIB functions *********************************************/

/*==========================================================================
 * Activate attention uniline message
 *--------------------------------------------------------------------------
 * arg: psc
 * ret: none
 *--------------------------------------------------------------------------
 * (Bus Activity)
 *   ATN
*/
static void  upd7210_atnon( GPIB_SOFTC *psc )
{
    int i;

    if( psc->atn ) return;

    aux_command( psc, TAKE_CONTROL_ASYNCHRONOUSLY );
    psc->atn = 1;
    for( i = 0; i < 1000; i++ )
    {
	if( !(address_status( psc ) & BIT_ATN) ) return;
	DELAY( 100 );
	aux_command( psc, TAKE_CONTROL_ASYNCHRONOUSLY );
    }
    printf( "uPD7210-GPIB: Can't activate ATN line.\n" );
}



/*==========================================================================
 * Inactivate attention uniline message
 *--------------------------------------------------------------------------
 * arg: psc
 * ret: none
 *--------------------------------------------------------------------------
 * (Bus Activity)
 *   /ATN
*/
static void  upd7210_atnoff( GPIB_SOFTC *psc )
{
    int i;

    if( ! psc->atn ) return;

    aux_command( psc, GO_TO_STANDBY );
    psc->atn = 0;
    for( i = 0; i < 1000; i++ )
    {
	if( address_status( psc ) & BIT_ATN ) return;
	DELAY( 100 );
	aux_command( psc, GO_TO_STANDBY );
    }
    printf( "uPD7210-GPIB: Can't inactivate ATN line.\n" );
}



/*==========================================================================
 * Interface clear uniline message
 *--------------------------------------------------------------------------
 * arg: psc
 * ret: int	gpib error code
 *--------------------------------------------------------------------------
 * (Bus Activity)
 *  ATN, IFC
*/
static int  upd7210_ifc( GPIB_SOFTC *psc )
{
    if( !FLAGS_IS_CONTROLLER(psc->flags) ) return GPIB_ECTRL;

    aux_command( psc, TAKE_CONTROL_ASYNCHRONOUSLY );
    psc->atn = 1;

    aux_command( psc, SET_IFC );
    DELAY( 100 );
    aux_command( psc, CLEAR_IFC );

    return GPIB_NOERR;
}



/*==========================================================================
 * Activate remote uniline message
 *--------------------------------------------------------------------------
 * arg: psc
 * ret: int	gpib error code
 *--------------------------------------------------------------------------
 * (Bus Activity)
 *  REN
*/
static int  upd7210_remote( GPIB_SOFTC *psc )
{
    if( !FLAGS_IS_CONTROLLER(psc->flags) ) return GPIB_ECTRL;
    aux_command( psc, SET_REN );
    return GPIB_NOERR;
}



/*==========================================================================
 * Inactivate remote uniline message
 *--------------------------------------------------------------------------
 * arg: psc
 * ret: int	gpib error code
 *--------------------------------------------------------------------------
 * (Bus Activity)
 *  /REN
*/
static int  upd7210_local( GPIB_SOFTC *psc )
{
    if( !FLAGS_IS_CONTROLLER(psc->flags) ) return GPIB_ECTRL;
    aux_command( psc, CLEAR_REN );
    return GPIB_NOERR;
}



/*==========================================================================
 * multiline message
 *--------------------------------------------------------------------------
 * arg: psc
 * ret: int	gpib error code
 *--------------------------------------------------------------------------
 * (Bus sequence)
 *  ATN - (command...)
*/
static int  upd7210_comout( GPIB_SOFTC *psc, char *cmd, int msglen )
{
    if( msglen <= 0 ) return GPIB_ERANGE;
    if( msglen > GPIB_BUFFERSIZE ) return GPIB_EBFULL;
    memcpy( psc->buf, cmd, msglen );
    psc->pbuf_end = (psc->pbuf = psc->buf) + msglen - 1;
    return upd7210_command( psc );
}



/*==========================================================================
 * Untalk multiline message
 *--------------------------------------------------------------------------
 * arg: psc
 * ret: int	gpib error code
 *--------------------------------------------------------------------------
 * (Bus sequence)
 *  ATN - UNT
*/
static int  upd7210_unt( GPIB_SOFTC *psc )
{
    psc->pbuf = psc->pbuf_end = psc->buf;
    *psc->pbuf_end = UNT;
    return upd7210_command( psc );
}



/*==========================================================================
 * Unlisten multiline message
 *--------------------------------------------------------------------------
 * arg: psc
 * ret: int	gpib error code
 *--------------------------------------------------------------------------
 * (Bus sequence)
 *  ATN - UNL
*/
static int  upd7210_unl( GPIB_SOFTC *psc )
{
    psc->pbuf = psc->pbuf_end = psc->buf;
    *psc->pbuf_end = UNL;
    return upd7210_command( psc );
}



/*==========================================================================
 * Set talker address multiline message
 *--------------------------------------------------------------------------
 * arg: psc
 * ret: int	gpib error code
 *--------------------------------------------------------------------------
 * (Bus sequence)
 *  ATN - TA
*/
static int  upd7210_sta( GPIB_SOFTC *psc, int talker )
{
    if( (talker < 0) || (talker > GPIB_MAX_ADDRESS) ) return GPIB_EADDR;
    psc->pbuf = psc->pbuf_end = psc->buf;
    *psc->pbuf_end = (talker | TAG);
    return upd7210_command( psc );
}



/*==========================================================================
 * Set listener address multiline message
 *--------------------------------------------------------------------------
 * arg: psc
 * ret: int	gpib error code
 *--------------------------------------------------------------------------
 * (Bus sequence)
 *  ATN - LA ...
*/
static int  upd7210_sla( GPIB_SOFTC *psc, int listener[] )
{
    int i;

    psc->pbuf = psc->pbuf_end = psc->buf;
    for( i = 0; i < GPIB_MAX_DEVICE; i++ )
    {
	if( listener[i] < 0 ) break;
	if( listener[i] > GPIB_MAX_ADDRESS ) return GPIB_EADDR;
	*psc->pbuf_end++ = (listener[i] | LAG);
    }
    if( i == 0 ) return GPIB_EADDR;

    psc->pbuf_end--;
    return upd7210_command( psc );
}



/*==========================================================================
 * Device clear multiline message
 *--------------------------------------------------------------------------
 * arg: psc
 * ret: int	gpib error code
 *--------------------------------------------------------------------------
 * (Bus sequence)
 *  ATN - DCL
*/
static int  upd7210_dcl( GPIB_SOFTC *psc )
{
    psc->pbuf = psc->pbuf_end = psc->buf;
    *psc->pbuf_end = DCL;
    return upd7210_command( psc );
}







/*==========================================================================
 * マルチラインコマンドの発行
 *--------------------------------------------------------------------------
 * arg: psc
 * ret: int	gpib error code
 *--------------------------------------------------------------------------
 * softcに設定されたマルチラインコマンドを発行する
*/
static int  upd7210_command( GPIB_SOFTC *psc )
{
    int ipl;
    int ret = GPIB_NOERR;
    
    ipl = splhigh();
    psc->state = STATE_COMMAND;
    upd7210_atnon( psc );
    byte_out( psc, *psc->pbuf++ );
    if( tsleep( psc, PRIBIO, "uPDcmd", psc->timeout*hz ) != 0 ) ret = GPIB_ETIME;
    splx( ipl );

    return ret;
}



/*==========================================================================
 * データ送信
 *--------------------------------------------------------------------------
 * arg: psc
 * ret: int	gpib error code
 *--------------------------------------------------------------------------
 * softcに設定された情報と割り込みルーチンを使って、送信を開始する
 * 現在は、マスターモードのみ実装
*/
static int  upd7210_talk( GPIB_SOFTC *psc )
{
    int ipl;
    int ret = GPIB_NOERR;

    ipl = splhigh();
    psc->state = STATE_TALK;
    upd7210_atnoff( psc );
    if( tsleep( psc, PRIBIO, "uPDtlk", psc->timeout*hz ) != 0 ) ret = GPIB_ETIME;
    splx( ipl );

    return ret;
}



/*==========================================================================
 * データ受信
 *--------------------------------------------------------------------------
 * arg: psc
 * ret: int	gpib error code
 *--------------------------------------------------------------------------
 * softcに設定された情報と割り込みルーチンを使って、受信を開始する。
 * 現在は、マスターモードのみ実装
*/
static int  upd7210_listen( GPIB_SOFTC *psc )
{
    int ipl;
    int ret = GPIB_NOERR;

    ipl = splhigh();
    psc->state = STATE_LISTEN;
    upd7210_atnoff( psc );
    if( tsleep( psc, PRIBIO, "uPDlsn", psc->timeout*hz ) != 0 ) ret = GPIB_ETIME;
    splx( ipl );

    return ret;
}








SYSINIT(gpibdev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE+CDEV_MAJOR, gpib_initalize, NULL);

#endif		/* NGPIB > 0 */



/*==========================================================================
 * 
 *--------------------------------------------------------------------------
 * arg: 
 * ret: 
 *--------------------------------------------------------------------------
 * 
*/
