/* 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 <errno.h> #include <getopt.h> #include <inttypes.h> #include <stdbool.h> #include <stdio.h> #include <stdint.h> #include <string.h> #include <syslog.h> #include <sys/select.h> #include <unistd.h> #include "cras_client.h" #include "cras_types.h" #include "cras_util.h" #include "cras_version.h" static void output_volume_changed(void *context, int32_t volume) { printf("output volume: %d/100\n", volume); } static void output_mute_changed(void *context, int muted, int user_muted, int mute_locked) { printf("output mute: muted: %d, user_muted: %d, mute_locked: %d\n", muted, user_muted, mute_locked); } static void capture_gain_changed(void *context, int32_t gain) { printf("capture gain: %d\n", gain); } static void capture_mute_changed(void *context, int muted, int mute_locked) { printf("capture mute: muted: %d, mute_locked: %d\n", muted, mute_locked); } static void nodes_changed(void *context) { printf("nodes changed\n"); } static const char *string_for_direction(enum CRAS_STREAM_DIRECTION dir) { switch(dir) { case CRAS_STREAM_OUTPUT: return "output"; case CRAS_STREAM_INPUT: return "input"; case CRAS_STREAM_POST_MIX_PRE_DSP: return "post_mix_pre_dsp"; default: break; } return "undefined"; } size_t node_array_index_of_node_id(struct cras_ionode_info *nodes, size_t num_nodes, cras_node_id_t node_id) { uint32_t dev_index = dev_index_of(node_id); uint32_t node_index = node_index_of(node_id); size_t i; for (i = 0; i < num_nodes; i++) { if (nodes[i].iodev_idx == dev_index && nodes[i].ionode_idx == node_index) return i; } return CRAS_MAX_IONODES; } const char *node_name_for_node_id(struct cras_client *client, enum CRAS_STREAM_DIRECTION dir, cras_node_id_t node_id) { struct cras_ionode_info nodes[CRAS_MAX_IONODES]; struct cras_iodev_info devs[CRAS_MAX_IODEVS]; size_t num_devs = CRAS_MAX_IODEVS; size_t num_nodes = CRAS_MAX_IONODES; uint32_t iodev_idx = dev_index_of(node_id); size_t node_index; char buf[1024]; int rc; if (node_id == 0) { return strdup("none"); } else if (iodev_idx <= 2) { return strdup("fallback"); } else if (dir == CRAS_STREAM_POST_MIX_PRE_DSP) { snprintf(buf, sizeof(buf), "%s node: %" PRIu64 "\n", string_for_direction(dir), node_id); return strdup(buf); } else if (dir == CRAS_STREAM_OUTPUT) { rc = cras_client_get_output_devices(client, devs, nodes, &num_devs, &num_nodes); } else if (dir == CRAS_STREAM_INPUT) { rc = cras_client_get_input_devices(client, devs, nodes, &num_devs, &num_nodes); } else { return strdup("unknown"); } if (rc != 0) { syslog(LOG_ERR, "Couldn't get output devices: %s\n", strerror(-rc)); snprintf(buf, sizeof(buf), "%u:%u", iodev_idx, node_index_of(node_id)); return strdup(buf); } node_index = node_array_index_of_node_id(nodes, num_nodes, node_id); if (node_index >= num_nodes) snprintf(buf, sizeof(buf), "unknown: %zu >= %zu", node_index, num_nodes); else snprintf(buf, sizeof(buf), "%u:%u: %s", nodes[node_index].iodev_idx, nodes[node_index].ionode_idx, nodes[node_index].name); return strdup(buf); } static void active_node_changed(void *context, enum CRAS_STREAM_DIRECTION dir, cras_node_id_t node_id) { struct cras_client *client = (struct cras_client *)context; const char *node_name = node_name_for_node_id(client, dir, node_id); printf("active node (%s): %s\n", string_for_direction(dir), node_name); free((void *)node_name); } static void output_node_volume_changed(void *context, cras_node_id_t node_id, int32_t volume) { struct cras_client *client = (struct cras_client *)context; const char *node_name = node_name_for_node_id(client, CRAS_STREAM_OUTPUT, node_id); printf("output node '%s' volume: %d\n", node_name, volume); free((void *)node_name); } static void node_left_right_swapped_changed(void *context, cras_node_id_t node_id, int swapped) { struct cras_client *client = (struct cras_client *)context; const char *node_name = node_name_for_node_id(client, CRAS_STREAM_OUTPUT, node_id); printf("output node '%s' left-right swapped: %d\n", node_name, swapped); free((void *)node_name); } static void input_node_gain_changed(void *context, cras_node_id_t node_id, int32_t gain) { struct cras_client *client = (struct cras_client *)context; const char *node_name = node_name_for_node_id(client, CRAS_STREAM_INPUT, node_id); printf("input node '%s' gain: %d\n", node_name, gain); free((void *)node_name); } static void num_active_streams_changed(void *context, enum CRAS_STREAM_DIRECTION dir, uint32_t num_active_streams) { printf("num active %s streams: %u\n", string_for_direction(dir), num_active_streams); } static void server_connection_callback(struct cras_client *client, cras_connection_status_t status, void *user_arg) { const char *status_str = "undefined"; switch (status) { case CRAS_CONN_STATUS_FAILED: status_str = "error"; break; case CRAS_CONN_STATUS_DISCONNECTED: status_str = "disconnected"; break; case CRAS_CONN_STATUS_CONNECTED: status_str = "connected"; break; } printf("server %s\n", status_str); } static void print_usage(const char *command) { fprintf(stderr, "%s [options]\n" " Where [options] are:\n" " --sync|-s - Use the synchronous connection functions.\n" " --log-level|-l <n> - Set the syslog level (7 == " "LOG_DEBUG).\n", command); } int main(int argc, char **argv) { struct cras_client *client; int rc; int option_character; bool synchronous = false; int log_level = LOG_WARNING; static struct option long_options[] = { {"sync", no_argument, NULL, 's'}, {"log-level", required_argument, NULL, 'l'}, {NULL, 0, NULL, 0}, }; while(true) { int option_index = 0; option_character = getopt_long(argc, argv, "sl:", long_options, &option_index); if (option_character == -1) break; switch (option_character) { case 's': synchronous = !synchronous; break; case 'l': log_level = atoi(optarg); if (log_level < 0) log_level = LOG_WARNING; else if (log_level > LOG_DEBUG) log_level = LOG_DEBUG; break; default: print_usage(argv[0]); return 1; } } if (optind < argc) { fprintf(stderr, "%s: Extra arguments.\n", argv[0]); print_usage(argv[0]); return 1; } openlog("cras_monitor", LOG_PERROR, LOG_USER); setlogmask(LOG_UPTO(log_level)); rc = cras_client_create(&client); if (rc < 0) { syslog(LOG_ERR, "Couldn't create client."); return rc; } cras_client_set_connection_status_cb( client, server_connection_callback, NULL); if (synchronous) { rc = cras_client_connect(client); if (rc != 0) { syslog(LOG_ERR, "Could not connect to server."); return -rc; } } cras_client_set_output_volume_changed_callback( client, output_volume_changed); cras_client_set_output_mute_changed_callback( client, output_mute_changed); cras_client_set_capture_gain_changed_callback( client, capture_gain_changed); cras_client_set_capture_mute_changed_callback( client, capture_mute_changed); cras_client_set_nodes_changed_callback( client, nodes_changed); cras_client_set_active_node_changed_callback( client, active_node_changed); cras_client_set_output_node_volume_changed_callback( client, output_node_volume_changed); cras_client_set_node_left_right_swapped_changed_callback( client, node_left_right_swapped_changed); cras_client_set_input_node_gain_changed_callback( client, input_node_gain_changed); cras_client_set_num_active_streams_changed_callback( client, num_active_streams_changed); cras_client_set_state_change_callback_context(client, client); rc = cras_client_run_thread(client); if (rc != 0) { syslog(LOG_ERR, "Could not start thread."); return -rc; } if (!synchronous) { rc = cras_client_connect_async(client); if (rc) { syslog(LOG_ERR, "Couldn't connect to server.\n"); goto destroy_exit; } } while(1) { int rc; char c; rc = read(STDIN_FILENO, &c, 1); if (rc < 0 || c == 'q') return 0; } destroy_exit: cras_client_destroy(client); return 0; }