/*
 * Function definition: ixEthAccCodeletSwBridgeStart()
 *
 * Start bridge datapath
 */
IX_STATUS ixEthAccCodeletSwBridgeStart(IxEthAccPortId firstPortId, 
				       IxEthAccPortId secondPortId)
{
    UINT32 firstPortCbTag = firstPortId | (secondPortId << 16);
    UINT32 secondPortCbTag = secondPortId | (firstPortId << 16);

    if (firstPortId == secondPortId)
    {
	printf("SwBridge: Cannot configure a bridge between Port %u and Port %u (ports must be different)\n", 
                firstPortId, 
                secondPortId);
	return (IX_FAIL);
    }

    /* Configure and register the traffic callbacks for both ports */
    if ( ixEthAccCodeletPortConfigure(firstPortId, 
				      ixEthAccCodeletBridgeRxCB, 
				      (IxEthAccPortMultiBufferRxCallback) NULL,
				      ixEthAccCodeletBridgeTxCB,
				      firstPortCbTag) != IX_SUCCESS)
    {
	printf("SwBridge: Failed to configure Port %u\n", firstPortId);
	return (IX_FAIL);
    }

    if ( ixEthAccCodeletPortConfigure(secondPortId, 
				      ixEthAccCodeletBridgeRxCB, 
				      NULL,
				      ixEthAccCodeletBridgeTxCB,
				      secondPortCbTag) != IX_SUCCESS)
    {
	printf("SwBridge: Failed to configure Port %u\n", secondPortId);
	return (IX_FAIL);
    }

    /* Enable the traffic over both ports */
    if ( ixEthAccPortEnable(firstPortId) != IX_SUCCESS)
    {
	printf("SwBridge: Failed to enable Port %u\n", firstPortId);
	return (IX_FAIL);
    }

    if ( ixEthAccPortEnable(secondPortId) != IX_SUCCESS)
    {
	printf("SwBridge: Failed to enable Port %u\n", secondPortId);
	return (IX_FAIL);
    }

    return (IX_SUCCESS);
}
/*
 * Function definition: ixEthAccCodeletRxSinkStart()
 *
 * Start datapath traffic for RX Sink demo.
 */
IX_STATUS ixEthAccCodeletRxSinkStart(IxEthAccPortId portId)
{
    
    if (ixEthAccCodeletPortConfigure(portId,
				     NULL,
				     ixEthAccCodeletRxSinkRxCB,
				     ixEthAccCodeletMemPoolFreeTxCB, 
				     portId) 
	!= IX_ETH_ACC_SUCCESS)
    {
	printf("Loopbacks: Failed to configure the port %u\n",
	       (UINT32)portId);
	return IX_FAIL;
    }

    /* ready to get traffic */
    if(ixEthAccPortEnable(portId) 
       != IX_ETH_ACC_SUCCESS)
    {
	printf("Loopbacks: Error enabling port %u\n",
	       (UINT32)portId);
	return (IX_FAIL);
    }

    return (IX_SUCCESS);
}
PRIVATE IX_STATUS 
ixEthAccCodeletTxGenRxSinkStart(IxEthAccPortId portId)
{
    IX_OSAL_MBUF *mBufPtr;
    UINT32 numBufs;
    IxEthDBStatus status;

    /* port transmits as fast as possible and drops rx traffic */
    if (ixEthAccCodeletPortConfigure(portId,
				     NULL,
				     ixEthAccCodeletRxSinkRxCB, 
				     ixEthAccCodeletTxGenTxCB,
				     portId)
	!= IX_ETH_ACC_SUCCESS)
    {
	printf("Loopbacks: Failed to start the Tx-Gen Rx-Sink Operation port %u\n",
	       (UINT32)portId);
	return IX_FAIL;
    }

    /* Disable MAC learning and filtering at the port that sinks the frame. 
       With learning enabled at both ports, TxGenRxSink will report buffer underrun and overruns 
       as this mode generates a heavy load of MAC address migrations in the learning/filtering 
       database. 
     */
    status = ixEthDBFeatureEnable (portId, IX_ETH_DB_FILTERING, FALSE);
    if (IX_ETH_DB_SUCCESS == status)
    {
        status = ixEthDBFeatureEnable (portId, IX_ETH_DB_LEARNING, FALSE); 
    }

    if (IX_ETH_DB_SUCCESS == status)
    {
        printf("\nMAC learning & filtering are disabled at port %d\n", portId);
        printf("This is to prohibit the MAC address from being migrated back and forth\n");
        printf("between two connected ports in the learning/filtering database.\n\n");
    }
    else
    {
        printf("\nFailed to disable MAC learning & filtering at port %d.\n", portId);
        printf("TxGenRxSink will report buffer underrun and overruns as this mode generates \n");
        printf("a heavy load of MAC address migrations in the learning/filtering database. \n");
        printf("With learning enabled at both ports, buffer underrun and overruns are expected.\n");
    }

    /* Generate our random data for the payload */
    IX_ETHACC_CODELET_DATAGEN(compData);
	
    /* Now start the loopback by transmitting the first few frames */
    for (numBufs=0; numBufs<IX_ETHACC_CODELET_TXGEN_PCKS; numBufs++)
    {
	IX_ETHACC_CODELET_REMOVE_MBUF_FROM_Q_HEAD(ixEthAccCodeletFreeBufQ,
						  mBufPtr);
	
	if (mBufPtr == NULL)
	{
	    printf("Loopbacks: Buffer queue empty. Not enough free buffers to transmit in TxGen-RxSink Loopback!\n");
	    return (IX_FAIL);
	}
	
	IX_OSAL_MBUF_MLEN(mBufPtr) = IX_ETHACC_CODELET_TXGEN_PCK_LEN;
	IX_OSAL_MBUF_PKT_LEN(mBufPtr) = IX_ETHACC_CODELET_TXGEN_PCK_LEN;
	
	memcpy(IX_OSAL_MBUF_MDATA(mBufPtr), 
	       &compData[0], 
	       IX_ETHACC_CODELET_TXGEN_PCK_LEN);
	
	IX_OSAL_CACHE_FLUSH(IX_OSAL_MBUF_MDATA(mBufPtr), 
				IX_OSAL_MBUF_MLEN(mBufPtr));
	
	if(ixEthAccPortTxFrameSubmit(portId, 
				     mBufPtr,
				     IX_ETH_ACC_TX_DEFAULT_PRIORITY)
	   != IX_ETH_ACC_SUCCESS)
	{
	    printf("Loopbacks: Error Submitting frame for transmission on port %u\n",
		   portId);
	    return (IX_FAIL);
	}
    }
    printf("Port %d Tx pool has %d buffers\n", portId, numBufs);

    /* enable traffic */
    if(ixEthAccPortEnable(portId) 
       != IX_ETH_ACC_SUCCESS)
    {
	printf("Loopbacks: Error Enabling port %u\n",
	       (UINT32)portId);
	return (IX_FAIL);
    }

    return (IX_SUCCESS);
}
/*
 * Function definition: ixEthAccCodeletSwBridgeQoSStart()
 *
 * Configure QoS and Start bridge datapath
 */
IX_STATUS ixEthAccCodeletSwBridgeQoSStart(IxEthAccPortId firstPortId, 
					  IxEthAccPortId secondPortId)
{
    UINT32 firstPortCbTag = firstPortId | (secondPortId << 16);
    UINT32 secondPortCbTag = secondPortId | (firstPortId << 16);

    IxEthDBPriorityTable priorityTable = { 0,1,2,3,4,5,6,7};
    IxEthDBFeature featureSet = 0;

    if (firstPortId == secondPortId)
    {
	printf("SwBridgeQoS: Cannot configure a Bridge Operation between port %u and port %u (ports must be different)\n",
	       firstPortId, 
               secondPortId);
	return (IX_FAIL);
    }

    /* initialize pendingTx for both ports */
    pendingTx[firstPortId] = pendingTx[secondPortId] = 0;

    /* register the rx/tx callback */
    if ( ixEthAccCodeletPortConfigure(firstPortId, 
	      ixEthAccCodeletSwBridgeQoSTaggedToUntaggedRxCB, 
	      (IxEthAccPortMultiBufferRxCallback) NULL,
	      ixEthAccCodeletSwBridgeQoSTxCB,
	      firstPortCbTag) != IX_SUCCESS)
    {
	printf("SwBridgeQoS: Failed to configure the Bridge Operation for port %u\n",
	       firstPortId);
	return (IX_FAIL);
    }

    if ( ixEthAccCodeletPortConfigure(secondPortId, 
	      ixEthAccCodeletSwBridgeQoSUntaggedToTaggedRxCB, 
	      NULL,
	      ixEthAccCodeletSwBridgeQoSTxCB,
	      secondPortCbTag) != IX_SUCCESS)
    {
	printf("SwBridgeQoS: Failed to configure the Bridge Operation for port %u\n",
	       secondPortId);
	return (IX_FAIL);
    }

    /* Enable the VLAN/QoS Feature in EthDB for each port but first 
     * check that the Firmware downloaded to the NPE can support it
     */
    ixEthDBFeatureCapabilityGet((IxEthDBPortId)firstPortId, &featureSet);
    
    if ((featureSet & IX_ETH_DB_VLAN_QOS) == 0)
    {
	printf("SwBridgeQoS: Port %u NPE image not VLAN/QoS capable\n",
	       firstPortId);
	return (IX_FAIL);
    }

    if ( ixEthDBFeatureEnable((IxEthDBPortId)firstPortId, 
			      IX_ETH_DB_VLAN_QOS,
			      TRUE) != IX_ETH_DB_SUCCESS )
    {
	printf("SwBridgeQoS: Failed to enable VLAN/QoS on port %u\n",
	       firstPortId);
	return (IX_FAIL);
    }

    /* Enable the EthDB Port in order to configure and download the
     * VLAN/QoS configuration information
     */ 
    if ((ixEthDBPortEnable(firstPortId)) != IX_ETH_DB_SUCCESS)
    {
        printf("SwBridgeQoS: Cannot enable port %u\n", firstPortId);
        return (IX_FAIL);
    }
    if ((ixEthDBPortEnable(secondPortId)) != IX_ETH_DB_SUCCESS)
    {
        printf("SwBridgeQos: Cannot enable port %u\n", secondPortId);
        return (IX_FAIL);
    }

    /* Configure Xscale QoS : the access layer datapath 
     * prioritizes the different classes of traffic.
     */
    printf("Set Tx Scheduling discipline...\n");
    if (ixEthAccTxSchedulingDisciplineSet(firstPortId, 
					  FIFO_PRIORITY)
	!= IX_ETH_ACC_SUCCESS)
    {
	printf("SwBridgeQos: Failed to set Tx Scheduling for discipline port %u\n",
	       (UINT32)firstPortId);
	return (IX_FAIL);
    }

    if (ixEthAccTxSchedulingDisciplineSet(secondPortId, 
					  FIFO_PRIORITY)
	!= IX_ETH_ACC_SUCCESS)
    {
	printf("SwBridgeQos: Failed to set Tx Scheduling for discipline port %u\n",
	       (UINT32)secondPortId);
	return (IX_FAIL);
    }

    printf("Set Rx Scheduling discipline...\n");
    if (ixEthAccRxSchedulingDisciplineSet(FIFO_PRIORITY)
	!= IX_ETH_ACC_SUCCESS)
    {
	printf("SwBridgeQos: Failed to set Rx Scheduling discipline!\n");
	return (IX_FAIL);
    }

    /* NPE QoS : Configure VLAN traffic for highest priority 
     * on first port. Tagging is enabled on Egress (use default tag
     * for this port) and untagging is enabled on ingress.
     * The traffic running on this bridge will be untagged.
     */
    printf("Set VLAN default tag...\n");
    if (ixEthDBPortVlanTagSet(firstPortId, 
			      IX_ETHACC_CODELET_VLANID_DEFAULT)
	!= IX_ETH_DB_SUCCESS)
    {
	printf("SwBridgeQos: Failed to set the default VLAN ID for port %u\n",
	       firstPortId);
	return (IX_FAIL);
    }

    printf("Enable tagged frames...\n");
    if (ixEthDBAcceptableFrameTypeSet(firstPortId,
				      IX_ETH_DB_VLAN_TAGGED_FRAMES
				      | IX_ETH_DB_UNTAGGED_FRAMES)
	!= IX_ETH_DB_SUCCESS)
    {
	printf("SwBridgeQos: Failed to set the acceptable frame type for port %u\n",
	       firstPortId);
	return (IX_FAIL);
    }

    printf("Setting VLAN membership...\n");

    /* by default the entire VLAN range 0-4094 is included in the
       port VLAN membership table, therefore we need to remove all 
       VLAN IDs but 0 (which is required to accept untagged frames) */
    if (ixEthDBPortVlanMembershipRangeRemove(firstPortId, 
        1, IX_ETH_DB_802_1Q_MAX_VLAN_ID) != IX_ETH_DB_SUCCESS)
    {
        printf("SwBridgeQos: Failed to set VLAN membership for port %u\n", firstPortId);
        return (IX_FAIL);
    }

    /* now add the range used by this codelet */
    if (ixEthDBPortVlanMembershipRangeAdd(firstPortId, 
					  IX_ETHACC_CODELET_VLANID_MIN,
					  IX_ETHACC_CODELET_VLANID_MAX)
	!= IX_ETH_DB_SUCCESS)
    {
	printf("SwBridgeQos: Failed to set VLAN membership for port %u\n",
	       firstPortId);
	return (IX_FAIL);
    }

    printf("Enable Egress VLAN tagging...\n");
    if (ixEthDBEgressVlanRangeTaggingEnabledSet(firstPortId, 
						IX_ETHACC_CODELET_VLANID_MIN,
						IX_ETHACC_CODELET_VLANID_MAX,
						TRUE)
	!= IX_ETH_DB_SUCCESS)
    {
	printf("SwBridgeQos: Failed to enable VLAN Egress tagging for port %u\n",
	       firstPortId);
	return (IX_FAIL);
    }

    printf("Enable Ingress VLAN untagging...\n");
    if (ixEthDBIngressVlanTaggingEnabledSet(firstPortId, 
					    IX_ETH_DB_REMOVE_TAG)
	!= IX_ETH_DB_SUCCESS)
    {
	printf("SwBridgeQos: Failed to enable VLAN Ingress untagging for port %u\n",
	       firstPortId);
	return (IX_FAIL);
    }
    
    printf("Setup priority mapping table...\n");
    if (ixEthDBPriorityMappingTableSet(firstPortId,
				       priorityTable)
	!= IX_ETH_DB_SUCCESS)
    {
	printf("SwBridgeQos: Failed to set the priority mapping Table for port %u\n",
	       firstPortId);
	return (IX_FAIL);	
    }
    
    /* Configure 10 mb/s on second port to create a 
     * traffic congestion on the bridge : the high 
     * priority traffic should pass, the low priority
     * traffic should starve.
     */
    if (ixEthAccCodeletLinkSlowSpeedSet(secondPortId)
	!= IX_SUCCESS)
    {
	printf("SwBridgeQos: Failed to set port %u to 10 Mbit\n", 
	       secondPortId);
	return (IX_FAIL);
    }

    /* Allow RX and TX traffic to run */
    if ( ixEthAccPortEnable(firstPortId) != IX_SUCCESS)
    {
	printf("SwBridgeQos: Failed to start the Bridge Operation for port %u\n",
	       firstPortId);
	return (IX_FAIL);
    }

    if ( ixEthAccPortEnable(secondPortId) != IX_SUCCESS)
    {
	printf("SwBridgeQos: Failed to start the Bridge Operation for port %u\n",
	       secondPortId);
	return (IX_FAIL);
    }

    /* display the default settings for both ports */
    printf("Port %u configuration:\n", 
	   (UINT32)firstPortId);
    printf("- Accept Ingress VLAN-tagged traffic, VLAN tag range is [%u-%u]\n",
	   (UINT32)IX_ETHACC_CODELET_VLANID_MIN,
	   (UINT32)IX_ETHACC_CODELET_VLANID_MAX);
    printf("- Strip tag from ingress traffic\n");
    printf("- Bridge Ingress to port %u without tag\n",
	   (UINT32)secondPortId);
    printf("- Frame priorities may change the frame order (QoS enabled)\n");
    printf("- Insert a default tag [%u] to egress traffic\n",
	   (UINT32)IX_ETHACC_CODELET_VLANID_DEFAULT);
 
    printf("Port %u configuration:\n", 
	   (UINT32)secondPortId);
    printf("- Set as default\n\n");

    return (IX_SUCCESS);
}
/*
 * Function definition: ixEthAccCodeletDBLearningRun()
 *
 * Run ethDB demo for all ports passed as parameters
 */
IX_STATUS 
ixEthAccCodeletDBLearningRun(BOOL validPorts[])
{
    IxEthAccPortId portId;
    IxEthDBPortId portPtr;

    IxEthDBMacAddr staticMacAddress[IX_ETHACC_CODELET_MAX_PORT] = 
	{{{0,1,2,3,4,5}}, {{6,7,8,9,0,1}}};

    IxEthDBMacAddr dynamMacAddress[IX_ETHACC_CODELET_MAX_PORT] = 
	{{{0xa,0xb,0xc,0xd,0xe,0xf}}, {{0x0,0xb,0x3,0xc,0x4,0xd}}};

    if (ixEthAccCodeletDBMaintenanceStart()
	!= IX_SUCCESS)
    {
	printf("DbLearning: Error spawning DB maintenance task\n");
	return (IX_FAIL);
    }

    /* configure all existing ports */
    for (portId = 0; portId < IX_ETHACC_CODELET_MAX_PORT; portId++)
    {
	if (validPorts[portId])
	{
	    /* Configure the port */
	    printf("Configuring the port...\n");

	    if (ixEthAccCodeletPortConfigure(portId,
					     ixEthAccCodeletMemPoolFreeRxCB,
					     (IxEthAccPortMultiBufferRxCallback) NULL,
					     ixEthAccCodeletMemPoolFreeTxCB,
					     portId) 
		!= IX_ETH_ACC_SUCCESS)
	    {
		printf("DbLearning: Failed to configure the port %u\n",
		       (UINT32)portId);
		return IX_FAIL;
	    }

	    /* Enable the port */
	    printf("Enabling the port...\n");

	    if(ixEthAccPortEnable(portId) != IX_ETH_ACC_SUCCESS)
	    {
		printf("DbLearning: Error enabling port %u\n", 
		       (UINT32)portId);
		return (IX_FAIL);
	    }
	    
	    /* Add some static entries */
	    printf("Adding static entries...\n");
	    
	    if (ixEthDBFilteringStaticEntryProvision(portId,
						     &staticMacAddress[portId])
		!= IX_ETH_DB_SUCCESS)
	    {
		printf("DbLearning: Failed to add static MAC to port %u\n", 
		       (UINT32)portId);
		return (IX_FAIL);
	    }

            ixOsalSleep(100);
	    
	    /* Verify that the address has successfully been added */
	    if (ixEthDBFilteringDatabaseSearch(&portPtr, 
					       &staticMacAddress[portId])
		== IX_ETH_DB_NO_SUCH_ADDR)
	    {
		printf("DbLearning: Static MAC address not found in Database\n");
		return (IX_FAIL);
	    }
	    
	    /* Add some dynamic entries */
	    printf("Adding dynamic entries...\n");
	    
	    if (ixEthDBFilteringDynamicEntryProvision(portId,
						      &dynamMacAddress[portId])
		!= IX_ETH_DB_SUCCESS)
	    {
		printf("DbLearning: Failed to add dynamic MAC to port %u\n", 
		       (UINT32)portId);
	    }
	    
	    printf("Enabling aging...\n");
	    
	    if (ixEthDBPortAgingEnable(portId) != IX_ETH_DB_SUCCESS)
	    {
		printf("DbLearning: Failed to enable aging for port %u\n", 
		       (UINT32)portId);
		return (IX_FAIL);
	    }
	}
    }
 
    ixEthDBFilteringDatabaseShowAll();

    /* Wait 4 minutes over aging time (for safety) and verify that the 
     * dynamic entries have been removed 
     */
    printf("Aging entries.\nWaiting for %d minutes...\n", 
	   IX_ETH_DB_LEARNING_ENTRY_AGE_TIME / 60 + 3);
    
    ixOsalSleep((IX_ETH_DB_LEARNING_ENTRY_AGE_TIME + 180) * 1000);
     
    ixEthDBFilteringDatabaseShowAll();
    
    for (portId = 0; portId < IX_ETHACC_CODELET_MAX_PORT; portId++)
    {
	if (validPorts[portId])
	{
	    if(ixEthDBFilteringEntryDelete(&staticMacAddress[portId])
	       != IX_ETH_DB_SUCCESS)
	    {
		printf("DbLearning: Failed to remove static MAC on port %u\n", 
		       (UINT32)portId);
	    }
	    
	    /* Disable ports */
	    if(ixEthAccPortDisable(portId) != IX_ETH_ACC_SUCCESS)
	    {
		printf("DbLearning: Error disabling port %u\n",
		       (UINT32)portId);
		return (IX_FAIL);
	    }
	}
    }

    /* wait for pending traffic to be completely received */
    if (ixEthAccCodeletRecoverBuffers() 
	!= IX_SUCCESS)
    {
	printf("Warning : Not all buffers are accounted for!\n");
    }    

    return (IX_SUCCESS);
}