]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blob - drivers/usb/host/dwc_common_port/dwc_notifier.c
Add dwc_otg driver
[mirror_ubuntu-artful-kernel.git] / drivers / usb / host / dwc_common_port / dwc_notifier.c
1 #ifdef DWC_NOTIFYLIB
2
3 #include "dwc_notifier.h"
4 #include "dwc_list.h"
5
6 typedef struct dwc_observer {
7 void *observer;
8 dwc_notifier_callback_t callback;
9 void *data;
10 char *notification;
11 DWC_CIRCLEQ_ENTRY(dwc_observer) list_entry;
12 } observer_t;
13
14 DWC_CIRCLEQ_HEAD(observer_queue, dwc_observer);
15
16 typedef struct dwc_notifier {
17 void *mem_ctx;
18 void *object;
19 struct observer_queue observers;
20 DWC_CIRCLEQ_ENTRY(dwc_notifier) list_entry;
21 } notifier_t;
22
23 DWC_CIRCLEQ_HEAD(notifier_queue, dwc_notifier);
24
25 typedef struct manager {
26 void *mem_ctx;
27 void *wkq_ctx;
28 dwc_workq_t *wq;
29 // dwc_mutex_t *mutex;
30 struct notifier_queue notifiers;
31 } manager_t;
32
33 static manager_t *manager = NULL;
34
35 static int create_manager(void *mem_ctx, void *wkq_ctx)
36 {
37 manager = dwc_alloc(mem_ctx, sizeof(manager_t));
38 if (!manager) {
39 return -DWC_E_NO_MEMORY;
40 }
41
42 DWC_CIRCLEQ_INIT(&manager->notifiers);
43
44 manager->wq = dwc_workq_alloc(wkq_ctx, "DWC Notification WorkQ");
45 if (!manager->wq) {
46 return -DWC_E_NO_MEMORY;
47 }
48
49 return 0;
50 }
51
52 static void free_manager(void)
53 {
54 dwc_workq_free(manager->wq);
55
56 /* All notifiers must have unregistered themselves before this module
57 * can be removed. Hitting this assertion indicates a programmer
58 * error. */
59 DWC_ASSERT(DWC_CIRCLEQ_EMPTY(&manager->notifiers),
60 "Notification manager being freed before all notifiers have been removed");
61 dwc_free(manager->mem_ctx, manager);
62 }
63
64 #ifdef DEBUG
65 static void dump_manager(void)
66 {
67 notifier_t *n;
68 observer_t *o;
69
70 DWC_ASSERT(manager, "Notification manager not found");
71
72 DWC_DEBUG("List of all notifiers and observers:\n");
73 DWC_CIRCLEQ_FOREACH(n, &manager->notifiers, list_entry) {
74 DWC_DEBUG("Notifier %p has observers:\n", n->object);
75 DWC_CIRCLEQ_FOREACH(o, &n->observers, list_entry) {
76 DWC_DEBUG(" %p watching %s\n", o->observer, o->notification);
77 }
78 }
79 }
80 #else
81 #define dump_manager(...)
82 #endif
83
84 static observer_t *alloc_observer(void *mem_ctx, void *observer, char *notification,
85 dwc_notifier_callback_t callback, void *data)
86 {
87 observer_t *new_observer = dwc_alloc(mem_ctx, sizeof(observer_t));
88
89 if (!new_observer) {
90 return NULL;
91 }
92
93 DWC_CIRCLEQ_INIT_ENTRY(new_observer, list_entry);
94 new_observer->observer = observer;
95 new_observer->notification = notification;
96 new_observer->callback = callback;
97 new_observer->data = data;
98 return new_observer;
99 }
100
101 static void free_observer(void *mem_ctx, observer_t *observer)
102 {
103 dwc_free(mem_ctx, observer);
104 }
105
106 static notifier_t *alloc_notifier(void *mem_ctx, void *object)
107 {
108 notifier_t *notifier;
109
110 if (!object) {
111 return NULL;
112 }
113
114 notifier = dwc_alloc(mem_ctx, sizeof(notifier_t));
115 if (!notifier) {
116 return NULL;
117 }
118
119 DWC_CIRCLEQ_INIT(&notifier->observers);
120 DWC_CIRCLEQ_INIT_ENTRY(notifier, list_entry);
121
122 notifier->mem_ctx = mem_ctx;
123 notifier->object = object;
124 return notifier;
125 }
126
127 static void free_notifier(notifier_t *notifier)
128 {
129 observer_t *observer;
130
131 DWC_CIRCLEQ_FOREACH(observer, &notifier->observers, list_entry) {
132 free_observer(notifier->mem_ctx, observer);
133 }
134
135 dwc_free(notifier->mem_ctx, notifier);
136 }
137
138 static notifier_t *find_notifier(void *object)
139 {
140 notifier_t *notifier;
141
142 DWC_ASSERT(manager, "Notification manager not found");
143
144 if (!object) {
145 return NULL;
146 }
147
148 DWC_CIRCLEQ_FOREACH(notifier, &manager->notifiers, list_entry) {
149 if (notifier->object == object) {
150 return notifier;
151 }
152 }
153
154 return NULL;
155 }
156
157 int dwc_alloc_notification_manager(void *mem_ctx, void *wkq_ctx)
158 {
159 return create_manager(mem_ctx, wkq_ctx);
160 }
161
162 void dwc_free_notification_manager(void)
163 {
164 free_manager();
165 }
166
167 dwc_notifier_t *dwc_register_notifier(void *mem_ctx, void *object)
168 {
169 notifier_t *notifier;
170
171 DWC_ASSERT(manager, "Notification manager not found");
172
173 notifier = find_notifier(object);
174 if (notifier) {
175 DWC_ERROR("Notifier %p is already registered\n", object);
176 return NULL;
177 }
178
179 notifier = alloc_notifier(mem_ctx, object);
180 if (!notifier) {
181 return NULL;
182 }
183
184 DWC_CIRCLEQ_INSERT_TAIL(&manager->notifiers, notifier, list_entry);
185
186 DWC_INFO("Notifier %p registered", object);
187 dump_manager();
188
189 return notifier;
190 }
191
192 void dwc_unregister_notifier(dwc_notifier_t *notifier)
193 {
194 DWC_ASSERT(manager, "Notification manager not found");
195
196 if (!DWC_CIRCLEQ_EMPTY(&notifier->observers)) {
197 observer_t *o;
198
199 DWC_ERROR("Notifier %p has active observers when removing\n", notifier->object);
200 DWC_CIRCLEQ_FOREACH(o, &notifier->observers, list_entry) {
201 DWC_DEBUGC(" %p watching %s\n", o->observer, o->notification);
202 }
203
204 DWC_ASSERT(DWC_CIRCLEQ_EMPTY(&notifier->observers),
205 "Notifier %p has active observers when removing", notifier);
206 }
207
208 DWC_CIRCLEQ_REMOVE_INIT(&manager->notifiers, notifier, list_entry);
209 free_notifier(notifier);
210
211 DWC_INFO("Notifier unregistered");
212 dump_manager();
213 }
214
215 /* Add an observer to observe the notifier for a particular state, event, or notification. */
216 int dwc_add_observer(void *observer, void *object, char *notification,
217 dwc_notifier_callback_t callback, void *data)
218 {
219 notifier_t *notifier = find_notifier(object);
220 observer_t *new_observer;
221
222 if (!notifier) {
223 DWC_ERROR("Notifier %p is not found when adding observer\n", object);
224 return -DWC_E_INVALID;
225 }
226
227 new_observer = alloc_observer(notifier->mem_ctx, observer, notification, callback, data);
228 if (!new_observer) {
229 return -DWC_E_NO_MEMORY;
230 }
231
232 DWC_CIRCLEQ_INSERT_TAIL(&notifier->observers, new_observer, list_entry);
233
234 DWC_INFO("Added observer %p to notifier %p observing notification %s, callback=%p, data=%p",
235 observer, object, notification, callback, data);
236
237 dump_manager();
238 return 0;
239 }
240
241 int dwc_remove_observer(void *observer)
242 {
243 notifier_t *n;
244
245 DWC_ASSERT(manager, "Notification manager not found");
246
247 DWC_CIRCLEQ_FOREACH(n, &manager->notifiers, list_entry) {
248 observer_t *o;
249 observer_t *o2;
250
251 DWC_CIRCLEQ_FOREACH_SAFE(o, o2, &n->observers, list_entry) {
252 if (o->observer == observer) {
253 DWC_CIRCLEQ_REMOVE_INIT(&n->observers, o, list_entry);
254 DWC_INFO("Removing observer %p from notifier %p watching notification %s:",
255 o->observer, n->object, o->notification);
256 free_observer(n->mem_ctx, o);
257 }
258 }
259 }
260
261 dump_manager();
262 return 0;
263 }
264
265 typedef struct callback_data {
266 void *mem_ctx;
267 dwc_notifier_callback_t cb;
268 void *observer;
269 void *data;
270 void *object;
271 char *notification;
272 void *notification_data;
273 } cb_data_t;
274
275 static void cb_task(void *data)
276 {
277 cb_data_t *cb = (cb_data_t *)data;
278
279 cb->cb(cb->object, cb->notification, cb->observer, cb->notification_data, cb->data);
280 dwc_free(cb->mem_ctx, cb);
281 }
282
283 void dwc_notify(dwc_notifier_t *notifier, char *notification, void *notification_data)
284 {
285 observer_t *o;
286
287 DWC_ASSERT(manager, "Notification manager not found");
288
289 DWC_CIRCLEQ_FOREACH(o, &notifier->observers, list_entry) {
290 int len = DWC_STRLEN(notification);
291
292 if (DWC_STRLEN(o->notification) != len) {
293 continue;
294 }
295
296 if (DWC_STRNCMP(o->notification, notification, len) == 0) {
297 cb_data_t *cb_data = dwc_alloc(notifier->mem_ctx, sizeof(cb_data_t));
298
299 if (!cb_data) {
300 DWC_ERROR("Failed to allocate callback data\n");
301 return;
302 }
303
304 cb_data->mem_ctx = notifier->mem_ctx;
305 cb_data->cb = o->callback;
306 cb_data->observer = o->observer;
307 cb_data->data = o->data;
308 cb_data->object = notifier->object;
309 cb_data->notification = notification;
310 cb_data->notification_data = notification_data;
311 DWC_DEBUGC("Observer found %p for notification %s\n", o->observer, notification);
312 DWC_WORKQ_SCHEDULE(manager->wq, cb_task, cb_data,
313 "Notify callback from %p for Notification %s, to observer %p",
314 cb_data->object, notification, cb_data->observer);
315 }
316 }
317 }
318
319 #endif /* DWC_NOTIFYLIB */