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