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