xref: /reactos/dll/win32/mmdrv/session.c (revision c2c66aff)
1 /*
2  *
3  * COPYRIGHT:            See COPYING in the top level directory
4  * PROJECT:              ReactOS Multimedia
5  * FILE:                 dll/win32/mmdrv/session.c
6  * PURPOSE:              Multimedia User Mode Driver (session management)
7  * PROGRAMMER:           Andrew Greenwood
8  * UPDATE HISTORY:
9  *                       Jan 14, 2007: Created
10  */
11 
12 #include "mmdrv.h"
13 
14 #define NDEBUG
15 #include <debug.h>
16 
17 /* Each session is tracked, but the list must be locked when in use  */
18 
19 SessionInfo* session_list = NULL;
20 CRITICAL_SECTION session_lock;
21 
22 
23 /*
24     Obtains a pointer to the session associated with a device type and ID.
25     If no session exists, returns NULL. This is mainly used to see if a
26     session already exists prior to creating a new one.
27 */
28 
29 SessionInfo*
GetSession(DeviceType device_type,UINT device_id)30 GetSession(
31     DeviceType device_type,
32     UINT device_id)
33 {
34     SessionInfo* session_info;
35 
36     EnterCriticalSection(&session_lock);
37     session_info = session_list;
38 
39     while ( session_info )
40     {
41         if ( ( session_info->device_type == device_type ) &&
42              ( session_info->device_id == device_id ) )
43         {
44             LeaveCriticalSection(&session_lock);
45             return session_info;
46         }
47 
48         session_info = session_info->next;
49     }
50 
51     LeaveCriticalSection(&session_lock);
52     return NULL;
53 }
54 
55 
56 /*
57     Creates a new session, associated with the specified device type and ID.
58     Whilst the session list is locked, this also checks to see if an existing
59     session is associated with the device.
60 */
61 
62 MMRESULT
CreateSession(DeviceType device_type,UINT device_id,SessionInfo ** session_info)63 CreateSession(
64     DeviceType device_type,
65     UINT device_id,
66     SessionInfo** session_info)
67 {
68     HANDLE heap = GetProcessHeap();
69 
70     ASSERT(session_info);
71 
72     EnterCriticalSection(&session_lock);
73 
74     /* Ensure we're not creating a duplicate session */
75 
76     if ( GetSession(device_type, device_id) )
77     {
78         DPRINT("Already allocated session\n");
79         LeaveCriticalSection(&session_lock);
80         return MMSYSERR_ALLOCATED;
81     }
82 
83     *session_info = HeapAlloc(heap, HEAP_ZERO_MEMORY, sizeof(SessionInfo));
84 
85     if ( ! *session_info )
86     {
87         DPRINT("Failed to allocate mem for session info\n");
88         LeaveCriticalSection(&session_lock);
89         return MMSYSERR_NOMEM;
90     }
91 
92     (*session_info)->device_type = device_type;
93     (*session_info)->device_id = device_id;
94 
95     /* Add to the list */
96 
97     (*session_info)->next = session_list;
98     session_list = *session_info;
99 
100     LeaveCriticalSection(&session_lock);
101 
102     return MMSYSERR_NOERROR;
103 }
104 
105 
106 /*
107     Removes a session from the list and destroys it. This function does NOT
108     perform any additional cleanup. Think of it as a slightly more advanced
109     free()
110 */
111 
112 VOID
DestroySession(SessionInfo * session)113 DestroySession(SessionInfo* session)
114 {
115     HANDLE heap = GetProcessHeap();
116     SessionInfo* session_node;
117     SessionInfo* session_prev;
118 
119     /* TODO: More cleanup stuff */
120 
121     /* Remove from the list */
122 
123     EnterCriticalSection(&session_lock);
124 
125     session_node = session_list;
126     session_prev = NULL;
127 
128     while ( session_node )
129     {
130         if ( session_node == session )
131         {
132             /* Bridge the gap for when we go */
133             session_prev->next = session->next;
134             break;
135         }
136 
137         /* Save the previous node, fetch the next */
138         session_prev = session_node;
139         session_node = session_node->next;
140     }
141 
142     LeaveCriticalSection(&session_lock);
143 
144     HeapFree(heap, 0, session);
145 }
146 
147 
148 /*
149     Allocates events and other resources for the session thread, starts it,
150     and waits for it to announce that it is ready to work for us.
151 */
152 
153 MMRESULT
StartSessionThread(SessionInfo * session_info)154 StartSessionThread(SessionInfo* session_info)
155 {
156     LPTASKCALLBACK task;
157     MMRESULT result;
158 
159     ASSERT(session_info);
160 
161     /* This is our "ready" event, sent when the thread is idle */
162 
163     session_info->thread.ready_event = CreateEvent(NULL, FALSE, FALSE, NULL);
164 
165     if ( ! session_info->thread.ready_event )
166     {
167         DPRINT("Couldn't create thread_ready event\n");
168         return MMSYSERR_NOMEM;
169     }
170 
171     /* This is our "go" event, sent when we want the thread to do something */
172 
173     session_info->thread.go_event = CreateEvent(NULL, FALSE, FALSE, NULL);
174 
175     if ( ! session_info->thread.go_event )
176     {
177         DPRINT("Couldn't create thread_go event\n");
178         CloseHandle(session_info->thread.ready_event);
179         return MMSYSERR_NOMEM;
180     }
181 
182     /* TODO - other kinds of devices need attention, too */
183     task = ( session_info->device_type == WaveOutDevice )
184            ? (LPTASKCALLBACK) WaveThread : NULL;
185 
186     ASSERT(task);
187 
188     /* Effectively, this is a beefed-up CreateThread */
189 
190     result = mmTaskCreate(task,
191                           &session_info->thread.handle,
192                           (DWORD_PTR)session_info);
193 
194     if ( result != MMSYSERR_NOERROR )
195     {
196         DPRINT("Task creation failed\n");
197         CloseHandle(session_info->thread.ready_event);
198         CloseHandle(session_info->thread.go_event);
199         return result;
200     }
201 
202     /* Wait for the thread to be ready before completing */
203 
204     WaitForSingleObject(session_info->thread.ready_event, INFINITE);
205 
206     return MMSYSERR_NOERROR;
207 }
208 
209 
210 /*
211     The session thread is pretty simple. Upon creation, it announces that it
212     is ready to do stuff for us. When we want it to perform an action, we use
213     CallSessionThread with an appropriate function and parameter, then tell
214     the thread we want it to do something. When it's finished, it announces
215     that it is ready once again.
216 */
217 
218 MMRESULT
CallSessionThread(SessionInfo * session_info,ThreadFunction function,PVOID thread_parameter)219 CallSessionThread(
220     SessionInfo* session_info,
221     ThreadFunction function,
222     PVOID thread_parameter)
223 {
224     ASSERT(session_info);
225 
226     session_info->thread.function = function;
227     session_info->thread.parameter = thread_parameter;
228 
229     DPRINT("Calling session thread\n");
230     SetEvent(session_info->thread.go_event);
231 
232     DPRINT("Waiting for thread response\n");
233     WaitForSingleObject(session_info->thread.ready_event, INFINITE);
234 
235     return session_info->thread.result;
236 }
237 
238 
239 DWORD
HandleBySessionThread(DWORD_PTR private_handle,DWORD_PTR message,DWORD_PTR parameter)240 HandleBySessionThread(
241     DWORD_PTR private_handle,
242     DWORD_PTR message,
243     DWORD_PTR parameter)
244 {
245     return CallSessionThread((SessionInfo*) private_handle,
246                              message,
247                              (PVOID) parameter);
248 }
249