xref: /reactos/drivers/wdm/audio/hdaudbus/fdo.cpp (revision 111e4003)
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 NTSTATUS
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         LARGE_INTEGER Timeout;
180         Timeout.QuadPart = -1000LL * 10000; // 1 sec
181 
182         NTSTATUS waitStatus = KeWaitForSingleObject(&Codec->ResponseSemaphore,
183                                                     Executive,
184                                                     KernelMode,
185                                                     FALSE,
186                                                     &Timeout);
187 
188         if (waitStatus == STATUS_TIMEOUT)
189         {
190             DPRINT1("HDA_SendVerbs: timeout! Queued: %u\n", Queued);
191             return STATUS_INVALID_DEVICE_REQUEST;
192         }
193     }
194 
195     if (Responses != NULL) {
196         memcpy(Responses, Codec->Responses, Codec->ResponseCount * sizeof(ULONG));
197     }
198 
199     return STATUS_SUCCESS;
200 }
201 
202 NTSTATUS
203 HDA_InitCodec(
204     IN PDEVICE_OBJECT DeviceObject,
205     IN ULONG codecAddress)
206 {
207     PHDA_CODEC_ENTRY Entry;
208     ULONG verbs[3];
209     PHDA_FDO_DEVICE_EXTENSION DeviceExtension;
210     CODEC_RESPONSE Response;
211     ULONG NodeId, GroupType;
212     NTSTATUS Status;
213     PHDA_CODEC_AUDIO_GROUP AudioGroup;
214     PHDA_PDO_DEVICE_EXTENSION ChildDeviceExtension;
215 
216     /* lets allocate the entry */
217     Entry = (PHDA_CODEC_ENTRY)AllocateItem(NonPagedPool, sizeof(HDA_CODEC_ENTRY));
218     if (!Entry)
219     {
220         DPRINT1("hda: failed to allocate memory");
221         return STATUS_UNSUCCESSFUL;
222     }
223 
224     /* init codec */
225     Entry->Addr = codecAddress;
226     KeInitializeSemaphore(&Entry->ResponseSemaphore, 0, MAX_CODEC_RESPONSES);
227 
228     /* get device extension */
229     DeviceExtension = (PHDA_FDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
230 
231     /* store codec */
232     DeviceExtension->Codecs[codecAddress] = Entry;
233 
234     verbs[0] = MAKE_VERB(codecAddress, 0, VID_GET_PARAMETER, PID_VENDOR_ID);
235     verbs[1] = MAKE_VERB(codecAddress, 0, VID_GET_PARAMETER, PID_REVISION_ID);
236     verbs[2] = MAKE_VERB(codecAddress, 0, VID_GET_PARAMETER, PID_SUB_NODE_COUNT);
237 
238     /* get basic info */
239     Status = HDA_SendVerbs(DeviceObject, Entry, verbs, (PULONG)&Response, 3);
240     if (!NT_SUCCESS(Status))
241     {
242         FreeItem(Entry);
243         DeviceExtension->Codecs[codecAddress] = NULL;
244         return Status;
245     }
246 
247     /* store codec details */
248     Entry->Major = Response.major;
249     Entry->Minor = Response.minor;
250     Entry->ProductId = Response.device;
251     Entry->Revision = Response.revision;
252     Entry->Stepping = Response.stepping;
253     Entry->VendorId = Response.vendor;
254 
255     DPRINT1("hda Codec %ld Vendor: %04lx Product: %04lx, Revision: %lu.%lu.%lu.%lu NodeStart %u NodeCount %u \n", codecAddress, Response.vendor,
256         Response.device, Response.major, Response.minor, Response.revision, Response.stepping, Response.start, Response.count);
257 
258     for (NodeId = Response.start; NodeId < Response.start + Response.count; NodeId++) {
259 
260         /* get function type */
261         verbs[0] = MAKE_VERB(codecAddress, NodeId, VID_GET_PARAMETER, PID_FUNCTION_GROUP_TYPE);
262 
263         Status = HDA_SendVerbs(DeviceObject, Entry, verbs, &GroupType, 1);
264         DPRINT1("Status %x NodeId %u GroupType %x\n", Status, NodeId, GroupType);
265 
266 
267         if (NT_SUCCESS(Status) &&
268             (GroupType & FUNCTION_GROUP_NODETYPE_MASK) == FUNCTION_GROUP_NODETYPE_AUDIO)
269         {
270             if (Entry->AudioGroupCount >= HDA_MAX_AUDIO_GROUPS)
271             {
272                 DPRINT1("Too many audio groups in node %u. Skipping.\n", NodeId);
273                 break;
274             }
275 
276             AudioGroup = (PHDA_CODEC_AUDIO_GROUP)AllocateItem(NonPagedPool, sizeof(HDA_CODEC_AUDIO_GROUP));
277             if (!AudioGroup)
278             {
279                 DPRINT1("hda: insufficient memory\n");
280                 return STATUS_INSUFFICIENT_RESOURCES;
281             }
282 
283             /* init audio group */
284             AudioGroup->NodeId = NodeId;
285             AudioGroup->FunctionGroup = FUNCTION_GROUP_NODETYPE_AUDIO;
286 
287             // Found an Audio Function Group!
288             DPRINT1("NodeId %x found an audio function group!\n", NodeId);
289 
290             Status = IoCreateDevice(DeviceObject->DriverObject, sizeof(HDA_PDO_DEVICE_EXTENSION), NULL, FILE_DEVICE_SOUND, FILE_AUTOGENERATED_DEVICE_NAME, FALSE, &AudioGroup->ChildPDO);
291             if (!NT_SUCCESS(Status))
292             {
293                 FreeItem(AudioGroup);
294                 DPRINT1("hda failed to create device object %x\n", Status);
295                 return Status;
296             }
297 
298             /* init child pdo*/
299             ChildDeviceExtension = (PHDA_PDO_DEVICE_EXTENSION)AudioGroup->ChildPDO->DeviceExtension;
300             ChildDeviceExtension->IsFDO = FALSE;
301             ChildDeviceExtension->ReportedMissing = FALSE;
302             ChildDeviceExtension->Codec = Entry;
303             ChildDeviceExtension->AudioGroup = AudioGroup;
304             ChildDeviceExtension->FDO = DeviceObject;
305 
306             /* setup flags */
307             AudioGroup->ChildPDO->Flags |= DO_POWER_PAGABLE;
308             AudioGroup->ChildPDO->Flags &= ~DO_DEVICE_INITIALIZING;
309 
310             /* add audio group*/
311             Entry->AudioGroups[Entry->AudioGroupCount] = AudioGroup;
312             Entry->AudioGroupCount++;
313         }
314     }
315     return STATUS_SUCCESS;
316 
317 }
318 
319 NTSTATUS
320 NTAPI
321 HDA_InitCorbRirbPos(
322     IN PDEVICE_OBJECT DeviceObject)
323 {
324     PHDA_FDO_DEVICE_EXTENSION DeviceExtension;
325     UCHAR corbSize, value, rirbSize;
326     PHYSICAL_ADDRESS HighestPhysicalAddress, CorbPhysicalAddress;
327     ULONG Index;
328     USHORT corbReadPointer, rirbWritePointer, interruptValue, corbControl, rirbControl;
329 
330     /* get device extension */
331     DeviceExtension = (PHDA_FDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
332 
333     // Determine and set size of CORB
334     corbSize = READ_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_CORB_SIZE);
335     if ((corbSize & CORB_SIZE_CAP_256_ENTRIES) != 0) {
336         DeviceExtension->CorbLength = 256;
337 
338         value = READ_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_CORB_SIZE) & ~HDAC_CORB_SIZE_MASK;
339         WRITE_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_CORB_SIZE, value | CORB_SIZE_256_ENTRIES);
340     }
341     else if (corbSize & CORB_SIZE_CAP_16_ENTRIES) {
342         DeviceExtension->CorbLength = 16;
343 
344         value = READ_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_CORB_SIZE) & ~HDAC_CORB_SIZE_MASK;
345         WRITE_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_CORB_SIZE, value | CORB_SIZE_16_ENTRIES);
346     }
347     else if (corbSize & CORB_SIZE_CAP_2_ENTRIES) {
348         DeviceExtension->CorbLength = 2;
349 
350         value = READ_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_CORB_SIZE) & ~HDAC_CORB_SIZE_MASK;
351         WRITE_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_CORB_SIZE, value | CORB_SIZE_2_ENTRIES);
352     }
353 
354     // Determine and set size of RIRB
355     rirbSize = READ_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_RIRB_SIZE);
356     if (rirbSize & RIRB_SIZE_CAP_256_ENTRIES) {
357         DeviceExtension->RirbLength = 256;
358 
359         value = READ_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_RIRB_SIZE) & ~HDAC_RIRB_SIZE_MASK;
360         WRITE_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_RIRB_SIZE, value | RIRB_SIZE_256_ENTRIES);
361     }
362     else if (rirbSize & RIRB_SIZE_CAP_16_ENTRIES) {
363         DeviceExtension->RirbLength = 16;
364 
365         value = READ_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_RIRB_SIZE) & ~HDAC_RIRB_SIZE_MASK;
366         WRITE_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_RIRB_SIZE, value | RIRB_SIZE_16_ENTRIES);
367     }
368     else if (rirbSize & RIRB_SIZE_CAP_2_ENTRIES) {
369         DeviceExtension->RirbLength = 2;
370 
371         value = READ_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_RIRB_SIZE) & ~HDAC_RIRB_SIZE_MASK;
372         WRITE_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_RIRB_SIZE, value | RIRB_SIZE_2_ENTRIES);
373     }
374 
375     /* init corb */
376     HighestPhysicalAddress.QuadPart = 0x00000000FFFFFFFF;
377     DeviceExtension->CorbBase = (PULONG)MmAllocateContiguousMemory(PAGE_SIZE * 3, HighestPhysicalAddress);
378     ASSERT(DeviceExtension->CorbBase != NULL);
379 
380     // FIXME align rirb 128bytes
381     ASSERT(DeviceExtension->CorbLength == 256);
382     ASSERT(DeviceExtension->RirbLength == 256);
383 
384     CorbPhysicalAddress = MmGetPhysicalAddress(DeviceExtension->CorbBase);
385     ASSERT(CorbPhysicalAddress.QuadPart != 0LL);
386 
387     // Program CORB/RIRB for these locations
388     WRITE_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_CORB_BASE_LOWER), CorbPhysicalAddress.LowPart);
389     WRITE_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_CORB_BASE_UPPER), CorbPhysicalAddress.HighPart);
390 
391     DeviceExtension->RirbBase = (PRIRB_RESPONSE)((ULONG_PTR)DeviceExtension->CorbBase + PAGE_SIZE);
392     CorbPhysicalAddress.QuadPart += PAGE_SIZE;
393     WRITE_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_RIRB_BASE_LOWER), CorbPhysicalAddress.LowPart);
394     WRITE_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_RIRB_BASE_UPPER), CorbPhysicalAddress.HighPart);
395 
396     // Program DMA position update
397     DeviceExtension->StreamPositions = (PVOID)((ULONG_PTR)DeviceExtension->RirbBase + PAGE_SIZE);
398     CorbPhysicalAddress.QuadPart += PAGE_SIZE;
399     WRITE_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_DMA_POSITION_BASE_LOWER), CorbPhysicalAddress.LowPart);
400     WRITE_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_DMA_POSITION_BASE_UPPER), CorbPhysicalAddress.HighPart);
401 
402     value = READ_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_CORB_WRITE_POS)) & ~HDAC_CORB_WRITE_POS_MASK;
403     WRITE_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_CORB_WRITE_POS), value);
404 
405     // Reset CORB read pointer. Preserve bits marked as RsvdP.
406     // After setting the reset bit, we must wait for the hardware
407     // to acknowledge it, then manually unset it and wait for that
408     // to be acknowledged as well.
409     corbReadPointer = READ_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_CORB_READ_POS));
410 
411     corbReadPointer |= CORB_READ_POS_RESET;
412     WRITE_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_CORB_READ_POS), corbReadPointer);
413 
414     for (Index = 0; Index < 10; Index++) {
415         KeStallExecutionProcessor(100);
416         corbReadPointer = READ_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_CORB_READ_POS));
417         if ((corbReadPointer & CORB_READ_POS_RESET) != 0)
418             break;
419     }
420     if ((corbReadPointer & CORB_READ_POS_RESET) == 0) {
421         DPRINT1("hda: CORB read pointer reset not acknowledged\n");
422 
423         // According to HDA spec v1.0a ch3.3.21, software must read the
424         // bit as 1 to verify that the reset completed. However, at least
425         // some nVidia HDA controllers do not update the bit after reset.
426         // Thus don't fail here on nVidia controllers.
427         //if (controller->pci_info.vendor_id != PCI_VENDOR_NVIDIA)
428         //	return B_BUSY;
429     }
430 
431     corbReadPointer &= ~CORB_READ_POS_RESET;
432     WRITE_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_CORB_READ_POS), corbReadPointer);
433     for (Index = 0; Index < 10; Index++) {
434         KeStallExecutionProcessor(100);
435         corbReadPointer = READ_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_CORB_READ_POS));
436         if ((corbReadPointer & CORB_READ_POS_RESET) == 0)
437             break;
438     }
439     if ((corbReadPointer & CORB_READ_POS_RESET) != 0) {
440         DPRINT1("hda: CORB read pointer reset failed\n");
441         return STATUS_UNSUCCESSFUL;
442     }
443 
444     // Reset RIRB write pointer
445     rirbWritePointer = READ_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_RIRB_WRITE_POS)) & ~RIRB_WRITE_POS_RESET;
446     WRITE_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_RIRB_WRITE_POS), rirbWritePointer | RIRB_WRITE_POS_RESET);
447 
448     // Generate interrupt for every response
449     interruptValue = READ_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_RESPONSE_INTR_COUNT)) & ~HDAC_RESPONSE_INTR_COUNT_MASK;
450     WRITE_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_RESPONSE_INTR_COUNT), interruptValue | 1);
451 
452     // Setup cached read/write indices
453     DeviceExtension->RirbReadPos = 1;
454     DeviceExtension->CorbWritePos = 0;
455 
456     // Gentlemen, start your engines...
457     corbControl = READ_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_CORB_CONTROL)) & ~HDAC_CORB_CONTROL_MASK;
458     WRITE_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_CORB_CONTROL), corbControl | CORB_CONTROL_RUN | CORB_CONTROL_MEMORY_ERROR_INTR);
459 
460     rirbControl = READ_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_RIRB_CONTROL)) & ~HDAC_RIRB_CONTROL_MASK;
461     WRITE_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_RIRB_CONTROL), rirbControl | RIRB_CONTROL_DMA_ENABLE | RIRB_CONTROL_OVERRUN_INTR | RIRB_CONTROL_RESPONSE_INTR);
462 
463     return STATUS_SUCCESS;
464 }
465 
466 NTSTATUS
467 NTAPI
468 HDA_ResetController(
469     IN PDEVICE_OBJECT DeviceObject)
470 {
471     USHORT ValCapabilities;
472     ULONG Index;
473     PHDA_FDO_DEVICE_EXTENSION DeviceExtension;
474     ULONG InputStreams, OutputStreams, BiDirStreams, Control;
475     UCHAR corbControl, rirbControl;
476 
477     /* get device extension */
478     DeviceExtension = (PHDA_FDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
479 
480     /* read caps */
481     ValCapabilities = READ_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_GLOBAL_CAP));
482 
483     InputStreams = GLOBAL_CAP_INPUT_STREAMS(ValCapabilities);
484     OutputStreams = GLOBAL_CAP_OUTPUT_STREAMS(ValCapabilities);
485     BiDirStreams = GLOBAL_CAP_BIDIR_STREAMS(ValCapabilities);
486 
487     DPRINT1("NumInputStreams %u\n", InputStreams);
488     DPRINT1("NumOutputStreams %u\n", OutputStreams);
489     DPRINT1("NumBiDirStreams %u\n", BiDirStreams);
490 
491     /* stop all streams */
492     for (Index = 0; Index < InputStreams; Index++)
493     {
494         WRITE_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_STREAM_CONTROL0 + HDAC_STREAM_BASE + HDAC_INPUT_STREAM_OFFSET(Index), 0);
495         WRITE_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_STREAM_STATUS + HDAC_STREAM_BASE + HDAC_INPUT_STREAM_OFFSET(Index), 0);
496     }
497 
498     for (Index = 0; Index < OutputStreams; Index++) {
499         WRITE_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_STREAM_CONTROL0 + HDAC_STREAM_BASE + HDAC_OUTPUT_STREAM_OFFSET(InputStreams, Index), 0);
500         WRITE_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_STREAM_STATUS + HDAC_STREAM_BASE + HDAC_OUTPUT_STREAM_OFFSET(InputStreams, Index), 0);
501     }
502 
503     for (Index = 0; Index < BiDirStreams; Index++) {
504         WRITE_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_STREAM_CONTROL0 + HDAC_STREAM_BASE + HDAC_BIDIR_STREAM_OFFSET(InputStreams, OutputStreams, Index), 0);
505         WRITE_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_STREAM_STATUS + HDAC_STREAM_BASE + HDAC_BIDIR_STREAM_OFFSET(InputStreams, OutputStreams, Index), 0);
506     }
507 
508     // stop DMA
509     Control = READ_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_CORB_CONTROL) & ~HDAC_CORB_CONTROL_MASK;
510     WRITE_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_CORB_CONTROL, Control);
511 
512     Control = READ_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_RIRB_CONTROL) & ~HDAC_RIRB_CONTROL_MASK;
513     WRITE_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_RIRB_CONTROL, Control);
514 
515     for (int timeout = 0; timeout < 10; timeout++) {
516         KeStallExecutionProcessor(100);
517 
518         corbControl = READ_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_CORB_CONTROL);
519         rirbControl = READ_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_RIRB_CONTROL);
520         if (corbControl == 0 && rirbControl == 0)
521             break;
522     }
523     if (corbControl != 0 || rirbControl != 0) {
524         DPRINT1("hda: unable to stop dma\n");
525         return STATUS_UNSUCCESSFUL;
526     }
527 
528     // reset DMA position buffer
529     WRITE_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_DMA_POSITION_BASE_LOWER), 0);
530     WRITE_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_DMA_POSITION_BASE_UPPER), 0);
531 
532     // Set reset bit - it must be asserted for at least 100us
533     Control = READ_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_GLOBAL_CONTROL));
534     WRITE_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_GLOBAL_CONTROL), Control & ~GLOBAL_CONTROL_RESET);
535 
536     for (int timeout = 0; timeout < 10; timeout++) {
537         KeStallExecutionProcessor(100);
538 
539         Control = READ_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_GLOBAL_CONTROL));
540         if ((Control & GLOBAL_CONTROL_RESET) == 0)
541             break;
542     }
543     if ((Control & GLOBAL_CONTROL_RESET) != 0)
544     {
545         DPRINT1("hda: unable to reset controller\n");
546         return STATUS_UNSUCCESSFUL;
547     }
548 
549     // Unset reset bit
550     Control = READ_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_GLOBAL_CONTROL));
551     WRITE_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_GLOBAL_CONTROL), Control | GLOBAL_CONTROL_RESET);
552 
553     for (int timeout = 0; timeout < 10; timeout++) {
554         KeStallExecutionProcessor(100);
555 
556         Control = READ_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_GLOBAL_CONTROL));
557         if ((Control & GLOBAL_CONTROL_RESET) != 0)
558             break;
559     }
560     if ((Control & GLOBAL_CONTROL_RESET) == 0) {
561         DPRINT1("hda: unable to exit reset\n");
562         return STATUS_UNSUCCESSFUL;
563     }
564 
565     // Wait for codecs to finish their own reset (apparently needs more
566     // time than documented in the specs)
567     KeStallExecutionProcessor(1000);
568 
569     // Enable unsolicited responses
570     Control = READ_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_GLOBAL_CONTROL));
571     WRITE_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_GLOBAL_CONTROL), Control | GLOBAL_CONTROL_UNSOLICITED);
572 
573     return STATUS_SUCCESS;
574 }
575 
576 NTSTATUS
577 NTAPI
578 HDA_FDOStartDevice(
579     IN PDEVICE_OBJECT DeviceObject,
580     IN PIRP Irp)
581 {
582     PIO_STACK_LOCATION IoStack;
583     NTSTATUS Status = STATUS_SUCCESS;
584     PHDA_FDO_DEVICE_EXTENSION DeviceExtension;
585     PCM_RESOURCE_LIST Resources;
586     ULONG Index;
587     USHORT Value;
588 
589     /* get device extension */
590     DeviceExtension = (PHDA_FDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
591     ASSERT(DeviceExtension->IsFDO == TRUE);
592 
593     /* forward irp to lower device */
594     if (!IoForwardIrpSynchronously(DeviceExtension->LowerDevice, Irp))
595     {
596         ASSERT(FALSE);
597         return STATUS_INVALID_DEVICE_REQUEST;
598     }
599     Status = Irp->IoStatus.Status;
600     if (!NT_SUCCESS(Status))
601     {
602         // failed to start
603         DPRINT1("HDA_StartDevice Lower device failed to start %x\n", Status);
604         return Status;
605     }
606 
607     /* get current irp stack location */
608     IoStack = IoGetCurrentIrpStackLocation(Irp);
609 
610     Resources = IoStack->Parameters.StartDevice.AllocatedResourcesTranslated;
611     for (Index = 0; Index < Resources->List[0].PartialResourceList.Count; Index++)
612     {
613         PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor = &Resources->List[0].PartialResourceList.PartialDescriptors[Index];
614 
615         if (Descriptor->Type == CmResourceTypeMemory)
616         {
617             DeviceExtension->RegLength = Descriptor->u.Memory.Length;
618             DeviceExtension->RegBase = (PUCHAR)MmMapIoSpace(Descriptor->u.Memory.Start, Descriptor->u.Memory.Length, MmNonCached);
619             if (DeviceExtension->RegBase == NULL)
620             {
621                 DPRINT1("[HDAB] Failed to map registers\n");
622                 Status = STATUS_UNSUCCESSFUL;
623                 break;
624             }
625         }
626         else if (Descriptor->Type == CmResourceTypeInterrupt)
627         {
628             Status = IoConnectInterrupt(&DeviceExtension->Interrupt,
629                 HDA_InterruptService,
630                 DeviceObject,
631                 NULL,
632                 Descriptor->u.Interrupt.Vector,
633                 Descriptor->u.Interrupt.Level,
634                 Descriptor->u.Interrupt.Level,
635                 (KINTERRUPT_MODE)(Descriptor->Flags & CM_RESOURCE_INTERRUPT_LATCHED),
636                 (Descriptor->ShareDisposition != CmResourceShareDeviceExclusive),
637                 Descriptor->u.Interrupt.Affinity,
638                 FALSE);
639             if (!NT_SUCCESS(Status))
640             {
641                 DPRINT1("[HDAB] Failed to connect interrupt. Status=%lx\n", Status);
642                 break;
643             }
644 
645         }
646     }
647 
648     if (NT_SUCCESS(Status))
649     {
650         // Get controller into valid state
651         Status = HDA_ResetController(DeviceObject);
652         if (!NT_SUCCESS(Status)) return Status;
653 
654         // Setup CORB/RIRB/DMA POS
655         Status = HDA_InitCorbRirbPos(DeviceObject);
656         if (!NT_SUCCESS(Status)) return Status;
657 
658 
659         // Don't enable codec state change interrupts. We don't handle
660         // them, as we want to use the STATE_STATUS register to identify
661         // available codecs. We'd have to clear that register in the interrupt
662         // handler to 'ack' the codec change.
663         Value = READ_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_WAKE_ENABLE)) & ~HDAC_WAKE_ENABLE_MASK;
664         WRITE_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_WAKE_ENABLE), Value);
665 
666         // Enable controller interrupts
667         WRITE_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_INTR_CONTROL), INTR_CONTROL_GLOBAL_ENABLE | INTR_CONTROL_CONTROLLER_ENABLE);
668 
669         KeStallExecutionProcessor(1000);
670 
671         Value = READ_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_STATE_STATUS));
672         if (!Value) {
673             DPRINT1("hda: bad codec status\n");
674             return STATUS_UNSUCCESSFUL;
675         }
676         WRITE_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_STATE_STATUS), Value);
677 
678         // Create codecs
679         DPRINT1("Codecs %lx\n", Value);
680         for (Index = 0; Index < HDA_MAX_CODECS; Index++) {
681             if ((Value & (1 << Index)) != 0) {
682                 HDA_InitCodec(DeviceObject, Index);
683             }
684         }
685     }
686 
687     return Status;
688 }
689 
690 NTSTATUS
691 NTAPI
692 HDA_FDORemoveDevice(
693     _In_ PDEVICE_OBJECT DeviceObject,
694     _Inout_ PIRP Irp)
695 {
696     NTSTATUS Status;
697     PHDA_FDO_DEVICE_EXTENSION DeviceExtension;
698     ULONG CodecIndex, AFGIndex;
699     PHDA_CODEC_ENTRY CodecEntry;
700     PDEVICE_OBJECT ChildPDO;
701     PHDA_PDO_DEVICE_EXTENSION ChildDeviceExtension;
702 
703     /* get device extension */
704     DeviceExtension = static_cast<PHDA_FDO_DEVICE_EXTENSION>(DeviceObject->DeviceExtension);
705     ASSERT(DeviceExtension->IsFDO == TRUE);
706 
707     Irp->IoStatus.Status = STATUS_SUCCESS;
708     IoSkipCurrentIrpStackLocation(Irp);
709     Status = IoCallDriver(DeviceExtension->LowerDevice, Irp);
710 
711     IoDetachDevice(DeviceExtension->LowerDevice);
712 
713     if (DeviceExtension->RegBase != NULL)
714     {
715         MmUnmapIoSpace(DeviceExtension->RegBase,
716                        DeviceExtension->RegLength);
717     }
718     if (DeviceExtension->Interrupt != NULL)
719     {
720         IoDisconnectInterrupt(DeviceExtension->Interrupt);
721     }
722     if (DeviceExtension->CorbBase != NULL)
723     {
724         MmFreeContiguousMemory(DeviceExtension->CorbBase);
725     }
726 
727     for (CodecIndex = 0; CodecIndex < HDA_MAX_CODECS; CodecIndex++)
728     {
729         CodecEntry = DeviceExtension->Codecs[CodecIndex];
730         if (CodecEntry == NULL)
731         {
732             continue;
733         }
734 
735         ASSERT(CodecEntry->AudioGroupCount <= HDA_MAX_AUDIO_GROUPS);
736         for (AFGIndex = 0; AFGIndex < CodecEntry->AudioGroupCount; AFGIndex++)
737         {
738             ChildPDO = CodecEntry->AudioGroups[AFGIndex]->ChildPDO;
739             if (ChildPDO != NULL)
740             {
741                 ChildDeviceExtension = static_cast<PHDA_PDO_DEVICE_EXTENSION>(ChildPDO->DeviceExtension);
742                 ChildDeviceExtension->Codec = NULL;
743                 ChildDeviceExtension->AudioGroup = NULL;
744                 ChildDeviceExtension->FDO = NULL;
745                 ChildDeviceExtension->ReportedMissing = TRUE;
746                 HDA_PDORemoveDevice(ChildPDO);
747             }
748             FreeItem(CodecEntry->AudioGroups[AFGIndex]);
749         }
750         FreeItem(CodecEntry);
751     }
752 
753     IoDeleteDevice(DeviceObject);
754 
755     return Status;
756 }
757 
758 NTSTATUS
759 NTAPI
760 HDA_FDOQueryBusRelations(
761     IN PDEVICE_OBJECT DeviceObject,
762     IN PIRP Irp)
763 {
764     ULONG DeviceCount, CodecIndex, AFGIndex;
765     PHDA_FDO_DEVICE_EXTENSION DeviceExtension;
766     PHDA_CODEC_ENTRY Codec;
767     PDEVICE_RELATIONS DeviceRelations;
768 
769     /* get device extension */
770     DeviceExtension = (PHDA_FDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
771     ASSERT(DeviceExtension->IsFDO == TRUE);
772 
773     DeviceCount = 0;
774     for (CodecIndex = 0; CodecIndex < HDA_MAX_CODECS; CodecIndex++)
775     {
776         if (DeviceExtension->Codecs[CodecIndex] == NULL)
777             continue;
778 
779         Codec = DeviceExtension->Codecs[CodecIndex];
780         DeviceCount += Codec->AudioGroupCount;
781     }
782 
783     if (DeviceCount == 0)
784         return STATUS_UNSUCCESSFUL;
785 
786     DeviceRelations = (PDEVICE_RELATIONS)AllocateItem(NonPagedPool, sizeof(DEVICE_RELATIONS) + (DeviceCount > 1 ? sizeof(PDEVICE_OBJECT) * (DeviceCount - 1) : 0));
787     if (!DeviceRelations)
788         return STATUS_INSUFFICIENT_RESOURCES;
789 
790     DeviceRelations->Count = 0;
791     for (CodecIndex = 0; CodecIndex < HDA_MAX_CODECS; CodecIndex++)
792     {
793         if (DeviceExtension->Codecs[CodecIndex] == NULL)
794             continue;
795 
796         Codec = DeviceExtension->Codecs[CodecIndex];
797         ASSERT(Codec->AudioGroupCount <= HDA_MAX_AUDIO_GROUPS);
798         for (AFGIndex = 0; AFGIndex < Codec->AudioGroupCount; AFGIndex++)
799         {
800             DeviceRelations->Objects[DeviceRelations->Count] = Codec->AudioGroups[AFGIndex]->ChildPDO;
801             ObReferenceObject(Codec->AudioGroups[AFGIndex]->ChildPDO);
802             DeviceRelations->Count++;
803         }
804     }
805 
806     /* FIXME handle existing device relations */
807     ASSERT(Irp->IoStatus.Information == 0);
808 
809     /* store device relations */
810     Irp->IoStatus.Information = (ULONG_PTR)DeviceRelations;
811 
812     /* done */
813     return STATUS_SUCCESS;
814 }
815 
816 
817