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