Home | History | Annotate | Download | only in tests
      1 /* Copyright 2016 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 <getopt.h>
      8 #include <inttypes.h>
      9 #include <stdbool.h>
     10 #include <stdio.h>
     11 #include <stdint.h>
     12 #include <string.h>
     13 #include <syslog.h>
     14 #include <sys/select.h>
     15 #include <unistd.h>
     16 
     17 #include "cras_client.h"
     18 #include "cras_types.h"
     19 #include "cras_util.h"
     20 #include "cras_version.h"
     21 
     22 static void output_volume_changed(void *context, int32_t volume)
     23 {
     24 	printf("output volume: %d/100\n", volume);
     25 }
     26 
     27 static void output_mute_changed(void *context, int muted,
     28 				int user_muted, int mute_locked)
     29 {
     30 	printf("output mute: muted: %d, user_muted: %d, mute_locked: %d\n",
     31 	       muted, user_muted, mute_locked);
     32 }
     33 
     34 static void capture_gain_changed(void *context, int32_t gain)
     35 {
     36 	printf("capture gain: %d\n", gain);
     37 }
     38 
     39 static void capture_mute_changed(void *context, int muted, int mute_locked)
     40 {
     41 	printf("capture mute: muted: %d, mute_locked: %d\n",
     42 	       muted, mute_locked);
     43 }
     44 
     45 static void nodes_changed(void *context)
     46 {
     47 	printf("nodes changed\n");
     48 }
     49 
     50 static const char *string_for_direction(enum CRAS_STREAM_DIRECTION dir)
     51 {
     52 	switch(dir) {
     53 		case CRAS_STREAM_OUTPUT:
     54 			return "output";
     55 		case CRAS_STREAM_INPUT:
     56 			return "input";
     57 		case CRAS_STREAM_POST_MIX_PRE_DSP:
     58 			return "post_mix_pre_dsp";
     59 		default:
     60 			break;
     61 	}
     62 
     63 	return "undefined";
     64 }
     65 
     66 size_t node_array_index_of_node_id(struct cras_ionode_info *nodes,
     67 				   size_t num_nodes,
     68 				   cras_node_id_t node_id)
     69 {
     70 	uint32_t dev_index = dev_index_of(node_id);
     71 	uint32_t node_index = node_index_of(node_id);
     72 	size_t i;
     73 
     74 	for (i = 0; i < num_nodes; i++) {
     75 		if (nodes[i].iodev_idx == dev_index &&
     76 		    nodes[i].ionode_idx == node_index)
     77 			return i;
     78 	}
     79 	return CRAS_MAX_IONODES;
     80 }
     81 
     82 const char *node_name_for_node_id(struct cras_client *client,
     83 				  enum CRAS_STREAM_DIRECTION dir,
     84 				  cras_node_id_t node_id)
     85 {
     86 	struct cras_ionode_info nodes[CRAS_MAX_IONODES];
     87 	struct cras_iodev_info devs[CRAS_MAX_IODEVS];
     88 	size_t num_devs = CRAS_MAX_IODEVS;
     89 	size_t num_nodes = CRAS_MAX_IONODES;
     90 	uint32_t iodev_idx = dev_index_of(node_id);
     91 	size_t node_index;
     92 	char buf[1024];
     93 	int rc;
     94 
     95 	if (node_id == 0) {
     96 		return strdup("none");
     97 	} else if (iodev_idx <= 2) {
     98 		return strdup("fallback");
     99 	} else if (dir == CRAS_STREAM_POST_MIX_PRE_DSP) {
    100 		snprintf(buf, sizeof(buf), "%s node: %" PRIu64 "\n",
    101 			 string_for_direction(dir), node_id);
    102 		return strdup(buf);
    103 	} else if (dir == CRAS_STREAM_OUTPUT) {
    104 		rc = cras_client_get_output_devices(client, devs, nodes,
    105 					       &num_devs, &num_nodes);
    106 	} else if (dir == CRAS_STREAM_INPUT) {
    107 		rc = cras_client_get_input_devices(client, devs, nodes,
    108 					      &num_devs, &num_nodes);
    109 	} else {
    110 		return strdup("unknown");
    111 	}
    112 
    113 	if (rc != 0) {
    114 		syslog(LOG_ERR, "Couldn't get output devices: %s\n",
    115 		       strerror(-rc));
    116 		snprintf(buf, sizeof(buf), "%u:%u",
    117 			 iodev_idx, node_index_of(node_id));
    118 		return strdup(buf);
    119 	}
    120 	node_index = node_array_index_of_node_id(nodes, num_nodes, node_id);
    121 	if (node_index >= num_nodes)
    122 		snprintf(buf, sizeof(buf),
    123 			 "unknown: %zu >= %zu", node_index, num_nodes);
    124 	else
    125 		snprintf(buf, sizeof(buf), "%u:%u: %s",
    126 			 nodes[node_index].iodev_idx,
    127 			 nodes[node_index].ionode_idx,
    128 			 nodes[node_index].name);
    129 	return strdup(buf);
    130 }
    131 
    132 static void active_node_changed(void *context,
    133 				enum CRAS_STREAM_DIRECTION dir,
    134 				cras_node_id_t node_id)
    135 {
    136 	struct cras_client *client = (struct cras_client *)context;
    137 	const char *node_name = node_name_for_node_id(client, dir, node_id);
    138 	printf("active node (%s): %s\n", string_for_direction(dir), node_name);
    139 	free((void *)node_name);
    140 }
    141 
    142 static void output_node_volume_changed(void *context,
    143 				       cras_node_id_t node_id, int32_t volume)
    144 {
    145 	struct cras_client *client = (struct cras_client *)context;
    146 	const char *node_name =
    147 		node_name_for_node_id(client, CRAS_STREAM_OUTPUT, node_id);
    148 	printf("output node '%s' volume: %d\n", node_name, volume);
    149 	free((void *)node_name);
    150 }
    151 
    152 static void node_left_right_swapped_changed(void *context,
    153 					    cras_node_id_t node_id, int swapped)
    154 {
    155 	struct cras_client *client = (struct cras_client *)context;
    156 	const char *node_name =
    157 		node_name_for_node_id(client, CRAS_STREAM_OUTPUT, node_id);
    158 	printf("output node '%s' left-right swapped: %d\n", node_name, swapped);
    159 	free((void *)node_name);
    160 }
    161 
    162 static void input_node_gain_changed(void *context,
    163 				    cras_node_id_t node_id, int32_t gain)
    164 {
    165 	struct cras_client *client = (struct cras_client *)context;
    166 	const char *node_name =
    167 		node_name_for_node_id(client, CRAS_STREAM_INPUT, node_id);
    168 	printf("input node '%s' gain: %d\n", node_name, gain);
    169 	free((void *)node_name);
    170 }
    171 
    172 static void num_active_streams_changed(void *context,
    173 				       enum CRAS_STREAM_DIRECTION dir,
    174 				       uint32_t num_active_streams)
    175 {
    176 	printf("num active %s streams: %u\n",
    177 	       string_for_direction(dir), num_active_streams);
    178 }
    179 
    180 static void server_connection_callback(struct cras_client *client,
    181 				       cras_connection_status_t status,
    182 				       void *user_arg)
    183 {
    184 	const char *status_str = "undefined";
    185 	switch (status) {
    186 		case CRAS_CONN_STATUS_FAILED:
    187 			status_str = "error";
    188 			break;
    189 		case CRAS_CONN_STATUS_DISCONNECTED:
    190 			status_str = "disconnected";
    191 			break;
    192 		case CRAS_CONN_STATUS_CONNECTED:
    193 			status_str = "connected";
    194 			break;
    195 	}
    196 	printf("server %s\n", status_str);
    197 }
    198 
    199 static void print_usage(const char *command) {
    200 	fprintf(stderr,
    201 		"%s [options]\n"
    202 		"  Where [options] are:\n"
    203 		"    --sync|-s  - Use the synchronous connection functions.\n"
    204 		"    --log-level|-l <n>  - Set the syslog level (7 == "
    205 			"LOG_DEBUG).\n",
    206 		command);
    207 }
    208 
    209 int main(int argc, char **argv)
    210 {
    211 	struct cras_client *client;
    212 	int rc;
    213 	int option_character;
    214 	bool synchronous = false;
    215 	int log_level = LOG_WARNING;
    216 	static struct option long_options[] = {
    217 		{"sync", no_argument, NULL, 's'},
    218 		{"log-level", required_argument, NULL, 'l'},
    219 		{NULL, 0, NULL, 0},
    220 	};
    221 
    222 	while(true) {
    223 		int option_index = 0;
    224 
    225 		option_character = getopt_long(argc, argv, "sl:",
    226 					       long_options, &option_index);
    227 		if (option_character == -1)
    228 			break;
    229 		switch (option_character) {
    230 		case 's':
    231 			synchronous = !synchronous;
    232 			break;
    233 		case 'l':
    234 			log_level = atoi(optarg);
    235 			if (log_level < 0)
    236 				log_level = LOG_WARNING;
    237 			else if (log_level > LOG_DEBUG)
    238 				log_level = LOG_DEBUG;
    239 			break;
    240 		default:
    241 			print_usage(argv[0]);
    242 			return 1;
    243 		}
    244 	}
    245 
    246 	if (optind < argc) {
    247 		fprintf(stderr, "%s: Extra arguments.\n", argv[0]);
    248 		print_usage(argv[0]);
    249 		return 1;
    250 	}
    251 
    252 	openlog("cras_monitor", LOG_PERROR, LOG_USER);
    253 	setlogmask(LOG_UPTO(log_level));
    254 
    255 	rc = cras_client_create(&client);
    256 	if (rc < 0) {
    257 		syslog(LOG_ERR, "Couldn't create client.");
    258 		return rc;
    259 	}
    260 
    261 	cras_client_set_connection_status_cb(
    262 			client, server_connection_callback, NULL);
    263 
    264 	if (synchronous) {
    265 		rc = cras_client_connect(client);
    266 		if (rc != 0) {
    267 			syslog(LOG_ERR, "Could not connect to server.");
    268 			return -rc;
    269 		}
    270 	}
    271 
    272 	cras_client_set_output_volume_changed_callback(
    273 			client, output_volume_changed);
    274 	cras_client_set_output_mute_changed_callback(
    275 			client, output_mute_changed);
    276 	cras_client_set_capture_gain_changed_callback(
    277 			client, capture_gain_changed);
    278 	cras_client_set_capture_mute_changed_callback(
    279 			client, capture_mute_changed);
    280 	cras_client_set_nodes_changed_callback(
    281 			client, nodes_changed);
    282 	cras_client_set_active_node_changed_callback(
    283 			client, active_node_changed);
    284 	cras_client_set_output_node_volume_changed_callback(
    285 			client, output_node_volume_changed);
    286 	cras_client_set_node_left_right_swapped_changed_callback(
    287 			client, node_left_right_swapped_changed);
    288 	cras_client_set_input_node_gain_changed_callback(
    289 			client, input_node_gain_changed);
    290 	cras_client_set_num_active_streams_changed_callback(
    291 			client, num_active_streams_changed);
    292 	cras_client_set_state_change_callback_context(client, client);
    293 
    294 	rc = cras_client_run_thread(client);
    295 	if (rc != 0) {
    296 		syslog(LOG_ERR, "Could not start thread.");
    297 		return -rc;
    298 	}
    299 
    300 	if (!synchronous) {
    301 		rc = cras_client_connect_async(client);
    302 		if (rc) {
    303 			syslog(LOG_ERR, "Couldn't connect to server.\n");
    304 			goto destroy_exit;
    305 		}
    306 	}
    307 
    308 	while(1) {
    309 		int rc;
    310 		char c;
    311 		rc = read(STDIN_FILENO, &c, 1);
    312 		if (rc < 0 || c == 'q')
    313 			return 0;
    314 	}
    315 
    316 destroy_exit:
    317 	cras_client_destroy(client);
    318 	return 0;
    319 }
    320