2 * PROJECT: ReactOS Sound System
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: dll/win32/wdmaud.drv/mmixer.c
6 * PURPOSE: WDM Audio Mixer API (User-mode part)
7 * PROGRAMMERS: Johannes Anderwald
13 PVOID
Alloc(ULONG NumBytes
);
14 MIXER_STATUS
Close(HANDLE hDevice
);
15 VOID
Free(PVOID Block
);
16 VOID
Copy(PVOID Src
, PVOID Dst
, ULONG NumBytes
);
17 MIXER_STATUS
Open(IN LPWSTR DevicePath
, OUT PHANDLE hDevice
);
18 MIXER_STATUS
Control(IN HANDLE hMixer
, IN ULONG dwIoControlCode
, IN PVOID lpInBuffer
, IN ULONG nInBufferSize
, OUT PVOID lpOutBuffer
, ULONG nOutBufferSize
, PULONG lpBytesReturned
);
19 MIXER_STATUS
Enum(IN PVOID EnumContext
, IN ULONG DeviceIndex
, OUT LPWSTR
* DeviceName
, OUT PHANDLE OutHandle
, OUT PHANDLE OutKey
);
20 MIXER_STATUS
OpenKey(IN HANDLE hKey
, IN LPWSTR SubKey
, IN ULONG DesiredAccess
, OUT PHANDLE OutKey
);
21 MIXER_STATUS
CloseKey(IN HANDLE hKey
);
22 MIXER_STATUS
QueryKeyValue(IN HANDLE hKey
, IN LPWSTR KeyName
, OUT PVOID
* ResultBuffer
, OUT PULONG ResultLength
, OUT PULONG KeyType
);
23 PVOID
AllocEventData(IN ULONG ExtraSize
);
24 VOID
FreeEventData(IN PVOID EventData
);
26 MIXER_CONTEXT MixerContext
=
28 sizeof(MIXER_CONTEXT
),
43 GUID CategoryGuid
= {STATIC_KSCATEGORY_AUDIO
};
49 OUT PVOID
* ResultBuffer
,
50 OUT PULONG ResultLength
,
53 if (RegQueryValueExW((HKEY
)hKey
, KeyName
, NULL
, KeyType
, NULL
, ResultLength
) == ERROR_FILE_NOT_FOUND
)
54 return MM_STATUS_UNSUCCESSFUL
;
56 *ResultBuffer
= HeapAlloc(GetProcessHeap(), 0, *ResultLength
);
57 if (*ResultBuffer
== NULL
)
58 return MM_STATUS_NO_MEMORY
;
60 if (RegQueryValueExW((HKEY
)hKey
, KeyName
, NULL
, KeyType
, *ResultBuffer
, ResultLength
) != ERROR_SUCCESS
)
62 HeapFree(GetProcessHeap(), 0, *ResultBuffer
);
63 return MM_STATUS_UNSUCCESSFUL
;
65 return MM_STATUS_SUCCESS
;
72 IN ULONG DesiredAccess
,
75 if (RegOpenKeyExW((HKEY
)hKey
, SubKey
, 0, DesiredAccess
, (PHKEY
)OutKey
) == ERROR_SUCCESS
)
76 return MM_STATUS_SUCCESS
;
78 return MM_STATUS_UNSUCCESSFUL
;
85 RegCloseKey((HKEY
)hKey
);
86 return MM_STATUS_SUCCESS
;
90 PVOID
Alloc(ULONG NumBytes
)
92 return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, NumBytes
);
98 if (CloseHandle(hDevice
))
99 return MM_STATUS_SUCCESS
;
101 return MM_STATUS_UNSUCCESSFUL
;
107 HeapFree(GetProcessHeap(), 0, Block
);
111 Copy(PVOID Src
, PVOID Dst
, ULONG NumBytes
)
113 CopyMemory(Src
, Dst
, NumBytes
);
118 IN LPWSTR DevicePath
,
121 DevicePath
[1] = L
'\\';
122 *hDevice
= CreateFileW(DevicePath
,
123 GENERIC_READ
| GENERIC_WRITE
,
127 FILE_FLAG_OVERLAPPED
,
129 if (*hDevice
== INVALID_HANDLE_VALUE
)
131 return MM_STATUS_UNSUCCESSFUL
;
134 return MM_STATUS_SUCCESS
;
140 IN ULONG dwIoControlCode
,
142 IN ULONG nInBufferSize
,
143 OUT PVOID lpOutBuffer
,
144 ULONG nOutBufferSize
,
145 PULONG lpBytesReturned
)
147 OVERLAPPED Overlapped
;
149 DWORD Transferred
= 0;
151 /* Overlapped I/O is done here - this is used for waiting for completion */
152 ZeroMemory(&Overlapped
, sizeof(OVERLAPPED
));
153 Overlapped
.hEvent
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
155 if ( ! Overlapped
.hEvent
)
156 return MM_STATUS_NO_MEMORY
;
158 /* Talk to the device */
159 IoResult
= DeviceIoControl(hMixer
,
168 /* If failure occurs, make sure it's not just due to the overlapped I/O */
171 if ( GetLastError() != ERROR_IO_PENDING
)
173 CloseHandle(Overlapped
.hEvent
);
175 if (GetLastError() == ERROR_MORE_DATA
|| GetLastError() == ERROR_INSUFFICIENT_BUFFER
)
177 if ( lpBytesReturned
)
178 *lpBytesReturned
= Transferred
;
179 return MM_STATUS_MORE_ENTRIES
;
182 return MM_STATUS_UNSUCCESSFUL
;
186 /* Wait for the I/O to complete */
187 IoResult
= GetOverlappedResult(hMixer
,
192 /* Don't need this any more */
193 CloseHandle(Overlapped
.hEvent
);
196 return MM_STATUS_UNSUCCESSFUL
;
198 if ( lpBytesReturned
)
199 *lpBytesReturned
= Transferred
;
201 return MM_STATUS_SUCCESS
;
206 IN PVOID EnumContext
,
207 IN ULONG DeviceIndex
,
208 OUT LPWSTR
* DeviceName
,
209 OUT PHANDLE OutHandle
,
212 SP_DEVICE_INTERFACE_DATA InterfaceData
;
213 SP_DEVINFO_DATA DeviceData
;
214 PSP_DEVICE_INTERFACE_DETAIL_DATA_W DetailData
;
219 //printf("Enum EnumContext %p DeviceIndex %lu OutHandle %p\n", EnumContext, DeviceIndex, OutHandle);
221 InterfaceData
.cbSize
= sizeof(InterfaceData
);
222 InterfaceData
.Reserved
= 0;
224 Result
= SetupDiEnumDeviceInterfaces(EnumContext
,
232 if (GetLastError() == ERROR_NO_MORE_ITEMS
)
234 return MM_STATUS_NO_MORE_DEVICES
;
236 return MM_STATUS_UNSUCCESSFUL
;
239 Length
= sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W
) + MAX_PATH
* sizeof(WCHAR
);
240 DetailData
= (PSP_DEVICE_INTERFACE_DETAIL_DATA_W
)HeapAlloc(GetProcessHeap(),
243 DetailData
->cbSize
= sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W
);
244 DeviceData
.cbSize
= sizeof(DeviceData
);
245 DeviceData
.Reserved
= 0;
247 Result
= SetupDiGetDeviceInterfaceDetailW(EnumContext
,
256 DPRINT("SetupDiGetDeviceInterfaceDetailW failed with %lu\n", GetLastError());
257 return MM_STATUS_UNSUCCESSFUL
;
261 *OutKey
= SetupDiOpenDeviceInterfaceRegKey(EnumContext
, &InterfaceData
, 0, KEY_READ
);
262 if ((HKEY
)*OutKey
== INVALID_HANDLE_VALUE
)
264 HeapFree(GetProcessHeap(), 0, DetailData
);
265 return MM_STATUS_UNSUCCESSFUL
;
268 Status
= Open(DetailData
->DevicePath
, OutHandle
);
270 if (Status
!= MM_STATUS_SUCCESS
)
272 RegCloseKey((HKEY
)*OutKey
);
273 HeapFree(GetProcessHeap(), 0, DetailData
);
277 *DeviceName
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, (wcslen(DetailData
->DevicePath
)+1) * sizeof(WCHAR
));
278 if (*DeviceName
== NULL
)
280 CloseHandle(*OutHandle
);
281 RegCloseKey((HKEY
)*OutKey
);
282 HeapFree(GetProcessHeap(), 0, DetailData
);
283 return MM_STATUS_NO_MEMORY
;
285 DPRINT1("DeviceName %S\n", DetailData
->DevicePath
);
286 wcscpy(*DeviceName
, DetailData
->DevicePath
);
287 HeapFree(GetProcessHeap(), 0, DetailData
);
296 PKSEVENTDATA Data
= (PKSEVENTDATA
)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(KSEVENTDATA
) + ExtraSize
);
300 Data
->EventHandle
.Event
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
301 if (!Data
->EventHandle
.Event
)
303 HeapFree(GetProcessHeap(), 0, Data
);
307 Data
->NotificationType
= KSEVENTF_EVENT_HANDLE
;
312 FreeEventData(IN PVOID EventData
)
314 PKSEVENTDATA Data
= (PKSEVENTDATA
)EventData
;
316 CloseHandle(Data
->EventHandle
.Event
);
317 HeapFree(GetProcessHeap(), 0, Data
);
322 WdmAudInitUserModeMixer()
324 HDEVINFO DeviceHandle
;
327 /* create a device list */
328 DeviceHandle
= SetupDiGetClassDevs(&CategoryGuid
,
331 DIGCF_DEVICEINTERFACE
/* FIXME |DIGCF_PRESENT*/);
333 if (DeviceHandle
== INVALID_HANDLE_VALUE
)
335 /* failed to create a device list */
340 /* initialize the mixer library */
341 Status
= MMixerInitialize(&MixerContext
, Enum
, (PVOID
)DeviceHandle
);
343 /* free device list */
344 SetupDiDestroyDeviceInfoList(DeviceHandle
);
346 if (Status
!= MM_STATUS_SUCCESS
)
348 /* failed to initialize mixer library */
349 DPRINT1("Failed to initialize mixer library with %x\n", Status
);
353 /* completed successfully */
358 WdmAudCleanupByMMixer()
361 return MMSYSERR_NOERROR
;
365 WdmAudGetMixerCapabilties(
367 LPMIXERCAPSW Capabilities
)
369 if (MMixerGetCapabilities(&MixerContext
, DeviceId
, Capabilities
) == MM_STATUS_SUCCESS
)
370 return MMSYSERR_NOERROR
;
372 return MMSYSERR_BADDEVICEID
;
378 IN LPMIXERLINEW MixLine
,
381 if (MMixerGetLineInfo(&MixerContext
, hMixer
, Flags
, MixLine
) == MM_STATUS_SUCCESS
)
382 return MMSYSERR_NOERROR
;
384 return MMSYSERR_ERROR
;
388 WdmAudGetLineControls(
390 IN LPMIXERLINECONTROLSW MixControls
,
393 if (MMixerGetLineControls(&MixerContext
, hMixer
, Flags
, MixControls
) == MM_STATUS_SUCCESS
)
394 return MMSYSERR_NOERROR
;
396 return MMSYSERR_ERROR
;
400 WdmAudSetControlDetails(
402 IN LPMIXERCONTROLDETAILS MixDetails
,
405 if (MMixerSetControlDetails(&MixerContext
, hMixer
, Flags
, MixDetails
) == MM_STATUS_SUCCESS
)
406 return MMSYSERR_NOERROR
;
408 return MMSYSERR_ERROR
;
413 WdmAudGetControlDetails(
415 IN LPMIXERCONTROLDETAILS MixDetails
,
418 if (MMixerGetControlDetails(&MixerContext
, hMixer
, Flags
, MixDetails
) == MM_STATUS_SUCCESS
)
419 return MMSYSERR_NOERROR
;
421 return MMSYSERR_ERROR
;
425 WdmAudGetWaveOutCapabilities(
427 LPWAVEOUTCAPSW Capabilities
)
429 if (MMixerWaveOutCapabilities(&MixerContext
, DeviceId
, Capabilities
) == MM_STATUS_SUCCESS
)
430 return MMSYSERR_NOERROR
;
432 return MMSYSERR_ERROR
;
437 WdmAudGetWaveInCapabilities(
439 LPWAVEINCAPSW Capabilities
)
441 if (MMixerWaveInCapabilities(&MixerContext
, DeviceId
, Capabilities
) == MM_STATUS_SUCCESS
)
442 return MMSYSERR_NOERROR
;
444 return MMSYSERR_ERROR
;
448 WdmAudSetWaveDeviceFormatByMMixer(
449 IN PSOUND_DEVICE_INSTANCE Instance
,
451 IN PWAVEFORMATEX WaveFormat
,
452 IN DWORD WaveFormatSize
)
454 MMDEVICE_TYPE DeviceType
;
455 PSOUND_DEVICE SoundDevice
;
459 Result
= GetSoundDeviceFromInstance(Instance
, &SoundDevice
);
461 if ( ! MMSUCCESS(Result
) )
463 return TranslateInternalMmResult(Result
);
466 Result
= GetSoundDeviceType(SoundDevice
, &DeviceType
);
467 SND_ASSERT( Result
== MMSYSERR_NOERROR
);
469 bWaveIn
= (DeviceType
== WAVE_IN_DEVICE_TYPE
? TRUE
: FALSE
);
471 if (MMixerOpenWave(&MixerContext
, DeviceId
, bWaveIn
, WaveFormat
, NULL
, NULL
, &Instance
->Handle
) == MM_STATUS_SUCCESS
)
473 if (DeviceType
== WAVE_OUT_DEVICE_TYPE
)
475 MMixerSetWaveStatus(&MixerContext
, Instance
->Handle
, KSSTATE_ACQUIRE
);
476 MMixerSetWaveStatus(&MixerContext
, Instance
->Handle
, KSSTATE_PAUSE
);
477 MMixerSetWaveStatus(&MixerContext
, Instance
->Handle
, KSSTATE_RUN
);
479 return MMSYSERR_NOERROR
;
481 return MMSYSERR_ERROR
;
486 WdmAudGetCapabilitiesByMMixer(
487 IN PSOUND_DEVICE SoundDevice
,
489 OUT PVOID Capabilities
,
490 IN DWORD CapabilitiesSize
)
492 MMDEVICE_TYPE DeviceType
;
495 Result
= GetSoundDeviceType(SoundDevice
, &DeviceType
);
496 SND_ASSERT( Result
== MMSYSERR_NOERROR
);
498 if (DeviceType
== MIXER_DEVICE_TYPE
)
500 return WdmAudGetMixerCapabilties(DeviceId
, (LPMIXERCAPSW
)Capabilities
);
502 else if (DeviceType
== WAVE_OUT_DEVICE_TYPE
)
504 return WdmAudGetWaveOutCapabilities(DeviceId
, (LPWAVEOUTCAPSW
)Capabilities
);
506 else if (DeviceType
== WAVE_IN_DEVICE_TYPE
)
508 return WdmAudGetWaveInCapabilities(DeviceId
, (LPWAVEINCAPSW
)Capabilities
);
513 return MMSYSERR_ERROR
;
518 WdmAudOpenSoundDeviceByMMixer(
519 IN
struct _SOUND_DEVICE
* SoundDevice
,
522 if (WdmAudInitUserModeMixer())
523 return MMSYSERR_NOERROR
;
525 return MMSYSERR_ERROR
;
529 WdmAudCloseSoundDeviceByMMixer(
530 IN
struct _SOUND_DEVICE_INSTANCE
* SoundDeviceInstance
,
533 MMDEVICE_TYPE DeviceType
;
534 PSOUND_DEVICE SoundDevice
;
537 Result
= GetSoundDeviceFromInstance(SoundDeviceInstance
, &SoundDevice
);
539 if ( ! MMSUCCESS(Result
) )
541 return TranslateInternalMmResult(Result
);
544 Result
= GetSoundDeviceType(SoundDevice
, &DeviceType
);
545 SND_ASSERT( Result
== MMSYSERR_NOERROR
);
547 if (DeviceType
== MIXER_DEVICE_TYPE
)
550 return MMSYSERR_NOERROR
;
552 else if (DeviceType
== WAVE_IN_DEVICE_TYPE
|| DeviceType
== WAVE_OUT_DEVICE_TYPE
)
554 /* make sure the pin is stopped */
555 MMixerSetWaveStatus(&MixerContext
, SoundDeviceInstance
->Handle
, KSSTATE_PAUSE
);
556 MMixerSetWaveStatus(&MixerContext
, SoundDeviceInstance
->Handle
, KSSTATE_ACQUIRE
);
557 MMixerSetWaveStatus(&MixerContext
, SoundDeviceInstance
->Handle
, KSSTATE_STOP
);
560 return MMSYSERR_NOERROR
;
563 /* midi is not supported */
564 return MMSYSERR_ERROR
;
568 WdmAudGetNumWdmDevsByMMixer(
569 IN MMDEVICE_TYPE DeviceType
,
570 OUT DWORD
* DeviceCount
)
574 case MIXER_DEVICE_TYPE
:
575 *DeviceCount
= MMixerGetCount(&MixerContext
);
577 case WAVE_OUT_DEVICE_TYPE
:
578 *DeviceCount
= MMixerGetWaveOutCount(&MixerContext
);
580 case WAVE_IN_DEVICE_TYPE
:
581 *DeviceCount
= MMixerGetWaveInCount(&MixerContext
);
586 return MMSYSERR_NOERROR
;
590 WdmAudQueryMixerInfoByMMixer(
591 IN
struct _SOUND_DEVICE_INSTANCE
* SoundDeviceInstance
,
596 LPMIXERLINEW MixLine
;
597 LPMIXERLINECONTROLSW MixControls
;
598 LPMIXERCONTROLDETAILS MixDetails
;
600 MixLine
= (LPMIXERLINEW
)Parameter
;
601 MixControls
= (LPMIXERLINECONTROLSW
)Parameter
;
602 MixDetails
= (LPMIXERCONTROLDETAILS
)Parameter
;
604 /* FIXME param checks */
608 case MXDM_GETLINEINFO
:
609 return WdmAudGetLineInfo(SoundDeviceInstance
->Handle
, MixLine
, Flags
);
610 case MXDM_GETLINECONTROLS
:
611 return WdmAudGetLineControls(SoundDeviceInstance
->Handle
, MixControls
, Flags
);
612 case MXDM_SETCONTROLDETAILS
:
613 return WdmAudSetControlDetails(SoundDeviceInstance
->Handle
, MixDetails
, Flags
);
615 case MXDM_GETCONTROLDETAILS
:
616 return WdmAudGetControlDetails(SoundDeviceInstance
->Handle
, MixDetails
, Flags
);
620 return MMSYSERR_NOTSUPPORTED
;
625 WdmAudGetDeviceInterfaceStringByMMixer(
626 IN MMDEVICE_TYPE DeviceType
,
629 IN DWORD InterfaceLength
,
630 OUT DWORD
* InterfaceSize
)
633 return MMSYSERR_NOTSUPPORTED
;
637 WdmAudSetMixerDeviceFormatByMMixer(
638 IN PSOUND_DEVICE_INSTANCE Instance
,
640 IN PWAVEFORMATEX WaveFormat
,
641 IN DWORD WaveFormatSize
)
643 Instance
->hNotifyEvent
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
644 if ( ! Instance
->hNotifyEvent
)
645 return MMSYSERR_NOMEM
;
647 if (MMixerOpen(&MixerContext
, DeviceId
, Instance
->hNotifyEvent
, NULL
/* FIXME */, &Instance
->Handle
) == MM_STATUS_SUCCESS
)
648 return MMSYSERR_NOERROR
;
650 return MMSYSERR_BADDEVICEID
;
654 WdmAudSetWaveStateByMMixer(
655 IN
struct _SOUND_DEVICE_INSTANCE
* SoundDeviceInstance
,
658 MMDEVICE_TYPE DeviceType
;
659 PSOUND_DEVICE SoundDevice
;
662 Result
= GetSoundDeviceFromInstance(SoundDeviceInstance
, &SoundDevice
);
663 SND_ASSERT( Result
== MMSYSERR_NOERROR
);
666 Result
= GetSoundDeviceType(SoundDevice
, &DeviceType
);
667 SND_ASSERT( Result
== MMSYSERR_NOERROR
);
669 if (DeviceType
== WAVE_IN_DEVICE_TYPE
|| DeviceType
== WAVE_OUT_DEVICE_TYPE
)
673 MMixerSetWaveStatus(&MixerContext
, SoundDeviceInstance
->Handle
, KSSTATE_ACQUIRE
);
674 MMixerSetWaveStatus(&MixerContext
, SoundDeviceInstance
->Handle
, KSSTATE_PAUSE
);
675 MMixerSetWaveStatus(&MixerContext
, SoundDeviceInstance
->Handle
, KSSTATE_RUN
);
679 MMixerSetWaveStatus(&MixerContext
, SoundDeviceInstance
->Handle
, KSSTATE_PAUSE
);
680 MMixerSetWaveStatus(&MixerContext
, SoundDeviceInstance
->Handle
, KSSTATE_ACQUIRE
);
681 MMixerSetWaveStatus(&MixerContext
, SoundDeviceInstance
->Handle
, KSSTATE_STOP
);
684 else if (DeviceType
== MIDI_IN_DEVICE_TYPE
|| DeviceType
== MIDI_OUT_DEVICE_TYPE
)
688 MMixerSetMidiStatus(&MixerContext
, SoundDeviceInstance
->Handle
, KSSTATE_ACQUIRE
);
689 MMixerSetMidiStatus(&MixerContext
, SoundDeviceInstance
->Handle
, KSSTATE_PAUSE
);
690 MMixerSetMidiStatus(&MixerContext
, SoundDeviceInstance
->Handle
, KSSTATE_RUN
);
694 MMixerSetMidiStatus(&MixerContext
, SoundDeviceInstance
->Handle
, KSSTATE_PAUSE
);
695 MMixerSetMidiStatus(&MixerContext
, SoundDeviceInstance
->Handle
, KSSTATE_ACQUIRE
);
696 MMixerSetMidiStatus(&MixerContext
, SoundDeviceInstance
->Handle
, KSSTATE_STOP
);
700 return MMSYSERR_NOERROR
;
704 WdmAudResetStreamByMMixer(
705 IN
struct _SOUND_DEVICE_INSTANCE
* SoundDeviceInstance
,
706 IN MMDEVICE_TYPE DeviceType
,
707 IN BOOLEAN bStartReset
)
710 return MMSYSERR_NOTSUPPORTED
;
714 WdmAudGetWavePositionByMMixer(
715 IN
struct _SOUND_DEVICE_INSTANCE
* SoundDeviceInstance
,
719 return MMSYSERR_NOTSUPPORTED
;
723 WdmAudCommitWaveBufferByMMixer(
724 IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance
,
727 IN PSOUND_OVERLAPPED Overlap
,
728 IN LPOVERLAPPED_COMPLETION_ROUTINE CompletionRoutine
)
730 KSSTREAM_HEADER Packet
;
731 PSOUND_DEVICE SoundDevice
;
732 MMDEVICE_TYPE DeviceType
;
735 Result
= GetSoundDeviceFromInstance(SoundDeviceInstance
, &SoundDevice
);
737 if ( ! MMSUCCESS(Result
) )
739 return TranslateInternalMmResult(Result
);
742 Result
= GetSoundDeviceType(SoundDevice
, &DeviceType
);
743 SND_ASSERT( Result
== MMSYSERR_NOERROR
);
745 /* setup stream packet */
746 ZeroMemory(&Packet
, sizeof(KSSTREAM_HEADER
));
747 Packet
.Size
= sizeof(KSSTREAM_HEADER
);
748 Packet
.PresentationTime
.Numerator
= 1;
749 Packet
.PresentationTime
.Denominator
= 1;
750 Packet
.Data
= OffsetPtr
;
751 Packet
.FrameExtent
= Length
;
753 if (DeviceType
== WAVE_OUT_DEVICE_TYPE
)
755 Packet
.DataUsed
= Length
;
758 Result
= SyncOverlappedDeviceIoControl(SoundDeviceInstance
->Handle
,
759 IOCTL_KS_WRITE_STREAM
,
763 sizeof(KSSTREAM_HEADER
),
767 * don't call completion routine directly
769 CompletionRoutine(ERROR_SUCCESS
, Length
, (LPOVERLAPPED
)Overlap
);
771 return MMSYSERR_NOERROR
;