/* 
 * This software is licensed under terms of the GNU GENERAL PUBLIC LICENSE
 *
 * This software 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 3 of the License, or
 * (at your option) any later version.
 *
 * This software  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 should have received a copy of the GNU General Public License
 * along with this software.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Source code is available at http://ppcenter.free.fr
 */

#include "parlink.h"

/* Extended comms functions. */
#include "../../satcom_lib/sc_common.h"

#include <windows.h>
#include <time.h>


/**
 * Synchronize with PAR.
 * Returns
 *  0 when no error.
 *  1 when synchronisation timeouted.
**/
int parx_sync(void)
{
    scl_log(SCLT_DLLPG, 7, "[parx_sync]Start");

    unsigned long i = 0;
    while (1)
    {
        if(i > _loop_max)
        {
            scl_log(SCLT_DLLPG, 0, "[parx_sync]Synchronize timeout ! (loop_max=%d)", _loop_max);
            return 1;
        }
        i++;

        if (par_exchange_byte('V') == SC_VERSION)
        {
            par_exchange_byte(SC_VERSION);
            break;
        }
    }
    scl_log(SCLT_DLLPG, 7, "[parx_sync]OK");
    return 0;
}

unsigned long _wait_delay = 1000;
void parx_wait_autoreset(void)
{
    scl_log(SCLT_DLLPG, 0, "[parx_wait_autoreset]Sleep (%d msec) STT", _wait_delay);

    // unsigned long start_time = timeGetTime();
    // while(1)
    // {
    //     if((timeGetTime() - start_time) > _wait_delay)
    //     {
    //         scl_log(SCLT_DLLPG, 0, "[parx_wait_autoreset]Sleep (%d msec) END", _wait_delay);
    //         _wait_delay = _wait_delay*2;
    //         if(_wait_delay > 15000) _wait_delay = 15000;
    //         return;
    //     }
    // }

    Sleep(_wait_delay);
    scl_log(SCLT_DLLPG, 0, "[parx_wait_autoreset]Sleep (%d msec) END", _wait_delay);
    _wait_delay = _wait_delay*2;
    if(_wait_delay > 8000) _wait_delay = 8000;

}



/* Buffer containing packet data to send. */
unsigned char _packet[256];



/**
 * Set packet data (automatically compute its CRC).
 * Need at most 256 bytes of data in the input/output buffer.
 * Return packet total length.
**/
unsigned short parx_set_packet(unsigned char command, unsigned long address, unsigned long length, unsigned long param, unsigned char* buffer, 
                               unsigned char* packet)
{
    int n;

    n=0;
    switch(command)
    {
    /* Set base address and base length. */
    case('A'):
        packet[n++] = command;
        /* Base address. */
        packet[n++] = (address >> 24) & 0xFF;
        packet[n++] = (address >> 16) & 0xFF;
        packet[n++] = (address >>  8) & 0xFF;
        packet[n++] = (address      ) & 0xFF;
        /* Base length. */
        packet[n++] = (length       ) & 0xFF;
        break;

    /* Read data from Saturn. */
    case('R'):
        packet[n++] = command;
        /* Muliplicator. */
        packet[n++] = length & 0xFF;
        break;

    /* Write data to Saturn. */
    case('W'):
        packet[n++] = command;
        /* Muliplicator. */
        packet[n++] = length & 0xFF;
        /* First byte value. */
        packet[n++] = param  & 0xFF;
        break;

    /* Execute Saturn code. */
    case('X'):
        packet[n++] = command;
        /* Temporary area start address. */
        packet[n++] = (address >> 24) & 0xFF;
        packet[n++] = (address >> 16) & 0xFF;
        packet[n++] = (address >>  8) & 0xFF;
        packet[n++] = (address      ) & 0xFF;
        /* Code length. */
        packet[n++] = (length  >> 24) & 0xFF;
        packet[n++] = (length  >> 16) & 0xFF;
        packet[n++] = (length  >>  8) & 0xFF;
        packet[n++] = (length       ) & 0xFF;
        /* Execution address. */
        packet[n++] = (param   >> 24) & 0xFF;
        packet[n++] = (param   >> 16) & 0xFF;
        packet[n++] = (param   >>  8) & 0xFF;
        packet[n++] = (param        ) & 0xFF;
        break;

    /* Read CRC from Saturn. */
    case('S'):
        packet[n++] = command;
        /* Muliplicator. */
        packet[n++] = length & 0xFF;
        break;

    /* Set polling loop parameters. */
    case('L'):
        packet[n++] = command;
        packet[n++] = (param        ) & 0xFF;
        break;

    /* Get temporary execute address. */
    case('Y'):
        packet[n++] = command;
        break;

    /* Get RS code (for `receive' function). */
    case('C'):
        packet[n++] = command;
        break;

    /* Send RS code (for `send' function). */
    case('D'):
        packet[n++] = command;

        /* Re-send 16 bits CRC. */
        packet[n++] = (address  >> 8) & 0xFF;
        packet[n++] = (address      ) & 0xFF;

        /* Supposed recovery target position. */
        packet[n++] = (length       ) & 0xFF;

        /* RS code + 8 bits CRC (SC_RSLEN (-> 4) bytes). */
        memcpy((void*)(packet+n), (void*)buffer, SC_RSLEN);
        n += SC_RSLEN;
        break;

    /* Send simple recovery data (for `send' function). */
    case('F'):
        packet[n++] = command;

        /* Re-send 16 bits CRC. */
        packet[n++] = (address  >> 8) & 0xFF;
        packet[n++] = (address      ) & 0xFF;

        /* Supposed recovery target position. */
        packet[n++] = (length       ) & 0xFF;

        /* Last byte value. */
        packet[n++] = (param        ) & 0xFF;
        break;


    /* Compress data to internal buffer. */
    case('Z'):
        packet[n++] = command;
        /* Source data start address. */
        packet[n++] = (address >> 24) & 0xFF;
        packet[n++] = (address >> 16) & 0xFF;
        packet[n++] = (address >>  8) & 0xFF;
        packet[n++] = (address      ) & 0xFF;
        /* Source data length. */
        packet[n++] = (length  >>  8) & 0xFF;
        packet[n++] = (length       ) & 0xFF;
        break;

    /* Decompress data from internal buffer. */
    case('U'):
        packet[n++] = command;
        /* Source data length. */
        packet[n++] = (length  >>  8) & 0xFF;
        packet[n++] = (length       ) & 0xFF;
        /* Destination data start address. */
        packet[n++] = (address >> 24) & 0xFF;
        packet[n++] = (address >> 16) & 0xFF;
        packet[n++] = (address >>  8) & 0xFF;
        packet[n++] = (address      ) & 0xFF;
        break;

    /* Get (de)compression return value. */
    case('G'):
        packet[n++] = command;
        break;

    /* Get (de)compression buffer address. */
    case('K'):
        packet[n++] = command;
        break;


    /* Unknown command. */
    default:
        scl_log(SCLT_DLLPG, 0, "[parx_set_packet]ERROR: Unknown packet command (%c) !", command);
        return 0;

    }

    /* compute CRC. */
    int i;
    unsigned short crc16 = crc16_start();
    for(i=0; i<n; i++)
    {
        crc16 = crc16_update(crc16, packet[i]);
        //scl_log(SCLT_DLLPG, 8, "[parx_set_packet]CRC16[%02X] = %04X", packet[i], crc16);
    }
    /* Add CRC to packet. */
    packet[n++] = crc16 >> 8;
    packet[n++] = crc16 & 0xFF;

    return n;
}



/**
 * Transfer statistics-related.
 * Yeah, it uses ONLY global variables. It is bad. I will go to hell.
**/
unsigned long _pkt_total = 0;
unsigned long _rs_total = 0;
unsigned long _rs_error = 0;

/* Dummy function in order to avoid "not a number" exception in float. */
float fdiv(int num, int denum)
{
    if(denum == 0) return (float)num;
    return (((float)num)/denum);
}
void parx_display_trans_stats(unsigned long data_length, unsigned long comp_length)
{
    /* Display transmission statistics. */
    unsigned long bcount = par_get_exchanged_bytes();
    unsigned long exec_time = exec_time_get(); /* Interface common function, see sc_dllcmn.c */
    scl_log(SCLT_DLLPG, 1, "Transfer Time    : %7d msec(s)", exec_time);
    scl_log(SCLT_DLLPG, 1, "Data Size        : %7d Bytes [%7.3f KB/s] (Virtual speed)", 
                data_length, 
                fdiv(data_length, exec_time));
    scl_log(SCLT_DLLPG, 1, "Data Size (COMP) : %7d Bytes [%7.3f KB/s] (Transfer speed)", 
                comp_length, 
                fdiv(comp_length, exec_time));
    scl_log(SCLT_DLLPG, 1, "Exchanged bytes  : %7d Bytes [%7.3f KB/s] (Device speed)", 
                bcount, 
                fdiv(bcount, exec_time));
    scl_log(SCLT_DLLPG, 1, "Overhead         : %7d Bytes (%f %%)", 
                (bcount-comp_length), 
                fdiv(100*(bcount-comp_length), comp_length));
    scl_log(SCLT_DLLPG, 1, "Packet count     : %7d", _pkt_total);
    scl_log(SCLT_DLLPG, 1, "Packet errors    : %7d (%f %%)", _rs_total, 
                fdiv(100*_rs_total, _pkt_total));
    scl_log(SCLT_DLLPG, 1, "RS errors        : %7d (%f %%)", _rs_error, 
                fdiv(100*_rs_error, _rs_total));
}




/**
 * Send packet to ARP.
 * Need at most 256 bytes of data in the input/output buffer.
 * Return 0 on success.
**/
unsigned short parx_send_packet(unsigned char command, unsigned long address, unsigned long length, unsigned long param, unsigned char* buffer, 
                                unsigned char* packet, unsigned char next_byte, unsigned char* answer_byte)
{
    scl_log(SCLT_DLLPG, 8, "[parx_send_packet]->start(command=%c, address=0x%08X, length=%d, param=%d(0x%08X), ...)", command, address, length, param, param);

    int n, i, j;
    if(answer_byte != NULL)
    {
        *answer_byte = 0;
    }

    /* Update packet count (for statistics display). */
    _pkt_total++;

    for(j=0; j<_checksum_retry; j++)
    {
        n = parx_set_packet(command, address, length, param, buffer, packet);
        if(n == 0)
        { /* Unknown command. */
            return 1;
        }
        
        // Need to re-review the packet sending proc

        /* (Re)-synchronize with PAR */
        unsigned char ret;

        scl_log(SCLT_DLLPG, 8, "[parx_send_packet]Fsync STT");
        par_exchange_byte(SC_VERSION);
        ret = par_exchange_byte(packet[0]);
        if(ret == '>')
        {
            scl_log(SCLT_DLLPG, 8, "[parx_send_packet]Fsync OK");
            packet[0] = ret;
            ret = 0;
        }
        else
        {
            scl_log(SCLT_DLLPG, 8, "[parx_send_packet]Fsync NG");
            //if(j >= (_checksum_retry-4))
            //{
            //    parx_wait_autoreset();
            //}
            ret = parx_sync();
            packet[0] = par_exchange_byte(packet[0]);
        }
        if(ret == 0)
        {
            //n++;
            unsigned char a;
            for(i=1; i<n; i++)
            {
                a = packet[i];
                packet[i] = par_exchange_byte(packet[i]);
            }

            /* Send byte directly after the packet data, and check its answer. */
            packet[i] = par_exchange_byte(next_byte);
            if((packet[i] == '<') || (packet[i] == '*'))
            {
                if(answer_byte != NULL)
                {
                    *answer_byte = packet[i];
                }
                scl_log(SCLT_DLLPG, 9, "[parx_send_packet] Packet send OK");
                return 0;
            }
            else
            {
                scl_log(SCLT_DLLPG, 9, "[parx_send_packet] Packet send NG !");
                //if(j >= (_checksum_retry-4))
                //{
                //    parx_wait_autoreset();
                //}
            }
        }
    }
    return 1;
}




/**
 * Enable/disable transmission.
 * Set loop to a value between 0 and 31.
 *  0 -> fast polling (idle), 30 -> very long polling (transfer).
 *  Special value : 31 -> infinite loop polling.
 * Set ret to 1 if you want the Saturn to exit the polling
 *  loop directly after sending the command.
 * Return 0 on success.
**/
/* Special case for the 'L` command. */
/* Format : r00l llll in one byte. */
#define SC_LOOP_IDLE     1
#define SC_LOOP_TRANSFER 16
#define SC_LOOP_INFINITE 31
#define SC_LOOP_PARAMS(_LOOP_, _RETURN_) ( ((_LOOP_)&0x1F) | (((_RETURN_)&0x01)<<7) )
int parx_set_transfer(int loop, int ret)
{
    return parx_send_packet('L', 0/*address(unused)*/, 0/*length(unused)*/, 
                            SC_LOOP_PARAMS(loop/*LOOP*/, ret/*RETURN*/)/*param*/, NULL/*buffer(unused)*/, 
                            _packet, 0/*next byte*/, NULL/*answer_byte*/);
}




/**
 * Note about reading and writing.
 * You first need to send a base address and base length to the ARP.
 * Then, reading/writing/etc is done by sending a multiplicator to the ARP.
 *  - Reading/writing/etc start address is computed as :
 *     address = base_address + multiplicator * base_length.
 *  - Reading/writing/etc operation is performed from address computed above, 
 *    for base_length bytes.
 *
 * The advantage is to reduce overhead : instead of sending address+length
 * (4bytes+1byte) a single byte is sent.
 * Return 0 on success.
**/



/**
 * Set base address and legnth, and, if needed, send it to the Saturn.
 * Set the multiplcator to an appropriate value.
 * Return 0 on success.
**/
int _force_address_update = 0;
unsigned long _last_adr = 0x00000000;
unsigned long _last_len = 0x00000000;
unsigned short parx_set_base_address(unsigned long address, unsigned long length, unsigned char* multiplicator)
{
    int need_update = 0;
    *multiplicator = 0;

    /* Avoid extra case which may crash below. */
    if(length == 0) return 1;

    /* Check whether we need to update base address and length. */
    if(address < _last_adr)
    { /* Multiplicator can't be < 0. */
        need_update = 1;
    }
    else if(length != _last_len)
    { /* Length can't be dynamically changed. */
        need_update = 1;
    }
    else if(((address - _last_adr)%length) != 0)
    { /* Multiplicator can't be a floating point number. */
        need_update = 1;
    }
    else if(((address - _last_adr) / length) > 255)
    { /* Multiplicator can't be > 255. */
        need_update = 1;
    }
    /* Force address update from user request. */
    if(_force_address_update)
    {
        need_update = 1;
        _force_address_update = 0;
    }


    /* If needed, redefine the base address and base length. */
    if(need_update)
    {
        _last_adr = address;
        _last_len = length;
    }
    *multiplicator = (address - _last_adr) / length;


    /* Send the new base address and base length. */
    if(need_update)
    {
        _last_adr = address;
        _last_len = length;
        return parx_send_packet('A', address, length, 0/*param(unused)*/, NULL/*buffer(unused)*/, 
                                _packet, 0/* next byte */, NULL/*answer_byte*/);
    }

    return 0;
}



/**
 * Get temporary address used when uploading data before executing it.
 * Returns
 *    SC_NOERROR            = No error.
 *    SC_ERROR_PKTSYNC      = Lost synchronisation with ARP.
 *    SC_ERROR_PKTCHECKSUM  = CRC mismatch.
**/
int parx_get_tmp_exec_adr(unsigned long* adr)
{
    *adr = 0;

    int j;
    for(j=0; j<_checksum_retry; j++)
    {
        if(parx_send_packet('Y', 0/*address(unused)*/, 0/*length(unused)*/, 0/*first_byte(unused)*/, NULL/*buffer(unused)*/, 
                            _packet, 0/* next byte */, NULL/*answer_byte*/) != 0)
        {
            scl_log(SCLT_DLLPG, 3, "[parx_get_tmp_exec_adr]*** [CompRet]Packet send failure !");
            return SC_ERROR_PKTSYNC;
        }

        unsigned long retdat;
        unsigned short crc16[2];

        /* Read data and generate CRC */
        crc16[0] = crc16_start();
        scl_log(SCLT_DLLPG, 9, "[parx_get_tmp_exec_adr] Read Return data ...");
        unsigned char tmp;
        tmp = par_exchange_byte(0); retdat  = tmp << 24; crc16[0] = crc16_update(crc16[0], tmp);
        tmp = par_exchange_byte(0); retdat |= tmp << 16; crc16[0] = crc16_update(crc16[0], tmp);
        tmp = par_exchange_byte(0); retdat |= tmp <<  8; crc16[0] = crc16_update(crc16[0], tmp);
        tmp = par_exchange_byte(0); retdat |= tmp;       crc16[0] = crc16_update(crc16[0], tmp);

        /* Read CRC */
        tmp = par_exchange_byte(0); crc16[1]  = tmp << 8;
        tmp = par_exchange_byte(0); crc16[1] |= tmp;

        if(crc16[0] == crc16[1])
        { /* CRC OK ! */
            *adr = retdat;
            return SC_NOERROR;
        }

        scl_log(SCLT_DLLPG, 5, "[parx_get_tmp_exec_adr][CompRet] Checksum error ! (0x%04X 0x%04X) (Trycount = %d)", crc16[0], crc16[1], j+1);
        { /* Resync with PAR. */
            scl_log(SCLT_DLLPG, 5, "[parx_get_tmp_exec_adr]Try to synchronize ...");
            if(j >= (_checksum_retry-4))
            {
                parx_wait_autoreset();
                /* Enable transmission */
                parx_set_transfer(SC_LOOP_TRANSFER/*LOOP*/, 0/*RETURN*/);
                /* Avoid the pathologic case where base address is corrupted. */
                _force_address_update = 1;
            }
            if(parx_sync() != 0)
            {
                return SC_ERROR_PKTSYNC;
            }
        }
    }

    return SC_ERROR_PKTCHECKSUM;
}



/**
 * Compress/Decompress function related : Receive (de)compressed data size.
 * Returns
 *    SC_NOERROR            = No error.
 *    SC_ERROR_PKTSYNC      = Lost synchronisation with ARP.
 *    SC_ERROR_PKTCHECKSUM  = CRC mismatch.
**/
int parx_get_cmp_size(unsigned short* size)
{
    *size = 0;

    int j;
    for(j=0; j<_checksum_retry; j++)
    {
        if(parx_send_packet('G', 0/*address(unused)*/, 0/*length(unused)*/, 0/*first_byte(unused)*/, NULL/*buffer(unused)*/, 
                            _packet, 0/* next byte */, NULL/*answer_byte*/) != 0)
        {
            scl_log(SCLT_DLLPG, 3, "[parx_get_cmp_size]*** [CompRet]Packet send failure !");
            return SC_ERROR_PKTSYNC;
        }

        unsigned short retdat;
        unsigned short crc16[2];

        /* Read data and generate CRC */
        crc16[0] = crc16_start();
        scl_log(SCLT_DLLPG, 9, "[parx_get_cmp_size] Read Return data ...");
        unsigned char tmp;
        tmp = par_exchange_byte(0); retdat  = tmp << 8; crc16[0] = crc16_update(crc16[0], tmp);
        tmp = par_exchange_byte(0); retdat |= tmp;      crc16[0] = crc16_update(crc16[0], tmp);

        /* Read CRC */
        tmp = par_exchange_byte(0); crc16[1]  = tmp << 8;
        tmp = par_exchange_byte(0); crc16[1] |= tmp;

        if(crc16[0] == crc16[1])
        { /* CRC OK ! */
            *size = retdat;
            return SC_NOERROR;
        }

        scl_log(SCLT_DLLPG, 5, "[parx_get_cmp_size][CompRet] Checksum error ! (0x%04X 0x%04X) (Trycount = %d)", crc16[0], crc16[1], j+1);
        { /* Resync with PAR. */
            scl_log(SCLT_DLLPG, 5, "[parx_get_cmp_size]Try to synchronize ...");
            if(j >= (_checksum_retry-4))
            {
                parx_wait_autoreset();
                /* Enable transmission */
                parx_set_transfer(SC_LOOP_TRANSFER/*LOOP*/, 0/*RETURN*/);
                /* Avoid the pathologic case where base address is corrupted. */
                _force_address_update = 1;
            }
            if(parx_sync() != 0)
            {
                return SC_ERROR_PKTSYNC;
            }
        }
    }

    return SC_ERROR_PKTCHECKSUM;
}



/**
 * Compress/Decompress function related : Receive (de)compression buffer address.
 * Returns
 *    SC_NOERROR            = No error.
 *    SC_ERROR_PKTSYNC      = Lost synchronisation with ARP.
 *    SC_ERROR_PKTCHECKSUM  = CRC mismatch.
**/
#define DUMMY_BUFFER_ADDRESS 0x00000000
unsigned long _buffer_address = DUMMY_BUFFER_ADDRESS;
unsigned long _buffer_length  = 0;
int parx_get_cmp_buff(unsigned long* address, unsigned long* length)
{
    /* In the case we requested it before, dont request it. */
    if(_buffer_address != DUMMY_BUFFER_ADDRESS)
    {
        *address = _buffer_address;
        *length  = _buffer_length;
    }


    *address = 0;
    *length  = 0;
    int j;
    for(j=0; j<_checksum_retry; j++)
    {
        if(parx_send_packet('K', 0/*address(unused)*/, 0/*length(unused)*/, 0/*first_byte(unused)*/, NULL/*buffer(unused)*/, 
                            _packet, 0/* next byte */, NULL/*answer_byte*/) != 0)
        {
            scl_log(SCLT_DLLPG, 3, "[parx_get_cmp_buff]*** [BufAddr]Packet send failure !");
            return SC_ERROR_PKTSYNC;
        }

        unsigned long  retdat1;
        unsigned short retdat2;
        unsigned short crc16[2];

        /* Read data and generate CRC */
        crc16[0] = crc16_start();
        scl_log(SCLT_DLLPG, 9, "[parx_get_cmp_buff] Read Return data ...");
        unsigned char tmp;
        tmp = par_exchange_byte(0); retdat1  = tmp << 24; crc16[0] = crc16_update(crc16[0], tmp);
        tmp = par_exchange_byte(0); retdat1 |= tmp << 16; crc16[0] = crc16_update(crc16[0], tmp);
        tmp = par_exchange_byte(0); retdat1 |= tmp <<  8; crc16[0] = crc16_update(crc16[0], tmp);
        tmp = par_exchange_byte(0); retdat1 |= tmp;       crc16[0] = crc16_update(crc16[0], tmp);

        tmp = par_exchange_byte(0); retdat2  = tmp <<  8; crc16[0] = crc16_update(crc16[0], tmp);
        tmp = par_exchange_byte(0); retdat2 |= tmp;       crc16[0] = crc16_update(crc16[0], tmp);

        /* Read CRC */
        tmp = par_exchange_byte(0); crc16[1]  = tmp << 8;
        tmp = par_exchange_byte(0); crc16[1] |= tmp;

        if(crc16[0] == crc16[1])
        { /* CRC OK ! */
            *address = retdat1; _buffer_address = *address;
            *length  = retdat2; _buffer_length = *length;
            return SC_NOERROR;
        }

        scl_log(SCLT_DLLPG, 5, "[parx_get_cmp_buff][BufAddr] Checksum error ! (0x%04X 0x%04X) (Trycount = %d)", crc16[0], crc16[1], j+1);
        { /* Resync with PAR. */
            scl_log(SCLT_DLLPG, 5, "[parx_get_cmp_buff]Try to synchronize ...");
            if(j >= (_checksum_retry-4))
            {
                parx_wait_autoreset();
                /* Enable transmission */
                parx_set_transfer(SC_LOOP_TRANSFER/*LOOP*/, 0/*RETURN*/);
                /* Avoid the pathologic case where base address is corrupted. */
                _force_address_update = 1;
            }
            if(parx_sync() != 0)
            {
                return SC_ERROR_PKTSYNC;
            }
        }
    }

    return SC_ERROR_PKTCHECKSUM;
}





/**
 * Receive function related : Receive data chunk.
 * Returns
 *     0 = No error.
 *     1 = too much CRC mismatch.
**/
int parxr_receive_datablock(unsigned long address, unsigned char* buffer, unsigned long length, unsigned char use_fpkt)
{
    scl_log(SCLT_DLLPG, 5, "[parxr_receive_datablock]->start(address=0x%08X, buffer, length=%d, use_fpkt=%d)", address, length, use_fpkt);

    unsigned short crc16[2];
    int i;

    /* Dummy case : noting to send */
    if(length == 0) return 0;

    /* If needed, send base address and base length. */
    scl_log(SCLT_DLLPG, 9, "[parxr_receive_datablock] Set BASE ADR=0x%08X, LEN=%d ...", address, length);
    unsigned char multiplicator;
    if(parx_set_base_address(address, length, &multiplicator) != 0)
    {
        scl_log(SCLT_DLLPG, 3, "[parxr_receive_datablock]*** [Base Address]Packet send failure !");
        return 1;
    }
    /* Set multiplicator. */
    scl_log(SCLT_DLLPG, 9, "[parxr_receive_datablock] Set MULT=%d", multiplicator);
    if(parx_send_packet('R', 0/*address(unused)*/, multiplicator/*multiplicator*/, 0/*param(unused)*/, NULL/*buffer(unused)*/, 
                        _packet, 0/* next byte */, NULL/*answer_byte*/) != 0)
    {
        scl_log(SCLT_DLLPG, 3, "[parxr_receive_datablock]*** [Multiplicator]Packet send failure !");
        return 1;
    }


    /* Read data and generate CRC */
    scl_log(SCLT_DLLPG, 9, "[parxr_receive_datablock] Read data ...");
    crc16[0] = crc16_start();
    for (i = 0; i < length; i++)
    {
        buffer[i] = par_exchange_byte(0);
        crc16[0] = crc16_update(crc16[0], buffer[i]);
    }

    /* See if the CRC matchs. */
    crc16[1]  = par_exchange_byte(0) << 8;
    crc16[1] |= par_exchange_byte(0);
    scl_log(SCLT_DLLPG, 9, "[parxr_receive_datablock] CHKS=0x%04X, 0x%04X", crc16[0], crc16[1]);

    if(crc16[0] != crc16[1])
    {
        scl_log(SCLT_DLLPG, 7, "[parxr_receive_datablock]*** CHKSM: CALC=%04X, PAR=%04X", crc16[0], crc16[1]);

        /* Recovery type : re-send */
        if(_rcv_recovery == 0)
        {
            _rs_total++;
            _rs_error++;
            return 1;
        }


        /* Recovery type : RS code */
        unsigned char rsdat[SC_RSLEN];
        unsigned short rscrc16[2];
        if(parx_send_packet('C', 0, 0, 0, NULL/*buffer(unused)*/, 
                            _packet, 0/* next byte */, NULL/*answer_byte*/) != 0)
        {
            scl_log(SCLT_DLLPG, 3, "[parxr_receive_datablock]*** RS Packet send failure !");
            return 1;
        }

        /* Read data and generate CRC */
        scl_log(SCLT_DLLPG, 9, "[parxr_receive_datablock] Read RS data ...");
        rscrc16[0] = crc16_start();
        for (i = 0; i < SC_RSLEN; i++)
        {
            rsdat[i] = par_exchange_byte(0);
            rscrc16[0] = crc16_update(rscrc16[0], rsdat[i]);
        }

        /* Read old CRC. */
        unsigned char tmp;
        tmp = par_exchange_byte(0);
        crc16[1]  = tmp << 8; rscrc16[0] = crc16_update(rscrc16[0], tmp);
        tmp = par_exchange_byte(0);
        crc16[1] |= tmp;      rscrc16[0] = crc16_update(rscrc16[0], tmp);

        /* See if the CRC matchs. */
        rscrc16[1]  = par_exchange_byte(0) << 8;
        rscrc16[1] |= par_exchange_byte(0);
        scl_log(SCLT_DLLPG, 9, "[parxr_receive_datablock] RS CHKS=0x%04X, 0x%04X", rscrc16[0], rscrc16[1]);
        if(rscrc16[0] != rscrc16[1])
        {
            scl_log(SCLT_DLLPG, 3, "[parxr_receive_datablock]*** RS CHKSM: CALC=%04X, PAR=%04X", rscrc16[0], rscrc16[1]);
            return 1;
        }
        /* Could retrieve RS data, try recovery. */
        int ret;
        ret = rsd_execute(buffer, length, crc16[1], rsdat, 0/*recSTT*/, length/*recEND*/, SC_RSD_ERASR | SC_RSD_NOISE);
        _rs_total++;
        if(ret == 0)
        {
            scl_log(SCLT_DLLPG, 4, "[parxr_receive_datablock]RS recover OK.");
            return 0;
        }
        else
        {
            _rs_error++;
            scl_log(SCLT_DLLPG, 3, "[parxr_receive_datablock]RS recover NG ! Re-download chunk.");
            return 1;
        }
    }

    return 0;
}


/**
 * Receive function related : Receive data (arbitrary length).
 * Returns :
 *    SC_NOERROR            = No error.
 *    SC_ERROR_PKTSYNC      = Lost synchronisation with ARP.
 *    SC_ERROR_PKTCHECKSUM  = CRC mismatch.
**/
int parxr_receive_data(unsigned long address, unsigned char* buffer, unsigned long length, unsigned char use_callback)
{
    scl_log(SCLT_DLLPG, 5, "[parxr_receive_data]->start(address=0x%08X, buffer, length=%d, use_callback=%d)", address, length, use_callback);

    /* 
     * Receiving all data in one pass is not recommended, since a transmission
     * error implies to re-download all the data.
     * And that we don't know what may happen when a transmission error happens.
     * So that transmission is divided in short-sized chunks.
     */
    unsigned long p   = 0;
    unsigned long psz = _rcv_packet_size;
    unsigned long l   = _rcv_packet_size;
    int err;
    unsigned char use_fpkt = 0;
    /* Before receiving the first datablock, send base address and length. */
    _force_address_update = 1;
    scl_log(SCLT_DLLPG, 8, "[parxr_receive_data]+++ Init: p=%d, l=%d, psz=%d", p, l, psz);

    for(p=0; p<length; p+=psz)
    {
        if(use_callback) STATUS_CALLBACK(CLBK_RECEIVE, address, buffer, p, length);

        l = psz;
        if((length-p) < psz) {l=length-p;}
        scl_log(SCLT_DLLPG, 8, "[parxr_receive_data]+++ parxr_receive_data length=%d, p=%d, l=%d, psz=%d", length, p, l, psz);

        int j;
        for(j=0; j<_checksum_retry; j++)
        {
            use_fpkt = (j==0 ? 1 : 0);
            err = parxr_receive_datablock(address+p, buffer+p, l, use_fpkt);
            if(err == 0) break;

            scl_log(SCLT_DLLPG, 5, "[parxr_receive_data][ofs=%d, len=%d] Checksum error ! (Trycount = %d)", p, l, j+1);

            { /* Resync with PAR. */

                scl_log(SCLT_DLLPG, 5, "[parxr_receive_data]Try to synchronize ...");
                if(j >= (_checksum_retry-4))
                {
                    parx_wait_autoreset();
                    /* Enable transmission */
                    parx_set_transfer(SC_LOOP_TRANSFER/*LOOP*/, 0/*RETURN*/);
                    /* Avoid the pathologic case where base address is corrupted. */
                    _force_address_update = 1;
                }
                if(parx_sync() != 0)
                {
                    return SC_ERROR_PKTSYNC;
                }
            }
        }
        /* In the case we looped _checksum_retry time, there are chances that the
         * checksum/transmission/anything else might be bad, so in this case write
         * an error message to the log.
         */
        if(err != 0)
        {
            return SC_ERROR_PKTCHECKSUM;
        }
    }

    return SC_NOERROR;
}


/**
 * Receive data from Saturn.
 * Returns :
 *    SC_NOERROR           = No error.
 *    SC_ERROR_PNOTOPEN    = Port is not opened.
 *    SC_ERROR_COMPRESSION = data (de)compression error.
 *    Other values      : See parxr_receive_data function.
**/
char parx_receive(unsigned long address, unsigned char* buffer, unsigned long length)
{
    scl_log(SCLT_DLLPG, 5, "[parx_receive]->start(address=0x%08X, buffer, length=%d)", address, length);

    if(!_port_init) return SC_ERROR_PNOTOPEN;

    int j, error;

    _wait_delay = 1000;

    /* Indicates that the process started. */
    STATUS_CALLBACK(CLBK_START, 0, buffer, 0, length);
    par_reset_exchanged_bytes();
    _pkt_total = 0;

    /* Init the RS code library. */
    rs_start();
    _rs_total = 0;
    _rs_error = 0;


    /* Enable transmission */
    parx_set_transfer(SC_LOOP_TRANSFER/*LOOP*/, 0/*RETURN*/);


    /* Receive data. */
    unsigned long comp_length=0;
    if((_use_compression == 0) || (length <= SC_COMP_MINSIZE))
    { /* Receive data without compressing it. */
        scl_log(SCLT_DLLPG, 8, "[parx_receive]+++ RAW parx_receive(address=0x%08X, length=%d) ...", address, length);
        error = parxr_receive_data(address, buffer, length, 1/*use_callback*/);
        if(error != SC_NOERROR)
        {
            STATUS_CALLBACK(CLBK_END, 0, buffer, 0, length);
            return error;
        }
        /* Update data for statistics. */
        comp_length = length;
    }
    else
    { /* Receive data while compressing it little by little. */
        scl_log(SCLT_DLLPG, 8, "[parx_receive]+++ CMP parx_receive(address=0x%08X, length=%d) ...", address, length);

        unsigned long  paddress = address;
        unsigned char* pbuffer  = buffer;
        unsigned long  plength  = length;

        /* Get compression buffer address. */
        unsigned long buffer_address, buffer_length;
        scl_log(SCLT_DLLPG, 5, "[parx_receive]*** Get compression buffer address and length ...");
        error = parx_get_cmp_buff(&buffer_address, &buffer_length);
        if(error != SC_NOERROR)
        {
            STATUS_CALLBACK(CLBK_END, 0, buffer, pbuffer-buffer, length);
            return error;
        }

        do
        {
            /* Buffer where to receive the compressed data. */
            unsigned char comp_buff[SC_BUFF_MAXSIZE];

            //unsigned short csrc_length = (csrc_length > plength ? plength : buffer_length - SC_COMP_HEADER_MAXSIZE);
            unsigned short csrc_length = MIN(plength, buffer_length - SC_COMP_HEADER_MAXSIZE);
            unsigned short cdst_length;
            scl_log(SCLT_DLLPG, 8, "[parx_receive]+++ plength=%d"    , plength);
            scl_log(SCLT_DLLPG, 8, "[parx_receive]+++ MAX=%d"        , buffer_length - SC_COMP_HEADER_MAXSIZE);
            scl_log(SCLT_DLLPG, 8, "[parx_receive]+++ csrc_length=%d", csrc_length);

            /* Compress data. */
            scl_log(SCLT_DLLPG, 5, "[parx_receive]*** Compress Data (paddress=0x%08X, csrc_length=%d) ...", paddress, csrc_length);
            if(parx_send_packet('Z', paddress/*address*/, csrc_length/*length*/, 0/*first_byte(unused)*/, NULL/*buffer(unused)*/, 
                                _packet, 0/* next byte */, NULL/*answer_byte*/) != 0)
            {
                STATUS_CALLBACK(CLBK_END, 0, buffer, pbuffer-buffer, length);
                return SC_ERROR_PKTSYNC;
            }

            /* Get compressed length. */
            scl_log(SCLT_DLLPG, 5, "[parx_receive]*** Get compressed length ...");
            error = parx_get_cmp_size(&cdst_length);
            if(error != SC_NOERROR)
            {
                STATUS_CALLBACK(CLBK_END, 0, buffer, pbuffer-buffer, length);
                return error;
            }

            /* Request compressed data. */
            scl_log(SCLT_DLLPG, 5, "[parx_receive]*** Receive compressed data (buffer_address=0X%08X, cdst_length=%d) ...", buffer_address, cdst_length);
            error = parxr_receive_data(buffer_address, comp_buff, cdst_length, 0/*use_callback*/);
            if(error != SC_NOERROR)
            {
                STATUS_CALLBACK(CLBK_END, 0, buffer, pbuffer-buffer, length);
                return error;
            }


            /* Decompress data. */
            scl_log(SCLT_DLLPG, 5, "[parx_receive]*** Decompress data ...");
            unsigned short decomp_sz = decomp_exec(comp_buff, cdst_length, pbuffer);

            /* Update data for statistics. */
            comp_length += cdst_length;

            /* Update status on host program. */
            STATUS_CALLBACK(CLBK_RECEIVE, address, buffer, pbuffer-buffer, length);

            /* Update pointers and remaining size. */
            paddress += csrc_length;
            pbuffer  += csrc_length;
            plength  -= csrc_length;

            scl_log(SCLT_DLLPG, 4, "[parx_receive] SAT SZ=%d -> COMP=%d Bytes, DECOMP=%d Bytes, REMAIN=%d Bytes.", csrc_length, cdst_length, decomp_sz, plength);
            if(decomp_sz != csrc_length)
            {
                scl_log(SCLT_DLLPG, 0, "[parx_receive]*** ERROR when decompressing data !");
                return SC_ERROR_COMPRESSION;
            }
        } while(plength != 0);
    }


    /* Stop transmission */
    parx_set_transfer(SC_LOOP_IDLE/*LOOP*/, 1/*RETURN*/);


    /* Display transmission statistics. */
    parx_display_trans_stats(length, comp_length);

    /* Indicates that the process ended */
    STATUS_CALLBACK(CLBK_END, 0, buffer, length, length);

    return SC_NOERROR;
}








/**
 * Send function related : Send data chunk.
 * Returns
 *     0 = No error.
 *     1 = too much CRC mismatch.
**/
int parxs_send_datablock(unsigned long address, unsigned char* buffer, unsigned long length)
{
    scl_log(SCLT_DLLPG, 5, "[parxs_send_datablock]->start(address=0x%08X, buffer, length=%d)", address, length);

    unsigned char rcv_buffer[64*1024];
    unsigned char next_byte;
    unsigned short crc16[2];
    int i;

    /* Dummy case : noting to send */
    if(length == 0) return 0;

    _wait_delay = 1000;


    /* If needed, send base address and base length. */
    scl_log(SCLT_DLLPG, 9, "[parxs_send_datablock] Set BASE ADR=0x%08X, LEN=%d ...", address, length);
    unsigned char multiplicator;
    if(parx_set_base_address(address, length, &multiplicator) != 0)
    {
        scl_log(SCLT_DLLPG, 3, "[parxs_send_datablock]*** [Base Address]Packet send failure !");
        return 1;
    }

    /* Set multiplicator. */
    scl_log(SCLT_DLLPG, 9, "[parxs_send_datablock] Set MULT=%d", multiplicator);

    /* Special case : only one byte to send
     * In this case, the byte sent after the packet should be set to
     * zero, in order to avoid synchronization errors.
     */
    if(length == 1)
    {
        next_byte = 0;
    }
    else
    {
        next_byte = buffer[1];
    }
    if(parx_send_packet('W', 0/*address(unused)*/, multiplicator/*multiplicator*/, buffer[0]/*first_byte*/, NULL/*buffer(unused)*/, 
                        _packet, next_byte/* next byte */, NULL/*answer_byte*/) != 0)
    {
        scl_log(SCLT_DLLPG, 3, "[parxs_send_datablock]*** [Multiplicator]Packet send failure !");
        return 1;
    }

    /* Special case : only one byte to send
     * The byte has been sent with the packet, and is
     * CRC-verified, hence we don't need to go any further.
     */
    if(length == 1) return 0;


    /* Write remaining data. */
    scl_log(SCLT_DLLPG, 9, "[parxs_send_datablock] Write data ...");
    crc16[0] = crc16_start();
    /* Note : the first byte (buffer[0]) doesn't need to be taken into
     * consideration, since its CRC has been verified with the packet's CRC.
     * However, we use it for easier processing in RS code.
     */
    crc16[0] = crc16_update(crc16[0], buffer[0]);
    crc16[0] = crc16_update(crc16[0], buffer[1]);


    /* Init and update RS code. */
    /* RScode-related. */
    unsigned char rs[SC_RSLEN];
    rse_init(rs);
    rse_update(rs, 0, buffer[0]);
    rse_update(rs, 1, buffer[1]);

    /* Write data and generate CRC */
    for (i = 2; i < length; i++)
    {
        crc16[0] = crc16_update(crc16[0], buffer[i]);
        rse_update(rs, i, buffer[i]);

        rcv_buffer[i] = par_exchange_byte(buffer[i]);
    }
    scl_log(SCLT_DLLPG, 9, "[parxs_send_datablock] Write data end ...");
    par_exchange_byte(0x42);
    //par_exchange_byte(0x53);
    scl_log(SCLT_DLLPG, 9, "[parxs_send_datablock] GET CRC ...");

    /* See if the CRC matchs. */
    crc16[1]  = par_exchange_byte(0) << 8;
    crc16[1] |= par_exchange_byte(0);
    scl_log(SCLT_DLLPG, 9, "[parxs_send_datablock] CHKS=0x%04X, 0x%04X", crc16[0], crc16[1]);

    if(crc16[0] != crc16[1])
    {
        scl_log(SCLT_DLLPG, 5, "[parxs_send_datablock]*** CHKSM: CALC=%04X, PAR=%04X", crc16[0], crc16[1]);

        /* Recovery type : re-send */
        if(_snd_recovery == 0)
        {
            _rs_total++;
            _rs_error++;
            return 1;
        }


        /* Recovery type : RS code, or fast-but-simple */

        unsigned long rs_tmp[2];
        rs_tmp[0] = 0; rs_tmp[1] = 0;
        memcpy((void*)rs_tmp, rs, SC_RSLEN);
        unsigned char answer_byte;

        /* Look for position where the inserted byte should be. */
        unsigned short recpos = 2;
        int diff;
        for (i = 2; i < length; i++)
        {
            diff = rcv_buffer[i] - (i-1);
            scl_log(SCLT_DLLPG, 3, "[parxs_send_datablock]rcv_buffer[%2d] = %02X (diff = %d)", i, rcv_buffer[i], diff);
            if(diff != 0)
            {
                scl_log(SCLT_DLLPG, 3, "[parxs_send_datablock]^ error found !");
                recpos = i+1;
                break;
            }
        }
        if(recpos >= (length-1))
        {
            recpos = 2;
        }
        scl_log(SCLT_DLLPG, 3, "[parxs_send_datablock]RS: recpos=%d recdat=0x %04X %02X", recpos, rs_tmp[0], crc16[0]);

        _rs_total++;
        if(parx_send_packet((_snd_recovery == 1 ? 'D'/*RS code*/ : 'F'/*simple-but-fast*/), 
                            crc16[0]/*address(CRC16)*/,
                            recpos/*length(recovery position)*/, 
                            buffer[length-1]/*param(last byte value)*/, 
                            rs/*buffer(RS code)*/, 
                            _packet, 0/* next byte */, &answer_byte) != 0)
        {
            _rs_error++;
            scl_log(SCLT_DLLPG, 3, "[parxs_send_datablock]*** RS code send failure !");
            return 1;
        }

        scl_log(SCLT_DLLPG, 5, "[parxs_send_datablock]RS answer byte = 0x%02X (\'*\'->0x2A, \'<\'->0x3C)", answer_byte);
        if(answer_byte == '<')
        { /* RS code recovery was OK ! */
            scl_log(SCLT_DLLPG, 4, "[parxs_send_datablock]RS recovery OK.");
            return 0;
        }
        /* Recovery was NG. */
        scl_log(SCLT_DLLPG, 3, "[parxs_send_datablock]*** RS recovery NG ! Re-upload chunk");
        _rs_error++;
        return 1;
    }

    return 0;
}


/**
 * Send function related : Send data (arbitrary length).
 * Returns :
 *    SC_NOERROR            = No error.
 *    SC_ERROR_PKTSYNC      = Lost synchronisation with ARP.
 *    SC_ERROR_PKTCHECKSUM  = CRC mismatch.
**/
int parxs_send_data(unsigned long address, unsigned char* buffer, unsigned long length, unsigned long loopcnt, unsigned char use_callback)
{

    scl_log(SCLT_DLLPG, 5, "[parxs_send_data]->start(address=0x%08X, buffer, length=%d, loopcnt=%d, use_callback=%d)", address, length, loopcnt, use_callback);

    /* 
     * Receiving all data in one pass is not recommended, since a transmission
     * error implies to re-download all the data.
     * And that we don't know what may happen when a transmission error happens.
     * So that transmission is divided in short-sized chunks.
     */
    unsigned long p   = 0;
    unsigned long psz = _snd_packet_size;
    unsigned long l   = _snd_packet_size;
    int err;
    /* Before sending the first datablock, send base address and length. */
    _force_address_update = 1;
    scl_log(SCLT_DLLPG, 8, "[parxs_send_data]+++ Init: p=%d, l=%d, psz=%d", p, l, psz);
    for(p=0; p<length; p+=psz)
    {
        if(use_callback) STATUS_CALLBACK(CLBK_SEND, address, buffer, p, length);

        l = psz;
        if((length-p) < psz) {l=length-p;}
        scl_log(SCLT_DLLPG, 8, "[parxs_send_data]+++ parxs_send_data length=%d, p=%d, l=%d, psz=%d", length, p, l, psz);

        int j;
        for(j=0; j<_checksum_retry; j++)
        {
            err = parxs_send_datablock(address+p, buffer+p, l);
            if(err == 0) break;

            scl_log(SCLT_DLLPG, 5, "[parxs_send_data][ofs=%d, len=%d] Checksum error ! (Trycount = %d)", p, l, j+1);

            { /* Resync with PAR. */

                scl_log(SCLT_DLLPG, 5, "[parxs_send_data]Try to synchronize ...");
                if(j >= (_checksum_retry-4))
                {
                    parx_wait_autoreset();
                    /* Enable transmission */
                    parx_set_transfer(loopcnt/*LOOP*/, 0/*RETURN*/);
                    /* Avoid the pathologic case where base address is corrupted. */
                    _force_address_update = 1;
                }
                if(parx_sync() != 0)
                {
                    return SC_ERROR_PKTSYNC;
                }
            }
        }
        /* In the case we looped _checksum_retry time, there are chances that the
         * checksum/transmission/anything else might be bad, so in this case write
         * an error message to the log.
         */
        if(err != 0)
        {
            return SC_ERROR_PKTCHECKSUM;
        }
    }
    return SC_NOERROR;
}


/**
 * Send data to Saturn.
 * Returns :
 *    SC_NOERROR           = No error.
 *    SC_ERROR_PNOTOPEN    = Port is not opened.
 *    SC_ERROR_COMPRESSION = data (de)compression error.
 *    Other values      : See parxs_send_data function.
**/
char parx_send(unsigned long address0, unsigned char* buffer, unsigned long length, char exec_flag)
{
    scl_log(SCLT_DLLPG, 5, "[parx_send]->start(address0=0x%08X, buffer, length=%d, exec_flag=%d)", address0, length, exec_flag);

    if(!_port_init) return SC_ERROR_PNOTOPEN;

    int j, error;

    /* Indicates that the process started. */
    STATUS_CALLBACK(CLBK_START, 0, buffer, 0, length);
    par_reset_exchanged_bytes();
    _pkt_total = 0;

    /* Init the RS code library. */
    rs_start();
    _rs_total = 0;
    _rs_error = 0;


    /* Enable transmission */
    unsigned long loopcnt = (exec_flag == 0 ? SC_LOOP_TRANSFER : SC_LOOP_INFINITE/*infinite loop*/);
    parx_set_transfer(loopcnt/*LOOP*/, 0/*RETURN*/);

    /* Set address where to send data. */
    unsigned long address = address0;
    if(exec_flag != 0)
    {
        /* In the case of executing data :
         * In order not to overwrite current code, first send the 
         * target code to a temporary area.
         * When sending the 'X' command, a stub will be launched in order to 
         * copy the code to the destination address, then execute it.
         */
        scl_log(SCLT_DLLPG, 5, "[parx_send]*** Request exec temporary address");
        error = parx_get_tmp_exec_adr(&address);
        if(error != SC_NOERROR)
        {
            STATUS_CALLBACK(CLBK_END, 0, buffer, 0, length);
            return error;
        }
    }
    scl_log(SCLT_DLLPG, 5, "[parx_send]*** Upload address = 0x%08X", address);





    /* Send data. */
    unsigned long comp_length=0;
    if((_use_compression == 0) || (length <= SC_COMP_MINSIZE))
    { /* Send data without compressing it. */
        error = parxs_send_data(address, buffer, length, loopcnt, 1/*use_callback*/);
        if(error != SC_NOERROR)
        {
            STATUS_CALLBACK(CLBK_END, 0, buffer, 0, length);
            return error;
        }
        /* Update data for statistics. */
        comp_length = length;
    }
    else
    { /* Send data while compressing it little by little. */
        unsigned long  paddress = address;
        unsigned char* pbuffer  = buffer;
        unsigned long  plength  = length;

        /* Get compression buffer address. */
        unsigned long buffer_address, buffer_length;
        scl_log(SCLT_DLLPG, 5, "[parx_send]*** Get compression buffer address and length ...");
        error = parx_get_cmp_buff(&buffer_address, &buffer_length);
        if(error != SC_NOERROR)
        {
            STATUS_CALLBACK(CLBK_END, 0, buffer, pbuffer-buffer, length);
            return error;
        }

        do
        {
            /* Buffer where to put the compressed data. */
            unsigned char comp_buff[SC_BUFF_MAXSIZE];

            //unsigned short csrc_length = (csrc_length > plength ? plength : buffer_length - SC_COMP_HEADER_MAXSIZE);
            unsigned short csrc_length = MIN(plength, buffer_length - SC_COMP_HEADER_MAXSIZE);
            unsigned short cdst_length;
            scl_log(SCLT_DLLPG, 8, "[parx_send]+++ plength=%d"    , plength);
            scl_log(SCLT_DLLPG, 8, "[parx_send]+++ MAX=%d"        , buffer_length - SC_COMP_HEADER_MAXSIZE);
            scl_log(SCLT_DLLPG, 8, "[parx_send]+++ csrc_length=%d", csrc_length);

            /* Compress data. */
            scl_log(SCLT_DLLPG, 5, "[parx_send]*** Compress Data (csrc_length=%d) ...", csrc_length);
            unsigned short comp_sz = comp_exec(pbuffer, csrc_length, comp_buff);
            if(comp_sz == 0)
            {
                STATUS_CALLBACK(CLBK_END, 0, buffer, pbuffer-buffer, length);
                return SC_ERROR_COMPRESSION;
            }

            /* Send compressed data. */
            scl_log(SCLT_DLLPG, 5, "[parx_send]*** Send compressed data (comp_sz = %d) ...", comp_sz);
            error = parxs_send_data(buffer_address, comp_buff, comp_sz, loopcnt, 0/*use_callback*/);
            if(error != SC_NOERROR)
            {
                STATUS_CALLBACK(CLBK_END, 0, buffer, pbuffer-buffer, length);
                return error;
            }

            /* Decompress data. */
            scl_log(SCLT_DLLPG, 5, "[parx_send]*** Decompress data (comp_sz=%d, dst_adr=0x%08X) ...", comp_sz, paddress);
            if(parx_send_packet('U', paddress/*address*/, comp_sz/*length*/, 0/*first_byte(unused)*/, NULL/*buffer(unused)*/, 
                                _packet, 0/* next byte */, NULL/*answer_byte*/) != 0)
            {
                STATUS_CALLBACK(CLBK_END, 0, buffer, pbuffer-buffer, length);
                return SC_ERROR_PKTSYNC;
            }

            /* Get decompressed length (just for error-checking, can be skipped). */
            scl_log(SCLT_DLLPG, 5, "[parx_send]*** Get decompressed length ...");
            error = parx_get_cmp_size(&cdst_length);
            if(error != SC_NOERROR)
            {
                STATUS_CALLBACK(CLBK_END, 0, buffer, pbuffer-buffer, length);
                return error;
            }

            /* Update data for statistics. */
            comp_length += comp_sz;

            /* Update status on host program. */
            STATUS_CALLBACK(CLBK_SEND, address, buffer, pbuffer-buffer, length);

            /* Update pointers and remaining size. */
            paddress += csrc_length;
            pbuffer  += csrc_length;
            plength  -= csrc_length;

            scl_log(SCLT_DLLPG, 4, "[parx_send] COMP SZ=%d -> %d Bytes, SAT DECOMP=%d Bytes, REMAIN=%d Bytes.", csrc_length, comp_sz, cdst_length, plength);
            if(csrc_length != cdst_length)
            {
                scl_log(SCLT_DLLPG, 0, "[parx_send]*** ERROR when decompressing data !");
                return SC_ERROR_COMPRESSION;
            }
        } while(plength != 0);
    }



    /* If needed, execute code */
    if(exec_flag)
    {
        scl_log(SCLT_DLLPG, 8, "[parx_send]+++ Execute: tmp_adr=0x%08X, length=%d, exec_adr=0x%08X", address, length, address0);
        parx_send_packet('X', address/*address(TMP)*/, length/*length*/, 
                              address0/*exec_address(DST)*/, 
                              NULL/*buffer(unused)*/, 
                              _packet, 0/*next byte*/, NULL/*answer_byte*/);
    }
    else
    {
        /* Stop transmission */
        parx_set_transfer(SC_LOOP_IDLE/*LOOP*/, 1/*RETURN*/);
    }


    /* Display transmission statistics. */
    parx_display_trans_stats(length, comp_length);

    /* Indicates that the process ended */
    STATUS_CALLBACK(CLBK_END, 0, buffer, length, length);

    return SC_NOERROR;
}



