1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS Kernel Streaming
4  * FILE:            drivers/wdm/audio/backpln/portcls/api.cpp
5  * PURPOSE:         Port Class driver / DriverEntry and IRP handlers
6  * PROGRAMMER:      Andrew Greenwood
7  *                  Johannes Anderwald
8  * HISTORY:
9  *                  27 Jan 07   Created
10  */
11 
12 #include "private.hpp"
13 
14 #define NDEBUG
15 #include <debug.h>
16 
17 //
18 //  This is called from DriverEntry so that PortCls can take care of some
19 //  IRPs and map some others to the main KS driver. In most cases this will
20 //  be the first function called by an audio driver.
21 //
22 //  First 2 parameters are from DriverEntry.
23 //
24 //  The AddDevice parameter is a driver-supplied pointer to a function which
25 //  typically then calls PcAddAdapterDevice (see below.)
26 //
27 NTSTATUS
28 NTAPI
29 PcInitializeAdapterDriver(
30     IN  PDRIVER_OBJECT DriverObject,
31     IN  PUNICODE_STRING RegistryPathName,
32     IN  PDRIVER_ADD_DEVICE AddDevice)
33 {
34     DPRINT("PcInitializeAdapterDriver\n");
35     PC_ASSERT_IRQL_EQUAL(PASSIVE_LEVEL);
36 
37     // Our IRP handlers
38     DPRINT("Setting IRP handlers\n");
39     DriverObject->MajorFunction[IRP_MJ_CREATE] = PcDispatchIrp;
40     DriverObject->MajorFunction[IRP_MJ_PNP] = PcDispatchIrp;
41     DriverObject->MajorFunction[IRP_MJ_POWER] = PcDispatchIrp;
42     DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = PcDispatchIrp;
43     DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = PcDispatchIrp;
44 
45     // The driver-supplied AddDevice
46     DriverObject->DriverExtension->AddDevice = AddDevice;
47 
48     // KS handles these
49     DPRINT("Setting KS function handlers\n");
50     KsSetMajorFunctionHandler(DriverObject, IRP_MJ_CLOSE);
51     KsSetMajorFunctionHandler(DriverObject, IRP_MJ_DEVICE_CONTROL);
52     KsSetMajorFunctionHandler(DriverObject, IRP_MJ_FLUSH_BUFFERS);
53     KsSetMajorFunctionHandler(DriverObject, IRP_MJ_QUERY_SECURITY);
54     KsSetMajorFunctionHandler(DriverObject, IRP_MJ_READ);
55     KsSetMajorFunctionHandler(DriverObject, IRP_MJ_SET_SECURITY);
56     KsSetMajorFunctionHandler(DriverObject, IRP_MJ_WRITE);
57 
58     DPRINT("PortCls has finished initializing the adapter driver\n");
59 
60     return STATUS_SUCCESS;
61 }
62 
63 //
64 //  Typically called by a driver's AddDevice function, which is set when
65 //  calling PcInitializeAdapterDriver. This performs some common driver
66 //  operations, such as creating a device extension.
67 //
68 //  The StartDevice parameter is a driver-supplied function which gets
69 //  called in response to IRP_MJ_PNP / IRP_MN_START_DEVICE.
70 //
71 NTSTATUS
72 NTAPI
73 PcAddAdapterDevice(
74     IN  PDRIVER_OBJECT DriverObject,
75     IN  PDEVICE_OBJECT PhysicalDeviceObject,
76     IN  PCPFNSTARTDEVICE StartDevice,
77     IN  ULONG MaxObjects,
78     IN  ULONG DeviceExtensionSize)
79 {
80     NTSTATUS status = STATUS_UNSUCCESSFUL;
81     PDEVICE_OBJECT fdo;
82     PDEVICE_OBJECT PrevDeviceObject;
83     PPCLASS_DEVICE_EXTENSION portcls_ext = NULL;
84 
85     DPRINT("PcAddAdapterDevice called\n");
86     PC_ASSERT_IRQL_EQUAL(PASSIVE_LEVEL);
87 
88     if (!DriverObject || !PhysicalDeviceObject || !StartDevice)
89     {
90         return STATUS_INVALID_PARAMETER;
91     }
92 
93     // check if the DeviceExtensionSize is provided
94     if ( DeviceExtensionSize < PORT_CLASS_DEVICE_EXTENSION_SIZE )
95     {
96         // driver does not need a device extension
97         if ( DeviceExtensionSize != 0 )
98         {
99             // DeviceExtensionSize must be zero
100             return STATUS_INVALID_PARAMETER;
101         }
102         // set size to our extension size
103         DeviceExtensionSize = PORT_CLASS_DEVICE_EXTENSION_SIZE;
104     }
105 
106     // create the device
107     status = IoCreateDevice(DriverObject,
108                             DeviceExtensionSize,
109                             NULL,
110                             FILE_DEVICE_KS,
111                             FILE_AUTOGENERATED_DEVICE_NAME | FILE_DEVICE_SECURE_OPEN,
112                             FALSE,
113                             &fdo);
114 
115     if (!NT_SUCCESS(status))
116     {
117         DPRINT("IoCreateDevice() failed with status 0x%08lx\n", status);
118         return status;
119     }
120 
121     // Obtain the new device extension
122     portcls_ext = (PPCLASS_DEVICE_EXTENSION) fdo->DeviceExtension;
123     // initialize the device extension
124     RtlZeroMemory(portcls_ext, DeviceExtensionSize);
125     // allocate create item
126     portcls_ext->CreateItems = (PKSOBJECT_CREATE_ITEM)AllocateItem(NonPagedPool, MaxObjects * sizeof(KSOBJECT_CREATE_ITEM), TAG_PORTCLASS);
127 
128     if (!portcls_ext->CreateItems)
129     {
130         // not enough resources
131         status = STATUS_INSUFFICIENT_RESOURCES;
132         goto cleanup;
133     }
134 
135     // store max subdevice count
136     portcls_ext->MaxSubDevices = MaxObjects;
137     // store the physical device object
138     portcls_ext->PhysicalDeviceObject = PhysicalDeviceObject;
139     // set up the start device function
140     portcls_ext->StartDevice = StartDevice;
141     // initialize timer lock
142     KeInitializeSpinLock(&portcls_ext->TimerListLock);
143     // initialize timer list
144     InitializeListHead(&portcls_ext->TimerList);
145     // initialize io timer
146     IoInitializeTimer(fdo, PcIoTimerRoutine, NULL);
147     // start the io timer
148     IoStartTimer(fdo);
149 
150     // set io flags
151     fdo->Flags |= DO_DIRECT_IO | DO_POWER_PAGABLE;
152     // clear initializing flag
153     fdo->Flags &= ~ DO_DEVICE_INITIALIZING;
154 
155     // allocate the device header
156     status = KsAllocateDeviceHeader(&portcls_ext->KsDeviceHeader, MaxObjects, portcls_ext->CreateItems);
157     // did we succeed
158     if (!NT_SUCCESS(status))
159     {
160         goto cleanup;
161     }
162 
163     // attach device to device stack
164     PrevDeviceObject = IoAttachDeviceToDeviceStack(fdo, PhysicalDeviceObject);
165     // did we succeed
166     if (PrevDeviceObject)
167     {
168         // store the device object in the device header
169         //KsSetDevicePnpBaseObject(portcls_ext->KsDeviceHeader, fdo, PrevDeviceObject);
170         portcls_ext->PrevDeviceObject = PrevDeviceObject;
171     }
172     else
173     {
174         // return error code
175         status = STATUS_UNSUCCESSFUL;
176         goto cleanup;
177     }
178 
179     // register shutdown notification
180     IoRegisterShutdownNotification(PhysicalDeviceObject);
181 
182     return status;
183 
184 cleanup:
185 
186     if (portcls_ext->KsDeviceHeader)
187     {
188         // free the device header
189         KsFreeDeviceHeader(portcls_ext->KsDeviceHeader);
190     }
191 
192     if (portcls_ext->CreateItems)
193     {
194         // free previously allocated create items
195         FreeItem(portcls_ext->CreateItems, TAG_PORTCLASS);
196     }
197 
198     // delete created fdo
199     IoDeleteDevice(fdo);
200 
201     return status;
202 }
203 
204 NTSTATUS
205 NTAPI
206 PcRegisterSubdevice(
207     IN  PDEVICE_OBJECT DeviceObject,
208     IN  PWCHAR Name,
209     IN  PUNKNOWN Unknown)
210 {
211     PPCLASS_DEVICE_EXTENSION DeviceExt;
212     NTSTATUS Status;
213     ISubdevice *SubDevice;
214     UNICODE_STRING SymbolicLinkName;
215     PSUBDEVICE_DESCRIPTOR SubDeviceDescriptor;
216     ULONG Index;
217     UNICODE_STRING RefName;
218     PSYMBOLICLINK_ENTRY SymEntry;
219 
220     DPRINT("PcRegisterSubdevice DeviceObject %p Name %S Unknown %p\n", DeviceObject, Name, Unknown);
221 
222     PC_ASSERT_IRQL_EQUAL(PASSIVE_LEVEL);
223 
224     // check if all parameters are valid
225     if (!DeviceObject || !Name || !Unknown)
226     {
227         DPRINT("PcRegisterSubdevice invalid parameter\n");
228         return STATUS_INVALID_PARAMETER;
229     }
230 
231     // get device extension
232     DeviceExt = (PPCLASS_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
233 
234     if (!DeviceExt)
235     {
236         // should not happen
237         DbgBreakPoint();
238         return STATUS_UNSUCCESSFUL;
239     }
240 
241     // look up our undocumented interface
242     Status = Unknown->QueryInterface(IID_ISubdevice, (LPVOID*)&SubDevice);
243     if (!NT_SUCCESS(Status))
244     {
245         DPRINT("No ISubdevice interface\n");
246         // the provided port driver doesnt support ISubdevice
247         return STATUS_INVALID_PARAMETER;
248     }
249 
250     // get the subdevice descriptor
251     Status = SubDevice->GetDescriptor(&SubDeviceDescriptor);
252     if (!NT_SUCCESS(Status))
253     {
254         DPRINT("Failed to get subdevice descriptor %x\n", Status);
255         SubDevice->Release();
256         return STATUS_UNSUCCESSFUL;
257     }
258 
259     // add an create item to the device header
260     Status = KsAddObjectCreateItemToDeviceHeader(DeviceExt->KsDeviceHeader, PcCreateItemDispatch, (PVOID)SubDevice, Name, NULL);
261     if (!NT_SUCCESS(Status))
262     {
263         // failed to attach
264         SubDevice->Release();
265         DPRINT("KsAddObjectCreateItemToDeviceHeader failed with %x\n", Status);
266         return Status;
267     }
268 
269     // initialize reference string
270     RtlInitUnicodeString(&RefName, Name);
271     RtlInitUnicodeString(&SubDeviceDescriptor->RefString, Name);
272 
273     for(Index = 0; Index < SubDeviceDescriptor->InterfaceCount; Index++)
274     {
275         // FIXME
276         // check if reference string with that name already exists
277 
278         Status = IoRegisterDeviceInterface(DeviceExt->PhysicalDeviceObject,
279                                            &SubDeviceDescriptor->Interfaces[Index],
280                                            &RefName,
281                                            &SymbolicLinkName);
282 
283         if (NT_SUCCESS(Status))
284         {
285             // activate device interface
286             IoSetDeviceInterfaceState(&SymbolicLinkName, TRUE);
287             // allocate symbolic link entry
288             SymEntry = (PSYMBOLICLINK_ENTRY)AllocateItem(NonPagedPool, sizeof(SYMBOLICLINK_ENTRY), TAG_PORTCLASS);
289             if (SymEntry)
290             {
291                 // initialize symbolic link item
292                 RtlInitUnicodeString(&SymEntry->SymbolicLink, SymbolicLinkName.Buffer);
293                 // store item
294                 InsertTailList(&SubDeviceDescriptor->SymbolicLinkList, &SymEntry->Entry);
295             }
296             else
297             {
298                 // allocating failed
299                 RtlFreeUnicodeString(&SymbolicLinkName);
300             }
301         }
302     }
303 
304     // release SubDevice reference
305     SubDevice->Release();
306 
307     return STATUS_SUCCESS;
308 }
309