Home | History | Annotate | Download | only in server
      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