• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright 2019 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 #include <assert.h>
6 #include <stdlib.h>
7 #include <syslog.h>
8 
9 #include "audio_thread.h"
10 #include "audio_thread_log.h"
11 #include "cras_apm_list.h"
12 #include "cras_bt_log.h"
13 #include "cras_config.h"
14 #include "cras_control_rclient.h"
15 #include "cras_dsp.h"
16 #include "cras_iodev.h"
17 #include "cras_iodev_list.h"
18 #include "cras_hfp_ag_profile.h"
19 #include "cras_main_thread_log.h"
20 #include "cras_messages.h"
21 #include "cras_observer.h"
22 #include "cras_rclient.h"
23 #include "cras_rclient_util.h"
24 #include "cras_rstream.h"
25 #include "cras_system_state.h"
26 #include "cras_types.h"
27 #include "cras_util.h"
28 #include "utlist.h"
29 
30 /* Handles dumping audio thread debug info back to the client. */
dump_audio_thread_info(struct cras_rclient * client)31 static void dump_audio_thread_info(struct cras_rclient *client)
32 {
33 	struct cras_client_audio_debug_info_ready msg;
34 	struct cras_server_state *state;
35 
36 	cras_fill_client_audio_debug_info_ready(&msg);
37 	state = cras_system_state_get_no_lock();
38 	audio_thread_dump_thread_info(cras_iodev_list_get_audio_thread(),
39 				      &state->audio_debug_info);
40 	client->ops->send_message_to_client(client, &msg.header, NULL, 0);
41 }
42 
43 /* Sends shared memory fd for audio thread event log back to the client. */
get_atlog_fd(struct cras_rclient * client)44 static void get_atlog_fd(struct cras_rclient *client)
45 {
46 	struct cras_client_atlog_fd_ready msg;
47 	int atlog_fd;
48 
49 	cras_fill_client_atlog_fd_ready(&msg);
50 	atlog_fd = audio_thread_event_log_shm_fd();
51 	client->ops->send_message_to_client(client, &msg.header, &atlog_fd, 1);
52 }
53 
54 /* Handles dumping audio snapshots to shared memory for the client. */
dump_audio_thread_snapshots(struct cras_rclient * client)55 static void dump_audio_thread_snapshots(struct cras_rclient *client)
56 {
57 	struct cras_client_audio_debug_info_ready msg;
58 
59 	cras_fill_client_audio_debug_info_ready(&msg);
60 	cras_system_state_dump_snapshots();
61 	client->ops->send_message_to_client(client, &msg.header, NULL, 0);
62 }
63 
handle_get_hotword_models(struct cras_rclient * client,cras_node_id_t node_id)64 static void handle_get_hotword_models(struct cras_rclient *client,
65 				      cras_node_id_t node_id)
66 {
67 	struct cras_client_get_hotword_models_ready *msg;
68 	char *hotword_models;
69 	unsigned hotword_models_size;
70 	uint8_t buf[CRAS_CLIENT_MAX_MSG_SIZE];
71 
72 	msg = (struct cras_client_get_hotword_models_ready *)buf;
73 	hotword_models = cras_iodev_list_get_hotword_models(node_id);
74 	if (!hotword_models)
75 		goto empty_reply;
76 	hotword_models_size = strlen(hotword_models);
77 	if (hotword_models_size > CRAS_MAX_HOTWORD_MODELS) {
78 		free(hotword_models);
79 		goto empty_reply;
80 	}
81 
82 	cras_fill_client_get_hotword_models_ready(msg, hotword_models,
83 						  hotword_models_size);
84 	client->ops->send_message_to_client(client, &msg->header, NULL, 0);
85 	free(hotword_models);
86 	return;
87 
88 empty_reply:
89 	cras_fill_client_get_hotword_models_ready(msg, NULL, 0);
90 	client->ops->send_message_to_client(client, &msg->header, NULL, 0);
91 }
92 
93 /* Client notification callback functions. */
94 
send_output_volume_changed(void * context,int32_t volume)95 static void send_output_volume_changed(void *context, int32_t volume)
96 {
97 	struct cras_client_volume_changed msg;
98 	struct cras_rclient *client = (struct cras_rclient *)context;
99 
100 	cras_fill_client_output_volume_changed(&msg, volume);
101 	client->ops->send_message_to_client(client, &msg.header, NULL, 0);
102 }
103 
send_output_mute_changed(void * context,int muted,int user_muted,int mute_locked)104 static void send_output_mute_changed(void *context, int muted, int user_muted,
105 				     int mute_locked)
106 {
107 	struct cras_client_mute_changed msg;
108 	struct cras_rclient *client = (struct cras_rclient *)context;
109 
110 	cras_fill_client_output_mute_changed(&msg, muted, user_muted,
111 					     mute_locked);
112 	client->ops->send_message_to_client(client, &msg.header, NULL, 0);
113 }
114 
send_capture_gain_changed(void * context,int32_t gain)115 static void send_capture_gain_changed(void *context, int32_t gain)
116 {
117 	struct cras_client_volume_changed msg;
118 	struct cras_rclient *client = (struct cras_rclient *)context;
119 
120 	cras_fill_client_capture_gain_changed(&msg, gain);
121 	client->ops->send_message_to_client(client, &msg.header, NULL, 0);
122 }
123 
send_capture_mute_changed(void * context,int muted,int mute_locked)124 static void send_capture_mute_changed(void *context, int muted, int mute_locked)
125 {
126 	struct cras_client_mute_changed msg;
127 	struct cras_rclient *client = (struct cras_rclient *)context;
128 
129 	cras_fill_client_capture_mute_changed(&msg, muted, mute_locked);
130 	client->ops->send_message_to_client(client, &msg.header, NULL, 0);
131 }
132 
send_nodes_changed(void * context)133 static void send_nodes_changed(void *context)
134 {
135 	struct cras_client_nodes_changed msg;
136 	struct cras_rclient *client = (struct cras_rclient *)context;
137 
138 	cras_fill_client_nodes_changed(&msg);
139 	client->ops->send_message_to_client(client, &msg.header, NULL, 0);
140 }
141 
send_active_node_changed(void * context,enum CRAS_STREAM_DIRECTION dir,cras_node_id_t node_id)142 static void send_active_node_changed(void *context,
143 				     enum CRAS_STREAM_DIRECTION dir,
144 				     cras_node_id_t node_id)
145 {
146 	struct cras_client_active_node_changed msg;
147 	struct cras_rclient *client = (struct cras_rclient *)context;
148 
149 	cras_fill_client_active_node_changed(&msg, dir, node_id);
150 	client->ops->send_message_to_client(client, &msg.header, NULL, 0);
151 }
152 
send_output_node_volume_changed(void * context,cras_node_id_t node_id,int32_t volume)153 static void send_output_node_volume_changed(void *context,
154 					    cras_node_id_t node_id,
155 					    int32_t volume)
156 {
157 	struct cras_client_node_value_changed msg;
158 	struct cras_rclient *client = (struct cras_rclient *)context;
159 
160 	cras_fill_client_output_node_volume_changed(&msg, node_id, volume);
161 	client->ops->send_message_to_client(client, &msg.header, NULL, 0);
162 }
163 
send_node_left_right_swapped_changed(void * context,cras_node_id_t node_id,int swapped)164 static void send_node_left_right_swapped_changed(void *context,
165 						 cras_node_id_t node_id,
166 						 int swapped)
167 {
168 	struct cras_client_node_value_changed msg;
169 	struct cras_rclient *client = (struct cras_rclient *)context;
170 
171 	cras_fill_client_node_left_right_swapped_changed(&msg, node_id,
172 							 swapped);
173 	client->ops->send_message_to_client(client, &msg.header, NULL, 0);
174 }
175 
send_input_node_gain_changed(void * context,cras_node_id_t node_id,int32_t gain)176 static void send_input_node_gain_changed(void *context, cras_node_id_t node_id,
177 					 int32_t gain)
178 {
179 	struct cras_client_node_value_changed msg;
180 	struct cras_rclient *client = (struct cras_rclient *)context;
181 
182 	cras_fill_client_input_node_gain_changed(&msg, node_id, gain);
183 	client->ops->send_message_to_client(client, &msg.header, NULL, 0);
184 }
185 
send_num_active_streams_changed(void * context,enum CRAS_STREAM_DIRECTION dir,uint32_t num_active_streams)186 static void send_num_active_streams_changed(void *context,
187 					    enum CRAS_STREAM_DIRECTION dir,
188 					    uint32_t num_active_streams)
189 {
190 	struct cras_client_num_active_streams_changed msg;
191 	struct cras_rclient *client = (struct cras_rclient *)context;
192 
193 	cras_fill_client_num_active_streams_changed(&msg, dir,
194 						    num_active_streams);
195 	client->ops->send_message_to_client(client, &msg.header, NULL, 0);
196 }
197 
register_for_notification(struct cras_rclient * client,enum CRAS_CLIENT_MESSAGE_ID msg_id,int do_register)198 static void register_for_notification(struct cras_rclient *client,
199 				      enum CRAS_CLIENT_MESSAGE_ID msg_id,
200 				      int do_register)
201 {
202 	struct cras_observer_ops observer_ops;
203 	int empty;
204 
205 	cras_observer_get_ops(client->observer, &observer_ops);
206 
207 	switch (msg_id) {
208 	case CRAS_CLIENT_OUTPUT_VOLUME_CHANGED:
209 		observer_ops.output_volume_changed =
210 			do_register ? send_output_volume_changed : NULL;
211 		break;
212 	case CRAS_CLIENT_OUTPUT_MUTE_CHANGED:
213 		observer_ops.output_mute_changed =
214 			do_register ? send_output_mute_changed : NULL;
215 		break;
216 	case CRAS_CLIENT_CAPTURE_GAIN_CHANGED:
217 		observer_ops.capture_gain_changed =
218 			do_register ? send_capture_gain_changed : NULL;
219 		break;
220 	case CRAS_CLIENT_CAPTURE_MUTE_CHANGED:
221 		observer_ops.capture_mute_changed =
222 			do_register ? send_capture_mute_changed : NULL;
223 		break;
224 	case CRAS_CLIENT_NODES_CHANGED:
225 		observer_ops.nodes_changed =
226 			do_register ? send_nodes_changed : NULL;
227 		break;
228 	case CRAS_CLIENT_ACTIVE_NODE_CHANGED:
229 		observer_ops.active_node_changed =
230 			do_register ? send_active_node_changed : NULL;
231 		break;
232 	case CRAS_CLIENT_OUTPUT_NODE_VOLUME_CHANGED:
233 		observer_ops.output_node_volume_changed =
234 			do_register ? send_output_node_volume_changed : NULL;
235 		break;
236 	case CRAS_CLIENT_NODE_LEFT_RIGHT_SWAPPED_CHANGED:
237 		observer_ops.node_left_right_swapped_changed =
238 			do_register ? send_node_left_right_swapped_changed :
239 				      NULL;
240 		break;
241 	case CRAS_CLIENT_INPUT_NODE_GAIN_CHANGED:
242 		observer_ops.input_node_gain_changed =
243 			do_register ? send_input_node_gain_changed : NULL;
244 		break;
245 	case CRAS_CLIENT_NUM_ACTIVE_STREAMS_CHANGED:
246 		observer_ops.num_active_streams_changed =
247 			do_register ? send_num_active_streams_changed : NULL;
248 		break;
249 	default:
250 		syslog(LOG_ERR, "Invalid client notification message ID: %u",
251 		       msg_id);
252 		break;
253 	}
254 
255 	empty = cras_observer_ops_are_empty(&observer_ops);
256 	if (client->observer) {
257 		if (empty) {
258 			cras_observer_remove(client->observer);
259 			client->observer = NULL;
260 		} else {
261 			cras_observer_set_ops(client->observer, &observer_ops);
262 		}
263 	} else if (!empty) {
264 		client->observer = cras_observer_add(&observer_ops, client);
265 	}
266 }
267 
direction_valid(enum CRAS_STREAM_DIRECTION direction)268 static int direction_valid(enum CRAS_STREAM_DIRECTION direction)
269 {
270 	return direction < CRAS_NUM_DIRECTIONS &&
271 	       direction != CRAS_STREAM_UNDEFINED;
272 }
273 
274 /* Entry point for handling a message from the client.  Called from the main
275  * server context.
276  *
277  * If the message from clients has incorrect length (truncated message), return
278  * an error up to CRAS server.
279  * If the message from clients has invalid content, should return the errors to
280  * clients by send_message_to_client and return 0 here.
281  *
282  */
ccr_handle_message_from_client(struct cras_rclient * client,const struct cras_server_message * msg,int * fds,unsigned int num_fds)283 static int ccr_handle_message_from_client(struct cras_rclient *client,
284 					  const struct cras_server_message *msg,
285 					  int *fds, unsigned int num_fds)
286 {
287 	int rc = 0;
288 	assert(client && msg);
289 
290 	rc = rclient_validate_message_fds(msg, fds, num_fds);
291 	if (rc < 0) {
292 		for (int i = 0; i < (int)num_fds; i++)
293 			if (fds[i] >= 0)
294 				close(fds[i]);
295 		return rc;
296 	}
297 	int fd = num_fds > 0 ? fds[0] : -1;
298 
299 	switch (msg->id) {
300 	case CRAS_SERVER_CONNECT_STREAM: {
301 		int client_shm_fd = num_fds > 1 ? fds[1] : -1;
302 		if (MSG_LEN_VALID(msg, struct cras_connect_message)) {
303 			rclient_handle_client_stream_connect(
304 				client,
305 				(const struct cras_connect_message *)msg, fd,
306 				client_shm_fd);
307 		} else {
308 			return -EINVAL;
309 		}
310 		break;
311 	}
312 	case CRAS_SERVER_DISCONNECT_STREAM:
313 		if (!MSG_LEN_VALID(msg, struct cras_disconnect_stream_message))
314 			return -EINVAL;
315 		rclient_handle_client_stream_disconnect(
316 			client,
317 			(const struct cras_disconnect_stream_message *)msg);
318 		break;
319 	case CRAS_SERVER_SET_SYSTEM_VOLUME:
320 		if (!MSG_LEN_VALID(msg, struct cras_set_system_volume))
321 			return -EINVAL;
322 		cras_system_set_volume(
323 			((const struct cras_set_system_volume *)msg)->volume);
324 		break;
325 	case CRAS_SERVER_SET_SYSTEM_MUTE:
326 		if (!MSG_LEN_VALID(msg, struct cras_set_system_mute))
327 			return -EINVAL;
328 		cras_system_set_mute(
329 			((const struct cras_set_system_mute *)msg)->mute);
330 		break;
331 	case CRAS_SERVER_SET_USER_MUTE:
332 		if (!MSG_LEN_VALID(msg, struct cras_set_system_mute))
333 			return -EINVAL;
334 		cras_system_set_user_mute(
335 			((const struct cras_set_system_mute *)msg)->mute);
336 		break;
337 	case CRAS_SERVER_SET_SYSTEM_MUTE_LOCKED:
338 		if (!MSG_LEN_VALID(msg, struct cras_set_system_mute))
339 			return -EINVAL;
340 		cras_system_set_mute_locked(
341 			((const struct cras_set_system_mute *)msg)->mute);
342 		break;
343 	case CRAS_SERVER_SET_SYSTEM_CAPTURE_MUTE:
344 		if (!MSG_LEN_VALID(msg, struct cras_set_system_mute))
345 			return -EINVAL;
346 		cras_system_set_capture_mute(
347 			((const struct cras_set_system_mute *)msg)->mute);
348 		break;
349 	case CRAS_SERVER_SET_SYSTEM_CAPTURE_MUTE_LOCKED:
350 		if (!MSG_LEN_VALID(msg, struct cras_set_system_mute))
351 			return -EINVAL;
352 		cras_system_set_capture_mute_locked(
353 			((const struct cras_set_system_mute *)msg)->mute);
354 		break;
355 	case CRAS_SERVER_SET_NODE_ATTR: {
356 		const struct cras_set_node_attr *m =
357 			(const struct cras_set_node_attr *)msg;
358 		if (!MSG_LEN_VALID(msg, struct cras_set_node_attr))
359 			return -EINVAL;
360 		cras_iodev_list_set_node_attr(m->node_id, m->attr, m->value);
361 		break;
362 	}
363 	case CRAS_SERVER_SELECT_NODE: {
364 		const struct cras_select_node *m =
365 			(const struct cras_select_node *)msg;
366 		if (!MSG_LEN_VALID(msg, struct cras_select_node) ||
367 		    !direction_valid(m->direction))
368 			return -EINVAL;
369 		cras_iodev_list_select_node(m->direction, m->node_id);
370 		break;
371 	}
372 	case CRAS_SERVER_ADD_ACTIVE_NODE: {
373 		const struct cras_add_active_node *m =
374 			(const struct cras_add_active_node *)msg;
375 		if (!MSG_LEN_VALID(msg, struct cras_add_active_node) ||
376 		    !direction_valid(m->direction))
377 			return -EINVAL;
378 		cras_iodev_list_add_active_node(m->direction, m->node_id);
379 		break;
380 	}
381 	case CRAS_SERVER_RM_ACTIVE_NODE: {
382 		const struct cras_rm_active_node *m =
383 			(const struct cras_rm_active_node *)msg;
384 		if (!MSG_LEN_VALID(msg, struct cras_rm_active_node) ||
385 		    !direction_valid(m->direction))
386 			return -EINVAL;
387 		cras_iodev_list_rm_active_node(m->direction, m->node_id);
388 		break;
389 	}
390 	case CRAS_SERVER_RELOAD_DSP:
391 		cras_dsp_reload_ini();
392 		break;
393 	case CRAS_SERVER_DUMP_DSP_INFO:
394 		cras_dsp_dump_info();
395 		break;
396 	case CRAS_SERVER_DUMP_AUDIO_THREAD:
397 		dump_audio_thread_info(client);
398 		break;
399 	case CRAS_SERVER_GET_ATLOG_FD:
400 		get_atlog_fd(client);
401 		break;
402 	case CRAS_SERVER_DUMP_MAIN: {
403 		struct cras_client_audio_debug_info_ready msg;
404 		struct cras_server_state *state;
405 
406 		state = cras_system_state_get_no_lock();
407 		memcpy(&state->main_thread_debug_info.main_log, main_log,
408 		       sizeof(struct main_thread_event_log));
409 
410 		cras_fill_client_audio_debug_info_ready(&msg);
411 		client->ops->send_message_to_client(client, &msg.header, NULL,
412 						    0);
413 		break;
414 	}
415 	case CRAS_SERVER_DUMP_BT: {
416 		struct cras_client_audio_debug_info_ready msg;
417 		struct cras_server_state *state;
418 
419 		state = cras_system_state_get_no_lock();
420 #ifdef CRAS_DBUS
421 		memcpy(&state->bt_debug_info.bt_log, btlog,
422 		       sizeof(struct cras_bt_event_log));
423 		memcpy(&state->bt_debug_info.wbs_logger,
424 		       cras_hfp_ag_get_wbs_logger(),
425 		       sizeof(struct packet_status_logger));
426 #else
427 		memset(&state->bt_debug_info.bt_log, 0,
428 		       sizeof(struct cras_bt_debug_info));
429 		memset(&state->bt_debug_info.wbs_logger, 0,
430 		       sizeof(struct packet_status_logger));
431 #endif
432 
433 		cras_fill_client_audio_debug_info_ready(&msg);
434 		client->ops->send_message_to_client(client, &msg.header, NULL,
435 						    0);
436 		break;
437 	}
438 	case CRAS_SERVER_SET_BT_WBS_ENABLED: {
439 		const struct cras_set_bt_wbs_enabled *m =
440 			(const struct cras_set_bt_wbs_enabled *)msg;
441 		if (!MSG_LEN_VALID(msg, struct cras_set_bt_wbs_enabled))
442 			return -EINVAL;
443 		cras_system_set_bt_wbs_enabled(m->enabled);
444 		break;
445 	}
446 	case CRAS_SERVER_DUMP_SNAPSHOTS:
447 		dump_audio_thread_snapshots(client);
448 		break;
449 	case CRAS_SERVER_ADD_TEST_DEV: {
450 		const struct cras_add_test_dev *m =
451 			(const struct cras_add_test_dev *)msg;
452 		if (!MSG_LEN_VALID(msg, struct cras_add_test_dev))
453 			return -EINVAL;
454 		cras_iodev_list_add_test_dev(m->type);
455 		break;
456 	}
457 	case CRAS_SERVER_TEST_DEV_COMMAND: {
458 		const struct cras_test_dev_command *m =
459 			(const struct cras_test_dev_command *)msg;
460 		if (!MSG_LEN_VALID(msg, struct cras_test_dev_command))
461 			return -EINVAL;
462 		cras_iodev_list_test_dev_command(
463 			m->iodev_idx, (enum CRAS_TEST_IODEV_CMD)m->command,
464 			m->data_len, m->data);
465 		break;
466 	}
467 	case CRAS_SERVER_SUSPEND:
468 		cras_system_set_suspended(1);
469 		break;
470 	case CRAS_SERVER_RESUME:
471 		cras_system_set_suspended(0);
472 		break;
473 	case CRAS_CONFIG_GLOBAL_REMIX: {
474 		const struct cras_config_global_remix *m =
475 			(const struct cras_config_global_remix *)msg;
476 		float *coefficient;
477 
478 		if (!MSG_LEN_VALID(msg, struct cras_config_global_remix) ||
479 		    m->num_channels > CRAS_MAX_REMIX_CHANNELS)
480 			return -EINVAL;
481 		const size_t coefficient_len =
482 			(size_t)m->num_channels * (size_t)m->num_channels;
483 		const size_t size_with_coefficients =
484 			sizeof(*m) +
485 			coefficient_len * sizeof(m->coefficient[0]);
486 		if (size_with_coefficients != msg->length)
487 			return -EINVAL;
488 
489 		coefficient =
490 			(float *)calloc(coefficient_len, sizeof(coefficient));
491 		if (!coefficient) {
492 			syslog(LOG_ERR,
493 			       "Failed to create local coefficient array.");
494 			break;
495 		}
496 		memcpy(coefficient, m->coefficient,
497 		       coefficient_len * sizeof(coefficient));
498 
499 		audio_thread_config_global_remix(
500 			cras_iodev_list_get_audio_thread(), m->num_channels,
501 			coefficient);
502 		free(coefficient);
503 		break;
504 	}
505 	case CRAS_SERVER_GET_HOTWORD_MODELS: {
506 		if (!MSG_LEN_VALID(msg, struct cras_get_hotword_models))
507 			return -EINVAL;
508 		handle_get_hotword_models(
509 			client,
510 			((const struct cras_get_hotword_models *)msg)->node_id);
511 		break;
512 	}
513 	case CRAS_SERVER_SET_HOTWORD_MODEL: {
514 		const struct cras_set_hotword_model *m =
515 			(const struct cras_set_hotword_model *)msg;
516 		if (!MSG_LEN_VALID(msg, struct cras_set_hotword_model))
517 			return -EINVAL;
518 		cras_iodev_list_set_hotword_model(m->node_id, m->model_name);
519 		break;
520 	}
521 	case CRAS_SERVER_REGISTER_NOTIFICATION: {
522 		const struct cras_register_notification *m =
523 			(struct cras_register_notification *)msg;
524 		if (!MSG_LEN_VALID(msg, struct cras_register_notification))
525 			return -EINVAL;
526 		register_for_notification(
527 			client, (enum CRAS_CLIENT_MESSAGE_ID)m->msg_id,
528 			m->do_register);
529 		break;
530 	}
531 	case CRAS_SERVER_SET_AEC_DUMP: {
532 		const struct cras_set_aec_dump *m =
533 			(const struct cras_set_aec_dump *)msg;
534 		if (!MSG_LEN_VALID(msg, struct cras_set_aec_dump))
535 			return -EINVAL;
536 		audio_thread_set_aec_dump(cras_iodev_list_get_audio_thread(),
537 					  m->stream_id, m->start, fd);
538 		break;
539 	}
540 	case CRAS_SERVER_RELOAD_AEC_CONFIG:
541 		cras_apm_list_reload_aec_config();
542 		break;
543 	default:
544 		break;
545 	}
546 
547 	return 0;
548 }
549 
550 /* Declarations of cras_rclient operators for cras_control_rclient. */
551 static const struct cras_rclient_ops cras_control_rclient_ops = {
552 	.handle_message_from_client = ccr_handle_message_from_client,
553 	.send_message_to_client = rclient_send_message_to_client,
554 	.destroy = rclient_destroy,
555 };
556 
557 /*
558  * Exported Functions.
559  */
560 
561 /* Creates a client structure and sends a message back informing the client that
562  * the conneciton has succeeded. */
cras_control_rclient_create(int fd,size_t id)563 struct cras_rclient *cras_control_rclient_create(int fd, size_t id)
564 {
565 	/* Supports all directions but not CRAS_STREAM_UNDEFINED. */
566 	int supported_directions =
567 		CRAS_STREAM_ALL_DIRECTION ^
568 		cras_stream_direction_mask(CRAS_STREAM_UNDEFINED);
569 
570 	return rclient_generic_create(fd, id, &cras_control_rclient_ops,
571 				      supported_directions);
572 }
573