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