2 * WINE HID Pseudo-Plug and Play support
4 * Copyright 2015 Aric Stewart
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #define NONAMELESSUNION
25 #include "ddk/hidtypes.h"
27 #include "wine/debug.h"
28 #include "wine/unicode.h"
29 #include "wine/list.h"
31 WINE_DEFAULT_DEBUG_CHANNEL(hid
);
33 static const WCHAR device_enumeratorW
[] = {'H','I','D',0};
34 static const WCHAR separator_W
[] = {'\\',0};
35 static const WCHAR device_deviceid_fmtW
[] = {'%','s','\\',
36 'v','i','d','_','%','0','4','x','&','p','i','d','_','%', '0','4','x'};
38 typedef struct _NATIVE_DEVICE
{
43 HID_MINIDRIVER_REGISTRATION
*minidriver
;
47 static struct list tracked_devices
= LIST_INIT(tracked_devices
);
49 static NTSTATUS WINAPI
internalComplete(DEVICE_OBJECT
*deviceObject
, IRP
*irp
,
52 SetEvent(irp
->UserEvent
);
53 return STATUS_MORE_PROCESSING_REQUIRED
;
56 static NTSTATUS
get_device_id(DEVICE_OBJECT
*device
, BUS_QUERY_ID_TYPE type
, WCHAR
**id
)
59 IO_STACK_LOCATION
*irpsp
;
60 IO_STATUS_BLOCK irp_status
;
62 HANDLE event
= CreateEventA(NULL
, FALSE
, FALSE
, NULL
);
64 irp
= IoBuildSynchronousFsdRequest(IRP_MJ_PNP
, device
, NULL
, 0, NULL
, NULL
, &irp_status
);
66 return STATUS_NO_MEMORY
;
68 irp
->UserEvent
= event
;
69 irpsp
= IoGetNextIrpStackLocation(irp
);
70 irpsp
->MinorFunction
= IRP_MN_QUERY_ID
;
71 irpsp
->Parameters
.QueryId
.IdType
= type
;
72 irpsp
->CompletionRoutine
= internalComplete
;
73 irpsp
->Control
= SL_INVOKE_ON_SUCCESS
| SL_INVOKE_ON_ERROR
;
75 IoCallDriver(device
, irp
);
76 if (irp
->IoStatus
.u
.Status
== STATUS_PENDING
)
77 WaitForSingleObject(event
, INFINITE
);
79 *id
= (WCHAR
*)irp
->IoStatus
.Information
;
80 status
= irp
->IoStatus
.u
.Status
;
81 IoCompleteRequest(irp
, IO_NO_INCREMENT
);
87 NTSTATUS WINAPI
PNP_AddDevice(DRIVER_OBJECT
*driver
, DEVICE_OBJECT
*PDO
)
89 DEVICE_OBJECT
*device
= NULL
;
91 minidriver
*minidriver
;
92 HID_DEVICE_ATTRIBUTES attr
;
93 BASE_DEVICE_EXTENSION
*ext
= NULL
;
94 NATIVE_DEVICE
*tracked_device
;
95 HID_DESCRIPTOR descriptor
;
96 BYTE
*reportDescriptor
;
101 status
= get_device_id(PDO
, BusQueryInstanceID
, &PDO_id
);
102 if (status
!= STATUS_SUCCESS
)
104 ERR("Failed to get PDO id(%x)\n",status
);
108 TRACE("PDO add device(%p:%s)\n", PDO
, debugstr_w(PDO_id
));
109 minidriver
= find_minidriver(driver
);
111 status
= HID_CreateDevice(PDO
, &minidriver
->minidriver
, &device
);
112 if (status
!= STATUS_SUCCESS
)
114 ERR("Failed to create HID object (%x)\n",status
);
115 HeapFree(GetProcessHeap(), 0, PDO_id
);
119 ext
= device
->DeviceExtension
;
120 InitializeListHead(&ext
->irp_queue
);
122 TRACE("Created device %p\n",device
);
123 status
= minidriver
->AddDevice(minidriver
->minidriver
.DriverObject
, device
);
124 if (status
!= STATUS_SUCCESS
)
126 ERR("Minidriver AddDevice failed (%x)\n",status
);
127 HeapFree(GetProcessHeap(), 0, PDO_id
);
128 HID_DeleteDevice(&minidriver
->minidriver
, device
);
132 status
= call_minidriver(IOCTL_HID_GET_DEVICE_ATTRIBUTES
, device
,
133 NULL
, 0, &attr
, sizeof(attr
));
135 if (status
!= STATUS_SUCCESS
)
137 ERR("Minidriver failed to get Attributes(%x)\n",status
);
138 HID_DeleteDevice(&minidriver
->minidriver
, device
);
139 HeapFree(GetProcessHeap(), 0, PDO_id
);
143 ext
->information
.VendorID
= attr
.VendorID
;
144 ext
->information
.ProductID
= attr
.ProductID
;
145 ext
->information
.VersionNumber
= attr
.VersionNumber
;
146 ext
->information
.Polled
= minidriver
->minidriver
.DevicesArePolled
;
148 tracked_device
= HeapAlloc(GetProcessHeap(), 0, sizeof(*tracked_device
));
149 tracked_device
->PDO
= PDO
;
150 tracked_device
->FDO
= device
;
151 tracked_device
->minidriver
= &minidriver
->minidriver
;
152 list_add_tail(&tracked_devices
, &tracked_device
->entry
);
154 status
= call_minidriver(IOCTL_HID_GET_DEVICE_DESCRIPTOR
, device
, NULL
, 0,
155 &descriptor
, sizeof(descriptor
));
156 if (status
!= STATUS_SUCCESS
)
158 ERR("Cannot get Device Descriptor(%x)\n",status
);
159 HID_DeleteDevice(&minidriver
->minidriver
, device
);
160 HeapFree(GetProcessHeap(), 0, PDO_id
);
163 for (i
= 0; i
< descriptor
.bNumDescriptors
; i
++)
164 if (descriptor
.DescriptorList
[i
].bReportType
== HID_REPORT_DESCRIPTOR_TYPE
)
167 if (i
>= descriptor
.bNumDescriptors
)
169 ERR("No Report Descriptor found in reply\n");
170 HID_DeleteDevice(&minidriver
->minidriver
, device
);
171 HeapFree(GetProcessHeap(), 0, PDO_id
);
175 reportDescriptor
= HeapAlloc(GetProcessHeap(), 0, descriptor
.DescriptorList
[i
].wReportLength
);
176 status
= call_minidriver(IOCTL_HID_GET_REPORT_DESCRIPTOR
, device
, NULL
, 0,
177 reportDescriptor
, descriptor
.DescriptorList
[i
].wReportLength
);
178 if (status
!= STATUS_SUCCESS
)
180 ERR("Cannot get Report Descriptor(%x)\n",status
);
181 HID_DeleteDevice(&minidriver
->minidriver
, device
);
182 HeapFree(GetProcessHeap(), 0, reportDescriptor
);
183 HeapFree(GetProcessHeap(), 0, PDO_id
);
187 ext
->preparseData
= ParseDescriptor(reportDescriptor
, descriptor
.DescriptorList
[0].wReportLength
);
189 HeapFree(GetProcessHeap(), 0, reportDescriptor
);
190 if (!ext
->preparseData
)
192 ERR("Cannot parse Report Descriptor\n");
193 HID_DeleteDevice(&minidriver
->minidriver
, device
);
194 HeapFree(GetProcessHeap(), 0, PDO_id
);
195 return STATUS_NOT_SUPPORTED
;
198 ext
->information
.DescriptorSize
= ext
->preparseData
->dwSize
;
200 lstrcpyW(ext
->instance_id
, device_enumeratorW
);
201 strcatW(ext
->instance_id
, separator_W
);
202 /* Skip the original enumerator */
203 id_ptr
= strchrW(PDO_id
, '\\');
205 strcatW(ext
->instance_id
, id_ptr
);
206 HeapFree(GetProcessHeap(), 0, PDO_id
);
208 sprintfW(ext
->device_id
, device_deviceid_fmtW
, device_enumeratorW
, ext
->information
.VendorID
, ext
->information
.ProductID
);
210 HID_LinkDevice(device
);
212 ext
->poll_interval
= DEFAULT_POLL_INTERVAL
;
214 ext
->ring_buffer
= RingBuffer_Create(sizeof(HID_XFER_PACKET
) + ext
->preparseData
->caps
.InputReportByteLength
);
216 HID_StartDeviceThread(device
);
218 return STATUS_SUCCESS
;
221 void PNP_CleanupPNP(DRIVER_OBJECT
*driver
)
223 NATIVE_DEVICE
*tracked_device
, *ptr
;
225 LIST_FOR_EACH_ENTRY_SAFE(tracked_device
, ptr
, &tracked_devices
,
226 NATIVE_DEVICE
, entry
)
228 if (tracked_device
->minidriver
->DriverObject
== driver
)
230 list_remove(&tracked_device
->entry
);
231 HID_DeleteDevice(tracked_device
->minidriver
, tracked_device
->FDO
);
232 HeapFree(GetProcessHeap(), 0, tracked_device
);
237 NTSTATUS WINAPI
HID_PNP_Dispatch(DEVICE_OBJECT
*device
, IRP
*irp
)
239 NTSTATUS rc
= STATUS_NOT_SUPPORTED
;
240 IO_STACK_LOCATION
*irpsp
= IoGetCurrentIrpStackLocation(irp
);
242 TRACE("%p, %p\n", device
, irp
);
244 switch(irpsp
->MinorFunction
)
246 case IRP_MN_QUERY_ID
:
248 BASE_DEVICE_EXTENSION
*ext
= device
->DeviceExtension
;
249 WCHAR
*id
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(WCHAR
)*REGSTR_VAL_MAX_HCID_LEN
);
250 TRACE("IRP_MN_QUERY_ID[%i]\n", irpsp
->Parameters
.QueryId
.IdType
);
251 switch (irpsp
->Parameters
.QueryId
.IdType
)
253 case BusQueryHardwareIDs
:
254 case BusQueryCompatibleIDs
:
259 strcpyW(ptr
, ext
->instance_id
);
260 ptr
+= lstrlenW(ext
->instance_id
) + 1;
262 strcpyW(ptr
, ext
->device_id
);
263 ptr
+= lstrlenW(ext
->device_id
) + 1;
265 strcpyW(ptr
, device_enumeratorW
);
266 ptr
+= lstrlenW(device_enumeratorW
) + 1;
268 irp
->IoStatus
.Information
= (ULONG_PTR
)id
;
272 case BusQueryDeviceID
:
273 strcpyW(id
, ext
->device_id
);
274 irp
->IoStatus
.Information
= (ULONG_PTR
)id
;
277 case BusQueryInstanceID
:
278 strcpyW(id
, ext
->instance_id
);
279 irp
->IoStatus
.Information
= (ULONG_PTR
)id
;
282 case BusQueryDeviceSerialNumber
:
283 FIXME("BusQueryDeviceSerialNumber not implemented\n");
284 HeapFree(GetProcessHeap(), 0, id
);
291 /* Forward IRP to the minidriver */
292 minidriver
*minidriver
= find_minidriver(device
->DriverObject
);
293 return minidriver
->PNPDispatch(device
, irp
);
297 irp
->IoStatus
.u
.Status
= rc
;
298 IoCompleteRequest( irp
, IO_NO_INCREMENT
);