xref: /wlan-dirver/qca-wifi-host-cmn/os_if/linux/wlan_osif_request_manager.c (revision 6ecd284e5a94a1c96e26d571dd47419ac305990d)
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