/******************************* joinlink **************************/
//includes
#include "joinlink.h"

// macro
#define NUM_MCAST                 53  // the max len of pkt in mcast, original: 13
#define NUM_BCAST                 36  // the max number of index of bcast
#define HEAD_LEN                  9   // sum(1 byte) + pwd_len(1 byte) + port(2 byte) + ip(4 byte) + ssid_len(1 byte) 
#define NUM_IDX                   10  // number of index in bcast
#define NUM_PKT			              4   // number of packets for every index
#define SEQ_INCREMENT_ONE_BCAST   1   // only the increment of 1 in seq of pkt is accepted

static char smac[6];
static u8 decoded_state = 0;
static int joinlink_state_mcast = 0;
static int joinlink_state_bcast = 0;
static u8 sync_label_mcast = 0;
static u8 version_mcast = 0;
// every pkt has two byte
static u8 *raw_data_mcast = NULL;
static u8 *decrypted_data_mcast = NULL;
static u8 count_mcast = 0;
static u8 sum_mcast = 0; // the total len of ssid and pwd
static char pass_len = -1;
static u8 ssid_len = 0;
static u8 ssid_offset = 0;
static u8 odd_check = 0;
static u8 total_len_mcast = 0;
static u8 *recved_flag_mcast = NULL;
static u8 range_mcast[NUM_MCAST >> 3]; // the range for aes decryption
static u8 decryp_flag_mcast[NUM_MCAST >> 3];

static u8 sync_label_bcast = 0;
static u8 version_bcast_ready = 0;
static u8 version_bcast = 0;
static u8 count_in_idx_bcast = 0;
static u8 count_decoded_bcast = 0;
static unsigned short seq_now_bcast = 0;
static u8 locked_bssid_bcast[6];
static u8 ssid_offset_bcast = 0;

// for data phase in bcast
// 0: wating index pkt, 1: waiting info pkt
// TODO: need to fix bssid/ssid for bcast to filter unnecessary pkt
static u8 data_phase_state_bcast = 0;
static u8 *raw_data_bcast = NULL;
static u8 *decrypted_data_bcast = NULL;

static u8 version_CRC = 0;
static u8 *decoded_flag_bcast = NULL;
static u8 current_idx_bcast = 0;

static u8 sum_bcast = 0; 
static char pass_len_bcast = -1;
static u8 ssid_len_bcast = 0;
static u8 fc_version_bcast = 0;
static u8 fc_data_bcast = 0;
static u8 idx_CRC = 0;
static u8 idx_data = 0;

//store decode result of AP profile
joinlink_result_t *AP_profile = NULL;

// AES decryption related
static u8 aes_iv[16];
static u8 aes_key[16];
static u8 ssid_range_mcast = 255;
static u8 decryp_data_buf[16];
/*
ret: 0, success, -1, failure
*/
int joinlink_init()
{
	decoded_state = 0;
	joinlink_state_mcast = 0;
	joinlink_state_bcast = 0;
	sync_label_mcast = 0;
	version_mcast = 0;
	
  raw_data_mcast = (u8 *)malloc(NUM_MCAST*2);
  decrypted_data_mcast = (u8 *)malloc(NUM_MCAST*2);
  recved_flag_mcast = (u8 *)malloc(NUM_MCAST);
  raw_data_bcast = (u8 *)malloc(NUM_BCAST*NUM_PKT);
  decrypted_data_bcast = (u8 *)malloc(NUM_BCAST*NUM_PKT);
  decoded_flag_bcast = (u8 *)malloc(NUM_BCAST);
  AP_profile = (joinlink_result_t *)malloc(sizeof(joinlink_result_t));
  
  if(!raw_data_mcast || !decrypted_data_mcast || !recved_flag_mcast||
  	 !raw_data_bcast || !decrypted_data_bcast || !decoded_flag_bcast||
  	 !AP_profile)
  {
  	printf("join_link: malloc memory fail\n");
  	return -1;
  }
  
	memset(raw_data_mcast, 0, NUM_MCAST*2);
  count_mcast = 0;
  sum_mcast = 0;
  pass_len = -1;
  ssid_len = 0;
  ssid_offset = 0;
  odd_check = 0;
  total_len_mcast = 0;
  memset(recved_flag_mcast, 0, NUM_MCAST);
  
  sync_label_bcast = 0;
  version_bcast_ready = 0;
  version_bcast = 0;
  seq_now_bcast = 0;
  memset(locked_bssid_bcast, 0, sizeof(locked_bssid_bcast));
  
  data_phase_state_bcast = 0;
  memset(raw_data_bcast, 0, NUM_BCAST*NUM_PKT);
  version_CRC = 0;
  count_in_idx_bcast = 0;
  count_decoded_bcast = 0;
  memset(decoded_flag_bcast, 0, NUM_BCAST);
  current_idx_bcast = 0;
  sum_bcast = 0; 
  pass_len_bcast = -1;
  ssid_len_bcast = 0;
  fc_version_bcast = 0;
  fc_data_bcast = 0;
  idx_CRC = 0;
  idx_data = 0;
  
  memset(AP_profile, 0, sizeof(joinlink_result_t));
  memset(smac, 0, sizeof(smac));
  
  memset(aes_iv, 0, sizeof(aes_iv));
  memset(aes_key, 0, sizeof(aes_key));
  memset(range_mcast, 0, sizeof(range_mcast));
  memset(decryp_flag_mcast, 0, sizeof(decryp_flag_mcast));
  ssid_range_mcast = 255;
  memset(decrypted_data_mcast, 0, NUM_MCAST*2);
  memset(decryp_data_buf, 0, sizeof(decryp_data_buf));
  memset(decrypted_data_bcast, 0, NUM_BCAST*NUM_PKT);
  ssid_offset_bcast = 0;
  
	return 0;
}

// set the aes_key, the max len should be 16
int set_aes_key(char *key, int len)
{
	if (len <= 0 || len > 16)
		return 0;
		
	memcpy(aes_key, key, len);
	if (rtl_crypto_aes_cbc_init(aes_key, sizeof(aes_key)) != 0) 
  {
		printf("AES CBC init failed\n");
		return 0;
	}
	printf("the AES key is set to %s\n", aes_key);
	return 1;
}
// free memory
void joinlink_deinit()
{
	free(raw_data_mcast);
	free(decrypted_data_mcast);
	free(recved_flag_mcast);
	free(raw_data_bcast);
	free(decrypted_data_bcast);
	free(decoded_flag_bcast);
	free(AP_profile);
	
	raw_data_mcast = NULL;
  decrypted_data_mcast = NULL;
  recved_flag_mcast = NULL;
  raw_data_bcast = NULL;
  decrypted_data_bcast = NULL;
  decoded_flag_bcast = NULL;
  AP_profile = NULL;
  
  return;
}
// restart joinlink when error
static void joinlink_restart()
{
	joinlink_deinit();
	joinlink_init();
	return;
}
/*
ret: 0, failure; 1 true.
*/
static int check_sync_mcast(u8 *da)
{
	if((da[3] == 0) && (da[4] == 1) && (da[5] >= 1) && (da[5] <= 3))
	{
		sync_label_mcast |= 0x01 << (da[5] - 1);
		if(sync_label_mcast == 0x07)
			return 1;
		else
			return 0;
	}
	else
		return 0;	
}
// ret: 0, failure; 1 true
static int check_version_mcast(u8 *da)
{
	// 239.0.{Version}.4
	if((da[3] == 0) && (da[5] == 4))
	{
		version_mcast = da[4];
		return 1;
	}
	else
		return 0;
}

static u8 getCrc(u8 *ptr, u8 len)
{
	u8 crc;
	u8 i;
	crc = 0;
	while (len--)
	{
		crc ^= *ptr++;
		for (i = 0; i < 8; i++)
		{
			if (crc & 0x01)
			{
				crc = (crc >> 1) ^ 0x8C;
			}
			else
				crc >>= 1;
		}
	}
	return crc;
}
// check whether received enough pkt to decrypt
static u8 decryp_ready(u8 range)
{
	int first = (range << 3) + 1;
	u8 count = 0;
	while(count < 8)
	{
		if(!recved_flag_mcast[first + count])
			return 0;
		++count;
	}
	return 1;
}
// ret: 0 suc, ret: -1 failure
static int decryp_data(u8 decryp_range)
{
	// before decryption dump
	memset(decryp_data_buf, 0, sizeof(decryp_data_buf));
	
	// this decrpytion API only accept 16 byte size
	if (rtl_crypto_aes_cbc_decrypt(raw_data_mcast + (decryp_range << 4), 16, aes_iv, sizeof(aes_iv), decryp_data_buf) != 0 ) 
	{
		printf("AES CBC decrypt failed\n");
		return -1;
	}
	memcpy(decrypted_data_mcast + (decryp_range << 4), decryp_data_buf, 16);
	// dump encrypted and decrypted data
#if 0	
	printf("range %d encryp data:", decryp_range);
	for(int i = 0; i < 16; i++)
	  printf("0x%02x ", raw_data_mcast[(decryp_range << 4) + i]);
	printf("\n");  
	
	printf("range %d decrypted to:", decryp_range);
	for(int i = 0; i < 16; i++)
	  printf("0x%02x ", decrypted_data_mcast[(decryp_range << 4) + i]);
	printf("\n");
#endif	
	return 0;
}

// for aes_cbc, need to remove the chain using xor
static void dechain_aes_mcast(u8 range)
{
	if(range != 0)
	{
	  for(int i = 0; i < 16; i++)
	    decrypted_data_mcast[(range << 4) + i] ^= raw_data_mcast[(range - 1 << 4) + i];		
	}
	// dump data
#if 0	
	printf("range %d dechained to: ", range);
	for(int i = 0; i < 16; i++)
	  printf("0x%02x ", decrypted_data_mcast[(range << 4) + i]);
	printf("\n");
#endif
  printf("mcast: block %d is dechained\n", range);	
	decryp_flag_mcast[range] = 2;
	count_mcast += 8;
	return;
}
/*
ret: 1, enough data; 0 error or not enough
239.{index}.{byte[i]}{byte[i+1]}
{index} = (CRCLSB*3bit) + (Index*5bit)
*/
static int check_data_mcast(u8 *da)
{
	u8 raw_index = da[3];
	u8 CRC_index = (raw_index & 0x40) >> 6;
	u8 idx = raw_index & 0x3f;
	u8 first, second;
	u8 range = 0;
	int first_in_range = 0;
	// check CRC

  // idx is invalid, start with 1
	if((idx > NUM_MCAST) || (idx < 1))
		return 0;
  
  // CRC check pass
	if(((da[4] ^ da[5]) & 0x01) == CRC_index)
	{
		// already received
		if(recved_flag_mcast[idx] == 1)
			return 0;
		
		// new pkt	
		recved_flag_mcast[idx] = 1;
		first = (idx -1) * 2;
		second = first + 1;
		
		raw_data_mcast[first] = da[4];
		raw_data_mcast[second] = da[5];
		printf("mcast: new pkt, idx is %d\n", idx);
		// range begins with 0, every 8 pkts belongs to 1 range,e.g idx: {1~8} -> range:0
    range = (idx - 1) >> 3;
    // not enough pkt for decryption
    if(!decryp_ready(range))
    	return 0;
    // start to decrypt
    first_in_range = range << 4;
    if(decryp_data(range) == -1)
    {
    	// clear the received flag for this range
    	for (int i = 1; i < 9; i++)
    		recved_flag_mcast[first_in_range + i] = 0;
    	printf("decryped error in range %d\n",range);
    	return 0;
    }
    // decryption success here;
    decryp_flag_mcast[range] = 1;
    printf("mcast: block %d is decrypted\n", range);
    		
		// this is the sum and pass_len
		//if((idx == 1) && (!sum_mcast))
		if(range == 0)
		{
			dechain_aes_mcast(range);
			
			sum_mcast = decrypted_data_mcast[0];
    	pass_len = decrypted_data_mcast[1];
    	printf("mcast: sum_mcast 0x%02x pass_len %d \n",sum_mcast, pass_len);
			
			// check whether the pass_len is valid
			if(pass_len < 0 || pass_len > 64)
			{
				printf("mcast: pass_len is wrong, clear\n");
	      decryp_flag_mcast[range] = 0;
	      count_mcast -= 8;
    	  for (int i = 1; i < 9; i++)
    		  recved_flag_mcast[first_in_range + i] = 0;
				return 0;
			}
			
			// printf("[DEBUG]_mcast: the 2nd flag is %d\n", decryp_flag_mcast[range + 1]);
			// check whether 2nd block is ready
			if(decryp_flag_mcast[range + 1] == 1)
			{
				printf("here\n");
			  dechain_aes_mcast(range + 1);
			}			
			
			if((pass_len & 0x01)  == 0)
			  odd_check = 2; // even
			else
				odd_check = 1; // odd	
			// get the idx of pkt which contains the ssid_len info	
			ssid_offset = 1 + (u8)((8 + pass_len)/2);	
			ssid_range_mcast = (ssid_offset - 1) >> 3;			
			printf("ssid_offset %d ssid_range_mcast %d\n",ssid_offset, ssid_range_mcast);
#if 1	
      // already dechained		
			if(decryp_flag_mcast[ssid_range_mcast] == 2)
			{
				if(total_len_mcast == 0)
				{
					if(ssid_len == 0)
					{
						if(odd_check == 2)
							ssid_len = decrypted_data_mcast[2 * (ssid_offset - 1)];
						if(odd_check == 1)
							ssid_len = decrypted_data_mcast[2 * (ssid_offset - 1) + 1];
						printf("ssid_len is %d\n",ssid_len);
					}
					total_len_mcast = (u8)((pass_len + ssid_len + HEAD_LEN + 1)/2);
			    printf("total_len_mcast is recalculated as %d\n",total_len_mcast);
				}
			}
#endif
		}
		// need to dechain for the 2nd and following block
		else
		{
			if(decryp_flag_mcast[range - 1] > 0)
				dechain_aes_mcast(range);
				
			if(decryp_flag_mcast[range + 1] == 1)
				dechain_aes_mcast(range + 1);
				
			if(!decryp_flag_mcast[range - 1] && !decryp_flag_mcast[range + 1])
				return 0;
		}
		// 8 new pkts has been de chained for AES
		
		// set the ssid_len
		if(ssid_range_mcast != 255 && decryp_flag_mcast[ssid_range_mcast] == 2)
		{
		   if(ssid_len == 0)
		   {
		   	if(odd_check == 2)
		   		ssid_len = decrypted_data_mcast[2 * (ssid_offset - 1)];
		   	if(odd_check == 1)
		   		ssid_len = decrypted_data_mcast[2 * (ssid_offset - 1) + 1];
		   	printf("ssid_len is %d\n",ssid_len);
		   }
		}

		// set the total_len 
		if((pass_len != -1) && (ssid_len != 0))
		{
			if(total_len_mcast == 0)
			{
			  total_len_mcast = (u8)((pass_len + ssid_len + HEAD_LEN + 1)/2);
			  printf("total_len_mcast is calculated as %d\n",total_len_mcast);
			}
		}
		
		printf("total_len needed is %d already decrypted %d\n", total_len_mcast, count_mcast);
		
		if(!total_len_mcast)
		{
		  if(count_mcast >= NUM_MCAST)
				return 1;
			else
				return 0;
		}
		else
		{
			//printf("count_mcast is %d total_len_mcast is %d\n");
		  if(count_mcast >= total_len_mcast)
		  {
		  		// check CRC
		  	u8 crc_ret = 0;
		  	printf("enough decrypted pkt, start to check sum\n");
		  	if((pass_len + ssid_len) & 0x01)
		  		crc_ret = getCrc(decrypted_data_mcast + 1, total_len_mcast * 2 - 1);
		  	else
		  		crc_ret = getCrc(decrypted_data_mcast + 1, total_len_mcast * 2 - 2);
				
				if(crc_ret == sum_mcast)			  					  		
          {printf("sum check pass\n"); return 1;}
        else
      	{
      		printf("sum crc check failure, restart\n");
      		joinlink_restart(); // fine tune: only restart the mcast part
      		return 0;
      	}
		  }
			else
				return 0;		
		}
	}
	// check CRC failure
	else
	{
		//printf("CRC failure in mcast, getCrc is 0x%02x, CRC is 0x%02x\n",(da[4] ^ da[5]), CRC_index);
		return 0;
	}

}
/*
ret: 0, failure; 1 true.
*/
static int check_sync_bcast(int len)
{
	// make sure the bits larger than 9 is 0
	if(len >= 256) 
		return 0;
	// only the least 9 bits are useful	
	len &= 0x01ff;
	if((len >=1) && (len <=4))
	{
		sync_label_bcast |= 0x01 << (len - 1);
		if(sync_label_bcast == 0x0f)
			return 1;
		else
			return 0;
	}
	else
		return 0;	
}

/* 
{0b10000*5bit}+{CRCLSB*4bit}
{0*1bit}{Version}
 ret: 0, failure; 1 true
 */
static int check_version_bcast(int len, u8 i_fc)
{
  version_bcast = 0;
  
	if(!version_bcast_ready)
	{
		u8 version_pre_CRC = len & 0x0007;
		u8 version_pre_data = (len & 0x01f8) >> 3;
		
		if(len >= 512)
			return 0;
		
		if(version_pre_data == 0x20)
		{
			version_bcast_ready = 1; 
			version_CRC = version_pre_CRC;
			// fix the direction(fromDS/toDS) to receive version info
  		fc_version_bcast = i_fc;
				
//			printf("get the CRC of version, change to wait version state\n");
		}	
			
		return 0;
	}
	else
	{
		if(i_fc != fc_version_bcast)
			return 0;
			
		if((len & 0xff00) != 0) 
			return 0;
		
		version_bcast = len & 0x00ff;
		if((getCrc(&version_bcast,1) & 0x07) == version_CRC)			
		{
			printf("version CRC pass\n");
			return 1;
		}
		else
		{
			//printf("version CRC failure,reset this state, version is 0x%02x calculated CRC is 0x%02x, CRC is 0x%02x\n", 
			//      version_bcast, getCrc(&version_bcast,1), version_CRC);
			version_bcast_ready = 0;
		}

	}
	return 0;
}

/*ret 1: valid seq, ret 0: invalid seq*/
static u8 check_and_update_seq(unsigned short frame_seq)
{
	int seq_delta = frame_seq - seq_now_bcast;
	
#if SEQ_INCREMENT_ONE_BCAST
	if((seq_delta == 1) || (seq_now_bcast == 4095) && (frame_seq == 0))
	{
		seq_now_bcast = frame_seq;
		return 1;
	}
	else
	{
		seq_now_bcast = frame_seq;
		return 0;
	}
#else	
	if(((seq_delta <= 10) && (seq_delta >= 0)) || 
		((seq_now_bcast > 4085) && (seq_delta + 4096 <= 10) && (seq_delta + 4096 >= 0)))
	{
		seq_now_bcast = frame_seq;
		//printf("valid seq, seq_delta %d seq_now is updated to %d\n", seq_delta, seq_now_bcast);
		return 1;
	}
	else
	{
		seq_now_bcast = frame_seq;
		//printf("invalid seq, seq_delta %d seq_now is updated to %d\n", seq_delta, seq_now_bcast);
		return 0;
	}
#endif
}
// idx starts with 1, every 4 is one decryption block
static int decryp_ready_bcast(u8 first_idx)
{	
	for(int i = 0; i < 4; i++)
	{
		if(decoded_flag_bcast[first_idx + i] == 0)
			return 0;
	}
	return 1;
}
// decryption for bcast
static int decryp_data_bcast(u8 idx)
{
	memset(decryp_data_buf, 0, sizeof(decryp_data_buf));
// dump the encryption info	
#if 0	
	printf("before decryption of idx %d:", idx);
	for(int i = 0; i < 16; i++)
	  printf("0x%02x ", raw_data_bcast[((idx >> 2) << 4) + i]);
	printf("\n");
#endif

	// this decrpytion API only accept 16 byte size
	if (rtl_crypto_aes_cbc_decrypt(raw_data_bcast + ((idx >> 2) << 4), 16, aes_iv, sizeof(aes_iv), decryp_data_buf) != 0 ) 
	{
		printf("AES CBC decrypt failed\n");
		return -1;
	}
	memcpy(decrypted_data_bcast + ((idx >> 2) << 4), decryp_data_buf, 16);
	printf("bcast: blcok %d is decrypted\n", idx >> 2);
// dump the info after decryption	
#if 0	
	printf("after decryption of idx %d:", idx);
	for(int i = 0; i < 16; i++)
	  printf("0x%02x ", decrypted_data_bcast[((idx >> 2) << 4) + i]);
	printf("\n");	
#endif
	
	return 0;
}
// remove chain for aes cbc for bcast mode
static void dechain_aes_bcast(u8 idx)
{
	u8 first_idx = (idx >> 2) << 4;
	if(idx != 1)
	{
		for(int i = 0; i < 16; i++)
			decrypted_data_bcast[first_idx + i] ^= raw_data_bcast[first_idx - 16 + i];
	}
	count_decoded_bcast += 4;
	// set the dechained flag
  for(int i = 0; i < 4; i++)
	  decoded_flag_bcast[idx + i] = 3;
	// dump the info after dechain
#if 0
	printf("idx %d is de chained to: ", idx);
	for(int i = 0; i < 16; i++)
	  printf("0x%02x ", decrypted_data_bcast[first_idx + i]);
	printf("\n");
#endif
  printf("bcast: block %d is dechained\n", idx >> 2);
	return;
}

/*
{EncodeIndex*5bit}+{CRCLSB*4bit}
{0*1bit}{Byte(i+0) *8bit}
{0*1bit}{Byte(i+1) *8bit}
{0*1bit}{Byte(i+2) *8bit}
{0*1bit}{Byte(i+3) *8bit}
*/
static int check_data_bcast(int len, u8 i_fc, unsigned short frame_seq, unsigned const char *temp_bssid)
{
	// waiting index pkt
	if(!data_phase_state_bcast)
	{
		
		// make sure the 9th bit is 1 and bit larger than 9 is 0
		if(((len & 0x0fff) >= 512) || ((len & 0x0fff) <= 256))
			return 0;
		// idx_data is increase from 1, not 0	
		idx_data = (len & 0x00f8) >> 3;
		if(idx_data == 0)
			return 0;
		
		if(idx_data > NUM_BCAST)
		{
			printf("index is too large\n");
			return 0;
		}
		
		// already decoded this idx
		if(decoded_flag_bcast[idx_data] >= 1)
			return 0;
		else
		{
			current_idx_bcast = idx_data;
			count_in_idx_bcast = 0;
			data_phase_state_bcast = 1;
			idx_CRC = len & 0x0007;
			seq_now_bcast = frame_seq;
			//printf("idx_CRC is 0x%02x len is 0x%02x\n",idx_CRC, len);
			fc_data_bcast = i_fc;
			//printf("waiting data pkt of idx %d, locked in i_fc %d, seq_now %d\n",
			//				current_idx_bcast,fc_data_bcast,seq_now_bcast);
			return 0;
		}
			
	}
	// waiting info pkt
	else
	{
		u8 array_idx = 0;
		// check whether the data is valid, the 9th bit should be 0
		if(len >= 256)
		{
			//printf("not info pkt\n");
			return 0;
		}
		
		// only receive the data from the previous idx direction
		if(i_fc != fc_data_bcast)
			return 0;
		
		array_idx = 4 * (current_idx_bcast - 1);

		//printf("from bssid 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
		//						 temp_bssid[0],temp_bssid[1],temp_bssid[2],temp_bssid[3],temp_bssid[4],temp_bssid[5]);		
		// check whether seq is valid
	  if(check_and_update_seq(frame_seq) == 0)
	  {
			//memset(raw_data_bcast + array_idx, 0, 4);
			data_phase_state_bcast = 0;
			return 0;  
	  }
		
		raw_data_bcast[array_idx + count_in_idx_bcast] = len & 0x00ff;
		count_in_idx_bcast++;
		
		//printf("len 0x%02x, info 0x%02x, i_fc %d i_seq %d\n",len, len & 0x00ff, i_fc, frame_seq);
		if(count_in_idx_bcast != NUM_PKT)
			return 0;
		else
		{
			u8 temp_ret = 0;
			u8 first_idx = 0;
      
#if 0			
			printf("enough data pkt for idx, check CRC\n",current_idx_bcast);
			printf("data to be decoded in idx %d: 0x%02x 0x%02x 0x%02x 0x%02x\n",current_idx_bcast,
			      *(raw_data_bcast + array_idx),*(raw_data_bcast + array_idx + 1),
			      *(raw_data_bcast + array_idx + 2),*(raw_data_bcast + array_idx + 3));
#endif			      
			
			// assume first encryption and then CRC, so CRC check first and then decryption for receiver.      
			temp_ret = getCrc(raw_data_bcast + array_idx, 4) & 0x07;
			//printf("calculated CRC is 0x%02x true CRC is 0x%02x\n",temp_ret, idx_CRC);
			// CRC pass
			if(temp_ret == idx_CRC)
			{
				printf("bcast: idx %d is decoded\n",current_idx_bcast);
				// the first idx in every decryption block
				first_idx = 1 + ((current_idx_bcast - 1 >> 2) << 2);
				// set the flag of this idx to 1, indicate pass CRC but not yet to decrypt
				decoded_flag_bcast[current_idx_bcast] = 1;
				// not enough neighbor idx for decryption
				if(decryp_ready_bcast(first_idx) == 0)
					return 0;
				
				if(decryp_data_bcast(first_idx) == -1)
				{
					for(int i = 0; i < 4; i++)
					  decoded_flag_bcast[first_idx + i] = 0;
					// clear the 4 idx data in this block;  
					memset(raw_data_bcast + ((current_idx_bcast - 1 >> 2) << 4), 0, 16);
					data_phase_state_bcast = 0;
					return 0;
				}
				// decryption PASS
				// set the decryption flag
				for(int i = 0; i < 4; i++)
				  decoded_flag_bcast[first_idx + i] = 2;
				
	      // if 1st block, get the pass_len
        if(current_idx_bcast <= 4)
        {
        	dechain_aes_bcast(first_idx);
        	sum_bcast = *decrypted_data_bcast; 
        	pass_len_bcast = *(decrypted_data_bcast + 1);
        	// make sure the pass len is in the right range {0,63}
        	if((pass_len_bcast > 63) || (pass_len_bcast < 0))
        	{
        		printf("pass_len is wrong, clear\n");
					  for(int i = 0; i < 4; i++)
					    decoded_flag_bcast[first_idx + i] = 0;
					  count_decoded_bcast -= 4;
					  // clear the 4 idx data in this block;  
					  memset(raw_data_bcast + ((current_idx_bcast - 1 >> 2) << 4), 0, 16);
					  data_phase_state_bcast = 0;
						return 0;        		
        	}
        	printf("sum_bcast is %d pass_len_bcast is %d\n",sum_bcast, pass_len_bcast);
        	// recalculate ssid_len if the idx containning ssid_len is already decoded
        	
        	// to de chain the neighbor block
        	if(decryp_ready_bcast(first_idx + 4))
        		dechain_aes_bcast(first_idx + 4);
        	
        	ssid_offset_bcast = (8 + pass_len_bcast)/4 + 1; 
        	if(decoded_flag_bcast[ssid_offset_bcast] == 3)
        	{
        	  ssid_len_bcast = *(decrypted_data_bcast + 8 + pass_len_bcast);
        	  // check whether ssid len is in the right range {0,32}
	        	if(ssid_len_bcast > 32)
	        	{
							//memset(raw_data_bcast + 4 * (2 + pass_len_bcast/4), 0, 4);
							data_phase_state_bcast = 0;
							for(int i = 0; i < 4; i++)
						  	decoded_flag_bcast[(ssid_len_bcast - 1 >> 2) + i] = 0;
						  count_decoded_bcast -= 4;
							printf("ssid_len_bcast is wrong, clear idx %d\n", ((8 + pass_len_bcast)/4 + 1));
							return 0;         		
	        	}
        	  printf("recalculated ssid_len_bcast is %d\n",ssid_len_bcast);
        	}
        }
        // for the 2nd and following block, need the preceeding block to de chain
        else
        {
        	if(decryp_ready_bcast(first_idx - 4))
        	  dechain_aes_bcast(first_idx);
        	  
        	if(decryp_ready_bcast(first_idx + 4))
        		dechain_aes_bcast(first_idx + 4);
        	
        	if(!decryp_ready_bcast(first_idx - 4) && !decryp_ready_bcast(first_idx + 4))	
        		return 0;
        }
        // check whether ssid_len idx has been dechained
        for(int i = 0; i < 4; i++)
        {
          if((pass_len_bcast != -1) && (decoded_flag_bcast[ssid_offset_bcast] == 3))
          {
          	ssid_len_bcast = *(decrypted_data_bcast + 8 + pass_len_bcast);
          	// make sure ssid_len is valid in the range {0, 32}
          	if(ssid_len_bcast > 32)
          	{
							//memset(raw_data_bcast + 4 * (2 + pass_len_bcast/4), 0, 4);
							data_phase_state_bcast = 0;
							for(int i = 0; i < 4; i++)
						  	decoded_flag_bcast[(ssid_offset_bcast >> 2) << 2 + i + 1] = 0;
						  count_decoded_bcast -= 4;
							printf("ssid_len_bcast is wrong\n");
							return 0;
          	}
          	printf("ssid_len_bcast is %d\n",ssid_len_bcast);
          	break;
          }        	
        }
        // check whether enough
        if((ssid_len_bcast != 0) && (pass_len_bcast != -1))
        {
        	u8 total_len_bcast = HEAD_LEN + ssid_len_bcast + pass_len_bcast;
        	printf("needed %d pkt, decoded %d\n",(u8)((total_len_bcast + 3)/4),count_decoded_bcast);
        	if(count_decoded_bcast >= (u8)((total_len_bcast + 3)/4))
        	{
        		printf("enough decoded packets, start to check sum\n");
        		if(getCrc(decrypted_data_bcast + 1,total_len_bcast - 1) == sum_bcast)
        		{
        			printf("sum check pass in bcast\n");
        			return 1;
        		}
        		else
        		{
        			//printf("bcast sum check failure, restart\n");
        			joinlink_restart(); // fine tune: only restart the bcast part
        			return 0;
        		}
        	}
        }
				
				data_phase_state_bcast = 0;
			}
			else
			{
				memset(raw_data_bcast + 4 * (current_idx_bcast - 1), 0, 4);
				data_phase_state_bcast = 0;
				printf("CRC failure of idx %d\n",current_idx_bcast);
				return 0;
			}
		}
		
	}
	
	return 0;
}
/*
ret: 0, failure, 1 true
if success, assign the AP profile to a structure.
*/ 
static int decode_AP_profile(u8 *raw_data)
{
	int pos = 0;
		
	AP_profile->sum = raw_data[pos];
	pos++;
	printf("AP_profile: sum %d\n",AP_profile->sum);
	
	AP_profile->pwd_length = raw_data[pos];
	pos++;
	printf("AP_profile: pwd_len %d\n",AP_profile->pwd_length);
	
	if(AP_profile->pwd_length > 64)
	{
		printf("the pwd len: %d, larger than 64\n",AP_profile->pwd_length);
		return 0;
	}
	
	memcpy(AP_profile->pwd, (raw_data + pos), AP_profile->pwd_length);
	pos += AP_profile->pwd_length;
	printf("AP_profile: pwd %s\n",AP_profile->pwd);
	
	AP_profile->source_ip[0] = *(raw_data + pos);
	AP_profile->source_ip[1] = *(raw_data + pos + 1);
	AP_profile->source_ip[2] = *(raw_data + pos + 2);
	AP_profile->source_ip[3] = *(raw_data + pos + 3);
	
	pos += 4;
	printf("AP_profile: sip %d %d %d %d\n",AP_profile->source_ip[0],
																				 AP_profile->source_ip[1],
																				 AP_profile->source_ip[2],
																				 AP_profile->source_ip[3]);
	// assume the high byte is the most significant
	#if 1
	AP_profile->source_port = ((unsigned int)(*(raw_data + pos + 1)) << 8) | (*(raw_data + pos));
	//printf("port high_part, low_part: %d %d\n", *(raw_data + pos + 1), *(raw_data + pos));
	#endif
	
	pos += 2;
	printf("AP_profile: port %d\n",AP_profile->source_port);
	
	AP_profile->ssid_length = *(raw_data + pos);
	pos++;
	printf("AP_profile: ssid_len %d\n",AP_profile->ssid_length);
	
	if(AP_profile->ssid_length > 64)
	{
		printf("the ssid len: %d, larger than 64\n",AP_profile->ssid_length);
		return 0;
	}
	memcpy(AP_profile->ssid, (raw_data + pos), AP_profile->ssid_length);
	printf("AP_profile: ssid %s\n",AP_profile->ssid);
	
	return 1;
}

joinlink_status_t joinlink_recv(u8 *da, u8 *sa, int len, void *user_data)
{
	joinlink_status_t ret;
	const ieee80211_frame_info_t *promisc_info = user_data;
	// 1 from ds, 2 to ds
	u8 i_fc = ((promisc_info->i_fc & 0x0100) == 0x0100)? 2: 1;
	unsigned short frame_seq = promisc_info->i_seq;
	unsigned const char *temp_bssid = promisc_info->bssid;
		
	// for mcast
	if(!((da[0] == 0xff) && (da[1] == 0xff) && (da[2] == 0xff) &&
			(da[3] == 0xff) && (da[4] == 0xff) && (da[5] == 0xff)))
	{
		if(joinlink_state_mcast == 0)
		{
			if(!check_sync_mcast(da))
				return JOINLINK_STATUS_CONTINUE;
			else
			{
				// TODO: consider to fix source mac here
				joinlink_state_mcast = 1;
				memcpy(smac, sa, 6);
				printf("turn to wait version state\n");
				return JOINLINK_STATUS_CONTINUE;
			}
				
		}
		else if(joinlink_state_mcast == 1)
		{
			// only accept the pkt from fixed source mac
			if(memcmp(smac, sa, 6))
				return JOINLINK_STATUS_CONTINUE;
				
			if(!check_version_mcast(da))
				return JOINLINK_STATUS_CONTINUE;
			else
			{
				joinlink_state_mcast = 2;
				printf("mcast version is %d\n",version_mcast);
				printf("turn to wait data state\n");
				return JOINLINK_STATUS_CHANNEL_LOCKED;
			}
		}
		else if(joinlink_state_mcast == 2)
		{
			if(memcmp(smac, sa, 6))
				return JOINLINK_STATUS_CONTINUE;
				
			if(!check_data_mcast(da))
				return JOINLINK_STATUS_CONTINUE;
			else
			{
				printf("enough packets, start to decode AP profile\n");
				// AP profile has been gotten
				if(!decode_AP_profile(decrypted_data_mcast))
				{
					printf("decode failure, restart joinlink\n");
					joinlink_restart();
					//TODO: intialize the data structure to restart receive data
					return JOINLINK_STATUS_CONTINUE;
				}
				else
				{
					decoded_state = 1;
					return JOINLINK_STATUS_COMPLETE;
				}
			}
		}
	}
	// for bcast
	else
	{
		len -= 42; // remove the unnecessary part
				
		if(joinlink_state_bcast == 0)
		{
			if(!check_sync_bcast(len))
				return JOINLINK_STATUS_CONTINUE;
			else
			{
				// fix the smac and bssid
				memcpy(smac, sa, 6);
				memcpy(locked_bssid_bcast, temp_bssid, 6);
				joinlink_state_bcast = 1;
				printf("change to bcast_state_1, lock channel\n");
				return /*JOINLINK_STATUS_CONTINUE*/JOINLINK_STATUS_CHANNEL_LOCKED;
			}
				
		}
		else if(joinlink_state_bcast == 1)
		{
			if(memcmp(smac, sa, 6) || memcmp(temp_bssid, locked_bssid_bcast, 6))
				return JOINLINK_STATUS_CONTINUE;			
			
			if(!check_version_bcast(len, i_fc))
				return JOINLINK_STATUS_CONTINUE;
			else
			{
				joinlink_state_bcast = 2;
				printf("change to bcast_state_2\n");
				return /*JOINLINK_STATUS_CHANNEL_LOCKED*/JOINLINK_STATUS_CONTINUE;
			}
		}
		else if(joinlink_state_bcast == 2)
		{
			if(memcmp(smac, sa, 6) || memcmp(temp_bssid, locked_bssid_bcast, 6))
				return JOINLINK_STATUS_CONTINUE;
							
			if(!check_data_bcast(len, i_fc, frame_seq, temp_bssid))
				return JOINLINK_STATUS_CONTINUE;
			else
			{
				// AP profile has been gotten
				if(!decode_AP_profile(decrypted_data_bcast))
				{
					printf("decode failure, restart joinlink\n");
					//TODO: intialize the data structure to restart receive data
					return JOINLINK_STATUS_CONTINUE;
				}
				else
				{
					decoded_state = 1;
					return JOINLINK_STATUS_COMPLETE;
				}
			}
		}
	}
	ret = JOINLINK_STATUS_CONTINUE;
	return ret;
}

/*
 * copy the decode AP info to user space
* store the AP profile to result;
* ret: 0, success
* ret: -1, failure
 */
int joinlink_get_result(joinlink_result_t *result)
{
	if(decoded_state == 0)
		return -1;
	else
	{
    memcpy(result, AP_profile, sizeof(joinlink_result_t));
	  return 0;		
	}
}



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