diff --git a/core/hdd/src/wlan_hdd_cfg80211.c b/core/hdd/src/wlan_hdd_cfg80211.c index 3ae8264033e6..880fee967b5b 100644 --- a/core/hdd/src/wlan_hdd_cfg80211.c +++ b/core/hdd/src/wlan_hdd_cfg80211.c @@ -624,6 +624,27 @@ static const struct wiphy_wowlan_support wowlan_support_cfg80211_init = { #define PARAM_TDLS_FEATURE_SUPPORT \ QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_FEATURES_SUPPORTED +/** + * hdd_add_channel_switch_support()- Adds Channel Switch flag if supported + * @flags: Pointer to the flags to Add channel switch flag. + * + * This Function adds Channel Switch support flag, if channel switch is + * supported by kernel. + * Return: void. + */ +#ifdef CHANNEL_SWITCH_SUPPORTED +static inline void hdd_add_channel_switch_support(uint32_t *flags) +{ + flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH; + return; +} +#else +static inline void hdd_add_channel_switch_support(uint32_t *flags) +{ + return; +} +#endif + /** * __wlan_hdd_cfg80211_get_tdls_capabilities() - Provide TDLS Capabilites. * @wiphy: WIPHY structure pointer @@ -4956,6 +4977,8 @@ int wlan_hdd_cfg80211_init(struct device *dev, wiphy->features |= NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE; #endif + hdd_add_channel_switch_support(&wiphy->flags); + EXIT(); return 0; } @@ -10551,6 +10574,72 @@ wlan_hdd_cfg80211_set_ap_channel_width(struct wiphy *wiphy, } #endif +#ifdef CHANNEL_SWITCH_SUPPORTED +/** + * __wlan_hdd_cfg80211_channel_switch()- function to switch + * channel in SAP/GO + * @wiphy: wiphy pointer + * @dev: dev pointer. + * @csa_params: Change channel params + * + * This function is called to switch channel in SAP/GO + * + * Return: 0 if success else return non zero + */ +static int __wlan_hdd_cfg80211_channel_switch(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_csa_settings *csa_params) +{ + hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev); + hdd_context_t *hdd_ctx; + uint8_t channel; + uint16_t freq; + int ret; + + hddLog(LOG1, FL("Set Freq %d"), + csa_params->chandef.chan->center_freq); + + hdd_ctx = WLAN_HDD_GET_CTX(adapter); + ret = wlan_hdd_validate_context(hdd_ctx); + + if (0 != ret) + return ret; + + if ((WLAN_HDD_P2P_GO != adapter->device_mode) && + (WLAN_HDD_SOFTAP != adapter->device_mode)) + return -ENOTSUPP; + + freq = csa_params->chandef.chan->center_freq; + channel = cds_freq_to_chan(freq); + + ret = hdd_softap_set_channel_change(dev, channel); + return ret; +} + +/** + * wlan_hdd_cfg80211_channel_switch()- function to switch + * channel in SAP/GO + * @wiphy: wiphy pointer + * @dev: dev pointer. + * @csa_params: Change channel params + * + * This function is called to switch channel in SAP/GO + * + * Return: 0 if success else return non zero + */ +static int wlan_hdd_cfg80211_channel_switch(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_csa_settings *csa_params) +{ + int ret; + + cds_ssr_protect(__func__); + ret = __wlan_hdd_cfg80211_channel_switch(wiphy, dev, csa_params); + cds_ssr_unprotect(__func__); + return ret; +} +#endif + /** * wlan_hdd_convert_nl_iftype_to_hdd_type() - provides the type * translation from NL to policy manager type @@ -10708,4 +10797,7 @@ static struct cfg80211_ops wlan_hdd_cfg80211_ops = { .set_ap_chanwidth = wlan_hdd_cfg80211_set_ap_channel_width, #endif .dump_survey = wlan_hdd_cfg80211_dump_survey, +#ifdef CHANNEL_SWITCH_SUPPORTED + .channel_switch = wlan_hdd_cfg80211_channel_switch, +#endif }; diff --git a/core/hdd/src/wlan_hdd_cfg80211.h b/core/hdd/src/wlan_hdd_cfg80211.h index 23e604a56c77..28fc309011fd 100644 --- a/core/hdd/src/wlan_hdd_cfg80211.h +++ b/core/hdd/src/wlan_hdd_cfg80211.h @@ -101,6 +101,11 @@ #define MAX_CHANNEL (NUM_24GHZ_CHANNELS + NUM_5GHZ_CHANNELS) #define MAX_SCAN_SSID 10 +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)) \ + || defined(BACKPORTED_CHANNEL_SWITCH_PRESENT) +#define CHANNEL_SWITCH_SUPPORTED +#endif + /** * typedef struct qcom_ie_age - age ie * diff --git a/core/hdd/src/wlan_hdd_hostapd.c b/core/hdd/src/wlan_hdd_hostapd.c index ed0119529662..17859ba9f50f 100644 --- a/core/hdd/src/wlan_hdd_hostapd.c +++ b/core/hdd/src/wlan_hdd_hostapd.c @@ -1693,6 +1693,16 @@ CDF_STATUS hdd_hostapd_sap_event_cb(tpSap_Event pSapEvent, if (!pHddCtx->config->force_sap_acs) wlan_hdd_cfg80211_acs_ch_select_evt(pHostapdAdapter); return CDF_STATUS_SUCCESS; + case eSAP_ECSA_CHANGE_CHAN_IND: + hddLog(LOG1, + FL("Channel change indication from peer for channel %d"), + pSapEvent->sapevt.sap_chan_cng_ind.new_chan); + if (hdd_softap_set_channel_change(dev, + pSapEvent->sapevt.sap_chan_cng_ind.new_chan)) + return CDF_STATUS_E_FAILURE; + else + return CDF_STATUS_SUCCESS; + default: hddLog(LOG1, "SAP message is not handled"); goto stopbss; @@ -1883,7 +1893,6 @@ int hdd_softap_unpack_ie(tHalHandle halHandle, * * Return: 0 for success, non zero for failure */ -static int hdd_softap_set_channel_change(struct net_device *dev, int target_channel) { CDF_STATUS status; @@ -1900,7 +1909,6 @@ int hdd_softap_set_channel_change(struct net_device *dev, int target_channel) ret = wlan_hdd_validate_context(pHddCtx); if (ret) { hddLog(LOGE, FL("invalid HDD context")); - ret = -EBUSY; return ret; } @@ -2251,9 +2259,10 @@ static __iw_softap_setparam(struct net_device *dev, break; case QCSAP_PARAM_SET_CHANNEL_CHANGE: - if (WLAN_HDD_SOFTAP == pHostapdAdapter->device_mode) { + if ((WLAN_HDD_SOFTAP == pHostapdAdapter->device_mode) || + (WLAN_HDD_P2P_GO == pHostapdAdapter->device_mode)) { hddLog(LOG1, - "SET SAP Channel Change to new channel= %d", + "SET Channel Change to new channel= %d", set_value); ret = hdd_softap_set_channel_change(dev, set_value); } else { diff --git a/core/hdd/src/wlan_hdd_hostapd.h b/core/hdd/src/wlan_hdd_hostapd.h index 02965d8457f7..f4f72c82d658 100644 --- a/core/hdd/src/wlan_hdd_hostapd.h +++ b/core/hdd/src/wlan_hdd_hostapd.h @@ -57,6 +57,9 @@ CDF_STATUS hdd_unregister_hostapd(hdd_adapter_t *pAdapter, bool rtnl_held); eCsrAuthType hdd_translate_rsn_to_csr_auth_type(uint8_t auth_suite[4]); +int hdd_softap_set_channel_change(struct net_device *dev, + int target_channel); + eCsrEncryptionType hdd_translate_rsn_to_csr_encryption_type(uint8_t cipher_suite[4]); diff --git a/core/hdd/src/wlan_hdd_wext.c b/core/hdd/src/wlan_hdd_wext.c index 047131f20456..bd22dcb139f8 100644 --- a/core/hdd/src/wlan_hdd_wext.c +++ b/core/hdd/src/wlan_hdd_wext.c @@ -215,6 +215,7 @@ static const hdd_freq_chan_map_t freq_chan_map[] = { #define WE_CLEAR_STATS 86 /* Private sub ioctl for starting/stopping the profiling */ #define WE_START_FW_PROFILE 87 +#define WE_SET_CHANNEL 88 /* Private ioctls and their sub-ioctls */ #define WLAN_PRIV_SET_NONE_GET_INT (SIOCIWFIRSTPRIV + 1) @@ -4938,6 +4939,7 @@ static int __iw_setint_getnone(struct net_device *dev, int set_value = value[1]; int ret; int enable_pbm, enable_mp; + CDF_STATUS status; INIT_COMPLETION(pWextState->completion_var); memset(&smeConfig, 0x00, sizeof(smeConfig)); @@ -6122,6 +6124,31 @@ static int __iw_setint_getnone(struct net_device *dev, set_value, DBG_CMD); break; } + case WE_SET_CHANNEL: + { + hddLog(LOG1, "Set Channel %d Session ID %d mode %d", set_value, + pAdapter->sessionId, pAdapter->device_mode); + + if ((WLAN_HDD_INFRA_STATION == pAdapter->device_mode) || + (WLAN_HDD_P2P_CLIENT == pAdapter->device_mode)) { + + status = sme_ext_change_channel(hHal, + set_value, pAdapter->sessionId); + if (status != CDF_STATUS_SUCCESS) { + hddLog(LOGE, + FL("Error in change channel status %d"), + status); + ret = -EINVAL; + } + } else { + hddLog(LOGE, + FL("change channel not supported for device mode %d"), + pAdapter->device_mode); + ret = -EINVAL; + } + break; + } + default: { hddLog(LOGE, "%s: Invalid sub command %d", __func__, @@ -10065,6 +10092,10 @@ static const struct iw_priv_args we_private_args[] = { IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "startProfile"}, + {WE_SET_CHANNEL, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "setChanChange" }, + {WLAN_PRIV_SET_NONE_GET_INT, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,