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, void *data,
125 			     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