Browse Source

qcacmn: Move hdd_request_manager to qcacmn osif layer

Move hdd_request_manager to qcacmn osif layer, which will be used
by CP_STATs component.

Change-Id: Iebf7f9d259793b56cc70c5b3f9dec14a5ef35b25
CRs-Fixed: 2220035
Naveen Rawat 7 years ago
parent
commit
685bf25eb3
2 changed files with 433 additions and 0 deletions
  1. 204 0
      os_if/linux/wlan_osif_request_manager.c
  2. 229 0
      os_if/linux/wlan_osif_request_manager.h

+ 204 - 0
os_if/linux/wlan_osif_request_manager.c

@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved.
+ *
+ * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
+ *
+ *
+ * Permission to use, copy, modify, and/or distribute this software for
+ * any purpose with or without fee is hereby granted, provided that the
+ * above copyright notice and this permission notice appear in all
+ * copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+ * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * This file was originally distributed by Qualcomm Atheros, Inc.
+ * under proprietary terms before Copyright ownership was assigned
+ * to the Linux Foundation.
+ */
+
+#include <linux/kernel.h>
+#include "qdf_mem.h"
+#include "qdf_list.h"
+#include "qdf_event.h"
+#include "wlan_cfg80211.h"
+#include "wlan_osif_request_manager.h"
+
+/* arbitrary value */
+#define MAX_NUM_REQUESTS 20
+
+static bool is_initialized;
+static qdf_list_t requests;
+static qdf_spinlock_t spinlock;
+static void *cookie;
+
+struct osif_request {
+	qdf_list_node_t node;
+	void *cookie;
+	uint32_t reference_count;
+	struct osif_request_params params;
+	qdf_event_t completed;
+};
+
+/* must be called with spinlock held */
+static void osif_request_unlink(struct osif_request *request)
+{
+	qdf_list_remove_node(&requests, &request->node);
+}
+
+static void osif_request_destroy(struct osif_request *request)
+{
+	struct osif_request_params *params;
+
+	params = &request->params;
+	if (params->dealloc) {
+		void *priv = osif_request_priv(request);
+
+		params->dealloc(priv);
+	}
+	qdf_event_destroy(&request->completed);
+	qdf_mem_free(request);
+}
+
+/* must be called with spinlock held */
+static struct osif_request *osif_request_find(void *cookie)
+{
+	QDF_STATUS status;
+	struct osif_request *request;
+	qdf_list_node_t *node;
+
+	status = qdf_list_peek_front(&requests, &node);
+	while (QDF_IS_STATUS_SUCCESS(status)) {
+		request = qdf_container_of(node, struct osif_request, node);
+		if (request->cookie == cookie)
+			return request;
+		status = qdf_list_peek_next(&requests, node, &node);
+	}
+
+	return NULL;
+}
+
+struct osif_request *osif_request_alloc(const struct osif_request_params *params)
+{
+	size_t length;
+	struct osif_request *request;
+
+	if (!is_initialized) {
+		cfg80211_err("invoked when not initialized from %pS",
+			(void *)_RET_IP_);
+		return NULL;
+	}
+
+	length = sizeof(*request) + params->priv_size;
+	request = qdf_mem_malloc(length);
+	if (!request) {
+		cfg80211_err("allocation failed for %pS", (void *)_RET_IP_);
+		return NULL;
+	}
+	request->reference_count = 1;
+	request->params = *params;
+	qdf_event_create(&request->completed);
+	qdf_spin_lock_bh(&spinlock);
+	request->cookie = cookie++;
+	qdf_list_insert_back(&requests, &request->node);
+	qdf_spin_unlock_bh(&spinlock);
+	cfg80211_debug("request %pK, cookie %pK, caller %pS",
+		  request, request->cookie, (void *)_RET_IP_);
+
+	return request;
+}
+
+void *osif_request_priv(struct osif_request *request)
+{
+	/* private data area immediately follows the struct osif_request */
+	return request + 1;
+}
+
+void *osif_request_cookie(struct osif_request *request)
+{
+	return request->cookie;
+}
+
+struct osif_request *osif_request_get(void *cookie)
+{
+	struct osif_request *request;
+
+	if (!is_initialized) {
+		cfg80211_err("invoked when not initialized from %pS",
+			(void *)_RET_IP_);
+		return NULL;
+	}
+	qdf_spin_lock_bh(&spinlock);
+	request = osif_request_find(cookie);
+	if (request)
+		request->reference_count++;
+	qdf_spin_unlock_bh(&spinlock);
+	cfg80211_debug("cookie %pK, request %pK, caller %pS",
+		  cookie, request, (void *)_RET_IP_);
+
+	return request;
+}
+
+void osif_request_put(struct osif_request *request)
+{
+	bool unlinked = false;
+
+	cfg80211_debug("request %pK, cookie %pK, caller %pS",
+		  request, request->cookie, (void *)_RET_IP_);
+	qdf_spin_lock_bh(&spinlock);
+	request->reference_count--;
+	if (0 == request->reference_count) {
+		osif_request_unlink(request);
+		unlinked = true;
+	}
+	qdf_spin_unlock_bh(&spinlock);
+	if (unlinked)
+		osif_request_destroy(request);
+}
+
+int osif_request_wait_for_response(struct osif_request *request)
+{
+	QDF_STATUS status;
+
+	status = qdf_wait_for_event_completion(&request->completed,
+				       request->params.timeout_ms);
+
+	return qdf_status_to_os_return(status);
+}
+
+void osif_request_complete(struct osif_request *request)
+{
+	(void) qdf_event_set(&request->completed);
+}
+
+void osif_request_manager_init(void)
+{
+	cfg80211_debug("%pS", (void *)_RET_IP_);
+	if (is_initialized)
+		return;
+
+	qdf_list_create(&requests, MAX_NUM_REQUESTS);
+	qdf_spinlock_create(&spinlock);
+	is_initialized = true;
+}
+
+/*
+ * osif_request_manager_deinit implementation note:
+ * It is intentional that we do not destroy the list or the spinlock.
+ * This allows threads to still access the infrastructure even when it
+ * has been deinitialized. Since neither lists nor spinlocks consume
+ * resources this does not result in a resource leak.
+ */
+void osif_request_manager_deinit(void)
+{
+	cfg80211_debug("%pS", (void *)_RET_IP_);
+	is_initialized = false;
+}

+ 229 - 0
os_if/linux/wlan_osif_request_manager.h

@@ -0,0 +1,229 @@
+/*
+ * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved.
+ *
+ * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
+ *
+ *
+ * Permission to use, copy, modify, and/or distribute this software for
+ * any purpose with or without fee is hereby granted, provided that the
+ * above copyright notice and this permission notice appear in all
+ * copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+ * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * This file was originally distributed by Qualcomm Atheros, Inc.
+ * under proprietary terms before Copyright ownership was assigned
+ * to the Linux Foundation.
+ */
+
+#ifndef __WLAN_OSIF_REQUEST_MANAGER_H__
+#define __WLAN_OSIF_REQUEST_MANAGER_H__
+
+/**
+ * DOC: WLAN OSIF REQUEST MANAGER
+ *
+ * Many operations within the wlan driver occur in an asynchronous
+ * manner. Requests are received by OSIF via one of the kernel
+ * interfaces (ioctl, nl80211, virtual file system, etc.). The
+ * requests are translated to an internal format and are then passed
+ * to lower layers, usually via SME, for processing. For requests
+ * which require a response, that response comes up from the lower
+ * layers in a separate thread of execution, ultimately resulting in a
+ * call to a callback function that was provided by OSIF as part of the
+ * initial request. So a mechanism is needed to synchronize the
+ * request and response. This framework provides that mechanism.
+ *
+ * Once the framework has been initialized, the typical sequence of
+ * events is as follows:
+ *
+ * Request Thread:
+ * 1. Create a &struct osif_request_params which describes the request.
+ * 2. Call osif_request_alloc() to allocate a &struct osif_request.
+ * 3. Call osif_request_priv() to get a pointer to the private data.
+ * 4. Place any information which must be shared with the Response
+ *    Callback in the private data area.
+ * 5. Call osif_request_cookie() to get the unique cookie assigned
+ *    to the request.
+ * 6. Call the underlying request handling API, passing the cookie
+ *    as the callback's private context.
+ * 7. Call osif_request_wait_for_response() to wait for the response
+ *    (or for the request to time out).
+ * 8. Use the return status to see if the request was successful. If
+ *    it was, retrieve any response information from the private
+ *    structure and prepare a response for userspace.
+ * 9. Call osif_request_put() to relinquish access to the request.
+ * 10. Return status to the caller.
+ *
+ * Response Callback:
+ * 1. Call osif_request_get() with the provided cookie to see if the
+ *    request structure is still valid.  If it returns %NULL then
+ *    return since this means the request thread has already timed
+ *    out.
+ * 2. Call osif_request_priv() to get access to the private data area.
+ * 3. Write response data into the private data area.
+ * 4. Call osif_request_complete() to indicate that the response is
+ *    ready to be processed by the request thread.
+ * 5. Call osif_request_put() to relinquish the callback function's
+ *    reference to the request.
+ */
+
+/* this is opaque to clients */
+struct osif_request;
+
+/**
+ * typedef osif_request_dealloc - Private data deallocation function
+ */
+typedef void (*osif_request_dealloc)(void *priv);
+
+/**
+ * struct osif_request_params - OSIF request parameters
+ * @priv_size: Size of the private data area required to pass
+ *      information between the request thread and the response callback.
+ * @timeout_ms: The amount of time to wait for a response in milliseconds.
+ * @dealloc: Function to be called when the request is destroyed to
+ *      deallocate any allocations made in the private area of the
+ *      request struct. Can be %NULL if no private allocations are
+ *      made.
+ */
+struct osif_request_params {
+	uint32_t priv_size;
+	uint32_t timeout_ms;
+	osif_request_dealloc dealloc;
+};
+
+/**
+ * osif_request_alloc() - Allocate a request struct
+ * @params: parameter block that specifies the attributes of the
+ *      request
+ *
+ * This function will attempt to allocate a &struct osif_request with
+ * the specified @params. If successful, the caller can then use
+ * request struct to make an asynchronous request. Once the request is
+ * no longer needed, the reference should be relinquished via a call
+ * to osif_request_put().
+ *
+ * Return: A pointer to an allocated &struct osif_request (which also
+ * contains room for the private buffer) if the allocation is
+ * successful, %NULL if the allocation fails.
+ */
+struct osif_request *osif_request_alloc(const struct osif_request_params *params);
+
+/**
+ * osif_request_priv() - Get pointer to request private data
+ * @request: The request struct that contains the private data
+ *
+ * This function will return a pointer to the private data area that
+ * is part of the request struct. The caller must already have a valid
+ * reference to @request from either osif_request_alloc() or
+ * osif_request_get().
+ *
+ * Returns: pointer to the private data area. Note that this pointer
+ * will always be an offset from the input @request pointer and hence
+ * this function will never return %NULL.
+ */
+void *osif_request_priv(struct osif_request *request);
+
+/**
+ * osif_request_cookie() - Get cookie of a request
+ * @request: The request struct associated with the request
+ *
+ * This function will return the unique cookie that has been assigned
+ * to the request. This cookie can subsequently be passed to
+ * osif_request_get() to retrieve the request.
+ *
+ * Note that the cookie is defined as a void pointer as it is intended
+ * to be passed as an opaque context pointer from OSIF to underlying
+ * layers when making a request, and subsequently passed back to OSIF
+ * as an opaque pointer in an asynchronous callback.
+ *
+ * Returns: The cookie assigned to the request.
+ */
+void *osif_request_cookie(struct osif_request *request);
+
+/**
+ * osif_request_get() - Get a reference to a request struct
+ * @cookie: The cookie of the request struct that needs to be
+ *      referenced
+ *
+ * This function will use the cookie to determine if the associated
+ * request struct is valid, and if so, will increment the reference
+ * count of the struct. This means the caller is guaranteed that the
+ * request struct is valid and the underlying private data can be
+ * dereferenced.
+ *
+ * Returns: The pointer to the request struct associated with @cookie
+ * if the request is still valid, %NULL if the underlying request
+ * struct is no longer valid.
+ */
+struct osif_request *osif_request_get(void *cookie);
+
+/**
+ * osif_request_put() - Release a reference to a request struct
+ * @request: The request struct that no longer needs to be referenced
+ *
+ * This function will decrement the reference count of the struct, and
+ * will clean up the request if this is the last reference. The caller
+ * must already have a valid reference to @request, either from
+ * osif_request_alloc() or osif_request_get().
+ *
+ * Returns: Nothing
+ */
+void osif_request_put(struct osif_request *request);
+
+/**
+ * osif_request_wait_for_response() - Wait for a response
+ * @request: The request struct associated with the request
+ *
+ * This function will wait until either a response is received and
+ * communicated via osif_request_complete(), or until the request
+ * timeout period expires.
+ *
+ * Returns: 0 if a response was received, -ETIMEDOUT if the response
+ * timed out.
+ */
+int osif_request_wait_for_response(struct osif_request *request);
+
+/**
+ * osif_request_complete() - Complete a request
+ * @request: The request struct associated with the request
+ *
+ * This function is used to indicate that a response has been received
+ * and that any information required by the request thread has been
+ * copied into the private data area of the request struct. This will
+ * unblock any osif_request_wait_for_response() that is pending on this
+ * @request.
+ *
+ * Returns: Nothing
+ */
+void osif_request_complete(struct osif_request *request);
+
+/**
+ * osif_request_manager_init() - Initialize the OSIF Request Manager
+ *
+ * This function must be called during system initialization to
+ * initialize the OSIF Request Manager.
+ *
+ * Returns: Nothing
+ */
+void osif_request_manager_init(void);
+
+/**
+ * osif_request_manager_deinit() - Deinitialize the OSIF Request Manager
+ *
+ * This function must be called during system shutdown to deinitialize
+ * the OSIF Request Manager.
+ *
+ * Returns: Nothing
+ */
+void osif_request_manager_deinit(void);
+
+#endif /* __WLAN_OSIF_REQUEST_MANAGER_H__ */