| /* |
| * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. |
| * |
| * Permission to use, copy, modify, and/or distribute this software for |
| * any purpose with or without fee is hereby granted, provided that the |
| * above copyright notice and this permission notice appear in all |
| * copies. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL |
| * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE |
| * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL |
| * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR |
| * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER |
| * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR |
| * PERFORMANCE OF THIS SOFTWARE. |
| */ |
| |
| /** |
| * DOC: wlan_hdd_hostapd.c |
| * |
| * WLAN Host Device Driver implementation |
| */ |
| |
| /* Include Files */ |
| |
| #include <linux/version.h> |
| #include <linux/module.h> |
| #include <linux/kernel.h> |
| #include <linux/init.h> |
| #include <linux/wireless.h> |
| #include <linux/semaphore.h> |
| #include <linux/compat.h> |
| #include <cdp_txrx_cmn.h> |
| #include <cds_api.h> |
| #include <cds_sched.h> |
| #include <linux/etherdevice.h> |
| #include "osif_sync.h" |
| #include <linux/ethtool.h> |
| #include <wlan_hdd_includes.h> |
| #include <qc_sap_ioctl.h> |
| #include "osif_sync.h" |
| #include <wlan_hdd_hostapd.h> |
| #include <wlan_hdd_hostapd_wext.h> |
| #include <wlan_hdd_green_ap.h> |
| #include <sap_api.h> |
| #include <sap_internal.h> |
| #include <wlan_hdd_softap_tx_rx.h> |
| #include <wlan_hdd_main.h> |
| #include <wlan_hdd_ioctl.h> |
| #include <wlan_hdd_stats.h> |
| #include <linux/netdevice.h> |
| #include <linux/rtnetlink.h> |
| #include <linux/mmc/sdio_func.h> |
| #include "wlan_hdd_p2p.h" |
| #include <wlan_hdd_ipa.h> |
| #include "wni_cfg.h" |
| #include "wlan_hdd_misc.h" |
| #include <cds_utils.h> |
| #include "pld_common.h" |
| #include "wma.h" |
| #ifdef WLAN_DEBUG |
| #include "wma_api.h" |
| #endif |
| #include "wlan_hdd_trace.h" |
| #include "qdf_str.h" |
| #include "qdf_types.h" |
| #include "qdf_trace.h" |
| #include "wlan_hdd_cfg.h" |
| #include "wlan_policy_mgr_api.h" |
| #include "wlan_hdd_tsf.h" |
| #include <cdp_txrx_misc.h> |
| #include "wlan_hdd_object_manager.h" |
| #include <qca_vendor.h> |
| #include <cds_api.h> |
| #include "wlan_hdd_he.h" |
| #include "wlan_dfs_tgt_api.h" |
| #include <wlan_reg_ucfg_api.h> |
| #include "wlan_utility.h" |
| #include <wlan_p2p_ucfg_api.h> |
| #include "sir_api.h" |
| #include "wlan_policy_mgr_ucfg.h" |
| #include "sme_api.h" |
| #include "wlan_hdd_regulatory.h" |
| #include <wlan_ipa_ucfg_api.h> |
| #include <wlan_cp_stats_mc_ucfg_api.h> |
| #include "wlan_mlme_ucfg_api.h" |
| #include "cfg_ucfg_api.h" |
| #include "wlan_crypto_global_api.h" |
| #include "wlan_action_oui_ucfg_api.h" |
| #include "wlan_fwol_ucfg_api.h" |
| #include "nan_ucfg_api.h" |
| #include <wlan_reg_services_api.h> |
| |
| #define ACS_SCAN_EXPIRY_TIMEOUT_S 4 |
| |
| /* Defines the BIT position of HT caps is support mode field of stainfo */ |
| #define HDD_HT_CAPS_PRESENT 0 |
| /* Defines the BIT position of VHT caps is support mode field of stainfo */ |
| #define HDD_VHT_CAPS_PRESENT 1 |
| /* Defines the BIT position of HE caps is support mode field of stainfo */ |
| #define HDD_HE_CAPS_PRESENT 2 |
| |
| /* |
| * 11B, 11G Rate table include Basic rate and Extended rate |
| * The IDX field is the rate index |
| * The HI field is the rate when RSSI is strong or being ignored |
| * (in this case we report actual rate) |
| * The MID field is the rate when RSSI is moderate |
| * (in this case we cap 11b rates at 5.5 and 11g rates at 24) |
| * The LO field is the rate when RSSI is low |
| * (in this case we don't report rates, actual current rate used) |
| */ |
| static const struct index_data_rate_type supported_data_rate[] = { |
| /* IDX HI HM LM LO (RSSI-based index */ |
| {2, { 10, 10, 10, 0} }, |
| {4, { 20, 20, 10, 0} }, |
| {11, { 55, 20, 10, 0} }, |
| {12, { 60, 55, 20, 0} }, |
| {18, { 90, 55, 20, 0} }, |
| {22, {110, 55, 20, 0} }, |
| {24, {120, 90, 60, 0} }, |
| {36, {180, 120, 60, 0} }, |
| {44, {220, 180, 60, 0} }, |
| {48, {240, 180, 90, 0} }, |
| {66, {330, 180, 90, 0} }, |
| {72, {360, 240, 90, 0} }, |
| {96, {480, 240, 120, 0} }, |
| {108, {540, 240, 120, 0} } |
| }; |
| |
| /* MCS Based rate table */ |
| /* HT MCS parameters with Nss = 1 */ |
| static const struct index_data_rate_type supported_mcs_rate_nss1[] = { |
| /* MCS L20 L40 S20 S40 */ |
| {0, { 65, 135, 72, 150} }, |
| {1, { 130, 270, 144, 300} }, |
| {2, { 195, 405, 217, 450} }, |
| {3, { 260, 540, 289, 600} }, |
| {4, { 390, 810, 433, 900} }, |
| {5, { 520, 1080, 578, 1200} }, |
| {6, { 585, 1215, 650, 1350} }, |
| {7, { 650, 1350, 722, 1500} } |
| }; |
| |
| /* HT MCS parameters with Nss = 2 */ |
| static const struct index_data_rate_type supported_mcs_rate_nss2[] = { |
| /* MCS L20 L40 S20 S40 */ |
| {0, {130, 270, 144, 300} }, |
| {1, {260, 540, 289, 600} }, |
| {2, {390, 810, 433, 900} }, |
| {3, {520, 1080, 578, 1200} }, |
| {4, {780, 1620, 867, 1800} }, |
| {5, {1040, 2160, 1156, 2400} }, |
| {6, {1170, 2430, 1300, 2700} }, |
| {7, {1300, 2700, 1444, 3000} } |
| }; |
| |
| /* MCS Based VHT rate table */ |
| /* MCS parameters with Nss = 1*/ |
| static const struct index_vht_data_rate_type supported_vht_mcs_rate_nss1[] = { |
| /* MCS L80 S80 L40 S40 L20 S40*/ |
| {0, {293, 325}, {135, 150}, {65, 72} }, |
| {1, {585, 650}, {270, 300}, {130, 144} }, |
| {2, {878, 975}, {405, 450}, {195, 217} }, |
| {3, {1170, 1300}, {540, 600}, {260, 289} }, |
| {4, {1755, 1950}, {810, 900}, {390, 433} }, |
| {5, {2340, 2600}, {1080, 1200}, {520, 578} }, |
| {6, {2633, 2925}, {1215, 1350}, {585, 650} }, |
| {7, {2925, 3250}, {1350, 1500}, {650, 722} }, |
| {8, {3510, 3900}, {1620, 1800}, {780, 867} }, |
| {9, {3900, 4333}, {1800, 2000}, {780, 867} } |
| }; |
| |
| /*MCS parameters with Nss = 2*/ |
| static const struct index_vht_data_rate_type supported_vht_mcs_rate_nss2[] = { |
| /* MCS L80 S80 L40 S40 L20 S40*/ |
| {0, {585, 650}, {270, 300}, {130, 144} }, |
| {1, {1170, 1300}, {540, 600}, {260, 289} }, |
| {2, {1755, 1950}, {810, 900}, {390, 433} }, |
| {3, {2340, 2600}, {1080, 1200}, {520, 578} }, |
| {4, {3510, 3900}, {1620, 1800}, {780, 867} }, |
| {5, {4680, 5200}, {2160, 2400}, {1040, 1156} }, |
| {6, {5265, 5850}, {2430, 2700}, {1170, 1300} }, |
| {7, {5850, 6500}, {2700, 3000}, {1300, 1444} }, |
| {8, {7020, 7800}, {3240, 3600}, {1560, 1733} }, |
| {9, {7800, 8667}, {3600, 4000}, {1560, 1733} } |
| }; |
| |
| /* Function definitions */ |
| |
| /** |
| * hdd_sap_context_init() - Initialize SAP context. |
| * @hdd_ctx: HDD context. |
| * |
| * Initialize SAP context. |
| * |
| * Return: 0 on success. |
| */ |
| int hdd_sap_context_init(struct hdd_context *hdd_ctx) |
| { |
| qdf_wake_lock_create(&hdd_ctx->sap_dfs_wakelock, "sap_dfs_wakelock"); |
| atomic_set(&hdd_ctx->sap_dfs_ref_cnt, 0); |
| |
| mutex_init(&hdd_ctx->sap_lock); |
| qdf_wake_lock_create(&hdd_ctx->sap_wake_lock, "qcom_sap_wakelock"); |
| |
| return 0; |
| } |
| |
| /** |
| * hdd_hostapd_init_sap_session() - To init the sap session completely |
| * @adapter: SAP/GO adapter |
| * @reinit: if called as part of reinit |
| * |
| * This API will do |
| * 1) sap_init_ctx() |
| * |
| * Return: 0 if success else non-zero value. |
| */ |
| static struct sap_context * |
| hdd_hostapd_init_sap_session(struct hdd_adapter *adapter, bool reinit) |
| { |
| struct sap_context *sap_ctx; |
| QDF_STATUS status; |
| |
| if (!adapter) { |
| hdd_err("invalid adapter"); |
| return NULL; |
| } |
| |
| sap_ctx = adapter->session.ap.sap_context; |
| |
| if (!sap_ctx) { |
| hdd_err("can't allocate the sap_ctx"); |
| return NULL; |
| } |
| status = sap_init_ctx(sap_ctx, adapter->device_mode, |
| adapter->mac_addr.bytes, |
| adapter->vdev_id, reinit); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| hdd_err("wlansap_start failed!! status: %d", status); |
| adapter->session.ap.sap_context = NULL; |
| goto error; |
| } |
| return sap_ctx; |
| error: |
| wlansap_context_put(sap_ctx); |
| hdd_err("releasing the sap context for session-id:%d", |
| adapter->vdev_id); |
| |
| return NULL; |
| } |
| |
| /** |
| * hdd_hostapd_deinit_sap_session() - To de-init the sap session completely |
| * @adapter: SAP/GO adapter |
| * |
| * This API will do |
| * 1) sap_init_ctx() |
| * 2) sap_destroy_ctx() |
| * |
| * Return: 0 if success else non-zero value. |
| */ |
| static int hdd_hostapd_deinit_sap_session(struct hdd_adapter *adapter) |
| { |
| struct sap_context *sap_ctx; |
| int status = 0; |
| |
| if (!adapter) { |
| hdd_err("invalid adapter"); |
| return -EINVAL; |
| } |
| |
| sap_ctx = WLAN_HDD_GET_SAP_CTX_PTR(adapter); |
| if (!sap_ctx) { |
| hdd_debug("sap context already released, nothing to be done"); |
| return 0; |
| } |
| |
| if (!QDF_IS_STATUS_SUCCESS(sap_deinit_ctx(sap_ctx))) { |
| hdd_err("Error stopping the sap session"); |
| status = -EINVAL; |
| } |
| |
| if (!QDF_IS_STATUS_SUCCESS(sap_destroy_ctx(sap_ctx))) { |
| hdd_err("Error closing the sap session"); |
| status = -EINVAL; |
| } |
| adapter->session.ap.sap_context = NULL; |
| |
| if (!QDF_IS_STATUS_SUCCESS(status)) |
| hdd_debug("sap has issue closing the session"); |
| else |
| hdd_debug("sap has been closed successfully"); |
| |
| |
| return status; |
| } |
| |
| /** |
| * hdd_hostapd_channel_allow_suspend() - allow suspend in a channel. |
| * Called when, 1. bss stopped, 2. channel switch |
| * |
| * @adapter: pointer to hdd adapter |
| * @channel: current channel |
| * |
| * Return: None |
| */ |
| static void hdd_hostapd_channel_allow_suspend(struct hdd_adapter *adapter, |
| uint8_t channel) |
| { |
| |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| struct hdd_hostapd_state *hostapd_state = |
| WLAN_HDD_GET_HOSTAP_STATE_PTR(adapter); |
| |
| hdd_debug("bss_state: %d, channel: %d, dfs_ref_cnt: %d", |
| hostapd_state->bss_state, channel, |
| atomic_read(&hdd_ctx->sap_dfs_ref_cnt)); |
| |
| /* Return if BSS is already stopped */ |
| if (hostapd_state->bss_state == BSS_STOP) |
| return; |
| |
| if (wlan_reg_get_channel_state(hdd_ctx->pdev, channel) != |
| CHANNEL_STATE_DFS) |
| return; |
| |
| /* Release wakelock when no more DFS channels are used */ |
| if (atomic_dec_and_test(&hdd_ctx->sap_dfs_ref_cnt)) { |
| hdd_err("DFS: allowing suspend (chan: %d)", channel); |
| qdf_wake_lock_release(&hdd_ctx->sap_dfs_wakelock, |
| WIFI_POWER_EVENT_WAKELOCK_DFS); |
| qdf_runtime_pm_allow_suspend(&hdd_ctx->runtime_context.dfs); |
| |
| } |
| } |
| |
| /** |
| * hdd_hostapd_channel_prevent_suspend() - prevent suspend in a channel. |
| * Called when, 1. bss started, 2. channel switch |
| * |
| * @adapter: pointer to hdd adapter |
| * @channel: current channel |
| * |
| * Return - None |
| */ |
| static void hdd_hostapd_channel_prevent_suspend(struct hdd_adapter *adapter, |
| uint8_t channel) |
| { |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| struct hdd_hostapd_state *hostapd_state = |
| WLAN_HDD_GET_HOSTAP_STATE_PTR(adapter); |
| |
| hdd_debug("bss_state: %d, channel: %d, dfs_ref_cnt: %d", |
| hostapd_state->bss_state, channel, |
| atomic_read(&hdd_ctx->sap_dfs_ref_cnt)); |
| |
| /* Return if BSS is already started && wakelock is acquired */ |
| if ((hostapd_state->bss_state == BSS_START) && |
| (atomic_read(&hdd_ctx->sap_dfs_ref_cnt) >= 1)) |
| return; |
| |
| if (wlan_reg_get_channel_state(hdd_ctx->pdev, channel) != |
| CHANNEL_STATE_DFS) |
| return; |
| |
| /* Acquire wakelock if we have at least one DFS channel in use */ |
| if (atomic_inc_return(&hdd_ctx->sap_dfs_ref_cnt) == 1) { |
| hdd_err("DFS: preventing suspend (chan: %d)", channel); |
| qdf_runtime_pm_prevent_suspend(&hdd_ctx->runtime_context.dfs); |
| qdf_wake_lock_acquire(&hdd_ctx->sap_dfs_wakelock, |
| WIFI_POWER_EVENT_WAKELOCK_DFS); |
| } |
| } |
| |
| /** |
| * hdd_sap_context_destroy() - Destroy SAP context |
| * |
| * @hdd_ctx: HDD context. |
| * |
| * Destroy SAP context. |
| * |
| * Return: None |
| */ |
| void hdd_sap_context_destroy(struct hdd_context *hdd_ctx) |
| { |
| if (atomic_read(&hdd_ctx->sap_dfs_ref_cnt)) { |
| qdf_wake_lock_release(&hdd_ctx->sap_dfs_wakelock, |
| WIFI_POWER_EVENT_WAKELOCK_DRIVER_EXIT); |
| |
| atomic_set(&hdd_ctx->sap_dfs_ref_cnt, 0); |
| hdd_debug("DFS: Allowing suspend"); |
| } |
| |
| qdf_wake_lock_destroy(&hdd_ctx->sap_dfs_wakelock); |
| |
| mutex_destroy(&hdd_ctx->sap_lock); |
| qdf_wake_lock_destroy(&hdd_ctx->sap_wake_lock); |
| } |
| |
| /** |
| * __hdd_hostapd_open() - hdd open function for hostapd interface |
| * This is called in response to ifconfig up |
| * @dev: pointer to net_device structure |
| * |
| * Return - 0 for success non-zero for failure |
| */ |
| static int __hdd_hostapd_open(struct net_device *dev) |
| { |
| struct hdd_adapter *adapter = netdev_priv(dev); |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| int ret; |
| |
| hdd_enter_dev(dev); |
| |
| qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, |
| TRACE_CODE_HDD_HOSTAPD_OPEN_REQUEST, |
| NO_SESSION, 0); |
| |
| /* Nothing to be done if device is unloading */ |
| if (cds_is_driver_unloading()) { |
| hdd_err("Driver is unloading can not open the hdd"); |
| return -EBUSY; |
| } |
| |
| if (cds_is_driver_recovering()) { |
| hdd_err("WLAN is currently recovering; Please try again."); |
| return -EBUSY; |
| } |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (ret) |
| return ret; |
| |
| /* ensure the physical soc is up */ |
| ret = hdd_psoc_idle_restart(hdd_ctx); |
| if (ret) { |
| hdd_err("Failed to start WLAN modules return"); |
| return ret; |
| } |
| |
| ret = hdd_start_adapter(adapter); |
| if (ret) { |
| hdd_err("Error Initializing the AP mode: %d", ret); |
| return ret; |
| } |
| |
| set_bit(DEVICE_IFACE_OPENED, &adapter->event_flags); |
| |
| /* Enable all Tx queues */ |
| hdd_debug("Enabling queues"); |
| wlan_hdd_netif_queue_control(adapter, |
| WLAN_START_ALL_NETIF_QUEUE_N_CARRIER, |
| WLAN_CONTROL_PATH); |
| hdd_exit(); |
| return 0; |
| } |
| |
| /** |
| * hdd_hostapd_open() - SSR wrapper for __hdd_hostapd_open |
| * @dev: pointer to net device |
| * |
| * Return: 0 on success, error number otherwise |
| */ |
| static int hdd_hostapd_open(struct net_device *net_dev) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_trans_start(net_dev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __hdd_hostapd_open(net_dev); |
| |
| osif_vdev_sync_trans_stop(vdev_sync); |
| |
| return errno; |
| } |
| |
| /** |
| * __hdd_hostapd_stop() - hdd stop function for hostapd interface |
| * This is called in response to ifconfig down |
| * |
| * @dev: pointer to net_device structure |
| * |
| * Return - 0 for success non-zero for failure |
| */ |
| static int __hdd_hostapd_stop(struct net_device *dev) |
| { |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| int ret; |
| |
| hdd_enter_dev(dev); |
| |
| qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, |
| TRACE_CODE_HDD_HOSTAPD_STOP_REQUEST, |
| NO_SESSION, 0); |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (ret) { |
| set_bit(DOWN_DURING_SSR, &adapter->event_flags); |
| return ret; |
| } |
| |
| /* |
| * Some tests requires to do "ifconfig down" only to bring |
| * down the SAP/GO without killing hostapd/wpa_supplicant. |
| * In such case, user will do "ifconfig up" to bring-back |
| * the SAP/GO session. to fulfill this requirement, driver |
| * needs to de-init the sap session here and re-init when |
| * __hdd_hostapd_open() API |
| */ |
| hdd_stop_adapter(hdd_ctx, adapter); |
| hdd_deinit_adapter(hdd_ctx, adapter, true); |
| clear_bit(DEVICE_IFACE_OPENED, &adapter->event_flags); |
| /* Stop all tx queues */ |
| hdd_debug("Disabling queues"); |
| wlan_hdd_netif_queue_control(adapter, |
| WLAN_STOP_ALL_NETIF_QUEUE_N_CARRIER, |
| WLAN_CONTROL_PATH); |
| |
| hdd_exit(); |
| return 0; |
| } |
| |
| /** |
| * hdd_hostapd_stop() - SSR wrapper for__hdd_hostapd_stop |
| * @dev: pointer to net_device |
| * |
| * This is called in response to ifconfig down |
| * |
| * Return: 0 on success, error number otherwise |
| */ |
| int hdd_hostapd_stop(struct net_device *net_dev) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_trans_start(net_dev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __hdd_hostapd_stop(net_dev); |
| |
| osif_vdev_sync_trans_stop(vdev_sync); |
| |
| return errno; |
| } |
| |
| /** |
| * hdd_hostapd_uninit() - hdd uninit function |
| * @dev: pointer to net_device structure |
| * |
| * This is called during the netdev unregister to uninitialize all data |
| * associated with the device. |
| * |
| * This function must be protected by a transition |
| * |
| * Return: None |
| */ |
| static void hdd_hostapd_uninit(struct net_device *dev) |
| { |
| struct hdd_adapter *adapter = netdev_priv(dev); |
| struct hdd_context *hdd_ctx; |
| |
| hdd_enter_dev(dev); |
| |
| if (adapter->magic != WLAN_HDD_ADAPTER_MAGIC) { |
| hdd_err("Invalid magic"); |
| return; |
| } |
| |
| hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| if (!hdd_ctx) { |
| hdd_err("NULL hdd_ctx"); |
| return; |
| } |
| |
| hdd_deinit_adapter(hdd_ctx, adapter, true); |
| |
| /* after uninit our adapter structure will no longer be valid */ |
| adapter->dev = NULL; |
| adapter->magic = 0; |
| |
| hdd_exit(); |
| } |
| |
| /** |
| * __hdd_hostapd_change_mtu() - change mtu |
| * @dev: pointer to net_device |
| * @new_mtu: new mtu |
| * |
| * Return: 0 on success, error number otherwise |
| */ |
| static int __hdd_hostapd_change_mtu(struct net_device *dev, int new_mtu) |
| { |
| hdd_enter_dev(dev); |
| |
| return 0; |
| } |
| |
| /** |
| * hdd_hostapd_change_mtu() - SSR wrapper for __hdd_hostapd_change_mtu |
| * @net_dev: pointer to net_device |
| * @new_mtu: new mtu |
| * |
| * Return: 0 on success, error number otherwise |
| */ |
| static int hdd_hostapd_change_mtu(struct net_device *net_dev, int new_mtu) |
| { |
| struct osif_vdev_sync *vdev_sync; |
| int errno; |
| |
| errno = osif_vdev_sync_op_start(net_dev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __hdd_hostapd_change_mtu(net_dev, new_mtu); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| |
| #ifdef QCA_HT_2040_COEX |
| QDF_STATUS hdd_set_sap_ht2040_mode(struct hdd_adapter *adapter, |
| uint8_t channel_type) |
| { |
| QDF_STATUS qdf_ret_status = QDF_STATUS_E_FAILURE; |
| mac_handle_t mac_handle; |
| |
| hdd_debug("change HT20/40 mode"); |
| |
| if (QDF_SAP_MODE == adapter->device_mode) { |
| mac_handle = adapter->hdd_ctx->mac_handle; |
| if (!mac_handle) { |
| hdd_err("mac handle is null"); |
| return QDF_STATUS_E_FAULT; |
| } |
| qdf_ret_status = |
| sme_set_ht2040_mode(mac_handle, adapter->vdev_id, |
| channel_type, true); |
| if (qdf_ret_status == QDF_STATUS_E_FAILURE) { |
| hdd_err("Failed to change HT20/40 mode"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| } |
| return QDF_STATUS_SUCCESS; |
| } |
| #endif |
| |
| /** |
| * __hdd_hostapd_set_mac_address() - |
| * This function sets the user specified mac address using |
| * the command ifconfig wlanX hw ether <mac address>. |
| * |
| * @dev: pointer to the net device. |
| * @addr: pointer to the sockaddr. |
| * |
| * Return: 0 for success, non zero for failure |
| */ |
| static int __hdd_hostapd_set_mac_address(struct net_device *dev, void *addr) |
| { |
| struct sockaddr *psta_mac_addr = addr; |
| struct hdd_adapter *adapter, *adapter_temp; |
| struct hdd_context *hdd_ctx; |
| int ret = 0; |
| struct qdf_mac_addr mac_addr; |
| |
| hdd_enter_dev(dev); |
| |
| adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != ret) |
| return ret; |
| |
| qdf_mem_copy(&mac_addr, psta_mac_addr->sa_data, sizeof(mac_addr)); |
| adapter_temp = hdd_get_adapter_by_macaddr(hdd_ctx, mac_addr.bytes); |
| if (adapter_temp) { |
| if (!qdf_str_cmp(adapter_temp->dev->name, dev->name)) |
| return 0; |
| hdd_err("%s adapter exist with same address " MAC_ADDRESS_STR, |
| adapter_temp->dev->name, |
| QDF_MAC_ADDR_ARRAY(mac_addr.bytes)); |
| return -EINVAL; |
| } |
| |
| if (qdf_is_macaddr_zero(&mac_addr)) { |
| hdd_err("MAC is all zero"); |
| return -EINVAL; |
| } |
| |
| if (qdf_is_macaddr_broadcast(&mac_addr)) { |
| hdd_err("MAC is Broadcast"); |
| return -EINVAL; |
| } |
| |
| if (ETHER_IS_MULTICAST(psta_mac_addr->sa_data)) { |
| hdd_err("MAC is Multicast"); |
| return -EINVAL; |
| } |
| |
| hdd_info("Changing MAC to " MAC_ADDRESS_STR " of interface %s ", |
| QDF_MAC_ADDR_ARRAY(mac_addr.bytes), |
| dev->name); |
| hdd_update_dynamic_mac(hdd_ctx, &adapter->mac_addr, &mac_addr); |
| memcpy(&adapter->mac_addr, psta_mac_addr->sa_data, ETH_ALEN); |
| memcpy(dev->dev_addr, psta_mac_addr->sa_data, ETH_ALEN); |
| hdd_exit(); |
| return 0; |
| } |
| |
| /** |
| * hdd_hostapd_set_mac_address() - set mac address |
| * @net_dev: pointer to net_device |
| * @addr: mac address |
| * |
| * Return: 0 on success, error number otherwise |
| */ |
| static int hdd_hostapd_set_mac_address(struct net_device *net_dev, void *addr) |
| { |
| struct osif_vdev_sync *vdev_sync; |
| int errno; |
| |
| errno = osif_vdev_sync_op_start(net_dev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __hdd_hostapd_set_mac_address(net_dev, addr); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| |
| static void hdd_clear_sta(struct hdd_adapter *adapter, uint8_t sta_id) |
| { |
| struct hdd_ap_ctx *ap_ctx; |
| struct hdd_station_info *sta_info; |
| struct csr_del_sta_params del_sta_params; |
| |
| ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(adapter); |
| |
| if (sta_id == ap_ctx->broadcast_sta_id) |
| return; |
| |
| sta_info = &adapter->sta_info[sta_id]; |
| if (!sta_info->in_use) |
| return; |
| |
| wlansap_populate_del_sta_params(sta_info->sta_mac.bytes, |
| eSIR_MAC_DEAUTH_LEAVING_BSS_REASON, |
| (SIR_MAC_MGMT_DISASSOC >> 4), |
| &del_sta_params); |
| |
| hdd_softap_sta_disassoc(adapter, &del_sta_params); |
| } |
| |
| static void hdd_clear_all_sta(struct hdd_adapter *adapter) |
| { |
| uint8_t sta_id; |
| |
| hdd_enter_dev(adapter->dev); |
| for (sta_id = 0; sta_id < WLAN_MAX_STA_COUNT; sta_id++) |
| hdd_clear_sta(adapter, sta_id); |
| } |
| |
| static int hdd_stop_bss_link(struct hdd_adapter *adapter) |
| { |
| struct hdd_context *hdd_ctx; |
| int errno; |
| QDF_STATUS status; |
| |
| hdd_enter(); |
| |
| hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| errno = wlan_hdd_validate_context(hdd_ctx); |
| if (errno) |
| return errno; |
| |
| if (test_bit(SOFTAP_BSS_STARTED, &adapter->event_flags)) { |
| status = wlansap_stop_bss( |
| WLAN_HDD_GET_SAP_CTX_PTR(adapter)); |
| if (QDF_IS_STATUS_SUCCESS(status)) |
| hdd_debug("Deleting SAP/P2P link!!!!!!"); |
| |
| clear_bit(SOFTAP_BSS_STARTED, &adapter->event_flags); |
| policy_mgr_decr_session_set_pcl(hdd_ctx->psoc, |
| adapter->device_mode, |
| adapter->vdev_id); |
| hdd_green_ap_start_state_mc(hdd_ctx, adapter->device_mode, |
| false); |
| errno = (status == QDF_STATUS_SUCCESS) ? 0 : -EBUSY; |
| } |
| hdd_exit(); |
| return errno; |
| } |
| |
| /** |
| * hdd_chan_change_notify() - Function to notify hostapd about channel change |
| * @hostapd_adapter: hostapd adapter |
| * @dev: Net device structure |
| * @chan_change: New channel change parameters |
| * @legacy_phymode: is the phymode legacy |
| * |
| * This function is used to notify hostapd about the channel change |
| * |
| * Return: Success on intimating userspace |
| * |
| */ |
| QDF_STATUS hdd_chan_change_notify(struct hdd_adapter *adapter, |
| struct net_device *dev, |
| struct hdd_chan_change_params chan_change, |
| bool legacy_phymode) |
| { |
| struct ieee80211_channel *chan; |
| struct cfg80211_chan_def chandef; |
| enum nl80211_channel_type channel_type; |
| uint32_t freq; |
| mac_handle_t mac_handle = adapter->hdd_ctx->mac_handle; |
| |
| if (!mac_handle) { |
| hdd_err("mac_handle is NULL"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| hdd_debug("chan:%d width:%d sec_ch_offset:%d seg0:%d seg1:%d", |
| chan_change.chan, chan_change.chan_params.ch_width, |
| chan_change.chan_params.sec_ch_offset, |
| chan_change.chan_params.center_freq_seg0, |
| chan_change.chan_params.center_freq_seg1); |
| |
| freq = cds_chan_to_freq(chan_change.chan); |
| |
| chan = ieee80211_get_channel(adapter->wdev.wiphy, freq); |
| |
| if (!chan) { |
| hdd_err("Invalid input frequency for channel conversion"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| if (legacy_phymode) { |
| channel_type = NL80211_CHAN_NO_HT; |
| } else { |
| switch (chan_change.chan_params.sec_ch_offset) { |
| case PHY_SINGLE_CHANNEL_CENTERED: |
| channel_type = NL80211_CHAN_HT20; |
| break; |
| case PHY_DOUBLE_CHANNEL_HIGH_PRIMARY: |
| channel_type = NL80211_CHAN_HT40MINUS; |
| break; |
| case PHY_DOUBLE_CHANNEL_LOW_PRIMARY: |
| channel_type = NL80211_CHAN_HT40PLUS; |
| break; |
| default: |
| channel_type = NL80211_CHAN_NO_HT; |
| break; |
| } |
| } |
| |
| cfg80211_chandef_create(&chandef, chan, channel_type); |
| |
| /* cfg80211_chandef_create() does update of width and center_freq1 |
| * only for NL80211_CHAN_NO_HT, NL80211_CHAN_HT20, NL80211_CHAN_HT40PLUS |
| * and NL80211_CHAN_HT40MINUS. |
| */ |
| switch (chan_change.chan_params.ch_width) { |
| case CH_WIDTH_80MHZ: |
| chandef.width = NL80211_CHAN_WIDTH_80; |
| break; |
| case CH_WIDTH_80P80MHZ: |
| chandef.width = NL80211_CHAN_WIDTH_80P80; |
| if (chan_change.chan_params.center_freq_seg1) |
| chandef.center_freq2 = cds_chan_to_freq( |
| chan_change.chan_params.center_freq_seg1); |
| break; |
| case CH_WIDTH_160MHZ: |
| chandef.width = NL80211_CHAN_WIDTH_160; |
| break; |
| default: |
| break; |
| } |
| |
| if ((chan_change.chan_params.ch_width == CH_WIDTH_80MHZ) || |
| (chan_change.chan_params.ch_width == CH_WIDTH_80P80MHZ) || |
| (chan_change.chan_params.ch_width == CH_WIDTH_160MHZ)) { |
| if (chan_change.chan_params.center_freq_seg0) |
| chandef.center_freq1 = cds_chan_to_freq( |
| chan_change.chan_params.center_freq_seg0); |
| } |
| |
| hdd_debug("notify: chan:%d width:%d freq1:%d freq2:%d", |
| chandef.chan->center_freq, chandef.width, chandef.center_freq1, |
| chandef.center_freq2); |
| |
| cfg80211_ch_switch_notify(dev, &chandef); |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| /** |
| * hdd_send_radar_event() - Function to send radar events to user space |
| * @hdd_context: HDD context |
| * @event: Type of radar event |
| * @dfs_info: Structure containing DFS channel and country |
| * @wdev: Wireless device structure |
| * |
| * This function is used to send radar events such as CAC start, CAC |
| * end etc., to userspace |
| * |
| * Return: Success on sending notifying userspace |
| * |
| */ |
| static QDF_STATUS hdd_send_radar_event(struct hdd_context *hdd_context, |
| eSapHddEvent event, |
| struct wlan_dfs_info dfs_info, |
| struct wireless_dev *wdev) |
| { |
| |
| struct sk_buff *vendor_event; |
| enum qca_nl80211_vendor_subcmds_index index; |
| uint32_t freq, ret; |
| uint32_t data_size; |
| |
| if (!hdd_context) { |
| hdd_err("HDD context is NULL"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| freq = cds_chan_to_freq(dfs_info.channel); |
| |
| switch (event) { |
| case eSAP_DFS_CAC_START: |
| index = |
| QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_STARTED_INDEX; |
| data_size = sizeof(uint32_t); |
| break; |
| case eSAP_DFS_CAC_END: |
| index = |
| QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_FINISHED_INDEX; |
| data_size = sizeof(uint32_t); |
| break; |
| case eSAP_DFS_RADAR_DETECT: |
| index = |
| QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED_INDEX; |
| data_size = sizeof(uint32_t); |
| break; |
| default: |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| vendor_event = cfg80211_vendor_event_alloc(hdd_context->wiphy, |
| wdev, |
| data_size + NLMSG_HDRLEN, |
| index, |
| GFP_KERNEL); |
| if (!vendor_event) { |
| hdd_err("cfg80211_vendor_event_alloc failed for %d", index); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| ret = nla_put_u32(vendor_event, NL80211_ATTR_WIPHY_FREQ, freq); |
| |
| if (ret) { |
| hdd_err("NL80211_ATTR_WIPHY_FREQ put fail"); |
| kfree_skb(vendor_event); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| cfg80211_vendor_event(vendor_event, GFP_KERNEL); |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| /** |
| * hdd_send_conditional_chan_switch_status() - Send conditional channel switch |
| * status |
| * @hdd_ctx: HDD context |
| * @wdev: Wireless device structure |
| * @status: Status of conditional channel switch |
| * (0: Success, Non-zero: Failure) |
| * |
| * Sends the status of conditional channel switch to user space. This is named |
| * conditional channel switch because the SAP will move to the provided channel |
| * after some condition (pre-cac) is met. |
| * |
| * Return: None |
| */ |
| static void hdd_send_conditional_chan_switch_status(struct hdd_context *hdd_ctx, |
| struct wireless_dev *wdev, |
| bool status) |
| { |
| struct sk_buff *event; |
| |
| hdd_enter_dev(wdev->netdev); |
| |
| if (!hdd_ctx) { |
| hdd_err("Invalid HDD context pointer"); |
| return; |
| } |
| |
| event = cfg80211_vendor_event_alloc(hdd_ctx->wiphy, |
| wdev, sizeof(uint32_t) + NLMSG_HDRLEN, |
| QCA_NL80211_VENDOR_SUBCMD_SAP_CONDITIONAL_CHAN_SWITCH_INDEX, |
| GFP_KERNEL); |
| if (!event) { |
| hdd_err("cfg80211_vendor_event_alloc failed"); |
| return; |
| } |
| |
| if (nla_put_u32(event, |
| QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_STATUS, |
| status)) { |
| hdd_err("nla put failed"); |
| kfree_skb(event); |
| return; |
| } |
| |
| cfg80211_vendor_event(event, GFP_KERNEL); |
| } |
| |
| /** |
| * wlan_hdd_set_pre_cac_complete_status() - Set pre cac complete status |
| * @ap_adapter: AP adapter |
| * @status: Status which can be true or false |
| * |
| * Sets the status of pre cac i.e., whether it is complete or not |
| * |
| * Return: Zero on success, non-zero on failure |
| */ |
| static int wlan_hdd_set_pre_cac_complete_status(struct hdd_adapter *ap_adapter, |
| bool status) |
| { |
| QDF_STATUS ret; |
| |
| ret = wlan_sap_set_pre_cac_complete_status( |
| WLAN_HDD_GET_SAP_CTX_PTR(ap_adapter), status); |
| if (QDF_IS_STATUS_ERROR(ret)) |
| return -EINVAL; |
| |
| return 0; |
| } |
| |
| /** |
| * __wlan_hdd_sap_pre_cac_failure() - Process the pre cac failure |
| * @adapter: AP adapter |
| * |
| * Deletes the pre cac adapter |
| * |
| * Return: None |
| */ |
| static void __wlan_hdd_sap_pre_cac_failure(struct hdd_adapter *adapter) |
| { |
| struct hdd_context *hdd_ctx; |
| |
| hdd_enter(); |
| |
| hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| if (wlan_hdd_validate_context(hdd_ctx)) { |
| hdd_err("HDD context is null"); |
| return; |
| } |
| |
| wlan_hdd_release_intf_addr(hdd_ctx, adapter->mac_addr.bytes); |
| hdd_stop_adapter(hdd_ctx, adapter); |
| hdd_close_adapter(hdd_ctx, adapter, false); |
| |
| hdd_exit(); |
| } |
| |
| /** |
| * wlan_hdd_sap_pre_cac_failure() - Process the pre cac failure |
| * @data: AP adapter |
| * |
| * Deletes the pre cac adapter |
| * |
| * Return: None |
| */ |
| void wlan_hdd_sap_pre_cac_failure(void *data) |
| { |
| struct hdd_adapter *adapter = data; |
| struct osif_vdev_sync *vdev_sync; |
| int errno; |
| |
| errno = osif_vdev_sync_trans_start_wait(adapter->dev, &vdev_sync); |
| if (errno) |
| return; |
| |
| osif_vdev_sync_unregister(adapter->dev); |
| osif_vdev_sync_wait_for_ops(vdev_sync); |
| |
| __wlan_hdd_sap_pre_cac_failure(data); |
| |
| osif_vdev_sync_trans_stop(vdev_sync); |
| osif_vdev_sync_destroy(vdev_sync); |
| } |
| |
| /** |
| * __wlan_hdd_sap_pre_cac_success() - Process the pre cac result |
| * @adapter: AP adapter |
| * |
| * Deletes the pre cac adapter and moves the existing SAP to the pre cac |
| * channel |
| * |
| * Return: None |
| */ |
| static void __wlan_hdd_sap_pre_cac_success(struct hdd_adapter *adapter) |
| { |
| struct hdd_adapter *ap_adapter; |
| int i; |
| struct hdd_context *hdd_ctx; |
| |
| hdd_enter(); |
| |
| hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| if (!hdd_ctx) { |
| hdd_err("HDD context is null"); |
| return; |
| } |
| |
| wlan_hdd_release_intf_addr(hdd_ctx, adapter->mac_addr.bytes); |
| hdd_stop_adapter(hdd_ctx, adapter); |
| hdd_close_adapter(hdd_ctx, adapter, false); |
| |
| /* Prepare to switch AP from 2.4GHz channel to the pre CAC channel */ |
| ap_adapter = hdd_get_adapter(hdd_ctx, QDF_SAP_MODE); |
| if (!ap_adapter) { |
| hdd_err("failed to get SAP adapter, no restart on pre CAC channel"); |
| return; |
| } |
| |
| /* |
| * Setting of the pre cac complete status will ensure that on channel |
| * switch to the pre CAC DFS channel, there is no CAC again. |
| */ |
| wlan_hdd_set_pre_cac_complete_status(ap_adapter, true); |
| i = hdd_softap_set_channel_change(ap_adapter->dev, |
| ap_adapter->pre_cac_chan, |
| CH_WIDTH_MAX, false); |
| if (0 != i) { |
| hdd_err("failed to change channel"); |
| wlan_hdd_set_pre_cac_complete_status(ap_adapter, false); |
| } |
| |
| hdd_exit(); |
| } |
| |
| static void wlan_hdd_sap_pre_cac_success(void *data) |
| { |
| struct hdd_adapter *adapter = data; |
| struct osif_vdev_sync *vdev_sync; |
| int errno; |
| |
| errno = osif_vdev_sync_trans_start_wait(adapter->dev, &vdev_sync); |
| if (errno) |
| return; |
| |
| osif_vdev_sync_unregister(adapter->dev); |
| osif_vdev_sync_wait_for_ops(vdev_sync); |
| |
| __wlan_hdd_sap_pre_cac_success(adapter); |
| |
| osif_vdev_sync_trans_stop(vdev_sync); |
| osif_vdev_sync_destroy(vdev_sync); |
| } |
| |
| #ifdef FEATURE_WLAN_AP_AP_ACS_OPTIMIZE |
| /** |
| * hdd_handle_acs_scan_event() - handle acs scan event for SAP |
| * @sap_event: tpSap_Event |
| * @adapter: struct hdd_adapter for SAP |
| * |
| * The function is to handle the eSAP_ACS_SCAN_SUCCESS_EVENT event. |
| * It will update scan result to cfg80211 and start a timer to flush the |
| * cached acs scan result. |
| * |
| * Return: QDF_STATUS_SUCCESS on success, |
| * other value on failure |
| */ |
| static QDF_STATUS hdd_handle_acs_scan_event(struct sap_event *sap_event, |
| struct hdd_adapter *adapter) |
| { |
| struct hdd_context *hdd_ctx; |
| struct sap_acs_scan_complete_event *comp_evt; |
| QDF_STATUS qdf_status; |
| int chan_list_size; |
| |
| hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| if (!hdd_ctx) { |
| hdd_err("HDD context is null"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| comp_evt = &sap_event->sapevt.sap_acs_scan_comp; |
| hdd_ctx->skip_acs_scan_status = eSAP_SKIP_ACS_SCAN; |
| qdf_spin_lock(&hdd_ctx->acs_skip_lock); |
| qdf_mem_free(hdd_ctx->last_acs_channel_list); |
| hdd_ctx->last_acs_channel_list = NULL; |
| hdd_ctx->num_of_channels = 0; |
| /* cache the previous ACS scan channel list . |
| * If the following OBSS scan chan list is covered by ACS chan list, |
| * we can skip OBSS Scan to save SAP starting total time. |
| */ |
| if (comp_evt->num_of_channels && comp_evt->channellist) { |
| chan_list_size = comp_evt->num_of_channels * |
| sizeof(comp_evt->channellist[0]); |
| hdd_ctx->last_acs_channel_list = qdf_mem_malloc( |
| chan_list_size); |
| if (hdd_ctx->last_acs_channel_list) { |
| qdf_mem_copy(hdd_ctx->last_acs_channel_list, |
| comp_evt->channellist, |
| chan_list_size); |
| hdd_ctx->num_of_channels = comp_evt->num_of_channels; |
| } |
| } |
| qdf_spin_unlock(&hdd_ctx->acs_skip_lock); |
| |
| hdd_debug("Reusing Last ACS scan result for %d sec", |
| ACS_SCAN_EXPIRY_TIMEOUT_S); |
| qdf_mc_timer_stop(&hdd_ctx->skip_acs_scan_timer); |
| qdf_status = qdf_mc_timer_start(&hdd_ctx->skip_acs_scan_timer, |
| ACS_SCAN_EXPIRY_TIMEOUT_S * 1000); |
| if (!QDF_IS_STATUS_SUCCESS(qdf_status)) |
| hdd_err("Failed to start ACS scan expiry timer"); |
| return QDF_STATUS_SUCCESS; |
| } |
| #else |
| static QDF_STATUS hdd_handle_acs_scan_event(struct sap_event *sap_event, |
| struct hdd_adapter *adapter) |
| { |
| return QDF_STATUS_SUCCESS; |
| } |
| #endif |
| |
| /** |
| * get_max_rate_vht() - calculate max rate for VHT mode |
| * @nss: num of streams |
| * @ch_width: channel width |
| * @sgi: short gi |
| * @vht_mcs_map: vht mcs map |
| * |
| * This function calculate max rate for VHT mode |
| * |
| * Return: max rate |
| */ |
| static int get_max_rate_vht(int nss, int ch_width, int sgi, int vht_mcs_map) |
| { |
| const struct index_vht_data_rate_type *supported_vht_mcs_rate; |
| enum data_rate_11ac_max_mcs vht_max_mcs; |
| int maxrate = 0; |
| int maxidx; |
| |
| if (nss == 1) { |
| supported_vht_mcs_rate = supported_vht_mcs_rate_nss1; |
| } else if (nss == 2) { |
| supported_vht_mcs_rate = supported_vht_mcs_rate_nss2; |
| } else { |
| /* Not Supported */ |
| hdd_err("nss %d not supported", nss); |
| return maxrate; |
| } |
| |
| vht_max_mcs = |
| (enum data_rate_11ac_max_mcs) |
| (vht_mcs_map & DATA_RATE_11AC_MCS_MASK); |
| |
| if (vht_max_mcs == DATA_RATE_11AC_MAX_MCS_7) { |
| maxidx = 7; |
| } else if (vht_max_mcs == DATA_RATE_11AC_MAX_MCS_8) { |
| maxidx = 8; |
| } else if (vht_max_mcs == DATA_RATE_11AC_MAX_MCS_9) { |
| if (ch_width == eHT_CHANNEL_WIDTH_20MHZ) |
| /* MCS9 is not valid for VHT20 when nss=1,2 */ |
| maxidx = 8; |
| else |
| maxidx = 9; |
| } else { |
| hdd_err("vht mcs map %x not supported", |
| vht_mcs_map & DATA_RATE_11AC_MCS_MASK); |
| return maxrate; |
| } |
| |
| if (ch_width == eHT_CHANNEL_WIDTH_20MHZ) { |
| maxrate = |
| supported_vht_mcs_rate[maxidx].supported_VHT20_rate[sgi]; |
| } else if (ch_width == eHT_CHANNEL_WIDTH_40MHZ) { |
| maxrate = |
| supported_vht_mcs_rate[maxidx].supported_VHT40_rate[sgi]; |
| } else if (ch_width == eHT_CHANNEL_WIDTH_80MHZ) { |
| maxrate = |
| supported_vht_mcs_rate[maxidx].supported_VHT80_rate[sgi]; |
| } else { |
| hdd_err("ch_width %d not supported", ch_width); |
| return maxrate; |
| } |
| |
| return maxrate; |
| } |
| |
| /** |
| * calculate_max_phy_rate() - calcuate maximum phy rate (100kbps) |
| * @mode: phymode: Legacy, 11a/b/g, HT, VHT |
| * @nss: num of stream (maximum num is 2) |
| * @ch_width: channel width |
| * @sgi: short gi enabled or not |
| * @supp_idx: max supported idx |
| * @ext_idx: max extended idx |
| * @ht_mcs_idx: max mcs index for HT |
| * @vht_mcs_map: mcs map for VHT |
| * |
| * return: maximum phy rate in 100kbps |
| */ |
| static int calcuate_max_phy_rate(int mode, int nss, int ch_width, |
| int sgi, int supp_idx, int ext_idx, |
| int ht_mcs_idx, int vht_mcs_map) |
| { |
| const struct index_data_rate_type *supported_mcs_rate; |
| int maxidx = 12; /*default 6M mode*/ |
| int maxrate = 0, tmprate; |
| int i; |
| |
| /* check supported rates */ |
| if (supp_idx != 0xff && maxidx < supp_idx) |
| maxidx = supp_idx; |
| |
| /* check extended rates */ |
| if (ext_idx != 0xff && maxidx < ext_idx) |
| maxidx = ext_idx; |
| |
| for (i = 0; i < QDF_ARRAY_SIZE(supported_data_rate); i++) { |
| if (supported_data_rate[i].beacon_rate_index == maxidx) |
| maxrate = supported_data_rate[i].supported_rate[0]; |
| } |
| |
| if (mode == SIR_SME_PHY_MODE_HT) { |
| /* check for HT Mode */ |
| maxidx = ht_mcs_idx; |
| if (nss == 1) { |
| supported_mcs_rate = supported_mcs_rate_nss1; |
| } else if (nss == 2) { |
| supported_mcs_rate = supported_mcs_rate_nss2; |
| } else { |
| /* Not Supported */ |
| hdd_err("nss %d not supported", nss); |
| return maxrate; |
| } |
| |
| if (ch_width == eHT_CHANNEL_WIDTH_20MHZ) { |
| tmprate = sgi ? |
| supported_mcs_rate[maxidx].supported_rate[2] : |
| supported_mcs_rate[maxidx].supported_rate[0]; |
| } else if (ch_width == eHT_CHANNEL_WIDTH_40MHZ) { |
| tmprate = sgi ? |
| supported_mcs_rate[maxidx].supported_rate[3] : |
| supported_mcs_rate[maxidx].supported_rate[1]; |
| } else { |
| hdd_err("invalid mode %d ch_width %d", |
| mode, ch_width); |
| return maxrate; |
| } |
| |
| if (maxrate < tmprate) |
| maxrate = tmprate; |
| } |
| |
| if (mode == SIR_SME_PHY_MODE_VHT) { |
| /* check for VHT Mode */ |
| tmprate = get_max_rate_vht(nss, ch_width, sgi, vht_mcs_map); |
| if (maxrate < tmprate) |
| maxrate = tmprate; |
| } |
| |
| return maxrate; |
| } |
| |
| /** |
| * hdd_convert_dot11mode_from_phymode() - get dot11 mode from phymode |
| * @phymode: phymode of sta associated to SAP |
| * |
| * The function is to convert the phymode to corresponding dot11 mode |
| * |
| * Return: dot11mode. |
| */ |
| |
| |
| static int hdd_convert_dot11mode_from_phymode(int phymode) |
| { |
| |
| switch (phymode) { |
| |
| case MODE_11A: |
| return QCA_WLAN_802_11_MODE_11A; |
| |
| case MODE_11B: |
| return QCA_WLAN_802_11_MODE_11B; |
| |
| case MODE_11G: |
| case MODE_11GONLY: |
| return QCA_WLAN_802_11_MODE_11G; |
| |
| case MODE_11NA_HT20: |
| case MODE_11NG_HT20: |
| case MODE_11NA_HT40: |
| case MODE_11NG_HT40: |
| return QCA_WLAN_802_11_MODE_11N; |
| |
| case MODE_11AC_VHT20: |
| case MODE_11AC_VHT40: |
| case MODE_11AC_VHT80: |
| case MODE_11AC_VHT20_2G: |
| case MODE_11AC_VHT40_2G: |
| case MODE_11AC_VHT80_2G: |
| #ifdef CONFIG_160MHZ_SUPPORT |
| case MODE_11AC_VHT80_80: |
| case MODE_11AC_VHT160: |
| #endif |
| return QCA_WLAN_802_11_MODE_11AC; |
| |
| default: |
| return QCA_WLAN_802_11_MODE_INVALID; |
| } |
| |
| } |
| |
| /** |
| * hdd_fill_station_info() - fill stainfo once connected |
| * @stainfo: peer stainfo associate to SAP |
| * @event: associate/reassociate event received |
| * |
| * The function is to update rate stats to stainfo |
| * |
| * Return: None. |
| */ |
| static void hdd_fill_station_info(struct hdd_adapter *adapter, |
| tSap_StationAssocReassocCompleteEvent *event) |
| { |
| struct hdd_station_info *stainfo; |
| uint8_t i = 0, oldest_disassoc_sta_idx = WLAN_MAX_STA_COUNT + 1; |
| qdf_time_t oldest_disassoc_sta_ts = 0; |
| |
| if (event->staId >= WLAN_MAX_STA_COUNT) { |
| hdd_err("invalid sta id"); |
| return; |
| } |
| |
| stainfo = &adapter->sta_info[event->staId]; |
| |
| if (!stainfo) { |
| hdd_err("invalid stainfo"); |
| return; |
| } |
| |
| qdf_mem_copy(&stainfo->capability, &event->capability_info, |
| sizeof(uint16_t)); |
| stainfo->freq = cds_chan_to_freq(event->chan_info.chan_id); |
| stainfo->sta_type = event->staType; |
| stainfo->dot11_mode = |
| hdd_convert_dot11mode_from_phymode(event->chan_info.info); |
| |
| stainfo->nss = event->chan_info.nss; |
| stainfo->rate_flags = event->chan_info.rate_flags; |
| stainfo->ampdu = event->ampdu; |
| stainfo->sgi_enable = event->sgi_enable; |
| stainfo->tx_stbc = event->tx_stbc; |
| stainfo->rx_stbc = event->rx_stbc; |
| stainfo->ch_width = event->ch_width; |
| stainfo->mode = event->mode; |
| stainfo->max_supp_idx = event->max_supp_idx; |
| stainfo->max_ext_idx = event->max_ext_idx; |
| stainfo->max_mcs_idx = event->max_mcs_idx; |
| stainfo->rx_mcs_map = event->rx_mcs_map; |
| stainfo->tx_mcs_map = event->tx_mcs_map; |
| stainfo->assoc_ts = qdf_system_ticks(); |
| stainfo->max_phy_rate = |
| calcuate_max_phy_rate(stainfo->mode, |
| stainfo->nss, |
| stainfo->ch_width, |
| stainfo->sgi_enable, |
| stainfo->max_supp_idx, |
| stainfo->max_ext_idx, |
| stainfo->max_mcs_idx, |
| stainfo->rx_mcs_map); |
| /* expect max_phy_rate report in kbps */ |
| stainfo->max_phy_rate *= 100; |
| |
| if (event->vht_caps.present) { |
| stainfo->vht_present = true; |
| hdd_copy_vht_caps(&stainfo->vht_caps, &event->vht_caps); |
| stainfo->support_mode |= |
| (stainfo->vht_present << HDD_VHT_CAPS_PRESENT); |
| } |
| if (event->ht_caps.present) { |
| stainfo->ht_present = true; |
| hdd_copy_ht_caps(&stainfo->ht_caps, &event->ht_caps); |
| stainfo->support_mode |= |
| (stainfo->ht_present << HDD_HT_CAPS_PRESENT); |
| } |
| stainfo->support_mode |= |
| (event->he_caps_present << HDD_HE_CAPS_PRESENT); |
| |
| /* Initialize DHCP info */ |
| stainfo->dhcp_phase = DHCP_PHASE_ACK; |
| stainfo->dhcp_nego_status = DHCP_NEGO_STOP; |
| |
| while (i < WLAN_MAX_STA_COUNT) { |
| if (!qdf_mem_cmp(adapter->cache_sta_info[i].sta_mac.bytes, |
| event->staMac.bytes, |
| QDF_MAC_ADDR_SIZE)) |
| break; |
| i++; |
| } |
| if (i >= WLAN_MAX_STA_COUNT) { |
| i = 0; |
| while (i < WLAN_MAX_STA_COUNT) { |
| if (adapter->cache_sta_info[i].in_use != TRUE) |
| break; |
| |
| if (adapter->cache_sta_info[i].disassoc_ts && |
| (!oldest_disassoc_sta_ts || |
| (qdf_system_time_after( |
| oldest_disassoc_sta_ts, |
| adapter-> |
| cache_sta_info[i].disassoc_ts)))) { |
| oldest_disassoc_sta_ts = |
| adapter-> |
| cache_sta_info[i].disassoc_ts; |
| oldest_disassoc_sta_idx = i; |
| } |
| i++; |
| } |
| } |
| |
| if ((i == WLAN_MAX_STA_COUNT) && oldest_disassoc_sta_ts) { |
| hdd_debug("reached max cached sta_id, removing oldest stainfo"); |
| i = oldest_disassoc_sta_idx; |
| } |
| if (i < WLAN_MAX_STA_COUNT) { |
| qdf_mem_zero(&adapter->cache_sta_info[i], |
| sizeof(*stainfo)); |
| qdf_mem_copy(&adapter->cache_sta_info[i], |
| stainfo, sizeof(struct hdd_station_info)); |
| |
| } else { |
| hdd_debug("reached max sta_id, stainfo can't be cached"); |
| } |
| |
| hdd_debug("cap %d %d %d %d %d %d %d %d %d %x %d", |
| stainfo->ampdu, |
| stainfo->sgi_enable, |
| stainfo->tx_stbc, |
| stainfo->rx_stbc, |
| stainfo->is_qos_enabled, |
| stainfo->ch_width, |
| stainfo->mode, |
| event->wmmEnabled, |
| event->chan_info.nss, |
| event->chan_info.rate_flags, |
| stainfo->max_phy_rate); |
| hdd_debug("rate info %d %d %d %d %d", |
| stainfo->max_supp_idx, |
| stainfo->max_ext_idx, |
| stainfo->max_mcs_idx, |
| stainfo->rx_mcs_map, |
| stainfo->tx_mcs_map); |
| } |
| |
| /** |
| * hdd_stop_sap_due_to_invalid_channel() - to stop sap in case of invalid chnl |
| * @work: pointer to work structure |
| * |
| * Let's say SAP detected RADAR and trying to select the new channel and if no |
| * valid channel is found due to none of the channels are available or |
| * regulatory restriction then SAP needs to be stopped. so SAP state-machine |
| * will create a work to stop the bss |
| * |
| * stop bss has to happen through worker thread because radar indication comes |
| * from FW through mc thread or main host thread and if same thread is used to |
| * do stopbss then waiting for stopbss to finish operation will halt mc thread |
| * to freeze which will trigger stopbss timeout. Instead worker thread can do |
| * the stopbss operation while mc thread waits for stopbss to finish. |
| * |
| * Return: none |
| */ |
| static void hdd_stop_sap_due_to_invalid_channel(struct work_struct *work) |
| { |
| struct hdd_adapter *sap_adapter = container_of(work, struct hdd_adapter, |
| sap_stop_bss_work); |
| struct osif_vdev_sync *vdev_sync; |
| |
| if (osif_vdev_sync_op_start(sap_adapter->dev, &vdev_sync)) |
| return; |
| |
| hdd_debug("work started for sap session[%d]", sap_adapter->vdev_id); |
| wlan_hdd_stop_sap(sap_adapter); |
| wlansap_cleanup_cac_timer(WLAN_HDD_GET_SAP_CTX_PTR(sap_adapter)); |
| hdd_debug("work finished for sap"); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| } |
| |
| /** |
| * hdd_hostapd_apply_action_oui() - Check for action_ouis to be applied on peers |
| * @hdd_ctx: pointer to hdd context |
| * @adapter: pointer to adapter |
| * @event: assoc complete params |
| * |
| * This function is used to check whether aggressive tx should be disabled |
| * based on the soft-ap configuration and action_oui ini |
| * gActionOUIDisableAggressiveTX |
| * |
| * Return: None |
| */ |
| static void |
| hdd_hostapd_apply_action_oui(struct hdd_context *hdd_ctx, |
| struct hdd_adapter *adapter, |
| tSap_StationAssocReassocCompleteEvent *event) |
| { |
| bool found; |
| uint32_t freq; |
| tSirMacHTChannelWidth ch_width; |
| enum sir_sme_phy_mode mode; |
| struct action_oui_search_attr attr = {0}; |
| QDF_STATUS status; |
| |
| ch_width = event->ch_width; |
| if (ch_width != eHT_CHANNEL_WIDTH_20MHZ) |
| return; |
| |
| freq = cds_chan_to_freq(event->chan_info.chan_id); |
| if (WLAN_REG_IS_24GHZ_CH_FREQ(freq)) |
| attr.enable_2g = true; |
| else if (WLAN_REG_IS_5GHZ_CH_FREQ(freq)) |
| attr.enable_5g = true; |
| else |
| return; |
| |
| mode = event->mode; |
| if (event->vht_caps.present && mode == SIR_SME_PHY_MODE_VHT) |
| attr.vht_cap = true; |
| else if (event->ht_caps.present && mode == SIR_SME_PHY_MODE_HT) |
| attr.ht_cap = true; |
| |
| attr.mac_addr = (uint8_t *)(&event->staMac); |
| |
| found = ucfg_action_oui_search(hdd_ctx->psoc, |
| &attr, |
| ACTION_OUI_DISABLE_AGGRESSIVE_TX); |
| if (!found) |
| return; |
| |
| status = sme_set_peer_param(attr.mac_addr, |
| WMI_PEER_PARAM_DISABLE_AGGRESSIVE_TX, |
| true, adapter->vdev_id); |
| if (QDF_IS_STATUS_ERROR(status)) |
| hdd_err("Failed to disable aggregation for peer"); |
| } |
| |
| #ifdef CRYPTO_SET_KEY_CONVERGED |
| static void hdd_hostapd_set_sap_key(struct hdd_adapter *adapter) |
| { |
| struct wlan_crypto_key *crypto_key; |
| |
| crypto_key = wlan_crypto_get_key(adapter->vdev, 0); |
| if (!crypto_key) { |
| QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR, |
| "Crypto KEY is NULL"); |
| return; |
| } |
| ucfg_crypto_set_key_req(adapter->vdev, crypto_key, |
| WLAN_CRYPTO_KEY_TYPE_UNICAST); |
| wma_update_set_key(adapter->vdev_id, true, 1, crypto_key->cipher_type); |
| ucfg_crypto_set_key_req(adapter->vdev, crypto_key, |
| WLAN_CRYPTO_KEY_TYPE_GROUP); |
| |
| wma_update_set_key(adapter->vdev_id, true, 0, crypto_key->cipher_type); |
| } |
| #else |
| static void hdd_hostapd_set_sap_key(struct hdd_adapter *adapter) |
| { |
| struct hdd_ap_ctx *ap_ctx; |
| QDF_STATUS status; |
| uint8_t i; |
| |
| ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(adapter); |
| /* Set group key / WEP key every time when BSS is restarted */ |
| if (ap_ctx->group_key.keyLength) { |
| status = wlansap_set_key_sta(WLAN_HDD_GET_SAP_CTX_PTR(adapter), |
| &ap_ctx->group_key); |
| if (!QDF_IS_STATUS_SUCCESS(status)) |
| hdd_err("wlansap_set_key_sta failed"); |
| } else { |
| for (i = 0; i < CSR_MAX_NUM_KEY; i++) { |
| if (!ap_ctx->wep_key[i].keyLength) |
| continue; |
| |
| status = wlansap_set_key_sta(WLAN_HDD_GET_SAP_CTX_PTR |
| (adapter), |
| &ap_ctx->wep_key[i]); |
| if (!QDF_IS_STATUS_SUCCESS(status)) |
| hdd_err("set_key failed idx: %d", i); |
| } |
| } |
| } |
| #endif |
| |
| QDF_STATUS hdd_hostapd_sap_event_cb(struct sap_event *sap_event, |
| void *context) |
| { |
| struct hdd_adapter *adapter; |
| struct hdd_ap_ctx *ap_ctx; |
| struct hdd_hostapd_state *hostapd_state; |
| struct net_device *dev; |
| eSapHddEvent event_id; |
| union iwreq_data wrqu; |
| uint8_t *we_custom_event_generic = NULL; |
| int we_event = 0; |
| int i = 0; |
| uint8_t sta_id; |
| QDF_STATUS qdf_status; |
| bool bAuthRequired = true; |
| char unknownSTAEvent[IW_CUSTOM_MAX + 1]; |
| char maxAssocExceededEvent[IW_CUSTOM_MAX + 1]; |
| uint8_t we_custom_start_event[64]; |
| char *startBssEvent; |
| struct hdd_context *hdd_ctx; |
| struct iw_michaelmicfailure msg; |
| uint8_t ignoreCAC = 0; |
| struct hdd_config *cfg = NULL; |
| struct wlan_dfs_info dfs_info; |
| uint8_t cc_len = WLAN_SVC_COUNTRY_CODE_LEN; |
| struct hdd_adapter *con_sap_adapter; |
| QDF_STATUS status = QDF_STATUS_SUCCESS; |
| struct hdd_chan_change_params chan_change; |
| tSap_StationAssocReassocCompleteEvent *event; |
| tSap_StationSetKeyCompleteEvent *key_complete; |
| int ret = 0; |
| struct ch_params sap_ch_param = {0}; |
| eCsrPhyMode phy_mode; |
| bool legacy_phymode; |
| tSap_StationDisassocCompleteEvent *disassoc_comp; |
| struct hdd_station_info *stainfo, *cache_stainfo; |
| mac_handle_t mac_handle; |
| struct sap_config *sap_config; |
| |
| dev = context; |
| if (!dev) { |
| hdd_err("context is null"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| adapter = netdev_priv(dev); |
| |
| if ((!adapter) || |
| (WLAN_HDD_ADAPTER_MAGIC != adapter->magic)) { |
| hdd_err("invalid adapter or adapter has invalid magic"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| hostapd_state = WLAN_HDD_GET_HOSTAP_STATE_PTR(adapter); |
| ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(adapter); |
| |
| if (!sap_event) { |
| hdd_err("sap_event is null"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| event_id = sap_event->sapHddEventCode; |
| memset(&wrqu, '\0', sizeof(wrqu)); |
| hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| |
| if (!hdd_ctx) { |
| hdd_err("HDD context is null"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| cfg = hdd_ctx->config; |
| |
| if (!cfg) { |
| hdd_err("HDD config is null"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| mac_handle = hdd_ctx->mac_handle; |
| dfs_info.channel = ap_ctx->operating_channel; |
| sme_get_country_code(mac_handle, dfs_info.country_code, &cc_len); |
| sta_id = sap_event->sapevt.sapStartBssCompleteEvent.staId; |
| sap_config = &adapter->session.ap.sap_config; |
| |
| switch (event_id) { |
| case eSAP_START_BSS_EVENT: |
| hdd_debug("BSS status = %s, channel = %u, bc sta Id = %d", |
| sap_event->sapevt.sapStartBssCompleteEvent. |
| status ? "eSAP_STATUS_FAILURE" : "eSAP_STATUS_SUCCESS", |
| sap_event->sapevt.sapStartBssCompleteEvent. |
| operatingChannel, |
| sap_event->sapevt.sapStartBssCompleteEvent.staId); |
| ap_ctx->operating_channel = |
| sap_event->sapevt.sapStartBssCompleteEvent |
| .operatingChannel; |
| |
| adapter->vdev_id = |
| sap_event->sapevt.sapStartBssCompleteEvent.sessionId; |
| |
| sap_config->channel = |
| sap_event->sapevt.sapStartBssCompleteEvent. |
| operatingChannel; |
| sap_config->ch_params.ch_width = |
| sap_event->sapevt.sapStartBssCompleteEvent.ch_width; |
| |
| sap_config->ch_params = ap_ctx->sap_context->ch_params; |
| sap_config->sec_ch = ap_ctx->sap_context->secondary_ch; |
| |
| hostapd_state->qdf_status = |
| sap_event->sapevt.sapStartBssCompleteEvent.status; |
| |
| qdf_atomic_set(&adapter->ch_switch_in_progress, 0); |
| wlansap_get_dfs_ignore_cac(mac_handle, &ignoreCAC); |
| |
| /* DFS requirement: DO NOT transmit during CAC. */ |
| if (CHANNEL_STATE_DFS != |
| wlan_reg_get_channel_state(hdd_ctx->pdev, |
| ap_ctx->operating_channel) |
| || ignoreCAC |
| || hdd_ctx->dev_dfs_cac_status == DFS_CAC_ALREADY_DONE) |
| ap_ctx->dfs_cac_block_tx = false; |
| else |
| ap_ctx->dfs_cac_block_tx = true; |
| |
| ucfg_ipa_set_dfs_cac_tx(hdd_ctx->pdev, |
| ap_ctx->dfs_cac_block_tx); |
| |
| hdd_debug("The value of dfs_cac_block_tx[%d] for ApCtx[%pK]:%d", |
| ap_ctx->dfs_cac_block_tx, ap_ctx, |
| adapter->vdev_id); |
| |
| if (hostapd_state->qdf_status) { |
| hdd_err("startbss event failed!!"); |
| /* |
| * Make sure to set the event before proceeding |
| * for error handling otherwise caller thread will |
| * wait till 10 secs and no other connection will |
| * go through before that. |
| */ |
| hostapd_state->bss_state = BSS_STOP; |
| qdf_event_set(&hostapd_state->qdf_event); |
| goto stopbss; |
| } else { |
| sme_ch_avoid_update_req(mac_handle); |
| |
| ap_ctx->broadcast_sta_id = |
| sap_event->sapevt.sapStartBssCompleteEvent.staId; |
| |
| cdp_hl_fc_set_td_limit( |
| cds_get_context(QDF_MODULE_ID_SOC), |
| adapter->vdev_id, |
| ap_ctx->operating_channel); |
| |
| hdd_register_tx_flow_control(adapter, |
| hdd_softap_tx_resume_timer_expired_handler, |
| hdd_softap_tx_resume_cb, |
| hdd_tx_flow_control_is_pause); |
| |
| hdd_register_hl_netdev_fc_timer( |
| adapter, |
| hdd_tx_resume_timer_expired_handler); |
| |
| /* @@@ need wep logic here to set privacy bit */ |
| qdf_status = |
| hdd_softap_register_bc_sta(adapter, |
| ap_ctx->privacy); |
| if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { |
| hdd_warn("Failed to register BC STA %d", |
| qdf_status); |
| hdd_stop_bss_link(adapter); |
| } |
| } |
| |
| if (ucfg_ipa_is_enabled()) { |
| status = ucfg_ipa_wlan_evt(hdd_ctx->pdev, |
| adapter->dev, |
| adapter->device_mode, |
| ap_ctx->broadcast_sta_id, |
| adapter->vdev_id, |
| WLAN_IPA_AP_CONNECT, |
| adapter->dev->dev_addr); |
| if (status) |
| hdd_err("WLAN_AP_CONNECT event failed"); |
| } |
| |
| #ifdef FEATURE_WLAN_AUTO_SHUTDOWN |
| wlan_hdd_auto_shutdown_enable(hdd_ctx, true); |
| #endif |
| hdd_hostapd_channel_prevent_suspend(adapter, |
| ap_ctx->operating_channel); |
| |
| hostapd_state->bss_state = BSS_START; |
| hdd_start_tsf_sync(adapter); |
| |
| /* Set default key index */ |
| hdd_debug("default key index %hu", ap_ctx->wep_def_key_idx); |
| |
| sme_roam_set_default_key_index(mac_handle, |
| adapter->vdev_id, |
| ap_ctx->wep_def_key_idx); |
| |
| hdd_hostapd_set_sap_key(adapter); |
| |
| /* Fill the params for sending IWEVCUSTOM Event |
| * with SOFTAP.enabled |
| */ |
| startBssEvent = "SOFTAP.enabled"; |
| memset(&we_custom_start_event, '\0', |
| sizeof(we_custom_start_event)); |
| memcpy(&we_custom_start_event, startBssEvent, |
| strlen(startBssEvent)); |
| memset(&wrqu, 0, sizeof(wrqu)); |
| wrqu.data.length = strlen(startBssEvent); |
| we_event = IWEVCUSTOM; |
| we_custom_event_generic = we_custom_start_event; |
| hdd_ipa_set_tx_flow_info(); |
| |
| if (policy_mgr_is_hw_mode_change_after_vdev_up( |
| hdd_ctx->psoc)) { |
| hdd_debug("check for possible hw mode change"); |
| status = policy_mgr_set_hw_mode_on_channel_switch( |
| hdd_ctx->psoc, adapter->vdev_id); |
| if (QDF_IS_STATUS_ERROR(status)) |
| hdd_debug("set hw mode change not done"); |
| policy_mgr_set_do_hw_mode_change_flag( |
| hdd_ctx->psoc, false); |
| } |
| hdd_debug("check for SAP restart"); |
| policy_mgr_check_concurrent_intf_and_restart_sap( |
| hdd_ctx->psoc); |
| /* |
| * set this event at the very end because once this events |
| * get set, caller thread is waiting to do further processing. |
| * so once this event gets set, current worker thread might get |
| * pre-empted by caller thread. |
| */ |
| qdf_status = qdf_event_set(&hostapd_state->qdf_event); |
| if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { |
| hdd_err("qdf_event_set failed! status: %d", qdf_status); |
| goto stopbss; |
| } |
| break; /* Event will be sent after Switch-Case stmt */ |
| |
| case eSAP_STOP_BSS_EVENT: |
| hdd_debug("BSS stop status = %s", |
| sap_event->sapevt.sapStopBssCompleteEvent. |
| status ? "eSAP_STATUS_FAILURE" : "eSAP_STATUS_SUCCESS"); |
| |
| hdd_hostapd_channel_allow_suspend(adapter, |
| ap_ctx->operating_channel); |
| |
| /* Invalidate the channel info. */ |
| ap_ctx->operating_channel = 0; |
| |
| /* reset the dfs_cac_status and dfs_cac_block_tx flag only when |
| * the last BSS is stopped |
| */ |
| con_sap_adapter = hdd_get_con_sap_adapter(adapter, true); |
| if (!con_sap_adapter) { |
| ap_ctx->dfs_cac_block_tx = true; |
| hdd_ctx->dev_dfs_cac_status = DFS_CAC_NEVER_DONE; |
| } |
| hdd_debug("bss_stop_reason=%d", ap_ctx->bss_stop_reason); |
| if ((BSS_STOP_DUE_TO_MCC_SCC_SWITCH != |
| ap_ctx->bss_stop_reason) && |
| (BSS_STOP_DUE_TO_VENDOR_CONFIG_CHAN != |
| ap_ctx->bss_stop_reason)) { |
| /* |
| * when MCC to SCC switching or vendor subcmd |
| * setting sap config channel happens, key storage |
| * should not be cleared due to hostapd will not |
| * repopulate the original keys |
| */ |
| ap_ctx->group_key.keyLength = 0; |
| for (i = 0; i < CSR_MAX_NUM_KEY; i++) |
| ap_ctx->wep_key[i].keyLength = 0; |
| } |
| |
| /* clear the reason code in case BSS is stopped |
| * in another place |
| */ |
| ap_ctx->bss_stop_reason = BSS_STOP_REASON_INVALID; |
| ap_ctx->ap_active = false; |
| goto stopbss; |
| |
| case eSAP_DFS_CAC_START: |
| wlan_hdd_send_svc_nlink_msg(hdd_ctx->radio_index, |
| WLAN_SVC_DFS_CAC_START_IND, |
| &dfs_info, |
| sizeof(struct wlan_dfs_info)); |
| hdd_ctx->dev_dfs_cac_status = DFS_CAC_IN_PROGRESS; |
| if (QDF_STATUS_SUCCESS != |
| hdd_send_radar_event(hdd_ctx, eSAP_DFS_CAC_START, |
| dfs_info, &adapter->wdev)) { |
| hdd_err("Unable to indicate CAC start NL event"); |
| } else { |
| hdd_debug("Sent CAC start to user space"); |
| } |
| |
| qdf_atomic_set(&adapter->ch_switch_in_progress, 0); |
| break; |
| case eSAP_DFS_CAC_INTERRUPTED: |
| /* |
| * The CAC timer did not run completely and a radar was detected |
| * during the CAC time. This new state will keep the tx path |
| * blocked since we do not want any transmission on the DFS |
| * channel. CAC end will only be reported here since the user |
| * space applications are waiting on CAC end for their state |
| * management. |
| */ |
| if (QDF_STATUS_SUCCESS != |
| hdd_send_radar_event(hdd_ctx, eSAP_DFS_CAC_END, |
| dfs_info, &adapter->wdev)) { |
| hdd_err("Unable to indicate CAC end (interrupted) event"); |
| } else { |
| hdd_debug("Sent CAC end (interrupted) to user space"); |
| } |
| break; |
| case eSAP_DFS_CAC_END: |
| wlan_hdd_send_svc_nlink_msg(hdd_ctx->radio_index, |
| WLAN_SVC_DFS_CAC_END_IND, |
| &dfs_info, |
| sizeof(struct wlan_dfs_info)); |
| ap_ctx->dfs_cac_block_tx = false; |
| ucfg_ipa_set_dfs_cac_tx(hdd_ctx->pdev, |
| ap_ctx->dfs_cac_block_tx); |
| hdd_ctx->dev_dfs_cac_status = DFS_CAC_ALREADY_DONE; |
| if (QDF_STATUS_SUCCESS != |
| hdd_send_radar_event(hdd_ctx, eSAP_DFS_CAC_END, |
| dfs_info, &adapter->wdev)) { |
| hdd_err("Unable to indicate CAC end NL event"); |
| } else { |
| hdd_debug("Sent CAC end to user space"); |
| } |
| break; |
| case eSAP_DFS_RADAR_DETECT: |
| { |
| int i; |
| struct sap_config *sap_config = |
| &adapter->session.ap.sap_config; |
| |
| hdd_dfs_indicate_radar(hdd_ctx); |
| wlan_hdd_send_svc_nlink_msg(hdd_ctx->radio_index, |
| WLAN_SVC_DFS_RADAR_DETECT_IND, |
| &dfs_info, |
| sizeof(struct wlan_dfs_info)); |
| hdd_ctx->dev_dfs_cac_status = DFS_CAC_NEVER_DONE; |
| for (i = 0; i < sap_config->channel_info_count; i++) { |
| if (sap_config->channel_info[i].ieee_chan_number |
| == dfs_info.channel) |
| sap_config->channel_info[i].flags |= |
| IEEE80211_CHAN_RADAR_DFS; |
| } |
| if (QDF_STATUS_SUCCESS != |
| hdd_send_radar_event(hdd_ctx, eSAP_DFS_RADAR_DETECT, |
| dfs_info, &adapter->wdev)) { |
| hdd_err("Unable to indicate Radar detect NL event"); |
| } else { |
| hdd_debug("Sent radar detected to user space"); |
| } |
| break; |
| } |
| case eSAP_DFS_RADAR_DETECT_DURING_PRE_CAC: |
| hdd_debug("notification for radar detect during pre cac:%d", |
| adapter->vdev_id); |
| hdd_send_conditional_chan_switch_status(hdd_ctx, |
| &adapter->wdev, false); |
| hdd_ctx->dev_dfs_cac_status = DFS_CAC_NEVER_DONE; |
| qdf_create_work(0, &hdd_ctx->sap_pre_cac_work, |
| wlan_hdd_sap_pre_cac_failure, |
| (void *)adapter); |
| qdf_sched_work(0, &hdd_ctx->sap_pre_cac_work); |
| break; |
| case eSAP_DFS_PRE_CAC_END: |
| hdd_debug("pre cac end notification received:%d", |
| adapter->vdev_id); |
| hdd_send_conditional_chan_switch_status(hdd_ctx, |
| &adapter->wdev, true); |
| ap_ctx->dfs_cac_block_tx = false; |
| ucfg_ipa_set_dfs_cac_tx(hdd_ctx->pdev, |
| ap_ctx->dfs_cac_block_tx); |
| hdd_ctx->dev_dfs_cac_status = DFS_CAC_ALREADY_DONE; |
| |
| qdf_create_work(0, &hdd_ctx->sap_pre_cac_work, |
| wlan_hdd_sap_pre_cac_success, |
| (void *)adapter); |
| qdf_sched_work(0, &hdd_ctx->sap_pre_cac_work); |
| break; |
| case eSAP_DFS_NO_AVAILABLE_CHANNEL: |
| wlan_hdd_send_svc_nlink_msg |
| (hdd_ctx->radio_index, |
| WLAN_SVC_DFS_ALL_CHANNEL_UNAVAIL_IND, &dfs_info, |
| sizeof(struct wlan_dfs_info)); |
| break; |
| |
| case eSAP_STA_SET_KEY_EVENT: |
| /* TODO: |
| * forward the message to hostapd once implementation |
| * is done for now just print |
| */ |
| key_complete = &sap_event->sapevt.sapStationSetKeyCompleteEvent; |
| hdd_debug("SET Key: configured status = %s", |
| key_complete->status ? |
| "eSAP_STATUS_FAILURE" : "eSAP_STATUS_SUCCESS"); |
| |
| if (QDF_IS_STATUS_SUCCESS(key_complete->status)) { |
| hdd_softap_change_sta_state(adapter, |
| &key_complete->peerMacAddr, |
| OL_TXRX_PEER_STATE_AUTH); |
| status = wlan_hdd_send_sta_authorized_event( |
| adapter, hdd_ctx, |
| &key_complete->peerMacAddr); |
| |
| } |
| return QDF_STATUS_SUCCESS; |
| case eSAP_STA_MIC_FAILURE_EVENT: |
| { |
| memset(&msg, '\0', sizeof(msg)); |
| msg.src_addr.sa_family = ARPHRD_ETHER; |
| memcpy(msg.src_addr.sa_data, |
| &sap_event->sapevt.sapStationMICFailureEvent. |
| staMac, QDF_MAC_ADDR_SIZE); |
| hdd_debug("MIC MAC " MAC_ADDRESS_STR, |
| QDF_MAC_ADDR_ARRAY(msg.src_addr.sa_data)); |
| if (sap_event->sapevt.sapStationMICFailureEvent. |
| multicast == true) |
| msg.flags = IW_MICFAILURE_GROUP; |
| else |
| msg.flags = IW_MICFAILURE_PAIRWISE; |
| memset(&wrqu, 0, sizeof(wrqu)); |
| wrqu.data.length = sizeof(msg); |
| we_event = IWEVMICHAELMICFAILURE; |
| we_custom_event_generic = (uint8_t *) &msg; |
| } |
| /* inform mic failure to nl80211 */ |
| cfg80211_michael_mic_failure(dev, |
| sap_event-> |
| sapevt.sapStationMICFailureEvent. |
| staMac.bytes, |
| ((sap_event->sapevt. |
| sapStationMICFailureEvent. |
| multicast == |
| true) ? |
| NL80211_KEYTYPE_GROUP : |
| NL80211_KEYTYPE_PAIRWISE), |
| sap_event->sapevt. |
| sapStationMICFailureEvent.keyId, |
| sap_event->sapevt. |
| sapStationMICFailureEvent.TSC, |
| GFP_KERNEL); |
| break; |
| |
| case eSAP_STA_ASSOC_EVENT: |
| case eSAP_STA_REASSOC_EVENT: |
| event = &sap_event->sapevt.sapStationAssocReassocCompleteEvent; |
| if (eSAP_STATUS_FAILURE == event->status) { |
| hdd_info("assoc failure: " MAC_ADDRESS_STR, |
| QDF_MAC_ADDR_ARRAY(wrqu.addr.sa_data)); |
| break; |
| } |
| |
| hdd_hostapd_apply_action_oui(hdd_ctx, adapter, event); |
| |
| wrqu.addr.sa_family = ARPHRD_ETHER; |
| memcpy(wrqu.addr.sa_data, |
| &event->staMac, QDF_MAC_ADDR_SIZE); |
| hdd_info("associated " MAC_ADDRESS_STR, |
| QDF_MAC_ADDR_ARRAY(wrqu.addr.sa_data)); |
| we_event = IWEVREGISTERED; |
| |
| if ((eCSR_ENCRYPT_TYPE_NONE == ap_ctx->encryption_type) || |
| (eCSR_ENCRYPT_TYPE_WEP40_STATICKEY == |
| ap_ctx->encryption_type) |
| || (eCSR_ENCRYPT_TYPE_WEP104_STATICKEY == |
| ap_ctx->encryption_type)) { |
| bAuthRequired = false; |
| } |
| |
| if (bAuthRequired) { |
| qdf_status = hdd_softap_register_sta( |
| adapter, |
| true, |
| ap_ctx->privacy, |
| event->staId, |
| (struct qdf_mac_addr *) |
| wrqu.addr.sa_data, |
| event->wmmEnabled); |
| if (!QDF_IS_STATUS_SUCCESS(qdf_status)) |
| hdd_err("Failed to register STA %d " |
| MAC_ADDRESS_STR "", qdf_status, |
| QDF_MAC_ADDR_ARRAY(wrqu.addr.sa_data)); |
| } else { |
| qdf_status = hdd_softap_register_sta( |
| adapter, |
| false, |
| ap_ctx->privacy, |
| event->staId, |
| (struct qdf_mac_addr *) |
| wrqu.addr.sa_data, |
| event->wmmEnabled); |
| if (!QDF_IS_STATUS_SUCCESS(qdf_status)) |
| hdd_err("Failed to register STA %d " |
| MAC_ADDRESS_STR "", qdf_status, |
| QDF_MAC_ADDR_ARRAY(wrqu.addr.sa_data)); |
| } |
| |
| sta_id = event->staId; |
| if (QDF_IS_STATUS_SUCCESS(qdf_status)) |
| hdd_fill_station_info(adapter, event); |
| |
| adapter->sta_info[sta_id].ecsa_capable = event->ecsa_capable; |
| |
| if (ucfg_ipa_is_enabled()) { |
| status = ucfg_ipa_wlan_evt(hdd_ctx->pdev, |
| adapter->dev, |
| adapter->device_mode, |
| event->staId, |
| adapter->vdev_id, |
| WLAN_IPA_CLIENT_CONNECT_EX, |
| event->staMac.bytes); |
| if (status) |
| hdd_err("WLAN_CLIENT_CONNECT_EX event failed"); |
| } |
| |
| DPTRACE(qdf_dp_trace_mgmt_pkt(QDF_DP_TRACE_MGMT_PACKET_RECORD, |
| adapter->vdev_id, |
| QDF_TRACE_DEFAULT_PDEV_ID, |
| QDF_PROTO_TYPE_MGMT, QDF_PROTO_MGMT_ASSOC)); |
| |
| #ifdef MSM_PLATFORM |
| /* start timer in sap/p2p_go */ |
| if (ap_ctx->ap_active == false) { |
| spin_lock_bh(&hdd_ctx->bus_bw_lock); |
| adapter->prev_tx_packets = |
| adapter->stats.tx_packets; |
| adapter->prev_rx_packets = |
| adapter->stats.rx_packets; |
| |
| cdp_get_intra_bss_fwd_pkts_count( |
| cds_get_context(QDF_MODULE_ID_SOC), |
| adapter->vdev_id, |
| &adapter->prev_fwd_tx_packets, |
| &adapter->prev_fwd_rx_packets); |
| |
| spin_unlock_bh(&hdd_ctx->bus_bw_lock); |
| hdd_bus_bw_compute_timer_start(hdd_ctx); |
| } |
| #endif |
| ap_ctx->ap_active = true; |
| #ifdef FEATURE_WLAN_AUTO_SHUTDOWN |
| wlan_hdd_auto_shutdown_enable(hdd_ctx, false); |
| #endif |
| cds_host_diag_log_work(&hdd_ctx->sap_wake_lock, |
| HDD_SAP_WAKE_LOCK_DURATION, |
| WIFI_POWER_EVENT_WAKELOCK_SAP); |
| qdf_wake_lock_timeout_acquire(&hdd_ctx->sap_wake_lock, |
| HDD_SAP_WAKE_LOCK_DURATION); |
| { |
| struct station_info *sta_info; |
| uint32_t ies_len = event->ies_len; |
| |
| sta_info = qdf_mem_malloc(sizeof(*sta_info)); |
| if (!sta_info) { |
| hdd_err("Failed to allocate station info"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| sta_info->assoc_req_ies = event->ies; |
| sta_info->assoc_req_ies_len = ies_len; |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)) && !defined(WITH_BACKPORTS) |
| /* |
| * After Kernel 4.0, it's no longer need to set |
| * STATION_INFO_ASSOC_REQ_IES flag, as it |
| * changed to use assoc_req_ies_len length to |
| * check the existence of request IE. |
| */ |
| sta_info->filled |= STATION_INFO_ASSOC_REQ_IES; |
| #endif |
| cfg80211_new_sta(dev, |
| (const u8 *)&event->staMac.bytes[0], |
| sta_info, GFP_KERNEL); |
| qdf_mem_free(sta_info); |
| } |
| /* Lets abort scan to ensure smooth authentication for client */ |
| if (ucfg_scan_get_vdev_status(adapter->vdev) != |
| SCAN_NOT_IN_PROGRESS) { |
| wlan_abort_scan(hdd_ctx->pdev, INVAL_PDEV_ID, |
| adapter->vdev_id, INVALID_SCAN_ID, |
| false); |
| } |
| if (adapter->device_mode == QDF_P2P_GO_MODE) { |
| /* send peer status indication to oem app */ |
| hdd_send_peer_status_ind_to_app( |
| &event->staMac, |
| ePeerConnected, |
| event->timingMeasCap, |
| adapter->vdev_id, |
| &event->chan_info, |
| adapter->device_mode); |
| } |
| |
| hdd_green_ap_add_sta(hdd_ctx); |
| break; |
| |
| case eSAP_STA_DISASSOC_EVENT: |
| disassoc_comp = |
| &sap_event->sapevt.sapStationDisassocCompleteEvent; |
| memcpy(wrqu.addr.sa_data, |
| &disassoc_comp->staMac, QDF_MAC_ADDR_SIZE); |
| |
| cache_stainfo = hdd_get_stainfo(adapter->cache_sta_info, |
| disassoc_comp->staMac); |
| if (cache_stainfo) { |
| /* Cache the disassoc info */ |
| cache_stainfo->rssi = disassoc_comp->rssi; |
| cache_stainfo->tx_rate = disassoc_comp->tx_rate; |
| cache_stainfo->rx_rate = disassoc_comp->rx_rate; |
| cache_stainfo->reason_code = disassoc_comp->reason_code; |
| cache_stainfo->disassoc_ts = qdf_system_ticks(); |
| } |
| hdd_info(" disassociated " MAC_ADDRESS_STR, |
| QDF_MAC_ADDR_ARRAY(wrqu.addr.sa_data)); |
| |
| qdf_status = qdf_event_set(&hostapd_state->qdf_sta_disassoc_event); |
| if (!QDF_IS_STATUS_SUCCESS(qdf_status)) |
| hdd_err("Station Deauth event Set failed"); |
| |
| if (sap_event->sapevt.sapStationDisassocCompleteEvent.reason == |
| eSAP_USR_INITATED_DISASSOC) |
| hdd_debug(" User initiated disassociation"); |
| else |
| hdd_debug(" MAC initiated disassociation"); |
| we_event = IWEVEXPIRED; |
| qdf_status = |
| hdd_softap_get_sta_id(adapter, |
| &sap_event->sapevt. |
| sapStationDisassocCompleteEvent.staMac, |
| &sta_id); |
| if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { |
| hdd_err("Failed to find sta id status: %d", qdf_status); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| DPTRACE(qdf_dp_trace_mgmt_pkt(QDF_DP_TRACE_MGMT_PACKET_RECORD, |
| adapter->vdev_id, |
| QDF_TRACE_DEFAULT_PDEV_ID, |
| QDF_PROTO_TYPE_MGMT, QDF_PROTO_MGMT_DISASSOC)); |
| |
| stainfo = hdd_get_stainfo(adapter->sta_info, |
| disassoc_comp->staMac); |
| if (stainfo) { |
| /* Send DHCP STOP indication to FW */ |
| stainfo->dhcp_phase = DHCP_PHASE_ACK; |
| if (stainfo->dhcp_nego_status == |
| DHCP_NEGO_IN_PROGRESS) |
| hdd_post_dhcp_ind(adapter, sta_id, |
| WMA_DHCP_STOP_IND); |
| stainfo->dhcp_nego_status = DHCP_NEGO_STOP; |
| } |
| hdd_softap_deregister_sta(adapter, sta_id); |
| |
| ap_ctx->ap_active = false; |
| spin_lock_bh(&adapter->sta_info_lock); |
| for (i = 0; i < WLAN_MAX_STA_COUNT; i++) { |
| if (adapter->sta_info[i].in_use |
| && i != |
| (WLAN_HDD_GET_AP_CTX_PTR(adapter))-> |
| broadcast_sta_id) { |
| ap_ctx->ap_active = true; |
| break; |
| } |
| } |
| spin_unlock_bh(&adapter->sta_info_lock); |
| |
| #ifdef FEATURE_WLAN_AUTO_SHUTDOWN |
| wlan_hdd_auto_shutdown_enable(hdd_ctx, true); |
| #endif |
| |
| cds_host_diag_log_work(&hdd_ctx->sap_wake_lock, |
| HDD_SAP_WAKE_LOCK_DURATION, |
| WIFI_POWER_EVENT_WAKELOCK_SAP); |
| qdf_wake_lock_timeout_acquire(&hdd_ctx->sap_wake_lock, |
| HDD_SAP_CLIENT_DISCONNECT_WAKE_LOCK_DURATION); |
| cfg80211_del_sta(dev, |
| (const u8 *)&sap_event->sapevt. |
| sapStationDisassocCompleteEvent.staMac. |
| bytes[0], GFP_KERNEL); |
| |
| /* Update the beacon Interval if it is P2P GO */ |
| qdf_status = policy_mgr_change_mcc_go_beacon_interval( |
| hdd_ctx->psoc, adapter->vdev_id, |
| adapter->device_mode); |
| if (QDF_STATUS_SUCCESS != qdf_status) { |
| hdd_err("Failed to update Beacon interval status: %d", |
| qdf_status); |
| } |
| if (adapter->device_mode == QDF_P2P_GO_MODE) { |
| /* send peer status indication to oem app */ |
| hdd_send_peer_status_ind_to_app(&sap_event->sapevt. |
| sapStationDisassocCompleteEvent. |
| staMac, ePeerDisconnected, |
| 0, |
| adapter->vdev_id, |
| NULL, |
| adapter->device_mode); |
| } |
| #ifdef MSM_PLATFORM |
| /*stop timer in sap/p2p_go */ |
| if (ap_ctx->ap_active == false) { |
| spin_lock_bh(&hdd_ctx->bus_bw_lock); |
| adapter->prev_tx_packets = 0; |
| adapter->prev_rx_packets = 0; |
| adapter->prev_fwd_tx_packets = 0; |
| adapter->prev_fwd_rx_packets = 0; |
| spin_unlock_bh(&hdd_ctx->bus_bw_lock); |
| hdd_bus_bw_compute_timer_try_stop(hdd_ctx); |
| } |
| #endif |
| hdd_green_ap_del_sta(hdd_ctx); |
| break; |
| |
| case eSAP_WPS_PBC_PROBE_REQ_EVENT: |
| hdd_debug("WPS PBC probe req"); |
| return QDF_STATUS_SUCCESS; |
| |
| case eSAP_UNKNOWN_STA_JOIN: |
| snprintf(unknownSTAEvent, IW_CUSTOM_MAX, |
| "JOIN_UNKNOWN_STA-%02x:%02x:%02x:%02x:%02x:%02x", |
| sap_event->sapevt.sapUnknownSTAJoin.macaddr.bytes[0], |
| sap_event->sapevt.sapUnknownSTAJoin.macaddr.bytes[1], |
| sap_event->sapevt.sapUnknownSTAJoin.macaddr.bytes[2], |
| sap_event->sapevt.sapUnknownSTAJoin.macaddr.bytes[3], |
| sap_event->sapevt.sapUnknownSTAJoin.macaddr.bytes[4], |
| sap_event->sapevt.sapUnknownSTAJoin.macaddr.bytes[5]); |
| we_event = IWEVCUSTOM; /* Discovered a new node (AP mode). */ |
| wrqu.data.pointer = unknownSTAEvent; |
| wrqu.data.length = strlen(unknownSTAEvent); |
| we_custom_event_generic = (uint8_t *) unknownSTAEvent; |
| hdd_err("%s", unknownSTAEvent); |
| break; |
| |
| case eSAP_MAX_ASSOC_EXCEEDED: |
| snprintf(maxAssocExceededEvent, IW_CUSTOM_MAX, |
| "Peer %02x:%02x:%02x:%02x:%02x:%02x denied" |
| " assoc due to Maximum Mobile Hotspot connections reached. Please disconnect" |
| " one or more devices to enable the new device connection", |
| sap_event->sapevt.sapMaxAssocExceeded.macaddr.bytes[0], |
| sap_event->sapevt.sapMaxAssocExceeded.macaddr.bytes[1], |
| sap_event->sapevt.sapMaxAssocExceeded.macaddr.bytes[2], |
| sap_event->sapevt.sapMaxAssocExceeded.macaddr.bytes[3], |
| sap_event->sapevt.sapMaxAssocExceeded.macaddr.bytes[4], |
| sap_event->sapevt.sapMaxAssocExceeded.macaddr. |
| bytes[5]); |
| we_event = IWEVCUSTOM; /* Discovered a new node (AP mode). */ |
| wrqu.data.pointer = maxAssocExceededEvent; |
| wrqu.data.length = strlen(maxAssocExceededEvent); |
| we_custom_event_generic = (uint8_t *) maxAssocExceededEvent; |
| hdd_debug("%s", maxAssocExceededEvent); |
| break; |
| case eSAP_STA_ASSOC_IND: |
| if (sap_event->sapevt.sapAssocIndication.owe_ie) { |
| hdd_send_update_owe_info_event(adapter, |
| sap_event->sapevt.sapAssocIndication.staMac.bytes, |
| sap_event->sapevt.sapAssocIndication.owe_ie, |
| sap_event->sapevt.sapAssocIndication.owe_ie_len); |
| qdf_mem_free( |
| sap_event->sapevt.sapAssocIndication.owe_ie); |
| sap_event->sapevt.sapAssocIndication.owe_ie = NULL; |
| sap_event->sapevt.sapAssocIndication.owe_ie_len = 0; |
| } |
| return QDF_STATUS_SUCCESS; |
| |
| case eSAP_DISCONNECT_ALL_P2P_CLIENT: |
| hdd_clear_all_sta(adapter); |
| return QDF_STATUS_SUCCESS; |
| |
| case eSAP_MAC_TRIG_STOP_BSS_EVENT: |
| ret = hdd_stop_bss_link(adapter); |
| if (ret) |
| hdd_warn("hdd_stop_bss_link failed %d", ret); |
| return QDF_STATUS_SUCCESS; |
| |
| case eSAP_CHANNEL_CHANGE_EVENT: |
| hdd_debug("Received eSAP_CHANNEL_CHANGE_EVENT event"); |
| if (hostapd_state->bss_state != BSS_STOP) { |
| /* Prevent suspend for new channel */ |
| hdd_hostapd_channel_prevent_suspend(adapter, |
| sap_event->sapevt.sap_ch_selected.pri_ch); |
| /* Allow suspend for old channel */ |
| hdd_hostapd_channel_allow_suspend(adapter, |
| ap_ctx->operating_channel); |
| } |
| /* SME/PE is already updated for new operation |
| * channel. So update HDD layer also here. This |
| * resolves issue in AP-AP mode where AP1 channel is |
| * changed due to RADAR then CAC is going on and |
| * START_BSS on new channel has not come to HDD. At |
| * this case if AP2 is started it needs current |
| * operation channel for MCC DFS restriction |
| */ |
| ap_ctx->operating_channel = |
| sap_event->sapevt.sap_ch_selected.pri_ch; |
| ap_ctx->sap_config.acs_cfg.pri_ch = |
| sap_event->sapevt.sap_ch_selected.pri_ch; |
| ap_ctx->sap_config.acs_cfg.ht_sec_ch = |
| sap_event->sapevt.sap_ch_selected.ht_sec_ch; |
| ap_ctx->sap_config.acs_cfg.vht_seg0_center_ch = |
| sap_event->sapevt.sap_ch_selected.vht_seg0_center_ch; |
| ap_ctx->sap_config.acs_cfg.vht_seg1_center_ch = |
| sap_event->sapevt.sap_ch_selected.vht_seg1_center_ch; |
| ap_ctx->sap_config.acs_cfg.ch_width = |
| sap_event->sapevt.sap_ch_selected.ch_width; |
| |
| sap_ch_param.ch_width = |
| sap_event->sapevt.sap_ch_selected.ch_width; |
| sap_ch_param.center_freq_seg0 = |
| sap_event->sapevt.sap_ch_selected.vht_seg0_center_ch; |
| sap_ch_param.center_freq_seg1 = |
| sap_event->sapevt.sap_ch_selected.vht_seg1_center_ch; |
| wlan_reg_set_channel_params(hdd_ctx->pdev, |
| sap_event->sapevt.sap_ch_selected.pri_ch, |
| sap_event->sapevt.sap_ch_selected.ht_sec_ch, |
| &sap_ch_param); |
| |
| cdp_hl_fc_set_td_limit(cds_get_context(QDF_MODULE_ID_SOC), |
| adapter->vdev_id, |
| ap_ctx->operating_channel); |
| |
| phy_mode = wlan_sap_get_phymode( |
| WLAN_HDD_GET_SAP_CTX_PTR(adapter)); |
| |
| switch (phy_mode) { |
| case eCSR_DOT11_MODE_11n: |
| case eCSR_DOT11_MODE_11n_ONLY: |
| case eCSR_DOT11_MODE_11ac: |
| case eCSR_DOT11_MODE_11ac_ONLY: |
| legacy_phymode = false; |
| break; |
| default: |
| legacy_phymode = true; |
| break; |
| } |
| |
| chan_change.chan = |
| sap_event->sapevt.sap_ch_selected.pri_ch; |
| chan_change.chan_params.ch_width = |
| sap_event->sapevt.sap_ch_selected.ch_width; |
| chan_change.chan_params.sec_ch_offset = |
| sap_ch_param.sec_ch_offset; |
| chan_change.chan_params.center_freq_seg0 = |
| sap_event->sapevt.sap_ch_selected.vht_seg0_center_ch; |
| chan_change.chan_params.center_freq_seg1 = |
| sap_event->sapevt.sap_ch_selected.vht_seg1_center_ch; |
| |
| return hdd_chan_change_notify(adapter, dev, |
| chan_change, legacy_phymode); |
| case eSAP_ACS_SCAN_SUCCESS_EVENT: |
| return hdd_handle_acs_scan_event(sap_event, adapter); |
| |
| case eSAP_ACS_CHANNEL_SELECTED: |
| hdd_debug("ACS Completed for wlan%d", |
| adapter->dev->ifindex); |
| clear_bit(ACS_PENDING, &adapter->event_flags); |
| clear_bit(ACS_IN_PROGRESS, &hdd_ctx->g_event_flags); |
| ap_ctx->sap_config.acs_cfg.pri_ch = |
| sap_event->sapevt.sap_ch_selected.pri_ch; |
| ap_ctx->sap_config.acs_cfg.ht_sec_ch = |
| sap_event->sapevt.sap_ch_selected.ht_sec_ch; |
| ap_ctx->sap_config.acs_cfg.vht_seg0_center_ch = |
| sap_event->sapevt.sap_ch_selected.vht_seg0_center_ch; |
| ap_ctx->sap_config.acs_cfg.vht_seg1_center_ch = |
| sap_event->sapevt.sap_ch_selected.vht_seg1_center_ch; |
| ap_ctx->sap_config.acs_cfg.ch_width = |
| sap_event->sapevt.sap_ch_selected.ch_width; |
| wlan_hdd_cfg80211_acs_ch_select_evt(adapter); |
| qdf_atomic_set(&adapter->session.ap.acs_in_progress, 0); |
| return QDF_STATUS_SUCCESS; |
| case eSAP_ECSA_CHANGE_CHAN_IND: |
| hdd_debug("Channel change indication from peer for channel %d", |
| sap_event->sapevt.sap_chan_cng_ind.new_chan); |
| if (hdd_softap_set_channel_change(dev, |
| sap_event->sapevt.sap_chan_cng_ind.new_chan, |
| CH_WIDTH_MAX, false)) |
| return QDF_STATUS_E_FAILURE; |
| else |
| return QDF_STATUS_SUCCESS; |
| |
| case eSAP_DFS_NEXT_CHANNEL_REQ: |
| hdd_debug("Sending next channel query to userspace"); |
| hdd_update_acs_timer_reason(adapter, |
| QCA_WLAN_VENDOR_ACS_SELECT_REASON_DFS); |
| return QDF_STATUS_SUCCESS; |
| |
| case eSAP_STOP_BSS_DUE_TO_NO_CHNL: |
| hdd_debug("Stop sap session[%d]", |
| adapter->vdev_id); |
| INIT_WORK(&adapter->sap_stop_bss_work, |
| hdd_stop_sap_due_to_invalid_channel); |
| schedule_work(&adapter->sap_stop_bss_work); |
| return QDF_STATUS_SUCCESS; |
| |
| case eSAP_CHANNEL_CHANGE_RESP: |
| hdd_debug("Channel change rsp status = %d", |
| sap_event->sapevt.ch_change_rsp_status); |
| /* |
| * Set the ch_switch_in_progress flag to zero and also enable |
| * roaming once channel change process (success/failure) |
| * is completed |
| */ |
| qdf_atomic_set(&adapter->ch_switch_in_progress, 0); |
| policy_mgr_set_chan_switch_complete_evt(hdd_ctx->psoc); |
| wlan_hdd_enable_roaming(adapter); |
| return QDF_STATUS_SUCCESS; |
| |
| default: |
| hdd_debug("SAP message is not handled"); |
| goto stopbss; |
| return QDF_STATUS_SUCCESS; |
| } |
| wireless_send_event(dev, we_event, &wrqu, |
| (char *)we_custom_event_generic); |
| |
| return QDF_STATUS_SUCCESS; |
| |
| stopbss: |
| { |
| uint8_t we_custom_event[64]; |
| char *stopBssEvent = "STOP-BSS.response"; /* 17 */ |
| int event_len = strlen(stopBssEvent); |
| |
| hdd_debug("BSS stop status = %s", |
| sap_event->sapevt.sapStopBssCompleteEvent.status ? |
| "eSAP_STATUS_FAILURE" : "eSAP_STATUS_SUCCESS"); |
| |
| /* Change the BSS state now since, as we are shutting |
| * things down, we don't want interfaces to become |
| * re-enabled |
| */ |
| hostapd_state->bss_state = BSS_STOP; |
| hdd_stop_tsf_sync(adapter); |
| |
| #ifdef FEATURE_WLAN_AUTO_SHUTDOWN |
| wlan_hdd_auto_shutdown_enable(hdd_ctx, true); |
| #endif |
| |
| /* Stop the pkts from n/w stack as we are going to free all of |
| * the TX WMM queues for all STAID's |
| */ |
| hdd_debug("Disabling queues"); |
| wlan_hdd_netif_queue_control(adapter, |
| WLAN_STOP_ALL_NETIF_QUEUE_N_CARRIER, |
| WLAN_CONTROL_PATH); |
| |
| /* reclaim all resources allocated to the BSS */ |
| qdf_status = hdd_softap_stop_bss(adapter); |
| if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { |
| hdd_warn("hdd_softap_stop_bss failed %d", |
| qdf_status); |
| if (ucfg_ipa_is_enabled()) { |
| ucfg_ipa_uc_disconnect_ap(hdd_ctx->pdev, |
| adapter->dev); |
| ucfg_ipa_cleanup_dev_iface(hdd_ctx->pdev, |
| adapter->dev); |
| } |
| } |
| |
| /* notify userspace that the BSS has stopped */ |
| memset(&we_custom_event, '\0', sizeof(we_custom_event)); |
| memcpy(&we_custom_event, stopBssEvent, event_len); |
| memset(&wrqu, 0, sizeof(wrqu)); |
| wrqu.data.length = event_len; |
| we_event = IWEVCUSTOM; |
| we_custom_event_generic = we_custom_event; |
| wireless_send_event(dev, we_event, &wrqu, |
| (char *)we_custom_event_generic); |
| |
| /* once the event is set, structure dev/adapter should |
| * not be touched since they are now subject to being deleted |
| * by another thread |
| */ |
| if (eSAP_STOP_BSS_EVENT == event_id) { |
| qdf_event_set(&hostapd_state->qdf_stop_bss_event); |
| hdd_bus_bw_compute_timer_try_stop(hdd_ctx); |
| } |
| |
| hdd_ipa_set_tx_flow_info(); |
| } |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| static int hdd_softap_unpack_ie(mac_handle_t mac_handle, |
| eCsrEncryptionType *encrypt_type, |
| eCsrEncryptionType *mc_encrypt_type, |
| tCsrAuthList *akm_list, |
| bool *mfp_capable, |
| bool *mfp_required, |
| uint16_t gen_ie_len, uint8_t *gen_ie) |
| { |
| uint32_t ret; |
| uint8_t *rsn_ie; |
| uint16_t rsn_ie_len, i; |
| tDot11fIERSN dot11_rsn_ie = {0}; |
| tDot11fIEWPA dot11_wpa_ie = {0}; |
| |
| if (!mac_handle) { |
| hdd_err("Error haHandle returned NULL"); |
| return -EINVAL; |
| } |
| /* Validity checks */ |
| if ((gen_ie_len < QDF_MIN(DOT11F_IE_RSN_MIN_LEN, DOT11F_IE_WPA_MIN_LEN)) |
| || (gen_ie_len > |
| QDF_MAX(DOT11F_IE_RSN_MAX_LEN, DOT11F_IE_WPA_MAX_LEN))) |
| return -EINVAL; |
| /* Type check */ |
| if (gen_ie[0] == DOT11F_EID_RSN) { |
| /* Validity checks */ |
| if ((gen_ie_len < DOT11F_IE_RSN_MIN_LEN) || |
| (gen_ie_len > DOT11F_IE_RSN_MAX_LEN)) { |
| return QDF_STATUS_E_FAILURE; |
| } |
| /* Skip past the EID byte and length byte */ |
| rsn_ie = gen_ie + 2; |
| rsn_ie_len = gen_ie_len - 2; |
| /* Unpack the RSN IE */ |
| memset(&dot11_rsn_ie, 0, sizeof(tDot11fIERSN)); |
| ret = sme_unpack_rsn_ie(mac_handle, rsn_ie, rsn_ie_len, |
| &dot11_rsn_ie, false); |
| if (DOT11F_FAILED(ret)) { |
| hdd_err("unpack failed, ret: 0x%x", ret); |
| return -EINVAL; |
| } |
| /* Copy out the encryption and authentication types */ |
| hdd_debug("pairwise cipher suite count: %d", |
| dot11_rsn_ie.pwise_cipher_suite_count); |
| hdd_debug("authentication suite count: %d", |
| dot11_rsn_ie.akm_suite_cnt); |
| /* |
| * Translate akms in akm suite |
| */ |
| for (i = 0; i < dot11_rsn_ie.akm_suite_cnt; i++) |
| akm_list->authType[i] = |
| hdd_translate_rsn_to_csr_auth_type( |
| dot11_rsn_ie.akm_suite[i]); |
| akm_list->numEntries = dot11_rsn_ie.akm_suite_cnt; |
| /* dot11_rsn_ie.pwise_cipher_suite_count */ |
| *encrypt_type = |
| hdd_translate_rsn_to_csr_encryption_type(dot11_rsn_ie. |
| pwise_cipher_suites[0]); |
| /* dot11_rsn_ie.gp_cipher_suite_count */ |
| *mc_encrypt_type = |
| hdd_translate_rsn_to_csr_encryption_type(dot11_rsn_ie. |
| gp_cipher_suite); |
| /* Set the PMKSA ID Cache for this interface */ |
| *mfp_capable = 0 != (dot11_rsn_ie.RSN_Cap[0] & 0x80); |
| *mfp_required = 0 != (dot11_rsn_ie.RSN_Cap[0] & 0x40); |
| } else if (gen_ie[0] == DOT11F_EID_WPA) { |
| /* Validity checks */ |
| if ((gen_ie_len < DOT11F_IE_WPA_MIN_LEN) || |
| (gen_ie_len > DOT11F_IE_WPA_MAX_LEN)) { |
| return QDF_STATUS_E_FAILURE; |
| } |
| /* Skip past the EID byte and length byte and 4 byte WiFi OUI */ |
| rsn_ie = gen_ie + 2 + 4; |
| rsn_ie_len = gen_ie_len - (2 + 4); |
| /* Unpack the WPA IE */ |
| memset(&dot11_wpa_ie, 0, sizeof(tDot11fIEWPA)); |
| ret = dot11f_unpack_ie_wpa(MAC_CONTEXT(mac_handle), |
| rsn_ie, rsn_ie_len, |
| &dot11_wpa_ie, false); |
| if (DOT11F_FAILED(ret)) { |
| hdd_err("unpack failed, ret: 0x%x", ret); |
| return -EINVAL; |
| } |
| /* Copy out the encryption and authentication types */ |
| hdd_debug("WPA unicast cipher suite count: %d", |
| dot11_wpa_ie.unicast_cipher_count); |
| hdd_debug("WPA authentication suite count: %d", |
| dot11_wpa_ie.auth_suite_count); |
| /* dot11_wpa_ie.auth_suite_count */ |
| /* |
| * Translate akms in akm suite |
| */ |
| for (i = 0; i < dot11_wpa_ie.auth_suite_count; i++) |
| akm_list->authType[i] = |
| hdd_translate_wpa_to_csr_auth_type( |
| dot11_wpa_ie.auth_suites[i]); |
| akm_list->numEntries = dot11_wpa_ie.auth_suite_count; |
| /* dot11_wpa_ie.unicast_cipher_count */ |
| *encrypt_type = |
| hdd_translate_wpa_to_csr_encryption_type(dot11_wpa_ie. |
| unicast_ciphers[0]); |
| /* dot11_wpa_ie.unicast_cipher_count */ |
| *mc_encrypt_type = |
| hdd_translate_wpa_to_csr_encryption_type(dot11_wpa_ie. |
| multicast_cipher); |
| *mfp_capable = false; |
| *mfp_required = false; |
| } else { |
| hdd_err("gen_ie[0]: %d", gen_ie[0]); |
| return QDF_STATUS_E_FAILURE; |
| } |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| /** |
| * hdd_is_any_sta_connecting() - check if any sta is connecting |
| * @hdd_ctx: hdd context |
| * |
| * Return: true if any sta is connecting |
| */ |
| static bool hdd_is_any_sta_connecting(struct hdd_context *hdd_ctx) |
| { |
| struct hdd_adapter *adapter = NULL; |
| struct hdd_station_ctx *sta_ctx; |
| |
| if (!hdd_ctx) { |
| hdd_err("HDD context is NULL"); |
| return false; |
| } |
| |
| hdd_for_each_adapter(hdd_ctx, adapter) { |
| sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); |
| if ((adapter->device_mode == QDF_STA_MODE) || |
| (adapter->device_mode == QDF_P2P_CLIENT_MODE) || |
| (adapter->device_mode == QDF_P2P_DEVICE_MODE)) { |
| if (sta_ctx->conn_info.conn_state == |
| eConnectionState_Connecting) { |
| hdd_debug("vdev_id %d: connecting", |
| adapter->vdev_id); |
| return true; |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| /** |
| * hdd_softap_set_channel_change() - |
| * This function to support SAP channel change with CSA IE |
| * set in the beacons. |
| * |
| * @dev: pointer to the net device. |
| * @target_channel: target channel number. |
| * @target_bw: Target bandwidth to move. |
| * If no bandwidth is specified, the value is CH_WIDTH_MAX |
| * @forced: Force to switch channel, ignore SCC/MCC check |
| * |
| * Return: 0 for success, non zero for failure |
| */ |
| int hdd_softap_set_channel_change(struct net_device *dev, int target_channel, |
| enum phy_ch_width target_bw, bool forced) |
| { |
| QDF_STATUS status; |
| int ret = 0; |
| struct hdd_adapter *adapter = (netdev_priv(dev)); |
| struct hdd_context *hdd_ctx = NULL; |
| struct hdd_adapter *sta_adapter; |
| struct hdd_station_ctx *sta_ctx; |
| uint8_t conc_rule1 = 0; |
| uint8_t scc_on_lte_coex = 0; |
| |
| hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (ret) |
| return ret; |
| |
| /* |
| * If sta connection is in progress do not allow SAP channel change from |
| * user space as it may change the HW mode requirement, for which sta is |
| * trying to connect. |
| */ |
| if (hdd_is_any_sta_connecting(hdd_ctx)) { |
| hdd_err("STA connection is in progress"); |
| return -EBUSY; |
| } |
| |
| ret = hdd_validate_channel_and_bandwidth(adapter, |
| target_channel, target_bw); |
| if (ret) { |
| hdd_err("Invalid CH and BW combo"); |
| return ret; |
| } |
| |
| sta_adapter = hdd_get_adapter(hdd_ctx, QDF_STA_MODE); |
| ucfg_policy_mgr_get_conc_rule1(hdd_ctx->psoc, &conc_rule1); |
| /* |
| * conc_custom_rule1: |
| * Force SCC for SAP + STA |
| * if STA is already connected then we shouldn't allow |
| * channel switch in SAP interface. |
| */ |
| if (sta_adapter && conc_rule1) { |
| sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(sta_adapter); |
| if (hdd_conn_is_connected(sta_ctx)) { |
| hdd_err("Channel switch not allowed after STA connection with conc_custom_rule1 enabled"); |
| return -EBUSY; |
| } |
| } |
| |
| /* |
| * Set the ch_switch_in_progress flag to mimic channel change |
| * when a radar is found. This will enable synchronizing |
| * SAP and HDD states similar to that of radar indication. |
| * Suspend the netif queues to stop queuing Tx frames |
| * from upper layers. netif queues will be resumed |
| * once the channel change is completed and SAP will |
| * post eSAP_START_BSS_EVENT success event to HDD. |
| */ |
| if (qdf_atomic_inc_return(&adapter->ch_switch_in_progress) > 1) { |
| hdd_err("Channel switch in progress!!"); |
| return -EBUSY; |
| } |
| |
| /* |
| * Do SAP concurrency check to cover channel switch case as following: |
| * There is already existing SAP+GO combination but due to upper layer |
| * notifying LTE-COEX event or sending command to move one connection |
| * to different channel. Before moving existing connection to new |
| * channel, check if new channel can co-exist with the other existing |
| * connection. For example, SAP1 is on channel-6 and SAP2 is on |
| * channel-36 and lets say they are doing DBS, and upper layer sends |
| * LTE-COEX to move SAP1 from channel-6 to channel-149. SAP1 and |
| * SAP2 will end up doing MCC which may not be desirable result. It |
| * should will be prevented. |
| */ |
| if (!policy_mgr_allow_concurrency_csa( |
| hdd_ctx->psoc, |
| policy_mgr_convert_device_mode_to_qdf_type( |
| adapter->device_mode), |
| target_channel, |
| adapter->vdev_id)) { |
| hdd_err("Channel switch failed due to concurrency check failure"); |
| qdf_atomic_set(&adapter->ch_switch_in_progress, 0); |
| return -EINVAL; |
| } |
| |
| status = |
| ucfg_policy_mgr_get_sta_sap_scc_lte_coex_chnl(hdd_ctx->psoc, |
| &scc_on_lte_coex); |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| hdd_err("can't get STA-SAP SCC on lte coex channel setting"); |
| qdf_atomic_set(&adapter->ch_switch_in_progress, 0); |
| return -EINVAL; |
| } |
| |
| /* |
| * Reject channel change req if reassoc in progress on any adapter. |
| * sme_is_any_session_in_middle_of_roaming is for LFR2 and |
| * hdd_is_roaming_in_progress is for LFR3 |
| */ |
| if (sme_is_any_session_in_middle_of_roaming(hdd_ctx->mac_handle) || |
| hdd_is_roaming_in_progress(hdd_ctx)) { |
| hdd_info("Channel switch not allowed as reassoc in progress"); |
| qdf_atomic_set(&adapter->ch_switch_in_progress, 0); |
| return -EINVAL; |
| } |
| /* Disable Roaming on all adapters before doing channel change */ |
| wlan_hdd_disable_roaming(adapter); |
| |
| /* |
| * Post the Channel Change request to SAP. |
| */ |
| status = wlansap_set_channel_change_with_csa( |
| WLAN_HDD_GET_SAP_CTX_PTR(adapter), |
| (uint32_t)target_channel, |
| target_bw, |
| forced && !scc_on_lte_coex); |
| |
| if (QDF_STATUS_SUCCESS != status) { |
| hdd_err("SAP set channel failed for channel: %d, bw: %d", |
| target_channel, target_bw); |
| /* |
| * If channel change command fails then clear the |
| * radar found flag and also restart the netif |
| * queues. |
| */ |
| qdf_atomic_set(&adapter->ch_switch_in_progress, 0); |
| |
| /* |
| * If Posting of the Channel Change request fails |
| * enable roaming on all adapters |
| */ |
| wlan_hdd_enable_roaming(adapter); |
| |
| ret = -EINVAL; |
| } |
| |
| return ret; |
| } |
| |
| #ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH |
| /** |
| * hdd_sap_restart_with_channel_switch() - SAP channel change with E/CSA |
| * @ap_adapter: HDD adapter |
| * @target_channel: Channel to which switch must happen |
| * @target_bw: Bandwidth of the target channel |
| * @forced: Force to switch channel, ignore SCC/MCC check |
| * |
| * Invokes the necessary API to perform channel switch for the SAP or GO |
| * |
| * Return: None |
| */ |
| void hdd_sap_restart_with_channel_switch(struct hdd_adapter *ap_adapter, |
| uint32_t target_channel, |
| uint32_t target_bw, |
| bool forced) |
| { |
| struct net_device *dev = ap_adapter->dev; |
| int ret; |
| |
| hdd_enter(); |
| |
| if (!dev) { |
| hdd_err("Invalid dev pointer"); |
| return; |
| } |
| |
| ret = hdd_softap_set_channel_change(dev, target_channel, |
| target_bw, forced); |
| if (ret) { |
| hdd_err("channel switch failed"); |
| return; |
| } |
| } |
| |
| void hdd_sap_restart_chan_switch_cb(struct wlan_objmgr_psoc *psoc, |
| uint8_t vdev_id, uint32_t channel, |
| uint32_t channel_bw, |
| bool forced) |
| { |
| struct hdd_adapter *ap_adapter = |
| wlan_hdd_get_adapter_from_vdev(psoc, vdev_id); |
| |
| if (!ap_adapter) { |
| hdd_err("Adapter is NULL"); |
| return; |
| } |
| hdd_sap_restart_with_channel_switch(ap_adapter, channel, |
| channel_bw, forced); |
| } |
| |
| QDF_STATUS wlan_hdd_get_channel_for_sap_restart( |
| struct wlan_objmgr_psoc *psoc, |
| uint8_t vdev_id, uint8_t *channel, |
| uint8_t *sec_ch) |
| { |
| mac_handle_t mac_handle; |
| struct hdd_ap_ctx *hdd_ap_ctx; |
| uint8_t intf_ch = 0; |
| struct hdd_context *hdd_ctx; |
| struct hdd_station_ctx *hdd_sta_ctx; |
| struct hdd_adapter *sta_adapter; |
| uint8_t mcc_to_scc_switch = 0; |
| struct ch_params ch_params; |
| struct hdd_adapter *ap_adapter = wlan_hdd_get_adapter_from_vdev( |
| psoc, vdev_id); |
| if (!ap_adapter) { |
| hdd_err("ap_adapter is NULL"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| hdd_ctx = WLAN_HDD_GET_CTX(ap_adapter); |
| if (!hdd_ctx) { |
| hdd_err("hdd_ctx is NULL"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| /* TODO: need work for 3 port case with sta+sta */ |
| sta_adapter = hdd_get_adapter(hdd_ctx, QDF_STA_MODE); |
| if (!sta_adapter) { |
| hdd_err("sta_adapter is NULL"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| if (!channel || !sec_ch) { |
| hdd_err("Null parameters"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| if (!test_bit(SOFTAP_BSS_STARTED, &ap_adapter->event_flags)) { |
| hdd_err("SOFTAP_BSS_STARTED not set"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| hdd_ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(ap_adapter); |
| hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(sta_adapter); |
| |
| mac_handle = hdd_ctx->mac_handle; |
| if (!mac_handle) { |
| hdd_err("mac_handle is NULL"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| if (policy_mgr_get_connection_count(psoc) == 1) { |
| /* |
| * If STA+SAP sessions are on DFS channel and STA+SAP SCC is |
| * enabled on DFS channel then move the SAP out of DFS channel |
| * as soon as STA gets disconnect. |
| */ |
| if (policy_mgr_is_sap_restart_required_after_sta_disconnect( |
| psoc, &intf_ch)) { |
| hdd_debug("Move the sap to user configured channel %u", |
| intf_ch); |
| goto sap_restart; |
| } |
| } |
| ucfg_policy_mgr_get_mcc_scc_switch(hdd_ctx->psoc, |
| &mcc_to_scc_switch); |
| /* |
| * Check if STA's channel is DFS or passive or part of LTE avoided |
| * channel list. In that case move SAP to other band if DBS is |
| * supported, return from here if DBS is not supported. |
| * Need to take care of 3 port cases with 2 STA iface in future. |
| */ |
| intf_ch = wlansap_check_cc_intf(hdd_ap_ctx->sap_context); |
| hdd_info("intf_ch: %d", intf_ch); |
| if (QDF_MCC_TO_SCC_SWITCH_FORCE_PREFERRED_WITHOUT_DISCONNECTION != |
| mcc_to_scc_switch) { |
| if (QDF_IS_STATUS_ERROR( |
| policy_mgr_valid_sap_conc_channel_check( |
| hdd_ctx->psoc, |
| &intf_ch, |
| policy_mgr_mode_specific_get_channel( |
| hdd_ctx->psoc, PM_SAP_MODE)))) { |
| hdd_debug("can't move sap to %d", |
| hdd_sta_ctx->conn_info.channel); |
| return QDF_STATUS_E_FAILURE; |
| } |
| } |
| |
| sap_restart: |
| if (intf_ch == 0) { |
| hdd_debug("interface channel is 0"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| hdd_info("SAP restart orig chan: %d, new chan: %d", |
| hdd_ap_ctx->sap_config.channel, intf_ch); |
| ch_params.ch_width = CH_WIDTH_MAX; |
| hdd_ap_ctx->bss_stop_reason = BSS_STOP_DUE_TO_MCC_SCC_SWITCH; |
| |
| wlan_reg_set_channel_params(hdd_ctx->pdev, |
| intf_ch, |
| 0, |
| &ch_params); |
| |
| wlansap_get_sec_channel(ch_params.sec_ch_offset, intf_ch, sec_ch); |
| |
| *channel = intf_ch; |
| |
| hdd_info("SAP channel change with CSA/ECSA"); |
| hdd_sap_restart_chan_switch_cb(psoc, vdev_id, |
| intf_ch, |
| ch_params.ch_width, false); |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| #endif |
| |
| #ifdef WLAN_FEATURE_TSF_PTP |
| static const struct ethtool_ops wlan_hostapd_ethtool_ops = { |
| .get_ts_info = wlan_get_ts_info, |
| }; |
| #endif |
| |
| const struct net_device_ops net_ops_struct = { |
| .ndo_open = hdd_hostapd_open, |
| .ndo_stop = hdd_hostapd_stop, |
| .ndo_uninit = hdd_hostapd_uninit, |
| .ndo_start_xmit = hdd_softap_hard_start_xmit, |
| .ndo_tx_timeout = hdd_softap_tx_timeout, |
| .ndo_get_stats = hdd_get_stats, |
| .ndo_set_mac_address = hdd_hostapd_set_mac_address, |
| .ndo_do_ioctl = hdd_ioctl, |
| .ndo_change_mtu = hdd_hostapd_change_mtu, |
| .ndo_select_queue = hdd_select_queue, |
| }; |
| |
| #ifdef WLAN_FEATURE_TSF_PTP |
| void hdd_set_ap_ops(struct net_device *dev) |
| { |
| dev->netdev_ops = &net_ops_struct; |
| dev->ethtool_ops = &wlan_hostapd_ethtool_ops; |
| } |
| #else |
| void hdd_set_ap_ops(struct net_device *dev) |
| { |
| dev->netdev_ops = &net_ops_struct; |
| } |
| #endif |
| |
| bool hdd_sap_create_ctx(struct hdd_adapter *adapter) |
| { |
| hdd_debug("creating sap context"); |
| adapter->session.ap.sap_context = sap_create_ctx(); |
| if (adapter->session.ap.sap_context) |
| return true; |
| |
| return false; |
| } |
| |
| bool hdd_sap_destroy_ctx(struct hdd_adapter *adapter) |
| { |
| hdd_debug("destroying sap context"); |
| sap_destroy_ctx(adapter->session.ap.sap_context); |
| adapter->session.ap.sap_context = NULL; |
| |
| return true; |
| } |
| |
| void hdd_sap_destroy_ctx_all(struct hdd_context *hdd_ctx, bool is_ssr) |
| { |
| struct hdd_adapter *adapter; |
| |
| /* sap_ctx is not destroyed as it will be leveraged for sap restart */ |
| if (is_ssr) |
| return; |
| |
| hdd_debug("destroying all the sap context"); |
| |
| hdd_for_each_adapter(hdd_ctx, adapter) { |
| if (adapter->device_mode == QDF_SAP_MODE) |
| hdd_sap_destroy_ctx(adapter); |
| } |
| } |
| |
| QDF_STATUS hdd_init_ap_mode(struct hdd_adapter *adapter, bool reinit) |
| { |
| struct hdd_hostapd_state *phostapdBuf; |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| QDF_STATUS status = QDF_STATUS_E_FAILURE; |
| struct sap_context *sap_context = NULL; |
| int ret; |
| enum dfs_mode acs_dfs_mode; |
| |
| hdd_enter(); |
| |
| hdd_info("SSR in progress: %d", reinit); |
| qdf_atomic_init(&adapter->session.ap.acs_in_progress); |
| |
| sap_context = hdd_hostapd_init_sap_session(adapter, reinit); |
| if (!sap_context) { |
| hdd_err("Invalid sap_ctx"); |
| goto error_release_vdev; |
| } |
| |
| if (!reinit) { |
| adapter->session.ap.sap_config.channel = |
| hdd_ctx->acs_policy.acs_channel; |
| acs_dfs_mode = hdd_ctx->acs_policy.acs_dfs_mode; |
| adapter->session.ap.sap_config.acs_dfs_mode = |
| wlan_hdd_get_dfs_mode(acs_dfs_mode); |
| } |
| |
| /* Allocate the Wireless Extensions state structure */ |
| phostapdBuf = WLAN_HDD_GET_HOSTAP_STATE_PTR(adapter); |
| |
| sme_set_curr_device_mode(hdd_ctx->mac_handle, adapter->device_mode); |
| /* Zero the memory. This zeros the profile structure. */ |
| memset(phostapdBuf, 0, sizeof(struct hdd_hostapd_state)); |
| |
| status = qdf_event_create(&phostapdBuf->qdf_event); |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| hdd_err("Hostapd HDD qdf event init failed!!"); |
| goto error_release_sap_session; |
| } |
| |
| status = qdf_event_create(&phostapdBuf->qdf_stop_bss_event); |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| hdd_err("Hostapd HDD stop bss event init failed!!"); |
| goto error_release_sap_session; |
| } |
| |
| status = qdf_event_create(&phostapdBuf->qdf_sta_disassoc_event); |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| hdd_err("Hostapd HDD sta disassoc event init failed!!"); |
| goto error_release_sap_session; |
| } |
| |
| /* Register as a wireless device */ |
| hdd_register_hostapd_wext(adapter->dev); |
| |
| /* Initialize the data path module */ |
| status = hdd_softap_init_tx_rx(adapter); |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| hdd_err("hdd_softap_init_tx_rx failed"); |
| goto error_release_sap_session; |
| } |
| |
| status = hdd_wmm_adapter_init(adapter); |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| hdd_err("hdd_wmm_adapter_init() failed code: %08d [x%08x]", |
| status, status); |
| goto error_release_wmm; |
| } |
| |
| set_bit(WMM_INIT_DONE, &adapter->event_flags); |
| |
| ret = wma_cli_set_command(adapter->vdev_id, |
| WMI_PDEV_PARAM_BURST_ENABLE, |
| HDD_ENABLE_SIFS_BURST_DEFAULT, |
| PDEV_CMD); |
| |
| if (0 != ret) |
| hdd_err("WMI_PDEV_PARAM_BURST_ENABLE set failed: %d", ret); |
| |
| if (cdp_cfg_get(cds_get_context(QDF_MODULE_ID_SOC), |
| cfg_dp_enable_ip_tcp_udp_checksum_offload)) |
| adapter->dev->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; |
| adapter->dev->features |= NETIF_F_RXCSUM; |
| |
| hdd_set_tso_flags(hdd_ctx, adapter->dev); |
| |
| if (!reinit) { |
| adapter->session.ap.sap_config.acs_cfg.acs_mode = false; |
| wlan_hdd_undo_acs(adapter); |
| qdf_mem_zero(&adapter->session.ap.sap_config.acs_cfg, |
| sizeof(struct sap_acs_cfg)); |
| } |
| |
| /* rcpi info initialization */ |
| qdf_mem_zero(&adapter->rcpi, sizeof(adapter->rcpi)); |
| |
| hdd_exit(); |
| |
| return status; |
| |
| error_release_wmm: |
| hdd_softap_deinit_tx_rx(adapter); |
| error_release_sap_session: |
| hdd_unregister_wext(adapter->dev); |
| hdd_hostapd_deinit_sap_session(adapter); |
| error_release_vdev: |
| QDF_BUG(!hdd_vdev_destroy(adapter)); |
| |
| hdd_exit(); |
| return status; |
| } |
| |
| void hdd_deinit_ap_mode(struct hdd_context *hdd_ctx, |
| struct hdd_adapter *adapter, |
| bool rtnl_held) |
| { |
| hdd_enter_dev(adapter->dev); |
| |
| if (test_bit(WMM_INIT_DONE, &adapter->event_flags)) { |
| hdd_wmm_adapter_close(adapter); |
| clear_bit(WMM_INIT_DONE, &adapter->event_flags); |
| } |
| qdf_atomic_set(&adapter->session.ap.acs_in_progress, 0); |
| wlan_hdd_undo_acs(adapter); |
| hdd_softap_deinit_tx_rx(adapter); |
| /* |
| * if we are being called during driver unload, |
| * then the dev has already been invalidated. |
| * if we are being called at other times, then we can |
| * detach the wireless device handlers |
| */ |
| if (adapter->dev) { |
| if (rtnl_held) { |
| adapter->dev->wireless_handlers = NULL; |
| } else { |
| rtnl_lock(); |
| adapter->dev->wireless_handlers = NULL; |
| rtnl_unlock(); |
| } |
| } |
| if (hdd_hostapd_deinit_sap_session(adapter)) |
| hdd_err("Failed:hdd_hostapd_deinit_sap_session"); |
| |
| hdd_exit(); |
| } |
| |
| /** |
| * hdd_wlan_create_ap_dev() - create an AP-mode device |
| * @hdd_ctx: Global HDD context |
| * @mac_addr: MAC address to assign to the interface |
| * @name_assign_type: the name of assign type of the netdev |
| * @iface_name: User-visible name of the interface |
| * |
| * This function will allocate a Linux net_device and configuration it |
| * for an AP mode of operation. Note that the device is NOT actually |
| * registered with the kernel at this time. |
| * |
| * Return: A pointer to the private data portion of the net_device if |
| * the allocation and initialization was successful, NULL otherwise. |
| */ |
| struct hdd_adapter *hdd_wlan_create_ap_dev(struct hdd_context *hdd_ctx, |
| tSirMacAddr mac_addr, |
| unsigned char name_assign_type, |
| uint8_t *iface_name) |
| { |
| struct net_device *dev; |
| struct hdd_adapter *adapter; |
| QDF_STATUS qdf_status; |
| |
| hdd_debug("iface_name = %s", iface_name); |
| |
| dev = alloc_netdev_mq(sizeof(struct hdd_adapter), iface_name, |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)) || defined(WITH_BACKPORTS) |
| name_assign_type, |
| #endif |
| ether_setup, NUM_TX_QUEUES); |
| |
| if (!dev) |
| return NULL; |
| |
| adapter = netdev_priv(dev); |
| |
| /* Init the net_device structure */ |
| ether_setup(dev); |
| |
| /* Initialize the adapter context to zeros. */ |
| qdf_mem_zero(adapter, sizeof(struct hdd_adapter)); |
| adapter->dev = dev; |
| adapter->hdd_ctx = hdd_ctx; |
| adapter->magic = WLAN_HDD_ADAPTER_MAGIC; |
| adapter->vdev_id = WLAN_UMAC_VDEV_ID_MAX; |
| |
| hdd_debug("dev = %pK, adapter = %pK, concurrency_mode=0x%x", |
| dev, adapter, |
| (int)policy_mgr_get_concurrency_mode(hdd_ctx->psoc)); |
| |
| /* Init the net_device structure */ |
| strlcpy(dev->name, (const char *)iface_name, IFNAMSIZ); |
| |
| hdd_set_ap_ops(dev); |
| |
| dev->watchdog_timeo = HDD_TX_TIMEOUT; |
| dev->mtu = HDD_DEFAULT_MTU; |
| dev->tx_queue_len = HDD_NETDEV_TX_QUEUE_LEN; |
| |
| qdf_mem_copy(dev->dev_addr, mac_addr, sizeof(tSirMacAddr)); |
| qdf_mem_copy(adapter->mac_addr.bytes, mac_addr, sizeof(tSirMacAddr)); |
| |
| adapter->offloads_configured = false; |
| hdd_dev_setup_destructor(dev); |
| dev->ieee80211_ptr = &adapter->wdev; |
| adapter->wdev.wiphy = hdd_ctx->wiphy; |
| adapter->wdev.netdev = dev; |
| |
| qdf_status = qdf_event_create( |
| &adapter->qdf_session_open_event); |
| if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { |
| hdd_err("failed to create session open QDF event!"); |
| free_netdev(adapter->dev); |
| return NULL; |
| } |
| |
| qdf_status = qdf_event_create( |
| &adapter->qdf_session_close_event); |
| if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { |
| hdd_err("failed to create session close QDF event!"); |
| free_netdev(adapter->dev); |
| return NULL; |
| } |
| |
| SET_NETDEV_DEV(dev, hdd_ctx->parent_dev); |
| spin_lock_init(&adapter->pause_map_lock); |
| adapter->start_time = adapter->last_time = qdf_system_ticks(); |
| |
| qdf_atomic_init(&adapter->ch_switch_in_progress); |
| |
| return adapter; |
| } |
| |
| /** |
| * wlan_hdd_rate_is_11g() - check if rate is 11g rate or not |
| * @rate: Rate to be checked |
| * |
| * Return: true if rate if 11g else false |
| */ |
| static bool wlan_hdd_rate_is_11g(u8 rate) |
| { |
| static const u8 gRateArray[8] = {12, 18, 24, 36, 48, 72, |
| 96, 108}; /* actual rate * 2 */ |
| u8 i; |
| |
| for (i = 0; i < 8; i++) { |
| if (rate == gRateArray[i]) |
| return true; |
| } |
| return false; |
| } |
| |
| #ifdef QCA_HT_2040_COEX |
| /** |
| * wlan_hdd_get_sap_obss() - Get SAP OBSS enable config based on HT_CAPAB IE |
| * @adapter: Pointer to hostapd adapter |
| * |
| * Return: HT support channel width config value |
| */ |
| static bool wlan_hdd_get_sap_obss(struct hdd_adapter *adapter) |
| { |
| uint32_t ret; |
| const uint8_t *ie; |
| uint8_t ht_cap_ie[DOT11F_IE_HTCAPS_MAX_LEN]; |
| tDot11fIEHTCaps dot11_ht_cap_ie = {0}; |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| struct hdd_beacon_data *beacon = adapter->session.ap.beacon; |
| mac_handle_t mac_handle; |
| |
| mac_handle = hdd_ctx->mac_handle; |
| ie = wlan_get_ie_ptr_from_eid(WLAN_EID_HT_CAPABILITY, |
| beacon->tail, beacon->tail_len); |
| if (ie && ie[1]) { |
| qdf_mem_copy(ht_cap_ie, &ie[2], DOT11F_IE_HTCAPS_MAX_LEN); |
| ret = dot11f_unpack_ie_ht_caps(MAC_CONTEXT(mac_handle), |
| ht_cap_ie, ie[1], |
| &dot11_ht_cap_ie, false); |
| if (DOT11F_FAILED(ret)) { |
| hdd_err("unpack failed, ret: 0x%x", ret); |
| return false; |
| } |
| return dot11_ht_cap_ie.supportedChannelWidthSet; |
| } |
| |
| return false; |
| } |
| #else |
| static bool wlan_hdd_get_sap_obss(struct hdd_adapter *adapter) |
| { |
| return false; |
| } |
| #endif |
| /** |
| * wlan_hdd_set_channel() - set channel in sap mode |
| * @wiphy: Pointer to wiphy structure |
| * @dev: Pointer to net_device structure |
| * @chandef: Pointer to channel definition structure |
| * @channel_type: Channel type |
| * |
| * Return: 0 for success non-zero for failure |
| */ |
| int wlan_hdd_set_channel(struct wiphy *wiphy, |
| struct net_device *dev, |
| struct cfg80211_chan_def *chandef, |
| enum nl80211_channel_type channel_type) |
| { |
| struct hdd_adapter *adapter = NULL; |
| uint32_t num_ch = 0; |
| int channel = 0; |
| int channel_seg2 = 0; |
| struct hdd_context *hdd_ctx; |
| int status; |
| mac_handle_t mac_handle; |
| struct sme_config_params *sme_config; |
| struct sap_config *sap_config; |
| |
| hdd_enter(); |
| |
| if (!dev) { |
| hdd_err("Called with dev = NULL"); |
| return -ENODEV; |
| } |
| adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| |
| qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, |
| TRACE_CODE_HDD_CFG80211_SET_CHANNEL, |
| adapter->vdev_id, channel_type); |
| |
| hdd_debug("Device_mode %s(%d) freq = %d", |
| qdf_opmode_str(adapter->device_mode), |
| adapter->device_mode, chandef->chan->center_freq); |
| |
| hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| status = wlan_hdd_validate_context(hdd_ctx); |
| if (status) |
| return status; |
| |
| mac_handle = hdd_ctx->mac_handle; |
| |
| /* |
| * Do freq to chan conversion |
| * TODO: for 11a |
| */ |
| |
| channel = ieee80211_frequency_to_channel(chandef->chan->center_freq); |
| |
| if (NL80211_CHAN_WIDTH_80P80 == chandef->width || |
| NL80211_CHAN_WIDTH_160 == chandef->width) { |
| if (chandef->center_freq2) |
| channel_seg2 = ieee80211_frequency_to_channel( |
| chandef->center_freq2); |
| else |
| hdd_err("Invalid center_freq2"); |
| } |
| |
| /* Check freq range */ |
| if ((WNI_CFG_CURRENT_CHANNEL_STAMIN > channel) || |
| (WNI_CFG_CURRENT_CHANNEL_STAMAX < channel)) { |
| hdd_err("Channel: %d is outside valid range from %d to %d", |
| channel, WNI_CFG_CURRENT_CHANNEL_STAMIN, |
| WNI_CFG_CURRENT_CHANNEL_STAMAX); |
| return -EINVAL; |
| } |
| |
| /* Check freq range */ |
| |
| if ((WNI_CFG_CURRENT_CHANNEL_STAMIN > channel_seg2) || |
| (WNI_CFG_CURRENT_CHANNEL_STAMAX < channel_seg2)) { |
| hdd_err("Channel: %d is outside valid range from %d to %d", |
| channel_seg2, WNI_CFG_CURRENT_CHANNEL_STAMIN, |
| WNI_CFG_CURRENT_CHANNEL_STAMAX); |
| return -EINVAL; |
| } |
| |
| num_ch = CFG_VALID_CHANNEL_LIST_LEN; |
| |
| if ((QDF_SAP_MODE != adapter->device_mode) && |
| (QDF_P2P_GO_MODE != adapter->device_mode)) { |
| if (QDF_STATUS_SUCCESS != |
| wlan_hdd_validate_operation_channel(adapter, channel)) { |
| hdd_err("Invalid Channel: %d", channel); |
| return -EINVAL; |
| } |
| hdd_debug("set channel to [%d] for device mode %s(%d)", channel, |
| qdf_opmode_str(adapter->device_mode), |
| adapter->device_mode); |
| } |
| |
| if ((adapter->device_mode == QDF_STA_MODE) || |
| (adapter->device_mode == QDF_P2P_CLIENT_MODE)) { |
| struct csr_roam_profile *roam_profile; |
| struct hdd_station_ctx *sta_ctx = |
| WLAN_HDD_GET_STATION_CTX_PTR(adapter); |
| |
| if (eConnectionState_IbssConnected == |
| sta_ctx->conn_info.conn_state) { |
| /* Link is up then return cant set channel */ |
| hdd_err("IBSS Associated, can't set the channel"); |
| return -EINVAL; |
| } |
| |
| roam_profile = hdd_roam_profile(adapter); |
| num_ch = roam_profile->ChannelInfo.numOfChannels = 1; |
| sta_ctx->conn_info.channel = channel; |
| roam_profile->ChannelInfo.ChannelList = |
| &sta_ctx->conn_info.channel; |
| } else if ((adapter->device_mode == QDF_SAP_MODE) |
| || (adapter->device_mode == QDF_P2P_GO_MODE) |
| ) { |
| sap_config = &((WLAN_HDD_GET_AP_CTX_PTR(adapter))->sap_config); |
| if (QDF_P2P_GO_MODE == adapter->device_mode) { |
| if (QDF_STATUS_SUCCESS != |
| wlan_hdd_validate_operation_channel(adapter, |
| channel)) { |
| hdd_err("Invalid Channel: %d", channel); |
| return -EINVAL; |
| } |
| sap_config->channel = channel; |
| sap_config->ch_params.center_freq_seg1 = channel_seg2; |
| } else { |
| /* set channel to what hostapd configured */ |
| if (QDF_STATUS_SUCCESS != |
| wlan_hdd_validate_operation_channel(adapter, |
| channel)) { |
| hdd_err("Invalid Channel: %d", channel); |
| return -EINVAL; |
| } |
| |
| sap_config->channel = channel; |
| sap_config->ch_params.center_freq_seg1 = channel_seg2; |
| |
| sme_config = qdf_mem_malloc(sizeof(*sme_config)); |
| |
| if (!sme_config) { |
| hdd_err("Unable to allocate memory for smeconfig!"); |
| return -ENOMEM; |
| } |
| sme_get_config_param(mac_handle, sme_config); |
| switch (channel_type) { |
| case NL80211_CHAN_HT20: |
| case NL80211_CHAN_NO_HT: |
| sme_config->csr_config.obssEnabled = false; |
| sap_config->sec_ch = 0; |
| break; |
| case NL80211_CHAN_HT40MINUS: |
| sap_config->sec_ch = sap_config->channel - 4; |
| break; |
| case NL80211_CHAN_HT40PLUS: |
| sap_config->sec_ch = sap_config->channel + 4; |
| break; |
| default: |
| hdd_err("Error!!! Invalid HT20/40 mode !"); |
| qdf_mem_free(sme_config); |
| return -EINVAL; |
| } |
| sme_config->csr_config.obssEnabled = |
| wlan_hdd_get_sap_obss(adapter); |
| |
| sme_update_config(mac_handle, sme_config); |
| qdf_mem_free(sme_config); |
| } |
| } else { |
| hdd_err("Invalid device mode failed to set valid channel"); |
| return -EINVAL; |
| } |
| hdd_exit(); |
| return status; |
| } |
| |
| /** |
| * wlan_hdd_check_11gmode() - check for 11g mode |
| * @ie: Pointer to IE |
| * @require_ht: Pointer to require ht |
| * @require_vht: Pointer to require vht |
| * @pCheckRatesfor11g: Pointer to check rates for 11g mode |
| * @pSapHw_mode: SAP HW mode |
| * |
| * Check for 11g rate and set proper 11g only mode |
| * |
| * Return: none |
| */ |
| static void wlan_hdd_check_11gmode(const u8 *ie, u8 *require_ht, |
| u8 *require_vht, u8 *pCheckRatesfor11g, |
| eCsrPhyMode *pSapHw_mode) |
| { |
| u8 i, num_rates = ie[0]; |
| |
| ie += 1; |
| for (i = 0; i < num_rates; i++) { |
| if (*pCheckRatesfor11g |
| && (true == wlan_hdd_rate_is_11g(ie[i] & RATE_MASK))) { |
| /* If rate set have 11g rate than change the mode |
| * to 11G |
| */ |
| *pSapHw_mode = eCSR_DOT11_MODE_11g; |
| if (ie[i] & BASIC_RATE_MASK) { |
| /* If we have 11g rate as basic rate, it |
| * means mode is 11g only mode. |
| */ |
| *pSapHw_mode = eCSR_DOT11_MODE_11g_ONLY; |
| *pCheckRatesfor11g = false; |
| } |
| } else { |
| if ((BASIC_RATE_MASK | |
| WLAN_BSS_MEMBERSHIP_SELECTOR_HT_PHY) == ie[i]) |
| *require_ht = true; |
| else if ((BASIC_RATE_MASK | |
| WLAN_BSS_MEMBERSHIP_SELECTOR_VHT_PHY) == ie[i]) |
| *require_vht = true; |
| } |
| } |
| } |
| |
| #ifdef WLAN_FEATURE_11AX |
| /** |
| * wlan_hdd_add_extn_ie() - add extension IE |
| * @adapter: Pointer to hostapd adapter |
| * @genie: Pointer to ie to be added |
| * @total_ielen: Pointer to store total ie length |
| * @oui: Pointer to oui |
| * @oui_size: Size of oui |
| * |
| * Return: 0 for success non-zero for failure |
| */ |
| static int wlan_hdd_add_extn_ie(struct hdd_adapter *adapter, uint8_t *genie, |
| uint16_t *total_ielen, uint8_t *oui, |
| uint8_t oui_size) |
| { |
| const uint8_t *ie; |
| uint16_t ielen = 0; |
| struct hdd_beacon_data *beacon = adapter->session.ap.beacon; |
| |
| ie = wlan_get_ext_ie_ptr_from_ext_id(oui, oui_size, |
| beacon->tail, |
| beacon->tail_len); |
| if (ie) { |
| ielen = ie[1] + 2; |
| if ((*total_ielen + ielen) <= MAX_GENIE_LEN) { |
| qdf_mem_copy(&genie[*total_ielen], ie, ielen); |
| } else { |
| hdd_err("**Ie Length is too big***"); |
| return -EINVAL; |
| } |
| *total_ielen += ielen; |
| } |
| return 0; |
| } |
| #endif |
| |
| /** |
| * wlan_hdd_add_hostapd_conf_vsie() - configure Vendor IE in sap mode |
| * @adapter: Pointer to hostapd adapter |
| * @genie: Pointer to Vendor IE |
| * @total_ielen: Pointer to store total ie length |
| * |
| * Return: none |
| */ |
| static void wlan_hdd_add_hostapd_conf_vsie(struct hdd_adapter *adapter, |
| uint8_t *genie, |
| uint16_t *total_ielen) |
| { |
| struct hdd_beacon_data *beacon = adapter->session.ap.beacon; |
| int left = beacon->tail_len; |
| uint8_t *ptr = beacon->tail; |
| uint8_t elem_id, elem_len; |
| uint16_t ielen = 0; |
| bool skip_ie; |
| |
| if (!ptr || 0 == left) |
| return; |
| |
| while (left >= 2) { |
| elem_id = ptr[0]; |
| elem_len = ptr[1]; |
| left -= 2; |
| if (elem_len > left) { |
| hdd_err("**Invalid IEs eid: %d elem_len: %d left: %d**", |
| elem_id, elem_len, left); |
| return; |
| } |
| if (IE_EID_VENDOR == elem_id) { |
| /* |
| * skipping the Vendor IE's which we don't want to |
| * include or it will be included by existing code. |
| */ |
| if (elem_len >= WPS_OUI_TYPE_SIZE && |
| (!qdf_mem_cmp(&ptr[2], WHITELIST_OUI_TYPE, |
| WPA_OUI_TYPE_SIZE) || |
| !qdf_mem_cmp(&ptr[2], BLACKLIST_OUI_TYPE, |
| WPA_OUI_TYPE_SIZE) || |
| !qdf_mem_cmp(&ptr[2], "\x00\x50\xf2\x02", |
| WPA_OUI_TYPE_SIZE) || |
| !qdf_mem_cmp(&ptr[2], WPA_OUI_TYPE, |
| WPA_OUI_TYPE_SIZE))) |
| skip_ie = true; |
| else |
| skip_ie = false; |
| |
| if (!skip_ie) { |
| ielen = ptr[1] + 2; |
| if ((*total_ielen + ielen) <= MAX_GENIE_LEN) { |
| qdf_mem_copy(&genie[*total_ielen], ptr, |
| ielen); |
| *total_ielen += ielen; |
| } else { |
| hdd_err("IE Length is too big IEs eid: %d elem_len: %d total_ie_lent: %d", |
| elem_id, elem_len, *total_ielen); |
| } |
| } |
| } |
| |
| left -= elem_len; |
| ptr += (elem_len + 2); |
| } |
| } |
| |
| /** |
| * wlan_hdd_add_extra_ie() - add extra ies in beacon |
| * @adapter: Pointer to hostapd adapter |
| * @genie: Pointer to extra ie |
| * @total_ielen: Pointer to store total ie length |
| * @temp_ie_id: ID of extra ie |
| * |
| * Return: none |
| */ |
| static void wlan_hdd_add_extra_ie(struct hdd_adapter *adapter, |
| uint8_t *genie, uint16_t *total_ielen, |
| uint8_t temp_ie_id) |
| { |
| struct hdd_beacon_data *beacon = adapter->session.ap.beacon; |
| int left = beacon->tail_len; |
| uint8_t *ptr = beacon->tail; |
| uint8_t elem_id, elem_len; |
| uint16_t ielen = 0; |
| |
| if (!ptr || 0 == left) |
| return; |
| |
| while (left >= 2) { |
| elem_id = ptr[0]; |
| elem_len = ptr[1]; |
| left -= 2; |
| if (elem_len > left) { |
| hdd_err("**Invalid IEs eid: %d elem_len: %d left: %d**", |
| elem_id, elem_len, left); |
| return; |
| } |
| |
| if (temp_ie_id == elem_id) { |
| ielen = ptr[1] + 2; |
| if ((*total_ielen + ielen) <= MAX_GENIE_LEN) { |
| qdf_mem_copy(&genie[*total_ielen], ptr, ielen); |
| *total_ielen += ielen; |
| } else { |
| hdd_err("IE Length is too big IEs eid: %d elem_len: %d total_ie_len: %d", |
| elem_id, elem_len, *total_ielen); |
| } |
| } |
| |
| left -= elem_len; |
| ptr += (elem_len + 2); |
| } |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_alloc_new_beacon() - alloc beacon in ap mode |
| * @adapter: Pointer to hostapd adapter |
| * @out_beacon: Location to store newly allocated beacon data |
| * @params: Pointer to beacon parameters |
| * @dtim_period: DTIM period |
| * |
| * Return: 0 for success non-zero for failure |
| */ |
| static int |
| wlan_hdd_cfg80211_alloc_new_beacon(struct hdd_adapter *adapter, |
| struct hdd_beacon_data **out_beacon, |
| struct cfg80211_beacon_data *params, |
| int dtim_period) |
| { |
| int size; |
| struct hdd_beacon_data *beacon = NULL; |
| struct hdd_beacon_data *old = NULL; |
| int head_len, tail_len, proberesp_ies_len, assocresp_ies_len; |
| const u8 *head, *tail, *proberesp_ies, *assocresp_ies; |
| |
| hdd_enter(); |
| if (params->head && !params->head_len) { |
| hdd_err("head_len is NULL"); |
| return -EINVAL; |
| } |
| |
| old = adapter->session.ap.beacon; |
| |
| if (!params->head && !old) { |
| hdd_err("session: %d old and new heads points to NULL", |
| adapter->vdev_id); |
| return -EINVAL; |
| } |
| |
| if (params->head) { |
| head_len = params->head_len; |
| head = params->head; |
| } else { |
| head_len = old->head_len; |
| head = old->head; |
| } |
| |
| if (params->tail || !old) { |
| tail_len = params->tail_len; |
| tail = params->tail; |
| } else { |
| tail_len = old->tail_len; |
| tail = old->tail; |
| } |
| |
| if (params->proberesp_ies || !old) { |
| proberesp_ies_len = params->proberesp_ies_len; |
| proberesp_ies = params->proberesp_ies; |
| } else { |
| proberesp_ies_len = old->proberesp_ies_len; |
| proberesp_ies = old->proberesp_ies; |
| } |
| |
| if (params->assocresp_ies || !old) { |
| assocresp_ies_len = params->assocresp_ies_len; |
| assocresp_ies = params->assocresp_ies; |
| } else { |
| assocresp_ies_len = old->assocresp_ies_len; |
| assocresp_ies = old->assocresp_ies; |
| } |
| |
| size = sizeof(struct hdd_beacon_data) + head_len + tail_len + |
| proberesp_ies_len + assocresp_ies_len; |
| |
| beacon = qdf_mem_malloc(size); |
| |
| if (!beacon) { |
| hdd_err("Mem allocation for beacon failed"); |
| return -ENOMEM; |
| } |
| if (dtim_period) |
| beacon->dtim_period = dtim_period; |
| else if (old) |
| beacon->dtim_period = old->dtim_period; |
| /* ----------------------------------------------- |
| * | head | tail | proberesp_ies | assocresp_ies | |
| * ----------------------------------------------- |
| */ |
| beacon->head = ((u8 *) beacon) + sizeof(struct hdd_beacon_data); |
| beacon->tail = beacon->head + head_len; |
| beacon->proberesp_ies = beacon->tail + tail_len; |
| beacon->assocresp_ies = beacon->proberesp_ies + proberesp_ies_len; |
| |
| beacon->head_len = head_len; |
| beacon->tail_len = tail_len; |
| beacon->proberesp_ies_len = proberesp_ies_len; |
| beacon->assocresp_ies_len = assocresp_ies_len; |
| |
| if (head && head_len) |
| memcpy(beacon->head, head, head_len); |
| if (tail && tail_len) |
| memcpy(beacon->tail, tail, tail_len); |
| if (proberesp_ies && proberesp_ies_len) |
| memcpy(beacon->proberesp_ies, proberesp_ies, proberesp_ies_len); |
| if (assocresp_ies && assocresp_ies_len) |
| memcpy(beacon->assocresp_ies, assocresp_ies, assocresp_ies_len); |
| |
| *out_beacon = beacon; |
| |
| adapter->session.ap.beacon = NULL; |
| qdf_mem_free(old); |
| |
| return 0; |
| |
| } |
| |
| #ifdef QCA_HT_2040_COEX |
| static void wlan_hdd_add_sap_obss_scan_ie( |
| struct hdd_adapter *hostapd_adapter, uint8_t *ie_buf, uint16_t *ie_len) |
| { |
| if (QDF_SAP_MODE == hostapd_adapter->device_mode) { |
| if (wlan_hdd_get_sap_obss(hostapd_adapter)) |
| wlan_hdd_add_extra_ie(hostapd_adapter, ie_buf, ie_len, |
| WLAN_EID_OVERLAP_BSS_SCAN_PARAM); |
| } |
| } |
| #else |
| static void wlan_hdd_add_sap_obss_scan_ie( |
| struct hdd_adapter *hostapd_adapter, uint8_t *ie_buf, uint16_t *ie_len) |
| { |
| } |
| #endif |
| |
| /** |
| * wlan_hdd_cfg80211_update_apies() - update ap mode 11ax ies |
| * @adapter: Pointer to hostapd adapter |
| * @genie: generic IE buffer |
| * @total_ielen: out param to update total ielen |
| * |
| * Return: 0 for success non-zero for failure |
| */ |
| |
| #ifdef WLAN_FEATURE_11AX |
| static int hdd_update_11ax_apies(struct hdd_adapter *adapter, |
| uint8_t *genie, uint16_t *total_ielen) |
| { |
| if (wlan_hdd_add_extn_ie(adapter, genie, total_ielen, |
| HE_CAP_OUI_TYPE, HE_CAP_OUI_SIZE)) { |
| hdd_err("Adding HE Cap ie failed"); |
| return -EINVAL; |
| } |
| |
| if (wlan_hdd_add_extn_ie(adapter, genie, total_ielen, |
| HE_OP_OUI_TYPE, HE_OP_OUI_SIZE)) { |
| hdd_err("Adding HE Op ie failed"); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| #else |
| static int hdd_update_11ax_apies(struct hdd_adapter *adapter, |
| uint8_t *genie, uint16_t *total_ielen) |
| { |
| return 0; |
| } |
| #endif |
| |
| /** |
| * wlan_hdd_cfg80211_update_apies() - update ap mode ies |
| * @adapter: Pointer to hostapd adapter |
| * |
| * Return: 0 for success non-zero for failure |
| */ |
| int wlan_hdd_cfg80211_update_apies(struct hdd_adapter *adapter) |
| { |
| uint8_t *genie; |
| uint16_t total_ielen = 0; |
| int ret = 0; |
| struct sap_config *config; |
| tSirUpdateIE update_ie; |
| struct hdd_beacon_data *beacon = NULL; |
| uint16_t proberesp_ies_len; |
| uint8_t *proberesp_ies = NULL; |
| mac_handle_t mac_handle; |
| |
| config = &adapter->session.ap.sap_config; |
| beacon = adapter->session.ap.beacon; |
| if (!beacon) { |
| hdd_err("Beacon is NULL !"); |
| return -EINVAL; |
| } |
| |
| genie = qdf_mem_malloc(MAX_GENIE_LEN); |
| |
| if (!genie) |
| return -ENOMEM; |
| |
| mac_handle = adapter->hdd_ctx->mac_handle; |
| |
| wlan_hdd_add_extra_ie(adapter, genie, &total_ielen, |
| WLAN_EID_VHT_TX_POWER_ENVELOPE); |
| |
| /* Extract and add the extended capabilities and interworking IE */ |
| wlan_hdd_add_extra_ie(adapter, genie, &total_ielen, |
| WLAN_EID_EXT_CAPABILITY); |
| |
| wlan_hdd_add_extra_ie(adapter, genie, &total_ielen, |
| WLAN_EID_INTERWORKING); |
| |
| #ifdef FEATURE_WLAN_WAPI |
| if (QDF_SAP_MODE == adapter->device_mode) { |
| wlan_hdd_add_extra_ie(adapter, genie, &total_ielen, |
| WLAN_ELEMID_WAPI); |
| } |
| #endif |
| |
| wlan_hdd_add_hostapd_conf_vsie(adapter, genie, |
| &total_ielen); |
| |
| ret = hdd_update_11ax_apies(adapter, genie, &total_ielen); |
| if (ret) |
| goto done; |
| |
| wlan_hdd_add_sap_obss_scan_ie(adapter, genie, &total_ielen); |
| |
| qdf_copy_macaddr(&update_ie.bssid, &adapter->mac_addr); |
| update_ie.smeSessionId = adapter->vdev_id; |
| |
| if (test_bit(SOFTAP_BSS_STARTED, &adapter->event_flags)) { |
| update_ie.ieBufferlength = total_ielen; |
| update_ie.pAdditionIEBuffer = genie; |
| update_ie.append = false; |
| update_ie.notify = true; |
| if (sme_update_add_ie(mac_handle, |
| &update_ie, |
| eUPDATE_IE_PROBE_BCN) == |
| QDF_STATUS_E_FAILURE) { |
| hdd_err("Could not pass on Add Ie probe beacon data"); |
| ret = -EINVAL; |
| goto done; |
| } |
| wlansap_reset_sap_config_add_ie(config, eUPDATE_IE_PROBE_BCN); |
| } else { |
| wlansap_update_sap_config_add_ie(config, |
| genie, |
| total_ielen, |
| eUPDATE_IE_PROBE_BCN); |
| } |
| |
| /* Added for Probe Response IE */ |
| proberesp_ies = qdf_mem_malloc(beacon->proberesp_ies_len + |
| MAX_GENIE_LEN); |
| if (!proberesp_ies) { |
| hdd_err("mem alloc failed for probe resp ies, size: %d", |
| beacon->proberesp_ies_len + MAX_GENIE_LEN); |
| ret = -EINVAL; |
| goto done; |
| } |
| qdf_mem_copy(proberesp_ies, beacon->proberesp_ies, |
| beacon->proberesp_ies_len); |
| proberesp_ies_len = beacon->proberesp_ies_len; |
| |
| wlan_hdd_add_sap_obss_scan_ie(adapter, proberesp_ies, |
| &proberesp_ies_len); |
| |
| if (test_bit(SOFTAP_BSS_STARTED, &adapter->event_flags)) { |
| update_ie.ieBufferlength = proberesp_ies_len; |
| update_ie.pAdditionIEBuffer = proberesp_ies; |
| update_ie.append = false; |
| update_ie.notify = false; |
| if (sme_update_add_ie(mac_handle, |
| &update_ie, |
| eUPDATE_IE_PROBE_RESP) == |
| QDF_STATUS_E_FAILURE) { |
| hdd_err("Could not pass on PROBE_RESP add Ie data"); |
| ret = -EINVAL; |
| goto done; |
| } |
| wlansap_reset_sap_config_add_ie(config, eUPDATE_IE_PROBE_RESP); |
| } else { |
| wlansap_update_sap_config_add_ie(config, |
| proberesp_ies, |
| proberesp_ies_len, |
| eUPDATE_IE_PROBE_RESP); |
| } |
| |
| /* Assoc resp Add ie Data */ |
| if (test_bit(SOFTAP_BSS_STARTED, &adapter->event_flags)) { |
| update_ie.ieBufferlength = beacon->assocresp_ies_len; |
| update_ie.pAdditionIEBuffer = (uint8_t *) beacon->assocresp_ies; |
| update_ie.append = false; |
| update_ie.notify = false; |
| if (sme_update_add_ie(mac_handle, |
| &update_ie, |
| eUPDATE_IE_ASSOC_RESP) == |
| QDF_STATUS_E_FAILURE) { |
| hdd_err("Could not pass on Add Ie Assoc Response data"); |
| ret = -EINVAL; |
| goto done; |
| } |
| wlansap_reset_sap_config_add_ie(config, eUPDATE_IE_ASSOC_RESP); |
| } else { |
| wlansap_update_sap_config_add_ie(config, |
| beacon->assocresp_ies, |
| beacon->assocresp_ies_len, |
| eUPDATE_IE_ASSOC_RESP); |
| } |
| |
| done: |
| qdf_mem_free(genie); |
| qdf_mem_free(proberesp_ies); |
| return ret; |
| } |
| |
| /** |
| * wlan_hdd_set_sap_hwmode() - set sap hw mode |
| * @adapter: Pointer to hostapd adapter |
| * |
| * Return: none |
| */ |
| static void wlan_hdd_set_sap_hwmode(struct hdd_adapter *adapter) |
| { |
| struct sap_config *config = &adapter->session.ap.sap_config; |
| struct hdd_beacon_data *beacon = adapter->session.ap.beacon; |
| struct ieee80211_mgmt *mgmt_frame = |
| (struct ieee80211_mgmt *)beacon->head; |
| u8 checkRatesfor11g = true; |
| u8 require_ht = false, require_vht = false; |
| const u8 *ie; |
| |
| config->SapHw_mode = eCSR_DOT11_MODE_11b; |
| |
| ie = wlan_get_ie_ptr_from_eid(WLAN_EID_SUPP_RATES, |
| &mgmt_frame->u.beacon.variable[0], |
| beacon->head_len); |
| if (ie) { |
| ie += 1; |
| wlan_hdd_check_11gmode(ie, &require_ht, &require_vht, |
| &checkRatesfor11g, &config->SapHw_mode); |
| } |
| |
| ie = wlan_get_ie_ptr_from_eid(WLAN_EID_EXT_SUPP_RATES, |
| beacon->tail, beacon->tail_len); |
| if (ie) { |
| ie += 1; |
| wlan_hdd_check_11gmode(ie, &require_ht, &require_vht, |
| &checkRatesfor11g, &config->SapHw_mode); |
| } |
| |
| if (config->channel > 14) |
| config->SapHw_mode = eCSR_DOT11_MODE_11a; |
| |
| ie = wlan_get_ie_ptr_from_eid(WLAN_EID_HT_CAPABILITY, |
| beacon->tail, beacon->tail_len); |
| if (ie) { |
| config->SapHw_mode = eCSR_DOT11_MODE_11n; |
| if (require_ht) |
| config->SapHw_mode = eCSR_DOT11_MODE_11n_ONLY; |
| } |
| |
| ie = wlan_get_ie_ptr_from_eid(WLAN_EID_VHT_CAPABILITY, |
| beacon->tail, beacon->tail_len); |
| if (ie) { |
| config->SapHw_mode = eCSR_DOT11_MODE_11ac; |
| if (require_vht) |
| config->SapHw_mode = eCSR_DOT11_MODE_11ac_ONLY; |
| } |
| |
| wlan_hdd_check_11ax_support(beacon, config); |
| |
| hdd_info("SAP hw_mode: %d", config->SapHw_mode); |
| } |
| |
| /** |
| * wlan_hdd_config_acs() - config ACS needed parameters |
| * @hdd_ctx: HDD context |
| * @adapter: Adapter pointer |
| * |
| * This function get ACS related INI parameters and populated |
| * sap config and smeConfig for ACS needed configurations. |
| * |
| * Return: The QDF_STATUS code associated with performing the operation. |
| */ |
| QDF_STATUS wlan_hdd_config_acs(struct hdd_context *hdd_ctx, |
| struct hdd_adapter *adapter) |
| { |
| struct sap_config *sap_config; |
| struct hdd_config *ini_config; |
| mac_handle_t mac_handle; |
| uint8_t is_overlap_enable = 0; |
| QDF_STATUS status; |
| |
| mac_handle = hdd_ctx->mac_handle; |
| sap_config = &adapter->session.ap.sap_config; |
| ini_config = hdd_ctx->config; |
| |
| status = ucfg_policy_mgr_get_enable_overlap_chnl(hdd_ctx->psoc, |
| &is_overlap_enable); |
| if (status != QDF_STATUS_SUCCESS) |
| hdd_err("can't get overlap channel INI value, using default"); |
| sap_config->enOverLapCh = !!is_overlap_enable; |
| |
| #ifdef FEATURE_WLAN_AP_AP_ACS_OPTIMIZE |
| hdd_debug("HDD_ACS_SKIP_STATUS = %d", hdd_ctx->skip_acs_scan_status); |
| if (hdd_ctx->skip_acs_scan_status == eSAP_SKIP_ACS_SCAN) { |
| struct hdd_adapter *con_sap_adapter; |
| struct sap_config *con_sap_config = NULL; |
| |
| con_sap_adapter = hdd_get_con_sap_adapter(adapter, false); |
| |
| if (con_sap_adapter) |
| con_sap_config = |
| &con_sap_adapter->session.ap.sap_config; |
| |
| sap_config->acs_cfg.skip_scan_status = eSAP_DO_NEW_ACS_SCAN; |
| |
| if (con_sap_config && |
| con_sap_config->acs_cfg.acs_mode == true && |
| hdd_ctx->skip_acs_scan_status == eSAP_SKIP_ACS_SCAN && |
| con_sap_config->acs_cfg.hw_mode == |
| sap_config->acs_cfg.hw_mode) { |
| uint8_t con_sap_st_ch, con_sap_end_ch; |
| uint8_t cur_sap_st_ch, cur_sap_end_ch; |
| uint8_t bandStartChannel, bandEndChannel; |
| |
| con_sap_st_ch = |
| con_sap_config->acs_cfg.start_ch; |
| con_sap_end_ch = |
| con_sap_config->acs_cfg.end_ch; |
| cur_sap_st_ch = sap_config->acs_cfg.start_ch; |
| cur_sap_end_ch = sap_config->acs_cfg.end_ch; |
| |
| wlansap_extend_to_acs_range(mac_handle, &cur_sap_st_ch, |
| &cur_sap_end_ch, &bandStartChannel, |
| &bandEndChannel); |
| |
| wlansap_extend_to_acs_range(mac_handle, |
| &con_sap_st_ch, &con_sap_end_ch, |
| &bandStartChannel, &bandEndChannel); |
| |
| if (con_sap_st_ch <= cur_sap_st_ch && |
| con_sap_end_ch >= cur_sap_end_ch) { |
| sap_config->acs_cfg.skip_scan_status = |
| eSAP_SKIP_ACS_SCAN; |
| |
| } else if (con_sap_st_ch >= cur_sap_st_ch && |
| con_sap_end_ch >= cur_sap_end_ch) { |
| sap_config->acs_cfg.skip_scan_status = |
| eSAP_DO_PAR_ACS_SCAN; |
| |
| sap_config->acs_cfg.skip_scan_range1_stch = |
| cur_sap_st_ch; |
| sap_config->acs_cfg.skip_scan_range1_endch = |
| con_sap_st_ch - 1; |
| sap_config->acs_cfg.skip_scan_range2_stch = |
| 0; |
| sap_config->acs_cfg.skip_scan_range2_endch = |
| 0; |
| |
| } else if (con_sap_st_ch <= cur_sap_st_ch && |
| con_sap_end_ch <= cur_sap_end_ch) { |
| sap_config->acs_cfg.skip_scan_status = |
| eSAP_DO_PAR_ACS_SCAN; |
| |
| sap_config->acs_cfg.skip_scan_range1_stch = |
| con_sap_end_ch + 1; |
| sap_config->acs_cfg.skip_scan_range1_endch = |
| cur_sap_end_ch; |
| sap_config->acs_cfg.skip_scan_range2_stch = |
| 0; |
| sap_config->acs_cfg.skip_scan_range2_endch = |
| 0; |
| |
| } else if (con_sap_st_ch >= cur_sap_st_ch && |
| con_sap_end_ch <= cur_sap_end_ch) { |
| sap_config->acs_cfg.skip_scan_status = |
| eSAP_DO_PAR_ACS_SCAN; |
| |
| sap_config->acs_cfg.skip_scan_range1_stch = |
| cur_sap_st_ch; |
| sap_config->acs_cfg.skip_scan_range1_endch = |
| con_sap_st_ch - 1; |
| sap_config->acs_cfg.skip_scan_range2_stch = |
| con_sap_end_ch; |
| sap_config->acs_cfg.skip_scan_range2_endch = |
| cur_sap_end_ch + 1; |
| |
| } else |
| sap_config->acs_cfg.skip_scan_status = |
| eSAP_DO_NEW_ACS_SCAN; |
| |
| |
| hdd_debug("SecAP ACS Skip=%d, ACS CH RANGE=%d-%d, %d-%d", |
| sap_config->acs_cfg.skip_scan_status, |
| sap_config->acs_cfg.skip_scan_range1_stch, |
| sap_config->acs_cfg.skip_scan_range1_endch, |
| sap_config->acs_cfg.skip_scan_range2_stch, |
| sap_config->acs_cfg.skip_scan_range2_endch); |
| } |
| } |
| #endif |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| /** |
| * wlan_hdd_sap_p2p_11ac_overrides: API to overwrite 11ac config in case of |
| * SAP or p2p go |
| * @ap_adapter: pointer to adapter |
| * |
| * This function overrides SAP / P2P Go configuration based on driver INI |
| * parameters for 11AC override and ACS. This overrides are done to support |
| * android legacy configuration method. |
| * |
| * NOTE: Non android platform supports concurrency and these overrides shall |
| * not be used. Also future driver based overrides shall be consolidated in this |
| * function only. Avoid random overrides in other location based on ini. |
| * |
| * Return: 0 for Success or Negative error codes. |
| */ |
| static int wlan_hdd_sap_p2p_11ac_overrides(struct hdd_adapter *ap_adapter) |
| { |
| struct sap_config *sap_cfg = &ap_adapter->session.ap.sap_config; |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(ap_adapter); |
| uint8_t ch_width; |
| uint8_t sub_20_chan_width; |
| QDF_STATUS status; |
| bool sap_force_11n_for_11ac = 0; |
| bool go_force_11n_for_11ac = 0; |
| uint32_t channel_bonding_mode; |
| bool go_11ac_override = 0; |
| bool sap_11ac_override = 0; |
| |
| ucfg_mlme_get_sap_force_11n_for_11ac(hdd_ctx->psoc, |
| &sap_force_11n_for_11ac); |
| ucfg_mlme_get_go_force_11n_for_11ac(hdd_ctx->psoc, |
| &go_force_11n_for_11ac); |
| |
| /* Fixed channel 11AC override: |
| * 11AC override in qcacld is introduced for following reasons: |
| * 1. P2P GO also follows start_bss and since p2p GO could not be |
| * configured to setup VHT channel width in wpa_supplicant |
| * 2. Android UI does not provide advanced configuration options for SAP |
| * |
| * Default override enabled (for android). MDM shall disable this in ini |
| */ |
| /* |
| * sub_20 MHz channel width is incompatible with 11AC rates, hence do |
| * not allow 11AC rates or more than 20 MHz channel width when |
| * enable_sub_20_channel_width is non zero |
| */ |
| status = ucfg_mlme_get_sub_20_chan_width(hdd_ctx->psoc, |
| &sub_20_chan_width); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| hdd_err("Failed to get sub_20_chan_width config"); |
| return -EIO; |
| } |
| |
| ucfg_mlme_is_go_11ac_override(hdd_ctx->psoc, |
| &go_11ac_override); |
| ucfg_mlme_is_sap_11ac_override(hdd_ctx->psoc, |
| &sap_11ac_override); |
| |
| if (!sub_20_chan_width && |
| (sap_cfg->SapHw_mode == eCSR_DOT11_MODE_11n || |
| sap_cfg->SapHw_mode == eCSR_DOT11_MODE_11ac || |
| sap_cfg->SapHw_mode == eCSR_DOT11_MODE_11ac_ONLY || |
| sap_cfg->SapHw_mode == eCSR_DOT11_MODE_11ax || |
| sap_cfg->SapHw_mode == eCSR_DOT11_MODE_11ax_ONLY) && |
| ((ap_adapter->device_mode == QDF_SAP_MODE && |
| !sap_force_11n_for_11ac && |
| sap_11ac_override) || |
| (ap_adapter->device_mode == QDF_P2P_GO_MODE && |
| !go_force_11n_for_11ac && |
| go_11ac_override))) { |
| hdd_debug("** Driver force 11AC override for SAP/Go **"); |
| |
| /* 11n only shall not be overridden since it may be on purpose*/ |
| if (sap_cfg->SapHw_mode == eCSR_DOT11_MODE_11n) |
| sap_cfg->SapHw_mode = eCSR_DOT11_MODE_11ac; |
| |
| if (sap_cfg->channel >= 36) { |
| status = |
| ucfg_mlme_get_vht_channel_width(hdd_ctx->psoc, |
| &ch_width); |
| if (!QDF_IS_STATUS_SUCCESS(status)) |
| hdd_err("Failed to set channel_width"); |
| sap_cfg->ch_width_orig = ch_width; |
| } else { |
| /* |
| * Allow 40 Mhz in 2.4 Ghz only if indicated by |
| * supplicant after OBSS scan and if 2.4 Ghz channel |
| * bonding is set in INI |
| */ |
| ucfg_mlme_get_channel_bonding_24ghz( |
| hdd_ctx->psoc, &channel_bonding_mode); |
| if (sap_cfg->ch_width_orig >= eHT_CHANNEL_WIDTH_40MHZ && |
| channel_bonding_mode) |
| sap_cfg->ch_width_orig = |
| eHT_CHANNEL_WIDTH_40MHZ; |
| else |
| sap_cfg->ch_width_orig = |
| eHT_CHANNEL_WIDTH_20MHZ; |
| } |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * wlan_hdd_setup_driver_overrides : Overrides SAP / P2P GO Params |
| * @adapter: pointer to adapter struct |
| * |
| * This function overrides SAP / P2P Go configuration based on driver INI |
| * parameters for 11AC override and ACS. These overrides are done to support |
| * android legacy configuration method. |
| * |
| * NOTE: Non android platform supports concurrency and these overrides shall |
| * not be used. Also future driver based overrides shall be consolidated in this |
| * function only. Avoid random overrides in other location based on ini. |
| * |
| * Return: 0 for Success or Negative error codes. |
| */ |
| static int wlan_hdd_setup_driver_overrides(struct hdd_adapter *ap_adapter) |
| { |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(ap_adapter); |
| QDF_STATUS qdf_status; |
| bool is_vendor_acs_support = |
| cfg_default(CFG_USER_AUTO_CHANNEL_SELECTION); |
| |
| qdf_status = ucfg_mlme_get_vendor_acs_support(hdd_ctx->psoc, |
| &is_vendor_acs_support); |
| if (!QDF_IS_STATUS_SUCCESS(qdf_status)) |
| hdd_err("get_vendor_acs_support failed, set default"); |
| |
| if (!is_vendor_acs_support) |
| return wlan_hdd_sap_p2p_11ac_overrides(ap_adapter); |
| else |
| return 0; |
| } |
| |
| void hdd_check_and_disconnect_sta_on_invalid_channel( |
| struct hdd_context *hdd_ctx) |
| { |
| struct hdd_adapter *sta_adapter; |
| uint8_t sta_chan; |
| |
| sta_chan = hdd_get_operating_channel(hdd_ctx, QDF_STA_MODE); |
| |
| if (!sta_chan) { |
| hdd_err("STA not connected"); |
| return; |
| } |
| |
| hdd_err("STA connected on chan %d", sta_chan); |
| |
| if (sme_is_channel_valid(hdd_ctx->mac_handle, sta_chan)) { |
| hdd_err("STA connected on chan %d and it is valid", sta_chan); |
| return; |
| } |
| |
| sta_adapter = hdd_get_adapter(hdd_ctx, QDF_STA_MODE); |
| |
| if (!sta_adapter) { |
| hdd_err("STA adapter does not exist"); |
| return; |
| } |
| |
| hdd_err("chan %d not valid, issue disconnect", sta_chan); |
| /* Issue Disconnect request */ |
| wlan_hdd_disconnect(sta_adapter, eCSR_DISCONNECT_REASON_DEAUTH); |
| } |
| |
| #ifdef DISABLE_CHANNEL_LIST |
| /** |
| * wlan_hdd_get_wiphy_channel() - Get wiphy channel |
| * @wiphy: Pointer to wiphy structure |
| * @freq: Frequency of the channel for which the wiphy hw value is required |
| * |
| * Return: wiphy channel for valid frequency else return NULL |
| */ |
| static struct ieee80211_channel *wlan_hdd_get_wiphy_channel( |
| struct wiphy *wiphy, |
| uint32_t freq) |
| { |
| uint32_t band_num, channel_num; |
| struct ieee80211_channel *wiphy_channel = NULL; |
| |
| for (band_num = 0; band_num < HDD_NUM_NL80211_BANDS; band_num++) { |
| for (channel_num = 0; channel_num < |
| wiphy->bands[band_num]->n_channels; |
| channel_num++) { |
| wiphy_channel = &(wiphy->bands[band_num]-> |
| channels[channel_num]); |
| if (wiphy_channel->center_freq == freq) |
| return wiphy_channel; |
| } |
| } |
| return wiphy_channel; |
| } |
| |
| int wlan_hdd_restore_channels(struct hdd_context *hdd_ctx, |
| bool notify_sap_event) |
| { |
| struct hdd_cache_channels *cache_chann; |
| struct wiphy *wiphy; |
| int freq, status, rf_channel; |
| int i; |
| struct ieee80211_channel *wiphy_channel = NULL; |
| |
| hdd_enter(); |
| |
| if (!hdd_ctx) { |
| hdd_err("HDD Context is NULL"); |
| return -EINVAL; |
| } |
| |
| wiphy = hdd_ctx->wiphy; |
| if (!wiphy) { |
| hdd_err("Wiphy is NULL"); |
| return -EINVAL; |
| } |
| |
| qdf_mutex_acquire(&hdd_ctx->cache_channel_lock); |
| |
| cache_chann = hdd_ctx->original_channels; |
| |
| if (!cache_chann || !cache_chann->num_channels) { |
| qdf_mutex_release(&hdd_ctx->cache_channel_lock); |
| hdd_err("channel list is NULL or num channels are zero"); |
| return -EINVAL; |
| } |
| |
| for (i = 0; i < cache_chann->num_channels; i++) { |
| freq = wlan_reg_chan_to_freq( |
| hdd_ctx->pdev, |
| cache_chann->channel_info[i].channel_num); |
| if (!freq) |
| continue; |
| |
| wiphy_channel = wlan_hdd_get_wiphy_channel(wiphy, freq); |
| if (!wiphy_channel) |
| continue; |
| rf_channel = wiphy_channel->hw_value; |
| /* |
| * Restore the orginal states of the channels |
| * only if we have cached non zero values |
| */ |
| wiphy_channel->flags = |
| cache_chann->channel_info[i].wiphy_status; |
| |
| hdd_debug("Restore channel %d reg_stat %d wiphy_stat 0x%x", |
| cache_chann->channel_info[i].channel_num, |
| cache_chann->channel_info[i].reg_status, |
| wiphy_channel->flags); |
| } |
| |
| qdf_mutex_release(&hdd_ctx->cache_channel_lock); |
| if (notify_sap_event) |
| ucfg_reg_notify_sap_event(hdd_ctx->pdev, false); |
| else |
| ucfg_reg_restore_cached_channels(hdd_ctx->pdev); |
| status = sme_update_channel_list(hdd_ctx->mac_handle); |
| if (status) |
| hdd_err("Can't Restore channel list"); |
| hdd_exit(); |
| |
| return 0; |
| } |
| |
| int wlan_hdd_disable_channels(struct hdd_context *hdd_ctx) |
| { |
| struct hdd_cache_channels *cache_chann; |
| struct wiphy *wiphy; |
| int freq, status, rf_channel; |
| int i; |
| struct ieee80211_channel *wiphy_channel = NULL; |
| |
| hdd_enter(); |
| |
| if (!hdd_ctx) { |
| hdd_err("HDD Context is NULL"); |
| return -EINVAL; |
| } |
| |
| wiphy = hdd_ctx->wiphy; |
| if (!wiphy) { |
| hdd_err("Wiphy is NULL"); |
| return -EINVAL; |
| } |
| |
| qdf_mutex_acquire(&hdd_ctx->cache_channel_lock); |
| cache_chann = hdd_ctx->original_channels; |
| |
| if (!cache_chann || !cache_chann->num_channels) { |
| qdf_mutex_release(&hdd_ctx->cache_channel_lock); |
| hdd_err("channel list is NULL or num channels are zero"); |
| return -EINVAL; |
| } |
| |
| for (i = 0; i < cache_chann->num_channels; i++) { |
| freq = wlan_reg_chan_to_freq(hdd_ctx->pdev, |
| cache_chann-> |
| channel_info[i].channel_num); |
| if (!freq) |
| continue; |
| wiphy_channel = wlan_hdd_get_wiphy_channel(wiphy, freq); |
| if (!wiphy_channel) |
| continue; |
| rf_channel = wiphy_channel->hw_value; |
| /* |
| * Cache the current states of |
| * the channels |
| */ |
| cache_chann->channel_info[i].reg_status = |
| wlan_reg_get_channel_state( |
| hdd_ctx->pdev, |
| rf_channel); |
| cache_chann->channel_info[i].wiphy_status = |
| wiphy_channel->flags; |
| hdd_debug("Disable channel %d reg_stat %d wiphy_stat 0x%x", |
| cache_chann->channel_info[i].channel_num, |
| cache_chann->channel_info[i].reg_status, |
| wiphy_channel->flags); |
| |
| wiphy_channel->flags |= IEEE80211_CHAN_DISABLED; |
| } |
| |
| qdf_mutex_release(&hdd_ctx->cache_channel_lock); |
| status = ucfg_reg_notify_sap_event(hdd_ctx->pdev, true); |
| status = sme_update_channel_list(hdd_ctx->mac_handle); |
| |
| hdd_exit(); |
| return status; |
| } |
| #else |
| int wlan_hdd_disable_channels(struct hdd_context *hdd_ctx) |
| { |
| return 0; |
| } |
| |
| int wlan_hdd_restore_channels(struct hdd_context *hdd_ctx, |
| bool notify_sap_event) |
| { |
| return 0; |
| } |
| #endif |
| |
| #ifdef DHCP_SERVER_OFFLOAD |
| static void wlan_hdd_set_dhcp_server_offload(struct hdd_adapter *adapter) |
| { |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| struct dhcp_offload_info_params dhcp_srv_info; |
| uint8_t num_entries = 0; |
| uint8_t *srv_ip; |
| uint8_t num; |
| uint32_t temp; |
| uint32_t dhcp_max_num_clients; |
| mac_handle_t mac_handle; |
| QDF_STATUS status; |
| |
| if (!hdd_ctx->config->dhcp_server_ip.is_dhcp_server_ip_valid) |
| return; |
| |
| srv_ip = hdd_ctx->config->dhcp_server_ip.dhcp_server_ip; |
| dhcp_srv_info.vdev_id = adapter->vdev_id; |
| dhcp_srv_info.dhcp_offload_enabled = true; |
| |
| status = ucfg_fwol_get_dhcp_max_num_clients(hdd_ctx->psoc, |
| &dhcp_max_num_clients); |
| if (QDF_IS_STATUS_ERROR(status)) |
| return; |
| |
| dhcp_srv_info.dhcp_client_num = dhcp_max_num_clients; |
| |
| if ((srv_ip[0] >= 224) && (srv_ip[0] <= 239)) { |
| hdd_err("Invalid IP address (%d)! It could NOT be multicast IP address!", |
| srv_ip[0]); |
| return; |
| } |
| |
| if (srv_ip[IPADDR_NUM_ENTRIES - 1] >= 100) { |
| hdd_err("Invalid IP address (%d)! The last field must be less than 100!", |
| srv_ip[IPADDR_NUM_ENTRIES - 1]); |
| return; |
| } |
| |
| dhcp_srv_info.dhcp_srv_addr = 0; |
| for (num = 0; num < num_entries; num++) { |
| temp = srv_ip[num]; |
| dhcp_srv_info.dhcp_srv_addr |= (temp << (8 * num)); |
| } |
| |
| mac_handle = hdd_ctx->mac_handle; |
| status = sme_set_dhcp_srv_offload(mac_handle, &dhcp_srv_info); |
| if (QDF_IS_STATUS_SUCCESS(status)) |
| hdd_debug("enable DHCP Server offload successfully!"); |
| else |
| hdd_err("sme_set_dhcp_srv_offload fail!"); |
| } |
| |
| /** |
| * wlan_hdd_dhcp_offload_enable: Enable DHCP offload |
| * @hdd_ctx: HDD context handler |
| * @adapter: Adapter pointer |
| * |
| * Enables the DHCP Offload feature in firmware if it has been configured. |
| * |
| * Return: None |
| */ |
| static void wlan_hdd_dhcp_offload_enable(struct hdd_context *hdd_ctx, |
| struct hdd_adapter *adapter) |
| { |
| bool enable_dhcp_server_offload; |
| QDF_STATUS status; |
| |
| status = ucfg_fwol_get_enable_dhcp_server_offload( |
| hdd_ctx->psoc, |
| &enable_dhcp_server_offload); |
| if (QDF_IS_STATUS_ERROR(status)) |
| return; |
| |
| if (enable_dhcp_server_offload) |
| wlan_hdd_set_dhcp_server_offload(adapter); |
| } |
| #else |
| static void wlan_hdd_dhcp_offload_enable(struct hdd_context *hdd_ctx, |
| struct hdd_adapter *adapter) |
| { |
| } |
| #endif /* DHCP_SERVER_OFFLOAD */ |
| |
| #ifdef WLAN_CONV_CRYPTO_SUPPORTED |
| /** |
| * hdd_set_vdev_crypto_prarams_from_ie - Sets vdev crypto params from IE info |
| * @vdev: vdev pointer |
| * @ie_ptr: pointer to IE |
| * @ie_len: IE length |
| * |
| * Return: QDF_STATUS_SUCCESS or error code |
| */ |
| static QDF_STATUS |
| hdd_set_vdev_crypto_prarams_from_ie(struct wlan_objmgr_vdev *vdev, |
| uint8_t *ie_ptr, uint16_t ie_len) |
| { |
| return wlan_set_vdev_crypto_prarams_from_ie(vdev, ie_ptr, ie_len); |
| } |
| #else |
| static QDF_STATUS |
| hdd_set_vdev_crypto_prarams_from_ie(struct wlan_objmgr_vdev *vdev, |
| uint8_t *ie_ptr, uint16_t ie_len) |
| { |
| return QDF_STATUS_SUCCESS; |
| } |
| #endif |
| |
| #ifdef FEATURE_AP_MCC_CH_AVOIDANCE |
| static void wlan_hdd_set_sap_mcc_chnl_avoid(struct hdd_context *hdd_ctx) |
| { |
| uint8_t sap_mcc_avoid = 0; |
| QDF_STATUS status; |
| |
| status = ucfg_mlme_get_sap_mcc_chnl_avoid(hdd_ctx->psoc, |
| &sap_mcc_avoid); |
| if (!QDF_IS_STATUS_SUCCESS(status)) |
| hdd_err("can't get sap mcc chnl avoid, use def"); |
| wlan_sap_set_channel_avoidance(hdd_ctx->mac_handle, sap_mcc_avoid); |
| } |
| #else |
| static void wlan_hdd_set_sap_mcc_chnl_avoid(struct hdd_context *hdd_ctx) |
| { |
| } |
| #endif |
| |
| /** |
| * wlan_hdd_cfg80211_start_bss() - start bss |
| * @adapter: Pointer to hostapd adapter |
| * @params: Pointer to start bss beacon parameters |
| * @ssid: Pointer ssid |
| * @ssid_len: Length of ssid |
| * @hidden_ssid: Hidden SSID parameter |
| * @check_for_concurrency: Flag to indicate if check for concurrency is needed |
| * |
| * Return: 0 for success non-zero for failure |
| */ |
| int wlan_hdd_cfg80211_start_bss(struct hdd_adapter *adapter, |
| struct cfg80211_beacon_data *params, |
| const u8 *ssid, size_t ssid_len, |
| enum nl80211_hidden_ssid hidden_ssid, |
| bool check_for_concurrency) |
| { |
| struct sap_config *config; |
| struct hdd_beacon_data *beacon = NULL; |
| struct ieee80211_mgmt *mgmt_frame; |
| struct ieee80211_mgmt mgmt; |
| const uint8_t *ie = NULL; |
| eCsrEncryptionType rsn_encrypt_type; |
| eCsrEncryptionType mc_rsn_encrypt_type; |
| uint16_t capab_info; |
| int status = QDF_STATUS_SUCCESS, ret; |
| int qdf_status = QDF_STATUS_SUCCESS; |
| sap_event_cb sap_event_callback; |
| struct hdd_hostapd_state *hostapd_state; |
| mac_handle_t mac_handle; |
| int32_t i; |
| uint32_t ii; |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| uint8_t mcc_to_scc_switch = 0, conc_rule1 = 0; |
| struct sme_config_params *sme_config; |
| bool mfp_capable = false; |
| bool mfp_required = false; |
| uint16_t prev_rsn_length = 0; |
| enum dfs_mode mode; |
| bool ignore_cac = 0; |
| uint8_t is_overlap_enable = 0, scc_on_dfs_chan = 0; |
| uint8_t beacon_fixed_len, indoor_chnl_marking = 0; |
| bool sap_force_11n_for_11ac = 0; |
| bool go_force_11n_for_11ac = 0; |
| bool bval = false; |
| bool enable_dfs_scan = true; |
| |
| hdd_enter(); |
| |
| hdd_notify_teardown_tdls_links(adapter->vdev); |
| |
| ucfg_mlme_get_sap_force_11n_for_11ac(hdd_ctx->psoc, |
| &sap_force_11n_for_11ac); |
| ucfg_mlme_get_go_force_11n_for_11ac(hdd_ctx->psoc, |
| &go_force_11n_for_11ac); |
| |
| if (policy_mgr_is_hw_mode_change_in_progress(hdd_ctx->psoc)) { |
| status = policy_mgr_wait_for_connection_update( |
| hdd_ctx->psoc); |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| hdd_err("qdf wait for event failed!!"); |
| return -EINVAL; |
| } |
| } |
| |
| /* |
| * For STA+SAP concurrency support from GUI, first STA connection gets |
| * triggered and while it is in progress, SAP start also comes up. |
| * Once STA association is successful, STA connect event is sent to |
| * kernel which gets queued in kernel workqueue and supplicant won't |
| * process M1 received from AP and send M2 until this NL80211_CONNECT |
| * event is received. Workqueue is not scheduled as RTNL lock is already |
| * taken by hostapd thread which has issued start_bss command to driver. |
| * Driver cannot complete start_bss as the pending command at the head |
| * of the SME command pending list is hw_mode_update for STA session |
| * which cannot be processed as SME is in WAITforKey state for STA |
| * interface. The start_bss command for SAP interface is queued behind |
| * the hw_mode_update command and so it cannot be processed until |
| * hw_mode_update command is processed. This is causing a deadlock so |
| * disconnect the STA interface first if connection or key exchange is |
| * in progress and then start SAP interface. |
| */ |
| hdd_abort_ongoing_sta_connection(hdd_ctx); |
| |
| /* |
| * Reject start bss if reassoc in progress on any adapter. |
| * sme_is_any_session_in_middle_of_roaming is for LFR2 and |
| * hdd_is_roaming_in_progress is for LFR3 |
| */ |
| mac_handle = hdd_ctx->mac_handle; |
| if (sme_is_any_session_in_middle_of_roaming(mac_handle) || |
| hdd_is_roaming_in_progress(hdd_ctx)) { |
| hdd_info("Reassociation in progress"); |
| return -EINVAL; |
| } |
| |
| /* Disable Roaming on all adapters before starting bss */ |
| wlan_hdd_disable_roaming(adapter); |
| |
| sme_config = qdf_mem_malloc(sizeof(*sme_config)); |
| if (!sme_config) { |
| hdd_err("failed to allocate memory"); |
| ret = -ENOMEM; |
| goto free; |
| } |
| |
| hostapd_state = WLAN_HDD_GET_HOSTAP_STATE_PTR(adapter); |
| |
| clear_bit(ACS_PENDING, &adapter->event_flags); |
| clear_bit(ACS_IN_PROGRESS, &hdd_ctx->g_event_flags); |
| |
| config = &adapter->session.ap.sap_config; |
| if (!config->channel) { |
| hdd_err("Invalid channel"); |
| ret = -EINVAL; |
| goto free; |
| } |
| |
| if (QDF_STATUS_SUCCESS != |
| ucfg_policy_mgr_get_indoor_chnl_marking(hdd_ctx->psoc, |
| &indoor_chnl_marking)) |
| hdd_err("can't get indoor channel marking, using default"); |
| /* Mark the indoor channel (passive) to disable */ |
| if (indoor_chnl_marking && adapter->device_mode == QDF_SAP_MODE) { |
| hdd_update_indoor_channel(hdd_ctx, true); |
| if (QDF_IS_STATUS_ERROR( |
| sme_update_channel_list(mac_handle))) { |
| hdd_update_indoor_channel(hdd_ctx, false); |
| hdd_err("Can't start BSS: update channel list failed"); |
| ret = -EINVAL; |
| goto free; |
| } |
| |
| /* check if STA is on indoor channel*/ |
| if (policy_mgr_is_force_scc(hdd_ctx->psoc)) |
| hdd_check_and_disconnect_sta_on_invalid_channel( |
| hdd_ctx); |
| } |
| |
| beacon = adapter->session.ap.beacon; |
| |
| /* |
| * beacon_fixed_len is the fixed length of beacon |
| * frame which includes only mac header length and |
| * beacon manadatory fields like timestamp, |
| * beacon_int and capab_info. |
| * (From the reference of struct ieee80211_mgmt) |
| */ |
| beacon_fixed_len = sizeof(mgmt) - sizeof(mgmt.u) + |
| sizeof(mgmt.u.beacon); |
| if (beacon->head_len < beacon_fixed_len) { |
| hdd_err("Invalid beacon head len"); |
| ret = -EINVAL; |
| goto error; |
| } |
| |
| mgmt_frame = (struct ieee80211_mgmt *)beacon->head; |
| |
| config->beacon_int = mgmt_frame->u.beacon.beacon_int; |
| config->dfs_cac_offload = hdd_ctx->dfs_cac_offload; |
| |
| status = ucfg_policy_mgr_get_enable_overlap_chnl(hdd_ctx->psoc, |
| &is_overlap_enable); |
| if (!QDF_IS_STATUS_SUCCESS(status)) |
| hdd_err("can't get overlap channel INI value, using default"); |
| config->enOverLapCh = is_overlap_enable; |
| |
| config->dtim_period = beacon->dtim_period; |
| |
| hdd_debug("acs_mode %d", config->acs_cfg.acs_mode); |
| |
| if (config->acs_cfg.acs_mode == true) { |
| hdd_debug("acs_channel %d, acs_dfs_mode %d", |
| hdd_ctx->acs_policy.acs_channel, |
| hdd_ctx->acs_policy.acs_dfs_mode); |
| |
| if (hdd_ctx->acs_policy.acs_channel) |
| config->channel = hdd_ctx->acs_policy.acs_channel; |
| mode = hdd_ctx->acs_policy.acs_dfs_mode; |
| config->acs_dfs_mode = wlan_hdd_get_dfs_mode(mode); |
| } |
| |
| policy_mgr_update_user_config_sap_chan(hdd_ctx->psoc, |
| config->channel); |
| hdd_debug("config->channel %d, config->acs_dfs_mode %d", |
| config->channel, config->acs_dfs_mode); |
| |
| hdd_debug("****config->dtim_period=%d***", |
| config->dtim_period); |
| |
| if (adapter->device_mode == QDF_SAP_MODE) { |
| ie = wlan_get_ie_ptr_from_eid(WLAN_EID_COUNTRY, |
| beacon->tail, beacon->tail_len); |
| if (ie) { |
| if (ie[1] < IEEE80211_COUNTRY_IE_MIN_LEN) { |
| hdd_err("Invalid Country IE len: %d", ie[1]); |
| ret = -EINVAL; |
| goto error; |
| } |
| |
| if (!qdf_mem_cmp(hdd_ctx->reg.alpha2, &ie[2], |
| REG_ALPHA2_LEN)) |
| config->ieee80211d = 1; |
| else |
| config->ieee80211d = 0; |
| } else |
| config->ieee80211d = 0; |
| |
| config->countryCode[0] = hdd_ctx->reg.alpha2[0]; |
| config->countryCode[1] = hdd_ctx->reg.alpha2[1]; |
| |
| /* Overwrite second AP's channel with first only when: |
| * 1. If operating mode is single mac |
| * 2. or if 2nd AP is coming up on 5G band channel |
| */ |
| ret = 0; |
| if (!policy_mgr_is_hw_dbs_capable(hdd_ctx->psoc) || |
| WLAN_REG_IS_5GHZ_CH(config->channel)) { |
| ret = wlan_hdd_sap_cfg_dfs_override(adapter); |
| if (ret < 0) |
| goto error; |
| } |
| if (!ret && wlan_reg_is_dfs_ch(hdd_ctx->pdev, config->channel)) |
| hdd_ctx->dev_dfs_cac_status = DFS_CAC_NEVER_DONE; |
| |
| if (QDF_STATUS_SUCCESS != |
| wlan_hdd_validate_operation_channel(adapter, |
| config->channel)) { |
| hdd_err("Invalid Channel: %d", config->channel); |
| ret = -EINVAL; |
| goto error; |
| } |
| ucfg_scan_cfg_get_dfs_chan_scan_allowed(hdd_ctx->psoc, |
| &enable_dfs_scan); |
| |
| /* reject SAP if DFS channel scan is not allowed */ |
| if (!(enable_dfs_scan) && |
| (CHANNEL_STATE_DFS == |
| wlan_reg_get_channel_state(hdd_ctx->pdev, |
| config->channel))) { |
| hdd_err("No SAP start on DFS channel"); |
| ret = -EOPNOTSUPP; |
| goto error; |
| } |
| |
| status = ucfg_mlme_get_dfs_ignore_cac(hdd_ctx->psoc, |
| &ignore_cac); |
| if (!QDF_IS_STATUS_SUCCESS(status)) |
| hdd_err("can't get sta-sap scc on dfs chnl, use def"); |
| status = |
| ucfg_policy_mgr_get_sta_sap_scc_on_dfs_chnl(hdd_ctx->psoc, |
| &scc_on_dfs_chan); |
| |
| if (!QDF_IS_STATUS_SUCCESS(status)) |
| hdd_err("can't get sta-sap scc on dfs chnl, use def"); |
| |
| ucfg_policy_mgr_get_mcc_scc_switch(hdd_ctx->psoc, |
| &mcc_to_scc_switch); |
| if (ignore_cac || |
| ((mcc_to_scc_switch != QDF_MCC_TO_SCC_SWITCH_DISABLE) && |
| scc_on_dfs_chan)) |
| ignore_cac = 1; |
| |
| wlansap_set_dfs_ignore_cac(mac_handle, ignore_cac); |
| |
| wlansap_set_dfs_preferred_channel_location(mac_handle); |
| |
| wlan_hdd_set_sap_mcc_chnl_avoid(hdd_ctx); |
| } else if (adapter->device_mode == QDF_P2P_GO_MODE) { |
| config->countryCode[0] = hdd_ctx->reg.alpha2[0]; |
| config->countryCode[1] = hdd_ctx->reg.alpha2[1]; |
| config->ieee80211d = 0; |
| } else { |
| config->ieee80211d = 0; |
| } |
| |
| tgt_dfs_set_tx_leakage_threshold(hdd_ctx->pdev); |
| |
| capab_info = mgmt_frame->u.beacon.capab_info; |
| |
| config->privacy = (mgmt_frame->u.beacon.capab_info & |
| WLAN_CAPABILITY_PRIVACY) ? true : false; |
| |
| (WLAN_HDD_GET_AP_CTX_PTR(adapter))->privacy = config->privacy; |
| |
| /*Set wps station to configured */ |
| ie = wlan_hdd_get_wps_ie_ptr(beacon->tail, beacon->tail_len); |
| |
| if (ie) { |
| /* To access ie[15], length needs to be at least 14 */ |
| if (ie[1] < 14) { |
| hdd_err("**Wps Ie Length(%hhu) is too small***", |
| ie[1]); |
| ret = -EINVAL; |
| goto error; |
| } else if (memcmp(&ie[2], WPS_OUI_TYPE, WPS_OUI_TYPE_SIZE) == |
| 0) { |
| hdd_debug("** WPS IE(len %d) ***", (ie[1] + 2)); |
| /* Check 15 bit of WPS IE as it contain information for |
| * wps state |
| */ |
| if (SAP_WPS_ENABLED_UNCONFIGURED == ie[15]) { |
| config->wps_state = |
| SAP_WPS_ENABLED_UNCONFIGURED; |
| } else if (SAP_WPS_ENABLED_CONFIGURED == ie[15]) { |
| config->wps_state = SAP_WPS_ENABLED_CONFIGURED; |
| } |
| } |
| } else { |
| hdd_debug("WPS disabled"); |
| config->wps_state = SAP_WPS_DISABLED; |
| } |
| /* Forward WPS PBC probe request frame up */ |
| config->fwdWPSPBCProbeReq = 1; |
| |
| config->RSNEncryptType = eCSR_ENCRYPT_TYPE_NONE; |
| config->mcRSNEncryptType = eCSR_ENCRYPT_TYPE_NONE; |
| (WLAN_HDD_GET_AP_CTX_PTR(adapter))->encryption_type = |
| eCSR_ENCRYPT_TYPE_NONE; |
| |
| config->RSNWPAReqIELength = 0; |
| memset(&config->RSNWPAReqIE[0], 0, sizeof(config->RSNWPAReqIE)); |
| ie = wlan_get_ie_ptr_from_eid(WLAN_EID_RSN, beacon->tail, |
| beacon->tail_len); |
| if (ie && ie[1]) { |
| config->RSNWPAReqIELength = ie[1] + 2; |
| if (config->RSNWPAReqIELength < sizeof(config->RSNWPAReqIE)) |
| memcpy(&config->RSNWPAReqIE[0], ie, |
| config->RSNWPAReqIELength); |
| else |
| hdd_err("RSNWPA IE MAX Length exceeded; length =%d", |
| config->RSNWPAReqIELength); |
| /* The actual processing may eventually be more extensive than |
| * this. Right now, just consume any PMKIDs that are sent in |
| * by the app. |
| */ |
| status = |
| hdd_softap_unpack_ie(cds_get_context |
| (QDF_MODULE_ID_SME), |
| &rsn_encrypt_type, |
| &mc_rsn_encrypt_type, |
| &config->akm_list, |
| &mfp_capable, |
| &mfp_required, |
| config->RSNWPAReqIE[1] + 2, |
| config->RSNWPAReqIE); |
| |
| if (QDF_STATUS_SUCCESS == status) { |
| /* Now copy over all the security attributes you have |
| * parsed out. Use the cipher type in the RSN IE |
| */ |
| config->RSNEncryptType = rsn_encrypt_type; |
| config->mcRSNEncryptType = mc_rsn_encrypt_type; |
| (WLAN_HDD_GET_AP_CTX_PTR(adapter))-> |
| encryption_type = rsn_encrypt_type; |
| hdd_debug("CSR EncryptionType = %d mcEncryptionType = %d", |
| rsn_encrypt_type, mc_rsn_encrypt_type); |
| hdd_debug("CSR AKM Suites %d", |
| config->akm_list.numEntries); |
| for (ii = 0; ii < config->akm_list.numEntries; |
| ii++) |
| hdd_debug("CSR AKM Suite [%d] = %d", ii, |
| config->akm_list.authType[ii]); |
| } |
| } |
| |
| ie = wlan_get_vendor_ie_ptr_from_oui(WPA_OUI_TYPE, WPA_OUI_TYPE_SIZE, |
| beacon->tail, beacon->tail_len); |
| |
| if (ie && ie[1] && (ie[0] == DOT11F_EID_WPA)) { |
| if (config->RSNWPAReqIE[0]) { |
| /*Mixed mode WPA/WPA2 */ |
| prev_rsn_length = config->RSNWPAReqIELength; |
| config->RSNWPAReqIELength += ie[1] + 2; |
| if (config->RSNWPAReqIELength < |
| sizeof(config->RSNWPAReqIE)) |
| memcpy(&config->RSNWPAReqIE[0] + |
| prev_rsn_length, ie, ie[1] + 2); |
| else |
| hdd_err("RSNWPA IE MAX Length exceeded; length: %d", |
| config->RSNWPAReqIELength); |
| } else { |
| config->RSNWPAReqIELength = ie[1] + 2; |
| if (config->RSNWPAReqIELength < |
| sizeof(config->RSNWPAReqIE)) |
| memcpy(&config->RSNWPAReqIE[0], ie, |
| config->RSNWPAReqIELength); |
| else |
| hdd_err("RSNWPA IE MAX Length exceeded; length: %d", |
| config->RSNWPAReqIELength); |
| status = hdd_softap_unpack_ie |
| (cds_get_context(QDF_MODULE_ID_SME), |
| &rsn_encrypt_type, |
| &mc_rsn_encrypt_type, |
| &config->akm_list, |
| &mfp_capable, &mfp_required, |
| config->RSNWPAReqIE[1] + 2, |
| config->RSNWPAReqIE); |
| |
| if (QDF_STATUS_SUCCESS == status) { |
| /* Now copy over all the security attributes |
| * you have parsed out. Use the cipher type |
| * in the RSN IE |
| */ |
| config->RSNEncryptType = rsn_encrypt_type; |
| config->mcRSNEncryptType = mc_rsn_encrypt_type; |
| (WLAN_HDD_GET_AP_CTX_PTR(adapter))-> |
| encryption_type = rsn_encrypt_type; |
| hdd_debug("CSR EncryptionType = %d mcEncryptionType = %d", |
| rsn_encrypt_type, |
| mc_rsn_encrypt_type); |
| hdd_debug("CSR AKM Suites %d", |
| config->akm_list.numEntries); |
| for (ii = 0; ii < config->akm_list.numEntries; |
| ii++) |
| hdd_debug("CSR AKM Suite [%d] = %d", ii, |
| config->akm_list. |
| authType[ii]); |
| } |
| } |
| } |
| |
| if (config->RSNWPAReqIELength > sizeof(config->RSNWPAReqIE)) { |
| hdd_err("**RSNWPAReqIELength is too large***"); |
| ret = -EINVAL; |
| goto error; |
| } |
| status = hdd_set_vdev_crypto_prarams_from_ie(adapter->vdev, |
| config->RSNWPAReqIE, |
| config->RSNWPAReqIELength |
| ); |
| if (QDF_IS_STATUS_ERROR(status)) |
| hdd_err("Failed to set crypto params from IE"); |
| |
| config->SSIDinfo.ssidHidden = false; |
| |
| if (ssid) { |
| qdf_mem_copy(config->SSIDinfo.ssid.ssId, ssid, ssid_len); |
| config->SSIDinfo.ssid.length = ssid_len; |
| |
| switch (hidden_ssid) { |
| case NL80211_HIDDEN_SSID_NOT_IN_USE: |
| hdd_debug("HIDDEN_SSID_NOT_IN_USE"); |
| config->SSIDinfo.ssidHidden = eHIDDEN_SSID_NOT_IN_USE; |
| break; |
| case NL80211_HIDDEN_SSID_ZERO_LEN: |
| hdd_debug("HIDDEN_SSID_ZERO_LEN"); |
| config->SSIDinfo.ssidHidden = eHIDDEN_SSID_ZERO_LEN; |
| break; |
| case NL80211_HIDDEN_SSID_ZERO_CONTENTS: |
| hdd_debug("HIDDEN_SSID_ZERO_CONTENTS"); |
| config->SSIDinfo.ssidHidden = |
| eHIDDEN_SSID_ZERO_CONTENTS; |
| break; |
| default: |
| hdd_err("Wrong hidden_ssid param: %d", hidden_ssid); |
| break; |
| } |
| } |
| |
| qdf_mem_copy(config->self_macaddr.bytes, |
| adapter->mac_addr.bytes, |
| QDF_MAC_ADDR_SIZE); |
| |
| /* default value */ |
| config->SapMacaddr_acl = eSAP_ACCEPT_UNLESS_DENIED; |
| config->num_accept_mac = 0; |
| config->num_deny_mac = 0; |
| status = ucfg_policy_mgr_get_conc_rule1(hdd_ctx->psoc, &conc_rule1); |
| if (!QDF_IS_STATUS_SUCCESS(status)) |
| hdd_err("can't get ucfg_policy_mgr_get_conc_rule1, use def"); |
| #ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH |
| /* |
| * We don't want P2PGO to follow STA's channel |
| * so lets limit the logic for SAP only. |
| * Later if we decide to make p2pgo follow STA's |
| * channel then remove this check. |
| */ |
| if ((0 == conc_rule1) || |
| (conc_rule1 && (QDF_SAP_MODE == adapter->device_mode))) |
| config->cc_switch_mode = mcc_to_scc_switch; |
| #endif |
| |
| if (!(ssid && qdf_str_len(PRE_CAC_SSID) == ssid_len && |
| (0 == qdf_mem_cmp(ssid, PRE_CAC_SSID, ssid_len)))) { |
| uint16_t beacon_data_len; |
| |
| beacon_data_len = beacon->head_len - beacon_fixed_len; |
| |
| ie = wlan_get_ie_ptr_from_eid(WLAN_EID_SUPP_RATES, |
| &mgmt_frame->u.beacon.variable[0], |
| beacon_data_len); |
| |
| if (ie) { |
| ie++; |
| if (ie[0] > WLAN_SUPPORTED_RATES_IE_MAX_LEN) { |
| hdd_err("Invalid supported rates %d", |
| ie[0]); |
| ret = -EINVAL; |
| goto error; |
| } |
| config->supported_rates.numRates = ie[0]; |
| ie++; |
| for (i = 0; |
| i < config->supported_rates.numRates; i++) { |
| if (ie[i]) { |
| config->supported_rates.rate[i] = ie[i]; |
| hdd_debug("Configured Supported rate is %2x", |
| config->supported_rates.rate[i]); |
| } |
| } |
| } |
| ie = wlan_get_ie_ptr_from_eid(WLAN_EID_EXT_SUPP_RATES, |
| beacon->tail, |
| beacon->tail_len); |
| if (ie) { |
| ie++; |
| if (ie[0] > WLAN_SUPPORTED_RATES_IE_MAX_LEN) { |
| hdd_err("Invalid supported rates %d", |
| ie[0]); |
| ret = -EINVAL; |
| goto error; |
| } |
| config->extended_rates.numRates = ie[0]; |
| ie++; |
| for (i = 0; i < config->extended_rates.numRates; i++) { |
| if (ie[i]) { |
| config->extended_rates.rate[i] = ie[i]; |
| hdd_debug("Configured ext Supported rate is %2x", |
| config->extended_rates.rate[i]); |
| } |
| } |
| } |
| } |
| |
| if (!cds_is_sub_20_mhz_enabled()) |
| wlan_hdd_set_sap_hwmode(adapter); |
| |
| status = ucfg_mlme_get_vht_for_24ghz(hdd_ctx->psoc, &bval); |
| if (!QDF_IS_STATUS_SUCCESS(qdf_status)) |
| hdd_err("Failed to get vht_for_24ghz"); |
| |
| if (IS_24G_CH(config->channel) && bval && |
| (config->SapHw_mode == eCSR_DOT11_MODE_11n || |
| config->SapHw_mode == eCSR_DOT11_MODE_11n_ONLY)) |
| config->SapHw_mode = eCSR_DOT11_MODE_11ac; |
| |
| if (((adapter->device_mode == QDF_SAP_MODE) && |
| (sap_force_11n_for_11ac)) || |
| ((adapter->device_mode == QDF_P2P_GO_MODE) && |
| (go_force_11n_for_11ac))) { |
| if (config->SapHw_mode == eCSR_DOT11_MODE_11ac || |
| config->SapHw_mode == eCSR_DOT11_MODE_11ac_ONLY) |
| config->SapHw_mode = eCSR_DOT11_MODE_11n; |
| } |
| |
| qdf_mem_zero(sme_config, sizeof(*sme_config)); |
| sme_get_config_param(mac_handle, sme_config); |
| /* Override hostapd.conf wmm_enabled only for 11n and 11AC configs (IOT) |
| * As per spec 11N/AC STA are QOS STA and may not connect or throughput |
| * may not be good with non QOS 11N AP |
| * Default: enable QOS for SAP unless WMM IE not present for 11bga |
| */ |
| sme_config->csr_config.WMMSupportMode = eCsrRoamWmmAuto; |
| ie = wlan_get_vendor_ie_ptr_from_oui(WMM_OUI_TYPE, WMM_OUI_TYPE_SIZE, |
| beacon->tail, beacon->tail_len); |
| if (!ie && (config->SapHw_mode == eCSR_DOT11_MODE_11a || |
| config->SapHw_mode == eCSR_DOT11_MODE_11g || |
| config->SapHw_mode == eCSR_DOT11_MODE_11b)) |
| sme_config->csr_config.WMMSupportMode = eCsrRoamWmmNoQos; |
| sme_update_config(mac_handle, sme_config); |
| |
| if (!((adapter->device_mode == QDF_SAP_MODE) && |
| (sap_force_11n_for_11ac)) || |
| ((adapter->device_mode == QDF_P2P_GO_MODE) && |
| (go_force_11n_for_11ac))) { |
| config->ch_width_orig = |
| hdd_map_nl_chan_width(config->ch_width_orig); |
| } else { |
| if (config->ch_width_orig >= NL80211_CHAN_WIDTH_40) |
| config->ch_width_orig = CH_WIDTH_40MHZ; |
| else |
| config->ch_width_orig = CH_WIDTH_20MHZ; |
| } |
| |
| if (wlan_hdd_setup_driver_overrides(adapter)) { |
| ret = -EINVAL; |
| goto error; |
| } |
| |
| config->ch_params.ch_width = config->ch_width_orig; |
| wlan_reg_set_channel_params(hdd_ctx->pdev, config->channel, |
| config->sec_ch, &config->ch_params); |
| |
| if (0 != wlan_hdd_cfg80211_update_apies(adapter)) { |
| hdd_err("SAP Not able to set AP IEs"); |
| ret = -EINVAL; |
| goto error; |
| } |
| |
| #ifdef WLAN_FEATURE_11W |
| config->mfpCapable = mfp_capable; |
| config->mfpRequired = mfp_required; |
| hdd_debug("Soft AP MFP capable %d, MFP required %d", |
| config->mfpCapable, config->mfpRequired); |
| #endif |
| |
| hdd_debug("SOftAP macaddress : " MAC_ADDRESS_STR, |
| QDF_MAC_ADDR_ARRAY(adapter->mac_addr.bytes)); |
| hdd_debug("ssid =%s, beaconint=%d, channel=%d", |
| config->SSIDinfo.ssid.ssId, (int)config->beacon_int, |
| (int)config->channel); |
| hdd_debug("hw_mode=%x, privacy=%d, authType=%d", |
| config->SapHw_mode, config->privacy, config->authType); |
| hdd_debug("RSN/WPALen=%d", (int)config->RSNWPAReqIELength); |
| |
| mutex_lock(&hdd_ctx->sap_lock); |
| if (cds_is_driver_unloading()) { |
| mutex_unlock(&hdd_ctx->sap_lock); |
| |
| hdd_err("The driver is unloading, ignore the bss starting"); |
| ret = -EINVAL; |
| goto error; |
| } |
| |
| if (test_bit(SOFTAP_BSS_STARTED, &adapter->event_flags)) { |
| mutex_unlock(&hdd_ctx->sap_lock); |
| |
| wlansap_reset_sap_config_add_ie(config, eUPDATE_IE_ALL); |
| /* Bss already started. just return. */ |
| /* TODO Probably it should update some beacon params. */ |
| hdd_debug("Bss Already started...Ignore the request"); |
| hdd_exit(); |
| ret = 0; |
| goto free; |
| } |
| |
| if (check_for_concurrency) { |
| if (!policy_mgr_allow_concurrency(hdd_ctx->psoc, |
| policy_mgr_convert_device_mode_to_qdf_type( |
| adapter->device_mode), |
| config->channel, HW_MODE_20_MHZ)) { |
| mutex_unlock(&hdd_ctx->sap_lock); |
| |
| hdd_err("This concurrency combination is not allowed"); |
| ret = -EINVAL; |
| goto error; |
| } |
| } |
| |
| if (!hdd_set_connection_in_progress(true)) { |
| mutex_unlock(&hdd_ctx->sap_lock); |
| |
| hdd_err("Can't start BSS: set connection in progress failed"); |
| ret = -EINVAL; |
| goto error; |
| } |
| |
| config->persona = adapter->device_mode; |
| |
| sap_event_callback = hdd_hostapd_sap_event_cb; |
| |
| (WLAN_HDD_GET_AP_CTX_PTR(adapter))->dfs_cac_block_tx = true; |
| set_bit(SOFTAP_INIT_DONE, &adapter->event_flags); |
| |
| qdf_event_reset(&hostapd_state->qdf_event); |
| status = wlansap_start_bss( |
| WLAN_HDD_GET_SAP_CTX_PTR(adapter), |
| sap_event_callback, config, adapter->dev); |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| mutex_unlock(&hdd_ctx->sap_lock); |
| |
| hdd_set_connection_in_progress(false); |
| hdd_err("SAP Start Bss fail"); |
| ret = -EINVAL; |
| goto error; |
| } |
| |
| hdd_debug("Waiting for Scan to complete(auto mode) and BSS to start"); |
| |
| qdf_status = qdf_wait_for_event_completion(&hostapd_state->qdf_event, |
| SME_CMD_START_BSS_TIMEOUT); |
| |
| wlansap_reset_sap_config_add_ie(config, eUPDATE_IE_ALL); |
| |
| if (!QDF_IS_STATUS_SUCCESS(qdf_status) || |
| !QDF_IS_STATUS_SUCCESS(hostapd_state->qdf_status)) { |
| mutex_unlock(&hdd_ctx->sap_lock); |
| |
| hdd_err("qdf wait for single_event failed!!"); |
| hdd_set_connection_in_progress(false); |
| sme_get_command_q_status(mac_handle); |
| wlansap_stop_bss(WLAN_HDD_GET_SAP_CTX_PTR(adapter)); |
| QDF_ASSERT(0); |
| ret = -EINVAL; |
| goto error; |
| } |
| /* Successfully started Bss update the state bit. */ |
| set_bit(SOFTAP_BSS_STARTED, &adapter->event_flags); |
| |
| mutex_unlock(&hdd_ctx->sap_lock); |
| |
| /* Initialize WMM configuation */ |
| hdd_wmm_init(adapter); |
| if (hostapd_state->bss_state == BSS_START) { |
| policy_mgr_incr_active_session(hdd_ctx->psoc, |
| adapter->device_mode, |
| adapter->vdev_id); |
| hdd_green_ap_start_state_mc(hdd_ctx, adapter->device_mode, |
| true); |
| } |
| |
| wlan_hdd_dhcp_offload_enable(hdd_ctx, adapter); |
| ucfg_p2p_status_start_bss(adapter->vdev); |
| |
| /* Check and restart SAP if it is on unsafe channel */ |
| hdd_unsafe_channel_restart_sap(hdd_ctx); |
| |
| hdd_set_connection_in_progress(false); |
| policy_mgr_nan_sap_post_enable_conc_check(hdd_ctx->psoc); |
| ret = 0; |
| goto free; |
| |
| error: |
| |
| /* Revert the indoor to passive marking if START BSS fails */ |
| if (indoor_chnl_marking && adapter->device_mode == QDF_SAP_MODE) { |
| hdd_update_indoor_channel(hdd_ctx, false); |
| sme_update_channel_list(mac_handle); |
| } |
| clear_bit(SOFTAP_INIT_DONE, &adapter->event_flags); |
| qdf_atomic_set(&adapter->session.ap.acs_in_progress, 0); |
| wlan_hdd_undo_acs(adapter); |
| wlansap_reset_sap_config_add_ie(config, eUPDATE_IE_ALL); |
| |
| free: |
| /* Enable Roaming after start bss in case of failure/success */ |
| wlan_hdd_enable_roaming(adapter); |
| qdf_mem_free(sme_config); |
| return ret; |
| } |
| |
| int hdd_destroy_acs_timer(struct hdd_adapter *adapter) |
| { |
| QDF_STATUS qdf_status = QDF_STATUS_E_FAILURE; |
| |
| if (!adapter->session.ap.vendor_acs_timer_initialized) |
| return 0; |
| |
| adapter->session.ap.vendor_acs_timer_initialized = false; |
| |
| clear_bit(VENDOR_ACS_RESPONSE_PENDING, &adapter->event_flags); |
| if (QDF_TIMER_STATE_RUNNING == |
| adapter->session.ap.vendor_acs_timer.state) { |
| qdf_status = |
| qdf_mc_timer_stop(&adapter->session.ap. |
| vendor_acs_timer); |
| if (!QDF_IS_STATUS_SUCCESS(qdf_status)) |
| hdd_err("Failed to stop ACS timer"); |
| } |
| |
| if (adapter->session.ap.vendor_acs_timer.user_data) |
| qdf_mem_free(adapter->session.ap.vendor_acs_timer.user_data); |
| |
| qdf_mc_timer_destroy(&adapter->session.ap.vendor_acs_timer); |
| |
| return 0; |
| } |
| |
| /** |
| * __wlan_hdd_cfg80211_stop_ap() - stop soft ap |
| * @wiphy: Pointer to wiphy structure |
| * @dev: Pointer to net_device structure |
| * |
| * Return: 0 for success non-zero for failure |
| */ |
| static int __wlan_hdd_cfg80211_stop_ap(struct wiphy *wiphy, |
| struct net_device *dev) |
| { |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| QDF_STATUS status = QDF_STATUS_E_FAILURE; |
| QDF_STATUS qdf_status = QDF_STATUS_E_FAILURE; |
| tSirUpdateIE update_ie; |
| struct hdd_beacon_data *old; |
| int ret; |
| mac_handle_t mac_handle; |
| |
| hdd_enter(); |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EINVAL; |
| } |
| |
| if (hdd_ctx->driver_status == DRIVER_MODULES_CLOSED) { |
| hdd_err("Driver module is closed; dropping request"); |
| return -EINVAL; |
| } |
| |
| if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) |
| return -EINVAL; |
| |
| qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, |
| TRACE_CODE_HDD_CFG80211_STOP_AP, |
| adapter->vdev_id, adapter->device_mode); |
| |
| if (!(adapter->device_mode == QDF_SAP_MODE || |
| adapter->device_mode == QDF_P2P_GO_MODE)) { |
| return -EOPNOTSUPP; |
| } |
| |
| /* Clear SOFTAP_INIT_DONE flag to mark stop_ap deinit. So that we do |
| * not restart SAP after SSR as SAP is already stopped from user space. |
| * This update is moved to start of this function to resolve stop_ap |
| * call during SSR case. Adapter gets cleaned up as part of SSR. |
| */ |
| clear_bit(SOFTAP_INIT_DONE, &adapter->event_flags); |
| hdd_debug("Device_mode %s(%d)", |
| qdf_opmode_str(adapter->device_mode), adapter->device_mode); |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != ret) |
| return ret; |
| |
| /* |
| * If a STA connection is in progress in another adapter, disconnect |
| * the STA and complete the SAP operation. STA will reconnect |
| * after SAP stop is done. |
| */ |
| hdd_abort_ongoing_sta_connection(hdd_ctx); |
| |
| if (adapter->device_mode == QDF_SAP_MODE) { |
| wlan_hdd_del_station(adapter); |
| mac_handle = hdd_ctx->mac_handle; |
| status = wlan_hdd_flush_pmksa_cache(adapter); |
| if (QDF_IS_STATUS_ERROR(status)) |
| hdd_debug("Cannot flush PMKIDCache"); |
| } |
| |
| cds_flush_work(&adapter->sap_stop_bss_work); |
| adapter->session.ap.sap_config.acs_cfg.acs_mode = false; |
| qdf_atomic_set(&adapter->session.ap.acs_in_progress, 0); |
| wlan_hdd_undo_acs(adapter); |
| qdf_mem_zero(&adapter->session.ap.sap_config.acs_cfg, |
| sizeof(struct sap_acs_cfg)); |
| hdd_debug("Disabling queues"); |
| wlan_hdd_netif_queue_control(adapter, |
| WLAN_STOP_ALL_NETIF_QUEUE_N_CARRIER, |
| WLAN_CONTROL_PATH); |
| |
| old = adapter->session.ap.beacon; |
| if (!old) { |
| hdd_err("Session id: %d beacon data points to NULL", |
| adapter->vdev_id); |
| return -EINVAL; |
| } |
| wlan_hdd_cleanup_actionframe(adapter); |
| wlan_hdd_cleanup_remain_on_channel_ctx(adapter); |
| mutex_lock(&hdd_ctx->sap_lock); |
| if (test_bit(SOFTAP_BSS_STARTED, &adapter->event_flags)) { |
| struct hdd_hostapd_state *hostapd_state = |
| WLAN_HDD_GET_HOSTAP_STATE_PTR(adapter); |
| |
| qdf_event_reset(&hostapd_state->qdf_stop_bss_event); |
| status = wlansap_stop_bss(WLAN_HDD_GET_SAP_CTX_PTR(adapter)); |
| if (QDF_IS_STATUS_SUCCESS(status)) { |
| qdf_status = |
| qdf_wait_for_event_completion(&hostapd_state-> |
| qdf_stop_bss_event, |
| SME_CMD_STOP_BSS_TIMEOUT); |
| |
| if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { |
| hdd_err("qdf wait for single_event failed!!"); |
| QDF_ASSERT(0); |
| } |
| } |
| clear_bit(SOFTAP_BSS_STARTED, &adapter->event_flags); |
| hdd_stop_tsf_sync(adapter); |
| |
| /*BSS stopped, clear the active sessions for this device mode*/ |
| policy_mgr_decr_session_set_pcl(hdd_ctx->psoc, |
| adapter->device_mode, |
| adapter->vdev_id); |
| hdd_green_ap_start_state_mc(hdd_ctx, adapter->device_mode, |
| false); |
| adapter->session.ap.beacon = NULL; |
| qdf_mem_free(old); |
| } |
| mutex_unlock(&hdd_ctx->sap_lock); |
| |
| mac_handle = hdd_ctx->mac_handle; |
| if (wlan_sap_is_pre_cac_active(mac_handle)) |
| hdd_clean_up_pre_cac_interface(hdd_ctx); |
| |
| if (status != QDF_STATUS_SUCCESS) { |
| hdd_err("Stopping the BSS"); |
| return -EINVAL; |
| } |
| |
| qdf_copy_macaddr(&update_ie.bssid, &adapter->mac_addr); |
| update_ie.smeSessionId = adapter->vdev_id; |
| update_ie.ieBufferlength = 0; |
| update_ie.pAdditionIEBuffer = NULL; |
| update_ie.append = true; |
| update_ie.notify = true; |
| if (sme_update_add_ie(mac_handle, |
| &update_ie, |
| eUPDATE_IE_PROBE_BCN) == QDF_STATUS_E_FAILURE) { |
| hdd_err("Could not pass on PROBE_RSP_BCN data to PE"); |
| } |
| |
| if (sme_update_add_ie(mac_handle, |
| &update_ie, |
| eUPDATE_IE_ASSOC_RESP) == QDF_STATUS_E_FAILURE) { |
| hdd_err("Could not pass on ASSOC_RSP data to PE"); |
| } |
| /* Reset WNI_CFG_PROBE_RSP Flags */ |
| wlan_hdd_reset_prob_rspies(adapter); |
| hdd_destroy_acs_timer(adapter); |
| |
| ucfg_p2p_status_stop_bss(adapter->vdev); |
| |
| hdd_exit(); |
| |
| return ret; |
| } |
| |
| /** |
| * wlan_hdd_get_channel_bw() - get channel bandwidth |
| * @width: input channel width in nl80211_chan_width value |
| * |
| * Return: channel width value defined by driver |
| */ |
| static enum hw_mode_bandwidth wlan_hdd_get_channel_bw( |
| enum nl80211_chan_width width) |
| { |
| enum hw_mode_bandwidth ch_bw = HW_MODE_20_MHZ; |
| |
| switch (width) { |
| case NL80211_CHAN_WIDTH_20_NOHT: |
| case NL80211_CHAN_WIDTH_20: |
| ch_bw = HW_MODE_20_MHZ; |
| break; |
| case NL80211_CHAN_WIDTH_40: |
| ch_bw = HW_MODE_40_MHZ; |
| break; |
| case NL80211_CHAN_WIDTH_80: |
| ch_bw = HW_MODE_80_MHZ; |
| break; |
| case NL80211_CHAN_WIDTH_80P80: |
| ch_bw = HW_MODE_80_PLUS_80_MHZ; |
| break; |
| case NL80211_CHAN_WIDTH_160: |
| ch_bw = HW_MODE_160_MHZ; |
| break; |
| default: |
| hdd_err("Invalid width: %d, using default 20MHz", width); |
| break; |
| } |
| |
| return ch_bw; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_stop_ap() - stop sap |
| * @wiphy: Pointer to wiphy |
| * @dev: Pointer to netdev |
| * |
| * Return: zero for success non-zero for failure |
| */ |
| int wlan_hdd_cfg80211_stop_ap(struct wiphy *wiphy, |
| struct net_device *dev) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(dev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_stop_ap(wiphy, dev); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)) || \ |
| defined(CFG80211_BEACON_TX_RATE_CUSTOM_BACKPORT) |
| /** |
| * hdd_get_data_rate_from_rate_mask() - convert mask to rate |
| * @wiphy: Pointer to wiphy |
| * @band: band |
| * @bit_rate_mask: pointer to bit_rake_mask |
| * |
| * This function takes band and bit_rate_mask as input and |
| * derives the beacon_tx_rate based on the supported rates |
| * published as part of wiphy register. |
| * |
| * Return: data rate for success or zero for failure |
| */ |
| static uint16_t hdd_get_data_rate_from_rate_mask(struct wiphy *wiphy, |
| enum nl80211_band band, |
| struct cfg80211_bitrate_mask *bit_rate_mask) |
| { |
| struct ieee80211_supported_band *sband = wiphy->bands[band]; |
| int sband_n_bitrates; |
| struct ieee80211_rate *sband_bitrates; |
| int i; |
| |
| if (sband) { |
| sband_bitrates = sband->bitrates; |
| sband_n_bitrates = sband->n_bitrates; |
| for (i = 0; i < sband_n_bitrates; i++) { |
| if (bit_rate_mask->control[band].legacy == |
| sband_bitrates[i].hw_value) |
| return sband_bitrates[i].bitrate; |
| } |
| } |
| return 0; |
| } |
| |
| /** |
| * hdd_update_beacon_rate() - Update beacon tx rate |
| * @adapter: Pointer to hdd_adapter_t |
| * @wiphy: Pointer to wiphy |
| * @params: Pointet to cfg80211_ap_settings |
| * |
| * This function updates the beacon tx rate which is provided |
| * as part of cfg80211_ap_settions in to the sap_config |
| * structure |
| * |
| * Return: none |
| */ |
| static void hdd_update_beacon_rate(struct hdd_adapter *adapter, |
| struct wiphy *wiphy, |
| struct cfg80211_ap_settings *params) |
| { |
| struct cfg80211_bitrate_mask *beacon_rate_mask; |
| enum nl80211_band band; |
| |
| band = params->chandef.chan->band; |
| beacon_rate_mask = ¶ms->beacon_rate; |
| if (beacon_rate_mask->control[band].legacy) { |
| adapter->session.ap.sap_config.beacon_tx_rate = |
| hdd_get_data_rate_from_rate_mask(wiphy, band, |
| beacon_rate_mask); |
| hdd_debug("beacon mask value %u, rate %hu", |
| params->beacon_rate.control[0].legacy, |
| adapter->session.ap.sap_config.beacon_tx_rate); |
| } |
| } |
| #else |
| static void hdd_update_beacon_rate(struct hdd_adapter *adapter, |
| struct wiphy *wiphy, |
| struct cfg80211_ap_settings *params) |
| { |
| } |
| #endif |
| |
| |
| /** |
| * __wlan_hdd_cfg80211_start_ap() - start soft ap mode |
| * @wiphy: Pointer to wiphy structure |
| * @dev: Pointer to net_device structure |
| * @params: Pointer to AP settings parameters |
| * |
| * Return: 0 for success non-zero for failure |
| */ |
| static int __wlan_hdd_cfg80211_start_ap(struct wiphy *wiphy, |
| struct net_device *dev, |
| struct cfg80211_ap_settings *params) |
| { |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct hdd_context *hdd_ctx; |
| enum hw_mode_bandwidth channel_width; |
| int status; |
| struct sme_sta_inactivity_timeout *sta_inactivity_timer; |
| uint8_t channel, mandt_chnl_list = 0; |
| bool sta_sap_scc_on_dfs_chan; |
| uint16_t sta_cnt, sap_cnt; |
| bool val; |
| struct wireless_dev *wdev = dev->ieee80211_ptr; |
| |
| hdd_enter(); |
| |
| clear_bit(SOFTAP_INIT_DONE, &adapter->event_flags); |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EINVAL; |
| } |
| |
| if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) |
| return -EINVAL; |
| |
| qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, |
| TRACE_CODE_HDD_CFG80211_START_AP, |
| adapter->vdev_id, params->beacon_interval); |
| |
| if (WLAN_HDD_ADAPTER_MAGIC != adapter->magic) { |
| hdd_err("HDD adapter magic is invalid"); |
| return -ENODEV; |
| } |
| |
| hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| status = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != status) |
| return status; |
| |
| hdd_debug("adapter = %pK, Device mode %s(%d) sub20 %d", |
| adapter, qdf_opmode_str(adapter->device_mode), |
| adapter->device_mode, cds_is_sub_20_mhz_enabled()); |
| |
| if (policy_mgr_is_hw_mode_change_in_progress(hdd_ctx->psoc)) { |
| status = policy_mgr_wait_for_connection_update( |
| hdd_ctx->psoc); |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| hdd_err("qdf wait for event failed!!"); |
| return -EINVAL; |
| } |
| } |
| |
| channel_width = wlan_hdd_get_channel_bw(params->chandef.width); |
| channel = ieee80211_frequency_to_channel( |
| params->chandef.chan->center_freq); |
| if (!channel) { |
| hdd_err("Invalid channel"); |
| return -EINVAL; |
| } |
| |
| if (QDF_STATUS_SUCCESS != |
| ucfg_policy_mgr_get_sap_mandt_chnl(hdd_ctx->psoc, &mandt_chnl_list)) |
| hdd_err("can't get mandatory channel list"); |
| if (mandt_chnl_list) { |
| if (WLAN_REG_IS_5GHZ_CH(channel)) { |
| hdd_debug("channel %hu, sap mandatory chan list enabled", |
| channel); |
| if (!policy_mgr_get_sap_mandatory_chan_list_len( |
| hdd_ctx->psoc)) |
| policy_mgr_init_sap_mandatory_2g_chan( |
| hdd_ctx->psoc); |
| |
| policy_mgr_add_sap_mandatory_chan(hdd_ctx->psoc, |
| channel); |
| } else { |
| policy_mgr_init_sap_mandatory_2g_chan( |
| hdd_ctx->psoc); |
| } |
| } |
| |
| adapter->session.ap.sap_config.ch_params.center_freq_seg0 = |
| cds_freq_to_chan(params->chandef.center_freq1); |
| adapter->session.ap.sap_config.ch_params.center_freq_seg1 = |
| cds_freq_to_chan(params->chandef.center_freq2); |
| |
| sta_sap_scc_on_dfs_chan = |
| policy_mgr_is_sta_sap_scc_allowed_on_dfs_chan( |
| hdd_ctx->psoc); |
| sta_cnt = policy_mgr_mode_specific_connection_count(hdd_ctx->psoc, |
| PM_STA_MODE, NULL); |
| sap_cnt = policy_mgr_mode_specific_connection_count(hdd_ctx->psoc, |
| PM_SAP_MODE, NULL); |
| |
| hdd_debug("sta_sap_scc_on_dfs_chan %u, sta_cnt %u", |
| sta_sap_scc_on_dfs_chan, sta_cnt); |
| |
| /* if sta_sap_scc_on_dfs_chan ini is set, DFS master capability is |
| * assumed disabled in the driver. |
| */ |
| if ((wlan_reg_get_channel_state(hdd_ctx->pdev, channel) == |
| CHANNEL_STATE_DFS) && sta_sap_scc_on_dfs_chan && !sta_cnt) { |
| hdd_err("SAP not allowed on DFS channel!!"); |
| return -EINVAL; |
| } |
| if (!wlan_reg_is_etsi13_srd_chan_allowed_master_mode(hdd_ctx->pdev) && |
| wlan_reg_is_etsi13_srd_chan(hdd_ctx->pdev, channel)) { |
| hdd_err("SAP not allowed on SRD channel."); |
| return -EINVAL; |
| } |
| if (cds_is_sub_20_mhz_enabled()) { |
| enum channel_state ch_state; |
| enum phy_ch_width sub_20_ch_width = CH_WIDTH_INVALID; |
| struct sap_config *sap_cfg = &adapter->session.ap.sap_config; |
| |
| if (CHANNEL_STATE_DFS == wlan_reg_get_channel_state( |
| hdd_ctx->pdev, channel)) { |
| hdd_err("Can't start SAP-DFS (channel=%d)with sub 20 MHz ch wd", |
| channel); |
| return -EINVAL; |
| } |
| if (channel_width != HW_MODE_20_MHZ) { |
| hdd_err("Hostapd (20+ MHz) conflits with config.ini (sub 20 MHz)"); |
| return -EINVAL; |
| } |
| if (cds_is_5_mhz_enabled()) |
| sub_20_ch_width = CH_WIDTH_5MHZ; |
| if (cds_is_10_mhz_enabled()) |
| sub_20_ch_width = CH_WIDTH_10MHZ; |
| if (WLAN_REG_IS_5GHZ_CH(channel)) |
| ch_state = wlan_reg_get_5g_bonded_channel_state( |
| hdd_ctx->pdev, channel, |
| sub_20_ch_width); |
| else |
| ch_state = wlan_reg_get_2g_bonded_channel_state( |
| hdd_ctx->pdev, channel, |
| sub_20_ch_width, 0); |
| if (CHANNEL_STATE_DISABLE == ch_state) { |
| hdd_err("Given ch width not supported by reg domain"); |
| return -EINVAL; |
| } |
| sap_cfg->SapHw_mode = eCSR_DOT11_MODE_abg; |
| } |
| |
| /* Disable NAN Disc before starting P2P GO or STA+SAP or SAP+SAP */ |
| if (adapter->device_mode == QDF_P2P_GO_MODE || sta_cnt || sap_cnt) { |
| hdd_debug("Invalid NAN concurrency. SAP: %d STA: %d P2P_GO: %d", |
| sap_cnt, sta_cnt, |
| (adapter->device_mode == QDF_P2P_GO_MODE)); |
| ucfg_nan_disable_concurrency(hdd_ctx->psoc); |
| } |
| |
| if (!policy_mgr_nan_sap_pre_enable_conc_check(hdd_ctx->psoc, |
| PM_SAP_MODE, channel)) |
| hdd_debug("NAN disabled due to concurrency constraints"); |
| |
| /* check if concurrency is allowed */ |
| if (!policy_mgr_allow_concurrency(hdd_ctx->psoc, |
| policy_mgr_convert_device_mode_to_qdf_type( |
| adapter->device_mode), |
| channel, |
| channel_width)) { |
| hdd_err("Connection failed due to concurrency check failure"); |
| return -EINVAL; |
| } |
| |
| status = policy_mgr_reset_connection_update(hdd_ctx->psoc); |
| if (!QDF_IS_STATUS_SUCCESS(status)) |
| hdd_err("ERR: clear event failed"); |
| |
| /* |
| * For Start Ap, the driver checks whether the SAP comes up in a |
| * different or same band( whether we require DBS or Not). |
| * If we dont require DBS, then the driver does nothing assuming |
| * the state would be already in non DBS mode, and just continues |
| * with vdev up on same MAC, by stoping the opportunistic timer, |
| * which results in a connection of 1x1 if already the state was in |
| * DBS. So first stop timer, and check the current hw mode. |
| * If the SAP comes up in band different from STA, DBS mode is already |
| * set. IF not, then well check for upgrade, and shift the connection |
| * back to single MAC 2x2 (if initial was 2x2). |
| */ |
| |
| policy_mgr_checkn_update_hw_mode_single_mac_mode(hdd_ctx->psoc, |
| channel); |
| if (status != QDF_STATUS_SUCCESS) { |
| hdd_err("Failed to stop DBS opportunistic timer"); |
| return -EINVAL; |
| } |
| |
| status = policy_mgr_current_connections_update(hdd_ctx->psoc, |
| adapter->vdev_id, channel, |
| POLICY_MGR_UPDATE_REASON_START_AP); |
| if (status == QDF_STATUS_E_FAILURE) { |
| hdd_err("ERROR: connections update failed!!"); |
| return -EINVAL; |
| } |
| |
| if (QDF_STATUS_SUCCESS == status) { |
| status = policy_mgr_wait_for_connection_update(hdd_ctx->psoc); |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| hdd_err("ERROR: qdf wait for event failed!!"); |
| return -EINVAL; |
| } |
| } |
| |
| if (adapter->device_mode == QDF_P2P_GO_MODE) { |
| struct hdd_adapter *p2p_adapter; |
| |
| p2p_adapter = hdd_get_adapter(hdd_ctx, QDF_P2P_DEVICE_MODE); |
| if (p2p_adapter) { |
| hdd_debug("Cancel active p2p device ROC before GO starting"); |
| wlan_hdd_cancel_existing_remain_on_channel( |
| p2p_adapter); |
| } |
| } |
| |
| if ((adapter->device_mode == QDF_SAP_MODE) |
| || (adapter->device_mode == QDF_P2P_GO_MODE) |
| ) { |
| struct hdd_beacon_data *old, *new; |
| enum nl80211_channel_type channel_type; |
| struct sap_config *sap_config = |
| &((WLAN_HDD_GET_AP_CTX_PTR(adapter))->sap_config); |
| |
| old = adapter->session.ap.beacon; |
| |
| if (old) |
| return -EALREADY; |
| |
| status = |
| wlan_hdd_cfg80211_alloc_new_beacon(adapter, &new, |
| ¶ms->beacon, |
| params->dtim_period); |
| |
| if (status != 0) { |
| hdd_err("Error!!! Allocating the new beacon"); |
| return -EINVAL; |
| } |
| adapter->session.ap.beacon = new; |
| |
| if (params->chandef.width < NL80211_CHAN_WIDTH_80) |
| channel_type = cfg80211_get_chandef_type( |
| &(params->chandef)); |
| else |
| channel_type = NL80211_CHAN_HT40PLUS; |
| |
| |
| wlan_hdd_set_channel(wiphy, dev, |
| ¶ms->chandef, |
| channel_type); |
| |
| hdd_update_beacon_rate(adapter, wiphy, params); |
| |
| /* set authentication type */ |
| switch (params->auth_type) { |
| case NL80211_AUTHTYPE_OPEN_SYSTEM: |
| adapter->session.ap.sap_config.authType = |
| eSAP_OPEN_SYSTEM; |
| break; |
| case NL80211_AUTHTYPE_SHARED_KEY: |
| adapter->session.ap.sap_config.authType = |
| eSAP_SHARED_KEY; |
| break; |
| default: |
| adapter->session.ap.sap_config.authType = |
| eSAP_AUTO_SWITCH; |
| } |
| adapter->session.ap.sap_config.ch_width_orig = |
| params->chandef.width; |
| |
| status = |
| wlan_hdd_cfg80211_start_bss(adapter, |
| ¶ms->beacon, |
| params->ssid, params->ssid_len, |
| params->hidden_ssid, true); |
| |
| if (status != 0) { |
| hdd_err("Error Start bss Failed"); |
| goto err_start_bss; |
| } |
| |
| hdd_start_tsf_sync(adapter); |
| |
| if (wdev->chandef.chan->center_freq != |
| params->chandef.chan->center_freq) |
| params->chandef = wdev->chandef; |
| /* |
| * If Do_Not_Break_Stream enabled send avoid channel list |
| * to application. |
| */ |
| if (policy_mgr_is_dnsc_set(adapter->vdev) && |
| sap_config->channel) { |
| wlan_hdd_send_avoid_freq_for_dnbs(hdd_ctx, |
| sap_config->channel); |
| } |
| |
| ucfg_mlme_get_sap_inactivity_override(hdd_ctx->psoc, &val); |
| if (val) { |
| sta_inactivity_timer = qdf_mem_malloc( |
| sizeof(*sta_inactivity_timer)); |
| if (!sta_inactivity_timer) { |
| hdd_err("Failed to allocate Memory"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| sta_inactivity_timer->session_id = adapter->vdev_id; |
| sta_inactivity_timer->sta_inactivity_timeout = |
| params->inactivity_timeout; |
| sme_update_sta_inactivity_timeout(hdd_ctx->mac_handle, |
| sta_inactivity_timer); |
| qdf_mem_free(sta_inactivity_timer); |
| } |
| } |
| |
| goto success; |
| |
| err_start_bss: |
| if (adapter->session.ap.beacon) |
| qdf_mem_free(adapter->session.ap.beacon); |
| adapter->session.ap.beacon = NULL; |
| |
| success: |
| hdd_exit(); |
| return status; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_start_ap() - start sap |
| * @wiphy: Pointer to wiphy |
| * @dev: Pointer to netdev |
| * @params: Pointer to start ap configuration parameters |
| * |
| * Return: zero for success non-zero for failure |
| */ |
| int wlan_hdd_cfg80211_start_ap(struct wiphy *wiphy, |
| struct net_device *dev, |
| struct cfg80211_ap_settings *params) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(dev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_start_ap(wiphy, dev, params); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| |
| /** |
| * __wlan_hdd_cfg80211_change_beacon() - change beacon for sofatap/p2p go |
| * @wiphy: Pointer to wiphy structure |
| * @dev: Pointer to net_device structure |
| * @params: Pointer to change beacon parameters |
| * |
| * Return: 0 for success non-zero for failure |
| */ |
| static int __wlan_hdd_cfg80211_change_beacon(struct wiphy *wiphy, |
| struct net_device *dev, |
| struct cfg80211_beacon_data *params) |
| { |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct hdd_context *hdd_ctx; |
| struct hdd_beacon_data *old, *new; |
| int status; |
| |
| hdd_enter(); |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EINVAL; |
| } |
| |
| if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) |
| return -EINVAL; |
| |
| qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, |
| TRACE_CODE_HDD_CFG80211_CHANGE_BEACON, |
| adapter->vdev_id, adapter->device_mode); |
| |
| hdd_debug("Device_mode %s(%d)", |
| qdf_opmode_str(adapter->device_mode), adapter->device_mode); |
| |
| hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| status = wlan_hdd_validate_context(hdd_ctx); |
| |
| if (0 != status) |
| return status; |
| |
| if (!(adapter->device_mode == QDF_SAP_MODE || |
| adapter->device_mode == QDF_P2P_GO_MODE)) { |
| return -EOPNOTSUPP; |
| } |
| |
| old = adapter->session.ap.beacon; |
| |
| if (!old) { |
| hdd_err("session id: %d beacon data points to NULL", |
| adapter->vdev_id); |
| return -EINVAL; |
| } |
| |
| status = wlan_hdd_cfg80211_alloc_new_beacon(adapter, &new, params, 0); |
| |
| if (status != QDF_STATUS_SUCCESS) { |
| hdd_err("new beacon alloc failed"); |
| return -EINVAL; |
| } |
| |
| adapter->session.ap.beacon = new; |
| hdd_debug("update beacon for P2P GO/SAP"); |
| status = wlan_hdd_cfg80211_start_bss(adapter, params, NULL, |
| 0, 0, false); |
| |
| hdd_exit(); |
| return status; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_change_beacon() - change beacon content in sap mode |
| * @wiphy: Pointer to wiphy |
| * @dev: Pointer to netdev |
| * @params: Pointer to change beacon parameters |
| * |
| * Return: zero for success non-zero for failure |
| */ |
| int wlan_hdd_cfg80211_change_beacon(struct wiphy *wiphy, |
| struct net_device *dev, |
| struct cfg80211_beacon_data *params) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(dev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_change_beacon(wiphy, dev, params); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| |
| /** |
| * hdd_sap_indicate_disconnect_for_sta() - Indicate disconnect indication |
| * to supplicant, if there any clients connected to SAP interface. |
| * @adapter: sap adapter context |
| * |
| * Return: nothing |
| */ |
| void hdd_sap_indicate_disconnect_for_sta(struct hdd_adapter *adapter) |
| { |
| struct sap_event sap_event; |
| int sta_id; |
| struct sap_context *sap_ctx; |
| |
| hdd_enter(); |
| |
| sap_ctx = WLAN_HDD_GET_SAP_CTX_PTR(adapter); |
| if (!sap_ctx) { |
| hdd_err("invalid sap context"); |
| return; |
| } |
| |
| for (sta_id = 0; sta_id < WLAN_MAX_STA_COUNT; sta_id++) { |
| if (adapter->sta_info[sta_id].in_use) { |
| hdd_debug("sta_id: %d in_use: %d %pK", |
| sta_id, adapter->sta_info[sta_id].in_use, |
| adapter); |
| |
| if (qdf_is_macaddr_broadcast( |
| &adapter->sta_info[sta_id].sta_mac)) |
| continue; |
| |
| sap_event.sapHddEventCode = eSAP_STA_DISASSOC_EVENT; |
| qdf_mem_copy( |
| &sap_event.sapevt. |
| sapStationDisassocCompleteEvent.staMac, |
| &adapter->sta_info[sta_id].sta_mac, |
| sizeof(struct qdf_mac_addr)); |
| sap_event.sapevt.sapStationDisassocCompleteEvent. |
| reason = |
| eSAP_MAC_INITATED_DISASSOC; |
| sap_event.sapevt.sapStationDisassocCompleteEvent. |
| statusCode = |
| QDF_STATUS_E_RESOURCES; |
| hdd_hostapd_sap_event_cb(&sap_event, |
| sap_ctx->user_context); |
| } |
| } |
| |
| hdd_exit(); |
| } |
| |
| bool hdd_is_peer_associated(struct hdd_adapter *adapter, |
| struct qdf_mac_addr *mac_addr) |
| { |
| uint32_t cnt; |
| struct hdd_station_info *sta_info; |
| |
| if (!adapter || !mac_addr) { |
| hdd_err("Invalid adapter or mac_addr"); |
| return false; |
| } |
| |
| sta_info = adapter->sta_info; |
| spin_lock_bh(&adapter->sta_info_lock); |
| for (cnt = 0; cnt < WLAN_MAX_STA_COUNT; cnt++) { |
| if ((sta_info[cnt].in_use) && |
| !qdf_mem_cmp(&(sta_info[cnt].sta_mac), mac_addr, |
| QDF_MAC_ADDR_SIZE)) |
| break; |
| } |
| spin_unlock_bh(&adapter->sta_info_lock); |
| if (cnt != WLAN_MAX_STA_COUNT) |
| return true; |
| |
| return false; |
| } |