1 /* 2 * Copyright (c) 2012-2015, 2020 The Linux Foundation. All rights reserved. 3 * 4 * Permission to use, copy, modify, and/or distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17 /** 18 * DOC: osif_cm_util.c 19 * 20 * This file maintains definitaions of connect, disconnect, roam 21 * common apis. 22 */ 23 #include <include/wlan_mlme_cmn.h> 24 #include "osif_cm_util.h" 25 #include "wlan_osif_priv.h" 26 #include "wlan_cfg80211.h" 27 #include "osif_cm_rsp.h" 28 #include "wlan_cfg80211_scan.h" 29 30 enum qca_sta_connect_fail_reason_codes 31 osif_cm_mac_to_qca_connect_fail_reason(enum wlan_status_code internal_reason) 32 { 33 enum qca_sta_connect_fail_reason_codes reason = 0; 34 35 if (internal_reason < STATUS_PROP_START) 36 return reason; 37 38 switch (internal_reason) { 39 case STATUS_NO_NETWORK_FOUND: 40 reason = QCA_STA_CONNECT_FAIL_REASON_NO_BSS_FOUND; 41 break; 42 case STATUS_AUTH_TX_FAIL: 43 reason = QCA_STA_CONNECT_FAIL_REASON_AUTH_TX_FAIL; 44 break; 45 case STATUS_AUTH_NO_ACK_RECEIVED: 46 reason = QCA_STA_CONNECT_FAIL_REASON_AUTH_NO_ACK_RECEIVED; 47 break; 48 case STATUS_AUTH_NO_RESP_RECEIVED: 49 reason = QCA_STA_CONNECT_FAIL_REASON_AUTH_NO_RESP_RECEIVED; 50 break; 51 case STATUS_ASSOC_TX_FAIL: 52 reason = QCA_STA_CONNECT_FAIL_REASON_ASSOC_REQ_TX_FAIL; 53 break; 54 case STATUS_ASSOC_NO_ACK_RECEIVED: 55 reason = QCA_STA_CONNECT_FAIL_REASON_ASSOC_NO_ACK_RECEIVED; 56 break; 57 case STATUS_ASSOC_NO_RESP_RECEIVED: 58 reason = QCA_STA_CONNECT_FAIL_REASON_ASSOC_NO_RESP_RECEIVED; 59 break; 60 default: 61 osif_debug("QCA code not present for internal status code %d", 62 internal_reason); 63 } 64 65 return reason; 66 } 67 68 const char * 69 osif_cm_qca_reason_to_str(enum qca_disconnect_reason_codes reason) 70 { 71 switch (reason) { 72 CASE_RETURN_STRING(QCA_DISCONNECT_REASON_INTERNAL_ROAM_FAILURE); 73 CASE_RETURN_STRING(QCA_DISCONNECT_REASON_EXTERNAL_ROAM_FAILURE); 74 CASE_RETURN_STRING(QCA_DISCONNECT_REASON_GATEWAY_REACHABILITY_FAILURE); 75 CASE_RETURN_STRING(QCA_DISCONNECT_REASON_UNSUPPORTED_CHANNEL_CSA); 76 CASE_RETURN_STRING(QCA_DISCONNECT_REASON_OPER_CHANNEL_DISABLED_INDOOR); 77 CASE_RETURN_STRING(QCA_DISCONNECT_REASON_OPER_CHANNEL_USER_DISABLED); 78 CASE_RETURN_STRING(QCA_DISCONNECT_REASON_DEVICE_RECOVERY); 79 CASE_RETURN_STRING(QCA_DISCONNECT_REASON_KEY_TIMEOUT); 80 CASE_RETURN_STRING(QCA_DISCONNECT_REASON_OPER_CHANNEL_BAND_CHANGE); 81 CASE_RETURN_STRING(QCA_DISCONNECT_REASON_IFACE_DOWN); 82 CASE_RETURN_STRING(QCA_DISCONNECT_REASON_PEER_XRETRY_FAIL); 83 CASE_RETURN_STRING(QCA_DISCONNECT_REASON_PEER_INACTIVITY); 84 CASE_RETURN_STRING(QCA_DISCONNECT_REASON_SA_QUERY_TIMEOUT); 85 CASE_RETURN_STRING(QCA_DISCONNECT_REASON_BEACON_MISS_FAILURE); 86 CASE_RETURN_STRING(QCA_DISCONNECT_REASON_CHANNEL_SWITCH_FAILURE); 87 CASE_RETURN_STRING(QCA_DISCONNECT_REASON_USER_TRIGGERED); 88 case QCA_DISCONNECT_REASON_UNSPECIFIED: 89 return ""; 90 default: 91 return "Unknown"; 92 } 93 } 94 95 enum qca_disconnect_reason_codes 96 osif_cm_mac_to_qca_reason(enum wlan_reason_code internal_reason) 97 { 98 enum qca_disconnect_reason_codes reason = 99 QCA_DISCONNECT_REASON_UNSPECIFIED; 100 101 if (internal_reason < REASON_PROP_START) 102 return reason; 103 104 switch (internal_reason) { 105 case REASON_HOST_TRIGGERED_ROAM_FAILURE: 106 case REASON_FW_TRIGGERED_ROAM_FAILURE: 107 reason = QCA_DISCONNECT_REASON_INTERNAL_ROAM_FAILURE; 108 break; 109 case REASON_USER_TRIGGERED_ROAM_FAILURE: 110 reason = QCA_DISCONNECT_REASON_EXTERNAL_ROAM_FAILURE; 111 break; 112 case REASON_GATEWAY_REACHABILITY_FAILURE: 113 reason = 114 QCA_DISCONNECT_REASON_GATEWAY_REACHABILITY_FAILURE; 115 break; 116 case REASON_UNSUPPORTED_CHANNEL_CSA: 117 reason = QCA_DISCONNECT_REASON_UNSUPPORTED_CHANNEL_CSA; 118 break; 119 case REASON_OPER_CHANNEL_DISABLED_INDOOR: 120 reason = 121 QCA_DISCONNECT_REASON_OPER_CHANNEL_DISABLED_INDOOR; 122 break; 123 case REASON_OPER_CHANNEL_USER_DISABLED: 124 reason = 125 QCA_DISCONNECT_REASON_OPER_CHANNEL_USER_DISABLED; 126 break; 127 case REASON_DEVICE_RECOVERY: 128 reason = QCA_DISCONNECT_REASON_DEVICE_RECOVERY; 129 break; 130 case REASON_KEY_TIMEOUT: 131 reason = QCA_DISCONNECT_REASON_KEY_TIMEOUT; 132 break; 133 case REASON_OPER_CHANNEL_BAND_CHANGE: 134 reason = QCA_DISCONNECT_REASON_OPER_CHANNEL_BAND_CHANGE; 135 break; 136 case REASON_IFACE_DOWN: 137 reason = QCA_DISCONNECT_REASON_IFACE_DOWN; 138 break; 139 case REASON_PEER_XRETRY_FAIL: 140 reason = QCA_DISCONNECT_REASON_PEER_XRETRY_FAIL; 141 break; 142 case REASON_PEER_INACTIVITY: 143 reason = QCA_DISCONNECT_REASON_PEER_INACTIVITY; 144 break; 145 case REASON_SA_QUERY_TIMEOUT: 146 reason = QCA_DISCONNECT_REASON_SA_QUERY_TIMEOUT; 147 break; 148 case REASON_CHANNEL_SWITCH_FAILED: 149 reason = QCA_DISCONNECT_REASON_CHANNEL_SWITCH_FAILURE; 150 break; 151 case REASON_BEACON_MISSED: 152 reason = QCA_DISCONNECT_REASON_BEACON_MISS_FAILURE; 153 break; 154 default: 155 osif_debug("No QCA reason code for mac reason: %u", 156 internal_reason); 157 /* Unspecified reason by default */ 158 } 159 160 return reason; 161 } 162 163 #ifdef FEATURE_CM_ENABLE 164 static struct osif_cm_ops *osif_cm_legacy_ops; 165 166 void osif_cm_reset_id_and_src_no_lock(struct vdev_osif_priv *osif_priv) 167 { 168 osif_priv->cm_info.last_id = CM_ID_INVALID; 169 osif_priv->cm_info.last_source = CM_SOURCE_INVALID; 170 } 171 172 QDF_STATUS osif_cm_reset_id_and_src(struct wlan_objmgr_vdev *vdev) 173 { 174 struct vdev_osif_priv *osif_priv = wlan_vdev_get_ospriv(vdev); 175 176 if (!osif_priv) { 177 osif_err("Invalid vdev osif priv"); 178 return QDF_STATUS_E_INVAL; 179 } 180 qdf_spinlock_acquire(&osif_priv->cm_info.cmd_id_lock); 181 osif_cm_reset_id_and_src_no_lock(osif_priv); 182 qdf_spinlock_release(&osif_priv->cm_info.cmd_id_lock); 183 184 return QDF_STATUS_SUCCESS; 185 } 186 187 /** 188 * osif_cm_connect_complete_cb() - Connect complete callback 189 * @vdev: vdev pointer 190 * @rsp: connect response 191 * 192 * Return: QDF_STATUS 193 */ 194 static QDF_STATUS 195 osif_cm_connect_complete_cb(struct wlan_objmgr_vdev *vdev, 196 struct wlan_cm_connect_resp *rsp) 197 { 198 return osif_connect_handler(vdev, rsp); 199 } 200 201 /** 202 * osif_cm_failed_candidate_cb() - Callback to indicate failed candidate 203 * @vdev: vdev pointer 204 * @rsp: connect response 205 * 206 * Return: QDF_STATUS 207 */ 208 static QDF_STATUS 209 osif_cm_failed_candidate_cb(struct wlan_objmgr_vdev *vdev, 210 struct wlan_cm_connect_resp *rsp) 211 { 212 return osif_failed_candidate_handler(vdev, rsp); 213 } 214 215 /** 216 * osif_cm_update_id_and_src_cb() - Callback to update id and 217 * source of the connect/disconnect request 218 * @vdev: vdev pointer 219 * @Source: Source of the connect req 220 * @id: Connect/disconnect id 221 * 222 * Context: Any context. Takes and releases cmd id spinlock 223 * Return: QDF_STATUS 224 */ 225 static QDF_STATUS 226 osif_cm_update_id_and_src_cb(struct wlan_objmgr_vdev *vdev, 227 enum wlan_cm_source source, wlan_cm_id cm_id) 228 { 229 struct vdev_osif_priv *osif_priv = wlan_vdev_get_ospriv(vdev); 230 231 if (!osif_priv) { 232 osif_err("Invalid vdev osif priv"); 233 return QDF_STATUS_E_INVAL; 234 } 235 236 qdf_spinlock_acquire(&osif_priv->cm_info.cmd_id_lock); 237 osif_priv->cm_info.last_id = cm_id; 238 osif_priv->cm_info.last_source = source; 239 qdf_spinlock_release(&osif_priv->cm_info.cmd_id_lock); 240 241 return QDF_STATUS_SUCCESS; 242 } 243 244 /** 245 * osif_cm_disconnect_complete_cb() - Disconnect done callback 246 * @vdev: vdev pointer 247 * @disconnect_rsp: Disconnect response 248 * 249 * Context: Any context 250 * Return: QDF_STATUS 251 */ 252 253 static QDF_STATUS 254 osif_cm_disconnect_complete_cb(struct wlan_objmgr_vdev *vdev, 255 struct wlan_cm_discon_rsp *rsp) 256 { 257 return osif_disconnect_handler(vdev, rsp); 258 } 259 260 #ifdef CONN_MGR_ADV_FEATURE 261 void osif_cm_unlink_bss(struct wlan_objmgr_vdev *vdev, 262 struct vdev_osif_priv *osif_priv, 263 struct qdf_mac_addr *bssid, 264 uint8_t *ssid, uint8_t ssid_len) 265 { 266 struct wiphy *wiphy = osif_priv->wdev->wiphy; 267 struct scan_filter *filter; 268 269 __wlan_cfg80211_unlink_bss_list(wiphy, bssid->bytes, 270 ssid_len ? ssid : NULL, ssid_len); 271 filter = qdf_mem_malloc(sizeof(*filter)); 272 if (!filter) 273 return; 274 275 filter->num_of_bssid = 1; 276 qdf_copy_macaddr(&filter->bssid_list[0], bssid); 277 ucfg_scan_flush_results(wlan_vdev_get_pdev(vdev), filter); 278 qdf_mem_free(filter); 279 } 280 281 static QDF_STATUS 282 osif_cm_disable_netif_queue(struct wlan_objmgr_vdev *vdev) 283 { 284 return osif_cm_netif_queue_ind(vdev, 285 WLAN_STOP_ALL_NETIF_QUEUE_N_CARRIER, 286 WLAN_CONTROL_PATH); 287 } 288 #else 289 static inline QDF_STATUS 290 osif_cm_disable_netif_queue(struct wlan_objmgr_vdev *vdev) 291 { 292 return QDF_STATUS_SUCCESS; 293 } 294 #endif 295 /** 296 * osif_cm_disconnect_start_cb() - Disconnect start callback 297 * @vdev: vdev pointer 298 * 299 * This callback indicates os_if that disconnection is started 300 * so that os_if can stop all the activity on this connection 301 * 302 * Return: QDF_STATUS 303 */ 304 static QDF_STATUS 305 osif_cm_disconnect_start_cb(struct wlan_objmgr_vdev *vdev) 306 { 307 /* Disable netif queue on disconnect start */ 308 return osif_cm_disable_netif_queue(vdev); 309 } 310 311 static struct mlme_cm_ops cm_ops = { 312 .mlme_cm_connect_complete_cb = osif_cm_connect_complete_cb, 313 .mlme_cm_failed_candidate_cb = osif_cm_failed_candidate_cb, 314 .mlme_cm_update_id_and_src_cb = osif_cm_update_id_and_src_cb, 315 .mlme_cm_disconnect_complete_cb = osif_cm_disconnect_complete_cb, 316 .mlme_cm_disconnect_start_cb = osif_cm_disconnect_start_cb, 317 }; 318 319 /** 320 * osif_cm_get_global_ops() - Get connection manager global ops 321 * 322 * Return: Connection manager global ops 323 */ 324 static struct mlme_cm_ops *osif_cm_get_global_ops(void) 325 { 326 return &cm_ops; 327 } 328 329 QDF_STATUS osif_cm_register_cb(void) 330 { 331 mlme_set_osif_cm_cb(osif_cm_get_global_ops); 332 333 return QDF_STATUS_SUCCESS; 334 } 335 336 QDF_STATUS osif_cm_osif_priv_init(struct wlan_objmgr_vdev *vdev) 337 { 338 struct vdev_osif_priv *osif_priv = wlan_vdev_get_ospriv(vdev); 339 enum QDF_OPMODE mode = wlan_vdev_mlme_get_opmode(vdev); 340 341 if (mode != QDF_STA_MODE && mode != QDF_P2P_CLIENT_MODE) 342 return QDF_STATUS_SUCCESS; 343 344 if (!osif_priv) { 345 osif_err("Invalid vdev osif priv"); 346 return QDF_STATUS_E_INVAL; 347 } 348 349 qdf_spinlock_create(&osif_priv->cm_info.cmd_id_lock); 350 351 return QDF_STATUS_SUCCESS; 352 } 353 354 QDF_STATUS osif_cm_osif_priv_deinit(struct wlan_objmgr_vdev *vdev) 355 { 356 struct vdev_osif_priv *osif_priv = wlan_vdev_get_ospriv(vdev); 357 enum QDF_OPMODE mode = wlan_vdev_mlme_get_opmode(vdev); 358 359 if (mode != QDF_STA_MODE && mode != QDF_P2P_CLIENT_MODE) 360 return QDF_STATUS_SUCCESS; 361 362 if (!osif_priv) { 363 osif_err("Invalid vdev osif priv"); 364 return QDF_STATUS_E_INVAL; 365 } 366 qdf_spinlock_destroy(&osif_priv->cm_info.cmd_id_lock); 367 368 return QDF_STATUS_SUCCESS; 369 } 370 371 QDF_STATUS osif_cm_connect_comp_ind(struct wlan_objmgr_vdev *vdev, 372 struct wlan_cm_connect_resp *rsp, 373 enum osif_cb_type type) 374 { 375 osif_cm_connect_comp_cb cb = NULL; 376 QDF_STATUS ret = QDF_STATUS_SUCCESS; 377 378 if (osif_cm_legacy_ops) 379 cb = osif_cm_legacy_ops->connect_complete_cb; 380 if (cb) 381 ret = cb(vdev, rsp, type); 382 383 return ret; 384 } 385 386 QDF_STATUS osif_cm_disconnect_comp_ind(struct wlan_objmgr_vdev *vdev, 387 struct wlan_cm_discon_rsp *rsp, 388 enum osif_cb_type type) 389 { 390 osif_cm_disconnect_comp_cb cb = NULL; 391 QDF_STATUS ret = QDF_STATUS_SUCCESS; 392 393 if (osif_cm_legacy_ops) 394 cb = osif_cm_legacy_ops->disconnect_complete_cb; 395 if (cb) 396 ret = cb(vdev, rsp, type); 397 398 return ret; 399 } 400 401 #ifdef CONN_MGR_ADV_FEATURE 402 QDF_STATUS osif_cm_netif_queue_ind(struct wlan_objmgr_vdev *vdev, 403 enum netif_action_type action, 404 enum netif_reason_type reason) 405 { 406 osif_cm_netif_queue_ctrl_cb cb = NULL; 407 QDF_STATUS ret = QDF_STATUS_SUCCESS; 408 409 if (osif_cm_legacy_ops) 410 cb = osif_cm_legacy_ops->netif_queue_control_cb; 411 if (cb) 412 ret = cb(vdev, action, reason); 413 414 return ret; 415 } 416 #endif 417 418 void osif_cm_set_legacy_cb(struct osif_cm_ops *osif_legacy_ops) 419 { 420 osif_cm_legacy_ops = osif_legacy_ops; 421 } 422 423 void osif_cm_reset_legacy_cb(void) 424 { 425 osif_cm_legacy_ops = NULL; 426 } 427 #endif 428