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 <fbl/auto_call.h>
6 
7 #include <zircon/device/audio.h>
8 
9 #include <audio-proto-utils/format-utils.h>
10 
11 #include <utility>
12 
13 #include "intel-audio-dsp.h"
14 #include "intel-dsp-stream.h"
15 
16 namespace audio {
17 namespace intel_hda {
18 
IntelDspStream(uint32_t id,bool is_input,const DspPipeline & pipeline,const audio_stream_unique_id_t * unique_id)19 IntelDspStream::IntelDspStream(uint32_t id,
20                                bool is_input,
21                                const DspPipeline& pipeline,
22                                const audio_stream_unique_id_t* unique_id)
23     : IntelHDAStreamBase(id, is_input), pipeline_(pipeline) {
24     snprintf(log_prefix_, sizeof(log_prefix_), "IHDA DSP %cStream #%u", is_input ? 'I' : 'O', id);
25 
26     if (unique_id) {
27         SetPersistentUniqueId(*unique_id);
28     } else {
29         const audio_stream_unique_id_t uid = {
30             'I', 'D', 'S', 'P',
31             static_cast<uint8_t>(id >> 24),
32             static_cast<uint8_t>(id >> 16),
33             static_cast<uint8_t>(id >> 8),
34             static_cast<uint8_t>(id),
35             static_cast<uint8_t>(is_input),
36             0
37         };
38         SetPersistentUniqueId(uid);
39     }
40 }
41 
ProcessSetStreamFmt(const ihda_proto::SetStreamFmtResp & codec_resp,zx::channel && ring_buffer_channel)42 zx_status_t IntelDspStream::ProcessSetStreamFmt(const ihda_proto::SetStreamFmtResp& codec_resp,
43                                                 zx::channel&& ring_buffer_channel) {
44     ZX_DEBUG_ASSERT(ring_buffer_channel.is_valid());
45 
46     fbl::AutoLock lock(obj_lock());
47     audio_proto::StreamSetFmtResp resp = { };
48     zx_status_t res = ZX_OK;
49 
50     // Are we shutting down?
51     if (!is_active()) {
52         return ZX_ERR_BAD_STATE;
53     }
54 
55     // The DSP needs to coordinate with ring buffer commands. Set up an additional
56     // channel to intercept messages on the ring buffer channel.
57     zx::channel client_endpoint;
58     res = CreateClientRingBufferChannelLocked(std::move(ring_buffer_channel), &client_endpoint);
59     if (res != ZX_OK) {
60         LOG(ERROR, "Failed to set up client ring buffer channel (res %d)\n", res);
61         goto finished;
62     }
63 
64     // Let the implementation send the commands required to finish changing the
65     // stream format.
66     res = FinishChangeStreamFormatLocked(encoded_fmt());
67     if (res != ZX_OK) {
68         LOG(ERROR, "Failed to finish set format (enc fmt 0x%04hx res %d)\n", encoded_fmt(), res);
69         goto finished;
70     }
71 
72     ZX_DEBUG_ASSERT(client_endpoint.is_valid());
73 
74     // Respond to the caller, transferring the DMA handle back in the process.
75     resp.hdr.cmd = AUDIO_STREAM_CMD_SET_FORMAT;
76     resp.hdr.transaction_id = set_format_tid();
77     resp.result = ZX_OK;
78     resp.external_delay_nsec = 0;   // report his properly based on the codec path delay.
79     res = stream_channel()->Write(&resp, sizeof(resp), std::move(client_endpoint));
80 
81     // If we don't have a set format operation in flight, or the stream channel
82     // has been closed, this set format operation has been canceled.  Do not
83     // return an error up the stack; we don't want to close the connection to
84     // our codec device.
85     if ((set_format_tid() == AUDIO_INVALID_TRANSACTION_ID) ||
86         (stream_channel() == nullptr)) {
87         goto finished;
88     }
89 
90 finished:
91     // Something went fatally wrong when trying to send the result back to the
92     // caller.  Close the stream channel.
93     if ((res != ZX_OK) && (stream_channel() != nullptr)) {
94         OnChannelDeactivateLocked(*stream_channel());
95         stream_channel()->Deactivate();
96         stream_channel() = nullptr;
97     }
98 
99     // One way or the other, this set format operation is finished.  Clear out
100     // the in-flight transaction ID
101     SetFormatTidLocked(AUDIO_INVALID_TRANSACTION_ID);
102 
103     return ZX_OK;
104 }
105 
CreateClientRingBufferChannelLocked(zx::channel && ring_buffer_channel,zx::channel * out_client_channel)106 zx_status_t IntelDspStream::CreateClientRingBufferChannelLocked(
107         zx::channel&& ring_buffer_channel,
108         zx::channel* out_client_channel) {
109     // Attempt to allocate a new ring buffer channel and bind it to us.
110     // This channel is connected to the upstream device.
111     auto channel = dispatcher::Channel::Create();
112     if (channel == nullptr) {
113         return ZX_ERR_NO_MEMORY;
114     }
115 
116     dispatcher::Channel::ProcessHandler phandler(
117     [stream = fbl::WrapRefPtr(this)](dispatcher::Channel* channel) -> zx_status_t {
118         OBTAIN_EXECUTION_DOMAIN_TOKEN(t, stream->domain());
119         return stream->ProcessRbRequest(channel);
120     });
121 
122     dispatcher::Channel::ChannelClosedHandler chandler(
123     [stream = fbl::WrapRefPtr(this)](const dispatcher::Channel* channel) -> void {
124         OBTAIN_EXECUTION_DOMAIN_TOKEN(t, stream->domain());
125         stream->ProcessRbDeactivate(channel);
126     });
127 
128     zx_status_t res = channel->Activate(std::move(ring_buffer_channel),
129                                         domain(),
130                                         std::move(phandler),
131                                         std::move(chandler));
132     if (res != ZX_OK) {
133         return res;
134     }
135     ZX_DEBUG_ASSERT(rb_channel_ == nullptr);
136     rb_channel_ = channel;
137 
138     // Attempt to allocate a new ring buffer channel and bind it to us.
139     // This channel is connected to the client.
140     auto client_channel = dispatcher::Channel::Create();
141     if (client_channel == nullptr) {
142         rb_channel_->Deactivate();
143         rb_channel_ = nullptr;
144         return ZX_ERR_NO_MEMORY;
145     }
146 
147     dispatcher::Channel::ProcessHandler client_phandler(
148     [stream = fbl::WrapRefPtr(this)](dispatcher::Channel* channel) -> zx_status_t {
149         OBTAIN_EXECUTION_DOMAIN_TOKEN(t, stream->domain());
150         return stream->ProcessClientRbRequest(channel);
151     });
152 
153     dispatcher::Channel::ChannelClosedHandler client_chandler(
154     [stream = fbl::WrapRefPtr(this)](const dispatcher::Channel* channel) -> void {
155         OBTAIN_EXECUTION_DOMAIN_TOKEN(t, stream->domain());
156         stream->ProcessClientRbDeactivate(channel);
157     });
158 
159     res = client_channel->Activate(out_client_channel,
160                                    domain(),
161                                    std::move(client_phandler),
162                                    std::move(client_chandler));
163     if (res == ZX_OK) {
164         ZX_DEBUG_ASSERT(client_rb_channel_ == nullptr);
165         client_rb_channel_ = client_channel;
166     } else {
167         rb_channel_->Deactivate();
168         rb_channel_ = nullptr;
169     }
170 
171     return res;
172 }
173 
ProcessRbRequest(dispatcher::Channel * channel)174 zx_status_t IntelDspStream::ProcessRbRequest(dispatcher::Channel* channel) {
175     ZX_DEBUG_ASSERT(channel != nullptr);
176     fbl::AutoLock lock(obj_lock());
177 
178     // If we have lost our connection to the codec device, or are in the process
179     // of shutting down, there is nothing further we can do.  Fail the request
180     // and close the connection to the caller.
181     if (!is_active() || (rb_channel_ == nullptr) || (client_rb_channel_ == nullptr)) {
182         return ZX_ERR_BAD_STATE;
183     }
184 
185     zx::handle rxed_handle;
186     uint32_t req_size;
187     union {
188         audio_proto::CmdHdr                  hdr;
189         audio_proto::RingBufGetFifoDepthResp get_fifo_depth;
190         audio_proto::RingBufGetBufferResp    get_buffer;
191         audio_proto::RingBufStartResp        start;
192         audio_proto::RingBufStopResp         stop;
193     } req;
194     // TODO(johngro) : How large is too large?
195     static_assert(sizeof(req) <= 256, "Request buffer is too large to hold on the stack!");
196 
197     zx_status_t res = channel->Read(&req, sizeof(req), &req_size, &rxed_handle);
198     if (res != ZX_OK) {
199         return res;
200     }
201 
202     switch (req.hdr.cmd) {
203     case AUDIO_RB_CMD_START:
204     {
205         auto dsp = fbl::RefPtr<IntelAudioDsp>::Downcast(parent_codec());
206         zx_status_t st = dsp->StartPipeline(pipeline_);
207         if (st != ZX_OK) {
208             audio_proto::RingBufStartResp resp = { };
209             resp.hdr = req.hdr;
210             resp.result = st;
211             return client_rb_channel_->Write(&resp, sizeof(resp));
212         }
213         break;
214     }
215     default:
216         break;
217     }
218 
219     return client_rb_channel_->Write(&req, req_size, std::move(rxed_handle));
220 }
221 
ProcessRbDeactivate(const dispatcher::Channel * channel)222 void IntelDspStream::ProcessRbDeactivate(const dispatcher::Channel* channel) {
223     ZX_DEBUG_ASSERT(channel != nullptr);
224     fbl::AutoLock lock(obj_lock());
225 
226     LOG(TRACE, "ProcessClientRbDeactivate\n");
227 
228     ZX_DEBUG_ASSERT(channel == rb_channel_.get());
229     rb_channel_->Deactivate();
230     rb_channel_ = nullptr;
231 
232     // Deactivate the client channel.
233     if (client_rb_channel_ != nullptr) {
234         client_rb_channel_->Deactivate();
235         client_rb_channel_ = nullptr;
236     }
237 }
238 
ProcessClientRbRequest(dispatcher::Channel * channel)239 zx_status_t IntelDspStream::ProcessClientRbRequest(dispatcher::Channel* channel) {
240     ZX_DEBUG_ASSERT(channel != nullptr);
241     fbl::AutoLock lock(obj_lock());
242 
243     // If we have lost our connection to the codec device, or are in the process
244     // of shutting down, there is nothing further we can do.  Fail the request
245     // and close the connection to the caller.
246     if (!is_active() || (rb_channel_ == nullptr) || (client_rb_channel_ == nullptr)) {
247         return ZX_ERR_BAD_STATE;
248     }
249 
250     uint32_t req_size;
251     union {
252         audio_proto::CmdHdr                 hdr;
253         audio_proto::RingBufGetFifoDepthReq get_fifo_depth;
254         audio_proto::RingBufGetBufferReq    get_buffer;
255         audio_proto::RingBufStartReq        start;
256         audio_proto::RingBufStopReq         stop;
257     } req;
258     // TODO(johngro) : How large is too large?
259     static_assert(sizeof(req) <= 256, "Request buffer is too large to hold on the stack!");
260 
261     zx_status_t res = channel->Read(&req, sizeof(req), &req_size);
262     if (res != ZX_OK) {
263         return res;
264     }
265 
266     switch (req.hdr.cmd) {
267     case AUDIO_RB_CMD_STOP:
268     {
269         auto dsp = fbl::RefPtr<IntelAudioDsp>::Downcast(parent_codec());
270         zx_status_t st = dsp->PausePipeline(pipeline_);
271         if (st != ZX_OK) {
272             audio_proto::RingBufStopResp resp = { };
273             resp.hdr = req.hdr;
274             resp.result = st;
275             return channel->Write(&resp, sizeof(resp));
276         }
277         break;
278     }
279     default:
280         break;
281     }
282 
283     return rb_channel_->Write(&req, req_size);
284 }
285 
ProcessClientRbDeactivate(const dispatcher::Channel * channel)286 void IntelDspStream::ProcessClientRbDeactivate(const dispatcher::Channel* channel) {
287     ZX_DEBUG_ASSERT(channel != nullptr);
288     fbl::AutoLock lock(obj_lock());
289 
290     LOG(TRACE, "ProcessClientRbDeactivate\n");
291 
292     ZX_DEBUG_ASSERT(channel == client_rb_channel_.get());
293     client_rb_channel_->Deactivate();
294     client_rb_channel_ = nullptr;
295 
296     // Deactivate the upstream channel.
297     if (rb_channel_ != nullptr) {
298         rb_channel_->Deactivate();
299         rb_channel_ = nullptr;
300     }
301 }
302 
OnActivateLocked()303 zx_status_t IntelDspStream::OnActivateLocked() {
304     // FIXME(yky) Hardcode supported formats.
305     audio_stream_format_range_t fmt;
306     fmt.sample_formats = AUDIO_SAMPLE_FORMAT_16BIT;
307     fmt.min_frames_per_second =
308     fmt.max_frames_per_second = 48000;
309     fmt.min_channels =
310     fmt.max_channels = 2;
311     fmt.flags = ASF_RANGE_FLAG_FPS_48000_FAMILY;
312 
313     fbl::Vector<audio_proto::FormatRange> supported_formats;
314     fbl::AllocChecker ac;
315     supported_formats.push_back(fmt, &ac);
316     if (!ac.check()) {
317         return ZX_ERR_NO_MEMORY;
318     }
319 
320     SetSupportedFormatsLocked(std::move(supported_formats));
321     return ZX_OK;
322 }
323 
OnDeactivateLocked()324 void IntelDspStream::OnDeactivateLocked() {
325     LOG(TRACE, "OnDeactivateLocked\n");
326 }
327 
OnChannelDeactivateLocked(const dispatcher::Channel & channel)328 void IntelDspStream::OnChannelDeactivateLocked(const dispatcher::Channel& channel) {
329     LOG(TRACE, "OnChannelDeactivateLocked\n");
330 }
331 
OnDMAAssignedLocked()332 zx_status_t IntelDspStream::OnDMAAssignedLocked() {
333     LOG(TRACE, "OnDMAAssignedLocked\n");
334     return PublishDeviceLocked();
335 }
336 
OnSolicitedResponseLocked(const CodecResponse & resp)337 zx_status_t IntelDspStream::OnSolicitedResponseLocked(const CodecResponse& resp) {
338     return ZX_ERR_NOT_SUPPORTED;
339 }
340 
OnUnsolicitedResponseLocked(const CodecResponse & resp)341 zx_status_t IntelDspStream::OnUnsolicitedResponseLocked(const CodecResponse& resp) {
342     return ZX_ERR_NOT_SUPPORTED;
343 }
344 
BeginChangeStreamFormatLocked(const audio_proto::StreamSetFmtReq & req)345 zx_status_t IntelDspStream::BeginChangeStreamFormatLocked(
346         const audio_proto::StreamSetFmtReq& req) {
347     LOG(TRACE, "BeginChangeStreamFormatLocked\n");
348     return ZX_OK;
349 }
350 
FinishChangeStreamFormatLocked(uint16_t encoded_fmt)351 zx_status_t IntelDspStream::FinishChangeStreamFormatLocked(uint16_t encoded_fmt) {
352     LOG(TRACE, "FinishChangeStreamFormatLocked\n");
353     return ZX_OK;
354 }
355 
OnGetGainLocked(audio_proto::GetGainResp * out_resp)356 void IntelDspStream::OnGetGainLocked(audio_proto::GetGainResp* out_resp) {
357     LOG(TRACE, "OnGetGainLocked\n");
358     IntelHDAStreamBase::OnGetGainLocked(out_resp);
359 }
360 
OnSetGainLocked(const audio_proto::SetGainReq & req,audio_proto::SetGainResp * out_resp)361 void IntelDspStream::OnSetGainLocked(const audio_proto::SetGainReq& req,
362                                      audio_proto::SetGainResp* out_resp) {
363     LOG(TRACE, "OnSetGainLocked\n");
364     IntelHDAStreamBase::OnSetGainLocked(req, out_resp);
365 }
366 
OnPlugDetectLocked(dispatcher::Channel * response_channel,const audio_proto::PlugDetectReq & req,audio_proto::PlugDetectResp * out_resp)367 void IntelDspStream::OnPlugDetectLocked(dispatcher::Channel* response_channel,
368                                         const audio_proto::PlugDetectReq& req,
369                                         audio_proto::PlugDetectResp* out_resp) {
370     LOG(TRACE, "OnPlugDetectLocked\n");
371     IntelHDAStreamBase::OnPlugDetectLocked(response_channel, req, out_resp);
372 }
373 
OnGetStringLocked(const audio_proto::GetStringReq & req,audio_proto::GetStringResp * out_resp)374 void IntelDspStream::OnGetStringLocked(const audio_proto::GetStringReq& req,
375                                        audio_proto::GetStringResp* out_resp) {
376     ZX_DEBUG_ASSERT(out_resp);
377     const char* requested_string = nullptr;
378 
379     switch (req.id) {
380         case AUDIO_STREAM_STR_ID_MANUFACTURER:
381             requested_string = "Intel";
382             break;
383 
384         case AUDIO_STREAM_STR_ID_PRODUCT:
385             requested_string = is_input() ? "Builtin Microphone" : "Builtin Speakers";
386             break;
387 
388         default:
389             IntelHDAStreamBase::OnGetStringLocked(req, out_resp);
390             return;
391     }
392 
393     int res = snprintf(reinterpret_cast<char*>(out_resp->str), sizeof(out_resp->str), "%s",
394                        requested_string ? requested_string : "<unassigned>");
395     ZX_DEBUG_ASSERT(res >= 0);
396     out_resp->result = ZX_OK;
397     out_resp->strlen = fbl::min<uint32_t>(res, sizeof(out_resp->str) - 1);
398     out_resp->id = req.id;
399 }
400 
401 }  // namespace intel_hda
402 }  // namespace audio
403