USB: fix Coding Style.
[firefly-linux-kernel-4.4.55.git] / drivers / usb / dwc_otg_310 / 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;
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_DEBUG("    %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_DEBUG("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 */