winebus.sys: Improve unloading the winebus driver.
[wine.git] / dlls / winebus.sys / main.c
blobfd795f127c84ba708a3c3d0614de932a10c24399
1 /*
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
20 #include "config.h"
21 #include <stdarg.h>
23 #define NONAMELESSUNION
24 #define NONAMELESSSTRUCT
26 #include "ntstatus.h"
27 #define WIN32_NO_STATUS
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winuser.h"
31 #include "winternl.h"
32 #include "winreg.h"
33 #include "setupapi.h"
34 #include "winioctl.h"
35 #include "ddk/wdm.h"
36 #include "ddk/hidport.h"
37 #include "wine/debug.h"
38 #include "wine/unicode.h"
39 #include "wine/list.h"
41 #include "bus.h"
43 WINE_DEFAULT_DEBUG_CHANNEL(plugplay);
44 WINE_DECLARE_DEBUG_CHANNEL(hid_report);
46 #define VID_MICROSOFT 0x045e
48 static const WORD PID_XBOX_CONTROLLERS[] = {
49 0x0202, /* Xbox Controller */
50 0x0285, /* Xbox Controller S */
51 0x0289, /* Xbox Controller S */
52 0x028e, /* Xbox360 Controller */
53 0x028f, /* Xbox360 Wireless Controller */
54 0x02d1, /* Xbox One Controller */
55 0x02dd, /* Xbox One Controller (Covert Forces/Firmware 2015) */
56 0x02e3, /* Xbox One Elite Controller */
57 0x02e6, /* Wireless XBox Controller Dongle */
58 0x02ea, /* Xbox One S Controller */
59 0x0719, /* Xbox 360 Wireless Adapter */
62 struct pnp_device
64 struct list entry;
65 DEVICE_OBJECT *device;
68 struct device_extension
70 struct pnp_device *pnp_device;
72 WORD vid, pid;
73 DWORD uid, version, index;
74 BOOL is_gamepad;
75 WCHAR *serial;
76 const WCHAR *busid; /* Expected to be a static constant */
78 const platform_vtbl *vtbl;
80 BYTE *last_report;
81 DWORD last_report_size;
82 BOOL last_report_read;
83 DWORD buffer_size;
84 LIST_ENTRY irp_queue;
85 CRITICAL_SECTION report_cs;
87 BYTE platform_private[1];
90 static CRITICAL_SECTION device_list_cs;
91 static CRITICAL_SECTION_DEBUG critsect_debug =
93 0, 0, &device_list_cs,
94 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
95 0, 0, { (DWORD_PTR)(__FILE__ ": device_list_cs") }
97 static CRITICAL_SECTION device_list_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
99 static struct list pnp_devset = LIST_INIT(pnp_devset);
101 static const WCHAR zero_serialW[]= {'0','0','0','0',0};
102 static const WCHAR imW[] = {'I','M',0};
103 static const WCHAR igW[] = {'I','G',0};
105 static inline WCHAR *strdupW(const WCHAR *src)
107 WCHAR *dst;
108 if (!src) return NULL;
109 dst = HeapAlloc(GetProcessHeap(), 0, (strlenW(src) + 1)*sizeof(WCHAR));
110 if (dst) strcpyW(dst, src);
111 return dst;
114 void *get_platform_private(DEVICE_OBJECT *device)
116 struct device_extension *ext = (struct device_extension *)device->DeviceExtension;
117 return ext->platform_private;
120 static DWORD get_vidpid_index(WORD vid, WORD pid)
122 struct pnp_device *ptr;
123 DWORD index = 1;
125 LIST_FOR_EACH_ENTRY(ptr, &pnp_devset, struct pnp_device, entry)
127 struct device_extension *ext = (struct device_extension *)ptr->device->DeviceExtension;
128 if (ext->vid == vid && ext->pid == pid)
129 index = max(ext->index + 1, index);
132 return index;
135 static WCHAR *get_instance_id(DEVICE_OBJECT *device)
137 static const WCHAR formatW[] = {'%','s','\\','V','i','d','_','%','0','4','x','&', 'P','i','d','_','%','0','4','x','&',
138 '%','s','_','%','i','\\','%','i','&','%','s','&','%','x',0};
139 struct device_extension *ext = (struct device_extension *)device->DeviceExtension;
140 const WCHAR *serial = ext->serial ? ext->serial : zero_serialW;
141 DWORD len = strlenW(ext->busid) + strlenW(serial) + 64;
142 WCHAR *dst;
144 if ((dst = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))))
145 sprintfW(dst, formatW, ext->busid, ext->vid, ext->pid, ext->is_gamepad ? igW : imW,
146 ext->index, ext->version, serial, ext->uid);
148 return dst;
151 static WCHAR *get_device_id(DEVICE_OBJECT *device)
153 static const WCHAR formatW[] = {'%','s','\\','V','i','d','_','%','0','4','x','&','P','i','d','_','%','0','4','x',0};
154 struct device_extension *ext = (struct device_extension *)device->DeviceExtension;
155 DWORD len = strlenW(ext->busid) + 19;
156 WCHAR *dst;
158 if ((dst = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))))
159 sprintfW(dst, formatW, ext->busid, ext->vid, ext->pid);
161 return dst;
164 static WCHAR *get_compatible_ids(DEVICE_OBJECT *device)
166 struct device_extension *ext = (struct device_extension *)device->DeviceExtension;
167 WCHAR *iid, *did, *dst, *ptr;
168 DWORD len;
170 if (!(iid = get_instance_id(device)))
171 return NULL;
173 if (!(did = get_device_id(device)))
175 HeapFree(GetProcessHeap(), 0, iid);
176 return NULL;
179 len = strlenW(iid) + strlenW(did) + strlenW(ext->busid) + 4;
180 if ((dst = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))))
182 ptr = dst;
183 strcpyW(ptr, iid);
184 ptr += strlenW(iid) + 1;
185 strcpyW(ptr, did);
186 ptr += strlenW(did) + 1;
187 strcpyW(ptr, ext->busid);
188 ptr += strlenW(ext->busid) + 1;
189 *ptr = 0;
192 HeapFree(GetProcessHeap(), 0, iid);
193 HeapFree(GetProcessHeap(), 0, did);
194 return dst;
197 DEVICE_OBJECT *bus_create_hid_device(DRIVER_OBJECT *driver, const WCHAR *busidW, WORD vid, WORD pid,
198 DWORD version, DWORD uid, const WCHAR *serialW, BOOL is_gamepad,
199 const GUID *class, const platform_vtbl *vtbl, DWORD platform_data_size)
201 static const WCHAR device_name_fmtW[] = {'\\','D','e','v','i','c','e','\\','%','s','#','%','p',0};
202 struct device_extension *ext;
203 struct pnp_device *pnp_dev;
204 DEVICE_OBJECT *device;
205 UNICODE_STRING nameW;
206 WCHAR dev_name[256];
207 HDEVINFO devinfo;
208 NTSTATUS status;
209 DWORD length;
211 TRACE("(%p, %s, %04x, %04x, %u, %u, %s, %u, %s, %p, %u)\n", driver, debugstr_w(busidW), vid, pid,
212 version, uid, debugstr_w(serialW), is_gamepad, debugstr_guid(class), vtbl, platform_data_size);
214 if (!(pnp_dev = HeapAlloc(GetProcessHeap(), 0, sizeof(*pnp_dev))))
215 return NULL;
217 sprintfW(dev_name, device_name_fmtW, busidW, pnp_dev);
218 RtlInitUnicodeString(&nameW, dev_name);
219 length = FIELD_OFFSET(struct device_extension, platform_private[platform_data_size]);
220 status = IoCreateDevice(driver, length, &nameW, 0, 0, FALSE, &device);
221 if (status)
223 FIXME("failed to create device error %x\n", status);
224 HeapFree(GetProcessHeap(), 0, pnp_dev);
225 return NULL;
228 EnterCriticalSection(&device_list_cs);
230 /* fill out device_extension struct */
231 ext = (struct device_extension *)device->DeviceExtension;
232 ext->pnp_device = pnp_dev;
233 ext->vid = vid;
234 ext->pid = pid;
235 ext->uid = uid;
236 ext->version = version;
237 ext->index = get_vidpid_index(vid, pid);
238 ext->is_gamepad = is_gamepad;
239 ext->serial = strdupW(serialW);
240 ext->busid = busidW;
241 ext->vtbl = vtbl;
242 ext->last_report = NULL;
243 ext->last_report_size = 0;
244 ext->last_report_read = TRUE;
245 ext->buffer_size = 0;
247 memset(ext->platform_private, 0, platform_data_size);
249 InitializeListHead(&ext->irp_queue);
250 InitializeCriticalSection(&ext->report_cs);
251 ext->report_cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": report_cs");
253 /* add to list of pnp devices */
254 pnp_dev->device = device;
255 list_add_tail(&pnp_devset, &pnp_dev->entry);
257 LeaveCriticalSection(&device_list_cs);
259 devinfo = SetupDiGetClassDevsW(class, NULL, NULL, DIGCF_DEVICEINTERFACE);
260 if (devinfo)
262 SP_DEVINFO_DATA data;
263 WCHAR *instance;
265 data.cbSize = sizeof(data);
266 if (!(instance = get_instance_id(device)))
267 ERR("failed to generate instance id\n");
268 else if (!SetupDiCreateDeviceInfoW(devinfo, instance, class, NULL, NULL, DICD_INHERIT_CLASSDRVS, &data))
269 ERR("failed to create device info: %x\n", GetLastError());
270 else if (!SetupDiRegisterDeviceInfo(devinfo, &data, 0, NULL, NULL, NULL))
271 ERR("failed to register device info: %x\n", GetLastError());
273 HeapFree(GetProcessHeap(), 0, instance);
274 SetupDiDestroyDeviceInfoList(devinfo);
276 else
277 ERR("failed to get ClassDevs: %x\n", GetLastError());
279 return device;
282 DEVICE_OBJECT *bus_find_hid_device(const platform_vtbl *vtbl, void *platform_dev)
284 struct pnp_device *dev;
285 DEVICE_OBJECT *ret = NULL;
287 TRACE("(%p, %p)\n", vtbl, platform_dev);
289 EnterCriticalSection(&device_list_cs);
290 LIST_FOR_EACH_ENTRY(dev, &pnp_devset, struct pnp_device, entry)
292 struct device_extension *ext = (struct device_extension *)dev->device->DeviceExtension;
293 if (ext->vtbl != vtbl) continue;
294 if (ext->vtbl->compare_platform_device(dev->device, platform_dev) == 0)
296 ret = dev->device;
297 break;
300 LeaveCriticalSection(&device_list_cs);
302 TRACE("returning %p\n", ret);
303 return ret;
306 DEVICE_OBJECT* bus_enumerate_hid_devices(const platform_vtbl *vtbl, enum_func function, void* context)
308 struct pnp_device *dev;
309 DEVICE_OBJECT *ret = NULL;
311 TRACE("(%p)\n", vtbl);
313 EnterCriticalSection(&device_list_cs);
314 LIST_FOR_EACH_ENTRY(dev, &pnp_devset, struct pnp_device, entry)
316 struct device_extension *ext = (struct device_extension *)dev->device->DeviceExtension;
317 if (ext->vtbl != vtbl) continue;
318 if (function(dev->device, context) == 0)
320 ret = dev->device;
321 break;
324 LeaveCriticalSection(&device_list_cs);
325 return ret;
328 void bus_remove_hid_device(DEVICE_OBJECT *device)
330 struct device_extension *ext = (struct device_extension *)device->DeviceExtension;
331 struct pnp_device *pnp_device = ext->pnp_device;
332 LIST_ENTRY *entry;
333 IRP *irp;
335 TRACE("(%p)\n", device);
337 EnterCriticalSection(&device_list_cs);
338 list_remove(&pnp_device->entry);
339 LeaveCriticalSection(&device_list_cs);
341 /* Cancel pending IRPs */
342 EnterCriticalSection(&ext->report_cs);
343 while ((entry = RemoveHeadList(&ext->irp_queue)) != &ext->irp_queue)
345 irp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.s.ListEntry);
346 irp->IoStatus.u.Status = STATUS_CANCELLED;
347 irp->IoStatus.Information = 0;
348 IoCompleteRequest(irp, IO_NO_INCREMENT);
350 LeaveCriticalSection(&ext->report_cs);
352 ext->report_cs.DebugInfo->Spare[0] = 0;
353 DeleteCriticalSection(&ext->report_cs);
355 HeapFree(GetProcessHeap(), 0, ext->serial);
356 HeapFree(GetProcessHeap(), 0, ext->last_report);
357 IoDeleteDevice(device);
359 /* pnp_device must be released after the device is gone */
360 HeapFree(GetProcessHeap(), 0, pnp_device);
363 static NTSTATUS handle_IRP_MN_QUERY_ID(DEVICE_OBJECT *device, IRP *irp)
365 NTSTATUS status = irp->IoStatus.u.Status;
366 IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp );
367 BUS_QUERY_ID_TYPE type = irpsp->Parameters.QueryId.IdType;
369 TRACE("(%p, %p)\n", device, irp);
371 switch (type)
373 case BusQueryHardwareIDs:
374 TRACE("BusQueryHardwareIDs\n");
375 irp->IoStatus.Information = (ULONG_PTR)get_compatible_ids(device);
376 break;
377 case BusQueryCompatibleIDs:
378 TRACE("BusQueryCompatibleIDs\n");
379 irp->IoStatus.Information = (ULONG_PTR)get_compatible_ids(device);
380 break;
381 case BusQueryDeviceID:
382 TRACE("BusQueryDeviceID\n");
383 irp->IoStatus.Information = (ULONG_PTR)get_device_id(device);
384 break;
385 case BusQueryInstanceID:
386 TRACE("BusQueryInstanceID\n");
387 irp->IoStatus.Information = (ULONG_PTR)get_instance_id(device);
388 break;
389 default:
390 FIXME("Unhandled type %08x\n", type);
391 return status;
394 status = irp->IoStatus.Information ? STATUS_SUCCESS : STATUS_NO_MEMORY;
395 return status;
398 NTSTATUS WINAPI common_pnp_dispatch(DEVICE_OBJECT *device, IRP *irp)
400 NTSTATUS status = irp->IoStatus.u.Status;
401 IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation(irp);
403 switch (irpsp->MinorFunction)
405 case IRP_MN_QUERY_DEVICE_RELATIONS:
406 TRACE("IRP_MN_QUERY_DEVICE_RELATIONS\n");
407 break;
408 case IRP_MN_QUERY_ID:
409 TRACE("IRP_MN_QUERY_ID\n");
410 status = handle_IRP_MN_QUERY_ID(device, irp);
411 irp->IoStatus.u.Status = status;
412 break;
413 case IRP_MN_QUERY_CAPABILITIES:
414 TRACE("IRP_MN_QUERY_CAPABILITIES\n");
415 break;
416 default:
417 FIXME("Unhandled function %08x\n", irpsp->MinorFunction);
418 break;
421 IoCompleteRequest(irp, IO_NO_INCREMENT);
422 return status;
425 static NTSTATUS deliver_last_report(struct device_extension *ext, DWORD buffer_length, BYTE* buffer, ULONG_PTR *out_length)
427 if (buffer_length < ext->last_report_size)
429 *out_length = 0;
430 return STATUS_BUFFER_TOO_SMALL;
432 else
434 if (ext->last_report)
435 memcpy(buffer, ext->last_report, ext->last_report_size);
436 *out_length = ext->last_report_size;
437 return STATUS_SUCCESS;
441 NTSTATUS WINAPI hid_internal_dispatch(DEVICE_OBJECT *device, IRP *irp)
443 NTSTATUS status = irp->IoStatus.u.Status;
444 IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation(irp);
445 struct device_extension *ext = (struct device_extension *)device->DeviceExtension;
447 TRACE("(%p, %p)\n", device, irp);
449 switch (irpsp->Parameters.DeviceIoControl.IoControlCode)
451 case IOCTL_HID_GET_DEVICE_ATTRIBUTES:
453 HID_DEVICE_ATTRIBUTES *attr = (HID_DEVICE_ATTRIBUTES *)irp->UserBuffer;
454 TRACE("IOCTL_HID_GET_DEVICE_ATTRIBUTES\n");
456 if (irpsp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(*attr))
458 irp->IoStatus.u.Status = status = STATUS_BUFFER_TOO_SMALL;
459 break;
462 memset(attr, 0, sizeof(*attr));
463 attr->Size = sizeof(*attr);
464 attr->VendorID = ext->vid;
465 attr->ProductID = ext->pid;
466 attr->VersionNumber = ext->version;
468 irp->IoStatus.u.Status = status = STATUS_SUCCESS;
469 irp->IoStatus.Information = sizeof(*attr);
470 break;
472 case IOCTL_HID_GET_DEVICE_DESCRIPTOR:
474 HID_DESCRIPTOR *descriptor = (HID_DESCRIPTOR *)irp->UserBuffer;
475 DWORD length;
476 TRACE("IOCTL_HID_GET_DEVICE_DESCRIPTOR\n");
478 if (irpsp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(*descriptor))
480 irp->IoStatus.u.Status = status = STATUS_BUFFER_TOO_SMALL;
481 break;
484 status = ext->vtbl->get_reportdescriptor(device, NULL, 0, &length);
485 if (status != STATUS_SUCCESS && status != STATUS_BUFFER_TOO_SMALL)
487 WARN("Failed to get platform report descriptor length\n");
488 irp->IoStatus.u.Status = status;
489 break;
492 memset(descriptor, 0, sizeof(*descriptor));
493 descriptor->bLength = sizeof(*descriptor);
494 descriptor->bDescriptorType = HID_HID_DESCRIPTOR_TYPE;
495 descriptor->bcdHID = HID_REVISION;
496 descriptor->bCountry = 0;
497 descriptor->bNumDescriptors = 1;
498 descriptor->DescriptorList[0].bReportType = HID_REPORT_DESCRIPTOR_TYPE;
499 descriptor->DescriptorList[0].wReportLength = length;
501 irp->IoStatus.u.Status = status = STATUS_SUCCESS;
502 irp->IoStatus.Information = sizeof(*descriptor);
503 break;
505 case IOCTL_HID_GET_REPORT_DESCRIPTOR:
507 DWORD length = irpsp->Parameters.DeviceIoControl.OutputBufferLength;
508 TRACE("IOCTL_HID_GET_REPORT_DESCRIPTOR\n");
510 irp->IoStatus.u.Status = status = ext->vtbl->get_reportdescriptor(device, irp->UserBuffer, length, &length);
511 irp->IoStatus.Information = length;
512 break;
514 case IOCTL_HID_GET_STRING:
516 DWORD length = irpsp->Parameters.DeviceIoControl.OutputBufferLength / sizeof(WCHAR);
517 DWORD index = (ULONG_PTR)irpsp->Parameters.DeviceIoControl.Type3InputBuffer;
518 TRACE("IOCTL_HID_GET_STRING[%08x]\n", index);
520 irp->IoStatus.u.Status = status = ext->vtbl->get_string(device, index, (WCHAR *)irp->UserBuffer, length);
521 if (status == STATUS_SUCCESS)
522 irp->IoStatus.Information = (strlenW((WCHAR *)irp->UserBuffer) + 1) * sizeof(WCHAR);
523 break;
525 case IOCTL_HID_GET_INPUT_REPORT:
527 HID_XFER_PACKET *packet = (HID_XFER_PACKET*)(irp->UserBuffer);
528 TRACE_(hid_report)("IOCTL_HID_GET_INPUT_REPORT\n");
529 EnterCriticalSection(&ext->report_cs);
530 status = ext->vtbl->begin_report_processing(device);
531 if (status != STATUS_SUCCESS)
533 irp->IoStatus.u.Status = status;
534 LeaveCriticalSection(&ext->report_cs);
535 break;
538 irp->IoStatus.u.Status = status = deliver_last_report(ext,
539 packet->reportBufferLen, packet->reportBuffer,
540 &irp->IoStatus.Information);
542 if (status == STATUS_SUCCESS)
543 packet->reportBufferLen = irp->IoStatus.Information;
544 LeaveCriticalSection(&ext->report_cs);
545 break;
547 case IOCTL_HID_READ_REPORT:
549 TRACE_(hid_report)("IOCTL_HID_READ_REPORT\n");
550 EnterCriticalSection(&ext->report_cs);
551 status = ext->vtbl->begin_report_processing(device);
552 if (status != STATUS_SUCCESS)
554 irp->IoStatus.u.Status = status;
555 LeaveCriticalSection(&ext->report_cs);
556 break;
558 if (!ext->last_report_read)
560 irp->IoStatus.u.Status = status = deliver_last_report(ext,
561 irpsp->Parameters.DeviceIoControl.OutputBufferLength,
562 irp->UserBuffer, &irp->IoStatus.Information);
563 ext->last_report_read = TRUE;
565 else
567 InsertTailList(&ext->irp_queue, &irp->Tail.Overlay.s.ListEntry);
568 status = STATUS_PENDING;
570 LeaveCriticalSection(&ext->report_cs);
571 break;
573 case IOCTL_HID_SET_OUTPUT_REPORT:
574 case IOCTL_HID_WRITE_REPORT:
576 HID_XFER_PACKET *packet = (HID_XFER_PACKET*)(irp->UserBuffer);
577 TRACE_(hid_report)("IOCTL_HID_WRITE_REPORT / IOCTL_HID_SET_OUTPUT_REPORT\n");
578 irp->IoStatus.u.Status = status = ext->vtbl->set_output_report(
579 device, packet->reportId, packet->reportBuffer,
580 packet->reportBufferLen, &irp->IoStatus.Information);
581 break;
583 case IOCTL_HID_GET_FEATURE:
585 HID_XFER_PACKET *packet = (HID_XFER_PACKET*)(irp->UserBuffer);
586 TRACE_(hid_report)("IOCTL_HID_GET_FEATURE\n");
587 irp->IoStatus.u.Status = status = ext->vtbl->get_feature_report(
588 device, packet->reportId, packet->reportBuffer,
589 packet->reportBufferLen, &irp->IoStatus.Information);
590 packet->reportBufferLen = irp->IoStatus.Information;
591 break;
593 case IOCTL_HID_SET_FEATURE:
595 HID_XFER_PACKET *packet = (HID_XFER_PACKET*)(irp->UserBuffer);
596 TRACE_(hid_report)("IOCTL_HID_SET_FEATURE\n");
597 irp->IoStatus.u.Status = status = ext->vtbl->set_feature_report(
598 device, packet->reportId, packet->reportBuffer,
599 packet->reportBufferLen, &irp->IoStatus.Information);
600 break;
602 default:
604 ULONG code = irpsp->Parameters.DeviceIoControl.IoControlCode;
605 FIXME("Unsupported ioctl %x (device=%x access=%x func=%x method=%x)\n",
606 code, code >> 16, (code >> 14) & 3, (code >> 2) & 0xfff, code & 3);
607 break;
611 if (status != STATUS_PENDING)
612 IoCompleteRequest(irp, IO_NO_INCREMENT);
614 return status;
617 void process_hid_report(DEVICE_OBJECT *device, BYTE *report, DWORD length)
619 struct device_extension *ext = (struct device_extension*)device->DeviceExtension;
620 IRP *irp;
621 LIST_ENTRY *entry;
623 if (!length || !report)
624 return;
626 EnterCriticalSection(&ext->report_cs);
627 if (length > ext->buffer_size)
629 HeapFree(GetProcessHeap(), 0, ext->last_report);
630 ext->last_report = HeapAlloc(GetProcessHeap(), 0, length);
631 if (!ext->last_report)
633 ERR_(hid_report)("Failed to alloc last report\n");
634 ext->buffer_size = 0;
635 ext->last_report_size = 0;
636 ext->last_report_read = TRUE;
637 LeaveCriticalSection(&ext->report_cs);
638 return;
640 else
641 ext->buffer_size = length;
644 memcpy(ext->last_report, report, length);
645 ext->last_report_size = length;
646 ext->last_report_read = FALSE;
648 while ((entry = RemoveHeadList(&ext->irp_queue)) != &ext->irp_queue)
650 IO_STACK_LOCATION *irpsp;
651 TRACE_(hid_report)("Processing Request\n");
652 irp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.s.ListEntry);
653 irpsp = IoGetCurrentIrpStackLocation(irp);
654 irp->IoStatus.u.Status = deliver_last_report(ext,
655 irpsp->Parameters.DeviceIoControl.OutputBufferLength,
656 irp->UserBuffer, &irp->IoStatus.Information);
657 ext->last_report_read = TRUE;
658 IoCompleteRequest(irp, IO_NO_INCREMENT);
660 LeaveCriticalSection(&ext->report_cs);
663 DWORD check_bus_option(UNICODE_STRING *registry_path, const UNICODE_STRING *option, DWORD default_value)
665 OBJECT_ATTRIBUTES attr;
666 HANDLE key;
667 DWORD output = default_value;
669 InitializeObjectAttributes(&attr, registry_path, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
670 if (NtOpenKey(&key, KEY_ALL_ACCESS, &attr) == STATUS_SUCCESS)
672 DWORD size;
673 char buffer[FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data[sizeof(DWORD)])];
675 KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION*)buffer;
677 if (NtQueryValueKey(key, option, KeyValuePartialInformation, info, sizeof(buffer), &size) == STATUS_SUCCESS)
679 if (info->Type == REG_DWORD)
680 output = *(DWORD*)info->Data;
683 NtClose(key);
686 return output;
689 BOOL is_xbox_gamepad(WORD vid, WORD pid)
691 int i;
693 if (vid != VID_MICROSOFT)
694 return FALSE;
696 for (i = 0; i < ARRAY_SIZE(PID_XBOX_CONTROLLERS); i++)
697 if (pid == PID_XBOX_CONTROLLERS[i]) return TRUE;
699 return FALSE;
702 static void WINAPI driver_unload(DRIVER_OBJECT *driver)
704 udev_driver_unload();
705 iohid_driver_unload();
706 sdl_driver_unload();
709 NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *path )
711 static const WCHAR udevW[] = {'\\','D','r','i','v','e','r','\\','U','D','E','V',0};
712 static UNICODE_STRING udev = {sizeof(udevW) - sizeof(WCHAR), sizeof(udevW), (WCHAR *)udevW};
713 static const WCHAR iohidW[] = {'\\','D','r','i','v','e','r','\\','I','O','H','I','D',0};
714 static UNICODE_STRING iohid = {sizeof(iohidW) - sizeof(WCHAR), sizeof(iohidW), (WCHAR *)iohidW};
715 static const WCHAR sdlW[] = {'\\','D','r','i','v','e','r','\\','S','D','L','J','O','Y',0};
716 static UNICODE_STRING sdl = {sizeof(sdlW) - sizeof(WCHAR), sizeof(sdlW), (WCHAR *)sdlW};
717 static const WCHAR SDL_enabledW[] = {'E','n','a','b','l','e',' ','S','D','L',0};
718 static const UNICODE_STRING SDL_enabled = {sizeof(SDL_enabledW) - sizeof(WCHAR), sizeof(SDL_enabledW), (WCHAR*)SDL_enabledW};
720 TRACE( "(%p, %s)\n", driver, debugstr_w(path->Buffer) );
722 if (check_bus_option(path, &SDL_enabled, 1))
724 if (IoCreateDriver(&sdl, sdl_driver_init) == STATUS_SUCCESS)
725 return STATUS_SUCCESS;
727 IoCreateDriver(&udev, udev_driver_init);
728 IoCreateDriver(&iohid, iohid_driver_init);
730 driver->DriverUnload = driver_unload;
732 return STATUS_SUCCESS;