From: Aric Stewart Subject: [1/13]hidclass.sys: Implement creating/destroying HID devices Message-Id: <55E60445.7070305@codeweavers.com> Date: Tue, 1 Sep 2015 15:02:13 -0500 --- dlls/hidclass.sys/Makefile.in | 1 + dlls/hidclass.sys/device.c | 196 ++++++++++++++++++++++++++++++++++++++++++ dlls/hidclass.sys/hid.h | 30 +++++++ dlls/hidclass.sys/parse.h | 80 +++++++++++++++++ include/ddk/hidport.h | 43 +++++++++ 5 files changed, 350 insertions(+) create mode 100644 dlls/hidclass.sys/device.c create mode 100644 dlls/hidclass.sys/parse.h diff --git a/dlls/hidclass.sys/Makefile.in b/dlls/hidclass.sys/Makefile.in index b3fdd1c..6b474f6 100644 --- a/dlls/hidclass.sys/Makefile.in +++ b/dlls/hidclass.sys/Makefile.in @@ -4,4 +4,5 @@ IMPORTS = ntoskrnl.exe DELAYIMPORTS = setupapi hid C_SRCS = \ + device.c \ main.c diff --git a/dlls/hidclass.sys/device.c b/dlls/hidclass.sys/device.c new file mode 100644 index 0000000..7760cb9 --- /dev/null +++ b/dlls/hidclass.sys/device.c @@ -0,0 +1,196 @@ +/* + * HIDClass device functions + * + * Copyright (C) 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 + */ + +#include "config.h" + +#include +#define NONAMELESSUNION +#include "hid.h" +#include "wine/unicode.h" +#include "winreg.h" +#include "winuser.h" +#include "setupapi.h" + +#include "wine/debug.h" +#include "ddk/hidsdi.h" +#include "ddk/hidtypes.h" + +#include "initguid.h" +#include "devguid.h" + +WINE_DEFAULT_DEBUG_CHANNEL(hid); + +static const WCHAR device_name_fmtW[] = {'\\','D','e','v','i','c','e', + '\\','H','I','D','#','%','p','&','%','p',0}; +static const WCHAR device_regname_fmtW[] = {'H','I','D','\\', + 'v','i','d','_','%','0','4','x','&','p','i','d','_','%', + '0','4','x','&','%','s','\\','%','i','&','%','s',0}; +static const WCHAR device_link_fmtW[] = {'\\','?','?','\\','h','i','d','#', + 'v','i','d','_','%','0','4','x','&','p','i','d','_','%', + '0','4','x','&','%','s','#','%','i','&','%','s','#','%','s',0}; +/* GUID_DEVINTERFACE_HID */ +static const WCHAR class_guid[] = {'{','4','D','1','E','5','5','B','2', + '-','F','1','6','F','-','1','1','C','F','-','8','8','C','B','-','0','0', + '1','1','1','1','0','0','0','0','3','0','}',0}; + + +NTSTATUS HID_CreateDevice(void *native_device, HID_MINIDRIVER_REGISTRATION *driver, DEVICE_OBJECT **device) +{ + WCHAR dev_name[255]; + UNICODE_STRING nameW; + NTSTATUS status; + BASE_DEVICE_EXTENSION *ext; + + sprintfW(dev_name, device_name_fmtW, driver->DriverObject, native_device); + RtlInitUnicodeString( &nameW, dev_name ); + + TRACE("Create base hid device %s\n", debugstr_w(dev_name)); + + status = IoCreateDevice(driver->DriverObject, driver->DeviceExtensionSize + sizeof(BASE_DEVICE_EXTENSION), &nameW, 0, 0, FALSE, device); + if (status) + { + FIXME( "failed to create device error %x\n", status ); + return status; + } + + ext = (*device)->DeviceExtension; + + ext->deviceExtension.MiniDeviceExtension = ((BYTE*)ext) + sizeof(BASE_DEVICE_EXTENSION); + ext->deviceExtension.PhysicalDeviceObject = *device; + ext->deviceExtension.NextDeviceObject = native_device; + ext->device_name = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(dev_name) + 1) * sizeof(WCHAR)); + lstrcpyW(ext->device_name, dev_name); + ext->link_name = NULL; + + return S_OK; +} + +NTSTATUS HID_LinkDevice(DEVICE_OBJECT *device, LPCWSTR serial, LPCWSTR index) +{ + WCHAR regname[255]; + WCHAR dev_link[255]; + SP_DEVINFO_DATA Data; + UNICODE_STRING nameW, linkW; + NTSTATUS status; + HDEVINFO devinfo; + GUID hidGuid; + BASE_DEVICE_EXTENSION *ext; + + HidD_GetHidGuid(&hidGuid); + ext = device->DeviceExtension; + + sprintfW(dev_link, device_link_fmtW, ext->information.VendorID, + ext->information.ProductID, index, ext->information.VersionNumber, serial, + class_guid); + struprW(dev_link); + + RtlInitUnicodeString( &nameW, ext->device_name); + RtlInitUnicodeString( &linkW, dev_link ); + + TRACE("Create link %s\n", debugstr_w(dev_link)); + + ext->link_name = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) * (lstrlenW(dev_link) + 1)); + lstrcpyW(ext->link_name, dev_link); + + status = IoCreateSymbolicLink( &linkW, &nameW ); + if (status) + { + FIXME( "failed to create link error %x\n", status ); + return status; + } + + sprintfW(regname, device_regname_fmtW, ext->information.VendorID, ext->information.ProductID, index, ext->information.VersionNumber, serial); + + devinfo = SetupDiGetClassDevsW(&GUID_DEVCLASS_HIDCLASS, NULL, NULL, DIGCF_DEVICEINTERFACE); + if (!devinfo) + { + FIXME( "failed to get ClassDevs %x\n", GetLastError()); + return GetLastError(); + } + Data.cbSize = sizeof(Data); + if (!SetupDiCreateDeviceInfoW(devinfo, regname, &GUID_DEVCLASS_HIDCLASS, NULL, NULL, DICD_INHERIT_CLASSDRVS, &Data)) + { + if (GetLastError() == ERROR_DEVINST_ALREADY_EXISTS) + { + SetupDiDestroyDeviceInfoList(devinfo); + return ERROR_SUCCESS; + } + FIXME( "failed to Create Device Info %x\n", GetLastError()); + return GetLastError(); + } + if (!SetupDiRegisterDeviceInfo( devinfo, &Data, 0, NULL, NULL, NULL )) + { + FIXME( "failed to Register Device Info %x\n", GetLastError()); + return GetLastError(); + } + if (!SetupDiCreateDeviceInterfaceW( devinfo, &Data, &hidGuid, NULL, 0, NULL)) + { + FIXME( "failed to Create Device Interface %x\n", GetLastError()); + return GetLastError(); + } + SetupDiDestroyDeviceInfoList(devinfo); + + return S_OK; +} + +void HID_DeleteDevice(HID_MINIDRIVER_REGISTRATION *driver, DEVICE_OBJECT *device) +{ + NTSTATUS status; + BASE_DEVICE_EXTENSION *ext; + UNICODE_STRING linkW; + LIST_ENTRY *entry; + IRP *irp; + + ext = device->DeviceExtension; + + if (ext->link_name) + { + TRACE("Delete link %s\n", debugstr_w(ext->link_name)); + RtlInitUnicodeString(&linkW, ext->link_name); + + status = IoDeleteSymbolicLink(&linkW); + if (status != STATUS_SUCCESS) + ERR("Delete Symbolic Link failed (%x)\n",status); + } + + if (ext->thread) + { + SetEvent(ext->halt_event); + WaitForSingleObject(ext->thread, INFINITE); + } + CloseHandle(ext->halt_event); + + HeapFree(GetProcessHeap(), 0, ext->preparseData); + + entry = RemoveHeadList(&ext->irp_queue); + while(entry != &ext->irp_queue) + { + irp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry); + irp->IoStatus.u.Status = STATUS_DEVICE_REMOVED; + IoCompleteRequest(irp, IO_NO_INCREMENT); + entry = RemoveHeadList(&ext->irp_queue); + } + + TRACE("Delete device(%p) %s\n", device, debugstr_w(ext->device_name)); + HeapFree(GetProcessHeap(), 0, ext->device_name); + HeapFree(GetProcessHeap(), 0, ext->link_name); + + IoDeleteDevice(device); +} diff --git a/dlls/hidclass.sys/hid.h b/dlls/hidclass.sys/hid.h index f058e46..d7c16ee 100644 --- a/dlls/hidclass.sys/hid.h +++ b/dlls/hidclass.sys/hid.h @@ -21,9 +21,34 @@ #include "windef.h" #include "winbase.h" #include "winternl.h" +#include "winioctl.h" #include "ddk/wdm.h" +#include "hidusage.h" #include "ddk/hidport.h" +#include "ddk/hidclass.h" +#include "ddk/hidpi.h" #include "wine/list.h" +#include "parse.h" + +#define DEFAULT_POLL_INTERVAL 200 +#define MAX_POLL_INTERVAL_MSEC 10000 + +typedef struct _BASE_DEVICE_EXTENSTION { + HID_DEVICE_EXTENSION deviceExtension; + + HID_COLLECTION_INFORMATION information; + WINE_HIDP_PREPARSED_DATA *preparseData; + + ULONG poll_interval; + WCHAR *device_name; + WCHAR *link_name; + HANDLE halt_event; + HANDLE thread; + + LIST_ENTRY irp_queue; + + /* Minidriver Specific stuff will end up here */ +} BASE_DEVICE_EXTENSION; typedef struct _minidriver { @@ -33,3 +58,8 @@ typedef struct _minidriver PDRIVER_UNLOAD DriverUnload; } minidriver; + +/* 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; diff --git a/dlls/hidclass.sys/parse.h b/dlls/hidclass.sys/parse.h new file mode 100644 index 0000000..6afd87f --- /dev/null +++ b/dlls/hidclass.sys/parse.h @@ -0,0 +1,80 @@ +/* + * Wine internal HID structures + * + * 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 + */ + +#ifndef __WINE_WINE_HID_H +#define __WINE_WINE_HID_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define HID_MAGIC 0x8491759 + +typedef enum __WINE_ELEMENT_TYPE { + UnknownElement = 0, + ButtonElement, + ValueElement, +} WINE_ELEMENT_TYPE; + +typedef struct __WINE_ELEMENT +{ + WINE_ELEMENT_TYPE ElementType; + UINT valueStartBit; + UINT bitCount; + union { + HIDP_VALUE_CAPS value; + HIDP_BUTTON_CAPS button; + } caps; +} WINE_HID_ELEMENT; + +typedef struct __WINE_HID_REPORT +{ + UCHAR reportID; + DWORD dwSize; + DWORD elementCount; + WINE_HID_ELEMENT Elements[1]; +} WINE_HID_REPORT; + +typedef struct __WINE_HIDP_PREPARSED_DATA +{ + DWORD magic; + DWORD dwSize; + HIDP_CAPS caps; + + DWORD dwInputReportCount; + DWORD dwOutputReportCount; + DWORD dwFeatureReportCount; + + DWORD dwOutputReportOffset; + DWORD dwFeatureReportOffset; + + WINE_HID_REPORT InputReports[1]; +} WINE_HIDP_PREPARSED_DATA; + +#define HID_NEXT_REPORT(d,r) ((r)?(WINE_HID_REPORT*)(((BYTE*)r)+r->dwSize):d->InputReports) +#define HID_INPUT_REPORTS(d) (d->InputReports) +#define HID_OUTPUT_REPORTS(d) (WINE_HID_REPORT*)(((BYTE*)d->InputReports)+d->dwOutputReportOffset) +#define HID_FEATURE_REPORTS(d) (WINE_HID_REPORT*)(((BYTE*)d->InputReports)+d->dwFeatureReportOffset) + +#ifdef __cplusplus +} +#endif + +#endif /* __WINE_WINE_HID_H */ diff --git a/include/ddk/hidport.h b/include/ddk/hidport.h index 219a8de..3ebe2735 100644 --- a/include/ddk/hidport.h +++ b/include/ddk/hidport.h @@ -33,4 +33,47 @@ typedef struct _HID_MINIDRIVER_REGISTRATION NTSTATUS WINAPI HidRegisterMinidriver(PHID_MINIDRIVER_REGISTRATION MinidriverRegistration); +typedef struct _HID_DEVICE_EXTENSION +{ + PDEVICE_OBJECT PhysicalDeviceObject; + PDEVICE_OBJECT NextDeviceObject; + PVOID MiniDeviceExtension; +} HID_DEVICE_EXTENSION, *PHID_DEVICE_EXTENSION; + +typedef struct _HID_DEVICE_ATTRIBUTES +{ + ULONG Size; + USHORT VendorID; + USHORT ProductID; + USHORT VersionNumber; + USHORT Reserved[11]; +} HID_DEVICE_ATTRIBUTES, *PHID_DEVICE_ATTRIBUTES; + +typedef struct _HID_DESCRIPTOR +{ + UCHAR bLength; + UCHAR bDescriptorType; + USHORT bcdHID; + UCHAR bCountry; + UCHAR bNumDescriptors; + struct _HID_DESCRIPTOR_DESC_LIST + { + UCHAR bReportType; + USHORT wReportLength; + } DescriptorList[1]; +} HID_DESCRIPTOR, *PHID_DESCRIPTOR; + +#define HID_HID_DESCRIPTOR_TYPE 0x21 +#define HID_REPORT_DESCRIPTOR_TYPE 0x22 + +#define IOCTL_HID_GET_DEVICE_DESCRIPTOR HID_CTL_CODE(0) +#define IOCTL_HID_GET_REPORT_DESCRIPTOR HID_CTL_CODE(1) +#define IOCTL_HID_READ_REPORT HID_CTL_CODE(2) +#define IOCTL_HID_WRITE_REPORT HID_CTL_CODE(3) +#define IOCTL_HID_GET_STRING HID_CTL_CODE(4) +#define IOCTL_HID_ACTIVATE_DEVICE HID_CTL_CODE(7) +#define IOCTL_HID_DEACTIVATE_DEVICE HID_CTL_CODE(8) +#define IOCTL_HID_GET_DEVICE_ATTRIBUTES HID_CTL_CODE(9) +#define IOCTL_HID_SEND_IDLE_NOTIFICATION_REQUEST HID_CTL_CODE(10) + #endif /* __HIDPORT_H__ */