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