/*
 * pressure.c -- TAI8570 interface for w1retap
 *  (c) 2005 Jonathan Hudson <jh+w1retap@daria.co.uk>
 *
 * Substansially derived from the tai8570.c file in the archive
 * tai8570sjm102_300.zip archive available from
 * http://oww.sourceforge.net/download/tai8570sjm102_300.zip via
 * http://www.aagelectronica.com/aag/ * tai8570.c
 *
 * tai8570.c contains the following notice:
 * -------------------------------------------------
 * tai8570.c
 * Version 1.02
 *
 * Simon Melhuish
 * http://melhuish.info/simon/
 * http://oww.sourceforge.net/
 *
 * Derived from source by AAG
 * ------------------------------------------------- 
 *
 * Other parts derived from the Dallas PD kit, so let's assume their
 * PD licence.
 *
 * Jonathan Hudson removed unused code, reformatted to his favourite
 * layout and added API entries suitable for w1retap.
 *
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>

#include "ownet.h"
#include "swt12.h"
#include "owfile.h"

/**
 *
 * @author  Aitor
 */

#define SEC_READW4     "111011010000"
#define SEC_READW2     "111010110000"
#define SEC_READW1     "111010101000"
#define SEC_READW3     "111011001000"

#define SEC_READD1     "11110100000"
#define SEC_READD2     "11110010000"
#define SEC_RESET      "101010101010101000000"

typedef unsigned char byte;
typedef int bool;
#define false 0
#define true  1

static u_char SwitchSN[2][8];
static int reader=-1, writer;

static char SW1[20], SW2[20], SW3[20], SW4[20];

static byte CFG_READ = (byte) 0xEC;	// '11101100'   Configuración de lectura para DS2407
static byte CFG_WRITE = (byte) 0x8C;	// '10001100'  Configuración de Escritura para DS2407
static byte CFG_READPULSE = (byte) 0xC8;	// '11001000'  Configuración de lectura de Pulso de conversion para DS2407

static int C1 = 0, C2 = 0, C3 = 0, C4 = 0, C5 = 0, C6 = 0;
static int D1 = 0, D2 = 0;

static void SendPressureBit (int portnum, bool i);
static bool CheckConversion (int portnum);


//----------------------------------------------------------------------
//      SUBROUTINE - WriteSwitch12 - Adapted from SetSwitch12 Dallas code
//
//  This routine sets the channel state of the specified DS2406
//
// 'portnum'     - number 0 to MAX_PORTNUM-1.  This number was provided to
//                 OpenCOM to indicate the port number.
// 'SerialNum'   - Serial Number of DS2406 to set the switch state
// 'st'          - Config byte to write
//
//  Returns: TRUE(1)  State of DS2406 set and verified
//           FALSE(0) could not set the DS2406, perhaps device is not
//                    in contact
//
int WriteSwitch12 (int portnum, uchar * SerialNum, ushort st)
{
    int rt = FALSE;
    uchar send_block[30];
    int send_cnt = 0;

    setcrc16 (portnum, 0);

	// set the device serial number to the counter device
    owSerialNum (portnum, SerialNum, FALSE);

	// access the device
    if (owAccess (portnum))
    {
            // create a block to send that reads the counter
            // write status command
        send_block[send_cnt++] = 0x55;
        docrc16 (portnum, 0x55);
        
            // address of switch state
        send_block[send_cnt++] = 0x07;
        docrc16 (portnum, 0x07);
        send_block[send_cnt++] = 0x00;
        docrc16 (portnum, 0x00);

        send_block[send_cnt++] = (uchar) st;
        docrc16 (portnum, st);

            // read CRC16
        send_block[send_cnt++] = 0xFF;
        send_block[send_cnt++] = 0xFF;
        
            // now send the block
        if (owBlock (portnum, FALSE, send_block, send_cnt))
        {
                // perform the CRC16 on the last 2 bytes of packet
            docrc16 (portnum, send_block[send_cnt - 2]);
            
                // verify crc16 is correct
            if (docrc16 (portnum, send_block[send_cnt - 1]) ==
                0xB001)
                rt = TRUE;
        }
    }

	// return the result flag rt
    return rt;
}

// Open (i.e. o/p goes high) the channel A switch
static int OpenA (int portnum, uchar * SerialNum)
{
    byte I = 0XFF, res = 0;
    
    int Ind = 5;
    do
    {
        owSerialNum (portnum, SerialNum, FALSE);
        I = ReadSwitch12 (portnum, false);
        WriteSwitch12 (portnum, SerialNum, I | 0X20);
        I = ReadSwitch12 (portnum, false);
        Ind--;
        res = (I & 0XDF);
        if (res != 0)
            return true;
    }
    while (Ind > 0);
    return (Ind > 0);
}

// Open (i.e. o/p goes high) the channel B switch
static int OpenB (int portnum, uchar * SerialNum)
{
    byte I = 0XFF, res = 0;
    
    int Ind = 5;
    do
    {
        owSerialNum (portnum, SerialNum, FALSE);
        I = ReadSwitch12 (portnum, false);
        WriteSwitch12 (portnum, SerialNum, I | 0X40);
        I = ReadSwitch12 (portnum, false);
        Ind--;
        res = (I & 0XBF);
        if (res != 0)
            return true;
    }
    while (Ind > 0);
    return (Ind > 0);
}

// Open B (data in/out) on both reader and writer
static bool OpenPIOS_B (int portnum)
{
    if (!OpenB (portnum, SwitchSN[reader]))
        return FALSE;
    if (!OpenB (portnum, SwitchSN[writer]))
        return FALSE;
    return TRUE;
}

// Open A (clock) on both reader and writer
static bool OpenPIOS_A (int portnum)
{
    if (!OpenA (portnum, SwitchSN[reader]))
        return FALSE;
    if (!OpenA (portnum, SwitchSN[writer]))
        return FALSE;
    
    return TRUE;
}

// Send a Channel Access command sequence
static int ChannelAccess (int portnum, uchar * SerialNum,
                          byte AChanC1, byte AChanC2, byte * ACInfo)
{
    owSerialNum (portnum, SerialNum, FALSE);
    if (!owAccess (portnum))
    {
        fprintf (stderr, "owAccess failed in ChannelAccess\n");
        return false;
    }
    owTouchByte (portnum, 0XF5);
    owTouchByte (portnum, AChanC1);
    owTouchByte (portnum, AChanC2);
    *ACInfo = owTouchByte (portnum, 0XFF);
    return true;
}

//---------------------------------------------------------------------------
static int ConfigWrite (int portnum)
{
    byte ACInfo = 0;
    return ChannelAccess (portnum, SwitchSN[writer], CFG_WRITE, 0xFF,
                          &ACInfo);
}

//---------------------------------------------------------------------------
static int ConfigRead (int portnum)
{
    byte ACInfo = 0;
    return ChannelAccess (portnum, SwitchSN[reader], CFG_READ, 0xFF,
                          &ACInfo);
}

//---------------------------------------------------------------------------
static int ConfigReadPulse (int portnum)
{
    byte ACInfo = 0;
    return ChannelAccess (portnum, SwitchSN[reader], CFG_READPULSE, 0xFF,
                          &ACInfo);
}

static bool WritePressureSensor (int portnum, char *Cmd)
{
    bool rslt = false;
    int lng, i;
    lng = strlen (Cmd);
    if (ConfigWrite (portnum))
    {
        for (i = 0; i < lng; i++)
        {
            if (Cmd[i] == '0')
                SendPressureBit (portnum, false);
            else
                SendPressureBit (portnum, true);
        }
        SendPressureBit (portnum, false);
        rslt = true;
    }
    return rslt;
}

static void SendPressureBit (int portnum, bool i)
{
    if (i)
        owTouchByte (portnum, 0x0e);
    else
        owTouchByte (portnum, 0x04);
}

    /******************************************************/

static bool ReadPressureBit (int portnum)
{
    int res = 0;
    res = owReadByte (portnum);
    res &= 0x80;
    owWriteByte (portnum, 0xfa);
    return (res != 0);
}

/******************************************************/
static char * ReadPressureSensor (int portnum, byte CountBit)
{
    static char StringBuffer[64];
    int i;

    strcpy (StringBuffer, "");
    if (ConfigRead (portnum))
    {
        for (i = 0; i < CountBit; i++)
        {
            if (ReadPressureBit (portnum))
                strcat (StringBuffer, "1");
            else
                strcat (StringBuffer, "0");
        }
    }
    else
    {
        fprintf (stderr, "ConfigRead failed\n");
    }
    return StringBuffer;
}

/******************************************************/
static bool PressureReset (int portnum)
{
    bool rsl = false;
    if (!OpenPIOS_A (portnum))
        return false;	// Inspired by AAG C++ version
    if (!ConfigWrite (portnum))
        return false;	// Inspired by AAG C++ version
    rsl = WritePressureSensor (portnum, SEC_RESET);
    return rsl;
}

/******************************************************/
static char *ReadSensorValueStr (int portnum, char *cmd, char *Lect)
{
    Lect[0] = '\0';
    
    if (!OpenPIOS_A (portnum))
        return FALSE;
    if (WritePressureSensor (portnum, cmd))
    {
        if (!OpenPIOS_A (portnum))
            return FALSE;
        strcpy (Lect, ReadPressureSensor (portnum, (byte) 16));
       OpenPIOS_B (portnum);
    }
    else
    {
        fprintf (stderr, "WritePressureSensor failed\n");
    }
    return Lect;
}

/******************************************************/
static int ReadSensorValue (int portnum, char *Comm)
{
    char Lect[30];
    int Res = 0;
    int i = 0;

    Lect[0] = 0;    
    if (!PressureReset (portnum))
        return 0;
    if (!WritePressureSensor (portnum, Comm))
        return 0;
    if (!CheckConversion (portnum))
        return 0;
    if (!OpenPIOS_A (portnum))
        return 0;
    strcpy (Lect, ReadPressureSensor (portnum, 16));
    
    if (!OpenPIOS_B (portnum))
        return 0;
    
    if (strlen (Lect) != 16)
        return 0;
    for (i = 0; i < 16; i++)
    {
        Res = (Res << 1);
        if (Lect[i] == '1')
            ++Res;
    }
    return Res;
}

static bool CheckConversion (int portnum)
{
    int i;
    if (!ConfigReadPulse (portnum))
    {
        fprintf (stderr,
                 "ConfigReadPulse failed in CheckConversion\n");
        return false;
    }

    for (i = 0; i < 100; i++)
        if (!owTouchBit (portnum, 1))
            break;
    return (i < 100);
}

/******************************************************/

static bool ReadSensor_Calibration (int portnum)
{
    int uno = 1, im = 0, Count = 0;
    int i = 0;
    int Lect = false;
    char SW11[20] = "0", SW22[20] = "0", SW33[20] = "0", SW44[20] = "0";

    do
    {
        if (!PressureReset (portnum))
            return false;
        ReadSensorValueStr (portnum, SEC_READW1, SW1);
        ReadSensorValueStr (portnum, SEC_READW2, SW2);
        ReadSensorValueStr (portnum, SEC_READW3, SW3);
        ReadSensorValueStr (portnum, SEC_READW4, SW4);

        if (strlen (SW1) != 16)
            return false;
        if (strlen (SW2) != 16)
            return false;
        if (strlen (SW3) != 16)
            return false;
        if (strlen (SW4) != 16)
            return false;
        
        Lect = ((strcmp (SW1, SW11) == 0)
                && (strcmp (SW2, SW22) == 0))
            && ((strcmp (SW3, SW33) == 0)
                && (strcmp (SW4, SW44) == 0));
        
        strcpy (SW11, SW1);
        strcpy (SW22, SW2);
        strcpy (SW33, SW3);
        strcpy (SW44, SW4);
        
        Count++;
    }
    while ((Count < 5) && (Lect == false));

    C1 = 0;
    C2 = 0;
    C3 = 0;
    C4 = 0;
    C5 = 0;
    C6 = 0;
    
    for (i = 14; i >= 0; i--)
    {
        if (SW1[i] == '1')
        {
            C1 = C1 | (uno << (14 - i));
        }
    }

    for (i = 9; i >= 0; i--)
    {
        if (SW2[i] == '1')
            C5 = C5 | (uno << (9 - i));
    }
    if (SW1[15] == '1')
        C5 = C5 | (uno << (10));
    
    for (i = 15; i >= 10; i--)
    {
        if (SW2[i] == '1')
            C6 = C6 | (uno << (15 - i));
    }
    
    for (i = 9; i >= 0; i--)
    {
        if (SW3[i] == '1')
            C4 = C4 | (uno << (9 - i));
    }
    
    for (i = 9; i >= 0; i--)
    {
        if (SW4[i] == '1')
            C3 = C3 | (uno << (9 - i));
    }
    
    im = 0;
    for (i = 15; i >= 10; i--)
    {
        if (SW4[i] == '1')
            C2 = C2 | (uno << (im));
        im++;
    }
    
    for (i = 15; i >= 10; i--)
    {
        if (SW3[i] == '1')
            C2 = C2 | (uno << (im));
        im++;
    }
    return true;
}

/******************************************************/

int ReadPressureValues (int portnum, float *temp, float *pressure)
{
	double DT, OFF, SENSE, X;
	D1 = ReadSensorValue (portnum, SEC_READD1);
	D2 = ReadSensorValue (portnum, SEC_READD2);
	DT = D2 - ((8 * C5) + 20224.0);
	OFF = C2 * 4 + (((C4 - 512) * DT) / 4096);
	SENSE = 24576 + C1 + ((C3 * DT) / 1024);
	X = ((SENSE * (D1 - 7168)) / 16384) - OFF;
	*pressure = 250.0 + X / 32;
	*temp = 20.0 + ((DT * (C6 + 50)) / 10240);
	return true;
}

static int find_press_roles(int portnum, u_char *serno)
{
    int found = 0, fl_len;
    short hnd;
    FileEntry fe ;
    uchar buf[100];
    int j;
    
    for(j =0; j < 2 && found == 0; j++)
    {
        reader = writer = -1;
        memcpy(SwitchSN[0], serno, sizeof(SwitchSN[0]));    
        owSerialNum (portnum, SwitchSN[0], FALSE);
        memset(&fe, 0, sizeof(fe));
        memcpy(fe.Name, "8570", 4) ;

        found = owOpenFile(portnum, SwitchSN[0], &fe, &hnd) ;
        if (found)
        {
            int info;
            owReadFile(portnum, SwitchSN[0], hnd, buf, 99, &fl_len) ;
        
            owSerialNum (portnum, SwitchSN[0], false);
            owCloseFile(portnum, SwitchSN[0], hnd);        
            
            info = ReadSwitch12(portnum, false) ;
            if (info == -1)
            {
                fprintf(stderr, "Unable to read info byte\n") ;
                found = 0;
            }
            else
            {
                if ((info & 0x80) == 0)
                {
                    reader=0;
                    writer=1;
                }
                else
                {
                    reader=1;
                    writer=0;
                }
                memcpy(SwitchSN[1], buf, sizeof(SwitchSN[1]));
            }
        }
        
    }
    return found;
}

int Init_Pressure(int portnum, u_char *serno)
{
    int iscalib = false;
    if(find_press_roles(portnum,serno))
    {
        if (ReadSensor_Calibration (portnum))
        {
            iscalib = true;
        }
    }
    return iscalib;
}


