#include "wlan_mac_high_sw_config.h" #include "wlan_platform_high.h" #include "include/w3_high.h" #include "include/w3_eth.h" #include "xparameters.h" #include "xintc.h" #include "wlan_exp_node.h" #include "wlan_platform_common.h" #include "wlan_platform_high.h" #include "wlan_mac_common.h" #include "wlan_mac_dl_list.h" #include "wlan_mac_high.h" #include "wlan_mac_eth_util.h" #include "wlan_mac_schedule.h" #include "wlan_mac_queue.h" #include "wlan_mac_802_11_defs.h" #include "wlan_axi_ethernet_intr.h" #include "xaxidma.h" #if WLAN_SW_CONFIG_ENABLE_ETH_BRIDGE static XAxiDma portal_eth_dma_instance; peripheral_args_t portal_peripheral_args; #endif #if WLAN_SW_CONFIG_ENABLE_WLAN_EXP static XAxiDma wlan_exp_eth_dma_instance; peripheral_args_t wlan_exp_peripheral_args; #endif // Local Function Declarations -- these are not intended to be called by functions outside of this file #if (WLAN_SW_CONFIG_ENABLE_WLAN_EXP || WLAN_SW_CONFIG_ENABLE_ETH_BRIDGE) void _eth_send_done( eth_tx_queue_buffer_t* eth_tx_queue_buffer ); #endif /*****************************************************************************/ /** * @brief Transmits a packet over Ethernet (Portal) * * This function transmits a single packet via Ethernet using the axi_dma. The * packet passed via pkt_ptr must be a valid Ethernet packet, complete with * 14-byte Ethernet header. This function does not check for a valid header * (i.e. the calling function must ensure this). * * The packet must be stored in a memory location accessible by the axi_dma core. * The MicroBlaze DLMB is *not* accessible to the DMA. Thus packets cannot be * stored in malloc'd areas (the heap is in the DLMB). In the reference implementation * all Ethernet transmissions start as wireless receptions. Thus, the Ethernet payloads * are stored in the wireless Rx packet buffer, which is accessible by the DMA. * * Custom code which needs to send Ethernet packets can use a spare wireless Tx/Rx * packet buffer, a spare Tx queue entry in DRAM or the user scratch space in DRAM * to create custom Ethernet payloads. * * This function blocks until the Ethernet transmission completes. * * @param eth_tx_queue_buffer_t* eth_tx_queue_buffer - Queue buffer describing to-be-sent packet * * @return 0 for successful Ethernet transmission, -1 otherwise */ #if WLAN_SW_CONFIG_ENABLE_ETH_BRIDGE int wlan_platform_portal_eth_send( eth_tx_queue_buffer_t* eth_tx_queue_buffer ) { int status; // IMPORTANT: If the data cache is enabled the cache must be flushed before // attempting to send a packet via the ETH DMA. The DMA will read packet // contents directly from RAM, bypassing any cache checking normally done by // the MicroBlaze. The data cache is disabled by default in the reference // implementation. // // Xil_DCacheFlushRange((u32)TxPacket, MAX_PKT_LEN); status = wlan_axi_eth_intr_send( &portal_peripheral_args, eth_tx_queue_buffer ); if(status & WLAN_AXI_ETH_INTR_SEND_ERR_NO_FREE_BD){ return WLAN_FAILURE; } else { return WLAN_SUCCESS; } } int wlan_platform_portal_eth_get_mtu(){ return wlan_axi_eth_intr_get_mtu(); } #endif //WLAN_SW_CONFIG_ENABLE_ETH_BRIDGE #if WLAN_SW_CONFIG_ENABLE_WLAN_EXP int wlan_platform_wlan_exp_eth_send( eth_tx_queue_buffer_t* eth_tx_queue_buffer ) { int status; status = wlan_axi_eth_intr_send( &wlan_exp_peripheral_args, eth_tx_queue_buffer ); if(status & WLAN_AXI_ETH_INTR_SEND_ERR_NO_FREE_BD){ return WLAN_FAILURE; } else { return WLAN_SUCCESS; } } int wlan_platform_wlan_exp_eth_get_mtu(){ return wlan_axi_eth_intr_get_mtu(); } #endif int w3_wlan_platform_ethernet_init() { #if WLAN_SW_CONFIG_ENABLE_ETH_BRIDGE || WLAN_SW_CONFIG_ENABLE_WLAN_EXP intr_conn_params_t intr_conn_params; int status; #endif #if WLAN_SW_CONFIG_ENABLE_ETH_BRIDGE || (WLAN_SW_CONFIG_ENABLE_WLAN_EXP && (ETH_WLAN_EXP == 0)) // FIXME: I'm not currently working on the single-Ethernet design, but when I do, // I need to trace through the implications of WLAN_SW_CONFIG_ENABLE_ETH_BRIDGE = 0 // and (WLAN_SW_CONFIG_ENABLE_WLAN_EXP = 1, ETH_WLAN_EXP = 0) portal_peripheral_args.driver_instance = (void*)&portal_eth_dma_instance; portal_peripheral_args.callback0 = (void*)wlan_enqueue_eth_rx; portal_peripheral_args.callback1 = (void*)_eth_send_done; // Set up ETH A intr_conn_params = wlan_axi_eth_intr_init(&portal_peripheral_args, PORTAL_ETH_DEV_ID, PORTAL_ETH_LINK_SPEED, PORTAL_ETH_MDIO_PHYADDR, PORTAL_ETH_BD_MEM_BASEADDR, PORTAL_ETH_BD_MEM_SIZE, 2, 1, 0); if(intr_conn_params.intr_handler0 == NULL){ return WLAN_FAILURE; } // Connect the peripheral to the interrupt controller status = wlan_platform_interrupt_connect(PORTAL_ETH_RX_INTR_ID, (wlan_intr_handler_t)intr_conn_params.intr_handler0, intr_conn_params.intr_handler_arg0); if (status != WLAN_SUCCESS) { xil_printf("ERROR: Failed to connect axi_dma interrupt: (%d)\n", status); return WLAN_FAILURE; } if(intr_conn_params.intr_handler0 == NULL){ return WLAN_FAILURE; } // Connect the peripheral to the interrupt controller status = wlan_platform_interrupt_connect(PORTAL_ETH_TX_INTR_ID, (wlan_intr_handler_t)intr_conn_params.intr_handler1, intr_conn_params.intr_handler_arg1); if (status != WLAN_SUCCESS) { xil_printf("ERROR: Failed to connect axi_dma interrupt: (%d)\n", status); return WLAN_FAILURE; } wlan_platform_interrupt_enable(PORTAL_ETH_RX_INTR_ID); wlan_platform_interrupt_enable(PORTAL_ETH_TX_INTR_ID); #endif #if WLAN_SW_CONFIG_ENABLE_WLAN_EXP && (ETH_WLAN_EXP == 1) // Set up ETH B wlan_exp_peripheral_args.driver_instance = (void*)&wlan_exp_eth_dma_instance; wlan_exp_peripheral_args.callback0 = (void*)wlan_exp_process_eth_rx; wlan_exp_peripheral_args.callback1 = (void*)_eth_send_done; // Some notes about the heuristics embedded in the following parameters: // - 12 Tx BDs -- this will support a maximum of 6 packets during log retrieval (which uses 2 BDs per packet) // - 3 Tx Coalescing -- in a backlogged scenario, we'll wait for 3 transmissions to be complete. In log retrieval, we'll // split out ring of 6 packets into 2 chunks and process Tx done interrupts one chunk at a time // - 154 timer delay -- in this hardware platform, C_DLYTMR_RESOLUTION is 125 and m_axi_sg_aclk is 160 Mhz. // The unit of this parameter is therefore an integer number of 125/160 usec (or 781 ns). Conservatively, assume that C // can sustain 100Mbps of Tx throughput with standard MTUs. This is equivalent to a 1500 byte packet every 120 usec. // If more than 120 usec elapses after the last Tx done completion, we know we aren't currently attempting to backlog // transmissions so we should just fire off an interrupt and not wait for coalescing. 120 usec is 154 delay units. intr_conn_params = wlan_axi_eth_intr_init(&wlan_exp_peripheral_args, WLAN_EXP_ETH_DEV_ID, WLAN_EXP_ETH_LINK_SPEED, WLAN_EXP_ETH_MDIO_PHYADDR, WLAN_EXP_ETH_BD_MEM_BASEADDR, WLAN_EXP_ETH_BD_MEM_SIZE, 12, 3, 154); if(intr_conn_params.intr_handler0 == NULL){ return WLAN_FAILURE; } // Connect the peripheral to the interrupt controller status = wlan_platform_interrupt_connect(WLAN_EXP_ETH_RX_INTR_ID, (wlan_intr_handler_t)intr_conn_params.intr_handler0, intr_conn_params.intr_handler_arg0); if (status != XST_SUCCESS) { xil_printf("ERROR: Failed to connect axi_dma interrupt: (%d)\n", status); return XST_FAILURE; } if(intr_conn_params.intr_handler1 == NULL){ return WLAN_FAILURE; } // Connect the peripheral to the interrupt controller status = wlan_platform_interrupt_connect(WLAN_EXP_ETH_TX_INTR_ID, (wlan_intr_handler_t)intr_conn_params.intr_handler1, intr_conn_params.intr_handler_arg1); if (status != XST_SUCCESS) { xil_printf("ERROR: Failed to connect axi_dma interrupt: (%d)\n", status); return XST_FAILURE; } wlan_platform_interrupt_enable(WLAN_EXP_ETH_RX_INTR_ID); wlan_platform_interrupt_enable(WLAN_EXP_ETH_TX_INTR_ID); #endif //WLAN_SW_CONFIG_ENABLE_WLAN_EXP return WLAN_SUCCESS; } void w3_wlan_platform_ethernet_free_queue_entry_notify(){ // We need to disable interrupts here if this function was called from a non-ISR context. Otherwise, // we may get called again and interrupt ourselves from an ISR context while manipulating BDs. interrupt_state_t prev_interrupt_state = wlan_platform_intc_stop(); #if WLAN_SW_CONFIG_ENABLE_ETH_BRIDGE wlan_axi_eth_intr_attach_free_rx_bd(&portal_peripheral_args); #endif #if WLAN_SW_CONFIG_ENABLE_WLAN_EXP wlan_axi_eth_intr_attach_free_rx_bd(&wlan_exp_peripheral_args); #endif //WLAN_SW_CONFIG_ENABLE_WLAN_EXP wlan_platform_intc_set_state(prev_interrupt_state); } #if WLAN_SW_CONFIG_ENABLE_ETH_BRIDGE || WLAN_SW_CONFIG_ENABLE_WLAN_EXP void _eth_send_done( eth_tx_queue_buffer_t* eth_tx_queue_buffer ){ wlan_mac_high_cdma_finish_transfer(); // Free the just-sent Tx queue buffer if(eth_tx_queue_buffer != NULL) queue_checkin(eth_tx_queue_buffer->pyld_queue_hdr.dle); } #endif