Sync to trunk revision 63857.
[reactos.git] / dll / win32 / mmdrv / session.c
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*
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
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
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
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
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
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 }