1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS Kernel Streaming 4 * FILE: lib/drivers/sound/mmixer/sup.c 5 * PURPOSE: Mixer Support Functions 6 * PROGRAMMER: Johannes Anderwald 7 */ 8 9 #include "precomp.h" 10 11 #define YDEBUG 12 #include <debug.h> 13 14 const GUID KSNODETYPE_SUM = {0xDA441A60L, 0xC556, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}}; 15 const GUID KSNODETYPE_DAC = {0x507AE360L, 0xC554, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}}; 16 const GUID KSNODETYPE_ADC = {0x4D837FE0L, 0xC555, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}}; 17 const GUID KSNODETYPE_AGC = {0xE88C9BA0L, 0xC557, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}}; 18 const GUID KSNODETYPE_LOUDNESS = {0x41887440L, 0xC558, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}}; 19 const GUID KSNODETYPE_MUTE = {0x02B223C0L, 0xC557, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}}; 20 const GUID KSNODETYPE_TONE = {0x7607E580L, 0xC557, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}}; 21 const GUID KSNODETYPE_VOLUME = {0x3A5ACC00L, 0xC557, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}}; 22 const GUID KSNODETYPE_PEAKMETER = {0xa085651e, 0x5f0d, 0x4b36, {0xa8, 0x69, 0xd1, 0x95, 0xd6, 0xab, 0x4b, 0x9e}}; 23 const GUID KSNODETYPE_MUX = {0x2CEAF780, 0xC556, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}}; 24 const GUID KSNODETYPE_STEREO_WIDE = {0xA9E69800L, 0xC558, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}}; 25 const GUID KSNODETYPE_CHORUS = {0x20173F20L, 0xC559, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}}; 26 const GUID KSNODETYPE_REVERB = {0xEF0328E0L, 0xC558, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}}; 27 const GUID KSNODETYPE_SUPERMIX = {0xE573ADC0L, 0xC555, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}}; 28 29 const GUID KSPROPSETID_Audio = {0x45FFAAA0L, 0x6E1B, 0x11D0, {0xBC, 0xF2, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}; 30 const GUID KSPROPSETID_Pin = {0x8C134960L, 0x51AD, 0x11CF, {0x87, 0x8A, 0x94, 0xF8, 0x01, 0xC1, 0x00, 0x00}}; 31 const GUID KSPROPSETID_General = {0x1464EDA5L, 0x6A8F, 0x11D1, {0x9A, 0xA7, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}}; 32 const GUID KSPROPSETID_Topology = {0x720D4AC0L, 0x7533, 0x11D0, {0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00}}; 33 const GUID KSEVENTSETID_AudioControlChange = {0xE85E9698L, 0xFA2F, 0x11D1, {0x95, 0xBD, 0x00, 0xC0, 0x4F, 0xB9, 0x25, 0xD3}}; 34 35 const GUID KSDATAFORMAT_TYPE_MUSIC = {0xE725D360L, 0x62CC, 0x11CF, {0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00}}; 36 const GUID KSDATAFORMAT_SUBTYPE_MIDI = {0x1D262760L, 0xE957, 0x11CF, {0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00}}; 37 const GUID KSDATAFORMAT_SPECIFIER_NONE = {0x0F6417D6L, 0xC318, 0x11D0, {0xA4, 0x3F, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}}; 38 39 40 MIXER_STATUS 41 MMixerVerifyContext( 42 IN PMIXER_CONTEXT MixerContext) 43 { 44 if (MixerContext->SizeOfStruct != sizeof(MIXER_CONTEXT)) 45 return MM_STATUS_INVALID_PARAMETER; 46 47 if (!MixerContext->Alloc || !MixerContext->Control || !MixerContext->Free || !MixerContext->Open || 48 !MixerContext->AllocEventData || !MixerContext->FreeEventData || 49 !MixerContext->Close || !MixerContext->OpenKey || !MixerContext->QueryKeyValue || !MixerContext->CloseKey) 50 return MM_STATUS_INVALID_PARAMETER; 51 52 if (!MixerContext->MixerContext) 53 return MM_STATUS_INVALID_PARAMETER; 54 55 return MM_STATUS_SUCCESS; 56 } 57 58 LPMIXERLINE_EXT 59 MMixerGetMixerLineContainingNodeId( 60 IN LPMIXER_INFO MixerInfo, 61 IN ULONG NodeID) 62 { 63 PLIST_ENTRY Entry, ControlEntry; 64 LPMIXERLINE_EXT MixerLineSrc; 65 LPMIXERCONTROL_EXT MixerControl; 66 67 /* get first entry */ 68 Entry = MixerInfo->LineList.Flink; 69 70 while(Entry != &MixerInfo->LineList) 71 { 72 MixerLineSrc = (LPMIXERLINE_EXT)CONTAINING_RECORD(Entry, MIXERLINE_EXT, Entry); 73 74 ControlEntry = MixerLineSrc->ControlsList.Flink; 75 while(ControlEntry != &MixerLineSrc->ControlsList) 76 { 77 MixerControl = (LPMIXERCONTROL_EXT)CONTAINING_RECORD(ControlEntry, MIXERCONTROL_EXT, Entry); 78 if (MixerControl->NodeID == NodeID) 79 { 80 return MixerLineSrc; 81 } 82 ControlEntry = ControlEntry->Flink; 83 } 84 Entry = Entry->Flink; 85 } 86 87 return NULL; 88 } 89 90 VOID 91 MMixerGetLowestLogicalTopologyPinOffsetFromArray( 92 IN ULONG LogicalPinArrayCount, 93 IN PULONG LogicalPinArray, 94 OUT PULONG PinOffset) 95 { 96 ULONG Index; 97 ULONG LowestId = 0; 98 99 for(Index = 1; Index < LogicalPinArrayCount; Index++) 100 { 101 if (LogicalPinArray[Index] != MAXULONG) 102 { 103 /* sanity check: logical pin id must be unique */ 104 ASSERT(LogicalPinArray[Index] != LogicalPinArray[LowestId]); 105 } 106 107 if (LogicalPinArray[Index] < LogicalPinArray[LowestId]) 108 LowestId = Index; 109 } 110 111 /* store result */ 112 *PinOffset = LowestId; 113 } 114 115 VOID 116 MMixerFreeMixerInfo( 117 IN PMIXER_CONTEXT MixerContext, 118 IN LPMIXER_INFO MixerInfo) 119 { 120 /* UNIMPLEMENTED; 121 * FIXME 122 * free all lines 123 */ 124 125 MixerContext->Free((PVOID)MixerInfo); 126 } 127 128 129 LPMIXER_DATA 130 MMixerGetMixerDataByDeviceHandle( 131 IN PMIXER_CONTEXT MixerContext, 132 IN HANDLE hDevice) 133 { 134 LPMIXER_DATA MixerData; 135 PLIST_ENTRY Entry; 136 PMIXER_LIST MixerList; 137 138 /* get mixer list */ 139 MixerList = (PMIXER_LIST)MixerContext->MixerContext; 140 141 if (!MixerList->MixerDataCount) 142 return NULL; 143 144 Entry = MixerList->MixerData.Flink; 145 146 while(Entry != &MixerList->MixerData) 147 { 148 MixerData = (LPMIXER_DATA)CONTAINING_RECORD(Entry, MIXER_DATA, Entry); 149 150 if (MixerData->hDevice == hDevice) 151 return MixerData; 152 153 /* move to next mixer entry */ 154 Entry = Entry->Flink; 155 } 156 return NULL; 157 } 158 159 160 LPMIXER_INFO 161 MMixerGetMixerInfoByIndex( 162 IN PMIXER_CONTEXT MixerContext, 163 IN ULONG MixerIndex) 164 { 165 LPMIXER_INFO MixerInfo; 166 PLIST_ENTRY Entry; 167 PMIXER_LIST MixerList; 168 ULONG Index = 0; 169 170 /* get mixer list */ 171 MixerList = (PMIXER_LIST)MixerContext->MixerContext; 172 173 if (!MixerList->MixerListCount) 174 return NULL; 175 176 Entry = MixerList->MixerList.Flink; 177 178 while(Entry != &MixerList->MixerList) 179 { 180 MixerInfo = (LPMIXER_INFO)CONTAINING_RECORD(Entry, MIXER_INFO, Entry); 181 182 if (Index == MixerIndex) 183 return MixerInfo; 184 185 /* move to next mixer entry */ 186 Index++; 187 Entry = Entry->Flink; 188 } 189 190 return NULL; 191 } 192 193 MIXER_STATUS 194 MMixerGetMixerByName( 195 IN PMIXER_LIST MixerList, 196 IN LPWSTR MixerName, 197 OUT LPMIXER_INFO *OutMixerInfo) 198 { 199 LPMIXER_INFO MixerInfo; 200 PLIST_ENTRY Entry; 201 202 Entry = MixerList->MixerList.Flink; 203 while(Entry != &MixerList->MixerList) 204 { 205 MixerInfo = (LPMIXER_INFO)CONTAINING_RECORD(Entry, MIXER_INFO, Entry); 206 207 DPRINT1("MixerName %S MixerName %S\n", MixerInfo->MixCaps.szPname, MixerName); 208 if (wcsicmp(MixerInfo->MixCaps.szPname, MixerName) == 0) 209 { 210 *OutMixerInfo = MixerInfo; 211 return MM_STATUS_SUCCESS; 212 } 213 /* move to next mixer entry */ 214 Entry = Entry->Flink; 215 } 216 217 return MM_STATUS_UNSUCCESSFUL; 218 } 219 220 LPMIXERLINE_EXT 221 MMixerGetSourceMixerLineByLineId( 222 LPMIXER_INFO MixerInfo, 223 DWORD dwLineID) 224 { 225 PLIST_ENTRY Entry; 226 LPMIXERLINE_EXT MixerLineSrc; 227 228 /* get first entry */ 229 Entry = MixerInfo->LineList.Flink; 230 231 while(Entry != &MixerInfo->LineList) 232 { 233 MixerLineSrc = (LPMIXERLINE_EXT)CONTAINING_RECORD(Entry, MIXERLINE_EXT, Entry); 234 DPRINT("dwLineID %x dwLineID %x MixerLineSrc %p\n", MixerLineSrc->Line.dwLineID, dwLineID, MixerLineSrc); 235 if (MixerLineSrc->Line.dwLineID == dwLineID) 236 return MixerLineSrc; 237 238 Entry = Entry->Flink; 239 } 240 241 return NULL; 242 } 243 244 LPGUID 245 MMixerGetNodeType( 246 IN PKSMULTIPLE_ITEM MultipleItem, 247 IN ULONG Index) 248 { 249 LPGUID NodeType; 250 251 ASSERT(Index < MultipleItem->Count); 252 253 NodeType = (LPGUID)(MultipleItem + 1); 254 return &NodeType[Index]; 255 } 256 257 LPMIXERLINE_EXT 258 MMixerGetSourceMixerLineByComponentType( 259 LPMIXER_INFO MixerInfo, 260 DWORD dwComponentType) 261 { 262 PLIST_ENTRY Entry; 263 LPMIXERLINE_EXT MixerLineSrc; 264 265 /* get first entry */ 266 Entry = MixerInfo->LineList.Flink; 267 268 while(Entry != &MixerInfo->LineList) 269 { 270 MixerLineSrc = (LPMIXERLINE_EXT)CONTAINING_RECORD(Entry, MIXERLINE_EXT, Entry); 271 if (MixerLineSrc->Line.dwComponentType == dwComponentType) 272 return MixerLineSrc; 273 274 Entry = Entry->Flink; 275 } 276 277 return NULL; 278 } 279 280 MIXER_STATUS 281 MMixerGetMixerControlById( 282 LPMIXER_INFO MixerInfo, 283 DWORD dwControlID, 284 LPMIXERLINE_EXT *OutMixerLine, 285 LPMIXERCONTROL_EXT *OutMixerControl, 286 PULONG NodeId) 287 { 288 PLIST_ENTRY Entry, ControlEntry; 289 LPMIXERLINE_EXT MixerLineSrc; 290 LPMIXERCONTROL_EXT MixerControl; 291 292 /* get first entry */ 293 Entry = MixerInfo->LineList.Flink; 294 295 while(Entry != &MixerInfo->LineList) 296 { 297 MixerLineSrc = (LPMIXERLINE_EXT)CONTAINING_RECORD(Entry, MIXERLINE_EXT, Entry); 298 299 ControlEntry = MixerLineSrc->ControlsList.Flink; 300 while(ControlEntry != &MixerLineSrc->ControlsList) 301 { 302 MixerControl = (LPMIXERCONTROL_EXT)CONTAINING_RECORD(ControlEntry, MIXERCONTROL_EXT, Entry); 303 if (MixerControl->Control.dwControlID == dwControlID) 304 { 305 if (OutMixerLine) 306 *OutMixerLine = MixerLineSrc; 307 if (OutMixerControl) 308 *OutMixerControl = MixerControl; 309 if (NodeId) 310 *NodeId = MixerControl->NodeID; 311 return MM_STATUS_SUCCESS; 312 } 313 ControlEntry = ControlEntry->Flink; 314 } 315 Entry = Entry->Flink; 316 } 317 318 return MM_STATUS_UNSUCCESSFUL; 319 } 320 321 ULONG 322 MMixerGetVolumeControlIndex( 323 LPMIXERVOLUME_DATA VolumeData, 324 LONG Value) 325 { 326 ULONG Index; 327 328 for(Index = 0; Index < VolumeData->ValuesCount; Index++) 329 { 330 if (VolumeData->Values[Index] > Value) 331 { 332 return VolumeData->InputSteppingDelta * Index; 333 } 334 } 335 return VolumeData->InputSteppingDelta * (VolumeData->ValuesCount-1); 336 } 337 338 VOID 339 MMixerNotifyControlChange( 340 IN PMIXER_CONTEXT MixerContext, 341 IN LPMIXER_INFO MixerInfo, 342 IN ULONG NotificationType, 343 IN ULONG Value) 344 { 345 PLIST_ENTRY Entry; 346 PEVENT_NOTIFICATION_ENTRY NotificationEntry; 347 348 /* enumerate list and perform notification */ 349 Entry = MixerInfo->EventList.Flink; 350 while(Entry != &MixerInfo->EventList) 351 { 352 /* get notification entry offset */ 353 NotificationEntry = (PEVENT_NOTIFICATION_ENTRY)CONTAINING_RECORD(Entry, EVENT_NOTIFICATION_ENTRY, Entry); 354 355 if (NotificationEntry->MixerEventRoutine) 356 { 357 /* now perform the callback */ 358 NotificationEntry->MixerEventRoutine(NotificationEntry->MixerEventContext, (HANDLE)MixerInfo, NotificationType, Value); 359 } 360 361 /* move to next notification entry */ 362 Entry = Entry->Flink; 363 } 364 } 365 366 MIXER_STATUS 367 MMixerSetGetMuteControlDetails( 368 IN PMIXER_CONTEXT MixerContext, 369 IN LPMIXER_INFO MixerInfo, 370 IN LPMIXERCONTROL_EXT MixerControl, 371 IN ULONG dwLineID, 372 IN LPMIXERCONTROLDETAILS MixerControlDetails, 373 IN ULONG bSet) 374 { 375 LPMIXERCONTROLDETAILS_BOOLEAN Input; 376 LONG Value; 377 MIXER_STATUS Status; 378 379 if (MixerControlDetails->cbDetails != sizeof(MIXERCONTROLDETAILS_BOOLEAN)) 380 return MM_STATUS_INVALID_PARAMETER; 381 382 /* get input */ 383 Input = (LPMIXERCONTROLDETAILS_BOOLEAN)MixerControlDetails->paDetails; 384 385 /* FIXME SEH */ 386 if (bSet) 387 Value = Input->fValue; 388 389 /* set control details */ 390 Status = MMixerSetGetControlDetails(MixerContext, MixerControl->hDevice, MixerControl->NodeID, bSet, KSPROPERTY_AUDIO_MUTE, 0, &Value); 391 392 if (Status != MM_STATUS_SUCCESS) 393 return Status; 394 395 /* FIXME SEH */ 396 if (!bSet) 397 { 398 Input->fValue = Value; 399 return Status; 400 } 401 else 402 { 403 /* notify wdmaud clients MM_MIXM_LINE_CHANGE dwLineID */ 404 MMixerNotifyControlChange(MixerContext, MixerInfo, MM_MIXM_LINE_CHANGE, dwLineID); 405 } 406 407 return Status; 408 } 409 410 MIXER_STATUS 411 MMixerSetGetMuxControlDetails( 412 IN PMIXER_CONTEXT MixerContext, 413 IN LPMIXER_INFO MixerInfo, 414 IN ULONG NodeId, 415 IN ULONG bSet, 416 IN ULONG Flags, 417 IN LPMIXERCONTROL_EXT MixerControl, 418 IN LPMIXERCONTROLDETAILS MixerControlDetails, 419 IN LPMIXERLINE_EXT MixerLine) 420 { 421 MIXER_STATUS Status; 422 PULONG LogicalNodes, ConnectedNodes; 423 ULONG LogicalNodesCount, ConnectedNodesCount, Index, CurLogicalPinOffset, BytesReturned, OldLogicalPinOffset; 424 LPMIXER_DATA MixerData; 425 LPMIXERCONTROLDETAILS_LISTTEXTW ListText; 426 LPMIXERCONTROLDETAILS_BOOLEAN Values; 427 LPMIXERLINE_EXT SourceLine; 428 KSNODEPROPERTY Request; 429 430 DPRINT("MixerControlDetails %p\n", MixerControlDetails); 431 DPRINT("bSet %lx\n", bSet); 432 DPRINT("Flags %lx\n", Flags); 433 DPRINT("NodeId %lu\n", MixerControl->NodeID); 434 DPRINT("MixerControlDetails dwControlID %lu\n", MixerControlDetails->dwControlID); 435 DPRINT("MixerControlDetails cChannels %lu\n", MixerControlDetails->cChannels); 436 DPRINT("MixerControlDetails cMultipleItems %lu\n", MixerControlDetails->cMultipleItems); 437 DPRINT("MixerControlDetails cbDetails %lu\n", MixerControlDetails->cbDetails); 438 DPRINT("MixerControlDetails paDetails %p\n", MixerControlDetails->paDetails); 439 440 if (MixerControl->Control.fdwControl & MIXERCONTROL_CONTROLF_UNIFORM) 441 { 442 /* control acts uniform */ 443 if (MixerControlDetails->cChannels != 1) 444 { 445 /* expected 1 channel */ 446 DPRINT1("Expected 1 channel but got %lu\n", MixerControlDetails->cChannels); 447 return MM_STATUS_UNSUCCESSFUL; 448 } 449 } 450 451 /* check if multiple items match */ 452 if (MixerControlDetails->cMultipleItems != MixerControl->Control.cMultipleItems) 453 { 454 DPRINT1("MultipleItems mismatch %lu expected %lu\n", MixerControlDetails->cMultipleItems, MixerControl->Control.cMultipleItems); 455 return MM_STATUS_UNSUCCESSFUL; 456 } 457 458 if (bSet) 459 { 460 if ((Flags & MIXER_SETCONTROLDETAILSF_QUERYMASK) == MIXER_SETCONTROLDETAILSF_CUSTOM) 461 { 462 /* tell me when this is hit */ 463 ASSERT(FALSE); 464 } 465 else if ((Flags & (MIXER_SETCONTROLDETAILSF_VALUE | MIXER_SETCONTROLDETAILSF_CUSTOM)) == MIXER_SETCONTROLDETAILSF_VALUE) 466 { 467 /* sanity check */ 468 ASSERT(bSet == TRUE); 469 ASSERT(MixerControlDetails->cbDetails == sizeof(MIXERCONTROLDETAILS_BOOLEAN)); 470 471 Values = (LPMIXERCONTROLDETAILS_BOOLEAN)MixerControlDetails->paDetails; 472 CurLogicalPinOffset = MAXULONG; 473 for(Index = 0; Index < MixerControlDetails->cMultipleItems; Index++) 474 { 475 if (Values[Index].fValue) 476 { 477 /* mux can only activate one line at a time */ 478 ASSERT(CurLogicalPinOffset == MAXULONG); 479 CurLogicalPinOffset = Index; 480 } 481 } 482 483 /* setup request */ 484 Request.NodeId = NodeId; 485 Request.Reserved = 0; 486 Request.Property.Flags = KSPROPERTY_TYPE_TOPOLOGY | KSPROPERTY_TYPE_GET; 487 Request.Property.Id = KSPROPERTY_AUDIO_MUX_SOURCE; 488 Request.Property.Set = KSPROPSETID_Audio; 489 490 /* perform getting source */ 491 Status = MixerContext->Control(MixerControl->hDevice, IOCTL_KS_PROPERTY, (PVOID)&Request, sizeof(KSNODEPROPERTY), &OldLogicalPinOffset, sizeof(ULONG), &BytesReturned); 492 if (Status != MM_STATUS_SUCCESS) 493 { 494 /* failed to get source */ 495 return Status; 496 } 497 498 DPRINT("OldLogicalPinOffset %lu CurLogicalPinOffset %lu\n", OldLogicalPinOffset, CurLogicalPinOffset); 499 500 if (OldLogicalPinOffset == CurLogicalPinOffset) 501 { 502 /* cannot be unselected */ 503 return MM_STATUS_UNSUCCESSFUL; 504 } 505 506 /* perform setting source */ 507 Request.Property.Flags = KSPROPERTY_TYPE_TOPOLOGY | KSPROPERTY_TYPE_SET; 508 Status = MixerContext->Control(MixerControl->hDevice, IOCTL_KS_PROPERTY, (PVOID)&Request, sizeof(KSNODEPROPERTY), &CurLogicalPinOffset, sizeof(ULONG), &BytesReturned); 509 if (Status != MM_STATUS_SUCCESS) 510 { 511 /* failed to set source */ 512 return Status; 513 } 514 515 /* notify control change */ 516 MMixerNotifyControlChange(MixerContext, MixerInfo, MM_MIXM_CONTROL_CHANGE, MixerControl->Control.dwControlID ); 517 518 return Status; 519 } 520 } 521 else 522 { 523 if ((Flags & MIXER_GETCONTROLDETAILSF_QUERYMASK) == MIXER_GETCONTROLDETAILSF_VALUE) 524 { 525 /* setup request */ 526 Request.NodeId = NodeId; 527 Request.Reserved = 0; 528 Request.Property.Flags = KSPROPERTY_TYPE_TOPOLOGY | KSPROPERTY_TYPE_GET; 529 Request.Property.Id = KSPROPERTY_AUDIO_MUX_SOURCE; 530 Request.Property.Set = KSPROPSETID_Audio; 531 532 /* perform getting source */ 533 Status = MixerContext->Control(MixerControl->hDevice, IOCTL_KS_PROPERTY, (PVOID)&Request, sizeof(KSNODEPROPERTY), &OldLogicalPinOffset, sizeof(ULONG), &BytesReturned); 534 if (Status != MM_STATUS_SUCCESS) 535 { 536 /* failed to get source */ 537 return Status; 538 } 539 540 /* gets the corresponding mixer data */ 541 MixerData = MMixerGetMixerDataByDeviceHandle(MixerContext, MixerControl->hDevice); 542 543 /* sanity check */ 544 ASSERT(MixerData); 545 ASSERT(MixerData->Topology); 546 ASSERT(MixerData->MixerInfo == MixerInfo); 547 548 /* now allocate logical pin array */ 549 Status = MMixerAllocateTopologyNodeArray(MixerContext, MixerData->Topology, &LogicalNodes); 550 if (Status != MM_STATUS_SUCCESS) 551 { 552 /* no memory */ 553 return MM_STATUS_NO_MEMORY; 554 } 555 556 /* get logical pin nodes */ 557 MMixerGetConnectedFromLogicalTopologyPins(MixerData->Topology, MixerControl->NodeID, &LogicalNodesCount, LogicalNodes); 558 559 /* sanity check */ 560 ASSERT(LogicalNodesCount == MixerControlDetails->cMultipleItems); 561 ASSERT(LogicalNodesCount == MixerControl->Control.Metrics.dwReserved[0]); 562 563 Values = (LPMIXERCONTROLDETAILS_BOOLEAN)MixerControlDetails->paDetails; 564 for(Index = 0; Index < LogicalNodesCount; Index++) 565 { 566 /* getting logical pin offset */ 567 MMixerGetLowestLogicalTopologyPinOffsetFromArray(LogicalNodesCount, LogicalNodes, &CurLogicalPinOffset); 568 569 if (CurLogicalPinOffset == OldLogicalPinOffset) 570 { 571 /* mark index as active */ 572 Values[Index].fValue = TRUE; 573 } 574 else 575 { 576 /* index not active */ 577 Values[Index].fValue = FALSE; 578 } 579 580 /* mark offset as consumed */ 581 LogicalNodes[CurLogicalPinOffset] = MAXULONG; 582 } 583 584 /* cleanup */ 585 MixerContext->Free(LogicalNodes); 586 587 /* done */ 588 return MM_STATUS_SUCCESS; 589 } 590 else if ((Flags & MIXER_GETCONTROLDETAILSF_QUERYMASK) == MIXER_GETCONTROLDETAILSF_LISTTEXT) 591 { 592 /* sanity check */ 593 ASSERT(bSet == FALSE); 594 595 /* gets the corresponding mixer data */ 596 MixerData = MMixerGetMixerDataByDeviceHandle(MixerContext, MixerControl->hDevice); 597 598 /* sanity check */ 599 ASSERT(MixerData); 600 ASSERT(MixerData->Topology); 601 ASSERT(MixerData->MixerInfo == MixerInfo); 602 603 /* now allocate logical pin array */ 604 Status = MMixerAllocateTopologyNodeArray(MixerContext, MixerData->Topology, &LogicalNodes); 605 if (Status != MM_STATUS_SUCCESS) 606 { 607 /* no memory */ 608 return MM_STATUS_NO_MEMORY; 609 } 610 611 /* allocate connected node array */ 612 Status = MMixerAllocateTopologyNodeArray(MixerContext, MixerData->Topology, &ConnectedNodes); 613 if (Status != MM_STATUS_SUCCESS) 614 { 615 /* no memory */ 616 MixerContext->Free(LogicalNodes); 617 return MM_STATUS_NO_MEMORY; 618 } 619 620 /* get logical pin nodes */ 621 MMixerGetConnectedFromLogicalTopologyPins(MixerData->Topology, MixerControl->NodeID, &LogicalNodesCount, LogicalNodes); 622 623 /* get connected nodes */ 624 MMixerGetNextNodesFromNodeIndex(MixerContext, MixerData->Topology, MixerControl->NodeID, TRUE, &ConnectedNodesCount, ConnectedNodes); 625 626 /* sanity check */ 627 ASSERT(ConnectedNodesCount == LogicalNodesCount); 628 ASSERT(ConnectedNodesCount == MixerControlDetails->cMultipleItems); 629 ASSERT(ConnectedNodesCount == MixerControl->Control.Metrics.dwReserved[0]); 630 631 ListText = (LPMIXERCONTROLDETAILS_LISTTEXTW)MixerControlDetails->paDetails; 632 633 for(Index = 0; Index < ConnectedNodesCount; Index++) 634 { 635 /* getting logical pin offset */ 636 MMixerGetLowestLogicalTopologyPinOffsetFromArray(LogicalNodesCount, LogicalNodes, &CurLogicalPinOffset); 637 638 /* get mixer line with that node */ 639 SourceLine = MMixerGetMixerLineContainingNodeId(MixerInfo, ConnectedNodes[CurLogicalPinOffset]); 640 641 /* sanity check */ 642 ASSERT(SourceLine); 643 644 DPRINT1("PinOffset %lu LogicalPin %lu NodeId %lu LineName %S\n", CurLogicalPinOffset, LogicalNodes[CurLogicalPinOffset], ConnectedNodes[CurLogicalPinOffset], SourceLine->Line.szName); 645 646 /* copy details */ 647 ListText[Index].dwParam1 = SourceLine->Line.dwLineID; 648 ListText[Index].dwParam2 = SourceLine->Line.dwComponentType; 649 MixerContext->Copy(ListText[Index].szName, SourceLine->Line.szName, (wcslen(SourceLine->Line.szName) + 1) * sizeof(WCHAR)); 650 651 /* mark offset as consumed */ 652 LogicalNodes[CurLogicalPinOffset] = MAXULONG; 653 } 654 655 /* cleanup */ 656 MixerContext->Free(LogicalNodes); 657 MixerContext->Free(ConnectedNodes); 658 659 /* done */ 660 return MM_STATUS_SUCCESS; 661 } 662 } 663 664 return MM_STATUS_NOT_IMPLEMENTED; 665 } 666 667 MIXER_STATUS 668 MMixerSetGetVolumeControlDetails( 669 IN PMIXER_CONTEXT MixerContext, 670 IN LPMIXER_INFO MixerInfo, 671 IN ULONG NodeId, 672 IN ULONG bSet, 673 LPMIXERCONTROL_EXT MixerControl, 674 IN LPMIXERCONTROLDETAILS MixerControlDetails, 675 LPMIXERLINE_EXT MixerLine) 676 { 677 LPMIXERCONTROLDETAILS_UNSIGNED Input; 678 LONG Value; 679 ULONG Index, Channel = 0; 680 ULONG dwValue; 681 MIXER_STATUS Status; 682 LPMIXERVOLUME_DATA VolumeData; 683 684 if (MixerControlDetails->cbDetails != sizeof(MIXERCONTROLDETAILS_SIGNED)) 685 return MM_STATUS_INVALID_PARAMETER; 686 687 VolumeData = (LPMIXERVOLUME_DATA)MixerControl->ExtraData; 688 if (!VolumeData) 689 return MM_STATUS_UNSUCCESSFUL; 690 691 /* get input */ 692 Input = (LPMIXERCONTROLDETAILS_UNSIGNED)MixerControlDetails->paDetails; 693 if (!Input) 694 return MM_STATUS_UNSUCCESSFUL; /* to prevent dereferencing NULL */ 695 696 if (bSet) 697 { 698 /* FIXME SEH */ 699 Value = Input->dwValue; 700 Index = Value / VolumeData->InputSteppingDelta; 701 702 if (Index >= VolumeData->ValuesCount) 703 { 704 DPRINT1("Index %u out of bounds %u \n", Index, VolumeData->ValuesCount); 705 return MM_STATUS_INVALID_PARAMETER; 706 } 707 708 Value = VolumeData->Values[Index]; 709 } 710 711 /* set control details */ 712 if (bSet) 713 { 714 /* TODO */ 715 Status = MMixerSetGetControlDetails(MixerContext, MixerControl->hDevice, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, 0, &Value); 716 Status = MMixerSetGetControlDetails(MixerContext, MixerControl->hDevice, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, 1, &Value); 717 } 718 else 719 { 720 Status = MMixerSetGetControlDetails(MixerContext, MixerControl->hDevice, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, Channel, &Value); 721 } 722 723 if (!bSet) 724 { 725 dwValue = MMixerGetVolumeControlIndex(VolumeData, (LONG)Value); 726 /* FIXME SEH */ 727 Input->dwValue = dwValue; 728 } 729 else 730 { 731 /* notify clients of a line change MM_MIXM_CONTROL_CHANGE with MixerControl->dwControlID */ 732 MMixerNotifyControlChange(MixerContext, MixerInfo, MM_MIXM_CONTROL_CHANGE, MixerControl->Control.dwControlID); 733 } 734 return Status; 735 } 736 737 LPMIXER_DATA 738 MMixerGetDataByDeviceId( 739 IN PMIXER_LIST MixerList, 740 IN ULONG DeviceId) 741 { 742 PLIST_ENTRY Entry; 743 LPMIXER_DATA MixerData; 744 745 Entry = MixerList->MixerData.Flink; 746 while(Entry != &MixerList->MixerData) 747 { 748 MixerData = (LPMIXER_DATA)CONTAINING_RECORD(Entry, MIXER_DATA, Entry); 749 if (MixerData->DeviceId == DeviceId) 750 { 751 return MixerData; 752 } 753 Entry = Entry->Flink; 754 } 755 return NULL; 756 } 757 758 LPMIXER_DATA 759 MMixerGetDataByDeviceName( 760 IN PMIXER_LIST MixerList, 761 IN LPWSTR DeviceName) 762 { 763 PLIST_ENTRY Entry; 764 LPMIXER_DATA MixerData; 765 766 Entry = MixerList->MixerData.Flink; 767 while(Entry != &MixerList->MixerData) 768 { 769 MixerData = (LPMIXER_DATA)CONTAINING_RECORD(Entry, MIXER_DATA, Entry); 770 if (wcsicmp(&DeviceName[2], &MixerData->DeviceName[2]) == 0) 771 { 772 /* found entry */ 773 return MixerData; 774 } 775 Entry = Entry->Flink; 776 } 777 return NULL; 778 } 779 780 MIXER_STATUS 781 MMixerCreateMixerData( 782 IN PMIXER_CONTEXT MixerContext, 783 IN PMIXER_LIST MixerList, 784 IN ULONG DeviceId, 785 IN LPWSTR DeviceName, 786 IN HANDLE hDevice, 787 IN HANDLE hKey) 788 { 789 LPMIXER_DATA MixerData; 790 791 MixerData = (LPMIXER_DATA)MixerContext->Alloc(sizeof(MIXER_DATA)); 792 if (!MixerData) 793 return MM_STATUS_NO_MEMORY; 794 795 MixerData->DeviceId = DeviceId; 796 MixerData->DeviceName = DeviceName; 797 MixerData->hDevice = hDevice; 798 MixerData->hDeviceInterfaceKey = hKey; 799 MixerData->Topology = NULL; 800 801 InsertTailList(&MixerList->MixerData, &MixerData->Entry); 802 MixerList->MixerDataCount++; 803 return MM_STATUS_SUCCESS; 804 } 805 806 MIXER_STATUS 807 MMixerGetDeviceNameWithComponentId( 808 IN PMIXER_CONTEXT MixerContext, 809 IN HANDLE hMixer, 810 OUT LPWSTR OutDeviceName) 811 { 812 MIXER_STATUS Status; 813 KSPROPERTY Property; 814 KSCOMPONENTID ComponentId; 815 ULONG Length; 816 UNICODE_STRING GuidString; 817 ULONG ResultLength, KeyType; 818 HANDLE hMediaKey, hGuidKey; 819 LPWSTR DeviceName; 820 821 /* prepare property */ 822 Property.Flags = KSPROPERTY_TYPE_GET; 823 Property.Set = KSPROPSETID_General; 824 Property.Id = KSPROPERTY_GENERAL_COMPONENTID; 825 826 /* try get component id */ 827 Status = MixerContext->Control(hMixer, IOCTL_KS_PROPERTY, (PVOID)&Property, sizeof(KSPROPERTY), &ComponentId, sizeof(KSCOMPONENTID), &Length); 828 829 if (Status == MM_STATUS_SUCCESS) 830 { 831 Status = MixerContext->OpenKey(NULL, L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\MediaCategories", KEY_READ, &hMediaKey); 832 if (Status == MM_STATUS_SUCCESS) 833 { 834 RtlStringFromGUID(&ComponentId.Name, &GuidString); 835 Status = MixerContext->OpenKey(hMediaKey, GuidString.Buffer, KEY_READ, &hGuidKey); 836 RtlFreeUnicodeString(&GuidString); 837 if (Status == MM_STATUS_SUCCESS) 838 { 839 Status = MixerContext->QueryKeyValue(hGuidKey, L"Name", (PVOID*)&DeviceName, &ResultLength, &KeyType); 840 if (Status == MM_STATUS_SUCCESS) 841 { 842 MixerContext->Copy(OutDeviceName, DeviceName, min(ResultLength, (MAXPNAMELEN-1)*2)); 843 MixerContext->Free(DeviceName); 844 } 845 846 MixerContext->CloseKey(hGuidKey); 847 } 848 MixerContext->CloseKey(hMediaKey); 849 } 850 } 851 return Status; 852 } 853 854 MIXER_STATUS 855 MMixerGetDeviceName( 856 IN PMIXER_CONTEXT MixerContext, 857 OUT LPWSTR DeviceName, 858 IN HANDLE hKey) 859 { 860 LPWSTR Name; 861 HANDLE hTemp; 862 ULONG Length; 863 ULONG Type; 864 MIXER_STATUS Status; 865 866 Status = MixerContext->QueryKeyValue(hKey, L"FriendlyName", (PVOID*)&Name, &Length, &Type); 867 if (Status == MM_STATUS_SUCCESS) 868 { 869 /* copy device name */ 870 MixerContext->Copy(DeviceName, Name, min(wcslen(Name), MAXPNAMELEN-1) * sizeof(WCHAR)); 871 872 /* make sure its null terminated */ 873 DeviceName[MAXPNAMELEN-1] = L'\0'; 874 875 /* free device name */ 876 MixerContext->Free(Name); 877 878 /* done */ 879 return Status; 880 } 881 882 Status = MixerContext->OpenKey(hKey, L"Device Parameters", KEY_READ, &hTemp); 883 if (Status != MM_STATUS_SUCCESS) 884 return Status; 885 886 Status = MixerContext->QueryKeyValue(hTemp, L"FriendlyName", (PVOID*)&Name, &Length, &Type); 887 if (Status == MM_STATUS_SUCCESS) 888 { 889 /* copy device name */ 890 MixerContext->Copy(DeviceName, Name, min(wcslen(Name), MAXPNAMELEN-1) * sizeof(WCHAR)); 891 892 /* make sure its null terminated */ 893 DeviceName[MAXPNAMELEN-1] = L'\0'; 894 895 /* free device name */ 896 MixerContext->Free(Name); 897 } 898 899 MixerContext->CloseKey(hTemp); 900 return Status; 901 } 902 903 VOID 904 MMixerInitializePinConnect( 905 IN OUT PKSPIN_CONNECT PinConnect, 906 IN ULONG PinId) 907 { 908 PinConnect->Interface.Set = KSINTERFACESETID_Standard; 909 PinConnect->Interface.Id = KSINTERFACE_STANDARD_STREAMING; 910 PinConnect->Interface.Flags = 0; 911 PinConnect->Medium.Set = KSMEDIUMSETID_Standard; 912 PinConnect->Medium.Id = KSMEDIUM_TYPE_ANYINSTANCE; 913 PinConnect->Medium.Flags = 0; 914 PinConnect->PinToHandle = NULL; 915 PinConnect->PinId = PinId; 916 PinConnect->Priority.PriorityClass = KSPRIORITY_NORMAL; 917 PinConnect->Priority.PrioritySubClass = 1; 918 } 919