• 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 
6 #include <syslog.h>
7 
8 #include "cras_iodev_list.h"
9 #include "cras_messages.h"
10 #include "cras_observer.h"
11 #include "cras_rclient.h"
12 #include "cras_rclient_util.h"
13 #include "cras_rstream.h"
14 #include "cras_server_metrics.h"
15 #include "cras_tm.h"
16 #include "cras_types.h"
17 #include "cras_util.h"
18 #include "stream_list.h"
19 
rclient_send_message_to_client(const struct cras_rclient * client,const struct cras_client_message * msg,int * fds,unsigned int num_fds)20 int rclient_send_message_to_client(const struct cras_rclient *client,
21 				   const struct cras_client_message *msg,
22 				   int *fds, unsigned int num_fds)
23 {
24 	return cras_send_with_fds(client->fd, (const void *)msg, msg->length,
25 				  fds, num_fds);
26 }
27 
rclient_destroy(struct cras_rclient * client)28 void rclient_destroy(struct cras_rclient *client)
29 {
30 	cras_observer_remove(client->observer);
31 	stream_list_rm_all_client_streams(cras_iodev_list_get_stream_list(),
32 					  client);
33 	free(client);
34 }
35 
rclient_validate_message_fds(const struct cras_server_message * msg,int * fds,unsigned int num_fds)36 int rclient_validate_message_fds(const struct cras_server_message *msg,
37 				 int *fds, unsigned int num_fds)
38 {
39 	switch (msg->id) {
40 	case CRAS_SERVER_CONNECT_STREAM:
41 		if (num_fds > 2)
42 			goto error;
43 		break;
44 	case CRAS_SERVER_SET_AEC_DUMP:
45 		if (num_fds != 1)
46 			goto error;
47 		syslog(LOG_ERR, "client msg for APM debug, fd %d", fds[0]);
48 		break;
49 	default:
50 		if (num_fds > 0)
51 			goto error;
52 		break;
53 	}
54 
55 	return 0;
56 
57 error:
58 	syslog(LOG_ERR, "Message %d should not have %u fds attached.", msg->id,
59 	       num_fds);
60 	return -EINVAL;
61 }
62 
63 static int
rclient_validate_stream_connect_message(const struct cras_rclient * client,const struct cras_connect_message * msg)64 rclient_validate_stream_connect_message(const struct cras_rclient *client,
65 					const struct cras_connect_message *msg)
66 {
67 	if (!cras_valid_stream_id(msg->stream_id, client->id)) {
68 		syslog(LOG_ERR,
69 		       "stream_connect: invalid stream_id: %x for "
70 		       "client: %zx.\n",
71 		       msg->stream_id, client->id);
72 		return -EINVAL;
73 	}
74 
75 	int direction = cras_stream_direction_mask(msg->direction);
76 	if (direction < 0 || !(client->supported_directions & direction)) {
77 		syslog(LOG_ERR,
78 		       "stream_connect: invalid stream direction: %x for "
79 		       "client: %zx.\n",
80 		       msg->direction, client->id);
81 		return -EINVAL;
82 	}
83 	return 0;
84 }
85 
rclient_validate_stream_connect_fds(int audio_fd,int client_shm_fd,size_t client_shm_size)86 static int rclient_validate_stream_connect_fds(int audio_fd, int client_shm_fd,
87 					       size_t client_shm_size)
88 {
89 	/* check audio_fd is valid. */
90 	if (audio_fd < 0) {
91 		syslog(LOG_ERR, "Invalid audio fd in stream connect.\n");
92 		return -EBADF;
93 	}
94 
95 	/* check client_shm_fd is valid if client wants to use client shm. */
96 	if (client_shm_size > 0 && client_shm_fd < 0) {
97 		syslog(LOG_ERR,
98 		       "client_shm_fd must be valid if client_shm_size > 0.\n");
99 		return -EBADF;
100 	} else if (client_shm_size == 0 && client_shm_fd >= 0) {
101 		syslog(LOG_ERR,
102 		       "client_shm_fd can be valid only if client_shm_size > 0.\n");
103 		return -EINVAL;
104 	}
105 	return 0;
106 }
107 
rclient_validate_stream_connect_params(const struct cras_rclient * client,const struct cras_connect_message * msg,int audio_fd,int client_shm_fd)108 int rclient_validate_stream_connect_params(
109 	const struct cras_rclient *client,
110 	const struct cras_connect_message *msg, int audio_fd, int client_shm_fd)
111 {
112 	int rc;
113 
114 	rc = rclient_validate_stream_connect_message(client, msg);
115 	if (rc)
116 		return rc;
117 
118 	rc = rclient_validate_stream_connect_fds(audio_fd, client_shm_fd,
119 						 msg->client_shm_size);
120 	if (rc)
121 		return rc;
122 
123 	return 0;
124 }
125 
rclient_handle_client_stream_connect(struct cras_rclient * client,const struct cras_connect_message * msg,int aud_fd,int client_shm_fd)126 int rclient_handle_client_stream_connect(struct cras_rclient *client,
127 					 const struct cras_connect_message *msg,
128 					 int aud_fd, int client_shm_fd)
129 {
130 	struct cras_rstream *stream;
131 	struct cras_client_stream_connected stream_connected;
132 	struct cras_client_message *reply;
133 	struct cras_audio_format remote_fmt;
134 	struct cras_rstream_config stream_config;
135 	int rc, header_fd, samples_fd;
136 	int stream_fds[2];
137 
138 	rc = rclient_validate_stream_connect_params(client, msg, aud_fd,
139 						    client_shm_fd);
140 	if (rc) {
141 		if (client_shm_fd >= 0)
142 			close(client_shm_fd);
143 		if (aud_fd >= 0)
144 			close(aud_fd);
145 		goto reply_err;
146 	}
147 
148 	unpack_cras_audio_format(&remote_fmt, &msg->format);
149 
150 	/* When full, getting an error is preferable to blocking. */
151 	cras_make_fd_nonblocking(aud_fd);
152 
153 	cras_rstream_config_init_with_message(client, msg, &aud_fd,
154 					      &client_shm_fd, &remote_fmt,
155 					      &stream_config);
156 	rc = stream_list_add(cras_iodev_list_get_stream_list(), &stream_config,
157 			     &stream);
158 	if (rc)
159 		goto cleanup_config;
160 
161 	/* Tell client about the stream setup. */
162 	syslog(LOG_DEBUG, "Send connected for stream %x\n", msg->stream_id);
163 	cras_fill_client_stream_connected(
164 		&stream_connected, 0, /* No error. */
165 		msg->stream_id, &remote_fmt,
166 		cras_rstream_get_samples_shm_size(stream),
167 		cras_rstream_get_effects(stream));
168 	reply = &stream_connected.header;
169 
170 	rc = cras_rstream_get_shm_fds(stream, &header_fd, &samples_fd);
171 	if (rc)
172 		goto cleanup_config;
173 
174 	stream_fds[0] = header_fd;
175 	/* If we're using client-provided shm, samples_fd here refers to the
176 	 * same shm area as client_shm_fd */
177 	stream_fds[1] = samples_fd;
178 
179 	rc = client->ops->send_message_to_client(client, reply, stream_fds, 2);
180 	if (rc < 0) {
181 		syslog(LOG_ERR, "Failed to send connected messaged\n");
182 		stream_list_rm(cras_iodev_list_get_stream_list(),
183 			       stream->stream_id);
184 		goto cleanup_config;
185 	}
186 
187 	/* Metrics logs the stream configurations. */
188 	cras_server_metrics_stream_config(&stream_config);
189 
190 	/* Cleanup local object explicitly. */
191 	cras_rstream_config_cleanup(&stream_config);
192 	return 0;
193 
194 cleanup_config:
195 	cras_rstream_config_cleanup(&stream_config);
196 
197 reply_err:
198 	/* Send the error code to the client. */
199 	cras_fill_client_stream_connected(&stream_connected, rc, msg->stream_id,
200 					  &remote_fmt, 0, msg->effects);
201 	reply = &stream_connected.header;
202 	client->ops->send_message_to_client(client, reply, NULL, 0);
203 
204 	return rc;
205 }
206 
207 /* Handles messages from the client requesting that a stream be removed from the
208  * server. */
rclient_handle_client_stream_disconnect(struct cras_rclient * client,const struct cras_disconnect_stream_message * msg)209 int rclient_handle_client_stream_disconnect(
210 	struct cras_rclient *client,
211 	const struct cras_disconnect_stream_message *msg)
212 {
213 	if (!cras_valid_stream_id(msg->stream_id, client->id)) {
214 		syslog(LOG_ERR,
215 		       "stream_disconnect: invalid stream_id: %x for "
216 		       "client: %zx.\n",
217 		       msg->stream_id, client->id);
218 		return -EINVAL;
219 	}
220 	return stream_list_rm(cras_iodev_list_get_stream_list(),
221 			      msg->stream_id);
222 }
223