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