OpenCore  1.0.4
OpenCore Bootloader
Loading...
Searching...
No Matches
OcAudio.c
Go to the documentation of this file.
1
15#include <Library/BaseMemoryLib.h>
16#include <Library/DebugLib.h>
17#include <Library/DevicePathLib.h>
18#include <Library/MemoryAllocationLib.h>
19#include <Library/OcAudioLib.h>
21#include <Library/OcMiscLib.h>
22#include <Library/UefiBootServicesTableLib.h>
23
24#include <Protocol/AppleHda.h>
27#include <Protocol/HdaIo.h>
28
29#include "OcAudioInternal.h"
30
31STATIC
32EFI_DEVICE_PATH_PROTOCOL *
34 IN EFI_DEVICE_PATH_PROTOCOL *ControllerDevicePath,
35 IN UINT8 CodecAddress
36 )
37{
38 EFI_HDA_IO_DEVICE_PATH CodecDevicePathNode;
39
40 CodecDevicePathNode.Header.Type = MESSAGING_DEVICE_PATH;
41 CodecDevicePathNode.Header.SubType = MSG_VENDOR_DP;
42 SetDevicePathNodeLength (&CodecDevicePathNode, sizeof (CodecDevicePathNode));
43 CopyGuid (&CodecDevicePathNode.Guid, &gEfiHdaIoDevicePathGuid);
44 CodecDevicePathNode.Address = CodecAddress;
45
46 return AppendDevicePathNode (
47 ControllerDevicePath,
48 (EFI_DEVICE_PATH_PROTOCOL *)&CodecDevicePathNode
49 );
50}
51
52STATIC
53EFI_STATUS
55 IN CONST EFI_AUDIO_IO_PROTOCOL *AudioIo
56 )
57{
58 if (AudioIo->Revision == EFI_AUDIO_IO_PROTOCOL_REVISION) {
59 return EFI_SUCCESS;
60 }
61
62 DEBUG ((
63 DEBUG_WARN,
64 "OCAU: Incorrect audio I/O protocol revision %u != %u\n",
65 AudioIo->Revision,
67 ));
68
69 return EFI_UNSUPPORTED;
70}
71
72STATIC
73EFI_STATUS
75 IN OUT OC_AUDIO_PROTOCOL_PRIVATE *Private,
76 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
77 IN EFI_HANDLE *AudioIoHandles,
78 IN UINTN AudioIoHandleCount
79 )
80{
81 EFI_STATUS Status;
82 UINTN Index;
83 CHAR16 *DevicePathText;
84 EFI_DEVICE_PATH_PROTOCOL *CodecDevicePath;
85 EFI_AUDIO_IO_PROTOCOL_PORT *OutputPorts;
86 UINTN OutputPortsCount;
87
88 DEBUG_CODE_BEGIN ();
89 DevicePathText = ConvertDevicePathToText (DevicePath, FALSE, FALSE);
90 DEBUG ((
91 DEBUG_INFO,
92 "OCAU: Matching %s...\n",
93 DevicePathText != NULL ? DevicePathText : L"<invalid>"
94 ));
95 if (DevicePathText != NULL) {
96 FreePool (DevicePathText);
97 }
98
99 DEBUG_CODE_END ();
100
101 for (Index = 0; Index < AudioIoHandleCount; ++Index) {
102 Status = gBS->HandleProtocol (
103 AudioIoHandles[Index],
105 (VOID **)&CodecDevicePath
106 );
107
108 DEBUG_CODE_BEGIN ();
109 DevicePathText = NULL;
110 if (!EFI_ERROR (Status)) {
111 DevicePathText = ConvertDevicePathToText (CodecDevicePath, FALSE, FALSE);
112 }
113
114 OutputPortsCount = 0;
115 Status = gBS->HandleProtocol (
116 AudioIoHandles[Index],
118 (VOID **)&Private->AudioIo
119 );
120 if (!EFI_ERROR (Status)) {
121 Status = AudioIoProtocolConfirmRevision (Private->AudioIo);
122 }
123
124 if (!EFI_ERROR (Status)) {
125 Status = Private->AudioIo->GetOutputs (
126 Private->AudioIo,
127 &OutputPorts,
128 &OutputPortsCount
129 );
130 if (!EFI_ERROR (Status)) {
131 FreePool (OutputPorts);
132 }
133 }
134
135 DEBUG ((
136 DEBUG_INFO,
137 "OCAU: %u/%u %s (%u outputs) - %r\n",
138 (UINT32)(Index + 1),
139 (UINT32)(AudioIoHandleCount),
140 DevicePathText != NULL ? DevicePathText : L"<invalid>",
141 (UINT32)OutputPortsCount,
142 Status
143 ));
144
145 if (DevicePathText != NULL) {
146 FreePool (DevicePathText);
147 }
148
149 DEBUG_CODE_END ();
150
151 if (IsDevicePathEqual (DevicePath, CodecDevicePath)) {
152 Status = gBS->HandleProtocol (
153 AudioIoHandles[Index],
155 (VOID **)&Private->AudioIo
156 );
157 if (!EFI_ERROR (Status)) {
158 Status = AudioIoProtocolConfirmRevision (Private->AudioIo);
159 }
160
161 return Status;
162 }
163 }
164
165 return EFI_NOT_FOUND;
166}
167
168EFI_STATUS
169EFIAPI
171 IN OUT OC_AUDIO_PROTOCOL *This,
172 IN INT8 Gain
173 )
174{
176
178
179 Private->Gain = Gain;
180
181 return EFI_SUCCESS;
182}
183
184EFI_STATUS
185EFIAPI
187 IN OUT OC_AUDIO_PROTOCOL *This,
188 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath OPTIONAL,
189 IN UINT8 CodecAddress OPTIONAL,
190 IN UINT64 OutputIndexMask
191 )
192{
193 EFI_STATUS Status;
195 EFI_HANDLE *AudioIoHandles;
196 UINTN AudioIoHandleCount;
197 EFI_DEVICE_PATH_PROTOCOL *TmpDevicePath;
198
200
201 Private->OutputIndexMask = OutputIndexMask;
202
203 if (DevicePath == NULL) {
204 Status = gBS->LocateProtocol (
206 NULL,
207 (VOID **)&Private->AudioIo
208 );
209 if (!EFI_ERROR (Status)) {
210 Status = AudioIoProtocolConfirmRevision (Private->AudioIo);
211 }
212 } else {
213 Status = gBS->LocateHandleBuffer (
214 ByProtocol,
216 NULL,
217 &AudioIoHandleCount,
218 &AudioIoHandles
219 );
220
221 if (!EFI_ERROR (Status)) {
222 DevicePath = OcAudioGetCodecDevicePath (DevicePath, CodecAddress);
223 if (DevicePath == NULL) {
224 DEBUG ((DEBUG_INFO, "OCAU: Cannot get full device path\n"));
225 FreePool (AudioIoHandles);
226 return EFI_INVALID_PARAMETER;
227 }
228
230 Private,
231 DevicePath,
232 AudioIoHandles,
233 AudioIoHandleCount
234 );
235
236 if (EFI_ERROR (Status)) {
237 //
238 // WARN: DevicePath must be allocated from pool as it may be reallocated.
239 //
240 if (OcFixAppleBootDevicePath (&DevicePath, &TmpDevicePath) > 0) {
241 DEBUG ((DEBUG_INFO, "OCAU: Retrying with fixed device path\n"));
243 Private,
244 DevicePath,
245 AudioIoHandles,
246 AudioIoHandleCount
247 );
248 }
249 }
250
251 FreePool (DevicePath);
252 FreePool (AudioIoHandles);
253 } else {
254 DEBUG ((DEBUG_INFO, "OCAU: No AudioIo instances - %r\n", Status));
255 }
256 }
257
258 if (EFI_ERROR (Status)) {
259 DEBUG ((DEBUG_INFO, "OCAU: Cannot find specified audio device - %r\n", Status));
260 Private->AudioIo = NULL;
261 return Status;
262 }
263
264 return EFI_SUCCESS;
265}
266
267EFI_STATUS
268EFIAPI
270 IN OUT OC_AUDIO_PROTOCOL *This,
271 IN OC_AUDIO_PROVIDER_ACQUIRE Acquire,
272 IN OC_AUDIO_PROVIDER_RELEASE Release OPTIONAL,
273 IN VOID *Context
274 )
275{
277
278 if (Acquire == NULL) {
279 return EFI_INVALID_PARAMETER;
280 }
281
283
284 Private->ProviderAcquire = Acquire;
285 Private->ProviderRelease = Release;
286 Private->ProviderContext = Context;
287
288 return EFI_SUCCESS;
289}
290
291STATIC
292VOID
293EFIAPI
295 IN EFI_AUDIO_IO_PROTOCOL *AudioIo,
296 IN VOID *Context
297 )
298{
300
301 Private = Context;
302
303 DEBUG ((DEBUG_VERBOSE, "OCAU: PlayFileDone signaling for completion\n"));
304
305 //
306 // The event callback is guaranteed to be called with TPL_NOTIFY,
307 // therefore we are guaranteed to have audio buffer set here.
308 //
309 ASSERT (Private->CurrentBuffer != NULL);
310
311 if (Private->ProviderRelease != NULL) {
312 Private->ProviderRelease (Private->ProviderContext, Private->CurrentBuffer);
313 }
314
315 Private->CurrentBuffer = NULL;
316
317 gBS->SignalEvent (Private->PlaybackEvent);
318}
319
320EFI_STATUS
321EFIAPI
323 IN OUT OC_AUDIO_PROTOCOL *This,
324 IN UINT8 GainParam,
325 OUT INT8 *Gain
326 )
327{
328 EFI_STATUS Status;
330
332
333 if (Private->AudioIo == NULL) {
334 DEBUG ((DEBUG_INFO, "OCAU: RawGainToDecibels has no AudioIo\n"));
335 return EFI_ABORTED;
336 }
337
338 Status = Private->AudioIo->RawGainToDecibels (
339 Private->AudioIo,
340 Private->OutputIndexMask,
341 GainParam,
342 Gain
343 );
344
345 if (EFI_ERROR (Status)) {
346 DEBUG ((DEBUG_INFO, "OCAU: RawGainToDecibels conversion failure - %r\n", Status));
347 }
348
349 return Status;
350}
351
352EFI_STATUS
353EFIAPI
355 IN OUT OC_AUDIO_PROTOCOL *This,
356 IN CONST CHAR8 *BasePath,
357 IN CONST CHAR8 *BaseType,
358 IN BOOLEAN Localised,
359 IN INT8 Gain OPTIONAL,
360 IN BOOLEAN UseGain,
361 IN BOOLEAN Wait
362 )
363{
364 EFI_STATUS Status;
366 UINT8 *RawBuffer;
367 UINT32 RawBufferSize;
370 UINT8 Channels;
371 EFI_TPL OldTpl;
372
374
375 if ((Private->AudioIo == NULL) || (Private->ProviderAcquire == NULL)) {
376 DEBUG ((DEBUG_INFO, "OCAU: PlayFile has no AudioIo or provider is unconfigured\n"));
377 return EFI_ABORTED;
378 }
379
380 Status = Private->ProviderAcquire (
381 Private->ProviderContext,
382 BasePath,
383 BaseType,
384 Localised,
385 Private->Language,
386 &RawBuffer,
387 &RawBufferSize,
388 &Frequency,
389 &Bits,
390 &Channels
391 );
392
393 if (EFI_ERROR (Status)) {
394 DEBUG ((DEBUG_INFO, "OCAU: PlayFile has no file %a for type %a lang %u - %r\n", BasePath, BaseType, Private->Language, Status));
395 return EFI_NOT_FOUND;
396 }
397
398 DEBUG ((
399 DEBUG_INFO,
400 "OCAU: File %a for type %a lang %u is %d %d %d (%u) - %r\n",
401 BasePath,
402 BaseType,
403 Private->Language,
404 Frequency,
405 Bits,
406 Channels,
407 (UINT32)RawBufferSize,
408 Status
409 ));
410
411 if (EFI_ERROR (Status)) {
412 DEBUG ((DEBUG_INFO, "OCAU: PlayFile has invalid file %a for type %a lang %u - %r\n", BasePath, BaseType, Private->Language, Status));
413 if (Private->ProviderRelease != NULL) {
414 Private->ProviderRelease (Private->ProviderContext, RawBuffer);
415 }
416
417 return EFI_NOT_FOUND;
418 }
419
420 This->StopPlayback (This, Wait);
421
422 OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
423 Private->CurrentBuffer = RawBuffer;
424
425 Status = Private->AudioIo->SetupPlayback (
426 Private->AudioIo,
427 Private->OutputIndexMask,
428 UseGain ? Gain : Private->Gain,
429 Frequency,
430 Bits,
431 Channels,
432 Private->PlaybackDelay
433 );
434 if (!EFI_ERROR (Status)) {
435 Status = Private->AudioIo->StartPlaybackAsync (
436 Private->AudioIo,
437 RawBuffer,
438 RawBufferSize,
439 0,
441 Private
442 );
443 if (EFI_ERROR (Status)) {
444 DEBUG ((DEBUG_INFO, "OCAU: PlayFile playback failure - %r\n", Status));
445 }
446 } else {
447 DEBUG ((DEBUG_INFO, "OCAU: PlayFile playback setup failure - %r\n", Status));
448 }
449
450 if (EFI_ERROR (Status)) {
451 if (Private->ProviderRelease != NULL) {
452 Private->ProviderRelease (Private->ProviderContext, Private->CurrentBuffer);
453 }
454
455 Private->CurrentBuffer = NULL;
456 }
457
458 gBS->RestoreTPL (OldTpl);
459 return Status;
460}
461
462EFI_STATUS
463EFIAPI
465 IN OUT OC_AUDIO_PROTOCOL *This,
466 IN BOOLEAN Wait
467 )
468{
470 EFI_TPL OldTpl;
471 UINTN Index;
472 EFI_STATUS Status;
473 BOOLEAN CheckEvent;
474
476
477 //
478 // Note, this function cannot call prints, as it may be called from within
479 // ExitBootServices handler.
480 //
481
482 DEBUG ((DEBUG_VERBOSE, "OCAU: StopPlayback %d %p\n", Wait, Private->CurrentBuffer != NULL));
483
484 //
485 // Ensure that we never have the events signaled.
486 //
487 CheckEvent = TRUE;
488
489 if (Wait) {
490 //
491 // CurrentBuffer is set when asynchronous audio data is playing.
492 // Try to wait for asynchronous audio playback for complete.
493 //
494 if (Private->CurrentBuffer != NULL) {
495 Status = gBS->WaitForEvent (1, &Private->PlaybackEvent, &Index);
496 DEBUG ((DEBUG_VERBOSE, "OCAU: StopPlayback wait - %r\n", Status));
497 //
498 // This can fail in the following cases when current TPL is not TPL_APPLICATION.
499 // boot.efi does it from TPL_NOTIFY for password clicks in FV2 UI.
500 //
501 if (!EFI_ERROR (Status)) {
502 //
503 // If our wait was a success, we must have freed the buffer due to callback
504 // execution (InernalOcAudioPlayFileDone).
505 //
506 CheckEvent = FALSE;
507 ASSERT (Private->CurrentBuffer == NULL);
508 }
509 }
510 }
511
512 OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
513
514 if (Private->CurrentBuffer != NULL) {
515 //
516 // The audio is still playing. Stop playback now.
517 //
518 Private->AudioIo->StopPlayback (
519 Private->AudioIo
520 );
521
522 //
523 // Calling StopPlayback ignores the registered callback, free file here.
524 //
525 if (Private->ProviderRelease != NULL) {
526 Private->ProviderRelease (Private->ProviderContext, Private->CurrentBuffer);
527 }
528
529 Private->CurrentBuffer = NULL;
530 }
531
532 if (CheckEvent) {
533 //
534 // 1. It is possible that the audio completed before we waited, and thus
535 // Private->CurrentBuffer was NULL at the time we checked it.
536 // 2. It is possible that we WaitForEvent failed due to wrong TPL.
537 // 3. It is possible that we were called with Wait = FALSE, and in this
538 // case we still need to ensure that the event is reset for next playback.
539 // CheckEvent may fail if neither of these is true, and this is expected.
540 // We can call CheckEvent with TPL_APPLICATION, as a call to StopPlayback
541 // in TPL_NOTIFY guarantees no callbacks.
542 //
543 Status = gBS->CheckEvent (Private->PlaybackEvent);
544 DEBUG ((DEBUG_VERBOSE, "OCAU: StopPlayback check - %r\n", Status));
545 }
546
547 gBS->RestoreTPL (OldTpl);
548
549 return EFI_SUCCESS;
550}
551
552UINTN
553EFIAPI
555 IN OUT OC_AUDIO_PROTOCOL *This,
556 IN UINTN Delay
557 )
558{
560 UINTN PreviousDelay;
561
563
564 PreviousDelay = Private->PlaybackDelay;
565 Private->PlaybackDelay = Delay;
566
567 return PreviousDelay;
568}
#define EFI_AUDIO_IO_PROTOCOL_REVISION
Definition AudioIo.h:40
EFI_AUDIO_IO_PROTOCOL_FREQ
Definition AudioIo.h:104
EFI_GUID gEfiAudioIoProtocolGuid
EFI_AUDIO_IO_PROTOCOL_BITS
Definition AudioIo.h:93
EFI_GUID gEfiHdaIoDevicePathGuid
EFI_STATUS EFIAPI InternalOcAudioStopPlayback(IN OUT OC_AUDIO_PROTOCOL *This, IN BOOLEAN Wait)
Definition OcAudio.c:464
EFI_STATUS EFIAPI InternalOcAudioPlayFile(IN OUT OC_AUDIO_PROTOCOL *This, IN CONST CHAR8 *BasePath, IN CONST CHAR8 *BaseType, IN BOOLEAN Localised, IN INT8 Gain OPTIONAL, IN BOOLEAN UseGain, IN BOOLEAN Wait)
Definition OcAudio.c:354
STATIC EFI_DEVICE_PATH_PROTOCOL * OcAudioGetCodecDevicePath(IN EFI_DEVICE_PATH_PROTOCOL *ControllerDevicePath, IN UINT8 CodecAddress)
Definition OcAudio.c:33
EFI_STATUS EFIAPI InternalOcAudioConnect(IN OUT OC_AUDIO_PROTOCOL *This, IN EFI_DEVICE_PATH_PROTOCOL *DevicePath OPTIONAL, IN UINT8 CodecAddress OPTIONAL, IN UINT64 OutputIndexMask)
Definition OcAudio.c:186
EFI_STATUS EFIAPI InternalOcAudioRawGainToDecibels(IN OUT OC_AUDIO_PROTOCOL *This, IN UINT8 GainParam, OUT INT8 *Gain)
Definition OcAudio.c:322
EFI_STATUS EFIAPI InternalOcAudioSetProvider(IN OUT OC_AUDIO_PROTOCOL *This, IN OC_AUDIO_PROVIDER_ACQUIRE Acquire, IN OC_AUDIO_PROVIDER_RELEASE Release OPTIONAL, IN VOID *Context)
Definition OcAudio.c:269
UINTN EFIAPI InternalOcAudioSetDelay(IN OUT OC_AUDIO_PROTOCOL *This, IN UINTN Delay)
Definition OcAudio.c:554
EFI_STATUS EFIAPI InternalOcAudioSetDefaultGain(IN OUT OC_AUDIO_PROTOCOL *This, IN INT8 Gain)
Definition OcAudio.c:170
STATIC EFI_STATUS AudioIoProtocolConfirmRevision(IN CONST EFI_AUDIO_IO_PROTOCOL *AudioIo)
Definition OcAudio.c:54
STATIC EFI_STATUS InternalMatchCodecDevicePath(IN OUT OC_AUDIO_PROTOCOL_PRIVATE *Private, IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, IN EFI_HANDLE *AudioIoHandles, IN UINTN AudioIoHandleCount)
Definition OcAudio.c:74
STATIC VOID EFIAPI InernalOcAudioPlayFileDone(IN EFI_AUDIO_IO_PROTOCOL *AudioIo, IN VOID *Context)
Definition OcAudio.c:294
EFI_STATUS(EFIAPI * OC_AUDIO_PROVIDER_RELEASE)(IN VOID *Context, IN UINT8 *Buffer)
Definition OcAudio.h:156
EFI_STATUS(EFIAPI * OC_AUDIO_PROVIDER_ACQUIRE)(IN VOID *Context, IN CONST CHAR8 *BasePath, IN CONST CHAR8 *BaseType, IN BOOLEAN Localised, IN APPLE_VOICE_OVER_LANGUAGE_CODE LanguageCode, OUT UINT8 **Buffer, OUT UINT32 *BufferSize, OUT EFI_AUDIO_IO_PROTOCOL_FREQ *Frequency, OUT EFI_AUDIO_IO_PROTOCOL_BITS *Bits, OUT UINT8 *Channels)
Definition OcAudio.h:133
#define OC_AUDIO_PROTOCOL_PRIVATE_FROM_OC_AUDIO(Proto)
EFI_BOOT_SERVICES * gBS
INTN OcFixAppleBootDevicePath(IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath, OUT EFI_DEVICE_PATH_PROTOCOL **RemainingDevicePath)
BOOLEAN EFIAPI IsDevicePathEqual(IN EFI_DEVICE_PATH_PROTOCOL *DevicePath1, IN EFI_DEVICE_PATH_PROTOCOL *DevicePath2)
GUID *EFIAPI CopyGuid(OUT GUID *DestinationGuid, IN CONST GUID *SourceGuid)
EFI_GUID gEfiDevicePathProtocolGuid
#define ASSERT(x)
Definition coder.h:55
EFI_AUDIO_IO_STOP_PLAYBACK StopPlayback
Definition AudioIo.h:285
EFI_AUDIO_IO_START_PLAYBACK_ASYNC StartPlaybackAsync
Definition AudioIo.h:284
EFI_AUDIO_IO_SETUP_PLAYBACK SetupPlayback
Definition AudioIo.h:282
EFI_AUDIO_IO_RAW_GAIN_TO_DECIBELS RawGainToDecibels
Definition AudioIo.h:281
EFI_DEVICE_PATH_PROTOCOL Header
Definition HdaIo.h:207
OC_AUDIO_PROVIDER_RELEASE ProviderRelease
OC_AUDIO_PROVIDER_ACQUIRE ProviderAcquire
EFI_AUDIO_IO_PROTOCOL * AudioIo