1 /*
2  * Copyright (c) 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 /**
20  * DOC: qdf_idr
21  * This file provides the ability to map an ID to a pointer
22  */
23 
24 /* Include files */
25 #include <qdf_idr.h>
26 #include <qdf_module.h>
27 
28 #define QDF_IDR_START     0x100
29 #define QDF_IDR_END       0
30 
qdf_idr_gpf_flag(void)31 static int qdf_idr_gpf_flag(void)
32 {
33 	if (in_interrupt() || irqs_disabled() || in_atomic())
34 		return GFP_ATOMIC;
35 
36 	return GFP_KERNEL;
37 }
38 
39 #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)
40 /**
41  * __qdf_idr_alloc() - Allocates an unused ID
42  * @idp:   pointer to qdf idr
43  * @ptr:   pointer to be associated with the new ID
44  * @start: the minimum ID
45  * @end:   the maximum ID
46  *
47  * Return: new ID
48  */
49 static inline int32_t
__qdf_idr_alloc(qdf_idr * idp,void * ptr,int32_t start,int32_t end)50 __qdf_idr_alloc(qdf_idr *idp, void *ptr, int32_t start, int32_t end)
51 {
52 	int32_t id = 0;
53 
54 	idr_get_new(&idp->idr, ptr, &id);
55 
56 	return id;
57 }
58 #else
59 static inline int32_t
__qdf_idr_alloc(qdf_idr * idp,void * ptr,int32_t start,int32_t end)60 __qdf_idr_alloc(qdf_idr *idp, void *ptr, int32_t start, int32_t end)
61 {
62 	return idr_alloc(&idp->idr, ptr, start, end, qdf_idr_gpf_flag());
63 }
64 #endif
65 
qdf_idr_create(qdf_idr * idp)66 QDF_STATUS qdf_idr_create(qdf_idr *idp)
67 {
68 	if (!idp)
69 		return QDF_STATUS_E_INVAL;
70 
71 	qdf_spinlock_create(&idp->lock);
72 
73 	idr_init(&idp->idr);
74 
75 	return QDF_STATUS_SUCCESS;
76 }
77 
78 qdf_export_symbol(qdf_idr_create);
79 
qdf_idr_destroy(qdf_idr * idp)80 QDF_STATUS qdf_idr_destroy(qdf_idr *idp)
81 {
82 	if (!idp)
83 		return QDF_STATUS_E_INVAL;
84 
85 	qdf_spinlock_destroy(&idp->lock);
86 	idr_destroy(&idp->idr);
87 
88 	return QDF_STATUS_SUCCESS;
89 }
90 
91 qdf_export_symbol(qdf_idr_destroy);
92 
qdf_idr_alloc(qdf_idr * idp,void * ptr,int32_t * id)93 QDF_STATUS qdf_idr_alloc(qdf_idr *idp, void *ptr, int32_t *id)
94 {
95 	int local_id;
96 
97 	if (!idp || !ptr)
98 		return QDF_STATUS_E_INVAL;
99 
100 	qdf_spinlock_acquire(&idp->lock);
101 	local_id = __qdf_idr_alloc(idp, ptr, QDF_IDR_START, QDF_IDR_END);
102 	qdf_spinlock_release(&idp->lock);
103 	if (local_id < QDF_IDR_START)
104 		return QDF_STATUS_E_FAILURE;
105 
106 	*id = local_id;
107 
108 	return QDF_STATUS_SUCCESS;
109 }
110 
111 qdf_export_symbol(qdf_idr_alloc);
112 
qdf_idr_remove(qdf_idr * idp,int32_t id)113 QDF_STATUS qdf_idr_remove(qdf_idr *idp, int32_t id)
114 {
115 	if (!idp || id < QDF_IDR_START)
116 		return QDF_STATUS_E_INVAL;
117 
118 	qdf_spinlock_acquire(&idp->lock);
119 	if (idr_find(&idp->idr, id))
120 		idr_remove(&idp->idr, id);
121 	qdf_spinlock_release(&idp->lock);
122 
123 	return QDF_STATUS_SUCCESS;
124 }
125 
126 qdf_export_symbol(qdf_idr_remove);
127 
qdf_idr_find(qdf_idr * idp,int32_t id,void ** ptr)128 QDF_STATUS qdf_idr_find(qdf_idr *idp, int32_t id, void **ptr)
129 {
130 	if (!ptr || (id < QDF_IDR_START))
131 		return QDF_STATUS_E_INVAL;
132 
133 	qdf_spinlock_acquire(&idp->lock);
134 	*ptr = idr_find(&idp->idr, id);
135 	qdf_spinlock_release(&idp->lock);
136 	if (!(*ptr))
137 		return QDF_STATUS_E_INVAL;
138 	else
139 		return QDF_STATUS_SUCCESS;
140 }
141 
142 qdf_export_symbol(qdf_idr_find);
143 
144