From: Aric Stewart Subject: [2/12]hidclass.sys: Add Plug-and-play and AddDevice Message-Id: <55A4FEAD.20505@codeweavers.com> Date: Tue, 14 Jul 2015 07:21:01 -0500 --- dlls/hidclass.sys/Makefile.in | 3 +- dlls/hidclass.sys/hid.h | 9 +++ dlls/hidclass.sys/main.c | 50 ++++++++++++- dlls/hidclass.sys/pnp.c | 164 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 224 insertions(+), 2 deletions(-) create mode 100644 dlls/hidclass.sys/pnp.c diff --git a/dlls/hidclass.sys/Makefile.in b/dlls/hidclass.sys/Makefile.in index 6b474f6..c5c9eb7 100644 --- a/dlls/hidclass.sys/Makefile.in +++ b/dlls/hidclass.sys/Makefile.in @@ -5,4 +5,5 @@ DELAYIMPORTS = setupapi hid C_SRCS = \ device.c \ - main.c + main.c \ + pnp.c diff --git a/dlls/hidclass.sys/hid.h b/dlls/hidclass.sys/hid.h index d7c16ee..9b700c3 100644 --- a/dlls/hidclass.sys/hid.h +++ b/dlls/hidclass.sys/hid.h @@ -57,9 +57,18 @@ typedef struct _minidriver HID_MINIDRIVER_REGISTRATION minidriver; PDRIVER_UNLOAD DriverUnload; + + void *AddDevice; } minidriver; +NTSTATUS call_minidriver(ULONG code, DEVICE_OBJECT *device, void *in_buff, ULONG in_size, void *out_buff, ULONG out_size) DECLSPEC_HIDDEN; +minidriver* find_minidriver(DRIVER_OBJECT* driver) DECLSPEC_HIDDEN; + /* Internal device functions */ NTSTATUS HID_CreateDevice(void *native_device, HID_MINIDRIVER_REGISTRATION *driver, DEVICE_OBJECT **device) DECLSPEC_HIDDEN; NTSTATUS HID_LinkDevice(DEVICE_OBJECT *device, LPCWSTR serial, LPCWSTR index) DECLSPEC_HIDDEN; void HID_DeleteDevice(HID_MINIDRIVER_REGISTRATION *driver, DEVICE_OBJECT *device) DECLSPEC_HIDDEN; + +/* Pseudo-Plug and Play support*/ +NTSTATUS WINAPI PNP_AddDevice(DRIVER_OBJECT *driver, void* native) DECLSPEC_HIDDEN; +void PNP_CleanupPNP() DECLSPEC_HIDDEN; diff --git a/dlls/hidclass.sys/main.c b/dlls/hidclass.sys/main.c index e4844862..a58d9834 100644 --- a/dlls/hidclass.sys/main.c +++ b/dlls/hidclass.sys/main.c @@ -30,7 +30,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(hid); static struct list minidriver_list = LIST_INIT(minidriver_list); -static minidriver* find_minidriver(DRIVER_OBJECT *driver) +minidriver* find_minidriver(DRIVER_OBJECT *driver) { minidriver *md; LIST_FOR_EACH_ENTRY(md, &minidriver_list, minidriver, entry) @@ -51,6 +51,7 @@ static VOID WINAPI UnloadDriver(DRIVER_OBJECT *driver) { if (md->DriverUnload) md->DriverUnload(md->minidriver.DriverObject); + PNP_CleanupPNP(md->minidriver.DriverObject); list_remove(&md->entry); HeapFree( GetProcessHeap(), 0, md ); } @@ -67,8 +68,55 @@ NTSTATUS WINAPI HidRegisterMinidriver(HID_MINIDRIVER_REGISTRATION *registration) driver->DriverUnload = registration->DriverObject->DriverUnload; registration->DriverObject->DriverUnload = UnloadDriver; + driver->AddDevice = registration->DriverObject->DriverExtension->AddDevice; + registration->DriverObject->DriverExtension->AddDevice = PNP_AddDevice; + driver->minidriver = *registration; list_add_tail(&minidriver_list, &driver->entry); return STATUS_SUCCESS; } + +static NTSTATUS WINAPI internalComplete(DEVICE_OBJECT *deviceObject, IRP *irp, + void *context ) +{ + SetEvent(irp->UserEvent); + return STATUS_MORE_PROCESSING_REQUIRED; +} + +NTSTATUS call_minidriver(ULONG code, DEVICE_OBJECT *device, void *in_buff, ULONG in_size, void *out_buff, ULONG out_size) +{ + IRP *irp; + IO_STATUS_BLOCK irp_status; + IO_STACK_LOCATION *irpsp; + NTSTATUS status; + void *buffer = NULL; + + HANDLE event = CreateEventA(NULL, FALSE, FALSE, NULL); + + if (out_size) + { + buffer = HeapAlloc(GetProcessHeap(), 0, out_size); + memcpy(buffer, out_buff, out_size); + } + + irp = IoBuildDeviceIoControlRequest(code, device, in_buff, in_size, + buffer, out_size, TRUE, event, &irp_status); + + irpsp = IoGetNextIrpStackLocation(irp); + irpsp->CompletionRoutine = internalComplete; + irpsp->Control = SL_INVOKE_ON_SUCCESS | SL_INVOKE_ON_ERROR; + + IoCallDriver(device, irp); + + if (irp->IoStatus.u.Status == STATUS_PENDING) + WaitForSingleObject(event, INFINITE); + + memcpy(out_buff, buffer, out_size); + status = irp->IoStatus.u.Status; + + IoCompleteRequest(irp, IO_NO_INCREMENT ); + CloseHandle(event); + + return status; +} diff --git a/dlls/hidclass.sys/pnp.c b/dlls/hidclass.sys/pnp.c new file mode 100644 index 0000000..e1e5962 --- /dev/null +++ b/dlls/hidclass.sys/pnp.c @@ -0,0 +1,164 @@ +/* + * WINE HID Pseudo-Plug and Play support + * + * Copyright 2015 Aric Stewart + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#define NONAMELESSUNION +#include +#include +#include "hid.h" +#include "ddk/hidtypes.h" +#include "wine/debug.h" +#include "wine/unicode.h" +#include "wine/list.h" + +WINE_DEFAULT_DEBUG_CHANNEL(hid); + +typedef struct _NATIVE_DEVICE { + struct list entry; + + DWORD vidpid; + void *native_device; + DEVICE_OBJECT *device; + HID_MINIDRIVER_REGISTRATION *minidriver; + +} NATIVE_DEVICE; + +static struct list tracked_devices = LIST_INIT(tracked_devices); + +typedef NTSTATUS WINAPI (*pAddDevice)(DRIVER_OBJECT *DriverObject, DEVICE_OBJECT *PhysicalDeviceObject); + +NTSTATUS WINAPI PNP_AddDevice(DRIVER_OBJECT *driver, void *native) +{ + DEVICE_OBJECT *device = NULL; + NTSTATUS status; + minidriver *minidriver; + HID_DEVICE_ATTRIBUTES attr; + BASE_DEVICE_EXTENSION *ext = NULL; + DWORD size; + WCHAR serial[256] = {0}; + WCHAR interface[256] = {0}; + DWORD index = HID_STRING_ID_ISERIALNUMBER; + NATIVE_DEVICE *tracked_device, *ptr; + INT interface_index = 1; + + static const WCHAR ig_fmtW[] = {'I','G','_','%','i',0}; + static const WCHAR im_fmtW[] = {'I','M','_','%','i',0}; + + + TRACE("native add device(%p)\n", native); + minidriver = find_minidriver(driver); + + status = HID_CreateDevice(native, &minidriver->minidriver, &device); + if (status != STATUS_SUCCESS) + { + ERR("Failed to create HID object (%x)\n",status); + return status; + } + + TRACE("Created device %p\n",device); + status = ((pAddDevice)minidriver->AddDevice)(minidriver->minidriver.DriverObject, device); + if (status != STATUS_SUCCESS) + { + ERR("Minidriver AddDevice failed (%x)\n",status); + HID_DeleteDevice(&minidriver->minidriver, device); + return status; + } + + status = call_minidriver(IOCTL_HID_GET_DEVICE_ATTRIBUTES, device, + NULL, 0, &attr, sizeof(attr)); + + if (status != STATUS_SUCCESS) + { + ERR("Minidriver failed to get Attributes(%x)\n",status); + HID_DeleteDevice(&minidriver->minidriver, device); + return status; + } + + ext = (BASE_DEVICE_EXTENSION*)device->DeviceExtension; + ext->information.VendorID = attr.VendorID; + ext->information.ProductID = attr.ProductID; + ext->information.VersionNumber = attr.VersionNumber; + ext->information.Polled = minidriver->minidriver.DevicesArePolled; + + tracked_device = HeapAlloc(GetProcessHeap(), 0, sizeof(*tracked_device)); + tracked_device->vidpid = MAKELONG(attr.VendorID, attr.ProductID); + tracked_device->native_device = native; + tracked_device->device = device; + tracked_device->minidriver = &minidriver->minidriver; + + LIST_FOR_EACH_ENTRY(ptr, &tracked_devices, NATIVE_DEVICE, entry) + if (ptr->vidpid == tracked_device->vidpid) interface_index++; + + list_add_tail(&tracked_devices, &tracked_device->entry); + + status = call_minidriver(IOCTL_WINE_HID_GET_PREPARSED_SIZE, device, + NULL, 0, &size, sizeof(size)); + + if (status == STATUS_SUCCESS) + { + ext->information.DescriptorSize = size; + ext->preparseData = HeapAlloc(GetProcessHeap(), 0, ext->information.DescriptorSize); + + status = call_minidriver(IOCTL_WINE_HID_GET_PREPARSED, device, + NULL, 0, ext->preparseData, size); + } + else + { + FIXME("Unable to get PreParsed Data\n"); + } + + status = call_minidriver(IOCTL_HID_GET_STRING, device, + (void*)index, sizeof(DWORD), serial, sizeof(serial)); + + if (serial[0] == 0) + { + static const WCHAR wZeroSerial[]= {'0','0','0','0',0}; + lstrcpyW(serial, wZeroSerial); + } + + if (ext->preparseData->caps.UsagePage == HID_USAGE_PAGE_GENERIC && + (ext->preparseData->caps.Usage == HID_USAGE_GENERIC_GAMEPAD || + ext->preparseData->caps.Usage == HID_USAGE_GENERIC_JOYSTICK)) + sprintfW(interface, ig_fmtW, interface_index); + else + sprintfW(interface, im_fmtW, interface_index); + + HID_LinkDevice(device, serial, interface); + + ext->poll_interval = DEFAULT_POLL_INTERVAL; + InitializeListHead(&ext->irp_queue); + + return STATUS_SUCCESS; +} + +void PNP_CleanupPNP(DRIVER_OBJECT *driver) +{ + NATIVE_DEVICE *tracked_device, *ptr; + + LIST_FOR_EACH_ENTRY_SAFE(tracked_device, ptr, &tracked_devices, + NATIVE_DEVICE, entry) + { + if (tracked_device->minidriver->DriverObject == driver) + { + list_remove(&tracked_device->entry); + HID_DeleteDevice(tracked_device->minidriver, tracked_device->device); + HeapFree(GetProcessHeap(), 0, tracked_device); + } + } +}