• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
CreateSocketPair(SharedFD * local,SharedFD * remote)58 bool CreateSocketPair(SharedFD* local, SharedFD* remote) {
59   auto ret = SharedFD::SocketPair(AF_UNIX, SOCK_SEQPACKET, 0, local, remote);
60   if (!ret) {
61     LOG(ERROR) << "Unable to create socket pair for audio IO signaling: "
62                << (*local)->StrError();
63   }
64   return ret;
65 }
66 
SendStatusCallback(uint32_t buffer_offset,SharedFD socket)67 std::function<void(AudioStatus, uint32_t, uint32_t)> SendStatusCallback(
68     uint32_t buffer_offset, SharedFD socket) {
69   // Consumption of an audio buffer is an asynchronous event, which could
70   // trigger after the client disconnected. A WeakFD ensures that the response
71   // will only be sent if there is still a client available.
72   auto weak_socket = WeakFD(socket);
73   return
74       [buffer_offset, weak_socket](AudioStatus status, uint32_t latency_bytes,
75                                    uint32_t consumed_length) {
76         auto socket = weak_socket.lock();
77         if (!socket->IsOpen()) {
78           return;
79         }
80         IoStatusMsg reply;
81         reply.status.status = Le32(static_cast<uint32_t>(status));
82         reply.status.latency_bytes = Le32(latency_bytes);
83         reply.buffer_offset = buffer_offset;
84         reply.consumed_length = consumed_length;
85         // Send the acknowledgment non-blockingly to avoid a slow client from
86         // blocking the server.
87         auto sent = socket->Send(&reply, sizeof(reply), MSG_DONTWAIT);
88         if (sent < sizeof(reply)) {
89           LOG(ERROR) << "Failed to send entire reply: " << socket->StrError();
90         }
91       };
92 }
93 
94 }  // namespace
95 
AcceptClient(uint32_t num_streams,uint32_t num_jacks,uint32_t num_chmaps,size_t tx_shm_len,size_t rx_shm_len)96 std::unique_ptr<AudioClientConnection> AudioServer::AcceptClient(
97     uint32_t num_streams, uint32_t num_jacks, uint32_t num_chmaps,
98     size_t tx_shm_len, size_t rx_shm_len) {
99   auto conn_fd = SharedFD::Accept(*server_socket_, nullptr, 0);
100   if (!conn_fd->IsOpen()) {
101     LOG(ERROR) << "Connection failed on audio server: " << conn_fd->StrError();
102     return nullptr;
103   }
104   return AudioClientConnection::Create(conn_fd, num_streams, num_jacks,
105                                        num_chmaps, tx_shm_len, rx_shm_len);
106 }
107 
108 /* 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)109 std::unique_ptr<AudioClientConnection> AudioClientConnection::Create(
110     SharedFD client_socket, uint32_t num_streams, uint32_t num_jacks,
111     uint32_t num_chmaps, size_t tx_shm_len, size_t rx_shm_len) {
112   SharedFD event_socket, event_pair;
113   SharedFD tx_socket, tx_pair;
114   SharedFD rx_socket, rx_pair;
115 
116   bool pairs_created = true;
117   pairs_created &= CreateSocketPair(&event_socket, &event_pair);
118   pairs_created &= CreateSocketPair(&tx_socket, &tx_pair);
119   pairs_created &= CreateSocketPair(&rx_socket, &rx_pair);
120   if (!pairs_created) {
121     return nullptr;
122   }
123 
124   SharedFD tx_shm_fd, rx_shm_fd;
125   auto tx_shm =
126       AllocateShm(tx_shm_len, "vios_audio_server_tx_queue", &tx_shm_fd);
127   if (!tx_shm) {
128     return nullptr;
129   }
130   auto rx_shm =
131       AllocateShm(rx_shm_len, "vios_audio_server_rx_queue", &rx_shm_fd);
132   if (!rx_shm) {
133     return nullptr;
134   }
135 
136   VioSConfig welcome_msg = {
137       .version = VIOS_VERSION,
138       .jacks = num_jacks,
139       .streams = num_streams,
140       .chmaps = num_chmaps,
141   };
142 
143   auto sent = client_socket->SendFileDescriptors(
144       &welcome_msg, sizeof(welcome_msg), event_pair, tx_pair, rx_pair,
145       tx_shm_fd, rx_shm_fd);
146   if (sent < 0) {
147     LOG(ERROR) << "Failed to send file descriptors to client: "
148                << client_socket->StrError();
149     return nullptr;
150   }
151 
152   return std::unique_ptr<AudioClientConnection>(new AudioClientConnection(
153       std::move(tx_shm), std::move(rx_shm), client_socket,
154       event_socket, tx_socket, rx_socket));
155 }
156 
ReceiveCommands(AudioServerExecutor & executor)157 bool AudioClientConnection::ReceiveCommands(AudioServerExecutor& executor) {
158   // The largest msg the client will send is 24 bytes long, using uint64_t
159   // guarantees it's aligned to 64 bits.
160   uint64_t recv_buffer[3];
161   auto recv_size =
162       ReceiveMsg(control_socket_, &recv_buffer, sizeof(recv_buffer));
163   if (recv_size <= 0) {
164     return false;
165   }
166   const auto cmd_hdr = reinterpret_cast<const virtio_snd_hdr*>(&recv_buffer[0]);
167   if (recv_size < sizeof(virtio_snd_hdr)) {
168     LOG(ERROR) << "Received control message is too small: " << recv_size;
169     return false;
170   }
171   switch (static_cast<AudioCommandType>(cmd_hdr->code.as_uint32_t())) {
172     case AudioCommandType::VIRTIO_SND_R_PCM_INFO: {
173       if (recv_size < sizeof(virtio_snd_query_info)) {
174         LOG(ERROR) << "Received QUERY_INFO message is too small: " << recv_size;
175         return false;
176       }
177       auto query_info = reinterpret_cast<const virtio_snd_query_info*>(cmd_hdr);
178       auto info_count = query_info->count.as_uint32_t();
179       auto start_id = query_info->start_id.as_uint32_t();
180       std::unique_ptr<virtio_snd_pcm_info[]> reply(
181           new virtio_snd_pcm_info[info_count]);
182       StreamInfoCommand cmd(start_id, info_count, reply.get());
183 
184       executor.StreamsInfo(cmd);
185       return CmdReply(cmd.status(), reply.get(),
186                       info_count * sizeof(reply[0]));
187     }
188     case AudioCommandType::VIRTIO_SND_R_PCM_SET_PARAMS: {
189       if (recv_size < sizeof(virtio_snd_pcm_set_params)) {
190         LOG(ERROR) << "Received SET_PARAMS message is too small: " << recv_size;
191         return false;
192       }
193       auto set_param_msg =
194           reinterpret_cast<const virtio_snd_pcm_set_params*>(cmd_hdr);
195       StreamSetParamsCommand cmd(set_param_msg->hdr.stream_id.as_uint32_t(),
196                                  set_param_msg->buffer_bytes.as_uint32_t(),
197                                  set_param_msg->period_bytes.as_uint32_t(),
198                                  set_param_msg->features.as_uint32_t(),
199                                  set_param_msg->channels, set_param_msg->format,
200                                  set_param_msg->rate);
201       executor.SetStreamParameters(cmd);
202       return CmdReply(cmd.status());
203     }
204     case AudioCommandType::VIRTIO_SND_R_PCM_PREPARE: {
205       if (recv_size < sizeof(virtio_snd_pcm_hdr)) {
206         LOG(ERROR) << "Received PREPARE message is too small: " << recv_size;
207         return false;
208       }
209       auto pcm_op_msg = reinterpret_cast<const virtio_snd_pcm_hdr*>(cmd_hdr);
210       StreamControlCommand cmd(AudioCommandType::VIRTIO_SND_R_PCM_PREPARE,
211                                pcm_op_msg->stream_id.as_uint32_t());
212       executor.PrepareStream(cmd);
213       return CmdReply(cmd.status());
214     }
215     case AudioCommandType::VIRTIO_SND_R_PCM_RELEASE: {
216       if (recv_size < sizeof(virtio_snd_pcm_hdr)) {
217         LOG(ERROR) << "Received RELEASE message is too small: " << recv_size;
218         return false;
219       }
220       auto pcm_op_msg = reinterpret_cast<const virtio_snd_pcm_hdr*>(cmd_hdr);
221       StreamControlCommand cmd(AudioCommandType::VIRTIO_SND_R_PCM_RELEASE,
222                                pcm_op_msg->stream_id.as_uint32_t());
223       executor.ReleaseStream(cmd);
224       return CmdReply(cmd.status());
225     }
226     case AudioCommandType::VIRTIO_SND_R_PCM_START: {
227       if (recv_size < sizeof(virtio_snd_pcm_hdr)) {
228         LOG(ERROR) << "Received START message is too small: " << recv_size;
229         return false;
230       }
231       auto pcm_op_msg = reinterpret_cast<const virtio_snd_pcm_hdr*>(cmd_hdr);
232       StreamControlCommand cmd(AudioCommandType::VIRTIO_SND_R_PCM_START,
233                                pcm_op_msg->stream_id.as_uint32_t());
234       executor.StartStream(cmd);
235       return CmdReply(cmd.status());
236     }
237     case AudioCommandType::VIRTIO_SND_R_PCM_STOP: {
238       if (recv_size < sizeof(virtio_snd_pcm_hdr)) {
239         LOG(ERROR) << "Received STOP message is too small: " << recv_size;
240         return false;
241       }
242       auto pcm_op_msg = reinterpret_cast<const virtio_snd_pcm_hdr*>(cmd_hdr);
243       StreamControlCommand cmd(AudioCommandType::VIRTIO_SND_R_PCM_STOP,
244                                pcm_op_msg->stream_id.as_uint32_t());
245       executor.StopStream(cmd);
246       return CmdReply(cmd.status());
247     }
248     case AudioCommandType::VIRTIO_SND_R_CHMAP_INFO: {
249       if (recv_size < sizeof(virtio_snd_query_info)) {
250         LOG(ERROR) << "Received QUERY_INFO message is too small: " << recv_size;
251         return false;
252       }
253       auto query_info = reinterpret_cast<const virtio_snd_query_info*>(cmd_hdr);
254       auto info_count = query_info->count.as_uint32_t();
255       auto start_id = query_info->start_id.as_uint32_t();
256       std::unique_ptr<virtio_snd_chmap_info[]> reply(
257           new virtio_snd_chmap_info[info_count]);
258       ChmapInfoCommand cmd(start_id, info_count, reply.get());
259 
260       executor.ChmapsInfo(cmd);
261       return CmdReply(cmd.status(), reply.get(),
262                       info_count * sizeof(reply[0]));
263     }
264     case AudioCommandType::VIRTIO_SND_R_JACK_INFO: {
265       if (recv_size < sizeof(virtio_snd_query_info)) {
266         LOG(ERROR) << "Received QUERY_INFO message is too small: " << recv_size;
267         return false;
268       }
269       auto query_info = reinterpret_cast<const virtio_snd_query_info*>(cmd_hdr);
270       auto info_count = query_info->count.as_uint32_t();
271       auto start_id = query_info->start_id.as_uint32_t();
272       std::unique_ptr<virtio_snd_jack_info[]> reply(
273           new virtio_snd_jack_info[info_count]);
274       JackInfoCommand cmd(start_id, info_count, reply.get());
275 
276       executor.JacksInfo(cmd);
277       return CmdReply(cmd.status(), reply.get(),
278                       info_count * sizeof(reply[0]));
279     }
280     case AudioCommandType::VIRTIO_SND_R_JACK_REMAP:
281       LOG(ERROR) << "Unsupported command type: " << cmd_hdr->code.as_uint32_t();
282       return CmdReply(AudioStatus::VIRTIO_SND_S_NOT_SUPP);
283     default:
284       LOG(ERROR) << "Unknown command type: " << cmd_hdr->code.as_uint32_t();
285       return CmdReply(AudioStatus::VIRTIO_SND_S_BAD_MSG);
286   }
287   return true;
288 }
289 
ReceivePlayback(AudioServerExecutor & executor)290 bool AudioClientConnection::ReceivePlayback(AudioServerExecutor& executor) {
291   // The largest msg the client will send is 12 bytes long, using uint32_t
292   // guarantees it's aligned to 32 bits.
293   uint32_t recv_buffer[3];
294   auto recv_size = ReceiveMsg(tx_socket_, &recv_buffer, sizeof(recv_buffer));
295   if (recv_size <= 0) {
296     return false;
297   }
298   const auto msg_hdr = reinterpret_cast<const IoTransferMsg*>(&recv_buffer[0]);
299 
300   if (recv_size < sizeof(IoTransferMsg)) {
301     LOG(ERROR) << "Received PCM_XFER message is too small: " << recv_size;
302     return false;
303   }
304   TxBuffer buffer(msg_hdr->io_xfer,
305                   TxBufferAt(msg_hdr->buffer_offset, msg_hdr->buffer_len),
306                   msg_hdr->buffer_len,
307                   SendStatusCallback(msg_hdr->buffer_offset, tx_socket_));
308   executor.OnPlaybackBuffer(std::move(buffer));
309   return true;
310 }
311 
ReceiveCapture(AudioServerExecutor & executor)312 bool AudioClientConnection::ReceiveCapture(AudioServerExecutor& executor) {
313   uint32_t recv_buffer[3];
314   auto recv_size = ReceiveMsg(rx_socket_, &recv_buffer, sizeof(recv_buffer));
315   if (recv_size <= 0) {
316     return false;
317   }
318   const auto msg_hdr = reinterpret_cast<const IoTransferMsg*>(&recv_buffer[0]);
319   if (recv_size < sizeof(IoTransferMsg)) {
320     LOG(ERROR) << "Received PCM_XFER message is too small: " << recv_size;
321     return false;
322   }
323   RxBuffer buffer(msg_hdr->io_xfer,
324                   RxBufferAt(msg_hdr->buffer_offset, msg_hdr->buffer_len),
325                   msg_hdr->buffer_len,
326                   SendStatusCallback(msg_hdr->buffer_offset, rx_socket_));
327   executor.OnCaptureBuffer(std::move(buffer));
328   return true;
329 }
330 
CmdReply(AudioStatus status,const void * data,size_t size)331 bool AudioClientConnection::CmdReply(AudioStatus status, const void* data,
332                                      size_t size) {
333   virtio_snd_hdr vio_status = {
334       .code = Le32(static_cast<uint32_t>(status)),
335   };
336   std::vector<uint8_t> buffer(sizeof(vio_status) + size, 0);
337   std::memcpy(buffer.data(), &vio_status, sizeof(vio_status));
338   if (data) {
339     std::memcpy(buffer.data() + sizeof(vio_status), data, size);
340   }
341   auto status_sent = control_socket_->Send(buffer.data(), buffer.size(), 0);
342   if (status_sent < sizeof(vio_status) + size) {
343     LOG(ERROR) << "Failed to send entire command status: "
344                << control_socket_->StrError();
345     return false;
346   }
347   return true;
348 }
349 
TxBufferAt(size_t offset,size_t len) const350 const volatile uint8_t* AudioClientConnection::TxBufferAt(size_t offset,
351                                                           size_t len) const {
352   CHECK(offset < tx_shm_.len() && tx_shm_.len() - offset > len)
353       << "Tx buffer bounds outside the buffer area: " << offset << " " << len;
354   const void* ptr = tx_shm_.get();
355   return &reinterpret_cast<const volatile uint8_t*>(ptr)[offset];
356 }
357 
RxBufferAt(size_t offset,size_t len)358 volatile uint8_t* AudioClientConnection::RxBufferAt(size_t offset,
359                                                     size_t len) {
360   CHECK(offset < rx_shm_.len() && rx_shm_.len() - offset > len)
361       << "Rx buffer bounds outside the buffer area: " << offset << " " << len;
362   void* ptr = rx_shm_.get();
363   return &reinterpret_cast<volatile uint8_t*>(ptr)[offset];
364 }
365 
SendEvent()366 bool AudioClientConnection::SendEvent(/*TODO*/) { return false; }
367 
ReceiveMsg(SharedFD socket,void * buffer,size_t size)368 ssize_t AudioClientConnection::ReceiveMsg(SharedFD socket, void* buffer,
369                                           size_t size) {
370   auto read = socket->Recv(buffer, size, MSG_TRUNC);
371   CHECK(read < 0 || read <= size)
372       << "Received a msg bigger than the buffer, msg was truncated: " << read
373       << " vs " << size;
374   if (read == 0) {
375     LOG(ERROR) << "Client closed the connection";
376   }
377   if (read < 0) {
378     LOG(ERROR) << "Error receiving messages from client: "
379                << socket->StrError();
380   }
381   return read;
382 }
383 
384 }  // namespace cuttlefish
385