Merge trunk head (r43756)
[reactos.git] / reactos / dll / win32 / mmdrv / common.c
1 /*
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS Multimedia
5 * FILE: dll/win32/mmdrv/common.c
6 * PURPOSE: Multimedia User Mode Driver (Common functions)
7 * PROGRAMMER: Andrew Greenwood
8 * UPDATE HISTORY:
9 * Jan 14, 2007: Created
10 */
11
12 #include <mmdrv.h>
13
14 /*
15 Translates errors to MMRESULT codes.
16 */
17
18 MMRESULT
19 ErrorToMmResult(UINT error_code)
20 {
21 switch ( error_code )
22 {
23 case NO_ERROR :
24 case ERROR_IO_PENDING :
25 return MMSYSERR_NOERROR;
26
27 case ERROR_BUSY :
28 return MMSYSERR_ALLOCATED;
29
30 case ERROR_NOT_SUPPORTED :
31 case ERROR_INVALID_FUNCTION :
32 return MMSYSERR_NOTSUPPORTED;
33
34 case ERROR_NOT_ENOUGH_MEMORY :
35 return MMSYSERR_NOMEM;
36
37 case ERROR_ACCESS_DENIED :
38 return MMSYSERR_BADDEVICEID;
39
40 case ERROR_INSUFFICIENT_BUFFER :
41 return MMSYSERR_INVALPARAM;
42 };
43
44 /* If all else fails, it's just a plain old error */
45
46 return MMSYSERR_ERROR;
47 }
48
49
50 /*
51 Obtains a device count for a specific kind of device.
52 */
53
54 DWORD
55 GetDeviceCount(DeviceType device_type)
56 {
57 UINT index = 0;
58 HANDLE handle;
59
60 /* Cycle through devices until an error occurs */
61
62 while ( OpenKernelDevice(device_type, index, GENERIC_READ, &handle) == MMSYSERR_NOERROR )
63 {
64 CloseHandle(handle);
65 index ++;
66 }
67
68 DPRINT("Found %d devices of type %d\n", index, device_type);
69
70 return index;
71 }
72
73
74 /*
75 Obtains device capabilities. This could either be done as individual
76 functions for wave, MIDI and aux, or like this. I chose this method as
77 it centralizes everything.
78 */
79
80 DWORD
81 GetDeviceCapabilities(
82 DeviceType device_type,
83 UINT device_id,
84 DWORD_PTR capabilities,
85 DWORD capabilities_size)
86 {
87 MMRESULT result;
88 DWORD ioctl;
89 HANDLE handle;
90 DWORD bytes_returned;
91 BOOL device_io_result;
92
93 ASSERT(capabilities);
94
95 /* Choose the right IOCTL for the job */
96
97 if ( IsWaveDevice(device_type) )
98 ioctl = IOCTL_WAVE_GET_CAPABILITIES;
99 else if ( IsMidiDevice(device_type) )
100 ioctl = IOCTL_MIDI_GET_CAPABILITIES;
101 else if ( IsAuxDevice(device_type) )
102 return MMSYSERR_NOTSUPPORTED; /* TODO */
103 else
104 return MMSYSERR_NOTSUPPORTED;
105
106 result = OpenKernelDevice(device_type,
107 device_id,
108 GENERIC_READ,
109 &handle);
110
111 if ( result != MMSYSERR_NOERROR )
112 {
113 DPRINT("Failed to open kernel device\n");
114 return result;
115 }
116
117 device_io_result = DeviceIoControl(handle,
118 ioctl,
119 NULL,
120 0,
121 (LPVOID) capabilities,
122 capabilities_size,
123 &bytes_returned,
124 NULL);
125
126 /* Translate result */
127
128 if ( device_io_result )
129 result = MMSYSERR_NOERROR;
130 else
131 result = ErrorToMmResult(GetLastError());
132
133 /* Clean up and return */
134
135 CloseKernelDevice(handle);
136
137 return result;
138 }
139
140
141 /*
142 A wrapper around OpenKernelDevice that creates a session,
143 opens the kernel device, initializes session data and notifies
144 the client (application) that the device has been opened. Again,
145 this supports any device type and the only real difference is
146 the open descriptor.
147 */
148
149 DWORD
150 OpenDevice(
151 DeviceType device_type,
152 UINT device_id,
153 PVOID open_descriptor,
154 DWORD flags,
155 DWORD_PTR private_handle)
156 {
157 SessionInfo* session_info;
158 MMRESULT result;
159 DWORD message;
160
161 /* This will automatically check for duplicate sessions */
162 result = CreateSession(device_type, device_id, &session_info);
163
164 if ( result != MMSYSERR_NOERROR )
165 {
166 DPRINT("Couldn't allocate session info\n");
167 return result;
168 }
169
170 result = OpenKernelDevice(device_type,
171 device_id,
172 GENERIC_READ,
173 &session_info->kernel_device_handle);
174
175 if ( result != MMSYSERR_NOERROR )
176 {
177 DPRINT("Failed to open kernel device\n");
178 DestroySession(session_info);
179 return result;
180 }
181
182 /* Set common session data */
183
184 session_info->flags = flags;
185
186 /* Set wave/MIDI specific data */
187
188 if ( IsWaveDevice(device_type) )
189 {
190 LPWAVEOPENDESC wave_open_desc = (LPWAVEOPENDESC) open_descriptor;
191 session_info->callback = wave_open_desc->dwCallback;
192 session_info->mme_wave_handle = wave_open_desc->hWave;
193 session_info->app_user_data = wave_open_desc->dwInstance;
194 }
195 else
196 {
197 DPRINT("Only wave devices are supported at present!\n");
198 DestroySession(session_info);
199 return MMSYSERR_NOTSUPPORTED;
200 }
201
202 /* Start the processing thread */
203
204 result = StartSessionThread(session_info);
205
206 if ( result != MMSYSERR_NOERROR )
207 {
208 DestroySession(session_info);
209 return result;
210 }
211
212 /* Store the session info */
213
214 *((SessionInfo**)private_handle) = session_info;
215
216 /* Send the right message */
217
218 message = (device_type == WaveOutDevice) ? WOM_OPEN :
219 (device_type == WaveInDevice) ? WIM_OPEN :
220 (device_type == MidiOutDevice) ? MOM_OPEN :
221 (device_type == MidiInDevice) ? MIM_OPEN : 0xFFFFFFFF;
222
223 NotifyClient(session_info, message, 0, 0);
224
225 return MMSYSERR_NOERROR;
226 }
227
228
229 /*
230 Attempts to close a device. This can fail if playback/recording has
231 not been stopped. We need to make sure it's safe to destroy the
232 session as well (mainly by killing the session thread.)
233 */
234
235 DWORD
236 CloseDevice(
237 DWORD_PTR private_handle)
238 {
239 MMRESULT result;
240 SessionInfo* session_info = (SessionInfo*) private_handle;
241 /* TODO: Maybe this is best off inside the playback thread? */
242
243 ASSERT(session_info);
244
245 result = CallSessionThread(session_info, WODM_CLOSE, 0);
246
247 if ( result == MMSYSERR_NOERROR )
248 {
249 /* TODO: Wait for it to be safe to terminate */
250
251 CloseKernelDevice(session_info->kernel_device_handle);
252
253 DestroySession(session_info);
254 }
255
256 return result;
257 }
258