mouhid.sys: Request preparsed data and inspect device caps.

This commit is contained in:
Rémi Bernon 2024-02-13 12:33:18 +01:00 committed by Alexandre Julliard
parent 0ed830eac5
commit a05b67b7e5
3 changed files with 120 additions and 2 deletions

View file

@ -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};

View file

@ -1,5 +1,5 @@
MODULE = mouhid.sys
IMPORTS = ntoskrnl
IMPORTS = ntoskrnl hidparse
EXTRADLLFLAGS = -Wl,--subsystem,native
SOURCES = \

View file

@ -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;