

/******************************************************************************
 * alt_gpio_drv11j.c
 *  Parallel Port HAL structure
 *  HAL (Custom Device)  : alt_gpio_drv11j.c
 *                       : alt_gpio_drv11j.h
 *
 *  Device driver :      gpio_drv11j.c
 *                       gpio_drv11j_regs.h
 *  /////////////////////////////////////////////// System.h
 *    Parallel port       0 DATA
 *                        4 DIRECTION
 *                        8 INTTERUPTMASK
 *                        12 EDGECAPTRUE
 *
 *  Created on: Aug 20, 2024
 *      Author: Peitao Shi
 *
 *      TODO: Update signal with basic_io.h API, Setting SigIn and SigOut with GPIO is not correct
 *       Replace all driver printf with alt_log_printf_proc
 ******************************************************************************/
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <limits.h>

#include <priv/alt_file.h>
#include <priv/alt_legacy_irq.h>
#include <priv/alt_busy_sleep.h>
#include "system.h"
#include "DE2_DRV11J_SIGIN.h"
#include "DE2_DRV11J_regs.h"
#include "altera_avalon_pio_regs.h"
#include "sys/alt_irq.h"



#define SEND_SIGNAL()      *drjOsig ^= DRJ_SIGOUT; bitTest ^= DRJ_SIGIN;         \

#define WAIT_FOR_SIGNAL(loc) \
    while ((DRJ_SIGIN & *drjIsig) == bitTest && --k); 	                          \
    if (k == 0) 										                          \
     {            															      \
        fprintf(stderr,"%s: Time out error:%d\r\n", fxnName, loc);                    \
       return (-1);                                                               \
     }


ALT_LLIST_HEAD(gpio_drv11j_dev_list);



static alt_pio_dev *g_pio_dev = NULL;

/* local function declaration */
static int gpio_drv11j_EnqueueRx(unsigned char *buffer, int size);
static int gpio_drv11j_DequeueRx(unsigned char *buffer, int size);
static int gpio_drv11j_EnqueueTx(unsigned char *buffer, int size);
static int gpio_drv11j_DequeueTx(unsigned char *buffer, int size);
static int gpio_drv11j_buffer_Reset( GPIO_OBJECT *gpio_buf);
static void gpio_drv11j_isr(void *arg, alt_u32 id_num);
static int gpio_drv11j_Read(DRV11J_IO_DEVICE* drv11j, unsigned char* buffer, int len);
static int gpio_drv11j_Write (DRV11J_IO_DEVICE* drv11j, const unsigned char* buffer, int len);
static int gpio_drv11j_Signal (DRV11J_IO_DEVICE *drv11j, int n);
static int gpio_drv11j_Attach (DRV11J_IO_DEVICE *drv11j);
static int gpio_drv11j_Detach (DRV11J_IO_DEVICE *drv11j);
static int gpio_drv11j_Close (DRV11J_IO_DEVICE *drv11j);

/** GPIO Driver Queue
  @Summary
    Defines the Transmit and Receive Buffers
*/
static GPIO_Q gpio_txByteQ[GPIO_CONFIG_TX_Q_LENGTH] ;
static GPIO_Q gpio_rxByteQ[GPIO_CONFIG_RX_Q_LENGTH] ;
static GPIO_OBJECT gpio_obj;


/******************************************************************************
 *  FUNCTION : gpio_drv11j_Init
 *
 *  DESCRIPTION: Initialize GPIO DRV11J device
 *
 *  PARAMETERS: DRV11J_IO_DEVICE  DRV11J IO device object
 *
 *  RETURN:
 *      0: SUCCESS
 *      other: error code:
 *
 ******************************************************************************/
int gpio_drv11j_Init(alt_pio_dev* pio_dev)
{
    int err = -EINVAL;

    printf("gpio_drv11j_init\r\n");

    g_pio_dev = pio_dev;
    if (!g_pio_dev)
    {
    	printf(" Error: Failed to initialize Drv11J device\r\n");
    	return GPIO_INVALID_HANDLE;
    }

    DRV11J_IO_DEVICE *drv11j = &(g_pio_dev->drv11j);

    /* Initialize tx/rx queue*/
    /*Initialize tx/rx queue */
    gpio_drv11j_buffer_Reset(&gpio_obj);

    /* set timer */
    drv11j->timeout = DEFAULT_TIMEOUT;
    drv11j->blocktime = DRJ_DEFAULT_BLOCKTIME;

    /* configure the port */
    drv11j->sigin   = IOADDR_ALTERA_AVALON_PIO_DATA(GPIO_SIGIN_BASE)  ;
    drv11j->sigout  = IOADDR_ALTERA_AVALON_PIO_DATA(GPIO_SIGOUT_BASE);
    drv11j->datin   = IOADDR_ALTERA_AVALON_PIO_DATA(GPIO_DATIN_BASE);
    drv11j->datout  = IOADDR_ALTERA_AVALON_PIO_DATA(GPIO_DATOUT_BASE);
    /* Set SinalIn, SignalOut, DataIn, DataOut directions*/
    IOWR_ALTERA_AVALON_PIO_DIRECTION(GPIO_SIGIN_BASE,0x00) ;/*A0-A8 is input*/
    IOWR_ALTERA_AVALON_PIO_DIRECTION(GPIO_SIGOUT_BASE, 0xFF) ;/*A0-A8 is output*/
    IOWR_ALTERA_AVALON_PIO_DIRECTION(GPIO_DATIN_BASE, 0x00) ;/*D0-D15 is input*/
    IOWR_ALTERA_AVALON_PIO_DIRECTION(GPIO_DATOUT_BASE, 0xFFFF) ;/*D0-D15 is output*/

    /* Enable a single interrupt iinput on bit location BIT_INTERRUPT0*/
    IOWR_ALTERA_AVALON_PIO_IRQ_MASK(GPIO_SIGIN_BASE, DRJ_INTERRUPT0);

    /* Reset the edge capture registers*/
    IOWR_ALTERA_AVALON_PIO_EDGE_CAP(GPIO_SIGIN_BASE, 0);
  //  IOWR_ALTERA_AVALON_PIO_EDGE_CAP(GPIO_DATIN_BASE, 0xFFFF);
   //IOWR_ALTERA_AVALON_PIO_EDGE_CAP(GPIO_DATOUT_BASE, 0xFFFF);


    /*Reset SignalOut and DataOut */
    IOWR_ALTERA_AVALON_PIO_DATA(GPIO_SIGOUT_BASE, 0);
    IOWR_ALTERA_AVALON_PIO_DATA(GPIO_DATOUT_BASE, 0);

    /* register the ISR with the ALTERA HAL interface */
    err = alt_irq_register (GPIO_SIGIN_IRQ, (void *)drv11j, (alt_isr_func) gpio_drv11j_isr);

    if (err)
    {
    	printf(" Error: Failed to initialize GPIOs of DRV11J interface!\r\n");

    }

    return (err);
}


/******************************************************************************
 *  FUNCTION: gpio_drv11j_Read
 *  DESCRIPTION: Read len byte data from parallel port
 *  PARAMETER:
 *        dev :  Parallel port device handle
 *        buffer: buffer holding reading data from parallel port
 *  RETURN :
 *          Actual data size in byte read in the buffer.
 *
 * The DRV11-J registers are used as follows:
 *
 *   bufa    is the input signal buffer
 *   bufb    is the output signal buffer
 *   bufc    is the input data buffer
 *   bufd    is the output data buffer
 ****************************************************************************/
int gpio_drv11j_Read(DRV11J_IO_DEVICE *drv11j, unsigned char* buffer, int len)
{
    char *fxnName = "gpio_drv11j_Read()";

    if ( !drv11j)
    {
    	printf("%s: Error: Invalid device handle, open the device first!\r\n", fxnName);
        return (-1);
    }

    volatile unsigned short dataIn;
    volatile unsigned char *drjOsig;   /* output signal register    */
    volatile unsigned char *drjIsig;   /* input signal register    */
    volatile unsigned short *drjBuf;    /* parallel port input buf    */
    volatile unsigned short *dataPtr;   /* pointer to 16 bit data buffer    */
    volatile unsigned long bitTest;    /* used to test for 'ready'    */
    volatile int count;            /* word count register    */

    short myChecksum;        /* computes checksum        */
    short theirChecksum;
    unsigned long k;        /* timeout counter        */
    //int byteCount;            /* number of bytes read in      */

    drjBuf = drv11j->datin;
    drjOsig = drv11j->sigout;
    drjIsig = drv11j->sigin;

    bitTest = (*drjIsig & DRJ_SIGIN);    /* get initial handshake bit value */

    *drjOsig |= DRJ_READ_READY;

    k = drv11j->blocktime * CYCLES_PER_MS;
    WAIT_FOR_SIGNAL(1);

    //read word counts first
    dataIn = IORD_ALTERA_AVALON_PIO_DATA(GPIO_DATIN_BASE);

    *drjOsig &= ~(DRJ_READ_READY);
    count = (int)(dataIn & 0xFFFF);        /* get word/bytes counts    */
    drv11j->byteCount = count*sizeof(short);

    /* ----- check for bogus count: negative, or too big ----- */
    if (count < 0 || len < drv11j->byteCount)
    {
    	printf("%s: count %d or size %d error \r\n", fxnName,
                count, len);
        *drjOsig &= ~DRJ_SIGOUT;
        return (-1);
    }
    
    /* ----- now read in all the words ----- */
    k = (count+2)*drv11j->timeout;

    dataPtr = (unsigned short *)buffer;
    myChecksum = 0;
    while (count--)
    {
        SEND_SIGNAL();
        WAIT_FOR_SIGNAL(2);
        dataIn = IORD_ALTERA_AVALON_PIO_DATA(GPIO_DATIN_BASE);
        *dataPtr = ( unsigned short ) ( dataIn  & 0xFFFF);
        myChecksum += *dataPtr++;
     }

    /* ----- now get their checksum ----- */
    SEND_SIGNAL();
    WAIT_FOR_SIGNAL(3);

    theirChecksum = *drjBuf;
    
    SEND_SIGNAL();
    WAIT_FOR_SIGNAL(4);

    /* input buffer is now clear */
    if (myChecksum != theirChecksum)
     {
    	printf("%s: Transmit error: their checsum: 0x%04X; my checksum: 0x%04X\r\n",
                fxnName, theirChecksum, myChecksum);
       return (-1);
     }
    return (drv11j->byteCount);
}

/******************************************************************************
 *  FUNCTION:  gpio_drv11j_Write
 *  DESCRIPTION: Write len size data to parallel port
 *  PARAMETER:
 *         dev : Parallel port device handle
 *         buffer: data to be written to the parallel port
 *         len : data size in the buffer.
 *  RETURN :
 *         Written data size in bytes to parallel port.
 ******************************************************************************/
int gpio_drv11j_Write(DRV11J_IO_DEVICE *drv11j, const unsigned char* buffer, int len)
{
    char *fxnName = "gpio_drv11j_Write";

    if ( !drv11j)
    {
    	printf("%s: Error: Invalid device handle, open the device first!", fxnName);
        return (-1);
    }

    volatile unsigned long *drjOsig;   /* output signal register    */
    volatile unsigned long *drjIsig;   /* input signal register    */
    volatile unsigned long *drjBuf;    /* parallel port output buf    */
    volatile unsigned short *dataPtr;   /* pointer to data buffer    */
    volatile unsigned long bitTest;    /* used to test for 'ready'    */
    volatile int count;            /* word count register    */

    short checksum;                /* computes checksum    */
    unsigned long k;            /* timeout counter        */
    //int byteCount;                /* number of bytes read in  */

    drjBuf = (unsigned long *) drv11j->datout;
    drjIsig = (unsigned long *) drv11j->sigin;
    drjOsig = (unsigned long *) drv11j->sigout;

    count = len/sizeof(short);
    if (len % sizeof(short))
    { count++;
    }
    if (count < 0)
    {
    	printf("%s: count error: %d\r\n", fxnName,count);
       *(drjOsig) = 0;
       return (-1);
     }

    *drjOsig |= DRJ_WRITE_READY;
    k = drv11j->blocktime * CYCLES_PER_MS;
    while (!(*drjIsig & DRJ_READ_READY) && --k);

    if (k == 0)
    {
    	printf( "%s: Timeout Error: \r\n", fxnName);
       (*drjOsig) = 0;
       return (-1);
    }

    //*drjBuf = count;
    IOWR_ALTERA_AVALON_PIO_DATA(GPIO_DATOUT_BASE, count);
    bitTest = (*drjIsig & DRJ_SIGIN) ^ DRJ_SIGIN;    /* get signal bit */

    k = (count+2) * drv11j->timeout;
    SEND_SIGNAL();
    WAIT_FOR_SIGNAL(1);

    dataPtr = (unsigned short *)buffer;
    
    /* ----- send words down the chute ----- */
    checksum = 0;
    while (count--)
    {
    	checksum += *dataPtr;
       // *drjBuf = *dataPtr++;        /* load the drj */
    	IOWR_ALTERA_AVALON_PIO_DATA(GPIO_DATOUT_BASE, *dataPtr);
        dataPtr++;
        SEND_SIGNAL();
        WAIT_FOR_SIGNAL(2);
    }

    //*drjBuf = checksum;
    IOWR_ALTERA_AVALON_PIO_DATA(GPIO_DATOUT_BASE, checksum);
    SEND_SIGNAL();
    WAIT_FOR_SIGNAL(3);

    *drjBuf = 0;
    SEND_SIGNAL();

    /* hack for ocsilliscope: we want to make sure that the we leave
       the routine with the signal bit clear */
    k = 100;
    while (--k);
    //usleep(100);
    (*drjOsig) &= ~DRJ_SIGOUT;

    return (len);
}

/******************************************************************************
 *   FUNCTION:  gpio_drv11j_attach
 *   DESCRIPTION: Attach GPIO DRV11J device
 *   PARAMETERS:
 *   	dev: Parallel port device handle
 *   RETURN:
 *      GPIO_ERROR_OK : Connect GPIO_DRV11J success
 *      GPIO_ATTACH_ERROR: Failed to connect  DRV11J device
 ******************************************************************************/
int gpio_drv11j_Attach (DRV11J_IO_DEVICE *drv11j)
{
    char *fxnName = "gpio_drv11j_Attach()";
    unsigned long k;

    if ( !drv11j)
    {
    	printf( "%s: Error: DRV11J_IO_DEVICE is NULL, init the device first!\r\n", fxnName);
        return (GPIO_INVALID_HANDLE);
    }

    *(drv11j->sigout) |= DRJ_LISTEN; /* Inform Unimation's DRV11-J to listen*/
    *(drv11j->sigout) &= ~DRJ_INTERRUPT1;/* Toggle DRJ_INTERRUPT1 to trigger
                                                DRV11-J's interrupt*/
    *(drv11j->sigout) |= DRJ_INTERRUPT1;// Send interrupt 1: up edge trigger

    drv11j->timeout = DEFAULT_TIMEOUT;
    drv11j->blocktime = DRJ_DEFAULT_BLOCKTIME;

    k = drv11j->timeout*100;
    //Check sigin DRJ_LISTEN bit. If buf a DRJ_LISTEN bit goes low, enter listen mode: unimation start signal out mode
    while ((*(drv11j->sigin) & DRJ_LISTEN) && --k) ;
    if (k == 0)
    {
    	 printf( "%s: Timeout error 1", fxnName);
         return (GPIO_ATTACH_ERROR);
    }
    //Cear Buf b all bit including interrupt bit, stop Unimation interrupt
    *(drv11j->sigout) = 0;

    /* Received signal ( Low) from unimation DRV11-J? */
    k = drv11j->timeout*100;
    while ((*(drv11j->sigin) & DRJ_SIGIN) && --k);
    if (k == 0)
    {
    	printf( "%s: Timeout error 2", fxnName);
        //errorMsg (ETimeout, driTimeoutMsg, fxnName, 2);
       return (GPIO_ATTACH_ERROR);
    }

    return (GPIO_ERROR_OK);
}




/******************************************************************************
 *  FUNCTION: gpio_drv11j_Signal
 *  DESCRIPTION: Signal GPIO_DRV11J device
 *  PARAMETERS: dev - Device handle
 *            n : Write Signal 1, or 2 to the device.
 *  RETURNS:
 *           GPIO_INVALID_VALUE;
 *           GPIO_ERROR_OK : OK otherwise
 *           GPIO_INVALID_VALUE,
 *           GPIO_TIMOUT
 *
 *****************************************************************************/
int gpio_drv11j_Signal (DRV11J_IO_DEVICE *drv11j, int n)
{
    unsigned long  k;
    int retval = GPIO_ERROR_OK;
    char *fxnName = "gpio_drv11j_Signal()";
    

    if ( !drv11j)
    {
    	printf( "%s: Error: Invalid device handle, open the device first!\r\n", fxnName);
        return (GPIO_INVALID_HANDLE);
    }

    if (n < 1 || n > 2)
    {
    	printf( "%s: Error: signal must be 1 or 2: %d\r\n",fxnName, n);
        return (GPIO_INVALID_VALUE);
    }
    if (*(drv11j->sigout) & DRJ_INTERRUPT0 || (*(drv11j->sigin) & DRJ_SIGNAL_ACK))
    {
    	printf( "%s: Signal %d sync error\r\n",  fxnName, n);
        return (GPIO_SYNC_ERROR);
    }
    *(drv11j->datout) = n;
    *(drv11j->sigout) |= DRJ_INTERRUPT0;

    /* wait for  signal ACK from DRV11-J*/
    drv11j->timeout = DEFAULT_TIMEOUT;
    k = drv11j->timeout * 100;
    while (!(*(drv11j->sigin) & DRJ_SIGNAL_ACK) && --k);

    if (k == 0)
    {
    	printf( "%s: time out error : no signal ACK from DRV11J\r\n", fxnName);
        //errorMsg (ETimeout, driTimeoutMsg, fxnName, 2);
        retval = GPIO_TIMOUT;
    }

    *(drv11j->sigout) = 0;    /* clear interrupt to DRV11-j */

    /* wait for signal complete*/
    k = drv11j->timeout * 100;
    while ((*(drv11j->sigin) & DRJ_SIGNAL_ACK) && --k);
    if (k == 0)
    {
    	printf( "%s time out error: Signal not complete\r\n", fxnName);
        //errorMsg (ETimeout, driTimeoutMsg, fxnName, 3);
        return (GPIO_TIMOUT);
    }

    return (retval);
}

/******************************************************************************
 *  FUNCTION: gpio_drv11j_Detach
 *  DESCRIPTION: Detach Drv11J device
 *  PARAMETERS: dev - Device handle
 *            n : Write Signal 1, or 2 to the device.
 *  RETURNS:
 *           GPIO_INVALID_VALUE;
 *           GPIO_ERROR_OK : OK otherwise
 *           GPIO_INVALID_VALUE,
 *           GPIO_TIMOUT
 *
 *****************************************************************************/
int gpio_drv11j_Detach (DRV11J_IO_DEVICE *drv11j)
{
	char *fxnName = "gpio_drv11j_Detach()";

	if ( !drv11j)
	{
		printf( "%s: Error: Invalid device handle, open the device first!\r\n", fxnName);
	    return (GPIO_INVALID_HANDLE);
	}

	/* disable interrupt*/
    IOWR_ALTERA_AVALON_PIO_IRQ_MASK(GPIO_SIGIN_BASE, 0x0);

	/* reset outputs*/
	 /*Reset SignalOut and DataOut */
	 IOWR_ALTERA_AVALON_PIO_DATA(GPIO_SIGOUT_BASE, 0);
	IOWR_ALTERA_AVALON_PIO_DATA(GPIO_DATOUT_BASE, 0);

	return GPIO_ERROR_OK;

}
int gpio_drv11j_Close (DRV11J_IO_DEVICE *drv11j)
{
	g_pio_dev = 0;


	return GPIO_ERROR_OK;
}

/******************************************************************************
 *  FUNCTION: gpio_drv11j_isr
 *  DESCRIPTION: GPIO DRV11J Interrrupt service routine
 *  PARAMETERS:
 *
 *  RETURNS:
 *           N/A
 *
 *****************************************************************************/
void gpio_drv11j_isr(void * arg, alt_u32 id_num)
{
    unsigned char u8irq, u8sigin;
    int             iResult;
    int              write_size;
    unsigned char drv_buffer[GPIO_DRV11J_MAX_BUF_SIZE] ;
    unsigned int    edge_capture, sigin;

    DRV11J_IO_DEVICE *drv11j = (DRV11J_IO_DEVICE *)arg;
    // increase interrupt counter
    drv11j->rx_ints ++;

    /* DISABLE GPIO interrupt */
    IOWR_ALTERA_AVALON_PIO_IRQ_MASK(GPIO_SIGIN_BASE, 0);

    /* read signal in port*/
    edge_capture = IORD_ALTERA_AVALON_PIO_EDGE_CAP(GPIO_SIGIN_BASE);
    sigin =  IORD_ALTERA_AVALON_PIO_DATA (GPIO_SIGIN_BASE);

    u8irq = edge_capture & 0xFF;
    u8sigin = sigin & 0xFF;

    /* Handle interrupt 0: receive signal out from Unimation DRV11-J*/
    if (u8irq & DRJ_INTERRUPT0)
    {
        /* Handle Signals  */
    	/* the device is WRITE_Ready, read data from the device */
    	if (u8sigin & DRJ_WRITE_READY)
    	{
    		iResult = gpio_drv11j_Read(drv11j, drv_buffer, GPIO_DRV11J_MAX_BUF_SIZE);
    		if (iResult > 0)
    		{
    			gpio_drv11j_EnqueueRx(drv_buffer,iResult);
    		}
    	}

    	/* the device sends DRJ_READ_READY and indicates it is ready to read*/
    	if (u8sigin &  DRJ_READ_READY)
        {
    	    while (gpio_obj.txTail != gpio_obj.txHead)
    	    {
    	        memset(drv_buffer,0,sizeof(drv_buffer));
    	        write_size = gpio_drv11j_DequeueTx(drv_buffer,sizeof(drv_buffer));
    	        if ( write_size > 0)
    	        {
    	             iResult = gpio_drv11j_Write(drv11j, (const unsigned char*) drv_buffer, write_size);
    	        }
    	     }
        }
    }
 //  printf("GPIO_DRV11 interrupt: %d,  u8irq=0x%02x, SigIn=0x%02x\n",drv11j->rx_ints, u8irq, u8sigin);
    // re-enable all interrupts
    IOWR_ALTERA_AVALON_PIO_IRQ_MASK(GPIO_SIGIN_BASE, DRJ_INTERRUPT0);
    /* Reset the edge capture registers*/
    IOWR_ALTERA_AVALON_PIO_EDGE_CAP(GPIO_SIGIN_BASE, 0);
}


/******************************************************************************
 *  FUNCTION: gpio_drv11j_buffer_Reset
 *  DESCRIPTION:  Rest Tx/RX buffer
 *  PARAMETERS:
 *  	None
 *
 *  RETURNS:
 *         1: always success
 *
 *****************************************************************************/
int gpio_drv11j_buffer_Reset (GPIO_OBJECT *gpio_buf)
{
    /* initialize tx/rx queue*/
	if (gpio_buf)
	{
		gpio_buf->rxHead = 0;
		gpio_buf->rxTail = 0;

		gpio_buf->txHead = 0;
		gpio_buf->txTail = 0;

		return 0;
	}

    return -1;
}




/******************************************************************************
 *  FUNCTION: gpio_drv11j_EqueueRx
 *  DESCRIPTION:  enqueue data from the lower data to local FIFO buffer (RING buffer)
 *  PARAMETERS:
 *  	buffer :  data sent to the local RING buffer
 *      size   :  Data size in bytes
 *
 *  RETURNS:
 *           0:false;
 *           1:true;
 *
 *****************************************************************************/
int gpio_drv11j_EnqueueRx(unsigned char *buffer, int size)
{
    int newItem = (gpio_obj.rxTail + 1) % GPIO_CONFIG_RX_Q_LENGTH;
    if (newItem != gpio_obj.rxHead)
    {  /* suppose buffer size is less than queue buffer, does not check here */
        memcpy(gpio_rxByteQ[gpio_obj.rxTail].data, buffer, size);
        gpio_rxByteQ[gpio_obj.rxTail].u8Len = size;

        gpio_obj.rxTail = newItem;
        return 1;
    }
    else
    {
    	printf("Messages from Unimation controller are overflow, data lost\r\n");
    }

    return 0;
}


/******************************************************************************
 *  FUNCTION: DequeueRx
 *  DESCRIPTION:  Dequeue received data from the parallele port and  return the caller ( hoste)
 *  PARAMETERS:
 *  	buffer :  data received from local RING buffer
 *      size   :  received data size in bytes
 *
 *  RETURNS:
 *         0: empty, or
 *         Actual received data size ;
 *
 *****************************************************************************/
int gpio_drv11j_DequeueRx(unsigned char *buffer, int size)
{
    int nextItem = 0;
    int len =0;
    if (gpio_obj.rxTail == gpio_obj.rxHead)
    {
        return 0;
    }
    nextItem = (gpio_obj.rxHead + 1) % GPIO_CONFIG_RX_Q_LENGTH;
    len = gpio_rxByteQ[gpio_obj.rxHead].u8Len;
    if ( len > 0 &&( len <= size))
    {
        memcpy(buffer, gpio_rxByteQ[gpio_obj.rxHead].data, len );
        gpio_obj.rxHead = nextItem;
    }

    return len;
}


/******************************************************************************
 *  FUNCTION: gpio_drv11j_EnqueueTx
 *  DESCRIPTION:  Sending message to Unimation controller over FIFO ring buffer
 *  PARAMETERS:
 *  	buffer :  data sent to  Unimation robot
 *      size   :  data size to be sent
 *
 *  RETURNS:
 *         0: false
 *         1: true
 *
 *****************************************************************************/
int gpio_drv11j_EnqueueTx(unsigned char *buffer, int size)
{
    int copy_len ;
    int ret = 0;
    int newItem = (gpio_obj.txTail + 1) % GPIO_CONFIG_TX_Q_LENGTH;
    if (newItem != gpio_obj.txHead)
    {
        if ( size <= GPIO_DRV11J_MAX_BUF_SIZE)
        {
            copy_len = size;
        }
        else if (size > GPIO_DRV11J_MAX_BUF_SIZE)
        {
        	printf("EqueueTx: message size: %d is too large, truncated to %d\n",
                    size, GPIO_DRV11J_MAX_BUF_SIZE);
            copy_len = GPIO_DRV11J_MAX_BUF_SIZE;
        }

        if (copy_len > 0)
        {
            memcpy(gpio_txByteQ[gpio_obj.txTail].data, buffer, copy_len);
            ret = 1;
        }

        gpio_obj.txTail = newItem;
    }
    else
    {
    	printf("Message buffer to Unimation controller are full, data lost\n");
    }

    return ret;
}


/******************************************************************************
 *  FUNCTION: DequeueTx
 *  DESCRIPTION:
 *  PARAMETERS:
 *  	buffer :  Dequeue data from local TX ring buffer and prepare to send to  Unimation robot
 *      size   :  data size to be sent
 *
 *  RETURNS:
 *         data length from FIFO ring buffer
 *
 *****************************************************************************/
int gpio_drv11j_DequeueTx(unsigned char *buffer, int size)
{
    int nextItem = 0;
    int len =0;
    if (gpio_obj.txTail == gpio_obj.txHead)
    {
    	printf("Messages buffer to Unimation controller is empty \n");
        return 0;
    }
    nextItem = (gpio_obj.txHead + 1) % GPIO_CONFIG_TX_Q_LENGTH;
    len = gpio_txByteQ[gpio_obj.txHead].u8Len;
    if ( len > 0 &&( len <= size))
    {
        memcpy(buffer, gpio_txByteQ[gpio_obj.txHead].data, len );
        gpio_obj.txHead = nextItem;
    }

    return len;
}

/*** Driver Interfaces ***/
/******************************************************************************
 *   FUNCTION:  alt_up_parallel_port_Open
 *   DESCRIPTION: Open GPIO DRV11J device
 *   PARAMETERS:
 *   	dev: Parallel port device handle
 *   RETURN:
 *      GPIO_ERROR_OK : Close the device sucessfully
 *      GPIO_INVALID_HANDLE: Invalid  DRV11J device handle
 ******************************************************************************/
 int alt_parallel_port_Open (alt_fd* fd, const char* name, int flags, int mode)
{
     int ret = 1;
	 char *fxnName = "drv11jOpen";

    if( ! strcmp(name, GPIO_SIGIN_NAME))
    {
    	alt_pio_dev * pio_dev = (alt_pio_dev *)(fd->dev);

    	if (pio_dev != g_pio_dev)
    	{
    		printf("%s: Error: Open device %s error : pio_dev:%p is not %p \n",fxnName, name, pio_dev, fd->dev );
    		ret =  (0 - GPIO_INVALID_HANDLE);
    	}
    }
    else
    {
    	printf("%s, Error: Open the wrong device: %s\n", fxnName, name);
    	ret = (0 - GPIO_OPEN_DEVICE_ERROR);

    }

    return ret ;

}

/******************************************************************************
 *   FUNCTION:  alt_up_parallel_port_Close
 *   DESCRIPTION: Close GPIO DRV11J device
 *   PARAMETERS:
 *   	dev: Parallel port device handle
 *   RETURN:
 *      GPIO_ERROR_OK : Close the device sucessfully
 *      GPIO_INVALID_HANDLE: Invalid  DRV11J device handle
 ******************************************************************************/
int alt_parallel_port_Close (alt_fd* fd)
{
    char *fxnName = "drv11jClose";

    alt_pio_dev * pio_dev = (alt_pio_dev *)fd->dev;

    if ( !pio_dev)
    {
    	printf("%s: Error: Invalid device handle, open the device first!", fxnName);
        return (GPIO_INVALID_HANDLE);
    }

    DRV11J_IO_DEVICE *drv11j = (DRV11J_IO_DEVICE * )&pio_dev->drv11j;

    /* Reset local TX buffer */
    gpio_drv11j_buffer_Reset(&gpio_obj);

    /* disable interrupts */
    gpio_drv11j_Close(drv11j);

    g_pio_dev = NULL;

    return (GPIO_ERROR_OK);

}

/******************************************************************************
 *  FUNCTION: alt_parallel_port_Read
 *  DESCRIPTION:  Host read received buffer from lower layer ( non block mode)
 *  PARAMETERS:
 *  	None
 *
 *  RETURNS:
 *         1: always success
 *
 *****************************************************************************/
int alt_parallel_port_Read( alt_fd* fd, unsigned char *buffer, const int bufLen)
{
    alt_pio_dev * pio_dev = (alt_pio_dev *)(fd->dev);
    int len =0;
    const char *fxnName= "alt_parallel_port_read";

    if ( !pio_dev)
    {
    	printf("%s: Error: Invalid device handle, open the device first!", fxnName);
        return (0 - GPIO_INVALID_HANDLE);
    }
    //read from gpio_buffer
	len = gpio_drv11j_DequeueRx(buffer, bufLen);

	if (len <= 0)
	{
		printf("no data from the robot\n");
		//Send Wake up signal to ->moper->slaveDrier ( read data from control and hostWrite, and then hostRead
		gpio_drv11j_Signal(&pio_dev->drv11j, 1);
	    //delay a little .
		alt_busy_sleep(10000); //wait 10 mSec
   	    len = gpio_drv11j_DequeueRx(buffer, bufLen);
	}


    return len;

}
/******************************************************************************
 *  FUNCTION: Host_BufferWrite
 *  DESCRIPTION:  Host writes the data to lower layer ( non block mode)
 *  PARAMETERS:
 *  	None
 *
 *  RETURNS:
 *         1: always success
 *
 *****************************************************************************/
int alt_parallel_port_Write(  alt_fd* fd, const unsigned char *buffer, const int bufLen )
{
    int Result = 0 ;
    const char *fxnName = "alt_parallel_port_write";

    alt_pio_dev * pio_dev = (alt_pio_dev *)(fd->dev);
    if ( !pio_dev)
    {
    	printf("%s: Error: Invalid device handle, open the device first!", fxnName);
        return (0 - GPIO_INVALID_HANDLE);
    }
    //block write until the target device is ready to read.
    Result = gpio_drv11j_Write(&pio_dev->drv11j, buffer, bufLen);

    if (Result != bufLen)
    {    
    	Result = gpio_drv11j_EnqueueTx((unsigned char *) buffer, bufLen);
    	if ( pio_dev != NULL)
    	{ /* For here we need signal unimation controller */
    		DRV11J_IO_DEVICE *drv11j = (DRV11J_IO_DEVICE * )&pio_dev->drv11j;
    		gpio_drv11j_Signal(drv11j, 1);
    	}
    }

    return Result;
}


/******************************************************************************
 *  FUNCTION: alt_parallel_port_Ioctl
 *  DESCRIPTION: GPIO DRV11J IO Control
 *  PARAMETERS: dev - Device handle
 *              req : device IO control commands.
 *              data: possible control handle
 *  RETURNS:
 *           GPIO_INVALID_VALUE;
 *           GPIO_ERROR_OK : OK otherwise
 *
 *****************************************************************************/
int alt_parallel_port_Ioctl (alt_fd* fd, int req, void *data)
{
    int rc = GPIO_ERROR_OK; /* 0: success*/
    unsigned char u8Val=0;
    int           signum = 0;
    char *fxnName = "gpio_drv11j_Ioctl()";

    gpio_drv11j_buf     *read_buf;
    gpio_drv11j_buf     *write_buf;

    alt_pio_dev * pio_dev = (alt_pio_dev *)(fd->dev);

    if ( !pio_dev)
    {
    	printf("%s: Error: Invalid device handle, open the device first!", fxnName);
        return GPIO_INVALID_HANDLE;
    }

    DRV11J_IO_DEVICE *drv11j = (DRV11J_IO_DEVICE *) &(pio_dev->drv11j);
    if ( !drv11j)
    {
    	printf( "%s: Error: DRV11J_IO_DEVICE is NULL, init the device first!", fxnName);
        return GPIO_INVALID_HANDLE;
    }
    switch (req)
    {
        case IOC_SIGINTER_ENABLE:
        {
        	u8Val = IORD_ALTERA_AVALON_PIO_IRQ_MASK(GPIO_SIGIN_BASE) ;
        	u8Val |= BIT_INTERRUPT0;
            IOWR_ALTERA_AVALON_PIO_IRQ_MASK(GPIO_SIGIN_BASE, u8Val);
           break;
        }
        case IOC_SIGINTER_DISABLE:
        {

        	u8Val = IORD_ALTERA_AVALON_PIO_IRQ_MASK(GPIO_SIGIN_BASE) ;
        	u8Val &= ~(BIT_INTERRUPT0);
            IOWR_ALTERA_AVALON_PIO_IRQ_MASK(GPIO_SIGIN_BASE, u8Val);

            break;
        }
        case IOC_TIMEOUT:
        {
        	drv11j->timeout = *(int *)data;

            break;
        }

        case IOC_WRITE_DATA:
        {
            write_buf = (gpio_drv11j_buf * )data;
            if (write_buf != NULL)
            {   //skip msg->size and msg->cmd, msg->mcd_ext
            	rc = gpio_drv11j_Write(drv11j, (const unsigned char *) write_buf->data + 4, write_buf->size);
            }

             break;
        }
        case IOC_READ_DATA:
        {
             read_buf = (gpio_drv11j_buf *)data;
             rc = 0;
             if ( read_buf != NULL)
             {
                 rc = gpio_drv11j_Read(drv11j,(unsigned char *) read_buf->data, GPIO_DRV11J_MAX_BUF_SIZE);
             }
             if (rc <= 0)
             {
            	 gpio_drv11j_Signal(drv11j, 1); //Send Wake up signal to ->moper->slaveDrier ( read data from control and hostWrite, and then hostRead
            	 alt_busy_sleep(10000); //wait 10 mSec
            	 rc = gpio_drv11j_DequeueRx((unsigned char *) read_buf->data, GPIO_DRV11J_MAX_BUF_SIZE);
             }
             read_buf->size = rc;
             break;
        }
        case IOC_SEND_SIGNAL: 
        {
             read_buf = (gpio_drv11j_buf *)data;
             signum = (unsigned short) read_buf->data[0];

             rc = gpio_drv11j_Signal(drv11j, signum);

              break;
        }
        case IOC_GET_BLOCKTIME:
        {
        	*(int *)data = drv11j->blocktime;
            break;
        }
        case IOC_SET_BLOCKTIME:
        {
        	drv11j->blocktime = *(int *)data;
          break;
        }
        case IOC_DRV_ATTACH:
        {
            rc = gpio_drv11j_Attach(drv11j);
            break;
        }
        case IOC_DRV_DETACH:
        {
           rc = gpio_drv11j_Detach (drv11j);
           break;
        }
        case IOC_RESET_BUFFER:
        {
        	rc = gpio_drv11j_buffer_Reset(&gpio_obj);
        	break;
        }
       default:
        {
        	printf("%s: none exist ioctl command: %d\n",fxnName, req);
            rc = GPIO_BAD_COMMAND;
        }
     }

    return (rc);
}



/**
  End of File
*/


