/* * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the * above copyright notice and this permission notice appear in all * copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #include "hal_hw_headers.h" #include "dp_types.h" #include "dp_rx.h" #include "dp_peer.h" #include "hal_rx.h" #include "hal_api.h" #include "qdf_trace.h" #include "qdf_nbuf.h" #include "hal_api_mon.h" #include "dp_rx_mon.h" #include "wlan_cfg.h" #include "dp_internal.h" /* The maxinum buffer length allocated for radio tap */ #define MAX_MONITOR_HEADER (512) /* * PPDU id is from 0 to 64k-1. PPDU id read from status ring and PPDU id * read from destination ring shall track each other. If the distance of * two ppdu id is less than 20000. It is assume no wrap around. Otherwise, * It is assume wrap around. */ #define NOT_PPDU_ID_WRAP_AROUND 20000 /* * The destination ring processing is stuck if the destrination is not * moving while status ring moves 16 ppdu. the destination ring processing * skips this destination ring ppdu as walkaround */ #define MON_DEST_RING_STUCK_MAX_CNT 16 /** * dp_rx_mon_link_desc_return() - Return a MPDU link descriptor to HW * (WBM), following error handling * * @dp_pdev: core txrx pdev context * @buf_addr_info: void pointer to monitor link descriptor buf addr info * Return: QDF_STATUS */ static QDF_STATUS dp_rx_mon_link_desc_return(struct dp_pdev *dp_pdev, hal_buff_addrinfo_t buf_addr_info, int mac_id) { struct dp_srng *dp_srng; hal_ring_handle_t hal_ring_hdl; hal_soc_handle_t hal_soc; QDF_STATUS status = QDF_STATUS_E_FAILURE; void *src_srng_desc; int mac_for_pdev = dp_get_mac_id_for_mac(dp_pdev->soc, mac_id); hal_soc = dp_pdev->soc->hal_soc; dp_srng = &dp_pdev->rxdma_mon_desc_ring[mac_for_pdev]; hal_ring_hdl = dp_srng->hal_srng; qdf_assert(hal_ring_hdl); if (qdf_unlikely(hal_srng_access_start(hal_soc, hal_ring_hdl))) { /* TODO */ /* * Need API to convert from hal_ring pointer to * Ring Type / Ring Id combo */ QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, "%s %d : \ HAL RING Access For WBM Release SRNG Failed -- %pK", __func__, __LINE__, hal_ring_hdl); goto done; } src_srng_desc = hal_srng_src_get_next(hal_soc, hal_ring_hdl); if (qdf_likely(src_srng_desc)) { /* Return link descriptor through WBM ring (SW2WBM)*/ hal_rx_mon_msdu_link_desc_set(hal_soc, src_srng_desc, buf_addr_info); status = QDF_STATUS_SUCCESS; } else { QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, "%s %d -- Monitor Link Desc WBM Release Ring Full", __func__, __LINE__); } done: hal_srng_access_end(hal_soc, hal_ring_hdl); return status; } /** * dp_mon_adjust_frag_len() - MPDU and MSDU may spread across * multiple nbufs. This function * is to return data length in * fragmented buffer * * @total_len: pointer to remaining data length. * @frag_len: pointer to data length in this fragment. */ static inline void dp_mon_adjust_frag_len(uint32_t *total_len, uint32_t *frag_len) { if (*total_len >= (RX_BUFFER_SIZE - RX_PKT_TLVS_LEN)) { *frag_len = RX_BUFFER_SIZE - RX_PKT_TLVS_LEN; *total_len -= *frag_len; } else { *frag_len = *total_len; *total_len = 0; } } /** * dp_rx_cookie_2_mon_link_desc() - Retrieve Link descriptor based on target * @pdev: core physical device context * @hal_buf_info: structure holding the buffer info * mac_id: mac number * * Return: link descriptor address */ static inline void *dp_rx_cookie_2_mon_link_desc(struct dp_pdev *pdev, struct hal_buf_info buf_info, uint8_t mac_id) { if (pdev->soc->wlan_cfg_ctx->rxdma1_enable) return dp_rx_cookie_2_mon_link_desc_va(pdev, &buf_info, mac_id); return dp_rx_cookie_2_link_desc_va(pdev->soc, &buf_info); } /** * dp_rx_monitor_link_desc_return() - Return Link descriptor based on target * @pdev: core physical device context * @p_last_buf_addr_info: MPDU Link descriptor * mac_id: mac number * * Return: QDF_STATUS */ static inline QDF_STATUS dp_rx_monitor_link_desc_return(struct dp_pdev *pdev, hal_buff_addrinfo_t p_last_buf_addr_info, uint8_t mac_id, uint8_t bm_action) { if (pdev->soc->wlan_cfg_ctx->rxdma1_enable) return dp_rx_mon_link_desc_return(pdev, p_last_buf_addr_info, mac_id); return dp_rx_link_desc_return_by_addr(pdev->soc, p_last_buf_addr_info, bm_action); } /** * dp_rxdma_get_mon_dst_ring() - Return the pointer to rxdma_err_dst_ring * or mon_dst_ring based on the target * @pdev: core physical device context * @mac_for_pdev: mac_id number * * Return: ring address */ static inline void *dp_rxdma_get_mon_dst_ring(struct dp_pdev *pdev, uint8_t mac_for_pdev) { if (pdev->soc->wlan_cfg_ctx->rxdma1_enable) return pdev->rxdma_mon_dst_ring[mac_for_pdev].hal_srng; return pdev->rxdma_err_dst_ring[mac_for_pdev].hal_srng; } /** * dp_rxdma_get_mon_buf_ring() - Return monitor buf ring address * based on target * @pdev: core physical device context * @mac_for_pdev: mac id number * * Return: ring address */ static inline struct dp_srng *dp_rxdma_get_mon_buf_ring(struct dp_pdev *pdev, uint8_t mac_for_pdev) { if (pdev->soc->wlan_cfg_ctx->rxdma1_enable) return &pdev->rxdma_mon_buf_ring[mac_for_pdev]; return &pdev->rx_refill_buf_ring; } /** * dp_rx_get_mon_desc_pool() - Return monitor descriptor pool * based on target * @soc: soc handle * @mac_id: mac id number * @pdev_id: pdev id number * * Return: descriptor pool address */ static inline struct rx_desc_pool *dp_rx_get_mon_desc_pool(struct dp_soc *soc, uint8_t mac_id, uint8_t pdev_id) { if (soc->wlan_cfg_ctx->rxdma1_enable) return &soc->rx_desc_mon[mac_id]; return &soc->rx_desc_buf[pdev_id]; } /** * dp_rx_get_mon_desc() - Return Rx descriptor based on target * @soc: soc handle * @cookie: cookie value * * Return: Rx descriptor */ static inline struct dp_rx_desc *dp_rx_get_mon_desc(struct dp_soc *soc, uint32_t cookie) { if (soc->wlan_cfg_ctx->rxdma1_enable) return dp_rx_cookie_2_va_mon_buf(soc, cookie); return dp_rx_cookie_2_va_rxdma_buf(soc, cookie); } /** * dp_rx_mon_mpdu_pop() - Return a MPDU link descriptor to HW * (WBM), following error handling * * @soc: core DP main context * @mac_id: mac id which is one of 3 mac_ids * @rxdma_dst_ring_desc: void pointer to monitor link descriptor buf addr info * @head_msdu: head of msdu to be popped * @tail_msdu: tail of msdu to be popped * @npackets: number of packet to be popped * @ppdu_id: ppdu id of processing ppdu * @head: head of descs list to be freed * @tail: tail of decs list to be freed * * Return: number of msdu in MPDU to be popped */ static inline uint32_t dp_rx_mon_mpdu_pop(struct dp_soc *soc, uint32_t mac_id, hal_rxdma_desc_t rxdma_dst_ring_desc, qdf_nbuf_t *head_msdu, qdf_nbuf_t *tail_msdu, uint32_t *npackets, uint32_t *ppdu_id, union dp_rx_desc_list_elem_t **head, union dp_rx_desc_list_elem_t **tail) { struct dp_pdev *dp_pdev = dp_get_pdev_for_mac_id(soc, mac_id); void *rx_desc_tlv; void *rx_msdu_link_desc; qdf_nbuf_t msdu; qdf_nbuf_t last; struct hal_rx_msdu_list msdu_list; uint16_t num_msdus; uint32_t rx_buf_size, rx_pkt_offset; struct hal_buf_info buf_info; uint32_t rx_bufs_used = 0; uint32_t msdu_ppdu_id, msdu_cnt; uint8_t *data; uint32_t i; uint32_t total_frag_len = 0, frag_len = 0; bool is_frag, is_first_msdu; bool drop_mpdu = false; uint8_t bm_action = HAL_BM_ACTION_PUT_IN_IDLE_LIST; uint64_t nbuf_paddr = 0; uint32_t rx_link_buf_info[HAL_RX_BUFFINFO_NUM_DWORDS]; msdu = 0; last = NULL; hal_rx_reo_ent_buf_paddr_get(rxdma_dst_ring_desc, &buf_info, &msdu_cnt); if ((hal_rx_reo_ent_rxdma_push_reason_get(rxdma_dst_ring_desc) == HAL_RX_WBM_RXDMA_PSH_RSN_ERROR)) { uint8_t rxdma_err = hal_rx_reo_ent_rxdma_error_code_get( rxdma_dst_ring_desc); if (qdf_unlikely((rxdma_err == HAL_RXDMA_ERR_FLUSH_REQUEST) || (rxdma_err == HAL_RXDMA_ERR_MPDU_LENGTH) || (rxdma_err == HAL_RXDMA_ERR_OVERFLOW))) { drop_mpdu = true; dp_pdev->rx_mon_stats.dest_mpdu_drop++; } } is_frag = false; is_first_msdu = true; do { /* WAR for duplicate link descriptors received from HW */ if (qdf_unlikely(dp_pdev->mon_last_linkdesc_paddr == buf_info.paddr)) { dp_pdev->rx_mon_stats.dup_mon_linkdesc_cnt++; return rx_bufs_used; } rx_msdu_link_desc = dp_rx_cookie_2_mon_link_desc(dp_pdev, buf_info, mac_id); qdf_assert(rx_msdu_link_desc); hal_rx_msdu_list_get(soc->hal_soc, rx_msdu_link_desc, &msdu_list, &num_msdus); for (i = 0; i < num_msdus; i++) { uint32_t l2_hdr_offset; struct dp_rx_desc *rx_desc = NULL; rx_desc = dp_rx_get_mon_desc(soc, msdu_list.sw_cookie[i]); qdf_assert_always(rx_desc); msdu = rx_desc->nbuf; if (msdu) nbuf_paddr = qdf_nbuf_get_frag_paddr(msdu, 0); /* WAR for duplicate buffers received from HW */ if (qdf_unlikely(dp_pdev->mon_last_buf_cookie == msdu_list.sw_cookie[i] || !msdu || msdu_list.paddr[i] != nbuf_paddr || !rx_desc->in_use)) { /* Skip duplicate buffer and drop subsequent * buffers in this MPDU */ drop_mpdu = true; dp_pdev->rx_mon_stats.dup_mon_buf_cnt++; dp_pdev->mon_last_linkdesc_paddr = buf_info.paddr; continue; } if (rx_desc->unmapped == 0) { qdf_nbuf_unmap_single(soc->osdev, msdu, QDF_DMA_FROM_DEVICE); rx_desc->unmapped = 1; } if (drop_mpdu) { dp_pdev->mon_last_linkdesc_paddr = buf_info.paddr; qdf_nbuf_free(msdu); msdu = NULL; goto next_msdu; } data = qdf_nbuf_data(msdu); rx_desc_tlv = HAL_RX_MON_DEST_GET_DESC(data); QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_DEBUG, "[%s] i=%d, ppdu_id=%x, num_msdus = %u", __func__, i, *ppdu_id, num_msdus); if (is_first_msdu) { if (!HAL_RX_HW_DESC_MPDU_VALID( rx_desc_tlv)) { drop_mpdu = true; qdf_nbuf_free(msdu); msdu = NULL; dp_pdev->mon_last_linkdesc_paddr = buf_info.paddr; goto next_msdu; } msdu_ppdu_id = hal_rx_hw_desc_get_ppduid_get( soc->hal_soc, rx_desc_tlv); is_first_msdu = false; QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_DEBUG, "[%s] msdu_ppdu_id=%x", __func__, msdu_ppdu_id); if (*ppdu_id > msdu_ppdu_id) QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_DEBUG, "[%s][%d] ppdu_id=%d " "msdu_ppdu_id=%d", __func__, __LINE__, *ppdu_id, msdu_ppdu_id); if ((*ppdu_id < msdu_ppdu_id) && ( (msdu_ppdu_id - *ppdu_id) < NOT_PPDU_ID_WRAP_AROUND)) { *ppdu_id = msdu_ppdu_id; return rx_bufs_used; } else if ((*ppdu_id > msdu_ppdu_id) && ( (*ppdu_id - msdu_ppdu_id) > NOT_PPDU_ID_WRAP_AROUND)) { *ppdu_id = msdu_ppdu_id; return rx_bufs_used; } dp_pdev->mon_last_linkdesc_paddr = buf_info.paddr; } if (hal_rx_desc_is_first_msdu(soc->hal_soc, rx_desc_tlv)) hal_rx_mon_hw_desc_get_mpdu_status(soc->hal_soc, rx_desc_tlv, &(dp_pdev->ppdu_info.rx_status)); if (msdu_list.msdu_info[i].msdu_flags & HAL_MSDU_F_MSDU_CONTINUATION) { if (!is_frag) { total_frag_len = msdu_list.msdu_info[i].msdu_len; is_frag = true; } dp_mon_adjust_frag_len( &total_frag_len, &frag_len); } else { if (is_frag) { dp_mon_adjust_frag_len( &total_frag_len, &frag_len); } else { frag_len = msdu_list.msdu_info[i].msdu_len; } is_frag = false; msdu_cnt--; } QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_DEBUG, "%s total_len %u frag_len %u flags %u", __func__, total_frag_len, frag_len, msdu_list.msdu_info[i].msdu_flags); rx_pkt_offset = HAL_RX_MON_HW_RX_DESC_SIZE(); /* * HW structures call this L3 header padding * -- even though this is actually the offset * from the buffer beginning where the L2 * header begins. */ l2_hdr_offset = hal_rx_msdu_end_l3_hdr_padding_get(soc->hal_soc, data); rx_buf_size = rx_pkt_offset + l2_hdr_offset + frag_len; qdf_nbuf_set_pktlen(msdu, rx_buf_size); #if 0 /* Disble it.see packet on msdu done set to 0 */ /* * Check if DMA completed -- msdu_done is the * last bit to be written */ if (!hal_rx_attn_msdu_done_get(rx_desc_tlv)) { QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, "%s:%d: Pkt Desc", __func__, __LINE__); QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, rx_desc_tlv, 128); qdf_assert_always(0); } #endif QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_DEBUG, "%s: rx_pkt_offset=%d, l2_hdr_offset=%d, msdu_len=%d, addr=%pK skb->len %u", __func__, rx_pkt_offset, l2_hdr_offset, msdu_list.msdu_info[i].msdu_len, qdf_nbuf_data(msdu), (uint32_t)qdf_nbuf_len(msdu)); if (head_msdu && !*head_msdu) { *head_msdu = msdu; } else { if (last) qdf_nbuf_set_next(last, msdu); } last = msdu; next_msdu: dp_pdev->mon_last_buf_cookie = msdu_list.sw_cookie[i]; rx_bufs_used++; dp_rx_add_to_free_desc_list(head, tail, rx_desc); } /* * Store the current link buffer into to the local * structure to be used for release purpose. */ hal_rxdma_buff_addr_info_set(rx_link_buf_info, buf_info.paddr, buf_info.sw_cookie, buf_info.rbm); hal_rx_mon_next_link_desc_get(rx_msdu_link_desc, &buf_info); if (dp_rx_monitor_link_desc_return(dp_pdev, (hal_buff_addrinfo_t) rx_link_buf_info, mac_id, bm_action) != QDF_STATUS_SUCCESS) QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, "dp_rx_monitor_link_desc_return failed"); } while (buf_info.paddr && msdu_cnt); if (last) qdf_nbuf_set_next(last, NULL); *tail_msdu = msdu; return rx_bufs_used; } static inline void dp_rx_msdus_set_payload(struct dp_soc *soc, qdf_nbuf_t msdu) { uint8_t *data; uint32_t rx_pkt_offset, l2_hdr_offset; data = qdf_nbuf_data(msdu); rx_pkt_offset = HAL_RX_MON_HW_RX_DESC_SIZE(); l2_hdr_offset = hal_rx_msdu_end_l3_hdr_padding_get(soc->hal_soc, data); qdf_nbuf_pull_head(msdu, rx_pkt_offset + l2_hdr_offset); } static inline qdf_nbuf_t dp_rx_mon_restitch_mpdu_from_msdus(struct dp_soc *soc, uint32_t mac_id, qdf_nbuf_t head_msdu, qdf_nbuf_t last_msdu, struct cdp_mon_status *rx_status) { qdf_nbuf_t msdu, mpdu_buf, prev_buf, msdu_orig, head_frag_list; uint32_t decap_format, wifi_hdr_len, sec_hdr_len, msdu_llc_len, mpdu_buf_len, decap_hdr_pull_bytes, frag_list_sum_len, dir, is_amsdu, is_first_frag, amsdu_pad; void *rx_desc; char *hdr_desc; unsigned char *dest; struct ieee80211_frame *wh; struct ieee80211_qoscntl *qos; struct dp_pdev *dp_pdev = dp_get_pdev_for_mac_id(soc, mac_id); head_frag_list = NULL; mpdu_buf = NULL; /* The nbuf has been pulled just beyond the status and points to the * payload */ if (!head_msdu) goto mpdu_stitch_fail; msdu_orig = head_msdu; rx_desc = qdf_nbuf_data(msdu_orig); if (HAL_RX_DESC_GET_MPDU_LENGTH_ERR(rx_desc)) { /* It looks like there is some issue on MPDU len err */ /* Need further investigate if drop the packet */ DP_STATS_INC(dp_pdev, dropped.mon_rx_drop, 1); return NULL; } rx_desc = qdf_nbuf_data(last_msdu); rx_status->cdp_rs_fcs_err = HAL_RX_DESC_GET_MPDU_FCS_ERR(rx_desc); dp_pdev->ppdu_info.rx_status.rs_fcs_err = HAL_RX_DESC_GET_MPDU_FCS_ERR(rx_desc); /* Fill out the rx_status from the PPDU start and end fields */ /* HAL_RX_GET_PPDU_STATUS(soc, mac_id, rx_status); */ rx_desc = qdf_nbuf_data(head_msdu); decap_format = HAL_RX_DESC_GET_DECAP_FORMAT(rx_desc); /* Easy case - The MSDU status indicates that this is a non-decapped * packet in RAW mode. */ if (decap_format == HAL_HW_RX_DECAP_FORMAT_RAW) { /* Note that this path might suffer from headroom unavailabilty * - but the RX status is usually enough */ dp_rx_msdus_set_payload(soc, head_msdu); QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_DEBUG, "[%s][%d] decap format raw head %pK head->next %pK last_msdu %pK last_msdu->next %pK", __func__, __LINE__, head_msdu, head_msdu->next, last_msdu, last_msdu->next); mpdu_buf = head_msdu; prev_buf = mpdu_buf; frag_list_sum_len = 0; msdu = qdf_nbuf_next(head_msdu); is_first_frag = 1; while (msdu) { dp_rx_msdus_set_payload(soc, msdu); if (is_first_frag) { is_first_frag = 0; head_frag_list = msdu; } frag_list_sum_len += qdf_nbuf_len(msdu); /* Maintain the linking of the cloned MSDUS */ qdf_nbuf_set_next_ext(prev_buf, msdu); /* Move to the next */ prev_buf = msdu; msdu = qdf_nbuf_next(msdu); } qdf_nbuf_trim_tail(prev_buf, HAL_RX_FCS_LEN); /* If there were more fragments to this RAW frame */ if (head_frag_list) { if (frag_list_sum_len < sizeof(struct ieee80211_frame_min_one)) { DP_STATS_INC(dp_pdev, dropped.mon_rx_drop, 1); return NULL; } frag_list_sum_len -= HAL_RX_FCS_LEN; qdf_nbuf_append_ext_list(mpdu_buf, head_frag_list, frag_list_sum_len); qdf_nbuf_set_next(mpdu_buf, NULL); } goto mpdu_stitch_done; } /* Decap mode: * Calculate the amount of header in decapped packet to knock off based * on the decap type and the corresponding number of raw bytes to copy * status header */ rx_desc = qdf_nbuf_data(head_msdu); hdr_desc = HAL_RX_DESC_GET_80211_HDR(rx_desc); QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_DEBUG, "[%s][%d] decap format not raw", __func__, __LINE__); /* Base size */ wifi_hdr_len = sizeof(struct ieee80211_frame); wh = (struct ieee80211_frame *)hdr_desc; dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK; if (dir == IEEE80211_FC1_DIR_DSTODS) wifi_hdr_len += 6; is_amsdu = 0; if (wh->i_fc[0] & QDF_IEEE80211_FC0_SUBTYPE_QOS) { qos = (struct ieee80211_qoscntl *) (hdr_desc + wifi_hdr_len); wifi_hdr_len += 2; is_amsdu = (qos->i_qos[0] & IEEE80211_QOS_AMSDU); } /*Calculate security header length based on 'Protected' * and 'EXT_IV' flag * */ if (wh->i_fc[1] & IEEE80211_FC1_WEP) { char *iv = (char *)wh + wifi_hdr_len; if (iv[3] & KEY_EXTIV) sec_hdr_len = 8; else sec_hdr_len = 4; } else { sec_hdr_len = 0; } wifi_hdr_len += sec_hdr_len; /* MSDU related stuff LLC - AMSDU subframe header etc */ msdu_llc_len = is_amsdu ? (14 + 8) : 8; mpdu_buf_len = wifi_hdr_len + msdu_llc_len; /* "Decap" header to remove from MSDU buffer */ decap_hdr_pull_bytes = 14; /* Allocate a new nbuf for holding the 802.11 header retrieved from the * status of the now decapped first msdu. Leave enough headroom for * accomodating any radio-tap /prism like PHY header */ mpdu_buf = qdf_nbuf_alloc(soc->osdev, MAX_MONITOR_HEADER + mpdu_buf_len, MAX_MONITOR_HEADER, 4, FALSE); if (!mpdu_buf) goto mpdu_stitch_done; /* Copy the MPDU related header and enc headers into the first buffer * - Note that there can be a 2 byte pad between heaader and enc header */ prev_buf = mpdu_buf; dest = qdf_nbuf_put_tail(prev_buf, wifi_hdr_len); if (!dest) goto mpdu_stitch_fail; qdf_mem_copy(dest, hdr_desc, wifi_hdr_len); hdr_desc += wifi_hdr_len; #if 0 dest = qdf_nbuf_put_tail(prev_buf, sec_hdr_len); adf_os_mem_copy(dest, hdr_desc, sec_hdr_len); hdr_desc += sec_hdr_len; #endif /* The first LLC len is copied into the MPDU buffer */ frag_list_sum_len = 0; msdu_orig = head_msdu; is_first_frag = 1; amsdu_pad = 0; while (msdu_orig) { /* TODO: intra AMSDU padding - do we need it ??? */ msdu = msdu_orig; if (is_first_frag) { head_frag_list = msdu; } else { /* Reload the hdr ptr only on non-first MSDUs */ rx_desc = qdf_nbuf_data(msdu_orig); hdr_desc = HAL_RX_DESC_GET_80211_HDR(rx_desc); } /* Copy this buffers MSDU related status into the prev buffer */ if (is_first_frag) { is_first_frag = 0; } /* Update protocol and flow tag for MSDU */ dp_rx_mon_update_protocol_flow_tag(soc, dp_pdev, msdu_orig, rx_desc); dest = qdf_nbuf_put_tail(prev_buf, msdu_llc_len + amsdu_pad); if (!dest) goto mpdu_stitch_fail; dest += amsdu_pad; qdf_mem_copy(dest, hdr_desc, msdu_llc_len); dp_rx_msdus_set_payload(soc, msdu); /* Push the MSDU buffer beyond the decap header */ qdf_nbuf_pull_head(msdu, decap_hdr_pull_bytes); frag_list_sum_len += msdu_llc_len + qdf_nbuf_len(msdu) + amsdu_pad; /* Set up intra-AMSDU pad to be added to start of next buffer - * AMSDU pad is 4 byte pad on AMSDU subframe */ amsdu_pad = (msdu_llc_len + qdf_nbuf_len(msdu)) & 0x3; amsdu_pad = amsdu_pad ? (4 - amsdu_pad) : 0; /* TODO FIXME How do we handle MSDUs that have fraglist - Should * probably iterate all the frags cloning them along the way and * and also updating the prev_buf pointer */ /* Move to the next */ prev_buf = msdu; msdu_orig = qdf_nbuf_next(msdu_orig); } #if 0 /* Add in the trailer section - encryption trailer + FCS */ qdf_nbuf_put_tail(prev_buf, HAL_RX_FCS_LEN); frag_list_sum_len += HAL_RX_FCS_LEN; #endif frag_list_sum_len -= msdu_llc_len; /* TODO: Convert this to suitable adf routines */ qdf_nbuf_append_ext_list(mpdu_buf, head_frag_list, frag_list_sum_len); QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_DEBUG, "%s %d mpdu_buf %pK mpdu_buf->len %u", __func__, __LINE__, mpdu_buf, mpdu_buf->len); mpdu_stitch_done: /* Check if this buffer contains the PPDU end status for TSF */ /* Need revist this code to see where we can get tsf timestamp */ #if 0 /* PPDU end TLV will be retrieved from monitor status ring */ last_mpdu = (*(((u_int32_t *)&rx_desc->attention)) & RX_ATTENTION_0_LAST_MPDU_MASK) >> RX_ATTENTION_0_LAST_MPDU_LSB; if (last_mpdu) rx_status->rs_tstamp.tsf = rx_desc->ppdu_end.tsf_timestamp; #endif return mpdu_buf; mpdu_stitch_fail: if ((mpdu_buf) && (decap_format != HAL_HW_RX_DECAP_FORMAT_RAW)) { QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, "%s mpdu_stitch_fail mpdu_buf %pK", __func__, mpdu_buf); /* Free the head buffer */ qdf_nbuf_free(mpdu_buf); } return NULL; } /** * dp_send_mgmt_packet_to_stack(): send indicataion to upper layers * * @soc: soc handle * @nbuf: Mgmt packet * @pdev: pdev handle * * Return: QDF_STATUS_SUCCESS on success * QDF_STATUS_E_INVAL in error */ #ifdef FEATURE_PERPKT_INFO static inline QDF_STATUS dp_send_mgmt_packet_to_stack(struct dp_soc *soc, qdf_nbuf_t nbuf, struct dp_pdev *pdev) { uint32_t *nbuf_data; struct ieee80211_frame *wh; if (!nbuf) return QDF_STATUS_E_INVAL; /*check if this is not a mgmt packet*/ wh = (struct ieee80211_frame *)qdf_nbuf_data(nbuf); if (((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_MGT) && ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_CTL)) { qdf_nbuf_free(nbuf); return QDF_STATUS_E_INVAL; } nbuf_data = (uint32_t *)qdf_nbuf_push_head(nbuf, 4); if (!nbuf_data) { QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, FL("No headroom")); qdf_nbuf_free(nbuf); return QDF_STATUS_E_INVAL; } *nbuf_data = pdev->ppdu_info.com_info.ppdu_id; dp_wdi_event_handler(WDI_EVENT_RX_MGMT_CTRL, soc, nbuf, HTT_INVALID_PEER, WDI_NO_VAL, pdev->pdev_id); return QDF_STATUS_SUCCESS; } #else static inline QDF_STATUS dp_send_mgmt_packet_to_stack(struct dp_soc *soc, qdf_nbuf_t nbuf, struct dp_pdev *pdev) { return QDF_STATUS_SUCCESS; } #endif /** * dp_rx_extract_radiotap_info(): Extract and populate information in * struct mon_rx_status type * @rx_status: Receive status * @mon_rx_status: Monitor mode status * * Returns: None */ static inline void dp_rx_extract_radiotap_info(struct cdp_mon_status *rx_status, struct mon_rx_status *rx_mon_status) { rx_mon_status->tsft = rx_status->cdp_rs_tstamp.cdp_tsf; rx_mon_status->chan_freq = rx_status->rs_freq; rx_mon_status->chan_num = rx_status->rs_channel; rx_mon_status->chan_flags = rx_status->rs_flags; rx_mon_status->rate = rx_status->rs_datarate; /* TODO: rx_mon_status->ant_signal_db */ /* TODO: rx_mon_status->nr_ant */ rx_mon_status->mcs = rx_status->cdf_rs_rate_mcs; rx_mon_status->is_stbc = rx_status->cdp_rs_stbc; rx_mon_status->sgi = rx_status->cdp_rs_sgi; /* TODO: rx_mon_status->ldpc */ /* TODO: rx_mon_status->beamformed */ /* TODO: rx_mon_status->vht_flags */ /* TODO: rx_mon_status->vht_flag_values1 */ } /* * dp_rx_mon_deliver(): function to deliver packets to stack * @soc: DP soc * @mac_id: MAC ID * @head_msdu: head of msdu list * @tail_msdu: tail of msdu list * * Return: status: 0 - Success, non-zero: Failure */ QDF_STATUS dp_rx_mon_deliver(struct dp_soc *soc, uint32_t mac_id, qdf_nbuf_t head_msdu, qdf_nbuf_t tail_msdu) { struct dp_pdev *pdev = dp_get_pdev_for_mac_id(soc, mac_id); struct cdp_mon_status *rs = &pdev->rx_mon_recv_status; qdf_nbuf_t mon_skb, skb_next; qdf_nbuf_t mon_mpdu = NULL; if (!pdev->monitor_vdev && !pdev->mcopy_mode) goto mon_deliver_fail; /* restitch mon MPDU for delivery via monitor interface */ mon_mpdu = dp_rx_mon_restitch_mpdu_from_msdus(soc, mac_id, head_msdu, tail_msdu, rs); /* monitor vap cannot be present when mcopy is enabled * hence same skb can be consumed */ if (pdev->mcopy_mode) return dp_send_mgmt_packet_to_stack(soc, mon_mpdu, pdev); if (mon_mpdu && pdev->monitor_vdev && pdev->monitor_vdev->osif_vdev && pdev->monitor_vdev->osif_rx_mon) { pdev->ppdu_info.rx_status.ppdu_id = pdev->ppdu_info.com_info.ppdu_id; pdev->ppdu_info.rx_status.device_id = soc->device_id; pdev->ppdu_info.rx_status.chan_noise_floor = pdev->chan_noise_floor; /* * if chan_num is not fetched correctly from ppdu RX TLV, * get it from pdev saved. */ if (pdev->ppdu_info.rx_status.chan_num == 0) pdev->ppdu_info.rx_status.chan_num = pdev->mon_chan_num; if (!qdf_nbuf_update_radiotap(&pdev->ppdu_info.rx_status, mon_mpdu, qdf_nbuf_headroom(mon_mpdu))) { DP_STATS_INC(pdev, dropped.mon_radiotap_update_err, 1); goto mon_deliver_fail; } pdev->monitor_vdev->osif_rx_mon(pdev->monitor_vdev->osif_vdev, mon_mpdu, &pdev->ppdu_info.rx_status); } else { QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_DEBUG, "[%s][%d] mon_mpdu=%pK monitor_vdev %pK osif_vdev %pK" , __func__, __LINE__, mon_mpdu, pdev->monitor_vdev, (pdev->monitor_vdev ? pdev->monitor_vdev->osif_vdev : NULL)); goto mon_deliver_fail; } return QDF_STATUS_SUCCESS; mon_deliver_fail: mon_skb = head_msdu; while (mon_skb) { skb_next = qdf_nbuf_next(mon_skb); QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_DEBUG, "[%s][%d] mon_skb=%pK len %u", __func__, __LINE__, mon_skb, mon_skb->len); qdf_nbuf_free(mon_skb); mon_skb = skb_next; } return QDF_STATUS_E_INVAL; } /** * dp_rx_mon_deliver_non_std() * @soc: core txrx main contex * @mac_id: MAC ID * * This function delivers the radio tap and dummy MSDU * into user layer application for preamble only PPDU. * * Return: QDF_STATUS */ QDF_STATUS dp_rx_mon_deliver_non_std(struct dp_soc *soc, uint32_t mac_id) { struct dp_pdev *pdev = dp_get_pdev_for_mac_id(soc, mac_id); ol_txrx_rx_mon_fp osif_rx_mon; qdf_nbuf_t dummy_msdu; /* Sanity checking */ if ((!pdev->monitor_vdev) || (!pdev->monitor_vdev->osif_rx_mon)) goto mon_deliver_non_std_fail; /* Generate a dummy skb_buff */ osif_rx_mon = pdev->monitor_vdev->osif_rx_mon; dummy_msdu = qdf_nbuf_alloc(soc->osdev, MAX_MONITOR_HEADER, MAX_MONITOR_HEADER, 4, FALSE); if (!dummy_msdu) goto allocate_dummy_msdu_fail; qdf_nbuf_set_pktlen(dummy_msdu, 0); qdf_nbuf_set_next(dummy_msdu, NULL); pdev->ppdu_info.rx_status.ppdu_id = pdev->ppdu_info.com_info.ppdu_id; /* Apply the radio header to this dummy skb */ if (!qdf_nbuf_update_radiotap(&pdev->ppdu_info.rx_status, dummy_msdu, qdf_nbuf_headroom(dummy_msdu))) { DP_STATS_INC(pdev, dropped.mon_radiotap_update_err, 1); qdf_nbuf_free(dummy_msdu); goto mon_deliver_non_std_fail; } /* deliver to the user layer application */ osif_rx_mon(pdev->monitor_vdev->osif_vdev, dummy_msdu, NULL); /* Clear rx_status*/ qdf_mem_zero(&pdev->ppdu_info.rx_status, sizeof(pdev->ppdu_info.rx_status)); pdev->mon_ppdu_status = DP_PPDU_STATUS_START; return QDF_STATUS_SUCCESS; allocate_dummy_msdu_fail: QDF_TRACE_DEBUG_RL(QDF_MODULE_ID_DP, "[%s][%d] mon_skb=%pK ", __func__, __LINE__, dummy_msdu); mon_deliver_non_std_fail: return QDF_STATUS_E_INVAL; } /** * dp_rx_mon_dest_process() - Brain of the Rx processing functionality * Called from the bottom half (tasklet/NET_RX_SOFTIRQ) * @soc: core txrx main contex * @hal_ring: opaque pointer to the HAL Rx Ring, which will be serviced * @quota: No. of units (packets) that can be serviced in one shot. * * This function implements the core of Rx functionality. This is * expected to handle only non-error frames. * * Return: none */ void dp_rx_mon_dest_process(struct dp_soc *soc, uint32_t mac_id, uint32_t quota) { struct dp_pdev *pdev = dp_get_pdev_for_mac_id(soc, mac_id); uint8_t pdev_id; hal_rxdma_desc_t rxdma_dst_ring_desc; hal_soc_handle_t hal_soc; void *mon_dst_srng; union dp_rx_desc_list_elem_t *head = NULL; union dp_rx_desc_list_elem_t *tail = NULL; uint32_t ppdu_id; uint32_t rx_bufs_used; uint32_t mpdu_rx_bufs_used; int mac_for_pdev = dp_get_mac_id_for_mac(soc, mac_id); struct cdp_pdev_mon_stats *rx_mon_stats; mon_dst_srng = dp_rxdma_get_mon_dst_ring(pdev, mac_for_pdev); if (!mon_dst_srng || !hal_srng_initialized(mon_dst_srng)) { QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, "%s %d : HAL Monitor Destination Ring Init Failed -- %pK", __func__, __LINE__, mon_dst_srng); return; } hal_soc = soc->hal_soc; qdf_assert((hal_soc && pdev)); qdf_spin_lock_bh(&pdev->mon_lock); if (qdf_unlikely(hal_srng_access_start(hal_soc, mon_dst_srng))) { QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, "%s %d : HAL Monitor Destination Ring access Failed -- %pK", __func__, __LINE__, mon_dst_srng); return; } pdev_id = pdev->pdev_id; ppdu_id = pdev->ppdu_info.com_info.ppdu_id; rx_bufs_used = 0; rx_mon_stats = &pdev->rx_mon_stats; while (qdf_likely(rxdma_dst_ring_desc = hal_srng_dst_peek(hal_soc, mon_dst_srng))) { qdf_nbuf_t head_msdu, tail_msdu; uint32_t npackets; head_msdu = (qdf_nbuf_t) NULL; tail_msdu = (qdf_nbuf_t) NULL; mpdu_rx_bufs_used = dp_rx_mon_mpdu_pop(soc, mac_id, rxdma_dst_ring_desc, &head_msdu, &tail_msdu, &npackets, &ppdu_id, &head, &tail); rx_bufs_used += mpdu_rx_bufs_used; if (mpdu_rx_bufs_used) pdev->mon_dest_ring_stuck_cnt = 0; else pdev->mon_dest_ring_stuck_cnt++; if (pdev->mon_dest_ring_stuck_cnt > MON_DEST_RING_STUCK_MAX_CNT) { dp_info("destination ring stuck"); dp_info("ppdu_id status=%d dest=%d", pdev->ppdu_info.com_info.ppdu_id, ppdu_id); rx_mon_stats->mon_rx_dest_stuck++; pdev->ppdu_info.com_info.ppdu_id = ppdu_id; continue; } if (ppdu_id != pdev->ppdu_info.com_info.ppdu_id) { rx_mon_stats->stat_ring_ppdu_id_hist[ rx_mon_stats->ppdu_id_hist_idx] = pdev->ppdu_info.com_info.ppdu_id; rx_mon_stats->dest_ring_ppdu_id_hist[ rx_mon_stats->ppdu_id_hist_idx] = ppdu_id; rx_mon_stats->ppdu_id_hist_idx = (rx_mon_stats->ppdu_id_hist_idx + 1) & (MAX_PPDU_ID_HIST - 1); pdev->mon_ppdu_status = DP_PPDU_STATUS_START; qdf_mem_zero(&(pdev->ppdu_info.rx_status), sizeof(pdev->ppdu_info.rx_status)); QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_DEBUG, "%s %d ppdu_id %x != ppdu_info.com_info .ppdu_id %x", __func__, __LINE__, ppdu_id, pdev->ppdu_info.com_info.ppdu_id); break; } if (qdf_likely((head_msdu) && (tail_msdu))) { rx_mon_stats->dest_mpdu_done++; dp_rx_mon_deliver(soc, mac_id, head_msdu, tail_msdu); } rxdma_dst_ring_desc = hal_srng_dst_get_next(hal_soc, mon_dst_srng); } hal_srng_access_end(hal_soc, mon_dst_srng); qdf_spin_unlock_bh(&pdev->mon_lock); if (rx_bufs_used) { rx_mon_stats->dest_ppdu_done++; dp_rx_buffers_replenish(soc, mac_id, dp_rxdma_get_mon_buf_ring(pdev, mac_for_pdev), dp_rx_get_mon_desc_pool(soc, mac_id, pdev_id), rx_bufs_used, &head, &tail); } } #ifndef DISABLE_MON_CONFIG #if !defined(QCA_WIFI_QCA6390) && !defined(QCA_WIFI_QCA6490) /** * dp_rx_pdev_mon_buf_attach() - Allocate the monitor descriptor pool * * @pdev: physical device handle * @mac_id: mac id * * Return: QDF_STATUS */ #define MON_BUF_MIN_ALLOC_ENTRIES 128 static QDF_STATUS dp_rx_pdev_mon_buf_attach(struct dp_pdev *pdev, int mac_id) { uint8_t pdev_id = pdev->pdev_id; struct dp_soc *soc = pdev->soc; struct dp_srng *mon_buf_ring; uint32_t num_entries; struct rx_desc_pool *rx_desc_pool; QDF_STATUS status = QDF_STATUS_SUCCESS; uint8_t mac_for_pdev = dp_get_mac_id_for_mac(soc, mac_id); uint32_t rx_desc_pool_size, replenish_size; mon_buf_ring = &pdev->rxdma_mon_buf_ring[mac_for_pdev]; num_entries = mon_buf_ring->num_entries; rx_desc_pool = &soc->rx_desc_mon[mac_id]; dp_debug("Mon RX Desc Pool[%d] entries=%u", pdev_id, num_entries); rx_desc_pool_size = wlan_cfg_get_dp_soc_rx_sw_desc_weight(soc->wlan_cfg_ctx) * num_entries; status = dp_rx_desc_pool_alloc(soc, mac_id, rx_desc_pool_size, rx_desc_pool); if (!QDF_IS_STATUS_SUCCESS(status)) return status; rx_desc_pool->owner = HAL_RX_BUF_RBM_SW3_BM; replenish_size = ((num_entries - 1) < MON_BUF_MIN_ALLOC_ENTRIES) ? (num_entries - 1) : MON_BUF_MIN_ALLOC_ENTRIES; status = dp_pdev_rx_buffers_attach(soc, mac_id, mon_buf_ring, rx_desc_pool, replenish_size); return status; } static QDF_STATUS dp_rx_pdev_mon_buf_detach(struct dp_pdev *pdev, int mac_id) { struct dp_soc *soc = pdev->soc; struct rx_desc_pool *rx_desc_pool; rx_desc_pool = &soc->rx_desc_mon[mac_id]; if (rx_desc_pool->pool_size != 0) { if (!dp_is_soc_reinit(soc)) dp_rx_desc_nbuf_and_pool_free(soc, mac_id, rx_desc_pool); else dp_rx_desc_nbuf_free(soc, rx_desc_pool); } return QDF_STATUS_SUCCESS; } /** * dp_mon_link_desc_pool_setup(): Allocate and setup link descriptor pool * that will be used by HW for various link * and queue descriptorsand managed by WBM * * @soc: soc handle * @mac_id: mac id * * Return: QDF_STATUS */ static QDF_STATUS dp_mon_link_desc_pool_setup(struct dp_soc *soc, uint32_t mac_id) { struct dp_pdev *dp_pdev = dp_get_pdev_for_mac_id(soc, mac_id); int mac_for_pdev = dp_get_mac_id_for_mac(soc, mac_id); int link_desc_size = hal_get_link_desc_size(soc->hal_soc); int link_desc_align = hal_get_link_desc_align(soc->hal_soc); uint32_t max_alloc_size = wlan_cfg_max_alloc_size(soc->wlan_cfg_ctx); uint32_t total_link_descs, total_mem_size; uint32_t num_link_desc_banks; uint32_t last_bank_size = 0; uint32_t entry_size, num_entries; void *mon_desc_srng; uint32_t num_replenish_buf; struct dp_srng *dp_srng; int i; qdf_dma_addr_t *baseaddr = NULL; dp_srng = &dp_pdev->rxdma_mon_desc_ring[mac_for_pdev]; num_entries = dp_srng->alloc_size/hal_srng_get_entrysize( soc->hal_soc, RXDMA_MONITOR_DESC); /* Round up to power of 2 */ total_link_descs = 1; while (total_link_descs < num_entries) total_link_descs <<= 1; QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_HIGH, "%s: total_link_descs: %u, link_desc_size: %d", __func__, total_link_descs, link_desc_size); total_mem_size = total_link_descs * link_desc_size; total_mem_size += link_desc_align; if (total_mem_size <= max_alloc_size) { num_link_desc_banks = 0; last_bank_size = total_mem_size; } else { num_link_desc_banks = (total_mem_size) / (max_alloc_size - link_desc_align); last_bank_size = total_mem_size % (max_alloc_size - link_desc_align); } QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_WARN, "%s: total_mem_size: %d, num_link_desc_banks: %u", __func__, total_mem_size, num_link_desc_banks); QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_WARN, "%s: max_alloc_size: %d last_bank_size: %d", __func__, max_alloc_size, last_bank_size); for (i = 0; i < num_link_desc_banks; i++) { baseaddr = &dp_pdev->link_desc_banks[mac_for_pdev][i]. base_paddr_unaligned; if (!dp_is_soc_reinit(soc)) { dp_pdev->link_desc_banks[mac_for_pdev][i]. base_vaddr_unaligned = qdf_mem_alloc_consistent(soc->osdev, soc->osdev->dev, max_alloc_size, baseaddr); if (!dp_pdev->link_desc_banks[mac_for_pdev][i]. base_vaddr_unaligned) { QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, "%s: Link desc mem alloc failed", __func__); goto fail; } } dp_pdev->link_desc_banks[mac_for_pdev][i].size = max_alloc_size; dp_pdev->link_desc_banks[mac_for_pdev][i].base_vaddr = (void *)((unsigned long) (dp_pdev->link_desc_banks[mac_for_pdev][i]. base_vaddr_unaligned) + ((unsigned long) (dp_pdev->link_desc_banks[mac_for_pdev][i]. base_vaddr_unaligned) % link_desc_align)); dp_pdev->link_desc_banks[mac_for_pdev][i].base_paddr = (unsigned long) (dp_pdev->link_desc_banks[mac_for_pdev][i]. base_paddr_unaligned) + ((unsigned long) (dp_pdev->link_desc_banks[mac_for_pdev][i].base_vaddr) - (unsigned long) (dp_pdev->link_desc_banks[mac_for_pdev][i]. base_vaddr_unaligned)); } if (last_bank_size) { /* Allocate last bank in case total memory required is not exact * multiple of max_alloc_size */ baseaddr = &dp_pdev->link_desc_banks[mac_for_pdev][i]. base_paddr_unaligned; if (!dp_is_soc_reinit(soc)) { dp_pdev->link_desc_banks[mac_for_pdev][i]. base_vaddr_unaligned = qdf_mem_alloc_consistent(soc->osdev, soc->osdev->dev, last_bank_size, baseaddr); if (!dp_pdev->link_desc_banks[mac_for_pdev][i]. base_vaddr_unaligned) { QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, "%s: alloc fail:mon link desc pool", __func__); goto fail; } } dp_pdev->link_desc_banks[mac_for_pdev][i].size = last_bank_size; dp_pdev->link_desc_banks[mac_for_pdev][i].base_vaddr = (void *)((unsigned long) (dp_pdev->link_desc_banks[mac_for_pdev][i]. base_vaddr_unaligned) + ((unsigned long) (dp_pdev->link_desc_banks[mac_for_pdev][i]. base_vaddr_unaligned) % link_desc_align)); dp_pdev->link_desc_banks[mac_for_pdev][i].base_paddr = (unsigned long) (dp_pdev->link_desc_banks[mac_for_pdev][i]. base_paddr_unaligned) + ((unsigned long) (dp_pdev->link_desc_banks[mac_for_pdev][i].base_vaddr) - (unsigned long) (dp_pdev->link_desc_banks[mac_for_pdev][i]. base_vaddr_unaligned)); } /* Allocate and setup link descriptor idle list for HW internal use */ entry_size = hal_srng_get_entrysize(soc->hal_soc, RXDMA_MONITOR_DESC); total_mem_size = entry_size * total_link_descs; mon_desc_srng = dp_pdev->rxdma_mon_desc_ring[mac_for_pdev].hal_srng; num_replenish_buf = 0; if (total_mem_size <= max_alloc_size) { void *desc; for (i = 0; i < MAX_MON_LINK_DESC_BANKS && dp_pdev->link_desc_banks[mac_for_pdev][i].base_paddr; i++) { uint32_t num_entries = (dp_pdev->link_desc_banks[mac_for_pdev][i].size - (unsigned long) (dp_pdev->link_desc_banks[mac_for_pdev][i].base_vaddr) - (unsigned long) (dp_pdev->link_desc_banks[mac_for_pdev][i]. base_vaddr_unaligned)) / link_desc_size; unsigned long paddr = (unsigned long) (dp_pdev->link_desc_banks[mac_for_pdev][i].base_paddr); unsigned long vaddr = (unsigned long) (dp_pdev->link_desc_banks[mac_for_pdev][i].base_vaddr); hal_srng_access_start_unlocked(soc->hal_soc, mon_desc_srng); while (num_entries && (desc = hal_srng_src_get_next(soc->hal_soc, mon_desc_srng))) { hal_set_link_desc_addr(desc, i, paddr); num_entries--; num_replenish_buf++; paddr += link_desc_size; vaddr += link_desc_size; } hal_srng_access_end_unlocked(soc->hal_soc, mon_desc_srng); } } else { qdf_assert(0); } QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_WARN, "%s: successfully replenished %d buffer", __func__, num_replenish_buf); return QDF_STATUS_SUCCESS; fail: for (i = 0; i < MAX_MON_LINK_DESC_BANKS; i++) { if (dp_pdev->link_desc_banks[mac_for_pdev][i]. base_vaddr_unaligned) { qdf_mem_free_consistent(soc->osdev, soc->osdev->dev, dp_pdev->link_desc_banks[mac_for_pdev][i].size, dp_pdev->link_desc_banks[mac_for_pdev][i]. base_vaddr_unaligned, dp_pdev->link_desc_banks[mac_for_pdev][i]. base_paddr_unaligned, 0); dp_pdev->link_desc_banks[mac_for_pdev][i]. base_vaddr_unaligned = NULL; } } return QDF_STATUS_E_FAILURE; } /* * Free link descriptor pool that was setup HW */ static void dp_mon_link_desc_pool_cleanup(struct dp_soc *soc, uint32_t mac_id) { struct dp_pdev *dp_pdev = dp_get_pdev_for_mac_id(soc, mac_id); int mac_for_pdev = dp_get_mac_id_for_mac(soc, mac_id); int i; for (i = 0; i < MAX_MON_LINK_DESC_BANKS; i++) { if (dp_pdev->link_desc_banks[mac_for_pdev][i]. base_vaddr_unaligned) { qdf_mem_free_consistent(soc->osdev, soc->osdev->dev, dp_pdev->link_desc_banks[mac_for_pdev][i].size, dp_pdev->link_desc_banks[mac_for_pdev][i]. base_vaddr_unaligned, dp_pdev->link_desc_banks[mac_for_pdev][i]. base_paddr_unaligned, 0); dp_pdev->link_desc_banks[mac_for_pdev][i]. base_vaddr_unaligned = NULL; } } } /** * dp_mon_buf_delayed_replenish() - Helper routine to replenish monitor dest buf * @pdev: DP pdev object * * Return: None */ void dp_mon_buf_delayed_replenish(struct dp_pdev *pdev) { struct dp_soc *soc; uint32_t mac_for_pdev; union dp_rx_desc_list_elem_t *tail = NULL; union dp_rx_desc_list_elem_t *desc_list = NULL; uint32_t num_entries; uint32_t mac_id, id; soc = pdev->soc; num_entries = wlan_cfg_get_dma_mon_buf_ring_size(pdev->wlan_cfg_ctx); for (id = 0; id < NUM_RXDMA_RINGS_PER_PDEV; id++) { mac_for_pdev = dp_get_mac_id_for_pdev(id, pdev->pdev_id); /* * Map mac_for_pdev appropriately for both MCL & WIN, * since MCL have multiple mon buf rings and WIN just * has one mon buffer ring, below API helps identify * accurate buffer_ring for both cases * */ mac_id = dp_get_mac_id_for_mac(soc, mac_for_pdev); dp_rx_buffers_replenish(soc, mac_for_pdev, dp_rxdma_get_mon_buf_ring(pdev, mac_id), dp_rx_get_mon_desc_pool(soc, mac_for_pdev, pdev->pdev_id), num_entries, &desc_list, &tail); } } #else static QDF_STATUS dp_mon_link_desc_pool_setup(struct dp_soc *soc, uint32_t mac_id) { return QDF_STATUS_SUCCESS; } static QDF_STATUS dp_rx_pdev_mon_buf_attach(struct dp_pdev *pdev, int mac_id) { return QDF_STATUS_SUCCESS; } static void dp_mon_link_desc_pool_cleanup(struct dp_soc *soc, uint32_t mac_id) { } static QDF_STATUS dp_rx_pdev_mon_buf_detach(struct dp_pdev *pdev, int mac_id) { return QDF_STATUS_SUCCESS; } void dp_mon_buf_delayed_replenish(struct dp_pdev *pdev) {} #endif /** * dp_rx_pdev_mon_cmn_detach() - detach dp rx for monitor mode * @pdev: core txrx pdev context * @mac_id: mac_id for which deinit is to be done * * This function will free DP Rx resources for * monitor mode * * Return: QDF_STATUS_SUCCESS: success * QDF_STATUS_E_RESOURCES: Error return */ static QDF_STATUS dp_rx_pdev_mon_cmn_detach(struct dp_pdev *pdev, int mac_id) { struct dp_soc *soc = pdev->soc; uint8_t pdev_id = pdev->pdev_id; int mac_for_pdev = dp_get_mac_id_for_pdev(mac_id, pdev_id); dp_mon_link_desc_pool_cleanup(soc, mac_for_pdev); dp_rx_pdev_mon_status_detach(pdev, mac_for_pdev); dp_rx_pdev_mon_buf_detach(pdev, mac_for_pdev); return QDF_STATUS_SUCCESS; } /** * dp_rx_pdev_mon_cmn_attach() - attach DP RX for monitor mode * @pdev: core txrx pdev context * @mac_id: mac_id for which init is to be done * * This function Will allocate dp rx resource and * initialize resources for monitor mode. * * Return: QDF_STATUS_SUCCESS: success * QDF_STATUS_E_RESOURCES: Error return */ static QDF_STATUS dp_rx_pdev_mon_cmn_attach(struct dp_pdev *pdev, int mac_id) { struct dp_soc *soc = pdev->soc; uint8_t pdev_id = pdev->pdev_id; int mac_for_pdev = dp_get_mac_id_for_pdev(mac_id, pdev_id); QDF_STATUS status; status = dp_rx_pdev_mon_buf_attach(pdev, mac_for_pdev); if (!QDF_IS_STATUS_SUCCESS(status)) { dp_err("%s: dp_rx_pdev_mon_buf_attach() failed\n", __func__); goto fail; } status = dp_rx_pdev_mon_status_attach(pdev, mac_for_pdev); if (!QDF_IS_STATUS_SUCCESS(status)) { dp_err("%s: dp_rx_pdev_mon_status_attach() failed", __func__); goto mon_buf_detach; } status = dp_mon_link_desc_pool_setup(soc, mac_for_pdev); if (!QDF_IS_STATUS_SUCCESS(status)) { dp_err("%s: dp_mon_link_desc_pool_setup() failed", __func__); goto mon_status_detach; } return status; mon_status_detach: dp_rx_pdev_mon_status_detach(pdev, mac_for_pdev); mon_buf_detach: dp_rx_pdev_mon_buf_detach(pdev, mac_for_pdev); fail: return status; } /** * dp_rx_pdev_mon_attach() - attach DP RX for monitor mode * @pdev: core txrx pdev context * * This function will attach a DP RX for monitor mode instance into * the main device (SOC) context. Will allocate dp rx resource and * initialize resources. * * Return: QDF_STATUS_SUCCESS: success * QDF_STATUS_E_RESOURCES: Error return */ QDF_STATUS dp_rx_pdev_mon_attach(struct dp_pdev *pdev) { QDF_STATUS status; uint8_t pdev_id = pdev->pdev_id; int mac_id; QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_WARN, "%s: pdev attach id=%d", __func__, pdev_id); for (mac_id = 0; mac_id < NUM_RXDMA_RINGS_PER_PDEV; mac_id++) { status = dp_rx_pdev_mon_cmn_attach(pdev, mac_id); if (!QDF_IS_STATUS_SUCCESS(status)) { QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, "%s: dp_rx_pdev_mon_cmn_attach(%d) failed\n", __func__, mac_id); goto fail; } } pdev->mon_last_linkdesc_paddr = 0; pdev->mon_last_buf_cookie = DP_RX_DESC_COOKIE_MAX + 1; qdf_spinlock_create(&pdev->mon_lock); return QDF_STATUS_SUCCESS; fail: for (mac_id = mac_id - 1; mac_id >= 0; mac_id--) dp_rx_pdev_mon_cmn_detach(pdev, mac_id); return status; } QDF_STATUS dp_mon_link_free(struct dp_pdev *pdev) { uint8_t pdev_id = pdev->pdev_id; struct dp_soc *soc = pdev->soc; int mac_id; for (mac_id = 0; mac_id < NUM_RXDMA_RINGS_PER_PDEV; mac_id++) { int mac_for_pdev = dp_get_mac_id_for_pdev(mac_id, pdev_id); dp_mon_link_desc_pool_cleanup(soc, mac_for_pdev); } return QDF_STATUS_SUCCESS; } /** * dp_rx_pdev_mon_detach() - detach dp rx for monitor mode * @pdev: core txrx pdev context * * This function will detach DP RX for monitor mode from * main device context. will free DP Rx resources for * monitor mode * * Return: QDF_STATUS_SUCCESS: success * QDF_STATUS_E_RESOURCES: Error return */ QDF_STATUS dp_rx_pdev_mon_detach(struct dp_pdev *pdev) { uint8_t pdev_id = pdev->pdev_id; int mac_id; qdf_spinlock_destroy(&pdev->mon_lock); for (mac_id = 0; mac_id < NUM_RXDMA_RINGS_PER_PDEV; mac_id++) { int mac_for_pdev = dp_get_mac_id_for_pdev(mac_id, pdev_id); dp_rx_pdev_mon_status_detach(pdev, mac_for_pdev); dp_rx_pdev_mon_buf_detach(pdev, mac_for_pdev); } return QDF_STATUS_SUCCESS; } #else QDF_STATUS dp_rx_pdev_mon_attach(struct dp_pdev *pdev) { return QDF_STATUS_SUCCESS; } QDF_STATUS dp_rx_pdev_mon_detach(struct dp_pdev *pdev) { return QDF_STATUS_SUCCESS; } QDF_STATUS dp_mon_link_free(struct dp_pdev *pdev) { return QDF_STATUS_SUCCESS; } void dp_mon_buf_delayed_replenish(struct dp_pdev *pdev) {} #endif /* DISABLE_MON_CONFIG */