/*
 * hifi_packet.c:
 *
 * Copyright (c) 2000 James McKenzie <james@fishsoup.dhs.org>.
 * All rights reserved.
 *
 * $Log: hifi_packet.c,v $
 * Revision 1.15  2000/11/23 23:45:24  root
 * #
 *
 * Revision 1.14  2000/11/23 23:44:47  root
 * #
 *
 * Revision 1.13  2000/10/07 18:30:09  root
 * #
 *
 * Revision 1.12  2000/10/06 19:52:22  root
 * #
 *
 * Revision 1.11  2000/09/29 13:24:47  root
 * #
 *
 * Revision 1.10  2000/09/29 13:24:03  root
 * #
 *
 * Revision 1.9  2000/09/29 13:18:53  root
 * #
 *
 * Revision 1.8  2000/09/29 13:18:24  root
 * #
 *
 * Revision 1.7  2000/09/24 01:46:07  root
 * #
 *
 * Revision 1.6  2000/09/24 01:12:53  root
 * #
 *
 * Revision 1.5  2000/02/20 18:23:02  root
 * #
 *
 * Revision 1.4  2000/02/20 14:59:19  root
 * #
 *
 * Revision 1.3  2000/02/20 03:15:34  root
 * #
 *
 * Revision 1.2  2000/02/20 03:10:18  root
 * #
 *
 *
 */

static char rcsid[] =
  "$Id: hifi_packet.c,v 1.15 2000/11/23 23:45:24 root Exp $";

#ifdef PALM
#include <Common.h>
#include <System/SysAll.h>
#include <mathf.h>
#else
#ifdef DEBUG
#include <stdio.h>
#endif

#include <time.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/unistd.h>
#include <string.h>
extern int hifimsg_recv (void *, void *, int);
extern int hifimsg_fd (void *_c);
#endif


#include "hifi.h"

extern int hifimsg_send (void *, void *, Word);

static Byte myclass;
static Byte myunit;


#ifdef PALM
static Byte hifi_msgbuf[HIFI_MAX_LEN];
#else
#include <malloc.h>
#endif

static Word serial = 0;

void
hifi_me (Byte class, Byte unit)
{
  myclass = class;
  myunit = unit;
}


#ifndef PALM
extern void *hifimsg_open (void);
void *
hifi_open (Byte class, Byte unit)
{
  hifi_me (class, unit);
  return hifimsg_open ();
}
#endif

Byte *
hifipkt_dword (Byte * buf, Word * w)
{
#if 0
#ifdef PALM
  *w = *((Word *) buf);
#else
  *w = ntohs (*((Word *) buf));
#endif
#else

//Palm can't do unaligned accesses
  //This also takes care of ntohs
  *w = (buf[0] << 8) | buf[1];

#endif
  return (buf + 2);
}



#ifdef PALM
static int isacked = 0;
Byte aclass;
Byte aunit;
Word aseq;

extern void irda_dispatch (void);

#endif





int
hifi_send (void *c, void *buf, int len)
{
  int ret;
  Byte *b = buf;
  Word w;

  ret = hifimsg_send (c, buf, len);

  hifipkt_dword (&b[6], &w);

  if (w & HIFI_SEQUENCE_REL)
    {

//Listen for ack frame

#ifdef PALM
      isacked = 8;              //Wait for 2secs
      aclass = b[2];
      aunit = b[3];
      aseq = (w & HIFI_SEQUENCE_MASK) | HIFI_SEQUENCE_ACK;


      {
        Int32 d;
        d = SysTicksPerSecond ();
        d = d / 3;
        SysTaskDelay (d);

        irda_dispatch ();

        while (isacked--)
          {

            hifimsg_send (c, buf, len);


            d = SysTicksPerSecond ();
            d = d / 3;
            SysTaskDelay (d);


            irda_dispatch ();
          }
      }


#else

      {
        Byte *b2 = hifi_getbuf (), *bp;
        Byte sclass, sunit;
        Byte dclass, dunit;
        Word rw = (w & HIFI_SEQUENCE_MASK) | HIFI_SEQUENCE_ACK;
        int try = 9;
        struct timeval last, now, sub, rep;
        int fd = hifimsg_fd (c);

#ifdef DEBUG
        printf ("RS: Sending seq %x, expecting ack %x from %d.%d \n", w, rw,
                b[2], b[3]);
#endif

        rep.tv_sec = 0;
        rep.tv_usec = 500000;

        gettimeofday (&last, NULL);
        while (try--)
          {
            fd_set readfds;

            FD_ZERO (&readfds);
            sub.tv_sec = 0;
            sub.tv_usec = 500000;

            FD_SET (fd, &readfds);
            select (fd + 1, &readfds, NULL, NULL, &sub);

            if (FD_ISSET (fd, &readfds))
              {
                int i;
                bzero (b2, HIFI_MAX_LEN);
                i = hifimsg_recv (c, b2, HIFI_MAX_LEN);
#ifdef DEBUG
                printf
                  ("RS: Recvd %d bytes %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
                   i, b2[0], b2[1], b2[2], b2[3], b2[4], b2[5], b2[6], b2[7],
                   b2[8]);
#endif
                bp = b2;
                //printf("%p\n",bp);
                bp = hifipkt_dword (bp, &w);
                if (w == HIFI_MAGIC)
                  {
                    bp = hifipkt_daddress (bp, &dclass, &dunit); /*Dest */
                    bp = hifipkt_daddress (bp, &sclass, &sunit); /*Src */
                    bp = hifipkt_dword (bp, &w); /*Sequence number */
#ifdef DEBUG
                    printf ("RS: Received %x from %d.%d\n", w, sclass, sunit);
#endif
                    if ((sclass == b[2]) && (sunit == b[3]) && (w == rw))
                      break;
                  }
              }

            gettimeofday (&now, NULL);
            timersub (&now, &last, &sub);

            if (timercmp (&sub, &rep, >))
              {
#ifdef DEBUG
                printf ("RS: Retransmitting...\n");
                printf ("hifi_packet_rel: Retransmit seq %x\n", rw);
#endif
                hifimsg_send (c, buf, len);

                last = now;
              }
          }
      }
#endif
    }
  return ret;
}

#ifndef PALM
int
hifi_recv (void *c, void *buf, int len)
{
  int ret;
  ret = hifimsg_recv (c, buf, len);
  return ret;
}
#endif


Byte *
hifi_getbuf ()
{
#ifdef PALM
  return (hifi_msgbuf);
#else
  return ((Byte *) malloc (HIFI_MAX_LEN));
#endif
}

void
hifi_freebuf (Byte * buf)
{
#ifndef PALM
  free (buf);
#endif
}

Byte *
hifipkt_ebyte (Byte * buf, Byte b)
{
  *buf = b;
  return (buf + 1);
}

Byte *
hifipkt_dbyte (Byte * buf, Byte * b)
{
  *b = *buf;
  return (buf + 1);
}

Byte *
hifipkt_ddword (Byte * buf, DWord * w)
{
#if 0
#ifdef PALM
  *w = *((DWord *) buf);
#else
  *w = ntohl (*((DWord *) buf));
#endif
#else

//Palm can't do unaligned accesses
  //This also takes care of ntohl
  *w = (((((((DWord) buf[0]) << 8) | ((DWord) buf[1])) << 8) | ((DWord) buf[2]))
     << 8) | ((DWord) buf[3]);
#endif
  return (buf + 4);
}

Byte *
hifipkt_dsdword (Byte * buf, SDWord * w)
{

  *w = (((((((SDWord) (buf[0] & 0x7f)) << 8) | ((SDWord) buf[1])) << 8) | ((SDWord) buf[2])) << 8) | ((SDWord) buf[3]); 

if (buf[0] & 0x80) (*w)+=(SDWord) -0x80000000L;

  return (buf + 4);
}

Byte *
hifipkt_edword (Byte * buf, DWord w)
{
#if 0
#ifdef PALM
  *((DWord *) buf) = w;
#else
  *((DWord *) buf) = htonl (w);
#endif
#else
//Palm can't do unaligned accesses
  //This also takes care of ntohs
  buf[0] = (w >> 24) & 0xff;
  buf[1] = (w >> 16) & 0xff;
  buf[2] = (w >> 8) & 0xff;
  buf[3] = w & 0xff;
#endif
  return buf + 4;
}

Byte *
hifipkt_esdword (Byte * buf, SDWord w)
{
DWord d;

if (w>=0) {
	d=(DWord) w;
} else {
	d=0xffffffffUL-(DWord) (-1 - w);
}

  return hifipkt_edword(buf,d);
}

Byte *
hifipkt_eword (Byte * buf, Word w)
{
#if 0
#ifdef PALM
  *((Word *) buf) = w;
#else
  *((Word *) buf) = htons (w);
#endif
#else
//Palm can't do unaligned accesses
  //This also takes care of ntohs
  buf[0] = (w >> 8) & 0xff;
  buf[1] = w & 0xff;
#endif
  return buf + 2;
}

Byte *
hifipkt_eaddress (Byte * buf, Byte class, Byte unit)
{
  buf = hifipkt_ebyte (buf, class);
  buf = hifipkt_ebyte (buf, unit);
  return buf;
}

Byte *
hifipkt_daddress (Byte * buf, Byte * class, Byte * unit)
{
  buf = hifipkt_dbyte (buf, class);
  buf = hifipkt_dbyte (buf, unit);
  return buf;
}

Byte *
hifipkt_new_rel (Byte * buf, Byte class, Byte unit, Byte dclass, Byte dunit)
{
  Word seq = (class << 6) ^ unit;
  if (class == HIFI_CLASS_MINE)
    class = myclass;
  if (unit == HIFI_UNIT_ME)
    class = myunit;
  seq += (serial++);
  seq &= HIFI_SEQUENCE_MASK;
  buf = hifipkt_eword (buf, HIFI_MAGIC);
  buf = hifipkt_eaddress (buf, dclass, dunit);
  buf = hifipkt_eaddress (buf, class, unit);
  buf = hifipkt_eword (buf, seq | HIFI_SEQUENCE_REL);

  return (buf);
}

Byte *
hifipkt_new (Byte * buf, Byte class, Byte unit, Byte dclass, Byte dunit)
{
  Word seq = (class << 6) ^ unit;
  if (class == HIFI_CLASS_MINE)
    class = myclass;
  if (unit == HIFI_UNIT_ME)
    class = myunit;
  seq += (serial++);
  seq &= HIFI_SEQUENCE_MASK;
  buf = hifipkt_eword (buf, HIFI_MAGIC);
  buf = hifipkt_eaddress (buf, dclass, dunit);
  buf = hifipkt_eaddress (buf, class, unit);
  buf = hifipkt_eword (buf, seq);
  return (buf);
}

Byte *
hifipkt_new_brd (Byte * buf, Byte class, Byte unit)
{
  Word seq = (class << 6) ^ unit;
  if (class == HIFI_CLASS_MINE)
    class = myclass;
  if (unit == HIFI_UNIT_ME)
    class = myunit;
  seq += (serial++);
  seq &= HIFI_SEQUENCE_MASK;
  buf = hifipkt_eword (buf, HIFI_MAGIC);
  buf = hifipkt_eaddress (buf, HIFI_CLASS_ALL, HIFI_UNIT_ALL);
  buf = hifipkt_eaddress (buf, class, unit);
  buf = hifipkt_eword (buf, seq);
  return (buf);
}

Byte *
hifipkt_ecmd (Byte * buf, Word command)
{
  buf = hifipkt_eword (buf, command);
  buf = hifipkt_eword (buf, 0);
  return buf;
}


Byte *
hifipkt_est (Byte * buf, Word status)
{
  buf = hifipkt_eword (buf, status);
  buf = hifipkt_eword (buf, 0);
  return buf;
}

Byte *
hifipkt_eeof (Byte * buf)
{
  buf = hifipkt_eword (buf, HIFI_CMD_EOF);
  return buf;
}

void
hifipkt_elen (Byte * ptr, Word len)
{
  hifipkt_eword (ptr + 2, len);
}

int
hifi_cmd (void *m, Byte class, Byte unit, Byte dclass, Byte dunit, Word cmd)
{
  Byte *ptr, *cptr, *buf;
  int ret;
  buf = ptr = hifi_getbuf ();
  ptr = hifipkt_new (ptr, class, unit, dclass, dunit);
  ptr = hifipkt_ecmd (cptr = ptr, cmd);
  hifipkt_elen (cptr, HIFI_PTR_DIFF (ptr, cptr));
  ptr = hifipkt_eeof (ptr);
  ret = hifi_send (m, buf, HIFI_PTR_DIFF (ptr, buf));
  hifi_freebuf (buf);
  return ret;
}

int
hifi_cmd_rel (void *m, Byte class, Byte unit, Byte dclass,
              Byte dunit, Word cmd)
{
  Byte *ptr, *cptr, *buf;
  int ret;
  buf = ptr = hifi_getbuf ();


  ptr = hifipkt_new_rel (ptr, class, unit, dclass, dunit);
  ptr = hifipkt_ecmd (cptr = ptr, cmd);
  hifipkt_elen (cptr, HIFI_PTR_DIFF (ptr, cptr));
  ptr = hifipkt_eeof (ptr);
  ret = hifi_send (m, buf, HIFI_PTR_DIFF (ptr, buf));
  hifi_freebuf (buf);
  //FIXME: need to check for recipt
  return ret;
}

void
hifi_new_packet (void *m, Byte * data, int len,
                 void (*callback) (Byte, Byte, Byte, Byte,
                                   Word, Byte *, Word))
{
  Word w, l;
  Byte sclass, sunit;
  Byte dclass, dunit;
  data = hifipkt_dword (data, &w);
  len -= 2;
  if (w != HIFI_MAGIC)
    return;
  data = hifipkt_daddress (data, &dclass, &dunit); /*Dest */
  len -= 2;
  data = hifipkt_daddress (data, &sclass, &sunit); /*Src */
  len -= 2;
  data = hifipkt_dword (data, &w); /*Sequence number */
  len -= 2;
/*
 *On palm data is not recvd it comes via a call back so the ack
 *finder needs to go in here:
 */
#ifdef PALM
#if 0
  if ((dclass == myclass) && (dunit == myunit) && (seq & HIFI_SEQUENCE_REL)
      {
//FIXME: Need to send ack not implemented for palm
      }
#endif

      if ((sclass == aclass) && (sunit == sunit) && (w == aseq)) isacked = 0;
#else /* PALM */

/* Not a palm send ACK frames here */
  if ((dclass == myclass) && (dunit == myunit) && (w & HIFI_SEQUENCE_REL))
    {
      Byte *ptr, *cptr, *buf;

#ifdef DEBUG
      printf ("RR: Received seq %x, acking with %x to %d.%d\n", w,
              (w & HIFI_SEQUENCE_MASK) | HIFI_SEQUENCE_ACK, sclass, sunit);
#endif
      buf = hifi_getbuf ();
      ptr = hifipkt_eword (buf, HIFI_MAGIC);
      ptr = hifipkt_eaddress (ptr, sclass, sunit);
      ptr = hifipkt_eaddress (ptr, dclass, dunit);
      ptr = hifipkt_eword (ptr, (w & HIFI_SEQUENCE_MASK) | HIFI_SEQUENCE_ACK);
      ptr = hifipkt_eeof (ptr);
      hifi_send (m, buf, HIFI_PTR_DIFF (ptr, buf));
      hifi_freebuf (buf);
    }

#endif /*PALM*/
    data = hifipkt_dword (data, &w); /*Command */
  len -= 2;
  while ((len > 0) && (w != HIFI_CMD_EOF))
    {

//if (HIFI_IS_ST(w)) {

      data = hifipkt_dword (data, &l); /*Length */
      len -= 2;
      l -= 4;
      if (l < len)
        {

          (*callback) (sclass, sunit, dclass, dunit, w, data, l);
          data += l;
          len -= l;
        }

//}else {
      //printf("failed cmd/st=%x\n",w);
      //return;
      //}

      data = hifipkt_dword (data, &w); /*Command */
      len -= 2;
    }

}

Byte *
hifipkt_eradio_frequency (Byte * ptr, Hifi_Radio_Frequency f)
{
  ptr = hifipkt_ebyte (ptr, f.band);
  ptr = hifipkt_edword (ptr, f.freq);
  return ptr;
}

Byte *
hifipkt_dradio_frequency (Byte * ptr, Hifi_Radio_Frequency * f)
{
  ptr = hifipkt_dbyte (ptr, &f->band);
  ptr = hifipkt_ddword (ptr, &f->freq);
  return ptr;
}

Byte *
hifipkt_etuner_state (Byte * ptr, Hifi_Tuner_State s)
{
  ptr = hifipkt_eradio_frequency (ptr, s.f);
  ptr = hifipkt_ebyte (ptr, s.lock);
  ptr = hifipkt_ebyte (ptr, s.stereo);
  return ptr;
}


Byte *
hifipkt_dtuner_state (Byte * ptr, Hifi_Tuner_State * s)
{
  ptr = hifipkt_dradio_frequency (ptr, &s->f);
  ptr = hifipkt_dbyte (ptr, &s->lock);
  ptr = hifipkt_dbyte (ptr, &s->stereo);
  return ptr;
}
