blob: 0d13de7454c640629fafde15e164c91a103f18ce [file] [log] [blame]
Jeff Johnson3a3bc292017-01-20 07:23:10 -08001/*
2 * Copyright (c) 2017 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 "wlan_hdd_request_manager.h"
30#include "wlan_hdd_main.h"
31#include "qdf_list.h"
32#include "qdf_event.h"
33#include "qdf_mem.h"
34
35/* arbitrary value */
36#define MAX_NUM_REQUESTS 20
37
38static bool is_initialized;
39static qdf_list_t requests;
40static qdf_spinlock_t spinlock;
41static void *cookie;
42
43struct hdd_request {
44 qdf_list_node_t node;
45 void *cookie;
46 uint32_t reference_count;
47 struct hdd_request_params params;
48 qdf_event_t completed;
49};
50
51/* must be called with spinlock held */
52static void hdd_request_unlink(struct hdd_request *request)
53{
54 qdf_list_remove_node(&requests, &request->node);
55}
56
57static void hdd_request_destroy(struct hdd_request *request)
58{
59 struct hdd_request_params *params;
60
61 params = &request->params;
62 if (params->dealloc) {
63 void *priv = hdd_request_priv(request);
Jeff Johnson4f7f7c62017-10-05 08:53:41 -070064
Jeff Johnson3a3bc292017-01-20 07:23:10 -080065 params->dealloc(priv);
66 }
67 qdf_event_destroy(&request->completed);
68 qdf_mem_free(request);
69}
70
71/* must be called with spinlock held */
72static struct hdd_request *hdd_request_find(void *cookie)
73{
74 QDF_STATUS status;
75 struct hdd_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 hdd_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
89struct hdd_request *hdd_request_alloc(const struct hdd_request_params *params)
90{
91 size_t length;
92 struct hdd_request *request;
93
94 if (!is_initialized) {
95 hdd_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 hdd_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);
Jeff Johnson36e74c42017-09-18 08:15:42 -0700113 hdd_debug("request %pK, cookie %pK, caller %pS",
Jeff Johnson3a3bc292017-01-20 07:23:10 -0800114 request, request->cookie, (void *)_RET_IP_);
115
116 return request;
117}
118
119void *hdd_request_priv(struct hdd_request *request)
120{
121 /* private data area immediately follows the struct hdd_request */
122 return request + 1;
123}
124
125void *hdd_request_cookie(struct hdd_request *request)
126{
127 return request->cookie;
128}
129
130struct hdd_request *hdd_request_get(void *cookie)
131{
132 struct hdd_request *request;
133
134 if (!is_initialized) {
135 hdd_err("invoked when not initialized from %pS",
136 (void *)_RET_IP_);
137 return NULL;
138 }
139 qdf_spin_lock_bh(&spinlock);
140 request = hdd_request_find(cookie);
141 if (request)
142 request->reference_count++;
143 qdf_spin_unlock_bh(&spinlock);
Jeff Johnson36e74c42017-09-18 08:15:42 -0700144 hdd_debug("cookie %pK, request %pK, caller %pS",
Jeff Johnson3a3bc292017-01-20 07:23:10 -0800145 cookie, request, (void *)_RET_IP_);
146
147 return request;
148}
149
150void hdd_request_put(struct hdd_request *request)
151{
152 bool unlinked = false;
153
Jeff Johnson36e74c42017-09-18 08:15:42 -0700154 hdd_debug("request %pK, cookie %pK, caller %pS",
Jeff Johnson3a3bc292017-01-20 07:23:10 -0800155 request, request->cookie, (void *)_RET_IP_);
156 qdf_spin_lock_bh(&spinlock);
157 request->reference_count--;
158 if (0 == request->reference_count) {
159 hdd_request_unlink(request);
160 unlinked = true;
161 }
162 qdf_spin_unlock_bh(&spinlock);
163 if (unlinked)
164 hdd_request_destroy(request);
165}
166
167int hdd_request_wait_for_response(struct hdd_request *request)
168{
169 QDF_STATUS status;
170
Nachiket Kukade0396b732017-11-14 16:35:16 +0530171 status = qdf_wait_for_event_completion(&request->completed,
Jeff Johnson3a3bc292017-01-20 07:23:10 -0800172 request->params.timeout_ms);
173
174 return qdf_status_to_os_return(status);
175}
176
177void hdd_request_complete(struct hdd_request *request)
178{
179 (void) qdf_event_set(&request->completed);
180}
181
182void hdd_request_manager_init(void)
183{
184 hdd_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 * hdd_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 */
200void hdd_request_manager_deinit(void)
201{
202 hdd_debug("%pS", (void *)_RET_IP_);
203 is_initialized = false;
204}