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