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 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 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 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. */ 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 118 void cras_alert_pending(struct cras_alert *alert) 119 { 120 alert->pending = 1; 121 has_alert_pending = 1; 122 } 123 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 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 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 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