From: Rémi Bernon rbernon@codeweavers.com
--- dlls/dinput/tests/device8.c | 1 - dlls/mouhid.sys/Makefile.in | 2 +- dlls/mouhid.sys/main.c | 119 ++++++++++++++++++++++++++++++++++++ 3 files changed, 120 insertions(+), 2 deletions(-)
diff --git a/dlls/dinput/tests/device8.c b/dlls/dinput/tests/device8.c index fa5b800b74f..2c44bb4c5c6 100644 --- a/dlls/dinput/tests/device8.c +++ b/dlls/dinput/tests/device8.c @@ -1906,7 +1906,6 @@ static void test_hid_touch_screen(void) .report_id = 1, .report_len = 2, .report_buf = {1,0x02}, - .todo = TRUE, };
RAWINPUTDEVICE rawdevice = {.usUsagePage = HID_USAGE_PAGE_DIGITIZER, .usUsage = HID_USAGE_DIGITIZER_TOUCH_SCREEN}; diff --git a/dlls/mouhid.sys/Makefile.in b/dlls/mouhid.sys/Makefile.in index 816d7e58120..52e65a82cb3 100644 --- a/dlls/mouhid.sys/Makefile.in +++ b/dlls/mouhid.sys/Makefile.in @@ -1,5 +1,5 @@ MODULE = mouhid.sys -IMPORTS = ntoskrnl +IMPORTS = ntoskrnl hidparse EXTRADLLFLAGS = -Wl,--subsystem,native
SOURCES = \ diff --git a/dlls/mouhid.sys/main.c b/dlls/mouhid.sys/main.c index af54ffbd70b..f652857f7ac 100644 --- a/dlls/mouhid.sys/main.c +++ b/dlls/mouhid.sys/main.c @@ -42,6 +42,13 @@ struct device { LONG removed; DEVICE_OBJECT *bus_device; + PHIDP_PREPARSED_DATA preparsed; + + ULONG caps_count; + ULONG contact_max; + HIDP_VALUE_CAPS *id_caps; + HIDP_VALUE_CAPS *x_caps; + HIDP_VALUE_CAPS *y_caps; };
static inline struct device *impl_from_DEVICE_OBJECT( DEVICE_OBJECT *device ) @@ -49,6 +56,21 @@ static inline struct device *impl_from_DEVICE_OBJECT( DEVICE_OBJECT *device ) return (struct device *)device->DeviceExtension; }
+static inline LONG sign_extend( ULONG value, const HIDP_VALUE_CAPS *caps ) +{ + UINT sign = 1 << (caps->BitSize - 1); + if (sign <= 1 || caps->LogicalMin >= 0) return value; + return value - ((value & sign) << 1); +} + +static inline LONG scale_value( ULONG value, const HIDP_VALUE_CAPS *caps, LONG min, LONG max ) +{ + LONG tmp = sign_extend( value, caps ); + if (caps->LogicalMin > caps->LogicalMax) return 0; + if (caps->LogicalMin > tmp || caps->LogicalMax < tmp) return 0; + return min + MulDiv( tmp - caps->LogicalMin, max - min, caps->LogicalMax - caps->LogicalMin ); +} + static NTSTATUS WINAPI driver_ioctl( DEVICE_OBJECT *device, IRP *irp ) { IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation( irp ); @@ -69,6 +91,98 @@ static NTSTATUS WINAPI driver_ioctl( DEVICE_OBJECT *device, IRP *irp ) return IoCallDriver( impl->bus_device, irp ); }
+static NTSTATUS call_hid_device( DEVICE_OBJECT *device, DWORD major, DWORD code, void *in_buf, + DWORD in_len, void *out_buf, DWORD out_len ) +{ + struct device *impl = impl_from_DEVICE_OBJECT( device ); + IO_STATUS_BLOCK io; + NTSTATUS status; + KEVENT event; + IRP *irp; + + KeInitializeEvent( &event, NotificationEvent, FALSE ); + irp = IoBuildDeviceIoControlRequest( code, device, in_buf, in_len, out_buf, out_len, + FALSE, &event, &io ); + if (!irp) return STATUS_NO_MEMORY; + + status = IoCallDriver( impl->bus_device, irp ); + if (status == STATUS_PENDING) KeWaitForSingleObject( &event, Executive, KernelMode, FALSE, NULL ); + return io.Status; +} + +static NTSTATUS initialize_device( DEVICE_OBJECT *device ) +{ + struct device *impl = impl_from_DEVICE_OBJECT( device ); + HID_COLLECTION_INFORMATION info; + USHORT usage, count, report_len; + HIDP_VALUE_CAPS value_caps; + char *report_buf; + NTSTATUS status; + HIDP_CAPS caps; + + if ((status = call_hid_device( device, IRP_MJ_DEVICE_CONTROL, IOCTL_HID_GET_COLLECTION_INFORMATION, + &info, 0, &info, sizeof(info) ))) + return status; + if (!(impl->preparsed = malloc( info.DescriptorSize ))) return STATUS_NO_MEMORY; + if ((status = call_hid_device( device, IRP_MJ_DEVICE_CONTROL, IOCTL_HID_GET_COLLECTION_DESCRIPTOR, + NULL, 0, impl->preparsed, info.DescriptorSize ))) + return status; + + status = HidP_GetCaps( impl->preparsed, &caps ); + if (status != HIDP_STATUS_SUCCESS) return status; + + count = 1; + usage = HID_USAGE_DIGITIZER_CONTACT_COUNT_MAX; + status = HidP_GetSpecificValueCaps( HidP_Feature, HID_USAGE_PAGE_DIGITIZER, 0, usage, + &value_caps, &count, impl->preparsed ); + if (status == HIDP_STATUS_SUCCESS) + { + report_len = caps.FeatureReportByteLength; + if (!(report_buf = malloc( report_len ))) return STATUS_NO_MEMORY; + report_buf[0] = value_caps.ReportID; + + status = call_hid_device( device, IRP_MJ_DEVICE_CONTROL, IOCTL_HID_GET_FEATURE, NULL, 0, report_buf, report_len ); + if (!status) status = HidP_GetUsageValue( HidP_Feature, HID_USAGE_PAGE_DIGITIZER, 0, usage, + &impl->contact_max, impl->preparsed, report_buf, report_len ); + free( report_buf ); + if (status && status != HIDP_STATUS_SUCCESS) return status; + + count = 1; + usage = HID_USAGE_DIGITIZER_CONTACT_COUNT; + status = HidP_GetSpecificValueCaps( HidP_Input, HID_USAGE_PAGE_DIGITIZER, 0, usage, + &value_caps, &count, impl->preparsed ); + if (status != HIDP_STATUS_SUCCESS) goto failed; + + count = caps.NumberLinkCollectionNodes; + usage = HID_USAGE_DIGITIZER_CONTACT_ID; + if (!(impl->id_caps = malloc( sizeof(*impl->id_caps) * caps.NumberLinkCollectionNodes ))) return STATUS_NO_MEMORY; + status = HidP_GetSpecificValueCaps( HidP_Input, HID_USAGE_PAGE_DIGITIZER, 0, usage, + impl->id_caps, &count, impl->preparsed ); + if (status != HIDP_STATUS_SUCCESS) goto failed; + impl->caps_count = count; + + count = impl->caps_count; + usage = HID_USAGE_GENERIC_X; + if (!(impl->x_caps = malloc( sizeof(*impl->x_caps) * impl->caps_count ))) return STATUS_NO_MEMORY; + status = HidP_GetSpecificValueCaps( HidP_Input, HID_USAGE_PAGE_GENERIC, 0, usage, + impl->x_caps, &count, impl->preparsed ); + if (status != HIDP_STATUS_SUCCESS) goto failed; + + count = impl->caps_count; + usage = HID_USAGE_GENERIC_Y; + if (!(impl->y_caps = malloc( sizeof(*impl->y_caps) * impl->caps_count ))) return STATUS_NO_MEMORY; + status = HidP_GetSpecificValueCaps( HidP_Input, HID_USAGE_PAGE_GENERIC, 0, usage, + impl->y_caps, &count, impl->preparsed ); + if (status != HIDP_STATUS_SUCCESS) goto failed; + + return STATUS_SUCCESS; + } + +failed: + ERR( "%#x usage not found, unsupported device\n", usage ); + return STATUS_NOT_SUPPORTED; +} + static NTSTATUS WINAPI set_event_completion( DEVICE_OBJECT *device, IRP *irp, void *context ) { if (irp->PendingReturned) KeSetEvent( (KEVENT *)context, IO_NO_INCREMENT, FALSE ); @@ -98,6 +212,7 @@ static NTSTATUS WINAPI driver_pnp( DEVICE_OBJECT *device, IRP *irp ) KeWaitForSingleObject( &event, Executive, KernelMode, FALSE, NULL ); status = irp->IoStatus.Status; } + if (!status) status = initialize_device( device );
if (status) irp->IoStatus.Status = status; IoCompleteRequest( irp, IO_NO_INCREMENT ); @@ -111,6 +226,10 @@ static NTSTATUS WINAPI driver_pnp( DEVICE_OBJECT *device, IRP *irp ) IoSkipCurrentIrpStackLocation( irp ); status = IoCallDriver( impl->bus_device, irp ); IoDetachDevice( impl->bus_device ); + free( impl->id_caps ); + free( impl->x_caps ); + free( impl->y_caps ); + free( impl->preparsed ); IoDeleteDevice( device ); return status;