1 /*
2  * PROJECT:     ReactOS Sound System "MME Buddy" Library
3  * LICENSE:     GPL - See COPYING in the top level directory
4  * FILE:        lib/drivers/sound/mmebuddy/devicelist.c
5  *
6  * PURPOSE:     Manages lists of sound devices.
7  *
8  * PROGRAMMERS: Andrew Greenwood (silverblade@reactos.org)
9 */
10 
11 #include "precomp.h"
12 
13 ULONG           SoundDeviceCounts[SOUND_DEVICE_TYPES];
14 PSOUND_DEVICE   SoundDeviceListHeads[SOUND_DEVICE_TYPES];
15 PSOUND_DEVICE   SoundDeviceListTails[SOUND_DEVICE_TYPES];
16 
17 /*
18     Handles the allocation and initialisation of a SOUND_DEVICE structure.
19 */
20 MMRESULT
AllocateSoundDevice(IN MMDEVICE_TYPE DeviceType,OUT PSOUND_DEVICE * SoundDevice)21 AllocateSoundDevice(
22     IN  MMDEVICE_TYPE DeviceType,
23     OUT PSOUND_DEVICE* SoundDevice)
24 {
25     PSOUND_DEVICE NewDevice;
26 
27     SND_ASSERT( IsValidSoundDeviceType(DeviceType) );
28     SND_ASSERT( SoundDevice );
29 
30     SND_TRACE(L"Allocating a SOUND_DEVICE structure\n");
31 
32     NewDevice = AllocateStruct(SOUND_DEVICE);
33 
34     if ( ! NewDevice )
35         return MMSYSERR_NOMEM;
36 
37     NewDevice->Type = DeviceType;
38 
39     /* Return the new structure to the caller and report success */
40     *SoundDevice = NewDevice;
41 
42     return MMSYSERR_NOERROR;
43 }
44 
45 /*
46     Handles the cleanup and freeing of a SOUND_DEVICE structure.
47 */
48 VOID
FreeSoundDevice(IN PSOUND_DEVICE SoundDevice)49 FreeSoundDevice(
50     IN  PSOUND_DEVICE SoundDevice)
51 {
52     SND_ASSERT( SoundDevice );
53 
54     SND_TRACE(L"Freeing a SOUND_DEVICE structure\n");
55 
56     /* For safety the whole struct gets zeroed */
57     ZeroMemory(SoundDevice, sizeof(SOUND_DEVICE));
58     FreeMemory(SoundDevice);
59 }
60 
61 /*
62     Returns the number of devices of the specified type which have been added
63     to the device lists. If an invalid device type is specified, the function
64     returns zero.
65 */
66 ULONG
GetSoundDeviceCount(IN MMDEVICE_TYPE DeviceType)67 GetSoundDeviceCount(
68     IN  MMDEVICE_TYPE DeviceType)
69 {
70     ULONG Index = SOUND_DEVICE_TYPE_TO_INDEX(DeviceType);
71 
72     if ( ! IsValidSoundDeviceType(DeviceType) )
73     {
74         return 0;
75     }
76 
77     SND_TRACE(L"Returning a count of %d devices\n", SoundDeviceCounts[Index]);
78     return SoundDeviceCounts[Index];
79 }
80 
81 /*
82     Determines if a sound device structure pointer is valid, firstly by
83     ensuring that it is not NULL, and then by checking that the device itself
84     exists in one of the device lists.
85 */
86 BOOLEAN
IsValidSoundDevice(IN PSOUND_DEVICE SoundDevice)87 IsValidSoundDevice(
88     IN  PSOUND_DEVICE SoundDevice)
89 {
90     UCHAR TypeIndex;
91     PSOUND_DEVICE CurrentDevice;
92 
93     if ( ! SoundDevice )
94         return FALSE;
95 
96     /* Go through all the device lists */
97     for ( TypeIndex = 0; TypeIndex < SOUND_DEVICE_TYPES; ++ TypeIndex )
98     {
99         CurrentDevice = SoundDeviceListHeads[TypeIndex];
100 
101         while ( CurrentDevice )
102         {
103             if ( CurrentDevice == SoundDevice )
104             {
105                 /* Found the device */
106                 return TRUE;
107             }
108 
109             CurrentDevice = CurrentDevice->Next;
110         }
111     }
112 
113     /* If we get here, nothing was found */
114     return FALSE;
115 }
116 
117 /*
118     Informs the MME-Buddy library that it should take ownership of a device.
119     The DevicePath is typically used for storing a device path (for subsequent
120     opening using CreateFile) but it can be a wide-string representing any
121     information that makes sense to your MME driver implementation.
122 
123     MME components which operate solely in user-mode (for example, MIDI
124     loopback devices) won't need to communicate with a kernel-mode device,
125     so in these situations DevicePath is likely to be NULL.
126 
127     Upon successful addition to the sound device list, the pointer to the new
128     device's SOUND_DEVICE structure is returned via SoundDevice.
129 */
130 MMRESULT
ListSoundDevice(IN MMDEVICE_TYPE DeviceType,IN PVOID Identifier OPTIONAL,OUT PSOUND_DEVICE * SoundDevice OPTIONAL)131 ListSoundDevice(
132     IN  MMDEVICE_TYPE DeviceType,
133     IN  PVOID Identifier OPTIONAL,
134     OUT PSOUND_DEVICE* SoundDevice OPTIONAL)
135 {
136     MMRESULT Result;
137     PSOUND_DEVICE NewDevice;
138     UCHAR TypeIndex = SOUND_DEVICE_TYPE_TO_INDEX(DeviceType);
139 
140     VALIDATE_MMSYS_PARAMETER( IsValidSoundDeviceType(DeviceType) );
141 
142     Result = AllocateSoundDevice(DeviceType, &NewDevice);
143 
144     if ( ! MMSUCCESS(Result) )
145     {
146         SND_ERR(L"Failed to allocate SOUND_DEVICE structure\n");
147         return Result;
148     }
149 
150     if ( ! SoundDeviceListHeads[TypeIndex] )
151     {
152         SND_TRACE(L"Putting first entry into device list %d\n", DeviceType);
153         SoundDeviceListHeads[TypeIndex] = NewDevice;
154         SoundDeviceListTails[TypeIndex] = NewDevice;
155     }
156     else
157     {
158         SND_TRACE(L"Putting another entry into device list %d\n", DeviceType);
159         SoundDeviceListTails[TypeIndex]->Next = NewDevice;
160         SoundDeviceListTails[TypeIndex] = NewDevice;
161     }
162 
163     /* Add to the count */
164     ++ SoundDeviceCounts[TypeIndex];
165 
166     /* Set up the default function table */
167     SetSoundDeviceFunctionTable(NewDevice, NULL);
168 
169     /* Set up other members of the structure */
170     NewDevice->Identifier = Identifier;
171     NewDevice->HeadInstance = NULL;
172     NewDevice->TailInstance = NULL;
173 
174     /* Fill in the caller's PSOUND_DEVICE */
175     if ( SoundDevice )
176     {
177         *SoundDevice = NewDevice;
178     }
179 
180     return MMSYSERR_NOERROR;
181 }
182 
183 /*
184     Removes a sound device from the list, and frees the memory associated
185     with its description.
186 */
187 MMRESULT
UnlistSoundDevice(IN MMDEVICE_TYPE DeviceType,IN PSOUND_DEVICE SoundDevice)188 UnlistSoundDevice(
189     IN  MMDEVICE_TYPE DeviceType,
190     IN  PSOUND_DEVICE SoundDevice)
191 {
192     PSOUND_DEVICE CurrentDevice, PreviousDevice;
193 
194     UCHAR TypeIndex = SOUND_DEVICE_TYPE_TO_INDEX(DeviceType);
195 
196     VALIDATE_MMSYS_PARAMETER( IsValidSoundDeviceType(DeviceType) );
197     VALIDATE_MMSYS_PARAMETER( IsValidSoundDevice(SoundDevice) );
198 
199     PreviousDevice = NULL;
200     CurrentDevice = SoundDeviceListHeads[TypeIndex];
201 
202     while ( CurrentDevice )
203     {
204         if ( CurrentDevice == SoundDevice )
205         {
206             if ( ! PreviousDevice )
207             {
208                 /* This is the head node */
209                 SND_TRACE(L"Removing head node from device list %d\n", DeviceType);
210                 SoundDeviceListHeads[TypeIndex] =
211                     SoundDeviceListHeads[TypeIndex]->Next;
212             }
213             else
214             {
215                 SND_TRACE(L"Removing node from device list %d\n", DeviceType);
216                 /* There are nodes before this one - cut our device out */
217                 PreviousDevice->Next = CurrentDevice->Next;
218             }
219 
220             if ( ! CurrentDevice->Next )
221             {
222                 /* This is the tail node */
223                 SND_TRACE(L"Removing tail node from device list %d\n", DeviceType);
224                 SoundDeviceListTails[TypeIndex] = PreviousDevice;
225             }
226         }
227 
228         PreviousDevice = CurrentDevice;
229         CurrentDevice = CurrentDevice->Next;
230     }
231 
232     /* Subtract from the count */
233     -- SoundDeviceCounts[TypeIndex];
234 
235     /* Finally, free up the deleted entry */
236     FreeSoundDevice(SoundDevice);
237 
238     return MMSYSERR_NOERROR;
239 }
240 
241 /*
242     Removes all devices from one of the device lists.
243 */
244 MMRESULT
UnlistSoundDevices(IN MMDEVICE_TYPE DeviceType)245 UnlistSoundDevices(
246     IN  MMDEVICE_TYPE DeviceType)
247 {
248     UCHAR TypeIndex;
249     VALIDATE_MMSYS_PARAMETER( IsValidSoundDeviceType(DeviceType) );
250 
251     SND_TRACE(L"Unlisting all sound devices of type %d\n", DeviceType);
252 
253     TypeIndex = SOUND_DEVICE_TYPE_TO_INDEX(DeviceType);
254 
255     /* Munch away at the head of the list until it's drained */
256     while ( SoundDeviceCounts[TypeIndex] > 0 )
257     {
258         MMRESULT Result;
259         Result = UnlistSoundDevice(DeviceType, SoundDeviceListHeads[TypeIndex]);
260         SND_ASSERT( Result == MMSYSERR_NOERROR );
261     }
262 
263     return MMSYSERR_NOERROR;
264 }
265 
266 /*
267     Removes all devices from all lists.
268 */
269 VOID
UnlistAllSoundDevices()270 UnlistAllSoundDevices()
271 {
272     MMDEVICE_TYPE Type;
273 
274     SND_TRACE(L"Unlisting all sound devices\n");
275 
276     for ( Type = MIN_SOUND_DEVICE_TYPE; Type <= MAX_SOUND_DEVICE_TYPE; ++ Type )
277     {
278         MMRESULT Result;
279         Result = UnlistSoundDevices(Type);
280         SND_ASSERT( Result == MMSYSERR_NOERROR );
281     }
282 }
283 
284 /*
285     Provides the caller with a pointer to its desired sound device, based on
286     the device type and index.
287 */
288 MMRESULT
GetSoundDevice(IN MMDEVICE_TYPE DeviceType,IN DWORD DeviceIndex,OUT PSOUND_DEVICE * SoundDevice)289 GetSoundDevice(
290     IN  MMDEVICE_TYPE DeviceType,
291     IN  DWORD DeviceIndex,
292     OUT PSOUND_DEVICE* SoundDevice)
293 {
294     UCHAR TypeIndex = SOUND_DEVICE_TYPE_TO_INDEX(DeviceType);
295     DWORD CurrentIndex = 0;
296     PSOUND_DEVICE CurrentDevice;
297 
298     VALIDATE_MMSYS_PARAMETER( IsValidSoundDeviceType(DeviceType) );
299 
300     if ( DeviceIndex >= SoundDeviceCounts[TypeIndex] )
301     {
302         SND_ERR(L"Invalid device ID %d for type %d\n", DeviceIndex, DeviceType);
303         return MMSYSERR_BADDEVICEID;
304     }
305 
306     CurrentDevice = SoundDeviceListHeads[TypeIndex];
307 
308     /* Following the earlier checks, the index should be valid here. */
309     for ( CurrentIndex = 0; CurrentIndex != DeviceIndex; ++ CurrentIndex )
310     {
311         SND_ASSERT( CurrentDevice );
312         CurrentDevice = CurrentDevice->Next;
313     }
314 
315     SND_TRACE(L"Returning sound device %x\n", CurrentDevice);
316 
317     *SoundDevice = CurrentDevice;
318 
319     return MMSYSERR_NOERROR;
320 }
321 
322 /*
323     Provides the caller with the device path of the specified sound device.
324     This will normally be the path to a device provided by a kernel-mode
325     driver.
326 */
327 MMRESULT
GetSoundDeviceIdentifier(IN PSOUND_DEVICE SoundDevice,OUT PVOID * Identifier)328 GetSoundDeviceIdentifier(
329     IN  PSOUND_DEVICE SoundDevice,
330     OUT PVOID* Identifier)
331 {
332     VALIDATE_MMSYS_PARAMETER( SoundDevice );
333     VALIDATE_MMSYS_PARAMETER( Identifier );
334 
335     /* The caller should not modify this! */
336     *Identifier = SoundDevice->Identifier;
337 
338     return MMSYSERR_NOERROR;
339 }
340 
341 /*
342     Provides the caller with the device type of the specified sound device.
343     This will be, for example, WAVE_OUT_DEVICE_TYPE, WAVE_IN_DEVICE_TYPE ...
344 */
345 MMRESULT
GetSoundDeviceType(IN PSOUND_DEVICE SoundDevice,OUT PMMDEVICE_TYPE DeviceType)346 GetSoundDeviceType(
347     IN  PSOUND_DEVICE SoundDevice,
348     OUT PMMDEVICE_TYPE DeviceType)
349 {
350     VALIDATE_MMSYS_PARAMETER( SoundDevice );
351     VALIDATE_MMSYS_PARAMETER( DeviceType );
352 
353     *DeviceType = SoundDevice->Type;
354 
355     return MMSYSERR_NOERROR;
356 }
357