/****************************************************************************/
/*  FILE:   ram_card.c                                                      */
/*                                                                          */
/*  PURPOSE:    Provides functions to provide low level access to the       */
/*              TASS Mk IV RAM Buffer card.                                 */
/*                                                                          */
/****************************************************************************/


/* Used by RCS and ident */
char ram_card_rcsid[] = 
    "$Id: ram_card.c,v 1.1 1998/09/28 06:41:51 chris Exp $";


#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <stdio_lim.h>
#include <fcntl.h>
#include <sys/ioctl.h>

#include "mk4.h"

#include "MLog.h"
#include "ram_card.h"
#include "parameters.h"

 

static int      FD_ram      = -1;
static int      open_status = RAM_CLOSED;


#if 0
/* Main used only for test */
void main(void)
{     
    int     res;
    int     len;
    char    buffer[100];
    
    ml_setup(ML_DEBUG, NULL, NULL);
    
    printf("opening for read...");
    res = ram_open(O_RDONLY);
    printf("opened %d\n", res);
    
    printf("setting status...");
    res = ram_set_user_status('R');
    printf("done.  %d\n", res);
    
    printf("setting buffer full...");
    res = ram_set_buffer_full(30);
    printf("done.  %d\n", res);
    
    printf("reading 25 bytes...\n", res);
    len = 25;
    res = ram_read_block(buffer, &len);
    printf("  got:<%s>\n", buffer);
    printf("  ...done res:%d len:%d\n", res, len);
    
    
    printf("return to quit");
    gets(buffer);
    exit(0);
}
#endif


/****************************************************************************/
/*                                                                          */
/*  ram_open                                                                */
/*                                                                          */
/*  PURPOSE:                                                                */
/*      Open the spepcial device file associated with the TASS Mk IV        */
/*      RAM Buffer Card.  Handle any errors detected.                       */
/*                                                                          */
/*  RETURNS:                                                                */
/*      0  =  The file is open.  No error.                                  */
/*     -1  =  Error.  The file could not be opened.                         */
/*                                                                          */
/****************************************************************************/
int ram_open(int open_type)
{
    int     res;
    
    
    /* Validate argument.  We disallow O_RDWR */
    if( (open_type != O_RDONLY) && (open_type != O_WRONLY) )
    {
        /* the open failed */
        ml_printf(ML_MANDATORY, ML_ERROR,
            "INTERNAL ERROR: open called with invalid argument\n");
        
        return RAM_ERROR;
    }
    
    /* Check if already open */
    if(open_status != RAM_CLOSED)
    {
        res = ram_close();
        if(res == RAM_ERROR)
        {
            /* Error on close */
            return res;
        }
    }         
     
             
    FD_ram = open(gbl_ram_device, open_type);
    
    if(-1 == FD_ram)
    {
        /* the open failed */
        ml_printf(ML_MANDATORY, ML_ERROR,
            "ERROR: failed to open RAM Buffer device <%s>\n",
            gbl_ram_device);
        ml_printf(ML_MANDATORY, ML_ERROR,
            "       %s\n", strerror(errno));
            
        return RAM_ERROR;
    }
    
    /* No error */
    open_status = open_type;
    return 0;
}


/****************************************************************************/
/*                                                                          */
/*  ram_close                                                               */
/*                                                                          */
/*  PURPOSE:                                                                */
/*      Close the special device file associated with the TASS Mk IV        */
/*      RAM Buffer Card.  Handle any errors detected.                       */
/*                                                                          */
/*  RETURNS:                                                                */
/*      0  =  The file is closed.  No error.                                */
/*     -1  =  Error.  The file could not be closed.                         */
/*                                                                          */
/****************************************************************************/
int ram_close(void)
{
    int     res;
    
    
    if(open_status == RAM_CLOSED)
    {
        /* Already closed */
        return RAM_OK;
    }         
             
    res = close(FD_ram);
    
    if(res == -1)
    {
        /* the close failed */
        ml_printf(ML_MANDATORY, ML_ERROR,
            "ERROR: failed to close RAM Buffer device <%s>\n",
            gbl_ram_device);
        ml_printf(ML_MANDATORY, ML_ERROR,
            "       %s\n", strerror(errno));
            
        return RAM_ERROR;
    }
    
    FD_ram = -1;
    return RAM_OK;
}


/****************************************************************************/
/*                                                                          */
/*  ram_read_block                                                          */
/*                                                                          */
/*  PURPOSE:                                                                */
/*      Reads a block of data out of the TASS Mk IV RAM Buffer card.        */
/*      This data my contain interleaved bytes from CCD0 and CCD1 but this  */
/*      function knows nothing about that.  Here we just move bytes.        */
/*                                                                          */
/*  RETURNS:                                                                */
/*     >0  =  The is read into the buffer.  No error.                       */
/*            The number of bytes read is returned                          */
/*      0  =  End of file.  No more data to read.                           */
/*     -1  =  Error.  The file could not be read.                           */
/*                                                                          */
/*      buffer_len will be set to the number of bytes actually placed into  */
/*      the buffer.  Mostly the function's return value and buffer_len      */
/*      will be equal.  Exceptions are 1) on errors, 2) possably, at End    */
/*      of File if the file is not an exact multiple of the buffer size     */
/*                                                                          */
/****************************************************************************/
int ram_read_block(char *buffer, int *buffer_len)
{
    int     res;
    int     bytes_read;
    int     bytes_requested;
    
    
    /* Remember how many bytes where requested */
    bytes_requested = *buffer_len;
    
    ml_printf(ML_DEBUG, ML_INFORMATION,
            "ram_read_block called. buffer_len: %d\n",
            *buffer_len);
    
    
    /* Make sure the file is open */
    if(open_status == RAM_CLOSED)
    {
        /* The file is not open.  Must first open for read */
        res = ram_open(O_RDONLY);
        if(res == RAM_ERROR)
        {
            /* Could not open */
            ml_printf(ML_MANDATORY, ML_ERROR,
                "ERROR: open faild, could not read block\n");
                
            return RAM_ERROR;
        }
    }  
    
    
    /* Keep reading data untill we get all the data we asked for    */
    /* return on the exception cases 1) Error and 2) EOF            */
    bytes_read = 0;
    while(bytes_read < bytes_requested)
    {       
        res = read(FD_ram, &buffer[bytes_read],
                          (bytes_requested-bytes_read));

        if(-1 == res)
        {
            /* error */
            ml_printf(ML_MANDATORY, ML_ERROR,
                "ERROR: read failed for RAM Buffer device <%s>\n",
                gbl_ram_device);
            ml_printf(ML_MANDATORY, ML_ERROR,
                "       %s\n", strerror(errno));

            *buffer_len = 0;
            return RAM_ERROR;
        }
        else if(res == 0)
        {
            /* We are at EOF */
            *buffer_len = bytes_read;
            return RAM_OK;
        }
        else if(res > 0)
        {
            /* This is the "normal" case */
            bytes_read += res;
        }
    }
    
    /* Only the "normal", non-error, non-EOF case is allowed to fall    */
    /* through out od the loop to here                                  */
    *buffer_len = bytes_read;
    return        bytes_read;
}


/****************************************************************************/
/*                                                                          */
/*  ram_write_block                                                         */
/*                                                                          */
/*  PURPOSE:                                                                */
/*      Writes a block of data to the TASS Mk IV RAM Buffer card.  This     */
/*      could be used for testing.  In normal operation we don't write      */
/*      to the card.                                                        */
/*                                                                          */
/*  RETURNS:                                                                */
/*     >0  =  The is read from the buffer.  No error.                       */
/*            The number of bytes moved is returned                         */
/*      0  =  The buffer was filled.  No more data will fit                 */
/*     -1  =  Error.                                                        */
/*                                                                          */
/*      buffer_len will be set to the number of bytes actually placed into  */
/*      the card.  Mostly the function's return value and buffer_len        */
/*      will be equal.  Exceptions are 1) on errors, 2) possably, at End    */
/*      of Frame if the frame is not an exact multiple of the buffer size   */
/*                                                                          */
/****************************************************************************/
int ram_write_block(char *buffer, int *buffer_len)
{
    int     res;
    int     bytes_requested;
        

    /* Make sure the file is open */
    if(open_status == RAM_CLOSED)
    {
        /* The file is not open.  Must first open for read */
        res = ram_open(O_WRONLY);
        if(res == RAM_ERROR)
        {
            /* Could not open */
            ml_printf(ML_MANDATORY, ML_ERROR,
                "ERROR: open faild, could not write block\n");
                
            return RAM_ERROR;
        }
    }  
     
    
    bytes_requested = *buffer_len;
    res = read(FD_ram, buffer, bytes_requested);
    
    if(-1 == res)
    {
        /* error */
        ml_printf(ML_MANDATORY, ML_ERROR,
            "ERROR: write failed for RAM Buffer device <%s>\n",
            gbl_ram_device);
        ml_printf(ML_MANDATORY, ML_ERROR,
            "       %s\n", strerror(errno));

        *buffer_len = 0;
        return -1;
    }
    else if(res == 0)
    {
        /* nothing was written.  We are at EOF. */
        *buffer_len = 0;
        return RAM_OK;
    }
    else if(res > 0)
    {
        /* This is the "normal" case */
        *buffer_len = res;
        return        res;
    }
    
    /* wierd case, shouldn't happen */
    ml_printf(ML_MANDATORY, ML_ERROR,
            "INTERNAL ERROR: wierd value of res %d\n", res);

    *buffer_len = 0;
    return RAM_ERROR;
}


/****************************************************************************/
/*                                                                          */
/*  ram_set_user_status                                                     */
/*                                                                          */
/*  PURPOSE:                                                                */
/*      Sets the "status" bytes.  This is purely cosmetic.  It's only       */
/*      effect is to appear in the /poc/mk4 file.                           */
/*                                                                          */
/*  RETURNS:                                                                */
/*      0  =  It worked.                                                    */
/*     -1  =  Error.                                                        */
/*                                                                          */
/****************************************************************************/
int ram_set_user_status(char stat)
{
    int     res;
        
    
    /* Make sure the file is open */
    if(open_status == RAM_CLOSED)
    {
        /* The file is not open.  Must first open for read */
        res = ram_open(O_WRONLY);
        if(res == RAM_ERROR)
        {
            /* Could not open */
            ml_printf(ML_MANDATORY, ML_ERROR,
                "ERROR: open faild, cloud not set user status\n");
                
            return RAM_ERROR;
        }
    } 
        
    res = ioctl(FD_ram, MK4_IOC_SUSERSTATUS, &stat);
    if (res < 0)
    {
        /* ERROR */
        ml_printf(ML_MANDATORY, ML_ERROR,
            "ERROR: set user status failed for RAM Buffer device <%s>\n",
            gbl_ram_device);
        ml_printf(ML_MANDATORY, ML_ERROR,
            "       %s\n", strerror(errno));

        return -1;
    } 
    
    /* Looks like it worked */
    return 0;
}


/****************************************************************************/
/*                                                                          */
/*  ram_set_buffer_full                                                     */
/*                                                                          */
/*  PURPOSE:                                                                */
/*      If we are running in NON-interrupt mode, the Mk IV RAM Buffer card  */
/*      has no way to know if it is full.  Calling this function will       */
/*      tell the card it is full.  This function serves the same purpose    */
/*      as an interrupt.  This function should be called after a known      */
/*      delay after issuing a readout comand to the STAMP.                  */
/*                                                                          */
/*  RETURNS:                                                                */
/*      0  =  It worked.                                                    */
/*     -1  =  Error.                                                        */
/*                                                                          */
/****************************************************************************/
int ram_set_buffer_full(int bytes)
{
    int     res;   
    
    /* Make sure the file is open */
    if(open_status == RAM_CLOSED)
    {
        /* The file is not open.  Must first open for read */
        res = ram_open(O_WRONLY);
        if(res == RAM_ERROR)
        {
            /* Could not open */
            ml_printf(ML_MANDATORY, ML_ERROR,
                "ERROR: open faild, cloud not set buffer full\n");
                
            return RAM_ERROR;
        }
    } 
        
    res = ioctl(FD_ram, MK4_IOC_SBUFFULL, &bytes);
    if (res < 0)
    {
        /* ERROR */
        ml_printf(ML_MANDATORY, ML_ERROR,
            "ERROR: set buffer full failed for RAM Buffer device <%s>\n",
            gbl_ram_device);
        ml_printf(ML_MANDATORY, ML_ERROR,
            "       %s\n", strerror(errno));

        return -1;
    } 
    
    /* Looks like it worked */
    return 0;
}


/****************************************************************************/
/*                                                                          */
/*  ram_set_expected                                                        */
/*                                                                          */
/*  PURPOSE:                                                                */
/*      The buffer assumes in contains "bytes" data bytes when it is told   */
/*      it is full.  THere are two types of readouts:  Full frame and       */
/*      sub-frame.  This function must be called rite after we issue the    */
/*      readout command to the STAMP to tell the driver how many bytes will */
/*      go into the buffer.                                                 */
/*                                                                          */
/*  RETURNS:                                                                */
/*      0  =  It worked.                                                    */
/*     -1  =  Error.                                                        */
/*                                                                          */
/****************************************************************************/
int ram_set_expected(int bytes)
{
    int     res;  
    
    /* Make sure the file is open */
    if(open_status == RAM_CLOSED)
    {
        /* The file is not open.  Must first open for read */
        res = ram_open(O_WRONLY);
        if(res == RAM_ERROR)
        {
            /* Could not open */
            ml_printf(ML_MANDATORY, ML_ERROR,
                "ERROR: open faild, cloud not set buffer full\n");
                
            return RAM_ERROR;
        }
    } 
        
    res = ioctl(FD_ram, MK4_IOC_SEXPECTED, &bytes);
    if (res < 0)
    {
        /* ERROR */
        ml_printf(ML_MANDATORY, ML_ERROR,
            "ERROR: set buffer full failed for RAM Buffer device <%s>\n",
            gbl_ram_device);
        ml_printf(ML_MANDATORY, ML_ERROR,
            "       %s\n", strerror(errno));

        return -1;
    } 
    
    /* Looks like it worked */
    return 0;
}

 


/****************************************************************************/
/*                                                                          */
/*  ram_hard_reset                                                          */
/*                                                                          */
/*  PURPOSE:                                                                */
/*      This should never be called in normal use.  If the driver module    */
/*      gets "stuck" and the "rmmod" command fails to unload the module     */
/*      Then this will reset module's usage count.  It this function needs  */
/*      to be called something is wrong.                                    */
/*                                                                          */
/*  RETURNS:                                                                */
/*      0  =  It worked.                                                    */
/*     -1  =  Error.                                                        */
/*                                                                          */
/****************************************************************************/
int ram_hard_reset(void)
{
    int     res;
    int     junk = 0; 
    
    /* Always print message */   
    ml_printf(ML_MANDATORY, ML_INFORMATION,
                "NOTE: ram_hard_reset called for %s\n", gbl_ram_device);
                
    /* Make sure the file is open */
    if(open_status == RAM_CLOSED)
    {
        /* The file is not open.  Must first open for write */
        res = ram_open(O_WRONLY);
        if(res == RAM_ERROR)
        {
            /* Could not open */
            ml_printf(ML_MANDATORY, ML_ERROR,
                "ERROR: open faild, cloud not hard reset buffer\n");
                
            return RAM_ERROR;
        }
    } 
        
    res = ioctl(FD_ram, MK4_IOC_HARDRESET, &junk);
    if (res < 0)
    {
        /* ERROR */
        ml_printf(ML_MANDATORY, ML_ERROR,
            "ERROR: hard reset bufferfailed for %s\n",
            gbl_ram_device);
        ml_printf(ML_MANDATORY, ML_ERROR,
            "       %s\n", strerror(errno));

        return -1;
    } 
    
    /* Looks like it worked */
    return 0;
}
   
