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"
28 #include "wine/debug.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};
36 static NTSTATUS WINAPI
internalComplete(DEVICE_OBJECT
*deviceObject
, IRP
*irp
,
39 HANDLE event
= context
;
41 return STATUS_MORE_PROCESSING_REQUIRED
;
44 static NTSTATUS
get_device_id(DEVICE_OBJECT
*device
, BUS_QUERY_ID_TYPE type
, WCHAR
*id
)
47 IO_STACK_LOCATION
*irpsp
;
48 IO_STATUS_BLOCK irp_status
;
52 irp
= IoBuildSynchronousFsdRequest(IRP_MJ_PNP
, device
, NULL
, 0, NULL
, NULL
, &irp_status
);
54 return STATUS_NO_MEMORY
;
56 event
= CreateEventA(NULL
, FALSE
, FALSE
, NULL
);
57 irpsp
= IoGetNextIrpStackLocation(irp
);
58 irpsp
->MinorFunction
= IRP_MN_QUERY_ID
;
59 irpsp
->Parameters
.QueryId
.IdType
= type
;
61 IoSetCompletionRoutine(irp
, internalComplete
, event
, TRUE
, TRUE
, TRUE
);
62 status
= IoCallDriver(device
, irp
);
63 if (status
== STATUS_PENDING
)
64 WaitForSingleObject(event
, INFINITE
);
66 lstrcpyW(id
, (WCHAR
*)irp
->IoStatus
.Information
);
67 ExFreePool( (WCHAR
*)irp
->IoStatus
.Information
);
68 status
= irp
->IoStatus
.u
.Status
;
69 IoCompleteRequest(irp
, IO_NO_INCREMENT
);
75 NTSTATUS WINAPI
PNP_AddDevice(DRIVER_OBJECT
*driver
, DEVICE_OBJECT
*PDO
)
77 WCHAR device_id
[MAX_DEVICE_ID_LEN
], instance_id
[MAX_DEVICE_ID_LEN
];
79 DEVICE_OBJECT
*device
= NULL
;
81 minidriver
*minidriver
;
82 HID_DEVICE_ATTRIBUTES attr
;
83 BASE_DEVICE_EXTENSION
*ext
= NULL
;
84 HID_DESCRIPTOR descriptor
;
85 BYTE
*reportDescriptor
;
88 if ((status
= get_device_id(PDO
, BusQueryDeviceID
, device_id
)))
90 ERR("Failed to get PDO device id, status %#x.\n", status
);
94 if ((status
= get_device_id(PDO
, BusQueryInstanceID
, instance_id
)))
96 ERR("Failed to get PDO instance id, status %#x.\n", status
);
100 TRACE("Adding device to PDO %p, id %s\\%s.\n", PDO
, debugstr_w(device_id
), debugstr_w(instance_id
));
101 minidriver
= find_minidriver(driver
);
103 hiddev
= HeapAlloc(GetProcessHeap(), 0, sizeof(*hiddev
));
105 return STATUS_NO_MEMORY
;
107 status
= HID_CreateDevice(PDO
, &minidriver
->minidriver
, &hiddev
->device
);
108 if (status
!= STATUS_SUCCESS
)
110 ERR("Failed to create HID object (%x)\n",status
);
111 HeapFree(GetProcessHeap(), 0, hiddev
);
114 device
= hiddev
->device
;
116 ext
= device
->DeviceExtension
;
117 InitializeListHead(&ext
->irp_queue
);
118 KeInitializeSpinLock(&ext
->irp_queue_lock
);
120 TRACE("Created device %p\n",device
);
121 status
= minidriver
->AddDevice(minidriver
->minidriver
.DriverObject
, device
);
122 if (status
!= STATUS_SUCCESS
)
124 ERR("Minidriver AddDevice failed (%x)\n",status
);
125 HID_DeleteDevice(device
);
126 HeapFree(GetProcessHeap(), 0, hiddev
);
130 status
= call_minidriver(IOCTL_HID_GET_DEVICE_ATTRIBUTES
, device
,
131 NULL
, 0, &attr
, sizeof(attr
));
133 if (status
!= STATUS_SUCCESS
)
135 ERR("Minidriver failed to get Attributes(%x)\n",status
);
136 HID_DeleteDevice(device
);
137 HeapFree(GetProcessHeap(), 0, hiddev
);
141 ext
->information
.VendorID
= attr
.VendorID
;
142 ext
->information
.ProductID
= attr
.ProductID
;
143 ext
->information
.VersionNumber
= attr
.VersionNumber
;
144 ext
->information
.Polled
= minidriver
->minidriver
.DevicesArePolled
;
146 status
= call_minidriver(IOCTL_HID_GET_DEVICE_DESCRIPTOR
, device
, NULL
, 0,
147 &descriptor
, sizeof(descriptor
));
148 if (status
!= STATUS_SUCCESS
)
150 ERR("Cannot get Device Descriptor(%x)\n",status
);
151 HID_DeleteDevice(device
);
152 HeapFree(GetProcessHeap(), 0, hiddev
);
155 for (i
= 0; i
< descriptor
.bNumDescriptors
; i
++)
156 if (descriptor
.DescriptorList
[i
].bReportType
== HID_REPORT_DESCRIPTOR_TYPE
)
159 if (i
>= descriptor
.bNumDescriptors
)
161 ERR("No Report Descriptor found in reply\n");
162 HID_DeleteDevice(device
);
163 HeapFree(GetProcessHeap(), 0, hiddev
);
167 reportDescriptor
= HeapAlloc(GetProcessHeap(), 0, descriptor
.DescriptorList
[i
].wReportLength
);
168 status
= call_minidriver(IOCTL_HID_GET_REPORT_DESCRIPTOR
, device
, NULL
, 0,
169 reportDescriptor
, descriptor
.DescriptorList
[i
].wReportLength
);
170 if (status
!= STATUS_SUCCESS
)
172 ERR("Cannot get Report Descriptor(%x)\n",status
);
173 HID_DeleteDevice(device
);
174 HeapFree(GetProcessHeap(), 0, reportDescriptor
);
175 HeapFree(GetProcessHeap(), 0, hiddev
);
179 ext
->preparseData
= ParseDescriptor(reportDescriptor
, descriptor
.DescriptorList
[0].wReportLength
);
181 HeapFree(GetProcessHeap(), 0, reportDescriptor
);
182 if (!ext
->preparseData
)
184 ERR("Cannot parse Report Descriptor\n");
185 HID_DeleteDevice(device
);
186 HeapFree(GetProcessHeap(), 0, hiddev
);
187 return STATUS_NOT_SUPPORTED
;
190 list_add_tail(&(minidriver
->device_list
), &hiddev
->entry
);
192 ext
->information
.DescriptorSize
= ext
->preparseData
->dwSize
;
194 lstrcpyW(ext
->instance_id
, instance_id
);
196 lstrcpyW(ext
->device_id
, device_enumeratorW
);
197 lstrcatW(ext
->device_id
, separator_W
);
198 lstrcatW(ext
->device_id
, wcschr(device_id
, '\\') + 1);
200 HID_LinkDevice(device
);
202 ext
->poll_interval
= DEFAULT_POLL_INTERVAL
;
204 ext
->ring_buffer
= RingBuffer_Create(sizeof(HID_XFER_PACKET
) + ext
->preparseData
->caps
.InputReportByteLength
);
206 HID_StartDeviceThread(device
);
208 return STATUS_SUCCESS
;
211 NTSTATUS
PNP_RemoveDevice(minidriver
*minidriver
, DEVICE_OBJECT
*device
, IRP
*irp
)
213 BASE_DEVICE_EXTENSION
*ext
= device
->DeviceExtension
;
215 NTSTATUS rc
= STATUS_NOT_SUPPORTED
;
217 rc
= IoSetDeviceInterfaceState(&ext
->link_name
, FALSE
);
220 FIXME("failed to disable interface %x\n", rc
);
225 IoSetDeviceInterfaceState(&ext
->mouse_link_name
, FALSE
);
228 rc
= minidriver
->PNPDispatch(device
, irp
);
229 HID_DeleteDevice(device
);
230 LIST_FOR_EACH_ENTRY(hiddev
, &minidriver
->device_list
, hid_device
, entry
)
232 if (hiddev
->device
== device
)
234 list_remove(&hiddev
->entry
);
235 HeapFree(GetProcessHeap(), 0, hiddev
);
242 NTSTATUS WINAPI
HID_PNP_Dispatch(DEVICE_OBJECT
*device
, IRP
*irp
)
244 NTSTATUS rc
= STATUS_NOT_SUPPORTED
;
245 IO_STACK_LOCATION
*irpsp
= IoGetCurrentIrpStackLocation(irp
);
246 minidriver
*minidriver
= find_minidriver(device
->DriverObject
);
248 TRACE("%p, %p\n", device
, irp
);
250 switch(irpsp
->MinorFunction
)
252 case IRP_MN_QUERY_ID
:
254 BASE_DEVICE_EXTENSION
*ext
= device
->DeviceExtension
;
255 WCHAR
*id
= ExAllocatePool(PagedPool
, sizeof(WCHAR
) * REGSTR_VAL_MAX_HCID_LEN
);
256 TRACE("IRP_MN_QUERY_ID[%i]\n", irpsp
->Parameters
.QueryId
.IdType
);
257 switch (irpsp
->Parameters
.QueryId
.IdType
)
259 case BusQueryHardwareIDs
:
260 case BusQueryCompatibleIDs
:
264 /* Device instance ID */
265 lstrcpyW(ptr
, ext
->device_id
);
266 ptr
+= lstrlenW(ext
->device_id
);
267 lstrcpyW(ptr
, separator_W
);
269 lstrcpyW(ptr
, ext
->instance_id
);
270 ptr
+= lstrlenW(ext
->instance_id
) + 1;
272 lstrcpyW(ptr
, ext
->device_id
);
273 ptr
+= lstrlenW(ext
->device_id
) + 1;
275 lstrcpyW(ptr
, device_enumeratorW
);
276 ptr
+= lstrlenW(device_enumeratorW
) + 1;
278 irp
->IoStatus
.Information
= (ULONG_PTR
)id
;
282 case BusQueryDeviceID
:
283 lstrcpyW(id
, ext
->device_id
);
284 irp
->IoStatus
.Information
= (ULONG_PTR
)id
;
287 case BusQueryInstanceID
:
288 lstrcpyW(id
, ext
->instance_id
);
289 irp
->IoStatus
.Information
= (ULONG_PTR
)id
;
292 case BusQueryDeviceSerialNumber
:
293 FIXME("BusQueryDeviceSerialNumber not implemented\n");
299 case IRP_MN_START_DEVICE
:
301 BASE_DEVICE_EXTENSION
*ext
= device
->DeviceExtension
;
303 rc
= minidriver
->PNPDispatch(device
, irp
);
305 IoSetDeviceInterfaceState(&ext
->link_name
, TRUE
);
307 IoSetDeviceInterfaceState(&ext
->mouse_link_name
, TRUE
);
310 case IRP_MN_REMOVE_DEVICE
:
312 return PNP_RemoveDevice(minidriver
, device
, irp
);
316 /* Forward IRP to the minidriver */
317 return minidriver
->PNPDispatch(device
, irp
);
321 irp
->IoStatus
.u
.Status
= rc
;
322 IoCompleteRequest( irp
, IO_NO_INCREMENT
);