xref: /reactos/dll/win32/mmdrv/common.c (revision 84ccccab)
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 
262