DMAing is one of the most important parts to understand for any network driver developer. In my last post I have explored the descriptor details in Tx path. In this post, we will try to explore few details of descriptors in Rx path. We will use ath9k as the reference driver. This should be similar for ath5k, madwifi or any other Atheros driver including Fusion and Aquila. Handling of Rx descriptors is slightly different from that of Tx descriptors.
Ath9K: EDMA and Non-EDMA
In Ath9k there are two paths for reception of frames from the hardware. The first one is the case where EDMA (Enhanced DMA) is supported and the other is where it is not supported. There are a couple of differences between the two. In case of EDMA, two queues are maintained by the hardware namely high priority queue and low priority queue. The second difference is in the way the memory is allocated for descriptors and buffers (skb). In case of non-EDMA, the allocation is more like that in the tx path. Rx descriptors are allocated followed by the allocation of buffers(skb). Both of them are DMA synced and the physical address of the buffer is populated in the corresponding descriptor. The following figures shows allocation of descriptors and their linkage with the frame buffers.
Rx descriptor allocation:
Rx Descriptors are generally allocated at the initialization of the driver. In Ath9k driver, please refer the function, ath_rx_init . There are two separate paths, one is for EDMA and the other is for Non-EDMA.
EDMA path allocations are handled in ath_rx_edma_init . In this function, you can see that the buffers are allocated using ath_rxbuf_alloc and dma-mapped using dma_map_single. Please observe that the size of the buffer is equal to rx_bufsize (the sum of rx status length and max buffer size).
Non-EDMA path allocations are handled in ath_rx_init itself. Its allocation is similar to that of Tx Descriptors.
Populate and pass descriptor to the hardware:
Once the descriptors are allocated, the next step is to pass them to the hardware. Generally it is not done as part of the initialization. Rather it is delayed further till reception of packets is enabled. Reception of packets generally is enabled when atleast one of the VAPs (created on top the device) is enabled. In Ath9k/mac80211 combination, this invocation path is ieee80211_do_open --> drv_start (ath9k_start) --> ath_complete_reset --> ath_startrecv.
Also, observe that calls are made to ath_rx_edma_buf_link (EDMA path, ath_edma_start_recv --> ath_rx_addbuffer_edma --> ath_rx_edma_buf_link ) and ath_rx_buf_link (Non-EDMA path) from ath_startrecv. In ath_rx_edma_buf_link and ath_rx_buf_link the descriptor's physical address is passed to the hardware.
Please observe in Non-EDMA path (ath_rx_buf_link)that, the physical address of the frame buffer is populated to the corresponding pointer of the descriptor (ds->ds_data = bf->bf_buf_addr;). This is not needed in EDMA path. Please refer the figures shown above.
Processing of frames:
When a frame is received, hardware populates the next available descriptor and raises an interrupt. Driver handles the interrupt and actual processing of the frames is done in a tasklet . The driver extracts the frame, dma-unmap it, processes it and sends it to the top layers. Once the frame is sent to the top layer, it also has to allocate a new frame buffer, dma-map it and pass to the hardware. Because it has taken out one frame buffer and passed it to the upper layers. The upper layers will free that frame once they are done with their processing.
In Ath9k driver, corresponding tasklet function is ath_rx_tasklet. Frames are extracted in ath_get_next_rx_buf and ath_edma_get_next_rx_buf functions respectively in Non-EDMA and EDMA paths respectively. Please also observe the creation of new frame buffer using ath_rxbuf_alloc and the new buffer is passed to the hardware using ath_rx_buf_link / ath_rx_edma_buf_link.