1 /*
2  * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
3  * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
4  *
5  * Permission to use, copy, modify, and/or distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 /**
19  * DOC: wlan_hdd_btc_chain_mode.c
20  *
21  * The implementation of bt coex chain mode configuration
22  *
23  */
24 
25 #include "wlan_hdd_main.h"
26 #include "wlan_hdd_btc_chain_mode.h"
27 #include "osif_sync.h"
28 #include "wlan_coex_ucfg_api.h"
29 #include "wlan_hdd_object_manager.h"
30 #include "wlan_cfg80211_coex.h"
31 
32 static QDF_STATUS
wlan_hdd_btc_chain_mode_handler(struct wlan_objmgr_vdev * vdev)33 wlan_hdd_btc_chain_mode_handler(struct wlan_objmgr_vdev *vdev)
34 {
35 	QDF_STATUS status;
36 	struct hdd_adapter *adapter;
37 	mac_handle_t mac_handle;
38 	uint8_t nss, band;
39 	enum coex_btc_chain_mode mode;
40 	uint8_t vdev_id;
41 	uint32_t freq;
42 	struct wlan_objmgr_psoc *psoc;
43 	struct wlan_hdd_link_info *link_info;
44 
45 	if (!vdev) {
46 		hdd_err("NULL vdev");
47 		return QDF_STATUS_E_INVAL;
48 	}
49 
50 	vdev_id = wlan_vdev_get_id(vdev);
51 	if (wlan_hdd_validate_vdev_id(vdev_id))
52 		return QDF_STATUS_E_INVAL;
53 
54 	psoc = wlan_vdev_get_psoc(vdev);
55 	if (!psoc) {
56 		hdd_err("NULL psoc");
57 		return QDF_STATUS_E_INVAL;
58 	}
59 
60 	link_info = wlan_hdd_get_link_info_from_vdev(psoc, vdev_id);
61 	if (!link_info) {
62 		hdd_err("Invalid vdev");
63 		return QDF_STATUS_E_INVAL;
64 	}
65 
66 	adapter =  link_info->adapter;
67 	status = ucfg_coex_psoc_get_btc_chain_mode(psoc, &mode);
68 	if (QDF_IS_STATUS_ERROR(status)) {
69 		hdd_err("failed to get cur BTC chain mode, status %d", status);
70 		return -EFAULT;
71 	}
72 
73 	mac_handle = adapter->hdd_ctx->mac_handle;
74 	if (!mac_handle) {
75 		hdd_err("NULL MAC handle");
76 		return -EINVAL;
77 	}
78 
79 	nss = ((mode == WLAN_COEX_BTC_CHAIN_MODE_FDD ||
80 		mode == WLAN_COEX_BTC_CHAIN_MODE_HYBRID) ? 1 : 2);
81 
82 	hdd_debug("update nss to %d for vdev %d, device mode %d",
83 		  nss, link_info->vdev_id, adapter->device_mode);
84 	band = NSS_CHAINS_BAND_2GHZ;
85 	sme_update_nss_in_mlme_cfg(mac_handle, nss, nss,
86 				   adapter->device_mode, band);
87 	sme_update_vdev_type_nss(mac_handle, nss, band);
88 
89 	status = wlan_objmgr_vdev_try_get_ref(vdev, WLAN_OSIF_ID);
90 	if (QDF_IS_STATUS_SUCCESS(status)) {
91 		hdd_store_nss_chains_cfg_in_vdev(adapter->hdd_ctx, vdev);
92 		wlan_objmgr_vdev_release_ref(vdev, WLAN_OSIF_ID);
93 	}
94 
95 	sme_update_he_cap_nss(mac_handle, link_info->vdev_id, nss);
96 	freq = hdd_get_link_info_home_channel(link_info);
97 
98 	/*
99 	 * BT coex chain mode is for COEX between BT and WiFi-2.4G.
100 	 * Nss and related parameters have been updated upon for
101 	 * NSS_CHAINS_BAND_2GHZ.
102 	 * If the current home channel is NOT 2.4G, these parameters
103 	 * will take effect when switching to 2.4G, so no need to do
104 	 * restart here.
105 	 */
106 	if (!WLAN_REG_IS_24GHZ_CH_FREQ(freq))
107 		return QDF_STATUS_SUCCESS;
108 
109 	switch (adapter->device_mode) {
110 	case QDF_STA_MODE:
111 	case QDF_P2P_CLIENT_MODE:
112 		wlan_hdd_cm_issue_disconnect(link_info,
113 					     REASON_PREV_AUTH_NOT_VALID, false);
114 		break;
115 	case QDF_SAP_MODE:
116 	case QDF_P2P_GO_MODE:
117 		hdd_restart_sap(link_info);
118 		break;
119 	default:
120 		break;
121 	}
122 
123 	return QDF_STATUS_SUCCESS;
124 }
125 
wlan_hdd_register_btc_chain_mode_handler(struct wlan_objmgr_psoc * psoc)126 void wlan_hdd_register_btc_chain_mode_handler(struct wlan_objmgr_psoc *psoc)
127 {
128 	ucfg_coex_register_cfg_updated_handler(psoc,
129 					       COEX_CONFIG_BTC_CHAIN_MODE,
130 					       wlan_hdd_btc_chain_mode_handler
131 					       );
132 }
133 
134 /**
135  * __wlan_hdd_cfg80211_set_btc_chain_mode() - set btc chain mode
136  * @wiphy: pointer to wireless wiphy structure.
137  * @wdev: pointer to wireless_dev structure.
138  * @data: pointer to btc chain mode command parameters.
139  * @data_len: the length in byte of btc chain mode command parameters.
140  *
141  * Return: An error code or 0 on success.
142  */
__wlan_hdd_cfg80211_set_btc_chain_mode(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)143 static int __wlan_hdd_cfg80211_set_btc_chain_mode(struct wiphy *wiphy,
144 						  struct wireless_dev *wdev,
145 						  const void *data,
146 						  int data_len)
147 {
148 	struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(wdev->netdev);
149 	struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
150 	int errno;
151 	struct wlan_objmgr_vdev *vdev;
152 
153 	hdd_enter_dev(wdev->netdev);
154 
155 	if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
156 		hdd_err("Command not allowed in FTM mode");
157 		return -EPERM;
158 	}
159 
160 	errno = wlan_hdd_validate_context(hdd_ctx);
161 	if (errno != 0)
162 		return errno;
163 
164 	if (hdd_ctx->num_rf_chains < 2) {
165 		hdd_debug("Num of chains [%u] is less than 2, setting BTC separate chain mode is not allowed",
166 			  hdd_ctx->num_rf_chains);
167 		return -EINVAL;
168 	}
169 
170 	vdev = hdd_objmgr_get_vdev_by_user(adapter->deflink, WLAN_OSIF_ID);
171 	if (!vdev)
172 		return -EINVAL;
173 
174 	errno = wlan_cfg80211_coex_set_btc_chain_mode(vdev, data, data_len);
175 	hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_ID);
176 
177 	return errno;
178 }
179 
180 /**
181  * wlan_hdd_cfg80211_set_btc_chain_mode() - set btc chain mode
182  * @wiphy: pointer to wireless wiphy structure.
183  * @wdev: pointer to wireless_dev structure.
184  * @data: pointer to btc chain mode command parameters.
185  * @data_len: the length in byte of btc chain mode command parameters.
186  *
187  * Return: An error code or 0 on success.
188  */
wlan_hdd_cfg80211_set_btc_chain_mode(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)189 int wlan_hdd_cfg80211_set_btc_chain_mode(struct wiphy *wiphy,
190 					 struct wireless_dev *wdev,
191 					 const void *data, int data_len)
192 {
193 	int errno;
194 	struct osif_vdev_sync *vdev_sync;
195 
196 	errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync);
197 	if (errno)
198 		return errno;
199 
200 	errno = __wlan_hdd_cfg80211_set_btc_chain_mode(wiphy, wdev,
201 						       data, data_len);
202 
203 	osif_vdev_sync_op_stop(vdev_sync);
204 
205 	return errno;
206 }
207