1 //
2 // Copyright (C) 2020 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15
16 #include "host/libs/audio_connector/server.h"
17
18 #include <errno.h>
19 #include <fcntl.h>
20 #include <strings.h>
21 #include <unistd.h>
22
23 #include <utility>
24 #include <vector>
25
26 #include <android-base/logging.h>
27
28 #include "common/libs/fs/shared_select.h"
29
30 namespace cuttlefish {
31
32 namespace {
33
AllocateShm(size_t size,const std::string & name,SharedFD * shm_fd)34 ScopedMMap AllocateShm(size_t size, const std::string& name, SharedFD* shm_fd) {
35 *shm_fd = SharedFD::MemfdCreate(name, 0);
36 if (!(*shm_fd)->IsOpen()) {
37 LOG(FATAL) << "Unable to allocate create file for " << name << ": "
38 << (*shm_fd)->StrError();
39 return ScopedMMap();
40 }
41
42 auto truncate_ret = (*shm_fd)->Truncate(size);
43 if (truncate_ret != 0) {
44 LOG(FATAL) << "Unable to resize " << name << " to " << size
45 << " bytes: " << (*shm_fd)->StrError();
46 return ScopedMMap();
47 }
48
49 auto mmap_res = (*shm_fd)->MMap(NULL /* addr */, size, PROT_READ | PROT_WRITE,
50 MAP_SHARED, 0 /*offset*/);
51 if (!mmap_res) {
52 LOG(FATAL) << "Unable to memory map " << name << ": "
53 << (*shm_fd)->StrError();
54 }
55 return mmap_res;
56 }
57
BufferAt(ScopedMMap & shm,size_t offset,size_t len)58 volatile uint8_t* BufferAt(ScopedMMap& shm, size_t offset, size_t len) {
59 CHECK(shm.WithinBounds(offset, len))
60 << "Tx buffer bounds outside the buffer area: " << offset << " " << len;
61 void* ptr = shm.get();
62 return &reinterpret_cast<volatile uint8_t*>(ptr)[offset];
63 }
64
CreateSocketPair(SharedFD * local,SharedFD * remote)65 bool CreateSocketPair(SharedFD* local, SharedFD* remote) {
66 auto ret = SharedFD::SocketPair(AF_UNIX, SOCK_SEQPACKET, 0, local, remote);
67 if (!ret) {
68 LOG(ERROR) << "Unable to create socket pair for audio IO signaling: "
69 << (*local)->StrError();
70 }
71 return ret;
72 }
73
SendStatusCallback(uint32_t buffer_offset,SharedFD socket)74 std::function<void(AudioStatus, uint32_t, uint32_t)> SendStatusCallback(
75 uint32_t buffer_offset, SharedFD socket) {
76 // Consumption of an audio buffer is an asynchronous event, which could
77 // trigger after the client disconnected. A WeakFD ensures that the response
78 // will only be sent if there is still a client available.
79 auto weak_socket = WeakFD(socket);
80 return
81 [buffer_offset, weak_socket](AudioStatus status, uint32_t latency_bytes,
82 uint32_t consumed_length) {
83 auto socket = weak_socket.lock();
84 if (!socket->IsOpen()) {
85 return;
86 }
87 IoStatusMsg reply;
88 reply.status.status = Le32(static_cast<uint32_t>(status));
89 reply.status.latency_bytes = Le32(latency_bytes);
90 reply.buffer_offset = buffer_offset;
91 reply.consumed_length = consumed_length;
92 // Send the acknowledgment non-blockingly to avoid a slow client from
93 // blocking the server.
94 auto sent = socket->Send(&reply, sizeof(reply), MSG_DONTWAIT);
95 if (sent < sizeof(reply)) {
96 LOG(ERROR) << "Failed to send entire reply: " << socket->StrError();
97 }
98 };
99 }
100
101 } // namespace
102
AcceptClient(uint32_t num_streams,uint32_t num_jacks,uint32_t num_chmaps,size_t tx_shm_len,size_t rx_shm_len)103 std::unique_ptr<AudioClientConnection> AudioServer::AcceptClient(
104 uint32_t num_streams, uint32_t num_jacks, uint32_t num_chmaps,
105 size_t tx_shm_len, size_t rx_shm_len) {
106 auto conn_fd = SharedFD::Accept(*server_socket_, nullptr, 0);
107 if (!conn_fd->IsOpen()) {
108 LOG(ERROR) << "Connection failed on audio server: " << conn_fd->StrError();
109 return nullptr;
110 }
111 return AudioClientConnection::Create(conn_fd, num_streams, num_jacks,
112 num_chmaps, tx_shm_len, rx_shm_len);
113 }
114
115 /* static */
Create(SharedFD client_socket,uint32_t num_streams,uint32_t num_jacks,uint32_t num_chmaps,size_t tx_shm_len,size_t rx_shm_len)116 std::unique_ptr<AudioClientConnection> AudioClientConnection::Create(
117 SharedFD client_socket, uint32_t num_streams, uint32_t num_jacks,
118 uint32_t num_chmaps, size_t tx_shm_len, size_t rx_shm_len) {
119 SharedFD event_socket, event_pair;
120 SharedFD tx_socket, tx_pair;
121 SharedFD rx_socket, rx_pair;
122
123 bool pairs_created = true;
124 pairs_created &= CreateSocketPair(&event_socket, &event_pair);
125 pairs_created &= CreateSocketPair(&tx_socket, &tx_pair);
126 pairs_created &= CreateSocketPair(&rx_socket, &rx_pair);
127 if (!pairs_created) {
128 return nullptr;
129 }
130
131 SharedFD tx_shm_fd, rx_shm_fd;
132 auto tx_shm =
133 AllocateShm(tx_shm_len, "vios_audio_server_tx_queue", &tx_shm_fd);
134 if (!tx_shm) {
135 return nullptr;
136 }
137 auto rx_shm =
138 AllocateShm(rx_shm_len, "vios_audio_server_rx_queue", &rx_shm_fd);
139 if (!rx_shm) {
140 return nullptr;
141 }
142
143 VioSConfig welcome_msg = {
144 .version = VIOS_VERSION,
145 .jacks = num_jacks,
146 .streams = num_streams,
147 .chmaps = num_chmaps,
148 };
149
150 auto sent = client_socket->SendFileDescriptors(
151 &welcome_msg, sizeof(welcome_msg), event_pair, tx_pair, rx_pair,
152 tx_shm_fd, rx_shm_fd);
153 if (sent < 0) {
154 LOG(ERROR) << "Failed to send file descriptors to client: "
155 << client_socket->StrError();
156 return nullptr;
157 }
158
159 return std::unique_ptr<AudioClientConnection>(new AudioClientConnection(
160 std::move(tx_shm), std::move(rx_shm), client_socket,
161 event_socket, tx_socket, rx_socket));
162 }
163
ReceiveCommands(AudioServerExecutor & executor)164 bool AudioClientConnection::ReceiveCommands(AudioServerExecutor& executor) {
165 // The largest msg the client will send is 24 bytes long, using uint64_t
166 // guarantees it's aligned to 64 bits.
167 uint64_t recv_buffer[3];
168 auto recv_size =
169 ReceiveMsg(control_socket_, &recv_buffer, sizeof(recv_buffer));
170 if (recv_size <= 0) {
171 return false;
172 }
173 const auto cmd_hdr = reinterpret_cast<const virtio_snd_hdr*>(&recv_buffer[0]);
174 if (recv_size < sizeof(virtio_snd_hdr)) {
175 LOG(ERROR) << "Received control message is too small: " << recv_size;
176 return false;
177 }
178 switch (static_cast<AudioCommandType>(cmd_hdr->code.as_uint32_t())) {
179 case AudioCommandType::VIRTIO_SND_R_PCM_INFO: {
180 if (recv_size < sizeof(virtio_snd_query_info)) {
181 LOG(ERROR) << "Received QUERY_INFO message is too small: " << recv_size;
182 return false;
183 }
184 auto query_info = reinterpret_cast<const virtio_snd_query_info*>(cmd_hdr);
185 auto info_count = query_info->count.as_uint32_t();
186 auto start_id = query_info->start_id.as_uint32_t();
187 std::unique_ptr<virtio_snd_pcm_info[]> reply(
188 new virtio_snd_pcm_info[info_count]);
189 StreamInfoCommand cmd(start_id, info_count, reply.get());
190
191 executor.StreamsInfo(cmd);
192 return CmdReply(cmd.status(), reply.get(),
193 info_count * sizeof(reply[0]));
194 }
195 case AudioCommandType::VIRTIO_SND_R_PCM_SET_PARAMS: {
196 if (recv_size < sizeof(virtio_snd_pcm_set_params)) {
197 LOG(ERROR) << "Received SET_PARAMS message is too small: " << recv_size;
198 return false;
199 }
200 auto set_param_msg =
201 reinterpret_cast<const virtio_snd_pcm_set_params*>(cmd_hdr);
202 StreamSetParamsCommand cmd(set_param_msg->hdr.stream_id.as_uint32_t(),
203 set_param_msg->buffer_bytes.as_uint32_t(),
204 set_param_msg->period_bytes.as_uint32_t(),
205 set_param_msg->features.as_uint32_t(),
206 set_param_msg->channels, set_param_msg->format,
207 set_param_msg->rate);
208 executor.SetStreamParameters(cmd);
209 return CmdReply(cmd.status());
210 }
211 case AudioCommandType::VIRTIO_SND_R_PCM_PREPARE: {
212 if (recv_size < sizeof(virtio_snd_pcm_hdr)) {
213 LOG(ERROR) << "Received PREPARE message is too small: " << recv_size;
214 return false;
215 }
216 auto pcm_op_msg = reinterpret_cast<const virtio_snd_pcm_hdr*>(cmd_hdr);
217 StreamControlCommand cmd(AudioCommandType::VIRTIO_SND_R_PCM_PREPARE,
218 pcm_op_msg->stream_id.as_uint32_t());
219 executor.PrepareStream(cmd);
220 return CmdReply(cmd.status());
221 }
222 case AudioCommandType::VIRTIO_SND_R_PCM_RELEASE: {
223 if (recv_size < sizeof(virtio_snd_pcm_hdr)) {
224 LOG(ERROR) << "Received RELEASE message is too small: " << recv_size;
225 return false;
226 }
227 auto pcm_op_msg = reinterpret_cast<const virtio_snd_pcm_hdr*>(cmd_hdr);
228 StreamControlCommand cmd(AudioCommandType::VIRTIO_SND_R_PCM_RELEASE,
229 pcm_op_msg->stream_id.as_uint32_t());
230 executor.ReleaseStream(cmd);
231 return CmdReply(cmd.status());
232 }
233 case AudioCommandType::VIRTIO_SND_R_PCM_START: {
234 if (recv_size < sizeof(virtio_snd_pcm_hdr)) {
235 LOG(ERROR) << "Received START message is too small: " << recv_size;
236 return false;
237 }
238 auto pcm_op_msg = reinterpret_cast<const virtio_snd_pcm_hdr*>(cmd_hdr);
239 StreamControlCommand cmd(AudioCommandType::VIRTIO_SND_R_PCM_START,
240 pcm_op_msg->stream_id.as_uint32_t());
241 executor.StartStream(cmd);
242 return CmdReply(cmd.status());
243 }
244 case AudioCommandType::VIRTIO_SND_R_PCM_STOP: {
245 if (recv_size < sizeof(virtio_snd_pcm_hdr)) {
246 LOG(ERROR) << "Received STOP message is too small: " << recv_size;
247 return false;
248 }
249 auto pcm_op_msg = reinterpret_cast<const virtio_snd_pcm_hdr*>(cmd_hdr);
250 StreamControlCommand cmd(AudioCommandType::VIRTIO_SND_R_PCM_STOP,
251 pcm_op_msg->stream_id.as_uint32_t());
252 executor.StopStream(cmd);
253 return CmdReply(cmd.status());
254 }
255 case AudioCommandType::VIRTIO_SND_R_CHMAP_INFO: {
256 if (recv_size < sizeof(virtio_snd_query_info)) {
257 LOG(ERROR) << "Received QUERY_INFO message is too small: " << recv_size;
258 return false;
259 }
260 auto query_info = reinterpret_cast<const virtio_snd_query_info*>(cmd_hdr);
261 auto info_count = query_info->count.as_uint32_t();
262 auto start_id = query_info->start_id.as_uint32_t();
263 std::unique_ptr<virtio_snd_chmap_info[]> reply(
264 new virtio_snd_chmap_info[info_count]);
265 ChmapInfoCommand cmd(start_id, info_count, reply.get());
266
267 executor.ChmapsInfo(cmd);
268 return CmdReply(cmd.status(), reply.get(),
269 info_count * sizeof(reply[0]));
270 }
271 case AudioCommandType::VIRTIO_SND_R_JACK_INFO: {
272 if (recv_size < sizeof(virtio_snd_query_info)) {
273 LOG(ERROR) << "Received QUERY_INFO message is too small: " << recv_size;
274 return false;
275 }
276 auto query_info = reinterpret_cast<const virtio_snd_query_info*>(cmd_hdr);
277 auto info_count = query_info->count.as_uint32_t();
278 auto start_id = query_info->start_id.as_uint32_t();
279 std::unique_ptr<virtio_snd_jack_info[]> reply(
280 new virtio_snd_jack_info[info_count]);
281 JackInfoCommand cmd(start_id, info_count, reply.get());
282
283 executor.JacksInfo(cmd);
284 return CmdReply(cmd.status(), reply.get(),
285 info_count * sizeof(reply[0]));
286 }
287 case AudioCommandType::VIRTIO_SND_R_JACK_REMAP:
288 LOG(ERROR) << "Unsupported command type: " << cmd_hdr->code.as_uint32_t();
289 return CmdReply(AudioStatus::VIRTIO_SND_S_NOT_SUPP);
290 default:
291 LOG(ERROR) << "Unknown command type: " << cmd_hdr->code.as_uint32_t();
292 return CmdReply(AudioStatus::VIRTIO_SND_S_BAD_MSG);
293 }
294 return true;
295 }
296
ReceivePlayback(AudioServerExecutor & executor)297 bool AudioClientConnection::ReceivePlayback(AudioServerExecutor& executor) {
298 // The largest msg the client will send is 12 bytes long, using uint32_t
299 // guarantees it's aligned to 32 bits.
300 uint32_t recv_buffer[3];
301 auto recv_size = ReceiveMsg(tx_socket_, &recv_buffer, sizeof(recv_buffer));
302 if (recv_size <= 0) {
303 return false;
304 }
305 const auto msg_hdr = reinterpret_cast<const IoTransferMsg*>(&recv_buffer[0]);
306
307 if (recv_size < sizeof(IoTransferMsg)) {
308 LOG(ERROR) << "Received PCM_XFER message is too small: " << recv_size;
309 return false;
310 }
311 TxBuffer buffer(
312 msg_hdr->io_xfer,
313 BufferAt(tx_shm_, msg_hdr->buffer_offset, msg_hdr->buffer_len),
314 msg_hdr->buffer_len,
315 SendStatusCallback(msg_hdr->buffer_offset, tx_socket_));
316 executor.OnPlaybackBuffer(std::move(buffer));
317 return true;
318 }
319
ReceiveCapture(AudioServerExecutor & executor)320 bool AudioClientConnection::ReceiveCapture(AudioServerExecutor& executor) {
321 uint32_t recv_buffer[3];
322 auto recv_size = ReceiveMsg(rx_socket_, &recv_buffer, sizeof(recv_buffer));
323 if (recv_size <= 0) {
324 return false;
325 }
326 const auto msg_hdr = reinterpret_cast<const IoTransferMsg*>(&recv_buffer[0]);
327 if (recv_size < sizeof(IoTransferMsg)) {
328 LOG(ERROR) << "Received PCM_XFER message is too small: " << recv_size;
329 return false;
330 }
331 RxBuffer buffer(
332 msg_hdr->io_xfer,
333 BufferAt(rx_shm_, msg_hdr->buffer_offset, msg_hdr->buffer_len),
334 msg_hdr->buffer_len,
335 SendStatusCallback(msg_hdr->buffer_offset, rx_socket_));
336 executor.OnCaptureBuffer(std::move(buffer));
337 return true;
338 }
339
CmdReply(AudioStatus status,const void * data,size_t size)340 bool AudioClientConnection::CmdReply(AudioStatus status, const void* data,
341 size_t size) {
342 virtio_snd_hdr vio_status = {
343 .code = Le32(static_cast<uint32_t>(status)),
344 };
345 std::vector<uint8_t> buffer(sizeof(vio_status) + size, 0);
346 std::memcpy(buffer.data(), &vio_status, sizeof(vio_status));
347 if (data) {
348 std::memcpy(buffer.data() + sizeof(vio_status), data, size);
349 }
350 auto status_sent = control_socket_->Send(buffer.data(), buffer.size(), 0);
351 if (status_sent < sizeof(vio_status) + size) {
352 LOG(ERROR) << "Failed to send entire command status: "
353 << control_socket_->StrError();
354 return false;
355 }
356 return true;
357 }
358
SendEvent()359 bool AudioClientConnection::SendEvent(/*TODO*/) { return false; }
360
ReceiveMsg(SharedFD socket,void * buffer,size_t size)361 ssize_t AudioClientConnection::ReceiveMsg(SharedFD socket, void* buffer,
362 size_t size) {
363 auto read = socket->Recv(buffer, size, MSG_TRUNC);
364 CHECK(read < 0 || read <= size)
365 << "Received a msg bigger than the buffer, msg was truncated: " << read
366 << " vs " << size;
367 if (read == 0) {
368 LOG(ERROR) << "Client closed the connection";
369 }
370 if (read < 0) {
371 LOG(ERROR) << "Error receiving messages from client: "
372 << socket->StrError();
373 }
374 return read;
375 }
376
377 } // namespace cuttlefish
378