text: /* Copyright 2016 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 "cras_observer.h"
  
  #include "cras_alert.h"
  #include "cras_iodev_list.h"
  #include "cras_types.h"
  #include "utlist.h"
  
  struct cras_observer_client {
  	struct cras_observer_ops ops;
  	void *context;
  	struct cras_observer_client *next, *prev;
  };
  
  struct cras_observer_alerts {
  	struct cras_alert *output_volume;
  	struct cras_alert *output_mute;
  	struct cras_alert *capture_gain;
  	struct cras_alert *capture_mute;
  	struct cras_alert *nodes;
  	struct cras_alert *active_node;
  	struct cras_alert *output_node_volume;
  	struct cras_alert *node_left_right_swapped;
  	struct cras_alert *input_node_gain;
  	struct cras_alert *suspend_changed;
  	struct cras_alert *hotword_triggered;
  	/* If all events for active streams went through a single alert then
           * we might miss some because the alert code does not send every
           * alert message. To ensure that the event sent contains the correct
           * number of active streams per direction, make the alerts
           * per-direciton. */
  	struct cras_alert *num_active_streams[CRAS_NUM_DIRECTIONS];
  	struct cras_alert *non_empty_audio_state_changed;
  	struct cras_alert *bt_battery_changed;
  	struct cras_alert *num_input_streams_with_permission;
  };
  
  struct cras_observer_server {
  	struct cras_observer_alerts alerts;
  	struct cras_observer_client *clients;
  };
  
  struct cras_observer_alert_data_volume {
  	int32_t volume;
  };
  
  struct cras_observer_alert_data_mute {
  	int muted;
  	int user_muted;
  	int mute_locked;
  };
  
  struct cras_observer_alert_data_active_node {
  	enum CRAS_STREAM_DIRECTION direction;
  	cras_node_id_t node_id;
  };
  
  struct cras_observer_alert_data_node_volume {
  	cras_node_id_t node_id;
  	int32_t volume;
  };
  
  struct cras_observer_alert_data_node_lr_swapped {
  	cras_node_id_t node_id;
  	int swapped;
  };
  
  struct cras_observer_alert_data_suspend {
  	int suspended;
  };
  
  struct cras_observer_alert_data_streams {
  	enum CRAS_STREAM_DIRECTION direction;
  	uint32_t num_active_streams;
  };
  
  struct cras_observer_alert_data_input_streams {
  	uint32_t num_input_streams[CRAS_NUM_CLIENT_TYPE];
  };
  
  struct cras_observer_alert_data_hotword_triggered {
  	int64_t tv_sec;
  	int64_t tv_nsec;
  };
  
  struct cras_observer_non_empty_audio_state {
  	int non_empty;
  };
  
  struct cras_observer_alert_data_bt_battery_changed {
  	const char *address;
  	uint32_t level;
  };
  
  /* Global observer instance. */
  static struct cras_observer_server *g_observer;
  
  /* Empty observer ops. */
  static struct cras_observer_ops g_empty_ops;
  
  /*
   * Alert handlers for delayed callbacks.
   */
  
  static void output_volume_alert(void *arg, void *data)
  {
  	struct cras_observer_client *client;
  	struct cras_observer_alert_data_volume *volume_data =
  		(struct cras_observer_alert_data_volume *)data;
  
  	DL_FOREACH (g_observer->clients, client) {
  		if (client->ops.output_volume_changed)
  			client->ops.output_volume_changed(client->context,
  							  volume_data->volume);
  	}
  }
  
  static void output_mute_alert(void *arg, void *data)
  {
  	struct cras_observer_client *client;
  	struct cras_observer_alert_data_mute *mute_data =
  		(struct cras_observer_alert_data_mute *)data;
  
  	DL_FOREACH (g_observer->clients, client) {
  		if (client->ops.output_mute_changed)
  			client->ops.output_mute_changed(client->context,
  							mute_data->muted,
  							mute_data->user_muted,
  							mute_data->mute_locked);
  	}
  }
  
  static void capture_gain_alert(void *arg, void *data)
  {
  	struct cras_observer_client *client;
  	struct cras_observer_alert_data_volume *volume_data =
  		(struct cras_observer_alert_data_volume *)data;
  
  	DL_FOREACH (g_observer->clients, client) {
  		if (client->ops.capture_gain_changed)
  			client->ops.capture_gain_changed(client->context,
  							 volume_data->volume);
  	}
  }
  
  static void capture_mute_alert(void *arg, void *data)
  {
  	struct cras_observer_client *client;
  	struct cras_observer_alert_data_mute *mute_data =
  		(struct cras_observer_alert_data_mute *)data;
  
  	DL_FOREACH (g_observer->clients, client) {
  		if (client->ops.capture_mute_changed)
  			client->ops.capture_mute_changed(
  				client->context, mute_data->muted,
  				mute_data->mute_locked);
  	}
  }
  
  static void nodes_prepare(struct cras_alert *alert)
  {
  	cras_iodev_list_update_device_list();
  }
  
  static void nodes_alert(void *arg, void *data)
  {
  	struct cras_observer_client *client;
  
  	DL_FOREACH (g_observer->clients, client) {
  		if (client->ops.nodes_changed)
  			client->ops.nodes_changed(client->context);
  	}
  }
  
  static void active_node_alert(void *arg, void *data)
  {
  	struct cras_observer_client *client;
  	struct cras_observer_alert_data_active_node *node_data =
  		(struct cras_observer_alert_data_active_node *)data;
  
  	DL_FOREACH (g_observer->clients, client) {
  		if (client->ops.active_node_changed)
  			client->ops.active_node_changed(client->context,
  							node_data->direction,
  							node_data->node_id);
  	}
  }
  
  static void output_node_volume_alert(void *arg, void *data)
  {
  	struct cras_observer_client *client;
  	struct cras_observer_alert_data_node_volume *node_data =
  		(struct cras_observer_alert_data_node_volume *)data;
  
  	DL_FOREACH (g_observer->clients, client) {
  		if (client->ops.output_node_volume_changed)
  			client->ops.output_node_volume_changed(
  				client->context, node_data->node_id,
  				node_data->volume);
  	}
  }
  
  static void node_left_right_swapped_alert(void *arg, void *data)
  {
  	struct cras_observer_client *client;
  	struct cras_observer_alert_data_node_lr_swapped *node_data =
  		(struct cras_observer_alert_data_node_lr_swapped *)data;
  
  	DL_FOREACH (g_observer->clients, client) {
  		if (client->ops.node_left_right_swapped_changed)
  			client->ops.node_left_right_swapped_changed(
  				client->context, node_data->node_id,
  				node_data->swapped);
  	}
  }
  
  static void input_node_gain_alert(void *arg, void *data)
  {
  	struct cras_observer_client *client;
  	struct cras_observer_alert_data_node_volume *node_data =
  		(struct cras_observer_alert_data_node_volume *)data;
  
  	DL_FOREACH (g_observer->clients, client) {
  		if (client->ops.input_node_gain_changed)
  			client->ops.input_node_gain_changed(client->context,
  							    node_data->node_id,
  							    node_data->volume);
  	}
  }
  
  static void suspend_changed_alert(void *arg, void *data)
  {
  	struct cras_observer_client *client;
  	struct cras_observer_alert_data_suspend *suspend_data =
  		(struct cras_observer_alert_data_suspend *)data;
  
  	DL_FOREACH (g_observer->clients, client) {
  		if (client->ops.suspend_changed)
  			client->ops.suspend_changed(client->context,
  						    suspend_data->suspended);
  	}
  }
  
  static void num_active_streams_alert(void *arg, void *data)
  {
  	struct cras_observer_client *client;
  	struct cras_observer_alert_data_streams *streams_data =
  		(struct cras_observer_alert_data_streams *)data;
  
  	DL_FOREACH (g_observer->clients, client) {
  		if (client->ops.num_active_streams_changed)
  			client->ops.num_active_streams_changed(
  				client->context, streams_data->direction,
  				streams_data->num_active_streams);
  	}
  }
  
  static void num_input_streams_with_permission_alert(void *arg, void *data)
  {
  	struct cras_observer_client *client;
  	struct cras_observer_alert_data_input_streams *input_streams_data =
  		(struct cras_observer_alert_data_input_streams *)data;
  
  	DL_FOREACH (g_observer->clients, client) {
  		if (client->ops.num_input_streams_with_permission_changed)
  			client->ops.num_input_streams_with_permission_changed(
  				client->context,
  				input_streams_data->num_input_streams);
  	}
  }
  
  static void hotword_triggered_alert(void *arg, void *data)
  {
  	struct cras_observer_client *client;
  	struct cras_observer_alert_data_hotword_triggered *triggered_data =
  		(struct cras_observer_alert_data_hotword_triggered *)data;
  
  	DL_FOREACH (g_observer->clients, client) {
  		if (client->ops.hotword_triggered)
  			client->ops.hotword_triggered(client->context,
  						      triggered_data->tv_sec,
  						      triggered_data->tv_nsec);
  	}
  }
  
  static void non_empty_audio_state_changed_alert(void *arg, void *data)
  {
  	struct cras_observer_client *client;
  	struct cras_observer_non_empty_audio_state *non_empty_audio_data =
  		(struct cras_observer_non_empty_audio_state *)data;
  
  	DL_FOREACH (g_observer->clients, client) {
  		if (client->ops.non_empty_audio_state_changed) {
  			client->ops.non_empty_audio_state_changed(
  				client->context,
  				non_empty_audio_data->non_empty);
  		}
  	}
  }
  
  static void bt_battery_changed_alert(void *arg, void *data)
  {
  	struct cras_observer_client *client;
  	struct cras_observer_alert_data_bt_battery_changed *triggered_data =
  		(struct cras_observer_alert_data_bt_battery_changed *)data;
  
  	DL_FOREACH (g_observer->clients, client) {
  		if (client->ops.bt_battery_changed)
  			client->ops.bt_battery_changed(client->context,
  						       triggered_data->address,
  						       triggered_data->level);
  	}
  }
  
  static int cras_observer_server_set_alert(struct cras_alert **alert,
  					  cras_alert_cb cb,
  					  cras_alert_prepare prepare,
  					  unsigned int flags)
  {
  	*alert = cras_alert_create(prepare, flags);
  	if (!*alert)
  		return -ENOMEM;
  	return cras_alert_add_callback(*alert, cb, NULL);
  }
  
  #define CRAS_OBSERVER_SET_ALERT(alert, prepare, flags)                         \
  	do {                                                                   \
  		rc = cras_observer_server_set_alert(&g_observer->alerts.alert, \
  						    alert##_alert, prepare,    \
  						    flags);                    \
  		if (rc)                                                        \
  			goto error;                                            \
  	} while (0)
  
  #define CRAS_OBSERVER_SET_ALERT_WITH_DIRECTION(alert, direction)               \
  	do {                                                                   \
  		rc = cras_observer_server_set_alert(                           \
  			&g_observer->alerts.alert[direction], alert##_alert,   \
  			NULL, 0);                                              \
  		if (rc)                                                        \
  			goto error;                                            \
  	} while (0)
  
  /*
   * Public interface
   */
  
  int cras_observer_server_init()
  {
  	int rc;
  
  	memset(&g_empty_ops, 0, sizeof(g_empty_ops));
  	g_observer = (struct cras_observer_server *)calloc(
  		1, sizeof(struct cras_observer_server));
  	if (!g_observer)
  		return -ENOMEM;
  
  	CRAS_OBSERVER_SET_ALERT(output_volume, NULL, 0);
  	CRAS_OBSERVER_SET_ALERT(output_mute, NULL, 0);
  	CRAS_OBSERVER_SET_ALERT(capture_gain, NULL, 0);
  	CRAS_OBSERVER_SET_ALERT(capture_mute, NULL, 0);
  	CRAS_OBSERVER_SET_ALERT(nodes, nodes_prepare, 0);
  	CRAS_OBSERVER_SET_ALERT(active_node, nodes_prepare,
  				CRAS_ALERT_FLAG_KEEP_ALL_DATA);
  	CRAS_OBSERVER_SET_ALERT(output_node_volume, NULL, 0);
  	CRAS_OBSERVER_SET_ALERT(node_left_right_swapped, NULL, 0);
  	CRAS_OBSERVER_SET_ALERT(input_node_gain, NULL, 0);
  	CRAS_OBSERVER_SET_ALERT(suspend_changed, NULL, 0);
  	CRAS_OBSERVER_SET_ALERT(hotword_triggered, NULL, 0);
  	CRAS_OBSERVER_SET_ALERT(non_empty_audio_state_changed, NULL, 0);
  	CRAS_OBSERVER_SET_ALERT(bt_battery_changed, NULL, 0);
  	CRAS_OBSERVER_SET_ALERT(num_input_streams_with_permission, NULL, 0);
  
  	CRAS_OBSERVER_SET_ALERT_WITH_DIRECTION(num_active_streams,
  					       CRAS_STREAM_OUTPUT);
  	CRAS_OBSERVER_SET_ALERT_WITH_DIRECTION(num_active_streams,
  					       CRAS_STREAM_INPUT);
  	CRAS_OBSERVER_SET_ALERT_WITH_DIRECTION(num_active_streams,
  					       CRAS_STREAM_POST_MIX_PRE_DSP);
  
  	return 0;
  
  error:
  	cras_observer_server_free();
  	return rc;
  }
  
  void cras_observer_server_free()
  {
  	if (!g_observer)
  		return;
  	cras_alert_destroy(g_observer->alerts.output_volume);
  	cras_alert_destroy(g_observer->alerts.output_mute);
  	cras_alert_destroy(g_observer->alerts.capture_gain);
  	cras_alert_destroy(g_observer->alerts.capture_mute);
  	cras_alert_destroy(g_observer->alerts.nodes);
  	cras_alert_destroy(g_observer->alerts.active_node);
  	cras_alert_destroy(g_observer->alerts.output_node_volume);
  	cras_alert_destroy(g_observer->alerts.node_left_right_swapped);
  	cras_alert_destroy(g_observer->alerts.input_node_gain);
  	cras_alert_destroy(g_observer->alerts.suspend_changed);
  	cras_alert_destroy(g_observer->alerts.hotword_triggered);
  	cras_alert_destroy(g_observer->alerts.non_empty_audio_state_changed);
  	cras_alert_destroy(g_observer->alerts.bt_battery_changed);
  	cras_alert_destroy(
  		g_observer->alerts.num_input_streams_with_permission);
  	cras_alert_destroy(
  		g_observer->alerts.num_active_streams[CRAS_STREAM_OUTPUT]);
  	cras_alert_destroy(
  		g_observer->alerts.num_active_streams[CRAS_STREAM_INPUT]);
  	cras_alert_destroy(
  		g_observer->alerts
  			.num_active_streams[CRAS_STREAM_POST_MIX_PRE_DSP]);
  	free(g_observer);
  	g_observer = NULL;
  }
  
  int cras_observer_ops_are_empty(const struct cras_observer_ops *ops)
  {
  	return memcmp(ops, &g_empty_ops, sizeof(*ops)) == 0;
  }
  
  void cras_observer_get_ops(const struct cras_observer_client *client,
  			   struct cras_observer_ops *ops)
  {
  	if (!ops)
  		return;
  	if (!client)
  		memset(ops, 0, sizeof(*ops));
  	else
  		memcpy(ops, &client->ops, sizeof(*ops));
  }
  
  void cras_observer_set_ops(struct cras_observer_client *client,
  			   const struct cras_observer_ops *ops)
  {
  	if (!client)
  		return;
  	if (!ops)
  		memset(&client->ops, 0, sizeof(client->ops));
  	else
  		memcpy(&client->ops, ops, sizeof(client->ops));
  }
  
  struct cras_observer_client *
  cras_observer_add(const struct cras_observer_ops *ops, void *context)
  {
  	struct cras_observer_client *client;
  
  	client = (struct cras_observer_client *)calloc(1, sizeof(*client));
  	if (!client)
  		return NULL;
  	client->context = context;
  	DL_APPEND(g_observer->clients, client);
  	cras_observer_set_ops(client, ops);
  	return client;
  }
  
  void cras_observer_remove(struct cras_observer_client *client)
  {
  	if (!client)
  		return;
  	DL_DELETE(g_observer->clients, client);
  	free(client);
  }
  
  /*
   * Public interface for notifiers.
   */
  
  void cras_observer_notify_output_volume(int32_t volume)
  {
  	struct cras_observer_alert_data_volume data;
  
  	data.volume = volume;
  	cras_alert_pending_data(g_observer->alerts.output_volume, &data,
  				sizeof(data));
  }
  
  void cras_observer_notify_output_mute(int muted, int user_muted,
  				      int mute_locked)
  {
  	struct cras_observer_alert_data_mute data;
  
  	data.muted = muted;
  	data.user_muted = user_muted;
  	data.mute_locked = mute_locked;
  	cras_alert_pending_data(g_observer->alerts.output_mute, &data,
  				sizeof(data));
  }
  
  void cras_observer_notify_capture_gain(int32_t gain)
  {
  	struct cras_observer_alert_data_volume data;
  
  	data.volume = gain;
  	cras_alert_pending_data(g_observer->alerts.capture_gain, &data,
  				sizeof(data));
  }
  
  void cras_observer_notify_capture_mute(int muted, int mute_locked)
  {
  	struct cras_observer_alert_data_mute data;
  
  	data.muted = muted;
  	data.user_muted = 0;
  	data.mute_locked = mute_locked;
  	cras_alert_pending_data(g_observer->alerts.capture_mute, &data,
  				sizeof(data));
  }
  
  void cras_observer_notify_nodes(void)
  {
  	cras_alert_pending(g_observer->alerts.nodes);
  }
  
  void cras_observer_notify_active_node(enum CRAS_STREAM_DIRECTION dir,
  				      cras_node_id_t node_id)
  {
  	struct cras_observer_alert_data_active_node data;
  
  	data.direction = dir;
  	data.node_id = node_id;
  	cras_alert_pending_data(g_observer->alerts.active_node, &data,
  				sizeof(data));
  }
  
  void cras_observer_notify_output_node_volume(cras_node_id_t node_id,
  					     int32_t volume)
  {
  	struct cras_observer_alert_data_node_volume data;
  
  	data.node_id = node_id;
  	data.volume = volume;
  	cras_alert_pending_data(g_observer->alerts.output_node_volume, &data,
  				sizeof(data));
  }
  
  void cras_observer_notify_node_left_right_swapped(cras_node_id_t node_id,
  						  int swapped)
  {
  	struct cras_observer_alert_data_node_lr_swapped data;
  
  	data.node_id = node_id;
  	data.swapped = swapped;
  	cras_alert_pending_data(g_observer->alerts.node_left_right_swapped,
  				&data, sizeof(data));
  }
  
  void cras_observer_notify_input_node_gain(cras_node_id_t node_id, int32_t gain)
  {
  	struct cras_observer_alert_data_node_volume data;
  
  	data.node_id = node_id;
  	data.volume = gain;
  	cras_alert_pending_data(g_observer->alerts.input_node_gain, &data,
  				sizeof(data));
  }
  
  void cras_observer_notify_suspend_changed(int suspended)
  {
  	struct cras_observer_alert_data_suspend data;
  
  	data.suspended = suspended;
  	cras_alert_pending_data(g_observer->alerts.suspend_changed, &data,
  				sizeof(data));
  }
  
  void cras_observer_notify_num_active_streams(enum CRAS_STREAM_DIRECTION dir,
  					     uint32_t num_active_streams)
  {
  	struct cras_observer_alert_data_streams data;
  	struct cras_alert *alert;
  
  	data.direction = dir;
  	data.num_active_streams = num_active_streams;
  	alert = g_observer->alerts.num_active_streams[dir];
  	if (!alert)
  		return;
  
  	cras_alert_pending_data(alert, &data, sizeof(data));
  }
  
  void cras_observer_notify_input_streams_with_permission(
  	uint32_t num_input_streams[CRAS_NUM_CLIENT_TYPE])
  {
  	struct cras_observer_alert_data_input_streams data;
  	struct cras_alert *alert;
  
  	memcpy(&data.num_input_streams, num_input_streams,
  	       sizeof(*num_input_streams) * CRAS_NUM_CLIENT_TYPE);
  	alert = g_observer->alerts.num_input_streams_with_permission;
  	if (!alert)
  		return;
  
  	cras_alert_pending_data(alert, &data, sizeof(data));
  }
  
  void cras_observer_notify_hotword_triggered(int64_t tv_sec, int64_t tv_nsec)
  {
  	struct cras_observer_alert_data_hotword_triggered data;
  
  	data.tv_sec = tv_sec;
  	data.tv_nsec = tv_nsec;
  	cras_alert_pending_data(g_observer->alerts.hotword_triggered, &data,
  				sizeof(data));
  }
  
  void cras_observer_notify_non_empty_audio_state_changed(int non_empty)
  {
  	struct cras_observer_non_empty_audio_state data;
  
  	data.non_empty = non_empty;
  
  	cras_alert_pending_data(
  		g_observer->alerts.non_empty_audio_state_changed, &data,
  		sizeof(data));
  }
  
  void cras_observer_notify_bt_battery_changed(const char *address,
  					     uint32_t level)
  {
  	struct cras_observer_alert_data_bt_battery_changed data;
  
  	data.address = address;
  	data.level = level;
  
  	cras_alert_pending_data(g_observer->alerts.bt_battery_changed, &data,
  				sizeof(data));
  }
  