xref: /reactos/drivers/wdm/audio/hdaudbus/fdo.cpp (revision 0f92924a)
1 /*
2 * COPYRIGHT:       See COPYING in the top level directory
3 * PROJECT:         ReactOS Kernel Streaming
4 * FILE:            drivers/wdm/audio/hdaudbus/fdo.cpp
5 * PURPOSE:         HDA Driver Entry
6 * PROGRAMMER:      Johannes Anderwald
7 */
8 #include "hdaudbus.h"
9 
10 BOOLEAN
11 NTAPI
12 HDA_InterruptService(
13     IN PKINTERRUPT  Interrupt,
14     IN PVOID  ServiceContext)
15 {
16     PDEVICE_OBJECT DeviceObject;
17     PHDA_FDO_DEVICE_EXTENSION DeviceExtension;
18     ULONG InterruptStatus;
19     UCHAR RirbStatus, CorbStatus;
20 
21     /* get device extension */
22     DeviceObject = static_cast<PDEVICE_OBJECT>(ServiceContext);
23     DeviceExtension = static_cast<PHDA_FDO_DEVICE_EXTENSION>(DeviceObject->DeviceExtension);
24     ASSERT(DeviceExtension->IsFDO == TRUE);
25 
26     // Check if this interrupt is ours
27     InterruptStatus = READ_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_INTR_STATUS));
28 
29     DPRINT1("HDA_InterruptService %lx\n", InterruptStatus);
30     if ((InterruptStatus & INTR_STATUS_GLOBAL) == 0)
31         return FALSE;
32 
33     // Controller or stream related?
34     if (InterruptStatus & INTR_STATUS_CONTROLLER) {
35         RirbStatus = READ_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_RIRB_STATUS);
36         CorbStatus = READ_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_CORB_STATUS);
37 
38         // Check for incoming responses
39         if (RirbStatus) {
40             WRITE_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_RIRB_STATUS, RirbStatus);
41 
42             if (DeviceExtension->RirbLength == 0)
43             {
44                 /* HACK: spurious interrupt */
45                 return FALSE;
46             }
47 
48             if ((RirbStatus & RIRB_STATUS_RESPONSE) != 0) {
49                 IoRequestDpc(DeviceObject, NULL, NULL);
50             }
51 
52             if ((RirbStatus & RIRB_STATUS_OVERRUN) != 0)
53                 DPRINT1("hda: RIRB Overflow\n");
54         }
55 
56         // Check for sending errors
57         if (CorbStatus) {
58             WRITE_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_CORB_STATUS, CorbStatus);
59 
60             if ((CorbStatus & CORB_STATUS_MEMORY_ERROR) != 0)
61                 DPRINT1("hda: CORB Memory Error!\n");
62         }
63     }
64 #if 0
65     if ((intrStatus & INTR_STATUS_STREAM_MASK) != 0) {
66         for (uint32 index = 0; index < HDA_MAX_STREAMS; index++) {
67             if ((intrStatus & (1 << index)) != 0) {
68                 if (controller->streams[index]) {
69                     if (stream_handle_interrupt(controller,
70                         controller->streams[index], index)) {
71                         handled = B_INVOKE_SCHEDULER;
72                     }
73                 }
74                 else {
75                     dprintf("hda: Stream interrupt for unconfigured stream "
76                         "%ld!\n", index);
77                 }
78             }
79         }
80     }
81 #endif
82     return TRUE;
83 }
84 
85 VOID
86 NTAPI
87 HDA_DpcForIsr(
88     _In_ PKDPC Dpc,
89     _In_opt_ PDEVICE_OBJECT DeviceObject,
90     _Inout_ PIRP Irp,
91     _In_opt_ PVOID Context)
92 {
93     PHDA_FDO_DEVICE_EXTENSION DeviceExtension;
94     ULONG Response, ResponseFlags, Cad;
95     USHORT WritePos;
96     PHDA_CODEC_ENTRY Codec;
97 
98     /* get device extension */
99     DeviceExtension = static_cast<PHDA_FDO_DEVICE_EXTENSION>(DeviceObject->DeviceExtension);
100     ASSERT(DeviceExtension->IsFDO == TRUE);
101 
102     WritePos = (READ_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_RIRB_WRITE_POS)) + 1) % DeviceExtension->RirbLength;
103 
104     for (; DeviceExtension->RirbReadPos != WritePos; DeviceExtension->RirbReadPos = (DeviceExtension->RirbReadPos + 1) % DeviceExtension->RirbLength)
105     {
106         Response = DeviceExtension->RirbBase[DeviceExtension->RirbReadPos].response;
107         ResponseFlags = DeviceExtension->RirbBase[DeviceExtension->RirbReadPos].flags;
108         Cad = ResponseFlags & RESPONSE_FLAGS_CODEC_MASK;
109         DPRINT1("Response %lx ResponseFlags %lx Cad %lx\n", Response, ResponseFlags, Cad);
110 
111         /* get codec */
112         Codec = DeviceExtension->Codecs[Cad];
113         if (Codec == NULL)
114         {
115             DPRINT1("hda: response for unknown codec %x Response %x ResponseFlags %x\n", Cad, Response, ResponseFlags);
116             continue;
117         }
118 
119         /* check response count */
120         if (Codec->ResponseCount >= MAX_CODEC_RESPONSES)
121         {
122             DPRINT1("too many responses for codec %x Response %x ResponseFlags %x\n", Cad, Response, ResponseFlags);
123             continue;
124         }
125 
126         // FIXME handle unsolicited responses
127         ASSERT((ResponseFlags & RESPONSE_FLAGS_UNSOLICITED) == 0);
128 
129         /* store response */
130         Codec->Responses[Codec->ResponseCount] = Response;
131         Codec->ResponseCount++;
132         KeReleaseSemaphore(&Codec->ResponseSemaphore, IO_NO_INCREMENT, 1, FALSE);
133     }
134 }
135 
136 
137 VOID
138 HDA_SendVerbs(
139     IN PDEVICE_OBJECT DeviceObject,
140     IN PHDA_CODEC_ENTRY Codec,
141     IN PULONG Verbs,
142     OUT PULONG Responses,
143     IN ULONG Count)
144 {
145     PHDA_FDO_DEVICE_EXTENSION DeviceExtension;
146     ULONG Sent = 0, ReadPosition, WritePosition, Queued;
147 
148     /* get device extension */
149     DeviceExtension = (PHDA_FDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
150     ASSERT(DeviceExtension->IsFDO);
151 
152     /* reset response count */
153     Codec->ResponseCount = 0;
154 
155     while (Sent < Count) {
156         ReadPosition = READ_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_CORB_READ_POS));
157 
158         Queued = 0;
159 
160         while (Sent < Count) {
161             WritePosition = (DeviceExtension->CorbWritePos + 1) % DeviceExtension->CorbLength;
162 
163             if (WritePosition == ReadPosition) {
164                 // There is no space left in the ring buffer; execute the
165                 // queued commands and wait until
166                 break;
167             }
168 
169             DeviceExtension->CorbBase[WritePosition] = Verbs[Sent++];
170             DeviceExtension->CorbWritePos = WritePosition;
171             Queued++;
172         }
173 
174         WRITE_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_CORB_WRITE_POS), DeviceExtension->CorbWritePos);
175     }
176 
177     while (Queued--)
178     {
179         KeWaitForSingleObject(&Codec->ResponseSemaphore,
180                               Executive,
181                               KernelMode,
182                               FALSE,
183                               NULL);
184     }
185 
186     if (Responses != NULL) {
187         memcpy(Responses, Codec->Responses, Codec->ResponseCount * sizeof(ULONG));
188     }
189 }
190 
191 NTSTATUS
192 HDA_InitCodec(
193     IN PDEVICE_OBJECT DeviceObject,
194     IN ULONG codecAddress)
195 {
196     PHDA_CODEC_ENTRY Entry;
197     ULONG verbs[3];
198     PHDA_FDO_DEVICE_EXTENSION DeviceExtension;
199     CODEC_RESPONSE Response;
200     ULONG NodeId, GroupType;
201     NTSTATUS Status;
202     PHDA_CODEC_AUDIO_GROUP AudioGroup;
203     PHDA_PDO_DEVICE_EXTENSION ChildDeviceExtension;
204 
205     /* lets allocate the entry */
206     Entry = (PHDA_CODEC_ENTRY)AllocateItem(NonPagedPool, sizeof(HDA_CODEC_ENTRY));
207     if (!Entry)
208     {
209         DPRINT1("hda: failed to allocate memory");
210         return STATUS_UNSUCCESSFUL;
211     }
212 
213     /* init codec */
214     Entry->Addr = codecAddress;
215     KeInitializeSemaphore(&Entry->ResponseSemaphore, 0, MAX_CODEC_RESPONSES);
216 
217     /* get device extension */
218     DeviceExtension = (PHDA_FDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
219 
220     /* store codec */
221     DeviceExtension->Codecs[codecAddress] = Entry;
222 
223     verbs[0] = MAKE_VERB(codecAddress, 0, VID_GET_PARAMETER, PID_VENDOR_ID);
224     verbs[1] = MAKE_VERB(codecAddress, 0, VID_GET_PARAMETER, PID_REVISION_ID);
225     verbs[2] = MAKE_VERB(codecAddress, 0, VID_GET_PARAMETER, PID_SUB_NODE_COUNT);
226 
227     /* get basic info */
228     HDA_SendVerbs(DeviceObject, Entry, verbs, (PULONG)&Response, 3);
229 
230     /* store codec details */
231     Entry->Major = Response.major;
232     Entry->Minor = Response.minor;
233     Entry->ProductId = Response.device;
234     Entry->Revision = Response.revision;
235     Entry->Stepping = Response.stepping;
236     Entry->VendorId = Response.vendor;
237 
238     DPRINT1("hda Codec %ld Vendor: %04lx Product: %04lx, Revision: %lu.%lu.%lu.%lu NodeStart %u NodeCount %u \n", codecAddress, Response.vendor,
239         Response.device, Response.major, Response.minor, Response.revision, Response.stepping, Response.start, Response.count);
240 
241     for (NodeId = Response.start; NodeId < Response.start + Response.count; NodeId++) {
242 
243         /* get function type */
244         verbs[0] = MAKE_VERB(codecAddress, NodeId, VID_GET_PARAMETER, PID_FUNCTION_GROUP_TYPE);
245 
246         HDA_SendVerbs(DeviceObject, Entry, verbs, &GroupType, 1);
247         DPRINT1("NodeId %u GroupType %x\n", NodeId, GroupType);
248 
249         if ((GroupType & FUNCTION_GROUP_NODETYPE_MASK) == FUNCTION_GROUP_NODETYPE_AUDIO) {
250             if (Entry->AudioGroupCount >= HDA_MAX_AUDIO_GROUPS)
251             {
252                 DPRINT1("Too many audio groups in node %u. Skipping.\n", NodeId);
253                 break;
254             }
255 
256             AudioGroup = (PHDA_CODEC_AUDIO_GROUP)AllocateItem(NonPagedPool, sizeof(HDA_CODEC_AUDIO_GROUP));
257             if (!AudioGroup)
258             {
259                 DPRINT1("hda: insufficient memory\n");
260                 return STATUS_INSUFFICIENT_RESOURCES;
261             }
262 
263             /* init audio group */
264             AudioGroup->NodeId = NodeId;
265             AudioGroup->FunctionGroup = FUNCTION_GROUP_NODETYPE_AUDIO;
266 
267             // Found an Audio Function Group!
268             DPRINT1("NodeId %x found an audio function group!\n", NodeId);
269 
270             Status = IoCreateDevice(DeviceObject->DriverObject, sizeof(HDA_PDO_DEVICE_EXTENSION), NULL, FILE_DEVICE_SOUND, FILE_AUTOGENERATED_DEVICE_NAME, FALSE, &AudioGroup->ChildPDO);
271             if (!NT_SUCCESS(Status))
272             {
273                 FreeItem(AudioGroup);
274                 DPRINT1("hda failed to create device object %x\n", Status);
275                 return Status;
276             }
277 
278             /* init child pdo*/
279             ChildDeviceExtension = (PHDA_PDO_DEVICE_EXTENSION)AudioGroup->ChildPDO->DeviceExtension;
280             ChildDeviceExtension->IsFDO = FALSE;
281             ChildDeviceExtension->ReportedMissing = FALSE;
282             ChildDeviceExtension->Codec = Entry;
283             ChildDeviceExtension->AudioGroup = AudioGroup;
284             ChildDeviceExtension->FDO = DeviceObject;
285 
286             /* setup flags */
287             AudioGroup->ChildPDO->Flags |= DO_POWER_PAGABLE;
288             AudioGroup->ChildPDO->Flags &= ~DO_DEVICE_INITIALIZING;
289 
290             /* add audio group*/
291             Entry->AudioGroups[Entry->AudioGroupCount] = AudioGroup;
292             Entry->AudioGroupCount++;
293         }
294     }
295     return STATUS_SUCCESS;
296 
297 }
298 
299 NTSTATUS
300 NTAPI
301 HDA_InitCorbRirbPos(
302     IN PDEVICE_OBJECT DeviceObject)
303 {
304     PHDA_FDO_DEVICE_EXTENSION DeviceExtension;
305     UCHAR corbSize, value, rirbSize;
306     PHYSICAL_ADDRESS HighestPhysicalAddress, CorbPhysicalAddress;
307     ULONG Index;
308     USHORT corbReadPointer, rirbWritePointer, interruptValue, corbControl, rirbControl;
309 
310     /* get device extension */
311     DeviceExtension = (PHDA_FDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
312 
313     // Determine and set size of CORB
314     corbSize = READ_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_CORB_SIZE);
315     if ((corbSize & CORB_SIZE_CAP_256_ENTRIES) != 0) {
316         DeviceExtension->CorbLength = 256;
317 
318         value = READ_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_CORB_SIZE) & ~HDAC_CORB_SIZE_MASK;
319         WRITE_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_CORB_SIZE, value | CORB_SIZE_256_ENTRIES);
320     }
321     else if (corbSize & CORB_SIZE_CAP_16_ENTRIES) {
322         DeviceExtension->CorbLength = 16;
323 
324         value = READ_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_CORB_SIZE) & ~HDAC_CORB_SIZE_MASK;
325         WRITE_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_CORB_SIZE, value | CORB_SIZE_16_ENTRIES);
326     }
327     else if (corbSize & CORB_SIZE_CAP_2_ENTRIES) {
328         DeviceExtension->CorbLength = 2;
329 
330         value = READ_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_CORB_SIZE) & ~HDAC_CORB_SIZE_MASK;
331         WRITE_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_CORB_SIZE, value | CORB_SIZE_2_ENTRIES);
332     }
333 
334     // Determine and set size of RIRB
335     rirbSize = READ_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_RIRB_SIZE);
336     if (rirbSize & RIRB_SIZE_CAP_256_ENTRIES) {
337         DeviceExtension->RirbLength = 256;
338 
339         value = READ_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_RIRB_SIZE) & ~HDAC_RIRB_SIZE_MASK;
340         WRITE_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_RIRB_SIZE, value | RIRB_SIZE_256_ENTRIES);
341     }
342     else if (rirbSize & RIRB_SIZE_CAP_16_ENTRIES) {
343         DeviceExtension->RirbLength = 16;
344 
345         value = READ_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_RIRB_SIZE) & ~HDAC_RIRB_SIZE_MASK;
346         WRITE_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_RIRB_SIZE, value | RIRB_SIZE_16_ENTRIES);
347     }
348     else if (rirbSize & RIRB_SIZE_CAP_2_ENTRIES) {
349         DeviceExtension->RirbLength = 2;
350 
351         value = READ_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_RIRB_SIZE) & ~HDAC_RIRB_SIZE_MASK;
352         WRITE_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_RIRB_SIZE, value | RIRB_SIZE_2_ENTRIES);
353     }
354 
355     /* init corb */
356     HighestPhysicalAddress.QuadPart = 0x00000000FFFFFFFF;
357     DeviceExtension->CorbBase = (PULONG)MmAllocateContiguousMemory(PAGE_SIZE * 3, HighestPhysicalAddress);
358     ASSERT(DeviceExtension->CorbBase != NULL);
359 
360     // FIXME align rirb 128bytes
361     ASSERT(DeviceExtension->CorbLength == 256);
362     ASSERT(DeviceExtension->RirbLength == 256);
363 
364     CorbPhysicalAddress = MmGetPhysicalAddress(DeviceExtension->CorbBase);
365     ASSERT(CorbPhysicalAddress.QuadPart != 0LL);
366 
367     // Program CORB/RIRB for these locations
368     WRITE_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_CORB_BASE_LOWER), CorbPhysicalAddress.LowPart);
369     WRITE_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_CORB_BASE_UPPER), CorbPhysicalAddress.HighPart);
370 
371     DeviceExtension->RirbBase = (PRIRB_RESPONSE)((ULONG_PTR)DeviceExtension->CorbBase + PAGE_SIZE);
372     CorbPhysicalAddress.QuadPart += PAGE_SIZE;
373     WRITE_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_RIRB_BASE_LOWER), CorbPhysicalAddress.LowPart);
374     WRITE_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_RIRB_BASE_UPPER), CorbPhysicalAddress.HighPart);
375 
376     // Program DMA position update
377     DeviceExtension->StreamPositions = (PVOID)((ULONG_PTR)DeviceExtension->RirbBase + PAGE_SIZE);
378     CorbPhysicalAddress.QuadPart += PAGE_SIZE;
379     WRITE_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_DMA_POSITION_BASE_LOWER), CorbPhysicalAddress.LowPart);
380     WRITE_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_DMA_POSITION_BASE_UPPER), CorbPhysicalAddress.HighPart);
381 
382     value = READ_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_CORB_WRITE_POS)) & ~HDAC_CORB_WRITE_POS_MASK;
383     WRITE_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_CORB_WRITE_POS), value);
384 
385     // Reset CORB read pointer. Preserve bits marked as RsvdP.
386     // After setting the reset bit, we must wait for the hardware
387     // to acknowledge it, then manually unset it and wait for that
388     // to be acknowledged as well.
389     corbReadPointer = READ_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_CORB_READ_POS));
390 
391     corbReadPointer |= CORB_READ_POS_RESET;
392     WRITE_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_CORB_READ_POS), corbReadPointer);
393 
394     for (Index = 0; Index < 10; Index++) {
395         KeStallExecutionProcessor(100);
396         corbReadPointer = READ_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_CORB_READ_POS));
397         if ((corbReadPointer & CORB_READ_POS_RESET) != 0)
398             break;
399     }
400     if ((corbReadPointer & CORB_READ_POS_RESET) == 0) {
401         DPRINT1("hda: CORB read pointer reset not acknowledged\n");
402 
403         // According to HDA spec v1.0a ch3.3.21, software must read the
404         // bit as 1 to verify that the reset completed. However, at least
405         // some nVidia HDA controllers do not update the bit after reset.
406         // Thus don't fail here on nVidia controllers.
407         //if (controller->pci_info.vendor_id != PCI_VENDOR_NVIDIA)
408         //	return B_BUSY;
409     }
410 
411     corbReadPointer &= ~CORB_READ_POS_RESET;
412     WRITE_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_CORB_READ_POS), corbReadPointer);
413     for (Index = 0; Index < 10; Index++) {
414         KeStallExecutionProcessor(100);
415         corbReadPointer = READ_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_CORB_READ_POS));
416         if ((corbReadPointer & CORB_READ_POS_RESET) == 0)
417             break;
418     }
419     if ((corbReadPointer & CORB_READ_POS_RESET) != 0) {
420         DPRINT1("hda: CORB read pointer reset failed\n");
421         return STATUS_UNSUCCESSFUL;
422     }
423 
424     // Reset RIRB write pointer
425     rirbWritePointer = READ_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_RIRB_WRITE_POS)) & ~RIRB_WRITE_POS_RESET;
426     WRITE_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_RIRB_WRITE_POS), rirbWritePointer | RIRB_WRITE_POS_RESET);
427 
428     // Generate interrupt for every response
429     interruptValue = READ_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_RESPONSE_INTR_COUNT)) & ~HDAC_RESPONSE_INTR_COUNT_MASK;
430     WRITE_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_RESPONSE_INTR_COUNT), interruptValue | 1);
431 
432     // Setup cached read/write indices
433     DeviceExtension->RirbReadPos = 1;
434     DeviceExtension->CorbWritePos = 0;
435 
436     // Gentlemen, start your engines...
437     corbControl = READ_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_CORB_CONTROL)) & ~HDAC_CORB_CONTROL_MASK;
438     WRITE_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_CORB_CONTROL), corbControl | CORB_CONTROL_RUN | CORB_CONTROL_MEMORY_ERROR_INTR);
439 
440     rirbControl = READ_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_RIRB_CONTROL)) & ~HDAC_RIRB_CONTROL_MASK;
441     WRITE_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_RIRB_CONTROL), rirbControl | RIRB_CONTROL_DMA_ENABLE | RIRB_CONTROL_OVERRUN_INTR | RIRB_CONTROL_RESPONSE_INTR);
442 
443     return STATUS_SUCCESS;
444 }
445 
446 NTSTATUS
447 NTAPI
448 HDA_ResetController(
449     IN PDEVICE_OBJECT DeviceObject)
450 {
451     USHORT ValCapabilities;
452     ULONG Index;
453     PHDA_FDO_DEVICE_EXTENSION DeviceExtension;
454     ULONG InputStreams, OutputStreams, BiDirStreams, Control;
455     UCHAR corbControl, rirbControl;
456 
457     /* get device extension */
458     DeviceExtension = (PHDA_FDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
459 
460     /* read caps */
461     ValCapabilities = READ_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_GLOBAL_CAP));
462 
463     InputStreams = GLOBAL_CAP_INPUT_STREAMS(ValCapabilities);
464     OutputStreams = GLOBAL_CAP_OUTPUT_STREAMS(ValCapabilities);
465     BiDirStreams = GLOBAL_CAP_BIDIR_STREAMS(ValCapabilities);
466 
467     DPRINT1("NumInputStreams %u\n", InputStreams);
468     DPRINT1("NumOutputStreams %u\n", OutputStreams);
469     DPRINT1("NumBiDirStreams %u\n", BiDirStreams);
470 
471     /* stop all streams */
472     for (Index = 0; Index < InputStreams; Index++)
473     {
474         WRITE_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_STREAM_CONTROL0 + HDAC_STREAM_BASE + HDAC_INPUT_STREAM_OFFSET(Index), 0);
475         WRITE_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_STREAM_STATUS + HDAC_STREAM_BASE + HDAC_INPUT_STREAM_OFFSET(Index), 0);
476     }
477 
478     for (Index = 0; Index < OutputStreams; Index++) {
479         WRITE_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_STREAM_CONTROL0 + HDAC_STREAM_BASE + HDAC_OUTPUT_STREAM_OFFSET(InputStreams, Index), 0);
480         WRITE_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_STREAM_STATUS + HDAC_STREAM_BASE + HDAC_OUTPUT_STREAM_OFFSET(InputStreams, Index), 0);
481     }
482 
483     for (Index = 0; Index < BiDirStreams; Index++) {
484         WRITE_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_STREAM_CONTROL0 + HDAC_STREAM_BASE + HDAC_BIDIR_STREAM_OFFSET(InputStreams, OutputStreams, Index), 0);
485         WRITE_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_STREAM_STATUS + HDAC_STREAM_BASE + HDAC_BIDIR_STREAM_OFFSET(InputStreams, OutputStreams, Index), 0);
486     }
487 
488     // stop DMA
489     Control = READ_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_CORB_CONTROL) & ~HDAC_CORB_CONTROL_MASK;
490     WRITE_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_CORB_CONTROL, Control);
491 
492     Control = READ_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_RIRB_CONTROL) & ~HDAC_RIRB_CONTROL_MASK;
493     WRITE_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_RIRB_CONTROL, Control);
494 
495     for (int timeout = 0; timeout < 10; timeout++) {
496         KeStallExecutionProcessor(100);
497 
498         corbControl = READ_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_CORB_CONTROL);
499         rirbControl = READ_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_RIRB_CONTROL);
500         if (corbControl == 0 && rirbControl == 0)
501             break;
502     }
503     if (corbControl != 0 || rirbControl != 0) {
504         DPRINT1("hda: unable to stop dma\n");
505         return STATUS_UNSUCCESSFUL;
506     }
507 
508     // reset DMA position buffer
509     WRITE_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_DMA_POSITION_BASE_LOWER), 0);
510     WRITE_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_DMA_POSITION_BASE_UPPER), 0);
511 
512     // Set reset bit - it must be asserted for at least 100us
513     Control = READ_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_GLOBAL_CONTROL));
514     WRITE_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_GLOBAL_CONTROL), Control & ~GLOBAL_CONTROL_RESET);
515 
516     for (int timeout = 0; timeout < 10; timeout++) {
517         KeStallExecutionProcessor(100);
518 
519         Control = READ_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_GLOBAL_CONTROL));
520         if ((Control & GLOBAL_CONTROL_RESET) == 0)
521             break;
522     }
523     if ((Control & GLOBAL_CONTROL_RESET) != 0)
524     {
525         DPRINT1("hda: unable to reset controller\n");
526         return STATUS_UNSUCCESSFUL;
527     }
528 
529     // Unset reset bit
530     Control = READ_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_GLOBAL_CONTROL));
531     WRITE_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_GLOBAL_CONTROL), Control | GLOBAL_CONTROL_RESET);
532 
533     for (int timeout = 0; timeout < 10; timeout++) {
534         KeStallExecutionProcessor(100);
535 
536         Control = READ_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_GLOBAL_CONTROL));
537         if ((Control & GLOBAL_CONTROL_RESET) != 0)
538             break;
539     }
540     if ((Control & GLOBAL_CONTROL_RESET) == 0) {
541         DPRINT1("hda: unable to exit reset\n");
542         return STATUS_UNSUCCESSFUL;
543     }
544 
545     // Wait for codecs to finish their own reset (apparently needs more
546     // time than documented in the specs)
547     KeStallExecutionProcessor(1000);
548 
549     // Enable unsolicited responses
550     Control = READ_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_GLOBAL_CONTROL));
551     WRITE_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_GLOBAL_CONTROL), Control | GLOBAL_CONTROL_UNSOLICITED);
552 
553     return STATUS_SUCCESS;
554 }
555 
556 NTSTATUS
557 NTAPI
558 HDA_FDOStartDevice(
559     IN PDEVICE_OBJECT DeviceObject,
560     IN PIRP Irp)
561 {
562     PIO_STACK_LOCATION IoStack;
563     NTSTATUS Status = STATUS_SUCCESS;
564     PHDA_FDO_DEVICE_EXTENSION DeviceExtension;
565     PCM_RESOURCE_LIST Resources;
566     ULONG Index;
567     USHORT Value;
568 
569     /* get device extension */
570     DeviceExtension = (PHDA_FDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
571     ASSERT(DeviceExtension->IsFDO == TRUE);
572 
573     /* forward irp to lower device */
574     if (!IoForwardIrpSynchronously(DeviceExtension->LowerDevice, Irp))
575     {
576         ASSERT(FALSE);
577         return STATUS_INVALID_DEVICE_REQUEST;
578     }
579     Status = Irp->IoStatus.Status;
580     if (!NT_SUCCESS(Status))
581     {
582         // failed to start
583         DPRINT1("HDA_StartDevice Lower device failed to start %x\n", Status);
584         return Status;
585     }
586 
587     /* get current irp stack location */
588     IoStack = IoGetCurrentIrpStackLocation(Irp);
589 
590     Resources = IoStack->Parameters.StartDevice.AllocatedResourcesTranslated;
591     for (Index = 0; Index < Resources->List[0].PartialResourceList.Count; Index++)
592     {
593         PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor = &Resources->List[0].PartialResourceList.PartialDescriptors[Index];
594 
595         if (Descriptor->Type == CmResourceTypeMemory)
596         {
597             DeviceExtension->RegLength = Descriptor->u.Memory.Length;
598             DeviceExtension->RegBase = (PUCHAR)MmMapIoSpace(Descriptor->u.Memory.Start, Descriptor->u.Memory.Length, MmNonCached);
599             if (DeviceExtension->RegBase == NULL)
600             {
601                 DPRINT1("[HDAB] Failed to map registers\n");
602                 Status = STATUS_UNSUCCESSFUL;
603                 break;
604             }
605         }
606         else if (Descriptor->Type == CmResourceTypeInterrupt)
607         {
608             Status = IoConnectInterrupt(&DeviceExtension->Interrupt,
609                 HDA_InterruptService,
610                 DeviceObject,
611                 NULL,
612                 Descriptor->u.Interrupt.Vector,
613                 Descriptor->u.Interrupt.Level,
614                 Descriptor->u.Interrupt.Level,
615                 (KINTERRUPT_MODE)(Descriptor->Flags & CM_RESOURCE_INTERRUPT_LATCHED),
616                 (Descriptor->ShareDisposition != CmResourceShareDeviceExclusive),
617                 Descriptor->u.Interrupt.Affinity,
618                 FALSE);
619             if (!NT_SUCCESS(Status))
620             {
621                 DPRINT1("[HDAB] Failed to connect interrupt. Status=%lx\n", Status);
622                 break;
623             }
624 
625         }
626     }
627 
628     if (NT_SUCCESS(Status))
629     {
630         // Get controller into valid state
631         Status = HDA_ResetController(DeviceObject);
632         if (!NT_SUCCESS(Status)) return Status;
633 
634         // Setup CORB/RIRB/DMA POS
635         Status = HDA_InitCorbRirbPos(DeviceObject);
636         if (!NT_SUCCESS(Status)) return Status;
637 
638 
639         // Don't enable codec state change interrupts. We don't handle
640         // them, as we want to use the STATE_STATUS register to identify
641         // available codecs. We'd have to clear that register in the interrupt
642         // handler to 'ack' the codec change.
643         Value = READ_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_WAKE_ENABLE)) & ~HDAC_WAKE_ENABLE_MASK;
644         WRITE_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_WAKE_ENABLE), Value);
645 
646         // Enable controller interrupts
647         WRITE_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_INTR_CONTROL), INTR_CONTROL_GLOBAL_ENABLE | INTR_CONTROL_CONTROLLER_ENABLE);
648 
649         KeStallExecutionProcessor(1000);
650 
651         Value = READ_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_STATE_STATUS));
652         if (!Value) {
653             DPRINT1("hda: bad codec status\n");
654             return STATUS_UNSUCCESSFUL;
655         }
656         WRITE_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_STATE_STATUS), Value);
657 
658         // Create codecs
659         DPRINT1("Codecs %lx\n", Value);
660         for (Index = 0; Index < HDA_MAX_CODECS; Index++) {
661             if ((Value & (1 << Index)) != 0) {
662                 HDA_InitCodec(DeviceObject, Index);
663             }
664         }
665     }
666 
667     return Status;
668 }
669 
670 NTSTATUS
671 NTAPI
672 HDA_FDORemoveDevice(
673     _In_ PDEVICE_OBJECT DeviceObject,
674     _Inout_ PIRP Irp)
675 {
676     NTSTATUS Status;
677     PHDA_FDO_DEVICE_EXTENSION DeviceExtension;
678     ULONG CodecIndex, AFGIndex;
679     PHDA_CODEC_ENTRY CodecEntry;
680     PDEVICE_OBJECT ChildPDO;
681     PHDA_PDO_DEVICE_EXTENSION ChildDeviceExtension;
682 
683     /* get device extension */
684     DeviceExtension = static_cast<PHDA_FDO_DEVICE_EXTENSION>(DeviceObject->DeviceExtension);
685     ASSERT(DeviceExtension->IsFDO == TRUE);
686 
687     Irp->IoStatus.Status = STATUS_SUCCESS;
688     IoSkipCurrentIrpStackLocation(Irp);
689     Status = IoCallDriver(DeviceExtension->LowerDevice, Irp);
690 
691     IoDetachDevice(DeviceExtension->LowerDevice);
692 
693     if (DeviceExtension->RegBase != NULL)
694     {
695         MmUnmapIoSpace(DeviceExtension->RegBase,
696                        DeviceExtension->RegLength);
697     }
698     if (DeviceExtension->Interrupt != NULL)
699     {
700         IoDisconnectInterrupt(DeviceExtension->Interrupt);
701     }
702     if (DeviceExtension->CorbBase != NULL)
703     {
704         MmFreeContiguousMemory(DeviceExtension->CorbBase);
705     }
706 
707     for (CodecIndex = 0; CodecIndex < HDA_MAX_CODECS; CodecIndex++)
708     {
709         CodecEntry = DeviceExtension->Codecs[CodecIndex];
710         if (CodecEntry == NULL)
711         {
712             continue;
713         }
714 
715         ASSERT(CodecEntry->AudioGroupCount <= HDA_MAX_AUDIO_GROUPS);
716         for (AFGIndex = 0; AFGIndex < CodecEntry->AudioGroupCount; AFGIndex++)
717         {
718             ChildPDO = CodecEntry->AudioGroups[AFGIndex]->ChildPDO;
719             if (ChildPDO != NULL)
720             {
721                 ChildDeviceExtension = static_cast<PHDA_PDO_DEVICE_EXTENSION>(ChildPDO->DeviceExtension);
722                 ChildDeviceExtension->Codec = NULL;
723                 ChildDeviceExtension->AudioGroup = NULL;
724                 ChildDeviceExtension->FDO = NULL;
725                 ChildDeviceExtension->ReportedMissing = TRUE;
726                 HDA_PDORemoveDevice(ChildPDO);
727             }
728             FreeItem(CodecEntry->AudioGroups[AFGIndex]);
729         }
730         FreeItem(CodecEntry);
731     }
732 
733     IoDeleteDevice(DeviceObject);
734 
735     return Status;
736 }
737 
738 NTSTATUS
739 NTAPI
740 HDA_FDOQueryBusRelations(
741     IN PDEVICE_OBJECT DeviceObject,
742     IN PIRP Irp)
743 {
744     ULONG DeviceCount, CodecIndex, AFGIndex;
745     PHDA_FDO_DEVICE_EXTENSION DeviceExtension;
746     PHDA_CODEC_ENTRY Codec;
747     PDEVICE_RELATIONS DeviceRelations;
748 
749     /* get device extension */
750     DeviceExtension = (PHDA_FDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
751     ASSERT(DeviceExtension->IsFDO == TRUE);
752 
753     DeviceCount = 0;
754     for (CodecIndex = 0; CodecIndex < HDA_MAX_CODECS; CodecIndex++)
755     {
756         if (DeviceExtension->Codecs[CodecIndex] == NULL)
757             continue;
758 
759         Codec = DeviceExtension->Codecs[CodecIndex];
760         DeviceCount += Codec->AudioGroupCount;
761     }
762 
763     if (DeviceCount == 0)
764         return STATUS_UNSUCCESSFUL;
765 
766     DeviceRelations = (PDEVICE_RELATIONS)AllocateItem(NonPagedPool, sizeof(DEVICE_RELATIONS) + (DeviceCount > 1 ? sizeof(PDEVICE_OBJECT) * (DeviceCount - 1) : 0));
767     if (!DeviceRelations)
768         return STATUS_INSUFFICIENT_RESOURCES;
769 
770     DeviceRelations->Count = 0;
771     for (CodecIndex = 0; CodecIndex < HDA_MAX_CODECS; CodecIndex++)
772     {
773         if (DeviceExtension->Codecs[CodecIndex] == NULL)
774             continue;
775 
776         Codec = DeviceExtension->Codecs[CodecIndex];
777         ASSERT(Codec->AudioGroupCount <= HDA_MAX_AUDIO_GROUPS);
778         for (AFGIndex = 0; AFGIndex < Codec->AudioGroupCount; AFGIndex++)
779         {
780             DeviceRelations->Objects[DeviceRelations->Count] = Codec->AudioGroups[AFGIndex]->ChildPDO;
781             ObReferenceObject(Codec->AudioGroups[AFGIndex]->ChildPDO);
782             DeviceRelations->Count++;
783         }
784     }
785 
786     /* FIXME handle existing device relations */
787     ASSERT(Irp->IoStatus.Information == 0);
788 
789     /* store device relations */
790     Irp->IoStatus.Information = (ULONG_PTR)DeviceRelations;
791 
792     /* done */
793     return STATUS_SUCCESS;
794 }
795 
796 
797