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