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