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