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(&notif, 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