/*
 * Copyright (c) 2005, Swedish Institute of Computer Science
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the Institute nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * This file is part of the Contiki operating system.
 *
 * @(#)$Id: tr1001.c,v 1.13 2010/03/02 22:40:39 nifi Exp $
 */
/**
 * \addtogroup esb
 * @{
 */

/**
 * \defgroup tr1001 TR1001 radio tranciever device driver
 * @{
 */

/**
 * \file
 * Device driver and packet framing for the RFM-TR1001 radio module.
 * \author Adam Dunkels <adam@sics.se>
 *
 * This file implements a device driver for the RFM-TR1001 radio
 * tranciever.
 *
 */

#include "contiki-esb.h"

#include "dev/tr1001.h"
#include "dev/radio-sensor.h"
#include "lib/me.h"
#include "lib/crc16.h"
#include "net/netstack.h"
#include "net/rime/rimestats.h"

#include <io.h>
#include <signal.h>
#include <string.h>

#ifdef TR1001_CONF_BEEP_ON_BAD_CRC
#define BEEP_ON_BAD_CRC TR1001_CONF_BEEP_ON_BAD_CRC
#else
#define BEEP_ON_BAD_CRC 1
#endif /* TR1001_CONF_BEEP_ON_BAD_CRC */

#if BEEP_ON_BAD_CRC
#include "dev/beep.h"
#define BEEP_BEEP(t) beep_beep(t)
#else
#define BEEP_BEEP(t)
#endif /* BEEP_ON_BAD_CRC */

#define RXSTATE_READY     0
#define RXSTATE_RECEIVING 1
#define RXSTATE_FULL      2

#define SYNCH1 0x3c
#define SYNCH2 0x03

#ifdef TR1001_CONF_BUFFER_SIZE
#define RXBUFSIZE TR1001_CONF_BUFFER_SIZE
#else
#define RXBUFSIZE PACKETBUF_SIZE
#endif /* TR1001_CONF_BUFFER_SIZE */

/*
 * Pending data to send when using prepare/transmit functions.
 */
static const void *pending_data;

/*
 * The buffer which holds incoming data.
 */
unsigned char tr1001_rxbuf[RXBUFSIZE];

/*
 * The length of the packet that currently is being received.
 */
static unsigned short tr1001_rxlen = 0;

/*
 * The reception state.
 */
volatile unsigned char tr1001_rxstate = RXSTATE_READY;

static uint16_t rxcrc, rxcrctmp;

/*
 * The structure of the packet header.
 */
struct tr1001_hdr {
  uint8_t len[2];  /**< The 16-bit length of the packet in network byte
		     order. */
};

/*
 * The length of the packet header.
 */
#define TR1001_HDRLEN sizeof(struct tr1001_hdr)

#define OFF 0
#define ON 1
static uint8_t onoroff = OFF;

#define NUM_SYNCHBYTES 4

void tr1001_default_rxhandler(unsigned char c);
PT_THREAD(tr1001_default_rxhandler_pt(unsigned char c));
static struct pt rxhandler_pt;

/*
 * This timer is used to keep track of when the last byte was received
 * over the radio. If the inter-byte time is too large, the packet
 * currently being received is discarded and a new packet reception is
 * initiated.
 */
static struct timer rxtimer;

static unsigned short tmp_sstrength, sstrength;
static unsigned short tmp_count;

#define DEBUG 0
#if DEBUG
#include <stdio.h>
#define LOG(...) printf(__VA_ARGS__)
#else
#define LOG(...)
#endif

/*---------------------------------------------------------------------------*/
PROCESS(tr1001_process, "TR1001 driver");
/*---------------------------------------------------------------------------*/

static int prepare_packet(const void *data, unsigned short len);
static int transmit_packet(unsigned short len);
static int receiving_packet(void);
static int pending_packet(void);
static int channel_clear(void);
static int tr1001_on(void);
static int tr1001_off(void);

const struct radio_driver tr1001_driver = {
  tr1001_init,
  prepare_packet,
  transmit_packet,
  tr1001_send,
  tr1001_read,
  channel_clear,
  receiving_packet,
  pending_packet,
  tr1001_on,
  tr1001_off
};

/*---------------------------------------------------------------------------*/
/*
 * Turn on data transmission in On-Off-Keyed mode.
 */
static void
txook(void)
{
  P3SEL = 0xf0;
  P5OUT |= 0x40;
  P5OUT &= 0x7f;
}
/*---------------------------------------------------------------------------*/
/*
 * Turn on data reception for the radio tranceiver.
 */
static void
rxon(void)
{
  P3SEL = 0xe0;
  P5OUT |= 0xc0;

  /* Enable the receiver. */
  ME1 |= URXE0;

  /* Turn on receive interrupt. */
  IE1 |= URXIE0;

}
/*---------------------------------------------------------------------------*/
/*
 * Turn off data reception for the radio tranceiver.
 */
static void
rxoff(void)
{
  P5OUT &= 0x3f;

  /* Disable the receiver. */
  ME1 &= ~URXE0;

  /* Turn off receive interrupt. */
  IE1 &= ~URXIE0;
}
/*---------------------------------------------------------------------------*/
/*
 * Clear the recevie buffer and reset the receiver state.
 */
static void
rxclear(void)
{
  tr1001_rxstate = RXSTATE_READY;
}
/*---------------------------------------------------------------------------*/
/*
 * Turn TR1001 radio transceiver off.
 */
/*---------------------------------------------------------------------------*/
static int
tr1001_off(void)
{
  if(onoroff == OFF) {
    return 1;
  }
  onoroff = OFF;
  rxoff();
  rxclear();

  ENERGEST_OFF(ENERGEST_TYPE_LISTEN);
  return 1;
}
/*---------------------------------------------------------------------------*/
/*
 * Turn TR1001 radio transceiver on.
 */
/*---------------------------------------------------------------------------*/
static int
tr1001_on(void)
{
  if(onoroff == ON) {
    return 1;
  }

  ENERGEST_ON(ENERGEST_TYPE_LISTEN);

  onoroff = ON;
  rxon();
  rxclear();
  return 1;
}
/*---------------------------------------------------------------------------*/
/*
 * Send a byte of data over the radio.
 *
 * \param b The byte to be sent.
 */
static void
send(unsigned char b)
{
  clock_time_t start;

  start = clock_time();

  /* Wait until the USART0 TX buffer is ready. */
  while((IFG1 & UTXIFG0) == 0) {
    /* Wait no more than one second. */
    if((clock_time_t)(clock_time() - start) > (clock_time_t)CLOCK_SECOND) {
      break;
    }
  }

  /* Send the byte. */
  TXBUF0 = b;
}
/*---------------------------------------------------------------------------*/
/*
 * Send a byte of data and its logical negation (all bits inverted)
 * over the radio.
 *
 * \param b The byte to be sent.
 */
static void
send2(unsigned char b)
{
  uint16_t m;
  m = me_encode(b);
  send(m >> 8);
  send(m & 0xff);
}
static uint16_t
send2_crc16(unsigned char b, uint16_t crcacc)
{
  uint16_t m;
  m = me_encode(b);
  send(m >> 8);
  send(m & 0xff);
  return crc16_add(b, crcacc);
}
/*---------------------------------------------------------------------------*/
void
tr1001_set_txpower(unsigned char p)
{
  int i;

  /* Clamp maximum power. */
  if(p > 100) {
    p = 100;
  }

  /* First, run the potentiometer down to zero so that we know the
     start value of the potentiometer. */
  P2OUT &= 0xDF;                                /* P25 = 0 (down selected) */
  P2OUT &= 0xBF;                                /* P26 = 0 (chipselect on) */
  for(i = 0; i < 102; ++i) {
    P2OUT &= 0xEF;                              /* P24 = 0 (inc) */
    P2OUT |= 0x10;
  }

  /* Now, start to increase the value of the potentiometer until it
     reaches the desired value.*/

  P2OUT |= 0x20;                                /* P25 = 1 (up selected) */
  for(i = 0; i < p; ++i) {
    P2OUT &= 0xEF;                              /* P24 = 0 (inc) */
    P2OUT |= 0x10;
  }
  P2OUT |= 0x40;                                /* P26 = 1 (chipselect off) */
}
/*---------------------------------------------------------------------------*/
int
tr1001_init(void)
{
  PT_INIT(&rxhandler_pt);

  onoroff = OFF;

  UCTL0 = CHAR;                         /* 8-bit character */
  UTCTL0 = SSEL1;                       /* UCLK = SMCLK */

  tr1001_set_speed(TR1001_19200);

  ME1 |= UTXE0 + URXE0;                 /* Enable USART0 TXD/RXD */

  /* Turn on receive interrupt. */
  IE1 |= URXIE0;

  timer_set(&rxtimer, CLOCK_SECOND / 4);


  tr1001_on();
  tr1001_set_txpower(100);

  /* Reset reception state. */
  rxclear();

  process_start(&tr1001_process, NULL);

  return 1;
}
/*---------------------------------------------------------------------------*/
interrupt (UART0RX_VECTOR)
     tr1001_rxhandler(void)
{
  ENERGEST_ON(ENERGEST_TYPE_IRQ);
  tr1001_default_rxhandler_pt(RXBUF0);
  if(tr1001_rxstate == RXSTATE_FULL) {
    LPM4_EXIT;
  }
  ENERGEST_OFF(ENERGEST_TYPE_IRQ);
}
/*---------------------------------------------------------------------------*/
#if DEBUG
static void
dump_packet(int len)
{
  int i;
  for(i = 0; i < len; ++i) {
    LOG("%d: 0x%02x\n", i, tr1001_rxbuf[i]);
  }
}
#endif /* DEBUG */
/*---------------------------------------------------------------------------*/
PT_THREAD(tr1001_default_rxhandler_pt(unsigned char incoming_byte))
{
  static unsigned char rxtmp, tmppos;

  if(timer_expired(&rxtimer) && tr1001_rxstate != RXSTATE_FULL) {
    PT_INIT(&rxhandler_pt);
  }

  timer_restart(&rxtimer);

  if(tr1001_rxstate == RXSTATE_RECEIVING) {
    unsigned short signal = radio_sensor.value(0);
    tmp_sstrength += (signal >> 2);
    tmp_count++;
  }

  PT_BEGIN(&rxhandler_pt);

  while(1) {

    /* Reset reception state. */
    rxclear();

    /* Wait until we receive the first syncronization byte. */
    PT_WAIT_UNTIL(&rxhandler_pt, incoming_byte == SYNCH1);

    tr1001_rxstate = RXSTATE_RECEIVING;

    /* Read all incoming syncronization bytes. */
    PT_WAIT_WHILE(&rxhandler_pt, incoming_byte == SYNCH1);

    /* We should receive the second synch byte by now, otherwise we'll
       restart the protothread. */
    if(incoming_byte != SYNCH2) {
      PT_RESTART(&rxhandler_pt);
    }

    /* Start signal strength measurement */
    tmp_sstrength = 0;
    tmp_count = 0;

    /* Reset the CRC. */
    rxcrc = 0xffff;

    /* Read packet header. */
    for(tmppos = 0; tmppos < TR1001_HDRLEN; ++tmppos) {

      /* Wait for the first byte of the packet to arrive. */
      PT_YIELD(&rxhandler_pt);

      /* If the incoming byte isn't a valid Manchester encoded byte,
	 we start again from the beginning. */
      if(!me_valid(incoming_byte)) {
	BEEP_BEEP(1000);
	LOG("Incorrect manchester in header at byte %d/1\n", tmppos);
	RIMESTATS_ADD(badsynch);
	PT_RESTART(&rxhandler_pt);
      }

      rxtmp = me_decode8(incoming_byte);

      /* Wait for the next byte to arrive. */
      PT_YIELD(&rxhandler_pt);

      if(!me_valid(incoming_byte)) {
	BEEP_BEEP(1000);
	LOG("Incorrect manchester in header at byte %d/2\n", tmppos);
	RIMESTATS_ADD(badsynch);
	PT_RESTART(&rxhandler_pt);
      }

      /* Put together the two bytes into a single Manchester decoded
	 byte. */

      tr1001_rxbuf[tmppos] = (rxtmp << 4) | me_decode8(incoming_byte);

      /* Calculate the CRC. */
      rxcrc = crc16_add(tr1001_rxbuf[tmppos], rxcrc);


    }

    /* Since we've got the header, we can grab the length from it. */
    tr1001_rxlen = ((((struct tr1001_hdr *)tr1001_rxbuf)->len[0] << 8) +
		    ((struct tr1001_hdr *)tr1001_rxbuf)->len[1]);

    /* If the length is longer than we can handle, we'll start from
       the beginning. */
    if(tmppos + tr1001_rxlen > sizeof(tr1001_rxbuf)) {
      RIMESTATS_ADD(toolong);
      PT_RESTART(&rxhandler_pt);
    }

    /* Read packet data. */
    for(; tmppos < tr1001_rxlen + TR1001_HDRLEN; ++tmppos) {
      PT_YIELD(&rxhandler_pt);

      if(!me_valid(incoming_byte)) {
	LOG("Incorrect manchester 0x%02x at byte %d/1\n", incoming_byte,
	    tmppos - TR1001_HDRLEN);
	BEEP_BEEP(1000);
	RIMESTATS_ADD(badsynch);
	PT_RESTART(&rxhandler_pt);
      }

      rxtmp = me_decode8(incoming_byte);

      PT_YIELD(&rxhandler_pt);

      if(!me_valid(incoming_byte)) {
	LOG("Incorrect manchester at byte %d/2\n", tmppos - TR1001_HDRLEN);
	BEEP_BEEP(1000);
	RIMESTATS_ADD(badsynch);
	PT_RESTART(&rxhandler_pt);
      }

      tr1001_rxbuf[tmppos] = (rxtmp << 4) | me_decode8(incoming_byte);
      rxcrc = crc16_add(tr1001_rxbuf[tmppos], rxcrc);

    }

    /* Read the frame CRC. */
    for(tmppos = 0; tmppos < 4; ++tmppos) {

      PT_YIELD(&rxhandler_pt);

      if(!me_valid(incoming_byte)) {
	BEEP_BEEP(1000);
	RIMESTATS_ADD(badsynch);
	PT_RESTART(&rxhandler_pt);
      }

      rxcrctmp = (rxcrctmp << 4) | me_decode8(incoming_byte);
    }

    if(rxcrctmp == rxcrc) {
      /* A full packet has been received and the CRC checks out. We'll
	 request the driver to take care of the incoming data. */

      RIMESTATS_ADD(llrx);
      process_poll(&tr1001_process);

      /* We'll set the receive state flag to signal that a full frame
	 is present in the buffer, and we'll wait until the buffer has
	 been taken care of. */
      tr1001_rxstate = RXSTATE_FULL;
      PT_WAIT_UNTIL(&rxhandler_pt, tr1001_rxstate != RXSTATE_FULL);

    } else {
      LOG("Incorrect CRC\n");
      BEEP_BEEP(1000);
      RIMESTATS_ADD(badcrc);
    }
  }
  PT_END(&rxhandler_pt);
}
/*---------------------------------------------------------------------------*/
static int
prepare_packet(const void *data, unsigned short len)
{
  pending_data = data;
  return 0;
}
/*---------------------------------------------------------------------------*/
static int
transmit_packet(unsigned short len)
{
  int ret = RADIO_TX_ERR;
  if(pending_data != NULL) {
    ret = tr1001_send(pending_data, len);
    pending_data = NULL;
  }
  return ret;
}
/*---------------------------------------------------------------------------*/
int
tr1001_send(const void *packet, unsigned short len)
{
  int i;
  uint16_t crc16;

  LOG("tr1001_send: sending %d bytes\n", len);

  if(onoroff == ON) {
    ENERGEST_OFF(ENERGEST_TYPE_LISTEN);
  }
  ENERGEST_ON(ENERGEST_TYPE_TRANSMIT);

  /* Prepare the transmission. */

  /* Delay the transmission for a short random duration. */
  clock_delay(random_rand() & 0x3ff);


  /* Check that we don't currently are receiving a packet, and if so
     we wait until the reception has been completed. Reception is done
     with interrupts so it is OK for us to wait in a while() loop. */

  while(tr1001_rxstate == RXSTATE_RECEIVING &&
	!timer_expired(&rxtimer)) {
    /* Delay the transmission for a short random duration. */
    clock_delay(random_rand() & 0x7ff);
  }


  /* Turn on OOK mode with transmission. */
  txook();

  /* According to the datasheet, the transmitter must wait for 12 us
     in order to settle. Empirical tests show that is it better to
     wait for something like 283 us... */
  clock_delay(200);


  /* Transmit preamble and synch bytes. */
  for(i = 0; i < 20; ++i) {
    send(0xaa);
  }
  /*  send(0xaa);
      send(0xaa);*/
  send(0xff);

  for(i = 0; i < NUM_SYNCHBYTES; ++i) {
    send(SYNCH1);
  }
  send(SYNCH2);

  crc16 = 0xffff;

  /* Send packet header. */
  crc16 = send2_crc16(len >> 8, crc16);
  crc16 = send2_crc16(len & 0xff, crc16);

  /* Send packet data. */
  for(i = 0; i < len; ++i) {
    crc16 = send2_crc16(((uint8_t *)packet)[i], crc16);
  }

  /* Send CRC */
  send2(crc16 >> 8);
  send2(crc16 & 0xff);

  /* Send trailing bytes. */
  send(0x33);
  send(0xcc);
  send(0x33);
  send(0xcc);

  /* Turn on (or off) reception again. */
  if(onoroff == ON) {
    ENERGEST_ON(ENERGEST_TYPE_LISTEN);
    rxon();
    rxclear();
  } else {
    rxoff();
    rxclear();
  }

  ENERGEST_OFF(ENERGEST_TYPE_TRANSMIT);
  RIMESTATS_ADD(lltx);

  return RADIO_TX_OK;
}
/*---------------------------------------------------------------------------*/
int
tr1001_read(void *buf, unsigned short bufsize)
{
  unsigned short tmplen;

  if(tr1001_rxstate == RXSTATE_FULL) {

#if DEBUG
    dump_packet(tr1001_rxlen + 2);
#endif /* DEBUG */

    tmplen = tr1001_rxlen;

    if(tmplen > bufsize) {
      LOG("tr1001_read: too large packet: %d/%d bytes\n", tmplen, bufsize);
      rxclear();
      RIMESTATS_ADD(toolong);
      return -1;
    }

    memcpy(buf, &tr1001_rxbuf[TR1001_HDRLEN], tmplen);

    /* header + content + CRC */
    sstrength = (tmp_count ? ((tmp_sstrength / tmp_count) << 2) : 0);

    rxclear();

    LOG("tr1001_read: got %d bytes\n", tmplen);

    return tmplen;
  }
  return 0;
}
/*---------------------------------------------------------------------------*/
static int
receiving_packet(void)
{
  return tr1001_rxstate == RXSTATE_RECEIVING &&
    !timer_expired(&rxtimer);
}
/*---------------------------------------------------------------------------*/
static int
pending_packet(void)
{
  return tr1001_rxstate == RXSTATE_FULL;
}
/*---------------------------------------------------------------------------*/
static int
channel_clear(void)
{
  /* TODO add CCA functionality */
  return 0;
}
/*---------------------------------------------------------------------------*/
PROCESS_THREAD(tr1001_process, ev, data)
{
  int len;
  PROCESS_BEGIN();

  /* Reset reception state now that the process is ready to receive data. */
  rxclear();

  while(1) {
    PROCESS_YIELD_UNTIL(ev == PROCESS_EVENT_POLL);
    packetbuf_clear();
    len = tr1001_read(packetbuf_dataptr(), PACKETBUF_SIZE);
    if(len > 0) {
      packetbuf_set_datalen(len);
      NETSTACK_RDC.input();
    }
  }

  PROCESS_END();
}
/*---------------------------------------------------------------------------*/
void
tr1001_set_speed(unsigned char speed)
{

  if(speed == TR1001_19200) {
    /* Set TR1001 to 19200 */
    UBR00 = 0x80;                         /* 2,457MHz/19200 = 128 -> 0x80 */
    UBR10 = 0x00;                         /* */
    UMCTL0 = 0x00;                        /* no modulation  */
  } else if(speed == TR1001_38400) {
    /* Set TR1001 to 38400 */
    UBR00 = 0x40;                         /* 2,457MHz/38400 = 64 -> 0x40 */
    UBR10 = 0x00;                         /* */
    UMCTL0 = 0x00;                        /* no modulation  */
  } else if(speed == TR1001_57600) {
    UBR00 = 0x2a;                         /* 2,457MHz/57600 = 42.7 -> 0x2A */
    UBR10 = 0x00;                         /* */
    UMCTL0 = 0x5b;                        /* */
  } else if(speed == TR1001_115200) {
    UBR00 = 0x15;                         /* 2,457MHz/115200 = 21.4 -> 0x15 */
    UBR10 = 0x00;                         /* */
    UMCTL0 = 0x4a;                        /* */
  } else {
    tr1001_set_speed(TR1001_19200);
  }
}
/*---------------------------------------------------------------------------*/
unsigned short
tr1001_sstrength(void)
{
  return sstrength;
}
/*---------------------------------------------------------------------------*/
/** @} */
/** @} */