| /* |
| * 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 <unistd.h> |
| #include <stdarg.h> |
| #include "hid.h" |
| #include "ddk/hidtypes.h" |
| #include "regstr.h" |
| #include "wine/debug.h" |
| #include "wine/unicode.h" |
| #include "wine/list.h" |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(hid); |
| |
| static const WCHAR device_enumeratorW[] = {'H','I','D',0}; |
| static const WCHAR separator_W[] = {'\\',0}; |
| static const WCHAR device_deviceid_fmtW[] = {'%','s','\\', |
| 'v','i','d','_','%','0','4','x','&','p','i','d','_','%', '0','4','x'}; |
| |
| static NTSTATUS WINAPI internalComplete(DEVICE_OBJECT *deviceObject, IRP *irp, |
| void *context) |
| { |
| HANDLE event = context; |
| SetEvent(event); |
| return STATUS_MORE_PROCESSING_REQUIRED; |
| } |
| |
| static NTSTATUS get_device_id(DEVICE_OBJECT *device, BUS_QUERY_ID_TYPE type, WCHAR **id) |
| { |
| NTSTATUS status; |
| IO_STACK_LOCATION *irpsp; |
| IO_STATUS_BLOCK irp_status; |
| HANDLE event; |
| IRP *irp; |
| |
| irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP, device, NULL, 0, NULL, NULL, &irp_status); |
| if (irp == NULL) |
| return STATUS_NO_MEMORY; |
| |
| event = CreateEventA(NULL, FALSE, FALSE, NULL); |
| irpsp = IoGetNextIrpStackLocation(irp); |
| irpsp->MinorFunction = IRP_MN_QUERY_ID; |
| irpsp->Parameters.QueryId.IdType = type; |
| |
| IoSetCompletionRoutine(irp, internalComplete, event, TRUE, TRUE, TRUE); |
| status = IoCallDriver(device, irp); |
| if (status == STATUS_PENDING) |
| WaitForSingleObject(event, INFINITE); |
| |
| *id = (WCHAR*)irp->IoStatus.Information; |
| status = irp->IoStatus.u.Status; |
| IoCompleteRequest(irp, IO_NO_INCREMENT ); |
| CloseHandle(event); |
| |
| return status; |
| } |
| |
| NTSTATUS WINAPI PNP_AddDevice(DRIVER_OBJECT *driver, DEVICE_OBJECT *PDO) |
| { |
| DEVICE_OBJECT *device = NULL; |
| NTSTATUS status; |
| minidriver *minidriver; |
| HID_DEVICE_ATTRIBUTES attr; |
| BASE_DEVICE_EXTENSION *ext = NULL; |
| HID_DESCRIPTOR descriptor; |
| BYTE *reportDescriptor; |
| INT i; |
| WCHAR *PDO_id; |
| WCHAR *id_ptr; |
| |
| status = get_device_id(PDO, BusQueryInstanceID, &PDO_id); |
| if (status != STATUS_SUCCESS) |
| { |
| ERR("Failed to get PDO id(%x)\n",status); |
| return status; |
| } |
| |
| TRACE("PDO add device(%p:%s)\n", PDO, debugstr_w(PDO_id)); |
| minidriver = find_minidriver(driver); |
| |
| status = HID_CreateDevice(PDO, &minidriver->minidriver, &device); |
| if (status != STATUS_SUCCESS) |
| { |
| ERR("Failed to create HID object (%x)\n",status); |
| HeapFree(GetProcessHeap(), 0, PDO_id); |
| return status; |
| } |
| |
| ext = device->DeviceExtension; |
| InitializeListHead(&ext->irp_queue); |
| |
| TRACE("Created device %p\n",device); |
| status = minidriver->AddDevice(minidriver->minidriver.DriverObject, device); |
| if (status != STATUS_SUCCESS) |
| { |
| ERR("Minidriver AddDevice failed (%x)\n",status); |
| HeapFree(GetProcessHeap(), 0, PDO_id); |
| 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); |
| HeapFree(GetProcessHeap(), 0, PDO_id); |
| return status; |
| } |
| |
| ext->information.VendorID = attr.VendorID; |
| ext->information.ProductID = attr.ProductID; |
| ext->information.VersionNumber = attr.VersionNumber; |
| ext->information.Polled = minidriver->minidriver.DevicesArePolled; |
| |
| status = call_minidriver(IOCTL_HID_GET_DEVICE_DESCRIPTOR, device, NULL, 0, |
| &descriptor, sizeof(descriptor)); |
| if (status != STATUS_SUCCESS) |
| { |
| ERR("Cannot get Device Descriptor(%x)\n",status); |
| HID_DeleteDevice(&minidriver->minidriver, device); |
| HeapFree(GetProcessHeap(), 0, PDO_id); |
| return status; |
| } |
| for (i = 0; i < descriptor.bNumDescriptors; i++) |
| if (descriptor.DescriptorList[i].bReportType == HID_REPORT_DESCRIPTOR_TYPE) |
| break; |
| |
| if (i >= descriptor.bNumDescriptors) |
| { |
| ERR("No Report Descriptor found in reply\n"); |
| HID_DeleteDevice(&minidriver->minidriver, device); |
| HeapFree(GetProcessHeap(), 0, PDO_id); |
| return status; |
| } |
| |
| reportDescriptor = HeapAlloc(GetProcessHeap(), 0, descriptor.DescriptorList[i].wReportLength); |
| status = call_minidriver(IOCTL_HID_GET_REPORT_DESCRIPTOR, device, NULL, 0, |
| reportDescriptor, descriptor.DescriptorList[i].wReportLength); |
| if (status != STATUS_SUCCESS) |
| { |
| ERR("Cannot get Report Descriptor(%x)\n",status); |
| HID_DeleteDevice(&minidriver->minidriver, device); |
| HeapFree(GetProcessHeap(), 0, reportDescriptor); |
| HeapFree(GetProcessHeap(), 0, PDO_id); |
| return status; |
| } |
| |
| ext->preparseData = ParseDescriptor(reportDescriptor, descriptor.DescriptorList[0].wReportLength); |
| |
| HeapFree(GetProcessHeap(), 0, reportDescriptor); |
| if (!ext->preparseData) |
| { |
| ERR("Cannot parse Report Descriptor\n"); |
| HID_DeleteDevice(&minidriver->minidriver, device); |
| HeapFree(GetProcessHeap(), 0, PDO_id); |
| return STATUS_NOT_SUPPORTED; |
| } |
| |
| ext->information.DescriptorSize = ext->preparseData->dwSize; |
| |
| lstrcpyW(ext->instance_id, device_enumeratorW); |
| strcatW(ext->instance_id, separator_W); |
| /* Skip the original enumerator */ |
| id_ptr = strchrW(PDO_id, '\\'); |
| id_ptr++; |
| strcatW(ext->instance_id, id_ptr); |
| HeapFree(GetProcessHeap(), 0, PDO_id); |
| |
| sprintfW(ext->device_id, device_deviceid_fmtW, device_enumeratorW, ext->information.VendorID, ext->information.ProductID); |
| |
| HID_LinkDevice(device); |
| |
| ext->poll_interval = DEFAULT_POLL_INTERVAL; |
| |
| ext->ring_buffer = RingBuffer_Create(sizeof(HID_XFER_PACKET) + ext->preparseData->caps.InputReportByteLength); |
| |
| HID_StartDeviceThread(device); |
| |
| return STATUS_SUCCESS; |
| } |
| |
| NTSTATUS WINAPI HID_PNP_Dispatch(DEVICE_OBJECT *device, IRP *irp) |
| { |
| NTSTATUS rc = STATUS_NOT_SUPPORTED; |
| IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation(irp); |
| minidriver *minidriver = find_minidriver(device->DriverObject); |
| |
| TRACE("%p, %p\n", device, irp); |
| |
| switch(irpsp->MinorFunction) |
| { |
| case IRP_MN_QUERY_ID: |
| { |
| BASE_DEVICE_EXTENSION *ext = device->DeviceExtension; |
| WCHAR *id = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WCHAR)*REGSTR_VAL_MAX_HCID_LEN); |
| TRACE("IRP_MN_QUERY_ID[%i]\n", irpsp->Parameters.QueryId.IdType); |
| switch (irpsp->Parameters.QueryId.IdType) |
| { |
| case BusQueryHardwareIDs: |
| case BusQueryCompatibleIDs: |
| { |
| WCHAR *ptr; |
| ptr = id; |
| /* Instance ID */ |
| strcpyW(ptr, ext->instance_id); |
| ptr += lstrlenW(ext->instance_id) + 1; |
| /* Device ID */ |
| strcpyW(ptr, ext->device_id); |
| ptr += lstrlenW(ext->device_id) + 1; |
| /* Bus ID */ |
| strcpyW(ptr, device_enumeratorW); |
| ptr += lstrlenW(device_enumeratorW) + 1; |
| *ptr = 0; |
| irp->IoStatus.Information = (ULONG_PTR)id; |
| rc = STATUS_SUCCESS; |
| break; |
| } |
| case BusQueryDeviceID: |
| strcpyW(id, ext->device_id); |
| irp->IoStatus.Information = (ULONG_PTR)id; |
| rc = STATUS_SUCCESS; |
| break; |
| case BusQueryInstanceID: |
| strcpyW(id, ext->instance_id); |
| irp->IoStatus.Information = (ULONG_PTR)id; |
| rc = STATUS_SUCCESS; |
| break; |
| case BusQueryDeviceSerialNumber: |
| FIXME("BusQueryDeviceSerialNumber not implemented\n"); |
| HeapFree(GetProcessHeap(), 0, id); |
| break; |
| } |
| break; |
| } |
| case IRP_MN_REMOVE_DEVICE: |
| { |
| rc = minidriver->PNPDispatch(device, irp); |
| HID_DeleteDevice(&minidriver->minidriver, device); |
| return rc; |
| } |
| default: |
| { |
| /* Forward IRP to the minidriver */ |
| return minidriver->PNPDispatch(device, irp); |
| } |
| } |
| |
| irp->IoStatus.u.Status = rc; |
| IoCompleteRequest( irp, IO_NO_INCREMENT ); |
| return rc; |
| } |