1 /* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
2 * Use of this source code is governed by a BSD-style license that can be
3 * found in the LICENSE file.
4 */
5
6 #include <errno.h>
7 #include <stddef.h>
8 #include <stdlib.h>
9 #include <string.h>
10
11 #include "cras_alert.h"
12 #include "utlist.h"
13
14 /* A list of callbacks for an alert */
15 struct cras_alert_cb_list {
16 cras_alert_cb callback;
17 void *arg;
18 struct cras_alert_cb_list *prev, *next;
19 };
20
21 /* A list of data args to callbacks. Variable-length structure. */
22 struct cras_alert_data {
23 struct cras_alert_data *prev, *next;
24 /* This field must be the last in this structure. */
25 char buf[];
26 };
27
28 struct cras_alert {
29 int pending;
30 unsigned int flags;
31 cras_alert_prepare prepare;
32 struct cras_alert_cb_list *callbacks;
33 struct cras_alert_data *data;
34 struct cras_alert *prev, *next;
35 };
36
37 /* A list of all alerts in the system */
38 static struct cras_alert *all_alerts;
39 /* If there is any alert pending. */
40 static int has_alert_pending;
41
cras_alert_create(cras_alert_prepare prepare,unsigned int flags)42 struct cras_alert *cras_alert_create(cras_alert_prepare prepare,
43 unsigned int flags)
44 {
45 struct cras_alert *alert;
46 alert = calloc(1, sizeof(*alert));
47 if (!alert)
48 return NULL;
49 alert->prepare = prepare;
50 alert->flags = flags;
51 DL_APPEND(all_alerts, alert);
52 return alert;
53 }
54
cras_alert_add_callback(struct cras_alert * alert,cras_alert_cb cb,void * arg)55 int cras_alert_add_callback(struct cras_alert *alert, cras_alert_cb cb,
56 void *arg)
57 {
58 struct cras_alert_cb_list *alert_cb;
59
60 if (cb == NULL)
61 return -EINVAL;
62
63 DL_FOREACH(alert->callbacks, alert_cb)
64 if (alert_cb->callback == cb && alert_cb->arg == arg)
65 return -EEXIST;
66
67 alert_cb = calloc(1, sizeof(*alert_cb));
68 if (alert_cb == NULL)
69 return -ENOMEM;
70 alert_cb->callback = cb;
71 alert_cb->arg = arg;
72 DL_APPEND(alert->callbacks, alert_cb);
73 return 0;
74 }
75
cras_alert_rm_callback(struct cras_alert * alert,cras_alert_cb cb,void * arg)76 int cras_alert_rm_callback(struct cras_alert *alert, cras_alert_cb cb,
77 void *arg)
78 {
79 struct cras_alert_cb_list *alert_cb;
80
81 DL_FOREACH(alert->callbacks, alert_cb)
82 if (alert_cb->callback == cb && alert_cb->arg == arg) {
83 DL_DELETE(alert->callbacks, alert_cb);
84 free(alert_cb);
85 return 0;
86 }
87 return -ENOENT;
88 }
89
90 /* Checks if the alert is pending, and invoke the prepare function and callbacks
91 * if so. */
cras_alert_process(struct cras_alert * alert)92 static void cras_alert_process(struct cras_alert *alert)
93 {
94 struct cras_alert_cb_list *cb;
95 struct cras_alert_data *data;
96
97 if (!alert->pending)
98 return;
99
100 alert->pending = 0;
101 if (alert->prepare)
102 alert->prepare(alert);
103
104 if (!alert->data) {
105 DL_FOREACH(alert->callbacks, cb)
106 cb->callback(cb->arg, NULL);
107 }
108
109 /* Have data arguments, pass each to the callbacks. */
110 DL_FOREACH(alert->data, data) {
111 DL_FOREACH(alert->callbacks, cb)
112 cb->callback(cb->arg, (void *)data->buf);
113 DL_DELETE(alert->data, data);
114 free(data);
115 }
116 }
117
cras_alert_pending(struct cras_alert * alert)118 void cras_alert_pending(struct cras_alert *alert)
119 {
120 alert->pending = 1;
121 has_alert_pending = 1;
122 }
123
cras_alert_pending_data(struct cras_alert * alert,void * data,size_t data_size)124 void cras_alert_pending_data(struct cras_alert *alert,
125 void *data, size_t data_size)
126 {
127 struct cras_alert_data *d;
128
129 alert->pending = 1;
130 has_alert_pending = 1;
131 d = calloc(1, offsetof(struct cras_alert_data, buf) + data_size);
132 memcpy(d->buf, data, data_size);
133
134 if (!(alert->flags & CRAS_ALERT_FLAG_KEEP_ALL_DATA) && alert->data) {
135 /* There will never be more than one item in the list. */
136 free(alert->data);
137 alert->data = NULL;
138 }
139
140 /* Even when there is only one item, it is important to use DL_APPEND
141 * here so that d's next and prev pointers are setup correctly. */
142 DL_APPEND(alert->data, d);
143 }
144
cras_alert_process_all_pending_alerts()145 void cras_alert_process_all_pending_alerts()
146 {
147 struct cras_alert *alert;
148
149 while (has_alert_pending) {
150 has_alert_pending = 0;
151 DL_FOREACH(all_alerts, alert)
152 cras_alert_process(alert);
153 }
154 }
155
cras_alert_destroy(struct cras_alert * alert)156 void cras_alert_destroy(struct cras_alert *alert)
157 {
158 struct cras_alert_cb_list *cb;
159 struct cras_alert_data *data;
160
161 if (!alert)
162 return;
163
164 DL_FOREACH(alert->callbacks, cb) {
165 DL_DELETE(alert->callbacks, cb);
166 free(cb);
167 }
168
169 DL_FOREACH(alert->data, data) {
170 DL_DELETE(alert->data, data);
171 free(data);
172 }
173
174 alert->callbacks = NULL;
175 DL_DELETE(all_alerts, alert);
176 free(alert);
177 }
178
cras_alert_destroy_all()179 void cras_alert_destroy_all()
180 {
181 struct cras_alert *alert;
182 DL_FOREACH(all_alerts, alert)
183 cras_alert_destroy(alert);
184 }
185