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"
26 #include "wine/debug.h"
27 #include "wine/unicode.h"
28 #include "wine/list.h"
30 WINE_DEFAULT_DEBUG_CHANNEL(hid
);
32 typedef struct _NATIVE_DEVICE
{
38 HID_MINIDRIVER_REGISTRATION
*minidriver
;
42 static struct list tracked_devices
= LIST_INIT(tracked_devices
);
44 static NTSTATUS WINAPI
internalComplete(DEVICE_OBJECT
*deviceObject
, IRP
*irp
,
47 SetEvent(irp
->UserEvent
);
48 return STATUS_MORE_PROCESSING_REQUIRED
;
51 static NTSTATUS
SendDeviceIRP(DEVICE_OBJECT
* device
, IRP
*irp
)
54 IO_STACK_LOCATION
*irpsp
;
55 HANDLE event
= CreateEventA(NULL
, FALSE
, FALSE
, NULL
);
57 irp
->UserEvent
= event
;
58 irpsp
= IoGetNextIrpStackLocation(irp
);
59 irpsp
->CompletionRoutine
= internalComplete
;
60 irpsp
->Control
= SL_INVOKE_ON_SUCCESS
| SL_INVOKE_ON_ERROR
;
62 IoCallDriver(device
, irp
);
64 if (irp
->IoStatus
.u
.Status
== STATUS_PENDING
)
65 WaitForSingleObject(event
, INFINITE
);
67 status
= irp
->IoStatus
.u
.Status
;
68 IoCompleteRequest(irp
, IO_NO_INCREMENT
);
73 static NTSTATUS
PNP_SendPnPIRP(DEVICE_OBJECT
*device
, UCHAR minor
)
75 IO_STACK_LOCATION
*irpsp
;
76 IO_STATUS_BLOCK irp_status
;
78 IRP
*irp
= IoBuildSynchronousFsdRequest(IRP_MJ_PNP
, device
, NULL
, 0, NULL
, NULL
, &irp_status
);
80 irpsp
= IoGetNextIrpStackLocation(irp
);
81 irpsp
->MinorFunction
= minor
;
83 irpsp
->Parameters
.StartDevice
.AllocatedResources
= NULL
;
84 irpsp
->Parameters
.StartDevice
.AllocatedResourcesTranslated
= NULL
;
86 return SendDeviceIRP(device
, irp
);
89 static NTSTATUS
PNP_SendPowerIRP(DEVICE_OBJECT
*device
, DEVICE_POWER_STATE power
)
91 IO_STATUS_BLOCK irp_status
;
92 IO_STACK_LOCATION
*irpsp
;
94 IRP
*irp
= IoBuildSynchronousFsdRequest(IRP_MJ_POWER
, device
, NULL
, 0, NULL
, NULL
, &irp_status
);
96 irpsp
= IoGetNextIrpStackLocation(irp
);
97 irpsp
->MinorFunction
= IRP_MN_SET_POWER
;
99 irpsp
->Parameters
.Power
.Type
= DevicePowerState
;
100 irpsp
->Parameters
.Power
.State
.DeviceState
= power
;
102 return SendDeviceIRP(device
, irp
);
105 NTSTATUS WINAPI
PNP_AddDevice(DRIVER_OBJECT
*driver
, DEVICE_OBJECT
*PDO
)
107 DEVICE_OBJECT
*device
= NULL
;
109 minidriver
*minidriver
;
110 HID_DEVICE_ATTRIBUTES attr
;
111 BASE_DEVICE_EXTENSION
*ext
= NULL
;
113 WCHAR interface
[256];
114 DWORD index
= HID_STRING_ID_ISERIALNUMBER
;
115 NATIVE_DEVICE
*tracked_device
, *ptr
;
116 INT interface_index
= 1;
117 HID_DESCRIPTOR descriptor
;
118 BYTE
*reportDescriptor
;
121 static const WCHAR ig_fmtW
[] = {'I','G','_','%','i',0};
122 static const WCHAR im_fmtW
[] = {'I','M','_','%','i',0};
125 TRACE("PDO add device(%p)\n", PDO
);
126 minidriver
= find_minidriver(driver
);
128 status
= HID_CreateDevice(PDO
, &minidriver
->minidriver
, &device
);
129 if (status
!= STATUS_SUCCESS
)
131 ERR("Failed to create HID object (%x)\n",status
);
135 ext
= device
->DeviceExtension
;
136 InitializeListHead(&ext
->irp_queue
);
138 TRACE("Created device %p\n",device
);
139 status
= minidriver
->AddDevice(minidriver
->minidriver
.DriverObject
, device
);
140 if (status
!= STATUS_SUCCESS
)
142 ERR("Minidriver AddDevice failed (%x)\n",status
);
143 HID_DeleteDevice(&minidriver
->minidriver
, device
);
147 status
= PNP_SendPnPIRP(device
, IRP_MN_START_DEVICE
);
148 if (status
!= STATUS_SUCCESS
)
150 ERR("Minidriver IRP_MN_START_DEVICE failed (%x)\n",status
);
151 HID_DeleteDevice(&minidriver
->minidriver
, device
);
155 status
= call_minidriver(IOCTL_HID_GET_DEVICE_ATTRIBUTES
, device
,
156 NULL
, 0, &attr
, sizeof(attr
));
158 if (status
!= STATUS_SUCCESS
)
160 ERR("Minidriver failed to get Attributes(%x)\n",status
);
161 PNP_SendPnPIRP(device
, IRP_MN_REMOVE_DEVICE
);
162 HID_DeleteDevice(&minidriver
->minidriver
, device
);
166 ext
->information
.VendorID
= attr
.VendorID
;
167 ext
->information
.ProductID
= attr
.ProductID
;
168 ext
->information
.VersionNumber
= attr
.VersionNumber
;
169 ext
->information
.Polled
= minidriver
->minidriver
.DevicesArePolled
;
171 tracked_device
= HeapAlloc(GetProcessHeap(), 0, sizeof(*tracked_device
));
172 tracked_device
->vidpid
= MAKELONG(attr
.VendorID
, attr
.ProductID
);
173 tracked_device
->PDO
= PDO
;
174 tracked_device
->FDO
= device
;
175 tracked_device
->minidriver
= &minidriver
->minidriver
;
177 LIST_FOR_EACH_ENTRY(ptr
, &tracked_devices
, NATIVE_DEVICE
, entry
)
178 if (ptr
->vidpid
== tracked_device
->vidpid
) interface_index
++;
180 list_add_tail(&tracked_devices
, &tracked_device
->entry
);
182 status
= call_minidriver(IOCTL_HID_GET_DEVICE_DESCRIPTOR
, device
, NULL
, 0,
183 &descriptor
, sizeof(descriptor
));
184 if (status
!= STATUS_SUCCESS
)
186 ERR("Cannot get Device Descriptor(%x)\n",status
);
187 PNP_SendPnPIRP(device
, IRP_MN_REMOVE_DEVICE
);
188 HID_DeleteDevice(&minidriver
->minidriver
, device
);
191 for (i
= 0; i
< descriptor
.bNumDescriptors
; i
++)
192 if (descriptor
.DescriptorList
[i
].bReportType
== HID_REPORT_DESCRIPTOR_TYPE
)
195 if (i
>= descriptor
.bNumDescriptors
)
197 ERR("No Report Descriptor found in reply\n");
198 PNP_SendPnPIRP(device
, IRP_MN_REMOVE_DEVICE
);
199 HID_DeleteDevice(&minidriver
->minidriver
, device
);
203 reportDescriptor
= HeapAlloc(GetProcessHeap(), 0, descriptor
.DescriptorList
[i
].wReportLength
);
204 status
= call_minidriver(IOCTL_HID_GET_REPORT_DESCRIPTOR
, device
, NULL
, 0,
205 reportDescriptor
, descriptor
.DescriptorList
[i
].wReportLength
);
206 if (status
!= STATUS_SUCCESS
)
208 ERR("Cannot get Report Descriptor(%x)\n",status
);
209 HID_DeleteDevice(&minidriver
->minidriver
, device
);
210 HeapFree(GetProcessHeap(), 0, reportDescriptor
);
214 ext
->preparseData
= ParseDescriptor(reportDescriptor
, descriptor
.DescriptorList
[0].wReportLength
);
216 HeapFree(GetProcessHeap(), 0, reportDescriptor
);
217 if (!ext
->preparseData
)
219 ERR("Cannot parse Report Descriptor\n");
220 HID_DeleteDevice(&minidriver
->minidriver
, device
);
221 return STATUS_NOT_SUPPORTED
;
224 ext
->information
.DescriptorSize
= ext
->preparseData
->dwSize
;
227 status
= call_minidriver(IOCTL_HID_GET_STRING
, device
,
228 &index
, sizeof(DWORD
), serial
, sizeof(serial
));
232 static const WCHAR wZeroSerial
[]= {'0','0','0','0',0};
233 lstrcpyW(serial
, wZeroSerial
);
236 if (ext
->preparseData
->caps
.UsagePage
== HID_USAGE_PAGE_GENERIC
&&
237 (ext
->preparseData
->caps
.Usage
== HID_USAGE_GENERIC_GAMEPAD
||
238 ext
->preparseData
->caps
.Usage
== HID_USAGE_GENERIC_JOYSTICK
))
239 sprintfW(interface
, ig_fmtW
, interface_index
);
241 sprintfW(interface
, im_fmtW
, interface_index
);
243 HID_LinkDevice(device
, serial
, interface
);
245 ext
->poll_interval
= DEFAULT_POLL_INTERVAL
;
247 ext
->ring_buffer
= RingBuffer_Create(sizeof(HID_XFER_PACKET
) + ext
->preparseData
->caps
.InputReportByteLength
);
249 HID_StartDeviceThread(device
);
250 PNP_SendPowerIRP(device
, PowerDeviceD0
);
252 return STATUS_SUCCESS
;
255 void PNP_CleanupPNP(DRIVER_OBJECT
*driver
)
257 NATIVE_DEVICE
*tracked_device
, *ptr
;
259 LIST_FOR_EACH_ENTRY_SAFE(tracked_device
, ptr
, &tracked_devices
,
260 NATIVE_DEVICE
, entry
)
262 if (tracked_device
->minidriver
->DriverObject
== driver
)
264 list_remove(&tracked_device
->entry
);
265 PNP_SendPowerIRP(tracked_device
->FDO
, PowerDeviceD3
);
266 PNP_SendPnPIRP(tracked_device
->FDO
, IRP_MN_REMOVE_DEVICE
);
267 HID_DeleteDevice(tracked_device
->minidriver
, tracked_device
->FDO
);
268 HeapFree(GetProcessHeap(), 0, tracked_device
);