[ros-diffs] [silverblade] 34349: Sound device I/O completion abstraction. thread.c now handles the overlapped I/O structure allocations etc. and gathers completed I/O requests, tagging them with contextual information within the I/O completion APC. Once the APC returns, the completed I/O requests are passed to a callback routine. This allows wavethread.c to just supply "request" and "I/O completion" callbacks. The I/O completion callback's context data is then cast to WAVEHDR...

silverblade at svn.reactos.org silverblade at svn.reactos.org
Mon Jul 7 01:12:29 CEST 2008


Author: silverblade
Date: Sun Jul  6 18:12:29 2008
New Revision: 34349

URL: http://svn.reactos.org/svn/reactos?rev=34349&view=rev
Log:
Sound device I/O completion abstraction. thread.c now handles the overlapped
I/O structure allocations etc. and gathers completed I/O requests, tagging
them with contextual information within the I/O completion APC. Once the APC
returns, the completed I/O requests are passed to a callback routine. This
allows wavethread.c to just supply "request" and "I/O completion" callbacks.
The I/O completion callback's context data is then cast to WAVEHDR...


Modified:
    branches/silverblade-audio/include/reactos/libs/sound/mmebuddy.h
    branches/silverblade-audio/lib/drivers/sound/mmebuddy/thread.c
    branches/silverblade-audio/lib/drivers/sound/mmebuddy/wave/wavethread.c

Modified: branches/silverblade-audio/include/reactos/libs/sound/mmebuddy.h
URL: http://svn.reactos.org/svn/reactos/branches/silverblade-audio/include/reactos/libs/sound/mmebuddy.h?rev=34349&r1=34348&r2=34349&view=diff
==============================================================================
--- branches/silverblade-audio/include/reactos/libs/sound/mmebuddy.h [iso-8859-1] (original)
+++ branches/silverblade-audio/include/reactos/libs/sound/mmebuddy.h [iso-8859-1] Sun Jul  6 18:12:29 2008
@@ -101,23 +101,49 @@
     MMRESULT Result;
 } THREAD_REQUEST, *PTHREAD_REQUEST;
 
+typedef struct _SOUND_THREAD_COMPLETED_IO
+{
+    struct _SOUND_THREAD_COMPLETED_IO* Next;
+    PVOID ContextData;  /* eg: PWAVEHDR */
+    DWORD BytesTransferred;
+} SOUND_THREAD_COMPLETED_IO, *PSOUND_THREAD_COMPLETED_IO;
+
+typedef struct _SOUND_THREAD_OVERLAPPED
+{
+    OVERLAPPED General;
+    struct _SOUND_DEVICE_INSTANCE* SoundDeviceInstance;
+    PVOID ContextData;  /* eg: PWAVEHDR */
+    PSOUND_THREAD_COMPLETED_IO CompletionData;
+} SOUND_THREAD_OVERLAPPED, *PSOUND_THREAD_OVERLAPPED;
+
 typedef MMRESULT (*SOUND_THREAD_REQUEST_HANDLER)(
     IN  struct _SOUND_DEVICE_INSTANCE* Instance,
     IN  PVOID PrivateThreadData,
     IN  DWORD RequestId,
     IN  PVOID Data);
 
+typedef VOID (*SOUND_THREAD_IO_COMPLETION_HANDLER)(
+    IN  struct _SOUND_DEVICE_INSTANCE* Instance,
+    IN  PVOID ContextData,
+    IN  DWORD BytesTransferred);
+
 typedef struct _SOUND_THREAD
 {
     /* Thread management */
     HANDLE Handle;
     PVOID PrivateData;
     BOOLEAN Running;
-    SOUND_THREAD_REQUEST_HANDLER RequestHandler;
+
     HANDLE ReadyEvent;      /* Thread waiting for a request */
     HANDLE RequestEvent;    /* Caller sending a request */
     HANDLE DoneEvent;       /* Thread completed a request */
+
+    SOUND_THREAD_REQUEST_HANDLER RequestHandler;
     THREAD_REQUEST Request;
+
+    SOUND_THREAD_OVERLAPPED Overlapped;
+    PSOUND_THREAD_COMPLETED_IO FirstCompletedIo;
+    SOUND_THREAD_IO_COMPLETION_HANDLER IoCompletionHandler;
 } SOUND_THREAD, *PSOUND_THREAD;
 
 
@@ -142,6 +168,8 @@
     PWAVEHDR CurrentBuffer;
     PWAVEHDR FirstBuffer;
     PWAVEHDR LastBuffer;
+
+    /*DWORD RemainingBytes;*/
 } WAVE_THREAD_DATA, *PWAVE_THREAD_DATA;
 
 /*
@@ -476,7 +504,8 @@
 StartSoundThread(
     IN  PSOUND_DEVICE_INSTANCE Instance,
     IN  SOUND_THREAD_REQUEST_HANDLER RequestHandler,
-    IN  PVOID Data);
+    IN  SOUND_THREAD_IO_COMPLETION_HANDLER IoCompletionHandler,
+    IN  LPVOID PrivateThreadData);
 
 MMRESULT
 StopSoundThread(
@@ -492,6 +521,19 @@
 GetSoundThreadPrivateData(
     IN  PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
     OUT PVOID* PrivateData);
+
+VOID CALLBACK
+CompleteSoundThreadIo(
+    IN  DWORD dwErrorCode,
+    IN  DWORD dwNumberOfBytesTransferred,
+    IN  LPOVERLAPPED lpOverlapped);
+
+MMRESULT
+OverlappedWriteToSoundDevice(
+    IN  PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
+    IN  PVOID ContextData,
+    IN  PVOID Buffer,
+    IN  DWORD BufferSize);
 
 
 

Modified: branches/silverblade-audio/lib/drivers/sound/mmebuddy/thread.c
URL: http://svn.reactos.org/svn/reactos/branches/silverblade-audio/lib/drivers/sound/mmebuddy/thread.c?rev=34349&r1=34348&r2=34349&view=diff
==============================================================================
--- branches/silverblade-audio/lib/drivers/sound/mmebuddy/thread.c [iso-8859-1] (original)
+++ branches/silverblade-audio/lib/drivers/sound/mmebuddy/thread.c [iso-8859-1] Sun Jul  6 18:12:29 2008
@@ -11,6 +11,7 @@
     History:
         4 July 2008 - Created
         5 July 2008 - Implemented basic request processing
+        6 July 2008 - Added I/O completion handling
 */
 
 /*
@@ -24,21 +25,24 @@
 #include <mmebuddy.h>
 
 
+/*
+    This is the sound thread's processing loop. Its job is to aid in
+    asynchronous streaming of sound data with a kernel-mode driver. It's
+    basically a loop in which we wait for a request from the client, or
+    completed data from the kernel-mode driver.
+
+    When either of these events occur, the relevant details get passed on
+    to one of the routines specified when the thread was started.
+*/
 DWORD WINAPI
 SoundThreadProc(
     IN  LPVOID lpParameter)
 {
     PSOUND_DEVICE_INSTANCE Instance;
     PSOUND_THREAD Thread;
-    /*HANDLE Events[2];*/
 
     Instance = (PSOUND_DEVICE_INSTANCE) lpParameter;
     Thread = Instance->Thread;
-
-/*
-    Events[0] = Thread->KillEvent;
-    Events[1] = Thread->RequestEvent;
-*/
 
     Thread->Running = TRUE;
 
@@ -76,7 +80,29 @@
         else if ( WaitResult == WAIT_IO_COMPLETION )
         {
             /* This gets called after I/O completion */
-            /* Do we need to do anything special here? */
+            PSOUND_THREAD_COMPLETED_IO CompletionData;
+            SOUND_ASSERT(Thread->FirstCompletedIo);
+
+            SOUND_DEBUG(L"Outside I/O completion APC");
+
+            /*
+                Purge the completed data queue
+                FIXME? This will be done in the WRONG ORDER!
+                Is this such a problem? The caller won't care. We'll need
+                to remove them from our queue though!
+            */
+            while ( (CompletionData = Thread->FirstCompletedIo) )
+            {
+                /* Call high-level custom I/O completion routine */
+                Thread->IoCompletionHandler(Instance,
+                                            CompletionData->ContextData,
+                                            CompletionData->BytesTransferred);
+
+                /* TODO: I'm sure I've forgotten something ... */
+
+                Thread->FirstCompletedIo = CompletionData->Next;
+                FreeMemory(CompletionData);
+            }
         }
     }
 
@@ -141,6 +167,7 @@
 StartSoundThread(
     IN  PSOUND_DEVICE_INSTANCE Instance,
     IN  SOUND_THREAD_REQUEST_HANDLER RequestHandler,
+    IN  SOUND_THREAD_IO_COMPLETION_HANDLER IoCompletionHandler,
     IN  LPVOID PrivateThreadData)
 {
     PSOUND_THREAD SoundThread = NULL;
@@ -166,10 +193,14 @@
     SoundThread->PrivateData = PrivateThreadData;
     SoundThread->Running = FALSE;
     SoundThread->Handle = INVALID_HANDLE_VALUE;
+
     SoundThread->RequestHandler = RequestHandler;
     SoundThread->ReadyEvent = INVALID_HANDLE_VALUE;
     SoundThread->RequestEvent = INVALID_HANDLE_VALUE;
     SoundThread->DoneEvent = INVALID_HANDLE_VALUE;
+
+    SoundThread->IoCompletionHandler = IoCompletionHandler;
+    SoundThread->FirstCompletedIo = NULL;
 
     /* No need to initialise the requests */
 
@@ -293,3 +324,92 @@
 
     return MMSYSERR_NOERROR;
 }
+
+VOID CALLBACK
+CompleteSoundThreadIo(
+    IN  DWORD dwErrorCode,
+    IN  DWORD dwNumberOfBytesTransferred,
+    IN  LPOVERLAPPED lpOverlapped)
+{
+    PSOUND_DEVICE_INSTANCE SoundDeviceInstance;
+    PSOUND_THREAD_OVERLAPPED SoundThreadOverlapped;
+    PSOUND_THREAD_COMPLETED_IO CompletionData;
+
+    SoundThreadOverlapped = (PSOUND_THREAD_OVERLAPPED) lpOverlapped;
+    SOUND_ASSERT(SoundThreadOverlapped);
+
+    CompletionData = SoundThreadOverlapped->CompletionData;
+    SOUND_ASSERT(CompletionData);
+
+    SoundDeviceInstance = SoundThreadOverlapped->SoundDeviceInstance;
+    SOUND_ASSERT(SoundDeviceInstance);
+    SOUND_ASSERT(SoundDeviceInstance->Thread);
+
+    SOUND_DEBUG(L"New I/O Completion Callback Called");
+
+    /* This is going at the start of the list */
+    CompletionData->Next = SoundDeviceInstance->Thread->FirstCompletedIo;
+    SoundDeviceInstance->Thread->FirstCompletedIo = CompletionData;
+
+    /* Whatever information was supplied to us originally by the caller */
+    CompletionData->ContextData = SoundThreadOverlapped->ContextData;
+
+    /* How much data was transferred */
+    CompletionData->BytesTransferred = dwNumberOfBytesTransferred;
+
+    /* Overlapped structure gets freed now, but we still need the completion */
+    FreeMemory(SoundThreadOverlapped);
+
+    SOUND_DEBUG(L"New I/O Completion Callback Done");
+}
+
+MMRESULT
+OverlappedWriteToSoundDevice(
+    IN  PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
+    IN  PVOID ContextData,
+    IN  PVOID Buffer,
+    IN  DWORD BufferSize)
+{
+    PSOUND_THREAD_OVERLAPPED Overlapped;
+    PSOUND_THREAD_COMPLETED_IO CompletedIo;
+
+    if ( ! SoundDeviceInstance )
+        return MMSYSERR_INVALPARAM;
+
+    if ( ! SoundDeviceInstance->Thread )
+        return MMSYSERR_ERROR;  /* FIXME - better return code? */
+
+    if ( ! Buffer )
+        return MMSYSERR_INVALPARAM;
+
+    /* This will contain information about the write operation */
+    Overlapped = AllocateMemoryFor(SOUND_THREAD_OVERLAPPED);
+    if ( ! Overlapped )
+        return MMSYSERR_NOMEM;
+
+    /* We collect this on I/O completion */
+    CompletedIo = AllocateMemoryFor(SOUND_THREAD_COMPLETED_IO);
+    if ( ! CompletedIo )
+    {
+        FreeMemory(Overlapped);
+        return MMSYSERR_NOMEM;
+    }
+
+    ZeroMemory(Overlapped, sizeof(SOUND_THREAD_OVERLAPPED));
+    ZeroMemory(CompletedIo, sizeof(SOUND_THREAD_COMPLETED_IO));
+
+    /* We'll want to know which device to queue completion data to */
+    Overlapped->SoundDeviceInstance = SoundDeviceInstance;
+
+    /* Caller-supplied data, gets passed back on completion */
+    Overlapped->ContextData = ContextData;
+
+    /* The completion data buffer which will be filled later */
+    Overlapped->CompletionData = CompletedIo;
+
+    return WriteSoundDeviceBuffer(SoundDeviceInstance,
+                                  Buffer,
+                                  BufferSize,
+                                  CompleteSoundThreadIo,
+                                  (LPOVERLAPPED) Overlapped);
+}

Modified: branches/silverblade-audio/lib/drivers/sound/mmebuddy/wave/wavethread.c
URL: http://svn.reactos.org/svn/reactos/branches/silverblade-audio/lib/drivers/sound/mmebuddy/wave/wavethread.c?rev=34349&r1=34348&r2=34349&view=diff
==============================================================================
--- branches/silverblade-audio/lib/drivers/sound/mmebuddy/wave/wavethread.c [iso-8859-1] (original)
+++ branches/silverblade-audio/lib/drivers/sound/mmebuddy/wave/wavethread.c [iso-8859-1] Sun Jul  6 18:12:29 2008
@@ -10,6 +10,7 @@
 
     History:
         4 July 2008 - Created
+        6 July 2008 - Restructured to hide some of the low-level threading
 
     TODO:
         Track if a buffer has already been inserted?
@@ -20,35 +21,10 @@
 
 #include <mmebuddy.h>
 
-MMRESULT
-WriteWaveBufferToSoundDevice(
-    IN  PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
-    IN  PWAVE_THREAD_DATA ThreadData,
-    IN  PWAVEHDR WaveHeader);
-
-
-
-VOID CALLBACK
-WaveBufferCompleted(
-    IN  DWORD dwErrorCode,
-    IN  DWORD dwNumberOfBytesTransferred,
-    IN  LPOVERLAPPED lpOverlapped)
-{
-    PWAVE_OVERLAPPED WaveOverlapped = (PWAVE_OVERLAPPED) lpOverlapped;
-    /*PWAVE_THREAD_DATA ThreadData = WaveOverlapped->ThreadData;*/
-    WCHAR msg[1024];
-
-    wsprintf(msg, L"Buffer %x done\nWrote %d bytes\nErrCode %d",
-             WaveOverlapped->Header,
-             dwNumberOfBytesTransferred,
-             dwErrorCode);
-
-    MessageBox(0, msg, L"File IO Callback", MB_OK | MB_TASKMODAL);
-
-    WriteWaveBufferToSoundDevice(WaveOverlapped->SoundDeviceInstance,
-                                 WaveOverlapped->ThreadData,
-                                 WaveOverlapped->Header);
-}
+
+/*
+    Just a neat wrapper around the writing routine.
+*/
 
 MMRESULT
 WriteWaveBufferToSoundDevice(
@@ -60,21 +36,16 @@
     SOUND_ASSERT(ThreadData);
     SOUND_ASSERT(WaveHeader);
 
-    /* Prepare our overlapped data */
-    ZeroMemory(&ThreadData->Overlapped, sizeof(WAVE_OVERLAPPED));
-    ThreadData->Overlapped.SoundDeviceInstance = SoundDeviceInstance;
-    ThreadData->Overlapped.ThreadData = ThreadData;
-    ThreadData->Overlapped.Header = WaveHeader;
-
-    return WriteSoundDeviceBuffer(SoundDeviceInstance,
-                                  WaveHeader->lpData,
-                                  WaveHeader->dwBufferLength,
-                                  WaveBufferCompleted,
-                                  (LPOVERLAPPED) &ThreadData->Overlapped);
-}
-
-
-/* Internal dispatch routines */
+    return OverlappedWriteToSoundDevice(SoundDeviceInstance,
+                                        (PVOID) WaveHeader,
+                                        WaveHeader->lpData,
+                                        WaveHeader->dwBufferLength);
+}
+
+
+/*
+    Private thread dispatch routines
+*/
 
 MMRESULT
 SubmitWaveBuffer(
@@ -123,7 +94,9 @@
 }
 
 
-/* Thread callback */
+/*
+    Thread request dispatcher
+*/
 
 MMRESULT
 ProcessWaveThreadRequest(
@@ -155,6 +128,48 @@
 
     return MMSYSERR_NOTSUPPORTED;
 }
+
+
+/*
+    I/O completion
+    Called from outside the overlapped I/O completion APC
+*/
+
+VOID
+ProcessWaveIoCompletion(
+    IN  struct _SOUND_DEVICE_INSTANCE* Instance,
+    IN  PVOID ContextData,
+    IN  DWORD BytesTransferred)
+{
+    LPWAVEHDR WaveHeader = (LPWAVEHDR) ContextData;
+
+    SOUND_DEBUG(L"ProcessWaveIoCompletion called :)");
+    SOUND_DEBUG_HEX(WaveHeader);
+
+    /*
+        At this point we know:
+        - The sound device instance involved in the transaction
+        - The wave header for the buffer which just completed
+        - How much data was transferred
+
+        The next task is to figure out how much of the buffer
+        got played, and enqueue the next buffer if the current
+        one has been completed.
+
+        Basically, this routine is responsible for keeping the
+        stream of audio data to/from the sound driver going.
+
+        When no more WAVEHDRs are queued (ie, WaveHeader->lpNext
+        is NULL), no more buffers are submitted and the sound
+        thread will wait for more requests from the client before
+        it does anything else.
+    */
+}
+
+
+/*
+    Wave thread start/stop routines
+*/
 
 MMRESULT
 StartWaveThread(
@@ -181,6 +196,7 @@
     /* Kick off the thread */
     Result = StartSoundThread(Instance,
                               ProcessWaveThreadRequest,
+                              ProcessWaveIoCompletion,
                               WaveThreadData);
     if ( Result != MMSYSERR_NOERROR )
     {



More information about the Ros-diffs mailing list