/**
 * PdaLink -- Connect Palm with Amiga
 *
 * SLP (Serial Link Protocol)
 *
 * (C) 1998-2000 Richard Körber <rkoerber@gmx.de>
 *
 *------------------------------------------------------------------
 *
 * 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
 * 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 should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * You must not use this source code to gain profit of any kind!
 */

#include "pdalink_glob.h"


/*------------------------------------------------------**
** Name:        CRC16                            private
**
** Funktion:    Calculate CRC16 checksum
**
** Parameter:   buf       Start des Buffers
**              len       Länge des Buffers
**              crc       CRC aus vorigem Bufferabschnitt,
**                        0 beim ersten mal
** Ergebnis:    CRC-Prüfsumme
//>
** Bemerkungen:
**
** Revision:    31. Mai 1998, 22:01:39
*/
static UWORD CRC16(UBYTE *buf, UWORD len, UWORD crc)
{
  register UWORD i;

  while(len--)
  {
    crc ^= (UWORD)(*buf++ << 8);
    for(i=0; i<8; i++)
    {
      if(crc & 0x8000)
        crc = (crc<<1) ^ 0x1021;
      else
        crc = crc<<1;
    }
  }
  return(crc);
}
//<

/*------------------------------------------------------**
** Name:        PL_SLPWrite                       public
**
** Funktion:    Verschickt ein SLP-Paket
**
** Parameter:   socket    Socket für die Übertragung
**              buffer    zu übertragener Puffer
**              length    Länge des Puffers
**              header    SLP-Header
**              PADP      Optional: PADP-Header
** Ergebnis:    length    Verschickte Daten oder
**                        -1: Fehler
//>
** Bemerkungen:
**
** Revision:    31. Mai 1998, 21:14:30
*/
__saveds __asm LONG PL_SLPWrite
(
  register __a0 APTR socket,
  register __a1 APTR buffer,
  register __d0 LONG length,
  register __a2 struct PL_SLP_Header *header,
  register __a3 struct PL_PADP_Header *padp
)
{
  struct PL_Socket *sock = (struct PL_Socket *)socket;
  UWORD i;
  UBYTE hdrsum;
  UWORD checksum;

  sock->lastError = PLERR_OKAY;

  /* Create signature */
  header->signature[0] = 0xBE;
  header->signature[1] = 0xEF;
  header->signature[2] = 0xED;
  header->dataSize     = (padp ? length+sizeof(struct PL_PADP_Header) : length) ;

  /* Calculate header checksum */
  for(hdrsum = i = 0; i<(sizeof(struct PL_SLP_Header)-1); i++)
  {
    hdrsum += ((UBYTE *)header)[i];
  }
  header->checksum = hdrsum;

  /* Calculate data checksum */
  checksum = CRC16((UBYTE *)header,sizeof(struct PL_SLP_Header),0);
  if(padp) checksum = CRC16((UBYTE *)padp,sizeof(struct PL_PADP_Header),checksum);
  checksum = CRC16((UBYTE *)buffer,length,checksum);

  /* Send packet, and we are done */
  if(PL_RawWrite(socket,(UBYTE *)header,sizeof(struct PL_SLP_Header)))
  {
    if(!padp || PL_RawWrite(socket,padp,sizeof(struct PL_PADP_Header)))
    {
      if(PL_RawWrite(socket,buffer,length))
      {
        if(PL_RawWrite(socket,(UBYTE *)&checksum,2))
        {
#ifdef SLPDEBUG
  Printf("SLP A->P: dest=%ld src=%ld type=%ld dataSize=%ld transID=0x%02lx sum=0x%02lx\n",header->destSocket,header->srcSocket,header->pckType,header->dataSize,header->transID,header->checksum);
#endif
          return(length);                 // Success
        }
      }
    }
  }
  return(-1);                           // Failure
}
//<

/*------------------------------------------------------**
** Name:        PL_SLPRead                        public
**
** Funktion:    Holt ein SLP-Paket
**
** Parameter:   socket    Socket für die Übertragung
**              buffer    Empfangspuffer
**              length    Länge des Empfangsbuffer
**              header    SLP-Header-Puffer
**              padp      Optional: PADP-Header
** Ergebnis:    length    Länge des empfangenen Puffers
**                        (ohne SLP-Header), -1: Fehler
//>
** Bemerkungen:
**
** Revision:     1. Juni 1998, 16:01:46
*/
__saveds __asm LONG PL_SLPRead
(
  register __a0 APTR socket,
  register __a1 APTR buffer,
  register __d0 LONG length,
  register __a2 struct PL_SLP_Header *header,
  register __a3 struct PL_PADP_Header *padp
)
{
  struct PL_Socket *sock = (struct PL_Socket *)socket;
  UBYTE  read;                          // Reading into this variable
  UBYTE  hdrsum;
  UWORD  i;
  UWORD  readChecksum;
  UWORD  checksum;

  sock->lastError = PLERR_OKAY;         // No error occured yet

  /* State machine: wait for header */
  if(PL_RawRead(socket,&read,1))
  {
    for(;;)
    {
      /* wait for 0xBEEFED */
      if(read==0xBE)
      {
        header->signature[0] = read;
        if(!PL_RawRead(socket,&read,1)) return(-1);   // <--- Failed ---
        if(read!=0xEF) continue;
        header->signature[1] = read;
        if(!PL_RawRead(socket,&read,1)) return(-1);   // <--- Failed ---
        if(read!=0xED) continue;
        header->signature[2] = read;
      }
      else
      {
        if(!PL_RawRead(socket,&read,1)) return(-1);   // <--- Failed ---
        continue;
      }

      /* Looks promising, read rest of the header */
      if(!PL_RawRead(socket,&header->destSocket,7)) return(-1); // <--- Failed ---

      /* Check sum okay? */
      for(hdrsum = i = 0; i<(sizeof(struct PL_SLP_Header)-1); i++)
      {
        hdrsum += ((UBYTE *)header)[i];
      }

      /* LOOP packet ? */
      if(header->pckType == PLSLPTYPE_LOOP)           // is ignored
      {
        for(i=0; i<(header->dataSize+2); i++)
        {
          if(!PL_RawRead(socket,&read,1)) return(-1); // <--- Failure ---
        }
        continue;                                     // Wait for next packet
      }

      if(header->checksum == hdrsum) break;           // Found a good header
    }
  }

  /* Fetch rest of data */
  if(padp)
  {
    length = min(header->dataSize - sizeof(struct PL_PADP_Header), length);
    if(!PL_RawRead(socket,padp,sizeof(struct PL_PADP_Header))) return(-1);
    if(!PL_RawRead(socket,buffer,length)) return(-1);   // Read error
  }
  else
  {
    length = min(header->dataSize,length);
    if(!PL_RawRead(socket,buffer,length)) return(-1);   // read error
  }

  /* Read CRC16 */
  if(!PL_RawRead(socket,(UBYTE *)&readChecksum,2)) return(-1); // read error

  /* Compare data checksum */
  checksum = CRC16((UBYTE *)header,sizeof(struct PL_SLP_Header),0);
  if(padp) checksum = CRC16((UBYTE *)padp,sizeof(struct PL_PADP_Header),checksum);
  checksum = CRC16((UBYTE *)buffer,length,checksum);
  if(checksum!=readChecksum)
  {
    sock->lastError = PLERR_CHECKSUM;
    return(-1);                                       // bad checksum
  }

#ifdef SLPDEBUG
  Printf("SLP A<-P: dest=%ld src=%ld type=%ld dataSize=%ld transID=0x%02lx sum=0x%02lx\n",header->destSocket,header->srcSocket,header->pckType,header->dataSize,header->transID,header->checksum);
#endif

  return(length);                                     // Packet received
}
//<


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