2 * WINE Platform native bus driver
4 * Copyright 2016 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
23 #define NONAMELESSUNION
26 #define WIN32_NO_STATUS
35 #include "ddk/hidport.h"
36 #include "wine/debug.h"
37 #include "wine/unicode.h"
38 #include "wine/list.h"
42 WINE_DEFAULT_DEBUG_CHANNEL(plugplay
);
47 DEVICE_OBJECT
*device
;
50 struct device_extension
52 struct pnp_device
*pnp_device
;
55 DWORD uid
, version
, index
;
58 const WCHAR
*busid
; /* Expected to be a static constant */
60 const platform_vtbl
*vtbl
;
61 BYTE platform_private
[1];
64 static CRITICAL_SECTION device_list_cs
;
65 static CRITICAL_SECTION_DEBUG critsect_debug
=
67 0, 0, &device_list_cs
,
68 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
69 0, 0, { (DWORD_PTR
)(__FILE__
": device_list_cs") }
71 static CRITICAL_SECTION device_list_cs
= { &critsect_debug
, -1, 0, 0, 0, 0 };
73 static struct list pnp_devset
= LIST_INIT(pnp_devset
);
75 static const WCHAR zero_serialW
[]= {'0','0','0','0',0};
76 static const WCHAR imW
[] = {'I','M',0};
77 static const WCHAR igW
[] = {'I','G',0};
79 static inline WCHAR
*strdupW(const WCHAR
*src
)
82 if (!src
) return NULL
;
83 dst
= HeapAlloc(GetProcessHeap(), 0, (strlenW(src
) + 1)*sizeof(WCHAR
));
84 if (dst
) strcpyW(dst
, src
);
88 void *get_platform_private(DEVICE_OBJECT
*device
)
90 struct device_extension
*ext
= (struct device_extension
*)device
->DeviceExtension
;
91 return ext
->platform_private
;
94 static DWORD
get_vidpid_index(WORD vid
, WORD pid
)
96 struct pnp_device
*ptr
;
99 LIST_FOR_EACH_ENTRY(ptr
, &pnp_devset
, struct pnp_device
, entry
)
101 struct device_extension
*ext
= (struct device_extension
*)ptr
->device
->DeviceExtension
;
102 if (ext
->vid
== vid
&& ext
->pid
== pid
)
103 index
= max(ext
->index
+ 1, index
);
109 static WCHAR
*get_instance_id(DEVICE_OBJECT
*device
)
111 static const WCHAR formatW
[] = {'%','s','\\','V','i','d','_','%','0','4','x','&', 'P','i','d','_','%','0','4','x','&',
112 '%','s','_','%','i','\\','%','i','&','%','s','&','%','x',0};
113 struct device_extension
*ext
= (struct device_extension
*)device
->DeviceExtension
;
114 const WCHAR
*serial
= ext
->serial
? ext
->serial
: zero_serialW
;
115 DWORD len
= strlenW(ext
->busid
) + strlenW(serial
) + 64;
118 if ((dst
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
))))
119 sprintfW(dst
, formatW
, ext
->busid
, ext
->vid
, ext
->pid
, ext
->is_gamepad
? igW
: imW
,
120 ext
->index
, ext
->version
, serial
, ext
->uid
);
125 static WCHAR
*get_device_id(DEVICE_OBJECT
*device
)
127 static const WCHAR formatW
[] = {'%','s','\\','V','i','d','_','%','0','4','x','&','P','i','d','_','%','0','4','x',0};
128 struct device_extension
*ext
= (struct device_extension
*)device
->DeviceExtension
;
129 DWORD len
= strlenW(ext
->busid
) + 19;
132 if ((dst
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
))))
133 sprintfW(dst
, formatW
, ext
->busid
, ext
->vid
, ext
->pid
);
138 static WCHAR
*get_compatible_ids(DEVICE_OBJECT
*device
)
140 struct device_extension
*ext
= (struct device_extension
*)device
->DeviceExtension
;
141 WCHAR
*iid
, *did
, *dst
, *ptr
;
144 if (!(iid
= get_instance_id(device
)))
147 if (!(did
= get_device_id(device
)))
149 HeapFree(GetProcessHeap(), 0, iid
);
153 len
= strlenW(iid
) + strlenW(did
) + strlenW(ext
->busid
) + 4;
154 if ((dst
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
))))
158 ptr
+= strlenW(iid
) + 1;
160 ptr
+= strlenW(did
) + 1;
161 strcpyW(ptr
, ext
->busid
);
162 ptr
+= strlenW(ext
->busid
) + 1;
166 HeapFree(GetProcessHeap(), 0, iid
);
167 HeapFree(GetProcessHeap(), 0, did
);
171 DEVICE_OBJECT
*bus_create_hid_device(DRIVER_OBJECT
*driver
, const WCHAR
*busidW
, WORD vid
, WORD pid
,
172 DWORD version
, DWORD uid
, const WCHAR
*serialW
, BOOL is_gamepad
,
173 const GUID
*class, const platform_vtbl
*vtbl
, DWORD platform_data_size
)
175 static const WCHAR device_name_fmtW
[] = {'\\','D','e','v','i','c','e','\\','%','s','#','%','p',0};
176 struct device_extension
*ext
;
177 struct pnp_device
*pnp_dev
;
178 DEVICE_OBJECT
*device
;
179 UNICODE_STRING nameW
;
185 TRACE("(%p, %s, %04x, %04x, %u, %u, %s, %u, %s, %p, %u)\n", driver
, debugstr_w(busidW
), vid
, pid
,
186 version
, uid
, debugstr_w(serialW
), is_gamepad
, debugstr_guid(class), vtbl
, platform_data_size
);
188 if (!(pnp_dev
= HeapAlloc(GetProcessHeap(), 0, sizeof(*pnp_dev
))))
191 sprintfW(dev_name
, device_name_fmtW
, busidW
, pnp_dev
);
192 RtlInitUnicodeString(&nameW
, dev_name
);
193 length
= FIELD_OFFSET(struct device_extension
, platform_private
[platform_data_size
]);
194 status
= IoCreateDevice(driver
, length
, &nameW
, 0, 0, FALSE
, &device
);
197 FIXME("failed to create device error %x\n", status
);
198 HeapFree(GetProcessHeap(), 0, pnp_dev
);
202 EnterCriticalSection(&device_list_cs
);
204 /* fill out device_extension struct */
205 ext
= (struct device_extension
*)device
->DeviceExtension
;
206 ext
->pnp_device
= pnp_dev
;
210 ext
->version
= version
;
211 ext
->index
= get_vidpid_index(vid
, pid
);
212 ext
->is_gamepad
= is_gamepad
;
213 ext
->serial
= strdupW(serialW
);
217 /* add to list of pnp devices */
218 pnp_dev
->device
= device
;
219 list_add_tail(&pnp_devset
, &pnp_dev
->entry
);
221 LeaveCriticalSection(&device_list_cs
);
223 devinfo
= SetupDiGetClassDevsW(class, NULL
, NULL
, DIGCF_DEVICEINTERFACE
);
226 SP_DEVINFO_DATA data
;
229 data
.cbSize
= sizeof(data
);
230 if (!(instance
= get_instance_id(device
)))
231 ERR("failed to generate instance id\n");
232 else if (!SetupDiCreateDeviceInfoW(devinfo
, instance
, class, NULL
, NULL
, DICD_INHERIT_CLASSDRVS
, &data
))
233 ERR("failed to create device info: %x\n", GetLastError());
234 else if (!SetupDiRegisterDeviceInfo(devinfo
, &data
, 0, NULL
, NULL
, NULL
))
235 ERR("failed to register device info: %x\n", GetLastError());
237 HeapFree(GetProcessHeap(), 0, instance
);
238 SetupDiDestroyDeviceInfoList(devinfo
);
241 ERR("failed to get ClassDevs: %x\n", GetLastError());
246 DEVICE_OBJECT
*bus_find_hid_device(const platform_vtbl
*vtbl
, void *platform_dev
)
248 struct pnp_device
*dev
;
249 DEVICE_OBJECT
*ret
= NULL
;
251 TRACE("(%p, %p)\n", vtbl
, platform_dev
);
253 EnterCriticalSection(&device_list_cs
);
254 LIST_FOR_EACH_ENTRY(dev
, &pnp_devset
, struct pnp_device
, entry
)
256 struct device_extension
*ext
= (struct device_extension
*)dev
->device
->DeviceExtension
;
257 if (ext
->vtbl
!= vtbl
) continue;
258 if (ext
->vtbl
->compare_platform_device(dev
->device
, platform_dev
) == 0)
264 LeaveCriticalSection(&device_list_cs
);
266 TRACE("returning %p\n", ret
);
270 void bus_remove_hid_device(DEVICE_OBJECT
*device
)
272 struct device_extension
*ext
= (struct device_extension
*)device
->DeviceExtension
;
273 struct pnp_device
*pnp_device
= ext
->pnp_device
;
275 TRACE("(%p)\n", device
);
277 EnterCriticalSection(&device_list_cs
);
278 list_remove(&pnp_device
->entry
);
279 LeaveCriticalSection(&device_list_cs
);
281 IoInvalidateDeviceRelations(device
, RemovalRelations
);
282 HeapFree(GetProcessHeap(), 0, ext
->serial
);
283 IoDeleteDevice(device
);
285 /* pnp_device must be released after the device is gone */
286 HeapFree(GetProcessHeap(), 0, pnp_device
);
289 static NTSTATUS
handle_IRP_MN_QUERY_ID(DEVICE_OBJECT
*device
, IRP
*irp
)
291 NTSTATUS status
= irp
->IoStatus
.u
.Status
;
292 IO_STACK_LOCATION
*irpsp
= IoGetCurrentIrpStackLocation( irp
);
293 BUS_QUERY_ID_TYPE type
= irpsp
->Parameters
.QueryId
.IdType
;
295 TRACE("(%p, %p)\n", device
, irp
);
299 case BusQueryHardwareIDs
:
300 TRACE("BusQueryHardwareIDs\n");
301 irp
->IoStatus
.Information
= (ULONG_PTR
)get_compatible_ids(device
);
303 case BusQueryCompatibleIDs
:
304 TRACE("BusQueryCompatibleIDs\n");
305 irp
->IoStatus
.Information
= (ULONG_PTR
)get_compatible_ids(device
);
307 case BusQueryDeviceID
:
308 TRACE("BusQueryDeviceID\n");
309 irp
->IoStatus
.Information
= (ULONG_PTR
)get_device_id(device
);
311 case BusQueryInstanceID
:
312 TRACE("BusQueryInstanceID\n");
313 irp
->IoStatus
.Information
= (ULONG_PTR
)get_instance_id(device
);
316 FIXME("Unhandled type %08x\n", type
);
320 status
= irp
->IoStatus
.Information
? STATUS_SUCCESS
: STATUS_NO_MEMORY
;
324 NTSTATUS WINAPI
common_pnp_dispatch(DEVICE_OBJECT
*device
, IRP
*irp
)
326 NTSTATUS status
= irp
->IoStatus
.u
.Status
;
327 IO_STACK_LOCATION
*irpsp
= IoGetCurrentIrpStackLocation(irp
);
329 switch (irpsp
->MinorFunction
)
331 case IRP_MN_QUERY_DEVICE_RELATIONS
:
332 TRACE("IRP_MN_QUERY_DEVICE_RELATIONS\n");
334 case IRP_MN_QUERY_ID
:
335 TRACE("IRP_MN_QUERY_ID\n");
336 status
= handle_IRP_MN_QUERY_ID(device
, irp
);
337 irp
->IoStatus
.u
.Status
= status
;
339 case IRP_MN_QUERY_CAPABILITIES
:
340 TRACE("IRP_MN_QUERY_CAPABILITIES\n");
343 FIXME("Unhandled function %08x\n", irpsp
->MinorFunction
);
347 IoCompleteRequest(irp
, IO_NO_INCREMENT
);
351 NTSTATUS WINAPI
hid_internal_dispatch(DEVICE_OBJECT
*device
, IRP
*irp
)
353 NTSTATUS status
= irp
->IoStatus
.u
.Status
;
354 IO_STACK_LOCATION
*irpsp
= IoGetCurrentIrpStackLocation(irp
);
355 struct device_extension
*extension
= (struct device_extension
*)device
->DeviceExtension
;
357 TRACE("(%p, %p)\n", device
, irp
);
359 switch (irpsp
->Parameters
.DeviceIoControl
.IoControlCode
)
361 case IOCTL_HID_GET_DEVICE_ATTRIBUTES
:
363 HID_DEVICE_ATTRIBUTES
*attr
= (HID_DEVICE_ATTRIBUTES
*)irp
->UserBuffer
;
364 TRACE("IOCTL_HID_GET_DEVICE_ATTRIBUTES\n");
366 if (irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
< sizeof(*attr
))
368 irp
->IoStatus
.u
.Status
= status
= STATUS_BUFFER_TOO_SMALL
;
372 memset(attr
, 0, sizeof(*attr
));
373 attr
->Size
= sizeof(HID_DEVICE_ATTRIBUTES
);
374 attr
->VendorID
= extension
->vid
;
375 attr
->ProductID
= extension
->pid
;
376 attr
->VersionNumber
= extension
->version
;
377 irp
->IoStatus
.u
.Status
= status
= STATUS_SUCCESS
;
378 irp
->IoStatus
.Information
= sizeof(HID_DEVICE_ATTRIBUTES
);
383 ULONG code
= irpsp
->Parameters
.DeviceIoControl
.IoControlCode
;
384 FIXME("Unsupported ioctl %x (device=%x access=%x func=%x method=%x)\n",
385 code
, code
>> 16, (code
>> 14) & 3, (code
>> 2) & 0xfff, code
& 3);
390 IoCompleteRequest(irp
, IO_NO_INCREMENT
);
395 NTSTATUS WINAPI
DriverEntry( DRIVER_OBJECT
*driver
, UNICODE_STRING
*path
)
397 static const WCHAR udevW
[] = {'\\','D','r','i','v','e','r','\\','U','D','E','V',0};
398 static UNICODE_STRING udev
= {sizeof(udevW
) - sizeof(WCHAR
), sizeof(udevW
), (WCHAR
*)udevW
};
400 TRACE( "(%p, %s)\n", driver
, debugstr_w(path
->Buffer
) );
402 IoCreateDriver(&udev
, udev_driver_init
);
404 return STATUS_SUCCESS
;