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