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_disconnect_rsp.c 19 * 20 * This file maintains definitaions of disconnect response 21 * fucntions. 22 */ 23 24 #include <wlan_cfg80211.h> 25 #include <linux/wireless.h> 26 #include "osif_cm_rsp.h" 27 #include "wlan_osif_priv.h" 28 #include "osif_cm_util.h" 29 30 /** 31 * osif_validate_disconnect_and_reset_src_id() - Validate disconnection 32 * and resets source and id 33 * @osif_priv: Pointer to vdev osif priv 34 * @rsp: Disconnect response from connectin manager 35 * 36 * This function validates disconnect response and if the disconnect 37 * response is valid, resets the source and id of the command 38 * 39 * Context: Any context. Takes and releases cmd id spinlock. 40 * Return: QDF_STATUS 41 */ 42 43 static QDF_STATUS 44 osif_validate_disconnect_and_reset_src_id(struct vdev_osif_priv *osif_priv, 45 struct wlan_cm_discon_rsp *rsp) 46 { 47 QDF_STATUS status = QDF_STATUS_SUCCESS; 48 49 /* Always drop internal disconnect */ 50 qdf_spinlock_acquire(&osif_priv->cm_info.cmd_id_lock); 51 if (rsp->req.req.source == CM_INTERNAL_DISCONNECT) { 52 osif_debug("ignore internal disconnect"); 53 status = QDF_STATUS_E_INVAL; 54 goto rel_lock; 55 } 56 57 /* 58 * Send to kernel only if last osif cmd type is disconnect and 59 * cookie match else drop. If cookie match reset the cookie 60 * and source 61 */ 62 if (rsp->req.cm_id != osif_priv->cm_info.last_id || 63 rsp->req.req.source != osif_priv->cm_info.last_source) { 64 osif_debug("Ignore as cm_id(%d)/src(%d) didn't match stored cm_id(%d)/src(%d)", 65 rsp->req.cm_id, rsp->req.req.source, 66 osif_priv->cm_info.last_id, 67 osif_priv->cm_info.last_source); 68 status = QDF_STATUS_E_INVAL; 69 goto rel_lock; 70 } 71 72 osif_cm_reset_id_and_src_no_lock(osif_priv); 73 rel_lock: 74 qdf_spinlock_release(&osif_priv->cm_info.cmd_id_lock); 75 76 return status; 77 } 78 79 #if defined(CFG80211_DISCONNECTED_V2) || \ 80 (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0)) 81 static void 82 osif_cm_indicate_disconnect(struct net_device *dev, 83 enum ieee80211_reasoncode reason, 84 bool locally_generated, const u8 *ie, 85 size_t ie_len, gfp_t gfp) 86 { 87 cfg80211_disconnected(dev, reason, ie, ie_len, locally_generated, gfp); 88 } 89 #else 90 static void 91 osif_cm_indicate_disconnect(struct net_device *dev, 92 enum ieee80211_reasoncode reason, 93 bool locally_generated, const u8 *ie, 94 size_t ie_len, gfp_t gfp) 95 { 96 cfg80211_disconnected(dev, reason, ie, ie_len, gfp); 97 } 98 #endif 99 100 static enum ieee80211_reasoncode 101 osif_cm_get_disconnect_reason(struct vdev_osif_priv *osif_priv, uint16_t reason) 102 { 103 enum ieee80211_reasoncode ieee80211_reason = WLAN_REASON_UNSPECIFIED; 104 105 if (reason < REASON_PROP_START) 106 ieee80211_reason = reason; 107 /* 108 * Applications expect reason code as 0 for beacon miss failure 109 * due to backward compatibility. So send ieee80211_reason as 0. 110 */ 111 if (reason == REASON_BEACON_MISSED) 112 ieee80211_reason = 0; 113 114 return ieee80211_reason; 115 } 116 117 QDF_STATUS osif_disconnect_handler(struct wlan_objmgr_vdev *vdev, 118 struct wlan_cm_discon_rsp *rsp) 119 { 120 enum ieee80211_reasoncode ieee80211_reason; 121 struct vdev_osif_priv *osif_priv = wlan_vdev_get_ospriv(vdev); 122 bool locally_generated = true; 123 QDF_STATUS status = QDF_STATUS_SUCCESS; 124 enum qca_disconnect_reason_codes qca_reason; 125 126 qca_reason = osif_cm_mac_to_qca_reason(rsp->req.req.reason_code); 127 ieee80211_reason = 128 osif_cm_get_disconnect_reason(osif_priv, 129 rsp->req.req.reason_code); 130 131 osif_nofl_info("%s(vdevid-%d): " QDF_MAC_ADDR_FMT " %sdisconnect " QDF_MAC_ADDR_FMT " cm_id %d source %d reason:%u %s vendor:%u %s", 132 osif_priv->wdev->netdev->name, 133 rsp->req.req.vdev_id, 134 QDF_MAC_ADDR_REF(wlan_vdev_mlme_get_macaddr(vdev)), 135 locally_generated ? "locally-generated " : "", 136 QDF_MAC_ADDR_REF(rsp->req.req.bssid.bytes), 137 rsp->req.cm_id, rsp->req.req.source, ieee80211_reason, 138 ucfg_cm_reason_code_to_str(rsp->req.req.reason_code), 139 qca_reason, 140 osif_cm_qca_reason_to_str(qca_reason)); 141 142 /* Unlink bss if disconnect is from peer or south bound */ 143 if (rsp->req.req.source == CM_PEER_DISCONNECT || 144 rsp->req.req.source == CM_SB_DISCONNECT) 145 osif_cm_unlink_bss(vdev, osif_priv, &rsp->req.req.bssid, 146 NULL, 0); 147 148 status = osif_validate_disconnect_and_reset_src_id(osif_priv, rsp); 149 if (QDF_IS_STATUS_ERROR(status)) { 150 osif_cm_disconnect_comp_ind(vdev, rsp, OSIF_NOT_HANDLED); 151 return status; 152 } 153 154 if (rsp->req.req.source == CM_PEER_DISCONNECT) 155 locally_generated = false; 156 157 osif_cm_disconnect_comp_ind(vdev, rsp, OSIF_PRE_USERSPACE_UPDATE); 158 osif_cm_indicate_disconnect(osif_priv->wdev->netdev, ieee80211_reason, 159 locally_generated, rsp->ap_discon_ie.ptr, 160 rsp->ap_discon_ie.len, GFP_KERNEL); 161 162 osif_cm_disconnect_comp_ind(vdev, rsp, OSIF_POST_USERSPACE_UPDATE); 163 164 return status; 165 } 166