/**
* Node Transport Processing
*
* This function is part of the callback system for the Ethernet transport.
* Based on the Command Group field in the header, it will call the appropriate
* processing function.
*
* @param    Message to Node   - WARPNet Host Message to the node
*           Message from Node - WARPNet Host Message from the node
*           Packet Source          - Ethernet Packet Source
*           Ethernet Device Number - Indicates which Ethernet device packet came from
*
* @return	None.
*
* @note		None.
*
******************************************************************************/
void node_rxFromTransport(wn_host_message* toNode, wn_host_message* fromNode, void* pktSrc, unsigned int eth_dev_num){
	unsigned char cmd_grp;

	unsigned int respSent;

#ifdef _DEBUG_
	xil_printf("In node_rxFromTransport() \n");
#endif

	//Helper struct pointers to interpret the received packet contents
	wn_cmdHdr* cmdHdr;
	void * cmdArgs;

	//Helper struct pointers to form a response packet
	wn_respHdr* respHdr;
	void * respArgs;

	cmdHdr  = (wn_cmdHdr*)(toNode->payload);
	cmdArgs = (toNode->payload) + sizeof(wn_cmdHdr);

	//Endian swap the command header (this is the first place we know what/where it is)
	cmdHdr->cmd     = Xil_Ntohl(cmdHdr->cmd);
	cmdHdr->length  = Xil_Ntohs(cmdHdr->length);
	cmdHdr->numArgs = Xil_Ntohs(cmdHdr->numArgs);

	//Outgoing response header must be endian swapped as it's filled in
	respHdr  = (wn_respHdr*)(fromNode->payload);
	respArgs = (fromNode->payload) + sizeof(wn_cmdHdr);

	cmd_grp = WN_CMD_TO_GRP(cmdHdr->cmd);
	switch(cmd_grp){
		case WARPNET_GRP:
		case NODE_GRP:
			respSent = node_processCmd(cmdHdr,cmdArgs,respHdr,respArgs, pktSrc, eth_dev_num);
		break;
		case TRANS_GRP:
			respSent = transport_processCmd(cmdHdr,cmdArgs,respHdr,respArgs, pktSrc, eth_dev_num);
		break;
		default:
			xil_printf("Unknown command group\n");
		break;
	}

	if(respSent == NO_RESP_SENT)	fromNode->length += (respHdr->length + sizeof(wn_cmdHdr));


	//Endian swap the response header before returning
	// Do it here so the transport sender doesn't have to understand any payload contents
	respHdr->cmd     = Xil_Ntohl(respHdr->cmd);
	respHdr->length  = Xil_Ntohs(respHdr->length);
	respHdr->numArgs = Xil_Ntohs(respHdr->numArgs);

	return;
}
/**
* Node Send Early Response
*
* Allows a node to send a response back to the host before the command has
* finished being processed.  This is to minimize the latency between commands
* since the node is able to finish processing the command during the time
* it takes to communicate to the host and receive another command.
*
* @param    Response Header        - WARPNet Response Header
*           Packet Source          - Ethernet Packet Source
*           Ethernet Device Number - Indicates which Ethernet device packet came from
*
* @return	None.
*
* @note		None.
*
******************************************************************************/
void node_sendEarlyResp(wn_respHdr* respHdr, void* pktSrc, unsigned int eth_dev_num){
	/* This function is used to send multiple command responses back to the host
	 * under the broader umbrella of a single command exchange. The best example
	 * of this functionality is a 'readIQ' command where a single packet from
	 * the host results in many response packets returning from the board.
	 *
	 * A key assumption in the use of this function is that the underlying command
	 * from the host does not raise the transport-level ACK flag in the transport
	 * header. Furthermore, this function exploits the fact that wn_node can determine
	 * the beginning of the overall send buffer from the location of the response to
	 * be sent.
	 */

	 wn_host_message nodeResp;

#ifdef _DEBUG_
	 xil_printf("In node_sendEarlyResp() \n");
#endif

	 nodeResp.payload = (void*) respHdr;
	 nodeResp.buffer  = (void*) respHdr - ( PAYLOAD_OFFSET + sizeof(wn_transport_header) );
	 nodeResp.length  = PAYLOAD_PAD_NBYTES + respHdr->length + sizeof(wn_cmdHdr); //Extra 2 bytes is for alignment

	//Endian swap the response header before before transport sends it
	// Do it here so the transport sender doesn't have to understand any payload contents
	respHdr->cmd     = Xil_Ntohl(respHdr->cmd);
	respHdr->length  = Xil_Ntohs(respHdr->length);
	respHdr->numArgs = Xil_Ntohs(respHdr->numArgs);

#ifdef _DEBUG_
	xil_printf("sendEarlyResp\n");
	xil_printf("payloadAddr = 0x%x, bufferAddr = 0x%x, len = %d\n",nodeResp.payload,nodeResp.buffer,nodeResp.length);
#endif

	 transport_send(&nodeResp, pktSrc, eth_dev_num);

}
/**
*
* This function calculates the checksum and returns a 16 bit result.
*
* @param 	RxFramePtr is a 16 bit pointer for the data to which checksum
* 		is to be calculated.
* @param	StartLoc is the starting location of the data from which the
*		checksum has to be calculated.
* @param	Length is the number of halfwords(16 bits) to which checksum is
* 		to be calculated.
*
* @return	It returns a 16 bit checksum value.
*
* @note		This can also be used for calculating checksum. The ones
* 		complement of this return value will give the final checksum.
*
******************************************************************************/
static u16 CheckSumCalculation(u16 *RxFramePtr, int StartLoc, int Length)
{
	u32 Sum = 0;
	u16 CheckSum = 0;
	int Index;

	/*
	 * Add all the 16 bit data.
	 */
	Index = StartLoc;
	while (Index < (StartLoc + Length)) {
		Sum = Sum + Xil_Ntohs(*(RxFramePtr + Index));
		Index++;
	}

	/*
	 * Add upper 16 bits to lower 16 bits.
	 */
	CheckSum = Sum;
	Sum = Sum >> 16;
	CheckSum = Sum + CheckSum;
	return CheckSum;
}
void wlan_poll_eth() {
	XAxiDma_BdRing *rxRing_ptr;
	XAxiDma_Bd *cur_bd_ptr;
	XAxiDma_Bd *first_bd_ptr;
	u8* mpdu_start_ptr;
	u8* eth_start_ptr;
	u8* eth_mid_ptr;
	packet_bd* tx_queue;
	u32 eth_rx_len, eth_rx_buf;
	u32 mpdu_tx_len;
	packet_bd_list tx_queue_list;
	u32 i;
	u8 continue_loop;

	int bd_count;
	int status;
	int packet_is_queued;

	ethernet_header* eth_hdr;
	ipv4_header* ip_hdr;
	arp_packet* arp;
	udp_header* udp;
	dhcp_packet* dhcp;

	llc_header* llc_hdr;
	u8 eth_dest[6];
	u8 eth_src[6];

	static u32 max_bd_count = 0;

	rxRing_ptr = XAxiDma_GetRxRing(&ETH_A_DMA_Instance);

	//Check if any Rx BDs have been executed
	//TODO: process XAXIDMA_ALL_BDS instead of 1 at a time
	//bd_count = XAxiDma_BdRingFromHw(rxRing_ptr, 1, &cur_bd_ptr);
	bd_count = XAxiDma_BdRingFromHw(rxRing_ptr, XAXIDMA_ALL_BDS, &first_bd_ptr);
	cur_bd_ptr = first_bd_ptr;

	if(bd_count > max_bd_count){
		max_bd_count = bd_count;
		//xil_printf("max_bd_count = %d\n",max_bd_count);
	}

	if(bd_count == 0) {
		//No Rx BDs have been processed - no new Eth receptions waiting
		return;
	}

	for(i=0;i<bd_count;i++){

		//A packet has been received and transferred by DMA
		tx_queue = (packet_bd*)XAxiDma_BdGetId(cur_bd_ptr);

		//xil_printf("DMA has filled in packet_bd at 0x%08x\n", tx_queue);

		eth_rx_len = XAxiDma_BdGetActualLength(cur_bd_ptr, rxRing_ptr->MaxTransferLen);
		eth_rx_buf = XAxiDma_BdGetBufAddr(cur_bd_ptr);

		//After encapsulation, byte[0] of the MPDU will be at byte[0] of the queue entry frame buffer
		mpdu_start_ptr = (void*)((tx_packet_buffer*)(tx_queue->buf_ptr))->frame;

		eth_start_ptr = (u8*)eth_rx_buf;

		//Calculate actual wireless Tx len (eth payload - eth header + wireless header)
		mpdu_tx_len = eth_rx_len - sizeof(ethernet_header) + sizeof(llc_header) + sizeof(mac_header_80211);

		//Helper pointers to interpret/fill fields in the new MPDU
		eth_hdr = (ethernet_header*)eth_start_ptr;
		llc_hdr = (llc_header*)(mpdu_start_ptr + sizeof(mac_header_80211));

		//Copy the src/dest addresses from the received Eth packet to temp space
		memcpy(eth_src, eth_hdr->address_source, 6);
		memcpy(eth_dest, eth_hdr->address_destination, 6);

		//Prepare the MPDU LLC header
		llc_hdr->dsap = LLC_SNAP;
		llc_hdr->ssap = LLC_SNAP;
		llc_hdr->control_field = LLC_CNTRL_UNNUMBERED;
		bzero((void *)(llc_hdr->org_code), 3); //Org Code 0x000000: Encapsulated Ethernet

		packet_is_queued = 0;
		packet_bd_list tx_queue_list;
		packet_bd_list_init(&tx_queue_list);
		packet_bd_insertEnd(&tx_queue_list, tx_queue);

		switch(eth_encap_mode){
			case ENCAP_MODE_AP:

				switch(eth_hdr->type) {
					case ETH_TYPE_ARP:
						llc_hdr->type = LLC_TYPE_ARP;
						packet_is_queued = eth_rx_callback(&tx_queue_list, eth_dest, eth_src, mpdu_tx_len);
					break;
					case ETH_TYPE_IP:
						llc_hdr->type = LLC_TYPE_IP;
						packet_is_queued = eth_rx_callback(&tx_queue_list, eth_dest, eth_src, mpdu_tx_len);
					break;

					/* WMP_START */
					case WMP4WARP_ETHER_TYPE:
						wmp_high_util_handle_wmp_cmd_from_ethernet(eth_hdr);
					break;
					/* WMP_END */
					default:
						//Unknown/unsupported EtherType; don't process the Eth frame
					break;
				}

			break;

			case ENCAP_MODE_STA:

				//Save this ethernet src address for d
				memcpy(eth_sta_mac_addr, eth_src, 6);
				memcpy(eth_src, hw_info.hw_addr_wlan, 6);

				switch(eth_hdr->type) {
					case ETH_TYPE_ARP:
						arp = (arp_packet*)((void*)eth_hdr + sizeof(ethernet_header));

						//Here we hijack ARP messages and overwrite their source MAC address field with
						//the station's wireless MAC address.
						memcpy(arp->eth_src, hw_info.hw_addr_wlan, 6);

						llc_hdr->type = LLC_TYPE_ARP;
						packet_is_queued = eth_rx_callback(&tx_queue_list, eth_dest, eth_src, mpdu_tx_len);


					break;
					case ETH_TYPE_IP:
						llc_hdr->type = LLC_TYPE_IP;
						ip_hdr = (ipv4_header*)((void*)eth_hdr + sizeof(ethernet_header));

						if(ip_hdr->prot == IPV4_PROT_UDP){
							udp = (udp_header*)((void*)ip_hdr + 4*((u8)(ip_hdr->ver_ihl) & 0xF));
							udp->checksum = 0; //Disable the checksum since we are about to mess with the bytes in the packet

							if(Xil_Ntohs(udp->src_port) == UDP_SRC_PORT_BOOTPC || Xil_Ntohs(udp->src_port) == UDP_SRC_PORT_BOOTPS){
								//This is a DHCP Discover packet, which contains the source hardware address
								//deep inside the packet (in addition to its usual location in the Eth header).
								//For STA encapsulation, we need to overwrite this address with the MAC addr
								//of the wireless station.

								dhcp = (dhcp_packet*)((void*)udp + sizeof(udp_header));

								if(Xil_Ntohl(dhcp->magic_cookie) == DHCP_MAGIC_COOKIE){
									eth_mid_ptr = (u8*)((void*)dhcp + sizeof(dhcp_packet));

									dhcp->flags = Xil_Htons(DHCP_BOOTP_FLAGS_BROADCAST);

									//Tagged DHCP Options
									continue_loop = 1;

									while(continue_loop){
										switch(eth_mid_ptr[0]){

											case DHCP_OPTION_TAG_TYPE:
												switch(eth_mid_ptr[2]){
													case DHCP_OPTION_TYPE_DISCOVER:
													case DHCP_OPTION_TYPE_REQUEST:
														//memcpy(dhcp->chaddr,hw_info.hw_addr_wlan,6);
													break;

												}

											break;

											case DHCP_OPTION_TAG_IDENTIFIER:
												//memcpy(&(eth_mid_ptr[3]),hw_info.hw_addr_wlan,6);

											break;

											case DHCP_OPTION_END:
												continue_loop = 0;
											break;
										}
										eth_mid_ptr += (2+eth_mid_ptr[1]);
									}
								}
							}
						}
						packet_is_queued = eth_rx_callback(&tx_queue_list, eth_dest, eth_src, mpdu_tx_len);

					break;

					/* WMP_START */
					case WMP4WARP_ETHER_TYPE:{
						wmp_high_util_handle_wmp_cmd_from_ethernet(eth_hdr);
					break;}
					/* WMP_END */
					default:
						//Unknown/unsupported EtherType; don't process the Eth frame
					break;
			}

			break;

		}

		if(packet_is_queued == 0){
			//xil_printf("   ...checking in\n");
			queue_checkin(&tx_queue_list);
		}

		//TODO: Option A: We free this single BD and run the routine to checkout as many queues as we can and hook them up to BDs
		//Results: pretty good TCP performance
		//Free this bd
		status = XAxiDma_BdRingFree(rxRing_ptr, 1, cur_bd_ptr);
		if(status != XST_SUCCESS) {xil_printf("Error in XAxiDma_BdRingFree of Rx BD! Err = %d\n", status); return;}
		wlan_eth_dma_update();

		//Update cur_bd_ptr to the next BD in the chain for the next iteration
		cur_bd_ptr = XAxiDma_BdRingNext(rxRing_ptr, cur_bd_ptr);

	}
	//TODO: Option B: We free all BDs at once and run the routine to checkout as many queues as we can and hook them up to BDs
	//Results: pretty lackluster TCP performance. needs further investigation
	//Free this bd
	//status = XAxiDma_BdRingFree(rxRing_ptr, bd_count, first_bd_ptr);
	//if(status != XST_SUCCESS) {xil_printf("Error in XAxiDma_BdRingFree of Rx BD! Err = %d\n", status); return;}
	//wlan_eth_dma_update();

	return;
}
//De-encapsulate packet and send over Ethernet
int wlan_mpdu_eth_send(void* mpdu, u16 length){
	int status;

	u8* eth_mid_ptr;

	mac_header_80211* rx80211_hdr;
	llc_header* llc_hdr;
	ethernet_header* eth_hdr;

	u8 continue_loop;
	ipv4_header* ip_hdr;
	arp_packet* arp;
	udp_header* udp;
	dhcp_packet* dhcp;

	u8 addr_cache[6];

	switch(eth_encap_mode){
		case ENCAP_MODE_AP:
			rx80211_hdr = (mac_header_80211*)((void *)mpdu);
			llc_hdr = (llc_header*)((void *)mpdu + sizeof(mac_header_80211));
			eth_hdr = (ethernet_header*)((void *)mpdu + sizeof(mac_header_80211) + sizeof(llc_header) - sizeof(ethernet_header));

			length = length - sizeof(mac_header_80211) - sizeof(llc_header) + sizeof(ethernet_header);

			memmove(eth_hdr->address_destination, rx80211_hdr->address_3, 6);
			memmove(eth_hdr->address_source, rx80211_hdr->address_2, 6);

			switch(llc_hdr->type){
				case LLC_TYPE_ARP:
					//xil_printf("Sending ARP\n");
					eth_hdr->type = ETH_TYPE_ARP;
				break;

				case LLC_TYPE_IP:
					//xil_printf("Sending IP\n");
					eth_hdr->type = ETH_TYPE_IP;
				break;
				default:
					//Invalid or unsupported Eth type; punt
					return -1;
				break;
			}
		break;

		case ENCAP_MODE_STA:
			rx80211_hdr = (mac_header_80211*)((void *)mpdu);
			llc_hdr = (llc_header*)((void *)mpdu + sizeof(mac_header_80211));
			eth_hdr = (ethernet_header*)((void *)mpdu + sizeof(mac_header_80211) + sizeof(llc_header) - sizeof(ethernet_header));

			length = length - sizeof(mac_header_80211) - sizeof(llc_header) + sizeof(ethernet_header);

			if(wlan_addr_eq(rx80211_hdr->address_3, hw_info.hw_addr_wlan)){
				//This case handles the behavior of an AP reflecting a station-sent broadcast packet back out over the air.
				//Without this filtering, a station would see its own packet. This leads to very bad DHCP and ARP behavior.
				return -1;
			}

			///DEBUG



			memcpy(addr_cache,rx80211_hdr->address_3,6);

			if(wlan_addr_eq(rx80211_hdr->address_1,hw_info.hw_addr_wlan)){
				memcpy(eth_hdr->address_destination, eth_sta_mac_addr, 6);
			} else {
				memmove(eth_hdr->address_destination, rx80211_hdr->address_1, 6);
			}
			//memmove(eth_hdr->address_source, rx80211_hdr->address_3, 6);
			memcpy(eth_hdr->address_source, addr_cache,6);




			switch(llc_hdr->type){
				case LLC_TYPE_ARP:
					arp = (arp_packet*)((void*)eth_hdr + sizeof(ethernet_header));

					//	Here we hijack ARP messages and overwrite their source MAC address field with
					//	the Ethernet-connected client's MAC address

					if(wlan_addr_eq(arp->eth_dst,hw_info.hw_addr_wlan)){
						memcpy(arp->eth_dst, eth_sta_mac_addr, 6);
					}

					eth_hdr->type = ETH_TYPE_ARP;
				break;

				case ETH_TYPE_IP:
					eth_hdr->type = ETH_TYPE_IP;
					ip_hdr = (ipv4_header*)((void*)eth_hdr + sizeof(ethernet_header));

					if(ip_hdr->prot == IPV4_PROT_UDP){
						udp = (udp_header*)((void*)ip_hdr + 4*((u8)(ip_hdr->ver_ihl) & 0xF));
						udp->checksum = 0; //Disable the checksum since we are about to mess with the bytes in the packet

						if(Xil_Ntohs(udp->src_port) == UDP_SRC_PORT_BOOTPC || Xil_Ntohs(udp->src_port) == UDP_SRC_PORT_BOOTPS){
							//This is a DHCP Discover packet, which contains the source hardware address
							//deep inside the packet (in addition to its usual location in the Eth header).
							//For STA encapsulation, we need to overwrite this address with the MAC addr
							//of the wireless station.

							dhcp = (dhcp_packet*)((void*)udp + sizeof(udp_header));

							if(Xil_Ntohl(dhcp->magic_cookie) == DHCP_MAGIC_COOKIE){

									eth_mid_ptr = (u8*)((void*)dhcp + sizeof(dhcp_packet));

									//Tagged DHCP Options
									continue_loop = 1;

									while(continue_loop){
										switch(eth_mid_ptr[0]){

											case DHCP_OPTION_TAG_TYPE:
												switch(eth_mid_ptr[2]){
													case DHCP_OPTION_TYPE_DISCOVER:
													case DHCP_OPTION_TYPE_REQUEST:
														//memcpy(dhcp->chaddr,hw_info.hw_addr_wlan,6);
													break;

													case DHCP_OPTION_TYPE_ACK:
													break;

													case DHCP_OPTION_TYPE_OFFER:

													break;

												}

											break;

											case DHCP_OPTION_TAG_IDENTIFIER:
											//	memcpy(&(eth_mid_ptr[3]),eth_sta_mac_addr,6);

											break;


											case DHCP_OPTION_END:
												continue_loop = 0;
											break;
										}
										eth_mid_ptr += (2+eth_mid_ptr[1]);
									}
							//	}


							}
						}
					}

				break;
				default:
					//Invalid or unsupported Eth type; punt
					return -1;
				break;
			}
		break;
	}

	status = wlan_eth_dma_send((u8*)eth_hdr, length);
	if(status != 0) {xil_printf("Erroor in wlan_mac_send_eth! Err = %d\n", status); return -1;}

	return 0;
}
/**
*
* This function processes the received packet and generates the corresponding
* reply packets.
*
* @param	InstancePtr is a pointer to the instance of the EmacLite.
*
* @return	None.
*
* @note		This function assumes MAC does not strip padding or CRC.
*
******************************************************************************/
static void ProcessRecvFrame(XEmacLite *InstancePtr)
{
	u16 *RxFramePtr;
	u16 *TxFramePtr;
	u16 *TempPtr;
	u16 CheckSum;
	u32 NextTxBuffBaseAddr;
	int Index;
	int PacketType = 0;

	TxFramePtr = (u16 *)TxFrame;
	RxFramePtr = (u16 *)RxFrame;

	/*
	 * Determine the next expected Tx buffer address.
	 */
	NextTxBuffBaseAddr = XEmacLite_NextTransmitAddr(InstancePtr);

	/*
	 * Check the packet type.
	 */
	Index = MAC_ADDR_LEN;
	TempPtr = (u16 *)LocalMacAddr;
	while (Index--) {
		if (Xil_Ntohs((*(RxFramePtr + Index)) == BROADCAST_ADDR) &&
					(PacketType != MAC_MATCHED_PACKET)) {
			PacketType = BROADCAST_PACKET;
		} else if (Xil_Ntohs((*(RxFramePtr + Index)) == *(TempPtr + Index)) &&
					(PacketType != BROADCAST_PACKET)) {
			PacketType = MAC_MATCHED_PACKET;
		} else {
			PacketType = 0;
			break;
		}
	}

	/*
	 * Process broadcast packet.
	 */
	if (PacketType == BROADCAST_PACKET) {

		/*
		 * Check for an ARP Packet if so generate a reply.
		 */
		if (Xil_Ntohs(*(RxFramePtr + ETHER_PROTO_TYPE_LOC)) ==
				XEL_ETHER_PROTO_TYPE_ARP) {

			/*
			 * IP address of the local machine.
			 */
			TempPtr = (u16 *)LocalIpAddr;

			/*
			 * Check destination IP address of the packet with
			 * local IP address.
			 */
			if (
			((*(RxFramePtr + ARP_REQ_DEST_IP_LOC_1)) == *TempPtr++) &&
			((*(RxFramePtr + ARP_REQ_DEST_IP_LOC_2)) == *TempPtr++)) {

				/*
				 * Check ARP packet type(request/reply).
				 */
				if (Xil_Ntohs(*(RxFramePtr + ARP_REQ_STATUS_LOC)) ==
								ARP_REQUEST) {

					/*
					 * Add destination MAC address
					 * to the reply packet (i.e) source
					 * address of the received packet.
					 */
					Index = SRC_MAC_ADDR_LOC;
					while (Index < (SRC_MAC_ADDR_LOC +
							MAC_ADDR_LEN)) {
						*TxFramePtr++ =
							*(RxFramePtr + Index);
						Index++;
					}

					/*
					 * Add source (local) MAC address
					 * to the reply packet.
					 */
					Index = 0;
					TempPtr = (u16 *)LocalMacAddr;
					while (Index < MAC_ADDR_LEN) {
						*TxFramePtr++ = *TempPtr++;
						Index++;
					}

					/*
					 * Add Ethernet proto type H/W
					 * type(10/3MBps),H/W address length and
					 * protocol address len (i.e)same as in
					 * the received packet
					 */
					Index = ETHER_PROTO_TYPE_LOC;
					while (Index < (ETHER_PROTO_TYPE_LOC +
							ETHER_PROTO_TYPE_LEN +
							ARP_HW_TYPE_LEN +
							ARP_HW_ADD_LEN
							+ ARP_PROTO_ADD_LEN)) {
						*TxFramePtr++ =
							*(RxFramePtr + Index);
						Index++;
					}

					/*
					 * Add ARP reply status to the reply
					 * packet.
					 */
					*TxFramePtr++ = Xil_Htons(ARP_REPLY);

					/*
					 * Add local MAC Address
					 * to the reply packet.
					 */
					TempPtr = (u16 *)LocalMacAddr;
					Index = 0;
					while (Index < MAC_ADDR_LEN) {
						*TxFramePtr++ = *TempPtr++;
						Index++;
					}

					/*
					 * Add local IP Address
					 * to the reply packet.
					 */
					TempPtr = (u16 *)LocalIpAddr;
					Index = 0;
					while (Index < IP_ADDR_LEN) {
						*TxFramePtr++ = *TempPtr++ ;
						Index++;
					}

					/*
					 * Add Destination MAC Address
					 * to the reply packet from the received
					 * packet.
					 */
					Index = SRC_MAC_ADDR_LOC;
					while (Index < (SRC_MAC_ADDR_LOC +
							MAC_ADDR_LEN)) {
						*TxFramePtr++ =
							*(RxFramePtr + Index);
						Index++;
					}

					/*
					 * Add Destination IP Address
					 * to the reply packet.
					 */
					Index = ARP_REQ_SRC_IP_LOC;
					while (Index < (ARP_REQ_SRC_IP_LOC +
							IP_ADDR_LEN)) {
						*TxFramePtr++ =
								*(RxFramePtr + Index);
						Index++;
					}

					/*
					 * Fill zeros as per protocol.
					 */
					Index = 0;
					while (Index < ARP_ZEROS_LEN) {
						*TxFramePtr++ = 0x0000;
						Index++;
					}

					/*
					 * Transmit the Reply Packet.
					 */
					XEmacLite_Send(InstancePtr,
							(u8 *)&TxFrame,
							ARP_PACKET_SIZE);
				}
			}
		}
	}

	/*
	 * Process packets whose MAC address is matched.
	 */
 	if (PacketType == MAC_MATCHED_PACKET) {

		/*
		 * Check ICMP packet.
		 */
		if (Xil_Ntohs(*(RxFramePtr + ETHER_PROTO_TYPE_LOC)) ==
						XEL_ETHER_PROTO_TYPE_IP) {

			/*
			 * Check the IP header checksum.
			 */
			CheckSum = CheckSumCalculation(RxFramePtr,
						IP_HDR_START_LOC,
						IP_HDR_LEN);

			/*
			 * Check the Data field checksum.
			 */
			if (CheckSum == CORRECT_CKSUM_VALUE) {
				CheckSum = CheckSumCalculation(RxFramePtr,
						ICMP_DATA_START_LOC,
						ICMP_DATA_FIELD_LEN);
				if (CheckSum == CORRECT_CKSUM_VALUE) {

					/*
					 * Add destination address
					 * to the reply packet (i.e)source
					 * address of the received packet.
					 */
					Index = SRC_MAC_ADDR_LOC;
					while (Index < (SRC_MAC_ADDR_LOC +
							MAC_ADDR_LEN)) {
						*TxFramePtr++ =
							*(RxFramePtr + Index);
						Index++;
					}

					/*
					 * Add local MAC address
					 * to the reply packet.
					 */
					Index = 0;
					TempPtr = (u16 *)LocalMacAddr;
					while (Index < MAC_ADDR_LEN) {
						*TxFramePtr++ = *TempPtr++;
						Index++;
					}

					/*
					 * Add protocol type
					 * header length and, packet
					 * length(60 Bytes) to the reply packet.
					 */
					Index = ETHER_PROTO_TYPE_LOC;
					while (Index < (ETHER_PROTO_TYPE_LOC +
							ETHER_PROTO_TYPE_LEN +
							IP_VERSION_LEN +
							IP_PACKET_LEN)) {
						*TxFramePtr++ =
							*(RxFramePtr + Index);
						Index++;
					}

					/*
					 * Identification field a random number
					 * which is set to IDENT_FIELD_VALUE.
					 */
					*TxFramePtr++ = IDENT_FIELD_VALUE;

					/*
					 * Add fragment type, time to live and
					 * ICM field. It is same as in the
					 * received packet.
					 */
					Index = IP_FRAG_FIELD_LOC;
					while (Index < (IP_FRAG_FIELD_LOC +
							IP_TTL_ICM_LEN +
							IP_FRAG_FIELD_LEN)) {
						*TxFramePtr++ =
							*(RxFramePtr + Index);
						Index++;
					}

					/*
					 * Checksum first set to 0 and
					 * added in this field later.
					 */
					*TxFramePtr++ = 0x0000;

					/*
					 * Add Source IP address
					 */
					Index = 0;
					TempPtr = (u16 *)LocalIpAddr;
					while (Index < IP_ADDR_LEN) {
						*TxFramePtr++ = *TempPtr++;
						Index++;
					}

					/*
					 * Add Destination IP address.
					 */
					Index = ICMP_REQ_SRC_IP_LOC;
					while (Index < (ICMP_REQ_SRC_IP_LOC +
							IP_ADDR_LEN)) {
						*TxFramePtr++ =
							*(RxFramePtr + Index);
						Index++;
					}

					/*
					 * Calculate checksum, and
					 * add it in the appropriate field.
					 */
					CheckSum = CheckSumCalculation(
						 	(u16 *)TxFrame,
						 	IP_HDR_START_LOC,
						 	IP_HDR_LEN);
					CheckSum = ~CheckSum;
					*(TxFramePtr - IP_CSUM_LOC_BACK) =
								Xil_Htons(CheckSum);

					/*
					 * Echo reply status & checksum.
					 */
					Index = ICMP_ECHO_FIELD_LOC;
					while (Index < (ICMP_ECHO_FIELD_LOC +
							ICMP_ECHO_FIELD_LEN)) {
						*TxFramePtr++ = 0x0000;
						Index++;
					}

					/*
					 * Add data to buffer which was
					 * received from the packet.
					 */
					Index = ICMP_DATA_LOC;
					while (Index < (ICMP_DATA_LOC +
							ICMP_DATA_LEN)) {
						*TxFramePtr++ =
								(*(RxFramePtr + Index));
						Index++;
					}

					/*
					 * Generate checksum for the data and
					 * add it in the appropriate field.
					 */
					CheckSum = CheckSumCalculation(
							(u16 *)TxFrame,
							ICMP_DATA_START_LOC,
							ICMP_DATA_FIELD_LEN);
					CheckSum = ~CheckSum;
					*(TxFramePtr - ICMP_DATA_CSUM_LOC_BACK)
								= Xil_Htons(CheckSum);

					/*
					 * Transmit the frame.
					 */
					XEmacLite_Send(InstancePtr,
							(u8 *)&TxFrame,
							ICMP_PACKET_SIZE);

					/*
					 * Increment the number of
					 * Ping replies sent.
					 */
					NumOfPingReplies++;

				}
			}
		}
	}
}
/**
*
* This function will process the received packet. This function sends
* the echo request packet based on the ARP reply packet.
*
* @param	InstancePtr is a pointer to the instance of the EmacLite.
*
* @return	XST_SUCCESS is returned when an echo reply is received.
*		Otherwise, XST_FAILURE is returned.
*
* @note		This assumes MAC does not strip padding or CRC.
*
******************************************************************************/
static int ProcessRecvFrame(XEmacLite *InstancePtr)
{
	u16 *RxFramePtr;
	u16 *TempPtr;
	u16 CheckSum;
	int Index;
	int Match = 0;
	int DataWrong = 0;

	RxFramePtr = (u16 *)RxFrame;
	TempPtr = (u16 *)LocalMacAddr;

	/*
	 * Check Dest Mac address of the packet with the LocalMac address.
	 */
	Match = CompareData(RxFramePtr, TempPtr, 0, 0, MAC_ADDR_LEN);
	if (Match == XST_SUCCESS) {

		/*
		 * Check ARP type.
		 */
		if (Xil_Ntohs(*(RxFramePtr + ETHER_PROTO_TYPE_LOC)) ==
				XEL_ETHER_PROTO_TYPE_ARP ) {

			/*
			 * Check ARP status.
			 */
			if (Xil_Ntohs(*(RxFramePtr + ARP_REQ_STATUS_LOC)) == ARP_REPLY) {

				/*
				 * Check destination IP address with
				 * packet's source IP address.
				 */
				TempPtr = (u16 *)DestIpAddress;
				Match = CompareData(RxFramePtr,
						TempPtr, ARP_REQ_SRC_IP_LOC,
						0, IP_ADDR_LEN);
				if (Match == XST_SUCCESS) {

					/*
					 * Copy src Mac address of the received
					 * packet.
					 */
					Index = MAC_ADDR_LEN;
					TempPtr = (u16 *)DestMacAddr;
					while (Index--) {
						*(TempPtr + Index) =
							*(RxFramePtr +
							(SRC_MAC_ADDR_LOC +
								Index));
					}

					/*
					 * Send Echo request packet.
					 */
					SendEchoReqFrame(InstancePtr);
				}
			}
		}

		/*
		 * Check for IP type.
		 */
		else if (Xil_Ntohs(*(RxFramePtr + ETHER_PROTO_TYPE_LOC)) ==
						XEL_ETHER_PROTO_TYPE_IP) {

			/*
			 * Calculate checksum.
			 */
			CheckSum = CheckSumCalculation(RxFramePtr,
							ICMP_DATA_START_LOC,
							ICMP_DATA_FIELD_LEN);

			/*
			 * Verify checksum, echo reply, identifier number and
			 * sequence number of the received packet.
			 */
			if ((CheckSum == CORRECT_CHECKSUM_VALUE) &&
			(Xil_Ntohs(*(RxFramePtr + ICMP_ECHO_FIELD_LOC)) == ECHO_REPLY) &&
			(Xil_Ntohs(*(RxFramePtr + ICMP_IDEN_FIELD_LOC)) == IDEN_NUM) &&
			(Xil_Ntohs(*(RxFramePtr + (ICMP_SEQ_NO_LOC))) == SeqNum)) {

				/*
				 * Verify data in the received packet with known
				 * data.
				 */
				TempPtr = IcmpData;
				Match = CompareData(RxFramePtr,
						TempPtr, ICMP_KNOWN_DATA_LOC,
							0, ICMP_KNOWN_DATA_LEN);
				if (Match == XST_FAILURE) {
					DataWrong = 1;
				}
			}
			if (DataWrong != 1) {
				xil_printf("Packet No: %d ",
				NUM_OF_PING_REQ_PKTS - NumOfPingReqPkts);
				xil_printf("Seq NO %d Echo Packet received\r\n",
								SeqNum);
				return XST_SUCCESS;
			}
		}
	}
	return XST_FAILURE;
}
/**
 * This function is used to send a message over Ethernet
 *
 * @param   socket_index     - Index of the socket on which to send message
 * @param   to               - Pointer to socket address structure to send message
 * @param   buffers          - Array of transport buffers to send
 * @param   num_buffers      - Number of transport buffers in 'buffers' array
 *
 * @return  None
 *
 * @note    This function requires that the first transport buffer in the 'buffers'
 *          array contain the Transport header.
 *
 *****************************************************************************/
void transport_send(int socket_index, struct sockaddr * to, warp_ip_udp_buffer ** buffers, u32 num_buffers) {

    u32                      i;
    int                      status;
    transport_header       * transport_header_tx;
    u16                      buffer_length = 0;
	// interrupt_state_t     prev_interrupt_state;

    // Check that we have a valid socket to send a message on
    if (socket_index == SOCKET_INVALID_SOCKET) {
        wlan_exp_printf(WLAN_EXP_PRINT_ERROR, print_type_transport, "Invalid socket.\n");
        return;
    }

    // Initialize the header
    //     NOTE:  We require that the first warp_ip_udp_buffer always contain the wl_transport_header
    //
    transport_header_tx = (transport_header *)(buffers[0]->data);

    // Compute the length
    for (i = 0; i < num_buffers; i++) {
        buffer_length += buffers[i]->size;
    }

    //
    // NOTE:  Through performance testing, we found that it was faster to just manipulate the header
    //   in place vs creating a copy, updating the header and then restoring the copy.
    //

    // Make the outgoing transport header endian safe for sending on the network
    //     NOTE:  Set the 'length' to the computed value above
    //
    transport_header_tx->dest_id = Xil_Htons(transport_header_tx->dest_id);
    transport_header_tx->src_id  = Xil_Htons(transport_header_tx->src_id);
    transport_header_tx->length  = Xil_Htons(buffer_length + WARP_IP_UDP_DELIM_LEN);
    transport_header_tx->seq_num = Xil_Htons(transport_header_tx->seq_num);
    transport_header_tx->flags   = Xil_Htons(transport_header_tx->flags);

	// Check the interrupt status; Disable interrupts if enabled
    //     NOTE:  This is done inside the Eth send function
	// prev_interrupt_state = wlan_mac_high_interrupt_stop();

    // Send the Ethernet packet
    status = socket_sendto(socket_index, to, buffers, num_buffers);

	// Restore interrupts
    //     NOTE:  This is done inside the Eth send function
	// wlan_mac_high_interrupt_restore_state(prev_interrupt_state);

    // Restore wl_header_tx
	transport_header_tx->dest_id = Xil_Ntohs(transport_header_tx->dest_id);
	transport_header_tx->src_id  = Xil_Ntohs(transport_header_tx->src_id);
	transport_header_tx->length  = 0;
	transport_header_tx->seq_num = Xil_Ntohs(transport_header_tx->seq_num);
	transport_header_tx->flags   = Xil_Ntohs(transport_header_tx->flags);

    // Check that the packet was sent correctly
    if (status == WARP_IP_UDP_FAILURE) {
        wlan_exp_printf(WLAN_EXP_PRINT_WARNING, print_type_transport, "Issue sending packet %d to host.\n", transport_header_tx->seq_num);
    }
}
/**
 * Process the received UDP packet by the transport
 *
 * @param   eth_dev_num      - Ethernet device number
 * @param   socket_index     - Index of the socket on which message was received
 * @param   from             - Pointer to socket address structure from which message was received
 * @param   recv_buffer      - Pointer to transport buffer with received message
 * @param   send_buffer      - Pointer to transport buffer for a node response to the message
 *
 * @return  None
 *
 * @note    If this packet is a host to node message, then the  process_hton_msg_callback
 *          is used to further process the packet.  This method will strip off the
 *          Transport header for future packet processing.
 *
 *****************************************************************************/
void transport_receive(u32 eth_dev_num, int socket_index, struct sockaddr * from, warp_ip_udp_buffer * recv_buffer, warp_ip_udp_buffer * send_buffer) {

    int                           status;
    u16                           dest_id;
    u16                           src_id;
    u16                           seq_num;
    u16                           flags;
    u32                           node_id;
    u32                           group_id;
    u32                           recv_flags = 0;

    transport_header            * transport_header_rx = (transport_header*)(recv_buffer->offset);   // Contains entire Ethernet frame; offset points to UDP payload
    transport_header            * transport_header_tx = (transport_header*)(send_buffer->offset);   // New buffer for UDP payload

    // Get the transport headers for the send / receive buffers
    //     NOTE:  For the receive buffer, offset points to UDP payload of the Ethernet frame
    //     NOTE:  For the send buffer, the offset points to the start of the buffer but since we will use the
    //            UDP header of the socket to transmit the frame, this is effectively the start of the UDP payload
    //
    transport_header_rx      = (transport_header*)(recv_buffer->offset);
    transport_header_tx      = (transport_header*)(send_buffer->offset);

    // Update the buffers to account for the transport headers
    recv_buffer->offset     += sizeof(transport_header);
    recv_buffer->length     -= sizeof(transport_header);                    // Remaining bytes in receive buffer

    send_buffer->offset     += sizeof(transport_header);
    send_buffer->length     += sizeof(transport_header);                    // Adding bytes to the send buffer
    send_buffer->size       += sizeof(transport_header);                    // Keep size in sync


    // Process the data based on the packet type
    //     NOTE:  The pkt_type does not need to be endian swapped because it is a u8
    //
    switch(transport_header_rx->pkt_type){

        //-------------------------------
        // Message from the Host to the Node
        //
        case PKT_TYPE_HTON_MSG:
            // Extract values from the received transport header
            //
            dest_id  = Xil_Ntohs(transport_header_rx->dest_id);
            src_id   = Xil_Ntohs(transport_header_rx->src_id);
            seq_num  = Xil_Ntohs(transport_header_rx->seq_num);
            flags    = Xil_Ntohs(transport_header_rx->flags);

            node_id  = eth_devices[eth_dev_num].node_id;
            group_id = eth_devices[eth_dev_num].info.group_id;

            // If this message is not for the given node, then ignore it
            if((dest_id != node_id) && (dest_id != TRANSPORT_BROADCAST_DEST_ID) && ((dest_id & (0xFF00 | group_id)) == 0)) { return; }

            // Set the receive flags
            //     [0] - Is the packet broadcast?  WLAN_EXP_TRUE / WLAN_EXP_FALSE
            //
            if (dest_id == TRANSPORT_BROADCAST_DEST_ID) {
                recv_flags |= 0x00000001;
            }

            // Form outgoing Transport header for any outgoing packet in response to this message
            //     NOTE:  The u16/u32 fields here will be endian swapped in transport_send
            //     NOTE:  The length field of the header will be set in transport_send
            //
            transport_header_tx->dest_id  = src_id;
            transport_header_tx->src_id   = node_id;
            transport_header_tx->pkt_type = PKT_TYPE_NTOH_MSG;
            transport_header_tx->seq_num  = seq_num;
            transport_header_tx->flags    = 0;
            transport_header_tx->reserved = 0;

            // Call the callback to further process the recv_buffer
            status = process_hton_msg_callback(socket_index, from, recv_buffer, recv_flags, send_buffer);

            if (send_buffer->size != send_buffer->length) {
                wlan_exp_printf(WLAN_EXP_PRINT_WARNING, print_type_transport, "Send buffer length (%d) does not match size (%d)\n", send_buffer->length, send_buffer->size);
            }

            // Based on the status, return a message to the host
            switch(status) {

                //-------------------------------
                // No response has been sent by the node
                //
                case NO_RESP_SENT:
                    // Check if the host requires a response from the node
                    if (flags & TRANSPORT_HDR_ROBUST_FLAG) {

                        // Check that the node has something to send to the host
                        if ((send_buffer->length) > sizeof(transport_header)) {
                            transport_send(socket_index, from, &send_buffer, 1);                        // Send the buffer of data
                        } else {
                            wlan_exp_printf(WLAN_EXP_PRINT_WARNING, print_type_transport, "Host requires response but node has nothing to send.\n");
                        }
                    }
                break;

                   //-------------------------------
                // A response has already been sent by the node
                //
                case RESP_SENT:
                    // The transport does not need to do anything else
                break;

                default:
                    wlan_exp_printf(WLAN_EXP_PRINT_ERROR, print_type_transport, "Received unknown status for message: %d\n", status);
                break;
               }
        break;

        default:
            wlan_exp_printf(WLAN_EXP_PRINT_ERROR, print_type_transport, "Received packet with unknown packet type: %d\n", (transport_header_rx->pkt_type));
        break;
    }
}
/**
*
* This example sends and receives a single packet in loopback mode with
* extended VLAN support.
*
* The transmit frame will have VLAN field populated.
*
* On receive, HW should pass the VLAN field to receive BDs.
*
* @param	AxiEthernetInstancePtr is a pointer to the instance of the
*		AxiEthernet component.
* @param	DmaInstancePtr   is a pointer to the instance of the Dma
*		component.
*
* @return	-XST_SUCCESS to indicate success.
*		-XST_FAILURE to indicate failure.
*
* @note		Summary of VLAN tags handling in this example
*
* Frame setup with Tpid1+Cfi1+TxPid => 0x88A83111
* Frame translated to TxTransVid => 0x88A83222
* Frame tagged to Tpid2+Cfi2+TxTagVid => 0x9100C333 + 0x88A83222
* Frame sent and loopbacked.
*
* Frame stripped with RxStrpVid(0x333) => 0x88A83222
* Frame translated (key:RxVid:0x222) RxTransVid => 0x88A83444
*
******************************************************************************/
int AxiEthernetSgDmaIntrExtVlanExample(XAxiEthernet *AxiEthernetInstancePtr,
				XAxiDma *DmaInstancePtr)
{
	int Status;
	u32 TxFrameLength;
	u32 RxFrameLength;
	int PayloadSize = PAYLOAD_SIZE;
	u16 Tpid1 = 0x88A8;
	u16 Tpid2 = 0x9100;
	u8  Cfi1  = 0x03;
	u8  Cfi2  = 0x0C;
	u16 TxVid      = 0x0111;
	u16 TxTransVid = 0x0222;
	u16 TxTagVid   = 0x0333;
	u16 RxVid      = 0x0222;
	u16 RxTransVid = 0x0444;
	u16 RxStrpVid  = 0x0333;
	u32 VTagCfiVid;
	u16 RxCfiVid;
	u16 RxTpid;
	u32 RxStatusControlWord;
	int Valid;


	XAxiDma_BdRing *RxRingPtr = XAxiDma_GetRxRing(DmaInstancePtr);
	XAxiDma_BdRing *TxRingPtr = XAxiDma_GetTxRing(DmaInstancePtr);
	XAxiDma_Bd *BdPtr;
	XAxiDma_Bd *BdCurPtr;
	u32 BdSts;

	/*
	 * Cannot run this example if extended features support is not enabled
	 */
	if (!(XAxiEthernet_IsTxVlanTran(AxiEthernetInstancePtr) &&
		XAxiEthernet_IsTxVlanStrp(AxiEthernetInstancePtr) &&
		XAxiEthernet_IsTxVlanTag(AxiEthernetInstancePtr)  &&
		XAxiEthernet_IsRxVlanTran(AxiEthernetInstancePtr) &&
		XAxiEthernet_IsRxVlanStrp(AxiEthernetInstancePtr) &&
		XAxiEthernet_IsRxVlanTag(AxiEthernetInstancePtr))) {
		AxiEthernetUtilErrorTrap("Extended VLAN not available");
		return XST_FAILURE;
	}

	/*
	 * Clear variables shared with callbacks
	 */
	FramesRx = 0;
	FramesTx = 0;
	DeviceErrors = 0;
	memset(RxFrame,0,sizeof(RxFrame));
	memset(TxFrame,0,sizeof(TxFrame));

	/*
	 * Calculate frame length (not including FCS) plus one VLAN tag
	 */
	TxFrameLength = XAE_HDR_VLAN_SIZE + PayloadSize;

	/*
	 * Setup the packet with one VALN tag = VtagCfiVid to be transmitted
	 * initially.
	 */
	VTagCfiVid = (((u32)Tpid1 << 16) | ((u32)Cfi1 << 12) | TxVid);
	AxiEthernetUtilFrameMemClear(&TxFrame);
	AxiEthernetUtilFrameHdrFormatMAC(&TxFrame, AxiEthernetMAC);
	AxiEthernetUtilFrameHdrVlanFormatVid(&TxFrame, 0, VTagCfiVid);
	AxiEthernetUtilFrameHdrVlanFormatType(&TxFrame, PayloadSize, 1);
	AxiEthernetUtilFrameSetVlanPayloadData(&TxFrame, PayloadSize, 1);

	/* Intended VLAN setup:
	 * TX translation and tagging. RX stripping and translation.
	 *
	 * Frame setup with Tpid1+Cfi1+TxPid => 0x88A83111
	 * Frame translated to TxTransVid => 0x88A83222
	 * Frame tagged to Tpid2+Cfi2+TxTagVid => 0x9100C333 + 0x88A83222
	 * Frame sent and loopbacked.
	 *
	 * Frame stripped with RxStrpVid(0x333) => 0x88A83222
	 * Frame translated (key:RxVid:0x222) RxTransVid => 0x88A83444
	 */

	/* Extended VLAN transmit side. Stripping->Translation->Tagging */
	/*
	 * Configure VLAN TX tag mode, set to XAE_VTAG_SELECT.
	 */
	Status  = XAxiEthernet_SetOptions(AxiEthernetInstancePtr,
						XAE_EXT_TXVLAN_TAG_OPTION);
	Status |= XAxiEthernet_SetVTagMode(AxiEthernetInstancePtr,
						XAE_VTAG_SELECT, XAE_TX);

	/*
	 * TX VLAN translation from TxVid to TxTransVid and enable tagging.
	 */
	Status |= XAxiEthernet_SetOptions(AxiEthernetInstancePtr,
						XAE_EXT_TXVLAN_TRAN_OPTION);
	Status |= XAxiEthernet_SetVidTable(AxiEthernetInstancePtr, TxVid,
						TxTransVid,0, 1, XAE_TX);

	/*
	 * TX VLAN tagging is keyed on TxVid to add one additional tag based
	 * on register XAE_TTAG_OFFSET value.
	 */
	VTagCfiVid = (((u32)Tpid2 << 16) | ((u32)Cfi2 << 12) | TxTagVid);
	Status |= XAxiEthernet_SetVTagValue(AxiEthernetInstancePtr,
							VTagCfiVid, XAE_TX);
	if (Status != XST_SUCCESS) {
		AxiEthernetUtilErrorTrap("Error setting TX VLAN");
		return XST_FAILURE;
	}

	/* Extended VLAN receive side. Stripping->Translation->Tagging */
	/*
	 * Configure VLAN RX strip mode, set to XAE_VSTRP_SELECT.
	 */
	Status = XAxiEthernet_SetOptions(AxiEthernetInstancePtr,
						XAE_EXT_RXVLAN_STRP_OPTION);
	Status |= XAxiEthernet_SetVStripMode(AxiEthernetInstancePtr,
						XAE_VSTRP_SELECT, XAE_RX);

	/*
	 * RX VLAN strips based on RxStrpVid and enable stripping.
	 */
	Status |= XAxiEthernet_SetVidTable(AxiEthernetInstancePtr, RxStrpVid,
						RxStrpVid, 1, 0, XAE_RX);

	/*
	 * RX VLAN translation from RxVid to RxTransVid only.
	 */
	Status |= XAxiEthernet_SetOptions(AxiEthernetInstancePtr,
						XAE_EXT_RXVLAN_TRAN_OPTION);
	Status |= XAxiEthernet_SetVidTable(AxiEthernetInstancePtr, RxVid,
						RxTransVid, 0, 0, XAE_RX);

	if (Status != XST_SUCCESS) {
		AxiEthernetUtilErrorTrap("Error setting RX VLAN");
		return XST_FAILURE;
	}

	/* Configure VLAN TPIDs for HW to recognize. */
	Status  = XAxiEthernet_SetTpid(AxiEthernetInstancePtr, Tpid1, 0);
	Status |= XAxiEthernet_SetTpid(AxiEthernetInstancePtr, Tpid2, 1);
	if (Status != XST_SUCCESS) {
		AxiEthernetUtilErrorTrap("Error setting TPIDs");
		return XST_FAILURE;
	}

	/*
	 * Flush the TX frame before giving it to DMA TX channel to transmit.
	 */
	Xil_DCacheFlushRange((u32)&TxFrame, TxFrameLength);

	/*
	 * Clear out receive packet memory area
	 */
	AxiEthernetUtilFrameMemClear(&RxFrame);

	/*
	 * Invalidate the RX frame before giving it to DMA RX channel to
	 * receive data.
 	 */
	Xil_DCacheInvalidateRange((u32)&RxFrame, TxFrameLength + 4);

	/*
	 * Interrupt coalescing parameters are set to their default settings
	 * which is to interrupt the processor after every frame has been
	 * processed by the DMA engine.
	 */

	Status = XAxiDma_BdRingSetCoalesce(TxRingPtr, 1, 1);
	if (Status != XST_SUCCESS) {
		AxiEthernetUtilErrorTrap("Error setting coalescing for transmit");
		return XST_FAILURE;
	}

	Status = XAxiDma_BdRingSetCoalesce(RxRingPtr, 1, 1);
	if (Status != XST_SUCCESS) {
		AxiEthernetUtilErrorTrap("Error setting coalescing for recv");
		return XST_FAILURE;
	}

	/*
	 * Make sure Tx and Rx are enabled
	 */
	Status = XAxiEthernet_SetOptions(AxiEthernetInstancePtr,
					XAE_RECEIVER_ENABLE_OPTION |
					XAE_TRANSMITTER_ENABLE_OPTION );
	if (Status != XST_SUCCESS) {
		AxiEthernetUtilErrorTrap("Error setting options");
		return XST_FAILURE;
	}

	Status = XAxiEthernet_SetOptions(AxiEthernetInstancePtr,
					XAE_JUMBO_OPTION);
	if (Status != XST_SUCCESS) {
		AxiEthernetUtilErrorTrap("Error setting options");
		return XST_FAILURE;
	}

	/*
	 * Start the AxiEthernet and enable its ERROR interrupts
	 */
	XAxiEthernet_Start(AxiEthernetInstancePtr);
	XAxiEthernet_IntEnable(&AxiEthernetInstance,
					XAE_INT_RECV_ERROR_MASK);

	/*
	 * Enable DMA receive related interrupts
	 */
	XAxiDma_BdRingIntEnable(RxRingPtr, XAXIDMA_IRQ_ALL_MASK);

	/*
	 * Allocate 1 RxBD.
	 */
	Status = XAxiDma_BdRingAlloc(RxRingPtr, 1, &BdPtr);
	if (Status != XST_SUCCESS) {
		AxiEthernetUtilErrorTrap("Error allocating RxBD");
		return XST_FAILURE;
	}

	/*
	 * Setup the BD.
	 */
	XAxiDma_BdSetBufAddr(BdPtr, (u32)&RxFrame);
#ifndef XPAR_AXIDMA_0_ENABLE_MULTI_CHANNEL
	XAxiDma_BdSetLength(BdPtr, sizeof(RxFrame));
#else
	XAxiDma_BdSetLength(BdPtr, sizeof(RxFrame),
				RxRingPtr->MaxTransferLen);
#endif
	XAxiDma_BdSetCtrl(BdPtr, 0);

	/*
	 * Enqueue to HW
	 */
	Status = XAxiDma_BdRingToHw(RxRingPtr, 1, BdPtr);
	if (Status != XST_SUCCESS) {
		AxiEthernetUtilErrorTrap("Error committing RxBD to HW");
		return XST_FAILURE;
	}

	/*
	 * Start DMA RX channel. Now it's ready to receive data.
	 */
	Status = XAxiDma_BdRingStart(RxRingPtr);
	if (Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	/*
	 * Enable DMA transmit related interrupts
	 */
	XAxiDma_BdRingIntEnable(TxRingPtr, XAXIDMA_IRQ_ALL_MASK);

	/*
	 * Allocate 1 TxBD
	 */
	Status = XAxiDma_BdRingAlloc(TxRingPtr, 1, &BdPtr);
	if (Status != XST_SUCCESS) {
		AxiEthernetUtilErrorTrap("Error allocating TxBD");
		return XST_FAILURE;
	}

	/*
	 * Setup the TxBD
	 */
	XAxiDma_BdSetBufAddr(BdPtr, (u32)&TxFrame);
#ifndef XPAR_AXIDMA_0_ENABLE_MULTI_CHANNEL
	XAxiDma_BdSetLength(BdPtr, TxFrameLength);
#else
	XAxiDma_BdSetLength(BdPtr, TxFrameLength,
				TxRingPtr->MaxTransferLen);
#endif
	XAxiDma_BdSetCtrl(BdPtr, XAXIDMA_BD_CTRL_TXSOF_MASK |
						XAXIDMA_BD_CTRL_TXEOF_MASK);

	/*
	 * Enqueue to HW
	 */
	Status = XAxiDma_BdRingToHw(TxRingPtr, 1, BdPtr);
	if (Status != XST_SUCCESS) {
		AxiEthernetUtilErrorTrap("Error committing TxBD to HW");
		return XST_FAILURE;
	}

	/*
	 * Start DMA TX channel. Transmission starts at once.
	 */
	Status = XAxiDma_BdRingStart(TxRingPtr);
	if (Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	/*
	 * Wait for transmission to complete
	 */
	while (!FramesTx);

	/*
	 * Now that the frame has been sent, post process our TxBDs.
	 * Since we have only submitted 2 to HW, then there should be only 2
	 * ready for post processing.
	 */
	if (XAxiDma_BdRingFromHw(TxRingPtr, 1, &BdPtr) == 0) {
		AxiEthernetUtilErrorTrap("TxBDs were not ready for post processing");
		return XST_FAILURE;
	}

	/*
	 * Examine the TxBDs.
	 *
	 * There isn't much to do. The only thing to check would be DMA
	 * exception bits. But this would also be caught in the error handler.
	 * So we just return these BDs to the free list
	 */
	Status = XAxiDma_BdRingFree(TxRingPtr, 1, BdPtr);
	if (Status != XST_SUCCESS) {
		AxiEthernetUtilErrorTrap("Error freeing up TxBDs");
		return XST_FAILURE;
	}

	/*
	 * Wait for Rx indication
	 */
	while (!FramesRx);
	/*
	 * Now that the frame has been received, post process our RxBD.
	 * Since we have only submitted 1 to HW, then there should be only 1
	 * ready for post processing.
	 */
	if (XAxiDma_BdRingFromHw(RxRingPtr, 1, &BdPtr) == 0) {
		AxiEthernetUtilErrorTrap("RxBD was not ready for post processing");
		return XST_FAILURE;
	}

	BdCurPtr = BdPtr;
	BdSts = XAxiDma_BdGetSts(BdCurPtr);

	if ((BdSts & XAXIDMA_BD_STS_ALL_ERR_MASK) ||
		(!(BdSts & XAXIDMA_BD_STS_COMPLETE_MASK))) {
			AxiEthernetUtilErrorTrap("Rx Error");
			return XST_FAILURE;
	}
	else {

		RxFrameLength =
		(XAxiDma_BdRead(BdCurPtr,XAXIDMA_BD_USR4_OFFSET)) & 0x0000FFFF;
	}

	/* Expected RX TPID+CFI+VID !!! */
	VTagCfiVid = (((u32)Tpid2 << 16) | ((u32)Cfi2 << 12) | RxStrpVid);

	/* Check on the VLAN CFI and VID */
	RxStatusControlWord = XAxiDma_BdGetAppWord(BdPtr,
						BD_VLAN_VID_OFFSET, &Valid);
	if(Valid) {
		RxCfiVid = RxStatusControlWord >> 16;
		RxCfiVid = Xil_Ntohs(RxCfiVid);
		if(RxCfiVid != (VTagCfiVid & 0x0000FFFF)) {
			AxiEthernetUtilErrorTrap("VLAN CFI and VID mismatch\n");
			return XST_FAILURE;
		}
	}
	else {