source: ReferenceDesigns/w3_802.11/c/high_periphs/eth/wlan_axi_ethernet_intr/wlan_axi_ethernet_intr.c

Last change on this file was 6319, checked in by chunter, 5 years ago

1.8.0 release wlan-mac-se

File size: 29.9 KB
Line 
1/*****************************************************************
2* File: wlan_axi_ethernet_intr.c
3* Copyright (c) 2019 Mango Communications, all rights reserved
4*****************************************************************/
5
6#include "wlan_axi_ethernet_intr.h"
7#include "wlan_mac_common.h"
8#include "wlan_mac_dl_list.h"
9#include "wlan_mac_eth_util.h"
10#include "wlan_mac_queue.h"
11#include "wlan_mac_high.h"
12#include "wlan_platform_high.h"
13#include "wlan_platform_intc.h"
14#include "wlan_common_types.h"
15
16#include "xaxiethernet.h"
17#include "xaxidma.h"
18#include "xaxidma_hw.h"
19
20// Local function declarations
21static int _init_rx_bd(XAxiDma_Bd* bd_ptr, dl_entry* qe_ptr, u32 max_transfer_len, u32 LengthMask);
22static u32 _wlan_axi_eth_intr_calc_num_bd(u32 mem_size);
23static void _eth_rx_interrupt_handler(void *callback_arg);
24static void _eth_tx_interrupt_handler(void *callback_arg);
25inline void _wlan_axi_eth_intr_free_tx_bd(peripheral_args_t* peripheral_args);
26
27/**
28 * @brief Initialize AXI ETHERNET INTR with Interrupts
29 *
30 * This function initializes the AXI ETHERNET INTR peripheral.
31 *
32 * @param peripheral_args_t* peripheral_args
33 *          - necessary arguments to distinguish between multiple instances of this peripheral
34 *        u32 eth_device_id
35 *          - The Device ID of the AXI ETHERNET known to platform code (typically from xparameters.h)
36 *        u32 wlan_eth_link_speed
37 *          - Link speed (Mbps)
38 *        u32 wlan_eth_mdio_phyaddr
39 *          - Offset of MDIO_PHYADDR register
40 *        u32 eth_bd_mem_base
41 *          - The base address where this peripheral can store Ethernet buffer descriptors
42 *        u32 eth_bd_mem_size
43 *          - The total space (in bytes) available at the provided base address
44 *        u32 num_tx_bd
45 *          - Number of Tx BDs that will be used. The remaining portion of eth_bd_mem_size
46 *            will be used for Rx BDs
47 *        u32 tx_coalescing_count
48 *          - Number of Tx events that will be coalesced into a single interrupt
49 *        u32 tx_coalescing_timer
50 *          - If tx_coalescing_count is not achieved but there are complete Tx events, an interrupt
51 *            will occur after this amount of time after the last reception. The units of this
52 *            parameter are platform specific. Namely, the resolution is C_DLYTMR_RESOLUTION cycles
53 *            of the m_axi_sg_aclk clock
54 * @return intr_conn_params_t
55 *          - A struct containing a pointer to a ISR handler and an ISR argument that platform code
56 *            should use when connecting the peripheral to the interrupt controller
57 */
58intr_conn_params_t wlan_axi_eth_intr_init( peripheral_args_t* peripheral_args,
59                                           u32 eth_device_id,
60                                           u32 wlan_eth_link_speed,
61                                           u32 wlan_eth_mdio_phyaddr,
62                                           u32 eth_bd_mem_base,
63                                           u32 eth_bd_mem_size,
64                                           u32 num_tx_bd,
65                                           u32 tx_coalescing_count,
66                                           u32 tx_coalescing_timer ){
67    intr_conn_params_t intr_conn_params;
68    XAxiDma* eth_dma_instance_ptr;
69    XAxiEthernet_Config* eth_cfg_ptr;
70    XAxiEthernet eth_instance;
71    XAxiDma_Config* eth_dma_cfg_ptr;
72    XAxiDma_Bd eth_dma_bd_template;
73    XAxiDma_BdRing* eth_tx_ring_ptr;
74    XAxiDma_BdRing* eth_rx_ring_ptr;
75    XAxiDma_Bd* bd_ptr;
76    XAxiDma_Bd* first_bd_ptr;
77    dl_list rx_queue_list;
78    dl_entry* curr_queue_element;
79    dl_entry* next_queue_element;
80    u32 bd_count;
81    u32 i;
82    u32 eth_tx_bd_mem_base;
83    u32 eth_tx_bd_mem_size;
84    u32 eth_tx_bd_mem_high;
85    u32 eth_rx_bd_mem_base;
86    u32 eth_rx_bd_mem_size;
87    u32 num_rx_bd;
88    int status;
89
90    intr_conn_params.intr_handler0 = NULL;
91    intr_conn_params.intr_handler_arg0 = NULL;
92    intr_conn_params.intr_handler1 = NULL;
93    intr_conn_params.intr_handler_arg1 = NULL;
94
95    eth_dma_instance_ptr = (XAxiDma*)peripheral_args->driver_instance;
96
97    // Split up the memory set aside for us in eth_bd_mem_base
98    eth_tx_bd_mem_base = eth_bd_mem_base;
99    eth_tx_bd_mem_size = num_tx_bd*XAXIDMA_BD_MINIMUM_ALIGNMENT;
100    eth_tx_bd_mem_high = CALC_MEM_HIGH_ADDR(eth_tx_bd_mem_base, eth_tx_bd_mem_size);
101    eth_rx_bd_mem_base = eth_tx_bd_mem_high+1;
102    eth_rx_bd_mem_size = eth_bd_mem_size - eth_tx_bd_mem_size;
103
104    if(num_tx_bd == 0){
105        xil_printf("WLAN_AXI_ETHERNET_INTR: Tx BD allocation failed, insufficient memory\n");
106        wlan_platform_high_userio_disp_status(USERIO_DISP_STATUS_CPU_ERROR, WLAN_ERROR_CODE_INSUFFICIENT_BD_SIZE);
107        return intr_conn_params;
108    }
109
110    num_rx_bd = _wlan_axi_eth_intr_calc_num_bd(eth_rx_bd_mem_size);
111    if(num_rx_bd == 0){
112        xil_printf("WLAN_AXI_ETHERNET_INTR: Rx BD allocation failed, insufficient memory\n");
113        wlan_platform_high_userio_disp_status(USERIO_DISP_STATUS_CPU_ERROR, WLAN_ERROR_CODE_INSUFFICIENT_BD_SIZE);
114        return intr_conn_params;
115    }
116
117    // Initialize buffer descriptor counts
118    //xil_printf("WLAN_AXI_ETHERNET_INTR: %3d Eth Tx BDs placed in 0x%08x: using %d B\n", num_tx_bd, eth_tx_bd_mem_base, num_tx_bd*XAXIDMA_BD_MINIMUM_ALIGNMENT);
119    //xil_printf("WLAN_AXI_ETHERNET_INTR: %3d Eth Rx BDs placed in 0x%08x: using %d kB\n", num_rx_bd, eth_rx_bd_mem_base, num_rx_bd*XAXIDMA_BD_MINIMUM_ALIGNMENT/1024);
120
121    // Initialize Ethernet instance
122    eth_cfg_ptr = XAxiEthernet_LookupConfig(eth_device_id);
123    status = XAxiEthernet_CfgInitialize(&eth_instance, eth_cfg_ptr, eth_cfg_ptr->BaseAddress);
124    if (status != XST_SUCCESS) {
125        xil_printf("Error in XAxiEthernet_CfgInitialize! Err = %d\n", status);
126        return intr_conn_params;
127    };
128
129    // Setup the Ethernet options
130    //     NOTE:  This Ethernet driver does support jumbo Ethernet frames.  Only 2KB is allocated for each Rx buffer
131    status  = XAxiEthernet_ClearOptions(&eth_instance, XAE_LENTYPE_ERR_OPTION | XAE_FLOW_CONTROL_OPTION );
132    status |= XAxiEthernet_SetOptions(&eth_instance, XAE_JUMBO_OPTION | XAE_FCS_STRIP_OPTION | XAE_PROMISC_OPTION | XAE_MULTICAST_OPTION | XAE_BROADCAST_OPTION | XAE_FCS_INSERT_OPTION);
133    status |= XAxiEthernet_SetOptions(&eth_instance, XAE_RECEIVER_ENABLE_OPTION | XAE_TRANSMITTER_ENABLE_OPTION);
134
135    if (status != XST_SUCCESS){
136        xil_printf("WLAN_AXI_ETHERNET_INTR: Error in XAxiEthernet_Set/ClearOptions! Err = %d\n", status);
137        return intr_conn_params;
138    };
139
140    // Set the operating speed
141    XAxiEthernet_SetOperatingSpeed(&eth_instance, wlan_eth_link_speed);
142
143    // If the link speed is 1 Gbps, then only advertise and link to 1 Gbps connection
144    //     See Ethernet PHY specification for documentation on the values used
145    if (wlan_eth_link_speed == 1000) {
146        XAxiEthernet_PhyWrite(&eth_instance, wlan_eth_mdio_phyaddr, 0, 0x0140);
147        XAxiEthernet_PhyWrite(&eth_instance, wlan_eth_mdio_phyaddr, 0, 0x8140);
148    }
149
150    // Initialize the DMA
151
152    // Initialize the DMA peripheral structures
153    eth_dma_cfg_ptr = XAxiDma_LookupConfig(eth_device_id);
154    status = XAxiDma_CfgInitialize(eth_dma_instance_ptr, eth_dma_cfg_ptr);
155    if (status != XST_SUCCESS) {
156        xil_printf("WLAN_AXI_ETHERNET_INTR: Error in XAxiDma_CfgInitialize! Err = %d\n", status);
157        return intr_conn_params;
158    }
159
160    // Zero-out the template buffer descriptor
161    XAxiDma_BdClear(&eth_dma_bd_template);
162
163    // Fetch handles to the Tx and Rx BD rings
164    eth_tx_ring_ptr = XAxiDma_GetTxRing(eth_dma_instance_ptr);
165    eth_rx_ring_ptr = XAxiDma_GetRxRing(eth_dma_instance_ptr);
166
167    // Disable all Tx/Rx DMA interrupts
168    XAxiDma_BdRingIntDisable(eth_tx_ring_ptr, XAXIDMA_IRQ_ALL_MASK);
169    XAxiDma_BdRingIntDisable(eth_rx_ring_ptr, XAXIDMA_IRQ_ALL_MASK);
170
171    // Disable Rx delay and coalescing
172    //  - We have tried having these on but have not observed any appreciable
173    //    performance improvement
174    //  - Intuitively, both sinks for Ethernet traffic (802.11 portal and wlan_exp
175    //    processing are slower than the Ethernet line rate so there isn't much to
176    //    gain
177    XAxiDma_BdRingSetCoalesce(eth_rx_ring_ptr, 1, 0);
178
179    // Enable Tx delay and coalescing.
180    XAxiDma_BdRingSetCoalesce(eth_tx_ring_ptr, tx_coalescing_count, tx_coalescing_timer);
181
182    // Setup Tx/Rx buffer descriptor rings in memory
183    status  = XAxiDma_BdRingCreate(eth_tx_ring_ptr, eth_tx_bd_mem_base, eth_tx_bd_mem_base, XAXIDMA_BD_MINIMUM_ALIGNMENT, num_tx_bd);
184    status |= XAxiDma_BdRingCreate(eth_rx_ring_ptr, eth_rx_bd_mem_base, eth_rx_bd_mem_base, XAXIDMA_BD_MINIMUM_ALIGNMENT, num_rx_bd);
185
186    if (status != XST_SUCCESS) {
187        xil_printf("WLAN_AXI_ETHERNET_INTR: Error creating DMA BD Rings! Err = %d\n", status);
188        return intr_conn_params;
189    }
190
191    // Populate each ring with empty buffer descriptors
192    status  = XAxiDma_BdRingClone(eth_tx_ring_ptr, &eth_dma_bd_template);
193    status |= XAxiDma_BdRingClone(eth_rx_ring_ptr, &eth_dma_bd_template);
194    if (status != XST_SUCCESS) {
195        xil_printf("WLAN_AXI_ETHERNET_INTR: Error in XAxiDma_BdRingClone()! Err = %d\n", status);
196        return intr_conn_params;
197    }
198
199    // Initialize the Rx buffer descriptors
200    bd_count = XAxiDma_BdRingGetFreeCnt(eth_rx_ring_ptr);
201    if (bd_count != num_rx_bd) {
202        xil_printf("WLAN_AXI_ETHERNET_INTR: Error in Eth Rx DMA init - not all Rx BDs were free at boot\n");
203    }
204
205    status = XAxiDma_BdRingAlloc(eth_rx_ring_ptr, bd_count, &bd_ptr);
206    if (status != XST_SUCCESS) {
207        xil_printf("WLAN_AXI_ETHERNET_INTR: Error in XAxiDma_BdRingAlloc()! Err = %d\n", status);
208        return intr_conn_params;
209    }
210
211    first_bd_ptr = bd_ptr; // We need to hold on to the first one when submitting the ring
212                           //  to the hardware.
213
214    // Checkout bd_count of free queue entries to attach to these buffer descriptors
215    dl_list_init(&rx_queue_list);
216    queue_checkout_list(&rx_queue_list, num_rx_bd);
217
218    if(rx_queue_list.length != num_rx_bd){
219        xil_printf("WLAN_AXI_ETHERNET_INTR: Error checking out queue entries during Eth peripheral init\n");
220        queue_checkin_list(&rx_queue_list);
221        return intr_conn_params;
222    }
223
224
225    next_queue_element = rx_queue_list.first;
226
227    // Loop bd_count buffer descriptors and attach them to queue entries
228    //  provided in rx_queue_list
229    for (i = 0; i < num_rx_bd; i++) {
230        // Take a queue element from the list provided as an argument
231        curr_queue_element = next_queue_element;
232        next_queue_element = dl_entry_next(next_queue_element);
233
234        if (curr_queue_element == NULL) {
235              xil_printf("WLAN_AXI_ETHERNET_INTR: Insufficient elements in rx_queue_list\n");
236              queue_checkin_list(&rx_queue_list);
237              return intr_conn_params;
238         }
239
240        // Remove the queue element from the list. Rx handling will be responsible for returning
241        //  it to a list at some point
242        dl_entry_remove(&rx_queue_list, curr_queue_element);
243
244
245        // Initialize the Rx buffer descriptor
246        status = _init_rx_bd(bd_ptr, curr_queue_element, WLAN_AXI_ETH_RX_INTR_BUF_SIZE, eth_rx_ring_ptr->MaxTransferLen);
247
248        if (status != XST_SUCCESS) {
249            xil_printf("Error initializing Rx BD %d\n", i);
250            queue_checkin_list(&rx_queue_list);
251            return intr_conn_params;
252        }
253
254        // Update bd_ptr to the next BD in the chain for the next iteration
255        bd_ptr = XAxiDma_BdRingNext(eth_rx_ring_ptr, bd_ptr);
256    }
257
258    // Push the Rx BD ring to hardware and start receiving
259    status = XAxiDma_BdRingToHw(eth_rx_ring_ptr, bd_count, first_bd_ptr);
260
261    if (status != XST_SUCCESS) {
262        xil_printf("WLAN_AXI_ETHERNET_INTR: Error submitting Rx BD ring to HW\n");
263        queue_checkin_list(&rx_queue_list);
264        return intr_conn_params;
265    }
266
267    // Enable Interrupts
268    XAxiDma_BdRingIntEnable(eth_rx_ring_ptr, XAXIDMA_IRQ_ALL_MASK);
269    XAxiDma_BdRingIntEnable(eth_tx_ring_ptr, XAXIDMA_IRQ_ALL_MASK);
270
271    status |= XAxiDma_BdRingStart(eth_rx_ring_ptr);
272    if (status != XST_SUCCESS){
273        xil_printf("WLAN_AXI_ETHERNET_INTR: Error in XAxiDma BdRingToHw/BdRingStart! Err = %d\n", status);
274        return intr_conn_params;
275    }
276
277    // Start the DMA Tx channel
278    //     NOTE:  No Eth packets are transmitted until actual Tx BD's are pushed to the DMA hardware
279    status = XAxiDma_BdRingStart(eth_tx_ring_ptr);
280
281
282    // Start the Ethernet controller
283    XAxiEthernet_Start(&eth_instance);
284
285    // Fill in return values
286    intr_conn_params.intr_handler0 = (function_ptr_t)_eth_rx_interrupt_handler;
287    intr_conn_params.intr_handler_arg0 = (void*)peripheral_args;
288    intr_conn_params.intr_handler1 = (function_ptr_t)_eth_tx_interrupt_handler;
289    intr_conn_params.intr_handler_arg1 = (void*)peripheral_args;
290
291    return intr_conn_params;
292}
293
294/**
295 * @brief Send Ethernet Frame
296 *
297 * This function sends an Ethernet frame. It does not perform error checking on the validity
298 * of the provided address. It is the responsibility of platform code to error check the arguments
299 * provided to this function.
300 *
301 * @param peripheral_args_t* peripheral_args
302 *          - necessary arguments to distinguish between multiple instances of this peripheral
303 * @param eth_tx_queue_buffer_t* eth_tx_queue_buffer
304 *          - Ethernet packet to send
305 * @return u32 status
306 *          - see header file
307 */
308inline u32 wlan_axi_eth_intr_send(peripheral_args_t* peripheral_args, eth_tx_queue_buffer_t* eth_tx_queue_buffer) {
309
310    int status;
311    XAxiDma_BdRing* tx_ring_ptr;
312    XAxiDma_Bd* cur_bd_ptr;
313    XAxiDma_Bd* first_bd_ptr;
314    u8* bd_addr[2];
315    u16 bd_len[2];
316    u8 num_required_bd, idx_bd;
317    u32 length;
318    u32 ctrl;
319    XAxiDma* eth_dma_instance_ptr;
320
321    interrupt_state_t prev_interrupt_state;
322
323    bd_addr[0] = NULL;
324    bd_addr[1] = NULL;
325    bd_len[0] = 0;
326    bd_len[1] = 0;
327    num_required_bd = 0;
328    ctrl = 0;
329
330    eth_dma_instance_ptr = (XAxiDma*)(peripheral_args->driver_instance);
331
332    length = eth_tx_queue_buffer->seg0_len + eth_tx_queue_buffer->seg1_len;
333
334    if( (length < 42) || (length > 9014) ){
335        xil_printf("ERROR (wlan_axi_eth_intr_send): Length %d out of range\n", length);
336        return WLAN_AXI_ETH_INTR_SEND_ERR_INVALID_ARGS;
337    }
338
339
340    prev_interrupt_state = wlan_platform_intc_stop();
341
342    // Get pointer to the axi_dma Tx buffer descriptor ring
343    tx_ring_ptr = XAxiDma_GetTxRing(eth_dma_instance_ptr);
344
345    if(eth_tx_queue_buffer->seg0_len > 0){
346        bd_addr[num_required_bd] = eth_tx_queue_buffer->seg0;
347        bd_len[num_required_bd] = eth_tx_queue_buffer->seg0_len;
348        num_required_bd++;
349    }
350    if(eth_tx_queue_buffer->seg1_len > 0){
351        bd_addr[num_required_bd] = eth_tx_queue_buffer->seg1_addr;
352        bd_len[num_required_bd] = eth_tx_queue_buffer->seg1_len;
353        num_required_bd++;
354    }
355
356    // Allocate and setup one Tx BD
357    status  = XAxiDma_BdRingAlloc(tx_ring_ptr, num_required_bd, &cur_bd_ptr);
358
359    if(status != XST_SUCCESS) {
360        // There are no free BDs. Rather than block here, we'll return and allow
361        //  the calling context to try transmitting later
362        wlan_platform_intc_set_state(prev_interrupt_state);
363        return WLAN_AXI_ETH_INTR_SEND_ERR_NO_FREE_BD;
364    }
365
366    first_bd_ptr = cur_bd_ptr;
367
368    for(idx_bd = 0; idx_bd < num_required_bd; idx_bd++){
369        // If this is a single BD transmission, both flags will be raised.
370        ctrl = 0;
371        if(idx_bd == 0) ctrl |= XAXIDMA_BD_CTRL_TXSOF_MASK;
372        if(idx_bd == (num_required_bd-1)) ctrl |= XAXIDMA_BD_CTRL_TXEOF_MASK;
373
374        status = XAxiDma_BdSetBufAddr(cur_bd_ptr, (u32)bd_addr[idx_bd]);
375        status |= XAxiDma_BdSetLength(cur_bd_ptr, bd_len[idx_bd], tx_ring_ptr->MaxTransferLen);
376
377        // TODO: this is a cache-sensitive context. In architectures with the cache enabled,
378        //  we should flush the cashe for the range covered in this BD.
379        //XCACHE_FLUSH_DCACHE_RANGE(bd_addr[idx_bd], bd_len[idx_bd]);
380
381        if(status != XST_SUCCESS) {
382            xil_printf("WLAN_AXI_ETHERNET_INTR: ERROR setting ETH TX BD! Err = %d\n", status);
383            status = XAxiDma_BdRingFree(tx_ring_ptr, num_required_bd, first_bd_ptr);
384            wlan_platform_intc_set_state(prev_interrupt_state);
385            return WLAN_AXI_ETH_INTR_SEND_ERR_OTHER;
386        }
387
388        XAxiDma_BdSetCtrl(cur_bd_ptr, ctrl);
389        cur_bd_ptr = (XAxiDma_Bd*)XAxiDma_BdRingNext(tx_ring_ptr, cur_bd_ptr);
390    }
391
392    // Push the BD ring to hardware; this initiates the actual DMA transfer and Ethernet Tx
393    status = XAxiDma_BdRingToHw(tx_ring_ptr, num_required_bd, first_bd_ptr);
394    if(status != XST_SUCCESS){
395        xil_printf("WLAN_AXI_ETHERNET_INTR: ERROR: TX XAxiDma_BdRingToHw! Err = %d\n", status);
396        status = XAxiDma_BdRingFree(tx_ring_ptr, num_required_bd, first_bd_ptr);
397        return WLAN_AXI_ETH_INTR_SEND_ERR_OTHER;
398    }
399
400    wlan_platform_intc_set_state(prev_interrupt_state);
401    return WLAN_SUCCESS;
402}
403
404
405/**
406 * @brief Attach any free buffer descriptors
407 *
408 * This function may be called at any time to attach any free buffer descriptors to free queue entries
409 * provided by the High MAC Framework. If either no buffer descriptors are free or there are no free
410 * queue entries, this function will successfully do nothing.
411 *
412 * @param peripheral_args_t* peripheral_args
413 *          - necessary arguments to distinguish between multiple instances of this peripheral
414 * @return none
415 */
416void wlan_axi_eth_intr_attach_free_rx_bd(peripheral_args_t* peripheral_args) {
417    int status;
418    int iter;
419    u32 bd_count;
420    u32 bd_queue_pairs_to_process;
421    u32 bd_queue_pairs_processed;
422
423    XAxiDma* eth_dma_instance_ptr;
424    XAxiDma_BdRing* rx_ring_ptr;
425    XAxiDma_Bd* first_bd_ptr;
426    XAxiDma_Bd* cur_bd_ptr;
427
428    dl_list checkout;
429    dl_entry* queue_entry;
430
431    interrupt_state_t prev_interrupt_state = wlan_platform_intc_stop();
432
433    eth_dma_instance_ptr = (XAxiDma*)(peripheral_args->driver_instance);
434
435    // Get the Rx ring pointer and the number of free Rx buffer descriptors
436    rx_ring_ptr = XAxiDma_GetRxRing(eth_dma_instance_ptr);
437    bd_count = XAxiDma_BdRingGetFreeCnt(rx_ring_ptr);
438
439    // If there are no BDs, then we are done
440    if (bd_count == 0) {
441        wlan_platform_intc_set_state(prev_interrupt_state);
442        return;
443    }
444
445    // Initialize the list to checkout Tx queue entries
446    dl_list_init(&checkout);
447
448    // Attempt to checkout Tx queue entries for all free buffer descriptors
449    queue_checkout_list(&checkout, bd_count);
450
451    // If there were not enough Tx queue entries available, the length of the
452    // checkout list will be the number of free Tx queue entries that were
453    // found.  Only process buffer descriptors that have a corresponding Tx
454    // queue entry.
455    bd_queue_pairs_to_process = WLAN_MIN(bd_count, checkout.length);
456
457    if (bd_queue_pairs_to_process > 0) {
458
459        // Initialize the number of buffer descriptors processed
460        bd_queue_pairs_processed = 0;
461
462        // Allocate the correct number of Rx buffer descriptors that have
463        // been freed and have Tx queue entries available.
464        status = XAxiDma_BdRingAlloc(rx_ring_ptr, bd_queue_pairs_to_process, &first_bd_ptr);
465        if(status != XST_SUCCESS) {xil_printf("WLAN_AXI_ETHERNET_INTR: Error in XAxiDma_BdRingAlloc()! Err = %d\n", status);
466            wlan_platform_intc_set_state(prev_interrupt_state);
467            return;
468        }
469
470        // Initialize loop variables
471        iter             = checkout.length;
472        queue_entry      = checkout.first;
473        cur_bd_ptr       = first_bd_ptr;
474
475        while ((queue_entry != NULL) && (iter-- > 0)) {
476
477            // Initialize the Rx buffer descriptor
478            status = _init_rx_bd(cur_bd_ptr, queue_entry, WLAN_AXI_ETH_RX_INTR_BUF_SIZE, rx_ring_ptr->MaxTransferLen);
479
480            if (status != XST_SUCCESS) {
481                // Clean up buffer descriptors and Tx queues
482                //     NOTE:  Regardless of where we are in the update, we will check
483                //         back in everything so the function can start fresh next invocation
484                status = XAxiDma_BdRingUnAlloc(rx_ring_ptr, bd_queue_pairs_to_process, first_bd_ptr);
485                if(status != XST_SUCCESS) {xil_printf("WLAN_AXI_ETHERNET_INTR: Error in XAxiDma_BdRingUnAlloc()! Err = %d\n", status);}
486
487                queue_checkin_list(&checkout);
488                wlan_platform_intc_set_state(prev_interrupt_state);
489                return;
490            }
491
492            // Update the BD and queue entry pointers to the next list elements
493            //     NOTE: This loop traverses both lists simultaneously
494            cur_bd_ptr     = XAxiDma_BdRingNext(rx_ring_ptr, cur_bd_ptr);
495            queue_entry = dl_entry_next(queue_entry);
496            bd_queue_pairs_processed++;
497
498        }
499
500        if (bd_queue_pairs_processed == bd_queue_pairs_to_process) {
501            // Push the Rx BD ring to hardware and start receiving
502            status = XAxiDma_BdRingToHw(rx_ring_ptr, bd_queue_pairs_to_process, first_bd_ptr);
503            if(status != XST_SUCCESS) {xil_printf("WLAN_AXI_ETHERNET_INTR: XAxiDma_BdRingToHw failed! Err = %d\n", status);}
504        } else {
505            // Clean up buffer descriptors and Tx queues
506            xil_printf("WLAN_AXI_ETHERNET_INTR: Error processing BD-queue pairs\n");
507
508            status = XAxiDma_BdRingUnAlloc(rx_ring_ptr, bd_queue_pairs_to_process, first_bd_ptr);
509            if(status != XST_SUCCESS) {xil_printf("WLAN_AXI_ETHERNET_INTR: Error in XAxiDma_BdRingUnAlloc()! Err = %d\n", status);}
510
511            queue_checkin_list(&checkout);
512        }
513    }
514    wlan_platform_intc_set_state(prev_interrupt_state);
515}
516
517int wlan_axi_eth_intr_get_mtu(){
518    return 9000;
519}
520
521// Local Helper Functions
522
523/*****************************************************************************/
524/**
525 * @brief Initializes an Rx buffer descriptor to use the given Tx queue entry
526 *
527 * @param XAxiDma_Bd * bd_ptr
528 *  - Pointer to buffer descriptor to be initialized
529 * @param dl_entry * qe_ptr
530 *  - Pointer to Tx queue element
531 * @param u32 max_transfer_len
532 *  - Max transfer length for Rx BD
533 * @param u32 LengthMask
534 *  - Mask for length in Rx BD
535 *
536 * @return WLAN_SUCCESS or WLAN_FAILURE
537 */
538static int _init_rx_bd(XAxiDma_Bd* bd_ptr, dl_entry* qe_ptr, u32 max_transfer_len, u32 LengthMask) {
539    int status;
540    u32 buf_addr;
541
542    if ((bd_ptr == NULL) || (qe_ptr == NULL)) { return WLAN_FAILURE; }
543
544    // Set the memory address for this BD's buffer to the corresponding Tx queue entry buffer
545    //     NOTE:  This pointer is offset by the size of a MAC header and LLC header, which results
546    //         in the Ethernet payload being copied to its post-encapsulated location. This
547    //         speeds up the encapsulation process by skipping any re-copying of Ethernet payloads
548    buf_addr = (u32)((void*)((eth_rx_queue_buffer_t*)(qe_ptr->data))->pkt);
549
550    status   = XAxiDma_BdSetBufAddr(bd_ptr, buf_addr);
551    if (status != XST_SUCCESS) { xil_printf("WLAN_AXI_ETHERNET_INTR: XAxiDma_BdSetBufAddr failed (addr 0x08x)! Err = %d\n", buf_addr, status); return WLAN_FAILURE; }
552
553    // Set every Rx BD to max length (this assures 1 BD per Rx pkt)
554    //     NOTE:  Jumbo Ethernet frames are not supported by the Ethernet device (ie the XAE_JUMBO_OPTION is cleared),
555    //         so the WLAN_ETH_PKT_BUF_SIZE must be at least large enough to support standard MTUs (ie greater than
556    //         1522 bytes) so the assumption of 1 BD = 1 Rx pkt is met.
557    status = XAxiDma_BdSetLength(bd_ptr, max_transfer_len, LengthMask);
558    if (status != XST_SUCCESS) { xil_printf("WLAN_AXI_ETHERNET_INTR: XAxiDma_BdSetLength failed (addr 0x08x)! Err = %d\n", buf_addr, status); return WLAN_FAILURE; }
559
560    // Rx BD's don't need control flags before use; DMA populates these post-Rx
561    XAxiDma_BdSetCtrl(bd_ptr, 0);
562
563    return WLAN_SUCCESS;
564}
565
566static u32 _wlan_axi_eth_intr_calc_num_bd(u32 mem_size){
567    return (mem_size/XAXIDMA_BD_MINIMUM_ALIGNMENT);
568}
569
570static void _eth_tx_interrupt_handler(void *callback_arg){
571    XAxiDma_BdRing* tx_ring_ptr;
572    u32 irq_status;
573    XAxiDma* eth_dma_instance_ptr;
574
575    peripheral_args_t* peripheral_args = (peripheral_args_t*)callback_arg;
576    eth_dma_instance_ptr = (XAxiDma*)(peripheral_args->driver_instance);
577
578    tx_ring_ptr = XAxiDma_GetTxRing(eth_dma_instance_ptr);
579
580    XAxiDma_BdRingIntDisable(tx_ring_ptr, XAXIDMA_IRQ_ALL_MASK);
581    irq_status = XAxiDma_BdRingGetIrq(tx_ring_ptr);
582
583    XAxiDma_BdRingAckIrq(tx_ring_ptr, irq_status);
584
585    if (irq_status & XAXIDMA_IRQ_ERROR_MASK) {
586        xil_printf("WLAN_AXI_ETHERNET_INTR: %s: error interrupt asserted\n", __FUNCTION__);
587        XAxiDma_Reset(eth_dma_instance_ptr);
588
589        return;
590    }
591
592    if (irq_status & (XAXIDMA_IRQ_DELAY_MASK | XAXIDMA_IRQ_IOC_MASK)) {
593        _wlan_axi_eth_intr_free_tx_bd(peripheral_args);
594    }
595
596    XAxiDma_BdRingIntEnable(tx_ring_ptr, XAXIDMA_IRQ_ALL_MASK);
597
598
599}
600
601static void _eth_rx_interrupt_handler(void *callback_arg){
602    XAxiDma_BdRing* rx_ring_ptr;
603    u32 irq_status;
604    XAxiDma_Bd* bd_set_to_process_ptr;
605    u32 eth_rx_len;
606    u32 eth_rx_buf;
607    u32 bd_set_count;
608    u32 status;
609    eth_rx_queue_buffer_t* eth_rx_queue_buffer;
610    XAxiDma* eth_dma_instance_ptr;
611
612
613    // FIXME: In the case of >WLAN_AXI_ETH_RX_INTR_BUF_SIZE receptions,
614    //  packet won't be dropped but rather split across multiple BDs. We
615    //  should only pass a received Ethernet frame to the framework for
616    //  further processing if it is both a Start of Frame and End of Frame.
617
618    peripheral_args_t* peripheral_args = (peripheral_args_t*)callback_arg;
619    eth_dma_instance_ptr = (XAxiDma*)(peripheral_args->driver_instance);
620
621    rx_ring_ptr = XAxiDma_GetRxRing(eth_dma_instance_ptr);
622    irq_status = XAxiDma_BdRingGetIrq(rx_ring_ptr);
623
624    if (!(irq_status & XAXIDMA_IRQ_ERROR_MASK)) {
625        // At least one reception is completed
626
627        // Disable the interrupt and then acknowledge the interrupt
628        XAxiDma_BdRingIntDisable(rx_ring_ptr, irq_status);
629        XAxiDma_BdRingAckIrq(rx_ring_ptr, irq_status);
630
631        // Get all the BDs that are available
632        //     NOTE:  Use global variable so it is easy to process a sub-set of
633        //         packets at a time.
634        bd_set_count = XAxiDma_BdRingFromHw(rx_ring_ptr, XAXIDMA_ALL_BDS, &bd_set_to_process_ptr);
635
636        if(bd_set_count > 0) {
637            while (bd_set_count > 0) {
638                // Lookup length and data pointers from the DMA metadata
639                eth_rx_len = XAxiDma_BdGetActualLength(bd_set_to_process_ptr, rx_ring_ptr->MaxTransferLen);
640                eth_rx_buf = XAxiDma_BdGetBufAddr(bd_set_to_process_ptr);
641
642                // Get the Rx Eth queue entry pointer. This is located further back in the queue_buffer_t struct.
643                //
644                eth_rx_queue_buffer = (eth_rx_queue_buffer_t*)( (u8*)eth_rx_buf - offsetof(eth_rx_queue_buffer_t, pkt));
645                eth_rx_queue_buffer->length = eth_rx_len;
646
647                // Check and make sure this is both the start and end of a frame.
648                //  A large MTU reception could be split among multiple BDs. We do
649                //  not currently support jumbo receptions.
650                // FIXME: we need similar code in the emac ps peripheral.
651                if ( ((XAxiDma_BdGetSts(bd_set_to_process_ptr) & XAXIDMA_BD_STS_RXSOF_MASK) == 0) ||
652                     ((XAxiDma_BdGetSts(bd_set_to_process_ptr) & XAXIDMA_BD_STS_RXEOF_MASK) == 0) ){
653                    queue_checkin(eth_rx_queue_buffer->pyld_queue_hdr.dle);
654                } else {
655                    // Add the Rx Eth queue entry to the list of packets to be processed by the High Framework
656                    if( peripheral_args->callback0(eth_rx_queue_buffer) == 0 ){
657                        queue_checkin(eth_rx_queue_buffer->pyld_queue_hdr.dle);
658                    }
659                }
660
661                // Free the ETH DMA buffer descriptor
662                status = XAxiDma_BdRingFree(rx_ring_ptr, 1, bd_set_to_process_ptr);
663                if(status != XST_SUCCESS) {
664                        xil_printf("WLAN_AXI_ETHERNET_INTR: Error in XAxiDma_BdRingFree of Rx BD! Err = %d\n", status);
665                }
666
667                bd_set_count--;
668
669                // Update to the next BD in the chain for the next iteration
670                bd_set_to_process_ptr = XAxiDma_BdRingNext(rx_ring_ptr, bd_set_to_process_ptr);
671            }
672
673            // At this point, we heave dealt with every BD in the ring
674
675            // Attach any newly free BDs
676            wlan_axi_eth_intr_attach_free_rx_bd(peripheral_args);
677        }
678
679        // Finished all available Eth Rx - re-enable interrupt
680        XAxiDma_BdRingIntEnable(rx_ring_ptr, irq_status);
681
682    } else {
683        // Acknowledge the error interrupt
684        XAxiDma_BdRingAckIrq(rx_ring_ptr, XAXIDMA_IRQ_ERROR_MASK);
685        // TODO:  Clean up from error condition !!!
686    }
687
688    return;
689}
690
691/**
692 * @brief Free any complete Tx BDs
693 *
694 * @param peripheral_args_t* peripheral_args
695 *          - necessary arguments to distinguish between multiple instances of this peripheral
696 * @return none
697 */
698inline void _wlan_axi_eth_intr_free_tx_bd(peripheral_args_t* peripheral_args) {
699       int i;
700       int status;
701
702       XAxiDma* eth_dma_instance_ptr;
703       eth_tx_queue_buffer_t* eth_tx_queue_buffer;
704       XAxiDma_BdRing* eth_tx_ring_ptr;
705       u32 eth_tx_buf;
706
707       int processed_bd_count;
708
709       XAxiDma_Bd* bd_set_ptr = NULL;
710       XAxiDma_Bd* bd_ptr = NULL;
711
712       eth_dma_instance_ptr = (XAxiDma*)(peripheral_args->driver_instance);
713       eth_tx_ring_ptr = XAxiDma_GetTxRing(eth_dma_instance_ptr);
714
715       // Check how many buffer descriptors have been processed
716       processed_bd_count = XAxiDma_BdRingFromHw(eth_tx_ring_ptr, XAXIDMA_ALL_BDS, &bd_set_ptr);
717
718       // Initialize the working descriptor pointer
719       bd_ptr = bd_set_ptr;
720
721       for (i = 0; i < processed_bd_count; i++) {
722
723           // Get the status of the descriptor
724           status = XAxiDma_BdGetSts(bd_ptr);
725
726           // Check the status
727           if ((status & XAXIDMA_BD_STS_ALL_ERR_MASK) || (!(status & XAXIDMA_BD_STS_COMPLETE_MASK))) {
728               xil_printf("WLAN_AXI_ETHERNET_INTR: Error in _wlan_axi_eth_poll_free_tx_bd(): Tx BD contained error status\n");
729           }
730
731           if(XAxiDma_BdGetCtrl(bd_ptr) & XAXIDMA_BD_CTRL_TXSOF_MASK){
732               // This is the start of a frame, i.e. it is seg0 of a queue buffer.
733
734               // Call the platform's Tx Done callback so it can clean up
735               eth_tx_buf = XAxiDma_BdGetBufAddr(bd_ptr);
736
737               // Get the Rx Eth queue entry pointer. This is located further back in the queue_buffer_t struct
738               eth_tx_queue_buffer = (eth_tx_queue_buffer_t*)( (u8*)eth_tx_buf - offsetof(eth_tx_queue_buffer_t, seg0));
739
740               // Per the documentation for XAxiDMA_BdRingFromHw, "If hardware has partially
741               //  completed a packet spanning multiple BDs, then none of the BDs for that
742               //  packet will be included in the results." So, if we see the BD marked as a
743               //  start of frame, we know the entire frame must be complete. So, we can call
744               //  the _done_callback() function even if we have not finished looping through
745               //  every BD.
746               peripheral_args->callback1(eth_tx_queue_buffer);
747           }
748
749           // Get next descriptor
750           bd_ptr = (XAxiDma_Bd*)XAxiDma_BdRingNext(eth_tx_ring_ptr, bd_ptr);
751        }
752
753
754        // Free all processed RX BDs for future transmission
755        status = XAxiDma_BdRingFree(eth_tx_ring_ptr, processed_bd_count, bd_set_ptr);
756
757        if (status != XST_SUCCESS) {
758            xil_printf("WLAN_AXI_ETHERNET_INTR: Error in _wlan_axi_eth_poll_free_tx_bd(): Unable to free Tx ring");
759            return;
760        }
761        return;
762}
763
764
Note: See TracBrowser for help on using the repository browser.