1 /* 2 * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. 3 * 4 * Previously licensed under the ISC license by Qualcomm Atheros, Inc. 5 * 6 * 7 * Permission to use, copy, modify, and/or distribute this software for 8 * any purpose with or without fee is hereby granted, provided that the 9 * above copyright notice and this permission notice appear in all 10 * copies. 11 * 12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 14 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 15 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 16 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 17 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 18 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 19 * PERFORMANCE OF THIS SOFTWARE. 20 */ 21 22 /* 23 * This file was originally distributed by Qualcomm Atheros, Inc. 24 * under proprietary terms before Copyright ownership was assigned 25 * to the Linux Foundation. 26 */ 27 28 #include <linux/kernel.h> 29 #include "qdf_mem.h" 30 #include "qdf_list.h" 31 #include "qdf_event.h" 32 #include "wlan_cfg80211.h" 33 #include "wlan_osif_request_manager.h" 34 35 /* arbitrary value */ 36 #define MAX_NUM_REQUESTS 20 37 38 static bool is_initialized; 39 static qdf_list_t requests; 40 static qdf_spinlock_t spinlock; 41 static void *cookie; 42 43 struct osif_request { 44 qdf_list_node_t node; 45 void *cookie; 46 uint32_t reference_count; 47 struct osif_request_params params; 48 qdf_event_t completed; 49 }; 50 51 /* must be called with spinlock held */ 52 static void osif_request_unlink(struct osif_request *request) 53 { 54 qdf_list_remove_node(&requests, &request->node); 55 } 56 57 static void osif_request_destroy(struct osif_request *request) 58 { 59 struct osif_request_params *params; 60 61 params = &request->params; 62 if (params->dealloc) { 63 void *priv = osif_request_priv(request); 64 65 params->dealloc(priv); 66 } 67 qdf_event_destroy(&request->completed); 68 qdf_mem_free(request); 69 } 70 71 /* must be called with spinlock held */ 72 static struct osif_request *osif_request_find(void *cookie) 73 { 74 QDF_STATUS status; 75 struct osif_request *request; 76 qdf_list_node_t *node; 77 78 status = qdf_list_peek_front(&requests, &node); 79 while (QDF_IS_STATUS_SUCCESS(status)) { 80 request = qdf_container_of(node, struct osif_request, node); 81 if (request->cookie == cookie) 82 return request; 83 status = qdf_list_peek_next(&requests, node, &node); 84 } 85 86 return NULL; 87 } 88 89 struct osif_request *osif_request_alloc(const struct osif_request_params *params) 90 { 91 size_t length; 92 struct osif_request *request; 93 94 if (!is_initialized) { 95 cfg80211_err("invoked when not initialized from %pS", 96 (void *)_RET_IP_); 97 return NULL; 98 } 99 100 length = sizeof(*request) + params->priv_size; 101 request = qdf_mem_malloc(length); 102 if (!request) { 103 cfg80211_err("allocation failed for %pS", (void *)_RET_IP_); 104 return NULL; 105 } 106 request->reference_count = 1; 107 request->params = *params; 108 qdf_event_create(&request->completed); 109 qdf_spin_lock_bh(&spinlock); 110 request->cookie = cookie++; 111 qdf_list_insert_back(&requests, &request->node); 112 qdf_spin_unlock_bh(&spinlock); 113 cfg80211_debug("request %pK, cookie %pK, caller %pS", 114 request, request->cookie, (void *)_RET_IP_); 115 116 return request; 117 } 118 119 void *osif_request_priv(struct osif_request *request) 120 { 121 /* private data area immediately follows the struct osif_request */ 122 return request + 1; 123 } 124 125 void *osif_request_cookie(struct osif_request *request) 126 { 127 return request->cookie; 128 } 129 130 struct osif_request *osif_request_get(void *cookie) 131 { 132 struct osif_request *request; 133 134 if (!is_initialized) { 135 cfg80211_err("invoked when not initialized from %pS", 136 (void *)_RET_IP_); 137 return NULL; 138 } 139 qdf_spin_lock_bh(&spinlock); 140 request = osif_request_find(cookie); 141 if (request) 142 request->reference_count++; 143 qdf_spin_unlock_bh(&spinlock); 144 cfg80211_debug("cookie %pK, request %pK, caller %pS", 145 cookie, request, (void *)_RET_IP_); 146 147 return request; 148 } 149 150 void osif_request_put(struct osif_request *request) 151 { 152 bool unlinked = false; 153 154 cfg80211_debug("request %pK, cookie %pK, caller %pS", 155 request, request->cookie, (void *)_RET_IP_); 156 qdf_spin_lock_bh(&spinlock); 157 request->reference_count--; 158 if (0 == request->reference_count) { 159 osif_request_unlink(request); 160 unlinked = true; 161 } 162 qdf_spin_unlock_bh(&spinlock); 163 if (unlinked) 164 osif_request_destroy(request); 165 } 166 167 int osif_request_wait_for_response(struct osif_request *request) 168 { 169 QDF_STATUS status; 170 171 status = qdf_wait_for_event_completion(&request->completed, 172 request->params.timeout_ms); 173 174 return qdf_status_to_os_return(status); 175 } 176 177 void osif_request_complete(struct osif_request *request) 178 { 179 (void) qdf_event_set(&request->completed); 180 } 181 182 void osif_request_manager_init(void) 183 { 184 cfg80211_debug("%pS", (void *)_RET_IP_); 185 if (is_initialized) 186 return; 187 188 qdf_list_create(&requests, MAX_NUM_REQUESTS); 189 qdf_spinlock_create(&spinlock); 190 is_initialized = true; 191 } 192 193 /* 194 * osif_request_manager_deinit implementation note: 195 * It is intentional that we do not destroy the list or the spinlock. 196 * This allows threads to still access the infrastructure even when it 197 * has been deinitialized. Since neither lists nor spinlocks consume 198 * resources this does not result in a resource leak. 199 */ 200 void osif_request_manager_deinit(void) 201 { 202 cfg80211_debug("%pS", (void *)_RET_IP_); 203 is_initialized = false; 204 } 205