/*
 *  Copyright (c) 1996-2007 Digi International Inc., All Rights Reserved
 *
 *  This software contains proprietary and confidential information of Digi
 *  International Inc.  By accepting transfer of this copy, Recipient agrees
 *  to retain this software in confidence, to prevent disclosure to others,
 *  and to make no use of this software other than that for which it was
 *  delivered.  This is an unpublished copyrighted work of Digi International
 *  Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly
 *  prohibited.
 *
 *  Restricted Rights Legend
 *
 *  Use, duplication, or disclosure by the Government is subject to
 *  restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in
 *  Technical Data and Computer Software clause at DFARS 252.227-7031 or
 *  subparagraphs (c)(1) and (2) of the Commercial Computer Software -
 *  Restricted Rights at 48 CFR 52.227-19, as applicable.
 *
 *  Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
 *
 * Description.
 * =======================================================================
 * applicationStart() is the entry point for user applications.  This
 * This function will be called after the kernel has started and the
 * TCP/IP stack has loaded.  applicationTcpDown() is called periodically
 * after the kernel has started while the system is waiting for the
 * TCP/IP stack to start.  
 *                                                                        
 *                                                                        
 * Edit Date/Ver   Edit Description
 * ==============  ===================================================
 *
 *
 */
#include <stdio.h>
#include <stdlib.h>
#include <tx_api.h>
#include "appconf.h"
#include <onewire_api.h>
#include <bsp_api.h>
#include <netosIo.h>
#include <boardparams.h>

#define APP_1WIRE_INTF "/1-wire/0"

/*
 * Set this to 1 to run the manufacturing burn in tests.
 */
int APP_BURN_IN_TEST = 0; 

#define DS2431_ROW_LENGTH           8
#define DS2431_DATA_ROWS            16
#define DS2431_DATA_SIZE            (DS2431_DATA_ROWS * DS2431_ROW_LENGTH)
#define DS2431_COUNT_ADDR           (DS2431_DATA_SIZE - DS2431_ROW_LENGTH)
#define DS2431_CRC16_VALUE          0xB001
#define DS2431_SKIP_ROM             0xCC
#define DS2431_WRITE_SCRATCHPAD     0x0F
#define DS2431_READ_SCRATCHPAD      0xAA
#define DS2431_COPY_SCRATCHPAD      0x55
#define DS2431_READ_MEMORY          0xF0

#define MAC_ADDR_SIZE   6

typedef struct devInfo1Wire_t
{
    unsigned char  mac[MAC_ADDR_SIZE];
    unsigned short count;  // stores number of times this sample is executed
}devInfo1Wire_t;

static unsigned short owcrc16;
static short oddparity[16] = { 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0 };

static void setcrc16(unsigned short reset)
{
   owcrc16 = reset;
}

static unsigned short getcrc16(void)
{
   return owcrc16;
}

static void docrc16(unsigned short cdata)
{
   cdata = (cdata ^ (owcrc16 & 0xff)) & 0xff;
   owcrc16 >>= 8;

   if (oddparity[cdata & 0xf] ^ oddparity[cdata >> 4])
     owcrc16 ^= 0xc001;

   cdata <<= 6;
   owcrc16 ^= cdata;
   cdata <<= 1;
   owcrc16 ^= cdata;
}

/*
 *
 *  Function: void applicationTcpDown (void)
 *
 *  Description:
 *
 *      This routine will be called by the NET+OS root thread once every 
 *      clock tick while it is waiting for the TCP/IP stack to come up.  
 *      This function can increment a counter everytime it's called to 
 *      keep track of how long we've been waiting for the stack to start.
 *      If we've been waiting too long, then this function can do something
 *      to handle the error.  
 *
 *      This function will not be called once the stack has started.
 *
 *  Parameters:
 *
 *      none
 *
 *  Return Values:
 *
 *      none
 *
 */

void applicationTcpDown (void)

{
    static int ticksPassed = 0;

    ticksPassed++;
/*
 * Code to handle error condition if the stack doesn't come up goes here.
 */
}

/*
 * Reads 8 bytes of data from the scratchpad in standard speed. Returns 0 on 
 * success.
 */
static int readScratchpad(int handle, unsigned short *addr, unsigned char *es, char *data)
{
    unsigned presence;
    int i;
    char buff[DS2431_ROW_LENGTH + 8];

    if (ioctl(handle, NA_1WIRE_RESET, (char *)&presence) < 0)
    {
        printf("writeScratchpad: ioctl failed, Errno[%d]\n", getErrno());
        return -1;
    }

    if (presence == 0)
    {
        printf("readScratchpad: No device found\n");
        return -1;    
    }

    buff[0] = DS2431_SKIP_ROM;
    buff[1] = DS2431_READ_SCRATCHPAD;    
    if (write(handle, buff, 2) != 2)
    {
        printf("readScratchpad: write failed, Errno[%d]\n", getErrno());
        return -1;
    }

    /* Read Address, E/S, data and CRC */    
    if (read(handle, buff, DS2431_ROW_LENGTH + 5) != (DS2431_ROW_LENGTH + 5))
    {
        printf("readScratchpad: read failed, Errno[%d]\n", getErrno());
        return -1;
    }
    
    /* check CRC */
    setcrc16(0);
    docrc16(0xAA);
    for (i = 0; i < (DS2431_ROW_LENGTH + 5); i++)
        docrc16(buff[i]);
            
    if (getcrc16() != DS2431_CRC16_VALUE)
    {
        printf("readScratchpad: CRC Error!!\n");
        return -1;
    }

    *addr = buff[1] << 8 | buff[0];
    *es   = buff[2];
    
    if (data)
        memcpy(data, &buff[3], DS2431_ROW_LENGTH);
        
    return 0;
}

/*
 * Writes 8 bytes of data to the scratchpad in standard speed. Returns 0 on
 * success.
 */
static int writeScratchpad(int handle, unsigned short addr, char *data)
{
    unsigned presence;
    int i = 0;
    char buff[DS2431_ROW_LENGTH + 8], *ptr;

    if (ioctl(handle, NA_1WIRE_RESET, (char *)&presence) < 0)
    {
        printf("writeScratchpad: ioctl failed, Errno[%d]\n", getErrno());
        return -1;
    }

    if (presence == 0)
    {
        printf("writeScratchpad: No device found\n");
        return -1;    
    }

    setcrc16(0);

    ptr = buff;
    *ptr++ = DS2431_SKIP_ROM;
    *ptr++ = DS2431_WRITE_SCRATCHPAD;
    *ptr++ = addr & 0xFF;
    *ptr++ = addr>>8 & 0xFF;
    memcpy(ptr, data, DS2431_ROW_LENGTH);    

    for (i = 1; i < (DS2431_ROW_LENGTH + 4); i++)
        docrc16(buff[i]);
            
    if (write(handle, buff, i) != i)
    {
        printf("writeScratchpad: write failed, Errno[%d]\n", getErrno());
        return -1;
    }

    ptr = buff;
    /* Read CRC */ 
    if (read(handle, ptr, 2) != 2)
    {
        printf("writeScratchpad: read failed, Errno[%d]\n", getErrno());
        return -1;
    }
    
    docrc16(*ptr++);
    docrc16(*ptr);
    
    /* check CRC */
    if (getcrc16() != DS2431_CRC16_VALUE)
    {
        printf("writeScratchpad: CRC Error!!\n");
        return -1;
    }
    
    return 0;
}

/*
 * Copies 8 bytes of data stored in scratchpad to the memory in standard 
 * speed. Returns 0 on success.
 */
static int copyScratchpad(int handle, unsigned short addr, char es)
{
    unsigned presence;
    char buff[8];

    if (ioctl(handle, NA_1WIRE_RESET, (char *)&presence) < 0)
    {
        printf("copyScratchpad: ioctl failed, Errno[%d]\n", getErrno());
        return -1;
    }

    if (presence == 0)
    {
        printf("copyScratchpad: No device found\n");
        return -1;    
    }
    
    buff[0] = DS2431_SKIP_ROM;
    buff[1] = DS2431_COPY_SCRATCHPAD;
    buff[2] = addr & 0xFF;
    buff[3] = (addr >> 8) & 0xFF;
    buff[4] = es;
    if (write(handle, buff, 5) != 5)
    {
        printf("copyScratchpad: write failed, Errno[%d]\n", getErrno());
        return -1;
    }

    /* wait for write to complete */
    tx_thread_sleep(NS_MILLISECONDS_TO_TICKS(20));
    
    if (read(handle, buff, 4) != 4)
    {
        printf("copyScratchpad: read failed, Errno[%d]\n", getErrno());
        return -1;
    }

    if (!((buff[3] == 0xAA) || (buff[3] == 0x55)))
    {
        printf("copyScratchpad: copy status 0x%X\n", *(unsigned *)buff);
        return -1;
    }

    return 0;
}

/*
 * Reads requested bytes of data from the memory. Returns 0 on success.
 */
static int readMemory(int handle, unsigned short addr, char *data, int length)
{
    unsigned presence;
    char buff[8];

    if (ioctl(handle, NA_1WIRE_RESET, (char *)&presence) < 0)
    {
        printf("readMemeory: ioctl error Errno[%d]\n", getErrno());
        return -1;
    }

    if (presence == 0)
    {
        printf("readMemeory: No device found\n");
        return -1;    
    }
    
    buff[0] = DS2431_SKIP_ROM;
    buff[1] = DS2431_READ_MEMORY;
    buff[2] = addr & 0xFF;
    buff[3] = (addr >> 8) & 0xFF;
    if (write(handle, buff, 4) != 4)
    {
        printf("readMemory: write error Errno[%d]\n", getErrno());
        return -1;
    }
    /* read memory from the given address */
    if (read(handle, data, length) != length)
    {
        printf("readMemory: read failed Errno[%d]\n", getErrno());
        return -1;
    }
        
    if (ioctl(handle, NA_1WIRE_RESET, (char *)&presence) < 0)
    {
        printf("readMemory: ioctl error Errno[%d]\n", getErrno());
        return -1;
    }

    return 0;
}

/*
 * Writes the requested bytes of data to the memory using scratchpad. Returns
 * 0 on success.
 */
static int writeMemory(int handle, unsigned short addr, char *data, int length)
{
    unsigned short spAddr;
    unsigned char spEs = 0;
    char buff[12];

    if ((addr & (DS2431_ROW_LENGTH - 1)) || (length % DS2431_ROW_LENGTH))
    {
        printf("writeMemory: Invalid address or length\n");
        return -1;
    }

    while (length > 0)
    {    
        if (writeScratchpad(handle, addr, data) < 0)
        {
            printf("writeMemory: writeScratchpad error\n");
            return -1;
        }    
    
        /* reset data and address */
        if (readScratchpad(handle, &spAddr, &spEs, buff) < 0)
        {
            printf("writeMemory: readScratchpad error\n");
            return -1;
        }
        
        if ((spAddr != addr) || (spEs != 0x7))
        {
            printf("writeMemory: Authorization error\n");
            return -1;
        }
        
        if (copyScratchpad(handle, spAddr, spEs) < 0)
        {
            printf("writeMemory: copyScratchpad error\n");
            return -1;
        }
        
        length -= DS2431_ROW_LENGTH;
        addr += DS2431_ROW_LENGTH;
        data += DS2431_ROW_LENGTH;            
    }

    return 0;
}

/*
 *
 *  Function: void applicationStart (void)
 *
 *  Description:
 *
 *      This routine is responsible for starting the user application.  It should 
 *      create any threads or other resources the application needs.
 *
 *      ThreadX, the NET+OS device drivers, and the TCP/IP stack will be running
 *      when this function is called.
 *
 *  Parameters:
 *
 *      none
 *
 *  Return Values:
 *
 *      none
 *
 */
void applicationStart (void)
{
    int handle;
    devInfo1Wire_t devInfo;
    BYTE macAddr[MAC_ADDR_SIZE];

    if (customizeGetMACAddress(macAddr) != BP_SUCCESS)
    {
        printf("Failed to read unit's ethernet MAC address\n");
        return;
    }
    
    if ((handle = open(APP_1WIRE_INTF, O_STANDARD)) < 0)
    {
        printf("Open failed\n");
        return;
    }

    if (readMemory(handle, 0, (char *)&devInfo, sizeof devInfo) < 0)
    {
        printf("readMemory failed\n");
        goto done;
    }

    if (memcmp(devInfo.mac, macAddr, MAC_ADDR_SIZE))
    {
        memcpy(devInfo.mac, macAddr, MAC_ADDR_SIZE);
    
        devInfo.count = 1;
    }
    else
        devInfo.count++;

    if (writeMemory(handle, 0, (char *)&devInfo, sizeof devInfo) < 0)
    {
        printf("writeMemory failed\n");
        goto done;
    }

    printf("Program executed on %02X:%02X:%02X:%02X:%02X:%02X  %d times!\n",
            devInfo.mac[0], devInfo.mac[1], devInfo.mac[2], devInfo.mac[3], 
            devInfo.mac[4], devInfo.mac[5], devInfo.count);    
    
    done:
    close(handle);     
}



