diff --git a/dp/inc/cdp_txrx_misc.h b/dp/inc/cdp_txrx_misc.h index 461130a31e..5c209565a4 100644 --- a/dp/inc/cdp_txrx_misc.h +++ b/dp/inc/cdp_txrx_misc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2020 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 @@ -747,4 +747,71 @@ static inline void cdp_pdev_reset_bundle_require_flag(ol_txrx_soc_handle soc, return soc->ops->misc_ops->pdev_reset_bundle_require_flag( soc, pdev_id); } + +/** + * cdp_txrx_ext_stats_request(): request dp tx and rx extended stats + * @soc: soc handle + * @pdev_id: pdev id + * @req: stats request structure to fill + * + * return: status + */ +static inline QDF_STATUS +cdp_txrx_ext_stats_request(ol_txrx_soc_handle soc, uint8_t pdev_id, + struct cdp_txrx_ext_stats *req) +{ + if (!soc || !soc->ops || !soc->ops->misc_ops || !req) { + QDF_TRACE(QDF_MODULE_ID_CDP, QDF_TRACE_LEVEL_DEBUG, + "%s: Invalid Instance:", __func__); + return QDF_STATUS_E_INVAL; + } + + if (soc->ops->misc_ops->txrx_ext_stats_request) + return soc->ops->misc_ops->txrx_ext_stats_request(soc, pdev_id, + req); + + return QDF_STATUS_SUCCESS; +} + +/** + * cdp_request_rx_hw_stats(): request rx hw stats + * @soc: soc handle + * @vdev_id: vdev id + * + * return: none + */ +static inline void +cdp_request_rx_hw_stats(ol_txrx_soc_handle soc, uint8_t vdev_id) +{ + if (!soc || !soc->ops || !soc->ops->misc_ops) { + QDF_TRACE(QDF_MODULE_ID_CDP, QDF_TRACE_LEVEL_DEBUG, + "%s: Invalid Instance:", __func__); + return; + } + + if (soc->ops->misc_ops->request_rx_hw_stats) + soc->ops->misc_ops->request_rx_hw_stats(soc, vdev_id); +} + +/** + * cdp_wait_for_ext_rx_stats(): wait for reo command status for stats + * @soc: soc handle + * + * return: status + */ +static inline QDF_STATUS +cdp_wait_for_ext_rx_stats(ol_txrx_soc_handle soc) +{ + if (!soc || !soc->ops || !soc->ops->misc_ops) { + QDF_TRACE(QDF_MODULE_ID_CDP, QDF_TRACE_LEVEL_DEBUG, + "%s: Invalid Instance:", __func__); + return QDF_STATUS_E_INVAL; + } + + if (soc->ops->misc_ops->wait_for_ext_rx_stats) + return soc->ops->misc_ops->wait_for_ext_rx_stats(soc); + + return QDF_STATUS_SUCCESS; +} + #endif /* _CDP_TXRX_MISC_H_ */ diff --git a/dp/inc/cdp_txrx_mob_def.h b/dp/inc/cdp_txrx_mob_def.h index 2550bb82e8..e91753bb72 100644 --- a/dp/inc/cdp_txrx_mob_def.h +++ b/dp/inc/cdp_txrx_mob_def.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2020 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 @@ -471,4 +471,22 @@ struct ol_rx_inv_peer_params { uint8_t ta[QDF_MAC_ADDR_SIZE]; }; +/** + * cdp_txrx_ext_stats: dp extended stats + * tx_msdu_enqueue: tx msdu queued to hw + * tx_msdu_overflow: tx msdu overflow + * rx_mpdu_received: rx mpdu processed by hw + * rx_mpdu_delivered: rx mpdu received from hw + * rx_mpdu_error: rx mpdu error count + * rx_mpdu_missed: rx mpdu missed by hw + */ +struct cdp_txrx_ext_stats { + uint32_t tx_msdu_enqueue; + uint32_t tx_msdu_overflow; + uint32_t rx_mpdu_received; + uint32_t rx_mpdu_delivered; + uint32_t rx_mpdu_error; + uint32_t rx_mpdu_missed; +}; + #endif /* __CDP_TXRX_MOB_DEF_H */ diff --git a/dp/inc/cdp_txrx_ops.h b/dp/inc/cdp_txrx_ops.h index 372d5dcde4..22346cc58b 100644 --- a/dp/inc/cdp_txrx_ops.h +++ b/dp/inc/cdp_txrx_ops.h @@ -1238,6 +1238,11 @@ struct cdp_misc_ops { uint32_t low_th); void (*pdev_reset_bundle_require_flag)(struct cdp_soc_t *soc_hdl, uint8_t pdev_id); + QDF_STATUS (*txrx_ext_stats_request)(struct cdp_soc_t *soc_hdl, + uint8_t pdev_id, + struct cdp_txrx_ext_stats *req); + void (*request_rx_hw_stats)(struct cdp_soc_t *soc_hdl, uint8_t vdev_id); + QDF_STATUS (*wait_for_ext_rx_stats)(struct cdp_soc_t *soc_hdl); }; /** diff --git a/dp/wifi3.0/dp_main.c b/dp/wifi3.0/dp_main.c index 769c4f9546..8cb11ade5c 100644 --- a/dp/wifi3.0/dp_main.c +++ b/dp/wifi3.0/dp_main.c @@ -2807,6 +2807,17 @@ static void dp_enable_verbose_debug(struct dp_soc *soc) } #endif +#ifdef WLAN_FEATURE_STATS_EXT +static inline void dp_create_ext_stats_event(struct dp_soc *soc) +{ + qdf_event_create(&soc->rx_hw_stats_event); +} +#else +static inline void dp_create_ext_stats_event(struct dp_soc *soc) +{ +} +#endif + /* * dp_soc_cmn_setup() - Common SoC level initializion * @soc: Datapath SOC handle @@ -3055,6 +3066,7 @@ static int dp_soc_cmn_setup(struct dp_soc *soc) wlan_cfg_get_defrag_timeout_check(soc_cfg_ctx); qdf_spinlock_create(&soc->rx.defrag.defrag_lock); + dp_create_ext_stats_event(soc); out: /* * set the fragment destination ring @@ -5302,6 +5314,26 @@ static inline void dp_peer_rx_bufq_resources_init(struct dp_peer *peer) } #endif +#ifdef WLAN_FEATURE_STATS_EXT +/* + * dp_set_ignore_reo_status_cb() - set ignore reo status cb flag + * @soc: dp soc handle + * @flag: flag to set or reset + * + * Return: None + */ +static inline void dp_set_ignore_reo_status_cb(struct dp_soc *soc, + bool flag) +{ + soc->ignore_reo_status_cb = flag; +} +#else +static inline void dp_set_ignore_reo_status_cb(struct dp_soc *soc, + bool flag) +{ +} +#endif + /* * dp_peer_create_wifi3() - attach txrx peer * @soc_hdl: Datapath soc handle @@ -5458,6 +5490,12 @@ static void *dp_peer_create_wifi3(struct cdp_soc_t *soc_hdl, uint8_t vdev_id, vdev->vap_self_peer = peer; } + if (wlan_op_mode_sta == vdev->opmode && + qdf_mem_cmp(peer->mac_addr.raw, vdev->mac_addr.raw, + QDF_MAC_ADDR_SIZE) != 0) { + dp_set_ignore_reo_status_cb(soc, false); + } + for (i = 0; i < DP_MAX_TIDS; i++) qdf_spinlock_create(&peer->rx_tid[i].tid_lock); @@ -6244,6 +6282,12 @@ static QDF_STATUS dp_peer_delete_wifi3(struct cdp_soc_t *soc, uint8_t vdev_id, qdf_spinlock_destroy(&peer->peer_info_lock); dp_peer_multipass_list_remove(peer); + if (wlan_op_mode_sta == peer->vdev->opmode && + qdf_mem_cmp(peer->mac_addr.raw, peer->vdev->mac_addr.raw, + QDF_MAC_ADDR_SIZE) != 0) { + dp_set_ignore_reo_status_cb(peer->vdev->pdev->soc, true); + } + /* * Remove the reference added during peer_attach. * The peer will still be left allocated until the @@ -6669,6 +6713,19 @@ QDF_STATUS dp_pdev_configure_monitor_rings(struct dp_pdev *pdev) 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->pdev_id); + /* + * If two back to back HTT msg sending happened in + * short time, the second HTT msg source SRNG HP + * writing has chance to fail, this has been confirmed + * by HST HW. + * for monitor mode, here is the last HTT msg for sending. + * if the 2nd HTT msg for monitor status ring sending failed, + * HW won't provide anything into 2nd monitor status ring. + * as a WAR, add some delay before 2nd HTT msg start sending, + * > 2us is required per HST HW, delay 100 us for safe. + */ + if (mac_id) + qdf_udelay(100); htt_h2t_rx_ring_cfg(soc->htt_handle, mac_for_pdev, pdev->rxdma_mon_status_ring[mac_id].hal_srng, @@ -9983,6 +10040,125 @@ dp_txrx_post_data_stall_event(struct cdp_soc_t *soc_hdl, } #endif /* WLAN_SUPPORT_DATA_STALL */ +#ifdef WLAN_FEATURE_STATS_EXT +/* rx hw stats event wait timeout in ms */ +#define DP_REO_STATUS_STATS_TIMEOUT 1000 +/** + * dp_txrx_ext_stats_request - request dp txrx extended stats request + * @soc_hdl: soc handle + * @pdev_id: pdev id + * @req: stats request + * + * Return: QDF_STATUS + */ +static QDF_STATUS +dp_txrx_ext_stats_request(struct cdp_soc_t *soc_hdl, uint8_t pdev_id, + struct cdp_txrx_ext_stats *req) +{ + struct dp_soc *soc = (struct dp_soc *)soc_hdl; + struct dp_pdev *pdev = dp_get_pdev_from_soc_pdev_id_wifi3(soc, pdev_id); + + if (!pdev) { + dp_err("pdev is null"); + return QDF_STATUS_E_INVAL; + } + + dp_aggregate_pdev_stats(pdev); + + req->tx_msdu_enqueue = pdev->stats.tx_i.processed.num; + req->tx_msdu_overflow = pdev->stats.tx_i.dropped.ring_full; + req->rx_mpdu_received = soc->ext_stats.rx_mpdu_received; + req->rx_mpdu_delivered = soc->ext_stats.rx_mpdu_received; + req->rx_mpdu_missed = soc->ext_stats.rx_mpdu_missed; + req->rx_mpdu_error = soc->stats.rx.err_ring_pkts - + soc->stats.rx.rx_frags; + + return QDF_STATUS_SUCCESS; +} + +/** + * dp_rx_hw_stats_cb - request rx hw stats response callback + * @soc: soc handle + * @cb_ctxt: callback context + * @reo_status: reo command response status + * + * Return: None + */ +static void dp_rx_hw_stats_cb(struct dp_soc *soc, void *cb_ctxt, + union hal_reo_status *reo_status) +{ + struct dp_rx_tid *rx_tid = (struct dp_rx_tid *)cb_ctxt; + struct hal_reo_queue_status *queue_status = &reo_status->queue_status; + + if (soc->ignore_reo_status_cb) { + qdf_event_set(&soc->rx_hw_stats_event); + return; + } + + if (queue_status->header.status != HAL_REO_CMD_SUCCESS) { + dp_info("REO stats failure %d for TID %d", + queue_status->header.status, rx_tid->tid); + return; + } + + soc->ext_stats.rx_mpdu_received += queue_status->mpdu_frms_cnt; + soc->ext_stats.rx_mpdu_missed += queue_status->late_recv_mpdu_cnt; + + if (rx_tid->tid == (DP_MAX_TIDS - 1)) + qdf_event_set(&soc->rx_hw_stats_event); +} + +/** + * dp_request_rx_hw_stats - request rx hardware stats + * @soc_hdl: soc handle + * @vdev_id: vdev id + * + * Return: None + */ +static void +dp_request_rx_hw_stats(struct cdp_soc_t *soc_hdl, uint8_t vdev_id) +{ + struct dp_soc *soc = (struct dp_soc *)soc_hdl; + struct dp_vdev *vdev = dp_get_vdev_from_soc_vdev_id_wifi3(soc, vdev_id); + struct dp_peer *peer; + + if (!vdev) { + dp_err("vdev is null"); + qdf_event_set(&soc->rx_hw_stats_event); + return; + } + + peer = vdev->vap_bss_peer; + + if (!peer || peer->delete_in_progress) { + dp_err("Peer deletion in progress"); + qdf_event_set(&soc->rx_hw_stats_event); + return; + } + + qdf_event_reset(&soc->rx_hw_stats_event); + dp_peer_rxtid_stats(peer, dp_rx_hw_stats_cb, NULL); +} + +/** + * dp_wait_for_ext_rx_stats - wait for rx reo status for rx stats + * @soc_hdl: cdp opaque soc handle + * + * Return: status + */ +static QDF_STATUS +dp_wait_for_ext_rx_stats(struct cdp_soc_t *soc_hdl) +{ + struct dp_soc *soc = (struct dp_soc *)soc_hdl; + QDF_STATUS status; + + status = qdf_wait_single_event(&soc->rx_hw_stats_event, + DP_REO_STATUS_STATS_TIMEOUT); + + return status; +} +#endif /* WLAN_FEATURE_STATS_EXT */ + #ifdef DP_PEER_EXTENDED_API static struct cdp_misc_ops dp_ops_misc = { #ifdef FEATURE_WLAN_TDLS @@ -10002,6 +10178,12 @@ static struct cdp_misc_ops dp_ops_misc = { .txrx_data_stall_cb_deregister = dp_deregister_data_stall_detect_cb, .txrx_post_data_stall_event = dp_txrx_post_data_stall_event, #endif + +#ifdef WLAN_FEATURE_STATS_EXT + .txrx_ext_stats_request = dp_txrx_ext_stats_request, + .request_rx_hw_stats = dp_request_rx_hw_stats, + .wait_for_ext_rx_stats = dp_wait_for_ext_rx_stats, +#endif }; #endif diff --git a/dp/wifi3.0/dp_rx.c b/dp/wifi3.0/dp_rx.c index 5af69964dc..b65e14bf7f 100644 --- a/dp/wifi3.0/dp_rx.c +++ b/dp/wifi3.0/dp_rx.c @@ -2237,9 +2237,20 @@ done: dp_peer_unref_del_find_by_id(peer); } - if (deliver_list_head && peer) - dp_rx_deliver_to_stack(vdev, peer, deliver_list_head, - deliver_list_tail); + if (qdf_likely(deliver_list_head)) { + if (qdf_likely(peer)) + dp_rx_deliver_to_stack(vdev, peer, deliver_list_head, + deliver_list_tail); + else { + nbuf = deliver_list_head; + while (nbuf) { + next = nbuf->next; + nbuf->next = NULL; + dp_rx_deliver_to_stack_no_peer(soc, nbuf); + nbuf = next; + } + } + } if (dp_rx_enable_eol_data_check(soc) && rx_bufs_used) { if (quota) { diff --git a/dp/wifi3.0/dp_rx_err.c b/dp/wifi3.0/dp_rx_err.c index 4e5e4a5e9e..d3d91ba318 100644 --- a/dp/wifi3.0/dp_rx_err.c +++ b/dp/wifi3.0/dp_rx_err.c @@ -1103,19 +1103,19 @@ void dp_rx_process_mic_error(struct dp_soc *soc, qdf_nbuf_t nbuf, return; if (!peer) { - dp_err_rl("peer not found"); + dp_info_rl("peer not found"); goto fail; } vdev = peer->vdev; if (!vdev) { - dp_err_rl("VDEV not found"); + dp_info_rl("VDEV not found"); goto fail; } pdev = vdev->pdev; if (!pdev) { - dp_err_rl("PDEV not found"); + dp_info_rl("PDEV not found"); goto fail; } @@ -1519,9 +1519,9 @@ done: peer = dp_peer_find_by_id(soc, peer_id); if (!peer) - dp_err_rl("peer is null! peer_id %u err_src %u err_rsn %u", - peer_id, wbm_err_info.wbm_err_src, - wbm_err_info.reo_psh_rsn); + dp_info_rl("peer is null peer_id%u err_src%u err_rsn%u", + peer_id, wbm_err_info.wbm_err_src, + wbm_err_info.reo_psh_rsn); /* Set queue_mapping in nbuf to 0 */ dp_set_rx_queue(nbuf, 0); @@ -1586,8 +1586,8 @@ done: break; default: - dp_err_rl("Got pkt with REO ERROR: %d", - wbm_err_info.reo_err_code); + dp_info_rl("Got pkt with REO ERROR: %d", + wbm_err_info.reo_err_code); break; } } diff --git a/dp/wifi3.0/dp_types.h b/dp/wifi3.0/dp_types.h index 9ec74261b3..37c3e07cbb 100644 --- a/dp/wifi3.0/dp_types.h +++ b/dp/wifi3.0/dp_types.h @@ -1163,6 +1163,17 @@ struct dp_soc { qdf_atomic_t ipa_pipes_enabled; #endif +#ifdef WLAN_FEATURE_STATS_EXT + struct { + uint32_t rx_mpdu_received; + uint32_t rx_mpdu_missed; + } ext_stats; + qdf_event_t rx_hw_stats_event; + + /* Ignore reo command queue status during peer delete */ + bool ignore_reo_status_cb; +#endif + /* Smart monitor capability for HKv2 */ uint8_t hw_nac_monitor_support; /* Flag to indicate if HTT v2 is enabled*/ diff --git a/hal/wifi3.0/hal_api.h b/hal/wifi3.0/hal_api.h index 46fb142f34..616bab4b21 100644 --- a/hal/wifi3.0/hal_api.h +++ b/hal/wifi3.0/hal_api.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2020 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 @@ -32,7 +32,7 @@ #define SHADOW_REGISTER_END_ADDRESS_OFFSET \ ((SHADOW_REGISTER_START_ADDRESS_OFFSET) + (4 * (MAX_SHADOW_REGISTERS))) #define SHADOW_REGISTER(x) ((SHADOW_REGISTER_START_ADDRESS_OFFSET) + (4 * (x))) -#elif defined(QCA_WIFI_QCA6290) +#elif defined(QCA_WIFI_QCA6290) || defined(QCA_WIFI_QCN9000) #define SHADOW_REGISTER_START_ADDRESS_OFFSET 0x00003024 #define SHADOW_REGISTER_END_ADDRESS_OFFSET \ ((SHADOW_REGISTER_START_ADDRESS_OFFSET) + (4 * (MAX_SHADOW_REGISTERS))) diff --git a/umac/dfs/core/src/dfs.h b/umac/dfs/core/src/dfs.h index 1aa1bde92c..24f5a84c95 100644 --- a/umac/dfs/core/src/dfs.h +++ b/umac/dfs/core/src/dfs.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2016-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2013, 2016-2020 The Linux Foundation. All rights reserved. * Copyright (c) 2005-2006 Atheros Communications, Inc. * * Permission to use, copy, modify, and/or distribute this software for any @@ -197,6 +197,12 @@ #define WLAN_DFS_DATA_STRUCT_LOCK_DESTROY(_dfs) \ qdf_spinlock_destroy(&(_dfs)->dfs_data_struct_lock) +/* Wrappers to call MLME radar during mode switch lock. */ +#define DFS_RADAR_MODE_SWITCH_LOCK(_dfs) \ + dfs_mlme_acquire_radar_mode_switch_lock((_dfs)->dfs_pdev_obj) +#define DFS_RADAR_MODE_SWITCH_UNLOCK(_dfs) \ + dfs_mlme_release_radar_mode_switch_lock((_dfs)->dfs_pdev_obj) + /* Mask for time stamp from descriptor */ #define DFS_TSMASK 0xFFFFFFFF /* Shift for time stamp from descriptor */ @@ -897,6 +903,20 @@ struct dfs_event_log { #define FREQ_OFFSET_BOUNDARY_FOR_80MHZ 40 +/** + * struct dfs_mode_switch_defer_params - Parameters storing DFS information + * before defer, as part of HW mode switch. + * + * @radar_params: Deferred radar parameters. + * @is_cac_completed: Boolean representing CAC completion event. + * @is_radar_detected: Boolean representing radar event. + */ +struct dfs_mode_switch_defer_params { + struct radar_found_info *radar_params; + bool is_cac_completed; + bool is_radar_detected; +}; + /** * struct wlan_dfs - The main dfs structure. * @dfs_debug_mask: Current debug bitmask. @@ -1061,6 +1081,9 @@ struct dfs_event_log { * be blocked and this variable should be * false so that HW pulses and synthetic * pulses do not get mixed up. + * defer timer running. + * @dfs_defer_params: DFS deferred event parameters (allocated + * only for the duration of defer alone). */ struct wlan_dfs { uint32_t dfs_debug_mask; @@ -1224,6 +1247,7 @@ struct wlan_dfs { #if defined(WLAN_DFS_PARTIAL_OFFLOAD) && defined(WLAN_DFS_SYNTHETIC_RADAR) bool dfs_allow_hw_pulses; #endif + struct dfs_mode_switch_defer_params dfs_defer_params; }; #if defined(QCA_SUPPORT_AGILE_DFS) || defined(ATH_SUPPORT_ZERO_CAC_DFS) @@ -1254,6 +1278,7 @@ struct wlan_dfs_priv { * @dfs_precac_timer: agile precac timer * @dfs_precac_timer_running: precac timer running flag * @ocac_status: Off channel CAC complete status + * @dfs_nol_ctx: dfs NOL data for all radios. */ struct dfs_soc_priv_obj { struct wlan_objmgr_psoc *psoc; @@ -1268,6 +1293,7 @@ struct dfs_soc_priv_obj { bool precac_state_started; bool ocac_status; #endif + struct dfsreq_nolinfo *dfs_psoc_nolinfo; }; /** @@ -2725,4 +2751,81 @@ int dfs_reinit_timers(struct wlan_dfs *dfs); * Return: None. */ void dfs_reset_dfs_prevchan(struct wlan_dfs *dfs); + +/** + * dfs_init_tmp_psoc_nol() - Init temporary psoc NOL structure. + * @dfs: Pointer to wlan_dfs object. + * @num_radios: Num of radios in the PSOC. + * + * Return: void. + */ +void dfs_init_tmp_psoc_nol(struct wlan_dfs *dfs, uint8_t num_radios); + +/** + * dfs_deinit_tmp_psoc_nol() - De-init temporary psoc NOL structure. + * @dfs: Pointer to wlan_dfs object. + * + * Return: void. + */ +void dfs_deinit_tmp_psoc_nol(struct wlan_dfs *dfs); + +/** + * dfs_save_dfs_nol_in_psoc() - Save NOL data of given pdev. + * @dfs: Pointer to wlan_dfs object. + * @pdev_id: The pdev ID which will have the NOL data. + * @low_5ghz_freq: The low 5GHz frequency value of the target pdev id. + * @high_5ghz_freq: The high 5GHz frequency value of the target pdev id. + * + * Based on the frequency of the NOL channel, copy it to the target pdev_id + * structure in psoc. + * + * Return: void. + */ +void dfs_save_dfs_nol_in_psoc(struct wlan_dfs *dfs, + uint8_t pdev_id, + uint16_t low_5ghz_freq, + uint16_t high_5ghz_freq); + +/** + * dfs_reinit_nol_from_psoc_copy() - Reinit saved NOL data to corresponding + * DFS object. + * @dfs: Pointer to wlan_dfs object. + * @pdev_id: pdev_id of the given dfs object. + * + * Return: void. + */ +void dfs_reinit_nol_from_psoc_copy(struct wlan_dfs *dfs, uint8_t pdev_id); + +/** + * dfs_is_hw_mode_switch_in_progress() - Check if HW mode switch in progress. + * @dfs: Pointer to wlan_dfs object. + * + * Return: True if mode switch is in progress, else false. + */ +bool dfs_is_hw_mode_switch_in_progress(struct wlan_dfs *dfs); + +/** + * dfs_start_mode_switch_defer_timer() - start mode switch defer timer. + * @dfs: Pointer to wlan_dfs object. + * + * Return: void. + */ +void dfs_start_mode_switch_defer_timer(struct wlan_dfs *dfs); + +/** + * dfs_complete_deferred_tasks() - Process mode switch completion event and + * handle deffered tasks. + * @dfs: Pointer to wlan_dfs object. + * + * Return: void. + */ +void dfs_complete_deferred_tasks(struct wlan_dfs *dfs); + +/** + * dfs_process_cac_completion() - Process DFS CAC completion event. + * @dfs: Pointer to wlan_dfs object. + * + * Return: void. + */ +void dfs_process_cac_completion(struct wlan_dfs *dfs); #endif /* _DFS_H_ */ diff --git a/umac/dfs/core/src/dfs_zero_cac.h b/umac/dfs/core/src/dfs_zero_cac.h index 405916fc8f..dd718e97f4 100644 --- a/umac/dfs/core/src/dfs_zero_cac.h +++ b/umac/dfs/core/src/dfs_zero_cac.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved. * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting * All rights reserved. * @@ -823,6 +823,32 @@ dfs_get_precac_chan_state_for_freq(struct wlan_dfs *dfs, */ void dfs_zero_cac_reset(struct wlan_dfs *dfs); +/** + * dfs_reinit_precac_lists() - Reinit DFS preCAC lists. + * @src_dfs: Source DFS from which the preCAC list is copied. + * @dest_dfs: Destination DFS to which the preCAC list is copied. + * @low_5g_freq: Low 5G frequency value of the destination DFS. + * @high_5g_freq: High 5G frequency value of the destination DFS. + * + * Copy all the preCAC list entries from the source DFS to the destination DFS + * which fall within the frequency range of low_5g_freq and high_5g_freq. + * + * Return: None (void). + */ +#if defined(WLAN_DFS_PARTIAL_OFFLOAD) && !defined(QCA_MCL_DFS_SUPPORT) +void dfs_reinit_precac_lists(struct wlan_dfs *src_dfs, + struct wlan_dfs *dest_dfs, + uint16_t low_5g_freq, + uint16_t high_5g_freq); +#else +static inline void dfs_reinit_precac_lists(struct wlan_dfs *src_dfs, + struct wlan_dfs *dest_dfs, + uint16_t low_5g_freq, + uint16_t high_5g_freq) +{ +} +#endif + /** * dfs_is_precac_done_on_ht20_40_80_chan() - Is precac done on a * VHT20/40/80 channel. diff --git a/umac/dfs/core/src/misc/dfs.c b/umac/dfs/core/src/misc/dfs.c index a1abe7ff93..e05d6be2c5 100644 --- a/umac/dfs/core/src/misc/dfs.c +++ b/umac/dfs/core/src/misc/dfs.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved. * Copyright (c) 2002-2006, Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any @@ -31,6 +31,7 @@ #include "../dfs_full_offload.h" #include #include "wlan_dfs_utils_api.h" +#include "../dfs_process_radar_found_ind.h" #include "../dfs_partial_offload_radar.h" /* Disable NOL in FW. */ @@ -917,3 +918,26 @@ void dfs_reset_dfs_prevchan(struct wlan_dfs *dfs) { qdf_mem_zero(dfs->dfs_prevchan, sizeof(struct dfs_channel)); } + +bool dfs_is_hw_mode_switch_in_progress(struct wlan_dfs *dfs) +{ + return lmac_dfs_is_hw_mode_switch_in_progress(dfs->dfs_pdev_obj); +} + +void dfs_complete_deferred_tasks(struct wlan_dfs *dfs) +{ + if (dfs->dfs_defer_params.is_radar_detected) { + /* Handle radar event that was deferred and free the temporary + * storage of the radar event parameters. + */ + dfs_process_radar_ind(dfs, dfs->dfs_defer_params.radar_params); + qdf_mem_free(dfs->dfs_defer_params.radar_params); + dfs->dfs_defer_params.is_radar_detected = false; + } else if (dfs->dfs_defer_params.is_cac_completed) { + /* Handle CAC completion event that was deferred for HW mode + * switch. + */ + dfs_process_cac_completion(dfs); + dfs->dfs_defer_params.is_cac_completed = false; + } +} diff --git a/umac/dfs/core/src/misc/dfs_cac.c b/umac/dfs/core/src/misc/dfs_cac.c index 7f35ea90ce..0bbeb00858 100644 --- a/umac/dfs/core/src/misc/dfs_cac.c +++ b/umac/dfs/core/src/misc/dfs_cac.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved. * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting * All rights reserved. * @@ -122,24 +122,16 @@ static void dfs_clear_cac_started_chan(struct wlan_dfs *dfs) sizeof(dfs->dfs_cac_started_chan)); } -/** - * dfs_cac_timeout() - DFS cactimeout function. - * - * Sets dfs_cac_timer_running to 0 and dfs_cac_valid_timer. - */ -#ifdef CONFIG_CHAN_FREQ_API -static os_timer_func(dfs_cac_timeout) +void dfs_process_cac_completion(struct wlan_dfs *dfs) { - struct wlan_dfs *dfs = NULL; enum phy_ch_width ch_width = CH_WIDTH_INVALID; uint16_t primary_chan_freq = 0, secondary_chan_freq = 0; struct dfs_channel *dfs_curchan; - OS_GET_TIMER_ARG(dfs, struct wlan_dfs *); dfs->dfs_cac_timer_running = 0; dfs_curchan = dfs->dfs_curchan; - dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, "cac expired, chan %d curr time %d", + dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, "cac expired, chan %d cur time %d", dfs->dfs_curchan->dfs_ch_freq, (qdf_system_ticks_to_msecs(qdf_system_ticks()) / 1000)); @@ -154,12 +146,13 @@ static os_timer_func(dfs_cac_timeout) dfs_curchan->dfs_ch_mhz_freq_seg2, dfs_curchan->dfs_ch_flags); dfs_debug(dfs, WLAN_DEBUG_DFS, - "CAC timer on channel %u (%u MHz) stopped due to radar", + "CAC timer on chan %u (%u MHz) stopped due to radar", dfs_curchan->dfs_ch_ieee, dfs_curchan->dfs_ch_freq); } else { dfs_debug(dfs, WLAN_DEBUG_DFS, - "CAC timer on channel %u (%u MHz) expired; no radar detected", + "CAC timer on channel %u (%u MHz) expired;" + "no radar detected", dfs_curchan->dfs_ch_ieee, dfs_curchan->dfs_ch_freq); @@ -195,6 +188,24 @@ static os_timer_func(dfs_cac_timeout) dfs->dfs_defer_precac_channel_change = 0; } } + +/** + * dfs_cac_timeout() - DFS cactimeout function. + * + * Sets dfs_cac_timer_running to 0 and dfs_cac_valid_timer. + */ +#ifdef CONFIG_CHAN_FREQ_API +static os_timer_func(dfs_cac_timeout) +{ + struct wlan_dfs *dfs = NULL; + + OS_GET_TIMER_ARG(dfs, struct wlan_dfs *); + + if (dfs_is_hw_mode_switch_in_progress(dfs)) + dfs->dfs_defer_params.is_cac_completed = true; + else + dfs_process_cac_completion(dfs); +} #else #ifdef CONFIG_CHAN_NUM_API static os_timer_func(dfs_cac_timeout) diff --git a/umac/dfs/core/src/misc/dfs_nol.c b/umac/dfs/core/src/misc/dfs_nol.c index 31f096c299..eda749ad4c 100644 --- a/umac/dfs/core/src/misc/dfs_nol.c +++ b/umac/dfs/core/src/misc/dfs_nol.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved. * Copyright (c) 2002-2010, Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any @@ -899,3 +899,101 @@ void dfs_remove_spoof_channel_from_nol(struct wlan_dfs *dfs) } #endif #endif + +void dfs_init_tmp_psoc_nol(struct wlan_dfs *dfs, uint8_t num_radios) +{ + struct dfs_soc_priv_obj *dfs_soc_obj = dfs->dfs_soc_obj; + + if (WLAN_UMAC_MAX_PDEVS < num_radios) { + dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, + "num_radios (%u) exceeds limit", num_radios); + return; + } + + /* Allocate the temporary psoc NOL copy structure for the number + * of radios provided. + */ + dfs_soc_obj->dfs_psoc_nolinfo = + qdf_mem_malloc(sizeof(struct dfsreq_nolinfo) * num_radios); +} + +void dfs_deinit_tmp_psoc_nol(struct wlan_dfs *dfs) +{ + struct dfs_soc_priv_obj *dfs_soc_obj = dfs->dfs_soc_obj; + + if (!dfs_soc_obj->dfs_psoc_nolinfo) + return; + + qdf_mem_free(dfs_soc_obj->dfs_psoc_nolinfo); + dfs_soc_obj->dfs_psoc_nolinfo = NULL; +} + +void dfs_save_dfs_nol_in_psoc(struct wlan_dfs *dfs, + uint8_t pdev_id, + uint16_t low_5ghz_freq, + uint16_t high_5ghz_freq) +{ + struct dfs_soc_priv_obj *dfs_soc_obj = dfs->dfs_soc_obj; + struct dfsreq_nolinfo tmp_nolinfo, *nolinfo; + uint32_t i, num_chans = 0; + uint16_t tmp_freq; + + if (!dfs->dfs_nol_count) + return; + + if (!dfs_soc_obj->dfs_psoc_nolinfo) + return; + + nolinfo = &dfs_soc_obj->dfs_psoc_nolinfo[pdev_id]; + /* Fetch the NOL entries for the DFS object. */ + dfs_getnol(dfs, &tmp_nolinfo); + + /* nolinfo might already have some data. Do not overwrite it */ + num_chans = nolinfo->dfs_ch_nchans; + for (i = 0; i < tmp_nolinfo.dfs_ch_nchans; i++) { + tmp_freq = tmp_nolinfo.dfs_nol[i].nol_freq; + + /* Add to nolinfo only if within the pdev's frequency range. */ + if ((low_5ghz_freq < tmp_freq) && (high_5ghz_freq > tmp_freq)) { + /* Figure out the completed duration of each NOL. */ + uint32_t nol_completed_ms = + qdf_system_ticks_to_msecs(qdf_system_ticks() - + tmp_nolinfo.dfs_nol[i].nol_start_ticks); + + nolinfo->dfs_nol[num_chans] = tmp_nolinfo.dfs_nol[i]; + /* Remember the remaining NOL time in the timeout + * variable. + */ + nolinfo->dfs_nol[num_chans++].nol_timeout_ms -= + nol_completed_ms; + } + } + + nolinfo->dfs_ch_nchans = num_chans; +} + +void dfs_reinit_nol_from_psoc_copy(struct wlan_dfs *dfs, uint8_t pdev_id) +{ + struct dfs_soc_priv_obj *dfs_soc_obj = dfs->dfs_soc_obj; + struct dfsreq_nolinfo *nol; + uint8_t i; + + if (!dfs_soc_obj->dfs_psoc_nolinfo) + return; + + if (!dfs_soc_obj->dfs_psoc_nolinfo[pdev_id].dfs_ch_nchans) + return; + + nol = &dfs_soc_obj->dfs_psoc_nolinfo[pdev_id]; + + /* The NOL timeout value in each entry points to the remaining time + * of the NOL. This is to indicate that the NOL entries are paused + * and are not left to continue. + * While adding these NOL, update the start ticks to current time + * to avoid losing entries which might have timed out during + * the pause and resume mechanism. + */ + for (i = 0; i < nol->dfs_ch_nchans; i++) + nol->dfs_nol[i].nol_start_ticks = qdf_system_ticks(); + dfs_set_nol(dfs, nol->dfs_nol, nol->dfs_ch_nchans); +} diff --git a/umac/dfs/core/src/misc/dfs_process_radar_found_ind.c b/umac/dfs/core/src/misc/dfs_process_radar_found_ind.c index bba638f946..6c02b7036b 100644 --- a/umac/dfs/core/src/misc/dfs_process_radar_found_ind.c +++ b/umac/dfs/core/src/misc/dfs_process_radar_found_ind.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2020 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 @@ -951,16 +951,50 @@ QDF_STATUS dfs_process_radar_ind(struct wlan_dfs *dfs, bool wait_for_csa = false; uint16_t freq_list[NUM_CHANNELS_160MHZ]; uint8_t num_channels; - QDF_STATUS status; + QDF_STATUS status = QDF_STATUS_E_FAILURE; uint32_t freq_center; uint32_t radarfound_freq; struct dfs_channel *dfs_curchan; + /* Acquire a lock to avoid initiating mode switch till radar + * processing is completed. + */ + DFS_RADAR_MODE_SWITCH_LOCK(dfs); + + /* Before processing radar, check if HW mode switch is in progress. + * If in progress, defer the processing of radar event received till + * the mode switch is completed. + */ + if (dfs_is_hw_mode_switch_in_progress(dfs)) { + struct radar_found_info *radar_params = NULL; + + radar_params = qdf_mem_malloc(sizeof(*radar_params)); + if (!radar_params) + goto exit; + + /* If CAC timer is running, cancel it here rather than + * after processing to avoid handling unnecessary CAC timeouts. + */ + if (dfs->dfs_cac_timer_running) + dfs_cac_stop(dfs); + + /* If CAC timer is to be handled after mode switch and then + * we receive radar, no point in handling CAC completion. + */ + if (dfs->dfs_defer_params.is_cac_completed) + dfs->dfs_defer_params.is_cac_completed = false; + qdf_mem_copy(radar_params, radar_found, sizeof(*radar_params)); + dfs->dfs_defer_params.radar_params = radar_params; + dfs->dfs_defer_params.is_radar_detected = true; + status = QDF_STATUS_SUCCESS; + goto exit; + } + dfs_curchan = dfs->dfs_curchan; if (!dfs_curchan) { dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, "dfs->dfs_curchan is NULL"); - return QDF_STATUS_E_FAILURE; + goto exit; } /* Check if the current channel is a non DFS channel @@ -972,7 +1006,7 @@ QDF_STATUS dfs_process_radar_ind(struct wlan_dfs *dfs, !(radar_found->detector_id == AGILE_DETECTOR_ID)) { dfs_err(dfs, WLAN_DEBUG_DFS, "radar event on a non-DFS channel"); - return QDF_STATUS_E_FAILURE; + goto exit; } /* Sanity checks for radar on Agile detector */ @@ -981,7 +1015,7 @@ QDF_STATUS dfs_process_radar_ind(struct wlan_dfs *dfs, { dfs_err(dfs, WLAN_DEBUG_DFS, "radar on Agile detector when ADFS is not running"); - return QDF_STATUS_E_FAILURE; + goto exit; } /* For Full Offload, FW sends segment id,freq_offset and chirp @@ -1019,7 +1053,8 @@ QDF_STATUS dfs_process_radar_ind(struct wlan_dfs *dfs, if (!dfs->dfs_use_nol) { dfs_reset_bangradar(dfs); dfs_send_csa_to_current_chan(dfs); - return QDF_STATUS_SUCCESS; + status = QDF_STATUS_SUCCESS; + goto exit; } if (dfs->dfs_bangradar_type == DFS_BANGRADAR_FOR_ALL_SUBCHANS) @@ -1049,7 +1084,7 @@ QDF_STATUS dfs_process_radar_ind(struct wlan_dfs *dfs, if (QDF_IS_STATUS_ERROR(status)) { dfs_err(dfs, WLAN_DEBUG_DFS, "radar event received on invalid channel"); - return status; + goto exit; } dfs->dfs_is_nol_ie_sent = false; @@ -1105,7 +1140,7 @@ QDF_STATUS dfs_process_radar_ind(struct wlan_dfs *dfs, * channel change is not required. */ if (radar_found->detector_id == AGILE_DETECTOR_ID) - return QDF_STATUS_SUCCESS; + goto exit; if (!dfs->dfs_is_offload_enabled && dfs->is_radar_found_on_secondary_seg) { dfs_second_segment_radar_disable(dfs); @@ -1113,7 +1148,7 @@ QDF_STATUS dfs_process_radar_ind(struct wlan_dfs *dfs, if (dfs->is_radar_during_precac) { dfs->is_radar_during_precac = 0; - return QDF_STATUS_SUCCESS; + goto exit; } } @@ -1124,7 +1159,7 @@ QDF_STATUS dfs_process_radar_ind(struct wlan_dfs *dfs, * needs to be fixed. See EV 105776. */ if (wait_for_csa) - return QDF_STATUS_SUCCESS; + goto exit; /* * EV 129487 : We have detected radar in the channel, @@ -1144,6 +1179,8 @@ QDF_STATUS dfs_process_radar_ind(struct wlan_dfs *dfs, dfs->dfs_curchan->dfs_ch_mhz_freq_seg2, dfs->dfs_curchan->dfs_ch_flags); - return QDF_STATUS_SUCCESS; +exit: + DFS_RADAR_MODE_SWITCH_UNLOCK(dfs); + return status; } #endif diff --git a/umac/dfs/dispatcher/inc/wlan_dfs_ioctl.h b/umac/dfs/dispatcher/inc/wlan_dfs_ioctl.h index 1ec4e97c9c..a17d29b32d 100644 --- a/umac/dfs/dispatcher/inc/wlan_dfs_ioctl.h +++ b/umac/dfs/dispatcher/inc/wlan_dfs_ioctl.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2016-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2011, 2016-2020 The Linux Foundation. All rights reserved. * Copyright (c) 2010, Atheros Communications Inc. * All Rights Reserved. * @@ -77,7 +77,7 @@ #define DFS_LAST_IOCTL 29 #ifndef DFS_CHAN_MAX -#define DFS_CHAN_MAX 1023 +#define DFS_CHAN_MAX 25 #endif /** diff --git a/umac/dfs/dispatcher/inc/wlan_dfs_lmac_api.h b/umac/dfs/dispatcher/inc/wlan_dfs_lmac_api.h index 99b671e915..5ed030da21 100644 --- a/umac/dfs/dispatcher/inc/wlan_dfs_lmac_api.h +++ b/umac/dfs/dispatcher/inc/wlan_dfs_lmac_api.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2018 The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved. * * * Permission to use, copy, modify, and/or distribute this software for @@ -118,4 +118,13 @@ static inline bool lmac_is_host_dfs_check_support_enabled( return false; } #endif + +/** + * lmac_dfs_is_hw_mode_switch_in_progress() - Check if HW mode switch is in + * progress. + * @pdev: Pointer to PDEV structure. + * + * Return: true if HW mode switch is in progress, else false. + */ +bool lmac_dfs_is_hw_mode_switch_in_progress(struct wlan_objmgr_pdev *pdev); #endif /* _WLAN_DFS_LMAC_API_H_ */ diff --git a/umac/dfs/dispatcher/inc/wlan_dfs_mlme_api.h b/umac/dfs/dispatcher/inc/wlan_dfs_mlme_api.h index f8d9a31523..3593f06d0f 100644 --- a/umac/dfs/dispatcher/inc/wlan_dfs_mlme_api.h +++ b/umac/dfs/dispatcher/inc/wlan_dfs_mlme_api.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved. * * * Permission to use, copy, modify, and/or distribute this software for @@ -440,4 +440,22 @@ void dfs_mlme_handle_dfs_scan_violation(struct wlan_objmgr_pdev *pdev) * Return: true if pdev opmode is STA, else false. */ bool dfs_mlme_is_opmode_sta(struct wlan_objmgr_pdev *pdev); + +/** + * dfs_mlme_acquire_radar_mode_switch_lock() - Acquire lock for radar processing + * over mode switch handling. + * @pdev: Pointer to DFS pdev object. + * + * Return: void. + */ +void dfs_mlme_acquire_radar_mode_switch_lock(struct wlan_objmgr_pdev *pdev); + +/** + * dfs_mlme_release_radar_mode_switch_lock() - Release lock taken for radar + * processing over mode switch handling. + * @pdev: Pointer to DFS pdev object. + * + * Return: void. + */ +void dfs_mlme_release_radar_mode_switch_lock(struct wlan_objmgr_pdev *pdev); #endif /* _WLAN_DFS_MLME_API_H_ */ diff --git a/umac/dfs/dispatcher/inc/wlan_dfs_tgt_api.h b/umac/dfs/dispatcher/inc/wlan_dfs_tgt_api.h index 724dea5a7c..c49a497ae2 100644 --- a/umac/dfs/dispatcher/inc/wlan_dfs_tgt_api.h +++ b/umac/dfs/dispatcher/inc/wlan_dfs_tgt_api.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved. * * * Permission to use, copy, modify, and/or distribute this software for @@ -604,4 +604,77 @@ void tgt_dfs_set_fw_adfs_support(struct wlan_objmgr_pdev *pdev, { } #endif + +/** + * tgt_dfs_init_tmp_psoc_nol() - Init temporary psoc NOL structure. + * @pdev: Pointer to pdev object. + * @num_radios: Number of radios in the psoc. + * + * Return: void. + */ +void tgt_dfs_init_tmp_psoc_nol(struct wlan_objmgr_pdev *pdev, + uint8_t num_radios); + +/** + * tgt_dfs_deinit_tmp_psoc_nol() - De-init temporary psoc NOL structure. + * @pdev: Pointer to pdev object. + * + * Return: void. + */ +void tgt_dfs_deinit_tmp_psoc_nol(struct wlan_objmgr_pdev *pdev); + +/** + * tgt_dfs_save_dfs_nol_in_psoc() - Save NOL data of given pdev. + * @pdev: Pointer to pdev object. + * @pdev_id: The pdev ID which will have the NOL data. + * @low_5ghz_freq: The low 5GHz frequency value of the target pdev id. + * @high_5ghz_freq: The high 5GHz frequency value of the target pdev id. + * + * Based on the frequency of the NOL channel, copy it to the target pdev_id + * structure in psoc. + * + * Return: void. + */ +void tgt_dfs_save_dfs_nol_in_psoc(struct wlan_objmgr_pdev *pdev, + uint8_t pdev_id, + uint16_t low_5ghz_freq, + uint16_t high_5ghz_freq); + +/** + * tgt_dfs_reinit_nol_from_psoc_copy() - Reinit saved NOL data to corresponding + * pdevs. + * @pdev: Pointer to pdev object. + * @pdev_id: pdev_id of the given pdev. + * + * Return: void. + */ +void tgt_dfs_reinit_nol_from_psoc_copy(struct wlan_objmgr_pdev *pdev, + uint8_t pdev_id); + +/** + * tgt_dfs_reinit_precac_lists() - Reinit preCAC lists. + * @src_pdev: Source pdev object from which the preCAC list is copied. + * @dest_pdev: Destination pdev object to which the preCAC list is copied. + * @low_5g_freq: Low 5G frequency value of the destination DFS. + * @high_5g_freq: High 5G frequency value of the destination DFS. + * + * Copy all the preCAC list entries from the source pdev object to the + * destination pdev object which fall within the frequency range of + * low_5g_freq and high_5g_freq. + * + * Return: None (void). + */ +void tgt_dfs_reinit_precac_lists(struct wlan_objmgr_pdev *src_pdev, + struct wlan_objmgr_pdev *dest_pdev, + uint16_t low_5g_freq, + uint16_t high_5g_freq); + +/** + * tgt_dfs_complete_deferred_tasks() - Process HW mode switch completion and + * handle deferred tasks. + * @pdev: Pointer to primary pdev object. + * + * Return: void. + */ +void tgt_dfs_complete_deferred_tasks(struct wlan_objmgr_pdev *pdev); #endif /* _WLAN_DFS_TGT_API_H_ */ diff --git a/umac/dfs/dispatcher/inc/wlan_dfs_ucfg_api.h b/umac/dfs/dispatcher/inc/wlan_dfs_ucfg_api.h index bbf9b0e9c7..2d7d7fb825 100644 --- a/umac/dfs/dispatcher/inc/wlan_dfs_ucfg_api.h +++ b/umac/dfs/dispatcher/inc/wlan_dfs_ucfg_api.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved. * * * Permission to use, copy, modify, and/or distribute this software for @@ -73,6 +73,10 @@ * list. * @mlme_get_cac_timeout_for_freq: Get CAC timeout for a given channel * frequency. + * @mlme_acquire_radar_mode_switch_lock: Acquire lock for radar processing over + * mode switch. + * @mlme_release_radar_mode_switch_lock: Release lock taken for radar processing + * over mode switch. */ struct dfs_to_mlme { QDF_STATUS (*pdev_component_obj_attach)(struct wlan_objmgr_pdev *pdev, @@ -242,6 +246,10 @@ struct dfs_to_mlme { (struct wlan_objmgr_pdev *pdev, uint16_t freq, enum WLAN_DFS_EVENTS event); + void (*mlme_acquire_radar_mode_switch_lock) + (struct wlan_objmgr_pdev *pdev); + void (*mlme_release_radar_mode_switch_lock) + (struct wlan_objmgr_pdev *pdev); }; extern struct dfs_to_mlme global_dfs_to_mlme; diff --git a/umac/dfs/dispatcher/src/wlan_dfs_init_deinit_api.c b/umac/dfs/dispatcher/src/wlan_dfs_init_deinit_api.c index ee87b8dd06..db9cc0c7ab 100644 --- a/umac/dfs/dispatcher/src/wlan_dfs_init_deinit_api.c +++ b/umac/dfs/dispatcher/src/wlan_dfs_init_deinit_api.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved. * * * Permission to use, copy, modify, and/or distribute this software for @@ -139,6 +139,10 @@ void register_dfs_callbacks(void) tmp_dfs_to_mlme->mlme_dfs_deliver_event = mlme_dfs_deliver_event; + tmp_dfs_to_mlme->mlme_acquire_radar_mode_switch_lock = + mlme_acquire_radar_mode_switch_lock; + tmp_dfs_to_mlme->mlme_release_radar_mode_switch_lock = + mlme_release_radar_mode_switch_lock; /* * Register precac auto channel switch feature related callbacks */ diff --git a/umac/dfs/dispatcher/src/wlan_dfs_lmac_api.c b/umac/dfs/dispatcher/src/wlan_dfs_lmac_api.c index bfa0b4ee4a..48c0bb454d 100644 --- a/umac/dfs/dispatcher/src/wlan_dfs_lmac_api.c +++ b/umac/dfs/dispatcher/src/wlan_dfs_lmac_api.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2018 The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved. * * * Permission to use, copy, modify, and/or distribute this software for @@ -197,3 +197,20 @@ bool lmac_is_host_dfs_check_support_enabled(struct wlan_objmgr_pdev *pdev) return enabled; } #endif + +bool lmac_dfs_is_hw_mode_switch_in_progress(struct wlan_objmgr_pdev *pdev) +{ + struct wlan_objmgr_psoc *psoc; + struct wlan_lmac_if_dfs_tx_ops *dfs_tx_ops; + bool is_hw_mode_switch_in_progress = false; + + psoc = wlan_pdev_get_psoc(pdev); + dfs_tx_ops = &psoc->soc_cb.tx_ops.dfs_tx_ops; + + if (dfs_tx_ops->dfs_check_mode_switch_state) + dfs_tx_ops->dfs_check_mode_switch_state( + pdev, + &is_hw_mode_switch_in_progress); + + return is_hw_mode_switch_in_progress; +} diff --git a/umac/dfs/dispatcher/src/wlan_dfs_mlme_api.c b/umac/dfs/dispatcher/src/wlan_dfs_mlme_api.c index d881a20506..54e68c6af7 100644 --- a/umac/dfs/dispatcher/src/wlan_dfs_mlme_api.c +++ b/umac/dfs/dispatcher/src/wlan_dfs_mlme_api.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved. * * * Permission to use, copy, modify, and/or distribute this software for @@ -567,3 +567,19 @@ bool dfs_mlme_is_opmode_sta(struct wlan_objmgr_pdev *pdev) return global_dfs_to_mlme.mlme_is_opmode_sta(pdev); } + +void dfs_mlme_acquire_radar_mode_switch_lock(struct wlan_objmgr_pdev *pdev) +{ + if (!global_dfs_to_mlme.mlme_acquire_radar_mode_switch_lock) + return; + + global_dfs_to_mlme.mlme_acquire_radar_mode_switch_lock(pdev); +} + +void dfs_mlme_release_radar_mode_switch_lock(struct wlan_objmgr_pdev *pdev) +{ + if (!global_dfs_to_mlme.mlme_release_radar_mode_switch_lock) + return; + + global_dfs_to_mlme.mlme_release_radar_mode_switch_lock(pdev); +} diff --git a/umac/dfs/dispatcher/src/wlan_dfs_tgt_api.c b/umac/dfs/dispatcher/src/wlan_dfs_tgt_api.c index fc955e3925..577976dad0 100644 --- a/umac/dfs/dispatcher/src/wlan_dfs_tgt_api.c +++ b/umac/dfs/dispatcher/src/wlan_dfs_tgt_api.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved. * * * Permission to use, copy, modify, and/or distribute this software for @@ -944,3 +944,102 @@ void tgt_dfs_set_fw_adfs_support(struct wlan_objmgr_pdev *pdev, qdf_export_symbol(tgt_dfs_set_fw_adfs_support); #endif + +void tgt_dfs_init_tmp_psoc_nol(struct wlan_objmgr_pdev *pdev, + uint8_t num_radios) +{ + struct wlan_dfs *dfs; + + dfs = wlan_pdev_get_dfs_obj(pdev); + if (!dfs) { + dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, "dfs is NULL"); + return; + } + + dfs_init_tmp_psoc_nol(dfs, num_radios); +} + +qdf_export_symbol(tgt_dfs_init_tmp_psoc_nol); + +void tgt_dfs_deinit_tmp_psoc_nol(struct wlan_objmgr_pdev *pdev) +{ + struct wlan_dfs *dfs; + + dfs = wlan_pdev_get_dfs_obj(pdev); + if (!dfs) { + dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, "dfs is NULL"); + return; + } + + dfs_deinit_tmp_psoc_nol(dfs); +} + +qdf_export_symbol(tgt_dfs_deinit_tmp_psoc_nol); + +void tgt_dfs_save_dfs_nol_in_psoc(struct wlan_objmgr_pdev *pdev, + uint8_t pdev_id, + uint16_t low_5ghz_freq, + uint16_t high_5ghz_freq) +{ + struct wlan_dfs *dfs; + + dfs = wlan_pdev_get_dfs_obj(pdev); + if (!dfs) { + dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, "dfs is NULL"); + return; + } + + dfs_save_dfs_nol_in_psoc(dfs, pdev_id, low_5ghz_freq, high_5ghz_freq); +} + +qdf_export_symbol(tgt_dfs_save_dfs_nol_in_psoc); + +void tgt_dfs_reinit_nol_from_psoc_copy(struct wlan_objmgr_pdev *pdev, + uint8_t pdev_id) +{ + struct wlan_dfs *dfs; + + dfs = wlan_pdev_get_dfs_obj(pdev); + if (!dfs) { + dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, "dfs is NULL"); + return; + } + + dfs_reinit_nol_from_psoc_copy(dfs, pdev_id); +} + +qdf_export_symbol(tgt_dfs_reinit_nol_from_psoc_copy); + +void tgt_dfs_reinit_precac_lists(struct wlan_objmgr_pdev *src_pdev, + struct wlan_objmgr_pdev *dest_pdev, + uint16_t low_5g_freq, + uint16_t high_5g_freq) +{ + struct wlan_dfs *src_dfs, *dest_dfs; + + src_dfs = wlan_pdev_get_dfs_obj(src_pdev); + if (!src_dfs) { + dfs_err(src_dfs, WLAN_DEBUG_DFS_ALWAYS, "dfs is NULL"); + return; + } + dest_dfs = wlan_pdev_get_dfs_obj(dest_pdev); + if (!dest_dfs) { + dfs_err(dest_dfs, WLAN_DEBUG_DFS_ALWAYS, "dfs is NULL"); + return; + } + + dfs_reinit_precac_lists(src_dfs, dest_dfs, low_5g_freq, high_5g_freq); +} + +void tgt_dfs_complete_deferred_tasks(struct wlan_objmgr_pdev *pdev) +{ + struct wlan_dfs *dfs; + + dfs = wlan_pdev_get_dfs_obj(pdev); + if (!dfs) { + dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, "dfs is NULL"); + return; + } + + dfs_complete_deferred_tasks(dfs); +} diff --git a/umac/global_umac_dispatcher/lmac_if/inc/wlan_lmac_if_def.h b/umac/global_umac_dispatcher/lmac_if/inc/wlan_lmac_if_def.h index 15cf260b79..5c5945a9bc 100644 --- a/umac/global_umac_dispatcher/lmac_if/inc/wlan_lmac_if_def.h +++ b/umac/global_umac_dispatcher/lmac_if/inc/wlan_lmac_if_def.h @@ -802,6 +802,7 @@ struct wlan_lmac_if_reg_tx_ops { * @dfs_send_avg_radar_params_to_fw: Send average radar parameters to FW. * @dfs_send_usenol_pdev_param: Send usenol pdev param to FW. * @dfs_send_subchan_marking_pdev_param: Send subchan marking pdev param to FW. + * @dfs_check_mode_switch_state: Find if HW mode switch is in progress. */ struct wlan_lmac_if_dfs_tx_ops { @@ -855,6 +856,9 @@ struct wlan_lmac_if_dfs_tx_ops { QDF_STATUS (*dfs_send_subchan_marking_pdev_param)( struct wlan_objmgr_pdev *pdev, bool subchanmark); + QDF_STATUS (*dfs_check_mode_switch_state)( + struct wlan_objmgr_pdev *pdev, + bool *is_hw_mode_switch_in_progress); }; /** @@ -1374,6 +1378,12 @@ struct wlan_lmac_if_wifi_pos_rx_ops { * @dfs_is_hw_pulses_allowed: Check if HW pulses are allowed or not. * @dfs_set_fw_adfs_support: Set the agile DFS FW support in DFS. * @dfs_reset_dfs_prevchan: Reset DFS previous channel structure. + * @dfs_init_tmp_psoc_nol: Init temporary PSOC NOL structure. + * @dfs_deinit_tmp_psoc_nol: Deinit temporary PSOC NOL structure. + * @dfs_save_dfs_nol_in_psoc: Copy DFS NOL data to the PSOC copy. + * @dfs_reinit_nol_from_psoc_copy: Reinit DFS NOL from the PSOC NOL copy. + * @dfs_reinit_precac_lists: Reinit precac lists from other pdev. + * @dfs_complete_deferred_tasks: Process mode switch completion in DFS. */ struct wlan_lmac_if_dfs_rx_ops { QDF_STATUS (*dfs_get_radars)(struct wlan_objmgr_pdev *pdev); @@ -1541,6 +1551,20 @@ struct wlan_lmac_if_dfs_rx_ops { bool fw_adfs_support_160, bool fw_adfs_support_non_160); void (*dfs_reset_dfs_prevchan)(struct wlan_objmgr_pdev *pdev); + void (*dfs_init_tmp_psoc_nol)(struct wlan_objmgr_pdev *pdev, + uint8_t num_radios); + void (*dfs_deinit_tmp_psoc_nol)(struct wlan_objmgr_pdev *pdev); + void (*dfs_save_dfs_nol_in_psoc)(struct wlan_objmgr_pdev *pdev, + uint8_t pdev_id, + uint16_t low_5ghz_freq, + uint16_t high_5ghz_freq); + void (*dfs_reinit_nol_from_psoc_copy)(struct wlan_objmgr_pdev *pdev, + uint8_t pdev_id); + void (*dfs_reinit_precac_lists)(struct wlan_objmgr_pdev *src_pdev, + struct wlan_objmgr_pdev *dest_pdev, + uint16_t low_5g_freq, + uint16_t high_5g_freq); + void (*dfs_complete_deferred_tasks)(struct wlan_objmgr_pdev *pdev); }; /** diff --git a/umac/global_umac_dispatcher/lmac_if/src/wlan_lmac_if.c b/umac/global_umac_dispatcher/lmac_if/src/wlan_lmac_if.c index 4c8359dec7..cd8fdf12a2 100644 --- a/umac/global_umac_dispatcher/lmac_if/src/wlan_lmac_if.c +++ b/umac/global_umac_dispatcher/lmac_if/src/wlan_lmac_if.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved. * * * Permission to use, copy, modify, and/or distribute this software for @@ -523,7 +523,18 @@ wlan_lmac_if_umac_dfs_rx_ops_register(struct wlan_lmac_if_rx_ops *rx_ops) tgt_dfs_set_fw_adfs_support; dfs_rx_ops->dfs_reset_dfs_prevchan = utils_dfs_reset_dfs_prevchan; - + dfs_rx_ops->dfs_init_tmp_psoc_nol = + tgt_dfs_init_tmp_psoc_nol; + dfs_rx_ops->dfs_deinit_tmp_psoc_nol = + tgt_dfs_deinit_tmp_psoc_nol; + dfs_rx_ops->dfs_save_dfs_nol_in_psoc = + tgt_dfs_save_dfs_nol_in_psoc; + dfs_rx_ops->dfs_reinit_nol_from_psoc_copy = + tgt_dfs_reinit_nol_from_psoc_copy; + dfs_rx_ops->dfs_reinit_precac_lists = + tgt_dfs_reinit_precac_lists; + dfs_rx_ops->dfs_complete_deferred_tasks = + tgt_dfs_complete_deferred_tasks; register_precac_auto_chan_rx_ops(dfs_rx_ops); register_precac_auto_chan_rx_ops_ieee(dfs_rx_ops); register_precac_auto_chan_rx_ops_freq(dfs_rx_ops); diff --git a/umac/regulatory/core/src/reg_services_common.c b/umac/regulatory/core/src/reg_services_common.c index b43a54f446..fc3abf6210 100644 --- a/umac/regulatory/core/src/reg_services_common.c +++ b/umac/regulatory/core/src/reg_services_common.c @@ -34,6 +34,7 @@ #include "reg_db_parser.h" #include "reg_build_chan_list.h" #include +#include const struct chan_map *channel_map; #ifdef CONFIG_CHAN_NUM_API @@ -2656,11 +2657,13 @@ QDF_STATUS reg_modify_pdev_chan_range(struct wlan_objmgr_pdev *pdev) struct wlan_lmac_if_reg_tx_ops *reg_tx_ops; struct wlan_psoc_host_hal_reg_capabilities_ext *reg_cap_ptr; uint32_t cnt; - uint32_t pdev_id; + uint32_t phy_id; enum direction dir; QDF_STATUS status = QDF_STATUS_SUCCESS; + struct target_pdev_info *tgt_pdev; - pdev_id = wlan_objmgr_pdev_get_pdev_id(pdev); + tgt_pdev = wlan_pdev_get_tgt_if_handle(pdev); + phy_id = (uint32_t)target_pdev_get_phy_idx(tgt_pdev); pdev_priv_obj = reg_get_pdev_obj(pdev); if (!IS_VALID_PDEV_REG_OBJ(pdev_priv_obj)) { reg_err("pdev reg component is NULL"); @@ -2688,7 +2691,7 @@ QDF_STATUS reg_modify_pdev_chan_range(struct wlan_objmgr_pdev *pdev) return QDF_STATUS_E_FAULT; } - if (reg_cap_ptr->phy_id == pdev_id) + if (reg_cap_ptr->phy_id == phy_id) break; reg_cap_ptr++; }