/** @file sample_endat_01.c
*
* @author El Bakali Laaziz
* Original Sample : ReadWriteOem
*i_APCI1711_EndatResetErrorBits
*i_APCI1711_EndatInitialiseSensor
*i_APCI1711_EndatSelectMemoryArea
*i_APCI1711_EndatSensorSendParameter
*i_APCI1711_EndatSelectMemoryArea
*i_APCI1711_EndatSensorSendParameter
*i_APCI1711_EndatSensorReceiveParameter
*/

/** @par LICENCE
* @verbatim
    Copyright (C) 2009  ADDI-DATA GmbH for the source code of this module.

    ADDI-DATA GmbH
    Airpark Business Center
    Airport Boulevard B210
    77836 Rheinm�nster
    Germany
    Tel: +49(0)7229/1847-0
    Fax: +49(0)7229/1847-200
    http://www.addi-data-com
    info@addi-data.com

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You shoud find the complete GPL in the COPYING file accompanying
    this source code.
* @endverbatim
*/

#include <linux/version.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
    #include <linux/config.h>
#else
    #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
        #include <linux/autoconf.h>
    #else
        #include <generated/autoconf.h>
    #endif
#endif

#include <linux/spinlock.h>
#include <linux/ioctl.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/pci.h> // struct pci_dev
#include <asm/io.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,4,0)
    #include <asm/system.h>
#endif
#include <asm/uaccess.h>
#include <asm/bitops.h>
#include <linux/sched.h>

#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/proc_fs.h>

#include <apci1710.h>
#include <apci1710-kapi.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("ADDI-DATA GmbH <info@addi-data.com>");
MODULE_DESCRIPTION("APCI-1710");

EXPORT_NO_SYMBOLS;

static const char modulename[] = "sample_endat";

/**
* Display the content of an area
**/
static void DisplayAreaSpace( int areaIndex, uint32_t mask)
{
    unsigned int area[6] = {0, 31, 63, 64, 127, 255};

    if ( mask == 0xFF || ((mask & 0xF)>5) || (((mask>>4) & 0xF)>5))
    {
        printk("Area %d: \t------\n", areaIndex);
        return;
    }
    printk("Area %d: \t%d -> %d\n", areaIndex, area[(mask & 0xF)], area[((mask >> 4) & 0xF)]);
}

/**
* entry point
**/
static int sample_endat_kernel (struct pci_dev *pdev)
{
    int i_ReturnValue = 0 ;
    uint32_t paramArea12 = 0;
    uint32_t paramArea34 = 0;


    //Parameter for the function
    uint8_t moduleIndex = 0;
    uint8_t sensorIndex = 0;
    uint32_t frequency = 1000;

    //Parameter fixed by the user
    int writeParameter = 1; // 0: to read; 1: to write

    /* reset the error bits */
    i_ReturnValue = i_APCI1711_EndatResetErrorBits(pdev,moduleIndex,sensorIndex);
    if (i_ReturnValue != 0)
    {

        printk("i_APCI1711_EndatResetErrorBits, error : %d \n", i_ReturnValue);
        return 1;
    }
    printk("i_APCI1711_EndatResetErrorBits: ok. \n");


    /* initialise the EnDat sensor */
    i_ReturnValue = i_APCI1711_EndatInitialiseSensor(pdev,moduleIndex,sensorIndex,frequency);
    if ( i_ReturnValue != 0 )
    {
        printk("i_APCI1711_EndatInitialiseSensor, error : %d \n", i_ReturnValue);
        return 1;
    }
    printk("i_APCI1711_EndatInitialiseSensor: ok. \n");


    /**
    * Before reading (or writing) something in (into) the OEM parameters, we have to get the start/end
    * address of the different area.
    * They are available with the MRS-Code 0xA1, at the addres 0x9 and 0xA.
    **/
    /* select the memory area 0xA1 */
    i_ReturnValue = i_APCI1711_EndatSelectMemoryArea(pdev,moduleIndex,sensorIndex,0xA1);
    if ( i_ReturnValue != 0 )
    {
        printk("i_APCI1711_EndatSelectMemoryArea, error : %d \n", i_ReturnValue);
        return 1;
    }
    printk("i_APCI1711_EndatSelectMemoryArea (0xA1): ok. \n");


    /* read the address 0x9 (contains addresses for area 1 and area 2) */
    i_ReturnValue = i_APCI1711_EndatSensorSendParameter(pdev,moduleIndex,sensorIndex,0xA1,0x9,&paramArea12);
    if ( i_ReturnValue != 0 )
    {
        printk("i_APCI1711_EndatSensorSendParameter, error : %d \n", i_ReturnValue);
        return 1;
    }
    printk("i_APCI1711_EndatSensorSendParameter (0xA1 - 0x9): ok. \n");

    /* display the area 1 and area 2 */
    DisplayAreaSpace(1, paramArea12 & 0xFF);
    DisplayAreaSpace(2, (paramArea12 >> 8) & 0xFF);


    /* read the address 0xA (contains addresses for area 3 and area 4) */
    i_ReturnValue = i_APCI1711_EndatSensorSendParameter(pdev,moduleIndex,sensorIndex, 0xA1, 0xA, &paramArea34);
    if ( i_ReturnValue != 0 )
    {
        printk("i_APCI1711_EndatSensorSendParameter, error : %d \n", i_ReturnValue);
        return 1;
    }
    printk("i_APCI1711_EndatSensorSendParameter (0xA1 - 0xA): ok.\n");

    /* display the area 3 and area 4 */
    DisplayAreaSpace(3, paramArea34 & 0xFF);
    DisplayAreaSpace(4, (paramArea34 >> 8) & 0xFF);


    /* user wants to read */
    if ( writeParameter == 0 )
    {
        int oemArea = 0;
        uint32_t address = 0;
        uint32_t mrsCode = 0;
        uint32_t value = 0;

        //Parameters of the OEM's area you want to read (1->4), in this sample only OEM = 1 is used, see documentation of the Endat Counter.
        for (oemArea=1;oemArea<2;oemArea++)
        {
            switch( oemArea )
            {
            case 1:
                DisplayAreaSpace(1, paramArea12 & 0xFF);
                mrsCode = 0xA9;
                break;
            case 2:
                DisplayAreaSpace(2, (paramArea12 >> 8) & 0xFF);
                mrsCode = 0xAB;
                break;
            case 3:
                mrsCode = 0xAD;
                DisplayAreaSpace(3, paramArea34 & 0xFF);
                break;
            case 4:
                mrsCode = 0xAF;
                DisplayAreaSpace(4, (paramArea34 >> 8) & 0xFF);
                break;
            }

            /* select the memory area */
            i_ReturnValue = i_APCI1711_EndatSelectMemoryArea(pdev, moduleIndex, sensorIndex, mrsCode);
            if ( i_ReturnValue != 0 )
            {
                printk("i_APCI1711_EndatSelectMemoryArea, error : %d \n", i_ReturnValue);
                return 1;
            }
            printk("i_APCI1711_EndatSelectMemoryArea (0x%X): ok. \n", mrsCode);


            //Address that you want to read (must be in the address space), see documentation of the Endat Counter.
            address = 0xA9;

            /* then read the content */
            i_ReturnValue = i_APCI1711_EndatSensorSendParameter(pdev, moduleIndex,sensorIndex,mrsCode,address,&value);
            if ( i_ReturnValue != 0 )
            {
                printk("i_APCI1711_EndatSensorSendParameter, error : %d \n", i_ReturnValue);
                return 1;
            }
            printk("i_APCI1711_EndatSensorSendParameter (0x%X - %d): ok. \n", mrsCode, address);
            printk("Value : 0x%X \n", value);

        }

    }


        /* user wants to write */
        if ( writeParameter == 1 )
        {
            int oemArea = 0;
            uint32_t address = 0;
            uint32_t mrsCode = 0;
            uint32_t value = 0;

            //Parameters of the OEM's area you want to set (1->4), see documentation of the Endat Counter.
            for (oemArea=1;oemArea<2;oemArea++)
            {

                printk("Valid address space: \n");
                switch( oemArea )
                {
                case 1:
                    DisplayAreaSpace(1, paramArea12 & 0xFF);
                    mrsCode = 0xA9;
                    break;
                case 2:
                    DisplayAreaSpace(2, (paramArea12 >> 8) & 0xFF);
                    mrsCode = 0xAB;
                    break;
                case 3:
                    mrsCode = 0xAD;
                    //DisplayAreaSpace(3, paramArea34 & 0xFF);
                    break;
                case 4:
                    mrsCode = 0xAF;
                    DisplayAreaSpace(4, (paramArea34 >> 8) & 0xFF);
                    break;
                }

                /* select the memory area */
                i_ReturnValue = i_APCI1711_EndatSelectMemoryArea(pdev, moduleIndex, sensorIndex, mrsCode);
                if ( i_ReturnValue != 0 )
                {
                    printk("i_APCI1711_EndatSelectMemoryArea, error : %d \n", i_ReturnValue);
                    return 1;
                }
                printk("i_APCI1711_EndatSelectMemoryArea (0x%X): ok. \n", mrsCode);


                //Address that you want to set (must be in the address space), see documentation of the Endat Counter
                address = 0xA9;

                //the new value to write
                value = 5; // As exemple


                /* then set the content */
                i_ReturnValue = i_APCI1711_EndatSensorReceiveParameter(pdev,
                                                             moduleIndex,
                                                             sensorIndex,
                                                             mrsCode,
                                                             address,
                                                             value);
                if ( i_ReturnValue != 0 )
                {
                    printk("i_APCI1711_EndatSensorReceiveParameter, error : %d \n", i_ReturnValue);
                    return 1;
                }
                printk("i_APCI1711_EndatSensorReceiveParameter (0x%X - %d): ok. \n", mrsCode, address);

            }

        }



    return 0;
}


/** Called when module loads. */
static int __init apci1710Sample_init(void)
{
    struct pci_dev *pdev;
    int board_index = 0;

    printk("%s: looking for board %u\n",modulename,board_index);
    pdev=apci1710_lookup_board_by_index(0);
    if( !pdev )
    {
        printk("%s: board %u not found\n",modulename,board_index);
        return -ENODEV;
    }
    printk("%s: beginning test\n",modulename);
    sample_endat_kernel (pdev);

    return 0;
}
//-------------------------------------------------------------------
/** Called when module is unloaded. */
static void __exit apci1710Sample_exit(void)
{


}
//------------------------------------------------------------------------------
module_exit(apci1710Sample_exit);
module_init(apci1710Sample_init);




