1// Copyright 2018 The Fuchsia 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#include <audio-proto-utils/format-utils.h> 6#include <ddk/debug.h> 7#include <fbl/limits.h> 8#include <lib/simple-audio-stream/simple-audio-stream.h> 9#include <zircon/device/audio.h> 10 11namespace audio { 12 13void SimpleAudioStream::Shutdown() { 14 if (domain_ != nullptr) { 15 domain_->Deactivate(); 16 17 } 18 19 { 20 // Now that we know our domain has been deactivated, it should be safe to 21 // assert that we are holding the domain token (assuming that users of 22 // Shutdown behave in a single threaded fashion) 23 OBTAIN_EXECUTION_DOMAIN_TOKEN(t, domain_); 24 ShutdownHook(); 25 } 26} 27 28zx_status_t SimpleAudioStream::CreateInternal() { 29 zx_status_t res; 30 31 ZX_DEBUG_ASSERT(domain_ == nullptr); 32 { 33 // We have not created the domain yet, it should be safe to pretend that 34 // we have the token (since we know that no dispatches are going to be 35 // invoked from the non-existent domain at this point) 36 OBTAIN_EXECUTION_DOMAIN_TOKEN(t, domain_); 37 res = Init(); 38 if (res != ZX_OK) { 39 zxlogf(ERROR, "Init failure in %s (res %d)\n", __PRETTY_FUNCTION__, res); 40 return res; 41 } 42 } 43 44 domain_ = dispatcher::ExecutionDomain::Create(); 45 if (domain_ == nullptr) { 46 zxlogf(ERROR, "Failed to create execution domain in %s\n", __PRETTY_FUNCTION__); 47 return ZX_ERR_NO_MEMORY; 48 } 49 50 res = InitPost(); 51 if (res != ZX_OK) { 52 zxlogf(ERROR, "InitPost failure in %s (res %d)\n", __PRETTY_FUNCTION__, res); 53 return res; 54 } 55 56 res = PublishInternal(); 57 if (res != ZX_OK) { 58 zxlogf(ERROR, "Publish failure in %s (res %d)\n", __PRETTY_FUNCTION__, res); 59 return res; 60 } 61 62 return ZX_OK; 63} 64 65zx_status_t SimpleAudioStream::PublishInternal() { 66 device_name_[sizeof(device_name_) - 1] = 0; 67 if (!strlen(device_name_)) { 68 return ZX_ERR_BAD_STATE; 69 } 70 71 // If we succeed in adding our device, add an explicit reference to 72 // ourselves to represent the reference now being held by the DDK. We will 73 // get this reference back when the DDK (eventually) calls release. 74 zx_status_t res = DdkAdd(device_name_); 75 if (res == ZX_OK) { 76 AddRef(); 77 } 78 79 return res; 80} 81 82zx_status_t SimpleAudioStream::NotifyPosition(const audio_proto::RingBufPositionNotify& notif) { 83 fbl::AutoLock channel_lock(&channel_lock_); 84 85 if (!expected_notifications_per_ring_.load() || (rb_channel_ == nullptr)) { 86 return ZX_ERR_BAD_STATE; 87 } 88 89 return rb_channel_->Write(¬if, sizeof(notif)); 90} 91 92void SimpleAudioStream::DdkUnbind() { 93 Shutdown(); 94 95 // TODO(johngro): We need to signal our SimpleAudioStream owner to let them 96 // know that we have been unbound and are in the process of shutting down. 97 98 // Unpublish our device node. 99 DdkRemove(); 100} 101 102void SimpleAudioStream::DdkRelease() { 103 // Recover our ref from the DDK, then let it fall out of scope. 104 auto thiz = fbl::internal::MakeRefPtrNoAdopt(this); 105} 106 107zx_status_t SimpleAudioStream::DdkIoctl(uint32_t op, const void* in_buf, size_t in_len, 108 void* out_buf, size_t out_len, size_t* out_actual) { 109 // The only IOCTL we support is get channel. 110 if (op != AUDIO_IOCTL_GET_CHANNEL) { 111 return ZX_ERR_NOT_SUPPORTED; 112 } 113 114 if ((out_buf == nullptr) || 115 (out_actual == nullptr) || 116 (out_len != sizeof(zx_handle_t))) { 117 return ZX_ERR_INVALID_ARGS; 118 } 119 120 fbl::AutoLock channel_lock(&channel_lock_); 121 122 // Attempt to allocate a new driver channel and bind it to us. If we don't 123 // already have an stream_channel_, flag this channel is the privileged 124 // connection (The connection which is allowed to do things like change 125 // formats). 126 bool privileged = (stream_channel_ == nullptr); 127 auto channel = dispatcher::Channel::Create(); 128 if (channel == nullptr) 129 return ZX_ERR_NO_MEMORY; 130 131 dispatcher::Channel::ProcessHandler phandler( 132 [ stream = fbl::WrapRefPtr(this), privileged ](dispatcher::Channel * channel)->zx_status_t { 133 OBTAIN_EXECUTION_DOMAIN_TOKEN(t, stream->domain_); 134 return stream->ProcessStreamChannel(channel, privileged); 135 }); 136 137 dispatcher::Channel::ChannelClosedHandler chandler; 138 if (privileged) { 139 chandler = dispatcher::Channel::ChannelClosedHandler( 140 [stream = fbl::WrapRefPtr(this)](const dispatcher::Channel* channel)->void { 141 OBTAIN_EXECUTION_DOMAIN_TOKEN(t, stream->domain_); 142 fbl::AutoLock channel_lock(&stream->channel_lock_); 143 stream->DeactivateStreamChannel(channel); 144 }); 145 } 146 147 zx::channel client_endpoint; 148 zx_status_t res = channel->Activate(&client_endpoint, 149 domain_, 150 fbl::move(phandler), 151 fbl::move(chandler)); 152 if (res == ZX_OK) { 153 if (privileged) { 154 ZX_DEBUG_ASSERT(stream_channel_ == nullptr); 155 stream_channel_ = channel; 156 } 157 158 *(reinterpret_cast<zx_handle_t*>(out_buf)) = client_endpoint.release(); 159 *out_actual = sizeof(zx_handle_t); 160 } 161 162 return res; 163} 164 165#define HREQ(_cmd, _payload, _handler, _allow_noack, ...) \ 166 case _cmd: \ 167 if (req_size != sizeof(req._payload)) { \ 168 zxlogf(ERROR, "Bad " #_cmd \ 169 " response length (%u != %zu)\n", \ 170 req_size, sizeof(req._payload)); \ 171 return ZX_ERR_INVALID_ARGS; \ 172 } \ 173 if (!_allow_noack && (req.hdr.cmd & AUDIO_FLAG_NO_ACK)) { \ 174 zxlogf(ERROR, "NO_ACK flag not allowed for " #_cmd "\n"); \ 175 return ZX_ERR_INVALID_ARGS; \ 176 } \ 177 return _handler(channel, req._payload, ##__VA_ARGS__); 178zx_status_t SimpleAudioStream::ProcessStreamChannel(dispatcher::Channel* channel, bool privileged) { 179 ZX_DEBUG_ASSERT(channel != nullptr); 180 181 union { 182 audio_proto::CmdHdr hdr; 183 audio_proto::StreamGetFmtsReq get_formats; 184 audio_proto::StreamSetFmtReq set_format; 185 audio_proto::GetGainReq get_gain; 186 audio_proto::SetGainReq set_gain; 187 audio_proto::PlugDetectReq plug_detect; 188 audio_proto::GetUniqueIdReq get_unique_id; 189 audio_proto::GetStringReq get_string; 190 } req; 191 192 static_assert(sizeof(req) <= 256, 193 "Request buffer is getting to be too large to hold on the stack!"); 194 195 uint32_t req_size; 196 zx_status_t res = channel->Read(&req, sizeof(req), &req_size); 197 if (res != ZX_OK) 198 return res; 199 200 if ((req_size < sizeof(req.hdr) || 201 (req.hdr.transaction_id == AUDIO_INVALID_TRANSACTION_ID))) 202 return ZX_ERR_INVALID_ARGS; 203 204 // Strip the NO_ACK flag from the request before selecting the dispatch target. 205 auto cmd = static_cast<audio_proto::Cmd>(req.hdr.cmd & ~AUDIO_FLAG_NO_ACK); 206 switch (cmd) { 207 HREQ(AUDIO_STREAM_CMD_GET_FORMATS, get_formats, OnGetStreamFormats, false); 208 HREQ(AUDIO_STREAM_CMD_SET_FORMAT, set_format, OnSetStreamFormat, false, privileged); 209 HREQ(AUDIO_STREAM_CMD_GET_GAIN, get_gain, OnGetGain, false); 210 HREQ(AUDIO_STREAM_CMD_SET_GAIN, set_gain, OnSetGain, true); 211 HREQ(AUDIO_STREAM_CMD_PLUG_DETECT, plug_detect, OnPlugDetect, true); 212 HREQ(AUDIO_STREAM_CMD_GET_UNIQUE_ID, get_unique_id, OnGetUniqueId, false); 213 HREQ(AUDIO_STREAM_CMD_GET_STRING, get_string, OnGetString, false); 214 default: 215 zxlogf(ERROR, "Unrecognized stream command 0x%04x\n", req.hdr.cmd); 216 return ZX_ERR_NOT_SUPPORTED; 217 } 218} 219 220zx_status_t SimpleAudioStream::ProcessRingBufferChannel(dispatcher::Channel* channel) { 221 ZX_DEBUG_ASSERT(channel != nullptr); 222 223 union { 224 audio_proto::CmdHdr hdr; 225 audio_proto::RingBufGetFifoDepthReq get_fifo_depth; 226 audio_proto::RingBufGetBufferReq get_buffer; 227 audio_proto::RingBufStartReq rb_start; 228 audio_proto::RingBufStopReq rb_stop; 229 } req; 230 231 static_assert(sizeof(req) <= 256, 232 "Request buffer is getting to be too large to hold on the stack!"); 233 234 uint32_t req_size; 235 zx_status_t res = channel->Read(&req, sizeof(req), &req_size); 236 if (res != ZX_OK) 237 return res; 238 239 if ((req_size < sizeof(req.hdr) || 240 (req.hdr.transaction_id == AUDIO_INVALID_TRANSACTION_ID))) 241 return ZX_ERR_INVALID_ARGS; 242 243 // Strip the NO_ACK flag from the request before selecting the dispatch target. 244 auto cmd = static_cast<audio_proto::Cmd>(req.hdr.cmd & ~AUDIO_FLAG_NO_ACK); 245 switch (cmd) { 246 HREQ(AUDIO_RB_CMD_GET_FIFO_DEPTH, get_fifo_depth, OnGetFifoDepth, false); 247 HREQ(AUDIO_RB_CMD_GET_BUFFER, get_buffer, OnGetBuffer, false); 248 HREQ(AUDIO_RB_CMD_START, rb_start, OnStart, false); 249 HREQ(AUDIO_RB_CMD_STOP, rb_stop, OnStop, false); 250 default: 251 zxlogf(ERROR, "Unrecognized ring buffer command 0x%04x\n", req.hdr.cmd); 252 return ZX_ERR_NOT_SUPPORTED; 253 } 254} 255#undef HREQ 256 257void SimpleAudioStream::DeactivateStreamChannel(const dispatcher::Channel* channel) { 258 if (stream_channel_.get() == channel) { 259 stream_channel_ = nullptr; 260 } 261} 262 263void SimpleAudioStream::DeactivateRingBufferChannel(const dispatcher::Channel* channel) { 264 if (channel == rb_channel_.get()) { 265 if (rb_started_) { 266 Stop(); 267 rb_started_ = false; 268 } 269 rb_fetched_ = false; 270 expected_notifications_per_ring_.store(0); 271 rb_channel_.reset(); 272 } 273} 274 275zx_status_t SimpleAudioStream::OnGetStreamFormats(dispatcher::Channel* channel, 276 const audio_proto::StreamGetFmtsReq& req) const { 277 ZX_DEBUG_ASSERT(channel != nullptr); 278 uint16_t formats_sent = 0; 279 audio_proto::StreamGetFmtsResp resp = { }; 280 281 if (supported_formats_.size() > fbl::numeric_limits<uint16_t>::max()) { 282 zxlogf(ERROR, 283 "Too many formats (%zu) to send during AUDIO_STREAM_CMD_GET_FORMATS request!\n", 284 supported_formats_.size()); 285 return ZX_ERR_INTERNAL; 286 } 287 288 resp.hdr = req.hdr; 289 resp.format_range_count = static_cast<uint16_t>(supported_formats_.size()); 290 291 do { 292 uint16_t todo, payload_sz; 293 zx_status_t res; 294 295 todo = fbl::min<uint16_t>(static_cast<uint16_t>(supported_formats_.size() - formats_sent), 296 AUDIO_STREAM_CMD_GET_FORMATS_MAX_RANGES_PER_RESPONSE); 297 payload_sz = static_cast<uint16_t>(sizeof(resp.format_ranges[0]) * todo); 298 299 resp.first_format_range_ndx = formats_sent; 300 ::memcpy(resp.format_ranges, supported_formats_.get() + formats_sent, payload_sz); 301 302 res = channel->Write(&resp, sizeof(resp)); 303 if (res != ZX_OK) { 304 zxlogf(ERROR, "Failed to send get stream formats response (res %d)\n", res); 305 return res; 306 } 307 308 formats_sent = (uint16_t)(formats_sent + todo); 309 } while (formats_sent < supported_formats_.size()); 310 311 return ZX_OK; 312} 313 314zx_status_t SimpleAudioStream::OnSetStreamFormat(dispatcher::Channel* channel, 315 const audio_proto::StreamSetFmtReq& req, 316 bool privileged) { 317 ZX_DEBUG_ASSERT(channel != nullptr); 318 319 zx::channel client_rb_channel; 320 audio_proto::StreamSetFmtResp resp = { }; 321 bool found_one = false; 322 resp.hdr = req.hdr; 323 324 // Only the privileged stream channel is allowed to change the format. 325 if (!privileged) { 326 resp.result = ZX_ERR_ACCESS_DENIED; 327 goto finished; 328 } 329 330 // Check the format for compatibility 331 for (const auto& fmt : supported_formats_) { 332 if (audio::utils::FormatIsCompatible(req.frames_per_second, 333 req.channels, 334 req.sample_format, 335 fmt)) { 336 found_one = true; 337 break; 338 } 339 } 340 341 if (!found_one) { 342 resp.result = ZX_ERR_INVALID_ARGS; 343 goto finished; 344 } 345 346 // Determine the frame size. 347 frame_size_ = audio::utils::ComputeFrameSize(req.channels, req.sample_format); 348 if (!frame_size_) { 349 zxlogf(ERROR, "Failed to compute frame size (ch %hu fmt 0x%08x)\n", 350 req.channels, req.sample_format); 351 resp.result = ZX_ERR_INTERNAL; 352 goto finished; 353 } 354 355 // Looks like we are going ahead with this format change. Tear down any 356 // exiting ring buffer interface before proceeding. 357 { 358 fbl::AutoLock channel_lock(&channel_lock_); 359 if (rb_channel_ != nullptr) { 360 rb_channel_->Deactivate(); 361 DeactivateRingBufferChannel(rb_channel_.get()); 362 ZX_DEBUG_ASSERT(rb_channel_ == nullptr); 363 } 364 } 365 366 // Actually attempt to change the format. 367 resp.result = ChangeFormat(req); 368 if (resp.result != ZX_OK) { 369 goto finished; 370 } 371 372 // Create a new ring buffer channel which can be used to move bulk data and 373 // bind it to us. 374 { 375 fbl::AutoLock channel_lock(&channel_lock_); 376 rb_channel_ = dispatcher::Channel::Create(); 377 if (rb_channel_ == nullptr) { 378 resp.result = ZX_ERR_NO_MEMORY; 379 } else { 380 dispatcher::Channel::ProcessHandler phandler( 381 [stream = fbl::WrapRefPtr(this)](dispatcher::Channel * channel) -> zx_status_t { 382 OBTAIN_EXECUTION_DOMAIN_TOKEN(t, stream->domain_); 383 return stream->ProcessRingBufferChannel(channel); 384 }); 385 386 dispatcher::Channel::ChannelClosedHandler chandler( 387 [stream = fbl::WrapRefPtr(this)](const dispatcher::Channel* channel) -> void { 388 OBTAIN_EXECUTION_DOMAIN_TOKEN(t, stream->domain_); 389 fbl::AutoLock channel_lock(&stream->channel_lock_); 390 stream->DeactivateRingBufferChannel(channel); 391 }); 392 393 resp.result = rb_channel_->Activate(&client_rb_channel, 394 domain_, 395 fbl::move(phandler), 396 fbl::move(chandler)); 397 if (resp.result != ZX_OK) { 398 rb_channel_.reset(); 399 } 400 } 401 } 402 403finished: 404 if (resp.result == ZX_OK) { 405 resp.external_delay_nsec = external_delay_nsec_; 406 return channel->Write(&resp, sizeof(resp), fbl::move(client_rb_channel)); 407 } else { 408 return channel->Write(&resp, sizeof(resp)); 409 } 410} 411 412zx_status_t SimpleAudioStream::OnGetGain(dispatcher::Channel* channel, 413 const audio_proto::GetGainReq& req) const { 414 ZX_DEBUG_ASSERT(channel != nullptr); 415 audio_proto::GetGainResp resp = cur_gain_state_; 416 resp.hdr = req.hdr; 417 return channel->Write(&resp, sizeof(resp)); 418} 419 420zx_status_t SimpleAudioStream::OnSetGain(dispatcher::Channel* channel, 421 const audio_proto::SetGainReq& req) { 422 audio_proto::SetGainResp resp; 423 resp.hdr = req.hdr; 424 425 // Sanity check the request before passing it along 426 if ((req.flags & AUDIO_SGF_MUTE_VALID) && (req.flags & AUDIO_SGF_MUTE) && 427 !cur_gain_state_.can_mute) { 428 resp.result = ZX_ERR_NOT_SUPPORTED; 429 goto finished; 430 } 431 432 if ((req.flags & AUDIO_SGF_AGC_VALID) && (req.flags & AUDIO_SGF_AGC) && 433 !cur_gain_state_.can_agc) { 434 resp.result = ZX_ERR_NOT_SUPPORTED; 435 goto finished; 436 } 437 438 if ((req.flags & AUDIO_SGF_GAIN_VALID) && 439 ((req.gain < cur_gain_state_.min_gain) || (req.gain > cur_gain_state_.max_gain))) { 440 resp.result = ZX_ERR_INVALID_ARGS; 441 goto finished; 442 } 443 444 resp.result = SetGain(req); 445 446finished: 447 resp.cur_mute = cur_gain_state_.cur_mute; 448 resp.cur_agc = cur_gain_state_.cur_agc; 449 resp.cur_gain = cur_gain_state_.cur_gain; 450 return (req.hdr.cmd & AUDIO_FLAG_NO_ACK) ? ZX_OK : channel->Write(&resp, sizeof(resp)); 451} 452 453zx_status_t SimpleAudioStream::OnPlugDetect(dispatcher::Channel* channel, 454 const audio_proto::PlugDetectReq& req) { 455 456 if (req.hdr.cmd & AUDIO_FLAG_NO_ACK) 457 return ZX_OK; 458 459 audio_proto::PlugDetectResp resp = { }; 460 resp.hdr = req.hdr; 461 resp.flags = static_cast<audio_pd_notify_flags_t>(AUDIO_PDNF_HARDWIRED | 462 AUDIO_PDNF_PLUGGED); 463 return channel->Write(&resp, sizeof(resp)); 464} 465 466zx_status_t SimpleAudioStream::OnGetUniqueId(dispatcher::Channel* channel, 467 const audio_proto::GetUniqueIdReq& req) const { 468 audio_proto::GetUniqueIdResp resp; 469 470 resp.hdr = req.hdr; 471 resp.unique_id = unique_id_; 472 473 return channel->Write(&resp, sizeof(resp)); 474} 475 476zx_status_t SimpleAudioStream::OnGetString(dispatcher::Channel* channel, 477 const audio_proto::GetStringReq& req) const { 478 audio_proto::GetStringResp resp; 479 480 resp.hdr = req.hdr; 481 resp.id = req.id; 482 483 const char* str; 484 switch (req.id) { 485 case AUDIO_STREAM_STR_ID_MANUFACTURER: str = mfr_name_; break; 486 case AUDIO_STREAM_STR_ID_PRODUCT: str = prod_name_; break; 487 default: str = nullptr; break; 488 } 489 490 if (str == nullptr) { 491 resp.result = ZX_ERR_NOT_FOUND; 492 resp.strlen = 0; 493 } else { 494 int res = snprintf(reinterpret_cast<char*>(resp.str), sizeof(resp.str), "%s", str); 495 ZX_DEBUG_ASSERT(res >= 0); 496 resp.result = ZX_OK; 497 resp.strlen = fbl::min<uint32_t>(res, sizeof(resp.str) - 1); 498 } 499 500 return channel->Write(&resp, sizeof(resp)); 501} 502 503zx_status_t SimpleAudioStream::OnGetFifoDepth(dispatcher::Channel* channel, 504 const audio_proto::RingBufGetFifoDepthReq& req) { 505 audio_proto::RingBufGetFifoDepthResp resp = {}; 506 507 resp.hdr = req.hdr; 508 resp.result = ZX_OK; 509 resp.fifo_depth = fifo_depth_; 510 511 return channel->Write(&resp, sizeof(resp)); 512} 513 514zx_status_t SimpleAudioStream::OnGetBuffer(dispatcher::Channel* channel, 515 const audio_proto::RingBufGetBufferReq& req) { 516 audio_proto::RingBufGetBufferResp resp = {}; 517 resp.hdr = req.hdr; 518 519 if (rb_started_) { 520 resp.result = ZX_ERR_BAD_STATE; 521 } else { 522 zx::vmo buffer; 523 resp.result = GetBuffer(req, &resp.num_ring_buffer_frames, &buffer); 524 if (resp.result == ZX_OK) { 525 zx_status_t res = channel->Write(&resp, sizeof(resp), fbl::move(buffer)); 526 if (res == ZX_OK) { 527 expected_notifications_per_ring_.store(req.notifications_per_ring); 528 rb_fetched_ = true; 529 } 530 return res; 531 } else { 532 expected_notifications_per_ring_.store(0); 533 } 534 } 535 536 ZX_DEBUG_ASSERT(resp.result != ZX_OK); 537 return channel->Write(&resp, sizeof(resp)); 538} 539 540zx_status_t SimpleAudioStream::OnStart(dispatcher::Channel* channel, 541 const audio_proto::RingBufStartReq& req) { 542 audio_proto::RingBufStartResp resp = {}; 543 resp.hdr = req.hdr; 544 545 if (rb_started_ || !rb_fetched_) { 546 resp.result = ZX_ERR_BAD_STATE; 547 } else { 548 resp.result = Start(&resp.start_time); 549 if (resp.result == ZX_OK) { 550 rb_started_ = true; 551 } 552 } 553 554 return channel->Write(&resp, sizeof(resp)); 555} 556 557zx_status_t SimpleAudioStream::OnStop(dispatcher::Channel* channel, 558 const audio_proto::RingBufStopReq& req) { 559 audio_proto::RingBufStopResp resp = {}; 560 resp.hdr = req.hdr; 561 562 if (!rb_started_) { 563 resp.result = ZX_ERR_BAD_STATE; 564 } else { 565 resp.result = Stop(); 566 if (resp.result == ZX_OK) { 567 rb_started_ = false; 568 } 569 } 570 571 return channel->Write(&resp, sizeof(resp)); 572} 573 574} // namespace audio 575