5d5fa1b0001661d54f9794f76149e175ad82d9ac
[reactos.git] / reactos / drivers / wdm / audio / drivers / CMIDriver / adapter.cpp
1 /*
2 Copyright (c) 2006-2008 dogbert <dogber1@gmail.com>
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
7 are met:
8 1. Redistributions of source code must retain the above copyright
9 notice, this list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
13 3. The name of the author may not be used to endorse or promote products
14 derived from this software without specific prior written permission.
15
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 #define PUT_GUIDS_HERE
29
30 #include "adapter.hpp"
31
32 #ifdef _MSC_VER
33 #pragma code_seg("PAGE")
34 #endif
35
36 NTSTATUS InstallSubdevice(PDEVICE_OBJECT DeviceObject, PIRP Irp, PWCHAR Name, REFGUID PortClassId, REFGUID MiniportClassId, PFNCREATEINSTANCE MiniportCreate, PUNKNOWN UnknownAdapter, PRESOURCELIST ResourceList, REFGUID PortInterfaceId, PUNKNOWN* OutPortUnknown)
37 {
38 PAGED_CODE();
39 DBGPRINT(("InstallSubdevice()"));
40
41 NTSTATUS ntStatus;
42 PPORT Port;
43 PMINIPORT MiniPort;
44
45 ntStatus = PcNewPort(&Port, PortClassId);
46 if (NT_SUCCESS(ntStatus)) {
47 if (MiniportCreate) {
48 ntStatus = MiniportCreate((PUNKNOWN*)&MiniPort, MiniportClassId, NULL, NonPagedPool);
49 } else {
50 ntStatus = PcNewMiniport(&MiniPort, MiniportClassId);
51 }
52 }
53
54 if (!NT_SUCCESS(ntStatus)) {
55 Port->Release();
56 return ntStatus;
57 }
58
59 ntStatus = Port->Init(DeviceObject, Irp, MiniPort, UnknownAdapter, ResourceList);
60 if (NT_SUCCESS(ntStatus)) {
61 ntStatus = PcRegisterSubdevice(DeviceObject, Name, Port);
62
63 if (OutPortUnknown && NT_SUCCESS (ntStatus)) {
64 ntStatus = Port->QueryInterface(IID_IUnknown, (PVOID *)OutPortUnknown);
65 }
66 }
67
68 if (MiniPort) {
69 MiniPort->Release();
70 }
71
72 if (Port) {
73 Port->Release();
74 }
75
76 return ntStatus;
77 }
78
79
80 NTSTATUS ProcessResources(PRESOURCELIST ResourceList, PRESOURCELIST* UartResourceList)
81 {
82 PAGED_CODE();
83 ASSERT(ResourceList);
84 ASSERT(UartResourceList);
85 DBGPRINT(("ProcessResources()"));
86 DBGPRINT(("NumberOfPorts: %d, NumberOfInterrupts: %d, NumberOfDmas: %d", ResourceList->NumberOfPorts(), ResourceList->NumberOfInterrupts(), ResourceList->NumberOfDmas()));
87 #ifdef UART
88 NTSTATUS ntStatus;
89
90 (*UartResourceList) = NULL;
91 #endif
92
93 if ((ResourceList->NumberOfPorts() == 0) || (ResourceList->NumberOfPorts() > 2) || (ResourceList->NumberOfInterrupts() != 1) || (ResourceList->NumberOfDmas() != 0)) {
94 DBGPRINT(("Unexpected configuration"));
95 return STATUS_DEVICE_CONFIGURATION_ERROR;
96 }
97
98 #ifdef UART
99 ntStatus = PcNewResourceSublist(UartResourceList, NULL, PagedPool, ResourceList, 2);
100 if (NT_SUCCESS(ntStatus)) {
101 (*UartResourceList)->AddPortFromParent(ResourceList, 1);
102 (*UartResourceList)->AddInterruptFromParent(ResourceList, 0);
103 }
104 #endif
105
106 return STATUS_SUCCESS;
107 }
108
109
110 NTSTATUS StartDevice(PDEVICE_OBJECT DeviceObject, PIRP Irp, PRESOURCELIST ResourceList)
111 {
112 PAGED_CODE();
113 ASSERT(DeviceObject);
114 ASSERT(Irp);
115 ASSERT(ResourceList);
116 DBGPRINT(("StartDevice()"));
117
118 NTSTATUS ntStatus;
119 PPORT pPort = 0;
120 #ifdef UART
121 ULONG* MPUBase;
122 #endif
123
124 ntStatus = PcNewPort(&pPort,CLSID_PortWaveCyclic);
125 if (NT_SUCCESS(ntStatus)) {
126 // not supported in the first edition of win98
127 PPORTEVENTS pPortEvents = 0;
128 ntStatus = pPort->QueryInterface(IID_IPortEvents, (PVOID *)&pPortEvents);
129 if (!NT_SUCCESS(ntStatus)) {
130 DBGPRINT(("ERROR: This driver doesn't work under Win98!"));
131 ntStatus = STATUS_UNSUCCESSFUL;
132 }
133 else
134 {
135 pPortEvents->Release();
136 }
137 pPort->Release ();
138 } else {
139 return ntStatus;
140 }
141
142 // resource validation
143 PRESOURCELIST UartResourceList = NULL;
144 ntStatus = ProcessResources(ResourceList, &UartResourceList);
145 if (!NT_SUCCESS(ntStatus)) {
146 DBGPRINT(("ProcessResources() failed"));
147 return ntStatus;
148 }
149
150 PCMIADAPTER pCMIAdapter = NULL;
151 PUNKNOWN pUnknownCommon = NULL;
152
153 // create the CMIAdapter object
154 ntStatus = NewCMIAdapter(&pUnknownCommon, IID_ICMIAdapter, NULL, NonPagedPool);
155 if (!NT_SUCCESS(ntStatus)) {
156 DBGPRINT(("NewCMIAdapter() failed"));
157 return ntStatus;
158 }
159
160 ntStatus = pUnknownCommon->QueryInterface(IID_ICMIAdapter, (PVOID *)&pCMIAdapter);
161 if (!NT_SUCCESS(ntStatus)) {
162 DBGPRINT(("QueryInterface() for ICMIAdapter failed"));
163 return ntStatus;
164 }
165 ntStatus = pCMIAdapter->init(ResourceList, DeviceObject);
166 if (!NT_SUCCESS(ntStatus)) {
167 DBGPRINT(("CMIAdapter->init() failed"));
168 return ntStatus;
169 }
170
171 ntStatus = PcRegisterAdapterPowerManagement((PUNKNOWN)pCMIAdapter, DeviceObject);
172
173 pUnknownCommon->Release();
174
175 PUNKNOWN unknownWave = NULL;
176 PUNKNOWN unknownTopology = NULL;
177
178 // install the topology miniport.
179 ntStatus = InstallSubdevice(DeviceObject, Irp, L"Topology", CLSID_PortTopology, CLSID_PortTopology, CreateMiniportTopologyCMI, pCMIAdapter, NULL, GUID_NULL, &unknownTopology);
180 if (!NT_SUCCESS (ntStatus)) {
181 DBGPRINT(("Topology miniport installation failed"));
182 return ntStatus;
183 }
184
185 #ifdef UART
186 // install the UART miniport - execution order important
187 ntStatus = STATUS_UNSUCCESSFUL;
188 MPUBase = 0;
189 for (int i=0;i<ResourceList->NumberOfPorts();i++) {
190 if (ResourceList->FindTranslatedPort(i)->u.Port.Length == 2) {
191 MPUBase = (UInt32*)ResourceList->FindTranslatedPort(i)->u.Port.Start.QuadPart;
192 }
193 }
194 if (MPUBase != 0) {
195 ntStatus = pCMIAdapter->activateMPU(MPUBase);
196 if (NT_SUCCESS(ntStatus)) {
197 ntStatus = InstallSubdevice(DeviceObject, Irp, L"Uart", CLSID_PortDMus, CLSID_MiniportDriverDMusUART, NULL, pCMIAdapter->getInterruptSync(), UartResourceList, IID_IPortDMus, NULL);
198 }
199 }
200 if (!NT_SUCCESS(ntStatus)) {
201 MPUBase = 0;
202 pCMIAdapter->activateMPU(0);
203 DBGPRINT(("UART miniport installation failed"));
204 }
205 if (UartResourceList) {
206 UartResourceList->Release();
207 }
208 #endif
209
210 // install the wave miniport - the order matters here
211 #ifdef WAVERT
212 ntStatus = InstallSubdevice(DeviceObject, Irp, L"Wave", CLSID_PortWaveRT, CLSID_PortWaveRT, CreateMiniportWaveCMI, pCMIAdapter, ResourceList, IID_IPortWaveRT, &unknownWave);
213 #else
214 ntStatus = InstallSubdevice(DeviceObject, Irp, L"Wave", CLSID_PortWaveCyclic, CLSID_PortWaveCyclic, CreateMiniportWaveCMI, pCMIAdapter, ResourceList, IID_IPortWaveCyclic, &unknownWave);
215 #endif
216 if (!NT_SUCCESS(ntStatus)) {
217 DBGPRINT(("Wave miniport installation failed"));
218 return ntStatus;
219 }
220
221 // connect wave and topology pins
222 ntStatus = PcRegisterPhysicalConnection(DeviceObject, unknownWave, PIN_WAVE_RENDER_SOURCE, unknownTopology, PIN_WAVEOUT_SOURCE);
223 if (!NT_SUCCESS(ntStatus)) {
224 DBGPRINT(("Cannot connect topology and wave miniport (render)!"));
225 return ntStatus;
226 }
227 ntStatus = PcRegisterPhysicalConnection(DeviceObject, unknownTopology, PIN_WAVEIN_DEST, unknownWave, PIN_WAVE_CAPTURE_SOURCE);
228 if (!NT_SUCCESS(ntStatus)) {
229 DBGPRINT(("Cannot connect topology and wave miniport (capture)!"));
230 return ntStatus;
231 }
232 if (!IoIsWdmVersionAvailable(6,0)) {
233 // this shit fixes the fucking XP mixer and breaks the vista mixer, so we have to check for vista here
234 ntStatus = PcRegisterPhysicalConnection(DeviceObject, unknownWave, PIN_WAVE_AC3_RENDER_SOURCE, unknownTopology, PIN_SPDIF_AC3_SOURCE);
235 if (!NT_SUCCESS(ntStatus)) {
236 DBGPRINT(("Cannot connect topology and wave miniport (ac3)!"));
237 }
238 }
239
240 // clean up
241 if (pCMIAdapter) {
242 pCMIAdapter->Release();
243 }
244 if (unknownTopology) {
245 unknownTopology->Release();
246 }
247 if (unknownWave) {
248 unknownWave->Release();
249 }
250
251 return ntStatus;
252 }
253
254 extern "C" NTSTATUS NTAPI AddDevice(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT PhysicalDeviceObject)
255 {
256 PAGED_CODE();
257 DBGPRINT(("AddDevice()"));
258
259 return PcAddAdapterDevice(DriverObject, PhysicalDeviceObject, (PCPFNSTARTDEVICE)StartDevice, MAX_MINIPORTS, 0);
260 }
261
262 bool CopyResourceDescriptor(PIO_RESOURCE_DESCRIPTOR pInResDescriptor, PIO_RESOURCE_DESCRIPTOR pOutResDescriptor)
263 {
264 PAGED_CODE();
265 ASSERT(pInResDescriptor);
266 ASSERT(pOutResDescriptor);
267 DBGPRINT(("CopyResourceDescriptor()"));
268
269 #if 0
270 RtlCopyMemory(pOutResDescriptor, pInResDescriptor, sizeof(IO_RESOURCE_DESCRIPTOR));
271 #else
272 pOutResDescriptor->Type = pInResDescriptor->Type;
273 pOutResDescriptor->ShareDisposition = pInResDescriptor->ShareDisposition;
274 pOutResDescriptor->Flags = pInResDescriptor->Flags;
275 pOutResDescriptor->Option = pInResDescriptor->Option;
276
277 switch (pInResDescriptor->Type) {
278 case CmResourceTypePort:
279 case CmResourceTypePort | CmResourceTypeNonArbitrated: // huh?
280 /* // filter crap
281 if ((pInResDescriptor->u.Port.Length == 0) ||
282 ( (pInResDescriptor->u.Port.MinimumAddress.HighPart == pInResDescriptor->u.Port.MaximumAddress.HighPart) && (pInResDescriptor->u.Port.MinimumAddress.LowPart == pInResDescriptor->u.Port.MaximumAddress.LowPart) ) ) {
283 return FALSE;
284 }
285 */ pOutResDescriptor->u.Port.MinimumAddress = pInResDescriptor->u.Port.MinimumAddress;
286 pOutResDescriptor->u.Port.MaximumAddress = pInResDescriptor->u.Port.MaximumAddress;
287 pOutResDescriptor->u.Port.Length = pInResDescriptor->u.Port.Length;
288 pOutResDescriptor->u.Port.Alignment = pInResDescriptor->u.Port.Alignment;
289 DBGPRINT((" Port: min %08x.%08x max %08x.%08x, Length: %x, Option: %x", pOutResDescriptor->u.Port.MinimumAddress.HighPart, pOutResDescriptor->u.Port.MinimumAddress.LowPart,
290 pOutResDescriptor->u.Port.MaximumAddress.HighPart, pOutResDescriptor->u.Port.MaximumAddress.LowPart,
291 pOutResDescriptor->u.Port.Length, pOutResDescriptor->Option));
292 break;
293 case CmResourceTypeInterrupt:
294 pOutResDescriptor->u.Interrupt.MinimumVector = pInResDescriptor->u.Interrupt.MinimumVector;
295 pOutResDescriptor->u.Interrupt.MaximumVector = pInResDescriptor->u.Interrupt.MaximumVector;
296 DBGPRINT((" IRQ: min %x max %x, Option: %d", pOutResDescriptor->u.Interrupt.MinimumVector, pOutResDescriptor->u.Interrupt.MaximumVector, pOutResDescriptor->Option));
297 break;
298 default:
299 return FALSE;
300 }
301 return TRUE;
302 #endif
303 }
304
305 extern "C" NTSTATUS AdapterDispatchPnp(PDEVICE_OBJECT pDeviceObject, PIRP pIrp)
306 {
307 PAGED_CODE();
308 ASSERT(pDeviceObject);
309 ASSERT(pIrp);
310 DBGPRINT(("AdapterDispatchPnp()"));
311
312 NTSTATUS ntStatus = STATUS_SUCCESS;
313 ULONG resourceListSize;
314 PIO_RESOURCE_REQUIREMENTS_LIST resourceList, list;
315 PIO_RESOURCE_DESCRIPTOR descriptor;
316 PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(pIrp);
317
318 if (pIrpStack->MinorFunction == IRP_MN_FILTER_RESOURCE_REQUIREMENTS) {
319 DBGPRINT(("[AdapterDispatchPnp] - IRP_MN_FILTER_RESOURCE_REQUIREMENTS"));
320
321 list = (PIO_RESOURCE_REQUIREMENTS_LIST)pIrp->IoStatus.Information;
322
323 // IO_RESOURCE_REQUIREMENTS_LIST has 1 IO_RESOURCE_LIST, IO_RESOURCE_LIST has 1 IO_RESOURCE_DESCRIPTOR and we want 2 more
324 resourceListSize = sizeof(IO_RESOURCE_REQUIREMENTS_LIST) + sizeof(IO_RESOURCE_DESCRIPTOR)*(list->List[0].Count+2) ;
325 resourceList = (PIO_RESOURCE_REQUIREMENTS_LIST)ExAllocatePoolWithTag(PagedPool, resourceListSize, 'LRDV');
326
327 if (!resourceList) {
328 ntStatus = STATUS_INSUFFICIENT_RESOURCES;
329 return ntStatus;
330 }
331
332
333 RtlZeroMemory(resourceList, resourceListSize);
334
335 // initialize the list header
336 resourceList->AlternativeLists = 1; // number of IO_RESOURCE_LISTs
337 resourceList->ListSize = resourceListSize;
338
339 resourceList->List[0].Version = 1;
340 resourceList->List[0].Revision = 1;
341 resourceList->List[0].Count = 0;
342
343 // copy the resources which have already been assigned
344 for (unsigned int i=0;i<list->List[0].Count;i++) {
345 if (CopyResourceDescriptor(&list->List[0].Descriptors[i], &resourceList->List[0].Descriptors[resourceList->List[0].Count])) {
346 resourceList->List[0].Count++;
347 }
348 }
349 ExFreePool(list);
350
351 // an additional port for mpu401
352 resourceList->List[0].Count++;
353 descriptor = &resourceList->List[0].Descriptors[resourceList->List[0].Count-1];
354 descriptor->Option = IO_RESOURCE_PREFERRED;
355 descriptor->Type = CmResourceTypePort;
356 descriptor->ShareDisposition = CmResourceShareDeviceExclusive;
357 descriptor->Flags = CM_RESOURCE_PORT_IO;
358 descriptor->u.Port.MinimumAddress.LowPart = 0x300;
359 descriptor->u.Port.MinimumAddress.HighPart = 0;
360 descriptor->u.Port.MaximumAddress.LowPart = 0x330;
361 descriptor->u.Port.MaximumAddress.HighPart = 0;
362 descriptor->u.Port.Length = 2;
363 descriptor->u.Port.Alignment = 0x10;
364
365 // mpu401 port should be optional. yes, this is severely braindamaged.
366 resourceList->List[0].Count++;
367 descriptor = &resourceList->List[0].Descriptors[resourceList->List[0].Count-1];
368 descriptor->Option = IO_RESOURCE_ALTERNATIVE;
369 descriptor->Type = CmResourceTypePort;
370 descriptor->ShareDisposition = CmResourceShareDeviceExclusive;
371 descriptor->Flags = CM_RESOURCE_PORT_IO;
372 descriptor->u.Port.MinimumAddress.LowPart = 0x0;
373 descriptor->u.Port.MinimumAddress.HighPart = 0;
374 descriptor->u.Port.MaximumAddress.LowPart = 0xFFFF;
375 descriptor->u.Port.MaximumAddress.HighPart = 0;
376 descriptor->u.Port.Length = 1;
377 descriptor->u.Port.Alignment = 0x10;
378
379 DBGPRINT(("number of resource list descriptors: %d", resourceList->List[0].Count));
380
381 pIrp->IoStatus.Information = (ULONG_PTR)resourceList;
382
383 // set the return status
384 pIrp->IoStatus.Status = ntStatus;
385 }
386
387 // Pass the IRPs on to PortCls
388 ntStatus = PcDispatchIrp(pDeviceObject, pIrp);
389
390 return ntStatus;
391 }
392
393 extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPathName)
394 {
395 PAGED_CODE();
396 DBGPRINT(("DriverEntry()"));
397
398 NTSTATUS ntStatus;
399
400 //bind the adapter driver to the portclass driver
401 ntStatus = PcInitializeAdapterDriver(DriverObject, RegistryPathName, AddDevice);
402 #ifdef UART
403 if(NT_SUCCESS(ntStatus)) {
404 DriverObject->MajorFunction[IRP_MJ_PNP] = AdapterDispatchPnp;
405 }
406 #endif
407 #ifdef WAVERT
408 if (!IoIsWdmVersionAvailable(6,0)) {
409 ntStatus = STATUS_UNSUCCESSFUL;
410 }
411 #endif
412
413 return ntStatus;
414 }
415
416 #ifdef _MSC_VER
417 #pragma code_seg()
418 #endif
419 int __cdecl _purecall (void)
420 {
421 return 0;
422 }