blob: dbea44df71f79e22977b1303f6c645c700c64f6f [file] [log] [blame]
/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include <errno.h>
#include <stdlib.h>
#include "cras_alert.h"
#include "utlist.h"
/* A list of callbacks for an alert */
struct cras_alert_cb_list {
cras_alert_cb callback;
void *arg;
struct cras_alert_cb_list *prev, *next;
};
struct cras_alert {
int pending;
cras_alert_prepare prepare;
struct cras_alert_cb_list *callbacks;
struct cras_alert *prev, *next;
};
/* A list of all alerts in the system */
static struct cras_alert *all_alerts;
/* If there is any alert pending. */
static int has_alert_pending;
struct cras_alert *cras_alert_create(cras_alert_prepare prepare)
{
struct cras_alert *alert;
alert = calloc(1, sizeof(*alert));
if (!alert)
return NULL;
alert->prepare = prepare;
DL_APPEND(all_alerts, alert);
return alert;
}
int cras_alert_add_callback(struct cras_alert *alert, cras_alert_cb cb,
void *arg)
{
struct cras_alert_cb_list *alert_cb;
if (cb == NULL)
return -EINVAL;
DL_FOREACH(alert->callbacks, alert_cb)
if (alert_cb->callback == cb && alert_cb->arg == arg)
return -EEXIST;
alert_cb = calloc(1, sizeof(*alert_cb));
if (alert_cb == NULL)
return -ENOMEM;
alert_cb->callback = cb;
alert_cb->arg = arg;
DL_APPEND(alert->callbacks, alert_cb);
return 0;
}
int cras_alert_rm_callback(struct cras_alert *alert, cras_alert_cb cb,
void *arg)
{
struct cras_alert_cb_list *alert_cb;
DL_FOREACH(alert->callbacks, alert_cb)
if (alert_cb->callback == cb && alert_cb->arg == arg) {
DL_DELETE(alert->callbacks, alert_cb);
free(alert_cb);
return 0;
}
return -ENOENT;
}
/* Checks if the alert is pending, and invoke the prepare function and callbacks
* if so. */
static void cras_alert_process(struct cras_alert *alert)
{
struct cras_alert_cb_list *cb;
if (alert->pending) {
alert->pending = 0;
if (alert->prepare)
alert->prepare(alert);
DL_FOREACH(alert->callbacks, cb)
cb->callback(cb->arg);
}
}
void cras_alert_pending(struct cras_alert *alert)
{
alert->pending = 1;
has_alert_pending = 1;
}
void cras_alert_process_all_pending_alerts()
{
struct cras_alert *alert;
while (has_alert_pending) {
has_alert_pending = 0;
DL_FOREACH(all_alerts, alert)
cras_alert_process(alert);
}
}
void cras_alert_destroy(struct cras_alert *alert)
{
struct cras_alert_cb_list *cb;
if (!alert)
return;
DL_FOREACH(alert->callbacks, cb) {
DL_DELETE(alert->callbacks, cb);
free(cb);
}
alert->callbacks = NULL;
DL_DELETE(all_alerts, alert);
free(alert);
}
void cras_alert_destroy_all()
{
struct cras_alert *alert;
DL_FOREACH(all_alerts, alert)
cras_alert_destroy(alert);
}