diff --git a/os_if/linux/scan/inc/wlan_cfg80211_scan.h b/os_if/linux/scan/inc/wlan_cfg80211_scan.h index 69cbc99691bc..91b5687479b8 100644 --- a/os_if/linux/scan/inc/wlan_cfg80211_scan.h +++ b/os_if/linux/scan/inc/wlan_cfg80211_scan.h @@ -115,11 +115,34 @@ struct scan_req { uint8_t source; }; +#ifdef FEATURE_WLAN_SCAN_PNO +/** + * wlan_cfg80211_sched_scan_start() - cfg80211 scheduled scan(pno) start + * @pdev: pdev pointer + * @dev: Pointer network device + * @request: Pointer to cfg80211 scheduled scan start request + * + * Return: 0 for success, non zero for failure + */ +int wlan_cfg80211_sched_scan_start(struct wlan_objmgr_pdev *pdev, + struct net_device *dev, + struct cfg80211_sched_scan_request *request); + +/** + * wlan_cfg80211_sched_scan_stop() - cfg80211 scheduled scan(pno) stop + * @pdev: pdev pointer + * @dev: Pointer network device + * + * Return: 0 for success, non zero for failure + */ +int wlan_cfg80211_sched_scan_stop(struct wlan_objmgr_pdev *pdev, + struct net_device *dev); +#endif /** * wlan_cfg80211_scan_priv_init() - API to initialize cfg80211 scan * @pdev: Pointer to net device - * + * * API to initialize cfg80211 scan module. * * Return: QDF_STATUS diff --git a/os_if/linux/scan/src/wlan_cfg80211_scan.c b/os_if/linux/scan/src/wlan_cfg80211_scan.c index cc6875af69e9..f4cfd8a7b5fa 100644 --- a/os_if/linux/scan/src/wlan_cfg80211_scan.c +++ b/os_if/linux/scan/src/wlan_cfg80211_scan.c @@ -35,6 +35,351 @@ #include #include +#ifdef FEATURE_WLAN_SCAN_PNO +#if ((LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)) || \ + defined(CFG80211_MULTI_SCAN_PLAN_BACKPORT)) + +/** + * wlan_config_sched_scan_plan() - configures the sched scan plans + * from the framework. + * @pno_req: pointer to PNO scan request + * @request: pointer to scan request from framework + * + * Return: None + */ +static void wlan_config_sched_scan_plan(struct pno_scan_req_params *pno_req, + struct cfg80211_sched_scan_request *request) +{ + pno_req->delay_start_time = request->delay; + /* + * As of now max 2 scan plans were supported by firmware + * if number of scan plan supported by firmware increased below logic + * must change. + */ + if (request->n_scan_plans == SCAN_PNO_MAX_PLAN_REQUEST) { + pno_req->fast_scan_period = + request->scan_plans[0].interval * MSEC_PER_SEC; + pno_req->fast_scan_max_cycles = + request->scan_plans[0].iterations; + pno_req->slow_scan_period = + request->scan_plans[1].interval * MSEC_PER_SEC; + } else if (request->n_scan_plans == 1) { + pno_req->fast_scan_period = + request->scan_plans[0].interval * MSEC_PER_SEC; + /* + * if only one scan plan is configured from framework + * then both fast and slow scan should be configured with the + * same value that is why fast scan cycles are hardcoded to one + */ + pno_req->fast_scan_max_cycles = 1; + pno_req->slow_scan_period = + request->scan_plans[0].interval * MSEC_PER_SEC; + } else { + cfg80211_err("Invalid number of scan plans %d !!", + request->n_scan_plans); + } +} +#else +static void wlan_config_sched_scan_plan(struct pno_scan_req_params *pno_req, + struct cfg80211_sched_scan_request *request) +{ + pno_req->fast_scan_period = request->interval; + pno_req->fast_scan_max_cycles = SCAN_PNO_DEF_SCAN_TIMER_REPEAT; + pno_req->slow_scan_period = + SCAN_PNO_DEF_SLOW_SCAN_MULTIPLIER * + pno_req->fast_scan_period; +} +#endif + +/** + * wlan_cfg80211_pno_callback() - pno callback function to handle + * pno events. + * @vdev: vdev ptr + * @event: scan events + * @args: argument + * + * Return: void + */ +static void wlan_cfg80211_pno_callback(struct wlan_objmgr_vdev *vdev, + struct scan_event *event, + void *args) +{ + struct wlan_objmgr_pdev *pdev; + struct pdev_osif_priv *pdev_ospriv; + + if (event->type != SCAN_EVENT_TYPE_NLO_COMPLETE) + return; + + cfg80211_info("vdev id = %d", event->vdev_id); + + wlan_vdev_obj_lock(vdev); + pdev = wlan_vdev_get_pdev(vdev); + wlan_vdev_obj_unlock(vdev); + if (!pdev) { + cfg80211_err("pdev is NULL"); + return; + } + + wlan_pdev_obj_lock(pdev); + pdev_ospriv = wlan_pdev_get_ospriv(pdev); + wlan_pdev_obj_unlock(pdev); + if (!pdev_ospriv) { + cfg80211_err("pdev_osprivis NULL"); + return; + } + cfg80211_sched_scan_results(pdev_ospriv->wiphy); +} + +static void +wlan_cfg80211_register_pno_cb(struct wlan_objmgr_psoc *psoc) +{ + ucfg_scan_register_pno_cb(psoc, + wlan_cfg80211_pno_callback, NULL); +} + +/** + * wlan_cfg80211_is_pno_allowed() - Check if PNO is allowed + * @vdev: vdev ptr + * + * The PNO Start request is coming from upper layers. + * It is to be allowed only for Infra STA device type + * and the link should be in a disconnected state. + * + * Return: Success if PNO is allowed, Failure otherwise. + */ +static QDF_STATUS wlan_cfg80211_is_pno_allowed(struct wlan_objmgr_vdev *vdev) +{ + enum wlan_vdev_state state; + enum tQDF_ADAPTER_MODE vdev_opmode; + uint8_t vdev_id; + + wlan_vdev_obj_lock(vdev); + vdev_opmode = wlan_vdev_mlme_get_opmode(vdev); + state = wlan_vdev_mlme_get_state(vdev); + vdev_id = wlan_vdev_get_id(vdev); + wlan_vdev_obj_unlock(vdev); + + cfg80211_notice("dev_mode=%d, state=%d vdev id %d", + vdev_opmode, state, vdev_id); + + if ((vdev_opmode == QDF_STA_MODE) && + ((state == WLAN_VDEV_S_INIT) || + (state == WLAN_VDEV_S_STOP))) + return QDF_STATUS_SUCCESS; + else + return QDF_STATUS_E_FAILURE; +} + +int wlan_cfg80211_sched_scan_start(struct wlan_objmgr_pdev *pdev, + struct net_device *dev, + struct cfg80211_sched_scan_request *request) +{ + struct pno_scan_req_params *req; + int i, j, ret = 0; + QDF_STATUS status; + uint8_t num_chan = 0, channel; + struct wlan_objmgr_vdev *vdev; + uint32_t valid_ch[SCAN_PNO_MAX_NETW_CHANNELS_EX] = {0}; + + vdev = wlan_objmgr_get_vdev_by_macaddr_from_pdev(pdev, dev->dev_addr, + WLAN_OSIF_ID); + if (!vdev) { + cfg80211_err("vdev object is NULL"); + return -EIO; + } + + status = wlan_cfg80211_is_pno_allowed(vdev); + if (QDF_IS_STATUS_ERROR(status)) { + cfg80211_err("pno is not allowed"); + wlan_objmgr_vdev_release_ref(vdev, WLAN_OSIF_ID); + return -ENOTSUPP; + } + + ucfg_scan_flush_results(pdev, NULL); + if (ucfg_scan_get_pdev_status(pdev) != + SCAN_NOT_IN_PROGRESS) { + status = wlan_abort_scan(pdev, + wlan_objmgr_pdev_get_pdev_id(pdev), + INVAL_VDEV_ID, INVAL_SCAN_ID); + if (QDF_IS_STATUS_ERROR(status)) { + cfg80211_err("aborting the existing scan is unsuccessful"); + wlan_objmgr_vdev_release_ref(vdev, WLAN_OSIF_ID); + return -EBUSY; + } + } + + req = qdf_mem_malloc(sizeof(*req)); + if (!req) { + cfg80211_err("req malloc failed"); + wlan_objmgr_vdev_release_ref(vdev, WLAN_OSIF_ID); + return -ENOMEM; + } + + req->networks_cnt = request->n_match_sets; + wlan_vdev_obj_lock(vdev); + req->vdev_id = wlan_vdev_get_id(vdev); + wlan_vdev_obj_unlock(vdev); + + if ((!req->networks_cnt) || + (req->networks_cnt > SCAN_PNO_MAX_SUPP_NETWORKS)) { + cfg80211_err("Network input is not correct %d", + req->networks_cnt); + ret = -EINVAL; + goto error; + } + + if (request->n_channels > SCAN_PNO_MAX_NETW_CHANNELS_EX) { + cfg80211_err("Incorrect number of channels %d", + request->n_channels); + ret = -EINVAL; + goto error; + } + + if (request->n_channels) { + char chl[(request->n_channels * 5) + 1]; + int len = 0; + + for (i = 0; i < request->n_channels; i++) { + channel = request->channels[i]->hw_value; + if (wlan_is_dsrc_channel(wlan_chan_to_freq(channel))) + continue; + len += snprintf(chl + len, 5, "%d ", channel); + valid_ch[num_chan++] = wlan_chan_to_freq(channel); + } + cfg80211_notice("No. of Scan Channels: %d", num_chan); + cfg80211_notice("Channel-List: %s", chl); + /* If all channels are DFS and dropped, + * then ignore the PNO request + */ + if (!num_chan) { + cfg80211_notice("Channel list empty due to filtering of DSRC"); + ret = -EINVAL; + goto error; + } + } + + /* Filling per profile params */ + for (i = 0; i < req->networks_cnt; i++) { + req->networks_list[i].ssid.length = + request->match_sets[i].ssid.ssid_len; + + if ((!req->networks_list[i].ssid.length) || + (req->networks_list[i].ssid.length > WLAN_SSID_MAX_LEN)) { + cfg80211_err(" SSID Len %d is not correct for network %d", + req->networks_list[i].ssid.length, i); + ret = -EINVAL; + goto error; + } + + qdf_mem_copy(req->networks_list[i].ssid.ssid, + request->match_sets[i].ssid.ssid, + req->networks_list[i].ssid.length); + req->networks_list[i].authentication = 0; /*eAUTH_TYPE_ANY */ + req->networks_list[i].encryption = 0; /*eED_ANY */ + req->networks_list[i].bc_new_type = 0; /*eBCAST_UNKNOWN */ + + cfg80211_notice("Received ssid:%.*s", + req->networks_list[i].ssid.length, + req->networks_list[i].ssid.ssid); + + /*Copying list of valid channel into request */ + qdf_mem_copy(req->networks_list[i].channels, valid_ch, + num_chan * sizeof(uint32_t)); + req->networks_list[i].channel_cnt = num_chan; + req->networks_list[i].rssi_thresh = + request->match_sets[i].rssi_thold; + } + + for (i = 0; i < request->n_ssids; i++) { + j = 0; + while (j < req->networks_cnt) { + if ((req->networks_list[j].ssid.length == + request->ssids[i].ssid_len) && + (!qdf_mem_cmp(req->networks_list[j].ssid.ssid, + request->ssids[i].ssid, + req->networks_list[j].ssid.length))) { + req->networks_list[j].bc_new_type = + SSID_BC_TYPE_HIDDEN; + break; + } + j++; + } + } + cfg80211_notice("Number of hidden networks being Configured = %d", + request->n_ssids); + + /* + * Before Kernel 4.4 + * Driver gets only one time interval which is hard coded in + * supplicant for 10000ms. + * + * After Kernel 4.4 + * User can configure multiple scan_plans, each scan would have + * separate scan cycle and interval. (interval is in unit of second.) + * For our use case, we would only have supplicant set one scan_plan, + * and firmware also support only one as well, so pick up the first + * index. + * + * Taking power consumption into account + * firmware after gPNOScanTimerRepeatValue times fast_scan_period + * switches slow_scan_period. This is less frequent scans and firmware + * shall be in slow_scan_period mode until next PNO Start. + */ + wlan_config_sched_scan_plan(req, request); + cfg80211_notice("Base scan interval: %d sec, scan cycles: %d, slow scan interval %d", + req->fast_scan_period, req->fast_scan_max_cycles, + req->slow_scan_period); + + ucfg_scan_get_pno_def_params(vdev, req); + status = ucfg_scan_pno_start(vdev, req); + if (QDF_IS_STATUS_ERROR(status)) { + cfg80211_err("Failed to enable PNO"); + ret = -EINVAL; + goto error; + } + + cfg80211_info("PNO scan request offloaded"); + +error: + wlan_objmgr_vdev_release_ref(vdev, WLAN_OSIF_ID); + qdf_mem_free(req); + return ret; +} + +int wlan_cfg80211_sched_scan_stop(struct wlan_objmgr_pdev *pdev, + struct net_device *dev) +{ + int ret = 0; + QDF_STATUS status; + struct wlan_objmgr_vdev *vdev; + + vdev = wlan_objmgr_get_vdev_by_macaddr_from_pdev(pdev, dev->dev_addr, + WLAN_OSIF_ID); + if (!vdev) { + cfg80211_err("vdev object is NULL"); + return -EIO; + } + + status = ucfg_scan_pno_stop(vdev); + if (QDF_IS_STATUS_ERROR(status)) { + cfg80211_err("Failed to disabled PNO"); + ret = -EINVAL; + } else { + cfg80211_info("PNO scan disabled"); + } + + wlan_objmgr_vdev_release_ref(vdev, WLAN_OSIF_ID); + return ret; +} +#else +static inline void +wlan_cfg80211_register_pno_cb(struct wlan_objmgr_psoc *psoc) +{ + return; +} + +#endif /*FEATURE_WLAN_SCAN_PNO */ + /** * wlan_scan_request_enqueue() - enqueue Scan Request * @pdev: pointer to pdev object @@ -352,6 +697,8 @@ QDF_STATUS wlan_cfg80211_scan_priv_init(struct wlan_objmgr_pdev *pdev) wlan_pdev_obj_lock(pdev); psoc = wlan_pdev_get_psoc(pdev); wlan_pdev_obj_unlock(pdev); + + wlan_cfg80211_register_pno_cb(psoc); req_id = ucfg_scan_register_requester(psoc, "HDD", wlan_cfg80211_scan_done_callback, NULL); diff --git a/power_management_offloads/core/inc/wlan_pmo_wow.h b/power_management_offloads/core/inc/wlan_pmo_wow.h index 7ae4cacf075d..69cbe77d3cd8 100644 --- a/power_management_offloads/core/inc/wlan_pmo_wow.h +++ b/power_management_offloads/core/inc/wlan_pmo_wow.h @@ -417,131 +417,6 @@ bool pmo_core_get_wow_initial_wake_up(struct pmo_psoc_priv_obj *psoc_ctx) return value; } -#ifdef FEATURE_WLAN_SCAN_PNO -/** - * pmo_core_is_nlo_scan_in_progress(): check if nlo scan is in progress - * @vdev: objmgr vdev handle - * - * Return: TRUE/FALSE - */ -static inline -bool pmo_core_is_nlo_scan_in_progress(struct wlan_objmgr_vdev *vdev) -{ - bool nlo_in_progress; - struct pmo_vdev_priv_obj *vdev_ctx; - - vdev_ctx = pmo_get_vdev_priv_ctx(vdev); - if (!vdev_ctx) - return false; - qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); - nlo_in_progress = vdev_ctx->nlo_in_progress; - qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); - - return nlo_in_progress; -} - -/** - * pmo_core_is_nlo_scan_match_found(): check if a nlo scan match was found - * @vdev: objmgr vdev handle - * - * Return: TRUE/FALSE - */ -static inline -bool pmo_core_is_nlo_scan_match_found(struct wlan_objmgr_vdev *vdev) -{ - bool nlo_match_received; - struct pmo_vdev_priv_obj *vdev_ctx; - - vdev_ctx = pmo_get_vdev_priv_ctx(vdev); - if (!vdev_ctx) - return false; - qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); - nlo_match_received = vdev_ctx->nlo_match_received; - qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); - - return nlo_match_received; -} - -/** - * pmo_core_update_nlo_scan_in_progress(): update nlo scan is in progress flags - * @vdev: objmgr vdev handle - * @value:true if pno scan is in progress else false - * - * Return: None - */ -static inline -void pmo_core_update_nlo_scan_in_progress(struct wlan_objmgr_vdev *vdev, - bool value) -{ - struct pmo_vdev_priv_obj *vdev_ctx; - - vdev_ctx = pmo_get_vdev_priv_ctx(vdev); - if (!vdev_ctx) - return; - qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); - vdev_ctx->nlo_in_progress = value; - qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); -} - -/** - * pmo_core_update_nlo_match_found(): Update nlo scan match flag to value - * @vdev: objmgr vdev handle - * @value:true if nlo scan match event received else false - * - * Return: TRUE/FALSE - */ -static inline -void pmo_core_update_nlo_match_found(struct wlan_objmgr_vdev *vdev, - bool value) -{ - struct pmo_vdev_priv_obj *vdev_ctx; - - vdev_ctx = pmo_get_vdev_priv_ctx(vdev); - if (!vdev_ctx) - return; - qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock); - vdev_ctx->nlo_match_received = value; - qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock); -} -#else -/** - * pmo_is_nlo_scan_in_progress(): dummy - * - * Return: False since no pnoscan cannot be in progress - * when feature flag is not defined. - */ -static inline -bool pmo_core_is_nlo_scan_in_progress(struct wlan_objmgr_vdev *vdev) -{ - return false; -} - -/** - * wma_is_pnoscan_match_found(): dummy - * @vdev: objmgr vdev handle - * - * Return: False since no pnoscan cannot occur - * when feature flag is not defined. - */ -static inline -bool pmo_core_is_nlo_scan_match_found(struct wlan_objmgr_vdev *vdev) -{ - return false; -} - -static inline -void pmo_core_update_nlo_scan_in_progress(struct wlan_objmgr_vdev *vdev, - bool value) -{ -} - -static inline -void pmo_core_update_nlo_match_found(struct wlan_objmgr_vdev *vdev, - bool value) -{ -} -#endif - #ifdef FEATURE_WLAN_EXTSCAN /** * pmo_core_is_extscan_in_progress(): check if a extscan is in progress diff --git a/power_management_offloads/core/src/wlan_pmo_suspend_resume.c b/power_management_offloads/core/src/wlan_pmo_suspend_resume.c index ea4cdf75798e..5884f2b4699d 100644 --- a/power_management_offloads/core/src/wlan_pmo_suspend_resume.c +++ b/power_management_offloads/core/src/wlan_pmo_suspend_resume.c @@ -29,6 +29,8 @@ #include "cdp_txrx_flow_ctrl_legacy.h" #include "htc_api.h" #include "wlan_pmo_obj_mgmt_api.h" +#include + /** * pmo_core_calculate_listen_interval() - Calculate vdev listen interval @@ -231,8 +233,8 @@ void pmo_core_configure_dynamic_wake_events(struct wlan_objmgr_psoc *psoc) enable_mask = 0; disable_mask = 0; - if (pmo_core_is_nlo_scan_in_progress(vdev)) { - if (pmo_core_is_nlo_scan_match_found(vdev)) + if (ucfg_scan_get_pno_in_progress(vdev)) { + if (ucfg_scan_get_pno_match(vdev)) enable_mask |= (1 << WOW_NLO_SCAN_COMPLETE_EVENT); else diff --git a/power_management_offloads/core/src/wlan_pmo_wow.c b/power_management_offloads/core/src/wlan_pmo_wow.c index a8326e1b7911..21cb4ebf1c14 100644 --- a/power_management_offloads/core/src/wlan_pmo_wow.c +++ b/power_management_offloads/core/src/wlan_pmo_wow.c @@ -23,6 +23,8 @@ #include "wlan_pmo_tgt_api.h" #include "wlan_pmo_main.h" #include "wlan_pmo_obj_mgmt_public_struct.h" +#include + static inline int pmo_find_wow_ptrn_len(const char *ptrn) { @@ -213,7 +215,7 @@ bool pmo_core_is_wow_applicable(struct wlan_objmgr_psoc *psoc) pmo_debug("STA is connected, enabling wow"); is_wow_applicable = true; break; - } else if (pmo_core_is_nlo_scan_in_progress(vdev)) { + } else if (ucfg_scan_get_pno_in_progress(vdev)) { wlan_objmgr_vdev_release_ref(vdev, WLAN_PMO_ID); pmo_debug("NLO is in progress, enabling wow"); is_wow_applicable = true; diff --git a/power_management_offloads/dispatcher/inc/wlan_pmo_ucfg_api.h b/power_management_offloads/dispatcher/inc/wlan_pmo_ucfg_api.h index 9ffe5c2cb430..a210784a7041 100644 --- a/power_management_offloads/dispatcher/inc/wlan_pmo_ucfg_api.h +++ b/power_management_offloads/dispatcher/inc/wlan_pmo_ucfg_api.h @@ -288,26 +288,6 @@ QDF_STATUS pmo_ucfg_disable_gtk_offload_in_fwr(struct wlan_objmgr_vdev *vdev); QDF_STATUS pmo_ucfg_get_gtk_rsp(struct wlan_objmgr_vdev *vdev, struct pmo_gtk_rsp_req *gtk_rsp_req); -/** - * pmo_ucfg_update_nlo_scan_in_progress(): update nlo scan is in progress flags - * @vdev: objmgr vdev handle - * @value:true if pno scan is in progress else false - * - * Return: TRUE/FALSE - */ -void pmo_ucfg_update_nlo_scan_in_progress(struct wlan_objmgr_vdev *vdev, - bool value); - -/** - * pmo_ucfg_update_nlo_match_found(): Update nlo scan match flag to value - * @vdev: objmgr vdev handle - * @value:true if nlo scan match event received else false - * - * Return: TRUE/FALSE - */ -void pmo_ucfg_update_nlo_match_found(struct wlan_objmgr_vdev *vdev, - bool value); - /** * pmo_ucfg_update_extscan_in_progress(): update extscan is in progress flags * @vdev: objmgr vdev handle diff --git a/power_management_offloads/dispatcher/src/wlan_pmo_ucfg_api.c b/power_management_offloads/dispatcher/src/wlan_pmo_ucfg_api.c index 9d4a9e32761e..fb2caffc5962 100644 --- a/power_management_offloads/dispatcher/src/wlan_pmo_ucfg_api.c +++ b/power_management_offloads/dispatcher/src/wlan_pmo_ucfg_api.c @@ -175,18 +175,6 @@ QDF_STATUS pmo_ucfg_get_gtk_rsp(struct wlan_objmgr_vdev *vdev, return pmo_core_get_gtk_rsp(vdev, gtk_rsp_req); } -void pmo_ucfg_update_nlo_scan_in_progress(struct wlan_objmgr_vdev *vdev, - bool value) -{ - pmo_core_update_nlo_scan_in_progress(vdev, value); -} - -void pmo_ucfg_update_nlo_match_found(struct wlan_objmgr_vdev *vdev, - bool value) -{ - pmo_core_update_nlo_match_found(vdev, value); -} - void pmo_ucfg_update_extscan_in_progress(struct wlan_objmgr_vdev *vdev, bool value) { diff --git a/target_if/scan/inc/target_if_scan.h b/target_if/scan/inc/target_if_scan.h index a6df3e6dc959..e49fea630c18 100644 --- a/target_if/scan/inc/target_if_scan.h +++ b/target_if/scan/inc/target_if_scan.h @@ -22,10 +22,42 @@ #ifndef __TARGET_SCAN_IF_H__ #define __TARGET_SCAN_IF_H__ +#include + struct scan_req_params; struct scan_cancel_param; struct wlan_objmgr_psoc; +#ifdef FEATURE_WLAN_SCAN_PNO +/** + * target_if_nlo_match_event_handler() - nlo match event handler + * @scn: scn handle + * @event: event data + * @len: data length + * + * Record NLO match event comes from FW. It's a indication that + * one of the profile is matched. + * + * Return: 0 for success or error code. + */ +int target_if_nlo_match_event_handler(ol_scn_t scn, uint8_t *data, + uint32_t len); + +/** + * target_if_nlo_complete_handler() - nlo complete event handler + * @scn: scn handle + * @event: event data + * @len: data length + * + * Record NLO match event comes from FW. It's a indication that + * one of the profile is matched. + * + * Return: 0 for success or error code. + */ +int target_if_nlo_complete_handler(ol_scn_t scn, uint8_t *data, + uint32_t len); +#endif + /** * target_if_scan_register_event_handler() - lmac handler API * to register for scan events diff --git a/target_if/scan/src/target_if_scan.c b/target_if/scan/src/target_if_scan.c index 7ef31bc5527e..2e6e8dc49c66 100644 --- a/target_if/scan/src/target_if_scan.c +++ b/target_if/scan/src/target_if_scan.c @@ -22,13 +22,13 @@ #include #include -#include +#include #include #include #include #include #include -#include + static inline struct wlan_lmac_if_scan_rx_ops * target_if_scan_get_rx_ops(struct wlan_objmgr_psoc *psoc) @@ -82,19 +82,229 @@ target_if_scan_event_handler(ol_scn_t scn, uint8_t *data, uint32_t datalen) return 0; } +#ifdef FEATURE_WLAN_SCAN_PNO + +int target_if_nlo_complete_handler(ol_scn_t scn, uint8_t *data, + uint32_t len) +{ + wmi_nlo_event *nlo_event; + struct scan_event_info *event_info; + struct wlan_objmgr_psoc *psoc; + struct wlan_lmac_if_scan_rx_ops *scan_rx_ops; + WMI_NLO_MATCH_EVENTID_param_tlvs *param_buf = + (WMI_NLO_MATCH_EVENTID_param_tlvs *) data; + QDF_STATUS status; + + if (!scn || !data) { + target_if_err("scn: 0x%p, data: 0x%p", scn, data); + return -EINVAL; + } + + psoc = target_if_get_psoc_from_scn_hdl(scn); + if (!psoc) { + target_if_err("null psoc"); + return -EINVAL; + } + + event_info = qdf_mem_malloc(sizeof(*event_info)); + if (!event_info) { + target_if_err("unable to allocate scan_event"); + return -ENOMEM; + } + + nlo_event = param_buf->fixed_param; + target_if_info("PNO complete event received for vdev %d", + nlo_event->vdev_id); + + event_info->event.type = SCAN_EVENT_TYPE_NLO_COMPLETE; + event_info->event.vdev_id = nlo_event->vdev_id; + + scan_rx_ops = target_if_scan_get_rx_ops(psoc); + if (scan_rx_ops->scan_ev_handler) { + status = scan_rx_ops->scan_ev_handler(psoc, event_info); + if (status != QDF_STATUS_SUCCESS) { + qdf_mem_free(event_info); + return -EINVAL; + } + } + + return 0; +} + +int target_if_nlo_match_event_handler(ol_scn_t scn, uint8_t *data, + uint32_t len) +{ + wmi_nlo_event *nlo_event; + struct scan_event_info *event_info; + struct wlan_objmgr_psoc *psoc; + struct wlan_lmac_if_scan_rx_ops *scan_rx_ops; + WMI_NLO_MATCH_EVENTID_param_tlvs *param_buf = + (WMI_NLO_MATCH_EVENTID_param_tlvs *) data; + QDF_STATUS status; + + if (!scn || !data) { + target_if_err("scn: 0x%p, data: 0x%p", scn, data); + return -EINVAL; + } + + psoc = target_if_get_psoc_from_scn_hdl(scn); + if (!psoc) { + target_if_err("null psoc"); + return -EINVAL; + } + + event_info = qdf_mem_malloc(sizeof(*event_info)); + if (!event_info) { + target_if_err("unable to allocate scan_event"); + return -ENOMEM; + } + + nlo_event = param_buf->fixed_param; + target_if_info("PNO match event received for vdev %d", + nlo_event->vdev_id); + + event_info->event.type = SCAN_EVENT_TYPE_NLO_MATCH; + event_info->event.vdev_id = nlo_event->vdev_id; + + scan_rx_ops = target_if_scan_get_rx_ops(psoc); + if (scan_rx_ops->scan_ev_handler) { + status = scan_rx_ops->scan_ev_handler(psoc, event_info); + if (status != QDF_STATUS_SUCCESS) { + qdf_mem_free(event_info); + return -EINVAL; + } + } + + return 0; +} + +static QDF_STATUS +target_if_scan_register_pno_event_handler(struct wlan_objmgr_psoc *psoc, + void *arg) +{ + QDF_STATUS status; + + status = wmi_unified_register_event(psoc->tgt_if_handle, + wmi_nlo_match_event_id, + target_if_nlo_match_event_handler); + if (status) { + target_if_err("Failed to register nlo match event cb"); + return QDF_STATUS_E_FAILURE; + } + + status = wmi_unified_register_event(psoc->tgt_if_handle, + wmi_nlo_scan_complete_event_id, + target_if_nlo_complete_handler); + if (status) { + target_if_err("Failed to register nlo scan comp event cb"); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS +target_if_scan_unregister_pno_event_handler(struct wlan_objmgr_psoc *psoc, + void *arg) +{ + QDF_STATUS status; + + status = wmi_unified_unregister_event(psoc->tgt_if_handle, + wmi_nlo_match_event_id); + if (status) { + target_if_err("Failed to unregister nlo match event cb"); + return QDF_STATUS_E_FAILURE; + } + + status = wmi_unified_unregister_event(psoc->tgt_if_handle, + wmi_nlo_scan_complete_event_id); + if (status) { + target_if_err("Failed to unregister nlo scan comp event cb"); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS +target_if_pno_start(struct wlan_objmgr_psoc *psoc, + struct pno_scan_req_params *req) +{ + return wmi_unified_pno_start_cmd(psoc->tgt_if_handle, req); +} + +static QDF_STATUS +target_if_pno_stop(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id) +{ + return wmi_unified_pno_stop_cmd(psoc->tgt_if_handle, vdev_id); +} + +#else + +static inline QDF_STATUS +target_if_scan_register_pno_event_handler(struct wlan_objmgr_psoc *psoc, + void *arg) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +target_if_scan_unregister_pno_event_handler(struct wlan_objmgr_psoc *psoc, + void *arg) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +target_if_pno_start(struct wlan_objmgr_psoc *psoc, + struct pno_scan_req_params *req) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +target_if_pno_stop(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id) +{ + return QDF_STATUS_SUCCESS; +} +#endif + + QDF_STATUS target_if_scan_register_event_handler(struct wlan_objmgr_psoc *psoc, void *arg) { - return wmi_unified_register_event(psoc->tgt_if_handle, + QDF_STATUS status; + + status = wmi_unified_register_event(psoc->tgt_if_handle, wmi_scan_event_id, target_if_scan_event_handler); + if (status) { + target_if_err("Failed to register Scan match event cb"); + return QDF_STATUS_E_FAILURE; + } + + status = target_if_scan_register_pno_event_handler(psoc, arg); + + return status; } QDF_STATUS target_if_scan_unregister_event_handler(struct wlan_objmgr_psoc *psoc, void *arg) { - return wmi_unified_unregister_event(psoc->tgt_if_handle, + QDF_STATUS status; + + status = wmi_unified_unregister_event(psoc->tgt_if_handle, wmi_scan_event_id); + if (status) { + target_if_err("Failed to unregister Scan match event cb"); + return QDF_STATUS_E_FAILURE; + } + + status = target_if_scan_unregister_pno_event_handler(psoc, arg); + + return status; } QDF_STATUS @@ -117,6 +327,8 @@ target_if_register_scan_tx_ops(struct wlan_lmac_if_scan_tx_ops *scan) { scan->scan_start = target_if_scan_start; scan->scan_cancel = target_if_scan_cancel; + scan->pno_start = target_if_pno_start; + scan->pno_stop = target_if_pno_stop; scan->scan_reg_ev_handler = target_if_scan_register_event_handler; scan->scan_unreg_ev_handler = target_if_scan_unregister_event_handler; 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 cc7086490570..7274615315ea 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 @@ -64,6 +64,8 @@ struct wlan_lmac_if_mgmt_txrx_tx_ops { * struct wlan_lmac_if_scan_tx_ops - south bound tx function pointers for scan * @scan_start: function to start scan * @scan_cancel: function to cancel scan + * @pno_start: start pno scan + * @pno_stop: stop pno scan * @scan_reg_ev_handler: function to register for scan events * @scan_unreg_ev_handler: function to unregister for scan events * @@ -74,6 +76,10 @@ struct wlan_lmac_if_scan_tx_ops { struct scan_start_request *req); QDF_STATUS (*scan_cancel)(struct wlan_objmgr_psoc *psoc, struct scan_cancel_param *req); + QDF_STATUS (*pno_start)(struct wlan_objmgr_psoc *psoc, + struct pno_scan_req_params *req); + QDF_STATUS (*pno_stop)(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id); QDF_STATUS (*scan_reg_ev_handler)(struct wlan_objmgr_psoc *psoc, void *arg); QDF_STATUS (*scan_unreg_ev_handler)(struct wlan_objmgr_psoc *psoc, diff --git a/umac/scan/core/src/wlan_scan_main.c b/umac/scan/core/src/wlan_scan_main.c index 2c7d425817af..09475f4806cc 100644 --- a/umac/scan/core/src/wlan_scan_main.c +++ b/umac/scan/core/src/wlan_scan_main.c @@ -70,3 +70,53 @@ QDF_STATUS wlan_scan_psoc_destroyed_notification( return status; } + +QDF_STATUS wlan_scan_vdev_created_notification(struct wlan_objmgr_vdev *vdev, + void *arg_list) +{ + struct scan_vdev_obj *scan_vdev_obj; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + scan_vdev_obj = qdf_mem_malloc(sizeof(struct scan_vdev_obj)); + if (scan_vdev_obj == NULL) { + scm_err("Failed to allocate memory"); + return QDF_STATUS_E_NOMEM; + } + + /* Attach scan private date to psoc */ + status = wlan_objmgr_vdev_component_obj_attach(vdev, + WLAN_UMAC_COMP_SCAN, (void *)scan_vdev_obj, + QDF_STATUS_SUCCESS); + if (QDF_IS_STATUS_ERROR(status)) { + scm_err("Failed to attach vdev scan component"); + qdf_mem_free(scan_vdev_obj); + } else { + scm_info("vdev scan object attach successful"); + } + + return status; +} + +QDF_STATUS wlan_scan_vdev_destroyed_notification( + struct wlan_objmgr_vdev *vdev, + void *arg_list) +{ + void *scan_vdev_obj = NULL; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + scan_vdev_obj = wlan_objmgr_vdev_get_comp_private_obj(vdev, + WLAN_UMAC_COMP_SCAN); + if (!scan_vdev_obj) { + scm_err("Failed to detach scan in vdev ctx"); + return QDF_STATUS_E_FAILURE; + } + + status = wlan_objmgr_vdev_component_obj_detach(vdev, + WLAN_UMAC_COMP_SCAN, scan_vdev_obj); + if (QDF_IS_STATUS_ERROR(status)) + scm_err("Failed to detach vdev scan component"); + + qdf_mem_free(scan_vdev_obj); + + return status; +} diff --git a/umac/scan/core/src/wlan_scan_main.h b/umac/scan/core/src/wlan_scan_main.h index 89426e87f1f1..1af2771fc528 100644 --- a/umac/scan/core/src/wlan_scan_main.h +++ b/umac/scan/core/src/wlan_scan_main.h @@ -147,6 +147,39 @@ struct pdev_scan_info { qdf_time_t last_scan_time; }; +/** + * struct scan_vdev_obj - scan vdev obj + * @pno_match_evt_received: pno match received + * @pno_in_progress: pno in progress + */ +struct scan_vdev_obj { + bool pno_match_evt_received; + bool pno_in_progress; +}; + +/** + * struct pno_def_config - def configuration for PNO + * @channel_prediction: config PNO channel prediction feature status + * @top_k_num_of_channels: def top K number of channels are used for tanimoto + * distance calculation. + * @stationary_thresh: def threshold val to determine that STA is stationary. + * @pnoscan_adaptive_dwell_mode: def adaptive dwelltime mode for pno scan + * @channel_prediction_full_scan: def periodic timer upon which full scan needs + * to be triggered. + * @pno_wake_lock: pno wake lock + * @pno_cb: callback to call on PNO completion + */ +struct pno_def_config { + bool channel_prediction; + uint8_t top_k_num_of_channels; + uint8_t stationary_thresh; + enum scan_dwelltime_adaptive_mode adaptive_dwell_mode; + uint32_t channel_prediction_full_scan; + qdf_wake_lock_t pno_wake_lock; + struct cb_handler pno_cb; +}; + + /** * struct scan_default_params - default scan parameters to be used * @active_dwell: default active dwell time @@ -310,6 +343,7 @@ struct wlan_scan_obj { struct scan_requester_info requesters[WLAN_MAX_REQUESTORS]; struct global_scan_ev_handlers global_evhandlers; struct pdev_scan_info pdev_info[WLAN_UMAC_MAX_PDEVS]; + struct pno_def_config pno_cfg; }; /** @@ -354,6 +388,20 @@ wlan_vdev_get_scan_obj(struct wlan_objmgr_vdev *vdev) return wlan_pdev_get_scan_obj(pdev); } +/** + * wlan_get_vdev_scan_obj() - private API to get scan object vdev + * @vdev: vdev object + * + * Return: scan object + */ +static inline struct scan_vdev_obj * +wlan_get_vdev_scan_obj(struct wlan_objmgr_vdev *vdev) +{ + return (struct scan_vdev_obj *) + wlan_objmgr_vdev_get_comp_private_obj(vdev, + WLAN_UMAC_COMP_SCAN); +} + /** * wlan_scan_vdev_get_pdev_id)() - private API to get pdev id from vdev object * @vdev: vdev object @@ -434,4 +482,25 @@ QDF_STATUS wlan_scan_psoc_created_notification(struct wlan_objmgr_psoc *psoc, */ QDF_STATUS wlan_scan_psoc_destroyed_notification(struct wlan_objmgr_psoc *psoc, void *arg_list); + +/** + * wlan_scan_vdev_created_notification() - scan psoc create handler + * @vdev: vdev object + * @arg_list: Argument list + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_scan_vdev_created_notification(struct wlan_objmgr_vdev *vdev, + void *arg_list); + +/** + * wlan_scan_vdev_destroyed_notification() - scan psoc delete handler + * @vdev: vdev object + * @arg_list: Argument list + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_scan_vdev_destroyed_notification(struct wlan_objmgr_vdev *vdev, + void *arg_list); + #endif diff --git a/umac/scan/core/src/wlan_scan_manager.c b/umac/scan/core/src/wlan_scan_manager.c index 7ee03cc8f0f3..4d5ef4e6cdbf 100644 --- a/umac/scan/core/src/wlan_scan_manager.c +++ b/umac/scan/core/src/wlan_scan_manager.c @@ -26,6 +26,9 @@ #include "wlan_scan_main.h" #include "wlan_scan_manager.h" #include "wlan_utility.h" +#ifdef FEATURE_WLAN_SCAN_PNO +#include +#endif static QDF_STATUS scm_free_scan_request_mem(struct scan_start_request *req) @@ -485,6 +488,64 @@ scm_scan_cancel_req(struct scheduler_msg *msg) return status; } +#ifdef FEATURE_WLAN_SCAN_PNO +static QDF_STATUS +scm_pno_event_handler(struct wlan_objmgr_vdev *vdev, + struct scan_event *event) +{ + struct scan_vdev_obj *scan_vdev_obj; + struct wlan_scan_obj *scan_psoc_obj; + scan_event_handler pno_cb; + void *cb_arg; + + scan_vdev_obj = wlan_get_vdev_scan_obj(vdev); + scan_psoc_obj = wlan_vdev_get_scan_obj(vdev); + if (!scan_vdev_obj || !scan_psoc_obj) { + scm_err("null scan_vdev_obj %p scan_obj %p", + scan_vdev_obj, scan_psoc_obj); + return QDF_STATUS_E_INVAL; + } + + switch (event->type) { + case SCAN_EVENT_TYPE_NLO_COMPLETE: + if (!scan_vdev_obj->pno_match_evt_received) + return QDF_STATUS_SUCCESS; + qdf_wake_lock_release(&scan_psoc_obj->pno_cfg.pno_wake_lock, + WIFI_POWER_EVENT_WAKELOCK_PNO); + qdf_wake_lock_timeout_acquire( + &scan_psoc_obj->pno_cfg.pno_wake_lock, + SCAN_PNO_SCAN_COMPLETE_WAKE_LOCK_TIMEOUT); + scan_vdev_obj->pno_match_evt_received = false; + break; + case SCAN_EVENT_TYPE_NLO_MATCH: + scan_vdev_obj->pno_match_evt_received = true; + qdf_wake_lock_timeout_acquire( + &scan_psoc_obj->pno_cfg.pno_wake_lock, + SCAN_PNO_MATCH_WAKE_LOCK_TIMEOUT); + return QDF_STATUS_SUCCESS; + default: + return QDF_STATUS_E_INVAL; + } + qdf_spin_lock_bh(&scan_psoc_obj->lock); + pno_cb = scan_psoc_obj->pno_cfg.pno_cb.func; + cb_arg = scan_psoc_obj->pno_cfg.pno_cb.arg; + qdf_spin_unlock_bh(&scan_psoc_obj->lock); + + if (pno_cb) + pno_cb(vdev, event, cb_arg); + + return QDF_STATUS_SUCCESS; +} +#else + +static QDF_STATUS +scm_pno_event_handler(struct wlan_objmgr_vdev *vdev, + struct scan_event *event) +{ + return QDF_STATUS_SUCCESS; +} +#endif + QDF_STATUS scm_scan_event_handler(struct scheduler_msg *msg) { @@ -510,12 +571,17 @@ scm_scan_event_handler(struct scheduler_msg *msg) case SCAN_EVENT_TYPE_DEQUEUED: scm_release_serialization_command(vdev, event->scan_id); break; + case SCAN_EVENT_TYPE_NLO_COMPLETE: + case SCAN_EVENT_TYPE_NLO_MATCH: + scm_pno_event_handler(vdev, event); + goto exit; default: break; } /* Notify all interested parties */ scm_scan_post_event(vdev, event); +exit: /* free event info memory */ qdf_mem_free(event_info); wlan_objmgr_vdev_release_ref(vdev, WLAN_SCAN_ID); diff --git a/umac/scan/dispatcher/inc/wlan_scan_public_structs.h b/umac/scan/dispatcher/inc/wlan_scan_public_structs.h index b60e57a11898..00ff1ecf6527 100644 --- a/umac/scan/dispatcher/inc/wlan_scan_public_structs.h +++ b/umac/scan/dispatcher/inc/wlan_scan_public_structs.h @@ -692,6 +692,7 @@ struct mlme_update_info { * @SCAN_EVENT_TYPE_SUSPENDED: scan got suspended * @SCAN_EVENT_TYPE_RESUMED: scan resumed * @SCAN_EVENT_TYPE_NLO_COMPLETE: NLO completed + * @SCAN_EVENT_TYPE_NLO_MATCH: NLO match event * @SCAN_EVENT_TYPE_INVALID: invalid request * @SCAN_EVENT_TYPE_GPIO_TIMEOUT: gpio timeout * @SCAN_EVENT_TYPE_RADIO_MEASUREMENT_START: radio measurement start @@ -713,6 +714,7 @@ enum scan_event_type { SCAN_EVENT_TYPE_SUSPENDED, SCAN_EVENT_TYPE_RESUMED, SCAN_EVENT_TYPE_NLO_COMPLETE, + SCAN_EVENT_TYPE_NLO_MATCH, SCAN_EVENT_TYPE_INVALID, SCAN_EVENT_TYPE_GPIO_TIMEOUT, SCAN_EVENT_TYPE_RADIO_MEASUREMENT_START, @@ -819,10 +821,110 @@ enum scan_cb_type { SCAN_CB_TYPE_UPDATE_BCN, }; +/* Set PNO */ +#define SCAN_PNO_MAX_PLAN_REQUEST 2 +#define SCAN_PNO_MAX_NETW_CHANNELS_EX 60 +#define SCAN_PNO_MAX_SUPP_NETWORKS 16 +#define SCAN_PNO_DEF_SLOW_SCAN_MULTIPLIER 6 +#define SCAN_PNO_DEF_SCAN_TIMER_REPEAT 20 +#define SCAN_PNO_MATCH_WAKE_LOCK_TIMEOUT (5 * 1000) /* in msec */ +#ifdef CONFIG_SLUB_DEBUG_ON +#define SCAN_PNO_SCAN_COMPLETE_WAKE_LOCK_TIMEOUT (2 * 1000) /* in msec */ +#else +#define SCAN_PNO_SCAN_COMPLETE_WAKE_LOCK_TIMEOUT (1 * 1000) /* in msec */ +#endif /* CONFIG_SLUB_DEBUG_ON */ + +#define SCAN_PNO_CHANNEL_PREDICTION 0 +#define SCAN_TOP_K_NUM_OF_CHANNELS 3 +#define SCAN_STATIONARY_THRESHOLD 10 +#define SCAN_CHANNEL_PREDICTION_FULL_SCAN_MS 60000 +#define SCAN_ADAPTIVE_PNOSCAN_DWELL_MODE 0 + /** - * struct pno_scan_req_params - forward declaration + * enum ssid_bc_type - SSID broadcast type + * @SSID_BC_TYPE_UNKNOWN: Broadcast unknown + * @SSID_BC_TYPE_NORMAL: Broadcast normal + * @SSID_BC_TYPE_HIDDEN: Broadcast hidden */ -struct pno_scan_req_params; +enum ssid_bc_type { + SSID_BC_TYPE_UNKNOWN = 0, + SSID_BC_TYPE_NORMAL = 1, + SSID_BC_TYPE_HIDDEN = 2, +}; + +/** + * struct pno_nw_type - pno nw type + * @ssid: ssid + * @authentication: authentication type + * @encryption: encryption type + * @bcastNetwType: broadcast nw type + * @ucChannelCount: uc channel count + * @aChannels: pno channel + * @rssiThreshold: rssi threshold + */ +struct pno_nw_type { + struct wlan_ssid ssid; + uint32_t authentication; + uint32_t encryption; + uint32_t bc_new_type; + uint8_t channel_cnt; + uint32_t channels[SCAN_PNO_MAX_NETW_CHANNELS_EX]; + int32_t rssi_thresh; +}; + +/** + * struct pno_scan_req_params - PNO Scan request structure + * @networks_cnt: Number of networks + * @vdev_id: vdev id + * @fast_scan_period: Fast Scan period + * @slow_scan_period: Slow scan period + * @delay_start_time: delay in seconds to use before starting the first scan + * @fast_scan_max_cycles: Fast scan max cycles + * @pno_channel_prediction: PNO channel prediction feature status + * @uint32_t active_dwell_time: active dwell time + * @uint32_t passive_dwell_time: passive dwell time + * @top_k_num_of_channels: top K number of channels are used for tanimoto + * distance calculation. + * @stationary_thresh: threshold value to determine that the STA is stationary. + * @adaptive_dwell_mode: adaptive dwelltime mode for pno scan + * @channel_prediction_full_scan: periodic timer upon which a full scan needs + * to be triggered. + * @networks_list: Preferred network list + */ +struct pno_scan_req_params { + uint32_t networks_cnt; + uint32_t vdev_id; + uint32_t fast_scan_period; + uint32_t slow_scan_period; + uint32_t delay_start_time; + uint32_t fast_scan_max_cycles; + uint32_t active_dwell_time; + uint32_t passive_dwell_time; + uint32_t pno_channel_prediction; + uint32_t top_k_num_of_channels; + uint32_t stationary_thresh; + enum scan_dwelltime_adaptive_mode adaptive_dwell_mode; + uint32_t channel_prediction_full_scan; + struct pno_nw_type networks_list[SCAN_PNO_MAX_SUPP_NETWORKS]; +}; + +/** + * struct pno_user_cfg - user configuration required for PNO + * @channel_prediction: config PNO channel prediction feature status + * @top_k_num_of_channels: def top K number of channels are used for tanimoto + * distance calculation. + * @stationary_thresh: def threshold val to determine that STA is stationary. + * @pnoscan_adaptive_dwell_mode: def adaptive dwelltime mode for pno scan + * @channel_prediction_full_scan: def periodic timer upon which full scan needs + * to be triggered. + */ +struct pno_user_cfg { + bool channel_prediction; + uint8_t top_k_num_of_channels; + uint8_t stationary_thresh; + enum scan_dwelltime_adaptive_mode adaptive_dwell_mode; + uint32_t channel_prediction_full_scan; +}; /** * struct scan_user_cfg - user configuration required for for scan @@ -835,6 +937,7 @@ struct pno_scan_req_params; * @conc_idle_time: default concurrent idle time * @scan_cache_aging_time: default scan cache aging time * @scan_dwell_time_mode: Adaptive dweltime mode + * @pno_cfg: Pno related config params */ struct scan_user_cfg { uint32_t active_dwell; @@ -846,6 +949,7 @@ struct scan_user_cfg { uint32_t conc_idle_time; uint32_t scan_cache_aging_time; enum scan_dwelltime_adaptive_mode scan_dwell_time_mode; + struct pno_user_cfg pno_cfg; }; /** diff --git a/umac/scan/dispatcher/inc/wlan_scan_tgt_api.h b/umac/scan/dispatcher/inc/wlan_scan_tgt_api.h index d3506b9b61ef..985f40c0292d 100644 --- a/umac/scan/dispatcher/inc/wlan_scan_tgt_api.h +++ b/umac/scan/dispatcher/inc/wlan_scan_tgt_api.h @@ -48,37 +48,6 @@ QDF_STATUS tgt_scan_bcn_probe_rx_callback(struct wlan_objmgr_psoc *psoc, struct mgmt_rx_event_params *rx_param, enum mgmt_frame_type frm_type); -/** - * tgt_scan_nlo_complete_evt_handler() - The callbeack registered - * to WMI for PNO complete - * @handle: psoc handle - * @event: event handler - * @len: length of data - * - * This function handles NLO scan completion event. - * - * Return: 0 for success or error code. - */ - -QDF_STATUS -tgt_scan_nlo_complete_evt_handler(void *handle, uint8_t *event, - uint32_t len); - -/** - * tgt_nlo_match_evt_handler() - nlo match event handler - * @handle: psoc handle - * @event: event data - * @len: data length - * - * Record NLO match event comes from FW. It's a indication that - * one of the profile is matched. - * - * Return: 0 for success or error code. - */ -QDF_STATUS -tgt_nlo_match_evt_handler(void *handle, uint8_t *event, - uint32_t len); - /** * tgt_scan_event_handler() - The callbeack registered to WMI for scan events * @psoc: psoc handle @@ -93,6 +62,30 @@ QDF_STATUS tgt_scan_event_handler(struct wlan_objmgr_psoc *psoc, struct scan_event_info *event_info); +#ifdef FEATURE_WLAN_SCAN_PNO + +/** + * tgt_scan_pno_start() - invoke lmac send PNO start req + * @vdev: vdev pointer + * @req: pno req params + * + * Return: 0 for success or error code. + */ +QDF_STATUS tgt_scan_pno_start(struct wlan_objmgr_vdev *vdev, + struct pno_scan_req_params *req); + +/** + * tgt_scan_pno_stop() - invoke lmac send PNO stop req + * @vdev: vdev pointer + * @vdev_id: pno req params + * + * Return: 0 for success or error code. + */ +QDF_STATUS tgt_scan_pno_stop(struct wlan_objmgr_vdev *vdev, + uint8_t vdev_id); + +#endif + /** * tgt_scan_start() - invoke lmac scan start * @req: scan request object diff --git a/umac/scan/dispatcher/inc/wlan_scan_ucfg_api.h b/umac/scan/dispatcher/inc/wlan_scan_ucfg_api.h index aeb52326832c..018dbac44c52 100644 --- a/umac/scan/dispatcher/inc/wlan_scan_ucfg_api.h +++ b/umac/scan/dispatcher/inc/wlan_scan_ucfg_api.h @@ -88,7 +88,79 @@ uint8_t *ucfg_get_scan_requester_name(struct wlan_objmgr_psoc *psoc, wlan_scan_id ucfg_scan_get_scan_id(struct wlan_objmgr_psoc *psoc); +#ifdef FEATURE_WLAN_SCAN_PNO +/** + * ucfg_scan_pno_start() - Public API to start PNO + * @vdev: vdev pointer + * @req: pno req params + * + * Return: 0 for success or error code. + */ +QDF_STATUS ucfg_scan_pno_start(struct wlan_objmgr_vdev *vdev, +struct pno_scan_req_params *req); +/** + * ucfg_scan_pno_stop() - Public API to stop PNO + * @vdev: vdev pointer + * @req: pno req params + * + * Return: 0 for success or error code. + */ +QDF_STATUS ucfg_scan_pno_stop(struct wlan_objmgr_vdev *vdev); + +/** + * ucfg_scan_get_pno_in_progress() - Public API to check if pno is in progress + * @vdev: vdev pointer + * + * Return: true if pno in progress else false. + */ +bool ucfg_scan_get_pno_in_progress(struct wlan_objmgr_vdev *vdev); + +/** + * ucfg_scan_get_pno_match() - Public API to check if pno matched + * @vdev: vdev pointer + * + * Return: true if pno matched else false. + */ +bool ucfg_scan_get_pno_match(struct wlan_objmgr_vdev *vdev); + +/** + * ucfg_scan_register_pno_cb() - register pno cb + * @psoc: psoc object + * @event_cb: callback function pointer + * @arg: argument to @event_cb + * + * Return: QDF_STATUS + */ +QDF_STATUS +ucfg_scan_register_pno_cb(struct wlan_objmgr_psoc *psoc, + scan_event_handler event_cb, void *arg); + +/** + * ucfg_scan_get_pno_def_params() - get the defaults pno params + * @vdev: vdev object + * @req: pno request object + * + * Return: QDF_STATUS_SUCCESS or error code + */ +QDF_STATUS +ucfg_scan_get_pno_def_params(struct wlan_objmgr_vdev *vdev, + struct pno_scan_req_params *req); + +#else + +static inline bool +ucfg_scan_get_pno_in_progress(struct wlan_objmgr_vdev *vdev) +{ + return false; +} + +static inline bool +ucfg_scan_get_pno_match(struct wlan_objmgr_vdev *vdev) +{ + return false; +} +#endif /* FEATURE_WLAN_SCAN_PNO */ /** * ucfg_scan_start() - Public API to start a scan * @req: start scan req params diff --git a/umac/scan/dispatcher/src/wlan_scan_tgt_api.c b/umac/scan/dispatcher/src/wlan_scan_tgt_api.c index c4cef8c0c5d4..169b7582beba 100644 --- a/umac/scan/dispatcher/src/wlan_scan_tgt_api.c +++ b/umac/scan/dispatcher/src/wlan_scan_tgt_api.c @@ -59,28 +59,39 @@ wlan_vdev_get_scan_rxops(struct wlan_objmgr_vdev *vdev) return &((psoc->soc_cb.rx_ops.scan)); } -QDF_STATUS -tgt_scan_nlo_complete_evt_handler(void *handle, uint8_t *event, - uint32_t len) +#ifdef FEATURE_WLAN_SCAN_PNO + +QDF_STATUS tgt_scan_pno_start(struct wlan_objmgr_vdev *vdev, + struct pno_scan_req_params *req) { - /* - * Convert the tlv/non tlv data to struct scan_event - * (SCM_EVENT_NLO_COMPLETE) (same as WIN does by calling a win API) and - * Post msg to target_if queue - */ + struct wlan_lmac_if_scan_tx_ops *scan_ops = NULL; + struct wlan_objmgr_psoc *psoc = wlan_vdev_get_psoc(vdev); + + scan_ops = wlan_vdev_get_scan_txops(vdev); + /* invoke wmi_unified_pno_start_cmd() */ + QDF_ASSERT(scan_ops->pno_start); + if (scan_ops->pno_start) + return scan_ops->pno_start(psoc, req); + return QDF_STATUS_SUCCESS; } -QDF_STATUS -tgt_nlo_match_evt_handler(void *handle, uint8_t *event, - uint32_t len) +QDF_STATUS tgt_scan_pno_stop(struct wlan_objmgr_vdev *vdev, + uint8_t vdev_id) { - /* - * Convert the tlv/non tlv data to comman data - * and set the pno match received flag in vdev scan info - */ + struct wlan_lmac_if_scan_tx_ops *scan_ops = NULL; + struct wlan_objmgr_psoc *psoc = wlan_vdev_get_psoc(vdev); + + scan_ops = wlan_vdev_get_scan_txops(vdev); + + /* invoke wmi_unified_pno_stop_cmd() */ + QDF_ASSERT(scan_ops->pno_stop); + if (scan_ops->pno_stop) + return scan_ops->pno_stop(psoc, vdev_id); + return QDF_STATUS_SUCCESS; } +#endif QDF_STATUS tgt_scan_start(struct scan_start_request *req) diff --git a/umac/scan/dispatcher/src/wlan_scan_ucfg_api.c b/umac/scan/dispatcher/src/wlan_scan_ucfg_api.c index bdd36ac27786..173d1d4d1661 100644 --- a/umac/scan/dispatcher/src/wlan_scan_ucfg_api.c +++ b/umac/scan/dispatcher/src/wlan_scan_ucfg_api.c @@ -78,12 +78,33 @@ QDF_STATUS ucfg_scan_init(void) status = wlan_objmgr_register_psoc_destroy_handler(WLAN_UMAC_COMP_SCAN, wlan_scan_psoc_destroyed_notification, NULL); + if (QDF_IS_STATUS_ERROR(status)) { + scm_err("Failed to create psoc delete handler"); + goto fail_psoc_destroy; + } + scm_info("scan psoc create and delete handler registered with objmgr"); + + status = wlan_objmgr_register_vdev_create_handler(WLAN_UMAC_COMP_SCAN, + wlan_scan_vdev_created_notification, NULL); + if (QDF_IS_STATUS_ERROR(status)) { + scm_err("Failed to register vdev create handler"); + goto fail_pdev_create; + } + + status = wlan_objmgr_register_vdev_destroy_handler(WLAN_UMAC_COMP_SCAN, + wlan_scan_vdev_destroyed_notification, NULL); if (QDF_IS_STATUS_SUCCESS(status)) { - scm_info("scan create and delete handler registered with objmgr"); + scm_info("scan vdev create and delete handler registered with objmgr"); return QDF_STATUS_SUCCESS; } - scm_err("Failed to create psoc delete handler"); + scm_err("Failed to destroy vdev delete handler"); + wlan_objmgr_unregister_vdev_create_handler(WLAN_UMAC_COMP_SCAN, + wlan_scan_vdev_created_notification, NULL); +fail_pdev_create: + wlan_objmgr_unregister_psoc_destroy_handler(WLAN_UMAC_COMP_SCAN, + wlan_scan_psoc_destroyed_notification, NULL); +fail_psoc_destroy: wlan_objmgr_unregister_psoc_create_handler(WLAN_UMAC_COMP_SCAN, wlan_scan_psoc_created_notification, NULL); fail_create_psoc: @@ -105,9 +126,206 @@ QDF_STATUS ucfg_scan_deinit(void) if (status != QDF_STATUS_SUCCESS) scm_err("Failed to unregister psoc delete handler"); + status = wlan_objmgr_unregister_vdev_create_handler(WLAN_UMAC_COMP_SCAN, + wlan_scan_vdev_created_notification, NULL); + if (status != QDF_STATUS_SUCCESS) + scm_err("Failed to unregister vdev create handler"); + + status = wlan_objmgr_unregister_vdev_destroy_handler( + WLAN_UMAC_COMP_SCAN, + wlan_scan_vdev_destroyed_notification, NULL); + if (status != QDF_STATUS_SUCCESS) + scm_err("Failed to unregister vdev delete handler"); + return status; } +#ifdef FEATURE_WLAN_SCAN_PNO + +QDF_STATUS ucfg_scan_pno_start(struct wlan_objmgr_vdev *vdev, + struct pno_scan_req_params *req) +{ + struct scan_vdev_obj *scan_vdev_obj; + QDF_STATUS status; + + scan_vdev_obj = wlan_get_vdev_scan_obj(vdev); + if (!scan_vdev_obj) { + scm_err("null scan_vdev_obj"); + return QDF_STATUS_E_INVAL; + } + if (scan_vdev_obj->pno_in_progress) { + scm_err("pno already in progress"); + return QDF_STATUS_E_ALREADY; + } + + status = tgt_scan_pno_start(vdev, req); + if (QDF_IS_STATUS_ERROR(status)) + scm_err("pno start failed"); + else + scan_vdev_obj->pno_in_progress = true; + + return status; +} + +QDF_STATUS ucfg_scan_pno_stop(struct wlan_objmgr_vdev *vdev) +{ + struct scan_vdev_obj *scan_vdev_obj; + QDF_STATUS status; + + scan_vdev_obj = wlan_get_vdev_scan_obj(vdev); + if (!scan_vdev_obj) { + scm_err("null scan_vdev_obj"); + return QDF_STATUS_E_INVAL; + } + if (!scan_vdev_obj->pno_in_progress) { + scm_err("pno already stopped"); + return QDF_STATUS_E_ALREADY; + } + + status = tgt_scan_pno_stop(vdev, wlan_vdev_get_id(vdev)); + if (QDF_IS_STATUS_ERROR(status)) + scm_err("pno start failed"); + else + scan_vdev_obj->pno_in_progress = false; + + return status; +} + +bool ucfg_scan_get_pno_in_progress(struct wlan_objmgr_vdev *vdev) +{ + struct scan_vdev_obj *scan_vdev_obj; + + scan_vdev_obj = wlan_get_vdev_scan_obj(vdev); + if (!scan_vdev_obj) { + scm_err("null scan_vdev_obj"); + return false; + } + + return scan_vdev_obj->pno_in_progress; +} + +bool ucfg_scan_get_pno_match(struct wlan_objmgr_vdev *vdev) +{ + struct scan_vdev_obj *scan_vdev_obj; + + scan_vdev_obj = wlan_get_vdev_scan_obj(vdev); + if (!scan_vdev_obj) { + scm_err("null scan_vdev_obj"); + return false; + } + + return scan_vdev_obj->pno_match_evt_received; +} + +static QDF_STATUS +wlan_pno_global_init(struct pno_def_config *pno_def) +{ + qdf_wake_lock_create(&pno_def->pno_wake_lock, "wlan_pno_wl"); + pno_def->channel_prediction = SCAN_PNO_CHANNEL_PREDICTION; + pno_def->top_k_num_of_channels = SCAN_TOP_K_NUM_OF_CHANNELS; + pno_def->stationary_thresh = SCAN_STATIONARY_THRESHOLD; + pno_def->channel_prediction_full_scan = + SCAN_CHANNEL_PREDICTION_FULL_SCAN_MS; + pno_def->adaptive_dwell_mode = SCAN_ADAPTIVE_PNOSCAN_DWELL_MODE; + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS +wlan_pno_global_deinit(struct pno_def_config *pno_def) +{ + qdf_wake_lock_destroy(&pno_def->pno_wake_lock); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_scan_get_pno_def_params(struct wlan_objmgr_vdev *vdev, + struct pno_scan_req_params *req) +{ + struct scan_default_params *scan_def; + struct wlan_scan_obj *scan = wlan_vdev_get_scan_obj(vdev); + struct pno_def_config *pno_def; + + if (!vdev | !req | !scan) { + scm_err("vdev: 0x%p, req: 0x%p scan_obj: 0x%p", + vdev, req, scan); + return QDF_STATUS_E_INVAL; + } + + scan_def = wlan_vdev_get_def_scan_params(vdev); + pno_def = &scan->pno_cfg; + + req->active_dwell_time = scan_def->active_dwell; + req->passive_dwell_time = scan_def->passive_dwell; + + req->adaptive_dwell_mode = pno_def->adaptive_dwell_mode; + + req->pno_channel_prediction = pno_def->adaptive_dwell_mode; + req->top_k_num_of_channels = pno_def->top_k_num_of_channels; + req->stationary_thresh = pno_def->stationary_thresh; + req->channel_prediction_full_scan = + pno_def->channel_prediction_full_scan; + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS ucfg_scan_update_pno_config(struct pno_def_config *pno, + struct pno_user_cfg *pno_cfg) +{ + pno->channel_prediction = pno_cfg->channel_prediction; + pno->top_k_num_of_channels = pno_cfg->top_k_num_of_channels; + pno->stationary_thresh = pno_cfg->stationary_thresh; + pno->adaptive_dwell_mode = pno_cfg->adaptive_dwell_mode; + pno->channel_prediction_full_scan = + pno_cfg->channel_prediction_full_scan; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_scan_register_pno_cb(struct wlan_objmgr_psoc *psoc, + scan_event_handler event_cb, void *arg) +{ + struct wlan_scan_obj *scan; + + if (!psoc) { + scm_err("null psoc"); + return QDF_STATUS_E_INVAL; + } + scan = wlan_psoc_get_scan_obj(psoc); + qdf_spin_lock_bh(&scan->lock); + scan->pno_cfg.pno_cb.func = event_cb; + scan->pno_cfg.pno_cb.arg = arg; + qdf_spin_unlock_bh(&scan->lock); + scm_info("event_cb: 0x%p, arg: 0x%p", event_cb, arg); + + return QDF_STATUS_SUCCESS; +} + +#else + +static inline QDF_STATUS +wlan_pno_global_init(struct pno_def_config *pno_def) +{ + return QDF_STATUS_SUCCESS; +} +static inline QDF_STATUS +wlan_pno_global_deinit(struct pno_def_config *pno_def) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS +ucfg_scan_update_pno_config(struct pno_def_config *pno, + struct pno_user_cfg *pno_cfg) +{ + return QDF_STATUS_SUCCESS; +} + +#endif + + QDF_STATUS ucfg_scan_start(struct scan_start_request *req) { @@ -378,7 +596,7 @@ wlan_scan_global_init(struct wlan_scan_obj *scan_obj) /* init scan id seed */ qdf_atomic_init(&scan_obj->scan_ids); - return QDF_STATUS_SUCCESS; + return wlan_pno_global_init(&scan_obj->pno_cfg); } static QDF_STATUS @@ -713,7 +931,8 @@ QDF_STATUS ucfg_scan_update_user_config(struct wlan_objmgr_psoc *psoc, scan_def->scan_cache_aging_time = scan_cfg->scan_cache_aging_time; scan_def->adaptive_dwell_time_mode = scan_cfg->scan_dwell_time_mode; - return QDF_STATUS_SUCCESS; + return ucfg_scan_update_pno_config(&scan_obj->pno_cfg, + &scan_cfg->pno_cfg); } QDF_STATUS @@ -754,6 +973,7 @@ ucfg_scan_psoc_close(struct wlan_objmgr_psoc *psoc) return QDF_STATUS_E_FAILURE; } qdf_spinlock_destroy(&scan_obj->lock); + wlan_pno_global_deinit(&scan_obj->pno_cfg); return QDF_STATUS_SUCCESS; } diff --git a/umac/scan/dispatcher/src/wlan_scan_utils_api.c b/umac/scan/dispatcher/src/wlan_scan_utils_api.c index 8f76d9e2e589..eb940b4a5f40 100644 --- a/umac/scan/dispatcher/src/wlan_scan_utils_api.c +++ b/umac/scan/dispatcher/src/wlan_scan_utils_api.c @@ -42,6 +42,7 @@ util_scan_get_ev_type_name(enum scan_event_type type) [SCAN_EVENT_TYPE_SUSPENDED] = "SUSPENDED", [SCAN_EVENT_TYPE_RESUMED] = "RESUMED", [SCAN_EVENT_TYPE_NLO_COMPLETE] = "NLO_COMPLETE", + [SCAN_EVENT_TYPE_NLO_MATCH] = "NLO_MATCH", [SCAN_EVENT_TYPE_INVALID] = "INVALID", [SCAN_EVENT_TYPE_GPIO_TIMEOUT] = "GPIO_TIMEOUT", [SCAN_EVENT_TYPE_RADIO_MEASUREMENT_START] =