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