/*
Copyright 2006, Freie Universitaet Berlin. All rights reserved.

These sources were developed at the Freie Universität Berlin, Computer
Systems and Telematics group.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.

- 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.
 
- Neither the name of Freie Universitaet Berlin (FUB) 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 FUB and the contributors on an "as is"
basis, without any representations or warranties of any kind, express
or implied including, but not limited to, representations or
warranties of non-infringement, merchantability or fitness for a
particular purpose. In no event shall FUB 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 implementation was developed by the CST group at the FUB.

For documentation and questions please use the web site
http://scatterweb.mi.fu-berlin.de and the mailinglist
scatterweb@lists.spline.inf.fu-berlin.de (subscription via the Website).
Berlin, 2006
*/
#include <string.h>
#include <ScatterWeb.System.h>
#include "ScatterWeb.CC1020.internal.h"
#include "ScatterWeb.CC1020.h"

// For CC1020_STATUS
#define CAL_COMPLETE   (0x80)
#define PA_POWER       0x0F
#define CAL_TIMEOUT    0x7FFE
#define LOCK_TIMEOUT   0x7FFE
#define RESET_TIMEOUT  0x7FFE


#define LNA_POWER_ON   do { P2OUT &= ~0x10; }  while(0) // Enable power for LNA (P24, low-active)

uint16_t tb1;
uint16_t tb2;

struct cc1020_config	currentCC1020Config;

const struct cc1020_config DefaultConfig_19200 = {
	{
		0x01, // 0x00, MAIN
		0x0F, // 0x01, INTERFACE
		0xFF, // 0x02, RESET
		0x8F, // 0x03, SEQUENCING
		//869.525 bei 50kHz
		0x3A, // 0x04, FREQ_2A
		0x32, // 0x05, FREQ_1A
		0x97, // 0x06, FREQ_0A // 19200
		0x38, // 0x07, CLOCK_A // 19200
		0x3A, // 0x08, FREQ_2B
		0x37, // 0x09, FREQ_1B
		0xEB, // 0x0A, FREQ_0B // 19200
		0x38, // 0x0B, CLOCK_B // 19200
		0x44, // 0x0C, VCO     44
		0x51, // 0x0D, MODEM   Manchester
		0x2B, // 0x0E, DEVIATION // FSK
		0x4C, // 0x0F, AFC_CONTROL Ruetten 0xCC
		0x25, // 0x10, FILTER Bandwith 100 kHz
		0x61, // 0x11, VGA1
		0x55, // 0x12, VGA2
		0x2D, // 0x13, VGA3
		0x37, //0x29,  // 0x14, VGA4 ADJUSTED CS to 23!
		0x40, // 0x15, LOCK is Carrier SENSE
		0x76, // 0x16, FRONTEND
		0x87, // 0x17, ANALOG, RX=86/TX=87
		0x10, // 0x18, BUFF_SWING
		0x25, // 0x19, BUFF_CURRENT
		0xAE, // 0x1A, PLL_BW
		0x34, // 0x1B, CALIBRATE
		0x0F, // 0x1C, PA_POWER
		0xF0, // 0x1D, MATCH
		0x00, // 0x1E, PHASE_COMP
		0x00, // 0x1F, GAIN_COMP
		0x00, // 0x20, POWERDOWN
		0x4d, // 0x21,
		0x10, // 0x22,
		0x06, // 0x23,
		0x00, // 0x24,
		0x40, // 0x25,
		0x00, // 0x26,
		0x00, // 0x27, 
	},
	16                 
};

const struct cc1020_config DefaultConfig_115200 = {
	{
		0x01, // 0x00, MAIN
		0x0F, // 0x01, INTERFACE
		0xFF, // 0x02, RESET
		0x8F, // 0x03, SEQUENCING
		// 869.525 bei 200kHz
		0x3A, // 0x04, FREQ_2A
		0x32, // 0x05, FREQ_1A
		0x97, // 0x06, FREQ_0A // 19200
		0x29, // 0x07, CLOCK_A // 19200
		0x3A, // 0x08, FREQ_2B
		0x37, // 0x09, FREQ_1B
		0xEB, // 0x0A, FREQ_0B // 19200
		0x29, // 0x0B, CLOCK_B // 19200
		0x44, // 0x0C, VCO 44
		0x51, // 0x0D, MODEM Manchester
		0x58, // 0x0E, DEVIATION // FSK
		0x4C, // 0x0F, AFC_CONTROL Ruetten 0xCC
		0x80, // 0x10, FILTER Bandwith 500 kHz
		0x61, // 0x11, VGA1
		0x55, // 0x12, VGA2
		0x30, // 0x13, VGA3
		0x35, // 0x14, VGA4
		0x20, // 0x15, LOCK is Carrier SENSE
		0x76, // 0x16, FRONTEND
		0x87, // 0x17, ANALOG, RX=86/TX=87
		0x10, // 0x18, BUFF_SWING
		0x25, // 0x19, BUFF_CURRENT
		0xAE, // 0x1A, PLL_BW
		0x34, // 0x1B, CALIBRATE
		0x0F, // 0x1C, PA_POWER
		0xF0, // 0x1D, MATCH
		0x00, // 0x1E, PHASE_COMP
		0x00, // 0x1F, GAIN_COMP
		0x00, // 0x20, POWERDOWN
		0x4d, // 0x21,
		0x10, // 0x22,
		0x06, // 0x23,
		0x00, // 0x24,
		0x40, // 0x25,
		0x00, // 0x26,
		0x00  // 0x27,
	},
	4         
};

/**
 * @brief	Implementation of the radio interface for CC1020
 */ 
struct radio_interface Radio = {
	CC1020_NUM_CHANNELS,
	*CC1020_selectChannel,
	*CC1020_setTxPower
};

void libCC1020_Init() {
	// radio
	CC1020_reset();
	CC1020_setupPD();	
}

void CC1020_loadSettings(const struct cc1020_config* config) {
	memcpy(&currentCC1020Config, config, sizeof(struct cc1020_config));
	currentCC1020Config.registerValues[CC1020_PA_POWER] = Configuration.radio_txpower;
}

void CC1020_writeSettings() {
  uint8_t i;  
  for(i=0;i<0x27;i++)
	  CC1020_writeRegister(i, currentCC1020Config.registerValues[i]);
}

void CC1020_init() {
	if( Configuration.radio_txpower == 0 )
		Configuration.radio_txpower = PA_POWER;

	if( Configuration.radio_channel <= 0 || Configuration.radio_channel > CC1020_NUM_CHANNELS)
		Configuration.radio_channel = 1;

	CC1020_setupPD();
	CC1020_loadSettings(&DefaultConfig_19200);
	CC1020_selectChannel(Configuration.radio_channel);
}

void CC1020_reset() {
  // Reset CC1020
  CC1020_writeRegister(CC1020_MAIN, 0x0F&~0x01);
  // Bring CC1020 out of reset
  CC1020_writeRegister(CC1020_MAIN, 0x1F);
}

uint8_t _CC1020_waitlock() {
	int t;
	uint8_t status;
	
	for( t = LOCK_TIMEOUT; t > 0; t-- ) {
		status = CC1020_readRegister(CC1020_STATUS);
		if( status & LOCK_CONTINOUS )
			return LOCK_OK;
	}
	return LOCK_NOK;
}

char CC1020_calibrate() {
  unsigned char tmp;
  enum CC1020_lock_status lock_status;
  // Turn off PA to avoid spurs during calibration in TX mode
  CC1020_writeRegister(CC1020_PA_POWER,0x00);
  // Start calibration
  CC1020_writeRegister(CC1020_CALIBRATE,0xB5);
  System_delay(3); 
  while((tmp =(CC1020_readRegister(CC1020_STATUS)&CAL_COMPLETE)) == 0);
  System_delay(2); 
  // Monitor lock
  lock_status = _CC1020_waitlock();
  // Restore PA_POWER
  CC1020_writeRegister(CC1020_PA_POWER, currentCC1020Config.registerValues[CC1020_PA_POWER]);
  // Return state of LOCK_CONTINUOUS bit
  return lock_status;
}

enum CC1020_lock_status CC1020_setupRX(char RXANALOG) {
  enum CC1020_lock_status lock_status;
  
  // Switch into RX, switch to freq. reg A
  CC1020_writeRegister(CC1020_MAIN,0x11);
  // Setup bias current adjustment
  CC1020_writeRegister(CC1020_ANALOG,RXANALOG);
  System_delay(1); // Wait for 1 msec  
  
  // Monitor LOCK
  lock_status = _CC1020_waitlock();
  if( lock_status != LOCK_OK ) {
	  // (PLL out of LOCK)
	  // If recalibration ok
    if(CC1020_calibrate())
    	lock_status = LOCK_RECAL_OK; // Indicate PLL in LOCK
	// Else (recalibration failed)
  }
  
  // Switch RX part of CC1020 on
  CC1020_writeRegister(CC1020_MAIN,0x01);
  //WriteToCC1020Register(CC1020_INTERFACE,0x12);
  CC1020_writeRegister(CC1020_INTERFACE,0x02);
  
  // Return LOCK status to application
  return lock_status;
}

enum CC1020_lock_status CC1020_setupTX(char TXANALOG) {
  enum CC1020_lock_status lock_status;
  // Setup bias current adjustment
  CC1020_writeRegister(CC1020_ANALOG,TXANALOG);
  // Switch into TX, switch to freq. reg B
  CC1020_writeRegister(CC1020_MAIN,0xC1);
  System_delay(1); // Wait for 1 msec
  // Monitor LOCK, lasts 420 - 510 cycles @ 4505600 = 93 us - 113 us
  lock_status = _CC1020_waitlock();
  if( lock_status != LOCK_OK ) {
	  // (PLL out of LOCK)
	  // If recalibration ok
    if(CC1020_calibrate())
    	lock_status = LOCK_RECAL_OK; // Indicate PLL in LOCK
	// Else (recalibration failed)
  }
  // Restore PA_POWER
  CC1020_writeRegister(CC1020_PA_POWER, currentCC1020Config.registerValues[CC1020_PA_POWER]);
  // Turn OFF DCLK squelch in TX
  CC1020_writeRegister(CC1020_INTERFACE, 0x01);
  // Return LOCK status to application
  return (lock_status);
}

void CC1020_setupPD() {
  // Put CC1020 into power-down
  CC1020_writeRegister(CC1020_MAIN,0x1F);
  // Turn off PA to minimise current draw
  CC1020_writeRegister(CC1020_PA_POWER,0x00);
}

void CC1020_wakeupRX(char RXANALOG) {
  // Turn on xtal oscillator core
  CC1020_writeRegister(CC1020_MAIN,0x1B);
  // Setup bias current adjustment
  CC1020_writeRegister(CC1020_ANALOG,RXANALOG);
  // Insert wait routine here, must wait for xtal oscillator to stabilise, 
  // typically takes 2-5ms.
  System_delay(3); // DelayMs(5); // NOT NEEDED?
  // Turn on bias generator
  CC1020_writeRegister(CC1020_MAIN,0x19);
  System_delay(1) ; // NOT NEEDED?
  // Turn on frequency synthesiser
  CC1020_writeRegister(CC1020_MAIN,0x11);
}

void CC1020_wakeupTX(char TXANALOG) {
  // Turn on xtal oscillator core
  CC1020_writeRegister(CC1020_MAIN,0xDB);
  // Setup bias current adjustment
  CC1020_writeRegister(CC1020_ANALOG,TXANALOG);
  // Insert wait routine here, must wait for xtal oscillator to stabilise, 
  // typically takes 2-5ms.
  System_delay(3); // DelayMs(5); // NOT NEEDED?
  // Turn on bias generator
  CC1020_writeRegister(CC1020_MAIN,0xD9);
  System_delay(1); // NOT NEEDED?
  // Turn on frequency synthesiser
  System_delay(1);
  CC1020_writeRegister(CC1020_MAIN,0xD1);
}

uint8_t CC1020_selectChannel(uint8_t channel) {
	static const struct {
		uint8_t	tx3;
		uint8_t tx2;
		uint8_t tx1;
		uint8_t rx3;
		uint8_t rx2;
		uint8_t rx1;
	} freq_settings[] = {
		{0x3A, 0x32, 0x97, 0x3A, 0x37, 0xEB}, // ch1
		{0x36, 0xCE, 0x89, 0x36, 0xD3, 0xDD}, // ch2
		{0x37, 0x7C, 0x25, 0x37, 0x81, 0x79}, // ch3
		{0x38, 0x29, 0xC1, 0x38, 0x2F, 0x17}, // ch4
		{0x38, 0xD7, 0x5D, 0x38, 0xDC, 0xB3}, // ch5
		{0x39, 0x84, 0xF9, 0x39, 0x8A, 0x4F}, // ch6
		{0x36, 0x20, 0xEB, 0x36, 0x26, 0x41}, // ch7
		{0x3A, 0xE0, 0x33, 0x3A, 0xE5, 0x89}  // ch8
	};
	
	if( (channel == 0) || (channel > CC1020_NUM_CHANNELS) )
		return 0;

	channel--;
	currentCC1020Config.registerValues[CC1020_FREQ_2A] = freq_settings[channel].tx3;
	currentCC1020Config.registerValues[CC1020_FREQ_1A] = freq_settings[channel].tx2;
	currentCC1020Config.registerValues[CC1020_FREQ_0A] = freq_settings[channel].tx1;
	currentCC1020Config.registerValues[CC1020_FREQ_2B] = freq_settings[channel].rx3;
	currentCC1020Config.registerValues[CC1020_FREQ_2B] = freq_settings[channel].rx3;
	currentCC1020Config.registerValues[CC1020_FREQ_1B] = freq_settings[channel].rx2;
	currentCC1020Config.registerValues[CC1020_FREQ_0B] = freq_settings[channel].rx1;
	channel++;

	CC1020_reset();
	CC1020_writeSettings();
	CC1020_wakeupRX(RX_CURRENT);
	CC1020_calibrate();
	CC1020_wakeupTX(TX_CURRENT);
	CC1020_calibrate();
	CC1020_wakeupRX(RX_CURRENT);
	CC1020_setupRX(RX_CURRENT) ;
	LNA_POWER_ON;
    
    return channel;
}

uint8_t CC1020_setTxPower(uint8_t power) {
	currentCC1020Config.registerValues[CC1020_PA_POWER] = power;
	return power;
}

#if SYSTEM_CMDLEVEL >= MEDIUM
/**
 * @name CC1020
 * @{
 */

/**
 * @brief Print RSSI value: <code>rssi</code>
 * @ingroup commands
 *
 * Prints the received signal strength indicator value read from the CC1020 register.
 */
COMMAND(rssi, 0, cmdargs) {
	uint16_t myrssi;
	myrssi = CC1020_readRegister(CC1020_RSS);
	snprintf(cmdargs->response_args, cmdargs->response_bufsize,
			"%u", myrssi);
}
/** @} */
#endif

