/*
 This project 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.

 Multiprotocol 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 Multiprotocol.  If not, see <http://www.gnu.org/licenses/>.
 */
//**************************
// Telemetry serial code   *
//**************************
#if defined TELEMETRY

uint8_t RetrySequence ;

#ifdef MULTI_TELEMETRY
	uint32_t lastMulti = 0;
	#define MULTI_TIME				500	//in ms
	#ifdef MULTI_SYNC
		#define INPUT_SYNC_TIME			100	//in ms
		#define INPUT_ADDITIONAL_DELAY	100	// in 10µs, 100 => 1000 µs
		uint32_t lastInputSync = 0;
		uint16_t inputDelay = 0;
	#endif // MULTI_SYNC
#endif // MULTI_TELEMETRY

#if defined SPORT_TELEMETRY	
	#define FRSKY_SPORT_PACKET_SIZE   8
	#define FX_BUFFERS	4
	uint8_t Sport_Data = 0;
	uint8_t pktx1[FRSKY_SPORT_PACKET_SIZE*FX_BUFFERS];

	// Store for out of sequence packet
	uint8_t FrSkyX_RX_ValidSeq ;
	struct t_FrSkyX_RX_Frame
	{
		boolean valid;
		uint8_t count;
		uint8_t payload[6];
	} ;

	// Store for FrskyX telemetry
	struct t_FrSkyX_RX_Frame FrSkyX_RX_Frames[4] ;
	uint8_t FrSkyX_RX_NextFrame=0;
#endif // SPORT_TELEMETRY

#if defined HUB_TELEMETRY
	#define USER_MAX_BYTES 6
	uint8_t prev_index;

	struct t_FrSkyD_User_Frame
	{
		uint8_t ID;
		uint8_t low;
		uint8_t high;
	} FrSkyD_User_Frame[8];
	uint8_t FrSkyD_User_Frame_Start=0, FrSkyD_User_Frame_End=0;
#endif // HUB_TELEMETRY

#define START_STOP	0x7e
#define BYTESTUFF	0x7d
#define STUFF_MASK	0x20
#define MAX_PKTX	10
uint8_t pktx[MAX_PKTX];
uint8_t frame[18];

#ifdef MULTI_TELEMETRY
static void multi_send_header(uint8_t type, uint8_t len)
{
	Serial_write('M');
	Serial_write('P');
	Serial_write(type);
	Serial_write(len);
}

#ifdef MULTI_SYNC
static void telemetry_set_input_sync(uint16_t refreshRate)
{
	#if defined(STM32_BOARD) && defined(DEBUG_PIN)
		static uint8_t c=0;
		if (c++%2==0)
		{	DEBUG_PIN_on;	}
		else
		{	DEBUG_PIN_off;	}
	#endif
	// Only record input Delay after a frame has really been received
	// Otherwise protocols with faster refresh rates then the TX sends (e.g. 3ms vs 6ms) will screw up the calcualtion
	inputRefreshRate = refreshRate;
	if (last_serial_input != 0)
	{
		cli();										// Disable global int due to RW of 16 bits registers
		inputDelay = TCNT1;
		sei();										// Enable global int
		//inputDelay = (inputDelay - last_serial_input)>>1;
		inputDelay -= last_serial_input;
		//if(inputDelay & 0x8000)
		//	inputDelay = inputDelay - 0x8000;
		last_serial_input=0;
	}
}
#endif

#ifdef MULTI_SYNC
	static void mult_send_inputsync()
	{
		multi_send_header(MULTI_TELEMETRY_SYNC, 6);
		Serial_write(inputRefreshRate >> 8);
		Serial_write(inputRefreshRate & 0xff);
//		Serial_write(inputDelay >> 8);
//		Serial_write(inputDelay & 0xff);
		Serial_write(inputDelay >> 9);
		Serial_write(inputDelay >> 1);
		Serial_write(INPUT_SYNC_TIME);
		Serial_write(INPUT_ADDITIONAL_DELAY);
	}
#endif //MULTI_SYNC

static void multi_send_status()
{
	if(protocol == 0) return;
	
	multi_send_header(MULTI_TELEMETRY_STATUS, 24);

	// Build flags
	uint8_t flags=0;
	if(IS_INPUT_SIGNAL_on)
		flags |= 0x01;
	if(mode_select==MODE_SERIAL)
		flags |= 0x02;
	if(remote_callback != 0)
	{
		if(multi_protocols_index != 0xFF && IS_SUB_PROTO_VALID)
			flags |= 0x04;				//Invalid protocol / sub protocol, can't make the distinction since there is no more flags...
		if(IS_WAIT_BIND_on)
			flags |= 0x10;
		else
			if(IS_BIND_IN_PROGRESS)
				flags |= 0x08;
		if(multi_protocols_index != 0xFF)
		{
			if(multi_protocols[multi_protocols_index].chMap)
				flags |= 0x40;			//Disable_ch_mapping supported
			#ifdef FAILSAFE_ENABLE
				if(multi_protocols[multi_protocols_index].failSafe)
					flags |= 0x20;		//Failsafe supported
			#endif
		}
		if(IS_DATA_BUFFER_LOW_on)
			flags |= 0x80;
	}
	Serial_write(flags);
	
	// Version number example: 1.1.6.1
	Serial_write(VERSION_MAJOR);
	Serial_write(VERSION_MINOR);
	Serial_write(VERSION_REVISION);
	Serial_write(VERSION_PATCH_LEVEL);

	// Channel order
	Serial_write(RUDDER<<6|THROTTLE<<4|ELEVATOR<<2|AILERON);
	
	if(multi_protocols_index == 0xFF)													// selection out of list... send first available protocol
	{
		Serial_write(multi_protocols[0].protocol);										// begining of list
		Serial_write(multi_protocols[0].protocol);										// begining of list
		for(uint8_t i=0;i<16;i++)
			Serial_write(0x00);															// everything else is invalid
	}
	else
	{
		// Protocol next/prev
		if(multi_protocols[multi_protocols_index+1].protocol != 0xFF)
		{
			if(multi_protocols[multi_protocols_index+1].protocol == PROTO_SCANNER )//|| multi_protocols[multi_protocols_index+1].protocol == PROTO_CONFIG )
			{// if next is scanner
				if(multi_protocols[multi_protocols_index+2].protocol != 0xFF)
					Serial_write(multi_protocols[multi_protocols_index+2].protocol);	// skip to next protocol number
				else
					Serial_write(multi_protocols[multi_protocols_index].protocol);		// or end of list
			}
			else
				Serial_write(multi_protocols[multi_protocols_index+1].protocol);		// next protocol number
		}
		else
			Serial_write(multi_protocols[multi_protocols_index].protocol);				// end of list
		if(multi_protocols_index>0 && multi_protocols[multi_protocols_index-1].protocol != 0)
		{
			if(multi_protocols[multi_protocols_index-1].protocol == PROTO_SCANNER )//|| multi_protocols[multi_protocols_index-1].protocol == PROTO_CONFIG )
			{// if prev is scanner
				if(multi_protocols_index > 1)
					Serial_write(multi_protocols[multi_protocols_index-2].protocol);	// skip to prev protocol number
				else
					Serial_write(multi_protocols[multi_protocols_index].protocol);		// begining of list
			}
			else
				Serial_write(multi_protocols[multi_protocols_index-1].protocol);		// prev protocol number
		}
		else
			Serial_write(multi_protocols[multi_protocols_index].protocol);				// begining of list
		// Protocol
		for(uint8_t i=0;i<7;i++)
			Serial_write(multi_protocols[multi_protocols_index].ProtoString[i]);		// protocol name
		// Sub-protocol
		uint8_t nbr=multi_protocols[multi_protocols_index].nbrSubProto;
		if(option_override>0x0F)
			Serial_write(nbr | (multi_protocols[multi_protocols_index].optionType<<4));	// number of sub protocols && option type
		else
			Serial_write(nbr | (option_override<<4));									// number of sub protocols && option_override type
		uint8_t j=0;
		if(IS_SUB_PROTO_VALID)
		{
			uint8_t len=multi_protocols[multi_protocols_index].SubProtoString[0];
			uint8_t offset=len*(sub_protocol&0x07)+1;
			for(;j<len;j++)
				Serial_write(multi_protocols[multi_protocols_index].SubProtoString[j+offset]);	// current sub protocol name
		}
		for(;j<8;j++)
			Serial_write(0x00);
	}
}

#ifdef MULTI_CONFIG_INO
	void CONFIG_frame()
	{
		multi_send_header(MULTI_TELEMETRY_CONFIG, 21);
		for (uint8_t i = 0; i < 21; i++)		// Config data
			Serial_write(packet_in[i]);
	}
#endif

#ifdef MLINK_FW_TELEMETRY
	void MLINK_frame()
	{
		multi_send_header(MULTI_TELEMETRY_MLINK, 10);
		Serial_write(TX_RSSI);					// RSSI
		Serial_write(TX_LQI);					// LQI
		for (uint8_t i = 0; i < 8; i++)			// followed by 8 bytes of telemetry data
			Serial_write(packet_in[i]);
	}
#endif

#ifdef DSM_TELEMETRY
	void DSM_frame()
	{
		if (packet_in[0] == 0x80)
		{
			multi_send_header(MULTI_TELEMETRY_DSMBIND, 10);
			for (uint8_t i = 1; i < 11; i++) 	// 10 bytes of DSM bind response
				Serial_write(packet_in[i]);

		}
		else
		{
			multi_send_header(MULTI_TELEMETRY_DSM, 17);
			for (uint8_t i = 0; i < 17; i++)	// RSSI value followed by 16 bytes of telemetry data
				Serial_write(packet_in[i]);
		}
	}
#endif

#ifdef SCANNER_TELEMETRY
	void spectrum_scanner_frame()
	{
		multi_send_header(MULTI_TELEMETRY_SCANNER, SCAN_CHANS_PER_PACKET + 1);
		Serial_write(packet_in[0]);					// start channel
		for(uint8_t ch = 0; ch < SCAN_CHANS_PER_PACKET; ch++)
			Serial_write(packet_in[ch+1]);			// RSSI power levels
	}
#endif

#if defined (FRSKY_RX_TELEMETRY) || defined (AFHDS2A_RX_TELEMETRY) || defined (BAYANG_RX_TELEMETRY) || defined (DSM_RX_CYRF6936_INO)
	void receiver_channels_frame()
	{
		uint16_t len = packet_in[3] * 11;			// 11 bit per channel
		if (len % 8 == 0)
			len = 4 + (len / 8);
		else
			len = 5 + (len / 8);
		multi_send_header(MULTI_TELEMETRY_RX_CHANNELS, len);
		for (uint8_t i = 0; i < len; i++)
			Serial_write(packet_in[i]);				// pps, rssi, ch start, ch count, 16x ch data
	}
#endif

#ifdef AFHDS2A_FW_TELEMETRY
	void AFHDSA_short_frame()
	{
		multi_send_header(packet_in[29]==0xAA?MULTI_TELEMETRY_AFHDS2A:MULTI_TELEMETRY_AFHDS2A_AC, 29);
		for (uint8_t i = 0; i < 29; i++)			// RSSI value followed by 4*7 bytes of telemetry data
			Serial_write(packet_in[i]);
	}
#endif

#ifdef HITEC_FW_TELEMETRY
	void HITEC_short_frame()
	{
		multi_send_header(MULTI_TELEMETRY_HITEC, 8);
		for (uint8_t i = 0; i < 8; i++)				// TX RSSI and TX LQI values followed by frame number and 5 bytes of telemetry data
			Serial_write(packet_in[i]);
	}
#endif

#ifdef HOTT_FW_TELEMETRY
	void HOTT_short_frame()
	{
		multi_send_header(MULTI_TELEMETRY_HOTT, 15);
		for (uint8_t i = 0; i < 15; i++)			// TX RSSI and TX LQI values followed by frame number and telemetry data
			Serial_write(packet_in[i]);
	}
#endif

#ifdef RLINK_HUB_TELEMETRY
	void RLINK_raw_frame()
	{
		multi_send_header(MULTI_TELEMETRY_RLINK, packet_in[0]);
		for (uint8_t i = 1; i <= packet_in[0]; i++)	// raw DumboRC reply payload
			Serial_write(packet_in[i]);
	}
#endif

static void multi_send_frskyhub()
{
	multi_send_header(MULTI_TELEMETRY_HUB, 9);
	for (uint8_t i = 0; i < 9; i++)
		Serial_write(frame[i]);
}

#endif //MULTI_TELEMETRY

void frskySendStuffed()
{
	Serial_write(START_STOP);
	for (uint8_t i = 0; i < 9; i++)
	{
		if ((frame[i] == START_STOP) || (frame[i] == BYTESTUFF))
		{
			Serial_write(BYTESTUFF);
			frame[i] ^= STUFF_MASK;
		}
		Serial_write(frame[i]);
	}
	Serial_write(START_STOP);
}

bool frsky_process_telemetry(uint8_t *buffer,uint8_t len)
{
	if(protocol!=PROTO_FRSKY_R9)
	{
		if(buffer[1] != rx_tx_addr[3] || buffer[2] != rx_tx_addr[2] || len != buffer[0] + 3 )
			return false;										// Bad address or length...
		// RSSI and LQI are the 2 last bytes
		TX_RSSI = buffer[len-2];
		if(TX_RSSI >=128)
			TX_RSSI -= 128;
		else
			TX_RSSI += 128;
	}
	telemetry_link|=1;								// Telemetry data is available

#if defined FRSKYD_CC2500_INO
	if (protocol==PROTO_FRSKYD)
	{
		TX_LQI = buffer[len-1]&0x7F;
		//Save current buffer
		for (uint8_t i=3;i<len-2;i++)
			telemetry_in_buffer[i]=buffer[i];	// Buffer telemetry values to be sent
	
		//Check incoming telemetry sequence
		if(telemetry_in_buffer[6]>0 && telemetry_in_buffer[6]<=10)
		{ //Telemetry length ok
			if ( ( telemetry_in_buffer[7] & 0x1F ) == (telemetry_counter & 0x1F) )
			{//Sequence is ok
				uint8_t topBit = 0 ;
				if ( telemetry_counter & 0x80 )
					if ( ( telemetry_counter & 0x1F ) != RetrySequence )
						topBit = 0x80 ;
				telemetry_counter = ( (telemetry_counter+1)%32 ) | topBit ;	// Request next telemetry frame
			}
			else
			{//Incorrect sequence
				RetrySequence = telemetry_in_buffer[7] & 0x1F ;
				telemetry_counter |= 0x80 ;
				telemetry_in_buffer[6]=0 ;			// Discard current packet and wait for retransmit
			}
		}
		else
			telemetry_in_buffer[6]=0; 				// Discard packet
	}
#endif

#if defined SPORT_TELEMETRY && (defined FRSKYX_CC2500_INO || defined FRSKYR9_SX1276_INO)
	if (protocol==PROTO_FRSKYX||protocol==PROTO_FRSKYX2)
	{
		/*Telemetry frames(RF) SPORT info 
		15 bytes payload
		SPORT frame valid 6+3 bytes
		[00] PKLEN  0E 0E 0E 0E 
		[01] TXID1  DD DD DD DD 
		[02] TXID2  6D 6D 6D 6D 
		[03] CONST  02 02 02 02 
		[04] RS/RB  2C D0 2C CE	//D0;CE=2*RSSI;....2C = RX battery voltage(5V from Bec)
		[05] HD-SK  03 10 21 32	//TX/RX telemetry hand-shake bytes
		[06] NO.BT  00 00 06 03	//No.of valid SPORT frame bytes in the frame		
		[07] STRM1  00 00 7E 00 
		[08] STRM2  00 00 1A 00 
		[09] STRM3  00 00 10 00 
		[10] STRM4  03 03 03 03  
		[11] STRM5  F1 F1 F1 F1 
		[12] STRM6  D1 D1 D0 D0
		[13] CHKSUM1 --|2 CRC bytes sent by RX (calculated on RX side crc16/table)
		[14] CHKSUM2 --|*/
		//len=17 -> len-7=10 -> 3..12
		uint16_t lcrc = FrSkyX_crc(&buffer[3], len-7 ) ;
		if ( ( (lcrc >> 8) != buffer[len-4]) || ( (lcrc & 0x00FF ) != buffer[len-3]) )
			return false;									// Bad CRC
		
		if(buffer[4] & 0x80)
			RX_RSSI=buffer[4] & 0x7F ;
		else
			v_lipo1 = (buffer[4]<<1) + 1 ;
		#if defined(TELEMETRY_FRSKYX_TO_FRSKYD) && defined(ENABLE_PPM)
			if(mode_select != MODE_SERIAL)
				return true;
		#endif
	}
	if (protocol==PROTO_FRSKYX||protocol==PROTO_FRSKYX2||protocol==PROTO_FRSKY_R9)
	{
		telemetry_lost=0;

		//Save outgoing telemetry sequence
		FrSkyX_TX_IN_Seq=buffer[5] >> 4;

		//Check incoming telemetry sequence
		uint8_t packet_seq=buffer[5] & 0x03;
		if ( buffer[5] & 0x08 )
		{//Request init
			FrSkyX_RX_Seq = 0x08 ;
			FrSkyX_RX_NextFrame = 0x00 ;
			FrSkyX_RX_Frames[0].valid = false ;
			FrSkyX_RX_Frames[1].valid = false ;
			FrSkyX_RX_Frames[2].valid = false ;
			FrSkyX_RX_Frames[3].valid = false ;
		}
		else if ( packet_seq == (FrSkyX_RX_Seq & 0x03 ) )
		{//In sequence
			struct t_FrSkyX_RX_Frame *p ;
			uint8_t count ;
			// buffer[4] RSSI
			// buffer[5] sequence control
			// buffer[6] payload count
			// buffer[7-12] payload			
			p = &FrSkyX_RX_Frames[packet_seq] ;
			count = buffer[6];					// Payload length
			if ( count <= 6 )
			{//Store payload
				p->count = count ;
				for ( uint8_t i = 0 ; i < count ; i++ )
					p->payload[i] = buffer[i+7] ;
			}
			else
				p->count = 0 ;					// Discard
			p->valid = true ;

			FrSkyX_RX_Seq = ( FrSkyX_RX_Seq + 1 ) & 0x03 ;	// Move to next sequence

			if ( FrSkyX_RX_ValidSeq & 0x80 )
			{
				FrSkyX_RX_Seq = ( FrSkyX_RX_ValidSeq + 1 ) & 3 ;
				FrSkyX_RX_ValidSeq &= 0x7F ;
			}

		}
		else
		{//Not in sequence
			struct t_FrSkyX_RX_Frame *q ;
			uint8_t count ;
			// buffer[4] RSSI
			// buffer[5] sequence control
			// buffer[6] payload count
			// buffer[7-12] payload			
			if ( packet_seq == ( ( FrSkyX_RX_Seq +1 ) & 3 ) )
			{//Received next sequence -> save it
				q = &FrSkyX_RX_Frames[packet_seq] ;
				count = buffer[6];				// Payload length
				if ( count <= 6 )
				{//Store payload
					q->count = count ;
					for ( uint8_t i = 0 ; i < count ; i++ )
						q->payload[i] = buffer[i+7] ;
				}
				else
					q->count = 0 ;
				q->valid = true ;
			
				FrSkyX_RX_ValidSeq = 0x80 | packet_seq ;
			}
			FrSkyX_RX_Seq = ( FrSkyX_RX_Seq & 0x03 ) | 0x04 ;	// Request re-transmission of original sequence
		}
	}
#endif
	return true;
}

void init_frskyd_link_telemetry()
{
	telemetry_link=0;
	telemetry_counter=0;
	telemetry_lost=1;
	v_lipo1=0;
	v_lipo2=0;
	RX_RSSI=0;
	TX_RSSI=0;
	RX_LQI=0;
	TX_LQI=0;
	#if defined HUB_TELEMETRY
		FrSkyD_User_Frame_Start=FrSkyD_User_Frame_End=0;
	#endif
}

void frsky_link_frame()
{
	frame[0] = 0xFE;			// Link frame
	if (protocol==PROTO_FRSKYD)
	{		
		frame[1] = telemetry_in_buffer[3];		// A1
		frame[2] = telemetry_in_buffer[4];		// A2
		frame[3] = telemetry_in_buffer[5];		// RX_RSSI
		telemetry_link &= ~1 ;		// Sent
		telemetry_link |= 2 ;		// Send hub if available
	}
	else
	{//PROTO_HUBSAN, PROTO_AFHDS2A, PROTO_BAYANG, PROTO_NCC1701, PROTO_CABELL, PROTO_HITEC, PROTO_BUGS, PROTO_BUGSMINI, PROTO_FRSKYX, PROTO_FRSKYX2, PROTO_PROPEL, PROTO_DEVO, PROTO_RLINK, PROTO_OMP, PROTO_WFLY2, PROTO_LOLI, PROTO_MLINK, PROTO_MT99XX
		frame[1] = v_lipo1;
		frame[2] = v_lipo2;
		frame[3] = RX_RSSI;
		telemetry_link &= ~1 ;		// Sent
	}
	frame[4] = TX_RSSI;
	frame[5] = RX_LQI;
	frame[6] = TX_LQI;
	frame[7] = frame[8] = 0;
	#if defined MULTI_TELEMETRY
		multi_send_frskyhub();
	#else
		frskySendStuffed();
	#endif
}

#if defined HUB_TELEMETRY
void frsky_user_frame()
{
	if(telemetry_in_buffer[6])
	{//only send valid hub frames
		frame[0] = 0xFD;				// user frame
		if(telemetry_in_buffer[6]>USER_MAX_BYTES)
		{
			frame[1]=USER_MAX_BYTES;	// packet size
			telemetry_in_buffer[6]-=USER_MAX_BYTES;
			telemetry_link |= 2 ;			// 2 packets need to be sent
		}
		else
		{
			frame[1]=telemetry_in_buffer[6];			// packet size
			telemetry_link &= ~2;			// only 1 packet or processing second packet
		}
		frame[2] = telemetry_in_buffer[7];
		for(uint8_t i=0;i<USER_MAX_BYTES;i++)
			frame[i+3]=telemetry_in_buffer[i+8];
		if(telemetry_link & 2)				// prepare the content of second packet
			for(uint8_t i=8;i<USER_MAX_BYTES+8;i++)
				telemetry_in_buffer[i]=telemetry_in_buffer[i+USER_MAX_BYTES];
		#if defined MULTI_TELEMETRY
			multi_send_frskyhub();
		#else
			frskySendStuffed();
		#endif
	}
	else
		telemetry_link &= ~2;
}
/*
HuB RX packets.
packet_in[6]|(counter++)|00 01 02 03 04 05 06 07 08 09 
        %32        
01     08          5E 28 12 00 5E 5E 3A 06 00 5E
0A     09          28 12 00 5E 5E 3A 06 00 5E 5E  
09     0A          3B 09 00 5E 5E 06 36 7D 5E 5E 
03     0B          5E 28 11 00 5E 5E 06 06 6C 5E
0A     0C          00 5E 5E 3A 06 00 5E 5E 3B 09
07     0D          00 5E 5E 06 06 6C 5E 16 72 5E
05     0E          5E 28 11 00 5E 5E 3A 06 00 5E
0A     0F          5E 3A 06 00 5E 5E 3B 09 00 5E
05     10          5E 06 16 72 5E 5E 3A 06 00 5E
*/
static void __attribute__((unused)) frsky_write_user_frame(uint8_t ID, uint8_t low, uint8_t high)
{
	telemetry_in_buffer[6]  = 0x04;		// number of bytes in the payload
	telemetry_in_buffer[7]  = 0x00;		// unknown?
	telemetry_in_buffer[8]  = 0x5E;		// start of payload
	telemetry_in_buffer[9]  = ID;		// ID must be less than 0x40
	uint8_t pos=10;
	uint8_t value = low;
	for(uint8_t i=0;i<2;i++)
	{// Byte stuffing
		if(value == 0x5D || value == 0x5E)
		{// Byte stuffing
			telemetry_in_buffer[pos+1] = value ^ 0x60;
			telemetry_in_buffer[pos] = 0x5D;
			telemetry_in_buffer[6]++;	// 1 more byte in the payload
			pos += 2;
		}
		else
			telemetry_in_buffer[pos++] = value;
		value = high;
	}
	telemetry_link |= 2;				// request to send frame
}

static void __attribute__((unused)) frsky_send_user_frame(uint8_t ID, uint8_t low, uint8_t high)
{
	if(telemetry_link&2)
	{ // add to buffer
		uint8_t test = (FrSkyD_User_Frame_End + 1) & 0x07;
		if(test == FrSkyD_User_Frame_Start)
			return;	// buffer full...
		FrSkyD_User_Frame_End = test;
		FrSkyD_User_Frame[FrSkyD_User_Frame_End].ID   = ID;
		FrSkyD_User_Frame[FrSkyD_User_Frame_End].low  = low;
		FrSkyD_User_Frame[FrSkyD_User_Frame_End].high = high;
	}
	else // send to TX direct
		frsky_write_user_frame(ID, low, high);
}

static void __attribute__((unused)) frsky_check_user_frame()
{
	if((telemetry_link&2) || FrSkyD_User_Frame_Start == FrSkyD_User_Frame_End)
		return;		// need to wait that the last frame is sent or buffer is empty
	frsky_write_user_frame(FrSkyD_User_Frame[FrSkyD_User_Frame_Start].ID, FrSkyD_User_Frame[FrSkyD_User_Frame_Start].low, FrSkyD_User_Frame[FrSkyD_User_Frame_Start].high);
	FrSkyD_User_Frame_Start++;
	FrSkyD_User_Frame_Start &= 0x07;
}
#endif


#if defined SPORT_TELEMETRY
/* SPORT details serial
	100K 8E2 normal-multiprotocol
	-every 12ms-or multiple of 12; %36
	1  2  3  4  5  6  7  8  9  CRC DESCR
	7E 98 10 05 F1 20 23 0F 00 A6 SWR_ID 
	7E 98 10 01 F1 33 00 00 00 C9 RSSI_ID 
	7E 98 10 04 F1 58 00 00 00 A1 BATT_ID 
	7E BA 10 03 F1 E2 00 00 00 18 ADC2_ID 
	7E BA 10 03 F1 E2 00 00 00 18 ADC2_ID 
	7E BA 10 03 F1 E2 00 00 00 18 ADC2_ID 
	7E BA 10 03 F1 E2 00 00 00 18 ADC2_ID 
	7E BA 10 03 F1 E2 00 00 00 18 ADC2_ID 
	7E BA 10 03 F1 E2 00 00 00 18 ADC2_ID 	
	
	
	Telemetry frames(RF) SPORT info 
	15 bytes payload
	SPORT frame valid 6+3 bytes
	[00] PKLEN  0E 0E 0E 0E 
	[01] TXID1  DD DD DD DD 
	[02] TXID2  6D 6D 6D 6D 
	[03] CONST  02 02 02 02 
	[04] RS/RB  2C D0 2C CE	//D0;CE=2*RSSI;....2C = RX battery voltage(5V from Bec)
	[05] HD-SK  03 10 21 32	//TX/RX telemetry hand-shake bytes
	[06] NO.BT  00 00 06 03	//No.of valid SPORT frame bytes in the frame		
	[07] STRM1  00 00 7E 00 
	[08] STRM2  00 00 1A 00 
	[09] STRM3  00 00 10 00 
	[10] STRM4  03 03 03 03  
	[11] STRM5  F1 F1 F1 F1 
	[12] STRM6  D1 D1 D0 D0
	[13] CHKSUM1 --|2 CRC bytes sent by RX (calculated on RX side crc16/table)
	[14] CHKSUM2 --|
	+2	appended bytes automatically  RSSI and LQI/CRC bytes(len=0x0E+3);
	
0x06	0x06	0x06	0x06	0x06

0x7E	0x00	0x03	0x7E	0x00
0x1A	0x00	0xF1	0x1A	0x00
0x10	0x00	0xD7	0x10	0x00
0x03	0x7E	0x00	0x03	0x7E
0xF1	0x1A	0x00	0xF1	0x1A
0xD7	0x10	0x00	0xD7	0x10

0xE1	0x1C	0xD0	0xEE	0x33
0x34	0x0A	0xC3	0x56	0xF3
*/

#if defined MULTI_TELEMETRY
const uint8_t PROGMEM Indices[] = {	0x00, 0xA1, 0x22, 0x83, 0xE4, 0x45,
									0xC6, 0x67, 0x48, 0xE9, 0x6A, 0xCB,
									0xAC, 0x0D, 0x8E, 0x2F, 0xD0, 0x71,
									0xF2, 0x53, 0x34, 0x95, 0x16, 0xB7,
									0x98, 0x39, 0xBA, 0x1B } ;
#endif

#ifdef MULTI_TELEMETRY
	void sportSend(uint8_t *p)
	{
		multi_send_header(MULTI_TELEMETRY_SPORT, 9);
		uint16_t crc_s = 0;
		uint8_t x = p[0] ;
		if ( x <= 0x1B )
			x = pgm_read_byte_near( &Indices[x] ) ;
		Serial_write(x) ;
		for (uint8_t i = 1; i < 8; i++)
		{
			Serial_write(p[i]);
			crc_s += p[i];			//0-1FF
			crc_s += crc_s >> 8;	//0-100
			crc_s &= 0x00ff;
		}
		Serial_write(0xff - crc_s);
	}
#else
	void sportSend(uint8_t *p)
	{
		uint16_t crc_s = 0;
		Serial_write(START_STOP);//+9
		Serial_write(p[0]) ;
		for (uint8_t i = 1; i < 9; i++)
		{
			if (i == 8)
				p[i] = 0xff - crc_s;
			
			if ((p[i] == START_STOP) || (p[i] == BYTESTUFF))
			{
				Serial_write(BYTESTUFF);//stuff again
				Serial_write(STUFF_MASK ^ p[i]);
			} 
			else			
				Serial_write(p[i]);					
			
			crc_s += p[i]; //0-1FF
			crc_s += crc_s >> 8; //0-100
			crc_s &= 0x00ff;
		}
	}	
#endif

void sportIdle()
{
	#if !defined MULTI_TELEMETRY
		Serial_write(START_STOP);
	#endif
}	

void sportSendFrame()
{
	static uint8_t sport_counter=0;
	uint8_t i;

	sport_counter = (sport_counter + 1) %36;
	if(telemetry_lost)
	{
		sportIdle();
		return;
	}
	if(sport_counter<6)
	{
		frame[0] = 0x98;
		frame[1] = 0x10;
		for (i=5;i<8;i++)
			frame[i]=0;
	}
	switch (sport_counter)
	{
		case 0:
			frame[2] = 0x05;
			frame[3] = 0xf1;
			frame[4] = 0x02; //dummy values if swr 20230f00
			frame[5] = 0x23;
			frame[6] = 0x0F;
			break;
		case 2: // RSSI
			frame[2] = 0x01;
			frame[3] = 0xf1;
			frame[4] = RX_RSSI;
			frame[5] = TX_RSSI;
			frame[6] = RX_LQI;
			frame[7] = TX_LQI;
			break;
		case 4: //BATT
			frame[2] = 0x04;
			frame[3] = 0xf1;
			frame[4] = v_lipo1; //a1;
			break;								
		default:
			if(Sport_Data)
			{	
				for (i=0;i<FRSKY_SPORT_PACKET_SIZE;i++)
					frame[i]=pktx1[i];
				Sport_Data -- ;
				if ( Sport_Data )
				{
					uint8_t j = Sport_Data * FRSKY_SPORT_PACKET_SIZE ;
					for (i=0;i<j;i++)
						pktx1[i] = pktx1[i+FRSKY_SPORT_PACKET_SIZE] ;
				}
				break;
			}
			else
			{
				sportIdle();
				return;
			}		
	}
	sportSend(frame);
}	

void proces_sport_data(uint8_t data)
{
	static uint8_t pass = 0, indx = 0;
	switch (pass)
	{
		case 0:
			if (data == START_STOP)
			{//waiting for 0x7e
				indx = 0;
				pass = 1;
			}
			break;		
		case 1:
			if (data == START_STOP)	// Happens if missed packet
			{//waiting for 0x7e
				indx = 0;
				pass = 1;
				break;		
			}
			if(data == BYTESTUFF)	//if they are stuffed
				pass=2;
			else
				if (indx < MAX_PKTX)		
					pktx[indx++] = data;		
			break;
		case 2:	
			if (indx < MAX_PKTX)	
				pktx[indx++] = data ^ STUFF_MASK;	//unstuff bytes	
			pass=1;
			break;	
	} // end switch
	if (indx >= FRSKY_SPORT_PACKET_SIZE)
	{//8 bytes no crc 
		if ( Sport_Data < FX_BUFFERS )
		{
			uint8_t dest = Sport_Data * FRSKY_SPORT_PACKET_SIZE ;
			uint8_t i ;
			for ( i = 0 ; i < FRSKY_SPORT_PACKET_SIZE ; i++ )
				pktx1[dest++] = pktx[i] ;	// Triple buffer
			Sport_Data += 1 ;//ok to send
		}
//		else
//		{
//			// Overrun
//		}
		pass = 0;//reset
	}
}

#endif

void TelemetryUpdate()
{
	// check for space in tx buffer
	#ifdef BASH_SERIAL
		uint8_t h ;
		uint8_t t ;
		h = SerialControl.head ;
		t = SerialControl.tail ;
		if ( h >= t )
			t += TXBUFFER_SIZE - h ;
		else
			t -= h ;
		if ( t < 64 )
		{
			return ;
		}
	#else
		uint8_t h ;
		uint8_t t ;
		h = tx_head ;
		t = tx_tail ;
		if ( h >= t )
			t += TXBUFFER_SIZE - h ;
		else
			t -= h ;
		if ( t < 48 ) //32 )
		{
			debugln("TEL_BUF_FULL %d",t);
			return ;
		}
/*		else
			if(t!=96)
				debugln("TEL_BUF %d",t);
*/
	#endif
	#ifdef MULTI_TELEMETRY
		uint32_t now = millis();
		if ((IS_SEND_MULTI_STATUS_on || ((now - lastMulti) > MULTI_TIME))&& protocol != PROTO_SCANNER)
		{
			multi_send_status();
			SEND_MULTI_STATUS_off;
			lastMulti = now;
			return;
		}
		#ifdef MULTI_SYNC
			if ( inputRefreshRate && (now - lastInputSync) > INPUT_SYNC_TIME )
			{
				mult_send_inputsync();
				lastInputSync = now;
				return;
			}
		#endif
	#endif
	#if defined SPORT_TELEMETRY
		if ((protocol==PROTO_FRSKYX || protocol==PROTO_FRSKYX2||protocol==PROTO_FRSKY_R9) && telemetry_link 
		#ifdef TELEMETRY_FRSKYX_TO_FRSKYD
			&& mode_select==MODE_SERIAL
		#endif
		)
		{	// FrSkyX
			for(;;)
			{ //Empty buffer
				struct t_FrSkyX_RX_Frame *p ;
				uint8_t count ;
				p = &FrSkyX_RX_Frames[FrSkyX_RX_NextFrame] ;
				if ( p->valid )
				{
					count = p->count ;
					for (uint8_t i=0; i < count ; i++)
						proces_sport_data(p->payload[i]) ;
					p->valid = false ;	// Sent
					FrSkyX_RX_NextFrame = ( FrSkyX_RX_NextFrame + 1 ) & 3 ;
				}
				else
					break ;
			}
			telemetry_link=0; 
			sportSendFrame();
		}
	#endif // SPORT_TELEMETRY

	#ifdef MULTI_TELEMETRY
		#if defined MULTI_CONFIG_INO
			if(telemetry_link && protocol == PROTO_CONFIG)
			{
				CONFIG_frame();
				telemetry_link=0;
				return;
			}
		#endif
		#if defined MLINK_FW_TELEMETRY
			if(telemetry_link && protocol == PROTO_MLINK)
			{
				MLINK_frame();
				telemetry_link=0;
				return;
			}
		#endif
		#if defined DSM_TELEMETRY
			if(telemetry_link && protocol == PROTO_DSM)
			{	// DSM
				DSM_frame();
				telemetry_link=0;
				return;
			}
		#endif
		#if defined AFHDS2A_FW_TELEMETRY
			if(telemetry_link == 2 && protocol == PROTO_AFHDS2A)
			{
				AFHDSA_short_frame();
				telemetry_link=0;
				return;
			}
		#endif
		#if defined HITEC_FW_TELEMETRY
			if(telemetry_link == 2 && protocol == PROTO_HITEC)
			{
				HITEC_short_frame();
				telemetry_link=0;
				return;
			}
		#endif
		#if defined HOTT_FW_TELEMETRY
			if(telemetry_link == 2 && protocol == PROTO_HOTT)
			{
				HOTT_short_frame();
				telemetry_link=0;
				return;
			}
		#endif
		#if defined RLINK_HUB_TELEMETRY
			if(telemetry_link == 2 && protocol == PROTO_RLINK)
			{
				RLINK_raw_frame();
				telemetry_link=0;
				return;
			}
		#endif
		#if defined SCANNER_TELEMETRY
			if (telemetry_link && protocol == PROTO_SCANNER)
			{
				spectrum_scanner_frame();
				telemetry_link = 0;
				return;
			}
		#endif
		#if defined (FRSKY_RX_TELEMETRY) || defined(AFHDS2A_RX_TELEMETRY) || defined (BAYANG_RX_TELEMETRY) || defined (DSM_RX_CYRF6936_INO)
			if ((telemetry_link & 1) && (protocol == PROTO_FRSKY_RX || protocol == PROTO_AFHDS2A_RX || protocol == PROTO_BAYANG_RX || protocol == PROTO_DSM_RX) )
			{
				receiver_channels_frame();
				telemetry_link &= ~1;
				return;
			}
		#endif
	#endif //MULTI_TELEMETRY
	
	if( telemetry_link & 1 )
	{	// FrSkyD + Hubsan + AFHDS2A + Bayang + Cabell + Hitec + Bugs + BugsMini + NCC1701 + PROPEL + RLINK + OMP + MLINK + DEVO
		// FrSkyX telemetry if in PPM
		frsky_link_frame();
		return;
	}
	#if defined HUB_TELEMETRY
		if((telemetry_link & 2) && ( protocol == PROTO_FRSKYD || protocol == PROTO_BAYANG || protocol == PROTO_MLINK  || protocol == PROTO_DEVO ) )
		{	// FrSkyD + Bayang + MLINK + DEVO
			frsky_user_frame();
			frsky_check_user_frame();
			return;
		}
	#endif
}


/**************************/
/**************************/
/**  Serial TX routines  **/
/**************************/
/**************************/

#ifndef BASH_SERIAL
	// Routines for normal serial output
	void Serial_write(uint8_t data)
	{
		uint8_t nextHead ;
		nextHead = tx_head + 1 ;
		if ( nextHead >= TXBUFFER_SIZE )
			nextHead = 0 ;
		tx_buff[nextHead]=data;
		tx_head = nextHead ;
		tx_resume();
	}

	void initTXSerial( uint8_t speed)
	{
		#ifdef ENABLE_PPM
			if(speed==SPEED_9600)
			{ // 9600
				#ifdef ORANGE_TX
					USARTC0.BAUDCTRLA = 207 ;
					USARTC0.BAUDCTRLB = 0 ;
					USARTC0.CTRLB = 0x18 ;
					USARTC0.CTRLA = (USARTC0.CTRLA & 0xCF) | 0x10 ;
					USARTC0.CTRLC = 0x03 ;
				#else
					#ifdef STM32_BOARD
						usart3_begin(9600,SERIAL_8N1);		//USART3 
					#else
						UBRR0H = 0x00;
						UBRR0L = 0x67;
						UCSR0A = 0 ;						// Clear X2 bit
						//Set frame format to 8 data bits, none, 1 stop bit
						UCSR0C = (1<<UCSZ01)|(1<<UCSZ00);
					#endif
				#endif
			}
			else if(speed==SPEED_57600)
			{ // 57600
				#ifdef ORANGE_TX
					/*USARTC0.BAUDCTRLA = 207 ;
					USARTC0.BAUDCTRLB = 0 ;
					USARTC0.CTRLB = 0x18 ;
					USARTC0.CTRLA = (USARTC0.CTRLA & 0xCF) | 0x10 ;
					USARTC0.CTRLC = 0x03 ;*/
				#else
					#ifdef STM32_BOARD
						usart3_begin(57600,SERIAL_8N1);		//USART3 
					#else
						UBRR0H = 0x00;
						UBRR0L = 0x22;
						UCSR0A = 0x02 ;	// Set X2 bit
						//Set frame format to 8 data bits, none, 1 stop bit
						UCSR0C = (1<<UCSZ01)|(1<<UCSZ00);
					#endif
				#endif
			}
			else if(speed==SPEED_125K)
			{ // 125000
				#ifdef ORANGE_TX
					/*USARTC0.BAUDCTRLA = 207 ;
					USARTC0.BAUDCTRLB = 0 ;
					USARTC0.CTRLB = 0x18 ;
					USARTC0.CTRLA = (USARTC0.CTRLA & 0xCF) | 0x10 ;
					USARTC0.CTRLC = 0x03 ;*/
				#else
					#ifdef STM32_BOARD
						usart3_begin(125000,SERIAL_8N1);	//USART3 
					#else
						UBRR0H = 0x00;
						UBRR0L = 0x07;
						UCSR0A = 0x00 ;	// Clear X2 bit
						//Set frame format to 8 data bits, none, 1 stop bit
						UCSR0C = (1<<UCSZ01)|(1<<UCSZ00);
					#endif
				#endif
			}
		#else
			(void)speed;
		#endif
		#ifndef ORANGE_TX
			#ifndef STM32_BOARD
				UCSR0B |= (1<<TXEN0);//tx enable
			#endif
		#endif
	}

	//Serial TX
	#ifdef ORANGE_TX
		ISR(USARTC0_DRE_vect)
	#else
		#ifdef STM32_BOARD
			void __irq_usart3()			
		#else
			ISR(USART_UDRE_vect)
		#endif
	#endif
	{	// Transmit interrupt
		#ifdef STM32_BOARD
			if(USART3_BASE->SR & USART_SR_TXE)
			{
		#endif
				if(tx_head!=tx_tail)
				{
					if(++tx_tail>=TXBUFFER_SIZE)//head 
						tx_tail=0;
					#ifdef STM32_BOARD	
						USART3_BASE->DR=tx_buff[tx_tail];//clears TXE bit				
					#else
						UDR0=tx_buff[tx_tail];
					#endif
				}
				if (tx_tail == tx_head)
				{
					tx_pause(); // Check if all data is transmitted. If yes disable transmitter UDRE interrupt.
				}
		#ifdef STM32_BOARD	
			}
		#endif		
	}
#else	//BASH_SERIAL
// Routines for bit-bashed serial output

// Speed is 0 for 100K and 1 for 9600
void initTXSerial( uint8_t speed)
{
	TIMSK0 = 0 ;	// Stop all timer 0 interrupts
	#ifdef INVERT_SERIAL
		SERIAL_TX_off;
	#else
		SERIAL_TX_on;
	#endif
	UCSR0B &= ~(1<<TXEN0) ;

	SerialControl.speed = speed ;
	if ( speed == SPEED_9600 )
	{
		OCR0A = 207 ;	// 104uS period
		TCCR0A = 3 ;
		TCCR0B = 0x0A ; // Fast PMM, 2MHz
	}
	else	// 100K
	{
		TCCR0A = 0 ;
		TCCR0B = 2 ;	// Clock/8 (0.5uS)
	}
}

void Serial_write( uint8_t byte )
{
	uint8_t temp ;
	uint8_t temp1 ;
	uint8_t byteLo ;

	#ifdef INVERT_SERIAL
		byte = ~byte ;
	#endif

	byteLo = byte ;
	byteLo >>= 7 ;		// Top bit
	if ( SerialControl.speed == SPEED_100K )
	{
		#ifdef INVERT_SERIAL
				byteLo |= 0x02 ;	// Parity bit
		#else
				byteLo |= 0xFC ;	// Stop bits
		#endif
		// calc parity
		temp = byte ;
		temp >>= 4 ;
		temp = byte ^ temp ;
		temp1 = temp ;
		temp1 >>= 2 ;
		temp = temp ^ temp1 ;
		temp1 = temp ;
		temp1 <<= 1 ;
		temp ^= temp1 ;
		temp &= 0x02 ;
		#ifdef INVERT_SERIAL
				byteLo ^= temp ;
		#else	
				byteLo |= temp ;
		#endif
	}
	else
	{
		byteLo |= 0xFE ;	// Stop bit
	}
	byte <<= 1 ;
	#ifdef INVERT_SERIAL
		byte |= 1 ;		// Start bit
	#endif
	uint8_t next = SerialControl.head + 2;
	if(next>=TXBUFFER_SIZE)
		next=0;
	if ( next != SerialControl.tail )
	{
		SerialControl.data[SerialControl.head] = byte ;
		SerialControl.data[SerialControl.head+1] = byteLo ;
		SerialControl.head = next ;
	}
	if(!IS_TX_PAUSE_on)
		tx_resume();
}

void resumeBashSerial()
{
	cli() ;
	if ( SerialControl.busy == 0 )
	{
		sei() ;
		// Start the transmission here
		#ifdef INVERT_SERIAL
			GPIOR2 = 0 ;
		#else
			GPIOR2 = 0x01 ;
		#endif
		if ( SerialControl.speed == SPEED_100K )
		{
			GPIOR1 = 1 ;
			OCR0B = TCNT0 + 40 ;
			OCR0A = OCR0B + 210 ;
			TIFR0 = (1<<OCF0A) | (1<<OCF0B) ;
			TIMSK0 |= (1<<OCIE0B) ;
			SerialControl.busy = 1 ;
		}
		else
		{
			GPIOR1 = 1 ;
			TIFR0 = (1<<TOV0) ;
			TIMSK0 |= (1<<TOIE0) ;
			SerialControl.busy = 1 ;
		}
	}
	else
	{
		sei() ;
	}
}

// Assume timer0 at 0.5uS clock

ISR(TIMER0_COMPA_vect)
{
	uint8_t byte ;
	byte = GPIOR0 ;
	if ( byte & 0x01 )
		SERIAL_TX_on;
	else
		SERIAL_TX_off;
	byte /= 2 ;		// Generates shorter code than byte >>= 1
	GPIOR0 = byte ;
	if ( --GPIOR1 == 0 )
	{
		TIMSK0 &= ~(1<<OCIE0A) ;
		GPIOR1 = 3 ;
	}
	else
		OCR0A += 20 ;
}

ISR(TIMER0_COMPB_vect)
{
	uint8_t byte ;
	byte = GPIOR2 ;
	if ( byte & 0x01 )
		SERIAL_TX_on;
	else
		SERIAL_TX_off;
	byte /= 2 ;		// Generates shorter code than byte >>= 1
	GPIOR2 = byte ;
	if ( --GPIOR1 == 0 )
	{
		if ( IS_TX_PAUSE_on )
		{
			SerialControl.busy = 0 ;
			TIMSK0 &= ~(1<<OCIE0B) ;
		}
		else
		{
			// prepare next byte and allow for 2 stop bits
			volatile struct t_serial_bash *ptr = &SerialControl ;
			if ( ptr->head != ptr->tail )
			{
				GPIOR0 = ptr->data[ptr->tail] ;
				GPIOR2 = ptr->data[ptr->tail+1] ;
				uint8_t nextTail = ptr->tail + 2 ;
				if ( nextTail >= TXBUFFER_SIZE )
					nextTail = 0 ;
				ptr->tail = nextTail ;
				GPIOR1 = 8 ;
				OCR0A = OCR0B + 40 ;
				OCR0B = OCR0A + 8 * 20 ;
				TIMSK0 |= (1<<OCIE0A) ;
			}
			else
			{
				SerialControl.busy = 0 ;
				TIMSK0 &= ~(1<<OCIE0B) ;
			}
		}
	}
	else
		OCR0B += 20 ;
}

ISR(TIMER0_OVF_vect)
{
	uint8_t byte ;
	if ( GPIOR1 > 2 )
		byte = GPIOR0 ;
	else
		byte = GPIOR2 ;
	if ( byte & 0x01 )
		SERIAL_TX_on;
	else
		SERIAL_TX_off;
	byte /= 2 ;		// Generates shorter code than byte >>= 1
	if ( GPIOR1 > 2 )
		GPIOR0 = byte ;
	else
		GPIOR2 = byte ;
	if ( --GPIOR1 == 0 )
	{	// prepare next byte
		volatile struct t_serial_bash *ptr = &SerialControl ;
		if ( ptr->head != ptr->tail )
		{
			GPIOR0 = ptr->data[ptr->tail] ;
			GPIOR2 = ptr->data[ptr->tail+1] ;
			uint8_t nextTail = ptr->tail + 2 ;
			if ( nextTail >= TXBUFFER_SIZE )
				nextTail = 0 ;
			ptr->tail = nextTail ;
			GPIOR1 = 10 ;
		}
		else
		{
			SerialControl.busy = 0 ;
			TIMSK0 &= ~(1<<TOIE0) ;
		}
	}
}


#endif // BASH_SERIAL

#endif // TELEMETRY
